@try-works/dsh-tool-mode 0.0.0-stage → 0.5.45

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 (432) hide show
  1. package/LICENSE +205 -0
  2. package/README.md +303 -2
  3. package/bin/dsh-tool-mode-credentials.mjs +20 -0
  4. package/bin/dsh-tool-mode-mcp-stdio.mjs +80 -0
  5. package/bin/dsh-tool-mode.mjs +211 -0
  6. package/builtins.lock.json +26 -0
  7. package/catalog/desert-ant-models.json +693 -0
  8. package/catalog/playbooks.json +531 -0
  9. package/client/client.model-tools.js +1910 -0
  10. package/client/index.js +4158 -0
  11. package/cordis.patch.yml +39 -0
  12. package/lib/adapters/index.d.ts +61 -0
  13. package/lib/adapters/index.d.ts.map +1 -0
  14. package/lib/adapters/index.js +82 -0
  15. package/lib/adapters/types.d.ts +106 -0
  16. package/lib/adapters/types.d.ts.map +1 -0
  17. package/lib/adapters/types.js +85 -0
  18. package/lib/approval.d.ts +189 -0
  19. package/lib/approval.d.ts.map +1 -0
  20. package/lib/approval.js +510 -0
  21. package/lib/asset-acquisition.d.ts +149 -0
  22. package/lib/asset-acquisition.d.ts.map +1 -0
  23. package/lib/asset-acquisition.js +175 -0
  24. package/lib/authority.d.ts +185 -0
  25. package/lib/authority.d.ts.map +1 -0
  26. package/lib/authority.js +223 -0
  27. package/lib/bindings.d.ts +144 -0
  28. package/lib/bindings.d.ts.map +1 -0
  29. package/lib/bindings.js +314 -0
  30. package/lib/builtins.d.ts +27 -0
  31. package/lib/builtins.d.ts.map +1 -0
  32. package/lib/builtins.js +37 -0
  33. package/lib/capability.d.ts +58 -0
  34. package/lib/capability.d.ts.map +1 -0
  35. package/lib/capability.js +192 -0
  36. package/lib/capture-path.d.ts +97 -0
  37. package/lib/capture-path.d.ts.map +1 -0
  38. package/lib/capture-path.js +152 -0
  39. package/lib/catalog.d.ts +37 -0
  40. package/lib/catalog.d.ts.map +1 -0
  41. package/lib/catalog.js +274 -0
  42. package/lib/contract-payload.d.ts +39 -0
  43. package/lib/contract-payload.d.ts.map +1 -0
  44. package/lib/contract-payload.js +56 -0
  45. package/lib/custom-tools/compile.d.ts +25 -0
  46. package/lib/custom-tools/compile.d.ts.map +1 -0
  47. package/lib/custom-tools/compile.js +61 -0
  48. package/lib/custom-tools/composite-executor.d.ts +42 -0
  49. package/lib/custom-tools/composite-executor.d.ts.map +1 -0
  50. package/lib/custom-tools/composite-executor.js +113 -0
  51. package/lib/custom-tools/dispatch.d.ts +28 -0
  52. package/lib/custom-tools/dispatch.d.ts.map +1 -0
  53. package/lib/custom-tools/dispatch.js +34 -0
  54. package/lib/custom-tools/http-executor.d.ts +48 -0
  55. package/lib/custom-tools/http-executor.d.ts.map +1 -0
  56. package/lib/custom-tools/http-executor.js +241 -0
  57. package/lib/custom-tools/index.d.ts +12 -0
  58. package/lib/custom-tools/index.d.ts.map +1 -0
  59. package/lib/custom-tools/index.js +12 -0
  60. package/lib/custom-tools/mcp-surface.d.ts +14 -0
  61. package/lib/custom-tools/mcp-surface.d.ts.map +1 -0
  62. package/lib/custom-tools/mcp-surface.js +41 -0
  63. package/lib/custom-tools/prompt-executor.d.ts +39 -0
  64. package/lib/custom-tools/prompt-executor.d.ts.map +1 -0
  65. package/lib/custom-tools/prompt-executor.js +59 -0
  66. package/lib/custom-tools/registry.d.ts +69 -0
  67. package/lib/custom-tools/registry.d.ts.map +1 -0
  68. package/lib/custom-tools/registry.js +130 -0
  69. package/lib/custom-tools/schema.d.ts +26 -0
  70. package/lib/custom-tools/schema.d.ts.map +1 -0
  71. package/lib/custom-tools/schema.js +375 -0
  72. package/lib/custom-tools/service.d.ts +90 -0
  73. package/lib/custom-tools/service.d.ts.map +1 -0
  74. package/lib/custom-tools/service.js +203 -0
  75. package/lib/custom-tools/store.d.ts +61 -0
  76. package/lib/custom-tools/store.d.ts.map +1 -0
  77. package/lib/custom-tools/store.js +240 -0
  78. package/lib/custom-tools/types.d.ts +131 -0
  79. package/lib/custom-tools/types.d.ts.map +1 -0
  80. package/lib/custom-tools/types.js +11 -0
  81. package/lib/decision-combine.d.ts +113 -0
  82. package/lib/decision-combine.d.ts.map +1 -0
  83. package/lib/decision-combine.js +156 -0
  84. package/lib/decision-evaluate.d.ts +185 -0
  85. package/lib/decision-evaluate.d.ts.map +1 -0
  86. package/lib/decision-evaluate.js +227 -0
  87. package/lib/deployment-coordinator.d.ts +213 -0
  88. package/lib/deployment-coordinator.d.ts.map +1 -0
  89. package/lib/deployment-coordinator.js +274 -0
  90. package/lib/diagnostics.d.ts +169 -0
  91. package/lib/diagnostics.d.ts.map +1 -0
  92. package/lib/diagnostics.js +136 -0
  93. package/lib/discovery-contracts.d.ts +14 -0
  94. package/lib/discovery-contracts.d.ts.map +1 -0
  95. package/lib/discovery-contracts.js +90 -0
  96. package/lib/discovery-describe.d.ts +35 -0
  97. package/lib/discovery-describe.d.ts.map +1 -0
  98. package/lib/discovery-describe.js +99 -0
  99. package/lib/embedding-index.d.ts +55 -0
  100. package/lib/embedding-index.d.ts.map +1 -0
  101. package/lib/embedding-index.js +126 -0
  102. package/lib/extension-dependencies.d.ts +355 -0
  103. package/lib/extension-dependencies.d.ts.map +1 -0
  104. package/lib/extension-dependencies.js +987 -0
  105. package/lib/extension-dispatch.d.ts +150 -0
  106. package/lib/extension-dispatch.d.ts.map +1 -0
  107. package/lib/extension-dispatch.js +178 -0
  108. package/lib/extension-page-state.d.ts +69 -0
  109. package/lib/extension-page-state.d.ts.map +1 -0
  110. package/lib/extension-page-state.js +82 -0
  111. package/lib/extension-registrar.d.ts +116 -0
  112. package/lib/extension-registrar.d.ts.map +1 -0
  113. package/lib/extension-registrar.js +144 -0
  114. package/lib/extensions-api.d.ts +346 -0
  115. package/lib/extensions-api.d.ts.map +1 -0
  116. package/lib/extensions-api.js +17 -0
  117. package/lib/extensions-remote.d.ts +57 -0
  118. package/lib/extensions-remote.d.ts.map +1 -0
  119. package/lib/extensions-remote.js +69 -0
  120. package/lib/extensions-service.d.ts +568 -0
  121. package/lib/extensions-service.d.ts.map +1 -0
  122. package/lib/extensions-service.js +2552 -0
  123. package/lib/harness-budgets.d.ts +158 -0
  124. package/lib/harness-budgets.d.ts.map +1 -0
  125. package/lib/harness-budgets.js +152 -0
  126. package/lib/harness-llm.d.ts +138 -0
  127. package/lib/harness-llm.d.ts.map +1 -0
  128. package/lib/harness-llm.js +218 -0
  129. package/lib/harness-serving.d.ts +115 -0
  130. package/lib/harness-serving.d.ts.map +1 -0
  131. package/lib/harness-serving.js +176 -0
  132. package/lib/headless.d.ts +97 -0
  133. package/lib/headless.d.ts.map +1 -0
  134. package/lib/headless.js +407 -0
  135. package/lib/http-acquisition.d.ts +27 -0
  136. package/lib/http-acquisition.d.ts.map +1 -0
  137. package/lib/http-acquisition.js +140 -0
  138. package/lib/index.d.ts +546 -0
  139. package/lib/index.d.ts.map +1 -0
  140. package/lib/index.js +5273 -0
  141. package/lib/invocation-accounting.d.ts +169 -0
  142. package/lib/invocation-accounting.d.ts.map +1 -0
  143. package/lib/invocation-accounting.js +148 -0
  144. package/lib/jev.d.ts +218 -0
  145. package/lib/jev.d.ts.map +1 -0
  146. package/lib/jev.js +366 -0
  147. package/lib/legacy-migration.d.ts +95 -0
  148. package/lib/legacy-migration.d.ts.map +1 -0
  149. package/lib/legacy-migration.js +429 -0
  150. package/lib/license-basis.d.ts +29 -0
  151. package/lib/license-basis.d.ts.map +1 -0
  152. package/lib/license-basis.js +58 -0
  153. package/lib/managed-workers.d.ts +158 -0
  154. package/lib/managed-workers.d.ts.map +1 -0
  155. package/lib/managed-workers.js +269 -0
  156. package/lib/mcp/auth-selection.d.ts +35 -0
  157. package/lib/mcp/auth-selection.d.ts.map +1 -0
  158. package/lib/mcp/auth-selection.js +64 -0
  159. package/lib/mcp/client-grant.d.ts +89 -0
  160. package/lib/mcp/client-grant.d.ts.map +1 -0
  161. package/lib/mcp/client-grant.js +63 -0
  162. package/lib/mcp/credential-cli.d.ts +38 -0
  163. package/lib/mcp/credential-cli.d.ts.map +1 -0
  164. package/lib/mcp/credential-cli.js +148 -0
  165. package/lib/mcp/credential-store.d.ts +143 -0
  166. package/lib/mcp/credential-store.d.ts.map +1 -0
  167. package/lib/mcp/credential-store.js +461 -0
  168. package/lib/mcp/export-policy.d.ts +25 -0
  169. package/lib/mcp/export-policy.d.ts.map +1 -0
  170. package/lib/mcp/export-policy.js +31 -0
  171. package/lib/mcp/host-binding.d.ts +144 -0
  172. package/lib/mcp/host-binding.d.ts.map +1 -0
  173. package/lib/mcp/host-binding.js +280 -0
  174. package/lib/mcp/http.d.ts +180 -0
  175. package/lib/mcp/http.d.ts.map +1 -0
  176. package/lib/mcp/http.js +602 -0
  177. package/lib/mcp/oauth.d.ts +149 -0
  178. package/lib/mcp/oauth.d.ts.map +1 -0
  179. package/lib/mcp/oauth.js +396 -0
  180. package/lib/mcp/protocol.d.ts +329 -0
  181. package/lib/mcp/protocol.d.ts.map +1 -0
  182. package/lib/mcp/protocol.js +459 -0
  183. package/lib/mcp/rate-limit.d.ts +68 -0
  184. package/lib/mcp/rate-limit.d.ts.map +1 -0
  185. package/lib/mcp/rate-limit.js +114 -0
  186. package/lib/mcp/sdk-v2.d.ts +17 -0
  187. package/lib/mcp/sdk-v2.d.ts.map +1 -0
  188. package/lib/mcp/sdk-v2.js +204 -0
  189. package/lib/mcp/server.d.ts +149 -0
  190. package/lib/mcp/server.d.ts.map +1 -0
  191. package/lib/mcp/server.js +302 -0
  192. package/lib/mcp/stdio.d.ts +80 -0
  193. package/lib/mcp/stdio.d.ts.map +1 -0
  194. package/lib/mcp/stdio.js +184 -0
  195. package/lib/mcp/subscriptions.d.ts +177 -0
  196. package/lib/mcp/subscriptions.d.ts.map +1 -0
  197. package/lib/mcp/subscriptions.js +288 -0
  198. package/lib/mcp-status.d.ts +96 -0
  199. package/lib/mcp-status.d.ts.map +1 -0
  200. package/lib/mcp-status.js +70 -0
  201. package/lib/mcp-usage.d.ts +110 -0
  202. package/lib/mcp-usage.d.ts.map +1 -0
  203. package/lib/mcp-usage.js +189 -0
  204. package/lib/model-qualification.d.ts +192 -0
  205. package/lib/model-qualification.d.ts.map +1 -0
  206. package/lib/model-qualification.js +178 -0
  207. package/lib/model-runtime-lane.d.ts +69 -0
  208. package/lib/model-runtime-lane.d.ts.map +1 -0
  209. package/lib/model-runtime-lane.js +122 -0
  210. package/lib/model-runtime-recipe.d.ts +132 -0
  211. package/lib/model-runtime-recipe.d.ts.map +1 -0
  212. package/lib/model-runtime-recipe.js +291 -0
  213. package/lib/planning.d.ts +256 -0
  214. package/lib/planning.d.ts.map +1 -0
  215. package/lib/planning.js +471 -0
  216. package/lib/playbook.d.ts +42 -0
  217. package/lib/playbook.d.ts.map +1 -0
  218. package/lib/playbook.js +65 -0
  219. package/lib/prepared-assets.d.ts +269 -0
  220. package/lib/prepared-assets.d.ts.map +1 -0
  221. package/lib/prepared-assets.js +471 -0
  222. package/lib/provider-bridge.d.ts +107 -0
  223. package/lib/provider-bridge.d.ts.map +1 -0
  224. package/lib/provider-bridge.js +423 -0
  225. package/lib/ptc-authority.d.ts +154 -0
  226. package/lib/ptc-authority.d.ts.map +1 -0
  227. package/lib/ptc-authority.js +242 -0
  228. package/lib/ptc-confinement.d.ts +60 -0
  229. package/lib/ptc-confinement.d.ts.map +1 -0
  230. package/lib/ptc-confinement.js +212 -0
  231. package/lib/ptc-runtime.d.ts +166 -0
  232. package/lib/ptc-runtime.d.ts.map +1 -0
  233. package/lib/ptc-runtime.js +231 -0
  234. package/lib/publication.d.ts +161 -0
  235. package/lib/publication.d.ts.map +1 -0
  236. package/lib/publication.js +181 -0
  237. package/lib/receipt-store.d.ts +57 -0
  238. package/lib/receipt-store.d.ts.map +1 -0
  239. package/lib/receipt-store.js +143 -0
  240. package/lib/receipt.d.ts +231 -0
  241. package/lib/receipt.d.ts.map +1 -0
  242. package/lib/receipt.js +165 -0
  243. package/lib/registrar.d.ts +75 -0
  244. package/lib/registrar.d.ts.map +1 -0
  245. package/lib/registrar.js +152 -0
  246. package/lib/resource-governance.d.ts +204 -0
  247. package/lib/resource-governance.d.ts.map +1 -0
  248. package/lib/resource-governance.js +213 -0
  249. package/lib/server-lifecycle.d.ts +175 -0
  250. package/lib/server-lifecycle.d.ts.map +1 -0
  251. package/lib/server-lifecycle.js +269 -0
  252. package/lib/skills.d.ts +31 -0
  253. package/lib/skills.d.ts.map +1 -0
  254. package/lib/skills.js +170 -0
  255. package/lib/speech-bridge.d.ts +74 -0
  256. package/lib/speech-bridge.d.ts.map +1 -0
  257. package/lib/speech-bridge.js +115 -0
  258. package/lib/state.d.ts +100 -0
  259. package/lib/state.d.ts.map +1 -0
  260. package/lib/state.js +160 -0
  261. package/lib/title-proposal.d.ts +194 -0
  262. package/lib/title-proposal.d.ts.map +1 -0
  263. package/lib/title-proposal.js +244 -0
  264. package/lib/tool-inventory.d.ts +90 -0
  265. package/lib/tool-inventory.d.ts.map +1 -0
  266. package/lib/tool-inventory.js +102 -0
  267. package/lib/tool-mode-run.d.ts +310 -0
  268. package/lib/tool-mode-run.d.ts.map +1 -0
  269. package/lib/tool-mode-run.js +411 -0
  270. package/lib/tool-sources.d.ts +80 -0
  271. package/lib/tool-sources.d.ts.map +1 -0
  272. package/lib/tool-sources.js +123 -0
  273. package/lib/tool-usage-projection.d.ts +390 -0
  274. package/lib/tool-usage-projection.d.ts.map +1 -0
  275. package/lib/tool-usage-projection.js +1038 -0
  276. package/lib/tool-usage-report.d.ts +153 -0
  277. package/lib/tool-usage-report.d.ts.map +1 -0
  278. package/lib/tool-usage-report.js +274 -0
  279. package/lib/transport.d.ts +158 -0
  280. package/lib/transport.d.ts.map +1 -0
  281. package/lib/transport.js +332 -0
  282. package/lib/types.d.ts +134 -0
  283. package/lib/types.d.ts.map +1 -0
  284. package/lib/types.js +8 -0
  285. package/lib/update-surface.d.ts +79 -0
  286. package/lib/update-surface.d.ts.map +1 -0
  287. package/lib/update-surface.js +224 -0
  288. package/lib/version.d.ts +3 -0
  289. package/lib/version.d.ts.map +1 -0
  290. package/lib/version.js +27 -0
  291. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts +13 -0
  292. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts.map +1 -0
  293. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.js +85 -0
  294. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
  295. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
  296. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +87 -0
  297. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
  298. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
  299. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
  300. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
  301. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
  302. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
  303. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
  304. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
  305. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
  306. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
  307. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
  308. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
  309. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
  310. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
  311. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
  312. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
  313. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
  314. package/node_modules/@try-works/tool-mode-contracts/lib/index.js +53 -0
  315. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +133 -0
  316. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
  317. package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +64 -0
  318. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +121 -0
  319. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
  320. package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
  321. package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
  322. package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
  323. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
  324. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
  325. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
  326. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +86 -0
  327. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
  328. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +145 -0
  329. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
  330. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
  331. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
  332. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
  333. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
  334. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
  335. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +33 -0
  336. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
  337. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +33 -0
  338. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
  339. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
  340. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +191 -0
  341. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +137 -0
  342. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
  343. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +349 -0
  344. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +274 -0
  345. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
  346. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +932 -0
  347. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
  348. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
  349. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
  350. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
  351. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
  352. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
  353. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
  354. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
  355. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
  356. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
  357. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
  358. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +299 -0
  359. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +395 -0
  360. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
  361. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +839 -0
  362. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts +44 -0
  363. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts.map +1 -0
  364. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.js +255 -0
  365. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
  366. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
  367. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
  368. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
  369. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
  370. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
  371. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts +221 -0
  372. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts.map +1 -0
  373. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.js +188 -0
  374. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
  375. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
  376. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
  377. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
  378. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
  379. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
  380. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
  381. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
  382. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
  383. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +128 -0
  384. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
  385. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
  386. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts +41 -0
  387. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts.map +1 -0
  388. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.js +52 -0
  389. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
  390. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
  391. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
  392. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +157 -0
  393. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
  394. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +688 -0
  395. package/node_modules/@try-works/tool-mode-extension-sdk/package.json +54 -0
  396. package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
  397. package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
  398. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +460 -0
  399. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
  400. package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +647 -0
  401. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +61 -0
  402. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
  403. package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +1106 -0
  404. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
  405. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
  406. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +205 -0
  407. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
  408. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
  409. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
  410. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +119 -0
  411. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
  412. package/node_modules/@try-works/tool-mode-recipes/lib/images.js +201 -0
  413. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
  414. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
  415. package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
  416. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +345 -0
  417. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
  418. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1465 -0
  419. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
  420. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
  421. package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
  422. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +231 -0
  423. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
  424. package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +435 -0
  425. package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
  426. package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
  427. package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
  428. package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
  429. package/package.json +127 -4
  430. package/remote/spec.mjs +993 -0
  431. package/remote/typert.host.mjs +14 -0
  432. package/remote/typert.remote-client.mjs +16 -0
