firedrill-run 2.2.5 → 3.0.1

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 +176 -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 +506 -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 +1627 -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 +96 -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,1627 @@
1
+ import { APP_ORIGIN } from './http.js';
2
+ import { parseArgs } from 'node:util';
3
+ import { once } from 'node:events';
4
+ import { mcpConnection } from './mcp-connection.js';
5
+ import { onboardingCommand } from './onboarding.js';
6
+ import { api, login, logout, openBrowser, startCredentialCompanion } from './auth.js';
7
+ import { configureCredentialMode } from './credential-mode.js';
8
+ import { killOwnedGroups } from './native/owned-groups.js';
9
+ import { randomUUID } from 'node:crypto';
10
+ import { CliError } from './http.js';
11
+ import { selectOne, terminalText, configurePrompts } from './prompts.js';
12
+ import { chooseWorkspace, refreshWorkspaceAccess, selectedWorld, selectedWorkspace } from './world-access.js';
13
+ import { workspaceRegistrationCommand } from './workspace-registration.js';
14
+ import { localSource } from './local-source.js';
15
+ import { sourceRuntimeCommand } from './source-runtime.js';
16
+ import { connectSource, sourceRequests } from './source-connection.js';
17
+ import { sourceSetsCommand } from './source-sets.js';
18
+ import { registrationGuide } from './registration-guide.js';
19
+ import { isScenarioDiscoveryAction, scenarioDiscoveryCommand } from './scenario-discovery.js';
20
+ import { recordedContextCommand } from './recorded-context.js';
21
+ import { worldRecipes } from './world-recipes.js';
22
+ import { dependencyPreparationCommand, isDependencyPreparationAction } from './dependency-preparation.js';
23
+ import { campaignDiagnoses } from './campaign-diagnoses.js';
24
+ import { sourceBatchCommand } from './source-batch.js';
25
+ import { runnerCommand } from './runner-identity.js';
26
+ import { runCleanupCommand } from './native/run-cleanup.js';
27
+ import { drillCleanupCommand } from './native/drill-cleanup.js';
28
+ import { runtimeCommand } from './runner-runtime.js';
29
+ import { runnerGrantCommand } from './runner-grants.js';
30
+ import { runnerJobs } from './runner-jobs.js';
31
+ import { localContainerPurpose, recoverLocalContainer } from './runner-container.js';
32
+ import { executeAssignment } from './runner-execute.js';
33
+ import { abandonLocalAssignment } from './runner-abandon.js';
34
+ import { resumeSimulation, simulationCommand, simulationRequest } from './simulation-run.js';
35
+ import { driverReviewCommand } from './driver-review.js';
36
+ import { reportCommand } from './campaign-reports.js';
37
+ import { policyCommand } from './company-policies.js';
38
+ import { toolsCommand } from './tools.js';
39
+ import { worldDatasetCommand } from './world-datasets.js';
40
+ import { campaignControl } from './campaign-controls.js';
41
+ import { worldSessionsCommand } from './world-sessions.js';
42
+ import { assessCommand, criteriaCommand } from './evaluation-authoring.js';
43
+ import { assessmentRequests } from './assessment-requests.js';
44
+ import { priorityCommand } from './priority.js';
45
+ import { changesCommand } from './agent-changes.js';
46
+ import { configurationCommand } from './configuration.js';
47
+ import { inspectCommand } from './world-inspection.js';
48
+ import { scenarioRequests } from './scenario-requests.js';
49
+ import { scenarioAuthoring } from './scenario-authoring.js';
50
+ import { revisionRequests } from './revision-requests.js';
51
+ import { scenarioPaths } from './scenario-paths.js';
52
+ import { targetModelsCommand } from './target-models.js';
53
+ import { realOperationsCommand } from './real-operations.js';
54
+ import { billingCommand } from './billing.js';
55
+ import { ciCommand } from './continuous-simulation.js';
56
+ import { settingsCommand, teamCommand } from './administration.js';
57
+ import { vaultCommand } from './vault.js';
58
+ import { localValuesCommand } from './local-values.js';
59
+ import { recordingsCommand } from './recordings.js';
60
+ import { commandNames, completionScript } from './completion.js';
61
+ import { resourceLifecycle } from './resource-lifecycle.js';
62
+ import { createWorld, worldCreationCommand } from './world-creation.js';
63
+ import { syncLocalTrialEvidence } from './runner-trial-sync.js';
64
+ import { syncLocalTrialCompletion } from './runner-trial-completion.js';
65
+ import { syncLocalWorldClosing } from './runner-world-completion.js';
66
+ import { syncLocalWorldEvidence } from './runner-world-evidence-sync.js';
67
+ import { forgetLocalEvidence, inspectLocalEvidence, pruneLocalEvidence } from './runner-evidence-erasure.js';
68
+ import { syncLocalEvidence } from './runner-evidence.js';
69
+ import { syncLocalCompletion } from './runner-completion.js';
70
+ import { COMMON_COMMANDS, COMMON_HELP } from './simple/help.js';
71
+ import { initCommand } from './simple/init.js';
72
+ import { statusCommand } from './simple/status.js';
73
+ import { applyCommand, planCommand } from './simple/plan-apply.js';
74
+ import { libraryCommand, worldCommand } from './simple/authoring.js';
75
+ import { runCommand } from './simple/run.js';
76
+ import { watchCommand } from './simple/watch.js';
77
+ import { nativeCancel, nativeReport, nativeReportShape } from './simple/native-report.js';
78
+ import { reassessmentCommand, reassessmentShape } from './simple/reassessment.js';
79
+ import { toolsAuthorCommand } from './simple/tools-author.js';
80
+ import { NATIVE_TOOL_ACTIONS, nativeToolsCommand } from './simple/native-tools.js';
81
+ import { TOOL_REUSE_VERB, toolReuseCommand } from './simple/tool-reuse.js';
82
+ import { descriptorsCommand, registerCommand } from './simple/register.js';
83
+ const kinds = new Set(['agents', 'worlds', 'scenarios', 'personas', 'evaluations', 'simulations']);
84
+ const ADVANCED_HELP = `Firedrill: advanced commands (use firedrill help for the common flow)
85
+
86
+ firedrill login Authorize this terminal in your browser
87
+ firedrill login --organization <id> Authorize one of your other organizations
88
+ firedrill workspace organizations List your organization choices, not their resources
89
+ firedrill signup Create or authorize an account with WorkOS
90
+ firedrill signup guide Read agent registration instructions without signing in
91
+ firedrill mcp [--agent codex|claude|other] Show remote MCP connection guidance
92
+ firedrill docs Open terminal and product guides
93
+ firedrill support Open workspace help and support
94
+ firedrill agents new [github|upload] Connect repository or upload code in the browser
95
+ firedrill onboarding Resume optional account and workspace setup
96
+ firedrill logout Revoke terminal access and remove credentials
97
+ firedrill settings View or change workspace settings
98
+ firedrill vault View, save or rotate values with hidden input
99
+ firedrill values Manage values kept in this device's OS credential store
100
+ firedrill vault list|show [id] Search metadata without printing saved values
101
+ firedrill vault save|rotate [id] Guided save, or --file metadata.json --value-stdin
102
+ firedrill vault expiry|remove <id> Change expiry or revoke saved access
103
+ firedrill vault request|resume <key> Inspect or continue the exact original save
104
+ firedrill settings update --file <settings.json> --approve-change
105
+ firedrill settings open [workspace|team|billing|account]
106
+ firedrill team View teammates or choose a team action
107
+ firedrill team list|invitations Search and page actual team records
108
+ firedrill team show <member> Read one teammate and current role
109
+ firedrill team invite [email] [role] Invite with original-request recovery
110
+ firedrill team request|resume|withdraw <key>
111
+ firedrill team role <member> [role] Change a role after confirmation
112
+ firedrill team remove <member> Remove organization access
113
+ firedrill team revoke <invitation> Revoke the exact invitation
114
+ firedrill completion bash|zsh|fish Print shell completion setup
115
+ firedrill agents|worlds archive|restore|remove [id]
116
+ firedrill agents|worlds requests Page original record-change history
117
+ firedrill agents|worlds request|resume|withdraw <key>
118
+ firedrill agents|worlds --records active|archived|all
119
+ firedrill billing Inspect balance and choose a billing action
120
+ firedrill billing credit-help Read How credits work, with --no-browser or --json for a link only
121
+ firedrill billing usage|credit-ledger Page resource credits with --from <ISO-time> --to <ISO-time>
122
+ firedrill billing credit-purchases Page prepaid credit purchases
123
+ firedrill billing credit-purchase <id> Inspect an original credit purchase
124
+ firedrill billing credit-allocations Inspect credit sources and expiry dates
125
+ firedrill billing options --json Read the configured catalog and available offers
126
+ firedrill billing subscribe Open subscription choices in Billing
127
+ firedrill billing change Review plan changes in Billing
128
+ firedrill billing topup Open credit top-up settings in Billing
129
+ firedrill billing portal Open hosted payment details and cancellation
130
+ firedrill billing purchases Page payment requests
131
+ firedrill billing purchase <id> Inspect the original payment status
132
+ firedrill billing open <id> Open its current hosted payment page
133
+ firedrill billing refresh-purchase <id> Request a current read, not another payment
134
+ firedrill billing invoices Page retained invoice history
135
+ firedrill billing invoice|lines <id> Inspect an invoice or its paged line items
136
+ firedrill billing refresh-invoice <id> Request a current invoice read
137
+ firedrill billing redeem Open private event-code entry in Billing
138
+ firedrill workspace Select an available workspace
139
+ firedrill workspace list List your available workspaces
140
+ firedrill workspace use <id> Select a workspace directly
141
+ firedrill workspace refresh Refresh available workspace access
142
+ firedrill workspace create [name] Create a workspace after the human accepts the terms [--request-key <key>]
143
+ firedrill workspace requests List workspace requests made from the terminal
144
+ firedrill workspace request <key> Recover one workspace request
145
+ firedrill workspace withdraw <key> Withdraw a request that has not been sent [--approve-change]
146
+ firedrill ci View retained CI configurations and run history
147
+ firedrill ci list|show [id] Search configurations or inspect one
148
+ firedrill ci scenarios [id] [rev] Inspect the exact saved scenario selection
149
+ firedrill ci revisions [id] [rev] Read saved configuration history
150
+ firedrill ci automatic-history [id] [rev]
151
+ firedrill ci approvals [id] [rev] Inspect retained CI access history
152
+ firedrill ci runs [configuration] [sha] Page exact-commit run history
153
+ firedrill ci run|result [run] Inspect a CI run or its retained result
154
+ firedrill ci scores [run] Page scores from the original CI policy
155
+ firedrill ci criteria [run] Inspect original CI evaluation coverage
156
+ firedrill ci delivery|delivery-history <run> [check|comment]
157
+ firedrill ci access <configuration> Review CI input access in the browser
158
+ firedrill ci inputs|open <run> Open exact-run input review or its report
159
+ firedrill worlds List saved worlds
160
+ firedrill worlds <id> Inspect a saved world
161
+ firedrill worlds new [agent] Create a world for a saved agent
162
+ firedrill worlds creation <key> Inspect the original world creation request
163
+ firedrill worlds continue-creation|withdraw-creation <key>
164
+ firedrill inspect Choose a workflow, rule or event schedule
165
+ firedrill inspect workflows|rules|events|collections Search world declarations
166
+ firedrill inspect schema <id> [JSON path] Page a declared collection schema
167
+ firedrill inspect show <id> Inspect exact declaration and source metadata
168
+ firedrill inspect entries <id> [path] Page authored paths, steps or scheduled changes
169
+ firedrill scenarios requests Page your saved scenario changes
170
+ firedrill scenarios create|edit [id] Guided scenario changes, or --file <change.json>
171
+ firedrill scenarios duplicate|archive|restore [id] Review a versioned change
172
+ firedrill scenarios definition [id] Read canonical editable scenario input
173
+ firedrill scenarios history [id] Page saved scenario revisions
174
+ firedrill scenarios paths [id] [path] [step] Inspect authored paths and mapped interfaces
175
+ firedrill scenarios world-events <id> Inspect the declared evolving-world schedule
176
+ firedrill scenarios request <key> Recover an original change without repeating it
177
+ firedrill scenarios draft-request <parent> --request-key <key> Read an original draft revision
178
+ firedrill scenarios execution-request <parent> --request-key <key> Read an original settings revision
179
+ firedrill scenarios withdraw-request <key> Cancel only an unsaved request
180
+ firedrill criteria requests Page your criterion-save history
181
+ firedrill criteria request <key> Inspect an original save without repeating it
182
+ firedrill criteria withdraw-request <key> Cancel only an unsaved criterion request
183
+ firedrill assess requests <campaign> <drill> Page your assessment request history
184
+ firedrill assess request <campaign> <drill> <key> Check an original assessment request
185
+ firedrill priority Choose proposals and assess their priority
186
+ firedrill priority create <turn> <proposal...> Assess an explicit saved selection
187
+ firedrill priority list <turn> Page priority assessment history
188
+ firedrill priority show <batch> Read the saved priority scores and scale
189
+ firedrill priority resume|forget --request-key <id> Recover intent or remove its local copy
190
+ firedrill priority recover-replies <batch> Check original saved replies, no reevaluation
191
+ firedrill priority requests <turn> Page your priority request history (--world)
192
+ firedrill priority request <turn> <key> Check an original priority request
193
+ firedrill priority withdraw-request <turn> <key> Cancel only an unaccepted priority request
194
+ firedrill assess withdraw-request <campaign> <drill> <key> Cancel before acceptance
195
+ firedrill tools Open the tools menu
196
+ firedrill tools show <version> Inspect saved package metadata
197
+ firedrill tools services <version> Search and page declared services
198
+ firedrill tools import Upload a private tool package
199
+ firedrill tools contribute <version> Open optional private contribution review
200
+ firedrill tools contributions [version] Page your contribution requests
201
+ firedrill tools contribution <request> Inspect the original contribution
202
+ firedrill tools contribution-withdraw <request> Withdraw with --approve-tool-change
203
+ firedrill tools instances --state all|active|closed|removed
204
+ firedrill tools runnable Saved versions and where each service runs (shared pool or isolated)
205
+ firedrill tools definitions [list|show|recipients|grants|share|share-request|withdraw-share-request|revoke|copy|copy-request|withdraw-copy-request|copy-status|withdraw-copy|dependents]
206
+ Share tool versions across your organization's workspaces and copy them privately
207
+ firedrill tools start Choose up to 32 tools with Space, then Enter; approve the estimate
208
+ firedrill tools start <version>... --seconds <n> [--service <key>]
209
+ Review saved versions and quote them; nothing starts until that estimate is approved
210
+ firedrill tools start --file selection.json Review an exact {duration_seconds, selections} file the same way
211
+ firedrill tools start --request-key <key> [--usage-quote <key> --approve-tool-change]
212
+ Continue a start, or approve the exact estimate it printed
213
+ firedrill tools sessions --state all|active|preparing|closed Page hosted tool sessions
214
+ firedrill tools session <session> [stop] Inspect a tool session, or stop it (--approve-tool-change)
215
+ firedrill tools session <session> replace [--request-key <key>] Review a stopped, never-approved session's tools again (--approve-tool-change)
216
+ firedrill tools session <session> renew --seconds <n> [--request-key <key> --usage-quote <quote-id>] [--approve-tool-change]
217
+ Review and approve an extension of its shared-pool tools
218
+ firedrill tools session <session> renewals|renewal <id>|cancel-renewal <id>|renewal-request <key>|withdraw-renewal <key>
219
+ Read renewals, close an unused review, or recover an original request
220
+ firedrill tools access <session> [--allow-writes] [--approve-tool-change]
221
+ Serve its tools to an agent on this machine (MCP + JSON)
222
+ firedrill tools state <instance> [checkpoint | edit <operation> --file <args.json> | operation <id> | snapshots]
223
+ Save or change a shared-pool tool's data (an edit needs --approve-tool-change); replay one only with its printed --request-key
224
+ firedrill tools state <instance> reset|fork <snapshot> --seconds <n> [--request-key <key> --usage-quote <key>] [--approve-tool-change]
225
+ Review, quote and (after approval of that exact estimate) start a reset or fork
226
+ firedrill tools state <instance> request|withdraw-request|clone-request|withdraw-clone <key>
227
+ Read back or explicitly withdraw an original state, reset or fork request
228
+ firedrill tools start-status <request> Read the original setup request
229
+ firedrill tools setups Search and page tool setups
230
+ firedrill tools setup <id> Inspect a setup's original status
231
+ firedrill tools setup-members <id> Inspect each original tool instance
232
+ firedrill tools setup-status <request> Recover an original multi-tool request
233
+ firedrill tools group-state <setup> Page coordinated checkpoints and recovery
234
+ firedrill tools capture-group <setup> Save supported state together
235
+ firedrill tools reset-group|fork-group <setup> Review all replacement instances
236
+ firedrill tools stop-setup <id> Cancel queued setup and stop its instances
237
+ firedrill tools stop [instance] Close connections and stop compute
238
+ firedrill tools remove [instance] Stop and move an instance to Removed history
239
+ firedrill tools removed Search and page removed instance history
240
+ firedrill tools removal [instance] Inspect original removal state
241
+ firedrill tools remove-status <request> Recover the original removal request
242
+ firedrill tools connect [instance] Select permissions and save private connection details
243
+ firedrill tools open [instance] Open the authenticated tool interface and history
244
+ firedrill tools connections [instance] Inspect scoped tool access
245
+ firedrill tools connection-request <instance> <request> Recover original issuance metadata
246
+ firedrill tools disconnect <instance> <connection> Revoke tool access
247
+ firedrill tools call Select an instance and submit an operation
248
+ firedrill tools activity <instance> Inspect retained history, --search/--outcome and saved --before/--anchor positions
249
+ firedrill tools download [instance] [command] [request|response] --into <file> Save original private bytes, --approve-download for automation
250
+ firedrill tools checkpoints [instance] Inspect saved tool data
251
+ firedrill tools data [instance] [checkpoint] [collection] [position] Inspect saved tables, schema or a record
252
+ firedrill tools datasets [checkpoint] Search saved tool dataset versions
253
+ firedrill tools dataset|dataset-document <version> Inspect metadata or private JSON
254
+ firedrill tools dataset-export [version] --into <file.json> Save original dataset bytes
255
+ firedrill tools dataset-save [checkpoint] [name] --file <edits.json> Save versioned edits
256
+ firedrill tools dataset-request <request> Recover the original save request
257
+ firedrill tools dataset-withdraw <version> Withdraw one saved version
258
+ firedrill tools checkpoint [instance] Save a private data checkpoint
259
+ firedrill tools reset|resume [instance] Start from saved starting/closing data
260
+ firedrill tools restore|fork [instance] [checkpoint] Restore or fork saved data
261
+ firedrill tools transitions [instance] Inspect data-operation history
262
+ firedrill tools transition <instance> <request> Read an original data operation
263
+ firedrill tools checkpoint-status <instance> <request> Read an original capture
264
+ firedrill datasets List saved world dataset versions
265
+ firedrill datasets show|document <version> Inspect a version or its JSON
266
+ firedrill datasets export [version] --into <file.json> Save original dataset bytes
267
+ firedrill datasets save [capture] [name] --file <edits.json> Save synthetic edits
268
+ firedrill datasets request <request> Recover the original save
269
+ firedrill datasets withdraw <version> Withdraw one version
270
+ firedrill datasets start-status <request> Read an original managed session opening request
271
+ firedrill sessions List this world's manual sessions
272
+ firedrill sessions show|close <id> Inspect history or request closure
273
+ firedrill sessions request|withdraw <key> Inspect or fence the original opening
274
+ firedrill sessions call Choose a session, connection and operation
275
+ firedrill sessions call <session> <connection> <operation> --file <request.json>
276
+ firedrill sessions connections [session] Browse its admitted connections
277
+ firedrill sessions requests [session] Browse manual request history
278
+ firedrill sessions request-show <session> <id> Inspect a saved request
279
+ firedrill sessions request-result <session> <key> Recover without resending
280
+ firedrill sessions captures [session] Browse captured database records
281
+ firedrill sessions capture [session] Capture current database records
282
+ firedrill sessions capture-show <session> <id> Inspect one capture
283
+ firedrill inspect data <capture> [collection] [row] Read prepared records
284
+ firedrill inspect capture <session> <capture> [collection] [row] Read session records
285
+ firedrill sessions capture-request <session> <key> Recover the original capture
286
+ firedrill sessions resets [session] Inspect reset history
287
+ firedrill sessions reset [session] Restore sealed starting data
288
+ firedrill sessions reset-request <session> <key> Recover the original reset
289
+ firedrill sessions voice [session] Read voice history and browser controls
290
+ firedrill sessions voice-request <session> <key> Recover the original audio action
291
+ firedrill sessions checkpoints [session] Search saved checkpoints
292
+ firedrill sessions checkpoint [session] [name] Save a database checkpoint
293
+ firedrill sessions restore [session] [checkpoint] Restore selected database state
294
+ firedrill sessions checkpoint-actions [session] Inspect save/restore history
295
+ firedrill sessions checkpoint-request <session> <key> Recover the original action
296
+ firedrill sessions coordinated-checkpoints [session] List saved world and agent state
297
+ firedrill sessions coordinated-checkpoint <id> Inspect a capture or original request
298
+ firedrill sessions save-world-and-agent <session> --name <name> Capture supported state
299
+ firedrill tools result <instance> <request> Read the original operation result
300
+ firedrill tools catalog Browse supported catalog tools
301
+ firedrill tools catalog --category <key> Filter the paged catalog
302
+ firedrill tools catalog-categories Browse available catalog categories
303
+ firedrill tools save [release] Save a catalog version without compute
304
+ firedrill tools versions List private saved versions
305
+ firedrill tools catalog-show [release] Inspect a service page; --page and --search available
306
+ firedrill tools catalog-operations [release] Choose a service or use --service in scripts
307
+ firedrill tools catalog-operation [release] Choose a service and operation interactively
308
+ firedrill tools catalog-setups [release] Inspect your paged catalog setup history
309
+ firedrill tools catalog-status <request> Recover the exact original catalog setup
310
+ firedrill tools catalog-withdraw <request> Withdraw an unfinished catalog setup
311
+ firedrill tools status|complete <request> Inspect or finish an original package request
312
+ firedrill tools requests --import-status <status> Filter package import history
313
+ firedrill configuration Review or change this world's variable mappings
314
+ firedrill configuration save Select saved values or use --file <metadata.json>
315
+ firedrill configuration vault Open Vault without printing secret values
316
+ firedrill recordings Inspect this world's recorded activity
317
+ firedrill recordings list|show|records [id] Page recordings or their observations
318
+ firedrill recordings upload|open Open upload, consent and review in the browser
319
+ firedrill recordings request <key> Inspect the original upload after an uncertain reply
320
+ firedrill configuration status|resume|withdraw|forget --request-key <id>
321
+ firedrill connections Review this world's dependency connections
322
+ firedrill connections show|save [id] Inspect or configure a dependency
323
+ firedrill connections activity [id] [selected|all] Inspect retained service receipts
324
+ firedrill connections preparation-decision <id> <digest> Read original decision acceptance
325
+ firedrill connections continue-preparation <id> --file <json> Continue the exact reviewed catalog cursor
326
+ firedrill configuration schema --json Read exact metadata input fields without signing in
327
+ firedrill connections schema --json Read dependency input fields without signing in
328
+ firedrill models View or change this world's scoped model access
329
+ firedrill models save Connect a saved runtime credential, with guided selection
330
+ firedrill models show|remove [id] Inspect or remove a model connection
331
+ firedrill models schema [remove] --json Read exact metadata input fields
332
+ firedrill models status|resume|withdraw|forget --request-key <id>
333
+ firedrill real-operations Inspect real-account requests for this world
334
+ firedrill real-operations show <id> Read an exact operation without resending it
335
+ firedrill real-operations review <id> Record an administrator's outcome review
336
+ firedrill real-operations recover|forget --request-key <id>
337
+ firedrill report [campaign-id] Inspect a saved simulation report
338
+ firedrill report <id> memory Inspect memory checkpoints (interactive or --json)
339
+ firedrill report <id> style Inspect selected prose measurements
340
+ firedrill report <id> checks Inspect recorded check outcomes
341
+ firedrill report <id> checks --method exact|semantic|both|unknown
342
+ firedrill report <id> checks <check> --status met|unmet|unconfirmed
343
+ firedrill report <id> scores [cohort-assessment] Inspect score distributions and drills
344
+ firedrill report <id> findings [scenario-revision] Read scenario outcomes and example drills
345
+ firedrill report <id> insights Read retained recommendations and follow-up links
346
+ firedrill report <id> export Create or inspect private JSONL reports
347
+ firedrill report <id> export download <export> --into report.jsonl
348
+ firedrill report <id> usage [drill] Inspect target-model relay usage
349
+ firedrill report <id> drill [drill] Inspect saved input/output and tool actions
350
+ firedrill report <id> drill <drill> requests [receipt] Inspect mapped service receipts
351
+ firedrill report <id> drill <drill> checks [position] Inspect original recorded checks
352
+ firedrill report <id> drill <drill> reassess Approve a separate new evaluation of retained evidence (no rerun)
353
+ firedrill report <id> drill <drill> reassessments|reassessment <id> [results]|reassessment-request <key>|cancel-reassessment <id>
354
+ firedrill report <id> drill <drill> coverage [messages|actions] Page evidence coverage
355
+ firedrill report <id> drill <drill> state [comparison] Inspect observed state changes
356
+ firedrill report <id> drill <drill> state <comparison> changes [change]
357
+ firedrill report <id> drill <drill> state <comparison> collections|events
358
+ firedrill report <id> drill <drill> state <comparison> causes <change>
359
+ firedrill report <id> drill <drill> state checkpoints [checkpoint]
360
+ firedrill report <id> drill <drill> state checkpoints <checkpoint> mutations
361
+ firedrill report <id> drill <drill> actions --service-request <receipt>
362
+ firedrill report <id> drill <drill> schedule Inspect scheduled changes and conditions
363
+ firedrill report <id> drill <drill> world-events Follow actual-time evolving world events
364
+ firedrill report <id> drill <drill> world-events controls|runtime
365
+ firedrill report <id> drill <drill> world-events pause|resume|cancel-future|revise-future
366
+ Guided review; automation uses --file --request-key --approve-change
367
+ firedrill report <id> drill <drill> world-events request <key>
368
+ firedrill report <id> drill --checks unmet Find drills with unmet recorded checks
369
+ firedrill report <id> drill --cohort <assessment> --score-bucket <index>
370
+ Find drills from one retained score interval
371
+ firedrill report <id> assignments Show current execution slots and drill links
372
+ firedrill report <id> activity Read recent completions and service activity
373
+ --search <service-or-operation>
374
+ firedrill report <id> composite Inspect frozen composite indices
375
+ firedrill report <id> compare [baseline] Compare saved simulation evidence
376
+ firedrill report <id> compare <baseline> semantic|memory|style <assessment>
377
+ --side both|current|baseline
378
+ firedrill report <id> compare <baseline> checks <check> --side baseline
379
+ firedrill report <id> compare <baseline> performance|usage <cohort> --side baseline
380
+ firedrill report <id> compare <baseline> composite <cohort> models|trials
381
+ --side both|current|baseline --status all|available|unavailable
382
+ --evaluator <version|unavailable>
383
+ firedrill report <id> assessments [drill] [assessment] Inspect evidence-based scores
384
+ firedrill report <id> assessment-status [drill] Inspect automatic assessment preparation
385
+ firedrill report <id> assessments <drill> <assessment> evidence Page used evidence
386
+ Drill list filters: --scenario <revision-id>, --status <execution-state>, --checks <outcome>
387
+ Check outcomes: met, unmet, unassessed, unavailable, missing. These are not semantic score thresholds.
388
+ Drill check filters: --status met|unmet|unassessed, --kind objective|semantic|both|unknown
389
+ Score filters: --score-bucket <index>, --assessment-status assessed|not-assessable|not-applicable
390
+ Score filters apply to a selected scores cohort. JSON output includes its exact interval indices.
391
+ firedrill criteria List saved evaluation criteria for a world
392
+ firedrill criteria save [id] Create a criterion or save an exact JSON revision
393
+ firedrill criteria show|history <id> Read a criterion or its immutable history
394
+ firedrill assess <campaign> [drill] Evaluate retained evidence without rerunning the agent
395
+ firedrill criteria resume|forget Inspect an original save or remove its local copy
396
+ firedrill assess resume|forget Recover a request or remove its local copy
397
+ firedrill changes Review agent source changes or propose one
398
+ firedrill changes <campaign> files List eligible baseline source files
399
+ firedrill changes <campaign> show|diff <change> Inspect a request or its actual diffs
400
+ firedrill changes <campaign> propose --file <change.json> Save a scoped proposal
401
+ firedrill changes <campaign> approve|accept <change> Approve drafting or save a reviewed candidate
402
+ firedrill changes <campaign> resume-draft|retry-save|dismiss <change>
403
+ firedrill changes recover|forget --request-key <id> Recover exact intent or remove its local copy
404
+ firedrill changes <campaign> copy <change> --into <new-folder> Download an isolated candidate
405
+ firedrill changes copy-resume --request-key <id> Resume the same candidate copy
406
+ firedrill changes copy-forget --request-key <id> Remove only private download/recovery files
407
+ firedrill changes <campaign> compare <change> Prepare frozen control and candidate worlds
408
+ firedrill changes <campaign> comparisons <change> Read comparison setup and history
409
+ firedrill changes <campaign> comparison-runs <change> <comparison> --side control|candidate
410
+ firedrill policies View optional company policies for an agent
411
+ firedrill policies upload Upload a private policy with --file and --name
412
+ firedrill policies paragraphs <id> Read a retained policy version
413
+ firedrill source inspect [path] Inspect local source without uploading it
414
+ firedrill source snapshot [path] Capture a private local source copy
415
+ firedrill sources use --agent <id> Choose source components for one application
416
+ firedrill sources options --agent <id> List paged connected sources for automation
417
+ firedrill sources status <key> --agent <id> Recover the original source selection
418
+ firedrill sources withdraw <key> --agent <id> Withdraw the original selection; automation uses --approve-change
419
+ firedrill connect [path] Capture and review source transfer before connecting
420
+ firedrill connect requests List cloud-retained source requests
421
+ --search <name-or-key> --status <state>
422
+ --after <returned-continuation>
423
+ firedrill connect status <key> Inspect a source connection
424
+ firedrill connect resume <key> Continue processing without repeating an upload
425
+ firedrill connect upload <key> Explicitly attempt the same approved archive upload
426
+ firedrill connect withdraw <key> Withdraw before source processing is accepted
427
+ firedrill runner enroll Register a local device identity, not execution permission
428
+ firedrill runner list List registered runners in this workspace
429
+ firedrill runner status <key> Inspect a saved runner receipt
430
+ firedrill runner connect <key> Maintain signed outbound presence only
431
+ firedrill runner revoke <key> Revoke this device or withdraw a delayed enrollment
432
+ firedrill runner runtime Inspect the pinned image and local container host
433
+ firedrill runner runtime install Download the pinned image after consent
434
+ firedrill runner runtime check Observe source-free container storage/isolation
435
+ firedrill runner runtime policy Export the exact local security profiles
436
+ firedrill runner runtime recover <key> Clean up the exact retained readiness request
437
+ firedrill runner authorize <key> Review local permission for the selected world
438
+ firedrill runner grants List this world's local permissions
439
+ firedrill runner grant status <key> Inspect an exact local permission request
440
+ firedrill runner grant revoke <key> Revoke or withdraw a local permission
441
+ firedrill runner jobs <device> Inspect saved local assignments
442
+ firedrill runner abandon <device> <job> Close an original claim before container handoff
443
+ firedrill runner claim status <device> <job> <key> Read exact ownership
444
+ firedrill runner claim withdraw <device> <job> <key> Withdraw exact ownership
445
+ firedrill runner cleanup <operation> Remove only the exact saved execution container
446
+ firedrill runner run-cleanup <device> <campaign> [--approve-change] Recover shared-service closing evidence, never launch
447
+ firedrill runner drill-cleanup <device> <assignment> [--approve-change] Recover verified original process-close facts after expiry
448
+ firedrill runner evidence sync <operation> Resume saved evidence transfer only
449
+ firedrill runner evidence forget <operation> Remove that operation’s private evidence bytes
450
+ firedrill runner evidence inspect <operation> Read private retention metadata only
451
+ firedrill runner evidence prune Remove expired synchronized evidence after cleanup
452
+ firedrill runner completion sync <operation> Submit or inspect saved closing facts
453
+ firedrill driver review [review-id] Inspect input scope, completion, actions and source references
454
+ firedrill context import Import a JSON recording for private review
455
+ firedrill context list|show <id> Inspect saved recordings
456
+ firedrill context accept <id> Use reviewed context in preparation
457
+ firedrill context discover <id> Request scenario drafts from accepted records
458
+ firedrill context withdraw <key> Withdraw the exact original upload with --approve-change
459
+ firedrill recipes list|save Reuse a private prepared world recipe
460
+ firedrill recipes show|use <id> Review its current inputs and open setup
461
+ firedrill recipes archive <id> Archive a bookmark, not its world or history
462
+ firedrill recipes request <key> Recover an original save in the selected world
463
+ firedrill simulation resume <id> Watch an original simulation (read-only; starts nothing)
464
+ firedrill simulation pause [id] Pause new drill scheduling, not active targets
465
+ firedrill simulation resume-scheduling [id] Resume scheduling the original plan
466
+ firedrill simulation cancel [id] Request cancellation and retained cleanup
467
+ firedrill simulation control-request <id> <pause|resume|cancel> <key> Inspect the original action
468
+ firedrill simulation continue-control|withdraw-control <id> <action> <key>
469
+ firedrill simulation requests Page saved setup requests (--after <id>)
470
+ firedrill simulation request status <key> Inspect exact setup acceptance
471
+ firedrill simulation request continue <key> Read the saved setup request back (never sent again)
472
+ firedrill simulation request withdraw <key> Fence setup before late acceptance
473
+
474
+ Directories: agents, worlds, scenarios, personas, evaluations, simulations
475
+ Options: --json, --page <number>, --search <text>, --workspace <id>, --world <id>, --no-browser
476
+ Accessible choices: --accessible, FIREDRILL_ACCESSIBLE=1, TERM=dumb or NO_COLOR use plain numbered prompts.
477
+ Piped/background commands never prompt. Secrets remain hidden or use their explicit stdin option.
478
+ Billing: subscription, top-up, builder and event-code changes open the secure web flow.
479
+ Payment offers and credit rates are server-owned. Opening a page does not confirm a payment.
480
+ Billing history: --status for purchases/invoices; --kind and --search for ledger.
481
+ Directory filters: --status <state>, --world <id> for world resources, --agent <id>
482
+ Source connection: --name <name>, --upload-source (explicit transfer consent for automation)
483
+ Source helper: firedrill source runtime install [--approve-download] or source runtime inspect
484
+ Runner enrollment: --name <name>, --enroll-device (explicit consent for automation)
485
+ Runtime download: --install-runtime (explicit image-download consent for automation)
486
+ Local permission: --operations validation,trial,world_session --approve-local
487
+ Optional permissions: --upload-evidence, --allow-vault-values, --allow-real-accounts, --allow-catalog-packages
488
+ Local ceilings: --minutes, --parallel, --cpu, --memory-mb. Permission does not start work.
489
+ Use runner abandon <device> <job> --world <id> to close an unstarted original claim, including after expiry. Container handoff requires cleanup instead.
490
+ Starting work from the older world commands is retired in this release, managed or on this device: validate, simulate,
491
+ simulation run, sessions open|resume|quote, runner execute, ci configure|setup-proposal and datasets start are refused before any request.
492
+ simulation resume <id> only watches an original simulation and hands its original claims to recovery; it starts nothing.
493
+ Retained history, reports, cancellation, cleanup and original-request recovery stay available. New simulations use plan, apply and run.
494
+ Capture playback: report <campaign> drill <drill> clips [clip-id] lists saved MP4 clips and opens the capture inspector. Clips use retained images, not new agent runs.
495
+ Evidence filters: report <campaign> drill <drill> evidence [artifact] --attempt <number> --step <id> --from-at <ISO-time> --to-at <ISO-time>.
496
+ Evidence times require Z or a UTC offset and filter when files were retained. Timeline accepts --attempt, --from-at and --to-at for observation times.
497
+ Criteria: --file <body.json>, --request-key <id>, --approve-criterion
498
+ Configuration: --file <metadata.json>, --request-key <id>, --approve-configuration
499
+ Configuration files contain variable names and saved-value references, never raw secret values.
500
+ Real-account history: --service <id>, --outcome <state>, --needs-review
501
+ Real-account review automation: --acknowledge-real-effects --acknowledge-duplicate-risk
502
+ These acknowledge actual account review. They do not authorize another provider request.
503
+ Saved evidence assessment: --file <rubric.json>, --request-key <id>, --approve-assessment
504
+ Evaluation requests retain a private local recovery copy until explicitly forgotten.
505
+ Source changes: --approve-change confirms the reviewed operation and private recovery retention.
506
+ Credit budgets: --approve-credits separately approves a displayed quote only when workspace policy requires it; execution approval remains separate.
507
+ Candidate acceptance in automation also requires --reviewed-draft <exact-sha256>.
508
+ Candidate source downloads need separate --approve-download consent in automation.
509
+ Proposals, drafting approval and diff acceptance are separate. Nothing applies edits or reruns an agent automatically.
510
+ Source transfer and evidence sharing require their separate permissions.
511
+ Logout recovery: --local-only removes this terminal's credentials without claiming cloud revocation.
512
+ `;
513
+ const LEGACY_LAUNCH_RETIRED = 'Starting work from the older world commands is retired in this release, managed or on this device; retained history, reports, cancellation, cleanup and original-request recovery stay available. Use firedrill plan, apply and run for new simulations. Nothing was sent.';
514
+ function identifier(value) {
515
+ if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value))
516
+ throw new CliError('identifier_required', 'Choose a saved item by its ID.', 2);
517
+ return value;
518
+ }
519
+ export async function main() {
520
+ const controller = new AbortController();
521
+ // Signals stop owned work first (the controller), then the credential
522
+ // companion closes with reason 'signal' in finally, after that work drained.
523
+ // A second signal does not wait for that: it SIGKILLs every process group the
524
+ // run still owns (detached, so the terminal's interrupt never reached them)
525
+ // and exits, instead of leaving them running after the CLI is gone.
526
+ let signalled = false, companion = null;
527
+ const interrupt = () => {
528
+ if (signalled) {
529
+ killOwnedGroups();
530
+ process.exit(130);
531
+ }
532
+ signalled = true;
533
+ controller.abort();
534
+ };
535
+ process.on('SIGINT', interrupt);
536
+ process.on('SIGTERM', interrupt);
537
+ process.on('SIGHUP', interrupt);
538
+ let json = process.argv.includes('--json');
539
+ try {
540
+ const parsed = parseArgs({ allowPositionals: true, strict: true, tokens: true, options: {
541
+ json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' }, 'no-browser': { type: 'boolean' },
542
+ organization: { type: 'string' },
543
+ 'local-only': { type: 'boolean' }, accessible: { type: 'boolean' }, state: { type: 'string' },
544
+ kind: { type: 'string' }, origin: { type: 'string' },
545
+ 'discard-evidence': { type: 'boolean' },
546
+ managed: { type: 'boolean' }, before: { type: 'string' },
547
+ local: { type: 'boolean' }, device: { type: 'string' }, run: { type: 'boolean' }, validate: { type: 'boolean' },
548
+ 'approve-real-startup': { type: 'boolean' }, 'acknowledge-external-effects': { type: 'boolean' },
549
+ 'approve-real-simulation': { type: 'boolean' }, 'acknowledge-shared-state': { type: 'boolean' }, 'approve-real-session': { type: 'boolean' },
550
+ 'all-scenarios': { type: 'boolean' }, scenario: { type: 'string', multiple: true }, repetitions: { type: 'string' },
551
+ seconds: { type: 'string' }, assess: { type: 'boolean' }, 'record-browser': { type: 'boolean' }, 'record-voice': { type: 'boolean' },
552
+ 'record-desktop': { type: 'boolean' }, 'acknowledge-desktop-screen-content': { type: 'boolean' },
553
+ 'approve-agent-input': { type: 'boolean' },
554
+ 'contract-sha256': { type: 'string' }, baseline: { type: 'string' }, view: { type: 'string' },
555
+ change: { type: 'string' }, 'page-size': { type: 'string' },
556
+ 'application-checkpoint': { type: 'string' }, 'acknowledge-fresh-world-state': { type: 'boolean' },
557
+ 'share-application-checkpoint-with-device': { type: 'boolean' },
558
+ 'coordinated-checkpoint': { type: 'string' }, 'acknowledge-supported-checkpoint-scope': { type: 'boolean' },
559
+ 'share-coordinated-checkpoint-with-device': { type: 'boolean' },
560
+ 'acknowledge-partial-state': { type: 'boolean' },
561
+ checkpoint: { type: 'string' }, checks: { type: 'string' }, status: { type: 'string' }, 'service-request': { type: 'string' },
562
+ 'score-bucket': { type: 'string' }, 'assessment-status': { type: 'string' },
563
+ cohort: { type: 'string' }, method: { type: 'string' }, evaluator: { type: 'string' },
564
+ agent: { type: 'string' }, file: { type: 'string' }, 'policy-id': { type: 'string' },
565
+ 'basis-version': { type: 'string' }, 'request-key': { type: 'string' }, 'approve-policy': { type: 'boolean' },
566
+ 'terms-version': { type: 'string' }, 'privacy-version': { type: 'string' }, 'declared-agent-name': { type: 'string' },
567
+ 'approve-criterion': { type: 'boolean' }, 'approve-assessment': { type: 'boolean' }, 'approve-credits': { type: 'boolean' },
568
+ 'approve-change': { type: 'boolean' }, 'reviewed-draft': { type: 'string' },
569
+ 'approve-configuration': { type: 'boolean' },
570
+ 'value-stdin': { type: 'boolean' },
571
+ 'approve-dataset': { type: 'boolean' }, dataset: { type: 'string' }, 'share-dataset-with-device': { type: 'boolean' },
572
+ 'share-checkpoint-with-device': { type: 'boolean' },
573
+ 'approve-tool-upload': { type: 'boolean' }, 'approve-tool-change': { type: 'boolean' },
574
+ 'usage-quote': { type: 'string' },
575
+ service: { type: 'string' }, outcome: { type: 'string' }, 'needs-review': { type: 'boolean' },
576
+ 'import-status': { type: 'string' },
577
+ 'acknowledge-real-effects': { type: 'boolean' }, 'acknowledge-duplicate-risk': { type: 'boolean' },
578
+ 'additional-scenario': { type: 'string', multiple: true }, side: { type: 'string' },
579
+ into: { type: 'string' }, 'approve-download': { type: 'boolean' },
580
+ attempt: { type: 'string' }, step: { type: 'string' }, 'from-at': { type: 'string' }, 'to-at': { type: 'string' },
581
+ 'upload-source': { type: 'boolean' }, name: { type: 'string' },
582
+ 'enroll-device': { type: 'boolean' },
583
+ parameters: { type: 'string' }, 'remove-parameters': { type: 'boolean' },
584
+ 'install-runtime': { type: 'boolean' },
585
+ 'approve-local': { type: 'boolean' }, operations: { type: 'string' }, minutes: { type: 'string' },
586
+ grant: { type: 'string' }, 'execute-local': { type: 'boolean' },
587
+ parallel: { type: 'string' }, cpu: { type: 'string' }, 'memory-mb': { type: 'string' },
588
+ 'upload-evidence': { type: 'boolean' }, 'allow-vault-values': { type: 'boolean' }, 'allow-real-accounts': { type: 'boolean' }, 'allow-catalog-packages': { type: 'boolean' },
589
+ 'allow-writes': { type: 'boolean' },
590
+ page: { type: 'string' }, after: { type: 'string' }, anchor: { type: 'string' }, search: { type: 'string' }, category: { type: 'string' }, workspace: { type: 'string' }, world: { type: 'string' },
591
+ from: { type: 'string' }, to: { type: 'string' },
592
+ trigger: { type: 'string' }, reference: { type: 'string' }, records: { type: 'string' },
593
+ 'session-only': { type: 'boolean' }, persistent: { type: 'boolean' }, open: { type: 'boolean' }, plan: { type: 'string' }, descriptor: { type: 'string' }, approve: { type: 'boolean' },
594
+ 'expected-revision': { type: 'string' }, request: { type: 'string' },
595
+ 'world-revision': { type: 'string' }, 'descriptor-revision': { type: 'string' }, archived: { type: 'boolean' }, runner: { type: 'string' },
596
+ follow: { type: 'boolean' }, campaign: { type: 'string' }, cancel: { type: 'boolean' },
597
+ } });
598
+ json = parsed.values.json === true;
599
+ configurePrompts({ accessible: parsed.values.accessible === true, interactive: !json });
600
+ delete parsed.values.accessible;
601
+ // Credential mode is selected once per process, before any credential access.
602
+ // --session-only/--persistent apply to every command; only init --session-only
603
+ // and run may make this process the credential companion.
604
+ const sessionOnly = parsed.values['session-only'] === true, persistent = parsed.values.persistent === true;
605
+ delete parsed.values['session-only'];
606
+ delete parsed.values.persistent;
607
+ const explicitOpen = parsed.values.open === true;
608
+ const helpOnly = parsed.values.help === true || !parsed.positionals.length || parsed.positionals[0] === 'help';
609
+ // Local-only commands never touch credentials, the state directory or a companion.
610
+ const [first, second] = parsed.positionals;
611
+ const localOnly = first === 'completion' || (first === 'signup' && second === 'guide')
612
+ || (first === 'tools' && (second === 'init' || second === 'validate'));
613
+ const credentialMode = helpOnly || localOnly ? null : await configureCredentialMode({ sessionOnly, persistent, signal: controller.signal,
614
+ companion: first === 'init' ? 'if-session' : first === 'run' ? 'if-absent' : 'never' });
615
+ // A failed companion start closes with 'exit' and must surface its own error
616
+ // code, not a cancellation; only a real shutdown (logout, signal, lost lock) aborts.
617
+ if (credentialMode?.companion)
618
+ companion = await startCredentialCompanion({ onShutdown: reason => { if (reason !== 'exit')
619
+ controller.abort(); } });
620
+ const args = parsed.positionals;
621
+ const evidenceFlags = new Set(['attempt', 'step', 'from-at', 'to-at']);
622
+ const selectedEvidenceFlags = parsed.tokens.filter(token => token.kind === 'option' && evidenceFlags.has(token.name));
623
+ if (new Set(selectedEvidenceFlags.map(token => token.kind === 'option' ? token.name : '')).size !== selectedEvidenceFlags.length)
624
+ throw new CliError('arguments_invalid', 'Provide each evidence filter once. Repeated filters do not select a different download.', 2);
625
+ if (parsed.values['approve-credits'] !== undefined && !(args[0] === 'apply'
626
+ || (args[0] === 'tools' && NATIVE_TOOL_ACTIONS.has(args[1]))
627
+ || (args[0] === 'report' && reassessmentShape(args.slice(1)))))
628
+ throw new CliError('arguments_invalid', 'Use --approve-credits only with native plan apply, tool admission or drill reassessment.', 2);
629
+ const driverFlags = new Set(['contract-sha256', 'baseline', 'view', 'change', 'page-size']);
630
+ if ([...driverFlags].some(key => parsed.values[key] !== undefined) && args[0] !== 'driver')
631
+ throw new CliError('arguments_invalid', 'Use driver review for recipe digest, baseline, view and change filters.', 2);
632
+ if (args[0] === 'driver') {
633
+ const selectors = new Set([...driverFlags, 'page', 'search', 'world', 'workspace']);
634
+ const supplied = parsed.tokens.filter(token => token.kind === 'option' && selectors.has(token.name));
635
+ if (new Set(supplied.map(token => token.kind === 'option' ? token.name : '')).size !== supplied.length)
636
+ throw new CliError('arguments_invalid', 'Provide each driver review selection and filter once.', 2);
637
+ }
638
+ if (parsed.values.category !== undefined && !(args[0] === 'tools' && args[1] === 'catalog'))
639
+ throw new CliError('arguments_invalid', 'Use --category only with tools catalog.', 2);
640
+ if ((parsed.values.from !== undefined || parsed.values.to !== undefined)
641
+ && !(args[0] === 'billing' && ['usage', 'credit-ledger'].includes(args[1])))
642
+ throw new CliError('arguments_invalid', 'Use --from and --to only with billing usage or billing credit-ledger history.', 2);
643
+ if ([parsed.values.attempt, parsed.values.step, parsed.values['from-at'], parsed.values['to-at']].some(value => value !== undefined)
644
+ && !(args[0] === 'report' && ['drill', 'trial'].includes(args[2]) && args[3] && ['evidence', 'timeline'].includes(args[4])))
645
+ throw new CliError('arguments_invalid', 'Use --attempt, --step, --from-at and --to-at only with report <campaign> drill <drill> evidence or timeline.', 2);
646
+ const output = (value, readable) => process.stdout.write(json ? JSON.stringify({ type: 'result', data: value }) + '\n' : readable + '\n');
647
+ const worldEvidenceOutput = async (value) => {
648
+ controller.signal.throwIfAborted();
649
+ const text = json ? JSON.stringify({ type: 'local_world_evidence_command', data: value })
650
+ : `Command ${value.command_id}: ${value.state === 'synchronized' ? 'evidence synchronized' : 'evidence unavailable (' + value.code + ')'}.`;
651
+ if (!process.stdout.write(text + '\n'))
652
+ await once(process.stdout, 'drain', { signal: controller.signal });
653
+ };
654
+ // Hosted tool session commands check their own flags (tools sessions takes --state).
655
+ if (parsed.values.state !== undefined && !(args[0] === 'tools' && (args[1] === 'instances' || NATIVE_TOOL_ACTIONS.has(args[1]))))
656
+ throw new CliError('arguments_invalid', 'Use --state only with tools instances (all, active, closed or removed) or tools sessions.', 2);
657
+ if (!parsed.values.help && (args[0] === 'docs' || args[0] === 'support' || args[0] === 'agents' && args[1] === 'new')) {
658
+ const intake = args[0] === 'agents';
659
+ if (args.length > (intake ? 3 : 1) || intake && args[2] && !['github', 'upload'].includes(args[2])
660
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser'].includes(key)))
661
+ throw new CliError('arguments_invalid', 'Use agents new [github|upload], docs or support, with optional --workspace, --json and --no-browser.', 2);
662
+ const context = await selectedWorkspace({ workspace: parsed.values.workspace, interactive: !json && !!process.stdin.isTTY && !!process.stderr.isTTY,
663
+ signal: controller.signal, openBrowser: !parsed.values['no-browser'] });
664
+ const url = new URL(intake ? '/new' : '/agents', APP_ORIGIN);
665
+ url.searchParams.set('workspace', context.workspace);
666
+ if (intake)
667
+ url.searchParams.set('source', args[2] === 'upload' ? 'upload' : 'repository');
668
+ if (!intake)
669
+ url.searchParams.set('help', '1');
670
+ const opened = !json && !parsed.values['no-browser'] ? await openBrowser(url.href) : false;
671
+ output({ type: 'browser_handoff', url: url.href, opened, workspace_id: context.workspace, source_transferred: false }, `${opened ? 'Opened' : 'Open'} ${intake ? 'agent connection' : 'workspace help'} in the browser.\n${url.href}`);
672
+ return;
673
+ }
674
+ if (parsed.values.method !== undefined && args[0] !== 'report')
675
+ throw new CliError('arguments_invalid', 'Use --method with report recorded-check group lists.', 2);
676
+ if (parsed.values.evaluator !== undefined && args[0] !== 'report')
677
+ throw new CliError('arguments_invalid', 'Use --evaluator with composite comparison drills.', 2);
678
+ if (parsed.values.side !== undefined && !['report', 'changes'].includes(args[0]))
679
+ throw new CliError('arguments_invalid', 'Use --side with report comparison observations or source-change comparisons.', 2);
680
+ if (parsed.values.records !== undefined && !['agents', 'worlds'].includes(args[0]))
681
+ throw new CliError('arguments_invalid', 'Use --records only to list active or archived agents/worlds.', 2);
682
+ if (parsed.values['service-request'] !== undefined && args[0] !== 'report')
683
+ throw new CliError('arguments_invalid', 'Use --service-request with report drill action lists.', 2);
684
+ if (args[0] !== 'ci' && (parsed.values.trigger !== undefined || parsed.values.reference !== undefined))
685
+ throw new CliError('arguments_invalid', 'Use --trigger and --reference with firedrill ci runs.', 2);
686
+ if (parsed.values.cohort !== undefined && args[0] !== 'report')
687
+ throw new CliError('arguments_invalid', 'Use --cohort with firedrill report <campaign> drill or export create.', 2);
688
+ if ((parsed.values['score-bucket'] !== undefined || parsed.values['assessment-status'] !== undefined) && args[0] !== 'report') {
689
+ throw new CliError('arguments_invalid', 'Use score filters with firedrill report <campaign> scores <cohort-assessment>.', 2);
690
+ }
691
+ if (parsed.values.checks !== undefined && args[0] !== 'report')
692
+ throw new CliError('arguments_invalid', 'Use --checks with firedrill report drill lists.', 2);
693
+ if (parsed.values.status !== undefined && !['report', 'billing', 'ci', 'vault'].includes(args[0]) && !kinds.has(args[0])
694
+ && !(args[0] === 'connections' && args[1] === 'activity')
695
+ && !(args[0] === 'connect' && args[1] === 'requests')
696
+ && !(['criteria', 'assess', 'priority'].includes(args[0]) && args[1] === 'requests'))
697
+ throw new CliError('arguments_invalid', 'Use --status with a supported saved-resource or request history.', 2);
698
+ if (parsed.values.origin !== undefined && args[0] !== 'report')
699
+ throw new CliError('arguments_invalid', 'Use --origin with report assessment history.', 2);
700
+ if (parsed.values.kind !== undefined && !['billing', 'report', 'context', 'library', 'world'].includes(args[0])
701
+ && !(args[0] === 'scenarios' && args[1] === 'world-events') && !(args[0] === 'tools' && args[1] === TOOL_REUSE_VERB))
702
+ throw new CliError('arguments_invalid', 'Use --kind with billing history, report evidence, recording imports, scenario world events or reuse dependents.', 2);
703
+ if (parsed.values['discard-evidence'] !== undefined && !(args[0] === 'runner' && args[1] === 'evidence' && args[2] === 'forget')) {
704
+ throw new CliError('arguments_invalid', 'Use --discard-evidence only with runner evidence forget <operation>.', 2);
705
+ }
706
+ if ((parsed.values['approve-real-startup'] !== undefined || parsed.values['acknowledge-external-effects'] !== undefined)
707
+ && !(['simulate', 'validate'].includes(args[0]) || args[0] === 'simulation' && args[1] === 'run')) {
708
+ throw new CliError('arguments_invalid', 'Use startup approvals only with validate or simulate.', 2);
709
+ }
710
+ if (parsed.values['approve-real-simulation'] !== undefined
711
+ && !(args[0] === 'simulate' || args[0] === 'simulation' && args[1] === 'run')) {
712
+ throw new CliError('arguments_invalid', 'Use real-simulation approvals only with simulate.', 2);
713
+ }
714
+ if (parsed.values['approve-real-session'] !== undefined && !(args[0] === 'sessions' && args[1] === 'open')) {
715
+ throw new CliError('arguments_invalid', 'Use --approve-real-session only with sessions open.', 2);
716
+ }
717
+ if (parsed.values['acknowledge-shared-state'] !== undefined && !(args[0] === 'simulate'
718
+ || args[0] === 'simulation' && args[1] === 'run' || args[0] === 'sessions' && args[1] === 'open')) {
719
+ throw new CliError('arguments_invalid', 'Acknowledge shared state only for a simulation or a new manual session.', 2);
720
+ }
721
+ if (parsed.values.help || !args.length || args[0] === 'help') {
722
+ if (args[0] === 'help' && (args.length > 2 || args[1] !== undefined && args[1] !== 'advanced'
723
+ || Object.keys(parsed.values).some(key => !['json', 'help'].includes(key))))
724
+ throw new CliError('arguments_invalid', 'Use firedrill help for the common flow, or firedrill help advanced for additional commands.', 2);
725
+ const advanced = args[0] === 'help' && args[1] === 'advanced';
726
+ output({ commands: advanced ? commandNames : COMMON_COMMANDS, advanced }, advanced ? ADVANCED_HELP : COMMON_HELP);
727
+ return;
728
+ }
729
+ // Retired launch entrypoints (FIR-504), matching root's release gates (managed target, CI and assistant
730
+ // launches and legacy local execution are off): refused before any request, managed or on this device.
731
+ // Retained reads, reports, cancellation, cleanup, original-request recovery and a watch-only
732
+ // `simulation resume` stay available; new simulations use plan, apply and run.
733
+ if (['validate', 'simulate'].includes(args[0]) || args[0] === 'simulation' && args[1] === 'run'
734
+ || args[0] === 'sessions' && ['open', 'resume', 'quote'].includes(args[1]) || args[0] === 'runner' && args[1] === 'execute'
735
+ || args[0] === 'ci' && ['configure', 'setup-proposal'].includes(args[1]) || args[0] === 'datasets' && args[1] === 'start'
736
+ || parsed.values.managed !== undefined && !(args[0] === 'simulation' && args[1] === 'resume')) {
737
+ throw new CliError('legacy_launch_retired', LEGACY_LAUNCH_RETIRED, 2);
738
+ }
739
+ const emit = (type, data, readable) => {
740
+ if (json)
741
+ process.stdout.write(JSON.stringify({ type, data }) + '\n');
742
+ else if (readable)
743
+ process.stdout.write(readable.split('\n').map(terminalText).join('\n') + '\n');
744
+ };
745
+ if (args[0] === 'init') {
746
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'open'].includes(key)))
747
+ throw new CliError('arguments_invalid', 'Use firedrill init with optional --session-only or --persistent, --workspace <id>, --no-browser, --open and --json.', 2);
748
+ const initialization = await initCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
749
+ openBrowser: !parsed.values['no-browser'], explicitOpen, mode: credentialMode.mode }, emit);
750
+ if (companion) {
751
+ // Session-only credentials live only in this process: keep serving until
752
+ // a signal or logout (companion shutdown) stops it.
753
+ emit('companion_running', { endpoint_ready: true, mode: 'session', initialization_state: initialization.state,
754
+ initialization_complete: initialization.state === 'ready',
755
+ command_phase_complete: true,
756
+ workspace_id: initialization.state === 'ready' ? initialization.workspace_id : null,
757
+ process_lifetime: 'until_logout_or_signal',
758
+ next_command: initialization.state === 'workspace_pending' ? 'firedrill workspace list --session-only --json' : null }, 'Session-only companion running. Keep this terminal open; other firedrill commands use it. Stop it with Ctrl-C or firedrill logout.');
759
+ if (!controller.signal.aborted)
760
+ await once(controller.signal, 'abort');
761
+ emit('companion_stopping', { reason: signalled ? 'signal' : 'shutdown' }, 'Stopping the companion. In-memory credentials are erased on close.');
762
+ }
763
+ return;
764
+ }
765
+ if (args[0] === 'status') {
766
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'plan'].includes(key)))
767
+ throw new CliError('arguments_invalid', 'Use firedrill status with optional --plan <path|-> and --json.', 2);
768
+ await statusCommand(args.slice(1), { json, signal: controller.signal, plan: parsed.values.plan }, emit);
769
+ return;
770
+ }
771
+ if (args[0] === 'register') {
772
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'descriptor', 'name', 'agent', 'expected-revision', 'request', 'approve-change', 'declared-agent-name'].includes(key)))
773
+ throw new CliError('arguments_invalid', 'Use register with --descriptor, --name or --agent and --expected-revision, and optional --request, --workspace and --json; or register request|withdraw-request <request-key>.', 2);
774
+ await registerCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
775
+ openBrowser: !parsed.values['no-browser'], descriptor: parsed.values.descriptor, name: parsed.values.name, agent: parsed.values.agent,
776
+ expectedRevision: parsed.values['expected-revision'], request: parsed.values.request, approve: parsed.values['approve-change'],
777
+ declaredAgentName: parsed.values['declared-agent-name'] }, emit);
778
+ return;
779
+ }
780
+ if (args[0] === 'descriptors') {
781
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'agent', 'descriptor', 'page', 'approve-change'].includes(key)))
782
+ throw new CliError('arguments_invalid', 'Use firedrill descriptors --agent <id> with optional --page <n> or --descriptor <id>, or descriptors request|withdraw-request <request-key> --agent <id>.', 2);
783
+ await descriptorsCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
784
+ openBrowser: !parsed.values['no-browser'], agent: parsed.values.agent, descriptor: parsed.values.descriptor, page: parsed.values.page,
785
+ approve: parsed.values['approve-change'] }, emit);
786
+ return;
787
+ }
788
+ if (args[0] === 'plan' || args[0] === 'apply') {
789
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser',
790
+ ...(args[0] === 'plan' ? ['world', 'world-revision', 'request'] : ['approve', 'approve-credits', 'request', 'follow', 'cancel'])].includes(key)))
791
+ throw new CliError('arguments_invalid', 'Use firedrill plan --world <world-id> [--world-revision <id>], plan show <plan-id>, firedrill apply <plan-id> --approve [--request <key>] [--follow], or firedrill apply <plan-id> --request <key> --cancel.', 2);
792
+ await (args[0] === 'plan' ? planCommand : applyCommand)(args.slice(1), { json, signal: controller.signal,
793
+ workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'], approve: parsed.values.approve === true, approveCredits: parsed.values['approve-credits'],
794
+ world: parsed.values.world, worldRevision: parsed.values['world-revision'], request: parsed.values.request,
795
+ follow: parsed.values.follow === true, cancel: parsed.values.cancel === true }, emit);
796
+ return;
797
+ }
798
+ if (args[0] === 'library' || args[0] === 'world') {
799
+ const allowed = args[0] === 'library'
800
+ ? ['json', 'help', 'workspace', 'no-browser', 'kind', 'file', 'search', 'archived', 'page', 'expected-revision', 'request', 'approve-change',
801
+ 'parameters', 'remove-parameters']
802
+ : ['json', 'help', 'workspace', 'no-browser', 'agent', 'file', 'world', 'descriptor-revision', 'page', 'expected-revision', 'request', 'approve-change',
803
+ 'kind', 'search'];
804
+ if (Object.keys(parsed.values).some(key => !allowed.includes(key)))
805
+ throw new CliError('arguments_invalid', `Use firedrill ${args[0]} with only: ${allowed.filter(key => key !== 'help').map(key => '--' + key).join(', ')}.`, 2);
806
+ await (args[0] === 'library' ? libraryCommand : worldCommand)(args.slice(1), { json, signal: controller.signal,
807
+ workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'], kind: parsed.values.kind, file: parsed.values.file,
808
+ search: parsed.values.search, archived: parsed.values.archived, page: parsed.values.page,
809
+ expectedRevision: parsed.values['expected-revision'], request: parsed.values.request, agent: parsed.values.agent,
810
+ world: parsed.values.world, descriptorRevision: parsed.values['descriptor-revision'], approve: parsed.values['approve-change'],
811
+ parameters: parsed.values.parameters, removeParameters: parsed.values['remove-parameters'] }, emit);
812
+ return;
813
+ }
814
+ if (args[0] === 'run') {
815
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'open', 'session-only', 'persistent', 'plan', 'descriptor', 'approve', 'runner', 'campaign', 'enroll-device'].includes(key)))
816
+ throw new CliError('arguments_invalid', 'Use firedrill run --plan <path|-> --descriptor <path|-> --campaign <id> with optional --approve, --runner, --workspace and --json (and --enroll-device in session-only mode).', 2);
817
+ await runCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
818
+ openBrowser: !parsed.values['no-browser'], explicitOpen, plan: parsed.values.plan, descriptor: parsed.values.descriptor,
819
+ approve: parsed.values.approve === true, runner: parsed.values.runner, campaign: parsed.values.campaign,
820
+ enrollDevice: parsed.values['enroll-device'] === true }, emit);
821
+ return;
822
+ }
823
+ if (args[0] === 'watch') {
824
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'follow'].includes(key)))
825
+ throw new CliError('arguments_invalid', 'Use firedrill watch <campaign> with optional --follow, --workspace and --json.', 2);
826
+ await watchCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
827
+ openBrowser: !parsed.values['no-browser'], follow: parsed.values.follow === true }, emit);
828
+ return;
829
+ }
830
+ if (args[0] === 'driver') {
831
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'contract-sha256', 'baseline',
832
+ 'view', 'change', 'page', 'page-size', 'search'].includes(key)))
833
+ throw new CliError('arguments_invalid', 'Use driver review [review-id] with scope, recipe and page filters only.', 2);
834
+ await driverReviewCommand(args.slice(1), { json, world: parsed.values.world, workspace: parsed.values.workspace,
835
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, contract: parsed.values['contract-sha256'],
836
+ baseline: parsed.values.baseline, view: parsed.values.view, change: parsed.values.change, page: parsed.values.page,
837
+ pageSize: parsed.values['page-size'], search: parsed.values.search }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
838
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
839
+ return;
840
+ }
841
+ if (['agents', 'worlds'].includes(args[0]) && ['archive', 'restore', 'remove', 'requests', 'request', 'resume', 'withdraw'].includes(args[1])) {
842
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'status', 'request-key', 'approve-change'].includes(key)))
843
+ throw new CliError('arguments_invalid', 'Use the exact record, original request or bounded history options.', 2);
844
+ await resourceLifecycle(args[0], args.slice(1), { json, workspace: parsed.values.workspace,
845
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search, status: parsed.values.status, request: parsed.values['request-key'],
846
+ approve: parsed.values['approve-change'], signal: controller.signal }, output);
847
+ return;
848
+ }
849
+ if (args[0] === 'completion') {
850
+ if (args.length !== 2 || Object.keys(parsed.values).some(key => !['json', 'help'].includes(key)))
851
+ throw new CliError('arguments_invalid', 'Use completion bash, zsh or fish without account or change options.', 2);
852
+ const script = completionScript(args[1]);
853
+ output({ shell: args[1], script }, script);
854
+ return;
855
+ }
856
+ if (args[0] === 'recordings') {
857
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search'].includes(key)))
858
+ throw new CliError('arguments_invalid', 'Use recording scope, page/search or browser handoff options only.', 2);
859
+ await recordingsCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
860
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search, signal: controller.signal }, output);
861
+ return;
862
+ }
863
+ if (args[0] === 'values') {
864
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'file', 'approve-change', 'value-stdin'].includes(key)))
865
+ throw new CliError('arguments_invalid', 'Use local value metadata, scope, hidden input and explicit change approval only.', 2);
866
+ await localValuesCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
867
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search, file: parsed.values.file,
868
+ approve: parsed.values['approve-change'], valueStdin: parsed.values['value-stdin'], signal: controller.signal }, output);
869
+ return;
870
+ }
871
+ if (args[0] === 'vault') {
872
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'status', 'file', 'request-key', 'approve-change', 'value-stdin'].includes(key)))
873
+ throw new CliError('arguments_invalid', 'Use Vault scope, metadata, original-request and hidden-value input options only.', 2);
874
+ await vaultCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
875
+ page: parsed.values.page, search: parsed.values.search, status: parsed.values.status, file: parsed.values.file,
876
+ request: parsed.values['request-key'], approve: parsed.values['approve-change'], valueStdin: parsed.values['value-stdin'], signal: controller.signal }, output);
877
+ return;
878
+ }
879
+ if (args[0] === 'settings' || args[0] === 'team') {
880
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'file', 'request-key', 'approve-change'].includes(key)))
881
+ throw new CliError('arguments_invalid', 'Use workspace, page/search and explicit reviewed-change options for administration.', 2);
882
+ const command = args[0] === 'settings' ? settingsCommand : teamCommand;
883
+ await command(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
884
+ page: parsed.values.page, search: parsed.values.search, file: parsed.values.file, request: parsed.values['request-key'],
885
+ approve: parsed.values['approve-change'], signal: controller.signal }, (value, readable) => output(value, readable.split('\n').map(terminalText).join('\n')));
886
+ return;
887
+ }
888
+ if (args[0] === 'recipes') {
889
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'name', 'page', 'search', 'request-key', 'approve-change'].includes(key)))
890
+ throw new CliError('arguments_invalid', 'Use recipe scope, name, bounded history or reviewed change options.', 2);
891
+ await worldRecipes(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
892
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, name: parsed.values.name, page: parsed.values.page,
893
+ search: parsed.values.search, request: parsed.values['request-key'], approve: parsed.values['approve-change'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
894
+ return;
895
+ }
896
+ if (args[0] === 'context') {
897
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'file', 'kind', 'approve-change', 'page', 'search', 'request-key'].includes(key)))
898
+ throw new CliError('arguments_invalid', 'Use context scope, import format, file, paging or reviewed change options only.', 2);
899
+ await recordedContextCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
900
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, file: parsed.values.file, format: parsed.values.kind,
901
+ approve: parsed.values['approve-change'], page: parsed.values.page, search: parsed.values.search, request: parsed.values['request-key'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
902
+ return;
903
+ }
904
+ if (args[0] === 'scenarios' && isScenarioDiscoveryAction(args[1])) {
905
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'file', 'approve-change', 'page', 'search', 'request-key', 'kind'].includes(key)))
906
+ throw new CliError('arguments_invalid', 'Use scenario discovery, paging, original request or reviewed change options only.', 2);
907
+ await scenarioDiscoveryCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
908
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, file: parsed.values.file,
909
+ approve: parsed.values['approve-change'], page: parsed.values.page, search: parsed.values.search, request: parsed.values['request-key'], kind: parsed.values.kind }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
910
+ return;
911
+ }
912
+ if (args[0] === 'scenarios' && ['create', 'edit', 'duplicate', 'archive', 'restore', 'definition', 'history'].includes(args[1])) {
913
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'file', 'approve-change', 'page', 'search'].includes(key)))
914
+ throw new CliError('arguments_invalid', 'Use scenario scope, reviewed input, explicit change approval or history paging only.', 2);
915
+ await scenarioAuthoring(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
916
+ openBrowser: !parsed.values['no-browser'], file: parsed.values.file, approve: parsed.values['approve-change'],
917
+ page: parsed.values.page, search: parsed.values.search, signal: controller.signal }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
918
+ return;
919
+ }
920
+ if (args[0] === 'scenarios' && args[1] === 'paths') {
921
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'basis-version'].includes(key)))
922
+ throw new CliError('arguments_invalid', 'Use scenario scope, page/search and an optional original definition digest only.', 2);
923
+ await scenarioPaths(args.slice(2), { json, workspace: parsed.values.workspace, world: parsed.values.world,
924
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search,
925
+ definition: parsed.values['basis-version'], signal: controller.signal }, output);
926
+ return;
927
+ }
928
+ if (args[0] === 'scenarios' && ['requests', 'request', 'withdraw-request'].includes(args[1])) {
929
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'status', 'approve-change'].includes(key)))
930
+ throw new CliError('arguments_invalid', 'Use scenario request scope, history filters and explicit withdrawal consent only.', 2);
931
+ await scenarioRequests(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
932
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search,
933
+ status: parsed.values.status, approve: parsed.values['approve-change'], signal: controller.signal }, output);
934
+ return;
935
+ }
936
+ if (args[0] === 'criteria' && ['requests', 'request', 'withdraw-request'].includes(args[1])) {
937
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'status', 'approve-change'].includes(key)))
938
+ throw new CliError('arguments_invalid', 'Use criterion request scope, history filters and explicit withdrawal consent only.', 2);
939
+ await revisionRequests('evaluation', args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
940
+ openBrowser: !parsed.values['no-browser'], page: parsed.values.page, search: parsed.values.search,
941
+ status: parsed.values.status, approve: parsed.values['approve-change'], signal: controller.signal }, output);
942
+ return;
943
+ }
944
+ if (parsed.values['import-status'] !== undefined && !(args[0] === 'tools' && args[1] === 'requests')) {
945
+ throw new CliError('arguments_invalid', 'Use --import-status only with firedrill tools requests.', 2);
946
+ }
947
+ if (args[0] === 'billing') {
948
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'status', 'kind', 'from', 'to'].includes(key))) {
949
+ throw new CliError('arguments_invalid', 'Use billing scope and history filters. Financial changes continue in the web billing flow.', 2);
950
+ }
951
+ await billingCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'], signal: controller.signal,
952
+ page: parsed.values.page, search: parsed.values.search, status: parsed.values.status, kind: parsed.values.kind,
953
+ from: parsed.values.from, to: parsed.values.to }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
954
+ return;
955
+ }
956
+ if (args[0] === 'ci') {
957
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'status', 'world', 'trigger', 'reference', 'outcome'].includes(key)))
958
+ throw new CliError('arguments_invalid', 'Use CI scope, search, paging, status and run-history filters only. Execution access is reviewed in the browser.', 2);
959
+ await ciCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'], signal: controller.signal,
960
+ page: parsed.values.page, search: parsed.values.search, status: parsed.values.status, world: parsed.values.world,
961
+ trigger: parsed.values.trigger, reference: parsed.values.reference, outcome: parsed.values.outcome }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
962
+ return;
963
+ }
964
+ if (args[0] === 'tools' && ['init', 'validate', 'push'].includes(args[1])) {
965
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'file', 'approve-tool-upload', 'request-key'].includes(key)))
966
+ throw new CliError('arguments_invalid', 'Use tools init|validate [directory], or tools push <package.zip> with --approve-tool-upload for automation.', 2);
967
+ await toolsAuthorCommand(args.slice(1), { json, signal: controller.signal, workspace: parsed.values.workspace,
968
+ openBrowser: !parsed.values['no-browser'], file: parsed.values.file, upload: parsed.values['approve-tool-upload'],
969
+ request: parsed.values['request-key'] }, emit);
970
+ return;
971
+ }
972
+ if (args[0] === 'tools' && args[1] === TOOL_REUSE_VERB) {
973
+ // Tool definition reuse across workspaces: sharing and exact private copies, never instances or state.
974
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'kind', 'request-key',
975
+ 'approve-tool-change'].includes(key)))
976
+ throw new CliError('arguments_invalid', `Use firedrill tools ${TOOL_REUSE_VERB} with only: --workspace, --no-browser, --page, --search, --kind, `
977
+ + '--request-key, --approve-tool-change and --json.', 2);
978
+ await toolReuseCommand(args.slice(2), { json, signal: controller.signal, workspace: parsed.values.workspace,
979
+ openBrowser: !parsed.values['no-browser'], search: parsed.values.search, page: parsed.values.page, kind: parsed.values.kind,
980
+ request: parsed.values['request-key'], approve: parsed.values['approve-tool-change'] }, emit);
981
+ return;
982
+ }
983
+ if (args[0] === 'tools' && NATIVE_TOOL_ACTIONS.has(args[1])) {
984
+ // Hosted tool sessions (mixed setups): saved tools on the shared pool or isolated, outside any drill.
985
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'state', 'service', 'seconds',
986
+ 'file', 'request-key', 'usage-quote', 'approve-tool-change', 'approve-credits', 'follow', 'allow-writes'].includes(key)))
987
+ throw new CliError('arguments_invalid', 'Use tool session scope, paging, an exact selection and explicit consent only. See firedrill help.', 2);
988
+ await nativeToolsCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
989
+ signal: controller.signal, file: parsed.values.file, seconds: parsed.values.seconds, service: parsed.values.service,
990
+ request: parsed.values['request-key'], quote: parsed.values['usage-quote'], approve: parsed.values['approve-tool-change'], approveCredits: parsed.values['approve-credits'], follow: parsed.values.follow,
991
+ state: parsed.values.state, search: parsed.values.search, page: parsed.values.page, allowWrites: parsed.values['allow-writes'] }, emit);
992
+ return;
993
+ }
994
+ if (args[0] === 'tools') {
995
+ if (Object.keys(parsed.values).some(one => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'file',
996
+ 'request-key', 'service', 'seconds', 'usage-quote', 'approve-tool-upload', 'approve-tool-change', 'import-status', 'before', 'after', 'anchor', 'outcome', 'into', 'approve-download', 'state', 'category'].includes(one))) {
997
+ throw new CliError('arguments_invalid', 'Use tool scope, paging, an exact package request and explicit upload/change consent only.', 2);
998
+ }
999
+ await toolsCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1000
+ signal: controller.signal, page: parsed.values.page, search: parsed.values.search, category: parsed.values.category, file: parsed.values.file, state: parsed.values.state,
1001
+ before: parsed.values.before, after: parsed.values.after, anchor: parsed.values.anchor, outcome: parsed.values.outcome,
1002
+ into: parsed.values.into, approveDownload: parsed.values['approve-download'], usageQuote: parsed.values['usage-quote'],
1003
+ request: parsed.values['request-key'], service: parsed.values.service, upload: parsed.values['approve-tool-upload'], change: parsed.values['approve-tool-change'], seconds: parsed.values.seconds, importStatus: parsed.values['import-status'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1004
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1005
+ return;
1006
+ }
1007
+ if (parsed.values.anchor !== undefined)
1008
+ throw new CliError('arguments_invalid', 'Use --anchor only with tools activity.', 2);
1009
+ if (args[0] === 'datasets') {
1010
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'file', 'request-key', 'approve-dataset', 'into', 'approve-download'].includes(key))) {
1011
+ throw new CliError('arguments_invalid', 'Use dataset scope, paging, an exact file/request and explicit save, withdrawal or download consent only.', 2);
1012
+ }
1013
+ await worldDatasetCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1014
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page, search: parsed.values.search,
1015
+ file: parsed.values.file, request: parsed.values['request-key'], approve: parsed.values['approve-dataset'],
1016
+ into: parsed.values.into, approveDownload: parsed.values['approve-download'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1017
+ return;
1018
+ }
1019
+ if (args[0] === 'sessions') {
1020
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'name', 'file', 'managed', 'local', 'device', 'grant', 'seconds', 'run', 'dataset', 'share-dataset-with-device', 'checkpoint', 'share-checkpoint-with-device', 'approve-real-session', 'acknowledge-shared-state', 'usage-quote', 'request-key', 'acknowledge-partial-state'].includes(key))) {
1021
+ throw new CliError('arguments_invalid', 'Use session scope, paging, execution choice, duration and --run consent only.', 2);
1022
+ }
1023
+ await worldSessionsCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1024
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page,
1025
+ managed: parsed.values.managed, local: parsed.values.local, device: parsed.values.device, grant: parsed.values.grant,
1026
+ seconds: parsed.values.seconds, run: parsed.values.run, dataset: parsed.values.dataset,
1027
+ usageQuote: parsed.values['usage-quote'], request: parsed.values['request-key'],
1028
+ acknowledgePartialState: parsed.values['acknowledge-partial-state'],
1029
+ shareDatasetWithDevice: parsed.values['share-dataset-with-device'], shareCheckpointWithDevice: parsed.values['share-checkpoint-with-device'],
1030
+ approveRealSession: parsed.values['approve-real-session'], acknowledgeSharedState: parsed.values['acknowledge-shared-state'],
1031
+ search: parsed.values.search, name: parsed.values.name, file: parsed.values.file, checkpoint: parsed.values.checkpoint }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1032
+ return;
1033
+ }
1034
+ if (parsed.values['share-checkpoint-with-device'] !== undefined)
1035
+ throw new CliError('arguments_invalid', 'Use --share-checkpoint-with-device only with sessions restore or open --checkpoint.', 2);
1036
+ if (parsed.values['approve-dataset'] !== undefined)
1037
+ throw new CliError('arguments_invalid', 'Use --approve-dataset only with firedrill datasets.', 2);
1038
+ if (parsed.values['approve-tool-upload'] !== undefined || parsed.values['approve-tool-change'] !== undefined) {
1039
+ throw new CliError('arguments_invalid', 'Use tool upload/change approvals only with firedrill tools.', 2);
1040
+ }
1041
+ if (args[0] === 'real-operations') {
1042
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'service', 'outcome', 'needs-review',
1043
+ 'request-key', 'acknowledge-real-effects', 'acknowledge-duplicate-risk'].includes(key))) {
1044
+ throw new CliError('arguments_invalid', 'Use real-account scope, history filters and exact review acknowledgements only.', 2);
1045
+ }
1046
+ await realOperationsCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1047
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page, search: parsed.values.search,
1048
+ service: parsed.values.service, outcome: parsed.values.outcome, needsReview: parsed.values['needs-review'],
1049
+ request: parsed.values['request-key'], effects: parsed.values['acknowledge-real-effects'], duplicateRisk: parsed.values['acknowledge-duplicate-risk'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1050
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1051
+ return;
1052
+ }
1053
+ if (['service', 'outcome', 'needs-review', 'acknowledge-real-effects', 'acknowledge-duplicate-risk'].some(key => Object.hasOwn(parsed.values, key))) {
1054
+ throw new CliError('arguments_invalid', 'Use real-account filters and review acknowledgements only with firedrill real-operations.', 2);
1055
+ }
1056
+ if (args[0] === 'inspect') {
1057
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search'].includes(key))) {
1058
+ throw new CliError('arguments_invalid', 'Use world/workspace scope, search and page options for inspection.', 2);
1059
+ }
1060
+ await inspectCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1061
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page, search: parsed.values.search }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1062
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1063
+ return;
1064
+ }
1065
+ if (args[0] === 'configuration' || args[0] === 'connections' || args[0] === 'models') {
1066
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'file', 'page', 'search', 'request-key', 'approve-configuration', ...(args[0] === 'connections' && args[1] === 'activity' ? ['status'] : [])].includes(key))) {
1067
+ throw new CliError('arguments_invalid', 'Use configuration scope, paging, metadata file, consent or exact recovery options only.', 2);
1068
+ }
1069
+ const configure = { json, workspace: parsed.values.workspace, world: parsed.values.world,
1070
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page, search: parsed.values.search,
1071
+ file: parsed.values.file, request: parsed.values['request-key'], approve: parsed.values['approve-configuration'] };
1072
+ const configured = (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1073
+ : readable.split('\n').map(terminalText).join('\n') + '\n');
1074
+ if (args[0] === 'models')
1075
+ await targetModelsCommand(args.slice(1), configure, configured);
1076
+ else if (args[0] === 'connections' && isDependencyPreparationAction(args[1]))
1077
+ await dependencyPreparationCommand(args.slice(1), configure, configured);
1078
+ else
1079
+ await configurationCommand(args[0], args.slice(1), { ...configure, status: parsed.values.status }, configured);
1080
+ return;
1081
+ }
1082
+ if (parsed.values['approve-configuration'] !== undefined)
1083
+ throw new CliError('arguments_invalid', 'Use --approve-configuration only with configuration, connections or models.', 2);
1084
+ if (args[0] === 'changes') {
1085
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'file', 'page', 'search', 'request-key', 'approve-change', 'reviewed-draft', 'into', 'approve-download', 'additional-scenario', 'side'].includes(key))) {
1086
+ throw new CliError('arguments_invalid', 'Use source-change scope, paging, review, consent and recovery options only.', 2);
1087
+ }
1088
+ await changesCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1089
+ signal: controller.signal, file: parsed.values.file, page: parsed.values.page, search: parsed.values.search,
1090
+ request: parsed.values['request-key'], approve: parsed.values['approve-change'], reviewedDraft: parsed.values['reviewed-draft'],
1091
+ into: parsed.values.into, approveDownload: parsed.values['approve-download'],
1092
+ additionalScenarios: parsed.values['additional-scenario'], side: parsed.values.side }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1093
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1094
+ return;
1095
+ }
1096
+ if (args[0] === 'simulation' && ['pause', 'resume-scheduling', 'cancel', 'control-request', 'continue-control', 'withdraw-control'].includes(args[1])) {
1097
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'approve-change', ...(args[1] === 'cancel' ? ['request'] : [])].includes(key)))
1098
+ throw new CliError('arguments_invalid', 'Use simulation scope and explicit scheduling-change approval only.', 2);
1099
+ // A native campaign is cancelled through its own route; any other id keeps the hosted campaign control.
1100
+ if (args[1] === 'cancel' && args.length === 3 && await nativeCancel(args[2], { json, workspace: parsed.values.workspace,
1101
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, approve: parsed.values['approve-change'] === true,
1102
+ request: parsed.values.request }, emit))
1103
+ return;
1104
+ if (parsed.values.request !== undefined)
1105
+ throw new CliError('arguments_invalid', 'Use --request only to recover a native campaign cancellation.', 2);
1106
+ await campaignControl(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1107
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, approve: parsed.values['approve-change'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1108
+ return;
1109
+ }
1110
+ const worldEventReport = args[0] === 'report' && ['trial', 'drill'].includes(args[2]) && args[4] === 'world-events';
1111
+ const sourceWithdrawal = args[0] === 'sources' && args[1] === 'withdraw';
1112
+ const runnerCleanup = args[0] === 'runner' && ['run-cleanup', 'drill-cleanup'].includes(args[1]);
1113
+ if ([...(worldEventReport || sourceWithdrawal || runnerCleanup ? [] : ['approve-change']), 'reviewed-draft', 'additional-scenario', ...(args[0] === 'report' ? [] : ['side']), ...(args[0] === 'report' || args[0] === 'source' ? [] : ['into', 'approve-download'])].some(key => Object.hasOwn(parsed.values, key))) {
1114
+ throw new CliError('arguments_invalid', 'Use source-change consent and review options only with firedrill changes.', 2);
1115
+ }
1116
+ if (args[0] === 'criteria' || args[0] === 'assess') {
1117
+ const criterion = args[0] === 'criteria';
1118
+ if (!criterion && ['requests', 'request', 'withdraw-request'].includes(args[1])) {
1119
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'status', 'approve-assessment'].includes(key))) {
1120
+ throw new CliError('arguments_invalid', 'Use only workspace, paging, search, status and explicit cancellation consent for assessment requests.', 2);
1121
+ }
1122
+ await assessmentRequests(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1123
+ signal: controller.signal, page: parsed.values.page, search: parsed.values.search, status: parsed.values.status,
1124
+ approve: parsed.values['approve-assessment'] }, (data, readable) => process.stdout.write(json ? JSON.stringify({ type: 'result', data }) + '\n'
1125
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1126
+ return;
1127
+ }
1128
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'file', 'request-key',
1129
+ ...(criterion ? ['world', 'page', 'search', 'approve-criterion'] : ['approve-assessment'])].includes(key))) {
1130
+ throw new CliError('arguments_invalid', 'Use only scope, authoring, consent and recovery options for this evaluation command.', 2);
1131
+ }
1132
+ await (criterion ? criteriaCommand : assessCommand)(args.slice(1), { json, workspace: parsed.values.workspace,
1133
+ world: parsed.values.world, openBrowser: !parsed.values['no-browser'], signal: controller.signal, file: parsed.values.file,
1134
+ page: parsed.values.page, search: parsed.values.search, request: parsed.values['request-key'],
1135
+ approve: criterion ? parsed.values['approve-criterion'] : parsed.values['approve-assessment'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1136
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1137
+ return;
1138
+ }
1139
+ if (args[0] === 'policies') {
1140
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'agent', 'file', 'name', 'policy-id', 'basis-version', 'request-key', 'approve-policy', 'page', 'search'].includes(key))) {
1141
+ throw new CliError('arguments_invalid', 'Use policy, agent, file, paging or consent options for this command.', 2);
1142
+ }
1143
+ await policyCommand(args.slice(1), { json, workspace: parsed.values.workspace, agent: parsed.values.agent,
1144
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, file: parsed.values.file, name: parsed.values.name,
1145
+ policy: parsed.values['policy-id'], basis: parsed.values['basis-version'], request: parsed.values['request-key'],
1146
+ consent: parsed.values['approve-policy'], page: parsed.values.page, search: parsed.values.search }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1147
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1148
+ return;
1149
+ }
1150
+ if (parsed.values.checkpoint !== undefined && args[0] !== 'report')
1151
+ throw new CliError('arguments_invalid', 'Use --checkpoint with firedrill report.', 2);
1152
+ if (args[0] === 'report') {
1153
+ // A native drill's separately approved reassessments: a new evaluation of retained evidence, never a rerun.
1154
+ if (reassessmentShape(args.slice(1))) {
1155
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'request-key', 'usage-quote', 'approve-assessment', 'approve-credits'].includes(key)))
1156
+ throw new CliError('arguments_invalid', 'Use --page, --request-key, --usage-quote or --approve-assessment with drill reassessments.', 2);
1157
+ await reassessmentCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1158
+ signal: controller.signal, page: parsed.values.page, request: parsed.values['request-key'], quote: parsed.values['usage-quote'],
1159
+ approve: parsed.values['approve-assessment'], approveCredits: parsed.values['approve-credits'] }, emit);
1160
+ return;
1161
+ }
1162
+ if (args[2] === 'diagnoses') {
1163
+ if (args.length > 4 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'kind'].includes(key)))
1164
+ throw new CliError('arguments_invalid', 'Use report <simulation> diagnoses [insight] with paging or insight filters.', 2);
1165
+ await campaignDiagnoses([args[1], ...args.slice(3)], { json, workspace: parsed.values.workspace,
1166
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page,
1167
+ search: parsed.values.search, kind: parsed.values.kind }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1168
+ return;
1169
+ }
1170
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'checkpoint', 'scenario', 'checks', 'status', 'score-bucket', 'assessment-status', 'request-key', 'into', 'approve-download', 'cohort', 'origin', 'kind', 'service-request', 'side', 'method', 'evaluator', 'attempt', 'step', 'from-at', 'to-at', ...(worldEventReport ? ['file', 'approve-change'] : [])].includes(key))
1171
+ || (parsed.values.scenario?.length ?? 0) > 1) {
1172
+ throw new CliError('arguments_invalid', 'Use report, paging or checkpoint options for this command.', 2);
1173
+ }
1174
+ // A native campaign reads its slots, executions and retained evidence; any other id keeps the hosted reports.
1175
+ if (nativeReportShape(args.slice(1)) && Object.keys(parsed.values).every(key => ['json', 'help', 'workspace', 'no-browser', 'page', 'kind'].includes(key))
1176
+ && await nativeReport(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1177
+ signal: controller.signal, page: parsed.values.page, kind: parsed.values.kind }, emit))
1178
+ return;
1179
+ await reportCommand(args.slice(1), { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1180
+ page: parsed.values.page, search: parsed.values.search, checkpoint: parsed.values.checkpoint,
1181
+ scoreBucket: parsed.values['score-bucket'], assessmentStatus: parsed.values['assessment-status'],
1182
+ cohort: parsed.values.cohort, origin: parsed.values.origin, evidenceKind: parsed.values.kind, side: parsed.values.side, checkMethod: parsed.values.method, evaluator: parsed.values.evaluator,
1183
+ requestKey: parsed.values['request-key'], into: parsed.values.into, approveDownload: parsed.values['approve-download'],
1184
+ evidenceAttempt: parsed.values.attempt, evidenceStep: parsed.values.step,
1185
+ evidenceFrom: parsed.values['from-at'], evidenceTo: parsed.values['to-at'],
1186
+ file: parsed.values.file, change: parsed.values['approve-change'],
1187
+ scenario: parsed.values.scenario?.[0], checks: parsed.values.checks, status: parsed.values.status,
1188
+ serviceRequest: parsed.values['service-request'], signal: controller.signal }, (value, readable) => output(value, readable.split('\n').map(terminalText).join('\n')));
1189
+ return;
1190
+ }
1191
+ if (['simulate', 'validate', 'simulation'].includes(args[0])) {
1192
+ const action = args[0] === 'simulate' ? 'run' : args[0] === 'validate' ? 'validate' : args[1];
1193
+ const common = ['json', 'help', 'workspace', 'world', 'no-browser'];
1194
+ const runOptions = ['managed', 'local', 'device', 'grant', 'run'];
1195
+ const allowed = [...common, ...(['run', 'validate', 'resume'].includes(action) ? runOptions : []),
1196
+ ...(action === 'requests' ? ['after'] : []),
1197
+ ...(action === 'request' && args[2] === 'continue' ? ['run'] : []),
1198
+ ...(['run', 'validate'].includes(action) ? ['approve-real-startup', 'acknowledge-external-effects'] : []),
1199
+ ...(action === 'run' ? ['validate', 'all-scenarios', 'scenario', 'repetitions', 'parallel', 'seconds', 'assess', 'record-browser', 'record-voice', 'record-desktop', 'acknowledge-desktop-screen-content', 'dataset', 'share-dataset-with-device', 'approve-real-simulation', 'acknowledge-shared-state', 'approve-agent-input', 'application-checkpoint', 'acknowledge-fresh-world-state', 'share-application-checkpoint-with-device', 'coordinated-checkpoint', 'acknowledge-supported-checkpoint-scope', 'share-coordinated-checkpoint-with-device'] : [])];
1200
+ if (Object.keys(parsed.values).some(key => !allowed.includes(key)))
1201
+ throw new CliError('arguments_invalid', 'Use only options for this simulation action.', 2);
1202
+ const options = { world: parsed.values.world, workspace: parsed.values.workspace, json, after: parsed.values.after,
1203
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, local: parsed.values.local,
1204
+ managed: parsed.values.managed, device: parsed.values.device, grant: parsed.values.grant, run: parsed.values.run,
1205
+ approveRealStartup: parsed.values['approve-real-startup'], acknowledgeExternalEffects: parsed.values['acknowledge-external-effects'],
1206
+ approveRealSimulation: parsed.values['approve-real-simulation'], acknowledgeSharedState: parsed.values['acknowledge-shared-state'],
1207
+ validate: parsed.values.validate, all: parsed.values['all-scenarios'], scenarios: parsed.values.scenario,
1208
+ repetitions: parsed.values.repetitions, parallel: parsed.values.parallel, seconds: parsed.values.seconds, assess: parsed.values.assess, recordBrowser: parsed.values['record-browser'], recordVoice: parsed.values['record-voice'], dataset: parsed.values.dataset,
1209
+ recordDesktop: parsed.values['record-desktop'], acknowledgeDesktopScreenContent: parsed.values['acknowledge-desktop-screen-content'],
1210
+ approveAgentInput: parsed.values['approve-agent-input'],
1211
+ applicationCheckpoint: parsed.values['application-checkpoint'], acknowledgeFreshWorldState: parsed.values['acknowledge-fresh-world-state'],
1212
+ shareApplicationCheckpointWithDevice: parsed.values['share-application-checkpoint-with-device'],
1213
+ coordinatedCheckpoint: parsed.values['coordinated-checkpoint'], acknowledgeSupportedCheckpointScope: parsed.values['acknowledge-supported-checkpoint-scope'],
1214
+ shareCoordinatedCheckpointWithDevice: parsed.values['share-coordinated-checkpoint-with-device'],
1215
+ shareDatasetWithDevice: parsed.values['share-dataset-with-device'] };
1216
+ const event = (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1217
+ : readable.split('\n').map(terminalText).join('\n') + '\n');
1218
+ if (['run', 'validate'].includes(action) && args.length === 1)
1219
+ await simulationCommand(action, options, event);
1220
+ else if (args[0] === 'simulation' && action === 'resume' && args.length === 3)
1221
+ await resumeSimulation(args[2], options, event);
1222
+ else if (args[0] === 'simulation' && action === 'requests' && args.length === 2)
1223
+ await simulationRequest('list', undefined, options, event);
1224
+ else if (args[0] === 'simulation' && action === 'request' && ['status', 'continue', 'withdraw'].includes(args[2]) && args.length === 4) {
1225
+ await simulationRequest(args[2], args[3], options, event);
1226
+ }
1227
+ else
1228
+ throw new CliError('arguments_invalid', 'Use simulation resume <id> (watch only), simulation requests, or simulation request status|continue|withdraw <key>.', 2);
1229
+ return;
1230
+ }
1231
+ if (args[0] === 'runner' && args[1] === 'abandon') {
1232
+ if (args.length !== 4 || Object.keys(parsed.values).some(key => !['json', 'help', 'world', 'workspace', 'no-browser'].includes(key)))
1233
+ throw new CliError('arguments_invalid', 'Use runner abandon <device> <job> --world <world>. This permanently closes only an original pre-launch claim.', 2);
1234
+ const selection = { workspace: parsed.values.workspace, interactive: !json && !!process.stdin.isTTY && !!process.stderr.isTTY,
1235
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal };
1236
+ // Historical closure must remain reachable after a world is archived or
1237
+ // its original grant expires. The closing API rechecks current membership.
1238
+ const scope = parsed.values.world ? await selectedWorkspace(selection) : undefined;
1239
+ const world = parsed.values.world ? identifier(parsed.values.world) : undefined;
1240
+ const context = scope && world ? { ...scope, world, prefix: `/api/workspaces/${scope.workspace}/worlds/${world}` }
1241
+ : await selectedWorld(selection);
1242
+ const receipt = await abandonLocalAssignment(context, identifier(args[2]), identifier(args[3]), controller.signal);
1243
+ output(receipt, receipt.settled ? 'Original unstarted job closed. No container or target was started.'
1244
+ : 'Original no-launch closing receipt retained. Final reconciliation is pending; the job cannot be resumed.');
1245
+ return;
1246
+ }
1247
+ if (args[0] === 'runner' && args[1] === 'execute') {
1248
+ if (![3, 4].includes(args.length) || Object.keys(parsed.values).some(key => !['json', 'help', 'world', 'workspace', 'no-browser', 'grant', 'execute-local'].includes(key))) {
1249
+ throw new CliError('arguments_invalid', 'Use runner execute <device> [job] with the saved world and local permission. This runs one existing assignment, not a new campaign.', 2);
1250
+ }
1251
+ await executeAssignment(args[2], args[3], { world: parsed.values.world, workspace: parsed.values.workspace,
1252
+ json, openBrowser: !parsed.values['no-browser'], signal: controller.signal, grant: parsed.values.grant,
1253
+ approve: parsed.values['execute-local'] === true }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1254
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1255
+ return;
1256
+ }
1257
+ if (args[0] === 'runner' && ['authorize', 'grants', 'grant'].includes(args[1])) {
1258
+ const action = args[1] === 'authorize' ? 'authorize' : args[1] === 'grants' ? 'list' : args[2];
1259
+ const allowed = ['json', 'help', 'workspace', 'world', 'no-browser', ...(action === 'list' ? ['page'] : []),
1260
+ ...(action === 'authorize' ? ['approve-local', 'operations', 'minutes', 'parallel', 'cpu', 'memory-mb', 'upload-evidence', 'allow-vault-values', 'allow-real-accounts', 'allow-catalog-packages'] : [])];
1261
+ if (!['authorize', 'list', 'status', 'revoke'].includes(action)
1262
+ || (action === 'authorize' ? args.length !== 3 : action === 'list' ? args.length !== 2 : args.length !== 4)
1263
+ || Object.keys(parsed.values).some(key => !allowed.includes(key))) {
1264
+ throw new CliError('arguments_invalid', 'Use runner authorize <device>, runner grants or runner grant status|revoke <request>. Choose the saved world with --world.', 2);
1265
+ }
1266
+ await runnerGrantCommand(action, action === 'authorize' ? args[2] : args[3], {
1267
+ world: parsed.values.world, workspace: parsed.values.workspace, json, openBrowser: !parsed.values['no-browser'], signal: controller.signal,
1268
+ approve: parsed.values['approve-local'] === true, operations: parsed.values.operations, minutes: parsed.values.minutes,
1269
+ parallel: parsed.values.parallel, cpu: parsed.values.cpu, memory: parsed.values['memory-mb'], evidence: parsed.values['upload-evidence'] === true,
1270
+ vault: parsed.values['allow-vault-values'] === true, real: parsed.values['allow-real-accounts'] === true,
1271
+ catalog: parsed.values['allow-catalog-packages'] === true, page: parsed.values.page
1272
+ }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1273
+ return;
1274
+ }
1275
+ if (args[0] === 'runner' && args[1] === 'completion') {
1276
+ if (args.length !== 4 || args[2] !== 'sync' || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser'].includes(key))) {
1277
+ throw new CliError('arguments_invalid', 'Use firedrill runner completion sync <operation>. This never starts or repeats an agent.', 2);
1278
+ }
1279
+ const scope = await selectedWorkspace({ workspace: parsed.values.workspace,
1280
+ interactive: !json && !!process.stdin.isTTY && !!process.stderr.isTTY,
1281
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal });
1282
+ const operation = identifier(args[3]);
1283
+ const purpose = await localContainerPurpose(scope, operation);
1284
+ const receipt = purpose === 'world_session' ? await syncLocalWorldClosing(scope, operation, controller.signal, { onCommand: worldEvidenceOutput }) : purpose === 'trial'
1285
+ ? await syncLocalTrialCompletion(scope, operation, controller.signal, { wait: true })
1286
+ : await syncLocalCompletion(scope, operation, controller.signal, { wait: true });
1287
+ output(receipt, receipt.settled ? 'The saved execution outcome is retained. No agent was started again.'
1288
+ : 'Closing receipt retained. Evidence processing is separate; no agent was started again.');
1289
+ return;
1290
+ }
1291
+ if (args[0] === 'runner' && args[1] === 'evidence') {
1292
+ if (['forget', 'inspect', 'prune'].includes(args[2])) {
1293
+ if ((args[2] !== 'prune' ? args.length !== 4 : args.length !== 3)
1294
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', ...(args[2] === 'forget' ? ['discard-evidence'] : [])].includes(key))) {
1295
+ throw new CliError('arguments_invalid', 'Use runner evidence inspect|forget <operation> or runner evidence prune. Only private local evidence is affected.', 2);
1296
+ }
1297
+ const options = { json, workspace: parsed.values.workspace, signal: controller.signal };
1298
+ if (args[2] === 'forget')
1299
+ await forgetLocalEvidence(args[3], { ...options, approve: parsed.values['discard-evidence'] === true }, output);
1300
+ else if (args[2] === 'inspect')
1301
+ await inspectLocalEvidence(args[3], options, output);
1302
+ else
1303
+ await pruneLocalEvidence(options, output);
1304
+ return;
1305
+ }
1306
+ if (args.length !== 4 || args[2] !== 'sync' || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser'].includes(key))) {
1307
+ throw new CliError('arguments_invalid', 'Use firedrill runner evidence sync <operation>. This never starts or repeats an agent.', 2);
1308
+ }
1309
+ const scope = await selectedWorkspace({ workspace: parsed.values.workspace,
1310
+ interactive: !json && !!process.stdin.isTTY && !!process.stderr.isTTY,
1311
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal });
1312
+ const operation = identifier(args[3]);
1313
+ const purpose = await localContainerPurpose(scope, operation);
1314
+ if (purpose === 'world_session') {
1315
+ const receipt = await syncLocalWorldEvidence(scope, operation, controller.signal, { onCommand: worldEvidenceOutput });
1316
+ output(receipt, receipt.state === 'closed' ? 'This world has closed. Inspect its saved report for retained and unavailable evidence.'
1317
+ : receipt.state === 'synced' ? 'Saved command records synchronized. World closure and file availability remain separate.'
1318
+ : 'Some original command evidence is unavailable. No agent action was repeated.');
1319
+ return;
1320
+ }
1321
+ if (purpose === 'trial') {
1322
+ const receipt = await syncLocalTrialEvidence(scope, operation, controller.signal, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1323
+ : readable.split('\n').map(terminalText).join('\n') + '\n'));
1324
+ output(receipt, 'Saved drill transfer inspected. Cloud publication and execution outcome remain separate in the closing receipt.');
1325
+ return;
1326
+ }
1327
+ const receipt = await syncLocalEvidence(scope, operation, controller.signal);
1328
+ output(receipt, receipt.state === 'cloud_retained' ? 'Evidence retained in the cloud. Execution completion is separate.'
1329
+ : 'Evidence remains local. No agent was started and cloud completion was not recorded.');
1330
+ return;
1331
+ }
1332
+ if (args[0] === 'runner' && ['run-cleanup', 'drill-cleanup'].includes(args[1])) {
1333
+ if (args.length !== 4 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'approve-change'].includes(key)))
1334
+ throw new CliError('arguments_invalid', 'Use firedrill runner run-cleanup <device-key> <campaign-id> or drill-cleanup <device-key> <assignment-id>, with optional --approve-change. This never starts or stops a process.', 2);
1335
+ await (args[1] === 'drill-cleanup' ? drillCleanupCommand : runCleanupCommand)(args[2], args[3], { workspace: parsed.values.workspace, json,
1336
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, approve: parsed.values['approve-change'] === true }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1337
+ return;
1338
+ }
1339
+ if (args[0] === 'runner' && args[1] === 'cleanup') {
1340
+ if (args.length !== 3 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser'].includes(key))) {
1341
+ throw new CliError('arguments_invalid', 'Use firedrill runner cleanup <operation>. This never starts or repeats execution.', 2);
1342
+ }
1343
+ const scope = await selectedWorkspace({ workspace: parsed.values.workspace,
1344
+ interactive: !json && !!process.stdin.isTTY && !!process.stderr.isTTY,
1345
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal });
1346
+ const receipt = await recoverLocalContainer(scope, identifier(args[2]), controller.signal);
1347
+ output(receipt, receipt.resource_state === 'not_dispatched' ? 'No container was dispatched. Closing receipt synchronization is separate.'
1348
+ : receipt.cleanup_confirmed ? 'Exact local container cleanup confirmed. Cloud completion is separate.'
1349
+ : 'The saved container is not visible. A delayed create is not replayed and cleanup remains unconfirmed.');
1350
+ if (!receipt.cleanup_confirmed)
1351
+ throw new CliError('local_container_cleanup_pending', 'Keep this exact operation journal for a later cleanup lookup.');
1352
+ return;
1353
+ }
1354
+ if (args[0] === 'runner' && ['jobs', 'claim'].includes(args[1])) {
1355
+ const action = args[1] === 'jobs' ? 'list' : args[2];
1356
+ if (!['list', 'status', 'withdraw'].includes(action)
1357
+ || (action === 'list' ? args.length !== 3 : args.length !== 6)
1358
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', ...(action === 'list' ? ['page', 'search'] : [])].includes(key))) {
1359
+ throw new CliError('arguments_invalid', 'Use runner jobs <device> or runner claim status|withdraw <device> <job> <request>. These commands do not start execution.', 2);
1360
+ }
1361
+ await runnerJobs(action, action === 'list' ? args[2] : args[3], args[4], args[5], {
1362
+ workspace: parsed.values.workspace, json, openBrowser: !parsed.values['no-browser'], signal: controller.signal,
1363
+ page: parsed.values.page, search: parsed.values.search
1364
+ }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1365
+ return;
1366
+ }
1367
+ if (args[0] === 'runner' && args[1] === 'runtime') {
1368
+ const action = args[2] ?? 'inspect';
1369
+ if (!['inspect', 'install', 'check', 'recover', 'policy'].includes(action)
1370
+ || (action === 'recover' ? args.length !== 4 : args.length > 3)
1371
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'install-runtime', 'workspace', 'no-browser'].includes(key))
1372
+ || action !== 'install' && parsed.values['install-runtime']) {
1373
+ throw new CliError('arguments_invalid', 'Use firedrill runner runtime [inspect|install|check|policy|recover <key>]. No source or simulation is run by these commands.', 2);
1374
+ }
1375
+ await runtimeCommand(action, args[3], {
1376
+ workspace: parsed.values.workspace, json, install: parsed.values['install-runtime'] === true,
1377
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal
1378
+ }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1379
+ return;
1380
+ }
1381
+ if (args[0] === 'runner') {
1382
+ const action = args[1] ?? 'list';
1383
+ if (!['enroll', 'list', 'status', 'connect', 'revoke'].includes(action)
1384
+ || (['enroll', 'list'].includes(action) ? args.length > 2 : args.length !== 3)
1385
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'name', 'enroll-device', 'workspace', 'no-browser', 'page', 'search'].includes(key))
1386
+ || action !== 'enroll' && (parsed.values.name || parsed.values['enroll-device'])
1387
+ || action !== 'list' && (parsed.values.page || parsed.values.search)) {
1388
+ throw new CliError('arguments_invalid', 'Use firedrill runner enroll|list|status|connect|revoke. Presence does not start local execution.', 2);
1389
+ }
1390
+ await runnerCommand(action, args[2], { workspace: parsed.values.workspace, json, openBrowser: !parsed.values['no-browser'],
1391
+ signal: controller.signal, name: parsed.values.name, enroll: parsed.values['enroll-device'] === true, page: parsed.values.page, search: parsed.values.search }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1392
+ return;
1393
+ }
1394
+ if (args[0] === 'connect') {
1395
+ if (['multi', 'multi-status', 'multi-resume'].includes(args[1])) {
1396
+ if (args.length > 3 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'file', 'name', 'upload-source'].includes(key)))
1397
+ throw new CliError('arguments_invalid', 'Use connect multi --file <roots.json>, or multi-status|multi-resume <key>.', 2);
1398
+ await sourceBatchCommand(args.slice(1), { json, workspace: parsed.values.workspace, file: parsed.values.file,
1399
+ name: parsed.values.name, upload: parsed.values['upload-source'], openBrowser: !parsed.values['no-browser'], signal: controller.signal }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1400
+ return;
1401
+ }
1402
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'name', 'upload-source', 'workspace', 'no-browser', 'after', 'search', 'status'].includes(key))) {
1403
+ throw new CliError('arguments_invalid', 'Source connection does not select an execution location or start a build.', 2);
1404
+ }
1405
+ const event = (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n'
1406
+ : readable.split('\n').map(terminalText).join('\n') + '\n');
1407
+ const options = { name: parsed.values.name, workspace: parsed.values.workspace, json, openBrowser: !parsed.values['no-browser'], signal: controller.signal };
1408
+ if (args[1] === 'requests' && args.length === 2) {
1409
+ if (parsed.values.name !== undefined || parsed.values['upload-source'] !== undefined)
1410
+ throw new CliError('arguments_invalid', 'Request history does not change a source capture or consent.', 2);
1411
+ await sourceRequests({ ...options, action: 'list', after: parsed.values.after, search: parsed.values.search, status: parsed.values.status }, event);
1412
+ return;
1413
+ }
1414
+ if (['status', 'resume', 'upload', 'withdraw'].includes(args[1]) && args.length === 3) {
1415
+ if (['name', 'upload-source', 'after', 'search', 'status'].some(key => Object.hasOwn(parsed.values, key)))
1416
+ throw new CliError('arguments_invalid', 'A saved request cannot change its capture or consent. Use the exact request key.', 2);
1417
+ await sourceRequests({ ...options, action: args[1], key: args[2] }, event);
1418
+ return;
1419
+ }
1420
+ if (args.length > 2 || ['after', 'search', 'status'].some(key => Object.hasOwn(parsed.values, key)))
1421
+ throw new CliError('arguments_invalid', 'Use firedrill connect [path] or a saved request command.', 2);
1422
+ await connectSource({ ...options, root: args[1], upload: parsed.values['upload-source'] === true }, event);
1423
+ return;
1424
+ }
1425
+ if (args[0] === 'sources') {
1426
+ if (args.length > (['status', 'withdraw'].includes(args[1]) ? 3 : 2) || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'agent', 'file', 'request-key', 'page', 'search', 'upload-source', 'approve-change', 'no-browser'].includes(key)))
1427
+ throw new CliError('arguments_invalid', 'Use sources list|options|use|status|withdraw with agent, paging or reviewed selection options.', 2);
1428
+ await sourceSetsCommand(args.slice(1), { json, workspace: parsed.values.workspace, agent: parsed.values.agent,
1429
+ file: parsed.values.file, request: parsed.values['request-key'], page: parsed.values.page, search: parsed.values.search,
1430
+ approve: parsed.values['upload-source'] === true, change: parsed.values['approve-change'], openBrowser: !parsed.values['no-browser'], signal: controller.signal }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1431
+ return;
1432
+ }
1433
+ if (args[0] === 'source') {
1434
+ if (args[1] === 'runtime') {
1435
+ const action = args[2] ?? 'inspect';
1436
+ if (!['inspect', 'install'].includes(action) || args.length > 3
1437
+ || Object.keys(parsed.values).some(key => !['json', 'help', 'approve-download'].includes(key))
1438
+ || action !== 'install' && Object.hasOwn(parsed.values, 'approve-download')) {
1439
+ throw new CliError('arguments_invalid', 'Use firedrill source runtime inspect or install [--approve-download]. This does not upload source or run an agent.', 2);
1440
+ }
1441
+ await sourceRuntimeCommand(action, { signal: controller.signal, approve: parsed.values['approve-download'] === true, interactive: !json }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : terminalText(readable) + '\n'));
1442
+ return;
1443
+ }
1444
+ if (!['inspect', 'snapshot'].includes(args[1]) || args.length > 3)
1445
+ throw new CliError('arguments_invalid', 'Use firedrill source inspect|snapshot [path]. Nothing is uploaded by these commands.', 2);
1446
+ if (Object.keys(parsed.values).some(key => key !== 'json' && key !== 'help'))
1447
+ throw new CliError('arguments_invalid', 'Local source capture accepts a path and --json only. Cloud and build options do not apply.', 2);
1448
+ const result = await localSource(args[1], args[2] ?? process.cwd(), controller.signal, !json);
1449
+ output(result, [`${result.files.length} files, ${result.expanded_bytes} bytes`, `Content: ${result.content_digest}`,
1450
+ `Base commit: ${result.repository?.base_commit ?? 'Not available'}`, `Detected configuration names: ${result.variables.map(item => item.name).join(', ') || 'None'}`,
1451
+ ...(result.repository ? [`Changes from base: ${result.changes_from_base.length}`] : []),
1452
+ ...(result.local_snapshot_path ? [`Saved locally: ${result.local_snapshot_path}`] : []),
1453
+ 'No source, environment values or credentials were uploaded.', result.disclosure].map(terminalText).join('\n'));
1454
+ return;
1455
+ }
1456
+ if (args[0] === 'onboarding') {
1457
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'no-browser'].includes(key)))
1458
+ throw new CliError('arguments_invalid', 'Use onboarding show or onboarding open with optional --json or --no-browser.', 2);
1459
+ await onboardingCommand(args.slice(1), { json, openBrowser: !parsed.values['no-browser'], signal: controller.signal }, output);
1460
+ return;
1461
+ }
1462
+ if (args[0] === 'mcp') {
1463
+ if (args.length !== 1 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'agent'].includes(key))
1464
+ || parsed.values.agent !== undefined && !['codex', 'claude', 'other'].includes(parsed.values.agent))
1465
+ throw new CliError('arguments_invalid', 'Use firedrill mcp with optional --agent codex|claude|other, --workspace, --json or --no-browser.', 2);
1466
+ await mcpConnection({ json, workspace: parsed.values.workspace, agent: parsed.values.agent,
1467
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal }, output);
1468
+ return;
1469
+ }
1470
+ if (args[0] === 'signup' && args[1] === 'guide') {
1471
+ if (args.length !== 2 || Object.keys(parsed.values).some(key => !['json', 'help'].includes(key)))
1472
+ throw new CliError('arguments_invalid', 'Use signup guide with optional --json. No account is created by reading the guide.', 2);
1473
+ await registrationGuide(controller.signal, output);
1474
+ return;
1475
+ }
1476
+ if (['login', 'signup'].includes(args[0]) && args.length === 1) {
1477
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'no-browser', 'organization', 'open'].includes(key)))
1478
+ throw new CliError('arguments_invalid', 'Use signup or login with optional --organization, --json, --open and --no-browser.', 2);
1479
+ const session = await login({ json, open: !parsed.values['no-browser'], explicitOpen, organization: parsed.values.organization, signal: controller.signal });
1480
+ output(session, `Signed in as ${terminalText(session.user.email)}.`);
1481
+ return;
1482
+ }
1483
+ if (args[0] === 'logout' && args.length === 1) {
1484
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'local-only'].includes(key)))
1485
+ throw new CliError('arguments_invalid', 'Use firedrill logout with optional --local-only and --json. Nothing was signed out.', 2);
1486
+ const result = await logout({ localOnly: parsed.values['local-only'] === true });
1487
+ output(result, result.revoked ? 'Terminal access revoked. Saved credentials removed.' : 'Saved credentials removed. Cloud revocation is not confirmed. Revoke the session in Account settings.');
1488
+ return;
1489
+ }
1490
+ if (args[0] === 'priority') {
1491
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'world', 'no-browser', 'page', 'search', 'status', 'request-key', 'approve-assessment'].includes(key)))
1492
+ throw new CliError('arguments_invalid', 'Use world/workspace, the selected action’s paging options, original request key and explicit assessment consent.', 2);
1493
+ await priorityCommand(args.slice(1), { json, workspace: parsed.values.workspace, world: parsed.values.world,
1494
+ openBrowser: !parsed.values['no-browser'], signal: controller.signal, page: parsed.values.page, search: parsed.values.search,
1495
+ status: parsed.values.status, request: parsed.values['request-key'], approve: parsed.values['approve-assessment'] }, (type, data, readable) => process.stdout.write(json ? JSON.stringify({ type, data }) + '\n' : readable.split('\n').map(terminalText).join('\n') + '\n'));
1496
+ return;
1497
+ }
1498
+ const pageNumber = parsed.values.page ?? '1';
1499
+ if (!/^[1-9][0-9]{0,8}$/.test(pageNumber) || (parsed.values.search?.length ?? 0) > 200)
1500
+ throw new CliError('query_invalid', 'Use a valid page number and a search of up to 200 characters.', 2);
1501
+ if (args[0] === 'workspace' && ['create', 'requests', 'request', 'withdraw'].includes(args[1])) {
1502
+ // Creating a workspace from the terminal: the human approves its name and accepts the terms; nothing is inferred.
1503
+ const allowed = args[1] === 'create' ? ['request-key', 'terms-version', 'privacy-version', 'approve-change', 'no-browser']
1504
+ : args[1] === 'requests' ? ['page'] : args[1] === 'withdraw' ? ['approve-change'] : [];
1505
+ if (Object.keys(parsed.values).some(key => !['json', 'help', ...allowed].includes(key)))
1506
+ throw new CliError('arguments_invalid', 'Use workspace create [name] [--request-key <key> --terms-version <v> --privacy-version <v>] [--approve-change], workspace requests [--page <n>], workspace request <key> or workspace withdraw <key> [--approve-change].', 2);
1507
+ const scope = await api('/api/auth/native/session', { signal: controller.signal });
1508
+ await workspaceRegistrationCommand(args.slice(1), { json, owner: identifier(scope.user.id), openBrowser: !parsed.values['no-browser'],
1509
+ signal: controller.signal, request: parsed.values['request-key'], termsVersion: parsed.values['terms-version'],
1510
+ privacyVersion: parsed.values['privacy-version'], approve: parsed.values['approve-change'], page: parsed.values.page }, output);
1511
+ return;
1512
+ }
1513
+ if (args[0] === 'workspace') {
1514
+ const refresh = args[1] === 'refresh' && args.length === 2;
1515
+ if (Object.keys(parsed.values).some(key => !['json', 'help', ...(refresh ? ['request-key'] : ['page', 'search', 'no-browser'])].includes(key)))
1516
+ throw new CliError('arguments_invalid', 'Use page/search for workspace lists, or an original request key for workspace refresh.', 2);
1517
+ const scope = await api('/api/auth/native/session', { signal: controller.signal });
1518
+ const owner = identifier(scope.user.id);
1519
+ if (args[1] === 'organizations' && args.length === 2) {
1520
+ const page = await api(`/api/auth/native/organizations?page=${pageNumber}&q=${encodeURIComponent(parsed.values.search ?? '')}`, { actor: owner, signal: controller.signal });
1521
+ output(page, [page.results.map(item => `${item.id} ${terminalText(item.name)}`).join('\n') || 'No matching organizations.',
1522
+ 'Use firedrill login --organization <id> for a new organization grant.'].join('\n'));
1523
+ return;
1524
+ }
1525
+ if (refresh) {
1526
+ const key = parsed.values['request-key'] ?? randomUUID();
1527
+ if (!/^[A-Za-z0-9_-]{1,128}$/.test(key))
1528
+ throw new CliError('membership_discovery_request_invalid', 'Use the original workspace refresh request key.', 2);
1529
+ if (json)
1530
+ output({ type: 'request_prepared', request_key: key }, '');
1531
+ else
1532
+ process.stderr.write(`Recovery: firedrill workspace refresh --request-key ${key}\n`);
1533
+ const result = await refreshWorkspaceAccess(owner, controller.signal, key);
1534
+ output({ ...result, request_key: key }, result.state === 'running' ? 'Checking workspace access. Use firedrill workspace list to see available workspaces.' :
1535
+ result.state === 'completed' ? 'Workspace access refreshed.' : 'Workspace access needs another refresh. No workspace was selected.');
1536
+ return;
1537
+ }
1538
+ if (args[1] === 'use' && args.length === 3) {
1539
+ const session = await api('/api/auth/native/workspaces', { method: 'POST', body: { workspace_id: identifier(args[2]) }, actor: owner, signal: controller.signal });
1540
+ output(session, `Workspace: ${terminalText(session.workspace?.name ?? 'Not selected')}`);
1541
+ return;
1542
+ }
1543
+ if (args.length === 1 && process.stdin.isTTY && !json) {
1544
+ const chosen = await chooseWorkspace(owner, controller.signal, !parsed.values['no-browser']);
1545
+ const session = await api('/api/auth/native/workspaces', { method: 'POST', body: { workspace_id: chosen }, actor: owner, signal: controller.signal });
1546
+ output(session, `Workspace: ${terminalText(session.workspace?.name ?? 'Not selected')}`);
1547
+ return;
1548
+ }
1549
+ if (args.length === 1 || args[1] === 'list' && args.length === 2) {
1550
+ const page = await api(`/api/auth/native/workspaces?page=${pageNumber}&q=${encodeURIComponent(parsed.values.search ?? '')}`, { actor: owner, signal: controller.signal });
1551
+ const progress = page.membership_sync.state === 'running' ? 'Checking workspace access.' :
1552
+ page.membership_sync.state === 'attention_required' ? 'Access refresh did not finish. Use firedrill workspace refresh.' : '';
1553
+ output(page, [page.results.map(item => `${item.id} ${terminalText(item.name)} ${terminalText(item.role)}`).join('\n') ||
1554
+ (progress ? '' : 'No matching workspaces.'), progress].filter(Boolean).join('\n'));
1555
+ return;
1556
+ }
1557
+ }
1558
+ if (args[0] === 'worlds' && ['new', 'creation', 'continue-creation', 'withdraw-creation'].includes(args[1])) {
1559
+ if (args.length > 3 || Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'request-key', 'approve-change'].includes(key))) {
1560
+ throw new CliError('arguments_invalid', 'Use worlds new [agent] or worlds creation|continue-creation|withdraw-creation <key>. Automation can pass --approve-change for a reviewed write.', 2);
1561
+ }
1562
+ const options = { json, workspace: parsed.values.workspace, openBrowser: !parsed.values['no-browser'],
1563
+ request: parsed.values['request-key'], approve: parsed.values['approve-change'], signal: controller.signal };
1564
+ if (args[1] === 'new')
1565
+ await createWorld(options, output, args[2]);
1566
+ else
1567
+ await worldCreationCommand(args.slice(1), options, output);
1568
+ return;
1569
+ }
1570
+ if (kinds.has(args[0]) && args.length <= 2) {
1571
+ if (Object.keys(parsed.values).some(key => !['json', 'help', 'workspace', 'no-browser', 'page', 'search', 'world', 'agent', 'status', 'records'].includes(key))) {
1572
+ throw new CliError('arguments_invalid', 'Use saved-directory search, scope, status and page options only.', 2);
1573
+ }
1574
+ if (args[1] && ['page', 'search', 'world', 'agent', 'status', 'records'].some(key => parsed.values[key] !== undefined)) {
1575
+ throw new CliError('arguments_invalid', 'Directory filters apply to a list, not a single saved item.', 2);
1576
+ }
1577
+ if ((parsed.values.world && ['agents', 'worlds'].includes(args[0])) || (parsed.values.agent && args[0] === 'agents')) {
1578
+ throw new CliError('arguments_invalid', 'Use --agent with worlds or their resources; --world applies to scenarios, personas, evaluations and simulations.', 2);
1579
+ }
1580
+ if (parsed.values.status && !/^[a-z][a-z0-9_]{0,31}$/.test(parsed.values.status))
1581
+ throw new CliError('query_invalid', 'Use the saved item status from the directory.', 2);
1582
+ if (parsed.values.records !== undefined && !['active', 'archived', 'all'].includes(parsed.values.records))
1583
+ throw new CliError('query_invalid', 'Choose active, archived or all records.', 2);
1584
+ const session = await api('/api/auth/native/session', { signal: controller.signal });
1585
+ const workspace = parsed.values.workspace ?? session.workspace?.id;
1586
+ if (!workspace)
1587
+ throw new CliError('workspace_required', 'Run firedrill workspace or pass --workspace <id>.', 2);
1588
+ const prefix = `/api/workspaces/${identifier(workspace)}/${args[0]}`;
1589
+ if (args[1]) {
1590
+ const result = await api(`${prefix}/${identifier(args[1])}`, { actor: session.user.id, signal: controller.signal });
1591
+ output(result, `${terminalText(result.name)}\n${terminalText(result.status)}\n${terminalText(result.description ?? '')}`);
1592
+ return;
1593
+ }
1594
+ const query = new URLSearchParams({ page: pageNumber, q: parsed.values.search ?? '', status: parsed.values.status ?? '' });
1595
+ if (parsed.values.records !== undefined)
1596
+ query.set('lifecycle', parsed.values.records);
1597
+ if (parsed.values.world)
1598
+ query.set('world', identifier(parsed.values.world));
1599
+ if (parsed.values.agent)
1600
+ query.set('agent', identifier(parsed.values.agent));
1601
+ const result = await api(`${prefix}?${query}`, { actor: session.user.id, signal: controller.signal });
1602
+ const rows = result.results.map(item => `${item.id} ${terminalText(item.name)} ${terminalText(item.status)}${item.world_name ? ` ${terminalText(item.world_name)}` : ''}${item.source_label ? ` ${terminalText(item.source_label)} (${terminalText(item.source_revision ?? '')})` : ''}${item.counts ? ` ${item.counts.worlds !== undefined ? `${item.counts.worlds} worlds, ` : ''}${item.counts.simulations} simulations` : ''}`);
1603
+ output(result, (rows.join('\n') || 'No matching saved items.') + `\nPage ${result.page}, ${result.count} matching items.${result.next ? ` Use --page ${result.page + 1} with the same filters to continue.` : ''}`);
1604
+ return;
1605
+ }
1606
+ throw new CliError('command_unknown', 'Use firedrill --help to choose a command.', 2);
1607
+ }
1608
+ catch (error) {
1609
+ const canceled = controller.signal.aborted || error instanceof CliError && error.code === 'canceled';
1610
+ const invalidArguments = error instanceof Error && 'code' in error && [
1611
+ 'ERR_PARSE_ARGS_UNKNOWN_OPTION', 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE', 'ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL',
1612
+ ].includes(String(error.code));
1613
+ const problem = canceled ? new CliError('canceled', 'Stopped. No pending operation is retried automatically.', 130)
1614
+ : error instanceof CliError ? error : invalidArguments
1615
+ ? new CliError('arguments_invalid', 'Unrecognized or invalid arguments. Use firedrill --help to see commands and options. No command was started.', 2)
1616
+ : new CliError('command_failed', 'The command could not finish. No success is implied.');
1617
+ process.stderr.write(json ? JSON.stringify({ type: 'error', error: { code: problem.code, message: problem.message },
1618
+ ...(problem.code === 'workspace_setup_required' ? { action_required: 'create_or_select_workspace', browser_url: APP_ORIGIN + '/onboarding' } : {}) }) + '\n' : problem.message + '\n');
1619
+ process.exitCode = problem.exitCode;
1620
+ }
1621
+ finally {
1622
+ process.off('SIGINT', interrupt);
1623
+ process.off('SIGTERM', interrupt);
1624
+ process.off('SIGHUP', interrupt);
1625
+ await companion?.close(signalled ? 'signal' : 'exit').catch(() => undefined);
1626
+ }
1627
+ }