firedrill-run 2.2.5 → 3.0.0

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 (392) hide show
  1. package/README.md +178 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +465 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1622 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +61 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,58 @@
1
+ import { api, openBrowser } from './auth.js';
2
+ import { APP_ORIGIN, CliError } from './http.js';
3
+ import { selectedWorkspace } from './world-access.js';
4
+ import { identifier } from './world-contracts.js';
5
+ import { selectOne } from './prompts.js';
6
+ import { contributionRead, contributionPageRead } from '../../contracts/tool-contribution-contracts.js';
7
+ const text = (row) => `${row.request_key} · ${row.state} · ${row.license}\nSaved version: ${row.implementation_id}`;
8
+ /** Owner rights are reviewed in the browser. The CLI never infers sharing consent. */
9
+ export async function toolContributionCommand(args, options, output) {
10
+ const action = args[0], interactive = !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
11
+ if (!['contribute', 'contributions', 'contribution', 'contribution-withdraw'].includes(action) || args.length > 2
12
+ || action !== 'contributions' && !args[1] || action !== 'contributions' && options.page !== undefined
13
+ || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page)
14
+ || options.search !== undefined || options.file !== undefined || options.upload || options.request !== undefined
15
+ || options.service !== undefined || options.seconds !== undefined || action !== 'contribution-withdraw' && options.change) {
16
+ throw new CliError('arguments_invalid', 'Use tools contribute <version>, contributions [version] --page <n>, contribution <request>, or contribution-withdraw <request> --approve-tool-change.', 2);
17
+ }
18
+ const scope = await selectedWorkspace({ ...options, interactive });
19
+ const owner = { workspace: scope.workspace, actor: scope.owner }, auth = { ...owner, signal: options.signal };
20
+ const base = `/api/workspaces/${scope.workspace}/tools/contributions`;
21
+ const selected = args[1] ? identifier(args[1]) : undefined;
22
+ if (action === 'contribute' || action === 'contributions') {
23
+ const page = Number(options.page ?? 1);
24
+ const value = contributionPageRead(await api(`${base}?page=${page}${selected ? `&implementation_id=${selected}` : ''}`, auth), owner, { page, implementation: selected });
25
+ if (action === 'contributions') {
26
+ output('tool_contributions', value, (value.results.map(text).join('\n\n') || 'No private review nominations.')
27
+ + `\nPage ${value.page}; ${value.count} requests.${value.next ? `\nNext: firedrill tools contributions${selected ? ` ${selected}` : ''} --workspace ${scope.workspace} --page ${value.page + 1}` : ''}`);
28
+ return;
29
+ }
30
+ if (!value.can_nominate)
31
+ throw new CliError('tool_contribution_source_unavailable', 'Choose an available private tool version with current workspace write access.', 2);
32
+ const url = new URL(`/tools/versions/${selected}`, APP_ORIGIN);
33
+ url.searchParams.set('workspace', scope.workspace);
34
+ url.searchParams.set('contribute', '1');
35
+ output('tool_contribution_handoff', { action_required: 'browser_owner_consent', workspace_id: scope.workspace,
36
+ implementation_id: selected, browser_url: url.href, submitted: false }, `Open ${url.href}\nReview the exact source, rights, synthetic-only state and license in the browser. No source is copied or shared by this command.`);
37
+ if (interactive && options.openBrowser)
38
+ await openBrowser(url.href);
39
+ return;
40
+ }
41
+ const original = contributionRead(await api(`${base}/requests/${selected}`, auth), owner, { key: selected });
42
+ if (action === 'contribution') {
43
+ output('tool_contribution', original, text(original));
44
+ return;
45
+ }
46
+ if (!original.can_withdraw && original.state !== 'withdrawn')
47
+ throw new CliError('tool_contribution_already_published', 'This review cannot be withdrawn here. A published license is not revoked; contact support to request catalog withdrawal.', 2);
48
+ const approved = options.change || interactive && await selectOne('Withdraw this private catalog review?', [
49
+ { value: false, label: 'Cancel' }, { value: true, label: 'Withdraw review', detail: 'Prevents later publication from this nomination. Does not erase private source or history.' },
50
+ ], options.signal);
51
+ if (!approved)
52
+ throw new CliError('tool_change_consent_required', 'Nothing was withdrawn. Use --approve-tool-change for an explicit original-request withdrawal.', 2);
53
+ contributionRead(await api(`${base}/requests/${selected}`, { ...auth, method: 'DELETE' }), owner, { key: selected });
54
+ const result = contributionRead(await api(`${base}/requests/${selected}`, auth), owner, { key: selected });
55
+ if (result.state !== 'withdrawn')
56
+ throw new CliError('tool_contribution_withdrawal_unconfirmed', `Check original review ${selected} before another action.`, 2);
57
+ output('tool_contribution', result, text(result));
58
+ }
@@ -0,0 +1,116 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { api } from './auth.js';
3
+ import { CliError } from './http.js';
4
+ import { selectOne } from './prompts.js';
5
+ import { identifier } from './world-contracts.js';
6
+ import { selectedWorkspace } from './world-access.js';
7
+ import { toolLifecycleReads } from './tool-lifecycle-reads.js';
8
+ import { toolStateReads } from './tool-state-reads.js';
9
+ import { allocationApproval } from './tool-runtime.js';
10
+ import { toolStateRead, toolCaptureAccepted, toolTransitionAccepted } from './tool-state-contracts.js';
11
+ const writes = new Set(['checkpoint', 'reset', 'restore', 'fork', 'resume']);
12
+ const interactive = (options) => !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
13
+ async function choose(path, title, describe, options, auth, allowed = () => true, read) {
14
+ let page = 1;
15
+ while (true) {
16
+ const result = read ? await read(page) : await api(`${path}${path.includes('?') ? '&' : '?'}page=${page}`, auth);
17
+ const value = await selectOne(title, [
18
+ ...result.results.filter(allowed).map(one => ({ value: one.id, label: describe(one) })),
19
+ ...(result.next ? [{ value: '__next__', label: 'Next page' }] : []), ...(page > 1 ? [{ value: '__previous__', label: 'Previous page' }] : []),
20
+ { value: '__cancel__', label: 'Cancel' },
21
+ ], options.signal);
22
+ if (value === '__cancel__')
23
+ throw new CliError('canceled', 'No new data operation was submitted.', 130);
24
+ if (value === '__next__')
25
+ page++;
26
+ else if (value === '__previous__')
27
+ page--;
28
+ else
29
+ return identifier(value);
30
+ }
31
+ }
32
+ function print(receipt, output) {
33
+ output('tool_data_request', receipt, `${'action' in receipt ? receipt.action : 'Data capture'}: ${receipt.state}\nRequest: ${receipt.request_key}`
34
+ + ('successor_id' in receipt && receipt.successor_id ? `\nNew instance: ${receipt.successor_id}. Check its current status and use its own connection credentials.` : '')
35
+ + (receipt.checkpoint_id ? `\nCheckpoint: ${receipt.checkpoint_id}` : '')
36
+ + (!('action' in receipt) && receipt.state === 'captured' && !receipt.checkpoint_available ? '\nCapture completed, but saved data is no longer available.' : '')
37
+ + (receipt.problem ? '\n' + receipt.problem : ''));
38
+ if (['failed', 'unavailable', 'canceled'].includes(receipt.state))
39
+ throw new CliError('tool_data_not_completed', receipt.problem ?? 'This data operation did not complete.', 2);
40
+ }
41
+ export async function toolDataCommand(args, options, output) {
42
+ const action = args[0], write = writes.has(action), receiptRead = ['checkpoint-status', 'transition'].includes(action);
43
+ if (!write && !receiptRead && action !== 'transitions' || args.length > 3 || options.file !== undefined || options.upload || options.service !== undefined || options.seconds !== undefined && (!write || action === 'checkpoint')
44
+ || action !== 'transitions' && (options.page !== undefined || options.search !== undefined)
45
+ || !write && (options.change || options.request !== undefined)
46
+ || options.usageQuote !== undefined && (!write || action === 'checkpoint')
47
+ || ['checkpoint', 'reset', 'resume', 'transitions'].includes(action) && args.length > 2
48
+ || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
49
+ throw new CliError('arguments_invalid', 'Use tools checkpoint|reset|resume [instance], restore|fork [instance] [checkpoint], transitions [instance], or checkpoint-status|transition <instance> <request>.', 2);
50
+ }
51
+ const scope = await selectedWorkspace({ ...options, interactive: interactive(options) });
52
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
53
+ const prefix = `/api/workspaces/${scope.workspace}/tools/instances`;
54
+ const lifecycle = toolLifecycleReads(scope, options.signal);
55
+ let instance = args[1] ? identifier(args[1]) : undefined;
56
+ if (!instance && interactive(options))
57
+ instance = await choose(prefix, 'Tool instance', one => `${one.name} ${one.version} · ${one.state}`, options, auth, () => true, page => lifecycle.instances(page));
58
+ if (!instance)
59
+ throw new CliError('tool_instance_required', 'Provide an instance from tools instances.', 2);
60
+ const path = `${prefix}/${instance}`;
61
+ const reads = toolStateReads(scope, instance, options.signal);
62
+ const selected = { workspace: scope.workspace, actor: scope.owner, instance };
63
+ if (action === 'transitions') {
64
+ const page = await reads.transitions(Number(options.page ?? 1), options.search ?? '');
65
+ const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
66
+ output('tool_data_transitions', page, (page.results.map(one => `${one.action}: ${one.state}\nRequest: ${one.request_key}${one.successor_id ? '\nNew instance: ' + one.successor_id : ''}`).join('\n\n') || 'No data operations in this instance.')
67
+ + `\nPage ${page.page}; ${page.count} requests.${page.next ? `\nNext: firedrill tools transitions ${instance} --workspace ${scope.workspace} --page ${page.page + 1}${options.search ? ' --search ' + quote(options.search) : ''}` : ''}`);
68
+ return;
69
+ }
70
+ if (receiptRead) {
71
+ if (!args[2])
72
+ throw new CliError('tool_request_required', 'Provide the original request ID printed before submission.', 2);
73
+ const receipt = action === 'checkpoint-status' ? await reads.capture(identifier(args[2])) : await reads.transition(identifier(args[2]));
74
+ print(receipt, output);
75
+ return;
76
+ }
77
+ const details = await lifecycle.instance(instance);
78
+ const stateless = details.data_mode === 'declared_stateless';
79
+ if (action === 'checkpoint' && stateless) {
80
+ throw new CliError('tool_state_not_applicable', 'This tool declares no persistent data to checkpoint. Use tools checkpoints to read its startup and closing records.', 2);
81
+ }
82
+ let checkpoint = args[2] ? identifier(args[2]) : undefined;
83
+ if (['restore', 'fork'].includes(action) && !checkpoint && interactive(options))
84
+ checkpoint = await choose(`${path}/checkpoints`, 'Saved data', one => `${one.scope === 'declared_stateless' ? one.kind === 'baseline' ? 'Startup record' : 'Closing record' : one.kind === 'manual' ? 'Checkpoint' : one.kind === 'baseline' ? 'Starting data' : 'Data before stop'} · ${one.captured_at}`, options, auth, one => one.available, page => reads.checkpoints(page));
85
+ if (['restore', 'fork'].includes(action) && !checkpoint)
86
+ throw new CliError('tool_checkpoint_required', 'Choose saved data from tools checkpoints.', 2);
87
+ const explanation = stateless ? action === 'fork'
88
+ ? 'Start separate isolated processes? The current instance stays unchanged. No persistent data or service memory is copied.'
89
+ : 'Start fresh isolated processes? Previous connections close. No persistent data or service memory is restored.'
90
+ : action === 'checkpoint' ? 'Retain a private copy of supported tool data?'
91
+ : action === 'fork' ? 'Start a separate instance from this saved data? The original instance stays unchanged.'
92
+ : 'Stop the old instance and start from saved data? Existing connections close and must be configured again.';
93
+ const approved = options.change || interactive(options) && await selectOne(explanation, [{ value: false, label: 'Cancel' }, { value: true, label: action === 'checkpoint' ? 'Save checkpoint' : 'Continue' }], options.signal);
94
+ if (!approved)
95
+ throw new CliError('tool_data_consent_required', 'No new data operation was submitted. Use --approve-tool-change for explicit automation consent.', 2);
96
+ const key = identifier(options.request ?? randomUUID());
97
+ const readCommand = `tools ${action === 'checkpoint' ? 'checkpoint-status' : 'transition'} ${instance} ${key}`;
98
+ output('tool_data_intent', { instance_id: instance, request_key: key, action }, `Request: ${key}\nIf interrupted, use ${readCommand}. Do not create a replacement request.`);
99
+ if (action === 'checkpoint') {
100
+ const value = await api(`${path}/capture-requests`, { ...auth, method: 'POST', idempotencyKey: key, body: { save_data_consent: true } });
101
+ toolStateRead(value, await toolCaptureAccepted(value, selected, { key }));
102
+ print(await reads.capture(key, { id: value.id, digest: value.request_digest }), output);
103
+ }
104
+ else {
105
+ const setup = await lifecycle.instances(), duration = options.seconds === undefined ? setup.setup.default_duration_seconds : Number(options.seconds);
106
+ if (!Number.isSafeInteger(duration) || duration < 60 || duration > setup.setup.maximum_duration_seconds)
107
+ throw new CliError('tool_duration_invalid', 'Choose a supported --seconds duration before starting a successor.', 2);
108
+ const usageQuote = await allocationApproval(scope, options, { implementation_id: details.implementation_id, release_id: null,
109
+ dataset_id: null, duration_seconds: duration, launch_request_key: key,
110
+ restore: { instance_id: instance, checkpoint_id: checkpoint ?? null, action: action } }, output);
111
+ const input = { action: action, checkpoint_id: checkpoint ?? null, duration_seconds: duration, usage_quote_id: usageQuote };
112
+ const value = await api(`${path}/transitions`, { ...auth, method: 'POST', idempotencyKey: key, body: { ...input, restore_consent: true } });
113
+ toolStateRead(value, await toolTransitionAccepted(value, selected, { key, input }));
114
+ print(await reads.transition(key, { id: value.id, digest: value.request_digest, input }), output);
115
+ }
116
+ }
@@ -0,0 +1,176 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import { createInterface } from 'node:readline/promises';
6
+ import { api } from './auth.js';
7
+ import { CliError } from './http.js';
8
+ import { selectOne, terminalText } from './prompts.js';
9
+ import { identifier } from './world-contracts.js';
10
+ import { selectedWorkspace } from './world-access.js';
11
+ import { exportDataset } from './dataset-export.js';
12
+ import { toolLifecycleReads } from './tool-lifecycle-reads.js';
13
+ import { toolStateReads } from './tool-state-reads.js';
14
+ import { datasetReads } from './dataset-reads.js';
15
+ import { datasetRead, datasetMatches, datasetEdits } from './dataset-contracts.js';
16
+ const actions = new Set(['datasets', 'dataset', 'dataset-document', 'dataset-export', 'dataset-request', 'dataset-save', 'dataset-withdraw']);
17
+ const interactive = (options) => !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
18
+ const text = (row) => `${row.id} ${terminalText(row.name)} · version ${row.version} · ${row.state}\n${row.edit_count} saved edits`
19
+ + (row.problem ? '\n' + terminalText(row.problem) : '') + '\nSaved edits do not change a running tool.';
20
+ async function question(title, signal) {
21
+ const prompt = createInterface({ input: process.stdin, output: process.stderr, terminal: false });
22
+ try {
23
+ return (await prompt.question(title, { signal })).trim();
24
+ }
25
+ finally {
26
+ prompt.close();
27
+ }
28
+ }
29
+ async function choose(path, title, describe, auth, signal, read) {
30
+ let page = 1;
31
+ while (true) {
32
+ const result = read ? await read(page) : await api(`${path}?page=${page}`, auth);
33
+ const rows = result.results.flatMap(row => { const label = describe(row); return label === null ? [] : [{ value: row.id, label }]; });
34
+ const value = await selectOne(title, [...rows,
35
+ ...(result.next ? [{ value: '__next__', label: 'Next page' }] : []),
36
+ ...(page > 1 ? [{ value: '__previous__', label: 'Previous page' }] : []), { value: '__cancel__', label: 'Cancel' }], signal);
37
+ if (value === '__cancel__')
38
+ throw new CliError('canceled', 'Dataset selection canceled. No changes were submitted.', 130);
39
+ if (value === '__next__')
40
+ page++;
41
+ else if (value === '__previous__')
42
+ page--;
43
+ else
44
+ return identifier(value);
45
+ }
46
+ }
47
+ export async function datasetRecipeFile(path, maximum, signal) {
48
+ if (!path.toLowerCase().endsWith('.json') || /[\u0000-\u001f\u007f]/.test(path))
49
+ throw new CliError('tool_dataset_file_invalid', 'Choose a regular JSON file.', 2);
50
+ let file;
51
+ try {
52
+ const before = await lstat(resolve(path));
53
+ if (!before.isFile() || before.isSymbolicLink() || !Number.isSafeInteger(before.size) || before.size < 1 || before.size > maximum)
54
+ throw new Error();
55
+ file = await open(resolve(path), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
56
+ const held = await file.stat();
57
+ if (!held.isFile() || held.ino !== before.ino || held.dev !== before.dev || held.size !== before.size || held.mtimeMs !== before.mtimeMs)
58
+ throw new Error();
59
+ const parts = [];
60
+ let size = 0;
61
+ for await (const part of file.createReadStream({ autoClose: false })) {
62
+ signal.throwIfAborted();
63
+ size += part.length;
64
+ if (size > maximum)
65
+ throw new Error();
66
+ parts.push(part);
67
+ }
68
+ const after = await file.stat();
69
+ if (after.size !== held.size || size !== held.size || after.mtimeMs !== held.mtimeMs || after.ctimeMs !== held.ctimeMs)
70
+ throw new Error();
71
+ return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(parts)));
72
+ }
73
+ catch {
74
+ if (signal.aborted)
75
+ throw new CliError('canceled', 'Dataset read canceled. No edits were submitted.', 130);
76
+ throw new CliError('tool_dataset_file_invalid', 'Choose an unchanged regular JSON file within dataset transport capacity. No edits were submitted.', 2);
77
+ }
78
+ finally {
79
+ await file?.close();
80
+ }
81
+ }
82
+ export async function toolDatasetCommand(args, options, output) {
83
+ const action = args[0], save = action === 'dataset-save', withdraw = action === 'dataset-withdraw', write = save || withdraw;
84
+ if (!actions.has(action) || args.length > (save ? 3 : 2) || options.upload || options.service !== undefined || options.seconds !== undefined
85
+ || !save && (options.file !== undefined || options.request !== undefined) || !write && options.change
86
+ || action !== 'dataset-export' && (options.into !== undefined || options.approveDownload !== undefined)
87
+ || action !== 'datasets' && (options.page !== undefined || options.search !== undefined)
88
+ || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
89
+ throw new CliError('arguments_invalid', 'Use tools datasets [checkpoint], dataset|dataset-document <version>, dataset-export [version] --into <file.json>, dataset-request <request>, dataset-save [checkpoint] [name] --file <edits.json>, or dataset-withdraw <version>.', 2);
90
+ }
91
+ const tty = interactive(options), scope = await selectedWorkspace({ ...options, interactive: tty });
92
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
93
+ const prefix = `/api/workspaces/${scope.workspace}/tools`;
94
+ const reads = datasetReads(prefix, scope, options.signal), selectedScope = { workspace: scope.workspace, actor: scope.owner, kind: 'tool' };
95
+ if (action === 'dataset-export')
96
+ return exportDataset(prefix, args[1], scope, options, output);
97
+ if (action === 'datasets') {
98
+ const result = await reads.page({ page: Number(options.page ?? 1), query: options.search, source: args[1] ? identifier(args[1]) : undefined });
99
+ output('tool_datasets', result, (result.results.map(text).join('\n\n') || 'No matching tool datasets.')
100
+ + `\nPage ${result.page}, ${result.count} matching datasets.${result.next ? ` Use --page ${result.page + 1} with the same checkpoint and search to continue.` : ''}`);
101
+ return;
102
+ }
103
+ if (['dataset', 'dataset-document', 'dataset-request'].includes(action)) {
104
+ const key = identifier(args[1] ?? ''), document = action === 'dataset-document';
105
+ const row = action === 'dataset-request' ? await reads.original(key) : await reads.detail(key);
106
+ if (document) {
107
+ const value = await reads.document(row);
108
+ output('tool_dataset_document', value, JSON.stringify(value, null, 2));
109
+ }
110
+ else
111
+ output('tool_dataset', row, text(row));
112
+ return;
113
+ }
114
+ if (withdraw) {
115
+ const key = identifier(args[1] ?? ''), row = await reads.detail(key);
116
+ const approved = options.change || tty && await selectOne(`Withdraw ${terminalText(row.name)} version ${row.version}? Other versions and running tools stay unchanged.`, [{ value: false, label: 'Cancel' }, { value: true, label: 'Withdraw version' }], options.signal);
117
+ if (!approved)
118
+ throw new CliError('tool_dataset_consent_required', 'Use --approve-tool-change to withdraw this exact version. No change was submitted.', 2);
119
+ const result = await api(`${prefix}/datasets/${key}`, { ...auth, method: 'DELETE' });
120
+ datasetRead(result, datasetMatches(result, selectedScope, { id: key, digest: row.request_digest, withdrawn: true }));
121
+ output('tool_dataset', result, text(result));
122
+ return;
123
+ }
124
+ let checkpoint = args[1];
125
+ if (!checkpoint && tty) {
126
+ const instance = await choose(`${prefix}/instances`, 'Tool instance', row => `${terminalText(row.name)} ${terminalText(row.version)}`, auth, options.signal, page => toolLifecycleReads(scope, options.signal).instances(page));
127
+ checkpoint = await choose(`${prefix}/instances/${instance}/checkpoints`, 'Saved tool data', row => row.records.available ? `${row.kind} · ${row.captured_at}` : null, auth, options.signal, page => toolStateReads(scope, instance, options.signal).checkpoints(page));
128
+ }
129
+ if (!checkpoint)
130
+ throw new CliError('tool_checkpoint_required', 'Provide a checkpoint with record pages from tools checkpoints.', 2);
131
+ checkpoint = identifier(checkpoint);
132
+ const name = args[2] ?? (tty ? await question('Dataset name: ', options.signal) : '');
133
+ if (!name || name !== name.trim() || name.length > 120 || /[\u0000-\u001f\u007f-\u009f]/.test(name))
134
+ throw new CliError('tool_dataset_name_required', 'Provide a dataset name of up to 120 characters.', 2);
135
+ const path = options.file ?? (tty ? await question('Dataset edits JSON path: ', options.signal) : '');
136
+ if (!path)
137
+ throw new CliError('tool_dataset_file_required', 'Provide --file <edits.json>. No file was read or uploaded.', 2);
138
+ const catalog = await reads.page({ source: checkpoint });
139
+ const maximum = catalog.capacity?.recipe_bytes;
140
+ if (!Number.isSafeInteger(maximum) || maximum < 1 || maximum > 8 * 1024 * 1024)
141
+ throw new CliError('tool_dataset_capacity_invalid', 'Dataset transport capacity is unavailable. No file was read.', 2);
142
+ const input = await datasetRecipeFile(path, maximum, options.signal);
143
+ let edits = input, previous = null;
144
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
145
+ const value = input;
146
+ if (value.schema === 'firedrill.tool-dataset.v1') {
147
+ if (value.checkpoint_id !== checkpoint)
148
+ throw new CliError('tool_dataset_base_invalid', 'This export belongs to a different checkpoint. No edits were submitted.', 2);
149
+ edits = value.edits;
150
+ }
151
+ else if (Object.keys(value).every(key => ['edits', 'previous_version_id'].includes(key)) && Array.isArray(value.edits)) {
152
+ edits = value.edits;
153
+ if (value.previous_version_id !== null && value.previous_version_id !== undefined)
154
+ previous = identifier(String(value.previous_version_id));
155
+ }
156
+ else
157
+ throw new CliError('tool_dataset_file_invalid', 'Use an edit array, an exported dataset or edits with an optional previous_version_id.', 2);
158
+ }
159
+ if (!datasetEdits(edits))
160
+ throw new CliError('tool_dataset_file_invalid', 'Use a typed edit array. Keep exact integer and decimal values as strings; unsafe numeric values are not rewritten.', 2);
161
+ const body = { checkpoint_id: checkpoint, previous_version_id: previous, name, edits, save_consent: true };
162
+ if (Buffer.byteLength(JSON.stringify(body)) > maximum)
163
+ throw new CliError('tool_dataset_capacity', 'These edits exceed dataset transport capacity.', 2);
164
+ const approved = options.change || tty && await selectOne(`Save ${edits.length} synthetic-data edits as a private version? This will not change a running tool.`, [{ value: false, label: 'Cancel' }, { value: true, label: 'Save dataset' }], options.signal);
165
+ if (!approved)
166
+ throw new CliError('tool_dataset_consent_required', 'Use --approve-tool-change to save the selected edits. No edits were submitted.', 2);
167
+ const key = identifier(options.request ?? randomUUID());
168
+ output('tool_dataset_intent', { request_key: key, checkpoint_id: checkpoint, name }, `Request: ${key}\nIf interrupted, use tools dataset-request ${key}. Keep the original file and request ID; do not submit a replacement.`);
169
+ const row = await api(`${prefix}/datasets`, { ...auth, method: 'POST', body, idempotencyKey: key });
170
+ const selection = { key, source: checkpoint, previous, name, editCount: edits.length };
171
+ datasetRead(row, datasetMatches(row, selectedScope, selection));
172
+ const recorded = await reads.original(key, { ...selection, id: row.id, digest: row.request_digest });
173
+ output('tool_dataset', recorded, text(recorded));
174
+ if (['failed', 'withdrawn'].includes(recorded.state))
175
+ throw new CliError('tool_dataset_not_saved', recorded.problem ?? 'The original dataset save did not complete.', 2);
176
+ }
@@ -0,0 +1 @@
1
+ export * from '../../contracts/tool-group-contracts.js';
@@ -0,0 +1,26 @@
1
+ import { api } from './auth.js';
2
+ import { identifier } from './world-contracts.js';
3
+ import { toolGroupRead, toolGroupsMatch, toolGroupMatches, toolGroupAccepted, toolGroupMembersMatch } from './tool-group-contracts.js';
4
+ export function toolGroupReads(owner, signal) {
5
+ const scope = { workspace: owner.workspace, actor: owner.owner }, auth = { actor: owner.owner, workspace: owner.workspace, signal };
6
+ const prefix = `/api/workspaces/${scope.workspace}/tools`;
7
+ return {
8
+ async page(page = 1, query = '') {
9
+ const value = await api(`${prefix}/setups?page=${page}&q=${encodeURIComponent(query)}`, auth);
10
+ return toolGroupRead(value, toolGroupsMatch(value, scope, query, page));
11
+ },
12
+ async detail(id, selected = {}) {
13
+ const value = await api(`${prefix}/setups/${identifier(id)}`, auth);
14
+ return toolGroupRead(value, toolGroupMatches(value, scope, { ...selected, id }));
15
+ },
16
+ async original(key, selected = {}) {
17
+ const value = await api(`${prefix}/setup-requests/${identifier(key)}`, auth);
18
+ return toolGroupRead(value, selected.input ? await toolGroupAccepted(value, scope, key, selected.input, selected)
19
+ : toolGroupMatches(value, scope, { ...selected, key }));
20
+ },
21
+ async members(group, page = 1, query = '') {
22
+ const value = await api(`${prefix}/setups/${identifier(group)}/members?page=${page}&q=${encodeURIComponent(query)}`, auth);
23
+ return toolGroupRead(value, toolGroupMembersMatch(value, scope, group, query, page));
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,147 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { api } from './auth.js';
3
+ import { CliError } from './http.js';
4
+ import { selectOne, terminalText } from './prompts.js';
5
+ import { selectedWorkspace } from './world-access.js';
6
+ import { identifier } from './world-contracts.js';
7
+ import { requestInput } from './request-input.js';
8
+ import { groupStateMatches, groupStateInputsEqual, groupStatePageMatches, groupStateMemberPageMatches, groupStateQuoteMatches } from '../../contracts/tool-group-state-contracts.js';
9
+ import { allocationCredits } from '../../contracts/tool-runtime-billing-contracts.js';
10
+ const interactive = (options) => !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
11
+ const actions = ['group-state', 'group-state-members', 'group-state-status', 'group-state-quote', 'capture-group', 'reset-group', 'fork-group'];
12
+ const text = (one) => `${terminalText(one.name)}: ${terminalText(one.state)}\nOperation: ${one.id}\nRequest: ${one.request_key}`
13
+ + `\n${one.counts.saved}/${one.counts.total} saved · ${one.counts.released}/${one.counts.total} resumed · ${one.counts.ready}/${one.counts.total} replacements ready`
14
+ + (one.problem ? '\n' + terminalText(one.problem) : '');
15
+ function input(value, action) {
16
+ const row = value;
17
+ if (!row || typeof row !== 'object' || Array.isArray(row)
18
+ || Object.keys(row).sort().join(',') !== 'action,duration_seconds,member_quotes,name,source_operation_id,state_consent'
19
+ || row.action !== action || typeof row.name !== 'string' || !row.name.trim() || row.name !== row.name.trim() || row.name.length > 120
20
+ || /[\u0000-\u001f\u007f]/.test(row.name) || row.state_consent !== true || !Array.isArray(row.member_quotes)
21
+ || (action === 'capture' ? row.duration_seconds !== null || row.source_operation_id !== null || row.member_quotes.length !== 0
22
+ : !Number.isSafeInteger(row.duration_seconds) || row.duration_seconds < 60 || typeof row.source_operation_id !== 'string'
23
+ || !row.member_quotes.length || row.member_quotes.some(one => !one || typeof one !== 'object'
24
+ || Object.keys(one).sort().join(',') !== 'position,usage_quote_id' || !Number.isSafeInteger(one.position) || one.position < 0
25
+ || typeof one.usage_quote_id !== 'string') || new Set(row.member_quotes.map(one => one.position)).size !== row.member_quotes.length)) {
26
+ throw new CliError('tool_group_state_input_invalid', 'Provide the complete group-state request with explicit consent and one reviewed runtime quote per replacement.', 2);
27
+ }
28
+ if (row.source_operation_id)
29
+ identifier(row.source_operation_id);
30
+ for (const one of row.member_quotes)
31
+ identifier(one.usage_quote_id);
32
+ return row;
33
+ }
34
+ export async function toolGroupStateCommand(args, options, output) {
35
+ const action = args[0], paged = ['group-state', 'group-state-members'].includes(action), changing = ['capture-group', 'reset-group', 'fork-group'].includes(action);
36
+ if (!actions.includes(action) || args.length !== (['group-state-members', 'group-state-status'].includes(action) ? 3 : 2)
37
+ || options.service !== undefined || options.upload || options.usageQuote !== undefined
38
+ || options.page !== undefined && !paged || options.search !== undefined && !paged
39
+ || options.file !== undefined && !changing && action !== 'group-state-quote'
40
+ || options.seconds !== undefined && !['reset-group', 'fork-group'].includes(action)
41
+ || options.request !== undefined && !changing && action !== 'group-state-quote' || options.change && !changing
42
+ || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
43
+ throw new CliError('arguments_invalid', 'Use tools group-state <setup>, group-state-members|group-state-status <setup> <operation>, capture-group <setup>, or group-state-quote|reset-group|fork-group <setup> [--file request.json].', 2);
44
+ }
45
+ const scope = await selectedWorkspace({ ...options, interactive: interactive(options) });
46
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal }, group = identifier(args[1]);
47
+ const base = `/api/workspaces/${scope.workspace}/tools/setups/${group}`;
48
+ const detail = async (id, own = false) => {
49
+ const value = await api(`${base}/state-operations/${identifier(id)}`, auth);
50
+ if (!groupStateMatches(value, scope.workspace, group, own ? scope.owner : undefined, own ? id : undefined))
51
+ throw new CliError('tool_group_state_response_invalid', 'The retained operation did not match its original setup.', 2);
52
+ return value;
53
+ };
54
+ const listing = async (page, search = '', capture = false) => {
55
+ const result = await api(`${base}/state-operations?page=${page}&q=${encodeURIComponent(search)}${capture ? '&action=capture' : ''}`, auth);
56
+ if (!groupStatePageMatches(result, scope.workspace, group, page) || !result.runtime
57
+ || !Number.isSafeInteger(result.runtime.default_seconds) || !Number.isSafeInteger(result.runtime.maximum_seconds)
58
+ || result.runtime.default_seconds < 60 || result.runtime.maximum_seconds < result.runtime.default_seconds)
59
+ throw new CliError('tool_group_state_response_invalid', 'The group-state page is unavailable. No change was requested.', 2);
60
+ return result;
61
+ };
62
+ const continuation = (value, command) => `\nPage ${value.page}; ${value.count} matching items.`
63
+ + (value.next ? `\nNext: firedrill tools ${command} --workspace ${scope.workspace} --page ${value.page + 1}`
64
+ + (options.search ? ` --search '${terminalText(options.search).replaceAll("'", "'\\''")}'` : '') : '');
65
+ if (action === 'group-state') {
66
+ const value = await listing(Number(options.page ?? 1), options.search ?? '');
67
+ output('tool_group_state_history', value, (value.results.map(text).join('\n\n') || 'No coordinated group state saved.') + continuation(value, `${action} ${group}`));
68
+ return;
69
+ }
70
+ if (action === 'group-state-status') {
71
+ const value = await detail(args[2]);
72
+ output('tool_group_state', value, text(value));
73
+ return;
74
+ }
75
+ if (action === 'group-state-members') {
76
+ const original = await detail(args[2]), page = Number(options.page ?? 1);
77
+ const value = await api(`${base}/state-operations/${original.id}/members?page=${page}&q=${encodeURIComponent(options.search ?? '')}`, auth);
78
+ if (!groupStateMemberPageMatches(value, page))
79
+ throw new CliError('tool_group_state_response_invalid', 'This member page could not be read.', 2);
80
+ output('tool_group_state_members', value, (value.results.map(one => `${terminalText(one.name)}: ${terminalText(one.state)}\nInstance: ${one.instance_id}`
81
+ + (one.event_watermark !== null ? `\nLast completed request: ${one.event_watermark}` : '') + (one.successor_id ? `\nReplacement: ${one.successor_id}` : '')
82
+ + (one.captured_at ? `\nSaved: ${one.captured_at}` : '')).join('\n\n') || 'No matching members.') + continuation(value, `${action} ${group} ${original.id}`));
83
+ return;
84
+ }
85
+ const key = identifier(options.request ?? randomUUID()), kind = action === 'capture-group' ? 'capture' : action === 'fork-group' ? 'fork' : 'reset';
86
+ let selected;
87
+ if (options.file && action !== 'group-state-quote')
88
+ selected = input(JSON.parse(await requestInput(options.file, options.signal)), kind);
89
+ else if (action === 'capture-group')
90
+ selected = { action: 'capture', name: 'Group checkpoint', source_operation_id: null, duration_seconds: null, member_quotes: [], state_consent: true };
91
+ else {
92
+ let quoteInput;
93
+ if (action === 'group-state-quote') {
94
+ if (!options.file)
95
+ throw new CliError('tool_group_state_input_required', 'Provide --file with action, source_operation_id and duration_seconds; keep --request-key for the eventual replacement.', 2);
96
+ const row = JSON.parse(await requestInput(options.file, options.signal));
97
+ if (!row || Object.keys(row).sort().join(',') !== 'action,duration_seconds,source_operation_id' || !['reset', 'fork'].includes(row.action)
98
+ || !Number.isSafeInteger(row.duration_seconds) || row.duration_seconds < 60)
99
+ throw new CliError('tool_group_state_input_invalid', 'Choose a reset or fork, a completed group checkpoint and finite runtime.', 2);
100
+ quoteInput = { ...row, source_operation_id: identifier(row.source_operation_id), operation_request_key: key };
101
+ }
102
+ else {
103
+ if (!interactive(options))
104
+ throw new CliError('tool_group_state_input_required', 'Review group-state-quote first, then provide its request as --file with the same --request-key and --approve-tool-change.', 2);
105
+ let checkpoint, page = 1, defaults;
106
+ while (!checkpoint) {
107
+ const result = await listing(page, '', true);
108
+ defaults = result.runtime;
109
+ const choice = await selectOne('Saved group checkpoint', [...result.results.filter(one => one.coordinated).map(one => ({ value: one.id, label: one.name, detail: `${one.counts.total} tools · ${one.created_at}` })),
110
+ ...(result.next ? [{ value: 'next', label: 'Next page' }] : []), ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : []), { value: 'cancel', label: 'Cancel' }], options.signal);
111
+ if (choice === 'cancel')
112
+ throw new CliError('canceled', 'Nothing changed.', 130);
113
+ if (choice === 'next')
114
+ page++;
115
+ else if (choice === 'previous')
116
+ page--;
117
+ else
118
+ checkpoint = result.results.find(one => one.id === choice);
119
+ }
120
+ const seconds = options.seconds === undefined ? await selectOne('Replacement runtime', [...new Set([defaults.default_seconds, 3600, 86400, 604800])]
121
+ .filter(one => one <= defaults.maximum_seconds).sort((a, b) => a - b).map(one => ({ value: one, label: `${one / 3600} hours` })), options.signal) : Number(options.seconds);
122
+ quoteInput = { action: kind, source_operation_id: checkpoint.id, duration_seconds: seconds, operation_request_key: key };
123
+ }
124
+ const quoted = await api(`${base}/state-quotes`, { ...auth, method: 'POST', idempotencyKey: randomUUID(), body: quoteInput });
125
+ if (!groupStateQuoteMatches(quoted, scope.workspace, group, key, quoteInput))
126
+ throw new CliError('billing_scope_mismatch', 'Review current runtime quotes for this entire group.', 2);
127
+ selected = { action: quoteInput.action, name: quoteInput.action === 'fork' ? 'Group fork' : 'Group reset', source_operation_id: quoted.source_operation_id,
128
+ duration_seconds: quoted.duration_seconds, member_quotes: quoted.members.map(one => ({ position: one.position, usage_quote_id: one.quote.id })), state_consent: true };
129
+ output('tool_group_state_quote', { ...quoted, request: selected }, quoted.members.map(one => `${terminalText(one.name)}: ${allocationCredits(one.quote)} credits maximum`).join('\n')
130
+ + `\nKeep request ${key}. Only supported native data is restored; process memory and real-account state are not included.`);
131
+ if (action === 'group-state-quote')
132
+ return;
133
+ }
134
+ const approved = options.change || interactive(options) && await selectOne(selected.action === 'fork'
135
+ ? 'Approve every runtime budget and start an isolated set from the saved checkpoint? Original instances stay unchanged.'
136
+ : 'Pause all source services for this group? An uncertain outcome closes the controlled instances instead of resuming them.', [{ value: false, label: 'Cancel' }, { value: true, label: selected.action === 'capture' ? 'Save group checkpoint' : 'Approve group replacement' }], options.signal);
137
+ if (!approved)
138
+ throw new CliError('tool_group_state_consent_required', 'Nothing changed. Use --approve-tool-change only for the selected request and budgets.', 2);
139
+ output('tool_group_state_intent', { group_id: group, request_key: key, request: selected }, `Request: ${key}\nRecovery: firedrill tools group-state-status ${group} ${key} --workspace ${scope.workspace}\nDo not replace an uncertain request.`);
140
+ const value = await api(`${base}/state-operations`, { ...auth, method: 'POST', idempotencyKey: key, body: selected });
141
+ if (!groupStateMatches(value, scope.workspace, group, scope.owner, key) || !groupStateInputsEqual(value.request, selected))
142
+ throw new CliError('tool_group_state_acceptance_unconfirmed', 'Inspect the original request. Nothing was repeated.', 2);
143
+ const retained = await detail(key, true);
144
+ if (!groupStateInputsEqual(retained.request, selected))
145
+ throw new CliError('tool_group_state_acceptance_unconfirmed', 'Inspect the exact original request. Nothing was repeated.', 2);
146
+ output('tool_group_state', retained, text(retained));
147
+ }