@@ -0,0 +1,1106 @@
1
+ /**
2
+ * The recipe authoring CLI (RCP-08).
3
+ *
4
+ * Three commands, each a required new tool rather than an existing npm/DSH
5
+ * command: `validate` checks a draft against BOTH authorities (the published
6
+ * `harness-model-recipe.schema.json` shipped beside this module AND the code
7
+ * validator `parseRecipe`), `describe` prints the effective bounds an operator
8
+ * reads before deciding, and `pack` builds a declarative-extension package
9
+ * (manifest + recipes, NO executable code) consumable by the extension install
10
+ * path.
11
+ *
12
+ * Guardrails built in rather than documented:
13
+ *
14
+ * - The schema is read from THIS PACKAGE'S SHIPPED FILE, resolved relative to
15
+ * the module (so a packed install validates against the packed artifact, not
16
+ * some source checkout).
17
+ * - Packing validates the exact production artifact and runs NO lifecycle
18
+ * scripts: the output is written directly (directory + a store-method zip),
19
+ * never through `npm pack`.
20
+ * - Nothing here runs inference, installs or activates anything, or touches
21
+ * the network (RCP-08's creation rules).
22
+ * - Exit codes are a contract: 0 valid, 1 invalid (every refusal named),
23
+ * 2 usage or IO failure.
24
+ *
25
+ * RCP-09 (agent-assisted creation with an authoring grant) is enforced here when the
26
+ * deployment configures `DSH_TOOL_MODE_AUTHORING_TOKEN` (required `--authoring-grant`)
27
+ * or `DSH_TOOL_MODE_AUTHORING_ROOT` (target containment). The live helper is also here:
28
+ * it requires explicit consent and an operator-supplied executor profile, and narrows
29
+ * the broker limits to the profile's figures.
30
+ *
31
+ * @module @try-works/tool-mode-recipes/cli
32
+ */
33
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
34
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
35
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
36
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
37
+ });
38
+ }
39
+ return path;
40
+ };
41
+ import { closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
42
+ import { fileURLToPath, pathToFileURL } from 'node:url';
43
+ import { dirname, join, parse, resolve, sep } from 'node:path';
44
+ import Ajv2020 from 'ajv/dist/2020.js';
45
+ import { INITIAL_MAX_IMAGE_INPUTS, INITIAL_MAX_OUTPUT_TOKENS, parseRecipe, toCanonicalRecipe, } from "./recipe-format.js";
46
+ import { EDITABLE_FIELDS, policyEditRefusal, templateFiles, validateCreateArguments, } from "./templates.js";
47
+ import { runFixtureSuite, validateFixtureDocument, } from "./fixture-provider.js";
48
+ import { INITIAL_HOST_POLICY, HarnessBroker, narrowBrokerPolicy } from "./broker.js";
49
+ /** The published schema, shipped beside this module (RCP-02's artifact). */
50
+ const SHIPPED_SCHEMA_URL = new URL('../schema/harness-model-recipe.schema.json', import.meta.url);
51
+ /** Load and compile the shipped schema, once per invocation. */
52
+ function schemaValidator() {
53
+ let text;
54
+ try {
55
+ text = readFileSync(fileURLToPath(SHIPPED_SCHEMA_URL), 'utf8');
56
+ }
57
+ catch (error) {
58
+ return { validate: () => [], loadError: `the shipped schema could not be read: ${messageOf(error)}` };
59
+ }
60
+ let schema;
61
+ try {
62
+ schema = JSON.parse(text);
63
+ }
64
+ catch (error) {
65
+ return { validate: () => [], loadError: `the shipped schema is not valid JSON: ${messageOf(error)}` };
66
+ }
67
+ try {
68
+ const validate = new Ajv2020({ strict: true, allErrors: true }).compile(schema);
69
+ return {
70
+ validate: value => (validate(value) ? [] : (validate.errors ?? []).map(error => `${error.instancePath === '' ? '(root)' : error.instancePath} ${error.message ?? 'is invalid'}`)),
71
+ };
72
+ }
73
+ catch (error) {
74
+ return { validate: () => [], loadError: `the shipped schema does not compile: ${messageOf(error)}` };
75
+ }
76
+ }
77
+ /** Validate one decoded document against BOTH authorities (RCP-02, RCP-08). */
78
+ export function judgeRecipe(document) {
79
+ const { validate, loadError } = schemaValidator();
80
+ const parsed = parseRecipe(document);
81
+ const schemaViolations = loadError !== undefined ? [loadError] : validate(document);
82
+ return {
83
+ schemaOk: loadError === undefined && schemaViolations.length === 0,
84
+ schemaViolations,
85
+ codeOk: parsed.ok,
86
+ codeViolations: parsed.ok ? [] : parsed.errors,
87
+ ...parsed.ok ? { recipe: parsed.recipe } : {},
88
+ };
89
+ }
90
+ /** Read and decode a recipe file; IO problems are the CALLER's to report as usage failures. */
91
+ function readRecipeFile(path) {
92
+ let text;
93
+ try {
94
+ text = readFileSync(path, 'utf8');
95
+ }
96
+ catch (error) {
97
+ return { ioError: `cannot read ${path}: ${messageOf(error)}` };
98
+ }
99
+ try {
100
+ return { document: JSON.parse(text) };
101
+ }
102
+ catch (error) {
103
+ return { ioError: `${path} is not valid JSON: ${messageOf(error)}` };
104
+ }
105
+ }
106
+ /** Print a verdict; returns whether it was fully clean. */
107
+ function printVerdict(io, subject, verdict) {
108
+ io.out(`schema: ${verdict.schemaOk ? 'OK' : `${verdict.schemaViolations.length} violation(s)`}`);
109
+ for (const violation of verdict.schemaViolations)
110
+ io.err(` ${subject}: ${violation}`);
111
+ io.out(`code: ${verdict.codeOk ? 'OK' : `${verdict.codeViolations.length} violation(s)`}`);
112
+ for (const violation of verdict.codeViolations)
113
+ io.err(` ${subject}: ${violation}`);
114
+ return verdict.schemaOk && verdict.codeOk;
115
+ }
116
+ const USAGE = `tool-mode-recipe — author declarative harness-model recipes (RCP-08)
117
+
118
+ validate <recipe.json> validate a draft against the published
119
+ harness-model-recipe.schema.json AND the
120
+ code validator; every refusal is named
121
+ describe <recipe.json> print the effective bounds: tool/contract,
122
+ inputs, output and image ceilings,
123
+ destinations, recording, context
124
+ create --template <image-description|text-title>
125
+ [--name <recipe-id>] [--description <text>]
126
+ [--out <dir>] emit a STATIC draft project (recipe, input/
127
+ output schemas, a starter skill note, a
128
+ starter fixtures file, a README) into a
129
+ NEW directory; nothing is installed,
130
+ activated, downloaded or inferred, and an
131
+ existing target is refused
132
+ edit-draft <recipe.json>
133
+ --set <field> --value <text> a bounded static mutation over the safe
134
+ fields (description, maxOutputTokens,
135
+ imageInputs.max); policy fields are
136
+ refused by name and the result must
137
+ validate before it is written
138
+ test <project-dir>
139
+ --fixtures <fixtures.json> run the recipe(s) through the REAL broker
140
+ against a SCRIPTED fake provider (never a
141
+ real model): binding, budget, failure
142
+ vocabulary and the provider-call ceiling
143
+ --live --profile <file> --input <args.json> --consent
144
+ run ONE validated recipe through an
145
+ OPERATOR-SUPPLIED executor module. Consent
146
+ is explicit; the profile narrows output
147
+ tokens/images; no credential is invented
148
+ pack <recipe.json | project-dir> build a declarative-extension package
149
+ [--name <pkg>] [--version <x.y.z>]
150
+ [--description <text>] [--out <dir>]
151
+ (default --out ./dist) — manifest +
152
+ recipes JSON, NO executable code, no
153
+ lifecycle scripts run
154
+
155
+ Authoring policy: when DSH_TOOL_MODE_AUTHORING_TOKEN is set, create/edit-draft
156
+ require --authoring-grant <token>; when DSH_TOOL_MODE_AUTHORING_ROOT is set, the
157
+ target must resolve inside it.
158
+
159
+ Exit codes: 0 valid / packed / passed, 1 invalid recipe or fixture failure, 2 usage or IO failure.`;
160
+ /**
161
+ * Run one CLI invocation.
162
+ * @param argv - arguments after the executable name.
163
+ * @param io - the output sinks.
164
+ * @returns a promise of the process exit code.
165
+ */
166
+ export async function runRecipeCli(argv, io) {
167
+ try {
168
+ return await dispatchRecipeCli(argv, io);
169
+ }
170
+ catch (error) {
171
+ // The exit-2 contract: an UNEXPECTED failure (IO, filesystem, decode) is a
172
+ // named usage/IO refusal — never a raw stack, never exit 1.
173
+ io.err(`IO failure: ${error instanceof Error ? error.message : String(error)}`);
174
+ return 2;
175
+ }
176
+ }
177
+ async function dispatchRecipeCli(argv, io) {
178
+ // A NULL-PROTOTYPE flags map: `--__proto__ x` must not touch the object's
179
+ // prototype chain (the name is still refused by the per-command flag set).
180
+ const positional = [];
181
+ const flags = Object.create(null);
182
+ const booleanFlags = new Set(['help', 'live', 'consent']);
183
+ for (let index = 0; index < argv.length; index += 1) {
184
+ const token = argv[index];
185
+ if (!token.startsWith('--')) {
186
+ positional.push(token);
187
+ continue;
188
+ }
189
+ const name = token.slice(2);
190
+ if (name === 'help') {
191
+ io.out(USAGE);
192
+ return 0;
193
+ }
194
+ if (booleanFlags.has(name)) {
195
+ // A boolean flag REFUSES a following value token: `--live <value>` must
196
+ // not silently consume the value and downgrade the live request.
197
+ const next = argv[index + 1];
198
+ if (next !== undefined && !next.startsWith('--')) {
199
+ io.err(`the flag --${name} takes no value; ${JSON.stringify(next)} is not a flag`);
200
+ io.err(USAGE);
201
+ return 2;
202
+ }
203
+ flags[name] = true;
204
+ continue;
205
+ }
206
+ const value = argv[index + 1];
207
+ if (value === undefined || value.startsWith('--')) {
208
+ flags[name] = true;
209
+ continue;
210
+ }
211
+ flags[name] = value;
212
+ index += 1;
213
+ }
214
+ const [command, ...rest] = positional;
215
+ if (command === undefined) {
216
+ io.err(USAGE);
217
+ return 2;
218
+ }
219
+ // A mistyped flag must be refused, never silently ignored (the contract the
220
+ // original parser pinned): each command names its accepted flags.
221
+ const acceptedFlags = {
222
+ validate: [], describe: [],
223
+ pack: ['out', 'name', 'version', 'description'],
224
+ create: ['template', 'name', 'description', 'out', 'authoring-grant'],
225
+ 'edit-draft': ['set', 'value', 'authoring-grant'],
226
+ test: ['fixtures', 'live', 'profile', 'input', 'consent', 'recipe'],
227
+ };
228
+ const allowed = acceptedFlags[command];
229
+ if (allowed === undefined) {
230
+ io.err(`unknown command ${JSON.stringify(command)}`);
231
+ io.err(USAGE);
232
+ return 2;
233
+ }
234
+ for (const name of Object.keys(flags)) {
235
+ if (!allowed.includes(name)) {
236
+ io.err(`unknown option --${name} for ${JSON.stringify(command)}`);
237
+ io.err(USAGE);
238
+ return 2;
239
+ }
240
+ }
241
+ // Flag-style commands: create / edit-draft / test.
242
+ if (command === 'create') {
243
+ return createCommand(flags, io);
244
+ }
245
+ if (command === 'edit-draft') {
246
+ return editDraftCommand(rest, flags, io);
247
+ }
248
+ if (command === 'test') {
249
+ return await testCommand(rest, flags, io);
250
+ }
251
+ // The original positional commands keep their exact contract.
252
+ if (rest.length !== 1) {
253
+ io.err(USAGE);
254
+ return 2;
255
+ }
256
+ const subject = rest[0];
257
+ if (command === 'validate' || command === 'describe') {
258
+ const { document, ioError } = readRecipeFile(subject);
259
+ if (ioError !== undefined) {
260
+ io.err(ioError);
261
+ return 2;
262
+ }
263
+ const verdict = judgeRecipe(document);
264
+ const clean = printVerdict(io, subject, verdict);
265
+ if (!clean)
266
+ return 1;
267
+ if (command === 'describe') {
268
+ describeTo(io, verdict.recipe);
269
+ }
270
+ return 0;
271
+ }
272
+ if (command === 'pack') {
273
+ return packCommand(subject, flags, io);
274
+ }
275
+ io.err(`unknown command ${JSON.stringify(command)}`);
276
+ io.err(USAGE);
277
+ return 2;
278
+ }
279
+ /**
280
+ * Refuse a target whose ancestor chain carries a reparse point (symlink or
281
+ * Windows junction) or a `tool-mode.extension.json` (an installed or packed
282
+ * artifact is immutable). `create`/`edit-draft` never write through either.
283
+ * @param target - the absolute target path (a directory for create, a file for edit-draft).
284
+ * @returns a refusal reason, or undefined when the target is writable-by-construction.
285
+ */
286
+ function writabilityRefusal(target) {
287
+ const absolute = resolve(target);
288
+ const parsed = parse(absolute);
289
+ // Walk from the FILESYSTEM ROOT, not from cwd: a target on another drive (or
290
+ // outside cwd) must be walked too — the old cwd-relative slice heuristic
291
+ // silently skipped it. Every EXISTING component is lstat'ed (a Windows
292
+ // junction reports as a symlink) and every ancestor is checked for the
293
+ // installed-artifact manifest.
294
+ const components = absolute.slice(parsed.root.length).split(sep).filter(Boolean);
295
+ let cursor = parsed.root;
296
+ // The root itself participates: a drive root that were a reparse point is as
297
+ // much an escape as any deeper component.
298
+ for (const component of components) {
299
+ cursor = join(cursor, component);
300
+ try {
301
+ if (lstatSync(cursor).isSymbolicLink()) {
302
+ return `the target path ${target} passes through a symbolic link or junction at ${cursor}; refused`;
303
+ }
304
+ }
305
+ catch {
306
+ // The component does not exist yet: no deeper component can exist either.
307
+ break;
308
+ }
309
+ }
310
+ // The installed-artifact rule applies to EVERY ancestor directory, not only
311
+ // the immediate parent (the reviewer's grandparent-manifest case): the target
312
+ // must not lie inside a directory tree that carries tool-mode.extension.json.
313
+ let ancestor = dirname(absolute);
314
+ for (;;) {
315
+ if (existsSync(join(ancestor, 'tool-mode.extension.json'))) {
316
+ return `the target path ${target} lies inside an installed or packed artifact at ${ancestor}; those are immutable and refused`;
317
+ }
318
+ const parent = dirname(ancestor);
319
+ if (parent === ancestor)
320
+ break;
321
+ ancestor = parent;
322
+ }
323
+ return undefined;
324
+ }
325
+ /**
326
+ * Verify a directory chain is still reparse-free IMMEDIATELY before a write.
327
+ * The TOCTOU residual: a junction flipped between this call and the following
328
+ * open() can still redirect that ONE open; the post-write verification below
329
+ * detects it and no FURTHER file is created (the window is a single sub-
330
+ * millisecond open per file, and the created project is refused and removed).
331
+ */
332
+ function chainStillClean(dir) {
333
+ return writabilityRefusal(join(dir, '.sentinel'));
334
+ }
335
+ /**
336
+ * Create one file EXCLUSIVELY ('wx') in a chain-verified directory, verifying
337
+ * the chain again after the write. A reparse point observed at any point
338
+ * aborts the operation (the caller refuses the whole command).
339
+ */
340
+ function safeWriteFile(target, data) {
341
+ const parent = dirname(target);
342
+ const before = chainStillClean(parent);
343
+ if (before !== undefined)
344
+ return before;
345
+ let descriptor;
346
+ try {
347
+ descriptor = openSync(target, 'wx');
348
+ writeFileSync(descriptor, data, 'utf8');
349
+ fsyncSync(descriptor);
350
+ }
351
+ finally {
352
+ if (descriptor !== undefined)
353
+ closeSync(descriptor);
354
+ }
355
+ const after = chainStillClean(parent);
356
+ if (after !== undefined)
357
+ return after;
358
+ if (lstatSync(target).isSymbolicLink()) {
359
+ return `the written file ${target} is a reparse point; refused`;
360
+ }
361
+ return undefined;
362
+ }
363
+ /**
364
+ * The operator's authoring grant and draft-directory policy (RCP-09).
365
+ *
366
+ * When `DSH_TOOL_MODE_AUTHORING_TOKEN` is configured, a create/edit must carry the
367
+ * matching `--authoring-grant`; when `DSH_TOOL_MODE_AUTHORING_ROOT` is configured,
368
+ * the target must resolve inside it. Unconfigured deployments keep local authoring.
369
+ * @param target - the absolute create directory or edit file.
370
+ * @param flags - the parsed flags.
371
+ * @returns the refusal, or undefined when allowed.
372
+ */
373
+ function authoringRefusal(target, flags) {
374
+ const requiredToken = process.env.DSH_TOOL_MODE_AUTHORING_TOKEN;
375
+ if (requiredToken !== undefined && requiredToken.length > 0) {
376
+ const supplied = flags['authoring-grant'];
377
+ if (typeof supplied !== 'string' || supplied !== requiredToken) {
378
+ return 'authoring refused: this host requires --authoring-grant matching DSH_TOOL_MODE_AUTHORING_TOKEN';
379
+ }
380
+ }
381
+ const configuredRoot = process.env.DSH_TOOL_MODE_AUTHORING_ROOT;
382
+ if (configuredRoot !== undefined && configuredRoot.length > 0) {
383
+ const root = resolve(configuredRoot);
384
+ const candidate = resolve(target);
385
+ const rootKey = process.platform === 'win32' ? root.toLowerCase() : root;
386
+ const candidateKey = process.platform === 'win32' ? candidate.toLowerCase() : candidate;
387
+ if (candidateKey !== rootKey && !candidateKey.startsWith(`${rootKey}${sep}`)) {
388
+ return `authoring refused: the draft directory policy confines writes to ${root}`;
389
+ }
390
+ }
391
+ return undefined;
392
+ }
393
+ /** `create`: emit the static project files, refusing anything unsafe. */
394
+ function createCommand(flags, io) {
395
+ const template = typeof flags.template === 'string' ? flags.template : undefined;
396
+ const out = resolve(typeof flags.out === 'string' ? flags.out : './draft');
397
+ const authoring = authoringRefusal(out, flags);
398
+ if (authoring !== undefined) {
399
+ io.err(authoring);
400
+ return 2;
401
+ }
402
+ const name = typeof flags.name === 'string' ? flags.name : basenameOf(out);
403
+ const description = typeof flags.description === 'string' ? flags.description : undefined;
404
+ const validated = validateCreateArguments(template, name, description);
405
+ if (validated.error !== undefined) {
406
+ io.err(validated.error);
407
+ io.err(USAGE);
408
+ return 2;
409
+ }
410
+ const refused = writabilityRefusal(out);
411
+ if (refused !== undefined) {
412
+ io.err(refused);
413
+ return 2;
414
+ }
415
+ if (existsSync(out)) {
416
+ io.err(`create refused: the target ${out} already exists; create never overwrites an existing path`);
417
+ return 2;
418
+ }
419
+ const files = templateFiles(validated.template, name, validated.description);
420
+ // The draft must validate under the SHARED validator BEFORE any file lands.
421
+ const draft = files.find(file => file.path === `recipes/${name}.json`);
422
+ const draftDocument = JSON.parse(draft?.data ?? '{}');
423
+ const verdict = judgeRecipe(draftDocument);
424
+ if (!verdict.schemaOk || !verdict.codeOk) {
425
+ io.err('create refused: the generated draft does not validate — this is a template bug, not your input');
426
+ for (const violation of [...verdict.schemaViolations, ...verdict.codeViolations])
427
+ io.err(` ${violation}`);
428
+ return 1;
429
+ }
430
+ const created = [];
431
+ // Cleanup runs ONLY when the chain re-verifies clean at refusal time:
432
+ // deleting through a now-junctioned path could remove VICTIM files.
433
+ const cleanupOwned = () => {
434
+ try {
435
+ if (chainStillClean(out) !== undefined)
436
+ return;
437
+ for (const path of created.reverse())
438
+ rmSync(path, { recursive: true, force: true });
439
+ }
440
+ catch {
441
+ // Best-effort: the refusal already stands; a stuck file reports honestly
442
+ // on the next run instead of being silently deleted through a link.
443
+ }
444
+ };
445
+ try {
446
+ mkdirSync(out, { recursive: true });
447
+ created.push(out);
448
+ const postMkdir = chainStillClean(out);
449
+ if (postMkdir !== undefined) {
450
+ cleanupOwned();
451
+ io.err(`create refused after creation: ${postMkdir}`);
452
+ return 2;
453
+ }
454
+ for (const file of files) {
455
+ const target = join(out, file.path);
456
+ mkdirSync(dirname(target), { recursive: true });
457
+ const writeRefusal = safeWriteFile(target, file.data);
458
+ if (writeRefusal !== undefined) {
459
+ cleanupOwned();
460
+ io.err(`create refused mid-write: ${writeRefusal}`);
461
+ return 2;
462
+ }
463
+ created.push(target);
464
+ }
465
+ // Post-write verification: if ANY reparse point appeared anywhere in the
466
+ // chain during the run, the command refuses and removes the project it
467
+ // created — but ONLY along a chain that is still verified clean.
468
+ const finalCheck = writabilityRefusal(out);
469
+ if (finalCheck !== undefined) {
470
+ cleanupOwned();
471
+ io.err(`create refused after verification: ${finalCheck}`);
472
+ return 2;
473
+ }
474
+ }
475
+ catch (error) {
476
+ cleanupOwned();
477
+ io.err(`create failed: ${messageOf(error)}`);
478
+ return 2;
479
+ }
480
+ io.out(`created draft project ${name} at ${out} (template ${validated.template})`);
481
+ io.out(` files: ${files.map(file => file.path).join(', ')}`);
482
+ io.out(' nothing was installed, activated, downloaded or inferred (RCP-08)');
483
+ io.out(` next: tool-mode-recipe validate recipes/${name}.json, then test ., then pack .`);
484
+ return 0;
485
+ }
486
+ /** The last path component of an absolute or relative directory path. */
487
+ function basenameOf(path) {
488
+ const parts = path.replace(/[\\/]+$/, '').split(/[\\/]/);
489
+ return parts[parts.length - 1] ?? path;
490
+ }
491
+ /** `edit-draft`: a bounded static mutation that must validate before it is written. */
492
+ function editDraftCommand(positional, flags, io) {
493
+ if (positional.length !== 1) {
494
+ io.err('edit-draft needs exactly one recipe file');
495
+ io.err(USAGE);
496
+ return 2;
497
+ }
498
+ const subject = resolve(positional[0]);
499
+ const authoring = authoringRefusal(subject, flags);
500
+ if (authoring !== undefined) {
501
+ io.err(authoring);
502
+ return 2;
503
+ }
504
+ const field = typeof flags.set === 'string' ? flags.set : undefined;
505
+ const value = flags.value;
506
+ if (field === undefined || typeof value !== 'string') {
507
+ io.err('edit-draft needs --set <field> --value <text>');
508
+ io.err(USAGE);
509
+ return 2;
510
+ }
511
+ if (field.startsWith('policy.')) {
512
+ io.err(policyEditRefusal(field));
513
+ return 2;
514
+ }
515
+ // The whitelist lives on a null-prototype map, so prototype-key lookups
516
+ // ('__proto__', 'constructor', 'toString', …) resolve to undefined and hit
517
+ // this refusal instead of traversing Object.prototype.
518
+ const editable = EDITABLE_FIELDS[field];
519
+ if (editable === undefined) {
520
+ io.err(`editing ${JSON.stringify(field)} is refused: only the safe fields are mutable (`
521
+ + `${Object.keys(EDITABLE_FIELDS).join(', ')}); rename the draft instead of reshaping its identity`);
522
+ return 2;
523
+ }
524
+ const valueError = editable.validate(value);
525
+ if (valueError !== undefined) {
526
+ io.err(`--set ${field} refused: ${valueError}`);
527
+ return 2;
528
+ }
529
+ const refused = writabilityRefusal(subject);
530
+ if (refused !== undefined) {
531
+ io.err(refused);
532
+ return 2;
533
+ }
534
+ const { document, ioError } = readRecipeFile(subject);
535
+ if (ioError !== undefined) {
536
+ io.err(ioError);
537
+ return 2;
538
+ }
539
+ if (typeof document !== 'object' || document === null || Array.isArray(document)) {
540
+ io.err(`${subject} is not a recipe object`);
541
+ return 2;
542
+ }
543
+ const mutated = { ...document };
544
+ // The DRAFT's intermediate shape is validated BEFORE any mutation: a draft
545
+ // whose imageInputs is a primitive refuses by name instead of crashing.
546
+ const shapeRefusal = editable.precondition(mutated);
547
+ if (shapeRefusal !== undefined) {
548
+ io.err(`--set ${field} refused: ${shapeRefusal}; ${subject} is unchanged`);
549
+ return 2;
550
+ }
551
+ editable.apply(mutated, value);
552
+ const verdict = judgeRecipe(mutated);
553
+ const clean = printVerdict(io, subject, verdict);
554
+ if (!clean) {
555
+ io.err(`edit-draft refused: the edited recipe would not validate; ${subject} is unchanged`);
556
+ return 1;
557
+ }
558
+ // Atomic write: chain-verified exclusive temp + rename, so a crash never
559
+ // leaves a truncated draft and a flipped junction cannot redirect the temp.
560
+ const temporary = `${subject}.tmp-${process.pid}-${Date.now().toString(36)}`;
561
+ const tempRefusal = safeWriteFile(temporary, `${JSON.stringify(mutated, null, 2)}\n`);
562
+ if (tempRefusal !== undefined) {
563
+ rmSync(temporary, { force: true });
564
+ io.err(`cannot write ${subject}: ${tempRefusal}`);
565
+ return 2;
566
+ }
567
+ try {
568
+ renameSync(temporary, subject);
569
+ }
570
+ catch (error) {
571
+ rmSync(temporary, { force: true });
572
+ io.err(`cannot write ${subject}: ${messageOf(error)}`);
573
+ return 2;
574
+ }
575
+ io.out(`edited ${field} of ${subject}; the result validates`);
576
+ return 0;
577
+ }
578
+ /**
579
+ * `test --live`: run one recipe through an OPERATOR-SUPPLIED executor profile.
580
+ *
581
+ * Consent is explicit (`--consent`), the profile is a local module the operator
582
+ * installed, and the broker policy is NARROWED by the profile's limits — a live
583
+ * run cannot widen output tokens, images or remote destinations. The CLI never
584
+ * invents credentials or falls back to a hosted model.
585
+ */
586
+ async function runLiveTest(positional, flags, io) {
587
+ if (flags.consent !== true) {
588
+ io.err('--live requires --consent: a live run may consume billable provider capacity or transfer data');
589
+ return 2;
590
+ }
591
+ if (positional.length !== 1) {
592
+ io.err('test needs exactly one project directory');
593
+ return 2;
594
+ }
595
+ if (typeof flags.profile !== 'string' || typeof flags.input !== 'string') {
596
+ io.err('--live requires --profile <profile.json> and --input <arguments.json>');
597
+ return 2;
598
+ }
599
+ const project = resolve(positional[0]);
600
+ const profilePath = resolve(flags.profile);
601
+ const inputPath = resolve(flags.input);
602
+ const recipesDir = join(project, 'recipes');
603
+ const recipePath = typeof flags.recipe === 'string'
604
+ ? resolve(flags.recipe)
605
+ : (existsSync(recipesDir)
606
+ ? readdirSync(recipesDir).filter(entry => entry.endsWith('.json')).sort().map(entry => join(recipesDir, entry))[0]
607
+ : undefined);
608
+ if (recipePath === undefined) {
609
+ io.err(`no recipe selected: pass --recipe or put one JSON file in ${recipesDir}`);
610
+ return 2;
611
+ }
612
+ let profile;
613
+ try {
614
+ profile = JSON.parse(readFileSync(profilePath, 'utf8'));
615
+ }
616
+ catch (error) {
617
+ io.err(`the live profile is not valid JSON: ${messageOf(error)}`);
618
+ return 2;
619
+ }
620
+ if (typeof profile.module !== 'string' || profile.module.length === 0) {
621
+ io.err('the live profile must name an executor "module" exporting a provider factory');
622
+ return 2;
623
+ }
624
+ const modulePath = resolve(dirname(profilePath), profile.module);
625
+ let factory;
626
+ try {
627
+ const imported = await import(__rewriteRelativeImportExtension(pathToFileURL(modulePath).href));
628
+ factory = profile.exportName === undefined ? imported.default ?? imported.createProvider : imported[String(profile.exportName)];
629
+ }
630
+ catch (error) {
631
+ io.err(`the live executor module could not be imported: ${messageOf(error)}`);
632
+ return 2;
633
+ }
634
+ if (typeof factory !== 'function') {
635
+ io.err('the live executor module must export a provider factory (default or createProvider)');
636
+ return 2;
637
+ }
638
+ const rawProvider = await factory(profile.options ?? {});
639
+ if (typeof rawProvider.complete !== 'function' || typeof rawProvider.capabilities !== 'function') {
640
+ io.err('the live executor factory returned no provider with complete() and capabilities()');
641
+ return 2;
642
+ }
643
+ const provider = {
644
+ id: typeof rawProvider.id === 'string' ? rawProvider.id : 'live-profile',
645
+ generation: typeof rawProvider.generation === 'number' ? rawProvider.generation : 1,
646
+ destination: rawProvider.destination === 'remote' ? 'remote' : 'local',
647
+ capabilities: rawProvider.capabilities,
648
+ complete: rawProvider.complete,
649
+ };
650
+ const { document, ioError } = readRecipeFile(recipePath);
651
+ if (ioError !== undefined) {
652
+ io.err(ioError);
653
+ return 2;
654
+ }
655
+ const verdict = judgeRecipe(document);
656
+ if (!verdict.schemaOk || !verdict.codeOk) {
657
+ io.err(`${recipePath} does not validate; a live run only uses validated recipes`);
658
+ return 2;
659
+ }
660
+ const recipe = verdict.recipe;
661
+ if (recipe === undefined) {
662
+ io.err(`${recipePath} produced no recipe object`);
663
+ return 2;
664
+ }
665
+ let input;
666
+ try {
667
+ input = JSON.parse(readFileSync(inputPath, 'utf8'));
668
+ }
669
+ catch (error) {
670
+ io.err(`the live input is not valid JSON: ${messageOf(error)}`);
671
+ return 2;
672
+ }
673
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
674
+ io.err('the live input must be one JSON object of recipe arguments');
675
+ return 2;
676
+ }
677
+ const policy = narrowBrokerPolicy(INITIAL_HOST_POLICY, {
678
+ ...typeof profile.maxOutputTokens === 'number' ? { outputTokens: profile.maxOutputTokens } : {},
679
+ ...typeof profile.maxImages === 'number' ? { imageInputs: profile.maxImages } : {},
680
+ ...profile.allowRemote === true ? {} : { remoteDestinations: false },
681
+ });
682
+ const journal = [];
683
+ const broker = new HarnessBroker({ record: record => { journal.push(record); }, policy });
684
+ broker.authorize(recipe.tool.name, provider);
685
+ const outcome = await broker.run(recipe, input);
686
+ if (outcome.kind !== 'completed') {
687
+ io.err(`live run ${outcome.kind === 'failed' ? `failed (${outcome.code})` : outcome.kind}: ${'reason' in outcome ? outcome.reason : ''}`);
688
+ return 1;
689
+ }
690
+ io.out(`live run completed through ${provider.id}; journal entries: ${journal.length}; `
691
+ + `limits: outputTokens=${policy.outputTokens} images=${policy.imageInputs} remote=${String(policy.remoteDestinations)}`);
692
+ io.out(outcome.text);
693
+ return 0;
694
+ }
695
+ /** `test`: the REAL broker over the scripted fixture provider. */
696
+ async function testCommand(positional, flags, io) {
697
+ if (flags.live === true) {
698
+ return await runLiveTest(positional, flags, io);
699
+ }
700
+ if (positional.length !== 1) {
701
+ io.err('test needs exactly one project directory');
702
+ io.err(USAGE);
703
+ return 2;
704
+ }
705
+ const project = resolve(positional[0]);
706
+ const fixturesPath = resolve(typeof flags.fixtures === 'string' ? flags.fixtures : join(project, 'tests', 'fixtures.json'));
707
+ const recipesDir = join(project, 'recipes');
708
+ if (!existsSync(recipesDir)) {
709
+ io.err(`no recipes directory at ${recipesDir}; create a project first (tool-mode-recipe create)`);
710
+ return 2;
711
+ }
712
+ const recipePaths = readdirSync(recipesDir).filter(entry => entry.endsWith('.json')).sort()
713
+ .map(entry => join(recipesDir, entry));
714
+ if (recipePaths.length === 0) {
715
+ io.err(`no recipe JSON files in ${recipesDir}`);
716
+ return 2;
717
+ }
718
+ if (!existsSync(fixturesPath)) {
719
+ io.err(`no fixtures file at ${fixturesPath}; pass --fixtures or create a project first`);
720
+ return 2;
721
+ }
722
+ let fixture;
723
+ try {
724
+ const parsed = JSON.parse(readFileSync(fixturesPath, 'utf8'));
725
+ const validated = validateFixtureDocument(parsed);
726
+ if (validated.error !== undefined || validated.document === undefined) {
727
+ io.err(`the fixtures file is not usable: ${validated.error ?? 'unknown shape'}`);
728
+ return 2;
729
+ }
730
+ fixture = validated.document;
731
+ }
732
+ catch (error) {
733
+ io.err(`the fixtures file is not valid JSON: ${messageOf(error)}`);
734
+ return 2;
735
+ }
736
+ let allPassed = true;
737
+ const failedCases = [];
738
+ for (const path of recipePaths) {
739
+ const { document, ioError } = readRecipeFile(path);
740
+ if (ioError !== undefined) {
741
+ io.err(ioError);
742
+ return 2;
743
+ }
744
+ const verdict = judgeRecipe(document);
745
+ if (!verdict.schemaOk || !verdict.codeOk) {
746
+ io.err(`${path} does not validate; the fixture suite runs only validated recipes`);
747
+ allPassed = false;
748
+ continue;
749
+ }
750
+ io.out(`testing ${path} against ${fixturesPath}`);
751
+ const verdicts = await runFixtureSuite(verdict.recipe, fixture, io, INITIAL_HOST_POLICY);
752
+ for (const entry of verdicts) {
753
+ if (!entry.pass) {
754
+ allPassed = false;
755
+ failedCases.push(entry.caseId);
756
+ }
757
+ }
758
+ }
759
+ if (!allPassed) {
760
+ io.err(`test failed: fixture case(s) ${failedCases.join(', ')} did not match their expected outcomes`);
761
+ return 1;
762
+ }
763
+ io.out('test passed: every fixture case matched its expected outcome against the scripted provider (not a real model)');
764
+ return 0;
765
+ }
766
+ /**
767
+ * Print the effective bounds for a recipe (RCP-08's describe).
768
+ *
769
+ * The recipe's own ceilings are RES-10's initial maxima by construction (the
770
+ * validator refuses anything above them); the host may narrow further at
771
+ * runtime, which the output says rather than hides.
772
+ */
773
+ function describeTo(io, recipe) {
774
+ const names = Object.keys(recipe.inputs);
775
+ const required = names.filter(name => recipe.inputs[name]?.required === true);
776
+ const images = names.filter(name => recipe.inputs[name]?.type === 'image');
777
+ io.out(`${recipe.tool.name} (contract ${recipe.tool.contractId}) — recipe ${recipe.id}@${recipe.version}`);
778
+ io.out(` description: ${recipe.description}`);
779
+ io.out(` inputs: ${names.length} (${required.length} required: ${required.join(', ') || '(none)'}`
780
+ + `${images.length > 0 ? `; ${images.length} image: ${images.join(', ')}` : ''})`);
781
+ io.out(` output ceiling: at most ${recipe.maxOutputTokens} tokens `
782
+ + `(RES-10 initial maximum ${INITIAL_MAX_OUTPUT_TOKENS}; the host policy may narrow this)`);
783
+ io.out(` image ceiling: at most ${recipe.imageInputs.max} image input(s) `
784
+ + `(RES-10 initial maximum ${INITIAL_MAX_IMAGE_INPUTS})`);
785
+ io.out(` recording: ${recipe.policy.record ? 'requested' : 'not requested'} — a host policy that `
786
+ + `requires recording will refuse a ${recipe.policy.record ? 'recorded' : 'record: false'} recipe rather than silently drop it`);
787
+ io.out(` destinations: ${recipe.policy.allowRemote ? 'local or remote, by the host policy' : 'local only, by the recipe'}`);
788
+ io.out(' context: none supplied — a recipe is data; no conversation, tools, attachments or memory are inherited (HLM-05)');
789
+ if (recipe.modelSelection !== undefined) {
790
+ io.out(` executor: approved-profile "${recipe.modelSelection.profile}" (RCP-05) — the operator binds the profile; `
791
+ + 'an unresolved binding keeps the tool unavailable with EXECUTOR_NOT_CONFIGURED');
792
+ }
793
+ else {
794
+ io.out(' executor: caller-model (RCP-05) — the invoking agent\'s authorized harness model, resolved once per invocation');
795
+ }
796
+ if (recipe.response !== undefined) {
797
+ io.out(` response: ${recipe.response.mode} into payload field "${recipe.response.field}" (RCP-04)`);
798
+ if (recipe.response.mode === 'json') {
799
+ io.out(' disclosure: json mode is prompt-enforced and locally validated in this build — the provider\'s '
800
+ + 'verified structured-output feature is NOT negotiated (RCP-04\'s disclosure requirement)');
801
+ }
802
+ }
803
+ else {
804
+ io.out(' response: plain text payload (RCP-04)');
805
+ }
806
+ }
807
+ /** The declarative-manifest template, mirroring the shipped declarative package. */
808
+ function manifestFor(name, version, description, recipes) {
809
+ return {
810
+ protocolVersion: '1.1.0',
811
+ id: name,
812
+ version,
813
+ displayName: name,
814
+ description,
815
+ license: 'MIT',
816
+ activation: { policy: 'operator', deactivatable: true },
817
+ runtimeApi: 1,
818
+ compatibility: {
819
+ hostApi: '^1.1.0',
820
+ requiredFeatures: ['declarative-recipes-v1'],
821
+ },
822
+ execution: { kind: 'declarative' },
823
+ permissions: {
824
+ networkOrigins: [],
825
+ inputFiles: false,
826
+ storage: 'none',
827
+ secrets: [],
828
+ nativeCode: false,
829
+ },
830
+ models: [],
831
+ tools: [],
832
+ implementations: recipes.map(recipe => ({
833
+ id: recipe.id,
834
+ contractId: recipe.tool.contractId,
835
+ contractVersion: '1.0.0',
836
+ recipeVersion: recipe.version,
837
+ })),
838
+ skills: [],
839
+ licenses: [
840
+ {
841
+ layer: 'code',
842
+ name: 'MIT',
843
+ url: 'https://opensource.org/license/mit',
844
+ openSource: true,
845
+ redistribution: 'permitted',
846
+ attributionRequired: false,
847
+ obligations: [],
848
+ },
849
+ {
850
+ layer: 'weights',
851
+ name: 'none: the package carries no weights',
852
+ url: 'https://example.invalid/none',
853
+ openSource: false,
854
+ redistribution: 'not-permitted',
855
+ attributionRequired: false,
856
+ obligations: ['the recipes reference a harness model supplied by the deployment, so no weights are distributed here'],
857
+ },
858
+ {
859
+ layer: 'engine',
860
+ name: 'none: the host broker is the engine',
861
+ url: 'https://example.invalid/none',
862
+ openSource: false,
863
+ redistribution: 'not-permitted',
864
+ attributionRequired: false,
865
+ obligations: ['execution happens in the host broker, which is the point of a declarative package'],
866
+ },
867
+ {
868
+ layer: 'sdk',
869
+ name: 'none: a declarative package links no SDK at runtime',
870
+ url: 'https://example.invalid/none',
871
+ openSource: false,
872
+ redistribution: 'not-permitted',
873
+ attributionRequired: false,
874
+ obligations: ['the host needs no runtime link for a package that ships no module'],
875
+ },
876
+ {
877
+ layer: 'conversion-artifacts',
878
+ name: 'none: no conversion is performed',
879
+ url: 'https://example.invalid/none',
880
+ openSource: false,
881
+ redistribution: 'permitted',
882
+ attributionRequired: false,
883
+ obligations: [],
884
+ },
885
+ ],
886
+ network: { requiredTelemetry: false, otherNetworkUse: false },
887
+ };
888
+ }
889
+ /** Run `pack`, emitting a package directory and a store-method zip. */
890
+ function packCommand(subject, options, io) {
891
+ const out = resolve(options.out ?? './dist');
892
+ let name = options.name;
893
+ let version = options.version;
894
+ const recipePaths = [];
895
+ const isFile = existsSync(subject) && statSync(subject).isFile();
896
+ if (isFile) {
897
+ recipePaths.push(resolve(subject));
898
+ }
899
+ else {
900
+ const dir = resolve(subject);
901
+ if (!existsSync(dir)) {
902
+ io.err(`no recipe or project directory at ${subject}`);
903
+ return 2;
904
+ }
905
+ const packagePath = join(dir, 'package.json');
906
+ if (existsSync(packagePath)) {
907
+ let manifest = {};
908
+ try {
909
+ manifest = JSON.parse(readFileSync(packagePath, 'utf8'));
910
+ }
911
+ catch (error) {
912
+ io.err(`the project package.json is not valid JSON: ${messageOf(error)}`);
913
+ return 2;
914
+ }
915
+ if (name === undefined && typeof manifest.name === 'string')
916
+ name = manifest.name;
917
+ if (version === undefined && typeof manifest.version === 'string')
918
+ version = manifest.version;
919
+ }
920
+ const recipesDir = join(dir, 'recipes');
921
+ const source = existsSync(recipesDir) ? recipesDir : dir;
922
+ for (const entry of readdirSync(source).sort()) {
923
+ if (!entry.endsWith('.json'))
924
+ continue;
925
+ if (source === dir && (entry === 'package.json' || entry === 'tool-mode.extension.json'))
926
+ continue;
927
+ recipePaths.push(join(source, entry));
928
+ }
929
+ }
930
+ if (name === undefined || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name)) {
931
+ io.err('pack needs a package name: pass --name <pkg> or pack a project directory with a package.json');
932
+ return 2;
933
+ }
934
+ if (recipePaths.length === 0) {
935
+ io.err('no recipe JSON files found to pack');
936
+ return 2;
937
+ }
938
+ const recipes = [];
939
+ let clean = true;
940
+ for (const path of recipePaths) {
941
+ const { document, ioError } = readRecipeFile(path);
942
+ if (ioError !== undefined) {
943
+ io.err(ioError);
944
+ clean = false;
945
+ continue;
946
+ }
947
+ const verdict = judgeRecipe(document);
948
+ const fileClean = printVerdict(io, path, verdict);
949
+ if (!fileClean) {
950
+ clean = false;
951
+ continue;
952
+ }
953
+ // Two recipes claiming one tool name would publish two implementations of
954
+ // one contract under one manifest; the packer refuses rather than guessing.
955
+ const recipe = verdict.recipe;
956
+ const clash = recipes.find(existing => existing.tool.name === recipe.tool.name);
957
+ if (clash !== undefined) {
958
+ io.err(`${path} claims the tool "${recipe.tool.name}" already claimed by recipe ${clash.id}; `
959
+ + 'one package serves one implementation per tool');
960
+ clean = false;
961
+ continue;
962
+ }
963
+ recipes.push(recipe);
964
+ }
965
+ if (!clean) {
966
+ io.err('pack refused: the production artifact is only packed from recipes that validate');
967
+ return 1;
968
+ }
969
+ const packageVersion = version ?? (recipes.length === 1 ? recipes[0]?.version : undefined) ?? '0.1.0';
970
+ const description = options.description
971
+ ?? `Declarative recipe package ${name}: ${recipes.map(recipe => recipe.tool.name).join(', ')}.`;
972
+ const files = [];
973
+ files.push({
974
+ path: 'package.json',
975
+ data: `${JSON.stringify({
976
+ name,
977
+ version: packageVersion,
978
+ type: 'module',
979
+ license: 'MIT',
980
+ description,
981
+ files: ['tool-mode.extension.json', 'recipes'],
982
+ toolMode: { manifest: './tool-mode.extension.json' },
983
+ }, null, 2)}\n`,
984
+ });
985
+ files.push({
986
+ path: 'tool-mode.extension.json',
987
+ data: `${JSON.stringify(manifestFor(name, packageVersion, description, recipes), null, 2)}\n`,
988
+ });
989
+ for (const recipe of recipes) {
990
+ // Pack emits the CANONICAL RCP-02 shape: the packaged artifact is the
991
+ // vocabulary the spec publishes, and the lossless binding constraints keep
992
+ // the canonical round trip identical to the frozen recipe.
993
+ files.push({ path: `recipes/${recipe.id}.json`, data: `${JSON.stringify(toCanonicalRecipe(recipe), null, 2)}\n` });
994
+ }
995
+ const packageDir = join(out, `${name.replaceAll('/', '__')}-${packageVersion}`);
996
+ mkdirSync(packageDir, { recursive: true });
997
+ for (const file of files) {
998
+ const target = join(packageDir, file.path);
999
+ mkdirSync(dirname(target), { recursive: true });
1000
+ writeFileSync(target, file.data, 'utf8');
1001
+ }
1002
+ const zipPath = `${packageDir}.zip`;
1003
+ writeFileSync(zipPath, writeZip(files.map(file => ({
1004
+ path: file.path,
1005
+ data: Buffer.from(file.data, 'utf8'),
1006
+ }))));
1007
+ io.out(`packed ${name}@${packageVersion}`);
1008
+ io.out(` package: ${packageDir}`);
1009
+ io.out(` archive: ${zipPath} (store-method zip, consumable by the extension install path)`);
1010
+ io.out(` contents: ${files.map(file => file.path).join(', ')}`);
1011
+ io.out(' NO executable code ships, and no lifecycle scripts were run (RCP-08)');
1012
+ return 0;
1013
+ }
1014
+ /** Best-effort message from an arbitrary thrown value. */
1015
+ function messageOf(error) {
1016
+ try {
1017
+ return error instanceof Error ? error.message : String(error);
1018
+ }
1019
+ catch {
1020
+ return '<unrenderable>';
1021
+ }
1022
+ }
1023
+ // ---------------------------------------------------------------------------
1024
+ // A minimal, dependency-free ZIP writer (STORE method, deterministic order).
1025
+ // ---------------------------------------------------------------------------
1026
+ /** The CRC-32 table and update, for the store-method entries. */
1027
+ const CRC_TABLE = (() => {
1028
+ const table = new Uint32Array(256);
1029
+ for (let n = 0; n < 256; n += 1) {
1030
+ let c = n;
1031
+ for (let k = 0; k < 8; k += 1)
1032
+ c = (c & 1) !== 0 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
1033
+ table[n] = c >>> 0;
1034
+ }
1035
+ return table;
1036
+ })();
1037
+ function crc32(bytes) {
1038
+ let crc = 0xffffffff;
1039
+ for (const byte of bytes)
1040
+ crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
1041
+ return (crc ^ 0xffffffff) >>> 0;
1042
+ }
1043
+ /** Write a STORE-method zip over the given files, in the given order. */
1044
+ function writeZip(files) {
1045
+ const chunks = [];
1046
+ const central = [];
1047
+ let offset = 0;
1048
+ const put = (buffer) => {
1049
+ chunks.push(buffer);
1050
+ offset += buffer.byteLength;
1051
+ };
1052
+ for (const file of files) {
1053
+ const name = Buffer.from(file.path, 'utf8');
1054
+ const crc = crc32(new Uint8Array(file.data));
1055
+ const header = Buffer.alloc(30);
1056
+ header.writeUInt32LE(0x04034b50, 0);
1057
+ header.writeUInt16LE(20, 4); // version needed
1058
+ header.writeUInt16LE(0, 6); // flags
1059
+ header.writeUInt16LE(0, 8); // STORE
1060
+ header.writeUInt16LE(0, 10); // time
1061
+ header.writeUInt16LE(0x21, 12); // date (1980-01-01, deterministic)
1062
+ header.writeUInt32LE(crc, 14);
1063
+ header.writeUInt32LE(file.data.byteLength, 18);
1064
+ header.writeUInt32LE(file.data.byteLength, 22);
1065
+ header.writeUInt16LE(name.byteLength, 26);
1066
+ header.writeUInt16LE(0, 28); // extra length
1067
+ put(header);
1068
+ put(name);
1069
+ put(file.data);
1070
+ const entry = Buffer.alloc(46);
1071
+ entry.writeUInt32LE(0x02014b50, 0);
1072
+ entry.writeUInt16LE(20, 4); // version made by
1073
+ entry.writeUInt16LE(20, 6); // version needed
1074
+ entry.writeUInt16LE(0, 8);
1075
+ entry.writeUInt16LE(0, 10);
1076
+ entry.writeUInt16LE(0, 12);
1077
+ entry.writeUInt16LE(0x21, 14);
1078
+ entry.writeUInt32LE(crc, 16);
1079
+ entry.writeUInt32LE(file.data.byteLength, 20);
1080
+ entry.writeUInt32LE(file.data.byteLength, 24);
1081
+ entry.writeUInt16LE(name.byteLength, 28);
1082
+ entry.writeUInt16LE(0, 30);
1083
+ entry.writeUInt16LE(0, 32);
1084
+ entry.writeUInt16LE(0, 34);
1085
+ entry.writeUInt16LE(0, 36); // external attrs
1086
+ entry.writeUInt32LE(offset - header.byteLength - name.byteLength - file.data.byteLength, 42); // local header offset
1087
+ central.push(entry);
1088
+ central.push(name);
1089
+ }
1090
+ const centralSize = central.reduce((sum, buffer) => sum + buffer.byteLength, 0);
1091
+ const centralStart = offset;
1092
+ for (const buffer of central)
1093
+ put(buffer);
1094
+ const end = Buffer.alloc(22);
1095
+ end.writeUInt32LE(0x06054b50, 0);
1096
+ end.writeUInt16LE(0, 4);
1097
+ end.writeUInt16LE(0, 6);
1098
+ end.writeUInt16LE(files.length, 8);
1099
+ end.writeUInt16LE(files.length, 10);
1100
+ end.writeUInt32LE(centralSize, 12);
1101
+ end.writeUInt32LE(centralStart, 16);
1102
+ end.writeUInt16LE(0, 20);
1103
+ put(end);
1104
+ return Buffer.concat(chunks);
1105
+ }
1106
+ //# sourceMappingURL=cli.js.map