1 line
38 KiB
JSON
1 line
38 KiB
JSON
{"repo": "chivukulavirinchi/kalpataru", "files": {"README.md": {"7d4822f89eb325c3203b0d9e4b024f7b4dabe56d": {"t": 1785596380.2102962, "n": 8555, "h": "c37d1be9b78634ba", "c": "<div align=\"center\">\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"site/public/kalpataru-wordmark-dark.svg\">\n <img src=\"site/public/kalpataru-wordmark-light.svg\" alt=\"Kalpataru — SolidWorks for any agent\" width=\"420\">\n</picture>\n\n### कल्पतरु · *the tree that grants any wish*\n\n**Drive SolidWorks from any AI agent.** gRPC bridge, MCP server, six skill bundles. Bring your own LLM (paid or free). MIT-licensed. Fully local. No SaaS.\n\n[**Site**](https://chivukulavirinchi.github.io/Kalpataru) · [**Quickstart**](https://chivukulavirinchi.github.io/Kalpataru/docs/quickstart) · [**Examples**](https://chivukulavirinchi.github.io/Kalpataru/examples) · [**Concepts**](https://chivukulavirinchi.github.io/Kalpataru/docs/concepts)\n\n\n\n\n\n\n<img src=\"site/public/kalpataru-solidworks-bridge-demo.gif\" alt=\"Kalpataru SolidWorks bridge demo — an AI agent building a part live\" width=\"800\">\n</div>\n\n---\n\n## Why this exists\n\nKalpataru is the mythic wish-fulfilling tree from Lord Indra's garden — whatever you ask of it, it gives. We named the project after the tree because that's the user fantasy this stack chases: **describe the part you want, get the part.** A bracket, a flange, a pulley housing — say it out loud, watch SolidWorks build it.\n\nIn mid-2026, the funded text-to-CAD startups (Adam, MecAgent, Zoo) point at consumer 3D-printing. The incumbents (SolidWorks LEO, Fusion Assistant, Onshape AI Advisor) are launching this quarter for paid seats. The open-source agentic-CAD wins so far have all been on Onshape ([jarvis-onshape-mcp](https://github.com/ReshefElisha/jarvis-onshape-mcp)) or FreeCAD ([freecad-mcp](https://github.com/bonninr/freecad_mcp)) — because their automation surfaces are clean. **Nobody had built the SolidWorks equivalent.** Every SolidWorks-MCP repo on GitHub has fewer than 20 stars and trips on the second feature because COM entity IDs drift across rebuilds.\n\nKalpataru solves entity-ID stability via `GetPersistReference3` + an EntityCache that reconciles after every rebuild. `F3` keeps meaning the same physical face after a fillet, even though the underlying COM object has been replaced. That single piece — taking six months to get right — is the wedge. Everything else (Python SDK, MCP server, agent skills, site) is what makes the wedge usable.\n\n---\n\n## Five-line install\n\n```sh\npip install kalpataru\nkalpataru check # diagnose your setup\nkalpataru skill install --target claude # or opencode / codex / cursor / all\nkalpataru demo # builds a cube-with-hole, ~3s\n# in your agent: \"build me a cube\" → cube appears in SolidWorks\n```\n\nYou need SolidWorks running on Windows (2023–2025) with the Kalpataru add-in enabled, or `kalpataru serve` for the standalone dev mode. See [`docs/install`](https://chivukulavirinchi.github.io/Kalpataru/docs/install) for the full walkthrough.\n\n## See it work — actual agent transcript\n\n> **You:** *Build me a 50 mm cube.*\n>\n> ```\n> agent → kalpataru_ping ✓ SolidWorks 2024 SP3\n> agent → kalpataru_new_part ✓ Part1\n> agent → kalpataru_create_sketch ✓ Sketch1 · ready_to_extrude=true\n> agent → kalpataru_extrude ✓ Boss-Extrude1 · +6 faces\n> done · wish granted 3.3s total\n> ```\n\nThat's it. No \"describe each step.\" No prompt engineering. The agent reads the `kalpataru-examples` skill, knows the four-call sequence for a cube, fires it. Same flow scales to L-brackets, revolved shafts, parts with mounting holes.\n\n## What you get\n\n| Layer | Path | What it does |\n|---|---|---|\n| **gRPC bridge** | `Server/`, `BRep/`, `AiCadServer/` | C# add-in (and standalone dev EXE) wrapping the SolidWorks COM API. Stable entity IDs via persist-references. |\n| **Python SDK** | `kalpataru_sdk/` | Typed Python façade. Sketch + feature builders. Typed exceptions. `pip install kalpataru`. |\n| **MCP server** | `kalpataru_mcp/` | Stdio MCP server, 25 tools, drop-in config for Claude Code / OpenCode / Cursor / Codex / Cline / Claude Desktop. |\n| **Skills** | `skills/` | Seven SKILL.md bundles — the one you'll feel is `kalpataru-examples` (copy-pasteable recipes for cube, cylinder, L-bracket, revolved shaft…). |\n| **CLI** | `kalpataru_sdk/cli.py` | `check`, `demo`, `serve`, `mcp-server`, `skill`, `version`. Rich terminal output. |\n| **Site** | `site/` | Astro + Tailwind GH Pages docs. Auto-deploys from `main`. |\n\n## Verified state — `tools/verify_all.py`\n\n**80 passing · 0 failing · 2 documented xfail.** Every layer of the stack — gRPC → Python SDK → MCP server → agent integration — runs end-to-end against a live SolidWorks. Full per-phase breakdown in [`tools/results/SUMMARY.md`](tools/results/SUMMARY.md).\n\n| Phase | Pass / Fail / xfail |\n|---|---|\n| Sketch entities (24 types) | 22 / 0 / 2 |\n| Features (16 types + §11 fixes) | 17 / 0 / 0 |\n| Queries | 11 / 0 / 0 |\n| Edit + undo/redo | 5 / 0 / 0 |\n| Demo + integration scripts | 3 / 0 / 0 |\n| MCP server tools | 11 / 0 / 0 |\n| Agent runtime integration | 11 / 0 / 0 |\n\nRe-run yourself: `python -m tools.verify_all`.\n\n## Benchmarks — `python -m bench`\n\n`tools/verify_all.py` proves the **bridge** works. `bench/` measures\nwhether the **agent** can actually use it. 22 curated CAD tasks across\nseven categories (smoke → easy → medium → hard → edit → read → hitl),\ngraded on five axes: geometric, procedural, decision, efficiency,\nrobustness. Goldens are programmatic Python recipes — no `.SLDPRT`\ncommitted.\n\nAll rows in the panel route through **OpenCode Go** —\n<https://opencode.ai/go> — one `OPENCODE_API_KEY` for six open-source\ncoders (DeepSeek V4 Pro/Flash, Kimi K2.6, GLM 5.1, Qwen 3.6 Plus,\nMiMo 2.5 Pro) at $10/month flat. Direct provider APIs would bill per\ntoken; Go caps the whole matrix at $60/month no matter how many runs.\n\n```sh\npython -m bench.tests.run_all # offline sanity (no SolidWorks)\npython -m bench --list-tasks\npython -m bench --list-models\npython -m bench --tasks smoke/cube --models deepseek-v4-flash\n```\n\nOutput lands under `bench/results/<timestamp>/<model>/<task>/` with full\nper-axis breakdowns plus a per-run `summary.md` and aggregated\n`leaderboard.md`. See [`bench/README.md`](bench/README.md) for the full\nspec, grading rubric, and how to add tasks or models.\n\n## What's honestly not solved\n\n- **Autonomous engineering-drawing → part.** Frontier benchmark across the industry is ~0.0 today. Kalpataru doesn't claim otherwise.\n- **5+ feature parts from a one-line prompt.** Reliability degrades the longer the chain. Multi-step parts work best when you describe them step-by-step.\n- **`sketch_chamfer` / `trim` via the programmatic path.** A COM-level fault in SolidWorks itself (documented in `CLAUDE.md` §11). The HITL path works.\n\n## Project layout\n\n```\nkalpataru/\n├── kalpataru_sdk/ Python SDK (pip-installable)\n├── kalpataru_mcp/ MCP server\n├── skills/ Seven SKILL.md bundles\n├── pyproject.toml Package metadata\n├── proto/ gRPC v2 definitions\n├── Server/ BRep/ C# gRPC service + feature/sketch bridges\n├── AiCadServer/ Standalone dev server (out-of-process COM)\n├── site/ Astro + Tailwind site\n├── examples/\n│ ├── mcp-config/ Config snippets for 6 agent runtimes\n│ ├── start-here/ First-project template\n│ └── integration/ Multi-feature integration scripts\n├── tools/ Verification harness (verify_all.py + phases)\n├── bench/ Benchmark suite (22 tasks × 5-axis grader)\n├── plans/ Phase 1 implementation plans\n└── CLAUDE.md Agent / contributor cheatsheet\n```\n\n## Contributing\n\n[`CONTRIBUTING.md`](CONTRIBUTING.md) has the specifics. We welcome bridge bug-fixes, new RPCs with tests, new SKILL.md bundles, MCP configs for runtimes we don't yet cover, doc fixes, and site improvements. We don't accept \"make the agent smarter\" PRs — that's Phase 2 work, intentionally out of scope for this repo today.\n\nNo `Co-Authored-By: Claude` or other LLM attribution in commits — if you used an LLM as a tool, the code is still yours.\n\n## Built by\n\n[ChivukulaVirinchi](mailto:virinchi@gurujada.coms)\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n"}}, "bench/config.yaml": {"7d4822f89eb325c3203b0d9e4b024f7b4dabe56d": {"t": 1785596398.4875333, "n": 2610, "h": "ab3bbc4a462f7144", "c": "# Kalpataru benchmark — model panel + defaults.\n#\n# All models are routed through the OpenCode Go plan\n# (https://opencode.ai/go) — a single flat-rate ($10/month after the first\n# month, with 5-hour / weekly / monthly usage caps) for open-source coding\n# models. One env var (OPENCODE_API_KEY) authenticates against the\n# OpenAI-compatible endpoint and gives access to every row below.\n#\n# The runner sets CAD_LLM_API_KEY / CAD_LLM_BASE_URL / CAD_LLM_MODEL from\n# each row before constructing CadAgent, so the agent's existing\n# OpenAI-compatible auth path picks them up unmodified.\n#\n# Why not direct provider APIs? Direct keys (DeepSeek, Zhipu, Moonshot,\n# Anthropic) bill per token — a full benchmark matrix (22 tasks × N\n# models × R repeats) easily exceeds $50 on the frontier ones. OpenCode\n# Go caps it at $60/month total no matter how many runs you do, with\n# generous rate limits for coding-agent workflows. Direct provider rows\n# can be re-enabled later by appending entries with different base_urls.\n#\n# Source: https://opencode.ai/docs/go/\n\nmodels:\n # GLM 5.1 — first row purely by convention (smallest reasoning budget).\n # All rows are reasoning-mode models; CadAgent round-trips the\n # reasoning_content / reasoning / reasoning_details fields so the\n # OpenCode Go gateway accepts subsequent turns.\n - name: glm-5.1\n model_id: glm-5.1\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\n # Heaviest open-source coder; best baseline for the suite\n - name: deepseek-v4-pro\n model_id: deepseek-v4-pro\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\n # Fast/cheap variant — useful for spotting where Pro's extra compute pays off\n - name: deepseek-v4-flash\n model_id: deepseek-v4-flash\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\n # Moonshot Kimi — strong agentic-coding scores in recent leaderboards\n - name: kimi-k2.6\n model_id: kimi-k2.6\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\n # Alibaba Qwen — coding-tuned, broadest language support (reasoning)\n - name: qwen-3.6-plus\n model_id: qwen3.6-plus\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\n # Xiaomi MiMo — newcomer worth measuring alongside the established players\n - name: mimo-v2.5-pro\n model_id: mimo-v2.5-pro\n base_url: https://opencode.ai/zen/go/v1\n api_key_env: OPENCODE_API_KEY\n\ndefaults:\n address: localhost:50052\n repeats: 1\n\nweights:\n geometric: 0.40\n procedural: 0.20\n decision: 0.15\n efficiency: 0.15\n robustness: 0.10\n"}}, "AiCadServer/Program.cs": {"7d4822f89eb325c3203b0d9e4b024f7b4dabe56d": {"t": 1785745807.7066789, "n": 8232, "h": "23f0daf490939dd5", "c": "// AiCadServer - Standalone gRPC server for development\n// Connects to a running SolidWorks instance via out-of-process COM\n// Hosts the same BRepService as the in-process add-in\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Grpc.Core;\nusing SolidWorks.Interop.sldworks;\nusing AiCadPlugin.Server;\nusing Kalpataru.Proto;\nusing AiCad.Proto;\n\nnamespace AiCadServer\n{\n class Program\n {\n private const int WebServerPort = 8765;\n private static Process _webServerProcess;\n\n static void Main(string[] args)\n {\n int port = 50052;\n if (args.Length > 0 && int.TryParse(args[0], out int customPort))\n port = customPort;\n\n // Route Debug/Trace writes to a known log file so external tools\n // (verification harness, log-tailers) can see what the bridge is\n // doing. Path: %TEMP%\\kalpataru-bridge.log\n try\n {\n string logPath = Path.Combine(Path.GetTempPath(), \"kalpataru-bridge.log\");\n var listener = new TextWriterTraceListener(logPath) { Name = \"kalpataru-file\" };\n Trace.Listeners.Add(listener);\n Debug.Listeners.Add(listener);\n Trace.AutoFlush = true;\n Trace.WriteLine($\"=== AiCadServer started at {DateTime.UtcNow:o} ===\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: could not attach trace listener: {ex.Message}\");\n }\n\n Console.WriteLine(\"AiCadServer - Standalone gRPC development server\");\n Console.WriteLine(\"================================================\");\n\n // 1. Connect to running SolidWorks via out-of-process COM\n ISldWorks swApp;\n try\n {\n swApp = (ISldWorks)Marshal.GetActiveObject(\"SldWorks.Application\");\n string version = \"unknown\";\n try { version = swApp.RevisionNumber(); } catch { }\n Console.WriteLine($\"Connected to SolidWorks {version}\");\n }\n catch (COMException ex)\n {\n Console.Error.WriteLine($\"ERROR: Cannot connect to SolidWorks: {ex.Message}\");\n Console.Error.WriteLine(\"Make sure SolidWorks is running before starting this server.\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 2. Create services\n var brepService = new BRepService(swApp);\n var v1Service = new GrpcAdapterService(swApp);\n\n // 3. Start gRPC server\n var server = new Grpc.Core.Server\n {\n Services =\n {\n CadAdapter.BindService(brepService),\n Adapter.BindService(v1Service)\n },\n Ports =\n {\n new ServerPort(\"localhost\", port, ServerCredentials.Insecure),\n new ServerPort(\"localhost\", 50051, ServerCredentials.Insecure)\n }\n };\n\n try\n {\n server.Start();\n Console.WriteLine($\"gRPC v2 server listening on localhost:{port}\");\n Console.WriteLine($\"gRPC v1 server listening on localhost:50051\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"ERROR: Failed to start gRPC server: {ex.Message}\");\n Console.Error.WriteLine(\"Is another instance already running on the same port?\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 3a. Start the Python web server (best-effort — failures are\n // logged but don't block the gRPC bridge).\n StartWebServer();\n\n Console.WriteLine();\n Console.WriteLine(\"Press Ctrl+C or Enter to stop...\");\n\n // 4. Wait for shutdown signal\n var shutdownEvent = new ManualResetEvent(false);\n Console.CancelKeyPress += (s, e) =>\n {\n e.Cancel = true;\n shutdownEvent.Set();\n };\n\n // Also allow Enter key to stop (only if stdin is interactive)\n if (System.Environment.UserInteractive && !Console.IsInputRedirected)\n {\n ThreadPool.QueueUserWorkItem(_ =>\n {\n try { Console.ReadLine(); } catch { }\n shutdownEvent.Set();\n });\n }\n\n shutdownEvent.WaitOne();\n\n // 5. Graceful shutdown\n Console.WriteLine(\"Shutting down web server...\");\n StopWebServer();\n Console.WriteLine(\"Shutting down gRPC server...\");\n server.ShutdownAsync().Wait(TimeSpan.FromSeconds(5));\n Console.WriteLine(\"Server stopped.\");\n }\n\n private static void StartWebServer()\n {\n try\n {\n // Project root is two levels up from bin\\Debug\\\n var assemblyDir = Path.GetDirectoryName(typeof(Program).Assembly.Location);\n var projectRoot = Path.GetFullPath(Path.Combine(assemblyDir, \"..\", \"..\", \"..\"));\n var launcherScript = Path.Combine(projectRoot, \"orchestrator\", \"start_web.py\");\n\n if (!File.Exists(launcherScript))\n {\n Console.Error.WriteLine($\"WARN: web launcher not found at {launcherScript}; UI will not start.\");\n return;\n }\n\n var psi = new ProcessStartInfo\n {\n FileName = \"python\",\n Arguments = $\"\\\"{launcherScript}\\\" --port {WebServerPort}\",\n WorkingDirectory = projectRoot,\n UseShellExecute = false,\n CreateNoWindow = true,\n RedirectStandardOutput = true,\n RedirectStandardError = true,\n };\n // Forward OPENCODE_API_KEY (and any other CAD_LLM_* env vars\n // the user set) to the child so the agent can switch to\n // OpenCode Go models without manual re-export.\n foreach (var key in new[] { \"OPENCODE_API_KEY\", \"CAD_LLM_API_KEY\", \"CAD_LLM_BASE_URL\", \"CAD_LLM_MODEL\", \"ANTHROPIC_API_KEY\", \"DEEPSEEK_API_KEY\" })\n {\n var val = System.Environment.GetEnvironmentVariable(key);\n if (!string.IsNullOrEmpty(val))\n psi.EnvironmentVariables[key] = val;\n }\n\n _webServerProcess = Process.Start(psi);\n\n if (_webServerProcess != null && !_webServerProcess.HasExited)\n {\n // Drain pipes so the child doesn't deadlock on a full buffer.\n _webServerProcess.BeginOutputReadLine();\n _webServerProcess.BeginErrorReadLine();\n Console.WriteLine($\"Web UI started (PID {_webServerProcess.Id}) → http://127.0.0.1:{WebServerPort}\");\n }\n else\n {\n Console.Error.WriteLine(\"WARN: web server failed to start; see start_web.py output for details.\");\n }\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: could not start web server: {ex.Message}\");\n }\n }\n\n private static void StopWebServer()\n {\n try\n {\n if (_webServerProcess != null && !_webServerProcess.HasExited)\n {\n _webServerProcess.Kill();\n _webServerProcess.WaitForExit(3000);\n }\n _webServerProcess?.Dispose();\n _webServerProcess = null;\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: error stopping web server: {ex.Message}\");\n }\n }\n }\n}\n"}, "7cfc3e6217959d9f001e39336feda9ab852fdfe0": {"t": 1785756722.5541494, "n": 8232, "h": "23f0daf490939dd5", "c": "// AiCadServer - Standalone gRPC server for development\n// Connects to a running SolidWorks instance via out-of-process COM\n// Hosts the same BRepService as the in-process add-in\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Grpc.Core;\nusing SolidWorks.Interop.sldworks;\nusing AiCadPlugin.Server;\nusing Kalpataru.Proto;\nusing AiCad.Proto;\n\nnamespace AiCadServer\n{\n class Program\n {\n private const int WebServerPort = 8765;\n private static Process _webServerProcess;\n\n static void Main(string[] args)\n {\n int port = 50052;\n if (args.Length > 0 && int.TryParse(args[0], out int customPort))\n port = customPort;\n\n // Route Debug/Trace writes to a known log file so external tools\n // (verification harness, log-tailers) can see what the bridge is\n // doing. Path: %TEMP%\\kalpataru-bridge.log\n try\n {\n string logPath = Path.Combine(Path.GetTempPath(), \"kalpataru-bridge.log\");\n var listener = new TextWriterTraceListener(logPath) { Name = \"kalpataru-file\" };\n Trace.Listeners.Add(listener);\n Debug.Listeners.Add(listener);\n Trace.AutoFlush = true;\n Trace.WriteLine($\"=== AiCadServer started at {DateTime.UtcNow:o} ===\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: could not attach trace listener: {ex.Message}\");\n }\n\n Console.WriteLine(\"AiCadServer - Standalone gRPC development server\");\n Console.WriteLine(\"================================================\");\n\n // 1. Connect to running SolidWorks via out-of-process COM\n ISldWorks swApp;\n try\n {\n swApp = (ISldWorks)Marshal.GetActiveObject(\"SldWorks.Application\");\n string version = \"unknown\";\n try { version = swApp.RevisionNumber(); } catch { }\n Console.WriteLine($\"Connected to SolidWorks {version}\");\n }\n catch (COMException ex)\n {\n Console.Error.WriteLine($\"ERROR: Cannot connect to SolidWorks: {ex.Message}\");\n Console.Error.WriteLine(\"Make sure SolidWorks is running before starting this server.\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 2. Create services\n var brepService = new BRepService(swApp);\n var v1Service = new GrpcAdapterService(swApp);\n\n // 3. Start gRPC server\n var server = new Grpc.Core.Server\n {\n Services =\n {\n CadAdapter.BindService(brepService),\n Adapter.BindService(v1Service)\n },\n Ports =\n {\n new ServerPort(\"localhost\", port, ServerCredentials.Insecure),\n new ServerPort(\"localhost\", 50051, ServerCredentials.Insecure)\n }\n };\n\n try\n {\n server.Start();\n Console.WriteLine($\"gRPC v2 server listening on localhost:{port}\");\n Console.WriteLine($\"gRPC v1 server listening on localhost:50051\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"ERROR: Failed to start gRPC server: {ex.Message}\");\n Console.Error.WriteLine(\"Is another instance already running on the same port?\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 3a. Start the Python web server (best-effort — failures are\n // logged but don't block the gRPC bridge).\n StartWebServer();\n\n Console.WriteLine();\n Console.WriteLine(\"Press Ctrl+C or Enter to stop...\");\n\n // 4. Wait for shutdown signal\n var shutdownEvent = new ManualResetEvent(false);\n Console.CancelKeyPress += (s, e) =>\n {\n e.Cancel = true;\n shutdownEvent.Set();\n };\n\n // Also allow Enter key to stop (only if stdin is interactive)\n if (System.Environment.UserInteractive && !Console.IsInputRedirected)\n {\n ThreadPool.QueueUserWorkItem(_ =>\n {\n try { Console.ReadLine(); } catch { }\n shutdownEvent.Set();\n });\n }\n\n shutdownEvent.WaitOne();\n\n // 5. Graceful shutdown\n Console.WriteLine(\"Shutting down web server...\");\n StopWebServer();\n Console.WriteLine(\"Shutting down gRPC server...\");\n server.ShutdownAsync().Wait(TimeSpan.FromSeconds(5));\n Console.WriteLine(\"Server stopped.\");\n }\n\n private static void StartWebServer()\n {\n try\n {\n // Project root is two levels up from bin\\Debug\\\n var assemblyDir = Path.GetDirectoryName(typeof(Program).Assembly.Location);\n var projectRoot = Path.GetFullPath(Path.Combine(assemblyDir, \"..\", \"..\", \"..\"));\n var launcherScript = Path.Combine(projectRoot, \"orchestrator\", \"start_web.py\");\n\n if (!File.Exists(launcherScript))\n {\n Console.Error.WriteLine($\"WARN: web launcher not found at {launcherScript}; UI will not start.\");\n return;\n }\n\n var psi = new ProcessStartInfo\n {\n FileName = \"python\",\n Arguments = $\"\\\"{launcherScript}\\\" --port {WebServerPort}\",\n WorkingDirectory = projectRoot,\n UseShellExecute = false,\n CreateNoWindow = true,\n RedirectStandardOutput = true,\n RedirectStandardError = true,\n };\n // Forward OPENCODE_API_KEY (and any other CAD_LLM_* env vars\n // the user set) to the child so the agent can switch to\n // OpenCode Go models without manual re-export.\n foreach (var key in new[] { \"OPENCODE_API_KEY\", \"CAD_LLM_API_KEY\", \"CAD_LLM_BASE_URL\", \"CAD_LLM_MODEL\", \"ANTHROPIC_API_KEY\", \"DEEPSEEK_API_KEY\" })\n {\n var val = System.Environment.GetEnvironmentVariable(key);\n if (!string.IsNullOrEmpty(val))\n psi.EnvironmentVariables[key] = val;\n }\n\n _webServerProcess = Process.Start(psi);\n\n if (_webServerProcess != null && !_webServerProcess.HasExited)\n {\n // Drain pipes so the child doesn't deadlock on a full buffer.\n _webServerProcess.BeginOutputReadLine();\n _webServerProcess.BeginErrorReadLine();\n Console.WriteLine($\"Web UI started (PID {_webServerProcess.Id}) → http://127.0.0.1:{WebServerPort}\");\n }\n else\n {\n Console.Error.WriteLine(\"WARN: web server failed to start; see start_web.py output for details.\");\n }\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: could not start web server: {ex.Message}\");\n }\n }\n\n private static void StopWebServer()\n {\n try\n {\n if (_webServerProcess != null && !_webServerProcess.HasExited)\n {\n _webServerProcess.Kill();\n _webServerProcess.WaitForExit(3000);\n }\n _webServerProcess?.Dispose();\n _webServerProcess = null;\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: error stopping web server: {ex.Message}\");\n }\n }\n }\n}\n"}, "c6b2dbc76dbfeced81853a30f2097af5c4b1179f": {"t": 1785756723.8155425, "n": 4747, "h": "622e763418630201", "c": "// AiCadServer - Standalone gRPC server for development\n// Connects to a running SolidWorks instance via out-of-process COM\n// Hosts the same BRepService as the in-process add-in\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Grpc.Core;\nusing SolidWorks.Interop.sldworks;\nusing AiCadPlugin.Server;\nusing Kalpataru.Proto;\nusing AiCad.Proto;\n\nnamespace AiCadServer\n{\n class Program\n {\n static void Main(string[] args)\n {\n int port = 50052;\n if (args.Length > 0 && int.TryParse(args[0], out int customPort))\n port = customPort;\n\n // Route Debug/Trace writes to a known log file so external tools\n // (verification harness, log-tailers) can see what the bridge is\n // doing. Path: %TEMP%\\kalpataru-bridge.log\n try\n {\n string logPath = Path.Combine(Path.GetTempPath(), \"kalpataru-bridge.log\");\n var listener = new TextWriterTraceListener(logPath) { Name = \"kalpataru-file\" };\n Trace.Listeners.Add(listener);\n Debug.Listeners.Add(listener);\n Trace.AutoFlush = true;\n Trace.WriteLine($\"=== AiCadServer started at {DateTime.UtcNow:o} ===\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"WARN: could not attach trace listener: {ex.Message}\");\n }\n\n Console.WriteLine(\"AiCadServer - Standalone gRPC development server\");\n Console.WriteLine(\"================================================\");\n\n // 1. Connect to running SolidWorks via out-of-process COM\n ISldWorks swApp;\n try\n {\n swApp = (ISldWorks)Marshal.GetActiveObject(\"SldWorks.Application\");\n string version = \"unknown\";\n try { version = swApp.RevisionNumber(); } catch { }\n Console.WriteLine($\"Connected to SolidWorks {version}\");\n }\n catch (COMException ex)\n {\n Console.Error.WriteLine($\"ERROR: Cannot connect to SolidWorks: {ex.Message}\");\n Console.Error.WriteLine(\"Make sure SolidWorks is running before starting this server.\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 2. Create services\n var brepService = new BRepService(swApp);\n var v1Service = new GrpcAdapterService(swApp);\n\n // 3. Start gRPC server\n var server = new Grpc.Core.Server\n {\n Services =\n {\n CadAdapter.BindService(brepService),\n Adapter.BindService(v1Service)\n },\n Ports =\n {\n new ServerPort(\"localhost\", port, ServerCredentials.Insecure),\n new ServerPort(\"localhost\", 50051, ServerCredentials.Insecure)\n }\n };\n\n try\n {\n server.Start();\n Console.WriteLine($\"gRPC v2 server listening on localhost:{port}\");\n Console.WriteLine($\"gRPC v1 server listening on localhost:50051\");\n Console.WriteLine();\n Console.WriteLine(\"Press Ctrl+C or Enter to stop...\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"ERROR: Failed to start gRPC server: {ex.Message}\");\n Console.Error.WriteLine(\"Is another instance already running on the same port?\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 4. Wait for shutdown signal\n var shutdownEvent = new ManualResetEvent(false);\n Console.CancelKeyPress += (s, e) =>\n {\n e.Cancel = true;\n shutdownEvent.Set();\n };\n\n // Also allow Enter key to stop (only if stdin is interactive)\n if (System.Environment.UserInteractive && !Console.IsInputRedirected)\n {\n ThreadPool.QueueUserWorkItem(_ =>\n {\n try { Console.ReadLine(); } catch { }\n shutdownEvent.Set();\n });\n }\n\n shutdownEvent.WaitOne();\n\n // 5. Graceful shutdown\n Console.WriteLine(\"Shutting down gRPC server...\");\n server.ShutdownAsync().Wait(TimeSpan.FromSeconds(5));\n Console.WriteLine(\"Server stopped.\");\n }\n }\n}\n"}, "fd215f5c4eaae3737150da3a525f33fadfbf2dce": {"t": 1785756725.31254, "n": 3879, "h": "2ec076e0522a3a19", "c": "// AiCadServer - Standalone gRPC server for development\n// Connects to a running SolidWorks instance via out-of-process COM\n// Hosts the same BRepService as the in-process add-in\n\nusing System;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Grpc.Core;\nusing SolidWorks.Interop.sldworks;\nusing AiCadPlugin.Server;\nusing Kalpataru.Proto;\nusing AiCad.Proto;\n\nnamespace AiCadServer\n{\n class Program\n {\n static void Main(string[] args)\n {\n int port = 50052;\n if (args.Length > 0 && int.TryParse(args[0], out int customPort))\n port = customPort;\n\n Console.WriteLine(\"AiCadServer - Standalone gRPC development server\");\n Console.WriteLine(\"================================================\");\n\n // 1. Connect to running SolidWorks via out-of-process COM\n ISldWorks swApp;\n try\n {\n swApp = (ISldWorks)Marshal.GetActiveObject(\"SldWorks.Application\");\n string version = \"unknown\";\n try { version = swApp.RevisionNumber(); } catch { }\n Console.WriteLine($\"Connected to SolidWorks {version}\");\n }\n catch (COMException ex)\n {\n Console.Error.WriteLine($\"ERROR: Cannot connect to SolidWorks: {ex.Message}\");\n Console.Error.WriteLine(\"Make sure SolidWorks is running before starting this server.\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 2. Create services\n var brepService = new BRepService(swApp);\n var v1Service = new GrpcAdapterService(swApp);\n\n // 3. Start gRPC server\n var server = new Grpc.Core.Server\n {\n Services =\n {\n CadAdapter.BindService(brepService),\n Adapter.BindService(v1Service)\n },\n Ports =\n {\n new ServerPort(\"localhost\", port, ServerCredentials.Insecure),\n new ServerPort(\"localhost\", 50051, ServerCredentials.Insecure)\n }\n };\n\n try\n {\n server.Start();\n Console.WriteLine($\"gRPC v2 server listening on localhost:{port}\");\n Console.WriteLine($\"gRPC v1 server listening on localhost:50051\");\n Console.WriteLine();\n Console.WriteLine(\"Press Ctrl+C or Enter to stop...\");\n }\n catch (Exception ex)\n {\n Console.Error.WriteLine($\"ERROR: Failed to start gRPC server: {ex.Message}\");\n Console.Error.WriteLine(\"Is another instance already running on the same port?\");\n Console.Error.WriteLine(\"Press any key to exit...\");\n Console.ReadKey();\n return;\n }\n\n // 4. Wait for shutdown signal\n var shutdownEvent = new ManualResetEvent(false);\n Console.CancelKeyPress += (s, e) =>\n {\n e.Cancel = true;\n shutdownEvent.Set();\n };\n\n // Also allow Enter key to stop (only if stdin is interactive)\n if (System.Environment.UserInteractive && !Console.IsInputRedirected)\n {\n ThreadPool.QueueUserWorkItem(_ =>\n {\n try { Console.ReadLine(); } catch { }\n shutdownEvent.Set();\n });\n }\n\n shutdownEvent.WaitOne();\n\n // 5. Graceful shutdown\n Console.WriteLine(\"Shutting down gRPC server...\");\n server.ShutdownAsync().Wait(TimeSpan.FromSeconds(5));\n Console.WriteLine(\"Server stopped.\");\n }\n }\n}\n"}}}} |