@deksden-com/dd-flow-cli 0.9.0-beta.15 → 0.9.0-beta.151
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/CHANGELOG.md +907 -0
- package/README.md +139 -10
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +386 -0
- package/dist/cli/command-metadata.js +232 -0
- package/dist/cli/command-option-descriptions.js +192 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/diagnostic-invocation.js +46 -0
- package/dist/cli/diagnostic.js +120 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +396 -19
- package/dist/cli/hook-ingress.js +102 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +50 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +249 -0
- package/dist/cli/memory-storage-admission.js +54 -0
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +90 -0
- package/dist/cli/native-hook-preparation.js +55 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +72 -0
- package/dist/cli/run-cli.js +1734 -550
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +150 -0
- package/dist/cli.js +89 -2
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +28 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +79 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +216 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +67 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +462 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1092 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +86 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +231 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +954 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +141 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +626 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +555 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +180 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +627 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +390 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +752 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1200 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +166 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +140 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +113 -0
- package/dist/harness-runtime/lib/native-children.d.mts +34 -0
- package/dist/harness-runtime/lib/native-children.mjs +265 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
- package/dist/harness-runtime/lib/runner-events.mjs +202 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-decision.schema.json +9 -0
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +211 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +29 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +56 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +12 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +54 -15
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +277 -0
- package/dist/services/check-runner-proof.js +305 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +26 -63
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1401 -119
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +16 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +71 -0
- package/dist/services/controller-child-observer.js +121 -0
- package/dist/services/controller-execution.js +94 -0
- package/dist/services/controller-fanout.js +226 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +49 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +107 -0
- package/dist/services/descriptor-run-start.js +475 -0
- package/dist/services/descriptor-stage-coordinator.js +250 -0
- package/dist/services/descriptor-stage-settlement.js +223 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +67 -0
- package/dist/services/diagnostic-run-bundle.js +74 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +108 -0
- package/dist/services/diagnostic-stage-results.js +132 -0
- package/dist/services/diagnostic-start.js +84 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +354 -163
- package/dist/services/eval-snapshots.js +1316 -150
- package/dist/services/execution-bundles.js +275 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +310 -0
- package/dist/services/flow-catalog.js +30 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +387 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +791 -326
- package/dist/services/hybrid-contracts.js +474 -0
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +226 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2148 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +449 -69
- package/dist/services/memory-accepted-composition.js +78 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +328 -0
- package/dist/services/memory-compose-handler.js +400 -0
- package/dist/services/memory-composition-acceptance.js +375 -0
- package/dist/services/memory-composition-graph.js +165 -0
- package/dist/services/memory-composition-registration.js +161 -0
- package/dist/services/memory-composition-work-custody.js +156 -0
- package/dist/services/memory-composition-work-results.js +108 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +37 -0
- package/dist/services/memory-corrected-review-findings.js +108 -0
- package/dist/services/memory-corrected-review-inputs.js +215 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +178 -0
- package/dist/services/memory-corrected-review-registration.js +192 -0
- package/dist/services/memory-correction-check-receipt.js +235 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +457 -0
- package/dist/services/memory-finish-handler.js +229 -0
- package/dist/services/memory-finish-integration.js +450 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-git-integration.js +220 -0
- package/dist/services/memory-historical-corrected-review.js +212 -0
- package/dist/services/memory-historical-original-review.js +143 -0
- package/dist/services/memory-historical-repair-registration.js +105 -0
- package/dist/services/memory-historical-second-review.js +147 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +131 -0
- package/dist/services/memory-init-controller-start.js +103 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +165 -0
- package/dist/services/memory-init-lint-capability.js +203 -0
- package/dist/services/memory-init-native-capacity.js +309 -0
- package/dist/services/memory-init-preflight.js +334 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +213 -0
- package/dist/services/memory-init-request-input.js +82 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +135 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +91 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +174 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +221 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +103 -0
- package/dist/services/memory-normal-flow-entry.js +87 -0
- package/dist/services/memory-ordinary-defaults.js +129 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +274 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +488 -0
- package/dist/services/memory-repair-history.js +496 -0
- package/dist/services/memory-repair-physical-binding.js +194 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +162 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +795 -0
- package/dist/services/memory-repair-registration.js +240 -0
- package/dist/services/memory-repair-work-baseline.js +415 -0
- package/dist/services/memory-repair-work-completion.js +117 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +108 -0
- package/dist/services/memory-repair-workspace.js +261 -0
- package/dist/services/memory-repair-writer-copy.js +736 -0
- package/dist/services/memory-result-binding.js +239 -0
- package/dist/services/memory-retained-review-authority.js +138 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +162 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +206 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +246 -0
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +117 -0
- package/dist/services/memory-review-work-custody.js +126 -0
- package/dist/services/memory-review-work-input.js +91 -0
- package/dist/services/memory-review-work-results.js +95 -0
- package/dist/services/memory-second-corrected-review-inputs.js +167 -0
- package/dist/services/memory-second-correction-plan.js +77 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +65 -0
- package/dist/services/memory-stage-results.js +290 -0
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +156 -0
- package/dist/services/memory-verification-checks.js +317 -0
- package/dist/services/memory-verification-receipt.js +160 -0
- package/dist/services/memory-verify-handler.js +137 -0
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +64 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +163 -35
- package/dist/services/migrations.js +164 -114
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +50 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +17 -87
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +463 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +588 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +150 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +31 -0
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +428 -0
- package/dist/services/run-control.js +1092 -0
- package/dist/services/run-controller-adapter.js +400 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +205 -0
- package/dist/services/run-controller-recovery.js +295 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1395 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +107 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +456 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/runs.js +1023 -246
- package/dist/services/runtime-budget.js +446 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +605 -0
- package/dist/services/runtime-scope-stop.js +211 -0
- package/dist/services/runtime-scope-worker.js +314 -0
- package/dist/services/runtime-service.js +19 -14
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +36 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +240 -18
- package/dist/services/stage-handlers.js +89 -0
- package/dist/services/stage-lifecycle.js +152 -116
- package/dist/services/stage-pause.js +202 -91
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +393 -0
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +235 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +355 -21
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +287 -132
- package/dist/services/vnext-plan.js +228 -104
- package/dist/services/vnext-protocolize.js +533 -95
- package/dist/services/vnext-specify.js +123 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1706 -200
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +105 -48
- package/dist/shared/entity-references.js +1 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +930 -40
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +159 -0
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paired-writer-migration.js +424 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +96 -0
- package/dist/storage/writer-migration.js +346 -0
- package/dist/storage/writer-restore.js +217 -0
- package/dist/verification-components.json +299 -0
- package/package.json +51 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/probe-codex-history.mjs +37 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { AppError } from "../shared/errors.js";
|
|
5
|
+
import { readSetupStatus } from "./setup.js";
|
|
6
|
+
import { readBoundedReleaseFile } from "./release-source.js";
|
|
7
|
+
import { hybridDigest, validateHybridShape } from "./hybrid-contracts.js";
|
|
8
|
+
import { admitBundledFlow } from "./stage-handlers.js";
|
|
9
|
+
import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
|
|
10
|
+
const limit = 1024 * 1024;
|
|
11
|
+
const flowIds = ["mb-init", "mb-upgrade", "mb-audit", "project-review"];
|
|
12
|
+
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
13
|
+
function refuse(reason) {
|
|
14
|
+
throw new AppError("installed_flow_source_invalid", "Installed flow source is not the selected verified canon", 1, { reason, phase: "prepare", effect: "no_effect" });
|
|
15
|
+
}
|
|
16
|
+
function directory(target) {
|
|
17
|
+
if (!fs.lstatSync(target).isDirectory())
|
|
18
|
+
refuse("unsafe_source_directory");
|
|
19
|
+
}
|
|
20
|
+
function containedFile(root, logical) {
|
|
21
|
+
if (!/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(logical)
|
|
22
|
+
|| logical.split("/").some(part => part === "." || part === ".."))
|
|
23
|
+
refuse("unsafe_source_path");
|
|
24
|
+
let current = root;
|
|
25
|
+
for (const part of logical.split("/").slice(0, -1)) {
|
|
26
|
+
current = path.join(current, part);
|
|
27
|
+
directory(current);
|
|
28
|
+
}
|
|
29
|
+
return path.join(root, logical);
|
|
30
|
+
}
|
|
31
|
+
/** Initial RUN materialization input, not handler/engine/storage admission.
|
|
32
|
+
* Built-in memory flows live in the global canon, not the curated project pack.
|
|
33
|
+
* Existing RUNs must restore their retained bundle rather than call this reader.
|
|
34
|
+
*/
|
|
35
|
+
export class PreparedInstalledFlowSource {
|
|
36
|
+
sha256;
|
|
37
|
+
#home;
|
|
38
|
+
#installationSha256;
|
|
39
|
+
#flowId;
|
|
40
|
+
#descriptor;
|
|
41
|
+
#canon;
|
|
42
|
+
#blobs;
|
|
43
|
+
constructor(home, installationSha256, flowId, descriptor, canon, blobs) {
|
|
44
|
+
this.#home = home;
|
|
45
|
+
this.#installationSha256 = installationSha256;
|
|
46
|
+
this.#flowId = flowId;
|
|
47
|
+
this.#descriptor = descriptor;
|
|
48
|
+
this.#canon = canon;
|
|
49
|
+
this.#blobs = blobs;
|
|
50
|
+
this.sha256 = hybridDigest({ installation_sha256: installationSha256, canon, inventory: this.inventory() });
|
|
51
|
+
Object.freeze(this);
|
|
52
|
+
}
|
|
53
|
+
static fromInstalledRelease(home, expectedInstallationSha256, flowId) {
|
|
54
|
+
try {
|
|
55
|
+
if (!path.isAbsolute(home) || !/^[a-f0-9]{64}$/.test(expectedInstallationSha256) || !flowIds.includes(flowId))
|
|
56
|
+
refuse("invalid_selection");
|
|
57
|
+
const selected = readSetupStatus(home);
|
|
58
|
+
if (!selected.installation || selected.installation_readiness !== "installed")
|
|
59
|
+
refuse("installed_release_unavailable");
|
|
60
|
+
const installation = selected.installation;
|
|
61
|
+
if (installation.manifest_sha256 !== expectedInstallationSha256)
|
|
62
|
+
refuse("installation_selection_changed");
|
|
63
|
+
directory(installation.canon_root);
|
|
64
|
+
const roots = fs.readdirSync(installation.canon_root);
|
|
65
|
+
if (roots.length !== 1)
|
|
66
|
+
refuse("ambiguous_archive_root");
|
|
67
|
+
const canonRoot = path.join(installation.canon_root, roots[0]);
|
|
68
|
+
directory(canonRoot);
|
|
69
|
+
const dotRoot = path.join(canonRoot, ".memory-bank"), memoryRoot = fs.existsSync(dotRoot) ? dotRoot : canonRoot;
|
|
70
|
+
directory(memoryRoot);
|
|
71
|
+
const flowRoot = path.join(memoryRoot, "dd-flow");
|
|
72
|
+
directory(flowRoot);
|
|
73
|
+
const descriptorPath = `${flowId}/flow-descriptor.json`;
|
|
74
|
+
const bytes = readBoundedReleaseFile(containedFile(flowRoot, descriptorPath), limit);
|
|
75
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
76
|
+
if (validateHybridShape("flow-descriptor", value).length || value.id !== flowId || value.origin !== "canon"
|
|
77
|
+
|| value.stages.length > 64)
|
|
78
|
+
refuse("invalid_descriptor");
|
|
79
|
+
const blobs = new Map([[descriptorPath, Buffer.from(bytes)]]), folded = new Map([[descriptorPath.toLowerCase(), descriptorPath]]);
|
|
80
|
+
let total = bytes.length;
|
|
81
|
+
for (const stage of value.stages)
|
|
82
|
+
for (const kind of ["instruction", "result_schema"]) {
|
|
83
|
+
const reference = stage[kind], existing = blobs.get(reference.path);
|
|
84
|
+
if (existing) {
|
|
85
|
+
if (hash(existing) !== reference.sha256)
|
|
86
|
+
refuse("conflicting_source_reference");
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (folded.has(reference.path.toLowerCase()))
|
|
90
|
+
refuse("conflicting_source_path");
|
|
91
|
+
const content = readBoundedReleaseFile(containedFile(flowRoot, reference.path), limit);
|
|
92
|
+
if (!content.length || (total += content.length) > 32 * limit || hash(content) !== reference.sha256)
|
|
93
|
+
refuse("source_bytes_changed");
|
|
94
|
+
new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
95
|
+
if (kind === "result_schema")
|
|
96
|
+
JSON.parse(content.toString("utf8"));
|
|
97
|
+
folded.set(reference.path.toLowerCase(), reference.path);
|
|
98
|
+
blobs.set(reference.path, Buffer.from(content));
|
|
99
|
+
}
|
|
100
|
+
if (isDiagnosticFlow(flowId)) {
|
|
101
|
+
const support = ["mbb/index.md", "mbb/memory-bank-structure.md", "mbb/frontmatter-standards.md", "mbb/cross-references.md", "mbb/named-deferrals-guide.md",
|
|
102
|
+
...fs.readdirSync(path.join(memoryRoot, "mbb/aspects")).filter(name => name.endsWith(".md")).map(name => `mbb/aspects/${name}`),
|
|
103
|
+
"dd-flow/mb-sdlc/review/aspects.md", "dd-flow/mb-sdlc/review/critics/universal.md", "dd-flow/mb-sdlc/review/critics/architectural-harmonization.md",
|
|
104
|
+
...fs.readdirSync(path.join(flowRoot, "mb-audit/aspects")).filter(name => name.endsWith(".md")).map(name => `dd-flow/mb-audit/aspects/${name}`)];
|
|
105
|
+
for (const logical of support) {
|
|
106
|
+
const content = readBoundedReleaseFile(containedFile(memoryRoot, logical), limit), name = `support/${logical}`;
|
|
107
|
+
if (!content.length || (total += content.length) > 32 * limit || folded.has(name.toLowerCase()))
|
|
108
|
+
refuse("invalid_support_inventory");
|
|
109
|
+
new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
110
|
+
folded.set(name.toLowerCase(), name);
|
|
111
|
+
blobs.set(name, Buffer.from(content));
|
|
112
|
+
}
|
|
113
|
+
const workSchema = "schemas/diagnostic-work-result.schema.json";
|
|
114
|
+
const content = readBoundedReleaseFile(containedFile(flowRoot, workSchema), limit);
|
|
115
|
+
if (!content.length || (total += content.length) > 32 * limit || folded.has(workSchema.toLowerCase()))
|
|
116
|
+
refuse("invalid_support_inventory");
|
|
117
|
+
JSON.parse(content.toString("utf8"));
|
|
118
|
+
blobs.set(workSchema, Buffer.from(content));
|
|
119
|
+
}
|
|
120
|
+
// Verify compressed/expanded archive identity again after collecting bytes.
|
|
121
|
+
// A coherent descriptor+prompt edit cannot become a new trusted selection.
|
|
122
|
+
const final = readSetupStatus(home);
|
|
123
|
+
if (final.installation_readiness !== "installed" || hybridDigest(final.installation) !== hybridDigest(installation))
|
|
124
|
+
refuse("source_changed_during_preparation");
|
|
125
|
+
return new PreparedInstalledFlowSource(home, expectedInstallationSha256, flowId, { path: descriptorPath, sha256: hash(bytes) }, { version: installation.manifest.canon.version, commit: installation.manifest.canon.commit, archive_sha256: installation.manifest.canon.archive.sha256 }, blobs);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof AppError && error.code === "installed_flow_source_invalid")
|
|
129
|
+
throw error;
|
|
130
|
+
return refuse("source_unavailable_or_changed");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
descriptorReference() { return { ...this.#descriptor }; }
|
|
134
|
+
installationSha256() { return this.#installationSha256; }
|
|
135
|
+
snapshot() {
|
|
136
|
+
return { schema_id: "dd-flow/installed-flow-source@1", installation_sha256: this.#installationSha256,
|
|
137
|
+
descriptor: this.descriptorReference(), canon: this.canonProvenance(),
|
|
138
|
+
blobs: [...this.#blobs].map(([logical, bytes]) => ({ path: logical, base64: bytes.toString("base64") })) };
|
|
139
|
+
}
|
|
140
|
+
/** Copied source restoration only. expectedSha256 must come from independent
|
|
141
|
+
* request custody, not from the same input. This does not verify a new release
|
|
142
|
+
* or admit its engine/storage; those remain the bootstrap owner's duties.
|
|
143
|
+
*/
|
|
144
|
+
static restoreRetained(input, expectedSha256) {
|
|
145
|
+
const result = this.restoreRetainedData(input, expectedSha256);
|
|
146
|
+
try {
|
|
147
|
+
result.resolveRegisteredFlow();
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return refuse("invalid_retained_source");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** Data recovery before storage/engine admission. This cannot register a
|
|
155
|
+
* handler; productive restoration still uses restoreRetained above. */
|
|
156
|
+
static restoreRetainedData(input, expectedSha256) {
|
|
157
|
+
try {
|
|
158
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || typeof expectedSha256 !== "string"
|
|
159
|
+
|| !/^[a-f0-9]{64}$/.test(expectedSha256))
|
|
160
|
+
refuse("invalid_retained_source");
|
|
161
|
+
const value = input;
|
|
162
|
+
if (Object.keys(value).sort().join() !== "blobs,canon,descriptor,installation_sha256,schema_id"
|
|
163
|
+
|| value.schema_id !== "dd-flow/installed-flow-source@1" || typeof value.installation_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(value.installation_sha256)
|
|
164
|
+
|| !value.canon || Object.keys(value.canon).sort().join() !== "archive_sha256,commit,version"
|
|
165
|
+
|| typeof value.canon.commit !== "string" || !/^[a-f0-9]{40}$/.test(value.canon.commit)
|
|
166
|
+
|| typeof value.canon.archive_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(value.canon.archive_sha256)
|
|
167
|
+
|| typeof value.canon.version !== "string" || !/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(value.canon.version)
|
|
168
|
+
|| !value.descriptor || Object.keys(value.descriptor).sort().join() !== "path,sha256"
|
|
169
|
+
|| !flowIds.some(id => value.descriptor.path === `${id}/flow-descriptor.json`) || typeof value.descriptor.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(value.descriptor.sha256)
|
|
170
|
+
|| !Array.isArray(value.blobs) || value.blobs.length < 1 || value.blobs.length > 129)
|
|
171
|
+
refuse("invalid_retained_source");
|
|
172
|
+
const blobs = new Map(), folded = new Set();
|
|
173
|
+
let total = 0;
|
|
174
|
+
for (const entry of value.blobs) {
|
|
175
|
+
if (!entry || Object.keys(entry).sort().join() !== "base64,path" || typeof entry.path !== "string"
|
|
176
|
+
|| !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(entry.path)
|
|
177
|
+
|| entry.path.split("/").some(part => part === "." || part === "..")
|
|
178
|
+
|| folded.has(entry.path.toLowerCase()) || typeof entry.base64 !== "string" || entry.base64.length > 2 * limit)
|
|
179
|
+
refuse("invalid_retained_source");
|
|
180
|
+
const content = Buffer.from(entry.base64, "base64");
|
|
181
|
+
if (!content.length || content.length > limit || content.toString("base64") !== entry.base64
|
|
182
|
+
|| (total += content.length) > 32 * limit)
|
|
183
|
+
refuse("invalid_retained_source");
|
|
184
|
+
new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
185
|
+
folded.add(entry.path.toLowerCase());
|
|
186
|
+
blobs.set(entry.path, content);
|
|
187
|
+
}
|
|
188
|
+
const descriptor = blobs.get(value.descriptor.path);
|
|
189
|
+
if (!descriptor || hash(descriptor) !== value.descriptor.sha256)
|
|
190
|
+
refuse("retained_source_changed");
|
|
191
|
+
const flowId = value.descriptor.path.slice(0, -"/flow-descriptor.json".length);
|
|
192
|
+
const declared = JSON.parse(descriptor.toString("utf8"));
|
|
193
|
+
if (validateHybridShape("flow-descriptor", declared).length || declared.id !== flowId || declared.origin !== "canon")
|
|
194
|
+
refuse("invalid_retained_source");
|
|
195
|
+
for (const stage of declared.stages)
|
|
196
|
+
for (const kind of ["instruction", "result_schema"]) {
|
|
197
|
+
const reference = stage[kind], content = blobs.get(reference.path);
|
|
198
|
+
if (!content || hash(content) !== reference.sha256)
|
|
199
|
+
refuse("retained_source_changed");
|
|
200
|
+
}
|
|
201
|
+
const result = new PreparedInstalledFlowSource(null, value.installation_sha256, flowId, { ...value.descriptor }, { ...value.canon }, blobs);
|
|
202
|
+
if (result.sha256 !== expectedSha256)
|
|
203
|
+
refuse("retained_source_changed");
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (error instanceof AppError && error.code === "installed_flow_source_invalid")
|
|
208
|
+
throw error;
|
|
209
|
+
return refuse("invalid_retained_source");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
canonProvenance() { return { ...this.#canon }; }
|
|
213
|
+
blobs() { return new Map([...this.#blobs].map(([logical, bytes]) => [logical, Buffer.from(bytes)])); }
|
|
214
|
+
inventory() { return [...this.#blobs].map(([logical, bytes]) => ({ path: logical, sha256: hash(bytes) })).sort((a, b) => a.path.localeCompare(b.path, "en")); }
|
|
215
|
+
/** Production handler registry only: source declarations cannot register code. */
|
|
216
|
+
resolveRegisteredFlow() { return admitBundledFlow(this.#blobs.get(this.#descriptor.path), this.#descriptor.sha256, this.#blobs); }
|
|
217
|
+
/** Initial materialization only, outside SQL writers. */
|
|
218
|
+
revalidate() {
|
|
219
|
+
if (this.#home === null) {
|
|
220
|
+
PreparedInstalledFlowSource.restoreRetained(this.snapshot(), this.sha256);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (PreparedInstalledFlowSource.fromInstalledRelease(this.#home, this.#installationSha256, this.#flowId).sha256 !== this.sha256)
|
|
224
|
+
refuse("source_changed_after_preparation");
|
|
225
|
+
}
|
|
226
|
+
}
|
package/dist/services/lanes.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import { spawnSync } from "node:child_process";
|
|
5
3
|
import { AppError } from "../shared/errors.js";
|
|
6
4
|
import { appendAudit } from "./audit.js";
|
|
@@ -8,6 +6,31 @@ import { requireProjectByRoot } from "./projects.js";
|
|
|
8
6
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
9
7
|
const defaultTtlSeconds = 300;
|
|
10
8
|
const defaultPollIntervalSeconds = 10;
|
|
9
|
+
export function prepareLaneWaitTiming(input) {
|
|
10
|
+
return {
|
|
11
|
+
timeoutSeconds: normalizeNonNegativeNumber(input.timeoutSeconds, 0, "timeout"),
|
|
12
|
+
pollIntervalSeconds: normalizePositiveNumber(input.pollIntervalSeconds, defaultPollIntervalSeconds, "poll-interval")
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function prepareLaneLock(context, input, action) {
|
|
16
|
+
if (typeof input.workerId !== "string" || !input.workerId.trim())
|
|
17
|
+
throw new AppError("validation", "Lane lock requires a nonempty worker ID", 2);
|
|
18
|
+
if (["acquire", "release", "wait-acquire"].includes(action) && (typeof input.reason !== "string" || !input.reason.trim()))
|
|
19
|
+
throw new AppError("validation", "Lane lock requires a nonempty reason", 2);
|
|
20
|
+
if (input.leaseToken !== undefined && (typeof input.leaseToken !== "string" || !input.leaseToken.trim()))
|
|
21
|
+
throw new AppError("validation", "Lease token must be nonempty text", 2);
|
|
22
|
+
const ttlSeconds = normalizePositiveNumber(input.ttlSeconds, defaultTtlSeconds, "ttl");
|
|
23
|
+
const { timeoutSeconds, pollIntervalSeconds } = prepareLaneWaitTiming(input);
|
|
24
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
25
|
+
const lane = normalizeLane(input.lane);
|
|
26
|
+
const workspace = input.workspacePath !== undefined ? requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath) : undefined;
|
|
27
|
+
if (input.workspacePath === undefined && ["acquire", "wait", "wait-acquire"].includes(action))
|
|
28
|
+
throw new AppError("validation", "Lane lock requires a workspace path", 2);
|
|
29
|
+
if (action === "acquire" || action === "wait-acquire")
|
|
30
|
+
assertWorkerMayAcquireLaneLock(context, project.id, lane, input.workerId);
|
|
31
|
+
const lock = action === "heartbeat" || action === "release" ? requireOwnedActiveLock(context, project.id, lane, input.workerId, input.leaseToken) : undefined;
|
|
32
|
+
return { project, lane, ttlSeconds, timeoutSeconds, pollIntervalSeconds, lock, workspace };
|
|
33
|
+
}
|
|
11
34
|
export function getLaneStatus(context, input) {
|
|
12
35
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
13
36
|
return {
|
|
@@ -23,10 +46,16 @@ export function expireProjectLaneLocks(context, projectId) {
|
|
|
23
46
|
export function expireProjectLaneWaiters(context, projectId) {
|
|
24
47
|
expireStaleWaiters(context, projectId);
|
|
25
48
|
}
|
|
26
|
-
export function
|
|
49
|
+
export function prepareLaneWorkspace(context, input) {
|
|
50
|
+
if (input.branch !== undefined && (typeof input.branch !== "string" || !input.branch.trim()))
|
|
51
|
+
throw new AppError("validation", "Expected branch must be nonempty text", 2);
|
|
27
52
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
28
53
|
const lane = normalizeLane(input.lane);
|
|
29
54
|
const workspacePath = resolveExistingPath(input.workspacePath);
|
|
55
|
+
return { project, lane, workspacePath };
|
|
56
|
+
}
|
|
57
|
+
export function setLaneWorkspace(context, input) {
|
|
58
|
+
const { project, lane, workspacePath } = prepareLaneWorkspace(context, input);
|
|
30
59
|
const now = context.now();
|
|
31
60
|
const existing = laneRecord(context, project.id, lane);
|
|
32
61
|
context.db.run(`INSERT INTO lanes
|
|
@@ -69,11 +98,12 @@ export function checkLaneWorkspace(context, input) {
|
|
|
69
98
|
};
|
|
70
99
|
}
|
|
71
100
|
export function acquireLaneLock(context, input) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
101
|
+
const prepared = prepareLaneLock(context, input, "acquire");
|
|
102
|
+
return context.db.writeTransaction(() => acquirePreparedLaneLock(context, input, prepared));
|
|
103
|
+
}
|
|
104
|
+
function acquirePreparedLaneLock(context, input, prepared) {
|
|
105
|
+
const { project, lane, ttlSeconds } = prepared;
|
|
106
|
+
assertPreparedLaneLock(context, prepared, input.workerId);
|
|
77
107
|
expireStaleLocks(context, project.id);
|
|
78
108
|
const active = activeLaneLock(context, project.id, lane);
|
|
79
109
|
if (active) {
|
|
@@ -115,14 +145,15 @@ export function acquireLaneLock(context, input) {
|
|
|
115
145
|
...(expired ? { expired_lock_id: expired.id } : {})
|
|
116
146
|
};
|
|
117
147
|
}
|
|
148
|
+
function assertPreparedLaneLock(context, prepared, workerId) {
|
|
149
|
+
if (JSON.stringify(laneRecord(context, prepared.project.id, prepared.lane)) !== JSON.stringify(prepared.workspace)
|
|
150
|
+
|| context.db.get("SELECT root FROM projects WHERE id = ?", [prepared.project.id])?.root !== prepared.project.root)
|
|
151
|
+
throw new AppError("lane_workspace_changed", "Lane registration changed after workspace preparation", 1);
|
|
152
|
+
assertWorkerMayAcquireLaneLock(context, prepared.project.id, prepared.lane, workerId);
|
|
153
|
+
}
|
|
118
154
|
export function heartbeatLaneLock(context, input) {
|
|
119
|
-
const project =
|
|
120
|
-
const
|
|
121
|
-
if (input.workspacePath) {
|
|
122
|
-
requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath);
|
|
123
|
-
}
|
|
124
|
-
const lock = requireOwnedActiveLock(context, project.id, lane, input.workerId, input.leaseToken);
|
|
125
|
-
const ttlSeconds = normalizePositiveNumber(input.ttlSeconds, defaultTtlSeconds, "ttl");
|
|
155
|
+
const { project, lock: preparedLock, ttlSeconds } = prepareLaneLock(context, input, "heartbeat");
|
|
156
|
+
const lock = preparedLock;
|
|
126
157
|
const now = context.now();
|
|
127
158
|
context.db.run(`UPDATE lane_locks SET heartbeat_at = ?, expires_at = ?, updated_at = ? WHERE id = ?`, [now, expiresAt(now, ttlSeconds), now, lock.id]);
|
|
128
159
|
appendAudit(context, {
|
|
@@ -133,12 +164,8 @@ export function heartbeatLaneLock(context, input) {
|
|
|
133
164
|
return { ok: true, lock: activeLaneLock(context, project.id, lock.lane) };
|
|
134
165
|
}
|
|
135
166
|
export function releaseLaneLock(context, input) {
|
|
136
|
-
const project =
|
|
137
|
-
const
|
|
138
|
-
if (input.workspacePath) {
|
|
139
|
-
requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath);
|
|
140
|
-
}
|
|
141
|
-
const lock = requireOwnedActiveLock(context, project.id, lane, input.workerId, input.leaseToken);
|
|
167
|
+
const { project, lock: preparedLock } = prepareLaneLock(context, input, "release");
|
|
168
|
+
const lock = preparedLock;
|
|
142
169
|
const now = context.now();
|
|
143
170
|
context.db.run(`UPDATE lane_locks
|
|
144
171
|
SET status = 'released', released_at = ?, reason = ?, updated_at = ?
|
|
@@ -152,11 +179,7 @@ export function releaseLaneLock(context, input) {
|
|
|
152
179
|
return { ok: true, lock: laneLockById(context, lock.id) };
|
|
153
180
|
}
|
|
154
181
|
export async function waitForLaneLock(context, input) {
|
|
155
|
-
const project =
|
|
156
|
-
const lane = normalizeLane(input.lane);
|
|
157
|
-
requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath);
|
|
158
|
-
const timeoutSeconds = normalizeNonNegativeNumber(input.timeoutSeconds, 0, "timeout");
|
|
159
|
-
const pollIntervalSeconds = normalizePositiveNumber(input.pollIntervalSeconds, defaultPollIntervalSeconds, "poll-interval");
|
|
182
|
+
const { project, lane, timeoutSeconds, pollIntervalSeconds } = prepareLaneLock(context, input, "wait");
|
|
160
183
|
const start = Date.now();
|
|
161
184
|
while (true) {
|
|
162
185
|
expireStaleLocks(context, project.id);
|
|
@@ -186,13 +209,18 @@ export function getLaneWaiters(context, input) {
|
|
|
186
209
|
}))
|
|
187
210
|
};
|
|
188
211
|
}
|
|
189
|
-
export function
|
|
212
|
+
export function prepareLaneWaiterCancellation(context, input) {
|
|
213
|
+
if (typeof input.workerId !== "string" || !input.workerId.trim())
|
|
214
|
+
throw new AppError("validation", "Lane waiter requires a nonempty worker ID", 2);
|
|
215
|
+
if (typeof input.reason !== "string" || !input.reason.trim())
|
|
216
|
+
throw new AppError("validation", "lane waiter cancel requires --reason", 2);
|
|
190
217
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
191
218
|
const lane = normalizeLane(input.lane);
|
|
192
219
|
const reason = input.reason.trim();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
220
|
+
return { project, lane, reason };
|
|
221
|
+
}
|
|
222
|
+
export function cancelLaneWaiter(context, input) {
|
|
223
|
+
const { project, lane, reason } = prepareLaneWaiterCancellation(context, input);
|
|
196
224
|
expireStaleWaiters(context, project.id);
|
|
197
225
|
const now = context.now();
|
|
198
226
|
const waiter = activeLaneWaiterForWorker(context, project.id, lane, input.workerId);
|
|
@@ -245,13 +273,7 @@ export function expireLaneWaiterById(context, input) {
|
|
|
245
273
|
return { ok: true, expired: result.changes === 1, waiter_id: input.waiterId };
|
|
246
274
|
}
|
|
247
275
|
export async function waitAcquireLaneLock(context, input) {
|
|
248
|
-
const project =
|
|
249
|
-
const lane = normalizeLane(input.lane);
|
|
250
|
-
requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath);
|
|
251
|
-
assertWorkerMayAcquireLaneLock(context, project.id, lane, input.workerId);
|
|
252
|
-
const timeoutSeconds = normalizeNonNegativeNumber(input.timeoutSeconds, 0, "timeout");
|
|
253
|
-
const pollIntervalSeconds = normalizePositiveNumber(input.pollIntervalSeconds, defaultPollIntervalSeconds, "poll-interval");
|
|
254
|
-
const ttlSeconds = normalizePositiveNumber(input.ttlSeconds, defaultTtlSeconds, "ttl");
|
|
276
|
+
const { project, lane, timeoutSeconds, pollIntervalSeconds, ttlSeconds } = prepareLaneLock(context, input, "wait-acquire");
|
|
255
277
|
const startedAt = Date.now();
|
|
256
278
|
const waiterDeadline = timeoutSeconds > 0 ? new Date(startedAt + timeoutSeconds * 1000).toISOString() : null;
|
|
257
279
|
while (true) {
|
|
@@ -375,10 +397,9 @@ function laneWaitersForProject(context, projectId, lane) {
|
|
|
375
397
|
LIMIT 100`, [projectId]);
|
|
376
398
|
}
|
|
377
399
|
function activeLaneLock(context, projectId, lane) {
|
|
378
|
-
expireStaleLocks(context, projectId);
|
|
379
400
|
return context.db.get(`SELECT * FROM lane_locks
|
|
380
|
-
WHERE project_id = ? AND lane = ? AND status = 'active'
|
|
381
|
-
ORDER BY updated_at DESC, id DESC LIMIT 1`, [projectId, lane]);
|
|
401
|
+
WHERE project_id = ? AND lane = ? AND status = 'active' AND expires_at > ?
|
|
402
|
+
ORDER BY updated_at DESC, id DESC LIMIT 1`, [projectId, lane, context.now()]);
|
|
382
403
|
}
|
|
383
404
|
function activeLaneWaiterForWorker(context, projectId, lane, workerId) {
|
|
384
405
|
return context.db.get(`SELECT * FROM lane_waiters
|
|
@@ -513,13 +534,15 @@ function ensureQueuedLaneWaiter(context, input) {
|
|
|
513
534
|
return waiter;
|
|
514
535
|
}
|
|
515
536
|
function tryAcquireQueuedLaneLock(context, input) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
537
|
+
const prepared = prepareLaneLock(context, input, "wait-acquire");
|
|
538
|
+
if (prepared.project.id !== input.projectId || prepared.lane !== input.lane)
|
|
539
|
+
throw new AppError("lane_workspace_changed", "Lane identity changed while waiting", 1);
|
|
540
|
+
return context.db.writeTransaction(() => {
|
|
541
|
+
assertPreparedLaneLock(context, prepared, input.workerId);
|
|
542
|
+
expireStaleLocks(context, input.projectId);
|
|
543
|
+
expireStaleWaiters(context, input.projectId);
|
|
520
544
|
const existingLock = activeLaneLock(context, input.projectId, input.lane);
|
|
521
545
|
if (existingLock?.worker_id === input.workerId) {
|
|
522
|
-
context.db.exec("COMMIT");
|
|
523
546
|
return {
|
|
524
547
|
acquired: true,
|
|
525
548
|
payload: {
|
|
@@ -537,7 +560,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
537
560
|
}
|
|
538
561
|
const latest = latestLaneWaiterForWorker(context, input.projectId, input.lane, input.workerId);
|
|
539
562
|
if (latest && ["cancelled", "expired"].includes(latest.status) && latest.updated_at >= input.invocationStartedAt) {
|
|
540
|
-
context.db.exec("COMMIT");
|
|
541
563
|
return {
|
|
542
564
|
acquired: false,
|
|
543
565
|
payload: {
|
|
@@ -561,14 +583,14 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
561
583
|
});
|
|
562
584
|
const head = firstQueuedLaneWaiter(context, input.projectId, input.lane);
|
|
563
585
|
if (!existingLock && head?.id === waiter.id) {
|
|
564
|
-
const lockResult =
|
|
586
|
+
const lockResult = acquirePreparedLaneLock(context, {
|
|
565
587
|
projectRoot: input.projectRoot,
|
|
566
588
|
lane: input.lane,
|
|
567
589
|
workerId: input.workerId,
|
|
568
590
|
workspacePath: input.workspacePath,
|
|
569
591
|
ttlSeconds: input.ttlSeconds,
|
|
570
592
|
reason: input.reason
|
|
571
|
-
});
|
|
593
|
+
}, prepared);
|
|
572
594
|
const now = context.now();
|
|
573
595
|
context.db.run(`UPDATE lane_waiters
|
|
574
596
|
SET status = 'acquired', acquired_at = ?, updated_at = ?
|
|
@@ -580,7 +602,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
580
602
|
reason: input.reason,
|
|
581
603
|
payload: { project_id: input.projectId, lane: input.lane, worker_id: input.workerId, waiter_id: waiter.id }
|
|
582
604
|
});
|
|
583
|
-
context.db.exec("COMMIT");
|
|
584
605
|
return {
|
|
585
606
|
acquired: true,
|
|
586
607
|
payload: {
|
|
@@ -597,7 +618,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
597
618
|
};
|
|
598
619
|
}
|
|
599
620
|
const position = queuedWaiterPosition(context, input.projectId, input.lane, waiter.id);
|
|
600
|
-
context.db.exec("COMMIT");
|
|
601
621
|
return {
|
|
602
622
|
acquired: false,
|
|
603
623
|
payload: {
|
|
@@ -611,18 +631,10 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
611
631
|
position
|
|
612
632
|
}
|
|
613
633
|
};
|
|
614
|
-
}
|
|
615
|
-
catch (error) {
|
|
616
|
-
context.db.exec("ROLLBACK");
|
|
617
|
-
throw error;
|
|
618
|
-
}
|
|
634
|
+
});
|
|
619
635
|
}
|
|
620
636
|
function resolveExistingPath(value) {
|
|
621
|
-
|
|
622
|
-
if (!fs.existsSync(absolute)) {
|
|
623
|
-
throw new AppError("not_found", `Workspace path does not exist: ${absolute}`, 1);
|
|
624
|
-
}
|
|
625
|
-
return fs.realpathSync(absolute);
|
|
637
|
+
return resolveProjectRoot(value);
|
|
626
638
|
}
|
|
627
639
|
function detectBranch(workspacePath) {
|
|
628
640
|
const result = spawnSync("git", ["-C", workspacePath, "rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" });
|
|
@@ -635,7 +647,7 @@ function expiresAt(now, ttlSeconds) {
|
|
|
635
647
|
return new Date(new Date(now).getTime() + ttlSeconds * 1000).toISOString();
|
|
636
648
|
}
|
|
637
649
|
function normalizeLane(value) {
|
|
638
|
-
const normalized = value.trim().toLowerCase();
|
|
650
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
639
651
|
if (!normalized || !/^[a-z0-9._-]+$/.test(normalized)) {
|
|
640
652
|
throw new AppError("validation", "Lane name must contain only lowercase letters, numbers, dot, underscore, or hyphen", 2);
|
|
641
653
|
}
|
|
@@ -643,15 +655,16 @@ function normalizeLane(value) {
|
|
|
643
655
|
}
|
|
644
656
|
function normalizePositiveNumber(value, fallback, label) {
|
|
645
657
|
const normalized = value ?? fallback;
|
|
646
|
-
if (!Number.isFinite(normalized) || normalized <= 0
|
|
647
|
-
|
|
658
|
+
if (!Number.isFinite(normalized) || normalized <= 0 ||
|
|
659
|
+
(label === "poll-interval" ? normalized * 1000 > 2 ** 31 - 1 : !Number.isFinite(new Date(Date.now() + normalized * 1000).getTime()))) {
|
|
660
|
+
throw new AppError("validation", `--${label} must be a positive number within the supported timer/date range`, 2);
|
|
648
661
|
}
|
|
649
662
|
return normalized;
|
|
650
663
|
}
|
|
651
664
|
function normalizeNonNegativeNumber(value, fallback, label) {
|
|
652
665
|
const normalized = value ?? fallback;
|
|
653
|
-
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
654
|
-
throw new AppError("validation", `--${label} must be zero or a positive number`, 2);
|
|
666
|
+
if (!Number.isFinite(normalized) || normalized < 0 || !Number.isFinite(new Date(Date.now() + normalized * 1000).getTime())) {
|
|
667
|
+
throw new AppError("validation", `--${label} must be zero or a positive number within the supported date range`, 2);
|
|
655
668
|
}
|
|
656
669
|
return normalized;
|
|
657
670
|
}
|