Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/chivukulavirinchi_kalpataru.json
T

1 line
38 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"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### कल्पतरु &nbsp;·&nbsp; *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![tests](https://img.shields.io/badge/tests-80%2F82_passing-d65a31?style=flat-square)\n![license](https://img.shields.io/badge/license-MIT-d65a31?style=flat-square)\n![python](https://img.shields.io/badge/python-3.10+-d65a31?style=flat-square)\n![solidworks](https://img.shields.io/badge/solidworks-2023%20|%202024%20|%202025-d65a31?style=flat-square)\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"}}}}