@sema-agent/server 1.323.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approval-hmac.d.ts +40 -0
- package/dist/approval-hmac.js +67 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +72 -4
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +40 -1
- package/dist/auth-bridge.d.ts +10 -0
- package/dist/auth-bridge.js +47 -4
- package/dist/auth-keys.d.ts +19 -0
- package/dist/auth-keys.js +13 -0
- package/dist/bake-runner/main.d.ts +5 -0
- package/dist/bake-runner/main.js +43 -4
- package/dist/bake-runner/protocol.d.ts +57 -0
- package/dist/bake-runner/protocol.js +58 -3
- package/dist/bake-runner/runner.d.ts +54 -0
- package/dist/bake-runner/runner.js +87 -5
- package/dist/bench/l8/artifact.d.ts +63 -0
- package/dist/bench/l8/artifact.js +61 -2
- package/dist/bench/l8/escape.d.ts +36 -0
- package/dist/bench/l8/escape.js +25 -0
- package/dist/bench/l8/index.d.ts +14 -0
- package/dist/bench/l8/index.js +14 -0
- package/dist/bench/l8/probes.d.ts +74 -0
- package/dist/bench/l8/probes.js +60 -0
- package/dist/bench/l8/run-probes.d.ts +43 -0
- package/dist/bench/l8/run-probes.js +45 -2
- package/dist/bench/s1/arms.d.ts +126 -0
- package/dist/bench/s1/arms.js +188 -13
- package/dist/bench/s1/live-deps.d.ts +66 -0
- package/dist/bench/s1/live-deps.js +249 -11
- package/dist/bench/s1/oracle.d.ts +49 -0
- package/dist/bench/s1/oracle.js +36 -1
- package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
- package/dist/bench/s1/repair-oracle-adapter.js +30 -0
- package/dist/bench/s1/reviewer.d.ts +38 -0
- package/dist/bench/s1/reviewer.js +34 -0
- package/dist/bench/s1/row.d.ts +77 -0
- package/dist/bench/s1/row.js +62 -0
- package/dist/bench/s1/run-firm.d.ts +47 -0
- package/dist/bench/s1/run-firm.js +70 -4
- package/dist/bench/s1/runner-ctx.d.ts +71 -0
- package/dist/bench/s1/runner-ctx.js +36 -0
- package/dist/bench/s1/tasks.d.ts +108 -0
- package/dist/bench/s1/tasks.js +115 -0
- package/dist/boot/budget-tracing.d.ts +48 -0
- package/dist/boot/budget-tracing.js +86 -0
- package/dist/boot/config-center.d.ts +62 -0
- package/dist/boot/config-center.js +995 -0
- package/dist/boot/coordinators.d.ts +33 -0
- package/dist/boot/coordinators.js +97 -0
- package/dist/boot/execution-env.d.ts +26 -0
- package/dist/boot/execution-env.js +370 -0
- package/dist/boot/leader.d.ts +27 -0
- package/dist/boot/leader.js +81 -0
- package/dist/boot/reapers.d.ts +53 -0
- package/dist/boot/reapers.js +252 -0
- package/dist/boot/resolve-spec.d.ts +70 -0
- package/dist/boot/resolve-spec.js +1072 -0
- package/dist/boot/runner-deps.d.ts +101 -0
- package/dist/boot/runner-deps.js +343 -0
- package/dist/boot/runtime-caps.d.ts +21 -0
- package/dist/boot/runtime-caps.js +62 -0
- package/dist/boot/session-faces.d.ts +57 -0
- package/dist/boot/session-faces.js +157 -0
- package/dist/boot/shutdown.d.ts +50 -0
- package/dist/boot/shutdown.js +129 -0
- package/dist/boot/stores.d.ts +32 -0
- package/dist/boot/stores.js +361 -0
- package/dist/boot/workflow-orchestration.d.ts +46 -0
- package/dist/boot/workflow-orchestration.js +150 -0
- package/dist/boot-reclaim.d.ts +19 -0
- package/dist/boot-reclaim.js +20 -0
- package/dist/brain.d.ts +25 -0
- package/dist/brain.js +61 -1
- package/dist/budget.d.ts +66 -0
- package/dist/budget.js +128 -4
- package/dist/capabilities/builtin-tools.d.ts +3 -0
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +17 -1
- package/dist/capabilities/center-plugins.js +45 -4
- package/dist/capabilities/center-prompts.d.ts +46 -0
- package/dist/capabilities/center-prompts.js +57 -3
- package/dist/capabilities/code-review-council.d.ts +28 -0
- package/dist/capabilities/code-review-council.js +40 -7
- package/dist/capabilities/collab-wire.d.ts +2 -0
- package/dist/capabilities/collab-workflows.d.ts +7 -0
- package/dist/capabilities/collab-workflows.js +44 -1
- package/dist/capabilities/oa-tools.d.ts +6 -0
- package/dist/capabilities/oa-tools.js +10 -0
- package/dist/capabilities/prompt.d.ts +14 -0
- package/dist/capabilities/prompt.js +14 -0
- package/dist/capabilities/prompts/code-review.d.ts +14 -0
- package/dist/capabilities/prompts/code-review.js +14 -0
- package/dist/capabilities/prompts/identity.d.ts +6 -0
- package/dist/capabilities/prompts/identity.js +6 -0
- package/dist/capabilities/prompts/team.d.ts +4 -0
- package/dist/capabilities/prompts/team.js +4 -0
- package/dist/capabilities/repo-tools.d.ts +11 -0
- package/dist/capabilities/repo-tools.js +8 -0
- package/dist/capabilities/sandbox-file-send.d.ts +95 -0
- package/dist/capabilities/sandbox-file-send.js +91 -3
- package/dist/capabilities/scenarios.d.ts +130 -0
- package/dist/capabilities/scenarios.js +180 -7
- package/dist/capabilities/select-environment-tool.d.ts +7 -0
- package/dist/capabilities/select-environment-tool.js +30 -0
- package/dist/capabilities/send-user-file-tool.d.ts +33 -0
- package/dist/capabilities/send-user-file-tool.js +65 -1
- package/dist/capabilities/skills.d.ts +29 -0
- package/dist/capabilities/skills.js +13 -2
- package/dist/capabilities/team.d.ts +22 -0
- package/dist/capabilities/team.js +22 -2
- package/dist/capabilities/tool-defer.d.ts +26 -0
- package/dist/capabilities/tool-defer.js +3 -0
- package/dist/config-center/apply-effective.d.ts +65 -0
- package/dist/config-center/apply-effective.js +204 -12
- package/dist/config-center/http-client.d.ts +34 -0
- package/dist/config-center/http-client.js +68 -0
- package/dist/config-center/restart-signal.d.ts +22 -0
- package/dist/config-center/restart-signal.js +39 -0
- package/dist/config-center/skills-mcp.d.ts +20 -1
- package/dist/config-center/skills-mcp.js +42 -7
- package/dist/config-center/types.d.ts +92 -1
- package/dist/config-lkg.d.ts +12 -0
- package/dist/config-lkg.js +41 -0
- package/dist/config-provider.d.ts +78 -0
- package/dist/config-provider.js +165 -2
- package/dist/config-types.d.ts +585 -1
- package/dist/config.d.ts +78 -2
- package/dist/config.js +874 -301
- package/dist/degenerate-instrument.d.ts +38 -0
- package/dist/degenerate-instrument.js +55 -3
- package/dist/elicitation.d.ts +39 -0
- package/dist/elicitation.js +87 -3
- package/dist/env-facts.d.ts +61 -0
- package/dist/env-facts.js +73 -7
- package/dist/fleet/fleet-bus.d.ts +185 -0
- package/dist/fleet/fleet-bus.js +333 -27
- package/dist/fleet/subagent-tail-bus.d.ts +11 -0
- package/dist/fleet/subagent-tail-bus.js +33 -2
- package/dist/fleet-client.d.ts +38 -0
- package/dist/fleet-client.js +44 -9
- package/dist/fleet-lease.d.ts +46 -0
- package/dist/fleet-lease.js +73 -5
- package/dist/hooks/branch-transcript.d.ts +8 -0
- package/dist/hooks/branch-transcript.js +33 -0
- package/dist/hooks/cc-stop-prompt.d.ts +42 -0
- package/dist/hooks/cc-stop-prompt.js +43 -1
- package/dist/hooks/hook-llm.d.ts +15 -0
- package/dist/hooks/hook-llm.js +47 -0
- package/dist/hooks/hook-runner.d.ts +89 -0
- package/dist/hooks/hook-runner.js +327 -18
- package/dist/http/idempotency.d.ts +31 -0
- package/dist/http/idempotency.js +34 -0
- package/dist/http/principal-gate.d.ts +17 -0
- package/dist/http/principal-gate.js +35 -3
- package/dist/http/route-ctx.d.ts +36 -0
- package/dist/http/routes/approvals-assistant.d.ts +25 -0
- package/dist/http/routes/approvals-assistant.js +253 -23
- package/dist/http/routes/attachments.js +22 -4
- package/dist/http/routes/capabilities.d.ts +7 -0
- package/dist/http/routes/capabilities.js +202 -1
- package/dist/http/routes/fleet.d.ts +6 -0
- package/dist/http/routes/fleet.js +94 -5
- package/dist/http/routes/images.d.ts +17 -0
- package/dist/http/routes/images.js +222 -14
- package/dist/http/routes/leader.d.ts +7 -0
- package/dist/http/routes/leader.js +10 -0
- package/dist/http/routes/memory-policy.d.ts +6 -0
- package/dist/http/routes/memory-policy.js +38 -6
- package/dist/http/routes/notify-wake.d.ts +10 -0
- package/dist/http/routes/notify-wake.js +19 -3
- package/dist/http/routes/observability.d.ts +6 -0
- package/dist/http/routes/observability.js +19 -2
- package/dist/http/routes/runs.d.ts +15 -0
- package/dist/http/routes/runs.js +464 -41
- package/dist/http/routes/session-sync.d.ts +15 -0
- package/dist/http/routes/session-sync.js +229 -19
- package/dist/http/routes/sessions-list.d.ts +6 -0
- package/dist/http/routes/sessions-list.js +35 -4
- package/dist/http/routes/sessions.d.ts +14 -0
- package/dist/http/routes/sessions.js +176 -31
- package/dist/http/routes/side-query.d.ts +8 -0
- package/dist/http/routes/side-query.js +26 -1
- package/dist/http/routes/tasks.d.ts +18 -0
- package/dist/http/routes/tasks.js +501 -38
- package/dist/http/routes/trace-usage.d.ts +10 -0
- package/dist/http/routes/trace-usage.js +66 -14
- package/dist/http/routes/workflows.d.ts +12 -0
- package/dist/http/routes/workflows.js +180 -19
- package/dist/http/run-meta.d.ts +11 -0
- package/dist/http/run-meta.js +6 -0
- package/dist/http/send.d.ts +19 -0
- package/dist/http/send.js +11 -0
- package/dist/http/server.d.ts +255 -0
- package/dist/http/server.js +921 -75
- package/dist/http/sse-log.d.ts +15 -0
- package/dist/http/sse-log.js +16 -0
- package/dist/http/tar.d.ts +9 -0
- package/dist/http/tar.js +21 -5
- package/dist/http/wire-gate.d.ts +9 -0
- package/dist/http/wire-gate.js +9 -0
- package/dist/http/wire-types.d.ts +187 -0
- package/dist/http/workspace-content.d.ts +8 -0
- package/dist/http/workspace-content.js +10 -0
- package/dist/images/bake-validate.d.ts +63 -0
- package/dist/images/bake-validate.js +71 -1
- package/dist/images/manifest.d.ts +19 -0
- package/dist/images/manifest.js +6 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +10 -5
- package/dist/key-resolver.d.ts +23 -0
- package/dist/key-resolver.js +27 -2
- package/dist/leader/diffout.d.ts +23 -0
- package/dist/leader/diffout.js +8 -1
- package/dist/leader/diffup.d.ts +32 -0
- package/dist/leader/diffup.js +43 -0
- package/dist/leader/endpoint.d.ts +20 -0
- package/dist/leader/endpoint.js +25 -2
- package/dist/leader/fanout.d.ts +60 -0
- package/dist/leader/fanout.js +29 -3
- package/dist/leader/grader-env-factory.d.ts +74 -0
- package/dist/leader/grader-env-factory.js +62 -3
- package/dist/leader/leader.d.ts +97 -0
- package/dist/leader/leader.js +151 -4
- package/dist/leader/merge.d.ts +94 -0
- package/dist/leader/merge.js +112 -9
- package/dist/leader/planner.d.ts +52 -0
- package/dist/leader/planner.js +47 -3
- package/dist/leader/repair-oracle.d.ts +61 -0
- package/dist/leader/repair-oracle.js +59 -1
- package/dist/leader/repair-wire.d.ts +78 -0
- package/dist/leader/repair-wire.js +79 -2
- package/dist/leader/wire.d.ts +79 -0
- package/dist/leader/wire.js +255 -8
- package/dist/lsp/e2b-bridge.d.ts +16 -0
- package/dist/lsp/e2b-bridge.js +32 -4
- package/dist/lsp/e2b-manager.d.ts +10 -0
- package/dist/lsp/e2b-manager.js +18 -2
- package/dist/lsp/lsp-frames.d.ts +12 -0
- package/dist/lsp/lsp-frames.js +12 -0
- package/dist/lsp/manager.d.ts +12 -0
- package/dist/lsp/manager.js +35 -2
- package/dist/lsp/types.d.ts +10 -0
- package/dist/lsp/ws-transport.d.ts +12 -0
- package/dist/lsp/ws-transport.js +53 -5
- package/dist/lsp-evict.d.ts +14 -0
- package/dist/lsp-evict.js +15 -1
- package/dist/main.js +530 -2129
- package/dist/memory-export.d.ts +1 -0
- package/dist/memory-export.js +4 -0
- package/dist/memory-scope.d.ts +49 -0
- package/dist/memory-scope.js +87 -4
- package/dist/memory-sync-client.d.ts +24 -0
- package/dist/memory-sync-client.js +41 -2
- package/dist/memory-sync.d.ts +23 -0
- package/dist/memory-sync.js +70 -1
- package/dist/model-select.d.ts +39 -0
- package/dist/model-select.js +42 -1
- package/dist/observability/cost-quota.d.ts +20 -0
- package/dist/observability/cost-quota.js +4 -0
- package/dist/observability/cost-taxonomy.d.ts +45 -0
- package/dist/observability/cost-taxonomy.js +34 -0
- package/dist/observability/logger.d.ts +1 -0
- package/dist/observability/logger.js +6 -0
- package/dist/observability/metrics.d.ts +13 -0
- package/dist/observability/metrics.js +63 -0
- package/dist/observability/otel-exporter.d.ts +9 -0
- package/dist/observability/otel-exporter.js +11 -3
- package/dist/observability/principal-context.d.ts +9 -0
- package/dist/observability/principal-context.js +9 -0
- package/dist/observability/prompt-manifest.d.ts +37 -0
- package/dist/observability/prompt-manifest.js +35 -1
- package/dist/observability/rate-limit.d.ts +10 -0
- package/dist/observability/rate-limit.js +1 -0
- package/dist/observability/tool-trace.d.ts +24 -0
- package/dist/observability/tool-trace.js +41 -1
- package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
- package/dist/orchestration/hardened-vm-runner.js +119 -4
- package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
- package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
- package/dist/orchestration/hardened-vm-worker.js +27 -0
- package/dist/orchestration/subagent-steer.d.ts +44 -0
- package/dist/orchestration/subagent-steer.js +45 -1
- package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
- package/dist/orchestration/workflow-agent-steer.js +80 -1
- package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
- package/dist/orchestration/workflow-completion-inbox.js +283 -32
- package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
- package/dist/orchestration/workflow-notify-journal.js +187 -13
- package/dist/parked-decide.d.ts +61 -0
- package/dist/parked-decide.js +58 -1
- package/dist/per-task-image.d.ts +54 -0
- package/dist/per-task-image.js +41 -0
- package/dist/plan-cache-probe.d.ts +35 -0
- package/dist/plan-cache-probe.js +18 -3
- package/dist/plugins/approval-exemption-store.d.ts +7 -0
- package/dist/plugins/approval-exemption-store.js +28 -2
- package/dist/plugins/approval-store-sql.d.ts +64 -0
- package/dist/plugins/approval-store-sql.js +32 -0
- package/dist/plugins/background-agent-store-sql.d.ts +55 -0
- package/dist/plugins/background-agent-store-sql.js +91 -4
- package/dist/plugins/background-shell-support.d.ts +98 -0
- package/dist/plugins/background-shell-support.js +117 -13
- package/dist/plugins/blob-backend.d.ts +104 -0
- package/dist/plugins/blob-backend.js +163 -6
- package/dist/plugins/breaker-state-sql.d.ts +65 -1
- package/dist/plugins/breaker-state-sql.js +46 -8
- package/dist/plugins/caching-session-store.d.ts +42 -0
- package/dist/plugins/caching-session-store.js +63 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
- package/dist/plugins/checkpoint-store-sql.js +239 -11
- package/dist/plugins/file-outcome-sink.d.ts +4 -0
- package/dist/plugins/file-outcome-sink.js +9 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
- package/dist/plugins/file-resume-anchor-store.js +43 -4
- package/dist/plugins/file-run-store.d.ts +90 -0
- package/dist/plugins/file-run-store.js +145 -13
- package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
- package/dist/plugins/file-snapshot-store-sql.js +178 -10
- package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
- package/dist/plugins/file-workflow-journal-store.js +10 -0
- package/dist/plugins/fork-routing-session-store.d.ts +47 -0
- package/dist/plugins/fork-routing-session-store.js +51 -3
- package/dist/plugins/host-platform.d.ts +69 -0
- package/dist/plugins/host-platform.js +81 -2
- package/dist/plugins/image-bake-store-sql.d.ts +136 -1
- package/dist/plugins/image-bake-store-sql.js +241 -7
- package/dist/plugins/image-index-sql.d.ts +46 -0
- package/dist/plugins/image-index-sql.js +113 -4
- package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
- package/dist/plugins/k8s-bg-scripts.js +88 -4
- package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
- package/dist/plugins/k8s-exec-protocol.js +41 -0
- package/dist/plugins/local-checkpoint-store.d.ts +40 -0
- package/dist/plugins/local-checkpoint-store.js +86 -7
- package/dist/plugins/local-session-store.d.ts +157 -0
- package/dist/plugins/local-session-store.js +289 -34
- package/dist/plugins/local-task-attachment-store.js +14 -2
- package/dist/plugins/mailbox-store-sql.d.ts +38 -0
- package/dist/plugins/mailbox-store-sql.js +39 -6
- package/dist/plugins/memory-engine-pg.d.ts +30 -0
- package/dist/plugins/memory-engine-pg.js +122 -4
- package/dist/plugins/memory-engine-tidb.d.ts +26 -0
- package/dist/plugins/memory-engine-tidb.js +115 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
- package/dist/plugins/memory-engine-vector-util.js +5 -0
- package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
- package/dist/plugins/memory-resume-anchor-store.js +17 -0
- package/dist/plugins/memory-run-store.d.ts +63 -0
- package/dist/plugins/memory-run-store.js +73 -8
- package/dist/plugins/memory-session-policy-store.d.ts +17 -0
- package/dist/plugins/memory-session-policy-store.js +17 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
- package/dist/plugins/memory-sync-store-pg.js +45 -2
- package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
- package/dist/plugins/memory-sync-store-tidb.js +31 -1
- package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
- package/dist/plugins/outcome-ledger-sql.js +97 -3
- package/dist/plugins/pg-approval-store.d.ts +7 -0
- package/dist/plugins/pg-approval-store.js +7 -0
- package/dist/plugins/pg-breaker-state.d.ts +6 -0
- package/dist/plugins/pg-breaker-state.js +6 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
- package/dist/plugins/pg-checkpoint-store.js +8 -0
- package/dist/plugins/pg-cost-quota.d.ts +17 -0
- package/dist/plugins/pg-cost-quota.js +6 -0
- package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/pg-file-snapshot-store.js +6 -0
- package/dist/plugins/pg-image-bake.d.ts +9 -0
- package/dist/plugins/pg-image-bake.js +9 -0
- package/dist/plugins/pg-image-index.d.ts +9 -0
- package/dist/plugins/pg-image-index.js +9 -0
- package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
- package/dist/plugins/pg-outcome-ledger.js +9 -0
- package/dist/plugins/pg-pool.d.ts +50 -0
- package/dist/plugins/pg-pool.js +78 -0
- package/dist/plugins/pg-query.d.ts +11 -0
- package/dist/plugins/pg-rate-limiter.d.ts +23 -0
- package/dist/plugins/pg-rate-limiter.js +12 -2
- package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/pg-resume-anchor-store.js +5 -0
- package/dist/plugins/pg-run-store.d.ts +7 -0
- package/dist/plugins/pg-run-store.js +7 -0
- package/dist/plugins/pg-safe-json.d.ts +13 -0
- package/dist/plugins/pg-safe-json.js +40 -4
- package/dist/plugins/pg-session-policy-store.d.ts +5 -0
- package/dist/plugins/pg-session-policy-store.js +5 -0
- package/dist/plugins/pg-session-storage.d.ts +72 -0
- package/dist/plugins/pg-session-storage.js +152 -20
- package/dist/plugins/pg-session-store.d.ts +10 -0
- package/dist/plugins/pg-session-store.js +10 -0
- package/dist/plugins/pg-tool-result-store.d.ts +7 -0
- package/dist/plugins/pg-tool-result-store.js +7 -0
- package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-journal-store.js +7 -0
- package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-run-store.js +7 -0
- package/dist/plugins/posix-shell-fs.d.ts +3 -0
- package/dist/plugins/posix-shell-fs.js +31 -1
- package/dist/plugins/remote-env-adb.d.ts +25 -0
- package/dist/plugins/remote-env-adb.js +95 -12
- package/dist/plugins/remote-env-e2b.d.ts +146 -0
- package/dist/plugins/remote-env-e2b.js +371 -40
- package/dist/plugins/remote-env-file-error.d.ts +34 -0
- package/dist/plugins/remote-env-file-error.js +37 -0
- package/dist/plugins/remote-env-host.d.ts +151 -0
- package/dist/plugins/remote-env-host.js +468 -70
- package/dist/plugins/remote-env-k8s.d.ts +135 -0
- package/dist/plugins/remote-env-k8s.js +318 -32
- package/dist/plugins/remote-env-local-docker.d.ts +65 -0
- package/dist/plugins/remote-env-local-docker.js +149 -24
- package/dist/plugins/remote-env-ssh.d.ts +35 -0
- package/dist/plugins/remote-env-ssh.js +117 -20
- package/dist/plugins/remote-scratchpad.d.ts +38 -0
- package/dist/plugins/remote-scratchpad.js +32 -2
- package/dist/plugins/remote-shell.d.ts +22 -0
- package/dist/plugins/remote-shell.js +27 -1
- package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
- package/dist/plugins/resume-anchor-store-sql.js +11 -0
- package/dist/plugins/roster-store-sql.d.ts +27 -0
- package/dist/plugins/roster-store-sql.js +54 -0
- package/dist/plugins/run-store-sql.d.ts +194 -0
- package/dist/plugins/run-store-sql.js +210 -9
- package/dist/plugins/s3-presign.d.ts +20 -0
- package/dist/plugins/s3-presign.js +49 -2
- package/dist/plugins/scheduler-support.d.ts +12 -0
- package/dist/plugins/scheduler-support.js +75 -3
- package/dist/plugins/send-file-ledger.d.ts +15 -0
- package/dist/plugins/send-file-ledger.js +53 -4
- package/dist/plugins/send-user-file.d.ts +77 -0
- package/dist/plugins/send-user-file.js +92 -4
- package/dist/plugins/session-policy-store-sql.d.ts +15 -0
- package/dist/plugins/session-policy-store-sql.js +75 -3
- package/dist/plugins/session-store.d.ts +45 -0
- package/dist/plugins/session-store.js +51 -0
- package/dist/plugins/sql-driver.d.ts +77 -0
- package/dist/plugins/sql-driver.js +4 -0
- package/dist/plugins/sql-escape.d.ts +8 -0
- package/dist/plugins/sql-escape.js +8 -0
- package/dist/plugins/sql-row-helpers.d.ts +25 -0
- package/dist/plugins/sql-row-helpers.js +25 -0
- package/dist/plugins/store-backend.d.ts +103 -0
- package/dist/plugins/store-backend.js +120 -27
- package/dist/plugins/store-contracts.d.ts +62 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +60 -0
- package/dist/plugins/task-attachment-store.js +49 -3
- package/dist/plugins/task-list-store-sql.d.ts +25 -0
- package/dist/plugins/task-list-store-sql.js +21 -0
- package/dist/plugins/tidb-approval-store.d.ts +6 -0
- package/dist/plugins/tidb-approval-store.js +6 -0
- package/dist/plugins/tidb-breaker-state.d.ts +5 -0
- package/dist/plugins/tidb-breaker-state.js +5 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
- package/dist/plugins/tidb-checkpoint-store.js +7 -0
- package/dist/plugins/tidb-cost-quota.d.ts +6 -0
- package/dist/plugins/tidb-cost-quota.js +1 -0
- package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/tidb-file-snapshot-store.js +6 -0
- package/dist/plugins/tidb-image-bake.d.ts +8 -0
- package/dist/plugins/tidb-image-bake.js +8 -0
- package/dist/plugins/tidb-image-index.d.ts +8 -0
- package/dist/plugins/tidb-image-index.js +8 -0
- package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
- package/dist/plugins/tidb-outcome-ledger.js +10 -0
- package/dist/plugins/tidb-pool.d.ts +56 -0
- package/dist/plugins/tidb-pool.js +181 -1
- package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
- package/dist/plugins/tidb-rate-limiter.js +5 -2
- package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/tidb-resume-anchor-store.js +5 -0
- package/dist/plugins/tidb-run-store.d.ts +7 -0
- package/dist/plugins/tidb-run-store.js +7 -0
- package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
- package/dist/plugins/tidb-session-policy-store.js +5 -0
- package/dist/plugins/tidb-session-storage.d.ts +43 -0
- package/dist/plugins/tidb-session-storage.js +62 -3
- package/dist/plugins/tidb-session-store.d.ts +142 -0
- package/dist/plugins/tidb-session-store.js +264 -16
- package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
- package/dist/plugins/tidb-tool-result-store.js +7 -0
- package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/tidb-workflow-journal-store.js +7 -0
- package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
- package/dist/plugins/tidb-workflow-run-store.js +8 -0
- package/dist/plugins/tool-result-store-sql.d.ts +58 -0
- package/dist/plugins/tool-result-store-sql.js +38 -2
- package/dist/plugins/web-search.d.ts +35 -0
- package/dist/plugins/web-search.js +45 -9
- package/dist/plugins/workflow-journal-limits.d.ts +10 -0
- package/dist/plugins/workflow-journal-limits.js +10 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
- package/dist/plugins/workflow-journal-store-sql.js +58 -2
- package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
- package/dist/plugins/workflow-run-store-sql.js +84 -6
- package/dist/plugins/worktree-isolation.d.ts +45 -0
- package/dist/plugins/worktree-isolation.js +105 -6
- package/dist/plugins/write-behind-counter.d.ts +56 -1
- package/dist/plugins/write-behind-counter.js +53 -14
- package/dist/principal-jwt.d.ts +24 -0
- package/dist/principal-jwt.js +60 -5
- package/dist/project-memory.d.ts +22 -0
- package/dist/project-memory.js +144 -15
- package/dist/prompts-domain-validate.d.ts +28 -0
- package/dist/prompts-domain-validate.js +42 -1
- package/dist/question.d.ts +33 -0
- package/dist/question.js +69 -2
- package/dist/resource-suspend.d.ts +21 -0
- package/dist/resource-suspend.js +16 -0
- package/dist/router/route-orchestration.d.ts +78 -0
- package/dist/router/route-orchestration.js +77 -0
- package/dist/run-local.d.ts +32 -0
- package/dist/run-local.js +172 -8
- package/dist/runs.d.ts +189 -4
- package/dist/runs.js +465 -24
- package/dist/runtime-caps-resolver.d.ts +106 -0
- package/dist/runtime-caps-resolver.js +89 -4
- package/dist/runtime-governance.d.ts +84 -0
- package/dist/runtime-governance.js +127 -1
- package/dist/sandbox-pkg-source.d.ts +23 -0
- package/dist/sandbox-pkg-source.js +37 -0
- package/dist/sealed-key.d.ts +54 -0
- package/dist/sealed-key.js +68 -3
- package/dist/security.d.ts +108 -0
- package/dist/security.js +145 -6
- package/dist/sema-registry.d.ts +21 -0
- package/dist/sema-registry.js +21 -0
- package/dist/session-leaf-bus.d.ts +31 -0
- package/dist/session-leaf-bus.js +32 -0
- package/dist/session-sync-content.d.ts +46 -0
- package/dist/session-sync-content.js +47 -1
- package/dist/session-sync-kernel.d.ts +89 -0
- package/dist/session-sync-kernel.js +48 -3
- package/dist/session-sync.d.ts +125 -0
- package/dist/session-sync.js +181 -8
- package/dist/session-titler.d.ts +26 -0
- package/dist/session-titler.js +41 -7
- package/dist/session-watch.d.ts +53 -1
- package/dist/session-watch.js +73 -9
- package/dist/sighup-idle.d.ts +30 -0
- package/dist/sighup-idle.js +12 -1
- package/dist/spec-fields.d.ts +91 -3
- package/dist/spec-fields.js +116 -4
- package/dist/task-cwd.d.ts +50 -0
- package/dist/task-cwd.js +68 -1
- package/dist/task-mcp.d.ts +47 -0
- package/dist/task-mcp.js +29 -0
- package/dist/task-settings.d.ts +175 -0
- package/dist/task-settings.js +226 -11
- package/dist/task-workflow.d.ts +67 -0
- package/dist/task-workflow.js +75 -8
- package/dist/tool-approval.d.ts +91 -0
- package/dist/tool-approval.js +215 -0
- package/dist/trace/artifacts.d.ts +9 -0
- package/dist/trace/artifacts.js +30 -5
- package/dist/trace/core-keyset-guard.d.ts +18 -0
- package/dist/trace/ledger-sink.d.ts +43 -0
- package/dist/trace/ledger-sink.js +64 -5
- package/dist/trace/project.d.ts +133 -0
- package/dist/trace/project.js +189 -2
- package/dist/trace/redact.d.ts +19 -0
- package/dist/trace/redact.js +66 -9
- package/dist/usage-analytics.d.ts +26 -0
- package/dist/usage-analytics.js +26 -3
- package/package.json +1 -1
package/dist/auth-bridge.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 鉴权桥 verifier(REGISTRY-REBUILD §5 M3 service 半场)——验 registry 铸的用户 access JWT,
|
|
3
|
+
* `sub` 直喂既有 x-agent-principal 体系。规范=`@sema-agent/registry-core/api/auth-bridge` R1-R10(零 IO 纯契约,
|
|
4
|
+
* 决策函数直接复用);本模块补齐 IO+crypto 半场:JWKS 拉取/缓存、RS256 真验签(node:crypto,零新依赖)。
|
|
5
|
+
*
|
|
6
|
+
* 信任模型:worker 已信 registry 的 effective config,同一信任根延伸到它的 JWKS——零新配置项
|
|
7
|
+
* (issuer/jwksUrl 默认从 SEMA_REGISTRY_URL 派生;split 部署可 env 显式覆盖)。既有 SERVICE_AUTH_TOKEN(S)
|
|
8
|
+
* 分支不动:bearer 长得像 JWT(三段 base64url)才走本分支(looksLikeJwt=路由预过滤,非校验)。
|
|
9
|
+
*
|
|
10
|
+
* R10:本模块任何路径不 log token/签名段/密钥材料——只 log kid + 错误码 + iss。
|
|
11
|
+
*/
|
|
1
12
|
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
2
13
|
import { AUTH_BRIDGE_ALGS, DEFAULT_CLOCK_SKEW_SEC, DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC, DEFAULT_OFFLINE_GRACE_SEC, REGISTRY_JWKS_PATH, RegistryJwks, classifyTimeClaims, jwksWithinOfflineGrace, shouldForceJwksRefresh, toVerifiedIdentity, } from "@sema-agent/registry-core/api/auth-bridge";
|
|
3
14
|
export { looksLikeJwt } from "@sema-agent/registry-core/api/auth-bridge";
|
|
4
15
|
const DEFAULT_JWKS_TTL_SEC = 15 * 60;
|
|
16
|
+
/** JWKS 响应体字节上限(一个配错或超大响应的端点不应让 worker 无界缓冲;正常 JWKS 千字节级)。 */
|
|
5
17
|
const MAX_JWKS_BYTES = 256 * 1024;
|
|
18
|
+
/** 整 token 长度上限(签名段无独立上限,故对整 bearer 统一设限;正常 JWT ~1KiB)。 */
|
|
6
19
|
const MAX_TOKEN_CHARS = 16 * 1024;
|
|
20
|
+
/** 单段解码上限(header/payload)。 */
|
|
7
21
|
const MAX_SEGMENT_BYTES = 64 * 1024;
|
|
22
|
+
/** base64url 字符集严格校验——Node 的 base64url 解码会容忍集外字符(仍解出同一字节),而契约要求"三段
|
|
23
|
+
* base64url"形态严格,故解码前每段必须匹配该字符集。 */
|
|
8
24
|
const B64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
9
25
|
function b64urlJson(seg) {
|
|
10
26
|
if (seg.length > MAX_SEGMENT_BYTES || !B64URL_RE.test(seg))
|
|
@@ -18,6 +34,10 @@ function b64urlJson(seg) {
|
|
|
18
34
|
}
|
|
19
35
|
}
|
|
20
36
|
const stripSlash = (s) => s.replace(/\/+$/, "");
|
|
37
|
+
/**
|
|
38
|
+
* registry-JWT verifier(单例,进程级 JWKS 缓存)。`verify(bearer)` 全程不 throw——一切异常折为
|
|
39
|
+
* AUTH_BRIDGE_ERROR_CODES(HTTP 面由调用方恒答 generic 401,码只进日志/metrics,R9)。
|
|
40
|
+
*/
|
|
21
41
|
export function createRegistryJwtVerifier(opts) {
|
|
22
42
|
const issuer = stripSlash(opts.issuer);
|
|
23
43
|
const jwksUrl = opts.jwksUrl ?? `${issuer}${REGISTRY_JWKS_PATH}`;
|
|
@@ -27,12 +47,14 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
27
47
|
const jwksTtlSec = opts.jwksTtlSec ?? DEFAULT_JWKS_TTL_SEC;
|
|
28
48
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
29
49
|
const now = opts.nowMs ?? Date.now;
|
|
50
|
+
// 进程级缓存(R3/R8 的状态面;shouldForceJwksRefresh/jwksWithinOfflineGrace 消费的快照即它)。
|
|
30
51
|
const keys = new Map();
|
|
31
52
|
let lastFetchAttemptMs;
|
|
32
53
|
let lastFetchSuccessMs;
|
|
33
|
-
let inflight;
|
|
54
|
+
let inflight; // 并发 verify 合并成一次拉取(大量未知 kid 时不重复拉取)
|
|
34
55
|
const fail = (error, kid) => {
|
|
35
56
|
opts.metrics?.inc("auth_bridge_verify_total", { outcome: error });
|
|
57
|
+
// R10:仅 kid+码+iss。kid 来自**调用方可控**的 token header(16K token 上限内可到 ~10KB)——截断防日志放大。
|
|
36
58
|
opts.logger?.warn?.("auth_bridge_rejected", { error, ...(kid ? { kid: kid.slice(0, 128) } : {}), iss: issuer });
|
|
37
59
|
return { ok: false, error };
|
|
38
60
|
};
|
|
@@ -45,16 +67,19 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
45
67
|
const res = await doFetch(jwksUrl, { redirect: "error", signal: AbortSignal.timeout(10_000) });
|
|
46
68
|
if (!res.ok)
|
|
47
69
|
throw new Error(`jwks HTTP ${res.status}`);
|
|
70
|
+
// 先看 Content-Length 早拒;chunked/无 CL 的响应会整体读入后按字节拒(非流式截断——jwksUrl 是
|
|
71
|
+
// operator 钉死的可信 registry + redirect:"error" + 10s timeout,瞬时内存风险接受,诚实记档)。
|
|
48
72
|
const cl = Number(res.headers.get("content-length"));
|
|
49
73
|
if (Number.isFinite(cl) && cl > MAX_JWKS_BYTES)
|
|
50
74
|
throw new Error("jwks content-length too large");
|
|
51
75
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
52
76
|
if (buf.byteLength > MAX_JWKS_BYTES)
|
|
53
77
|
throw new Error("jwks body too large");
|
|
54
|
-
const doc = RegistryJwks.parse(JSON.parse(buf.toString("utf8")));
|
|
78
|
+
const doc = RegistryJwks.parse(JSON.parse(buf.toString("utf8"))); // zod:形状不对=拉取失败(R8 路),绝不采用半坏文档
|
|
55
79
|
const next = new Map();
|
|
56
80
|
for (const k of doc.keys) {
|
|
57
81
|
try {
|
|
82
|
+
// 公钥成员 only(契约警告:即便 registry 出 bug 带了私有成员也绝不使用/回显)。
|
|
58
83
|
next.set(k.kid, createPublicKey({ key: { kty: "RSA", n: k.n, e: k.e }, format: "jwk" }));
|
|
59
84
|
}
|
|
60
85
|
catch {
|
|
@@ -67,6 +92,7 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
67
92
|
lastFetchSuccessMs = now();
|
|
68
93
|
}
|
|
69
94
|
catch (e) {
|
|
95
|
+
// 失败=保留旧缓存(R8 离线宽限判定在 verify 侧);只记账不 throw。
|
|
70
96
|
opts.logger?.warn?.("auth_bridge_jwks_fetch_failed", { iss: issuer, error: String(e).slice(0, 200) });
|
|
71
97
|
}
|
|
72
98
|
finally {
|
|
@@ -76,6 +102,8 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
76
102
|
return inflight;
|
|
77
103
|
}
|
|
78
104
|
async function verify(bearer) {
|
|
105
|
+
// 延迟观测(auth_bridge_verify_seconds):热路径(kid 命中)应为亚毫秒级;冷路径(JWKS 拉取)带一次
|
|
106
|
+
// 网络 RTT——首请求/轮换时的尾延迟在这根直方图上可见,不用猜。
|
|
79
107
|
const t0 = now();
|
|
80
108
|
try {
|
|
81
109
|
return await verifyInner(bearer);
|
|
@@ -88,43 +116,57 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
88
116
|
if (bearer.length > MAX_TOKEN_CHARS)
|
|
89
117
|
return fail("malformed");
|
|
90
118
|
const parts = bearer.split(".");
|
|
119
|
+
// 三段非空 + 每段 base64url 字符集(b64urlJson 已校验 header/payload;签名段在此单独校验)。
|
|
91
120
|
if (parts.length !== 3 || parts.some((p) => p.length === 0) || !B64URL_RE.test(parts[2]))
|
|
92
121
|
return fail("malformed");
|
|
93
122
|
const header = b64urlJson(parts[0]);
|
|
94
123
|
const payload = b64urlJson(parts[1]);
|
|
95
124
|
if (!header || !payload)
|
|
96
125
|
return fail("malformed");
|
|
126
|
+
// R1 — alg 固定在调用侧:只接受 RS256,不读 token 的 alg 字段来决定验法(none/HS*/ES*/PS* 一律拒)。
|
|
127
|
+
// 错误码归类跟契约(AUTH_BRIDGE_ERROR_CODES 注释):disallowed alg / 缺 kid = `malformed`
|
|
128
|
+
// (invalid_signature 留给"键在、验签不过"——异源复审对齐,跨侧 metrics 口径一致)。
|
|
97
129
|
if (header.alg !== AUTH_BRIDGE_ALGS[0])
|
|
98
130
|
return fail("malformed");
|
|
99
131
|
const kid = typeof header.kid === "string" ? header.kid : undefined;
|
|
100
132
|
if (!kid)
|
|
101
133
|
return fail("malformed");
|
|
134
|
+
// R4 — issuer 先于取键比较(iss 不对根本不必碰 JWKS)。
|
|
102
135
|
const iss = typeof payload.iss === "string" ? stripSlash(payload.iss) : undefined;
|
|
103
136
|
if (iss !== issuer)
|
|
104
137
|
return fail("unknown_issuer", kid);
|
|
138
|
+
// R5 — 时间窗(60s 偏移;exp 必须在)。
|
|
105
139
|
if (classifyTimeClaims(payload, Math.floor(now() / 1000), clockSkewSec) !== "ok")
|
|
106
140
|
return fail("expired", kid);
|
|
141
|
+
// R2/R3 — 取键。热路径:kid 命中 ⇒ 直用,零 fetch(TTL 陈旧只在无在途拉取且过节流时【后台】刷一次
|
|
142
|
+
// [void,不 await],不阻塞验证)。kid miss ⇒ 先 await 任何在途拉取(并发请求不应被 lastFetchAttemptMs
|
|
143
|
+
// 节流跳过而错过正在完成的刷新),再查;仍 miss 且过节流 ⇒ 强制刷一次。
|
|
107
144
|
const snapshot = () => ({ cachedKids: [...keys.keys()], lastFetchAttemptMs, nowMs: now() });
|
|
108
145
|
if (keys.has(kid)) {
|
|
109
146
|
const ttlStale = lastFetchSuccessMs === undefined || now() - lastFetchSuccessMs > jwksTtlSec * 1000;
|
|
110
147
|
if (ttlStale && !inflight && (lastFetchAttemptMs === undefined || now() - lastFetchAttemptMs >= refreshMin * 1000)) {
|
|
111
|
-
void refreshJwks();
|
|
148
|
+
void refreshJwks(); // 后台刷新,当前请求继续用现有 key
|
|
112
149
|
}
|
|
113
150
|
}
|
|
114
151
|
else {
|
|
115
152
|
if (inflight)
|
|
116
|
-
await inflight;
|
|
153
|
+
await inflight; // 有在途拉取:等它完成再查(可能正好带来这个 kid)
|
|
117
154
|
if (!keys.has(kid) && shouldForceJwksRefresh(kid, snapshot(), { jwksRefreshMinIntervalSec: refreshMin }))
|
|
118
155
|
await refreshJwks();
|
|
119
156
|
}
|
|
120
157
|
const key = keys.get(kid);
|
|
121
158
|
if (!key) {
|
|
159
|
+
// R8 — kid 仍未命中:有【活】缓存(有成功拉取且在 24h 宽限内)⇒ 键该在却不在=token 与当前键集不符
|
|
160
|
+
// (invalid_signature)。从没成功拉过 / 超宽限 ⇒ jwks_unavailable(失败即拒:不用 `keys.size>0` 兜底,
|
|
161
|
+
// 因为超宽限的陈旧缓存 size>0 时也不应把 unavailable 归成 invalid_signature)。
|
|
122
162
|
const graceOk = lastFetchSuccessMs !== undefined && jwksWithinOfflineGrace({ lastFetchSuccessMs, nowMs: now() }, { offlineGraceSec });
|
|
123
163
|
return graceOk ? fail("invalid_signature", kid) : fail("jwks_unavailable", kid);
|
|
124
164
|
}
|
|
125
165
|
if (lastFetchSuccessMs !== undefined && !jwksWithinOfflineGrace({ lastFetchSuccessMs, nowMs: now() }, { offlineGraceSec })) {
|
|
166
|
+
// 缓存超出 24h 离线宽限——即便 kid 命中也拒(R8:宽限后不再使用陈旧键)。
|
|
126
167
|
return fail("jwks_unavailable", kid);
|
|
127
168
|
}
|
|
169
|
+
// 真验签(RS256 = RSASSA-PKCS1-v1_5 + SHA-256 over `header.payload`)。
|
|
128
170
|
let sigOk = false;
|
|
129
171
|
try {
|
|
130
172
|
sigOk = cryptoVerify("RSA-SHA256", Buffer.from(`${parts[0]}.${parts[1]}`, "utf8"), key, Buffer.from(parts[2], "base64url"));
|
|
@@ -134,6 +176,7 @@ export function createRegistryJwtVerifier(opts) {
|
|
|
134
176
|
}
|
|
135
177
|
if (!sigOk)
|
|
136
178
|
return fail("invalid_signature", kid);
|
|
179
|
+
// R6/R7/R9 — 身份映射(契约纯函数)。
|
|
137
180
|
const mapped = toVerifiedIdentity(payload, opts.audience ? { audience: opts.audience } : undefined);
|
|
138
181
|
if (!mapped.ok)
|
|
139
182
|
return fail(mapped.error, kid);
|
package/dist/auth-keys.d.ts
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTH KEY-SET parsing (design/158 A4) — the env→key-set decoders for the two D-G credential planes, moved VERBATIM
|
|
3
|
+
* out of security.ts: {@link parseApprovalHmacKeys} (APPROVAL_HMAC_KEYS — the rotating HMAC secret set, decision
|
|
4
|
+
* INTEGRITY) and {@link parsePrincipalJwks} (PRINCIPAL_JWT_PUBKEYS — the rotating issuer public-key set, principal
|
|
5
|
+
* IDENTITY). config.ts consumes ONLY these two functions + their row types at env-load time, and importing them from
|
|
6
|
+
* security.ts made the base config layer value-depend on the whole 55KiB auth module (lens2 §F). This file is a pure
|
|
7
|
+
* leaf (JSON.parse + shape filters, zero repo imports); the VERIFIERS that consume the parsed key-sets
|
|
8
|
+
* (verifyApprovalHmac / verifyPrincipalJwt) stay in security.ts, which re-exports these four names for compat.
|
|
9
|
+
*/
|
|
10
|
+
/** One key in the rotating HMAC key-set. `kid` lets the verifier pick the signing key (or fall back to trying the
|
|
11
|
+
* whole set); `status` lets sema-registry rotate without breaking in-flight approvals: `active` = sign + verify,
|
|
12
|
+
* `retiring` = verify-only (kept for the overlap window so a MAC signed with the old key still verifies). */
|
|
1
13
|
export interface ApprovalHmacKey {
|
|
2
14
|
kid: string;
|
|
3
15
|
key: string;
|
|
4
16
|
status?: "active" | "retiring";
|
|
5
17
|
}
|
|
18
|
+
/** Parse APPROVAL_HMAC_KEYS — a sema-registry-managed, rotating key-SET, as a JSON array of {kid,key,status}.
|
|
19
|
+
* Empty / unset / unparseable ⇒ `[]` ⇒ HMAC verification is OFF (D-G inactive; back-compat, BFF-only door). */
|
|
6
20
|
export declare function parseApprovalHmacKeys(raw: string | undefined): ApprovalHmacKey[];
|
|
21
|
+
/** One issuer public key in the rotating principal JWKS (mirror of the HMAC key-set rotation discipline). The
|
|
22
|
+
* `alg` is PINNED per key — the JWT header's alg MUST match it, killing alg-confusion (`none`, HS-with-the-pubkey).
|
|
23
|
+
* `key` is a PEM (SPKI) public key. PUBLIC, non-secret. */
|
|
7
24
|
export interface PrincipalJwtKey {
|
|
8
25
|
kid: string;
|
|
9
26
|
alg: "EdDSA" | "RS256" | "ES256";
|
|
10
27
|
key: string;
|
|
11
28
|
status?: "active" | "retiring";
|
|
12
29
|
}
|
|
30
|
+
/** Parse PRINCIPAL_JWT_PUBKEYS — a JSON array of {kid,alg,key,status}. Empty/unset/malformed ⇒ [] ⇒ no trust
|
|
31
|
+
* anchor ⇒ the direct door cannot open (D-G inactive). Each entry must have kid + a pinned alg + a PEM key. */
|
|
13
32
|
export declare function parsePrincipalJwks(raw: string | undefined): PrincipalJwtKey[];
|
|
14
33
|
//# sourceMappingURL=auth-keys.d.ts.map
|
package/dist/auth-keys.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTH KEY-SET parsing (design/158 A4) — the env→key-set decoders for the two D-G credential planes, moved VERBATIM
|
|
3
|
+
* out of security.ts: {@link parseApprovalHmacKeys} (APPROVAL_HMAC_KEYS — the rotating HMAC secret set, decision
|
|
4
|
+
* INTEGRITY) and {@link parsePrincipalJwks} (PRINCIPAL_JWT_PUBKEYS — the rotating issuer public-key set, principal
|
|
5
|
+
* IDENTITY). config.ts consumes ONLY these two functions + their row types at env-load time, and importing them from
|
|
6
|
+
* security.ts made the base config layer value-depend on the whole 55KiB auth module (lens2 §F). This file is a pure
|
|
7
|
+
* leaf (JSON.parse + shape filters, zero repo imports); the VERIFIERS that consume the parsed key-sets
|
|
8
|
+
* (verifyApprovalHmac / verifyPrincipalJwt) stay in security.ts, which re-exports these four names for compat.
|
|
9
|
+
*/
|
|
10
|
+
/** Parse APPROVAL_HMAC_KEYS — a sema-registry-managed, rotating key-SET, as a JSON array of {kid,key,status}.
|
|
11
|
+
* Empty / unset / unparseable ⇒ `[]` ⇒ HMAC verification is OFF (D-G inactive; back-compat, BFF-only door). */
|
|
1
12
|
export function parseApprovalHmacKeys(raw) {
|
|
2
13
|
if (!raw)
|
|
3
14
|
return [];
|
|
@@ -11,6 +22,8 @@ export function parseApprovalHmacKeys(raw) {
|
|
|
11
22
|
return [];
|
|
12
23
|
}
|
|
13
24
|
}
|
|
25
|
+
/** Parse PRINCIPAL_JWT_PUBKEYS — a JSON array of {kid,alg,key,status}. Empty/unset/malformed ⇒ [] ⇒ no trust
|
|
26
|
+
* anchor ⇒ the direct door cannot open (D-G inactive). Each entry must have kid + a pinned alg + a PEM key. */
|
|
14
27
|
export function parsePrincipalJwks(raw) {
|
|
15
28
|
if (!raw)
|
|
16
29
|
return [];
|
|
@@ -12,8 +12,13 @@ interface RunnerEnv {
|
|
|
12
12
|
minFreeGb: number;
|
|
13
13
|
}
|
|
14
14
|
declare function loadEnv(): RunnerEnv;
|
|
15
|
+
/** The real image-api HTTP client. Authenticates claim/heartbeat with the runner bearer; ingest additionally
|
|
16
|
+
* carries the per-bake ingest secret header (so image-api can reject a stale/rogue runner — §P2.4e). */
|
|
15
17
|
declare function makeApiClient(env: RunnerEnv): ImageApiClient;
|
|
18
|
+
/** The real child spawner: `setsid`-style detached process GROUP so a cancel/deadline kill signals the whole tree
|
|
19
|
+
* (channel-close / a plain kill of the immediate child does NOT stop a detached build — §P2.11). */
|
|
16
20
|
declare function makeSpawner(): ChildSpawner;
|
|
21
|
+
/** The real host ops: fs manifest read, df /data, recipe checkout, and the thin docker-push big-blob retry. */
|
|
17
22
|
declare function makeHostOps(env: RunnerEnv): HostOps;
|
|
18
23
|
declare function main(): Promise<void>;
|
|
19
24
|
export { main, loadEnv, makeApiClient, makeSpawner, makeHostOps };
|
package/dist/bake-runner/main.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bake-runner ENTRYPOINT (IMAGE-API-DESIGN.md §P2.0/§P2.12) — the privileged build-host daemon. It wires the REAL I/O
|
|
3
|
+
* (HTTP to image-api, the setsid-detached build.sh child, the fs manifest read, git checkout, df) into the
|
|
4
|
+
* I/O-injected `BakeRunner` core (./runner.ts). This process runs ONLY on the build-host build host — it CANNOT be
|
|
5
|
+
* unit-run here (no docker/ctr/recipes); the testable logic is the pure protocol + the runner orchestration.
|
|
6
|
+
*
|
|
7
|
+
* Crown-jewel discipline (§P2.4e): the only secrets are BAKE_RUNNER_TOKEN (the operator-principal bearer for
|
|
8
|
+
* claim/heartbeat) and the per-bake ingest secret minted by image-api at claim — neither is ever logged.
|
|
9
|
+
*/
|
|
1
10
|
import { spawn, exec as execCb } from "node:child_process";
|
|
2
11
|
import { readFile } from "node:fs/promises";
|
|
3
12
|
import { promisify } from "node:util";
|
|
@@ -24,9 +33,13 @@ function loadEnv() {
|
|
|
24
33
|
minFreeGb: Number(process.env.IMAGE_BAKE_MIN_FREE_GB || "20"),
|
|
25
34
|
};
|
|
26
35
|
}
|
|
36
|
+
/** The real image-api HTTP client. Authenticates claim/heartbeat with the runner bearer; ingest additionally
|
|
37
|
+
* carries the per-bake ingest secret header (so image-api can reject a stale/rogue runner — §P2.4e). */
|
|
27
38
|
function makeApiClient(env) {
|
|
28
39
|
const auth = { authorization: `Bearer ${env.token}`, "content-type": "application/json" };
|
|
29
40
|
return {
|
|
41
|
+
// Long-poll claim (§P2.12): the bare `POST …/bakes/claim` finds the oldest queued bake AND leases it in one
|
|
42
|
+
// call (the discovery + the single-flight CAS server-side). 204 ⇒ the queue is empty (the runner idle-polls).
|
|
30
43
|
async claimNext() {
|
|
31
44
|
const res = await fetch(`${env.imageApiBase}/v1/images/bakes/claim`, {
|
|
32
45
|
method: "POST",
|
|
@@ -34,7 +47,7 @@ function makeApiClient(env) {
|
|
|
34
47
|
body: JSON.stringify({ runnerId: env.runnerId }),
|
|
35
48
|
});
|
|
36
49
|
if (res.status === 204 || res.status === 409)
|
|
37
|
-
return null;
|
|
50
|
+
return null; // empty queue, or lost the single-flight CAS
|
|
38
51
|
if (!res.ok)
|
|
39
52
|
return null;
|
|
40
53
|
const c = (await res.json());
|
|
@@ -61,10 +74,11 @@ function makeApiClient(env) {
|
|
|
61
74
|
return {
|
|
62
75
|
accepted: res.ok && body.accepted !== false,
|
|
63
76
|
cancelRequested: body.cancelRequested === true,
|
|
64
|
-
leaseValid: res.status !== 409,
|
|
77
|
+
leaseValid: res.status !== 409, // a 409 on ingest = the lease/claim no longer ours
|
|
65
78
|
};
|
|
66
79
|
},
|
|
67
80
|
async heartbeat(bakeId, ingestSecret) {
|
|
81
|
+
// A heartbeat is a no-frame ingest: image-api advances the lease and echoes the cancel flag (§P2.7/§P2.11).
|
|
68
82
|
const res = await fetch(`${env.imageApiBase}/v1/images/bakes/${encodeURIComponent(bakeId)}/ingest`, {
|
|
69
83
|
method: "POST",
|
|
70
84
|
headers: { ...auth, "x-bake-ingest-secret": ingestSecret },
|
|
@@ -79,9 +93,13 @@ function makeApiClient(env) {
|
|
|
79
93
|
},
|
|
80
94
|
};
|
|
81
95
|
}
|
|
96
|
+
/** The real child spawner: `setsid`-style detached process GROUP so a cancel/deadline kill signals the whole tree
|
|
97
|
+
* (channel-close / a plain kill of the immediate child does NOT stop a detached build — §P2.11). */
|
|
82
98
|
function makeSpawner() {
|
|
83
99
|
return {
|
|
84
100
|
spawn(opts) {
|
|
101
|
+
// detached:true → the child becomes a process-group leader (its pgid == its pid); `process.kill(-pid, sig)`
|
|
102
|
+
// then signals the WHOLE group. We DON'T `unref()` — the runner waits on the exit promise.
|
|
85
103
|
const child = spawn(opts.argv[0], opts.argv.slice(1), {
|
|
86
104
|
cwd: opts.cwd,
|
|
87
105
|
detached: true,
|
|
@@ -93,12 +111,13 @@ function makeSpawner() {
|
|
|
93
111
|
bufferLines(child.stderr, opts.onStderrLine);
|
|
94
112
|
const exited = new Promise((resolve) => {
|
|
95
113
|
child.on("exit", (code, signal) => resolve({ code, signal }));
|
|
96
|
-
child.on("error", () => resolve({ code: 127, signal: null }));
|
|
114
|
+
child.on("error", () => resolve({ code: 127, signal: null })); // spawn failed (build.sh missing, etc.)
|
|
97
115
|
});
|
|
98
116
|
const handle = {
|
|
99
117
|
pgid,
|
|
100
118
|
exited,
|
|
101
119
|
killGroup(signal) {
|
|
120
|
+
// Negative pid → signal the process GROUP (the detached tree), not just the immediate child.
|
|
102
121
|
if (pgid > 0)
|
|
103
122
|
process.kill(-pgid, signal);
|
|
104
123
|
},
|
|
@@ -107,6 +126,8 @@ function makeSpawner() {
|
|
|
107
126
|
},
|
|
108
127
|
};
|
|
109
128
|
}
|
|
129
|
+
/** Split a readable stream into lines, invoking `onLine` per complete line (the runner parses ONLY stdout as JSON;
|
|
130
|
+
* stderr lines are the failure tail). Carries a partial trailing line across chunks; flushes it on stream end. */
|
|
110
131
|
function bufferLines(stream, onLine) {
|
|
111
132
|
if (!stream)
|
|
112
133
|
return;
|
|
@@ -126,6 +147,7 @@ function bufferLines(stream, onLine) {
|
|
|
126
147
|
onLine(buf);
|
|
127
148
|
});
|
|
128
149
|
}
|
|
150
|
+
/** The real host ops: fs manifest read, df /data, recipe checkout, and the thin docker-push big-blob retry. */
|
|
129
151
|
function makeHostOps(env) {
|
|
130
152
|
return {
|
|
131
153
|
async readManifest(path) {
|
|
@@ -136,6 +158,14 @@ function makeHostOps(env) {
|
|
|
136
158
|
return stdout;
|
|
137
159
|
},
|
|
138
160
|
async checkoutRecipe(ref) {
|
|
161
|
+
// The recipe dir is a git checkout of the service repo pinned to the runner's deployed commit (NOT a frozen
|
|
162
|
+
// copy) so build.sh's `git rev-parse` gitsha is REAL and the manifest's recipeGitSha is replay-sufficient.
|
|
163
|
+
// A fixed ref (the deployed image's commit) → fetch + checkout is idempotent; a non-default ref is fetched.
|
|
164
|
+
// 🔴 A ref may be a branch/tag (fetchable by name) OR a BARE COMMIT SHA — and `git fetch origin <sha>` fails
|
|
165
|
+
// on most servers ("couldn't find remote ref <sha>"; only refs are fetchable). So try the cheap shallow
|
|
166
|
+
// ref-fetch, and on failure fall back to fetching the repo and checking the SHA out of the local history
|
|
167
|
+
// (the init-container clone usually already contains it). RECIPE_REF=HEAD skips this entirely (uses the
|
|
168
|
+
// checkout as-is — the prod path, the init-container pins the commit). [live-caught on center's prod deploy]
|
|
139
169
|
if (ref && ref !== "HEAD") {
|
|
140
170
|
const dir = shellSafe(env.recipeDir);
|
|
141
171
|
try {
|
|
@@ -146,9 +176,14 @@ function makeHostOps(env) {
|
|
|
146
176
|
await exec(`git -C ${dir} fetch origin && git -C ${dir} checkout --quiet --detach ${shellSafe(ref)}`);
|
|
147
177
|
}
|
|
148
178
|
}
|
|
179
|
+
// build.sh resolves itself relative to its own dir; the cwd for the spawn is the recipe checkout root so the
|
|
180
|
+
// path RECIPE_DIR + BUILD_SH_PATH is the script the runner invokes.
|
|
149
181
|
return env.recipeDir;
|
|
150
182
|
},
|
|
151
183
|
async dockerPushRetry(tag, attempts) {
|
|
184
|
+
// Thin `docker push` wrapper (NOT build.sh --replay — that re-bakes + hits the dup-check, exit 10). Retries a
|
|
185
|
+
// flaky big-blob registry layer push with backoff (born on SWR — deprecated; pool now docker.io/claybobby);
|
|
186
|
+
// NO_PROXY (incl. the registry domain) lives in this process's env.
|
|
152
187
|
for (let i = 0; i < attempts; i++) {
|
|
153
188
|
try {
|
|
154
189
|
await exec(`docker push ${shellSafe(tag)}`);
|
|
@@ -166,6 +201,8 @@ function makeHostOps(env) {
|
|
|
166
201
|
},
|
|
167
202
|
};
|
|
168
203
|
}
|
|
204
|
+
/** Shell-quote a single argument for the `exec` string forms above (df/git/docker take server-fixed paths/refs, but
|
|
205
|
+
* a defensive quote keeps a surprising RECIPE_DIR/ref from breaking the command — argv to build.sh is a vector). */
|
|
169
206
|
function shellSafe(s) {
|
|
170
207
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
171
208
|
}
|
|
@@ -184,7 +221,7 @@ async function main() {
|
|
|
184
221
|
recipeRef: env.recipeRef,
|
|
185
222
|
heartbeatMs: env.heartbeatMs,
|
|
186
223
|
minFreeGb: env.minFreeGb,
|
|
187
|
-
});
|
|
224
|
+
}); // NB: the token + per-bake ingest secret are NEVER logged (§P2.4e)
|
|
188
225
|
const runner = new BakeRunner({
|
|
189
226
|
api: makeApiClient(env),
|
|
190
227
|
spawner: makeSpawner(),
|
|
@@ -205,9 +242,11 @@ async function main() {
|
|
|
205
242
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
206
243
|
await runner.loop();
|
|
207
244
|
}
|
|
245
|
+
// Only run when invoked directly (not when imported by a test).
|
|
208
246
|
const invokedDirectly = process.argv[1] !== undefined && /bake-runner[/\\]main\.(ts|js)$/.test(process.argv[1]);
|
|
209
247
|
if (invokedDirectly) {
|
|
210
248
|
main().catch((err) => {
|
|
249
|
+
// eslint-disable-next-line no-console
|
|
211
250
|
console.error(JSON.stringify({ ts: new Date().toISOString(), level: "error", msg: "bake_runner_fatal", err: String(err) }));
|
|
212
251
|
process.exit(1);
|
|
213
252
|
});
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bake-runner PURE protocol layer (IMAGE-API-DESIGN.md §P2.12) — the line→frame mapping + the small policy
|
|
3
|
+
* helpers, factored free of I/O so they are unit-testable WITHOUT the privileged build-host build host (the runner
|
|
4
|
+
* itself can only be exercised live; this is the part a unit test pins).
|
|
5
|
+
*
|
|
6
|
+
* The runner spawns `build.sh <vetted argv> --json` and forwards each STDOUT line to image-api's ingest. build.sh
|
|
7
|
+
* emits a small set of JSON object lines (`resolved | state | step | image | manifest | done`) on stdout and tees
|
|
8
|
+
* the raw `docker build` output to `$BUILD_DIR/build.log` (NOT onto stdout). On failure it does `tail -40 build.log
|
|
9
|
+
* >&2` — that is STDERR, the failure tail, NOT JSON; we parse JSON ONLY from stdout (a naive JSON.parse over both
|
|
10
|
+
* crashes the parser — §P2.5). Each forwarded frame carries an incrementing `lineOrd` (at-least-once ingest dedupe;
|
|
11
|
+
* image-api's `uq_line` ignores a re-sent ordinal — §P2.10).
|
|
12
|
+
*/
|
|
13
|
+
/** One build.sh stdout line, already JSON-parsed into an object (or a marker for an unparseable line). */
|
|
1
14
|
export type BuildShEvent = {
|
|
2
15
|
event: "resolved";
|
|
3
16
|
[k: string]: unknown;
|
|
@@ -29,21 +42,65 @@ export type BuildShEvent = {
|
|
|
29
42
|
event: string;
|
|
30
43
|
[k: string]: unknown;
|
|
31
44
|
};
|
|
45
|
+
/** The frame the runner POSTs to `…/ingest` (image-api maps it to an event-log row). `lineOrd` is the dedupe key. */
|
|
32
46
|
export interface IngestFrame {
|
|
47
|
+
/** build.sh's `event` (or "log" for an unparseable/raw line). */
|
|
33
48
|
event: string;
|
|
49
|
+
/** Monotonic per-bake ordinal — image-api dedupes on (bakeId, lineOrd) so an at-least-once re-send is a no-op. */
|
|
34
50
|
lineOrd: number;
|
|
51
|
+
/** The remaining build.sh fields (event already lifted out), passed through verbatim. */
|
|
35
52
|
[k: string]: unknown;
|
|
36
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Parse ONE raw stdout line into a build.sh event, or null when it is not a JSON object line (a blank line, a
|
|
56
|
+
* stray non-JSON print). build.sh's protocol is strictly one JSON OBJECT per stdout line (`jq -nc`), so anything
|
|
57
|
+
* that is not a `{…}` object is treated as non-protocol noise the caller forwards as a `log` frame (NOT crashed on).
|
|
58
|
+
*/
|
|
37
59
|
export declare function parseBuildShLine(raw: string): BuildShEvent | null;
|
|
60
|
+
/**
|
|
61
|
+
* Map ONE stdout line → the ingest frame the runner POSTs (the PURE core a unit test pins). A parseable protocol
|
|
62
|
+
* line is forwarded under its own `event`; an unparseable stdout line becomes an `info` log frame (build.sh's
|
|
63
|
+
* structured stdout is naturally quiet, so this is rare — usually a generator print). The raw `docker build` tail
|
|
64
|
+
* never reaches here: it is on stderr (the runner only feeds stdout through this) — §P2.5.
|
|
65
|
+
*/
|
|
38
66
|
export declare function lineToFrame(raw: string, lineOrd: number): IngestFrame;
|
|
67
|
+
/**
|
|
68
|
+
* Synthesize the `docker-build status:running elapsedSec` heartbeat step (§P2.2) — emitted every ~30s during the
|
|
69
|
+
* long, silent `docker build` (with logs off) so a resuming reader sees forward motion instead of a dead spinner.
|
|
70
|
+
* It is a synthesized frame, so it still carries a `lineOrd` (image-api dedupes the same way).
|
|
71
|
+
*/
|
|
39
72
|
export declare function dockerBuildTickFrame(lineOrd: number, elapsedSec: number): IngestFrame;
|
|
73
|
+
/**
|
|
74
|
+
* Synthesize the terminal `done` the runner sends when build.sh did NOT emit one (a hard cancel/timeout kill —
|
|
75
|
+
* build.sh has no SIGTERM trap, so a killed process never prints `done:CANCELLED`/`done:FAILED`; the runner MUST
|
|
76
|
+
* synthesize it — §P2.11). `cancelled` → `CANCELLED`; a timeout → `FAILED exitCode:124`.
|
|
77
|
+
*/
|
|
40
78
|
export declare function synthDoneFrame(lineOrd: number, kind: "cancelled" | "timeout", detail?: {
|
|
41
79
|
error?: string;
|
|
42
80
|
}): IngestFrame;
|
|
81
|
+
/**
|
|
82
|
+
* The runner-side terminal `done` for a build.sh CHILD that exited non-zero WITHOUT printing its own `done` (build.sh
|
|
83
|
+
* normally dies via `die()` which DOES print a `done:FAILED`, but a SIGKILL or a crash before the trap can leave the
|
|
84
|
+
* child exiting non-zero silently). The runner backstops with a FAILED done carrying the child exit code + the
|
|
85
|
+
* captured stderr failure tail (the `tail -40 build.log` build.sh prints on failure), redacted by image-api on store.
|
|
86
|
+
*/
|
|
43
87
|
export declare function exitDoneFrame(lineOrd: number, exitCode: number, stderrTail: string): IngestFrame;
|
|
88
|
+
/** Keep only the last `n` lines of a (possibly long) stderr tail — bounds the error string we forward. */
|
|
44
89
|
export declare function lastLines(text: string, n: number): string;
|
|
90
|
+
/**
|
|
91
|
+
* The profile-aware HARD DEADLINE (§P2.7/§P2.11) the runner enforces by killing the process group. flutter/full are
|
|
92
|
+
* the heavy profiles (~60min); everything else ~45min. A bands build ("custom") is treated as heavy if it includes
|
|
93
|
+
* a heavy band. The reaper backstops this; the runner is the primary enforcer (it owns the child).
|
|
94
|
+
*/
|
|
45
95
|
export declare function deadlineMsForProfile(profile: string, bands: string[] | null | undefined): number;
|
|
96
|
+
/** Parse `df -P -BG /data` (POSIX, 1G blocks) → free GiB on the build-host data fs. Returns null if unparseable
|
|
97
|
+
* (the caller then fails OPEN — a df parse miss must not block a bake; the reaper still guards a mid-build fill). */
|
|
46
98
|
export declare function parseDfFreeGb(dfOutput: string): number | null;
|
|
99
|
+
/** The path build.sh's `done`/`manifest` line points at (a build-host FS path). The done carries `manifest:<path>`, the
|
|
100
|
+
* manifest event carries `path` — the runner reads whichever is present off disk for the auto-register body. */
|
|
47
101
|
export declare function manifestPathFromLine(doneLine: Record<string, unknown>, lastManifestPath: string | null): string | null;
|
|
102
|
+
/** Pull the post-push runtime digest the auto-register should key on. build.sh rewrites DIGEST from RepoDigests[0]
|
|
103
|
+
* AFTER a push, so the `done`/`image` line's `digest` is already the post-push registry RepoDigest on a push bake and the
|
|
104
|
+
* LOCAL_ID on a no-push bake — the runner forwards it verbatim; this just normalizes the field name (§P2.10). */
|
|
48
105
|
export declare function digestFromDoneLine(doneLine: Record<string, unknown>, lastImageDigest: string | null): string | null;
|
|
49
106
|
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bake-runner PURE protocol layer (IMAGE-API-DESIGN.md §P2.12) — the line→frame mapping + the small policy
|
|
3
|
+
* helpers, factored free of I/O so they are unit-testable WITHOUT the privileged build-host build host (the runner
|
|
4
|
+
* itself can only be exercised live; this is the part a unit test pins).
|
|
5
|
+
*
|
|
6
|
+
* The runner spawns `build.sh <vetted argv> --json` and forwards each STDOUT line to image-api's ingest. build.sh
|
|
7
|
+
* emits a small set of JSON object lines (`resolved | state | step | image | manifest | done`) on stdout and tees
|
|
8
|
+
* the raw `docker build` output to `$BUILD_DIR/build.log` (NOT onto stdout). On failure it does `tail -40 build.log
|
|
9
|
+
* >&2` — that is STDERR, the failure tail, NOT JSON; we parse JSON ONLY from stdout (a naive JSON.parse over both
|
|
10
|
+
* crashes the parser — §P2.5). Each forwarded frame carries an incrementing `lineOrd` (at-least-once ingest dedupe;
|
|
11
|
+
* image-api's `uq_line` ignores a re-sent ordinal — §P2.10).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Parse ONE raw stdout line into a build.sh event, or null when it is not a JSON object line (a blank line, a
|
|
15
|
+
* stray non-JSON print). build.sh's protocol is strictly one JSON OBJECT per stdout line (`jq -nc`), so anything
|
|
16
|
+
* that is not a `{…}` object is treated as non-protocol noise the caller forwards as a `log` frame (NOT crashed on).
|
|
17
|
+
*/
|
|
1
18
|
export function parseBuildShLine(raw) {
|
|
2
19
|
const s = raw.trim();
|
|
3
20
|
if (s.length === 0 || s[0] !== "{")
|
|
4
|
-
return null;
|
|
21
|
+
return null; // fast reject: protocol lines are JSON objects
|
|
5
22
|
let parsed;
|
|
6
23
|
try {
|
|
7
24
|
parsed = JSON.parse(s);
|
|
@@ -13,26 +30,49 @@ export function parseBuildShLine(raw) {
|
|
|
13
30
|
return null;
|
|
14
31
|
const obj = parsed;
|
|
15
32
|
if (typeof obj.event !== "string")
|
|
16
|
-
return null;
|
|
33
|
+
return null; // not a protocol line → caller forwards as a log frame
|
|
17
34
|
return obj;
|
|
18
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Map ONE stdout line → the ingest frame the runner POSTs (the PURE core a unit test pins). A parseable protocol
|
|
38
|
+
* line is forwarded under its own `event`; an unparseable stdout line becomes an `info` log frame (build.sh's
|
|
39
|
+
* structured stdout is naturally quiet, so this is rare — usually a generator print). The raw `docker build` tail
|
|
40
|
+
* never reaches here: it is on stderr (the runner only feeds stdout through this) — §P2.5.
|
|
41
|
+
*/
|
|
19
42
|
export function lineToFrame(raw, lineOrd) {
|
|
20
43
|
const ev = parseBuildShLine(raw);
|
|
21
44
|
if (ev === null) {
|
|
45
|
+
// Non-protocol stdout noise → an info log frame (NOT crashed on, NOT a raw docker tail — that is stderr).
|
|
22
46
|
return { event: "log", lineOrd, level: "info", line: raw.replace(/\s+$/, "") };
|
|
23
47
|
}
|
|
24
48
|
const { event, ...rest } = ev;
|
|
25
49
|
return { event, lineOrd, ...rest };
|
|
26
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Synthesize the `docker-build status:running elapsedSec` heartbeat step (§P2.2) — emitted every ~30s during the
|
|
53
|
+
* long, silent `docker build` (with logs off) so a resuming reader sees forward motion instead of a dead spinner.
|
|
54
|
+
* It is a synthesized frame, so it still carries a `lineOrd` (image-api dedupes the same way).
|
|
55
|
+
*/
|
|
27
56
|
export function dockerBuildTickFrame(lineOrd, elapsedSec) {
|
|
28
57
|
return { event: "step", lineOrd, step: "docker-build", status: "running", elapsedSec };
|
|
29
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Synthesize the terminal `done` the runner sends when build.sh did NOT emit one (a hard cancel/timeout kill —
|
|
61
|
+
* build.sh has no SIGTERM trap, so a killed process never prints `done:CANCELLED`/`done:FAILED`; the runner MUST
|
|
62
|
+
* synthesize it — §P2.11). `cancelled` → `CANCELLED`; a timeout → `FAILED exitCode:124`.
|
|
63
|
+
*/
|
|
30
64
|
export function synthDoneFrame(lineOrd, kind, detail) {
|
|
31
65
|
if (kind === "cancelled") {
|
|
32
66
|
return { event: "done", lineOrd, status: "CANCELLED", exitCode: null, error: detail?.error ?? "cancelled by operator" };
|
|
33
67
|
}
|
|
34
68
|
return { event: "done", lineOrd, status: "FAILED", exitCode: 124, error: detail?.error ?? "bake hard-deadline exceeded (killed)" };
|
|
35
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* The runner-side terminal `done` for a build.sh CHILD that exited non-zero WITHOUT printing its own `done` (build.sh
|
|
72
|
+
* normally dies via `die()` which DOES print a `done:FAILED`, but a SIGKILL or a crash before the trap can leave the
|
|
73
|
+
* child exiting non-zero silently). The runner backstops with a FAILED done carrying the child exit code + the
|
|
74
|
+
* captured stderr failure tail (the `tail -40 build.log` build.sh prints on failure), redacted by image-api on store.
|
|
75
|
+
*/
|
|
36
76
|
export function exitDoneFrame(lineOrd, exitCode, stderrTail) {
|
|
37
77
|
const tail = stderrTail.trim();
|
|
38
78
|
return {
|
|
@@ -43,10 +83,16 @@ export function exitDoneFrame(lineOrd, exitCode, stderrTail) {
|
|
|
43
83
|
error: tail.length > 0 ? `build.sh exited ${exitCode}: ${lastLines(tail, 12)}` : `build.sh exited ${exitCode}`,
|
|
44
84
|
};
|
|
45
85
|
}
|
|
86
|
+
/** Keep only the last `n` lines of a (possibly long) stderr tail — bounds the error string we forward. */
|
|
46
87
|
export function lastLines(text, n) {
|
|
47
88
|
const lines = text.split(/\r?\n/).filter((l) => l.length > 0);
|
|
48
89
|
return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
49
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The profile-aware HARD DEADLINE (§P2.7/§P2.11) the runner enforces by killing the process group. flutter/full are
|
|
93
|
+
* the heavy profiles (~60min); everything else ~45min. A bands build ("custom") is treated as heavy if it includes
|
|
94
|
+
* a heavy band. The reaper backstops this; the runner is the primary enforcer (it owns the child).
|
|
95
|
+
*/
|
|
50
96
|
export function deadlineMsForProfile(profile, bands) {
|
|
51
97
|
const HEAVY_MS = 60 * 60 * 1000;
|
|
52
98
|
const NORMAL_MS = 45 * 60 * 1000;
|
|
@@ -58,19 +104,25 @@ export function deadlineMsForProfile(profile, bands) {
|
|
|
58
104
|
return HEAVY_MS;
|
|
59
105
|
return NORMAL_MS;
|
|
60
106
|
}
|
|
107
|
+
/** Parse `df -P -BG /data` (POSIX, 1G blocks) → free GiB on the build-host data fs. Returns null if unparseable
|
|
108
|
+
* (the caller then fails OPEN — a df parse miss must not block a bake; the reaper still guards a mid-build fill). */
|
|
61
109
|
export function parseDfFreeGb(dfOutput) {
|
|
110
|
+
// `df -P -BG <path>` prints a header line then one data line; the "Available" column is field index 3 (0-based),
|
|
111
|
+
// a value like "812G". POSIX `-P` guarantees one physical line per fs (no wrapping), so field positions are fixed.
|
|
62
112
|
const lines = dfOutput.trim().split(/\r?\n/);
|
|
63
113
|
if (lines.length < 2)
|
|
64
114
|
return null;
|
|
65
115
|
const data = lines[lines.length - 1].trim().split(/\s+/);
|
|
66
116
|
if (data.length < 4)
|
|
67
117
|
return null;
|
|
68
|
-
const avail = data[3];
|
|
118
|
+
const avail = data[3]; // Filesystem 1G-blocks Used Available …
|
|
69
119
|
const m = /^(\d+)G?$/.exec(avail);
|
|
70
120
|
if (!m)
|
|
71
121
|
return null;
|
|
72
122
|
return Number(m[1]);
|
|
73
123
|
}
|
|
124
|
+
/** The path build.sh's `done`/`manifest` line points at (a build-host FS path). The done carries `manifest:<path>`, the
|
|
125
|
+
* manifest event carries `path` — the runner reads whichever is present off disk for the auto-register body. */
|
|
74
126
|
export function manifestPathFromLine(doneLine, lastManifestPath) {
|
|
75
127
|
if (typeof doneLine.manifest === "string" && doneLine.manifest.length > 0)
|
|
76
128
|
return doneLine.manifest;
|
|
@@ -78,6 +130,9 @@ export function manifestPathFromLine(doneLine, lastManifestPath) {
|
|
|
78
130
|
return doneLine.path;
|
|
79
131
|
return lastManifestPath;
|
|
80
132
|
}
|
|
133
|
+
/** Pull the post-push runtime digest the auto-register should key on. build.sh rewrites DIGEST from RepoDigests[0]
|
|
134
|
+
* AFTER a push, so the `done`/`image` line's `digest` is already the post-push registry RepoDigest on a push bake and the
|
|
135
|
+
* LOCAL_ID on a no-push bake — the runner forwards it verbatim; this just normalizes the field name (§P2.10). */
|
|
81
136
|
export function digestFromDoneLine(doneLine, lastImageDigest) {
|
|
82
137
|
if (typeof doneLine.digest === "string" && doneLine.digest.length > 0)
|
|
83
138
|
return doneLine.digest;
|