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,81 @@
1
+ import { CliError } from './http.js';
2
+ import { selectOne } from './prompts.js';
3
+ import { currentLocalRunner, enrollLocalRunner, localRunnerSelectionPage } from './runner-identity.js';
4
+ import { scopedRuntimeCommand } from './runner-runtime.js';
5
+ import { authorizeLocalExecution } from './runner-grants.js';
6
+ import { identifier } from './world-contracts.js';
7
+ export function localSetupRequired(context, purpose, output) {
8
+ const scope = `--workspace ${context.workspace} --world ${context.world}`;
9
+ output('local_execution_setup_required', { world_id: context.world, purpose, required_input: ['runner_identity', 'runtime_readiness', 'local_permission'],
10
+ commands: [`firedrill runner enroll --workspace ${context.workspace} --enroll-device`,
11
+ `firedrill runner runtime install --workspace ${context.workspace} --install-runtime`,
12
+ `firedrill runner runtime check --workspace ${context.workspace}`,
13
+ `firedrill runner authorize <device-key> ${scope} --operations ${purpose} --approve-local`],
14
+ execution_started: false }, 'Local execution needs a registered runner, the pinned runtime and permission for this world. Use the explicit runner commands to review setup. No execution was started.');
15
+ throw new CliError('local_execution_setup_required', 'Review local setup in an interactive terminal, or provide the original --grant <permission-request-id>. No setup was approved automatically.', 2);
16
+ }
17
+ /** Orchestration only: the existing enrollment, runtime and permission handlers
18
+ * retain their own original requests, current authority checks and consent.
19
+ * This is never called to replace a saved campaign's execution identity.
20
+ */
21
+ export async function setupLocalExecution(context, purpose, options, output) {
22
+ if (options.json || !process.stdin.isTTY || !process.stderr.isTTY)
23
+ localSetupRequired(context, purpose, output);
24
+ let device = options.device;
25
+ if (!device) {
26
+ let page = 1;
27
+ while (!device) {
28
+ const { directory, candidates } = await localRunnerSelectionPage(context, page, options.signal);
29
+ const choices = candidates.map(row => ({ value: row.device_key, label: row.label, detail: 'Registered in this terminal session. Local ownership is checked before use.' }));
30
+ choices.push({ value: 'register', label: 'Register this device', detail: 'Create a new runner identity after approval. This does not grant execution.' });
31
+ if (page > 1)
32
+ choices.push({ value: 'previous', label: 'Previous page', detail: '' });
33
+ if (directory.next)
34
+ choices.push({ value: 'next', label: 'Next page', detail: '' });
35
+ choices.push({ value: 'cancel', label: 'Cancel local setup', detail: 'Keep this world. Do not start local or cloud execution.' });
36
+ const selected = await selectOne('Local runner', choices, options.signal);
37
+ if (selected === 'previous') {
38
+ page--;
39
+ continue;
40
+ }
41
+ if (selected === 'next') {
42
+ page++;
43
+ continue;
44
+ }
45
+ if (selected === 'cancel')
46
+ throw new CliError('canceled', 'Local setup canceled. No execution was started.', 130);
47
+ if (selected === 'register') {
48
+ const receipt = await enrollLocalRunner(context, options, output);
49
+ if (receipt.state === 'not_retained')
50
+ throw new CliError('runner_enrollment_unconfirmed', 'The original runner enrollment is not confirmed. Inspect its exact device key before continuing; no replacement was enrolled.');
51
+ device = receipt.device_key;
52
+ }
53
+ else
54
+ device = identifier(selected);
55
+ }
56
+ }
57
+ const runner = await currentLocalRunner(context, identifier(device), options.signal);
58
+ const runtimeOptions = { ...options, workspace: context.workspace, install: false };
59
+ const image = await scopedRuntimeCommand(context, 'install', undefined, runtimeOptions, output);
60
+ if (!image?.installed)
61
+ throw new CliError('local_runtime_required', 'The pinned local execution image is not available. No permission or execution was created.', 2);
62
+ if (!await selectOne('Check local execution isolation?', [
63
+ { value: true, label: 'Check isolation', detail: 'Start and remove a source-free readiness container. No project, model or simulation runs.' },
64
+ { value: false, label: 'Cancel' }
65
+ ], options.signal))
66
+ throw new CliError('canceled', 'Local setup canceled. No project or simulation was started.', 130);
67
+ const observed = await scopedRuntimeCommand(context, 'check', undefined, runtimeOptions, output);
68
+ if (!observed?.isolation_observed || !observed.cleanup_confirmed)
69
+ throw new CliError('local_runtime_cleanup_pending', 'The readiness container has not finished cleanup. Recover the exact runtime request shown above before continuing. No project or simulation was started.', 2);
70
+ // Enrollment does not outlive its original native session. The current grant
71
+ // offer is read again by authorizeLocalExecution after this host review.
72
+ const current = await currentLocalRunner(context, runner.device_key, options.signal);
73
+ if (current.native_session_id !== runner.native_session_id)
74
+ throw new CliError('runner_scope_changed', 'The runner session changed during setup. No permission was created.');
75
+ const permission = await authorizeLocalExecution(context, current.device_key, { ...options, world: context.world, workspace: context.workspace,
76
+ operations: purpose, expectedRuntime: observed.runtime }, output);
77
+ if (permission.state !== 'active' || permission.device_key !== current.device_key || !permission.operations.includes(purpose)) {
78
+ throw new CliError('local_permission_changed', 'The original local permission is not currently active. Inspect its exact request; no replacement or execution was started.');
79
+ }
80
+ return { request: permission.request_key, device: permission.device_key };
81
+ }
@@ -0,0 +1,73 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { realpath } from 'node:fs/promises';
3
+ import { api } from './auth.js';
4
+ import { CliError } from './http.js';
5
+ import { localSource } from './local-source.js';
6
+ import { originalLocalSourceBatch } from './source-batch.js';
7
+ import { savedProject } from './source-connection.js';
8
+ import { localGrantSourceRootsMatch } from '../../contracts/local-grant-contracts.js';
9
+ import { nativeMetadataCanonical } from '../../contracts/local-value-source-contracts.js';
10
+ const digest = (value) => createHash('sha256').update(nativeMetadataCanonical(value)).digest('hex');
11
+ const unavailable = () => new CliError('local_source_basis_changed', 'The local folders do not match the original connected source selection. No folder was substituted, uploaded or executed.', 2);
12
+ /** One source verifier shared by new permission review and every local claim.
13
+ * Actual execution still consumes the retained immutable composed snapshot.
14
+ */
15
+ export async function verifyLocalSourceBasis(context, device, session, basis, signal, allowPrompt = false) {
16
+ const root = await realpath(process.cwd());
17
+ if (!basis.source_set) {
18
+ const capture = await localSource('inspect', root, signal, allowPrompt);
19
+ if (capture.content_digest !== basis.source_content_digest)
20
+ throw unavailable();
21
+ return { project_digest: createHash('sha256').update(root).digest('hex'), file_count: capture.files.length, component_count: 1 };
22
+ }
23
+ const binding = await savedProject(context, root);
24
+ if (binding?.world !== context.world || binding.source !== basis.source_id || !binding.batch_key) {
25
+ throw new CliError('local_source_batch_required', 'Open an original selected source folder with its private multi-folder selection journal. If that original connection is pending, use connect multi-resume <original-key>. No source was reconnected or uploaded.', 2);
26
+ }
27
+ const batch = await originalLocalSourceBatch(context, binding.batch_key, context.world, basis.source_id, basis.source_set, signal);
28
+ const basisDigest = digest(basis), rows = [];
29
+ let page = 1, pageSize = null;
30
+ while (true) {
31
+ const value = await api(`${context.prefix}/local-runners/${device}/grant-offer/source-roots?basis_sha256=${basisDigest}&page=${page}`, { actor: context.owner, workspace: context.workspace, signal });
32
+ if (!localGrantSourceRootsMatch(value, context.owner, context.workspace, context.world, device, session, basisDigest, basis.source_set, page)
33
+ || pageSize !== null && pageSize !== value.page_size || rows.length + value.results.length > basis.source_set.root_count)
34
+ throw unavailable();
35
+ pageSize = value.page_size;
36
+ rows.push(...value.results);
37
+ if (!value.next)
38
+ break;
39
+ if (page >= 128)
40
+ throw unavailable();
41
+ page++;
42
+ }
43
+ if (rows.length !== basis.source_set.root_count || new Set(rows.map(row => row.alias)).size !== rows.length
44
+ || new Set(rows.map(row => row.source_id)).size !== rows.length
45
+ || digest({ schema: 'firedrill.local-source-roots.v1', roots: rows }) !== basis.source_set.roots_sha256
46
+ || digest({ schema: 'firedrill.source-set.v1', roots: rows.map(({ content_digest: _capture, ...row }) => row) }) !== basis.source_set.source_set_sha256)
47
+ throw unavailable();
48
+ let fileCount = 0;
49
+ const mapping = [];
50
+ for (let index = 0; index < rows.length; index++) {
51
+ const declared = rows[index], selected = batch.roots[index];
52
+ if (declared.content_digest === null)
53
+ throw new CliError('local_source_binding_unavailable', 'This component has no original local capture to compare. Use a reviewed local source connection; no local path or digest was guessed.', 2);
54
+ if (!selected || declared.alias !== selected.alias || declared.source_id !== selected.source_id
55
+ || declared.content_digest !== selected.content_digest || declared.directory !== `components/${selected.alias}`
56
+ || !batch.composition.roots.some(row => row.alias === declared.alias && row.source_id === declared.source_id
57
+ && row.revision === declared.revision && row.directory === declared.directory && row.label === declared.label))
58
+ throw unavailable();
59
+ const physical = await realpath(selected.path);
60
+ if (physical !== selected.path)
61
+ throw unavailable();
62
+ const capture = await localSource('inspect', physical, signal, allowPrompt);
63
+ if (capture.content_digest !== declared.content_digest)
64
+ throw unavailable();
65
+ fileCount += capture.files.length;
66
+ if (!Number.isSafeInteger(fileCount))
67
+ throw unavailable();
68
+ mapping.push({ alias: declared.alias, path: physical });
69
+ }
70
+ if (!mapping.some(row => row.path === root))
71
+ throw unavailable();
72
+ return { project_digest: digest({ schema: 'firedrill.local-project-roots.v1', roots: mapping }), file_count: fileCount, component_count: mapping.length };
73
+ }
@@ -0,0 +1,66 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+ import { realpath } from 'node:fs/promises';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { CliError } from './http.js';
6
+ import { privateDirectory } from './credentials.js';
7
+ import { sourcePython } from './source-runtime.js';
8
+ // Only our bundled helper is invoked. Customer source is data, not a script,
9
+ // package hook or Python import. Capture itself is offline. If there is no
10
+ // compatible interpreter, acquisition requires separate explicit consent.
11
+ export const trustedPython = sourcePython;
12
+ export async function localSource(action, root, signal, allowPrompt = false) {
13
+ const script = fileURLToPath(new URL('../runtime/source_snapshot.py', import.meta.url));
14
+ const project = await realpath(resolve(root));
15
+ const runtime = await trustedPython(project, signal, allowPrompt);
16
+ const args = ['-I', '-B', script, action, '--root', project];
17
+ if (action === 'snapshot')
18
+ args.push('--storage', await privateDirectory());
19
+ signal.throwIfAborted();
20
+ return new Promise((accept, refuse) => {
21
+ let bytes = 0, overflow = false;
22
+ const chunks = [];
23
+ const environment = Object.fromEntries(['PATH', 'SYSTEMROOT'].flatMap(key => process.env[key] === undefined ? [] : [[key, process.env[key]]]));
24
+ const child = spawn(runtime, args, { shell: false, windowsHide: true, env: environment,
25
+ stdio: ['ignore', 'pipe', 'pipe'], signal });
26
+ child.stdout.on('data', (chunk) => {
27
+ bytes += chunk.length;
28
+ if (bytes > 32 * 1024 * 1024) {
29
+ overflow = true;
30
+ child.kill('SIGTERM');
31
+ return;
32
+ }
33
+ if (!overflow)
34
+ chunks.push(chunk);
35
+ });
36
+ child.stderr.resume(); // Never echo source paths/provider/configuration text.
37
+ child.once('error', () => refuse(new CliError(signal.aborted ? 'canceled' : 'python_runtime_required', signal.aborted ? 'Source capture stopped. No upload occurred.' : 'The source helper could not start. Inspect firedrill source runtime inspect. No upload occurred.', signal.aborted ? 130 : 2)));
38
+ child.once('close', code => {
39
+ if (signal.aborted) {
40
+ refuse(new CliError('canceled', 'Source capture stopped. No upload occurred.', 130));
41
+ return;
42
+ }
43
+ if (overflow) {
44
+ refuse(new CliError('source_capacity_required', 'The source manifest exceeds local intake capacity. Choose a smaller root.'));
45
+ return;
46
+ }
47
+ try {
48
+ const result = JSON.parse(Buffer.concat(chunks).toString('utf8'));
49
+ if (!result.ok) {
50
+ if (!/^[a-z_]{1,64}$/.test(result.error?.code) || typeof result.error?.message !== 'string')
51
+ throw new Error();
52
+ refuse(new CliError(result.error.code, result.error.message, 2));
53
+ return;
54
+ }
55
+ const value = result.data;
56
+ if (code !== 0 || value.schema !== 'firedrill.local-source.v1' || value.uploaded !== false
57
+ || value.provenance !== 'customer_device' || !/^[a-f0-9]{64}$/.test(value.content_digest) || !Array.isArray(value.files))
58
+ throw new Error();
59
+ accept(value);
60
+ }
61
+ catch {
62
+ refuse(new CliError('source_capture_failed', 'Local source capture did not return a complete manifest. No upload or execution occurred.'));
63
+ }
64
+ });
65
+ });
66
+ }
@@ -0,0 +1,91 @@
1
+ import { CliError, API_ORIGIN } from './http.js';
2
+ import { identifier } from './world-contracts.js';
3
+ const invalid = () => new CliError('local_value_unavailable', 'The exact private value is unavailable in this OS credential store. No replacement or file fallback was used.');
4
+ async function entry(scope, id) {
5
+ try {
6
+ const { Entry } = await import('@napi-rs/keyring');
7
+ return new Entry('run.firedrill.simulation.device-value.v1', [API_ORIGIN, identifier(scope.workspace), identifier(scope.world), identifier(id)].join(':'), process.platform === 'linux' ? { linux: { store: 'secret-service' } } : undefined);
8
+ }
9
+ catch {
10
+ throw invalid();
11
+ }
12
+ }
13
+ export async function saveLocalValue(scope, id, metadata, value) {
14
+ if (!value || value.includes('\0') || Buffer.byteLength(value) > 65536)
15
+ throw new CliError('local_value_input_invalid', 'Enter a nonempty value of at most 64 KiB without NUL bytes. It was not saved.', 2);
16
+ const store = await entry(scope, id);
17
+ const body = JSON.stringify({ schema: 'firedrill.device-value.v1', ...scope, id, metadata, value });
18
+ try {
19
+ if (store.getPassword() !== null)
20
+ throw invalid();
21
+ store.setPassword(body);
22
+ if (store.getPassword() !== body)
23
+ throw invalid();
24
+ }
25
+ catch {
26
+ throw invalid();
27
+ }
28
+ }
29
+ export async function savedLocalValue(scope, id) {
30
+ try {
31
+ const raw = (await entry(scope, id)).getPassword();
32
+ if (raw === null || raw.length > 420000)
33
+ throw invalid();
34
+ const value = JSON.parse(raw), metadata = value.metadata;
35
+ if (value.schema !== 'firedrill.device-value.v1' || value.id !== identifier(id)
36
+ || value.owner !== scope.owner || value.workspace !== scope.workspace || value.world !== scope.world
37
+ || !metadata || identifier(metadata.device_key) !== metadata.device_key || !/^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(metadata.name)
38
+ || !['build', 'runtime', 'both'].includes(metadata.phase) || !Number.isSafeInteger(metadata.configuration_revision)
39
+ || metadata.configuration_revision < 1 || metadata.value_stored_locally !== true
40
+ || typeof value.value !== 'string' || !value.value || value.value.includes('\0') || Buffer.byteLength(value.value) > 65536)
41
+ throw invalid();
42
+ return value;
43
+ }
44
+ catch {
45
+ throw invalid();
46
+ }
47
+ }
48
+ export async function forgetLocalValue(scope, id) {
49
+ const store = await entry(scope, id);
50
+ try {
51
+ if (store.getPassword() !== null)
52
+ store.deleteCredential();
53
+ if (store.getPassword() !== null)
54
+ throw invalid();
55
+ }
56
+ catch {
57
+ throw invalid();
58
+ }
59
+ }
60
+ /** Called after the signed packet passes its normal checks. No value is sent
61
+ * back to the cloud or persisted in a job journal. JS strings are not zeroized. */
62
+ export async function resolveLocalValues(owner, references, phases, secretNames) {
63
+ if (!Array.isArray(references) || references.length > 10000 || !Array.isArray(secretNames))
64
+ throw invalid();
65
+ const seen = new Set();
66
+ let bytes = Buffer.byteLength(JSON.stringify(phases));
67
+ for (const reference of references) {
68
+ if (!reference || reference.schema !== 'firedrill.local-value-reference.v1' || identifier(reference.id) !== reference.id
69
+ || reference.world_id !== owner.world || reference.actor_id !== owner.owner || reference.device_key !== owner.device
70
+ || reference.state !== 'available' || reference.value_uploaded !== false || reference.revoked_at !== null
71
+ || !secretNames.includes(reference.name) || seen.has(reference.id))
72
+ throw invalid();
73
+ seen.add(reference.id);
74
+ const saved = await savedLocalValue(owner, reference.id);
75
+ try {
76
+ if (saved.metadata.name !== reference.name || saved.metadata.phase !== reference.phase || saved.metadata.device_key !== owner.device)
77
+ throw invalid();
78
+ for (const phase of reference.phase === 'both' ? ['build', 'runtime'] : [reference.phase]) {
79
+ if (!['build', 'runtime'].includes(phase) || Object.hasOwn(phases[phase], reference.name))
80
+ throw invalid();
81
+ bytes += Buffer.byteLength(saved.value) + Buffer.byteLength(reference.name) + 16;
82
+ if (bytes > 1536 * 1024)
83
+ throw new CliError('local_value_capacity', 'Local values exceed this runtime packet capacity. No target was started.');
84
+ Object.defineProperty(phases[phase], reference.name, { value: saved.value, writable: true, configurable: true, enumerable: true });
85
+ }
86
+ }
87
+ finally {
88
+ saved.value = '';
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,163 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { api } from './auth.js';
3
+ import { auth, choosePaged, interactive, line, query } from './configuration.js';
4
+ import { readMetadata, record } from './configuration-intents.js';
5
+ import { CliError } from './http.js';
6
+ import { selectOne, terminalText } from './prompts.js';
7
+ import { signRunnerRequest } from './runner-identity.js';
8
+ import { selectedWorld } from './world-access.js';
9
+ import { identifier } from './world-contracts.js';
10
+ import { readPrivateValue } from './vault.js';
11
+ import { forgetLocalValue, savedLocalValue, saveLocalValue } from './local-value-store.js';
12
+ import { localValueLookupMatches, localValueOriginalMatches, localValuePageMatches, localValueRegistrationMatches, nativeMetadataCanonical } from '../../contracts/local-value-source-contracts.js';
13
+ import { localRunnersMatch } from '../../contracts/local-runner-contracts.js';
14
+ const invalid = () => new CliError('local_value_input_invalid', 'Use name, phase and enrolled device_key metadata only. Values belong in hidden input or --value-stdin, never a JSON file or command arguments.', 2);
15
+ const responseInvalid = () => new CliError('local_value_receipt_invalid', 'The saved metadata did not match this account, world, device or original request. No private value was uploaded. Inspect the original reference before continuing.');
16
+ const digest = (body) => createHash('sha256').update(nativeMetadataCanonical(body)).digest('hex');
17
+ const describe = (row) => `${terminalText(row.name)} · ${row.phase} · ${terminalText(row.device_label)} · ${row.state}\n${row.id}`;
18
+ async function approved(options, question) {
19
+ if (options.approve)
20
+ return;
21
+ if (!interactive(options) || !await selectOne(question, [{ value: false, label: 'Cancel' }, { value: true, label: 'Confirm' }], options.signal))
22
+ throw new CliError('approval_required', 'Nothing was changed. Review the action and use --approve-change for automation.', 2);
23
+ }
24
+ async function lookup(context, options, id) {
25
+ const result = await api(`${context.prefix}/local-values/${id}`, auth(context, options));
26
+ if (!localValueLookupMatches(result, context, id))
27
+ throw responseInvalid();
28
+ return result;
29
+ }
30
+ async function original(context, options, id, metadata) {
31
+ const result = await api(`${context.prefix}/local-values/${id}`, { ...auth(context, options), method: 'PUT', body: metadata });
32
+ const retained = await lookup(context, options, id);
33
+ if (retained.state === 'not_retained' || !localValueOriginalMatches(result, context, id, metadata, digest(metadata))
34
+ || !localValueOriginalMatches(retained, context, id, metadata, digest(metadata))
35
+ || nativeMetadataCanonical(result) !== nativeMetadataCanonical(retained))
36
+ throw responseInvalid();
37
+ return retained;
38
+ }
39
+ async function page(context, options, selectedPage, search) {
40
+ const result = await api(`${context.prefix}/local-values?${query({ ...options, page: String(selectedPage), search })}`, auth(context, options));
41
+ if (!localValuePageMatches(result, context, search, selectedPage))
42
+ throw responseInvalid();
43
+ return result;
44
+ }
45
+ export async function localValuesCommand(args, options, output) {
46
+ let action = args[0] ?? (interactive(options) ? '' : 'list');
47
+ if (args.length > 2 || options.request || options.status || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page)
48
+ || (options.search?.length ?? 0) > 200)
49
+ throw invalid();
50
+ // Exact OS-owned removal does not need cloud authentication or current access.
51
+ if (action === 'forget') {
52
+ if (!args[1] || !options.world || !options.workspace || options.file || options.valueStdin || options.page || options.search)
53
+ throw invalid();
54
+ await approved(options, 'Remove this private OS credential only? Cloud references, running processes and previous effects will not be erased.');
55
+ await forgetLocalValue({ workspace: identifier(options.workspace), world: identifier(options.world) }, identifier(args[1]));
56
+ output({ id: args[1], local_value_removed: true, cloud_reference_revoked: false }, 'Local value removed. Cloud reference and already issued runtime values are unchanged.');
57
+ return;
58
+ }
59
+ if (!action)
60
+ action = await selectOne('Values kept on this device', [{ value: 'list', label: 'View local value references' },
61
+ { value: 'save', label: 'Save a private local value' }], options.signal);
62
+ if (!['list', 'show', 'save', 'recover', 'remove'].includes(action) || action !== 'save' && (options.file || options.valueStdin)
63
+ || !['list', 'show', 'remove'].includes(action) && (options.page || options.search)
64
+ || ['list', 'save'].includes(action) && args[1])
65
+ throw invalid();
66
+ const context = await selectedWorld({ ...options, interactive: interactive(options) }), access = auth(context, options);
67
+ const path = `${context.prefix}/local-values`;
68
+ if (action === 'list') {
69
+ const result = await page(context, options, Number(options.page ?? 1), options.search ?? '');
70
+ output(result, [...result.results.map(describe), `${result.count} references · Page ${result.page}`,
71
+ ...(result.next ? [`Continue with --page ${result.page + 1}.`] : [])].join('\n\n'));
72
+ return;
73
+ }
74
+ let id = args[1];
75
+ if (action === 'save') {
76
+ let input;
77
+ if (options.file)
78
+ input = await readMetadata(options.file);
79
+ else {
80
+ if (!interactive(options))
81
+ throw invalid();
82
+ let page = 1, device;
83
+ while (!device) {
84
+ const rows = await api(`/api/workspaces/${context.workspace}/local-runners?page=${page}`, access);
85
+ if (!localRunnersMatch(rows, context.owner, context.workspace, '', page))
86
+ throw responseInvalid();
87
+ const choices = rows.results.filter(row => !['revoked', 'expired', 'access_removed'].includes(row.state))
88
+ .map(row => ({ value: identifier(row.device_key), label: terminalText(row.label), detail: row.state }));
89
+ if (page > 1)
90
+ choices.push({ value: 'previous', label: 'Previous page', detail: '' });
91
+ if (rows.next)
92
+ choices.push({ value: 'next', label: 'Next page', detail: '' });
93
+ choices.push({ value: 'cancel', label: 'Cancel', detail: '' });
94
+ const value = await selectOne('Enrolled device', choices, options.signal);
95
+ if (value === 'cancel')
96
+ throw new CliError('canceled', 'No value was saved.', 130);
97
+ if (value === 'next' || value === 'previous')
98
+ page += value === 'next' ? 1 : -1;
99
+ else
100
+ device = value;
101
+ }
102
+ input = { device_key: device, name: await line('Variable name', 128, options.signal), phase: await selectOne('Used during', [{ value: 'runtime', label: 'Runtime' }, { value: 'build', label: 'Build' }, { value: 'both', label: 'Build and Runtime' }], options.signal) };
103
+ }
104
+ if (!record(input) || Object.keys(input).sort().join(',') !== 'device_key,name,phase' || typeof input.name !== 'string'
105
+ || !/^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(input.name) || typeof input.device_key !== 'string'
106
+ || typeof input.phase !== 'string' || !['build', 'runtime', 'both'].includes(input.phase))
107
+ throw invalid();
108
+ const device = identifier(input.device_key);
109
+ // Prove this OS installation has the selected device identity, without
110
+ // submitting a pulse, authorizing a job or sending a value to the cloud.
111
+ await signRunnerRequest(context, device, { schema: 'firedrill.local-value-owner.v1', world_id: context.world }, options.signal);
112
+ const revision = await api(`${context.prefix}/configuration?page_size=1`, access);
113
+ if (!Number.isSafeInteger(revision.configuration_revision) || revision.configuration_revision < 1)
114
+ throw invalid();
115
+ const metadata = { name: input.name, phase: input.phase, device_key: device,
116
+ configuration_revision: revision.configuration_revision, value_stored_locally: true };
117
+ await approved(options, `Keep ${input.name} in this OS credential store and map it to ${input.phase} on this device only? Managed execution will require a different mapping.`);
118
+ id = randomUUID();
119
+ output({ id, world_id: context.world, workspace_id: context.workspace, value_uploaded: false }, `Local request ${id}\nRecover: firedrill values recover ${id} --workspace ${context.workspace} --world ${context.world}`);
120
+ let value = await readPrivateValue(options);
121
+ try {
122
+ await saveLocalValue(context, id, metadata, value);
123
+ }
124
+ finally {
125
+ value = '';
126
+ }
127
+ const result = await original(context, options, id, metadata);
128
+ output(result, describe(result));
129
+ return;
130
+ }
131
+ if (!id && interactive(options) && action !== 'recover')
132
+ id = (await choosePaged('Local value', path, context, options, row => ({ label: row.name, detail: `${row.phase} · ${row.device_label} · ${row.state}` }), false, (selectedPage, search) => page(context, options, selectedPage, search)))?.id;
133
+ if (!id)
134
+ throw new CliError('local_value_required', 'Choose a saved reference ID or use its original recovery ID.', 2);
135
+ id = identifier(id);
136
+ if (action === 'recover') {
137
+ const saved = await savedLocalValue(context, id);
138
+ try {
139
+ await approved(options, 'Continue the original local-value reference request without uploading its value?');
140
+ const prior = await lookup(context, options, id);
141
+ const result = prior.state === 'not_retained' ? await original(context, options, id, saved.metadata) : prior;
142
+ if (!localValueOriginalMatches(result, context, id, saved.metadata, digest(saved.metadata)))
143
+ throw responseInvalid();
144
+ output(result, describe(result));
145
+ }
146
+ finally {
147
+ saved.value = '';
148
+ }
149
+ return;
150
+ }
151
+ if (action === 'remove') {
152
+ await approved(options, 'Revoke this cloud reference? Its private value remains on this OS until explicitly forgotten.');
153
+ const result = await api(`${path}/${id}`, { ...access, method: 'DELETE' });
154
+ const retained = await lookup(context, options, id);
155
+ if (!localValueRegistrationMatches(result, context, id) || result.state !== 'revoked' || retained.state === 'not_retained'
156
+ || retained.state !== 'revoked' || nativeMetadataCanonical(result) !== nativeMetadataCanonical(retained))
157
+ throw responseInvalid();
158
+ output(retained, `${describe(retained)}\nRemove the private copy separately with firedrill values forget ${id} --workspace ${context.workspace} --world ${context.world}.`);
159
+ return;
160
+ }
161
+ const result = await lookup(context, options, id);
162
+ output(result, result.state === 'not_retained' ? 'No matching reference is retained. No value was read.' : describe(result));
163
+ }
@@ -0,0 +1,70 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { CliError } from './http.js';
3
+ import { selectOne } from './prompts.js';
4
+ export function loginOrganizationIdentifier(value) {
5
+ if (value === undefined)
6
+ return undefined;
7
+ if (!/^org_[A-Za-z0-9]{1,120}$/.test(value) && !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value))
8
+ throw new CliError('organization_invalid', 'Use an organization ID from firedrill workspace organizations.', 2);
9
+ return value;
10
+ }
11
+ export async function chooseLoginOrganization(read, options) {
12
+ if (options.organization) {
13
+ const page = await read('', 1, options.organization);
14
+ if (page.results.length !== 1 || page.count !== 1)
15
+ throw new CliError('organization_not_available', page.membership_sync.state === 'running'
16
+ ? 'Organization access is still being checked. Finish workspace discovery, then authorize the selected organization again.'
17
+ : 'This organization is not available to the signed-in account. No workspace access was granted.', 2);
18
+ return page.results[0].workos_organization_id;
19
+ }
20
+ // Scripts keep the provider's original grant unless they explicitly request
21
+ // another organization. No hidden prompt, all-page fetch or scope expansion.
22
+ if (options.json || !process.stdin.isTTY || !process.stderr.isTTY)
23
+ return undefined;
24
+ let number = 1, query = '';
25
+ while (true) {
26
+ const page = await read(query, number);
27
+ if (page.count === 1 && !query && page.membership_sync.state !== 'running')
28
+ return page.results[0].workos_organization_id;
29
+ if (!page.count && !query && page.membership_sync.state !== 'running')
30
+ return undefined;
31
+ const choices = page.results.map(row => ({ value: row.workos_organization_id, label: row.name,
32
+ detail: row.workos_organization_id === page.grant_organization_id ? 'Current sign-in' : '' }));
33
+ if (number > 1)
34
+ choices.push({ value: 'previous', label: 'Previous page', detail: '' });
35
+ if (page.next)
36
+ choices.push({ value: 'next', label: 'Next page', detail: '' });
37
+ if (page.membership_sync.state === 'running')
38
+ choices.push({ value: 'refresh', label: 'Check organization access again', detail: 'Access discovery is running.' });
39
+ choices.push({ value: 'search', label: query ? 'Change search' : 'Search organizations', detail: query }, { value: 'cancel', label: 'Cancel', detail: '' });
40
+ const choice = await selectOne('Organization', choices, options.signal);
41
+ if (choice === 'cancel')
42
+ throw new CliError('canceled', 'Organization selection canceled. No workspace access was granted.', 130);
43
+ if (choice === 'previous') {
44
+ number--;
45
+ continue;
46
+ }
47
+ if (choice === 'next') {
48
+ number++;
49
+ continue;
50
+ }
51
+ if (choice === 'refresh')
52
+ continue;
53
+ if (choice === 'search') {
54
+ const prompt = createInterface({ input: process.stdin, output: process.stderr, terminal: false });
55
+ try {
56
+ query = (await prompt.question('Find organizations (blank for all): ', { signal: options.signal })).trim();
57
+ if (query.length > 200 || /[\u0000-\u001f\u007f-\u009f]/.test(query))
58
+ throw new CliError('search_invalid', 'Use search text up to 200 characters without terminal controls.', 2);
59
+ number = 1;
60
+ }
61
+ finally {
62
+ prompt.close();
63
+ }
64
+ continue;
65
+ }
66
+ if (!page.results.some(row => row.workos_organization_id === choice))
67
+ throw new CliError('organization_unconfirmed', 'The organization selection could not be confirmed.', 2);
68
+ return choice;
69
+ }
70
+ }