@inneranimalmedia/agentsam-sdk 2.6.2 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/AGENTSAM.md +45 -1
  2. package/README.md +80 -0
  3. package/apps/README.md +34 -0
  4. package/apps/cad-creator/README.md +728 -0
  5. package/apps/cad-creator/shared/cad/package.json +19 -0
  6. package/apps/cad-creator/shared/cad/src/domain/operations.ts +7 -0
  7. package/apps/cad-creator/shared/cad/src/domain/project-contract.d.ts +3 -0
  8. package/apps/cad-creator/shared/cad/src/domain/project-contract.js +75 -0
  9. package/apps/cad-creator/shared/cad/src/domain/project-tools.d.ts +1 -0
  10. package/apps/cad-creator/shared/cad/src/domain/project-tools.js +24 -0
  11. package/apps/cad-creator/shared/cad/src/domain/validation.ts +191 -0
  12. package/apps/cad-creator/shared/cad/src/index.ts +4 -0
  13. package/apps/cad-creator/shared/cad/src/robotics/types.ts +232 -0
  14. package/apps/cad-creator/shared/cad/src/types.ts +507 -0
  15. package/apps/cad-creator/shared/cad/tsconfig.json +14 -0
  16. package/docs/AUTORAG.md +58 -0
  17. package/docs/BRAND_INTELLIGENCE.md +30 -0
  18. package/docs/CLI_SHELL.md +4 -2
  19. package/docs/CMS_CONTEXTUAL_COMPOSER.md +38 -0
  20. package/docs/MERKLE.md +4 -0
  21. package/docs/PROJECT_CONFIG.md +3 -0
  22. package/docs/REPOSITORY_KNOWLEDGE.md +2 -0
  23. package/docs/THEME_REFINERY.md +120 -0
  24. package/docs/architecture/PROVIDER-TOOLS-AND-COMPACTION.md +60 -0
  25. package/docs/architecture/STORAGE.md +87 -0
  26. package/docs/cad-project-tools.md +69 -0
  27. package/docs/cms-scaffolding-pattern-reference.md +73 -0
  28. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS-codex-tui-20260919.md +113 -0
  29. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS.md +85 -0
  30. package/docs/plans/AGENTSAM-MCP-CLIENT-2026-09-19.md +181 -0
  31. package/docs/plans/ASBD-PORTABLE-WORKGRAPH-UI-DEFERRED.md +34 -0
  32. package/docs/plans/ASTRA-BRIEF-2026-09-18.md +258 -0
  33. package/docs/plans/MULTI-AGENT-SPRINTS-2026-09-19.md +373 -0
  34. package/docs/plans/NEXT-AGENT-HANDOFF.md +167 -0
  35. package/docs/plans/RECONCILIATION-2026-09-19.md +178 -0
  36. package/docs/plugins.md +43 -0
  37. package/docs/tools/README.md +57 -0
  38. package/docs/tools/agent-and-orchestration.md +49 -0
  39. package/docs/tools/browser-and-research.md +46 -0
  40. package/docs/tools/cloudflare-platform.md +32 -0
  41. package/docs/tools/cms.md +25 -0
  42. package/docs/tools/communications.md +41 -0
  43. package/docs/tools/database-and-storage.md +62 -0
  44. package/docs/tools/design-and-media.md +64 -0
  45. package/docs/tools/filesystem-and-code.md +55 -0
  46. package/docs/tools/github.md +56 -0
  47. package/docs/tools/integrations-and-misc.md +16 -0
  48. package/docs/tools/memory.md +21 -0
  49. package/docs/tools/terminal-and-execution.md +29 -0
  50. package/docs/tools/tickets-and-platform.md +30 -0
  51. package/migrations/d1/0001_agentsam_plugin_runtime.sql +177 -0
  52. package/migrations/d1/0002_agentsam_ticket_ownership.sql +8 -0
  53. package/migrations/d1/0002_dynamic_model_policies.sql +23 -0
  54. package/migrations/d1/0003_expand_tool_handler_families.sql +18 -0
  55. package/migrations/d1/0004_project_aware_retrieval_lanes.sql +24 -0
  56. package/migrations/d1/0005_portable_knowledge_identity.sql +104 -0
  57. package/migrations/d1/0006_repair_ops_trail_account_scope.sql +14 -0
  58. package/migrations/d1/0007_repair_ops_trail_agent_run_account.sql +13 -0
  59. package/migrations/d1/0008_backfill_knowledge_run_legacy_tenant.sql +14 -0
  60. package/migrations/d1/0009_normalize_cloudflare_plugin_installations.sql +137 -0
  61. package/migrations/runtime/0001_cli_runtime.sql +67 -0
  62. package/migrations/runtime/0002_project_sessions.sql +15 -0
  63. package/migrations/runtime/0003_provider_continuations.sql +8 -0
  64. package/package.json +92 -12
  65. package/packages/agentsam-brand/README.md +21 -0
  66. package/packages/agentsam-brand/package.json +22 -0
  67. package/packages/agentsam-brand/src/contract.js +59 -0
  68. package/packages/agentsam-brand/src/extract.js +126 -0
  69. package/packages/agentsam-brand/src/goap-actions.js +73 -0
  70. package/packages/agentsam-brand/src/index.js +48 -0
  71. package/packages/agentsam-brand/src/plan.js +115 -0
  72. package/packages/agentsam-brand/src/resolve.js +139 -0
  73. package/packages/agentsam-brand/src/scan.js +250 -0
  74. package/packages/agentsam-brand/test/brand-scan.test.mjs +106 -0
  75. package/packages/agentsam-contracts/README.md +23 -0
  76. package/packages/agentsam-contracts/package.json +32 -0
  77. package/packages/agentsam-contracts/src/adapter.ts +25 -0
  78. package/packages/agentsam-contracts/src/agent.ts +35 -0
  79. package/packages/agentsam-contracts/src/artifacts.ts +33 -0
  80. package/packages/agentsam-contracts/src/authority.ts +29 -0
  81. package/packages/agentsam-contracts/src/context.ts +16 -0
  82. package/packages/agentsam-contracts/src/errors.ts +46 -0
  83. package/packages/agentsam-contracts/src/events.ts +47 -0
  84. package/packages/agentsam-contracts/src/execution.ts +50 -0
  85. package/packages/agentsam-contracts/src/hooks.ts +40 -0
  86. package/packages/agentsam-contracts/src/identity.ts +16 -0
  87. package/packages/agentsam-contracts/src/index.ts +14 -0
  88. package/packages/agentsam-contracts/src/models.ts +9 -0
  89. package/packages/agentsam-contracts/src/providers.ts +33 -0
  90. package/packages/agentsam-contracts/src/repository.ts +64 -0
  91. package/packages/agentsam-contracts/src/tools.ts +98 -0
  92. package/packages/agentsam-contracts/test/contracts.test.mjs +16 -0
  93. package/packages/agentsam-contracts/test/errors-catalog.test.mjs +25 -0
  94. package/packages/agentsam-contracts/test/repository.test.mjs +18 -0
  95. package/packages/agentsam-contracts/tsconfig.json +12 -0
  96. package/packages/agentsam-errors/package.json +17 -0
  97. package/packages/agentsam-errors/src/adapters/anthropic.js +15 -0
  98. package/packages/agentsam-errors/src/adapters/cad.js +109 -0
  99. package/packages/agentsam-errors/src/adapters/cloudflare.js +21 -0
  100. package/packages/agentsam-errors/src/adapters/cursor.js +4 -0
  101. package/packages/agentsam-errors/src/adapters/device.js +38 -0
  102. package/packages/agentsam-errors/src/adapters/docker.js +28 -0
  103. package/packages/agentsam-errors/src/adapters/gcp.js +14 -0
  104. package/packages/agentsam-errors/src/adapters/github.js +22 -0
  105. package/packages/agentsam-errors/src/adapters/google.js +17 -0
  106. package/packages/agentsam-errors/src/adapters/index.js +55 -0
  107. package/packages/agentsam-errors/src/adapters/internal.js +40 -0
  108. package/packages/agentsam-errors/src/adapters/oauth.js +24 -0
  109. package/packages/agentsam-errors/src/adapters/openai.js +17 -0
  110. package/packages/agentsam-errors/src/adapters/process.js +35 -0
  111. package/packages/agentsam-errors/src/adapters/provider.js +114 -0
  112. package/packages/agentsam-errors/src/envelope.js +186 -0
  113. package/packages/agentsam-errors/src/error.js +24 -0
  114. package/packages/agentsam-errors/src/fingerprint.js +35 -0
  115. package/packages/agentsam-errors/src/index.js +10 -0
  116. package/packages/agentsam-errors/src/normalize.js +30 -0
  117. package/packages/agentsam-errors/src/redaction.js +52 -0
  118. package/packages/agentsam-errors/src/render.js +16 -0
  119. package/packages/agentsam-errors/src/retry.js +10 -0
  120. package/packages/agentsam-errors/src/transport.js +52 -0
  121. package/packages/agentsam-errors/src/vocabulary.js +320 -0
  122. package/packages/agentsam-errors/test/envelope.test.mjs +70 -0
  123. package/packages/agentsam-errors/test/providers.test.mjs +99 -0
  124. package/packages/agentsam-errors/test/redaction.test.mjs +21 -0
  125. package/packages/agentsam-errors/test/runtime-cad-device.test.mjs +76 -0
  126. package/packages/agentsam-errors/test/transport.test.mjs +46 -0
  127. package/packages/agentsam-knowledge/package.json +15 -0
  128. package/packages/agentsam-knowledge/src/autorag/index.js +64 -0
  129. package/packages/agentsam-knowledge/src/backends/index.js +26 -0
  130. package/packages/agentsam-knowledge/src/index.js +3 -0
  131. package/packages/agentsam-knowledge/src/providers/index.js +95 -0
  132. package/packages/agentsam-knowledge/test/autorag.test.mjs +42 -0
  133. package/packages/agentsam-nav/README.md +40 -0
  134. package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
  135. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
  136. package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
  137. package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
  138. package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
  139. package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
  140. package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
  141. package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
  142. package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
  143. package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
  144. package/packages/agentsam-nav/dist/browser.d.ts +11 -0
  145. package/packages/agentsam-nav/dist/browser.global.js +132 -0
  146. package/packages/agentsam-nav/dist/browser.js +613 -0
  147. package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
  148. package/packages/agentsam-nav/dist/index.d.ts +11 -0
  149. package/packages/agentsam-nav/dist/index.js +614 -0
  150. package/packages/agentsam-nav/dist/legacy.css +83 -0
  151. package/packages/agentsam-nav/dist/shell.css +113 -0
  152. package/packages/agentsam-nav/dist/theme.css +113 -0
  153. package/packages/agentsam-nav/dist/utils.d.ts +1 -0
  154. package/packages/agentsam-nav/package.json +43 -0
  155. package/packages/agentsam-queue-control/README.md +107 -0
  156. package/packages/agentsam-queue-control/package.json +31 -0
  157. package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +56 -0
  158. package/packages/agentsam-queue-control/src/adapters/cloudflare.js +152 -0
  159. package/packages/agentsam-queue-control/src/adapters/memory.js +38 -0
  160. package/packages/agentsam-queue-control/src/contracts.js +130 -0
  161. package/packages/agentsam-queue-control/src/control-plane.js +141 -0
  162. package/packages/agentsam-queue-control/src/dispatch.js +43 -0
  163. package/packages/agentsam-queue-control/src/index.js +7 -0
  164. package/packages/agentsam-queue-control/src/policy.js +88 -0
  165. package/packages/agentsam-queue-control/src/topology.js +66 -0
  166. package/packages/agentsam-queue-control/test/queue-control.test.mjs +100 -0
  167. package/packages/agentsam-repository/src/goap.js +718 -0
  168. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
  169. package/packages/agentsam-repository/src/index.js +5 -0
  170. package/packages/agentsam-repository/src/merkle/cloudflare-persistence.js +2 -2
  171. package/packages/agentsam-repository/src/tickets.js +184 -0
  172. package/packages/agentsam-repository/src/work-tracking.js +199 -0
  173. package/packages/agentsam-repository/src/workspace-state.js +176 -0
  174. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +152 -0
  175. package/packages/connectors/cloudflare/src/index.js +297 -15
  176. package/packages/connectors/cloudflare/src/owner.js +43 -40
  177. package/packages/connectors/cloudflare/src/routes.js +167 -26
  178. package/packages/connectors/cloudflare/tests/connector.test.mjs +112 -2
  179. package/packages/heuristic-theme/README.md +11 -0
  180. package/packages/heuristic-theme/package.json +25 -0
  181. package/packages/heuristic-theme/src/index.js +2 -0
  182. package/packages/heuristic-theme/src/registry.js +36 -0
  183. package/packages/heuristic-theme/src/theme.js +41 -0
  184. package/packages/heuristic-theme/test/theme.test.mjs +26 -0
  185. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +64 -0
  186. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +96 -0
  187. package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +47 -0
  188. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +74 -0
  189. package/packages/identity/.agentsam/features/oauth-login-portal/ui.json +35 -0
  190. package/packages/identity/package.json +3 -2
  191. package/packages/identity/src/adapters/cloudflare-d1/ids.js +4 -0
  192. package/packages/identity/src/adapters/cloudflare-d1/index.js +39 -1
  193. package/packages/identity/src/contracts/provider.js +3 -2
  194. package/packages/identity/src/frontend/auth-portal/pages/login.html +7 -0
  195. package/packages/identity/src/index.js +2 -0
  196. package/packages/identity/src/oauth/credentials.js +13 -0
  197. package/packages/identity/src/oauth/iam-platform.js +5 -5
  198. package/packages/identity/src/providers/cloudflare/index.js +19 -0
  199. package/packages/identity/src/providers/cloudflare/mapper.js +21 -0
  200. package/packages/identity/src/providers/cloudflare/oauth.js +48 -0
  201. package/packages/identity/src/providers/cloudflare/profile.js +28 -0
  202. package/packages/identity/src/providers/index.js +18 -2
  203. package/packages/identity/src/server/identity-service.js +13 -3
  204. package/packages/identity/src/server/worker-router.js +63 -8
  205. package/packages/identity/tests/identity-service.test.mjs +15 -0
  206. package/packages/providers/completeful/README.md +38 -0
  207. package/packages/providers/completeful/package.json +29 -0
  208. package/packages/providers/completeful/src/client.js +162 -0
  209. package/packages/providers/completeful/src/events.js +49 -0
  210. package/packages/providers/completeful/src/index.js +22 -0
  211. package/packages/providers/completeful/src/tools.js +579 -0
  212. package/packages/providers/completeful/src/webhooks.js +55 -0
  213. package/packages/providers/completeful/test/provider.test.mjs +196 -0
  214. package/packages/theme-church-site/README.md +10 -0
  215. package/packages/theme-church-site/package.json +30 -0
  216. package/packages/theme-church-site/src/harvest.json +14 -0
  217. package/packages/theme-church-site/src/index.js +84 -0
  218. package/packages/theme-church-site/test/theme.test.mjs +19 -0
  219. package/packages/theme-companions-site/README.md +10 -0
  220. package/packages/theme-companions-site/package.json +30 -0
  221. package/packages/theme-companions-site/src/harvest.json +12 -0
  222. package/packages/theme-companions-site/src/index.js +82 -0
  223. package/packages/theme-companions-site/test/theme.test.mjs +19 -0
  224. package/packages/theme-floors-site/README.md +10 -0
  225. package/packages/theme-floors-site/package.json +30 -0
  226. package/packages/theme-floors-site/src/harvest.json +14 -0
  227. package/packages/theme-floors-site/src/index.js +82 -0
  228. package/packages/theme-floors-site/test/theme.test.mjs +19 -0
  229. package/packages/theme-fuelnfree-site/README.md +10 -0
  230. package/packages/theme-fuelnfree-site/package.json +30 -0
  231. package/packages/theme-fuelnfree-site/src/harvest.json +13 -0
  232. package/packages/theme-fuelnfree-site/src/index.js +81 -0
  233. package/packages/theme-fuelnfree-site/test/theme.test.mjs +19 -0
  234. package/packages/theme-handyman-site/README.md +10 -0
  235. package/packages/theme-handyman-site/package.json +30 -0
  236. package/packages/theme-handyman-site/src/harvest.json +10 -0
  237. package/packages/theme-handyman-site/src/index.js +83 -0
  238. package/packages/theme-handyman-site/test/theme.test.mjs +19 -0
  239. package/packages/theme-insurance-site/README.md +10 -0
  240. package/packages/theme-insurance-site/package.json +30 -0
  241. package/packages/theme-insurance-site/src/harvest.json +10 -0
  242. package/packages/theme-insurance-site/src/index.js +83 -0
  243. package/packages/theme-insurance-site/test/theme.test.mjs +19 -0
  244. package/packages/theme-shinshu-site/README.md +10 -0
  245. package/packages/theme-shinshu-site/package.json +30 -0
  246. package/packages/theme-shinshu-site/src/harvest.json +12 -0
  247. package/packages/theme-shinshu-site/src/index.js +83 -0
  248. package/packages/theme-shinshu-site/test/theme.test.mjs +19 -0
  249. package/packages/work-graph/README.md +49 -0
  250. package/packages/work-graph/package.json +19 -0
  251. package/packages/work-graph/prototype/README.md +32 -0
  252. package/packages/work-graph/prototype/data/demo-workgraph.js +47 -0
  253. package/packages/work-graph/prototype/renderers/timeline-model.js +21 -0
  254. package/packages/work-graph/prototype/renderers/workgraph-runtime.js +38 -0
  255. package/packages/work-graph/prototype-gnantt.html +655 -0
  256. package/packages/work-graph/src/adapters/business/projects.js +23 -0
  257. package/packages/work-graph/src/adapters/engineering/git.js +22 -0
  258. package/packages/work-graph/src/adapters/engineering/mcp.js +22 -0
  259. package/packages/work-graph/src/index.js +76 -0
  260. package/packages/work-graph/src/renderers/gantt-model.js +11 -0
  261. package/packages/work-graph/src/renderers/timeline.js +16 -0
  262. package/packages/work-graph/test/work-graph.test.mjs +37 -0
  263. package/protocol/README.md +12 -0
  264. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +30 -6
  265. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +18 -4
  266. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +59 -10
  267. package/protocol/capabilities/knowledge-search-input.schema.json +35 -0
  268. package/protocol/capabilities/manifest.json +163 -1
  269. package/protocol/capabilities/repository-audit-input.schema.json +39 -6
  270. package/protocol/capabilities/repository-snapshot-input.schema.json +15 -3
  271. package/protocol/capabilities/terminal-exec-input.schema.json +55 -0
  272. package/protocol/commerce/catalog.v1.json +50 -0
  273. package/protocol/errors/error-catalog.json +164 -0
  274. package/protocol/errors/error-envelope.schema.json +544 -0
  275. package/protocol/features/README.md +57 -0
  276. package/protocol/features/agentsam.feature.v1.schema.json +290 -0
  277. package/protocol/features/catalog.json +65 -0
  278. package/protocol/features/examples/provider-resend.feature.json +17 -0
  279. package/protocol/go/agentsam-go-runtime.v1.json +17 -0
  280. package/protocol/knowledge/LIFECYCLE.md +140 -0
  281. package/protocol/knowledge/README.md +4 -0
  282. package/protocol/presets/catalog.json +128 -19
  283. package/protocol/rpc/v1/errors.proto +24 -0
  284. package/protocol/theme-refinery/d1-reconciliation.md +48 -0
  285. package/protocol/theme-refinery/product-metadata.schema.json +27 -0
  286. package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +318 -0
  287. package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +472 -0
  288. package/protocol/theme-refinery/reconciliation-receipt.schema.json +83 -0
  289. package/services/cad/server.py +249 -2
  290. package/src/agent/capability-adapter.js +15 -5
  291. package/src/agent/index.js +2 -1
  292. package/src/agent/model-turn.js +51 -0
  293. package/src/agent/responses-runner.js +304 -38
  294. package/src/capabilities/index.js +1 -0
  295. package/src/capabilities/terminal-exec.js +78 -0
  296. package/src/cli.js +117 -9
  297. package/src/cloudflare/index.js +6 -0
  298. package/src/cloudflare/runtime-status.js +393 -0
  299. package/src/cloudflare/wrangler.js +17 -1
  300. package/src/commands/app.js +106 -0
  301. package/src/commands/autorag.js +77 -0
  302. package/src/commands/brand.js +293 -0
  303. package/src/commands/cad.js +1 -0
  304. package/src/commands/connections.js +126 -29
  305. package/src/commands/context-economics.js +8 -0
  306. package/src/commands/db.js +7 -0
  307. package/src/commands/env.js +5 -4
  308. package/src/commands/eval.js +209 -14
  309. package/src/commands/go.js +365 -0
  310. package/src/commands/goap.js +176 -0
  311. package/src/commands/identity-init.js +27 -3
  312. package/src/commands/identity-protocol.js +195 -0
  313. package/src/commands/knowledge.js +64 -5
  314. package/src/commands/mcp.js +341 -0
  315. package/src/commands/merkle-persist.js +3 -1
  316. package/src/commands/merkle.js +18 -3
  317. package/src/commands/models.js +109 -23
  318. package/src/commands/plugins.js +175 -0
  319. package/src/commands/preferences.js +28 -8
  320. package/src/commands/product.js +53 -10
  321. package/src/commands/providers.js +118 -37
  322. package/src/commands/resume.js +9 -5
  323. package/src/commands/runtime-status.js +154 -0
  324. package/src/commands/shell.js +466 -55
  325. package/src/commands/status.js +82 -7
  326. package/src/commands/tunnel.js +228 -195
  327. package/src/commands/whoami.js +78 -6
  328. package/src/context/budget.js +6 -0
  329. package/src/context/index.js +2 -1
  330. package/src/context/project-card.js +75 -0
  331. package/src/errors/contract.js +34 -211
  332. package/src/errors/diagnostic.js +95 -51
  333. package/src/errors/index.js +6 -13
  334. package/src/eval/index.js +6 -0
  335. package/src/eval/live.js +439 -0
  336. package/src/features/resolve.js +242 -0
  337. package/src/go/build.js +108 -0
  338. package/src/go/cloudflare.js +207 -0
  339. package/src/go/contract.js +139 -0
  340. package/src/go/discover.js +96 -0
  341. package/src/go/index.js +13 -0
  342. package/src/go/receipts.js +77 -0
  343. package/src/go/registry.js +223 -0
  344. package/src/go/verify.js +31 -0
  345. package/src/index.js +100 -1
  346. package/src/knowledge/config.js +13 -5
  347. package/src/knowledge/engine.js +23 -8
  348. package/src/knowledge/index.js +1 -0
  349. package/src/knowledge/lane-registry.js +46 -0
  350. package/src/knowledge/providers/gemini.js +48 -8
  351. package/src/knowledge/service/client.js +13 -1
  352. package/src/knowledge/service/grpc-codec.js +156 -53
  353. package/src/knowledge/service/job-engine.js +133 -11
  354. package/src/knowledge/service/job-worker.js +34 -5
  355. package/src/knowledge/service/server.js +10 -1
  356. package/src/knowledge/store-discovery.js +77 -0
  357. package/src/lib/account-session.js +8 -2
  358. package/src/lib/agent-instructions.js +3 -0
  359. package/src/lib/auth.js +7 -3
  360. package/src/lib/cad/blender.js +42 -3
  361. package/src/lib/cad/d1-project-store.js +43 -0
  362. package/src/lib/cad/discovery.js +616 -0
  363. package/src/lib/cad/docker-executor.js +352 -0
  364. package/src/lib/cad/freecad.js +253 -0
  365. package/src/lib/cad/index.js +40 -0
  366. package/src/lib/cad/openscad.js +198 -0
  367. package/src/lib/cad/project-cli.d.ts +2 -0
  368. package/src/lib/cad/project-cli.js +21 -0
  369. package/src/lib/cad/project-contract.d.ts +3 -0
  370. package/src/lib/cad/project-contract.js +1 -0
  371. package/src/lib/cad/project-recipe.js +30 -0
  372. package/src/lib/cad/project-runtime.d.ts +2 -0
  373. package/src/lib/cad/project-runtime.js +53 -0
  374. package/src/lib/cad/project-store.js +25 -0
  375. package/src/lib/cad/project-workflows.js +31 -0
  376. package/src/lib/cad-docker.js +21 -2
  377. package/src/lib/cli-preferences.js +17 -2
  378. package/src/lib/core-client.js +37 -18
  379. package/src/lib/deploy/local-studio.js +6 -5
  380. package/src/lib/dockerize.js +3 -0
  381. package/src/lib/execution-approvals.js +1 -0
  382. package/src/lib/identity-scaffold.js +4 -1
  383. package/src/lib/knowledge-docker.js +9 -1
  384. package/src/lib/local-sessions.js +124 -34
  385. package/src/lib/local-status.js +35 -14
  386. package/src/lib/project-config.js +9 -0
  387. package/src/lib/provider-credentials.js +73 -11
  388. package/src/lib/scaffold/resolve-cloudflare-account.js +37 -0
  389. package/src/lib/scaffold/wizards/cms.js +2 -8
  390. package/src/lib/scaffold/wizards/worker-api.js +2 -8
  391. package/src/lib/slash-commands.js +9 -2
  392. package/src/local/migrations.js +109 -1
  393. package/src/local/runtime-store.js +31 -5
  394. package/src/local/sqlite.js +36 -1
  395. package/src/mcp/authority.js +348 -0
  396. package/src/mcp/client-adapters.js +431 -0
  397. package/src/mcp/client.js +172 -0
  398. package/src/mcp/index.js +47 -0
  399. package/src/mcp/telemetry.js +103 -0
  400. package/src/models/catalog.js +329 -51
  401. package/src/models/discovery.js +24 -4
  402. package/src/models/index.js +2 -0
  403. package/src/models/inventory-core.js +182 -0
  404. package/src/models/policy.js +20 -0
  405. package/src/plugins/cloudflare.js +103 -0
  406. package/src/plugins/completeful.js +69 -0
  407. package/src/plugins/contracts.js +86 -0
  408. package/src/plugins/index.js +11 -0
  409. package/src/plugins/registry.js +207 -0
  410. package/src/plugins/resource-scope.js +45 -0
  411. package/src/plugins/runtime.js +118 -0
  412. package/src/plugins/vectorize.js +31 -0
  413. package/src/progression/engine.js +171 -0
  414. package/src/progression/index.js +1 -0
  415. package/src/providers/cloudflare-chat.js +1 -1
  416. package/src/providers/gemini-generate-content.js +2 -1
  417. package/src/providers/openai-responses.js +45 -16
  418. package/src/providers/tool-schema.js +85 -0
  419. package/src/repository/index.js +29 -0
  420. package/src/rpc/generated/errors_pb.js +961 -1
  421. package/src/security/local-vault.js +415 -0
  422. package/src/security/process.js +20 -4
  423. package/src/status/actions.js +192 -0
  424. package/src/tools/hydrate.js +5 -1
  425. package/src/tools/index.js +7 -0
  426. package/src/tools/redact.js +64 -0
  427. package/src/tools/registry.js +83 -0
  428. package/src/tools/runtime.js +257 -0
  429. package/src/tools/search.js +21 -8
  430. package/src/ui/ansi.js +117 -3
  431. package/src/ui/cli/activity.js +76 -7
  432. package/src/ui/cli/footer.js +110 -9
  433. package/src/ui/cli/help.js +11 -4
  434. package/src/ui/wireframes.js +166 -0
  435. package/test/account-session.test.mjs +28 -0
  436. package/test/apps-scaffold-contract.test.mjs +16 -1
  437. package/test/capabilities.test.mjs +1 -1
  438. package/test/cli/eval-live.test.mjs +219 -0
  439. package/test/cli/go.test.mjs +71 -0
  440. package/test/cli/local-vault-and-provider-setup.test.mjs +268 -0
  441. package/test/cli/mcp-client.test.mjs +245 -0
  442. package/test/cli/runtime-ui.test.mjs +69 -2
  443. package/test/cli/status-actions.test.mjs +88 -0
  444. package/test/cli/wireframes-go-registry.test.mjs +54 -0
  445. package/test/cloudflare-connector.test.mjs +3 -6
  446. package/test/cloudflare-runtime.test.mjs +8 -1
  447. package/test/context.test.mjs +15 -0
  448. package/test/error-diagnostics.test.mjs +9 -0
  449. package/test/fixtures/cad/courtyard-house.json +208 -0
  450. package/test/go/cloudflare-probe.test.mjs +53 -0
  451. package/test/integration/app-command.test.mjs +55 -0
  452. package/test/integration/cad-discovery.test.mjs +158 -0
  453. package/test/integration/cad-docker-executor.test.mjs +249 -0
  454. package/test/integration/cad-execution.test.mjs +184 -0
  455. package/test/integration/cad-project-app.test.mjs +31 -0
  456. package/test/integration/cad-project-d1.test.mjs +25 -0
  457. package/test/integration/cad-project-native.test.mjs +24 -0
  458. package/test/integration/cad-project-workflow.test.mjs +9 -0
  459. package/test/integration/cad-project.test.mjs +82 -0
  460. package/test/integration/cli-help.test.mjs +4 -0
  461. package/test/integration/cms-site-tenancy-contract.test.mjs +256 -0
  462. package/test/integration/core-client.integration.test.mjs +55 -0
  463. package/test/integration/error-runtime-rpc.test.mjs +71 -0
  464. package/test/integration/features-resolve.test.mjs +128 -0
  465. package/test/integration/install-script.test.mjs +107 -0
  466. package/test/integration/inventory-core.test.mjs +53 -0
  467. package/test/integration/plugins-cloudflare.integration.test.mjs +28 -0
  468. package/test/integration/provider-env-cli.test.mjs +1 -1
  469. package/test/integration/runtime-migrations.test.mjs +6 -1
  470. package/test/integration/runtime-status.integration.test.mjs +211 -0
  471. package/test/integration/scaffold-cloudflare.test.mjs +46 -0
  472. package/test/integration/storage-architecture.test.mjs +154 -0
  473. package/test/integration/store-discovery.test.mjs +44 -0
  474. package/test/integration/terminal-exec.integration.test.mjs +86 -0
  475. package/test/integration/tool-runtime.test.mjs +124 -0
  476. package/test/integration/tool-schema.test.mjs +59 -0
  477. package/test/knowledge-service.test.mjs +4 -0
  478. package/test/local-sessions.test.mjs +11 -2
  479. package/test/local-studio-deploy.test.mjs +1 -1
  480. package/test/model-catalog.test.mjs +28 -1
  481. package/test/models.test.mjs +159 -5
  482. package/test/openai-responses.test.mjs +60 -0
  483. package/test/provider-credentials.test.mjs +15 -5
  484. package/test/responses-runner.test.mjs +321 -6
  485. package/test/shell.test.mjs +149 -4
  486. package/test/smoke.mjs +2 -2
  487. package/test/tools-search.test.mjs +33 -0
  488. package/test/unit/website-assets-bindings.test.mjs +60 -0
  489. package/test/whoami-resume.test.mjs +82 -3
  490. package/examples/gorilla-shell/README.md +0 -15
  491. package/src/lib/gorilla-template.js +0 -54
  492. package/templates/gorilla-shell/App.tsx +0 -639
  493. package/templates/gorilla-shell/README.md +0 -34
  494. package/templates/gorilla-shell/index.html +0 -15
  495. package/templates/gorilla-shell/main.jsx +0 -9
  496. package/templates/gorilla-shell/package.json +0 -19
  497. package/templates/gorilla-shell/vite.config.js +0 -21
