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,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* http.v1 driver (Plan20 spec 3.3).
|
|
3
|
+
*
|
|
4
|
+
* Sends each message/task step to the approved service's loopback URL (from the
|
|
5
|
+
* supervisor's ServiceHandle) at the plan's path with POST or PUT:
|
|
6
|
+
* - `message-text` template: `{"message": <text>, "session": <session key>}`
|
|
7
|
+
* - `task-json` template: the task input itself as JSON.
|
|
8
|
+
* The session key goes into `session_header` when the plan names one. A
|
|
9
|
+
* synthetic session token from the identity grant goes into the grant's header
|
|
10
|
+
* (target-auth.ts `identityHeaders`); login flows are handled by target-auth.
|
|
11
|
+
*
|
|
12
|
+
* Responses are read with byte and time bounds. `sse` and `ndjson` streams are
|
|
13
|
+
* parsed incrementally; `response_pointer` (RFC 6901) extracts the reply from the
|
|
14
|
+
* JSON body or from each stream event (string pieces are concatenated).
|
|
15
|
+
*
|
|
16
|
+
* Requests use node:http directly (no redirects followed, no proxy, no pooling)
|
|
17
|
+
* so the driver knows whether any byte may have reached the target: a connection
|
|
18
|
+
* is treated as "written" from the moment it is established. The primitive
|
|
19
|
+
* `loopbackRequest` is shared with the signed-http-event driver.
|
|
20
|
+
*/
|
|
21
|
+
import { request as httpRequest } from 'node:http';
|
|
22
|
+
import { CliError } from '../../http.js';
|
|
23
|
+
import { redact } from '../redaction.js';
|
|
24
|
+
import { identityHeaders } from '../target-auth.js';
|
|
25
|
+
import { DRIVER_ERRORS, StepRecorder, clipText, driverBounds, driverPlanFor, emit, loopbackBase, loopbackTarget, parsePointer, planError, precheck, readPointer, resolveLoopback, serviceHandle, stepAbort, stepInput, stepTimeoutMs, unattested, validHeaderName, validHeaderValue, } from './types.js';
|
|
26
|
+
/** A request that did not produce a complete response. `written` is conservative. */
|
|
27
|
+
export class LoopbackFailure extends Error {
|
|
28
|
+
kind;
|
|
29
|
+
written;
|
|
30
|
+
status;
|
|
31
|
+
constructor(kind, written, status) {
|
|
32
|
+
super(`The local request did not complete (${kind}).`);
|
|
33
|
+
this.kind = kind;
|
|
34
|
+
this.written = written;
|
|
35
|
+
this.status = status;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Thrown by a stream consumer to stop reading. */
|
|
39
|
+
export class StreamFault extends Error {
|
|
40
|
+
kind;
|
|
41
|
+
constructor(kind) { super(`The response stream was ${kind}.`); this.kind = kind; }
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One HTTP request to a loopback address. Resolves only on a complete response.
|
|
45
|
+
* Rejects with LoopbackFailure otherwise (CliError before connecting when the
|
|
46
|
+
* address is not loopback). `refused` is reported only when the connection was
|
|
47
|
+
* refused before it was established, i.e. no byte was sent.
|
|
48
|
+
*/
|
|
49
|
+
export async function loopbackRequest(options) {
|
|
50
|
+
if (options.signal.aborted)
|
|
51
|
+
throw new LoopbackFailure('aborted', false, null);
|
|
52
|
+
const target = await resolveLoopback(options.url);
|
|
53
|
+
if (options.signal.aborted)
|
|
54
|
+
throw new LoopbackFailure('aborted', false, null);
|
|
55
|
+
return await new Promise((resolve, reject) => {
|
|
56
|
+
const state = { written: false, settled: false, status: null };
|
|
57
|
+
const chunks = [];
|
|
58
|
+
let size = 0;
|
|
59
|
+
const onAbort = () => fail('aborted');
|
|
60
|
+
const cleanup = () => options.signal.removeEventListener('abort', onAbort);
|
|
61
|
+
const fail = (kind) => {
|
|
62
|
+
if (state.settled)
|
|
63
|
+
return;
|
|
64
|
+
state.settled = true;
|
|
65
|
+
cleanup();
|
|
66
|
+
request.destroy();
|
|
67
|
+
reject(new LoopbackFailure(kind, state.written, state.status));
|
|
68
|
+
};
|
|
69
|
+
const request = httpRequest({
|
|
70
|
+
host: target.host, port: target.port, method: options.method,
|
|
71
|
+
path: `${options.url.pathname}${options.url.search}`,
|
|
72
|
+
headers: { ...options.headers, host: target.hostHeader, 'content-length': String(options.body.byteLength) },
|
|
73
|
+
agent: false,
|
|
74
|
+
});
|
|
75
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
76
|
+
request.on('socket', (socket) => {
|
|
77
|
+
if (socket.connecting)
|
|
78
|
+
socket.once('connect', () => { state.written = true; });
|
|
79
|
+
else
|
|
80
|
+
state.written = true;
|
|
81
|
+
});
|
|
82
|
+
request.on('error', (error) => {
|
|
83
|
+
fail(state.written ? 'dropped' : error.code === 'ECONNREFUSED' ? 'refused' : 'unreachable');
|
|
84
|
+
});
|
|
85
|
+
request.on('response', (response) => {
|
|
86
|
+
state.written = true;
|
|
87
|
+
state.status = response.statusCode ?? 0;
|
|
88
|
+
const streaming = !!options.onChunk && state.status >= 200 && state.status < 300;
|
|
89
|
+
response.on('data', (chunk) => {
|
|
90
|
+
if (state.settled)
|
|
91
|
+
return;
|
|
92
|
+
size += chunk.byteLength;
|
|
93
|
+
if (size > options.maxBytes) {
|
|
94
|
+
fail('oversized');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!streaming) {
|
|
98
|
+
chunks.push(chunk);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
options.onChunk(chunk);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
fail(error instanceof StreamFault ? error.kind : 'malformed');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
response.on('end', () => {
|
|
109
|
+
if (state.settled)
|
|
110
|
+
return;
|
|
111
|
+
state.settled = true;
|
|
112
|
+
cleanup();
|
|
113
|
+
resolve({ status: state.status ?? 0, headers: response.headers, body: Buffer.concat(chunks), streamed: streaming, bytes: size });
|
|
114
|
+
});
|
|
115
|
+
response.on('error', () => fail('dropped'));
|
|
116
|
+
response.on('close', () => { if (!response.complete)
|
|
117
|
+
fail('dropped'); });
|
|
118
|
+
});
|
|
119
|
+
request.end(Buffer.from(options.body.buffer, options.body.byteOffset, options.body.byteLength));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
export function failureCause(failure, abortCause) {
|
|
123
|
+
return failure.kind === 'aborted' ? (abortCause ?? 'cancelled') : failure.kind;
|
|
124
|
+
}
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Incremental stream parsing (SSE / NDJSON).
|
|
127
|
+
export class StreamCollector {
|
|
128
|
+
mode;
|
|
129
|
+
pointer;
|
|
130
|
+
bounds;
|
|
131
|
+
decoder = new TextDecoder('utf-8');
|
|
132
|
+
line = '';
|
|
133
|
+
skipLineFeed = false;
|
|
134
|
+
dataLines = [];
|
|
135
|
+
pieces = [];
|
|
136
|
+
count = 0;
|
|
137
|
+
constructor(mode, pointer, bounds) {
|
|
138
|
+
this.mode = mode;
|
|
139
|
+
this.pointer = pointer;
|
|
140
|
+
this.bounds = bounds;
|
|
141
|
+
}
|
|
142
|
+
push(chunk) { this.consume(this.decoder.decode(chunk, { stream: true })); }
|
|
143
|
+
/**
|
|
144
|
+
* Flush the trailing line/event and return the extracted reply. With a
|
|
145
|
+
* `response_pointer` that no event held, the stream is malformed, exactly like
|
|
146
|
+
* a JSON body without it: never a completed step with no reply.
|
|
147
|
+
*/
|
|
148
|
+
finish() {
|
|
149
|
+
this.consume(this.decoder.decode());
|
|
150
|
+
if (this.line) {
|
|
151
|
+
const last = this.line;
|
|
152
|
+
this.line = '';
|
|
153
|
+
this.handleLine(last);
|
|
154
|
+
}
|
|
155
|
+
if (this.mode === 'sse')
|
|
156
|
+
this.dispatch();
|
|
157
|
+
if (this.pointer && !this.pieces.length)
|
|
158
|
+
throw new StreamFault('malformed');
|
|
159
|
+
return this.output();
|
|
160
|
+
}
|
|
161
|
+
/** Reply assembled so far (used as evidence when a stream is interrupted). */
|
|
162
|
+
output() {
|
|
163
|
+
if (!this.pieces.length)
|
|
164
|
+
return null;
|
|
165
|
+
if (this.pieces.every((piece) => typeof piece === 'string'))
|
|
166
|
+
return this.pieces.join('');
|
|
167
|
+
return this.pieces.length === 1 ? this.pieces[0] : [...this.pieces];
|
|
168
|
+
}
|
|
169
|
+
consume(text) {
|
|
170
|
+
let start = 0;
|
|
171
|
+
if (this.skipLineFeed && text.charCodeAt(0) === 10)
|
|
172
|
+
start = 1;
|
|
173
|
+
this.skipLineFeed = false;
|
|
174
|
+
for (let index = start; index < text.length; index += 1) {
|
|
175
|
+
const code = text.charCodeAt(index);
|
|
176
|
+
if (code !== 10 && code !== 13)
|
|
177
|
+
continue;
|
|
178
|
+
const line = this.line + text.slice(start, index);
|
|
179
|
+
this.line = '';
|
|
180
|
+
if (code === 13) {
|
|
181
|
+
if (index + 1 < text.length && text.charCodeAt(index + 1) === 10)
|
|
182
|
+
index += 1;
|
|
183
|
+
else if (index + 1 === text.length)
|
|
184
|
+
this.skipLineFeed = true;
|
|
185
|
+
}
|
|
186
|
+
start = index + 1;
|
|
187
|
+
this.handleLine(line);
|
|
188
|
+
}
|
|
189
|
+
this.line += text.slice(start);
|
|
190
|
+
if (Buffer.byteLength(this.line, 'utf8') > this.bounds.maxLineBytes)
|
|
191
|
+
throw new StreamFault('oversized');
|
|
192
|
+
}
|
|
193
|
+
handleLine(line) {
|
|
194
|
+
if (this.mode === 'ndjson') {
|
|
195
|
+
const trimmed = line.trim();
|
|
196
|
+
if (trimmed)
|
|
197
|
+
this.addPiece(trimmed, true);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (line === '') {
|
|
201
|
+
this.dispatch();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (line.startsWith(':'))
|
|
205
|
+
return;
|
|
206
|
+
const colon = line.indexOf(':');
|
|
207
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
208
|
+
let value = colon === -1 ? '' : line.slice(colon + 1);
|
|
209
|
+
if (value.startsWith(' '))
|
|
210
|
+
value = value.slice(1);
|
|
211
|
+
if (field === 'data')
|
|
212
|
+
this.dataLines.push(value);
|
|
213
|
+
}
|
|
214
|
+
dispatch() {
|
|
215
|
+
if (!this.dataLines.length)
|
|
216
|
+
return;
|
|
217
|
+
const data = this.dataLines.join('\n');
|
|
218
|
+
this.dataLines = [];
|
|
219
|
+
if (data === '[DONE]')
|
|
220
|
+
return;
|
|
221
|
+
this.addPiece(data, false);
|
|
222
|
+
}
|
|
223
|
+
addPiece(raw, jsonRequired) {
|
|
224
|
+
this.count += 1;
|
|
225
|
+
if (this.count > this.bounds.maxStreamEvents)
|
|
226
|
+
throw new StreamFault('oversized');
|
|
227
|
+
let document;
|
|
228
|
+
let parsed = false;
|
|
229
|
+
try {
|
|
230
|
+
document = JSON.parse(raw);
|
|
231
|
+
parsed = true;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
parsed = false;
|
|
235
|
+
}
|
|
236
|
+
if (!parsed && (jsonRequired || this.pointer))
|
|
237
|
+
throw new StreamFault('malformed');
|
|
238
|
+
if (this.pointer) {
|
|
239
|
+
const value = readPointer(document, this.pointer);
|
|
240
|
+
if (value !== undefined)
|
|
241
|
+
this.pieces.push(value);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
this.pieces.push(parsed ? document : raw);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function parseBody(body, pointer) {
|
|
248
|
+
const text = body.toString('utf8');
|
|
249
|
+
let document;
|
|
250
|
+
try {
|
|
251
|
+
document = text ? JSON.parse(text) : null;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
if (pointer)
|
|
255
|
+
return { ok: false, code: DRIVER_ERRORS.response_malformed };
|
|
256
|
+
return { ok: true, value: text };
|
|
257
|
+
}
|
|
258
|
+
if (!pointer)
|
|
259
|
+
return { ok: true, value: document };
|
|
260
|
+
const value = readPointer(document, pointer);
|
|
261
|
+
return value === undefined ? { ok: false, code: DRIVER_ERRORS.response_pointer_missing } : { ok: true, value };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Redact the whole (already size-bounded) body before clipping, so a secret
|
|
265
|
+
* that straddles the clip point is never left half-visible.
|
|
266
|
+
*/
|
|
267
|
+
export function bodyPreview(body, secrets, maxChars = 4096) {
|
|
268
|
+
return clipText(redact(body.toString('utf8'), secrets), maxChars);
|
|
269
|
+
}
|
|
270
|
+
const RESERVED_HEADERS = new Set(['host', 'content-length', 'content-type', 'accept', 'transfer-encoding', 'connection']);
|
|
271
|
+
export class HttpDriver {
|
|
272
|
+
profile = 'http.v1';
|
|
273
|
+
prepared = null;
|
|
274
|
+
sessions = new Set();
|
|
275
|
+
async describe() {
|
|
276
|
+
return { available: true, reason: null };
|
|
277
|
+
}
|
|
278
|
+
async prepare(ctx) {
|
|
279
|
+
const plan = driverPlanFor(ctx, 'http.v1');
|
|
280
|
+
if (plan.method !== 'POST' && plan.method !== 'PUT')
|
|
281
|
+
throw planError('The approved HTTP method is not POST or PUT, so the driver was not prepared.');
|
|
282
|
+
if (plan.stream !== 'none' && plan.stream !== 'sse' && plan.stream !== 'ndjson')
|
|
283
|
+
throw planError('The approved stream mode is not supported, so the driver was not prepared.');
|
|
284
|
+
if (plan.request_template !== 'message-text' && plan.request_template !== 'task-json')
|
|
285
|
+
throw planError('The approved request template is not supported, so the driver was not prepared.');
|
|
286
|
+
if (plan.session_header !== null && (!validHeaderName(plan.session_header) || RESERVED_HEADERS.has(plan.session_header.toLowerCase())))
|
|
287
|
+
throw planError('The approved session header name is not allowed, so the driver was not prepared.');
|
|
288
|
+
const handle = serviceHandle(ctx, plan.service_alias);
|
|
289
|
+
const target = loopbackTarget(loopbackBase(handle.url, plan.service_alias), plan.path, plan.service_alias);
|
|
290
|
+
const pointer = parsePointer(plan.response_pointer);
|
|
291
|
+
for (const name of Object.keys(identityHeaders(ctx.identity))) {
|
|
292
|
+
const lower = name.toLowerCase();
|
|
293
|
+
if (RESERVED_HEADERS.has(lower) || (plan.session_header && lower === plan.session_header.toLowerCase()))
|
|
294
|
+
throw planError('The identity header collides with a header the driver sets, so the driver was not prepared.');
|
|
295
|
+
}
|
|
296
|
+
this.prepared = { plan, target, pointer };
|
|
297
|
+
}
|
|
298
|
+
async openSession(ctx, sessionKey) {
|
|
299
|
+
if (!this.prepared)
|
|
300
|
+
throw new CliError(DRIVER_ERRORS.session_failed, 'The HTTP driver was not prepared, so no session was opened.');
|
|
301
|
+
if (!sessionKey || !validHeaderValue(sessionKey) || sessionKey.length > 256)
|
|
302
|
+
throw new CliError(DRIVER_ERRORS.session_failed, 'The session key cannot be sent to the target, so no session was opened.');
|
|
303
|
+
const session = new HttpSession(ctx, this.prepared, sessionKey, () => this.sessions.delete(session));
|
|
304
|
+
this.sessions.add(session);
|
|
305
|
+
emit(ctx, 'action.observed', { type: 'session.opened', profile: this.profile, session_key: sessionKey });
|
|
306
|
+
return session;
|
|
307
|
+
}
|
|
308
|
+
/** The 2xx status the entry service's approved HTTP readiness check accepted. */
|
|
309
|
+
async readinessProbe(ctx) {
|
|
310
|
+
const plan = driverPlanFor(ctx, 'http.v1');
|
|
311
|
+
const ready = serviceHandle(ctx, plan.service_alias).ready();
|
|
312
|
+
if (ready?.kind !== 'http' || ready.status_code < 200 || ready.status_code > 299) {
|
|
313
|
+
throw unattested(`The HTTP target "${plan.service_alias}" needs an approved HTTP readiness check that answered 2xx before the service can admit the drill. Nothing was claimed.`);
|
|
314
|
+
}
|
|
315
|
+
return { kind: 'http', status_code: ready.status_code };
|
|
316
|
+
}
|
|
317
|
+
async dispose() {
|
|
318
|
+
for (const session of [...this.sessions])
|
|
319
|
+
await session.close();
|
|
320
|
+
this.prepared = null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
class HttpSession {
|
|
324
|
+
sessionKey;
|
|
325
|
+
ctx;
|
|
326
|
+
prepared;
|
|
327
|
+
onClose;
|
|
328
|
+
closed = false;
|
|
329
|
+
constructor(ctx, prepared, sessionKey, onClose) {
|
|
330
|
+
this.ctx = ctx;
|
|
331
|
+
this.prepared = prepared;
|
|
332
|
+
this.sessionKey = sessionKey;
|
|
333
|
+
this.onClose = onClose;
|
|
334
|
+
}
|
|
335
|
+
async invoke(step, signal) {
|
|
336
|
+
const { ctx } = this;
|
|
337
|
+
const { plan, target, pointer } = this.prepared;
|
|
338
|
+
const bounds = driverBounds(ctx);
|
|
339
|
+
const recorder = new StepRecorder(ctx, step);
|
|
340
|
+
if (this.closed)
|
|
341
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.session_closed);
|
|
342
|
+
const refused = precheck(step, this.sessionKey, ['message', 'task']);
|
|
343
|
+
if (refused)
|
|
344
|
+
return recorder.finish('failed', null, refused);
|
|
345
|
+
const input = stepInput(step);
|
|
346
|
+
let text;
|
|
347
|
+
if (plan.request_template === 'message-text') {
|
|
348
|
+
if (typeof input.input !== 'string')
|
|
349
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.step_unsupported);
|
|
350
|
+
text = JSON.stringify({ message: input.input, session: this.sessionKey });
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
if (input.kind !== 'task')
|
|
354
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.step_unsupported);
|
|
355
|
+
try {
|
|
356
|
+
text = JSON.stringify(input.input);
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
text = undefined;
|
|
360
|
+
}
|
|
361
|
+
if (text === undefined)
|
|
362
|
+
return recorder.finish('failed', null, DRIVER_ERRORS.step_unsupported);
|
|
363
|
+
}
|
|
364
|
+
const body = Buffer.from(text, 'utf8');
|
|
365
|
+
let headers;
|
|
366
|
+
try {
|
|
367
|
+
headers = {
|
|
368
|
+
'content-type': 'application/json',
|
|
369
|
+
accept: plan.stream === 'sse' ? 'text/event-stream' : plan.stream === 'ndjson' ? 'application/x-ndjson' : 'application/json',
|
|
370
|
+
...identityHeaders(ctx.identity),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
return recorder.finish('failed', null, error instanceof CliError ? error.code : DRIVER_ERRORS.session_failed);
|
|
375
|
+
}
|
|
376
|
+
if (plan.session_header)
|
|
377
|
+
headers[plan.session_header] = this.sessionKey;
|
|
378
|
+
recorder.record('action.observed', { type: 'target.exchange',
|
|
379
|
+
direction: 'to_target', kind: input.kind, session_key: this.sessionKey, method: plan.method, path: target.pathname, content: input.input,
|
|
380
|
+
});
|
|
381
|
+
const collector = plan.stream === 'none' ? null : new StreamCollector(plan.stream, pointer, bounds);
|
|
382
|
+
const abort = stepAbort(ctx, signal, stepTimeoutMs(step, bounds));
|
|
383
|
+
try {
|
|
384
|
+
const response = await loopbackRequest({
|
|
385
|
+
url: target, method: plan.method, headers, body, signal: abort.signal, maxBytes: bounds.maxResponseBytes,
|
|
386
|
+
onChunk: collector ? (chunk) => collector.push(chunk) : undefined,
|
|
387
|
+
});
|
|
388
|
+
if (response.status < 200 || response.status > 299) {
|
|
389
|
+
recorder.record('action.observed', { type: 'target.exchange', direction: 'from_target', status: response.status, body_preview: bodyPreview(response.body, ctx.secrets) });
|
|
390
|
+
return recorder.finish('failed', { status: response.status }, DRIVER_ERRORS.target_status);
|
|
391
|
+
}
|
|
392
|
+
let output;
|
|
393
|
+
if (collector) {
|
|
394
|
+
try {
|
|
395
|
+
output = collector.finish();
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
recorder.record('action.observed', { type: 'target.exchange', direction: 'from_target', status: response.status, partial: collector.output(), events: collector.count });
|
|
399
|
+
// The target accepted the request (2xx), so its effect may have happened:
|
|
400
|
+
// a non-idempotent step is unknown, never a clean failure.
|
|
401
|
+
return recorder.interrupted('malformed', true);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
const parsed = parseBody(response.body, pointer);
|
|
406
|
+
if (!parsed.ok) {
|
|
407
|
+
recorder.record('action.observed', { type: 'target.exchange', direction: 'from_target', status: response.status, body_preview: bodyPreview(response.body, ctx.secrets) });
|
|
408
|
+
// Accepted (2xx) but unusable: the effect may have happened, so a
|
|
409
|
+
// non-idempotent step is unknown rather than failed.
|
|
410
|
+
return recorder.interrupted('malformed', true);
|
|
411
|
+
}
|
|
412
|
+
output = parsed.value;
|
|
413
|
+
}
|
|
414
|
+
recorder.record('action.observed', { type: 'target.exchange',
|
|
415
|
+
direction: 'from_target', status: response.status, content: output, bytes: response.bytes,
|
|
416
|
+
...(collector ? { events: collector.count } : {}),
|
|
417
|
+
});
|
|
418
|
+
return recorder.finish('completed', output, null);
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
if (error instanceof LoopbackFailure) {
|
|
422
|
+
if (collector)
|
|
423
|
+
recorder.record('action.observed', { type: 'target.exchange', direction: 'from_target', partial: collector.output(), events: collector.count, status: error.status });
|
|
424
|
+
return recorder.interrupted(failureCause(error, abort.cause()), error.written);
|
|
425
|
+
}
|
|
426
|
+
// Refused before connecting (not loopback / unresolvable): nothing was written.
|
|
427
|
+
if (error instanceof CliError)
|
|
428
|
+
return recorder.finish('failed', null, error.code);
|
|
429
|
+
// Anything else: treat conservatively as possibly written.
|
|
430
|
+
return recorder.interrupted('thrown', true);
|
|
431
|
+
}
|
|
432
|
+
finally {
|
|
433
|
+
abort.dispose();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async close() {
|
|
437
|
+
if (this.closed)
|
|
438
|
+
return;
|
|
439
|
+
this.closed = true;
|
|
440
|
+
this.onClose();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver registry and capability detection (Plan20 spec 3.3).
|
|
3
|
+
*
|
|
4
|
+
* `driverFor(profile)` returns a NEW driver instance on every call; a drill gets
|
|
5
|
+
* its own instance (and so its own driver-owned process or browser).
|
|
6
|
+
* `detectCapabilities(plan)` reports every supported profile with availability.
|
|
7
|
+
* Desktop, audio/voice and arbitrary queue interfaces are not driver profiles
|
|
8
|
+
* and are not listed; an unknown profile is refused with
|
|
9
|
+
* `native_driver_unavailable`.
|
|
10
|
+
*/
|
|
11
|
+
import { DRIVER_PROFILES, nativeError } from '../contracts.js';
|
|
12
|
+
import { FunctionWrapperDriver } from './function-wrapper.js';
|
|
13
|
+
import { HttpDriver } from './http.js';
|
|
14
|
+
import { JsonlProcessDriver } from './jsonl-process.js';
|
|
15
|
+
import { PlaywrightDriver } from './playwright.js';
|
|
16
|
+
import { SignedEventDriver } from './signed-event.js';
|
|
17
|
+
export { DEFAULT_DRIVER_BOUNDS, DRIVER_ERRORS } from './types.js';
|
|
18
|
+
export { DELIVERY_ID_HEADER } from './signed-event.js';
|
|
19
|
+
export function driverFor(profile, options = {}) {
|
|
20
|
+
switch (profile) {
|
|
21
|
+
case 'http.v1': return new HttpDriver();
|
|
22
|
+
case 'jsonl-process.v1': return new JsonlProcessDriver();
|
|
23
|
+
case 'function-wrapper.v1': return new FunctionWrapperDriver();
|
|
24
|
+
case 'playwright.v1': return new PlaywrightDriver({ plan: options.plan ?? null });
|
|
25
|
+
case 'signed-http-event.v1': return new SignedEventDriver();
|
|
26
|
+
default:
|
|
27
|
+
throw nativeError('driver_unavailable', 'The requested driver profile is not supported by this client, so no driver was prepared.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Availability of every supported driver profile on this machine. A profile
|
|
32
|
+
* whose describe() fails is reported unavailable with a reason; nothing is
|
|
33
|
+
* started, installed or probed over the network.
|
|
34
|
+
*/
|
|
35
|
+
export async function detectCapabilities(plan) {
|
|
36
|
+
const report = [];
|
|
37
|
+
for (const profile of DRIVER_PROFILES) {
|
|
38
|
+
try {
|
|
39
|
+
const { available, reason } = await driverFor(profile, { plan }).describe();
|
|
40
|
+
report.push({ profile, available, reason: available ? null : reason ?? 'This driver is not available on this machine.' });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
report.push({ profile, available: false, reason: 'This driver could not report its availability on this machine.' });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return report;
|
|
47
|
+
}
|