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