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,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signed-http-event.v1 driver (Plan20 spec 3.3).
|
|
3
|
+
*
|
|
4
|
+
* Delivers `event` steps to the approved loopback receiver (service handle URL)
|
|
5
|
+
* under the plan's `path_prefix`:
|
|
6
|
+
* - URL path = path_prefix + payload.path (no `..`/`.` segments, no fragment,
|
|
7
|
+
* same loopback origin);
|
|
8
|
+
* - body bytes are exactly base64-decoded `body_base64` (never re-encoded);
|
|
9
|
+
* - payload headers are passed through after validation (hop-by-hop, Host,
|
|
10
|
+
* Content-Length and driver-owned names refused);
|
|
11
|
+
* - delivery id (root's action id) in DELIVERY_ID_HEADER;
|
|
12
|
+
* - root names the signature header (`x-firedrill-signature`) but never the key:
|
|
13
|
+
* the key is the receiver's secret from the variable the approved plan names
|
|
14
|
+
* (`signing_secret_env`, required because every event names the header).
|
|
15
|
+
* HMAC-SHA256 over the exact body bytes is sent as `sha256=<lowercase hex>`.
|
|
16
|
+
* An event that asks for a signature with no secret fails without being sent,
|
|
17
|
+
* and is never recorded as the target's result.
|
|
18
|
+
*
|
|
19
|
+
* Retries: only when the connection is refused before it is established (no
|
|
20
|
+
* byte sent), at most `maxEventRetries` times with short backoff. Once a
|
|
21
|
+
* connection is established, a timeout, cancel or dropped connection is
|
|
22
|
+
* `unknown` (for every event step, idempotent or not) and never retried.
|
|
23
|
+
* Signature values, header values and the key never enter observations.
|
|
24
|
+
*/
|
|
25
|
+
import { createHmac } from 'node:crypto';
|
|
26
|
+
import { CliError } from '../../http.js';
|
|
27
|
+
import { NATIVE_ID, SecretValue } from '../contracts.js';
|
|
28
|
+
import { sha256Hex } from '../canonical.js';
|
|
29
|
+
import { LoopbackFailure, bodyPreview, failureCause, loopbackRequest } from './http.js';
|
|
30
|
+
import { DRIVER_ERRORS, OwnDigest, StepRecorder, abortableDelay, driverBounds, driverPlanFor, emit, loopbackBase, planError, precheck, serviceHandle, stepAbort, stepTimeoutMs, unattested, validHeaderName, validHeaderValue, } from './types.js';
|
|
31
|
+
/** Header carrying the cloud delivery id (pending root W11 freeze). */
|
|
32
|
+
export const DELIVERY_ID_HEADER = 'firedrill-delivery-id';
|
|
33
|
+
const FORBIDDEN_HEADERS = new Set([
|
|
34
|
+
'host', 'content-length', 'transfer-encoding', 'connection', 'keep-alive', 'upgrade', 'te', 'trailer', 'expect',
|
|
35
|
+
'proxy-authorization', 'proxy-authenticate', 'proxy-connection', DELIVERY_ID_HEADER,
|
|
36
|
+
]);
|
|
37
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
38
|
+
const MAX_EVENT_HEADERS = 64;
|
|
39
|
+
function validPrefix(prefix) {
|
|
40
|
+
if (!prefix.startsWith('/') || prefix.startsWith('//') || /[\\?#\r\n\0%]/.test(prefix))
|
|
41
|
+
return false;
|
|
42
|
+
return !prefix.split('/').some((segment) => segment === '..' || segment === '.');
|
|
43
|
+
}
|
|
44
|
+
/** Compose the delivery URL or return null when the path is not acceptable. */
|
|
45
|
+
function deliveryUrl(prepared, eventPath) {
|
|
46
|
+
if (typeof eventPath !== 'string' || !eventPath.startsWith('/') || eventPath.startsWith('//') || eventPath.length > 2048)
|
|
47
|
+
return null;
|
|
48
|
+
if (/[\\#\r\n\0\s]/.test(eventPath))
|
|
49
|
+
return null;
|
|
50
|
+
const query = eventPath.indexOf('?');
|
|
51
|
+
const pathname = query === -1 ? eventPath : eventPath.slice(0, query);
|
|
52
|
+
if (pathname.split('/').some((segment) => segment === '..' || segment === '.' || /^%2e/i.test(segment) || /%2f|%5c/i.test(segment)))
|
|
53
|
+
return null;
|
|
54
|
+
let url;
|
|
55
|
+
try {
|
|
56
|
+
url = new URL(`${prepared.prefix}${eventPath}`, prepared.base);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (url.origin !== prepared.base.origin)
|
|
62
|
+
return null;
|
|
63
|
+
if (url.pathname !== prepared.prefix && !url.pathname.startsWith(prepared.prefix.endsWith('/') ? prepared.prefix : `${prepared.prefix}/`))
|
|
64
|
+
return null;
|
|
65
|
+
return url;
|
|
66
|
+
}
|
|
67
|
+
function decodeBody(encoded) {
|
|
68
|
+
if (typeof encoded !== 'string' || !BASE64.test(encoded))
|
|
69
|
+
return null;
|
|
70
|
+
return Buffer.from(encoded, 'base64');
|
|
71
|
+
}
|
|
72
|
+
export class SignedEventDriver {
|
|
73
|
+
profile = 'signed-http-event.v1';
|
|
74
|
+
prepared = null;
|
|
75
|
+
sessions = new Set();
|
|
76
|
+
async describe() {
|
|
77
|
+
return { available: true, reason: null };
|
|
78
|
+
}
|
|
79
|
+
async prepare(ctx) {
|
|
80
|
+
const plan = driverPlanFor(ctx, 'signed-http-event.v1');
|
|
81
|
+
if (!validPrefix(plan.path_prefix))
|
|
82
|
+
throw planError('The approved event path prefix is not a plain absolute path, so the event driver was not prepared.');
|
|
83
|
+
const handle = serviceHandle(ctx, plan.service_alias);
|
|
84
|
+
const base = loopbackBase(handle.url, plan.service_alias);
|
|
85
|
+
const prefix = plan.path_prefix.length > 1 ? plan.path_prefix.replace(/\/+$/, '') : '';
|
|
86
|
+
const signingKey = ctx.eventSigningKey instanceof SecretValue ? ctx.eventSigningKey : null;
|
|
87
|
+
if ((plan.signing_secret_env !== undefined) !== (signingKey !== null))
|
|
88
|
+
throw planError('The approved event signing secret is not available, so the event driver was not prepared.');
|
|
89
|
+
this.prepared = { plan, base, prefix, signingKey };
|
|
90
|
+
}
|
|
91
|
+
async openSession(ctx, sessionKey) {
|
|
92
|
+
if (!this.prepared)
|
|
93
|
+
throw new CliError(DRIVER_ERRORS.session_failed, 'The event driver was not prepared, so no session was opened.');
|
|
94
|
+
const session = new SignedEventSession(ctx, this.prepared, sessionKey, () => this.sessions.delete(session));
|
|
95
|
+
this.sessions.add(session);
|
|
96
|
+
emit(ctx, 'action.observed', { type: 'session.opened', profile: this.profile, session_key: sessionKey });
|
|
97
|
+
return session;
|
|
98
|
+
}
|
|
99
|
+
/** SHA-256 of the body the entry service returned to its approved HTTP readiness check. */
|
|
100
|
+
async readinessProbe(ctx) {
|
|
101
|
+
const plan = driverPlanFor(ctx, 'signed-http-event.v1');
|
|
102
|
+
const ready = serviceHandle(ctx, plan.service_alias).ready();
|
|
103
|
+
if (ready?.kind !== 'http' || ready.body_sha256 === null) {
|
|
104
|
+
throw unattested(`The event target "${plan.service_alias}" needs an approved HTTP readiness check whose response body is at most 64 KiB before the service can admit the drill. Nothing was claimed.`);
|
|
105
|
+
}
|
|
106
|
+
return { kind: 'signed_event_handshake', response_sha256: ready.body_sha256 };
|
|
107
|
+
}
|
|
108
|
+
async dispose() {
|
|
109
|
+
for (const session of [...this.sessions])
|
|
110
|
+
await session.close();
|
|
111
|
+
this.prepared = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
class SignedEventSession {
|
|
115
|
+
sessionKey;
|
|
116
|
+
ctx;
|
|
117
|
+
prepared;
|
|
118
|
+
onClose;
|
|
119
|
+
closed = false;
|
|
120
|
+
constructor(ctx, prepared, sessionKey, onClose) {
|
|
121
|
+
this.ctx = ctx;
|
|
122
|
+
this.prepared = prepared;
|
|
123
|
+
this.sessionKey = sessionKey;
|
|
124
|
+
this.onClose = onClose;
|
|
125
|
+
}
|
|
126
|
+
async invoke(step, signal) {
|
|
127
|
+
const { ctx, prepared } = this;
|
|
128
|
+
const bounds = driverBounds(ctx);
|
|
129
|
+
const payload = step.payload;
|
|
130
|
+
// Only the locally approved secret signs; the cloud names the header, never the key.
|
|
131
|
+
const signingKey = prepared.signingKey;
|
|
132
|
+
const recorder = new StepRecorder(ctx, step, signingKey ? [signingKey] : []);
|
|
133
|
+
if (this.closed)
|
|
134
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.session_closed);
|
|
135
|
+
const refused = precheck(step, this.sessionKey, ['event']);
|
|
136
|
+
if (refused)
|
|
137
|
+
return recorder.finish('failed', null, refused);
|
|
138
|
+
if (payload.kind !== 'event')
|
|
139
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.step_unsupported);
|
|
140
|
+
const invalid = (reason) => {
|
|
141
|
+
recorder.record('action.observed', { type: 'event.refused', delivery_id: payload.delivery_id, reason });
|
|
142
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.event_invalid);
|
|
143
|
+
};
|
|
144
|
+
if (typeof payload.delivery_id !== 'string' || !NATIVE_ID.test(payload.delivery_id))
|
|
145
|
+
return invalid('delivery id is not a valid identifier');
|
|
146
|
+
if (payload.method !== 'POST' && payload.method !== 'PUT' && payload.method !== 'PATCH')
|
|
147
|
+
return invalid('method is not POST, PUT or PATCH');
|
|
148
|
+
const url = deliveryUrl(prepared, payload.path);
|
|
149
|
+
if (!url)
|
|
150
|
+
return invalid('path is not a plain path under the approved prefix');
|
|
151
|
+
const body = decodeBody(payload.body_base64);
|
|
152
|
+
if (!body)
|
|
153
|
+
return invalid('body is not valid base64');
|
|
154
|
+
const headers = {};
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
const entries = Object.entries(payload.headers ?? {});
|
|
157
|
+
if (entries.length > MAX_EVENT_HEADERS)
|
|
158
|
+
return invalid('too many headers');
|
|
159
|
+
const signatureHeader = payload.signature_header;
|
|
160
|
+
// Never sent unsigned when a signature is asked for; with no header named, nothing is signed.
|
|
161
|
+
if (signatureHeader && signingKey === null)
|
|
162
|
+
return invalid('the event asks for a signature but the approved plan names no signing secret');
|
|
163
|
+
if (signatureHeader && (!validHeaderName(signatureHeader) || FORBIDDEN_HEADERS.has(signatureHeader.toLowerCase())))
|
|
164
|
+
return invalid('signature header name is not allowed');
|
|
165
|
+
for (const [name, value] of entries) {
|
|
166
|
+
const lower = name.toLowerCase();
|
|
167
|
+
if (!validHeaderName(name) || typeof value !== 'string' || !validHeaderValue(value))
|
|
168
|
+
return invalid('a header name or value is not allowed');
|
|
169
|
+
if (FORBIDDEN_HEADERS.has(lower) || lower.startsWith('proxy-') || seen.has(lower))
|
|
170
|
+
return invalid('a header is reserved or duplicated');
|
|
171
|
+
if (signatureHeader && lower === signatureHeader.toLowerCase())
|
|
172
|
+
return invalid('a header collides with the signature header');
|
|
173
|
+
seen.add(lower);
|
|
174
|
+
headers[name] = value;
|
|
175
|
+
}
|
|
176
|
+
headers[DELIVERY_ID_HEADER] = payload.delivery_id;
|
|
177
|
+
if (signingKey && signatureHeader)
|
|
178
|
+
headers[signatureHeader] = `sha256=${createHmac('sha256', signingKey.reveal()).update(body).digest('hex')}`;
|
|
179
|
+
recorder.record('action.observed', {
|
|
180
|
+
type: 'event.delivery', delivery_id: payload.delivery_id, method: payload.method, path: `${url.pathname}${url.search}`,
|
|
181
|
+
body_bytes: body.byteLength, body_sha256: new OwnDigest(sha256Hex(body)), body_preview: bodyPreview(body, ctx.secrets),
|
|
182
|
+
header_names: Object.keys(headers).filter((name) => name !== signatureHeader), signed: !!signingKey,
|
|
183
|
+
});
|
|
184
|
+
const abort = stepAbort(ctx, signal, stepTimeoutMs(step, bounds));
|
|
185
|
+
let attempts = 0;
|
|
186
|
+
try {
|
|
187
|
+
for (;;) {
|
|
188
|
+
attempts += 1;
|
|
189
|
+
try {
|
|
190
|
+
const response = await loopbackRequest({
|
|
191
|
+
url, method: payload.method, headers, body, signal: abort.signal, maxBytes: bounds.maxResponseBytes,
|
|
192
|
+
});
|
|
193
|
+
const ok = response.status >= 200 && response.status < 300;
|
|
194
|
+
const text = bodyPreview(response.body, ctx.secrets, bounds.maxResponseBytes);
|
|
195
|
+
let parsed = text;
|
|
196
|
+
try {
|
|
197
|
+
parsed = text ? JSON.parse(text) : null;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
parsed = text;
|
|
201
|
+
}
|
|
202
|
+
const output = { status: response.status, delivery_id: payload.delivery_id, body: parsed };
|
|
203
|
+
recorder.record('action.observed', { type: 'event.response', delivery_id: payload.delivery_id, status: response.status, attempts, body: parsed });
|
|
204
|
+
const result = recorder.finish(ok ? 'completed' : 'failed', output, ok ? null : DRIVER_ERRORS.target_status);
|
|
205
|
+
// bodyPreview can already redact or clip before StepRecorder sees it.
|
|
206
|
+
// Carry that producer fact without retaining the original body.
|
|
207
|
+
if (text !== response.body.toString('utf8'))
|
|
208
|
+
result.output_coverage = 'redacted';
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
if (error instanceof LoopbackFailure && error.kind === 'refused' && !error.written && attempts <= bounds.maxEventRetries) {
|
|
213
|
+
try {
|
|
214
|
+
await abortableDelay(100 * 2 ** (attempts - 1), abort.signal);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return recorder.interrupted(abort.cause() ?? 'cancelled', false, { unknownAfterWrite: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
if (error instanceof LoopbackFailure) {
|
|
227
|
+
recorder.record('action.observed', { type: 'event.interrupted', delivery_id: payload.delivery_id, attempts, cause: error.kind });
|
|
228
|
+
return recorder.interrupted(failureCause(error, abort.cause()), error.written, { unknownAfterWrite: true });
|
|
229
|
+
}
|
|
230
|
+
if (error instanceof CliError)
|
|
231
|
+
return recorder.finish('failed', null, error.code);
|
|
232
|
+
return recorder.interrupted('thrown', true, { unknownAfterWrite: true });
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
abort.dispose();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async close() {
|
|
239
|
+
if (this.closed)
|
|
240
|
+
return;
|
|
241
|
+
this.closed = true;
|
|
242
|
+
this.onClose();
|
|
243
|
+
}
|
|
244
|
+
}
|