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,73 @@
|
|
|
1
|
+
import { toolProtocolMetadataMatches } from './tool-protocol-contracts.js';
|
|
2
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
3
|
+
const digest = /^[a-f0-9]{64}$/;
|
|
4
|
+
const serviceKey = /^[a-z][a-z0-9_-]{0,79}$/;
|
|
5
|
+
const operationName = /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/;
|
|
6
|
+
const optionalId = (value) => value === null || uuid.test(value);
|
|
7
|
+
const setupStates = ['preparing', 'inspecting', 'starting', 'instance_requested', 'saved', 'failed', 'withdrawn'];
|
|
8
|
+
export function catalogRead(value, matches) {
|
|
9
|
+
if (!matches)
|
|
10
|
+
throw new Error('The catalog response did not match this selection. Refresh the original item or request.');
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function page(value, selected) {
|
|
14
|
+
return Number.isSafeInteger(value.page) && value.page >= 1 && (!selected || value.page === selected)
|
|
15
|
+
&& Number.isSafeInteger(value.page_size) && value.page_size >= 1 && value.page_size <= 100
|
|
16
|
+
&& Number.isSafeInteger(value.count) && value.count >= 0 && value.results.length <= value.page_size;
|
|
17
|
+
}
|
|
18
|
+
export function catalogReleaseMatches(value, workspace, id) {
|
|
19
|
+
return !!value && uuid.test(workspace) && value.workspace_id === workspace && uuid.test(value.id) && (!id || id === value.id)
|
|
20
|
+
&& typeof value.category === 'string' && value.category.length <= 64
|
|
21
|
+
&& typeof value.available === 'boolean' && Number.isSafeInteger(value.operation_count) && value.operation_count >= 0
|
|
22
|
+
&& Number.isSafeInteger(value.service_count) && value.service_count >= 1 && value.interfaces.every(one => one === 'http' || one === 'mcp')
|
|
23
|
+
&& (value.logo_url === null || value.logo_url === `/api/workspaces/${workspace}/tools/library/${value.id}/logo`)
|
|
24
|
+
&& (!value.saved_version || uuid.test(value.saved_version.id));
|
|
25
|
+
}
|
|
26
|
+
export function catalogReleaseDetailMatches(value, workspace, id) {
|
|
27
|
+
return catalogReleaseMatches(value, workspace, id) && digest.test(value.contract_digest) && digest.test(value.manifest_digest)
|
|
28
|
+
&& value.setup.default_duration_seconds >= 60 && value.setup.default_duration_seconds <= value.setup.maximum_duration_seconds;
|
|
29
|
+
}
|
|
30
|
+
export function catalogPageMatches(value, workspace, query = '', selectedPage, category = '') {
|
|
31
|
+
return !!value && value.workspace_id === workspace && value.query === query.trim() && value.category === category && page(value, selectedPage)
|
|
32
|
+
&& value.results.every(row => catalogReleaseMatches(row, workspace));
|
|
33
|
+
}
|
|
34
|
+
function selection(value, release) {
|
|
35
|
+
return value.workspace_id === release.workspace_id && value.release_id === release.id
|
|
36
|
+
&& value.contract_digest === release.contract_digest && value.manifest_digest === release.manifest_digest;
|
|
37
|
+
}
|
|
38
|
+
export function catalogServiceMatches(value, release, key) {
|
|
39
|
+
return !!value && selection(value, release) && serviceKey.test(key) && value.key === key && value.id === key;
|
|
40
|
+
}
|
|
41
|
+
export function catalogServicesMatch(value, release, query = '', selectedPage) {
|
|
42
|
+
return !!value && selection(value, release) && value.query === query.trim() && page(value, selectedPage)
|
|
43
|
+
&& value.results.every(row => row.id === row.key && serviceKey.test(row.key) && Number.isSafeInteger(row.operation_count) && row.operation_count >= 1);
|
|
44
|
+
}
|
|
45
|
+
export function catalogOperationsMatch(value, release, service, query = '', selectedPage) {
|
|
46
|
+
return !!value && selection(value, release) && serviceKey.test(service) && value.service_key === service
|
|
47
|
+
&& value.query === query.trim() && page(value, selectedPage) && value.results.every(row => operationName.test(row.name)
|
|
48
|
+
&& toolProtocolMetadataMatches(row)
|
|
49
|
+
&& ['none', 'read', 'write', 'mixed'].includes(row.effect) && ['http', 'mcp'].includes(row.transport.kind));
|
|
50
|
+
}
|
|
51
|
+
export function catalogOperationMatches(value, release, service, name) {
|
|
52
|
+
return !!value && selection(value, release) && value.service_key === service && serviceKey.test(service)
|
|
53
|
+
&& value.name === name && operationName.test(name) && ['http', 'mcp'].includes(value.transport.kind)
|
|
54
|
+
&& !!value.input_schema && (value.output_schema !== null || value.transport.kind === 'http' && value.transport.response_encoding !== undefined
|
|
55
|
+
&& value.transport.response_encoding !== 'json') && toolProtocolMetadataMatches(value);
|
|
56
|
+
}
|
|
57
|
+
export function catalogSetupMatches(value, scope, selected) {
|
|
58
|
+
const input = selected?.input;
|
|
59
|
+
return !!value && uuid.test(scope.workspace) && uuid.test(scope.actor) && value.workspace_id === scope.workspace && value.actor_id === scope.actor
|
|
60
|
+
&& [value.id, value.request_key, value.release_id, value.upload_request_key].every(one => uuid.test(one))
|
|
61
|
+
&& [value.implementation_id, value.instance_id, value.group_id].every(optionalId) && setupStates.includes(value.state)
|
|
62
|
+
&& optionalId(value.usage_quote_id) && (value.duration_seconds === null ? value.usage_quote_id === null : value.usage_quote_id !== null)
|
|
63
|
+
&& (!selected?.key || value.request_key === selected.key) && (!selected?.id || value.id === selected.id)
|
|
64
|
+
&& (!input || value.release_id === input.release_id && value.duration_seconds === input.duration_seconds && value.usage_quote_id === input.usage_quote_id)
|
|
65
|
+
&& (value.duration_seconds === null || Number.isSafeInteger(value.duration_seconds) && value.duration_seconds >= 60)
|
|
66
|
+
&& (value.state !== 'instance_requested' || value.instance_id !== null && value.implementation_id !== null)
|
|
67
|
+
&& (value.state !== 'saved' || value.implementation_id !== null && value.instance_id === null)
|
|
68
|
+
&& (!value.can_withdraw || !['instance_requested', 'saved', 'withdrawn'].includes(value.state));
|
|
69
|
+
}
|
|
70
|
+
export function catalogSetupsMatch(value, scope, release = null, selectedPage) {
|
|
71
|
+
return !!value && value.workspace_id === scope.workspace && value.actor_id === scope.actor && value.release_filter === release
|
|
72
|
+
&& page(value, selectedPage) && value.results.every(row => catalogSetupMatches(row, scope) && (!release || row.release_id === release));
|
|
73
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const ciAcknowledgementNames = ['prepare_source', 'simulate_source', 'driver_input', 'execute_source', 'future_commits', 'untrusted_fork', 'saved_values', 'target_models',
|
|
2
|
+
'real_startup', 'real_trials', 'shared_real_state', 'evaluation_evidence', 'starting_data', 'browser_recording', 'voice_recording', 'desktop_recording'];
|
|
3
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
4
|
+
const sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
5
|
+
const count = (v, min = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= min;
|
|
6
|
+
const date = (v) => v === null || Number.isFinite(Date.parse(v));
|
|
7
|
+
export function ciAccessRead(value, matches) {
|
|
8
|
+
if (!matches)
|
|
9
|
+
throw new Error('The CI access response did not match the selected workspace, configuration or original request. Review its retained history before continuing.');
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function identity(v, workspace, original) {
|
|
13
|
+
return !!v && v.workspace_id === workspace && [v.id, v.workspace_id, v.request_key, v.configuration_id, v.configuration_revision_id].every(uuid)
|
|
14
|
+
&& sha(v.request_digest) && count(v.revision, 1) && (!original.id || original.id === v.id)
|
|
15
|
+
&& (!original.configuration || original.configuration === v.configuration_id) && (!original.key || original.key === v.request_key)
|
|
16
|
+
&& (!original.digest || original.digest === v.request_digest) && (original.revision === undefined || original.revision === v.revision);
|
|
17
|
+
}
|
|
18
|
+
export function ciApprovalMatches(v, workspace, original = {}) {
|
|
19
|
+
if (!identity(v, workspace, original) || !uuid(v.approved_by_id) || original.actor && original.actor !== v.approved_by_id
|
|
20
|
+
|| !sha(v.sha256) || !count(v.vault_entry_count) || !Number.isFinite(Date.parse(v.created_at)) || !date(v.revoked_at)
|
|
21
|
+
|| !['recorded', 'revoked'].includes(v.state) || (v.state === 'revoked') !== (v.revoked_at !== null) || typeof v.fork !== 'boolean'
|
|
22
|
+
|| !v.acknowledgements || Object.keys(v.acknowledgements).length !== ciAcknowledgementNames.length
|
|
23
|
+
|| ciAcknowledgementNames.some(key => typeof v.acknowledgements[key] !== 'boolean')
|
|
24
|
+
|| !v.acknowledgements.execute_source || !v.acknowledgements.starting_data || v.acknowledgements.untrusted_fork !== v.fork)
|
|
25
|
+
return false;
|
|
26
|
+
return v.scope === 'same_repository' ? v.run_id === null && v.head_sha === null && !v.fork && v.acknowledgements.future_commits
|
|
27
|
+
: v.scope === 'exact_run' && uuid(v.run_id) && typeof v.head_sha === 'string' && /^[a-f0-9]{40}$/.test(v.head_sha) && !v.acknowledgements.future_commits;
|
|
28
|
+
}
|
|
29
|
+
function page(v, number) {
|
|
30
|
+
return v.page === number && count(v.page, 1) && count(v.page_size, 1) && v.page_size <= 100 && count(v.count) && Array.isArray(v.results) && v.results.length <= v.page_size;
|
|
31
|
+
}
|
|
32
|
+
export function ciApprovalsMatch(v, workspace, configuration, filters = {}, number = 1) {
|
|
33
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && !!v.filters && page(v, number)
|
|
34
|
+
&& ['revision', 'state', 'scope', 'run_id'].every(key => v.filters[key] === (filters[key] ?? ''))
|
|
35
|
+
&& v.results.every(row => ciApprovalMatches(row, workspace, { configuration }) && (!filters.revision || String(row.revision) === filters.revision)
|
|
36
|
+
&& (!filters.state || row.state === filters.state) && (!filters.scope || row.scope === filters.scope) && (!filters.run_id || row.run_id === filters.run_id));
|
|
37
|
+
}
|
|
38
|
+
export function ciApprovalBasisMatches(v, workspace, configuration, revision, world) {
|
|
39
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && v.revision === revision && count(v.revision, 1)
|
|
40
|
+
&& sha(v.basis_sha256) && uuid(v.world_id) && (!world || world === v.world_id) && count(v.configuration_revision, 1)
|
|
41
|
+
&& [v.vault_entry_count, v.model_connection_count, v.real_connection_count, v.scenario_count, v.planned_trials].every(one => count(one))
|
|
42
|
+
&& count(v.parallelism, 1) && count(v.trial_seconds, 1) && count(v.source_component_count, 1)
|
|
43
|
+
&& (v.driver_kind === null || ['browser_workflow', 'adaptive_browser', 'desktop', 'process', 'event'].includes(v.driver_kind))
|
|
44
|
+
&& typeof v.driver_input_required === 'boolean' && v.driver_input_required === (v.driver_kind !== null)
|
|
45
|
+
&& (v.preparation_budget === null || count(v.preparation_budget.maximum_units, 1) && count(v.preparation_budget.credit_scale, 1)
|
|
46
|
+
&& typeof v.preparation_budget.denomination === 'string' && typeof v.preparation_budget.policy_version === 'string')
|
|
47
|
+
&& (v.simulation_budget === null || count(v.simulation_budget.maximum_units) && count(v.simulation_budget.credit_scale, 1)
|
|
48
|
+
&& typeof v.simulation_budget.denomination === 'string' && typeof v.simulation_budget.policy_version === 'string')
|
|
49
|
+
&& [v.evaluation_evidence, v.browser_recording, v.voice_recording, v.desktop_recording].every(one => typeof one === 'boolean');
|
|
50
|
+
}
|
|
51
|
+
export function ciApprovalRequestMatches(v, workspace, original) {
|
|
52
|
+
return !!v && v.workspace_id === workspace && v.actor_id === original.actor && v.request_key === original.key
|
|
53
|
+
&& (v.state === 'not_received' ? v.approval === null && !original.id : v.state === 'received' && v.approval !== null && ciApprovalMatches(v.approval, workspace, original));
|
|
54
|
+
}
|
|
55
|
+
export function ciActivationMatches(v, workspace, original = {}) {
|
|
56
|
+
return identity(v, workspace, original) && uuid(v.created_by_id) && (!original.actor || original.actor === v.created_by_id)
|
|
57
|
+
&& uuid(v.input_approval_id) && Number.isFinite(Date.parse(v.created_at)) && date(v.revoked_at)
|
|
58
|
+
&& ['enabled', 'paused'].includes(v.state) && (v.revoked_at === null || v.state === 'paused')
|
|
59
|
+
&& (v.revoke_reason === null || ['user_paused', 'configuration_changed', 'input_approval_revoked', 'authorization_changed'].includes(v.revoke_reason));
|
|
60
|
+
}
|
|
61
|
+
export function ciActivationsMatch(v, workspace, configuration, revision = '', state = '', number = 1) {
|
|
62
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && v.revision === revision && v.state === state && page(v, number)
|
|
63
|
+
&& v.results.every(row => ciActivationMatches(row, workspace, { configuration }) && (!revision || String(row.revision) === revision) && (!state || row.state === state));
|
|
64
|
+
}
|
|
65
|
+
export function ciActivationRequestMatches(v, workspace, original) {
|
|
66
|
+
return !!v && v.workspace_id === workspace && v.actor_id === original.actor && v.request_key === original.key
|
|
67
|
+
&& (v.state === 'not_received' ? v.activation === null && !original.id : v.state === 'received' && v.activation !== null && ciActivationMatches(v.activation, workspace, original));
|
|
68
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { gitHubId } from './github-directory-contracts.js';
|
|
2
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
3
|
+
const sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
4
|
+
const integer = (v, minimum = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= minimum;
|
|
5
|
+
const text = (v, maximum) => typeof v === 'string' && v.length > 0 && v.length <= maximum && !/[\x00-\x1f\x7f]/.test(v);
|
|
6
|
+
export function ciConfigurationRead(value, matches) {
|
|
7
|
+
if (!matches)
|
|
8
|
+
throw new Error('The CI response did not match the selected workspace, configuration, revision or original request. Reload its saved history.');
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
export function ciRevisionMatches(v, workspace, configuration) {
|
|
12
|
+
if (!v || v.workspace_id !== workspace || configuration && v.configuration_id !== configuration
|
|
13
|
+
|| ![v.id, v.workspace_id, v.configuration_id, v.actor_id, v.request_key].every(uuid)
|
|
14
|
+
|| !sha(v.request_digest) || !sha(v.sha256) || !integer(v.revision, 1) || !Number.isFinite(Date.parse(v.created_at)))
|
|
15
|
+
return false;
|
|
16
|
+
const doc = v.document;
|
|
17
|
+
return !!doc && text(doc.name, 120) && ['draft', 'disconnected'].includes(doc.state) && uuid(doc.world_id) && uuid(doc.plan_id)
|
|
18
|
+
&& typeof doc.source_alias === 'string' && /^[a-z][a-z0-9_-]{0,63}$/.test(doc.source_alias)
|
|
19
|
+
&& (doc.preparation_budget === null || integer(doc.preparation_budget.maximum_units, 1) && integer(doc.preparation_budget.credit_scale, 1)
|
|
20
|
+
&& text(doc.preparation_budget.denomination, 64) && text(doc.preparation_budget.policy_version, 64))
|
|
21
|
+
&& (doc.simulation_budget === null || integer(doc.simulation_budget.maximum_units) && integer(doc.simulation_budget.credit_scale, 1)
|
|
22
|
+
&& text(doc.simulation_budget.denomination, 64) && text(doc.simulation_budget.policy_version, 64))
|
|
23
|
+
&& !!doc.scenario_policy && ['all', 'change_relevant'].includes(doc.scenario_policy.mode)
|
|
24
|
+
&& Array.isArray(doc.scenario_policy.mandatory_scenario_ids) && doc.scenario_policy.mandatory_scenario_ids.length <= 100
|
|
25
|
+
&& doc.scenario_policy.mandatory_scenario_ids.every(uuid)
|
|
26
|
+
&& new Set(doc.scenario_policy.mandatory_scenario_ids).size === doc.scenario_policy.mandatory_scenario_ids.length
|
|
27
|
+
&& sha(doc.scenario_policy.sha256)
|
|
28
|
+
&& (doc.nightly_hour_utc === null || integer(doc.nightly_hour_utc) && doc.nightly_hour_utc <= 23)
|
|
29
|
+
&& ['report_only', 'hard_constraints'].includes(doc.mode) && /^[a-f0-9]{40}$/.test(doc.baseline_sha) && text(doc.default_branch, 512)
|
|
30
|
+
&& Array.isArray(doc.triggers) && doc.triggers.length >= 1 && doc.triggers.length <= 3
|
|
31
|
+
&& new Set(doc.triggers).size === doc.triggers.length && doc.triggers.every(one => ['pull_request', 'default_branch', 'nightly'].includes(one))
|
|
32
|
+
&& doc.triggers.includes('nightly') === (doc.nightly_hour_utc !== null);
|
|
33
|
+
}
|
|
34
|
+
export function ciConfigurationMatches(v, workspace, id) {
|
|
35
|
+
if (!v || v.workspace_id !== workspace || !uuid(v.id) || id !== undefined && v.id !== id
|
|
36
|
+
|| !integer(v.revision, 1) || !text(v.name, 120) || !['draft', 'active', 'disconnected'].includes(v.state)
|
|
37
|
+
|| !gitHubId(v.installation_id) || !gitHubId(v.repository_id) || !text(v.repository, 512)
|
|
38
|
+
|| !text(v.account, 255) || typeof v.connection_state !== 'string' || !Number.isFinite(Date.parse(v.updated_at))
|
|
39
|
+
|| v.activation_id !== null && !uuid(v.activation_id) || !v.head
|
|
40
|
+
|| !ciRevisionMatches(v.head, workspace, v.id) || v.head.revision !== v.revision
|
|
41
|
+
|| (v.state === 'disconnected') !== (v.head.document.state === 'disconnected'))
|
|
42
|
+
return false;
|
|
43
|
+
const b = v.baseline;
|
|
44
|
+
return b === null || b.world_id === v.head.document.world_id && b.plan_id === v.head.document.plan_id
|
|
45
|
+
&& integer(b.plan_revision, 1) && integer(b.scenario_count) && integer(b.planned_trials)
|
|
46
|
+
&& typeof b.world_name === 'string' && b.source_revision === v.head.document.baseline_sha;
|
|
47
|
+
}
|
|
48
|
+
function pageMatches(v, page) {
|
|
49
|
+
return v.page === page && integer(v.page, 1) && integer(v.page_size, 1) && v.page_size <= 100
|
|
50
|
+
&& integer(v.count) && Array.isArray(v.results) && v.results.length <= v.page_size;
|
|
51
|
+
}
|
|
52
|
+
export function ciConfigurationsMatch(v, workspace, query = '', state = '', page = 1, repository = '') {
|
|
53
|
+
return !!v && v.workspace_id === workspace && v.query === query.trim() && v.state === state && v.repository_id === repository
|
|
54
|
+
&& pageMatches(v, page) && v.results.every(row => ciConfigurationMatches(row, workspace));
|
|
55
|
+
}
|
|
56
|
+
export function ciRevisionPageMatches(v, workspace, configuration, revision = '', mode = '', page = 1) {
|
|
57
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && v.revision === revision && v.mode === mode
|
|
58
|
+
&& pageMatches(v, page) && v.results.every(row => ciRevisionMatches(row, workspace, configuration)
|
|
59
|
+
&& (!revision || String(row.revision) === revision) && (!mode || row.document.mode === mode)
|
|
60
|
+
&& (row.world_url === null || row.world_url === `/worlds/${row.document.world_id}`));
|
|
61
|
+
}
|
|
62
|
+
export function ciConfigurationRequestMatches(v, workspace, original) {
|
|
63
|
+
return !!v && v.workspace_id === workspace && v.actor_id === original.actor && v.request_key === original.key
|
|
64
|
+
&& (v.state === 'not_received' ? v.configuration_id === null && v.saved_revision === null
|
|
65
|
+
: v.state === 'received' && uuid(v.configuration_id) && (!original.configuration || v.configuration_id === original.configuration)
|
|
66
|
+
&& v.saved_revision !== null && ciRevisionMatches(v.saved_revision, workspace, v.configuration_id)
|
|
67
|
+
&& v.saved_revision.actor_id === original.actor && v.saved_revision.request_key === original.key && v.saved_revision.request_digest === original.digest);
|
|
68
|
+
}
|
|
69
|
+
export function ciPlansMatch(v, workspace, installation, repository, query = '', page = 1) {
|
|
70
|
+
return !!v && v.workspace_id === workspace && v.installation_id === installation && v.repository_id === repository
|
|
71
|
+
&& gitHubId(installation) && gitHubId(repository) && v.query === query.trim() && pageMatches(v, page)
|
|
72
|
+
&& v.results.every(row => uuid(row.id) && uuid(row.world_id) && integer(row.revision, 1) && integer(row.scenario_count)
|
|
73
|
+
&& integer(row.planned_trials) && typeof row.world_name === 'string' && /^[a-f0-9]{40}$/.test(row.source_sha)
|
|
74
|
+
&& Array.isArray(row.source_aliases) && row.source_aliases.length > 0 && row.source_aliases.length <= 128
|
|
75
|
+
&& new Set(row.source_aliases.map(one => one.alias)).size === row.source_aliases.length
|
|
76
|
+
&& row.source_aliases.every(one => /^[a-z][a-z0-9_-]{0,63}$/.test(one.alias) && /^[a-f0-9]{40}$/.test(one.revision))
|
|
77
|
+
&& Number.isFinite(Date.parse(row.created_at)));
|
|
78
|
+
}
|
|
79
|
+
export function ciScenariosMatch(v, workspace, configuration, revision, query = '', page = 1) {
|
|
80
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && String(v.configuration_revision) === revision
|
|
81
|
+
&& v.query === query.trim() && pageMatches(v, page) && uuid(v.plan_id) && uuid(v.world_id) && integer(v.plan_revision, 1)
|
|
82
|
+
&& integer(v.selected_scenarios) && v.count <= v.selected_scenarios && typeof v.world_name === 'string'
|
|
83
|
+
&& v.results.every((row, index) => integer(row.position) && row.position < v.selected_scenarios
|
|
84
|
+
&& (index === 0 || row.position > v.results[index - 1].position)
|
|
85
|
+
&& (row.available ? uuid(row.scenario_id) && uuid(row.item_id) && typeof row.name === 'string' && integer(row.revision, 1)
|
|
86
|
+
&& sha(row.definition_sha256) && row.detail_path === `/scenarios/${row.item_id}`
|
|
87
|
+
: row.available === false && [row.scenario_id, row.item_id, row.name, row.revision, row.definition_sha256, row.detail_path].every(one => one === null)));
|
|
88
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { gitHubId } from './github-directory-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 sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
4
|
+
const integer = (v, min = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= min;
|
|
5
|
+
const code = (v) => typeof v === 'string' && /^[a-z][a-z0-9_]{0,63}$/.test(v);
|
|
6
|
+
const path = (v) => typeof v === 'string' && v.length > 0 && v.length <= 1024
|
|
7
|
+
&& !v.startsWith('/') && !v.includes('\\') && !v.split('/').some(one => one === '..' || one === '.' || one === '') && !/[\x00-\x1f\x7f]/.test(v);
|
|
8
|
+
const page = (v, number) => integer(v.page, 1) && v.page === number && integer(v.count)
|
|
9
|
+
&& integer(v.page_size, 1) && v.page_size <= 100 && Array.isArray(v.results) && v.results.length <= v.page_size
|
|
10
|
+
&& v.results.length <= v.count;
|
|
11
|
+
export function ciPlanScenariosMatch(v, workspace, plan, selected) {
|
|
12
|
+
return !!v && v.workspace_id === workspace && uuid(v.workspace_id) && v.plan_id === plan && uuid(v.plan_id)
|
|
13
|
+
&& v.installation_id === selected.installation && gitHubId(v.installation_id) && v.repository_id === selected.repository && gitHubId(v.repository_id)
|
|
14
|
+
&& v.source_alias === selected.sourceAlias && /^[a-z][a-z0-9_-]{0,63}$/.test(v.source_alias)
|
|
15
|
+
&& [v.world_id, v.source_id].every(uuid) && [v.plan_revision, v.world_revision].every(one => integer(one, 1))
|
|
16
|
+
&& integer(v.configuration_revision) && sha(v.plan_sha256) && v.max_mandatory === 100
|
|
17
|
+
&& v.query === (selected.query ?? '').trim() && page(v, selected.page ?? 1)
|
|
18
|
+
&& v.results.every((row, index) => integer(row.position) && (index === 0 || row.position > v.results[index - 1].position)
|
|
19
|
+
&& typeof row.protected_required_checks === 'boolean' && (row.available
|
|
20
|
+
? uuid(row.scenario_id) && uuid(row.item_id) && integer(row.revision, 1) && sha(row.definition_sha256)
|
|
21
|
+
&& typeof row.name === 'string' && row.detail_path === `/scenarios/${row.item_id}`
|
|
22
|
+
: row.available === false && row.protected_required_checks === true
|
|
23
|
+
&& [row.scenario_id, row.item_id, row.revision, row.definition_sha256, row.name, row.detail_path].every(one => one === null)));
|
|
24
|
+
}
|
|
25
|
+
export function ciCoverageMatches(v, workspace, run) {
|
|
26
|
+
if (!v || v.workspace_id !== workspace || v.run_id !== run || ![v.workspace_id, v.run_id].every(uuid))
|
|
27
|
+
return false;
|
|
28
|
+
if (v.state === 'not_available')
|
|
29
|
+
return v.selection === null;
|
|
30
|
+
const s = v.selection;
|
|
31
|
+
return v.state === 'ready' && !!s && uuid(s.id) && ['all', 'change_relevant'].includes(s.policy_mode)
|
|
32
|
+
&& ['all', 'reduced'].includes(s.state) && code(s.reason)
|
|
33
|
+
&& [s.baseline_count, s.selected_count, s.skipped_count, s.baseline_drills, s.selected_drills].every(one => integer(one))
|
|
34
|
+
&& (s.changed_files === null || integer(s.changed_files))
|
|
35
|
+
&& s.selected_count + s.skipped_count === s.baseline_count && s.selected_drills <= s.baseline_drills
|
|
36
|
+
&& s.reduced === (s.state === 'reduced') && (s.reduced ? s.policy_mode === 'change_relevant' && s.skipped_count > 0 && s.selected_count > 0 : s.skipped_count === 0)
|
|
37
|
+
&& s.source_scope === 'retained_sanitized_files' && sha(s.basis_sha256) && sha(s.document_sha256)
|
|
38
|
+
&& typeof s.created_at === 'string' && Number.isFinite(Date.parse(s.created_at));
|
|
39
|
+
}
|
|
40
|
+
function originalPage(v, workspace, run, selection, query, number) {
|
|
41
|
+
return !!v && v.workspace_id === workspace && v.run_id === run && [v.workspace_id, v.run_id].every(uuid)
|
|
42
|
+
&& v.selection_id === selection.id && v.document_sha256 === selection.document_sha256 && v.query === query.trim() && page(v, number);
|
|
43
|
+
}
|
|
44
|
+
export function ciCoverageScenariosMatch(v, workspace, run, selection, filters = {}) {
|
|
45
|
+
return originalPage(v, workspace, run, selection, filters.query ?? '', filters.page ?? 1)
|
|
46
|
+
&& v.selected === (filters.selected ?? 'all') && v.reason === (filters.reason ?? '') && v.count <= selection.baseline_count
|
|
47
|
+
&& v.results.every((row, index) => integer(row.position) && row.position < selection.baseline_count
|
|
48
|
+
&& (index === 0 || row.position > v.results[index - 1].position) && [row.scenario_id, row.item_id].every(uuid)
|
|
49
|
+
&& integer(row.revision, 1) && sha(row.definition_sha256) && code(row.reason) && typeof row.selected === 'boolean'
|
|
50
|
+
&& (v.selected === 'all' || row.selected === (v.selected === 'selected')) && (!v.reason || row.reason === v.reason)
|
|
51
|
+
&& (row.available ? typeof row.name === 'string' && row.detail_path === `/scenarios/${row.item_id}`
|
|
52
|
+
: row.available === false && row.name === null && row.detail_path === null)
|
|
53
|
+
&& Array.isArray(row.links) && row.links.length <= 24 && row.links.every(link => path(link.path)
|
|
54
|
+
&& [link.before_sha256, link.after_sha256].every(one => one === null || sha(one))
|
|
55
|
+
&& ['changed', 'unchanged', 'missing', 'stale', 'unscoped'].includes(link.status)));
|
|
56
|
+
}
|
|
57
|
+
export function ciCoverageFilesMatch(v, workspace, run, selection, query = '', number = 1) {
|
|
58
|
+
return originalPage(v, workspace, run, selection, query, number) && selection.changed_files !== null && v.count <= selection.changed_files
|
|
59
|
+
&& (query.trim() !== '' || v.count === selection.changed_files) && new Set(v.results.map(row => row.path)).size === v.results.length
|
|
60
|
+
&& v.results.every(row => path(row.path)
|
|
61
|
+
&& [row.before_sha256, row.after_sha256].every(one => one === null || sha(one))
|
|
62
|
+
&& (row.change === 'added' ? row.before_sha256 === null && sha(row.after_sha256)
|
|
63
|
+
: row.change === 'removed' ? sha(row.before_sha256) && row.after_sha256 === null
|
|
64
|
+
: ['modified', 'metadata_changed'].includes(row.change) && sha(row.before_sha256) && sha(row.after_sha256)));
|
|
65
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Human descriptions of the original selection reasons, not inferred impact. */
|
|
2
|
+
const reasons = {
|
|
3
|
+
policy_all: 'All scenarios selected by the reviewed policy',
|
|
4
|
+
broad_trigger: 'The full plan is kept for this run type',
|
|
5
|
+
comparison_unavailable: 'The source comparison was unavailable, so the full plan was kept',
|
|
6
|
+
no_changed_files: 'No changes were found in the source comparison, so the full plan was kept',
|
|
7
|
+
change_relevant: 'Selection based on reviewed source links',
|
|
8
|
+
additional_mandatory: 'Included as an additional mandatory case',
|
|
9
|
+
required_checks: 'Included to preserve required checks',
|
|
10
|
+
unknown_checks: 'Included because check coverage is uncertain',
|
|
11
|
+
coverage_unlinked: 'Included because no source links were provided',
|
|
12
|
+
coverage_invalid: 'Included because its source links could not be validated',
|
|
13
|
+
coverage_capacity: 'Included because its source links could not be fully reviewed',
|
|
14
|
+
coverage_missing: 'Included because linked source is missing',
|
|
15
|
+
coverage_stale: 'Included because its source links are out of date',
|
|
16
|
+
coverage_unscoped: 'Included because its source links are outside the compared component',
|
|
17
|
+
linked_change: 'Included because linked source changed',
|
|
18
|
+
linked_unchanged: 'Skipped because its linked source was unchanged',
|
|
19
|
+
empty_selection_kept_all: 'No safe subset was found, so the full plan was kept',
|
|
20
|
+
};
|
|
21
|
+
export const ciCoverageReason = (value) => reasons[value] ?? 'Inspect the saved selection details';
|
|
22
|
+
export const ciCoverageLinkLabel = (value) => ({ changed: 'Changed', unchanged: 'Unchanged', missing: 'Source missing',
|
|
23
|
+
stale: 'Link out of date', unscoped: 'Outside compared source' }[value] ?? 'Source link details unavailable');
|
|
24
|
+
export const ciCoverageFileLabel = (value) => ({ added: 'Added', removed: 'Removed', modified: 'Modified',
|
|
25
|
+
metadata_changed: 'File metadata changed' }[value] ?? 'Change details unavailable');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const ciDeliveryStates = ['queued', 'prepared', 'dispatching', 'uncertain', 'attention_required', 'confirmed', 'rejected', 'superseded'];
|
|
2
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
3
|
+
const count = (v, min = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= min;
|
|
4
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
5
|
+
export function ciDeliveryRead(value, matches) {
|
|
6
|
+
if (!matches)
|
|
7
|
+
throw new Error('The GitHub delivery did not match this workspace, run or original request. Reload its retained history before continuing.');
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function ciDeliveryIntentMatches(v, run) {
|
|
11
|
+
return !!v && uuid(v.intent_id) && uuid(v.run_id) && (!run || v.run_id === run) && ciDeliveryStates.includes(v.state)
|
|
12
|
+
&& count(v.sequence) && count(v.source_revision) && count(v.retry_number) && ['report', 'progress', 'withdrawal', 'rerun_review'].includes(v.purpose)
|
|
13
|
+
&& (v.purpose === 'rerun_review' ? uuid(v.rerun_event_id) : v.rerun_event_id === null)
|
|
14
|
+
&& date(v.created_at) && [v.dispatched_at, v.confirmed_at].every(one => one === null || date(one))
|
|
15
|
+
&& (v.progress_revision === null || count(v.progress_revision, 1)) && (v.phase === null || typeof v.phase === 'string')
|
|
16
|
+
&& (v.write_http_status === null || count(v.write_http_status, 100) && v.write_http_status <= 599)
|
|
17
|
+
&& (v.conclusion === null || ['success', 'failure', 'neutral', 'cancelled', 'action_required'].includes(v.conclusion))
|
|
18
|
+
&& (v.state !== 'confirmed' || v.dispatched_at !== null && v.confirmed_at !== null);
|
|
19
|
+
}
|
|
20
|
+
export function ciDeliveryMatches(v, workspace, run, kind) {
|
|
21
|
+
if (!v || v.workspace_id !== workspace || v.run_id !== run || v.kind !== kind || !uuid(workspace) || !uuid(run)
|
|
22
|
+
|| ![...ciDeliveryStates, 'not_available', 'not_applicable'].includes(v.state)
|
|
23
|
+
|| ![v.check_confirmed, v.comment_confirmed, v.confirmed_for_run, v.result_current].every(one => typeof one === 'boolean')
|
|
24
|
+
|| v.head !== null && (!ciDeliveryIntentMatches(v.head, kind === 'check' ? run : undefined) || v.head.state !== v.state)
|
|
25
|
+
|| v.waiting_for !== null && !ciDeliveryIntentMatches(v.waiting_for, kind === 'check' ? run : undefined))
|
|
26
|
+
return false;
|
|
27
|
+
if (v.url !== null) {
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(v.url);
|
|
30
|
+
if (url.protocol !== 'https:' || url.hostname !== 'github.com' || url.username || url.password)
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (v.state === 'not_available' || v.state === 'not_applicable')
|
|
38
|
+
return v.head === null && v.waiting_for === null && !v.check_confirmed && !v.comment_confirmed
|
|
39
|
+
&& !v.result_current && !v.confirmed_for_run && (v.state !== 'not_applicable' || kind === 'comment');
|
|
40
|
+
const confirmed = kind === 'check' ? v.check_confirmed : v.comment_confirmed;
|
|
41
|
+
return v.head !== null && (kind === 'check' ? !v.comment_confirmed : !v.check_confirmed)
|
|
42
|
+
&& (!confirmed || v.head.state === 'confirmed' && v.head.dispatched_at !== null && v.head.confirmed_at !== null)
|
|
43
|
+
&& (!v.result_current || v.head.purpose === 'report' && v.head.run_id === run);
|
|
44
|
+
}
|
|
45
|
+
export function ciDeliveriesMatch(v, workspace, run, kind, status, page) {
|
|
46
|
+
return !!v && v.workspace_id === workspace && v.run_id === run && v.kind === kind && v.status === status && count(v.count)
|
|
47
|
+
&& v.page === page && count(v.page_size, 1) && v.page_size <= 100 && Array.isArray(v.results) && v.results.length <= v.page_size
|
|
48
|
+
&& v.results.every(one => ciDeliveryIntentMatches(one, kind === 'check' ? run : undefined) && (!status || one.state === status))
|
|
49
|
+
&& new Set(v.results.map(one => one.intent_id)).size === v.results.length;
|
|
50
|
+
}
|
|
51
|
+
export function ciDeliveryRecoveryMatches(v, workspace, intent, kind, original = {}) {
|
|
52
|
+
return !!v && v.workspace_id === workspace && v.intent_id === intent && v.kind === kind
|
|
53
|
+
&& [v.id, v.workspace_id, v.actor_id, v.request_key, v.intent_id].every(uuid) && /^[a-f0-9]{64}$/.test(v.request_digest)
|
|
54
|
+
&& count(v.sequence, 1) && typeof v.retry_rejected === 'boolean' && /^[a-z_]{1,24}$/.test(v.state) && date(v.created_at)
|
|
55
|
+
&& (!original.id || original.id === v.id) && (!original.actor || original.actor === v.actor_id)
|
|
56
|
+
&& (!original.key || original.key === v.request_key) && (!original.digest || original.digest === v.request_digest);
|
|
57
|
+
}
|
|
58
|
+
export function ciDeliveryRecoveryRequestMatches(v, workspace, intent, kind, original) {
|
|
59
|
+
return !!v && v.workspace_id === workspace && v.intent_id === intent && v.kind === kind && v.actor_id === original.actor && v.request_key === original.key
|
|
60
|
+
&& (v.state === 'not_received' ? v.request === null && !original.id : v.state === 'received' && v.request !== null && ciDeliveryRecoveryMatches(v.request, workspace, intent, kind, original));
|
|
61
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ciCoverageMatches } from './ci-coverage-contracts.js';
|
|
2
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
3
|
+
const sha = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
4
|
+
const count = (v, min = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= min;
|
|
5
|
+
const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
|
|
6
|
+
const reportStates = ['waiting', 'collecting', 'ready', 'attention_required', 'changed', 'canceled', 'not_available'];
|
|
7
|
+
const trialCounts = ['retained', 'completed', 'failed', 'platform_failed', 'timed_out', 'canceled', 'unfinished', 'invoked', 'results',
|
|
8
|
+
'declared', 'expected_exact', 'observed', 'exact_met', 'exact_unmet', 'unknown', 'native_unmet'];
|
|
9
|
+
export function ciResultRead(value, matches) {
|
|
10
|
+
if (!matches)
|
|
11
|
+
throw new Error('The report did not match the selected workspace, run or original request. Reload its retained result before continuing.');
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
export function ciResultMatches(v, workspace, run, selected = {}) {
|
|
15
|
+
if (!v || v.workspace_id !== workspace || v.run_id !== run || !uuid(workspace) || !uuid(run) || !reportStates.includes(v.state)
|
|
16
|
+
|| typeof v.published !== 'boolean' || typeof v.check_confirmed !== 'boolean' || typeof v.publication_current !== 'boolean'
|
|
17
|
+
|| ![true, false, null].includes(v.comment_required) || ![true, false, null].includes(v.comment_confirmed)
|
|
18
|
+
|| ![null, 'pending', 'settled'].includes(v.withdrawal) || v.published !== (v.ci_verdict !== null)
|
|
19
|
+
|| v.ci_verdict !== null && !['neutral', 'success', 'failure'].includes(v.ci_verdict))
|
|
20
|
+
return false;
|
|
21
|
+
if (v.state === 'not_available')
|
|
22
|
+
return v.result === null && v.id === null && v.campaign_id === null && v.sequence === null
|
|
23
|
+
&& v.source_revision === null && v.observed_at === null && v.report_path === null && !v.published && !v.check_confirmed && !v.publication_current
|
|
24
|
+
&& v.comment_required === null && v.comment_confirmed === null && v.withdrawal === null;
|
|
25
|
+
if (!uuid(v.id) || !uuid(v.campaign_id) || !count(v.sequence, 1) || !count(v.source_revision)
|
|
26
|
+
|| v.report_path !== `/campaigns/${v.campaign_id}` || selected.campaign && selected.campaign !== v.campaign_id
|
|
27
|
+
|| v.observed_at !== null && !date(v.observed_at))
|
|
28
|
+
return false;
|
|
29
|
+
if (v.state !== 'ready')
|
|
30
|
+
return v.result === null && !v.published && v.ci_verdict === null;
|
|
31
|
+
const result = v.result;
|
|
32
|
+
if (!result || result.schema !== 'firedrill.github-ci-result.v1' || result.run_id !== run || result.campaign_id !== v.campaign_id
|
|
33
|
+
|| !/^[a-f0-9]{40}$/.test(result.head_sha) || selected.sha && selected.sha !== result.head_sha
|
|
34
|
+
|| !uuid(result.configuration_revision_id) || !sha(result.plan_sha256) || !date(result.observed_at)
|
|
35
|
+
|| !date(v.observed_at) || Date.parse(result.observed_at) !== Date.parse(v.observed_at)
|
|
36
|
+
|| result.report_path !== v.report_path || result.scores_path !== `/api/workspaces/${workspace}/ci/runs/${run}/result/scores`
|
|
37
|
+
|| !count(result.scenarios) || !count(result.planned_trials) || !['completed', 'failed', 'canceled'].includes(result.execution_state))
|
|
38
|
+
return false;
|
|
39
|
+
if (result.coverage !== undefined && result.coverage !== null && (!ciCoverageMatches({ workspace_id: workspace, run_id: run,
|
|
40
|
+
state: 'ready', selection: result.coverage }, workspace, run) || result.scenarios !== result.coverage.selected_count
|
|
41
|
+
|| result.planned_trials !== result.coverage.selected_drills))
|
|
42
|
+
return false;
|
|
43
|
+
const trials = result.trials, assessment = result.assessment, decision = result.decision;
|
|
44
|
+
return !!trials && trialCounts.every(key => count(trials[key])) && !!assessment
|
|
45
|
+
&& [assessment.expected, assessment.assessed, assessment.not_applicable, assessment.not_assessable, assessment.unavailable, assessment.score_groups].every(one => count(one))
|
|
46
|
+
&& assessment.assessed + assessment.not_applicable + assessment.not_assessable + assessment.unavailable === assessment.expected
|
|
47
|
+
&& typeof assessment.requested === 'boolean' && !!decision && decision.published === false && decision.constraint_scope === 'source_exact_checks'
|
|
48
|
+
&& ['report_only', 'hard_constraints'].includes(decision.mode) && ['report_only', 'met', 'unmet', 'incomplete'].includes(decision.status)
|
|
49
|
+
&& ['neutral', 'success', 'failure'].includes(decision.suggested_conclusion)
|
|
50
|
+
&& [decision.complete, decision.execution_complete, decision.check_coverage_complete, decision.assessment_coverage_complete, decision.has_exact_constraints].every(one => typeof one === 'boolean')
|
|
51
|
+
&& decision.complete === (decision.execution_complete && decision.check_coverage_complete && decision.assessment_coverage_complete)
|
|
52
|
+
&& (!v.published || v.publication_current && v.check_confirmed && (!v.comment_required || v.comment_confirmed === true)
|
|
53
|
+
&& v.ci_verdict === decision.suggested_conclusion);
|
|
54
|
+
}
|
|
55
|
+
function reportPageMatches(v, workspace, run, query, page) {
|
|
56
|
+
if (!v || v.workspace_id !== workspace || v.run_id !== run || v.query !== query.trim() || !uuid(v.campaign_id)
|
|
57
|
+
|| !count(v.source_revision) || v.observed_at !== null && !date(v.observed_at) || !Array.isArray(v.results))
|
|
58
|
+
return false;
|
|
59
|
+
if (v.state !== 'ready')
|
|
60
|
+
return reportStates.includes(v.state) && v.count === null && v.page === null && v.page_size === null
|
|
61
|
+
&& v.next === null && v.previous === null && v.results.length === 0;
|
|
62
|
+
return count(v.count) && v.page === page && count(v.page_size, 1) && v.page_size <= 100 && v.results.length <= v.page_size
|
|
63
|
+
&& date(v.observed_at) && v.results.every((one, index) => count(one.position)
|
|
64
|
+
&& (index === 0 || one.position > v.results[index - 1].position) && uuid(one.criterion_id));
|
|
65
|
+
}
|
|
66
|
+
export function ciCriteriaMatches(v, workspace, run, query, status, page) {
|
|
67
|
+
if (!reportPageMatches(v, workspace, run, query, page) || v.status !== status)
|
|
68
|
+
return false;
|
|
69
|
+
return v.state !== 'ready' || v.results.every(one => {
|
|
70
|
+
const values = one.counts;
|
|
71
|
+
return sha(one.definition_sha256) && (one.name === null || typeof one.name === 'string') && !!values && Object.values(values).every(number => count(number))
|
|
72
|
+
&& values.assessed + values.not_applicable + values.not_assessable + values.unavailable === values.expected
|
|
73
|
+
&& ['complete', 'incomplete', 'not_assessable'].includes(one.coverage_state)
|
|
74
|
+
&& (!status || status === one.coverage_state) && (one.definition_url === null || /^\/evaluations\/[a-f0-9-]{36}$/.test(one.definition_url));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
export function ciScoresMatch(v, workspace, run, query, page) {
|
|
78
|
+
if (!reportPageMatches(v, workspace, run, query, page))
|
|
79
|
+
return false;
|
|
80
|
+
return v.state !== 'ready' || v.results.every(one => {
|
|
81
|
+
const { cohort, summary } = one;
|
|
82
|
+
if (!uuid(one.example_execution_id) || !cohort || !uuid(cohort.assessment_id) || !uuid(cohort.scenario_id) || !sha(cohort.identity)
|
|
83
|
+
|| !Array.isArray(cohort.levels) || cohort.levels.length < 2 || cohort.levels.length > 10 || !cohort.levels.every(level => typeof level === 'string')
|
|
84
|
+
|| !['higher', 'lower'].includes(cohort.direction) || !summary
|
|
85
|
+
|| ![summary.observed, summary.assessed, summary.not_assessable, summary.not_applicable].every(number => count(number))
|
|
86
|
+
|| summary.assessed + summary.not_assessable + summary.not_applicable !== summary.observed)
|
|
87
|
+
return false;
|
|
88
|
+
const values = [summary.mean, summary.minimum, summary.maximum];
|
|
89
|
+
return summary.assessed === 0 ? values.every(value => value === null) : values.every(value => typeof value === 'number'
|
|
90
|
+
&& Number.isFinite(value) && value >= 0 && value <= cohort.levels.length - 1)
|
|
91
|
+
&& summary.minimum <= summary.mean && summary.mean <= summary.maximum;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export function ciResultRefreshMatches(v, workspace, run, original = {}) {
|
|
95
|
+
return !!v && v.workspace_id === workspace && v.run_id === run && [v.id, v.workspace_id, v.actor_id, v.request_key, v.run_id].every(uuid)
|
|
96
|
+
&& sha(v.request_digest) && count(v.sequence, 1) && /^[a-z_]{1,24}$/.test(v.state) && date(v.created_at)
|
|
97
|
+
&& (!original.id || original.id === v.id) && (!original.actor || original.actor === v.actor_id)
|
|
98
|
+
&& (!original.key || original.key === v.request_key) && (!original.digest || original.digest === v.request_digest);
|
|
99
|
+
}
|
|
100
|
+
export function ciResultRefreshRequestMatches(v, workspace, run, original) {
|
|
101
|
+
return !!v && v.workspace_id === workspace && v.run_id === run && v.actor_id === original.actor && v.request_key === original.key
|
|
102
|
+
&& (v.state === 'not_received' ? v.request === null && !original.id : v.state === 'received' && v.request !== null && ciResultRefreshMatches(v.request, workspace, run, original));
|
|
103
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const ciRunStates = ['queued', 'acquiring_source', 'preparing_world', 'configuration_required', 'validating', 'planning', 'running',
|
|
2
|
+
'report_pending', 'attention_required', 'canceling', 'completed', 'failed', 'canceled', 'superseded'];
|
|
3
|
+
const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
4
|
+
const integer = (v, min = 0) => typeof v === 'number' && Number.isSafeInteger(v) && v >= min;
|
|
5
|
+
const date = (v) => v === null || Number.isFinite(Date.parse(v));
|
|
6
|
+
export function ciRunRead(value, matches) {
|
|
7
|
+
if (!matches)
|
|
8
|
+
throw new Error('The CI run response did not match the selected workspace, original preparation or history filters. Check the retained run before continuing.');
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
export function ciRunMatches(v, workspace, selected = {}) {
|
|
12
|
+
if (!v || v.workspace_id !== workspace || ![v.id, v.workspace_id, v.configuration_id, v.created_by_id, v.event_id, v.request_key].every(uuid)
|
|
13
|
+
|| !/^[a-f0-9]{64}$/.test(v.request_digest) || !integer(v.configuration_revision, 1)
|
|
14
|
+
|| selected.id !== undefined && v.id !== selected.id || selected.configuration !== undefined && v.configuration_id !== selected.configuration
|
|
15
|
+
|| selected.actor !== undefined && v.created_by_id !== selected.actor || selected.key !== undefined && v.request_key !== selected.key
|
|
16
|
+
|| selected.digest !== undefined && v.request_digest !== selected.digest || !/^[a-f0-9]{40}$/.test(v.head_sha)
|
|
17
|
+
|| typeof v.reference !== 'string' || !v.reference.length || v.reference.length > 1024 || /[\x00-\x1f\x7f]/.test(v.reference)
|
|
18
|
+
|| typeof v.fork !== 'boolean' || !ciRunStates.includes(v.state) || typeof v.cleanup_pending !== 'boolean'
|
|
19
|
+
|| v.cleanup_pending !== (v.state === 'canceling') || !Number.isFinite(Date.parse(v.created_at)) || !Number.isFinite(Date.parse(v.updated_at))
|
|
20
|
+
|| !date(v.observed_head_at) || ![v.source_id, v.world_id, v.campaign_id, v.validation_id, v.activation_id].every(one => one === null || uuid(one))
|
|
21
|
+
|| !v.head_status || !['pending', 'current', 'changed'].includes(v.head_status.state) || !date(v.head_status.checked_at)
|
|
22
|
+
|| v.world_url !== null && v.world_url !== `/worlds/${v.world_id}` || v.report_url !== null && v.report_url !== `/campaigns/${v.campaign_id}`
|
|
23
|
+
|| v.world_configuration_revision !== null && (!v.world_id || !integer(v.world_configuration_revision, 1)))
|
|
24
|
+
return false;
|
|
25
|
+
if (v.trigger === 'pull_request' ? !integer(v.pull_number, 1) : v.trigger === 'check_run'
|
|
26
|
+
? !(v.pull_number === null ? !v.fork : integer(v.pull_number, 1))
|
|
27
|
+
: !['push', 'scheduled'].includes(v.trigger) || v.pull_number !== null || v.fork)
|
|
28
|
+
return false;
|
|
29
|
+
if (v.source && (!uuid(v.source.id) || v.source.id !== v.source_id
|
|
30
|
+
|| !(v.source.revision === v.head_sha || /^set:[a-f0-9]{64}$/.test(v.source.revision))
|
|
31
|
+
|| !integer(v.source.excluded_files)))
|
|
32
|
+
return false;
|
|
33
|
+
const report = v.recorded_report;
|
|
34
|
+
return report === undefined || report === null || report.basis === 'recorded_report' && ['met', 'unmet', 'incomplete', 'report_only'].includes(report.status)
|
|
35
|
+
&& typeof report.complete === 'boolean' && (report.status !== 'met' || report.complete) && Number.isFinite(Date.parse(report.observed_at));
|
|
36
|
+
}
|
|
37
|
+
export function ciRunsMatch(v, workspace, configuration, filters = {}, page = 1) {
|
|
38
|
+
return !!v && v.workspace_id === workspace && v.configuration_id === configuration && v.page === page && integer(v.page, 1)
|
|
39
|
+
&& integer(v.count) && integer(v.page_size, 1) && v.page_size <= 100 && Array.isArray(v.results) && v.results.length <= v.page_size
|
|
40
|
+
&& !!v.filters && ['sha', 'state', 'trigger', 'reference', 'outcome'].every(key => v.filters[key] === (filters[key] ?? ''))
|
|
41
|
+
&& (filters.world ? v.filters.world?.id === filters.world : v.filters.world === null)
|
|
42
|
+
&& v.results.every(row => ciRunMatches(row, workspace, { configuration }) && (!filters.sha || row.head_sha === filters.sha.toLowerCase())
|
|
43
|
+
&& (!filters.state || row.state === filters.state) && (!filters.trigger || row.trigger === filters.trigger)
|
|
44
|
+
&& (!filters.reference || row.reference.startsWith(filters.reference))
|
|
45
|
+
&& (!filters.outcome || (filters.outcome === 'no_report' ? !row.recorded_report : row.recorded_report?.status === filters.outcome)));
|
|
46
|
+
}
|
|
47
|
+
export function ciRunRequestMatches(v, workspace, original) {
|
|
48
|
+
return !!v && v.workspace_id === workspace && v.actor_id === original.actor && v.request_key === original.key
|
|
49
|
+
&& (v.state === 'not_received' ? v.run === null && !original.id : v.state === 'received' && v.run !== null
|
|
50
|
+
&& ciRunMatches(v.run, workspace, original) && v.run.activation_id === null);
|
|
51
|
+
}
|