firedrill-run 2.2.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +178 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +465 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1622 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +61 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,414 @@
1
+ // `firedrill library recipients | grants | share | share-request | revoke |
2
+ // dependents | bind`: reusing a library version across workspaces of one
3
+ // organization and fixing a parameterized version's values.
4
+ //
5
+ // - Sharing grants one exact immutable version to another workspace, read
6
+ // only or read and use. Only an administrator of the owner workspace shares
7
+ // or revokes, and only after the human approves. A grant never lets the
8
+ // recipient edit the source and never carries credentials. Revoking stops
9
+ // new reads and uses; copies the recipient already made stay its own.
10
+ // - The request key is printed before a grant is sent. The same key returns
11
+ // the original grant; a lost answer is read back, never sent under a new key.
12
+ // - Binding creates a new private immutable version of this workspace with the
13
+ // exact parameter values. A world uses that bound version in place of the
14
+ // parameterized one. Nothing here provisions, runs or spends anything.
15
+ import { randomUUID } from 'node:crypto';
16
+ import { CliError } from '../http.js';
17
+ import { selectOne, terminalText } from '../prompts.js';
18
+ import { selectedWorkspace } from '../world-access.js';
19
+ import { getLibraryRevision, sharedHere } from '../native/cloud-resources.js';
20
+ import { errorCode, retryTransient } from '../native/control-plane.js';
21
+ import { bindLibraryVersion, boundFrom, checkBindingValues, checkBoundVersion, DEPENDENT_KINDS, libraryId, listLibraryDependents, listLibraryGrants, listLibraryRecipients, recoverLibraryGrant, revokeLibraryGrant, shareLibraryVersion, } from '../native/library-sharing.js';
22
+ import { answerLost, recoverLostWrite, recoverMetadataRequest } from '../native/metadata-requests.js';
23
+ import { answeredRefusal } from '../native/world-reuse.js';
24
+ import { readJsonObject } from './plan-apply.js';
25
+ /** The verbs this module owns under `firedrill library`. */
26
+ export const LIBRARY_SHARING_VERBS = new Set(['recipients', 'grants', 'share', 'share-request', 'revoke', 'dependents', 'bind']);
27
+ const RECOVER_MS = 15_000;
28
+ const VALUES_BYTES = 256 * 1024;
29
+ const VALUE_PREVIEW = 120;
30
+ const tty = (options) => !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY;
31
+ const scopeOf = (options) => selectedWorkspace({ workspace: options.workspace, signal: options.signal,
32
+ interactive: tty(options), openBrowser: options.openBrowser && !options.json });
33
+ function pageNumber(value) {
34
+ if (value === undefined)
35
+ return undefined;
36
+ if (!/^[1-9][0-9]{0,6}$/.test(value))
37
+ throw new CliError('arguments_invalid', 'Use --page with a number of 1 or more.', 2);
38
+ return Number(value);
39
+ }
40
+ /**
41
+ * Who owns a version and what this workspace may do with it. root's can_use
42
+ * is false for a shared version without a use grant, and for any version whose
43
+ * resource is archived (its owner's too) until the resource is restored.
44
+ */
45
+ export function libraryAccess(scope, row) {
46
+ if (sharedHere(scope, row)) {
47
+ return `shared from workspace ${row.owner_workspace_id}: `
48
+ + `${row.can_use ? 'read and use' : 'read only here (shared read only, or its owner archived the resource)'}; never editable here`;
49
+ }
50
+ const parts = ['owned by this workspace'];
51
+ if (!row.can_edit)
52
+ parts.push('read only for your account');
53
+ if (row.state === 'archived')
54
+ parts.push('archived');
55
+ else if (!row.can_use)
56
+ parts.push('its resource is archived, so it cannot be used until the resource is restored');
57
+ return parts.join('; ');
58
+ }
59
+ /** Why a version cannot be bound or used here, with the one step that changes that. */
60
+ function unusableText(scope, row) {
61
+ if (sharedHere(scope, row)) {
62
+ return `Workspace ${row.owner_workspace_id} shared this version read only, or has since archived its resource, so it cannot be bound here. `
63
+ + 'Ask its owner for read-and-use access.';
64
+ }
65
+ if (row.state === 'archived')
66
+ return 'This version records that its resource was archived, so it cannot be bound. Restore the resource and bind its new version.';
67
+ return `Its resource ${row.resource_id} is archived. Restore it first (firedrill library history ${row.resource_id} shows the revision to restore from).`;
68
+ }
69
+ /** A bound value, shortened for the terminal. */
70
+ const preview = (value) => typeof value === 'number' ? String(value)
71
+ : JSON.stringify(value.length > VALUE_PREVIEW ? `${value.slice(0, VALUE_PREVIEW)}...` : value);
72
+ /** Parameter declaration and binding lines for one version's detail. */
73
+ export function parameterLines(row) {
74
+ const lines = [];
75
+ if (row.parameter_schema) {
76
+ lines.push(`Parameters (bind them before a world can use this version: firedrill library bind ${row.id} --file <values.json>):`);
77
+ for (const item of row.parameter_schema.parameters) {
78
+ const bounds = item.type === 'string' ? `string, at most ${item.maximum} characters` : `integer ${item.minimum}-${item.maximum}`;
79
+ lines.push(` ${terminalText(item.name)} ${bounds} at ${terminalText(item.path.join('.'))} `
80
+ + (item.required ? 'required' : `default ${terminalText(preview(item.default))}`));
81
+ }
82
+ }
83
+ if (row.parameter_binding) {
84
+ lines.push(`Bound from version ${row.parameter_binding.source_revision_id} with:`);
85
+ for (const [name, value] of Object.entries(row.parameter_binding.values))
86
+ lines.push(` ${terminalText(name)} = ${terminalText(preview(value))}`);
87
+ }
88
+ return lines;
89
+ }
90
+ function grantLine(grant) {
91
+ return ` ${grant.id} workspace ${grant.recipient_workspace_id} ${grant.allow_use ? 'read and use' : 'read only'} `
92
+ + `shared ${terminalText(grant.created_at)}${grant.revoked_at ? ` revoked ${terminalText(grant.revoked_at)}` : ''}`;
93
+ }
94
+ /** Root answers 404 for a page past the last one; that is not a missing version. */
95
+ const pastEnd = (page, otherwise) => (error) => {
96
+ if ((page ?? 1) > 1 && error instanceof CliError && error.httpStatus === 404) {
97
+ throw new CliError('page_not_found', `There is no page ${page}. Start again from --page 1.`, 2);
98
+ }
99
+ return otherwise(error);
100
+ };
101
+ /** Only an organization administrator shares, revokes or lists recipients; root says so with admin_required. */
102
+ const adminOnly = (error) => {
103
+ if (errorCode(error) === 'admin_required') {
104
+ throw new CliError('native_library_admin_required', 'Only an administrator of this organization can share library versions, revoke sharing or '
105
+ + 'list recipient workspaces. Nothing was shared or changed.', 3, error instanceof CliError ? error.httpStatus : null);
106
+ }
107
+ throw error;
108
+ };
109
+ const notOwned = (error) => {
110
+ if (error instanceof CliError && error.httpStatus === 404) {
111
+ throw new CliError('native_library_version_not_found', 'That is not a library version this workspace can read. Find versions with firedrill library.', 2);
112
+ }
113
+ throw error;
114
+ };
115
+ /** Grants are listed and revoked only through the workspace that owns the version; any other gets 404. */
116
+ const ownerOnly = (error) => {
117
+ if (error instanceof CliError && error.httpStatus === 404) {
118
+ throw new CliError('native_library_version_not_found', 'That is not a library version this workspace owns. Only the owner workspace lists '
119
+ + 'or revokes its sharing grants; find this workspace\'s versions with firedrill library.', 2);
120
+ }
121
+ throw error;
122
+ };
123
+ /**
124
+ * Root's original-grant read is administrator-only, so reading a fresh key
125
+ * confirms that before the human is asked. It changes nothing.
126
+ */
127
+ const confirmAdministrator = (scope, revisionId, signal) => recoverLibraryGrant(scope, { revisionId, requestKey: randomUUID(), signal }).catch(adminOnly);
128
+ /** Each verb's arguments and flags, checked before anything is read or sent. */
129
+ export function checkSharingArguments(args, options) {
130
+ const [verb, first, second, third] = args;
131
+ const takes = {
132
+ recipients: ['search', 'page'], grants: ['page'], share: ['request', 'approve'], 'share-request': [], revoke: ['approve'],
133
+ dependents: ['kind', 'page'], bind: ['file', 'request'],
134
+ };
135
+ const flags = { kind: '--kind', file: '--file', search: '--search', archived: '--archived',
136
+ page: '--page', expectedRevision: '--expected-revision', request: '--request', approve: '--approve-change', parameters: '--parameters',
137
+ removeParameters: '--remove-parameters' };
138
+ const extra = Object.keys(flags)
139
+ .filter((flag) => options[flag] !== undefined && options[flag] !== false && !takes[verb]?.includes(flag));
140
+ if (extra.length)
141
+ throw new CliError('arguments_invalid', `firedrill library ${verb} does not take ${extra.map((flag) => flags[flag]).join(', ')}.`, 2);
142
+ const counts = { recipients: 1, grants: 2, share: 4, 'share-request': 3, revoke: 3, dependents: 2, bind: 2 };
143
+ if (args.length !== counts[verb] || (verb === 'share' && third !== 'read' && third !== 'use')) {
144
+ throw new CliError('arguments_invalid', 'Use firedrill library recipients [--search <prefix>], library grants <version-id>, '
145
+ + 'library share <version-id> <workspace-id> read|use, library share-request <version-id> <request-key>, library revoke <version-id> <grant-id>, '
146
+ + 'library dependents <version-id> [--kind world|scenario] or library bind <version-id> [--file <values.json|->].', 2);
147
+ }
148
+ if (first !== undefined)
149
+ libraryId(first, 'library version id');
150
+ if (second !== undefined)
151
+ libraryId(second, verb === 'share' ? 'workspace id' : verb === 'revoke' ? 'grant id' : 'request key');
152
+ if (options.request !== undefined)
153
+ libraryId(options.request, 'request key');
154
+ if (verb === 'dependents' && options.kind !== undefined && !DEPENDENT_KINDS.includes(options.kind)) {
155
+ throw new CliError('arguments_invalid', `Use --kind ${DEPENDENT_KINDS.join('|')} with library dependents.`, 2);
156
+ }
157
+ pageNumber(options.page);
158
+ }
159
+ /** `share <version-id> <workspace-id> read|use [--request <key>] [--approve-change]`. */
160
+ async function share(scope, args, options, emit) {
161
+ const [, version, recipientArg, mode] = args;
162
+ const signal = options.signal;
163
+ const recipient = libraryId(recipientArg, 'workspace id');
164
+ if (recipient === scope.workspace.toLowerCase()) {
165
+ throw new CliError('arguments_invalid', 'Share with another workspace of this organization; this one already owns the version. Nothing was sent.', 2);
166
+ }
167
+ const allowUse = mode === 'use';
168
+ const access = allowUse ? 'read and use' : 'read only';
169
+ const requestKey = options.request === undefined ? randomUUID() : libraryId(options.request, 'request key');
170
+ const rerun = `firedrill library share ${version.toLowerCase()} ${recipient} ${mode} --request ${requestKey}`;
171
+ const read = `firedrill library share-request ${version.toLowerCase()} ${requestKey}`;
172
+ const row = await getLibraryRevision(scope, { revisionId: version, signal }).catch(notOwned);
173
+ if (sharedHere(scope, row)) {
174
+ throw new CliError('native_library_share_owner_only', `Only workspace ${row.owner_workspace_id}, which owns this version, can share it. Nothing was sent.`, 2);
175
+ }
176
+ // A grant already made under this key is shown, never made again. The read
177
+ // is administrator-only, so a fresh key also confirms that before the human is asked.
178
+ const original = await recoverLibraryGrant(scope, { revisionId: row.id, requestKey, signal }).catch(adminOnly);
179
+ if (original) {
180
+ if (original.recipient_workspace_id !== recipient || original.allow_use !== allowUse) {
181
+ throw new CliError('idempotency_conflict', `Request ${requestKey} shared this version with workspace ${original.recipient_workspace_id} `
182
+ + `(${original.allow_use ? 'read and use' : 'read only'}). Nothing was changed; share differently under a new request.`, 2);
183
+ }
184
+ emit('library_grant', original, `Already shared under request ${requestKey}:\n${grantLine(original)}`
185
+ + (original.revoked_at ? '\nThis grant was revoked. A request key is never renewed: to share again, start without --request.' : ''));
186
+ return;
187
+ }
188
+ if (row.state !== 'draft') {
189
+ throw new CliError('native_library_share_archived', 'This version records that its resource was archived, so it cannot be shared. Share an earlier '
190
+ + `version (firedrill library history ${row.resource_id}), or restore the resource and share its new version. Nothing was sent.`, 2);
191
+ }
192
+ emit('library_share_review', { version: row, recipient_workspace_id: recipient, allow_use: allowUse, request_key: requestKey }, [
193
+ `Share ${row.kind} ${terminalText(row.name)} v${row.revision} (version ${row.id}) with workspace ${recipient}: ${access}.`,
194
+ allowUse ? 'It can read this exact version and use it in its worlds, forks and parameter bindings.'
195
+ : 'It can read this exact version, but not use it in worlds, forks or bindings.',
196
+ 'It can never edit this version or see other versions of it. Revoking later stops new reads and uses; copies it already made stay its own.',
197
+ allowUse && !row.can_use ? 'Its resource is archived now, so that workspace can read this version but cannot use it until the resource is restored.' : null,
198
+ ].filter((line) => line !== null).join('\n'));
199
+ let approved = options.approve === true;
200
+ if (!approved && tty(options)) {
201
+ approved = await selectOne(`Share this version with workspace ${recipient} (${access})?`, [
202
+ { value: false, label: 'Not now', detail: 'Nothing is shared.' },
203
+ { value: true, label: 'Share it', detail: `Grants ${access} access to this exact version only.` },
204
+ ], signal);
205
+ }
206
+ if (!approved)
207
+ throw new CliError('library_share_consent_required', `Nothing was shared. After the human approves: ${rerun} --approve-change`, 2);
208
+ if (options.json)
209
+ emit('request_prepared', { request_key: requestKey }, '');
210
+ else
211
+ process.stderr.write(`Request key: ${requestKey} (if interrupted, rerun ${rerun} --approve-change; ${read} reads it back)\n`);
212
+ let grant;
213
+ try {
214
+ // The same key and body replay the original grant, so a retry never shares twice.
215
+ grant = await retryTransient(() => shareLibraryVersion(scope, { revisionId: row.id, recipientWorkspaceId: recipient, allowUse, requestKey, signal }), { signal, attempts: 3 });
216
+ }
217
+ catch (error) {
218
+ if (errorCode(error) === 'admin_required')
219
+ adminOnly(error);
220
+ // A refusal with the service's own code wrote nothing; only an answer that never arrived is recovered by its key.
221
+ if (signal.aborted || !answerLost(error) || answeredRefusal(error)) {
222
+ if (error instanceof CliError && error.httpStatus === 404) {
223
+ throw new CliError('native_library_recipient_unavailable', 'That workspace is not another active workspace of this organization, or this '
224
+ + 'version is no longer shareable. Find recipients with firedrill library recipients. Nothing was shared.', 2);
225
+ }
226
+ throw error;
227
+ }
228
+ const recovered = await recoverLibraryGrant(scope, { revisionId: row.id, requestKey, signal: AbortSignal.timeout(RECOVER_MS) }).catch(() => null);
229
+ if (!recovered) {
230
+ throw new CliError('native_library_share_unconfirmed', `The share was not confirmed and may still land. Read it with ${read}, or rerun ${rerun} `
231
+ + '--approve-change, which returns the original instead of sharing twice. Never share it again under a new key.');
232
+ }
233
+ if (recovered.recipient_workspace_id !== recipient || recovered.allow_use !== allowUse) {
234
+ throw new CliError('native_response_unconfirmed', `Request ${requestKey} recorded a different grant, not this share. Nothing else was sent. `
235
+ + `Read it with ${read}.`, 2);
236
+ }
237
+ grant = recovered;
238
+ }
239
+ emit('library_grant', grant, `Shared ${row.kind} ${terminalText(row.name)} v${row.revision} with workspace ${grant.recipient_workspace_id}: `
240
+ + `${grant.allow_use ? 'read and use' : 'read only'}.\n${grantLine(grant)}\nRevoke it with firedrill library revoke ${row.id} ${grant.id}`);
241
+ }
242
+ /** `revoke <version-id> <grant-id> [--approve-change]`. */
243
+ async function revoke(scope, args, options, emit) {
244
+ const [, version, grantArg] = args;
245
+ const signal = options.signal;
246
+ const revisionId = libraryId(version, 'library version id');
247
+ const grantId = libraryId(grantArg, 'grant id');
248
+ // Revoked by its exact id: root's revoke needs no listing, and grant history
249
+ // is never searched (an old grant may sit beyond any page bound). The
250
+ // administrator-only original-request read confirms access before the
251
+ // human is asked; it changes nothing.
252
+ const command = `firedrill library revoke ${revisionId} ${grantId}`;
253
+ await confirmAdministrator(scope, revisionId, signal);
254
+ let approved = options.approve === true;
255
+ if (!approved && tty(options)) {
256
+ approved = await selectOne(`Revoke sharing grant ${grantId} of library version ${revisionId}?`, [
257
+ { value: false, label: 'Not now', detail: 'Nothing is changed.' },
258
+ { value: true, label: 'Revoke', detail: 'Stops that workspace\'s new reads and uses. Copies it already made, and work already accepted, are not undone.' },
259
+ ], signal);
260
+ }
261
+ if (!approved)
262
+ throw new CliError('library_share_consent_required', `Nothing was changed. After the human approves: ${command} --approve-change`, 2);
263
+ // Idempotent: a grant revoked earlier is returned as it is, with its original revocation time.
264
+ const revoked = await retryTransient(() => revokeLibraryGrant(scope, { revisionId, grantId, signal }), { signal, attempts: 3 })
265
+ .catch((error) => {
266
+ if (errorCode(error) !== 'admin_required' && error instanceof CliError && error.httpStatus === 404) {
267
+ throw new CliError('native_library_grant_not_found', 'That is not a sharing grant of a library version this workspace owns. Nothing was changed. '
268
+ + `List them with firedrill library grants ${revisionId}.`, 2);
269
+ }
270
+ return adminOnly(error);
271
+ });
272
+ emit('library_grant', revoked, `Grant ${revoked.id} is revoked (since ${terminalText(revoked.revoked_at)}).\n${grantLine(revoked)}\n`
273
+ + `Workspace ${revoked.recipient_workspace_id} can no longer read or newly use this version. Forks and bound copies it already made stay its own, `
274
+ + 'and work it already started is reconciled, not rolled back.');
275
+ }
276
+ /** The bound version's lines. */
277
+ function boundText(bound, sourceId, lead) {
278
+ return [
279
+ `${lead} ${bound.kind} ${terminalText(bound.name)} into a new private resource ${bound.resource_id}, version ${bound.id} (v${bound.revision}).`,
280
+ ...parameterLines(bound),
281
+ `Use version ${bound.id} in a world in place of the parameterized version ${sourceId}. Nothing was provisioned or spent.`,
282
+ `Request key (rerun with --request to replay): ${bound.request_key}`,
283
+ ].join('\n');
284
+ }
285
+ /** `bind <version-id> [--file <values.json|->] [--request <key>]`. */
286
+ async function bind(scope, args, options, emit) {
287
+ const [, version] = args;
288
+ const signal = options.signal;
289
+ const sourceId = libraryId(version, 'library version id');
290
+ const values = options.file === undefined ? {} : await readJsonObject(options.file, 'the parameter values', signal, VALUES_BYTES);
291
+ const requestKey = options.request === undefined ? randomUUID() : libraryId(options.request, 'request key');
292
+ // Root returns the original under a used key before it checks current
293
+ // access, so a replay reads that original first: it still answers after
294
+ // the source was revoked or its resource archived.
295
+ if (options.request !== undefined) {
296
+ const original = await recoverMetadataRequest(scope, { kind: 'library', requestKey }, signal);
297
+ if (original.state === 'withdrawn') {
298
+ throw new CliError('native_metadata_request_withdrawn', `Request ${requestKey} was withdrawn, so it will not land. Bind again without --request `
299
+ + 'only if the human still wants it.', 2);
300
+ }
301
+ if (original.state === 'removed')
302
+ throw new CliError('native_metadata_removed', `The version bound under request ${requestKey} has since been removed.`, 2);
303
+ if (original.state === 'committed') {
304
+ const receipt = original.receipt;
305
+ if (!boundFrom(receipt, sourceId)) {
306
+ throw new CliError('idempotency_conflict', `Request ${requestKey} recorded a different library change, not a binding of version ${sourceId}. `
307
+ + 'Nothing was sent; bind without --request.', 2);
308
+ }
309
+ const recorded = receipt.parameter_binding.values;
310
+ const differ = Object.keys(values).filter((name) => recorded[name] !== values[name]);
311
+ if (differ.length) {
312
+ throw new CliError('idempotency_conflict', `Request ${requestKey} bound other values for ${differ.join(', ')}. Nothing was sent; `
313
+ + 'bind these values without --request.', 2);
314
+ }
315
+ const bound = { ...checkBoundVersion(scope, receipt, { id: sourceId }), request_key: requestKey };
316
+ emit('library_binding', bound, boundText(bound, sourceId, 'Already bound under this request key:'));
317
+ return;
318
+ }
319
+ }
320
+ const source = await getLibraryRevision(scope, { revisionId: sourceId, signal }).catch(notOwned);
321
+ if (!source.parameter_schema) {
322
+ throw new CliError('native_library_not_parameterized', 'This version declares no parameters, so there is nothing to bind. Use it directly. Nothing was sent.', 2);
323
+ }
324
+ if (!source.can_use)
325
+ throw new CliError('native_library_use_not_granted', `${unusableText(scope, source)} Nothing was sent.`, 2);
326
+ const checked = checkBindingValues(source.parameter_schema, values);
327
+ const replay = `firedrill library bind ${source.id}${options.file === undefined ? '' : ' --file <the same values>'} --request ${requestKey}`;
328
+ if (options.json)
329
+ emit('request_prepared', { request_key: requestKey }, '');
330
+ else
331
+ process.stderr.write(`Request key: ${requestKey} (rerun with --request ${requestKey} to replay this exact binding; firedrill library request ${requestKey} reads it back)\n`);
332
+ let bound;
333
+ try {
334
+ bound = await bindLibraryVersion(scope, { source, values: checked, requestKey, signal });
335
+ }
336
+ catch (error) {
337
+ // No answer: read the original back by its key. Never sent again here, never under a new key.
338
+ // A refusal with the service's own code (for example a 503 before any write) wrote nothing.
339
+ if (!answerLost(error) || answeredRefusal(error) || signal.aborted)
340
+ throw error;
341
+ const recovered = await recoverLostWrite(scope, { kind: 'library', requestKey }, { replay, withdraw: `firedrill library withdraw-request ${requestKey}` });
342
+ if (!boundFrom(recovered, source.id)) {
343
+ throw new CliError('native_response_unconfirmed', `Request ${requestKey} recorded a different library change, not this binding. Nothing else was sent.`, 2);
344
+ }
345
+ bound = { ...checkBoundVersion(scope, recovered, source), request_key: requestKey };
346
+ }
347
+ emit('library_binding', bound, boundText(bound, source.id, 'Bound'));
348
+ }
349
+ /**
350
+ * `library recipients [--search <prefix>] [--page <n>] | grants <version-id> [--page <n>]
351
+ * | share <version-id> <workspace-id> read|use [--request <key>] [--approve-change] | share-request <version-id> <key>
352
+ * | revoke <version-id> <grant-id> [--approve-change] | dependents <version-id> [--kind world|scenario] [--page <n>]
353
+ * | bind <version-id> [--file <values.json|->] [--request <key>]`.
354
+ */
355
+ export async function librarySharingCommand(args, options, emit) {
356
+ checkSharingArguments(args, options);
357
+ const [verb, first, second] = args;
358
+ const signal = options.signal;
359
+ const scope = await scopeOf(options);
360
+ if (verb === 'share')
361
+ return share(scope, args, options, emit);
362
+ if (verb === 'revoke')
363
+ return revoke(scope, args, options, emit);
364
+ if (verb === 'bind')
365
+ return bind(scope, args, options, emit);
366
+ if (verb === 'recipients') {
367
+ const number = pageNumber(options.page);
368
+ const page = await listLibraryRecipients(scope, { search: options.search, page: number, signal }).catch(pastEnd(number, adminOnly));
369
+ emit('library_recipients', page, [
370
+ `Workspaces of this organization a version can be shared with, page ${page.page} (${page.count} in all):`,
371
+ ...page.results.map((one) => ` ${one.id} ${terminalText(one.name)}`),
372
+ page.results.length ? null : ' none',
373
+ page.next ? `More: --page ${page.page + 1}` : null,
374
+ 'Share a version: firedrill library share <version-id> <workspace-id> read|use',
375
+ ].filter((line) => line !== null).join('\n'));
376
+ return;
377
+ }
378
+ if (verb === 'grants') {
379
+ const revisionId = libraryId(first, 'library version id');
380
+ const number = pageNumber(options.page);
381
+ const page = await listLibraryGrants(scope, { revisionId, page: number, signal }).catch(pastEnd(number, ownerOnly));
382
+ emit('library_grants', { revision_id: revisionId, ...page }, [
383
+ `Sharing grants of version ${revisionId}, page ${page.page} (${page.count} in all, revoked ones included):`,
384
+ ...page.results.map(grantLine),
385
+ page.results.length ? null : ' none',
386
+ page.next ? `More: --page ${page.page + 1}` : null,
387
+ `Share it: firedrill library share ${revisionId} <workspace-id> read|use`,
388
+ ].filter((line) => line !== null).join('\n'));
389
+ return;
390
+ }
391
+ if (verb === 'share-request') {
392
+ const revisionId = libraryId(first, 'library version id');
393
+ const requestKey = libraryId(second, 'request key');
394
+ const grant = await recoverLibraryGrant(scope, { revisionId, requestKey, signal }).catch(adminOnly);
395
+ emit('library_grant', grant ?? { revision_id: revisionId, request_key: requestKey, state: 'not_found' }, grant ? grantLine(grant).trim()
396
+ : `None of your sharing requests for this version is recorded under ${requestKey}. An unanswered request may still arrive. `
397
+ + 'Rerun the original share with the same --request key (it returns the original instead of sharing twice); never share it under a new key.');
398
+ return;
399
+ }
400
+ const revisionId = libraryId(first, 'library version id');
401
+ const kind = (options.kind ?? 'world');
402
+ const number = pageNumber(options.page);
403
+ const page = await listLibraryDependents(scope, { revisionId, kind, page: number, signal }).catch(pastEnd(number, notOwned));
404
+ emit('library_dependents', { revision_id: revisionId, kind, ...page }, [
405
+ `${kind === 'world' ? 'World' : 'Scenario'} versions in this workspace that reference version ${revisionId}, page ${page.page} (${page.count} in all):`,
406
+ ...page.results.map((one) => one.kind === 'world'
407
+ ? ` world ${one.world_id} revision ${one.revision} (${one.id}) ${terminalText(one.name)} ${terminalText(one.created_at)}`
408
+ : ` scenario ${terminalText(one.name)} v${one.revision} (version ${one.id}) ${terminalText(one.created_at)}`),
409
+ page.results.length ? null : ' none',
410
+ page.next ? `More: --page ${page.page + 1}` : null,
411
+ 'Each is an exact version. Other workspaces\' uses are never shown here.',
412
+ kind === 'world' ? `Scenario versions: firedrill library dependents ${revisionId} --kind scenario` : null,
413
+ ].filter((line) => line !== null).join('\n'));
414
+ }
@@ -0,0 +1,46 @@
1
+ // `library|world|register|descriptors request <key>` and `... withdraw-request <key>`:
2
+ // read an original native metadata write back by its request key, or withdraw
3
+ // it while it was never committed (see native/metadata-requests.ts). Neither
4
+ // ever sends the change again.
5
+ import { CliError } from '../http.js';
6
+ import { selectOne } from '../prompts.js';
7
+ import { metadataNoun, recoverMetadataRequest, withdrawMetadataRequest } from '../native/metadata-requests.js';
8
+ /** What to say about one original, by its state. */
9
+ function stateText(kind, key, state) {
10
+ const noun = metadataNoun(kind);
11
+ if (state === 'withdrawn')
12
+ return `The ${noun} with request key ${key} was withdrawn. It will not land.`;
13
+ if (state === 'removed')
14
+ return `The ${noun} with request key ${key} was saved and has since been removed.`;
15
+ return `No ${noun} with request key ${key} is saved yet. It may still land: replay the same command with --request ${key}, or withdraw it before making a different change.`;
16
+ }
17
+ export async function metadataRequestCommand(action, target, scope, options, emit, describe, withdrawCommand) {
18
+ const key = target.requestKey.toLowerCase();
19
+ const original = await recoverMetadataRequest(scope, target, options.signal);
20
+ if (action === 'request' || original.state !== 'not_found') {
21
+ // Committed, withdrawn or removed: there is nothing to withdraw, so this only reports it.
22
+ if (original.state === 'committed') {
23
+ emit('native_request', { kind: target.kind, request_key: key, state: 'committed', receipt: original.receipt }, `${describe(original.receipt)}\nRequest key: ${key}${action === 'withdraw-request' ? '\nIt was already saved, so there is nothing to withdraw.' : ''}`);
24
+ return;
25
+ }
26
+ emit('native_request', { kind: target.kind, request_key: key, state: original.state }, original.state === 'not_found' ? `${stateText(target.kind, key, original.state)} Withdraw it with: ${withdrawCommand}` : stateText(target.kind, key, original.state));
27
+ return;
28
+ }
29
+ if (!options.json && process.stdin.isTTY) {
30
+ const yes = await selectOne(`Withdraw this ${metadataNoun(target.kind)} (request ${key})?`, [
31
+ { value: true, label: 'Withdraw it', detail: 'It can never land after this. Nothing saved is deleted.' },
32
+ { value: false, label: 'Keep it', detail: 'It may still land.' }
33
+ ], options.signal);
34
+ if (!yes)
35
+ throw new CliError('canceled', 'The request was kept.', 130);
36
+ }
37
+ else if (!options.approve) {
38
+ throw new CliError('change_consent_required', `Withdrawing needs the human's approval. Nothing was withdrawn. After they approve: ${withdrawCommand} --approve-change`, 2);
39
+ }
40
+ const result = await withdrawMetadataRequest(scope, target, options.signal);
41
+ if (result.state === 'committed') {
42
+ emit('native_request', { kind: target.kind, request_key: key, state: 'committed', receipt: result.receipt }, `${describe(result.receipt)}\nRequest key: ${key}\nIt had already been saved, so nothing was withdrawn.`);
43
+ return;
44
+ }
45
+ emit('native_request', { kind: target.kind, request_key: key, state: 'withdrawn' }, `Withdrawn. The ${metadataNoun(target.kind)} with request key ${key} can no longer land; make a new change only if the human still wants it.`);
46
+ }