firedrill-run 2.2.5 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -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 +506 -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 +1627 -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 +96 -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,99 @@
|
|
|
1
|
+
import { driverBasisMatches } from './agent-execution-contracts.js';
|
|
2
|
+
const object = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
3
|
+
const id = (value) => typeof value === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
|
|
4
|
+
const hash = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
5
|
+
const integer = (value, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum && value <= maximum;
|
|
6
|
+
const text = (value, maximum) => typeof value === 'string' && value.length <= maximum && !/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(value);
|
|
7
|
+
const nullableText = (value, maximum) => value === null || text(value, maximum);
|
|
8
|
+
const kinds = ['conversation', 'http_task', 'browser_workflow', 'adaptive_browser', 'desktop', 'process', 'event'];
|
|
9
|
+
const changes = ['current', 'added', 'removed', 'changed', 'unchanged'];
|
|
10
|
+
const exact = (value, keys) => Object.keys(value).sort().join(',') === keys.sort().join(',');
|
|
11
|
+
function locator(value) {
|
|
12
|
+
return object(value) && exact(value, ['kind', 'value', 'name', 'exact', 'frames', 'presentation_omitted'])
|
|
13
|
+
&& ['role', 'label', 'test_id', 'text', 'css'].includes(String(value.kind)) && nullableText(value.value, 2048)
|
|
14
|
+
&& nullableText(value.name, 2048) && typeof value.exact === 'boolean' && typeof value.presentation_omitted === 'boolean'
|
|
15
|
+
&& Array.isArray(value.frames) && value.frames.length <= 8 && value.frames.every(one => text(one, 2048))
|
|
16
|
+
&& (value.value !== null || value.presentation_omitted);
|
|
17
|
+
}
|
|
18
|
+
function source(value) {
|
|
19
|
+
return object(value) && exact(value, ['key', 'root', 'path', 'sha256', 'line', 'presentation_omitted'])
|
|
20
|
+
&& hash(value.key) && nullableText(value.root, 128) && nullableText(value.path, 2048) && hash(value.sha256)
|
|
21
|
+
&& (value.line === null || integer(value.line, 1, 2147483647)) && typeof value.presentation_omitted === 'boolean'
|
|
22
|
+
&& (value.root !== null && value.path !== null || value.presentation_omitted);
|
|
23
|
+
}
|
|
24
|
+
function action(value) {
|
|
25
|
+
return object(value) && exact(value, ['id', 'position', 'action', 'effect', 'page', 'timeout_ms', 'locator', 'path',
|
|
26
|
+
'input_path', 'output', 'point', 'has_literal_value', 'presentation_omitted', 'source'])
|
|
27
|
+
&& text(value.id, 128) && /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(value.id)
|
|
28
|
+
&& integer(value.position, 1, 10000) && text(value.action, 32)
|
|
29
|
+
&& ['navigate', 'click', 'fill', 'select', 'check', 'press', 'wait_visible', 'wait_hidden', 'wait_text', 'extract', 'switch_page', 'close_page', 'upload', 'download', 'dialog', 'point'].includes(value.action)
|
|
30
|
+
&& ['read', 'write'].includes(String(value.effect)) && integer(value.page, 0, 31) && integer(value.timeout_ms, 100, 300000)
|
|
31
|
+
&& (value.locator === null || locator(value.locator)) && nullableText(value.path, 2048)
|
|
32
|
+
&& nullableText(value.input_path, 256) && nullableText(value.output, 64)
|
|
33
|
+
&& (value.point === null || Array.isArray(value.point) && value.point.length === 2 && value.point.every(one => integer(one, Number.MIN_SAFE_INTEGER)))
|
|
34
|
+
&& typeof value.has_literal_value === 'boolean' && typeof value.presentation_omitted === 'boolean' && source(value.source);
|
|
35
|
+
}
|
|
36
|
+
function completion(value) {
|
|
37
|
+
return object(value) && exact(value, ['kind', 'label', 'locator', 'result_locator', 'page', 'attribute', 'literal_value_omitted'])
|
|
38
|
+
&& ['source_dom_marker', 'process_exit', 'result_file', 'source_status_endpoint', 'not_required'].includes(String(value.kind))
|
|
39
|
+
&& text(value.label, 160) && (value.locator === null || locator(value.locator))
|
|
40
|
+
&& (value.result_locator === null || locator(value.result_locator)) && (value.page === null || integer(value.page, 0, 31))
|
|
41
|
+
&& nullableText(value.attribute, 128) && typeof value.literal_value_omitted === 'boolean';
|
|
42
|
+
}
|
|
43
|
+
export function driverReviewMatches(value, world, selection = {}) {
|
|
44
|
+
if (!object(value) || !exact(value, ['schema', 'id', 'world_id', 'validation_job_id', 'contract_sha256', 'review_sha256', 'revision',
|
|
45
|
+
'kind', 'origin', 'current', 'available', 'created_at', 'source', 'configuration', 'basis', 'action_count', 'source_count',
|
|
46
|
+
'omitted_count', 'scope', 'completion', 'previous']) || value.schema !== 'firedrill.driver-review.v1'
|
|
47
|
+
|| !id(value.id) || value.world_id !== world || !id(world) || !id(value.validation_job_id)
|
|
48
|
+
|| selection.id !== undefined && value.id !== selection.id || selection.contract !== undefined && value.contract_sha256 !== selection.contract
|
|
49
|
+
|| !hash(value.contract_sha256) || !hash(value.review_sha256) || !text(value.revision, 128) || !kinds.includes(String(value.kind))
|
|
50
|
+
|| !['guest_report', 'customer_device'].includes(String(value.origin)) || typeof value.current !== 'boolean' || typeof value.available !== 'boolean'
|
|
51
|
+
|| typeof value.created_at !== 'string' || !Number.isFinite(Date.parse(value.created_at))
|
|
52
|
+
|| !object(value.source) || !exact(value.source, ['id', 'revision']) || !id(value.source.id) || !text(value.source.revision, 256)
|
|
53
|
+
|| !object(value.configuration) || !exact(value.configuration, ['id', 'revision']) || !id(value.configuration.id) || !integer(value.configuration.revision, 1)
|
|
54
|
+
|| !driverBasisMatches(value.basis) || !integer(value.action_count, 0, 10000) || !integer(value.source_count, 0, 15000)
|
|
55
|
+
|| !integer(value.omitted_count) || !Array.isArray(value.scope) || value.scope.length > 64
|
|
56
|
+
|| !value.scope.every(one => object(one) && exact(one, ['name', 'label', 'value', 'omitted']) && text(one.name, 128)
|
|
57
|
+
&& text(one.label, 80) && nullableText(one.value, 16384) && typeof one.omitted === 'boolean')
|
|
58
|
+
|| new Set(value.scope.map(one => one.name)).size !== value.scope.length
|
|
59
|
+
|| !(value.completion === null || completion(value.completion)))
|
|
60
|
+
return false;
|
|
61
|
+
if (!value.available && (value.scope.length !== 0 || value.completion !== null))
|
|
62
|
+
return false;
|
|
63
|
+
return value.previous === null || object(value.previous) && exact(value.previous, ['id', 'contract_sha256', 'revision', 'kind', 'available'])
|
|
64
|
+
&& id(value.previous.id) && value.previous.id !== value.id && hash(value.previous.contract_sha256)
|
|
65
|
+
&& text(value.previous.revision, 128) && kinds.includes(String(value.previous.kind)) && typeof value.previous.available === 'boolean';
|
|
66
|
+
}
|
|
67
|
+
function page(value, world, review, selection) {
|
|
68
|
+
if (!object(value) || !exact(value, ['results', 'count', 'page', 'page_size', 'next', 'previous', 'world_id', 'review_id',
|
|
69
|
+
'review_sha256', 'baseline_id', 'baseline_review_sha256', 'query', 'change']) || value.world_id !== world || review.world_id !== world
|
|
70
|
+
|| value.review_id !== review.id || value.review_sha256 !== review.review_sha256 || value.baseline_id !== (selection.baseline ?? null)
|
|
71
|
+
|| value.baseline_id === null && value.baseline_review_sha256 !== null || value.baseline_id !== null && (!id(value.baseline_id) || !hash(value.baseline_review_sha256))
|
|
72
|
+
|| !integer(value.count, 0, 30000) || !integer(value.page, 1) || !integer(value.page_size, 1, 100)
|
|
73
|
+
|| selection.page !== undefined && value.page !== selection.page || !Array.isArray(value.results) || value.results.length > value.page_size
|
|
74
|
+
|| value.query !== (selection.query ?? '').trim() || value.change !== (selection.change ?? 'all'))
|
|
75
|
+
return false;
|
|
76
|
+
const prefix = `/api/workspaces/`;
|
|
77
|
+
return [value.next, value.previous].every(one => one === null || typeof one === 'string'
|
|
78
|
+
&& (one.startsWith(prefix) || /^https?:\/\//.test(one)) && !/[\x00-\x20]/.test(one));
|
|
79
|
+
}
|
|
80
|
+
function changed(value, baseline) {
|
|
81
|
+
return changes.includes(String(value.change)) && typeof value.omitted_change === 'boolean'
|
|
82
|
+
&& (baseline === null ? value.change === 'current' && value.before === null && value.after !== null
|
|
83
|
+
: value.change !== 'current' && (value.change === 'added' ? value.before === null && value.after !== null
|
|
84
|
+
: value.change === 'removed' ? value.before !== null && value.after === null : value.before !== null && value.after !== null));
|
|
85
|
+
}
|
|
86
|
+
export function driverReviewActionsMatch(value, world, review, selection = {}) {
|
|
87
|
+
return page(value, world, review, selection) && new Set(value.results.map(one => object(one) ? one.key : null)).size === value.results.length
|
|
88
|
+
&& value.results.every(one => object(one) && exact(one, ['key', 'change', 'before', 'after', 'omitted_change', 'order_changed'])
|
|
89
|
+
&& text(one.key, 128) && changed(one, selection.baseline ?? null) && typeof one.order_changed === 'boolean'
|
|
90
|
+
&& (one.before === null || action(one.before) && one.before.id === one.key)
|
|
91
|
+
&& (one.after === null || action(one.after) && one.after.id === one.key));
|
|
92
|
+
}
|
|
93
|
+
export function driverReviewSourcesMatch(value, world, review, selection = {}) {
|
|
94
|
+
return page(value, world, review, selection) && new Set(value.results.map(one => object(one) ? one.key : null)).size === value.results.length
|
|
95
|
+
&& value.results.every(one => object(one) && exact(one, ['key', 'change', 'before', 'after', 'omitted_change'])
|
|
96
|
+
&& hash(one.key) && changed(one, selection.baseline ?? null)
|
|
97
|
+
&& (one.before === null || source(one.before) && one.before.key === one.key)
|
|
98
|
+
&& (one.after === null || source(one.after) && one.after.key === one.key));
|
|
99
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Private evidence identities shared by the portal, native CLI and MCP. */
|
|
2
|
+
import { fileMatchesTrial } from './media-contracts.js';
|
|
3
|
+
const uuid = (value) => typeof value === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value);
|
|
4
|
+
const digest = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
5
|
+
const date = (value) => typeof value === 'string' && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
6
|
+
const natural = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
7
|
+
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
+
const categories = new Set(['message', 'driver', 'action', 'service', 'state', 'file', 'assessment', 'event']);
|
|
9
|
+
const kinds = new Set(['transcript', 'tool_trace', 'trace', 'result', 'state_snapshot', 'dataset_application', 'browser_capture', 'desktop_capture', 'other',
|
|
10
|
+
'recording_combined', 'recording_stereo', 'recording_customer', 'recording_assistant', 'recording_segment']);
|
|
11
|
+
function page(value) {
|
|
12
|
+
return Array.isArray(value.results) && value.results.length <= 100
|
|
13
|
+
&& natural(value.count) && natural(value.page) && value.page > 0 && natural(value.page_size) && value.page_size > 0 && value.page_size <= 100
|
|
14
|
+
&& (value.next === null || typeof value.next === 'string') && (value.previous === null || typeof value.previous === 'string');
|
|
15
|
+
}
|
|
16
|
+
export function evidenceFileMatches(value, prefix, execution) {
|
|
17
|
+
if (!record(value) || !uuid(value.id) || value.execution_id !== execution || !natural(value.attempt_number) || value.attempt_number < 1
|
|
18
|
+
|| typeof value.kind !== 'string' || !kinds.has(value.kind) || !natural(value.size) || !date(value.created_at)
|
|
19
|
+
|| !['guest_report', 'customer_device'].includes(String(value.origin)) || typeof value.content_type !== 'string' || value.content_type.length > 255
|
|
20
|
+
|| value.filename !== `evidence-${value.id}.${value.kind === 'tool_trace' ? 'jsonl' : value.content_type === 'audio/wav' ? 'wav' : value.content_type === 'application/json' ? 'json' : 'bin'}`
|
|
21
|
+
|| value.generation !== null && (typeof value.generation !== 'string' || !/^[0-9]{1,30}$/.test(value.generation))
|
|
22
|
+
|| value.sha256 !== null && !digest(value.sha256) || value.expires_at !== null && !date(value.expires_at)
|
|
23
|
+
|| !['available', 'expired', 'deleted', 'pending', 'unavailable'].includes(String(value.state))
|
|
24
|
+
|| value.media !== null && (!record(value.media) || value.media.id !== value.id || value.state !== 'available'
|
|
25
|
+
|| value.media.preview !== null && !record(value.media.preview) || value.media.audio !== null && !record(value.media.audio)
|
|
26
|
+
|| !fileMatchesTrial(value.media, prefix)))
|
|
27
|
+
return false;
|
|
28
|
+
return value.state === 'available' ? digest(value.sha256) && value.generation !== null && value.download_path === `${prefix}/evidence/${value.id}/bytes`
|
|
29
|
+
: value.download_path === null;
|
|
30
|
+
}
|
|
31
|
+
export function evidenceManifestMatches(value, prefix, execution) {
|
|
32
|
+
return record(value) && page(value) && value.schema === 'firedrill.evidence-manifest.v1' && value.execution_id === execution && date(value.through_at)
|
|
33
|
+
&& value.results.every(row => evidenceFileMatches(row, prefix, execution));
|
|
34
|
+
}
|
|
35
|
+
export function timelineMatches(value, prefix, execution) {
|
|
36
|
+
const selected = prefix.match(/^\/api\/workspaces\/[a-f0-9-]+\/campaigns\/([a-f0-9-]+)\/trials\/([a-f0-9-]+)$/);
|
|
37
|
+
if (!selected || selected[2] !== execution)
|
|
38
|
+
return false;
|
|
39
|
+
const portal = `/campaigns/${selected[1]}/trials/${execution}`;
|
|
40
|
+
return record(value) && page(value) && value.execution_id === execution && value.results.every(one => record(one)
|
|
41
|
+
&& uuid(one.id) && one.execution_id === execution && categories.has(String(one.category)) && typeof one.label === 'string' && one.label.length <= 300
|
|
42
|
+
&& (one.attempt_number === null || natural(one.attempt_number) && one.attempt_number > 0) && (one.position === null || natural(one.position))
|
|
43
|
+
&& (one.observed_at === null || typeof one.observed_at === 'number' && Number.isFinite(one.observed_at) && one.observed_at > 0)
|
|
44
|
+
&& date(one.recorded_at) && one.time_basis === (one.observed_at === null ? 'recorded' : 'observed')
|
|
45
|
+
&& (one.evidence_id === null || uuid(one.evidence_id)) && (one.origin === null || ['guest_report', 'customer_device'].includes(String(one.origin)))
|
|
46
|
+
&& (one.category === 'service' ? typeof one.receipt_available === 'boolean' && one.observed_at === null && one.evidence_id === null
|
|
47
|
+
&& one.position === null && (one.receipt_available ? ['guest_report', 'customer_device'].includes(String(one.origin)) && natural(one.attempt_number) && one.attempt_number > 0
|
|
48
|
+
: one.origin === null && one.attempt_number === null && one.label === 'Service receipt unavailable') : one.receipt_available === null)
|
|
49
|
+
&& one.detail_path === (one.category === 'message' ? `${prefix}/messages/${one.id}`
|
|
50
|
+
: one.category === 'action' ? `${prefix}/actions/${one.id}` : one.category === 'driver' ? `${prefix}/timeline/driver/${one.id}`
|
|
51
|
+
: one.category === 'service' ? `${prefix}/requests/${one.id}`
|
|
52
|
+
: one.category === 'assessment' ? `${prefix}/assessments/${one.id}` : one.category === 'state' ? `${prefix}/state-comparisons/checkpoints/${one.id}` : null)
|
|
53
|
+
&& one.portal_path === (one.category === 'message' ? `${portal}?view=conversation&record=${one.id}`
|
|
54
|
+
: one.category === 'action' ? `${portal}?view=actions&record=${one.id}` : one.category === 'driver' ? `${portal}?view=timeline&step=${one.id}`
|
|
55
|
+
: one.category === 'service' ? `${portal}?view=requests&request=${one.id}`
|
|
56
|
+
: one.category === 'assessment' ? `${portal}?view=assessment&assessment=${one.id}` : one.category === 'state' ? `${portal}?view=state&checkpoint=${one.id}`
|
|
57
|
+
: one.category === 'file' ? `${portal}?view=files&artifact=${one.id}` : `${portal}?view=timeline`));
|
|
58
|
+
}
|
|
59
|
+
export function driverEventMatches(value, prefix, execution, id) {
|
|
60
|
+
return record(value) && value.id === id && value.execution_id === execution && uuid(value.evidence_id) && natural(value.attempt_number)
|
|
61
|
+
&& ['guest_report', 'customer_device'].includes(String(value.origin))
|
|
62
|
+
&& value.attempt_number > 0 && natural(value.position) && ['observation', 'action', 'completion'].includes(String(value.kind))
|
|
63
|
+
&& typeof value.summary === 'string' && digest(value.contract_sha256) && typeof value.inline_available === 'boolean'
|
|
64
|
+
&& value.inline_available === (value.content !== null) && value.file_path === `${prefix}/evidence/${value.evidence_id}/bytes`;
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Coverage belongs to the selected attempt and evidence channel, not a grade. */
|
|
2
|
+
export function coveragePageMatches(page, kind, attempt) {
|
|
3
|
+
const selectedAttempt = attempt?.trim();
|
|
4
|
+
return (kind === 'messages' || kind === 'actions') && page.kind === kind && page.results.every(row => Number.isSafeInteger(row.attempt_number) && row.attempt_number > 0
|
|
5
|
+
&& (!selectedAttempt || row.attempt_number === Number(selectedAttempt))
|
|
6
|
+
&& [...row.task_message_streams, ...row.message_streams, ...row.voice_streams, ...row.speech_measurements, ...row.operation_streams]
|
|
7
|
+
.every(stream => stream.attempt_number === row.attempt_number)
|
|
8
|
+
&& (kind === 'actions' ? row.task_message_streams.length + row.message_streams.length + row.voice_streams.length + row.speech_measurements.length === 0
|
|
9
|
+
: kind === 'messages' && row.operation_streams.length === 0));
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
2
|
+
const executionStates = new Set(['planned', 'claimed', 'dispatching', 'running', 'reconciling', 'completed', 'failed', 'timed_out',
|
|
3
|
+
'canceled', 'platform_failed', 'platform_failed_before_start', 'canceled_before_start']);
|
|
4
|
+
const exportStates = new Set(['queued', 'preparing', 'uploading', 'retained', 'failed', 'canceled', 'unavailable']);
|
|
5
|
+
export function normalizedExportFilters(value) {
|
|
6
|
+
const keys = ['q', 'status', 'checks', 'scenario', 'cohort', 'assessment_status', 'score_bucket'];
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.entries(value).some(([key, one]) => !keys.includes(key) || typeof one !== 'string'))
|
|
8
|
+
return null;
|
|
9
|
+
const q = value.q ?? '', status = value.status ?? '', checks = value.checks ?? '', scenario = value.scenario?.toLowerCase() ?? '';
|
|
10
|
+
if ([...q].length > 200 || /[\u0000-\u001f\u007f]/.test(q.trim()) || status && !executionStates.has(status)
|
|
11
|
+
|| checks && !['met', 'unmet', 'unassessed', 'unavailable', 'missing'].includes(checks) || scenario && !uuid.test(scenario))
|
|
12
|
+
return null;
|
|
13
|
+
const result = { q: q.trim(), status, checks, scenario };
|
|
14
|
+
if (!value.cohort)
|
|
15
|
+
return value.assessment_status || value.score_bucket ? null : result;
|
|
16
|
+
const cohort = value.cohort.toLowerCase(), assessment = value.assessment_status ?? '', bucket = value.score_bucket ?? '';
|
|
17
|
+
if (!uuid.test(cohort) || assessment && !['succeeded', 'not_assessable', 'not_applicable'].includes(assessment)
|
|
18
|
+
|| bucket && (!/^[0-8]$/.test(bucket) || assessment && assessment !== 'succeeded'))
|
|
19
|
+
return null;
|
|
20
|
+
return { ...result, cohort, assessment_status: assessment, score_bucket: bucket };
|
|
21
|
+
}
|
|
22
|
+
function sameFilters(left, right) {
|
|
23
|
+
const first = normalizedExportFilters(left), second = normalizedExportFilters(right);
|
|
24
|
+
return first !== null && second !== null && Object.keys(first).length === Object.keys(second).length
|
|
25
|
+
&& Object.entries(first).every(([key, value]) => second[key] === value);
|
|
26
|
+
}
|
|
27
|
+
export function reportExportMatches(value, scope, selected) {
|
|
28
|
+
return !!value && uuid.test(value.id) && uuid.test(value.actor_id) && uuid.test(value.request_key)
|
|
29
|
+
&& value.workspace_id === scope.workspace && value.campaign_id === scope.campaign && (!scope.actor || value.actor_id === scope.actor)
|
|
30
|
+
&& (!selected?.id || value.id === selected.id) && (!selected?.key || value.request_key === selected.key)
|
|
31
|
+
&& exportStates.has(value.state) && normalizedExportFilters(value.filters) !== null
|
|
32
|
+
&& (!selected?.filters || sameFilters(value.filters, selected.filters))
|
|
33
|
+
&& Object.values(value.counts).every(one => Number.isSafeInteger(one) && one >= 0)
|
|
34
|
+
&& (!value.file || value.state === 'retained' && Number.isSafeInteger(value.file.size) && value.file.size > 0
|
|
35
|
+
&& /^[a-f0-9]{64}$/.test(value.file.sha256)
|
|
36
|
+
&& value.file.download_path === `/api/workspaces/${scope.workspace}/campaigns/${scope.campaign}/report-exports/${value.id}/download`);
|
|
37
|
+
}
|
|
38
|
+
export function reportExportsPageMatches(value, scope) {
|
|
39
|
+
return 'results' in value && value.workspace_id === scope.workspace && value.campaign_id === scope.campaign
|
|
40
|
+
&& value.results.every(row => reportExportMatches(row, scope) && row.file === null && !row.can_cancel);
|
|
41
|
+
}
|
|
42
|
+
export function reportExportRecoveryMatches(value, scope, key) {
|
|
43
|
+
return !('results' in value) && reportExportMatches(value, scope, { key });
|
|
44
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const connectionFailures = {
|
|
2
|
+
github_connection_expired: 'GitHub authorization expired or your workspace access changed. Authorize GitHub again to continue.',
|
|
3
|
+
github_installation_denied: 'This GitHub account cannot access the selected installation. Choose an authorized account or update GitHub access.',
|
|
4
|
+
github_connection_retry: 'GitHub could not confirm this authorization. Check your connected accounts below, then authorize GitHub again if needed.',
|
|
5
|
+
github_connection_failed: 'The GitHub connection was not confirmed. Check your connected accounts below before authorizing again.',
|
|
6
|
+
};
|
|
7
|
+
export function gitHubConnectionFailure(reason) {
|
|
8
|
+
return reason && Object.prototype.hasOwnProperty.call(connectionFailures, reason) ? connectionFailures[reason] : null;
|
|
9
|
+
}
|
|
10
|
+
export const gitHubId = (value) => typeof value === 'string' && /^[1-9][0-9]{0,18}$/.test(value) && BigInt(value) < 2n ** 63n;
|
|
11
|
+
const uuid = (value) => typeof value === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value);
|
|
12
|
+
const text = (value, maximum, empty = false) => typeof value === 'string' && (empty || value.length > 0)
|
|
13
|
+
&& value.length <= maximum && !/[\x00-\x1f\x7f]/.test(value);
|
|
14
|
+
const date = (value) => typeof value === 'string' && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
15
|
+
export function gitHubConnectionMatches(value, workspace, installation) {
|
|
16
|
+
return !!value && uuid(value.id) && value.workspace_id === workspace && value.installation_id === installation && gitHubId(installation)
|
|
17
|
+
&& text(value.account, 255) && text(value.state, 24) && date(value.verified_at) && (value.disconnected_at === null || date(value.disconnected_at));
|
|
18
|
+
}
|
|
19
|
+
export function gitHubConnectionChangeMatches(value, original) {
|
|
20
|
+
if (!value || value.workspace_id !== original.workspace || value.installation_id !== original.installation
|
|
21
|
+
|| value.actor_id !== original.actor || value.request_key !== original.key || value.connection_id !== original.connection_id
|
|
22
|
+
|| !text(value.current_connection_state, 24) || !date(value.current_verified_at))
|
|
23
|
+
return false;
|
|
24
|
+
if (value.state === 'not_received')
|
|
25
|
+
return value.connection_verified_at === null && value.disconnected_at === null
|
|
26
|
+
&& value.cleanup_state === null && value.cleanup_completed_at === null;
|
|
27
|
+
return value.state === 'received' && value.connection_verified_at === original.verified_at && date(value.disconnected_at)
|
|
28
|
+
&& (value.cleanup_state === 'pending' || value.cleanup_state === 'recovery_required') && value.cleanup_completed_at === null
|
|
29
|
+
|| value.state === 'received' && value.connection_verified_at === original.verified_at && date(value.disconnected_at)
|
|
30
|
+
&& value.cleanup_state === 'complete' && date(value.cleanup_completed_at);
|
|
31
|
+
}
|
|
32
|
+
export function gitHubConnectionsMatch(value, workspace, query, page) {
|
|
33
|
+
return !!value && value.workspace_id === workspace && value.query === query.trim() && pageMatches(value, page)
|
|
34
|
+
&& value.results.every(row => gitHubConnectionMatches(row, workspace, row.installation_id));
|
|
35
|
+
}
|
|
36
|
+
export function gitHubConnectionChangesMatch(value, workspace, actor, page) {
|
|
37
|
+
return !!value && value.workspace_id === workspace && value.actor_id === actor && pageMatches(value, page)
|
|
38
|
+
&& value.results.every(row => row.state === 'received' && uuid(row.request_key) && uuid(row.connection_id) && date(row.connection_verified_at)
|
|
39
|
+
&& gitHubConnectionChangeMatches(row, { workspace, actor, installation: row.installation_id, key: row.request_key,
|
|
40
|
+
connection_id: row.connection_id, verified_at: row.connection_verified_at }));
|
|
41
|
+
}
|
|
42
|
+
export function gitHubRead(value, matches) {
|
|
43
|
+
if (!matches)
|
|
44
|
+
throw new Error('The GitHub response did not match the selected workspace, account or original request. Check the connection before continuing.');
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
function pageMatches(value, page) {
|
|
48
|
+
return value.page === page && Number.isSafeInteger(value.page) && value.page >= 1 && Number.isSafeInteger(value.page_size)
|
|
49
|
+
&& value.page_size >= 1 && value.page_size <= 100 && Number.isSafeInteger(value.count) && value.count >= 0
|
|
50
|
+
&& Array.isArray(value.results) && value.results.length <= value.page_size;
|
|
51
|
+
}
|
|
52
|
+
export function gitHubAccountMatches(value, workspace, installation) {
|
|
53
|
+
return !!value && value.workspace_id === workspace && gitHubId(value.id) && (!installation || installation === value.id)
|
|
54
|
+
&& text(value.account, 255) && (value.avatar_url === null || typeof value.avatar_url === 'string' && value.avatar_url.length <= 2048);
|
|
55
|
+
}
|
|
56
|
+
export function gitHubAccountsMatch(value, workspace, query = '', page = 1) {
|
|
57
|
+
return !!value && value.workspace_id === workspace && value.query === query.trim() && pageMatches(value, page)
|
|
58
|
+
&& value.results.every(row => gitHubAccountMatches(row, workspace));
|
|
59
|
+
}
|
|
60
|
+
export function gitHubSyncMatches(value, workspace, installation, original) {
|
|
61
|
+
return !!value && value.workspace_id === workspace && value.installation_id === installation && gitHubId(installation)
|
|
62
|
+
&& [value.id, value.workspace_id, value.actor_id, value.request_key].every(uuid)
|
|
63
|
+
&& (!original || value.actor_id === original.actor && value.request_key === original.key && (!original.id || original.id === value.id))
|
|
64
|
+
&& ['pending', 'ready', 'failed', 'canceled'].includes(value.state) && typeof value.observed_count === 'string'
|
|
65
|
+
&& /^(0|[1-9][0-9]*)$/.test(value.observed_count) && value.observed_count.length <= 19 && BigInt(value.observed_count) < 2n ** 63n
|
|
66
|
+
&& (value.completed_at === null || Number.isFinite(Date.parse(value.completed_at)));
|
|
67
|
+
}
|
|
68
|
+
export function gitHubRepositoriesMatch(value, workspace, installation, query = '', page = 1) {
|
|
69
|
+
return !!value && value.workspace_id === workspace && value.installation_id === installation && value.query === query.trim()
|
|
70
|
+
&& pageMatches(value, page) && typeof value.directory_ready === 'boolean' && gitHubSyncMatches(value.sync, workspace, installation)
|
|
71
|
+
&& value.results.every(row => gitHubId(row.id) && text(row.full_name, 512) && row.full_name.split('/').length === 2
|
|
72
|
+
&& row.full_name.split('/').every(Boolean) && typeof row.description === 'string' && row.description.length <= 2048
|
|
73
|
+
&& (row.default_branch === null || text(row.default_branch, 512, true)) && typeof row.private === 'boolean');
|
|
74
|
+
}
|
|
75
|
+
export function gitHubRequestMatches(value, workspace, installation, original) {
|
|
76
|
+
return !!value && value.workspace_id === workspace && value.installation_id === installation && value.actor_id === original.actor
|
|
77
|
+
&& value.request_key === original.key && (value.state === 'not_received' ? value.sync === null && !original.id
|
|
78
|
+
: value.state === 'received' && value.sync !== null && gitHubSyncMatches(value.sync, workspace, installation, original));
|
|
79
|
+
}
|
|
80
|
+
export function gitHubBranchesMatch(value, workspace, installation, repository, page) {
|
|
81
|
+
return !!value && value.workspace_id === workspace && value.installation_id === installation && value.repository_id === repository
|
|
82
|
+
&& gitHubId(installation) && gitHubId(repository) && value.page === page && Number.isSafeInteger(page) && page >= 1 && page <= 99999999
|
|
83
|
+
&& typeof value.has_next === 'boolean' && value.has_previous === (page > 1) && Array.isArray(value.results) && value.results.length <= 25
|
|
84
|
+
&& value.results.every(row => text(row.name, 512));
|
|
85
|
+
}
|
|
86
|
+
export function gitHubHandoff(value, workspace) {
|
|
87
|
+
try {
|
|
88
|
+
const url = new URL(value.url);
|
|
89
|
+
if (value.workspace_id === workspace && url.protocol === 'https:' && url.hostname === 'github.com' && !url.port
|
|
90
|
+
&& !url.username && !url.password && !url.hash && /^\/apps\/[a-zA-Z0-9_-]+\/installations\/new$/.test(url.pathname)
|
|
91
|
+
&& [...url.searchParams.keys()].join(',') === 'state' && /^[a-zA-Z0-9_-]{32,256}$/.test(url.searchParams.get('state') ?? ''))
|
|
92
|
+
return url.href;
|
|
93
|
+
}
|
|
94
|
+
catch { /* Opaque setup URL stays out of errors and retained history. */ }
|
|
95
|
+
throw new Error('The GitHub installation page could not be confirmed. Start setup again.');
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function findingsMatch(value, selected) {
|
|
2
|
+
return value.campaign_id === selected.campaign && value.filters.q === selected.query.trim()
|
|
3
|
+
&& value.filters.scenario === (selected.scenario ?? null)
|
|
4
|
+
&& value.sample_selection === 'first_retained_trial_per_observed_outcome'
|
|
5
|
+
&& value.results.every(row => !selected.scenario || row.id === selected.scenario.toLowerCase());
|
|
6
|
+
}
|
|
7
|
+
export function insightsMatch(value, campaign) {
|
|
8
|
+
return value.campaign_id === campaign && value.proposals.length <= value.proposal_page_size
|
|
9
|
+
&& value.proposal_count >= value.proposals.length
|
|
10
|
+
&& (value.state !== 'unavailable' || !value.content && !value.proposals.length && !value.can_apply);
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
|
|
2
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
3
|
+
const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
4
|
+
const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
5
|
+
export function localAssignmentMatches(v, selected) {
|
|
6
|
+
if (!v || v.actor_id !== selected.actor || v.workspace_id !== selected.workspace || v.device_key !== selected.device
|
|
7
|
+
|| !uuid(v.native_session_id) || selected.nativeSession && v.native_session_id !== selected.nativeSession
|
|
8
|
+
|| !uuid(v.job_id) || selected.job && v.job_id !== selected.job || !uuid(v.world_id) || selected.world && v.world_id !== selected.world
|
|
9
|
+
|| !uuid(v.grant_id) || typeof v.world_name !== 'string' || !hex(v.payload_digest) || v.provenance !== 'customer_device'
|
|
10
|
+
|| !['validation', 'trial', 'world_session'].includes(v.purpose)
|
|
11
|
+
|| !['received', 'queued', 'admitted', 'provisioning', 'running', 'finalizing', 'awaiting_validation', 'cleaning_up', 'retry_wait', 'completed', 'failed', 'canceled'].includes(v.job_state)
|
|
12
|
+
|| ![v.deadline_at, v.created_at, v.server_time].every(date))
|
|
13
|
+
return false;
|
|
14
|
+
const session = v.world_session;
|
|
15
|
+
if (v.purpose === 'world_session' ? !session || !uuid(session.session_id) || !uuid(session.configuration_id) || !hex(session.input_sha256) || !date(session.expires_at)
|
|
16
|
+
: session !== undefined)
|
|
17
|
+
return false;
|
|
18
|
+
if (v.claim === null)
|
|
19
|
+
return v.input_preparation === undefined;
|
|
20
|
+
const claim = v.claim, input = v.input_preparation;
|
|
21
|
+
return !!claim && uuid(claim.id) && uuid(claim.request_key) && (!selected.request || claim.request_key === selected.request)
|
|
22
|
+
&& ['claimed', 'draining', 'withdrawn', 'reconciling', 'expired'].includes(claim.state) && date(claim.expires_at)
|
|
23
|
+
&& integer(claim.last_sequence) && (claim.last_pulse_digest === null || hex(claim.last_pulse_digest))
|
|
24
|
+
&& (claim.request_digest === null ? claim.state === 'withdrawn' && claim.attempt_id === null : hex(claim.request_digest) && uuid(claim.attempt_id))
|
|
25
|
+
&& typeof claim.cleanup_recorded === 'boolean' && claim.cleanup_provenance === (claim.cleanup_recorded ? 'customer_device' : null)
|
|
26
|
+
&& !!input && ['pending', 'preparing', 'ready', 'failed'].includes(input.state)
|
|
27
|
+
&& (input.problem === null || typeof input.problem === 'string') && (input.state !== 'failed' || !!input.problem);
|
|
28
|
+
}
|
|
29
|
+
export function localClaimLookupMatches(v, selected) {
|
|
30
|
+
return localAssignmentMatches(v, selected) && v.request_key === selected.request && v.claim_retained === (v.claim !== null);
|
|
31
|
+
}
|
|
32
|
+
export function localAssignmentsMatch(v, selected, filters = {}) {
|
|
33
|
+
return !!v && v.actor_id === selected.actor && v.workspace_id === selected.workspace && v.device_key === selected.device
|
|
34
|
+
&& v.world_id === (filters.world ?? null) && v.campaign_id === (filters.campaign ?? null) && v.query === (filters.query ?? '').trim()
|
|
35
|
+
&& v.available === (filters.available ?? false) && v.page === (filters.page ?? 1) && integer(v.count) && integer(v.page_size)
|
|
36
|
+
&& v.page_size >= 1 && v.page_size <= 100 && Array.isArray(v.results) && v.results.length <= v.page_size
|
|
37
|
+
&& new Set(v.results.map(one => one.job_id)).size === v.results.length
|
|
38
|
+
&& v.results.every(one => localAssignmentMatches(one, { ...selected, world: filters.world ?? selected.world }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function localConfigurationIdentityMatches(value, selected) {
|
|
2
|
+
return !!value && value.schema === 'firedrill.local-runtime-configuration.v1'
|
|
3
|
+
&& value.actor_id === selected.owner && value.workspace_id === selected.workspace && value.native_session_id === selected.nativeSession
|
|
4
|
+
&& value.device_key === selected.device && value.job_id === selected.job && value.request_key === selected.request
|
|
5
|
+
&& value.lease_id === selected.lease && value.operation_id === selected.lease && value.attempt_id === selected.attempt
|
|
6
|
+
&& value.provenance === 'customer_device';
|
|
7
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
2
|
+
const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
3
|
+
const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
4
|
+
export function localHistoryIdentityMatches(value, selected) {
|
|
5
|
+
return !!value && value.actor_id === selected.owner && value.workspace_id === selected.workspace && value.device_key === selected.device
|
|
6
|
+
&& value.job_id === selected.job && value.request_key === selected.request && value.lease_id === selected.lease && value.attempt_id === selected.attempt
|
|
7
|
+
&& value.manifest_sha256 === selected.manifestSha256 && value.provenance === 'customer_device' && date(value.server_time);
|
|
8
|
+
}
|
|
9
|
+
export function localEvidenceReceiptMatches(value, selected, key) {
|
|
10
|
+
if (!localHistoryIdentityMatches(value, selected) || value.transfer_key !== key || value.cloud_completion_recorded !== false
|
|
11
|
+
|| !['not_retained', 'receiving', 'verifying', 'retained', 'refused', 'expired', 'unavailable'].includes(value.state)
|
|
12
|
+
|| typeof value.upload_available !== 'boolean' || typeof value.authorization_current !== 'boolean' || !date(value.upload_until)
|
|
13
|
+
|| !Array.isArray(value.chunks) || value.chunks.length > 512 || new Set(value.chunks.map(one => one.index)).size !== value.chunks.length
|
|
14
|
+
|| value.chunks.some(one => !integer(one.index) || one.index > 511 || !integer(one.size) || one.size < 1 || one.size > 1024 ** 2
|
|
15
|
+
|| !hex(one.sha256) || typeof one.retained !== 'boolean'))
|
|
16
|
+
return false;
|
|
17
|
+
if (value.state === 'not_retained')
|
|
18
|
+
return value.document === null && value.request_digest === null && value.artifact === null && value.chunks.length === 0;
|
|
19
|
+
if (!value.document || !hex(value.request_digest))
|
|
20
|
+
return false;
|
|
21
|
+
const artifact = value.artifact;
|
|
22
|
+
return value.state === 'retained' ? !!artifact && /^[a-f0-9-]{36}$/.test(artifact.id) && hex(artifact.sha256)
|
|
23
|
+
&& integer(artifact.size) && /^[1-9][0-9]{0,30}$/.test(artifact.generation) : artifact === null;
|
|
24
|
+
}
|
|
25
|
+
export function localClosingReceiptMatches(value, selected) {
|
|
26
|
+
return localHistoryIdentityMatches(value, selected) && value.schema === 'firedrill.local-completion-receipt.v1'
|
|
27
|
+
&& value.independently_attested === false && typeof value.received === 'boolean' && typeof value.settled === 'boolean'
|
|
28
|
+
&& typeof value.cleanup_recorded === 'boolean' && (value.received ? value.document !== null && hex(value.digest) && value.cleanup_recorded
|
|
29
|
+
: value.document === null && value.digest === null && !value.cleanup_recorded && !value.settled)
|
|
30
|
+
&& ['received', 'queued', 'admitted', 'provisioning', 'running', 'finalizing', 'awaiting_validation', 'cleaning_up', 'retry_wait', 'completed', 'failed', 'canceled'].includes(value.job_state)
|
|
31
|
+
&& (!value.settled || ['completed', 'failed', 'canceled'].includes(value.job_state));
|
|
32
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { localRuntimeImageMatches } from './local-runner-contracts.js';
|
|
2
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
|
|
3
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
4
|
+
const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
5
|
+
const integer = (v, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => typeof v === 'number' && Number.isSafeInteger(v) && v >= minimum && v <= maximum;
|
|
6
|
+
const operations = (v) => Array.isArray(v) && v.length <= 3
|
|
7
|
+
&& v.every(one => ['trial', 'validation', 'world_session'].includes(one)) && new Set(v).size === v.length;
|
|
8
|
+
export function localGrantRead(v, matches) {
|
|
9
|
+
if (!matches)
|
|
10
|
+
throw new Error('The saved local permission did not match this account, world, device or original approval. No local work was started.');
|
|
11
|
+
return v;
|
|
12
|
+
}
|
|
13
|
+
export function localGrantBasisMatches(v) {
|
|
14
|
+
return !!v && integer(v.world_revision, 1) && integer(v.config_revision, 1) && uuid(v.source_id) && uuid(v.source_snapshot_id)
|
|
15
|
+
&& typeof v.source_revision === 'string' && v.source_revision.length > 0 && v.source_revision.length <= 128
|
|
16
|
+
&& hex(v.source_sha256) && hex(v.source_content_digest) && hex(v.configuration_digest)
|
|
17
|
+
&& (v.source_set === undefined || localGrantSourceSetMatches(v.source_set))
|
|
18
|
+
&& (v.world_definition === null || !!v.world_definition && uuid(v.world_definition.artifact_id) && hex(v.world_definition.sha256)
|
|
19
|
+
&& typeof v.world_definition.generation === 'string' && /^[1-9][0-9]*$/.test(v.world_definition.generation));
|
|
20
|
+
}
|
|
21
|
+
export function localGrantSourceSetMatches(v) {
|
|
22
|
+
return !!v && uuid(v.id) && [v.source_set_sha256, v.graph_sha256, v.roots_sha256].every(hex) && integer(v.root_count, 1, 128);
|
|
23
|
+
}
|
|
24
|
+
export function localGrantSourceRootsMatch(v, actor, workspace, world, device, session, basisDigest, sourceSet, page) {
|
|
25
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.device_key === device
|
|
26
|
+
&& v.native_session_id === session && v.basis_sha256 === basisDigest && hex(basisDigest)
|
|
27
|
+
&& localGrantSourceSetMatches(v.source_set) && ['id', 'source_set_sha256', 'graph_sha256', 'roots_sha256', 'root_count']
|
|
28
|
+
.every(key => v.source_set[key] === sourceSet[key])
|
|
29
|
+
&& v.page === page && integer(v.page_size, 1, 100) && v.count === sourceSet.root_count && Array.isArray(v.results)
|
|
30
|
+
&& v.results.length === Math.min(v.page_size, Math.max(0, v.count - (page - 1) * v.page_size))
|
|
31
|
+
&& (v.next === null || typeof v.next === 'string') && (v.previous === null || typeof v.previous === 'string')
|
|
32
|
+
&& (v.next !== null) === (page * v.page_size < v.count) && (v.previous !== null) === (page > 1)
|
|
33
|
+
&& v.results.every(row => !!row && /^[a-z][a-z0-9_-]{0,63}$/.test(row.alias) && uuid(row.source_id) && uuid(row.snapshot_id)
|
|
34
|
+
&& typeof row.revision === 'string' && row.revision.length > 0 && row.revision.length <= 128 && hex(row.snapshot_sha256)
|
|
35
|
+
&& typeof row.generation === 'string' && /^[1-9][0-9]*$/.test(row.generation) && typeof row.label === 'string' && row.label.length <= 512
|
|
36
|
+
&& row.directory === `components/${row.alias}` && (row.content_digest === null || hex(row.content_digest)))
|
|
37
|
+
&& new Set(v.results.map(row => row.alias)).size === v.results.length && new Set(v.results.map(row => row.source_id)).size === v.results.length;
|
|
38
|
+
}
|
|
39
|
+
export function localGrantMatches(v, actor, workspace, selected = {}) {
|
|
40
|
+
if (!v || v.actor_id !== actor || v.workspace_id !== workspace || !uuid(v.id) || !uuid(v.request_key) || !uuid(v.device_key)
|
|
41
|
+
|| !uuid(v.native_session_id) || !uuid(v.world_id) || selected.world && v.world_id !== selected.world || selected.device && v.device_key !== selected.device
|
|
42
|
+
|| selected.key && v.request_key !== selected.key || selected.id && v.id !== selected.id
|
|
43
|
+
|| typeof v.device_label !== 'string' || typeof v.world_name !== 'string' || typeof v.world_available !== 'boolean'
|
|
44
|
+
|| !['active', 'revoked', 'expired', 'access_removed', 'superseded'].includes(v.state)
|
|
45
|
+
|| ![v.server_time, v.expires_at, v.created_at].every(date) || !operations(v.operations) || !v.basis)
|
|
46
|
+
return false;
|
|
47
|
+
if (v.request_digest === null)
|
|
48
|
+
return v.state === 'revoked' && v.runtime === null && v.limits === null && v.consent === null
|
|
49
|
+
&& v.operations.length === 0 && Object.keys(v.basis).length === 0;
|
|
50
|
+
return hex(v.request_digest) && localGrantBasisMatches(v.basis) && !!v.runtime && localRuntimeImageMatches(v.runtime)
|
|
51
|
+
&& v.operations.length > 0 && !!v.limits && integer(v.limits.parallelism, 1, 10000) && integer(v.limits.cpu_units, 1, 65536)
|
|
52
|
+
&& integer(v.limits.memory_mb, 1024, 16777216) && !!v.consent && v.consent.policy === 'firedrill.local-execution-consent.v1'
|
|
53
|
+
&& v.consent.cloud_context_processing === true && v.consent.run_metadata === true
|
|
54
|
+
&& [v.consent.evidence_upload, v.consent.vault_values_to_device, v.consent.real_account_access, v.consent.catalog_packages_to_device].every(one => typeof one === 'boolean');
|
|
55
|
+
}
|
|
56
|
+
export function localGrantLookupMatches(v, actor, workspace, world, key) {
|
|
57
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.request_key === key
|
|
58
|
+
&& (v.state === 'not_retained' || localGrantMatches(v, actor, workspace, { world, key }));
|
|
59
|
+
}
|
|
60
|
+
export function localGrantsMatch(v, actor, workspace, selected) {
|
|
61
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === selected.world && v.device_key === (selected.device ?? null)
|
|
62
|
+
&& v.grant_id === (selected.grant ?? null) && v.purpose === (selected.purpose ?? '') && v.available === (selected.available ?? false)
|
|
63
|
+
&& v.query === (selected.query ?? '').trim() && v.page === (selected.page ?? 1) && integer(v.count) && integer(v.page_size, 1, 100)
|
|
64
|
+
&& Array.isArray(v.results) && v.results.length <= v.page_size && new Set(v.results.map(one => one.id)).size === v.results.length
|
|
65
|
+
&& v.results.every(one => localGrantMatches(one, actor, workspace, { world: selected.world ?? undefined, device: selected.device, id: selected.grant })
|
|
66
|
+
&& (!selected.purpose || one.operations.includes(selected.purpose)));
|
|
67
|
+
}
|
|
68
|
+
export function localGrantOfferMatches(v, actor, workspace, world, device) {
|
|
69
|
+
const values = v?.disclosures;
|
|
70
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.device_key === device
|
|
71
|
+
&& uuid(v.native_session_id) && v.schema === 'firedrill.local-grant-offer.v1' && typeof v.world_name === 'string' && date(v.server_time)
|
|
72
|
+
&& localGrantBasisMatches(v.basis) && operations(v.operations) && v.operations.length === 3 && Array.isArray(v.runtimes)
|
|
73
|
+
&& v.runtimes.length <= 1 && v.runtimes.every(localRuntimeImageMatches) && !!values
|
|
74
|
+
&& [values.real_connections, values.target_model_connections, values.catalog_packages].every(one => integer(one))
|
|
75
|
+
&& [values.vault_values, values.local_values].every(list => Array.isArray(list) && list.every(one => one && typeof one.name === 'string'
|
|
76
|
+
&& ['build', 'runtime', 'both'].includes(one.phase)));
|
|
77
|
+
}
|
|
78
|
+
export function localGrantWorld(prefix, workspace) {
|
|
79
|
+
const world = prefix.split('/').at(-1);
|
|
80
|
+
if (!uuid(world) || prefix !== `/api/workspaces/${workspace}/worlds/${world}`)
|
|
81
|
+
throw new Error('Open this world in its selected workspace.');
|
|
82
|
+
return world;
|
|
83
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
2
|
+
const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
3
|
+
export function localInputIdentityMatches(v, selected) {
|
|
4
|
+
return !!v && v.actor_id === selected.owner && v.workspace_id === selected.workspace && v.native_session_id === selected.nativeSession
|
|
5
|
+
&& v.device_key === selected.device && v.job_id === selected.job && v.request_key === selected.request && v.lease_id === selected.lease && v.attempt_id === selected.attempt;
|
|
6
|
+
}
|
|
7
|
+
export function localInputPacketMatches(v, selected, requested = {}) {
|
|
8
|
+
return localInputIdentityMatches(v, selected) && v.schema === 'firedrill.local-input-response.v1'
|
|
9
|
+
&& hex(v.manifest_sha256) && !!v.manifest && v.manifest.schema === 'firedrill.local-input-manifest.v1'
|
|
10
|
+
&& v.manifest.job_id === selected.job && integer(v.offset) && (!requested.part || v.part === requested.part)
|
|
11
|
+
&& (requested.offset === undefined || v.offset === requested.offset)
|
|
12
|
+
&& (v.part === 'manifest' ? v.offset === 0 : ['source', 'candidate', 'compiled', 'scenarios', 'packages', 'dataset', 'checkpoint', 'application_checkpoint'].includes(v.part)
|
|
13
|
+
&& integer(v.length) && v.length >= 1 && v.length <= 256 * 1024 && (requested.length === undefined || v.length === requested.length)
|
|
14
|
+
&& typeof v.content_base64 === 'string' && v.content_base64.length === 4 * Math.ceil(v.length / 3) && hex(v.chunk_sha256));
|
|
15
|
+
}
|
|
16
|
+
export function localInputPendingMatches(v, selected) {
|
|
17
|
+
return localInputIdentityMatches(v, selected) && v.schema === 'firedrill.local-input-pending.v1'
|
|
18
|
+
&& ['preparing_packages', 'preparing_starting_data'].includes(v.input_state) && v.retry_after_ms === 1000;
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
|
|
2
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
3
|
+
const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
4
|
+
const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
5
|
+
export function localRunnerRead(v, matches) {
|
|
6
|
+
if (!matches)
|
|
7
|
+
throw new Error('The local runner response did not match this account, workspace or original device. No local work was started.');
|
|
8
|
+
return v;
|
|
9
|
+
}
|
|
10
|
+
export function localRunnerMatches(v, actor, workspace, device) {
|
|
11
|
+
if (!v || v.actor_id !== actor || v.workspace_id !== workspace || !uuid(v.device_key) || device && v.device_key !== device
|
|
12
|
+
|| !uuid(v.native_session_id) || !date(v.server_time) || !date(v.created_at) || !date(v.expires_at)
|
|
13
|
+
|| typeof v.label !== 'string' || v.label.length < 1 || v.label.length > 80 || !v.host || typeof v.host !== 'object'
|
|
14
|
+
|| !['enrolled', 'idle', 'draining', 'stopped', 'offline', 'revoked', 'expired', 'access_removed'].includes(v.state)
|
|
15
|
+
|| v.provenance !== 'customer_device' || !integer(v.last_sequence)
|
|
16
|
+
|| ![v.last_seen_at, v.presence_until].every(one => one === null || date(one))
|
|
17
|
+
|| ![v.enrollment_digest, v.last_pulse_digest].every(one => one === null || hex(one)))
|
|
18
|
+
return false;
|
|
19
|
+
const host = v.host;
|
|
20
|
+
return v.enrollment_digest === null ? v.state === 'revoked' && Object.keys(host).length === 0
|
|
21
|
+
: Object.keys(host).length === 2 && ['darwin', 'linux'].includes(host.os ?? '') && ['x64', 'arm64'].includes(host.arch ?? '');
|
|
22
|
+
}
|
|
23
|
+
export function localRunnerLookupMatches(v, actor, workspace, device) {
|
|
24
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.device_key === device && uuid(device) && date(v.server_time)
|
|
25
|
+
&& (v.state === 'not_retained' || localRunnerMatches(v, actor, workspace, device));
|
|
26
|
+
}
|
|
27
|
+
export function localRunnersMatch(v, actor, workspace, query, page = 1) {
|
|
28
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.query === query.trim() && v.page === page
|
|
29
|
+
&& integer(v.count) && integer(v.page_size) && v.page_size >= 1 && v.page_size <= 100 && Array.isArray(v.results)
|
|
30
|
+
&& v.results.length <= v.page_size && new Set(v.results.map(one => one.device_key)).size === v.results.length
|
|
31
|
+
&& v.results.every(one => localRunnerMatches(one, actor, workspace));
|
|
32
|
+
}
|
|
33
|
+
export function localRuntimeCatalogMatches(v, actor, workspace) {
|
|
34
|
+
return !!v && v.actor_id === actor && v.workspace_id === workspace && v.schema === 'firedrill.local-runtime-catalog.v1'
|
|
35
|
+
&& Array.isArray(v.runtimes) && v.runtimes.length <= 2 && new Set(v.runtimes.map(one => one.arch)).size === v.runtimes.length
|
|
36
|
+
&& v.runtimes.every(localRuntimeImageMatches);
|
|
37
|
+
}
|
|
38
|
+
export function localRuntimeImageMatches(one) {
|
|
39
|
+
return !!one && ['amd64', 'arm64'].includes(one.arch) && one.os === 'linux' && one.profile === 'firedrill.local-container.v1'
|
|
40
|
+
&& typeof one.source_revision === 'string' && /^[a-f0-9]{40}$/.test(one.source_revision) && typeof one.image === 'string'
|
|
41
|
+
&& /^[a-z][a-z0-9-]{0,62}-docker\.pkg\.dev\/[a-z][a-z0-9-]{4,62}\/[a-z][a-z0-9-]{0,62}\/[a-z][a-z0-9-]{0,62}@sha256:[a-f0-9]{64}$/.test(one.image);
|
|
42
|
+
}
|