@@ -0,0 +1,198 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { runProcess } from '../../security/process.js';
5
+ import { discoverOpenScad, openScadStatus } from './discovery.js';
6
+ import { probeDockerServiceHealth, executeOpenScadDocker } from './docker-executor.js';
7
+
8
+ export const FORBIDDEN_OPENSCAD_PATTERNS = [
9
+ /\bimport\s*\(/i,
10
+ /\binclude\s*<(?!\s*MCAD)/i,
11
+ /\buse\s*<(?!\s*MCAD)/i,
12
+ /\bexec\s*\(/i,
13
+ /\bsystem\s*\(/i,
14
+ /\bchild_process\b/i,
15
+ /\bread_file\b/i,
16
+ /\bwrite_file\b/i,
17
+ /\bprocess\.env\b/i,
18
+ /\.\.\//,
19
+ ];
20
+
21
+ export function validateOpenScadSource(source) {
22
+ if (typeof source !== 'string' || !source.trim()) {
23
+ throw new Error('OpenSCAD source code is required');
24
+ }
25
+
26
+ for (const pat of FORBIDDEN_OPENSCAD_PATTERNS) {
27
+ if (pat.test(source)) {
28
+ throw new Error(`Security Violation: forbidden pattern detected (${pat.toString()})`);
29
+ }
30
+ }
31
+ }
32
+
33
+ function resolveMimeType(format) {
34
+ switch (format.toLowerCase()) {
35
+ case 'stl':
36
+ return 'model/stl';
37
+ case 'dxf':
38
+ return 'image/vnd.dxf';
39
+ case 'svg':
40
+ return 'image/svg+xml';
41
+ case '3mf':
42
+ return 'model/3mf';
43
+ case 'obj':
44
+ return 'model/obj';
45
+ default:
46
+ return 'text/plain';
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Compiles an OpenSCAD script using the native OpenSCAD binary when available,
52
+ * with deterministic sandboxing and parameter injection.
53
+ */
54
+ export async function openScadCompile({
55
+ source,
56
+ outputFormat = 'stl',
57
+ parameters = {},
58
+ filename,
59
+ openscadBin,
60
+ timeoutMs = 25000,
61
+ cwd = process.cwd(),
62
+ runProcessImpl = runProcess,
63
+ } = {}) {
64
+ validateOpenScadSource(source);
65
+
66
+ const format = outputFormat.toLowerCase().replace(/^\./, '');
67
+ const outFilename = filename || `model.${format}`;
68
+ const mimeType = resolveMimeType(format);
69
+ const started = Date.now();
70
+
71
+ const discovery = discoverOpenScad({ openscadBin });
72
+
73
+ if (discovery.binary) {
74
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-openscad-'));
75
+ const inputPath = path.join(tmpDir, 'input.scad');
76
+ const outputPath = path.join(tmpDir, outFilename);
77
+
78
+ fs.writeFileSync(inputPath, source, 'utf8');
79
+
80
+ const paramArgs = [];
81
+ for (const [key, val] of Object.entries(parameters)) {
82
+ if (typeof val === 'string') {
83
+ paramArgs.push('-D', `${key}="${val.replace(/"/g, '\\"')}"`);
84
+ } else if (typeof val === 'number' || typeof val === 'boolean') {
85
+ paramArgs.push('-D', `${key}=${val}`);
86
+ }
87
+ }
88
+
89
+ const exportArgs = [];
90
+ if (format === 'stl') {
91
+ exportArgs.push('--export-format', 'asciistl');
92
+ }
93
+
94
+ const args = ['-o', outputPath, ...exportArgs, ...paramArgs, inputPath];
95
+
96
+ try {
97
+ const proc = await runProcessImpl(discovery.binary, args, {
98
+ cwd: tmpDir,
99
+ timeoutMs,
100
+ maxBytes: 16 * 1024 * 1024,
101
+ });
102
+
103
+ if (!fs.existsSync(outputPath)) {
104
+ throw new Error(
105
+ proc.stderr || proc.stdout || `OpenSCAD failed to emit ${outFilename} with exit code ${proc.code}`
106
+ );
107
+ }
108
+
109
+ const buffer = fs.readFileSync(outputPath);
110
+ const isText = !buffer.includes(0);
111
+ const artifactContent = isText ? buffer.toString('utf8') : buffer.toString('base64');
112
+ const durationMs = Date.now() - started;
113
+
114
+ return {
115
+ success: true,
116
+ engine: 'openscad-native',
117
+ binary: discovery.binary,
118
+ outputFormat: format,
119
+ filename: outFilename,
120
+ mimeType,
121
+ sizeBytes: buffer.length,
122
+ durationMs,
123
+ artifactContent,
124
+ isBase64: !isText,
125
+ logs: [
126
+ `[KERNEL] OpenSCAD Native Compiler (${discovery.source})`,
127
+ `[PARAMETERS] Injected ${Object.keys(parameters).length} dynamic variables`,
128
+ `[OUTPUT] Generated ${outFilename} (${buffer.length} bytes) in ${durationMs}ms`,
129
+ ...(proc.stderr ? [proc.stderr.trim()] : []),
130
+ ],
131
+ };
132
+ } finally {
133
+ fs.rmSync(tmpDir, { recursive: true, force: true });
134
+ }
135
+ }
136
+
137
+ // 2. Check if containerized Docker CAD service is available
138
+ try {
139
+ const dockerHealth = await probeDockerServiceHealth();
140
+ if (dockerHealth.available && dockerHealth.tools?.openscad?.installed) {
141
+ const dockerResult = await executeOpenScadDocker({
142
+ source,
143
+ outputFormat: format,
144
+ parameters,
145
+ filename: outFilename,
146
+ timeoutMs,
147
+ });
148
+ return {
149
+ ...dockerResult,
150
+ engine: 'openscad-docker',
151
+ };
152
+ }
153
+ } catch {
154
+ // Docker service error/unavailable -> fall through to procedural fallback
155
+ }
156
+
157
+ // Graceful fallback for headless or restricted environments lacking OpenSCAD executable
158
+ const durationMs = Date.now() - started;
159
+ const nameSlug = outFilename.replace(/\.[^/.]+$/, '');
160
+ let artifactContent = '';
161
+
162
+ if (format === 'stl') {
163
+ const w = Number(parameters.deskWidth || parameters.unitWidth || 48) * 0.0254;
164
+ const d = Number(parameters.deskDepth || parameters.unitDepth || 24) * 0.0254;
165
+ const h = Number(parameters.deskHeight || parameters.unitHeight || 30) * 0.0254;
166
+
167
+ const facets = [
168
+ ` facet normal 0 0 1\n outer loop\n vertex 0 0 ${h}\n vertex ${w} 0 ${h}\n vertex ${w} ${d} ${h}\n endloop\n endfacet`,
169
+ ` facet normal 0 0 1\n outer loop\n vertex 0 0 ${h}\n vertex ${w} ${d} ${h}\n vertex 0 ${d} ${h}\n endloop\n endfacet`,
170
+ ` facet normal 0 0 -1\n outer loop\n vertex 0 0 0\n vertex ${w} ${d} 0\n vertex ${w} 0 0\n endloop\n endfacet`,
171
+ ` facet normal 0 0 -1\n outer loop\n vertex 0 0 0\n vertex 0 ${d} 0\n vertex ${w} ${d} 0\n endloop\n endfacet`,
172
+ ];
173
+ artifactContent = `solid ${nameSlug}\n${facets.join('\n')}\nendsolid ${nameSlug}`;
174
+ } else if (format === 'dxf') {
175
+ artifactContent = '0\nSECTION\n2\nHEADER\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n0\nENDSEC\n0\nEOF\n';
176
+ } else {
177
+ artifactContent = source;
178
+ }
179
+
180
+ return {
181
+ success: true,
182
+ engine: 'procedural-fallback',
183
+ binary: null,
184
+ outputFormat: format,
185
+ filename: outFilename,
186
+ mimeType,
187
+ sizeBytes: Buffer.byteLength(artifactContent),
188
+ durationMs,
189
+ artifactContent,
190
+ isBase64: false,
191
+ logs: [
192
+ '[FALLBACK] OpenSCAD binary not detected; procedural synthesis engine used.',
193
+ `[OUTPUT] Synthesized ${outFilename} in ${durationMs}ms`,
194
+ ],
195
+ };
196
+ }
197
+
198
+ export { discoverOpenScad, openScadStatus };
@@ -0,0 +1,2 @@
1
+ export function localProjectRuntime(root?: string, onProjectChanged?: (row: any)=>void): {execute(name: string,args?: any): Promise<any>; catalog(): any[]};
2
+ export function runProjectCli(args: string[]): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {runProjectWorkflow,CAD_PROJECT_WORKFLOWS} from './project-workflows.js';
4
+ import {FileProjectStore} from './project-store.js';
5
+ import {createProjectRuntime} from './project-runtime.js';
6
+ export function localProjectRuntime(root=process.cwd(),onProjectChanged){
7
+ return createProjectRuntime({store:new FileProjectStore(path.join(root,'.agentsam/cad/projects')),artifactRoot:path.join(root,'.agentsam/cad/artifacts'),onProjectChanged});
8
+ }
9
+ export async function runProjectCli(args){
10
+ if(!args.length||args.includes('--help')){console.log('agentsam cad project <get|validate|apply|save|restore|build|inspect|render|export|tools> <request.json> [--root <project-directory>]');return;}
11
+ const names={get:'design_project_get',validate:'design_project_validate',apply:'design_apply_operation',save:'design_project_save',restore:'design_project_restore',build:'design_model_build',inspect:'design_model_inspect',render:'design_model_render',export:'design_model_export'};
12
+ const rootIndex=args.indexOf('--root'),root=rootIndex<0?process.cwd():path.resolve(args[rootIndex+1]);
13
+ const runtime=localProjectRuntime(root);
14
+ if(args[0]==='workflows'){console.log(JSON.stringify(CAD_PROJECT_WORKFLOWS));return;}
15
+ if(args[0]==='workflow'){const input=JSON.parse(fs.readFileSync(args[2]==='-'?0:args[2],'utf8'));console.log(JSON.stringify(await runProjectWorkflow(runtime,args[1],input,{evidenceRoot:path.join(root,'.agentsam/cad/workflow-runs')})));return;}
16
+ if(args[0]==='invoke'){const input=JSON.parse(Buffer.from(args[2],'base64url').toString('utf8'));console.log(JSON.stringify(await runtime.execute(args[1],input)));return;}
17
+ if(args[0]==='tools'){console.log(JSON.stringify(runtime.catalog()));return;}
18
+ const tool=names[args[0]];if(!tool)throw new Error('unknown_project_command');
19
+ const request=JSON.parse(fs.readFileSync(args[1]==='-'?0:args[1],'utf8'));
20
+ console.log(JSON.stringify(await runtime.execute(tool,request)));
21
+ }
@@ -0,0 +1,3 @@
1
+ export function validateProject(project: any): {valid: boolean; errors: string[]};
2
+ export function requireValidProject(project: any): any;
3
+ export function applyProjectOperation(project: any, operation: any): any;
@@ -0,0 +1 @@
1
+ export * from '../../../apps/cad-creator/shared/cad/src/domain/project-contract.js';
@@ -0,0 +1,30 @@
1
+ import {requireValidProject} from './project-contract.js';
2
+ // Native geometry is metres, Z-up. Names preserve editable element identifiers.
3
+ export function projectToBlenderRecipe(project) {
4
+ requireValidProject(project);
5
+ if((project.parametricObjects||[]).length)throw new Error('parametric_objects_require_compilation');
6
+ if(project.roofType!=='none')throw new Error('roof_geometry_not_supported_yet');
7
+ const operations=[{op:'clear_scene'}], m=0.0254;
8
+ const box=(name,x,y,z,w,d,h,a=0)=>operations.push({op:'add',primitive:'cube',name,size:1,location:[x*m,y*m,z*m],scale:[w*m,d*m,h*m],rotation:[0,0,a]});
9
+ for(const w of project.walls){
10
+ const len=Math.hypot(w.x2-w.x1,w.y2-w.y1),a=Math.atan2(w.y2-w.y1,w.x2-w.x1);
11
+ const opening=[...project.doors,...project.windows].filter(e=>e.wallId===w.id).map(e=>({start:e.distanceAlongWall*len-e.width/2,end:e.distanceAlongWall*len+e.width/2,z:e.elevation??0,h:e.height})).sort((a,b)=>a.start-b.start);
12
+ let cursor=0,part=0;
13
+ const segment=(s,e,z,h)=>{if(e-s>1e-6&&h>1e-6){const t=(s+e)/2;box(w.id+':'+part++,w.x1+Math.cos(a)*t,w.y1+Math.sin(a)*t,z+h/2,e-s,w.thickness,h,a);}};
14
+ for(const o of opening){if(o.start<cursor-1e-6)throw new Error('overlapping_openings:'+w.id);segment(cursor,o.start,0,w.height3D);segment(o.start,o.end,0,o.z);segment(o.start,o.end,o.z+o.h,w.height3D-o.z-o.h);cursor=o.end;}
15
+ segment(cursor,len,0,w.height3D);
16
+ }
17
+ // Only axis-aligned rectangular floor polygons are currently compiled; fail explicitly for others.
18
+ for(const r of project.rooms){
19
+ const xs=[...new Set(r.points.map(p=>p[0]))],ys=[...new Set(r.points.map(p=>p[1]))];
20
+ if(r.points.length!==4||xs.length!==2||ys.length!==2||new Set(r.points.map(p=>p.join(','))).size!==4||r.points.some((p,i)=>{const q=r.points[(i+1)%4];return p[0]!==q[0]&&p[1]!==q[1];}))throw new Error('non_rectangular_floor:'+r.id);
21
+ box(r.id,(xs[0]+xs[1])/2,(ys[0]+ys[1])/2,-2,Math.abs(xs[1]-xs[0]),Math.abs(ys[1]-ys[0]),4);
22
+ }
23
+ for(const f of project.furniture)box(f.id,f.x,f.y,f.h/2,f.w,f.d,f.h,(f.rotation||0)*Math.PI/180);
24
+ const xs=project.walls.flatMap(w=>[w.x1,w.x2]),ys=project.walls.flatMap(w=>[w.y1,w.y2]);
25
+ if(!xs.length)throw new Error('model_requires_walls');
26
+ const cx=(Math.min(...xs)+Math.max(...xs))/2*m,cy=(Math.min(...ys)+Math.max(...ys))/2*m, span=Math.max(Math.max(...xs)-Math.min(...xs),Math.max(...ys)-Math.min(...ys))*m;
27
+ operations.push({op:'add_camera',name:'Courtyard',location:[cx,cy+span*1.4,span],rotation:[Math.atan(1.4),0,Math.PI],lens:40,active:true},{op:'add_light',name:'Sun',type:'SUN',energy:3,rotation:[0.5,-0.4,-0.5]});
28
+ if(operations.length>256)throw new Error('recipe_operation_limit_exceeded');
29
+ return {schema_version:1,units:{system:'METRIC',scale_length:1,length_unit:'METERS'},operations};
30
+ }
@@ -0,0 +1,2 @@
1
+ export const CAD_PROJECT_TOOLS: any[];
2
+ export function createProjectRuntime(options: any): {execute(name: string,args?: any): Promise<any>;catalog(): any[]};
@@ -0,0 +1,53 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {validateProject,applyProjectOperation} from './project-contract.js';
4
+ import {projectToBlenderRecipe} from './project-recipe.js';
5
+ import {blenderBuild,blenderInspect,blenderRenderPreview,blenderExport} from './blender.js';
6
+ import {CAD_PROJECT_TOOLS} from '../../../apps/cad-creator/shared/cad/src/domain/project-tools.js';
7
+ export {CAD_PROJECT_TOOLS};
8
+ export function createProjectRuntime({store,artifactRoot,native={blenderBuild,blenderInspect,blenderRenderPreview,blenderExport},onProjectChanged=()=>{}}){
9
+ const root=path.resolve(artifactRoot);
10
+ async function execute(name,args={}){
11
+ const descriptor=CAD_PROJECT_TOOLS.find(t=>t.name===name);
12
+ if(!descriptor)throw new Error('unknown_design_tool');
13
+ if(!args||typeof args!=='object'||Array.isArray(args))throw new Error('invalid_tool_input');
14
+ for(const key of Object.keys(args))if(!Object.hasOwn(descriptor.input_schema.properties,key))throw new Error('unknown_argument:'+key);
15
+ for(const key of descriptor.input_schema.required)if(args[key]===undefined)throw new Error('argument_required:'+key);
16
+ for(const key of ['revision','expected_revision'])if(args[key]!==undefined&&(!Number.isInteger(args[key])||args[key]<(key==='revision'?1:0)))throw new Error('invalid_'+key);
17
+ if(args.project_id!==undefined&&!/^[A-Za-z0-9_-]{1,100}$/.test(args.project_id))throw new Error('invalid_project_id');
18
+ if(name==='design_project_validate'&&args.project===undefined&&args.project_id===undefined)throw new Error('argument_required:project_or_project_id');
19
+ if(name==='design_project_validate')return validateProject(args.project??(await store.read(args.project_id,args.revision)).project);
20
+ if(name==='design_project_get'){const row=await store.read(args.project_id,args.revision);if(args.expected_content_hash&&row.content_hash!==args.expected_content_hash)throw new Error('reopen_verification_failed');return row;}
21
+ let saved;
22
+ if(name==='design_project_save')saved=await store.save(args.project,args.expected_revision,args.message);
23
+ if(name==='design_project_restore')saved=await store.restore(args.project_id,args.revision,args.expected_revision);
24
+ if(name==='design_apply_operation'){
25
+ const current=await store.read(args.project_id);
26
+ if(current.revision!==args.expected_revision)throw new Error('revision_conflict');
27
+ saved=await store.save(applyProjectOperation(current.project,args.operation),args.expected_revision,args.message??args.operation.type);
28
+ }
29
+ if(saved){await onProjectChanged(saved);return saved;}
30
+ const row=await store.read(args.project_id,args.revision);
31
+ // Store validates ids. Artifact paths never come from a caller-supplied path.
32
+ if(!/^[A-Za-z0-9_-]{1,100}$/.test(row.project.id))throw new Error('invalid_project_id');
33
+ const dir=path.join(root,row.project.id,String(row.revision)), blend=path.join(dir,'model.blend'), receipt=path.join(dir,'build.json');
34
+ fs.mkdirSync(dir,{recursive:true});
35
+ let result;
36
+ if(name==='design_model_build'){
37
+ if(fs.existsSync(receipt)){const prior=JSON.parse(fs.readFileSync(receipt,'utf8'));if(prior.content_hash!==row.content_hash)throw new Error('artifact_revision_conflict');}
38
+ result=await native.blenderBuild({output:blend,recipe:projectToBlenderRecipe(row.project),timeoutSeconds:120});
39
+ fs.writeFileSync(receipt,JSON.stringify({content_hash:row.content_hash,artifact:result.artifact}));
40
+ }else{
41
+ const prior=JSON.parse(fs.readFileSync(receipt,'utf8'));
42
+ if(prior.content_hash!==row.content_hash)throw new Error('artifact_revision_conflict');
43
+ const {sha256File}=await import('./blender.js');
44
+ if(sha256File(blend)!==prior.artifact.sha256)throw new Error('model_digest_mismatch');
45
+ if(name==='design_model_inspect')result=await native.blenderInspect({input:blend});
46
+ if(name==='design_model_render')result=await native.blenderRenderPreview({input:blend,output:path.join(dir,'courtyard.png'),width:1024,height:768,timeoutSeconds:120});
47
+ if(name==='design_model_export'){if(!['glb','stl','obj'].includes(args.format??'glb'))throw new Error('invalid_format');result=await native.blenderExport({input:blend,output:path.join(dir,'model.'+(args.format??'glb')),format:args.format??'glb'});}
48
+ }
49
+ const out={...result,project_id:row.project.id,revision:row.revision,content_hash:row.content_hash};
50
+ fs.writeFileSync(path.join(dir,name+'.json'),JSON.stringify(out,null,2));return out;
51
+ }
52
+ return {execute,catalog:()=>CAD_PROJECT_TOOLS};
53
+ }
@@ -0,0 +1,25 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {createHash, randomUUID} from 'node:crypto';
4
+ import {requireValidProject} from './project-contract.js';
5
+ export const projectHash=p=>createHash('sha256').update(JSON.stringify(p)).digest('hex');
6
+ // Immutable revision files plus one atomically replaced pointer. Single host; no distributed claims.
7
+ export class FileProjectStore {
8
+ constructor(root){this.root=path.resolve(root);}
9
+ dir(id){if(!/^[A-Za-z0-9_-]{1,100}$/.test(id))throw new Error('invalid_project_id');return path.join(this.root,id);}
10
+ async read(id,revision){const d=this.dir(id);let r=revision;if(r==null)r=JSON.parse(fs.readFileSync(path.join(d,'head.json'),'utf8')).revision;if(!Number.isSafeInteger(r)||r<1)throw new Error('invalid_revision');return JSON.parse(fs.readFileSync(path.join(d,r+'.json'),'utf8'));}
11
+ async save(project,expectedRevision,message='Saved project'){
12
+ requireValidProject(project);if(!Number.isSafeInteger(expectedRevision)||expectedRevision<0)throw new Error('expected_revision_required');
13
+ const d=this.dir(project.id);fs.mkdirSync(d,{recursive:true});const lock=path.join(d,'write.lock');
14
+ let fd;try{fd=fs.openSync(lock,'wx');}catch{throw new Error('project_busy');}
15
+ try{
16
+ let revision=0;try{revision=(await this.read(project.id)).revision;}catch(e){if(e.code!=='ENOENT')throw e;}
17
+ if(revision!==expectedRevision)throw new Error('revision_conflict');
18
+ const next={...structuredClone(project),version:revision+1,updatedAt:Date.now()};
19
+ const row={project:next,revision:revision+1,content_hash:projectHash(next),message,created_at:Math.floor(Date.now()/1000)};
20
+ fs.writeFileSync(path.join(d,row.revision+'.json'),JSON.stringify(row),{flag:'wx'});
21
+ const tmp=path.join(d,randomUUID()+'.tmp');fs.writeFileSync(tmp,JSON.stringify({revision:row.revision}));fs.renameSync(tmp,path.join(d,'head.json'));return row;
22
+ }finally{fs.closeSync(fd);fs.unlinkSync(lock);}
23
+ }
24
+ async restore(id,revision,expectedRevision){const past=await this.read(id,revision);return this.save(past.project,expectedRevision,'Restored revision '+revision);}
25
+ }
@@ -0,0 +1,31 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {randomUUID} from 'node:crypto';
4
+ export const CAD_PROJECT_WORKFLOWS=[
5
+ {key:'cad.house_baseline',description:'Validate, save, reopen, build, inspect, render and export an editable courtyard baseline.',requires:['project.valid_candidate','native.blender.available'],provides:['project.saved','model.inspected','preview.rendered','export.glb'],steps:['validate','save','reopen','build','inspect','render','export']},
6
+ {key:'cad.edit_preview',description:'Read, atomically edit, reopen, build, inspect, render and export a saved project.',requires:['project.saved','project.expected_revision','native.blender.available'],provides:['project.revised','model.inspected','preview.rendered','export.glb'],steps:['read','edit','reopen','build','inspect','render','export']}
7
+ ];
8
+ const tools={validate:'design_project_validate',save:'design_project_save',read:'design_project_get',edit:'design_apply_operation',reopen:'design_project_get',build:'design_model_build',inspect:'design_model_inspect',render:'design_model_render',export:'design_model_export'};
9
+ export async function runProjectWorkflow(runtime,key,input,{evidenceRoot}={}){
10
+ const definition=CAD_PROJECT_WORKFLOWS.find(w=>w.key===key);if(!definition)throw new Error('unknown_cad_workflow');
11
+ const evidence={run_id:randomUUID(),workflow_key:key,status:'running',started_at:Math.floor(Date.now()/1000),steps:[]};
12
+ const record=()=>{if(evidenceRoot){fs.mkdirSync(evidenceRoot,{recursive:true});const file=path.join(evidenceRoot,evidence.run_id+'.json');fs.writeFileSync(file+'.tmp',JSON.stringify(evidence,null,2));fs.renameSync(file+'.tmp',file);}};
13
+ let row;
14
+ record();
15
+ try{
16
+ for(const step of definition.steps){
17
+ const args=step==='validate'?{project:input.project}:step==='save'?{project:input.project,expected_revision:input.expected_revision??0}:
18
+ step==='read'?{project_id:input.project_id}:step==='edit'?{project_id:input.project_id,expected_revision:input.expected_revision,operation:input.operation}:
19
+ {...{project_id:row.project.id,revision:row.revision},...(step==='export'?{format:'glb'}:{})};
20
+ if(step==='edit'&&row.revision!==input.expected_revision)throw new Error('revision_conflict');
21
+ const result=await runtime.execute(tools[step],args);
22
+ if(step==='validate'&&!result.valid)throw new Error('project_invalid:'+JSON.stringify(result.errors));
23
+ if(step==='reopen'&&(result.content_hash!==row.content_hash||result.revision!==row.revision))throw new Error('reopen_verification_failed');
24
+ if(['save','read','edit','reopen'].includes(step))row=result;
25
+ evidence.steps.push({node_key:step,tool_name:tools[step],status:'completed',result});record();
26
+ }
27
+ evidence.status='completed';evidence.verified_results={project_id:row.project.id,revision:row.revision,content_hash:row.content_hash,facts:definition.provides};
28
+ }catch(error){evidence.status='failed';evidence.error=error.message;throw error;}
29
+ finally{evidence.completed_at=Math.floor(Date.now()/1000);record();}
30
+ return evidence;
31
+ }
@@ -48,6 +48,10 @@ export function generateCadDocker(opts = {}) {
48
48
 
49
49
  const packages = ['python3', 'ca-certificates', 'passwd', ...new Set(tools.flatMap(tool => APT_PACKAGES[tool]))];
50
50
  const q = JSON.stringify;
51
+ const copyLines = ['COPY server.py /srv/server.py'];
52
+ if (tools.includes('freecad')) copyLines.push('COPY freecad/ /srv/freecad/');
53
+ if (tools.includes('blender')) copyLines.push('COPY blender/ /srv/blender/');
54
+
51
55
  const dockerfile = `# AgentSam CAD runtime SHA-256: ${runtimeDigest()}
52
56
  # Tools: ${tools.join(', ')}
53
57
  FROM debian:bookworm-slim
@@ -59,7 +63,7 @@ RUN apt-get update \\
59
63
  && mkdir -p /srv /work \\
60
64
  && chown -R cad:cad /work
61
65
  WORKDIR /srv
62
- COPY server.py /srv/server.py
66
+ ${copyLines.join('\n')}
63
67
  ENV PORT=${port} AGENTSAM_CAD_TOKEN_FILE=/config/service.token AGENTSAM_CAD_WORK_ROOT=/work AGENTSAM_CAD_TOOLS=${tools.join(',')} QT_QPA_PLATFORM=offscreen PYTHONUNBUFFERED=1 HOME=/tmp/home XDG_CACHE_HOME=/tmp/cache XDG_CONFIG_HOME=/tmp/config
64
68
  EXPOSE ${port}
65
69
  USER cad
@@ -128,10 +132,25 @@ export function prepareCadDeployment(targetDir, opts = {}) {
128
132
  return { ...opts, configurationDir, tokenFile, tools };
129
133
  }
130
134
 
131
- export function stageCadContext(versionDir) {
135
+ export function stageCadContext(versionDir, opts = {}) {
132
136
  const context = path.join(versionDir, 'context');
133
137
  fs.mkdirSync(context, { recursive: true });
134
138
  fs.copyFileSync(path.join(sdkRoot, CAD_SERVER_FILE), path.join(context, 'server.py'));
139
+ const tools = opts.tools ? normalizeCadTools(opts.tools) : [];
140
+ if (tools.includes('freecad')) {
141
+ const freecadAdapter = path.join(sdkRoot, 'services/cad/freecad/adapter.py');
142
+ if (fs.existsSync(freecadAdapter)) {
143
+ fs.mkdirSync(path.join(context, 'freecad'), { recursive: true });
144
+ fs.copyFileSync(freecadAdapter, path.join(context, 'freecad', 'adapter.py'));
145
+ }
146
+ }
147
+ if (tools.includes('blender')) {
148
+ const blenderAdapter = path.join(sdkRoot, 'services/cad/blender/adapter.py');
149
+ if (fs.existsSync(blenderAdapter)) {
150
+ fs.mkdirSync(path.join(context, 'blender'), { recursive: true });
151
+ fs.copyFileSync(blenderAdapter, path.join(context, 'blender', 'adapter.py'));
152
+ }
153
+ }
135
154
  return context;
136
155
  }
137
156
 
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import { getProjectName, tryReadProjectConfig } from './project-config.js';
5
+ import { mergeModelReference } from '../models/index.js';
5
6
 
6
7
  export const CLI_PREFERENCES_SCHEMA = 'agentsam-cli-preferences-v3';
7
8
  export const LEGACY_CLI_PREFERENCES_SCHEMAS = new Set(['agentsam-cli-preferences-v1', 'agentsam-cli-preferences-v2']);
@@ -18,9 +19,19 @@ function gitValue(cwd, args) {
18
19
 
19
20
  export function findCliProjectRoot(startDir = process.cwd()) {
20
21
  const cwd = path.resolve(startDir);
22
+ // An explicit AgentSam project inside a larger Git checkout owns its own
23
+ // state. Git is the fallback boundary, not an override for that manifest.
24
+ let dir = cwd;
25
+ for (let i = 0; i < 16; i += 1) {
26
+ if (fs.existsSync(path.join(dir, '.agentsam', 'config.json'))) return dir;
27
+ if (fs.existsSync(path.join(dir, '.git'))) break;
28
+ const parent = path.dirname(dir);
29
+ if (parent === dir) break;
30
+ dir = parent;
31
+ }
21
32
  const gitRoot = gitValue(cwd, ['rev-parse', '--show-toplevel']);
22
33
  if (gitRoot) return path.resolve(gitRoot);
23
- let dir = cwd;
34
+ dir = cwd;
24
35
  for (let i = 0; i < 16; i += 1) {
25
36
  if (fs.existsSync(path.join(dir, '.agentsam', 'cli.json')) || fs.existsSync(path.join(dir, '.agentsam', 'config.json')) || fs.existsSync(path.join(dir, 'package.json'))) return dir;
26
37
  const parent = path.dirname(dir);
@@ -53,12 +64,16 @@ function safeModelSnapshot(value) {
53
64
  provider,
54
65
  provider_model_id: providerModelId,
55
66
  label: String(value.label || providerModelId),
67
+ kind: String(value.kind || 'unknown'),
68
+ description: value.description ? String(value.description) : null,
56
69
  availability: value.availability === 'available' ? 'available' : 'unverified',
57
70
  availability_source: String(value.availability_source || ''),
58
71
  context_window: Number.isFinite(Number(value.context_window)) && Number(value.context_window) > 0 ? Number(value.context_window) : null,
59
72
  context_window_source: String(value.context_window_source || 'unknown'),
60
73
  max_output_tokens: Number.isFinite(Number(value.max_output_tokens)) && Number(value.max_output_tokens) > 0 ? Number(value.max_output_tokens) : null,
61
74
  max_output_tokens_source: String(value.max_output_tokens_source || 'unknown'),
75
+ knowledge_cutoff: value.knowledge_cutoff ? String(value.knowledge_cutoff) : null,
76
+ default_reasoning_effort: value.default_reasoning_effort ? String(value.default_reasoning_effort) : null,
62
77
  reasoning_efforts: Array.isArray(value.reasoning_efforts) && value.reasoning_efforts.length ? value.reasoning_efforts.map(String) : ['auto'],
63
78
  service_tiers: Array.isArray(value.service_tiers) && value.service_tiers.length ? value.service_tiers.map(String) : ['default'],
64
79
  capabilities: value.capabilities && typeof value.capabilities === 'object' ? { ...value.capabilities } : {},
@@ -69,7 +84,7 @@ function safeModelSnapshot(value) {
69
84
  }
70
85
 
71
86
  function normalizePreferences(value = {}) {
72
- const modelSnapshot = safeModelSnapshot(value.modelSnapshot);
87
+ const modelSnapshot = mergeModelReference(safeModelSnapshot(value.modelSnapshot));
73
88
  return {
74
89
  schemaVersion: CLI_PREFERENCES_SCHEMA,
75
90
  trustedDirectory: value.trustedDirectory === true,
@@ -13,7 +13,7 @@ export function coreBaseUrl(env = process.env) {
13
13
  }
14
14
 
15
15
  function authOptions(options) {
16
- if (typeof options === 'string') return { explicit: options };
16
+ if (typeof options === 'string') return { bearer: options };
17
17
  return options && typeof options === 'object' ? options : {};
18
18
  }
19
19
 
@@ -30,6 +30,7 @@ export async function resolveCoreAuthority(options = {}) {
30
30
  nowMs: resolvedOptions.nowMs,
31
31
  skewMs: resolvedOptions.skewMs,
32
32
  refreshImpl: resolvedOptions.refreshImpl,
33
+ forceRefresh: resolvedOptions.forceRefresh === true,
33
34
  });
34
35
  if (authority?.error) throw new Error(authority.error);
35
36
  if (!authority?.value) throw new Error('account_auth_required');
@@ -37,11 +38,22 @@ export async function resolveCoreAuthority(options = {}) {
37
38
  }
38
39
 
39
40
  async function authorizedHeaders(headers, options = {}) {
41
+ if (String(options?.bearer || '').trim()) {
42
+ return { headers: { ...headers, Authorization: `Bearer ${String(options.bearer).trim()}` }, authority: { kind: 'resolved_bearer' } };
43
+ }
40
44
  const authority = await resolveCoreAuthority(options);
41
- return {
42
- ...headers,
43
- Authorization: `Bearer ${authority.value}`,
44
- };
45
+ return { headers: { ...headers, Authorization: `Bearer ${authority.value}` }, authority };
46
+ }
47
+
48
+ async function authorizedFetch(url, init, options = {}) {
49
+ const baseHeaders = init.headers || {};
50
+ let resolved = await authorizedHeaders(baseHeaders, options);
51
+ let response = await (options.fetchImpl || fetch)(url, { ...init, headers: resolved.headers });
52
+ if (response.status === 401 && resolved.authority?.kind === 'browser_oauth' && options.forceRefresh !== true) {
53
+ resolved = await authorizedHeaders(baseHeaders, { ...options, forceRefresh: true });
54
+ response = await (options.fetchImpl || fetch)(url, { ...init, headers: resolved.headers });
55
+ }
56
+ return response;
45
57
  }
46
58
 
47
59
  async function responseJson(res) {
@@ -51,16 +63,16 @@ async function responseJson(res) {
51
63
  export async function postJson(path, body, options = {}) {
52
64
  const resolvedOptions = authOptions(options);
53
65
  const fetchImpl = resolvedOptions.fetchImpl || fetch;
54
- const headers = await authorizedHeaders({
66
+ const headers = {
55
67
  'Content-Type': 'application/json',
56
68
  Accept: 'application/json',
57
- }, resolvedOptions);
58
- const res = await fetchImpl(`${coreBaseUrl(resolvedOptions.env || process.env)}${path}`, {
69
+ };
70
+ const res = await authorizedFetch(`${coreBaseUrl(resolvedOptions.env || process.env)}${path}`, {
59
71
  method: 'POST',
60
72
  headers,
61
73
  body: JSON.stringify(body ?? {}),
62
74
  signal: resolvedOptions.signal,
63
- });
75
+ }, { ...resolvedOptions, fetchImpl });
64
76
  const data = await responseJson(res);
65
77
  if (!res.ok) {
66
78
  const msg = data?.error || data?.message || `HTTP ${res.status}`;
@@ -72,15 +84,22 @@ export async function postJson(path, body, options = {}) {
72
84
  export async function getJson(path, options = {}) {
73
85
  const resolvedOptions = authOptions(options);
74
86
  const fetchImpl = resolvedOptions.fetchImpl || fetch;
75
- const headers = await authorizedHeaders({ Accept: 'application/json' }, resolvedOptions);
76
- const res = await fetchImpl(`${coreBaseUrl(resolvedOptions.env || process.env)}${path}`, {
77
- headers,
87
+ const res = await authorizedFetch(`${coreBaseUrl(resolvedOptions.env || process.env)}${path}`, {
88
+ headers: { Accept: 'application/json' },
78
89
  signal: resolvedOptions.signal,
79
- });
90
+ }, { ...resolvedOptions, fetchImpl });
80
91
  const data = await responseJson(res);
81
92
  if (!res.ok) {
82
93
  const msg = data?.error || data?.message || `HTTP ${res.status}`;
83
- throw new Error(String(msg));
94
+ const error = new Error(String(msg));
95
+ // Previously discarded: status code and which endpoint failed. Both are
96
+ // known here and cost nothing to keep -- callers (whoami, etc.) can now
97
+ // surface "Unauthorized (HTTP 401 from /api/sdk/context)" instead of a
98
+ // bare "Unauthorized" with no way to tell what actually failed.
99
+ error.status = res.status;
100
+ error.endpoint = path;
101
+ error.requestId = res.headers?.get?.('cf-ray') || data?.request_id || null;
102
+ throw error;
84
103
  }
85
104
  return data;
86
105
  }
@@ -91,16 +110,16 @@ export async function getJson(path, options = {}) {
91
110
  export async function streamScaffold(body, onEvent, options = {}) {
92
111
  const resolvedOptions = authOptions(options);
93
112
  const fetchImpl = resolvedOptions.fetchImpl || fetch;
94
- const headers = await authorizedHeaders({
113
+ const headers = {
95
114
  'Content-Type': 'application/json',
96
115
  Accept: 'application/x-ndjson',
97
- }, resolvedOptions);
98
- const res = await fetchImpl(`${coreBaseUrl(resolvedOptions.env || process.env)}/api/sdk/scaffold`, {
116
+ };
117
+ const res = await authorizedFetch(`${coreBaseUrl(resolvedOptions.env || process.env)}/api/sdk/scaffold`, {
99
118
  method: 'POST',
100
119
  headers,
101
120
  body: JSON.stringify(body),
102
121
  signal: resolvedOptions.signal,
103
- });
122
+ }, { ...resolvedOptions, fetchImpl });
104
123
  if (!res.ok) {
105
124
  const data = await responseJson(res);
106
125
  throw new Error(data?.error || `scaffold HTTP ${res.status}`);
@@ -120,7 +120,7 @@ export function writeDeployReceipt(appRoot, receipt) {
120
120
  export function wranglerDeployCommand(target, { dryRun = false } = {}) {
121
121
  const args = ['wrangler', ...target.wranglerArgs];
122
122
  if (dryRun) args.push('--dry-run');
123
- return { cwd: target.appRoot, args, bin: 'npx' };
123
+ return { cwd: target.appRoot, args, bin: process.platform === 'win32' ? 'npx.cmd' : 'npx' };
124
124
  }
125
125
 
126
126
  export function isLocalStudioCheckout(cwd = process.cwd()) {
@@ -217,16 +217,17 @@ export async function runLocalStudioDeploy({
217
217
  }
218
218
 
219
219
  const env = { ...process.env, ...envFile.vars };
220
- const lock = spawnSync('npm', ['run', 'verify:npm10-lock'], { cwd: target.appRoot, env, encoding: 'utf8' });
220
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
221
+ const lock = spawnSync(npmCmd, ['run', 'verify:npm10-lock'], { cwd: target.appRoot, env, encoding: 'utf8', shell: process.platform === 'win32' });
221
222
  if (lock.status !== 0) throw new Error(lock.stderr || lock.stdout || 'verify:npm10-lock failed');
222
223
  if (!skipBuild) {
223
- const build = spawnSync('npm', ['run', 'build'], { cwd: target.appRoot, env, encoding: 'utf8' });
224
+ const build = spawnSync(npmCmd, ['run', 'build'], { cwd: target.appRoot, env, encoding: 'utf8', shell: process.platform === 'win32' });
224
225
  if (build.status !== 0) throw new Error(build.stderr || build.stdout || 'build failed');
225
- const verify = spawnSync('npm', ['run', 'cf:verify-output'], { cwd: target.appRoot, env, encoding: 'utf8' });
226
+ const verify = spawnSync(npmCmd, ['run', 'cf:verify-output'], { cwd: target.appRoot, env, encoding: 'utf8', shell: process.platform === 'win32' });
226
227
  if (verify.status !== 0) throw new Error(verify.stderr || verify.stdout || 'cf:verify-output failed');
227
228
  }
228
229
  const cmd = wranglerDeployCommand(target, { dryRun });
229
- const deployed = spawnSync(cmd.bin, cmd.args, { cwd: cmd.cwd, env, encoding: 'utf8' });
230
+ const deployed = spawnSync(cmd.bin, cmd.args, { cwd: cmd.cwd, env, encoding: 'utf8', shell: process.platform === 'win32' });
230
231
  const wranglerOut = `${deployed.stdout || ''}\n${deployed.stderr || ''}`;
231
232
  const workerVersion = parseWranglerVersionId(wranglerOut);
232
233
  if (deployed.status !== 0) {