@wardby/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +131 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +282 -0
- package/bin/wardby.js +3 -0
- package/deploy/local/docker-compose.yml +19 -0
- package/dist/claude-coding-worker/driver.d.ts +70 -0
- package/dist/claude-coding-worker/driver.js +128 -0
- package/dist/claude-coding-worker/main.d.ts +2 -0
- package/dist/claude-coding-worker/main.js +41 -0
- package/dist/claude-coding-worker/sdk.d.ts +4 -0
- package/dist/claude-coding-worker/sdk.js +43 -0
- package/dist/claude-coding-worker/tool-relay.d.ts +1 -0
- package/dist/claude-coding-worker/tool-relay.js +9 -0
- package/dist/cli-help.d.ts +1 -0
- package/dist/cli-help.js +20 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +629 -0
- package/dist/coding/observability.d.ts +59 -0
- package/dist/coding/observability.js +77 -0
- package/dist/coding/profile.d.ts +88 -0
- package/dist/coding/profile.js +137 -0
- package/dist/coding/protocol.d.ts +345 -0
- package/dist/coding/protocol.js +445 -0
- package/dist/coding/provider.d.ts +4 -0
- package/dist/coding/provider.js +18 -0
- package/dist/coding-proxy/main.d.ts +1 -0
- package/dist/coding-proxy/main.js +51 -0
- package/dist/coding-worker/artifact.d.ts +16 -0
- package/dist/coding-worker/artifact.js +37 -0
- package/dist/coding-worker/driver.d.ts +46 -0
- package/dist/coding-worker/driver.js +158 -0
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +23 -0
- package/dist/coding-worker/isolation-probe.d.ts +1 -0
- package/dist/coding-worker/isolation-probe.js +146 -0
- package/dist/coding-worker/keeper.d.ts +4 -0
- package/dist/coding-worker/keeper.js +29 -0
- package/dist/coding-worker/main.d.ts +2 -0
- package/dist/coding-worker/main.js +43 -0
- package/dist/coding-worker/sdk.d.ts +26 -0
- package/dist/coding-worker/sdk.js +28 -0
- package/dist/coding-worker/types.d.ts +64 -0
- package/dist/coding-worker/types.js +1 -0
- package/dist/config/providers.d.ts +158 -0
- package/dist/config/providers.js +162 -0
- package/dist/core/budget-groups.d.ts +72 -0
- package/dist/core/budget-groups.js +149 -0
- package/dist/core/budget.d.ts +63 -0
- package/dist/core/budget.js +69 -0
- package/dist/core/coding-queue.d.ts +32 -0
- package/dist/core/coding-queue.js +54 -0
- package/dist/core/cron.d.ts +31 -0
- package/dist/core/cron.js +43 -0
- package/dist/core/datastores.d.ts +41 -0
- package/dist/core/datastores.js +105 -0
- package/dist/core/db.d.ts +2 -0
- package/dist/core/db.js +2 -0
- package/dist/core/dispatch.d.ts +81 -0
- package/dist/core/dispatch.js +184 -0
- package/dist/core/engine-native.d.ts +29 -0
- package/dist/core/engine-native.js +259 -0
- package/dist/core/http-runtime.d.ts +7 -0
- package/dist/core/http-runtime.js +101 -0
- package/dist/core/lease.d.ts +13 -0
- package/dist/core/lease.js +22 -0
- package/dist/core/logger.d.ts +13 -0
- package/dist/core/logger.js +45 -0
- package/dist/core/memory-tools.d.ts +11 -0
- package/dist/core/memory-tools.js +122 -0
- package/dist/core/reconciler.d.ts +45 -0
- package/dist/core/reconciler.js +152 -0
- package/dist/core/runner.d.ts +87 -0
- package/dist/core/runner.js +529 -0
- package/dist/core/scheduler.d.ts +47 -0
- package/dist/core/scheduler.js +121 -0
- package/dist/core/secrets.d.ts +46 -0
- package/dist/core/secrets.js +100 -0
- package/dist/core/subagent-memory-tools.d.ts +18 -0
- package/dist/core/subagent-memory-tools.js +121 -0
- package/dist/core/timing.d.ts +7 -0
- package/dist/core/timing.js +7 -0
- package/dist/core/webhooks.d.ts +23 -0
- package/dist/core/webhooks.js +113 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.js +13 -0
- package/dist/import/budgets.d.ts +16 -0
- package/dist/import/budgets.js +21 -0
- package/dist/import/bundle.d.ts +16 -0
- package/dist/import/bundle.js +61 -0
- package/dist/import/cli-args.d.ts +2 -0
- package/dist/import/cli-args.js +44 -0
- package/dist/import/create.d.ts +34 -0
- package/dist/import/create.js +363 -0
- package/dist/import/index.d.ts +25 -0
- package/dist/import/index.js +130 -0
- package/dist/import/model-gate.d.ts +1 -0
- package/dist/import/model-gate.js +3 -0
- package/dist/import/neutral-schema.d.ts +369 -0
- package/dist/import/neutral-schema.js +109 -0
- package/dist/import/preflight.d.ts +41 -0
- package/dist/import/preflight.js +71 -0
- package/dist/import/report.d.ts +5 -0
- package/dist/import/report.js +98 -0
- package/dist/import/tool-scan.d.ts +8 -0
- package/dist/import/tool-scan.js +20 -0
- package/dist/mcp/auth/ownership.d.ts +113 -0
- package/dist/mcp/auth/ownership.js +87 -0
- package/dist/mcp/auth/principal.d.ts +9 -0
- package/dist/mcp/auth/principal.js +9 -0
- package/dist/mcp/auth/resource-server.d.ts +47 -0
- package/dist/mcp/auth/resource-server.js +82 -0
- package/dist/mcp/auth/self-hosted/browser.d.ts +3 -0
- package/dist/mcp/auth/self-hosted/browser.js +273 -0
- package/dist/mcp/auth/self-hosted/cli.d.ts +2 -0
- package/dist/mcp/auth/self-hosted/cli.js +39 -0
- package/dist/mcp/auth/self-hosted/credentials.d.ts +58 -0
- package/dist/mcp/auth/self-hosted/credentials.js +106 -0
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +13 -0
- package/dist/mcp/auth/self-hosted/rate-limit.js +20 -0
- package/dist/mcp/auth/self-hosted/session.d.ts +36 -0
- package/dist/mcp/auth/self-hosted/session.js +110 -0
- package/dist/mcp/capabilities.d.ts +11 -0
- package/dist/mcp/capabilities.js +10 -0
- package/dist/mcp/context.d.ts +46 -0
- package/dist/mcp/context.js +1 -0
- package/dist/mcp/errors.d.ts +13 -0
- package/dist/mcp/errors.js +22 -0
- package/dist/mcp/index.d.ts +32 -0
- package/dist/mcp/index.js +187 -0
- package/dist/mcp/jsonrpc.d.ts +8 -0
- package/dist/mcp/jsonrpc.js +1 -0
- package/dist/mcp/server.d.ts +107 -0
- package/dist/mcp/server.js +203 -0
- package/dist/mcp/tasks/manager.d.ts +63 -0
- package/dist/mcp/tasks/manager.js +103 -0
- package/dist/mcp/tools/agents.d.ts +2 -0
- package/dist/mcp/tools/agents.js +375 -0
- package/dist/mcp/tools/budget-groups.d.ts +2 -0
- package/dist/mcp/tools/budget-groups.js +136 -0
- package/dist/mcp/tools/datastore.d.ts +2 -0
- package/dist/mcp/tools/datastore.js +217 -0
- package/dist/mcp/tools/memory.d.ts +3 -0
- package/dist/mcp/tools/memory.js +60 -0
- package/dist/mcp/tools/models.d.ts +8 -0
- package/dist/mcp/tools/models.js +15 -0
- package/dist/mcp/tools/runs.d.ts +2 -0
- package/dist/mcp/tools/runs.js +61 -0
- package/dist/mcp/tools/scheduling.d.ts +2 -0
- package/dist/mcp/tools/scheduling.js +48 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -0
- package/dist/mcp/tools/secret-elicitation-form.js +93 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +5 -0
- package/dist/mcp/tools/secret-elicitation-server.js +57 -0
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -0
- package/dist/mcp/tools/secret-elicitation.js +52 -0
- package/dist/mcp/tools/secrets.d.ts +13 -0
- package/dist/mcp/tools/secrets.js +124 -0
- package/dist/mcp/tools/subagents.d.ts +2 -0
- package/dist/mcp/tools/subagents.js +133 -0
- package/dist/mcp/tools/text-result.d.ts +6 -0
- package/dist/mcp/tools/text-result.js +3 -0
- package/dist/mcp/tools/tools.d.ts +2 -0
- package/dist/mcp/tools/tools.js +211 -0
- package/dist/mcp/tools/trigger.d.ts +2 -0
- package/dist/mcp/tools/trigger.js +120 -0
- package/dist/mcp/tools/webhooks.d.ts +2 -0
- package/dist/mcp/tools/webhooks.js +34 -0
- package/dist/mcp/transport/http-limits.d.ts +17 -0
- package/dist/mcp/transport/http-limits.js +92 -0
- package/dist/mcp/transport/stdio.d.ts +23 -0
- package/dist/mcp/transport/stdio.js +6 -0
- package/dist/mcp/transport/streamable-http.d.ts +34 -0
- package/dist/mcp/transport/streamable-http.js +158 -0
- package/dist/mcp/unattended-schedules.d.ts +15 -0
- package/dist/mcp/unattended-schedules.js +21 -0
- package/dist/mcp/webhooks/ingress.d.ts +17 -0
- package/dist/mcp/webhooks/ingress.js +33 -0
- package/dist/observability/config.d.ts +6 -0
- package/dist/observability/config.js +20 -0
- package/dist/observability/metrics-server.d.ts +11 -0
- package/dist/observability/metrics-server.js +47 -0
- package/dist/observability/metrics.d.ts +36 -0
- package/dist/observability/metrics.js +158 -0
- package/dist/providers/auth/authorization-server.d.ts +41 -0
- package/dist/providers/auth/authorization-server.js +1 -0
- package/dist/providers/auth/delegating.d.ts +44 -0
- package/dist/providers/auth/delegating.js +105 -0
- package/dist/providers/auth/index.d.ts +11 -0
- package/dist/providers/auth/index.js +20 -0
- package/dist/providers/auth/release-gate.d.ts +1 -0
- package/dist/providers/auth/release-gate.js +4 -0
- package/dist/providers/auth/self-hosted.d.ts +78 -0
- package/dist/providers/auth/self-hosted.js +377 -0
- package/dist/providers/auth/subject.d.ts +1 -0
- package/dist/providers/auth/subject.js +6 -0
- package/dist/providers/auth/types.d.ts +63 -0
- package/dist/providers/auth/types.js +22 -0
- package/dist/providers/coding-proxy/deny-port.d.ts +5 -0
- package/dist/providers/coding-proxy/deny-port.js +45 -0
- package/dist/providers/coding-proxy/environment-credentials.d.ts +7 -0
- package/dist/providers/coding-proxy/environment-credentials.js +16 -0
- package/dist/providers/coding-proxy/index.d.ts +7 -0
- package/dist/providers/coding-proxy/index.js +7 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/memory-ledger.js +103 -0
- package/dist/providers/coding-proxy/metering.d.ts +20 -0
- package/dist/providers/coding-proxy/metering.js +161 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +213 -0
- package/dist/providers/coding-proxy/proxy.d.ts +75 -0
- package/dist/providers/coding-proxy/proxy.js +721 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -0
- package/dist/providers/coding-proxy/runtime.js +39 -0
- package/dist/providers/coding-proxy/secure-fetch.d.ts +9 -0
- package/dist/providers/coding-proxy/secure-fetch.js +72 -0
- package/dist/providers/coding-proxy/server.d.ts +17 -0
- package/dist/providers/coding-proxy/server.js +139 -0
- package/dist/providers/coding-proxy/types.d.ts +99 -0
- package/dist/providers/coding-proxy/types.js +1 -0
- package/dist/providers/datastore/index.d.ts +2 -0
- package/dist/providers/datastore/index.js +2 -0
- package/dist/providers/datastore/postgres.d.ts +20 -0
- package/dist/providers/datastore/postgres.js +130 -0
- package/dist/providers/datastore/scoped.d.ts +10 -0
- package/dist/providers/datastore/scoped.js +46 -0
- package/dist/providers/datastore/types.d.ts +31 -0
- package/dist/providers/datastore/types.js +7 -0
- package/dist/providers/email/types.d.ts +46 -0
- package/dist/providers/email/types.js +8 -0
- package/dist/providers/engine/index.d.ts +1 -0
- package/dist/providers/engine/index.js +1 -0
- package/dist/providers/engine/types.d.ts +67 -0
- package/dist/providers/engine/types.js +16 -0
- package/dist/providers/executor/build.d.ts +6 -0
- package/dist/providers/executor/build.js +15 -0
- package/dist/providers/executor/composition.d.ts +14 -0
- package/dist/providers/executor/composition.js +123 -0
- package/dist/providers/executor/container.d.ts +184 -0
- package/dist/providers/executor/container.js +936 -0
- package/dist/providers/executor/dbos-status.d.ts +50 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +53 -0
- package/dist/providers/executor/dbos.js +269 -0
- package/dist/providers/executor/in-process.d.ts +18 -0
- package/dist/providers/executor/in-process.js +38 -0
- package/dist/providers/executor/index.d.ts +8 -0
- package/dist/providers/executor/index.js +8 -0
- package/dist/providers/executor/routing.d.ts +48 -0
- package/dist/providers/executor/routing.js +70 -0
- package/dist/providers/executor/types.d.ts +55 -0
- package/dist/providers/executor/types.js +1 -0
- package/dist/providers/index.d.ts +43 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/jobs/docker-isolation.d.ts +166 -0
- package/dist/providers/jobs/docker-isolation.js +724 -0
- package/dist/providers/jobs/docker.d.ts +127 -0
- package/dist/providers/jobs/docker.js +1027 -0
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +98 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +142 -0
- package/dist/providers/jobs/fake.d.ts +23 -0
- package/dist/providers/jobs/fake.js +132 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +60 -0
- package/dist/providers/jobs/kubernetes-api.js +20 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +36 -0
- package/dist/providers/jobs/kubernetes-client.js +171 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +49 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +141 -0
- package/dist/providers/jobs/kubernetes-isolation.d.ts +147 -0
- package/dist/providers/jobs/kubernetes-isolation.js +607 -0
- package/dist/providers/jobs/kubernetes-platform.d.ts +177 -0
- package/dist/providers/jobs/kubernetes-platform.js +280 -0
- package/dist/providers/jobs/kubernetes-preflight.d.ts +59 -0
- package/dist/providers/jobs/kubernetes-preflight.js +339 -0
- package/dist/providers/jobs/kubernetes-witness.d.ts +10 -0
- package/dist/providers/jobs/kubernetes-witness.js +193 -0
- package/dist/providers/jobs/kubernetes.d.ts +127 -0
- package/dist/providers/jobs/kubernetes.js +874 -0
- package/dist/providers/jobs/safe-extract.d.ts +9 -0
- package/dist/providers/jobs/safe-extract.js +325 -0
- package/dist/providers/jobs/types.d.ts +78 -0
- package/dist/providers/jobs/types.js +6 -0
- package/dist/providers/jobs/workspace-swap.d.ts +2 -0
- package/dist/providers/jobs/workspace-swap.js +47 -0
- package/dist/providers/llm/anthropic.d.ts +13 -0
- package/dist/providers/llm/anthropic.js +21 -0
- package/dist/providers/llm/bedrock.d.ts +19 -0
- package/dist/providers/llm/bedrock.js +33 -0
- package/dist/providers/llm/claude-messages.d.ts +106 -0
- package/dist/providers/llm/claude-messages.js +157 -0
- package/dist/providers/llm/claude-provider.d.ts +40 -0
- package/dist/providers/llm/claude-provider.js +31 -0
- package/dist/providers/llm/index.d.ts +10 -0
- package/dist/providers/llm/index.js +10 -0
- package/dist/providers/llm/openai.d.ts +23 -0
- package/dist/providers/llm/openai.js +156 -0
- package/dist/providers/llm/pricing-anthropic.d.ts +4 -0
- package/dist/providers/llm/pricing-anthropic.js +30 -0
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +12 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +37 -0
- package/dist/providers/llm/pricing-core.d.ts +26 -0
- package/dist/providers/llm/pricing-core.js +17 -0
- package/dist/providers/llm/pricing.d.ts +30 -0
- package/dist/providers/llm/pricing.js +74 -0
- package/dist/providers/llm/registration.d.ts +8 -0
- package/dist/providers/llm/registration.js +39 -0
- package/dist/providers/llm/routing.d.ts +25 -0
- package/dist/providers/llm/routing.js +32 -0
- package/dist/providers/llm/types.d.ts +85 -0
- package/dist/providers/llm/types.js +12 -0
- package/dist/providers/memory/index.d.ts +2 -0
- package/dist/providers/memory/index.js +2 -0
- package/dist/providers/memory/postgres.d.ts +20 -0
- package/dist/providers/memory/postgres.js +72 -0
- package/dist/providers/memory/types.d.ts +32 -0
- package/dist/providers/memory/types.js +17 -0
- package/dist/providers/secrets/app-key.d.ts +8 -0
- package/dist/providers/secrets/app-key.js +50 -0
- package/dist/providers/secrets/index.d.ts +10 -0
- package/dist/providers/secrets/index.js +13 -0
- package/dist/providers/secrets/transfer-envelope.d.ts +12 -0
- package/dist/providers/secrets/transfer-envelope.js +35 -0
- package/dist/providers/secrets/types.d.ts +18 -0
- package/dist/providers/secrets/types.js +12 -0
- package/dist/providers/storage/types.d.ts +18 -0
- package/dist/providers/storage/types.js +7 -0
- package/dist/providers/vcs/git.d.ts +105 -0
- package/dist/providers/vcs/git.js +710 -0
- package/dist/providers/vcs/github.d.ts +105 -0
- package/dist/providers/vcs/github.js +394 -0
- package/dist/providers/vcs/index.d.ts +6 -0
- package/dist/providers/vcs/index.js +26 -0
- package/dist/providers/vcs/types.d.ts +118 -0
- package/dist/providers/vcs/types.js +1 -0
- package/dist/sandbox/bounded-json.d.ts +2 -0
- package/dist/sandbox/bounded-json.js +36 -0
- package/dist/sandbox/bridge.d.ts +15 -0
- package/dist/sandbox/bridge.js +78 -0
- package/dist/sandbox/eval-core.d.ts +37 -0
- package/dist/sandbox/eval-core.js +159 -0
- package/dist/sandbox/fetch-policy.d.ts +24 -0
- package/dist/sandbox/fetch-policy.js +96 -0
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +5 -0
- package/dist/sandbox/generated/zod.bundle.js +1 -0
- package/dist/sandbox/host-functions.d.ts +29 -0
- package/dist/sandbox/host-functions.js +168 -0
- package/dist/sandbox/limits.d.ts +39 -0
- package/dist/sandbox/limits.js +39 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.js +6 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.js +11 -0
- package/dist/sandbox/parser-worker/pool.d.ts +13 -0
- package/dist/sandbox/parser-worker/pool.js +111 -0
- package/dist/sandbox/parser-worker/worker.d.ts +14 -0
- package/dist/sandbox/parser-worker/worker.js +63 -0
- package/dist/sandbox/pii-redaction.d.ts +1 -0
- package/dist/sandbox/pii-redaction.js +29 -0
- package/dist/sandbox/prelude.d.ts +26 -0
- package/dist/sandbox/prelude.js +408 -0
- package/dist/sandbox/quickjs-module.d.ts +13 -0
- package/dist/sandbox/quickjs-module.js +19 -0
- package/dist/sandbox/run-in-sandbox.d.ts +34 -0
- package/dist/sandbox/run-in-sandbox.js +53 -0
- package/dist/sandbox/safe-fetch.d.ts +26 -0
- package/dist/sandbox/safe-fetch.js +140 -0
- package/dist/sandbox/tool-capabilities.d.ts +66 -0
- package/dist/sandbox/tool-capabilities.js +103 -0
- package/dist/sandbox/zod-params.d.ts +22 -0
- package/dist/sandbox/zod-params.js +60 -0
- package/dist/serve.d.ts +14 -0
- package/dist/serve.js +54 -0
- package/dist/wardby-bin.d.ts +2 -0
- package/dist/wardby-bin.js +20 -0
- package/package.json +123 -0
- package/prisma/migrations/20260905000000_init/migration.sql +34 -0
- package/prisma/migrations/20260905010000_scheduler_durability/migration.sql +31 -0
- package/prisma/migrations/20260905020000_tools_multiturn/migration.sql +47 -0
- package/prisma/migrations/20260905030000_cache_tool_json_schema/migration.sql +13 -0
- package/prisma/migrations/20260906000000_mcp_phase4/migration.sql +115 -0
- package/prisma/migrations/20260906010000_run_final_text/migration.sql +7 -0
- package/prisma/migrations/20260906020000_run_turns/migration.sql +6 -0
- package/prisma/migrations/20260906030000_task_principal_id/migration.sql +5 -0
- package/prisma/migrations/20260906040000_secure_self_hosted_oauth/migration.sql +67 -0
- package/prisma/migrations/20260906050000_phase5_coding_agents/migration.sql +52 -0
- package/prisma/migrations/20260906060000_tool_capability_scoping/migration.sql +22 -0
- package/prisma/migrations/20260906070000_datastore_pii_flag/migration.sql +6 -0
- package/prisma/migrations/20260906080000_secret_elicitation_outcomes/migration.sql +13 -0
- package/prisma/migrations/20260907010000_coding_proxy_ledger/migration.sql +57 -0
- package/prisma/migrations/20260907020000_budget_groups/migration.sql +24 -0
- package/prisma/migrations/20260907030000_coding_webhook_task_override/migration.sql +4 -0
- package/prisma/migrations/20260908010000_run_execution_backend/migration.sql +2 -0
- package/prisma/migrations/20260908020000_coding_run_audit_metadata/migration.sql +7 -0
- package/prisma/migrations/20260910010000_agent_secret_bound_name/migration.sql +16 -0
- package/prisma/migrations/20260911010000_coding_worker_toolchains/migration.sql +5 -0
- package/prisma/migrations/20260912010000_coding_provider_contract/migration.sql +24 -0
- package/prisma/migrations/20260912020000_coding_proxy_protocol/migration.sql +10 -0
- package/prisma/migrations/20260912030000_agent_memory/migration.sql +18 -0
- package/prisma/migrations/20260912040000_named_shared_datastores/migration.sql +68 -0
- package/prisma/migrations/20260913000000_run_trigger_webhook/migration.sql +2 -0
- package/prisma/migrations/20260913010000_agent_sub_agent/migration.sql +32 -0
- package/prisma/migrations/20260913020000_run_task_override/migration.sql +5 -0
- package/prisma/migrations/20260913030000_coding_run_root/migration.sql +11 -0
- package/prisma/migrations/20260922010000_coding_run_queued_at/migration.sql +8 -0
- package/prisma/migrations/20260922020000_coding_workspace_disk/migration.sql +8 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +723 -0
- package/scripts/git-askpass.sh +11 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KubernetesApi over @kubernetes/client-node 2.x. A thin mapping: each seam
|
|
3
|
+
* method is one client call plus HTTP-status translation into the seam's
|
|
4
|
+
* error classes. Proven against a real cluster by kubernetes.integration.test.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { ApiException, CoreV1Api, Exec, KubeConfig, NetworkingV1Api, VersionApi, } from "@kubernetes/client-node";
|
|
7
|
+
import { Writable } from "node:stream";
|
|
8
|
+
import { KubernetesAlreadyExistsError, KubernetesConflictError, KubernetesNotFoundError, } from "./kubernetes-api.js";
|
|
9
|
+
function statusCode(error) {
|
|
10
|
+
return error instanceof ApiException ? error.code : undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Undefined for a 404; rethrows anything else. */
|
|
13
|
+
async function readOrUndefined(read) {
|
|
14
|
+
try {
|
|
15
|
+
return await read();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (statusCode(error) === 404)
|
|
19
|
+
return undefined;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Swallows a 404 so deletes are idempotent. */
|
|
24
|
+
async function deleteIgnoringMissing(remove) {
|
|
25
|
+
try {
|
|
26
|
+
await remove();
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (statusCode(error) === 404)
|
|
30
|
+
return;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function create(what, write) {
|
|
35
|
+
try {
|
|
36
|
+
return await write();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const code = statusCode(error);
|
|
40
|
+
if (code === 409)
|
|
41
|
+
throw new KubernetesAlreadyExistsError(what);
|
|
42
|
+
if (code === 404)
|
|
43
|
+
throw new KubernetesNotFoundError(what);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function replace(what, write) {
|
|
48
|
+
try {
|
|
49
|
+
return await write();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const code = statusCode(error);
|
|
53
|
+
if (code === 409)
|
|
54
|
+
throw new KubernetesConflictError(what);
|
|
55
|
+
if (code === 404)
|
|
56
|
+
throw new KubernetesNotFoundError(what);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Exit code from the exec status channel: "Success" is 0, else the ExitCode cause, else 1. */
|
|
61
|
+
function exitCodeFromStatus(status) {
|
|
62
|
+
if (status.status === "Success")
|
|
63
|
+
return 0;
|
|
64
|
+
const cause = status.details?.causes?.find((c) => c.reason === "ExitCode");
|
|
65
|
+
const parsed = cause?.message === undefined ? Number.NaN : Number.parseInt(cause.message, 10);
|
|
66
|
+
return Number.isInteger(parsed) ? parsed : 1;
|
|
67
|
+
}
|
|
68
|
+
export class ClientNodeKubernetesApi {
|
|
69
|
+
config;
|
|
70
|
+
core;
|
|
71
|
+
networking;
|
|
72
|
+
version;
|
|
73
|
+
constructor(options = {}) {
|
|
74
|
+
this.config = new KubeConfig();
|
|
75
|
+
this.config.loadFromDefault();
|
|
76
|
+
if (options.context)
|
|
77
|
+
this.config.setCurrentContext(options.context);
|
|
78
|
+
this.core = this.config.makeApiClient(CoreV1Api);
|
|
79
|
+
this.networking = this.config.makeApiClient(NetworkingV1Api);
|
|
80
|
+
this.version = this.config.makeApiClient(VersionApi);
|
|
81
|
+
}
|
|
82
|
+
createConfigMap(namespace, body) {
|
|
83
|
+
return create(`configmap/${namespace}/${body.metadata?.name}`, () => this.core.createNamespacedConfigMap({ namespace, body }));
|
|
84
|
+
}
|
|
85
|
+
readConfigMap(namespace, name) {
|
|
86
|
+
return readOrUndefined(() => this.core.readNamespacedConfigMap({ namespace, name }));
|
|
87
|
+
}
|
|
88
|
+
replaceConfigMap(namespace, name, body) {
|
|
89
|
+
return replace(`configmap/${namespace}/${name}`, () => this.core.replaceNamespacedConfigMap({ namespace, name, body }));
|
|
90
|
+
}
|
|
91
|
+
createSecret(namespace, body) {
|
|
92
|
+
return create(`secret/${namespace}/${body.metadata?.name}`, () => this.core.createNamespacedSecret({ namespace, body }));
|
|
93
|
+
}
|
|
94
|
+
deleteSecret(namespace, name) {
|
|
95
|
+
return deleteIgnoringMissing(() => this.core.deleteNamespacedSecret({ namespace, name }));
|
|
96
|
+
}
|
|
97
|
+
createPod(namespace, body) {
|
|
98
|
+
return create(`pod/${namespace}/${body.metadata?.name}`, () => this.core.createNamespacedPod({ namespace, body }));
|
|
99
|
+
}
|
|
100
|
+
dryRunCreatePod(namespace, body) {
|
|
101
|
+
return create(`pod/${namespace}/${body.metadata?.name}?dryRun`, () => this.core.createNamespacedPod({ namespace, body, dryRun: "All" }));
|
|
102
|
+
}
|
|
103
|
+
readPod(namespace, name) {
|
|
104
|
+
return readOrUndefined(() => this.core.readNamespacedPod({ namespace, name }));
|
|
105
|
+
}
|
|
106
|
+
deletePod(namespace, name, gracePeriodSeconds) {
|
|
107
|
+
return deleteIgnoringMissing(() => this.core.deleteNamespacedPod({ namespace, name, gracePeriodSeconds }));
|
|
108
|
+
}
|
|
109
|
+
createNetworkPolicy(namespace, body) {
|
|
110
|
+
return create(`networkpolicy/${namespace}/${body.metadata?.name}`, () => this.networking.createNamespacedNetworkPolicy({ namespace, body }));
|
|
111
|
+
}
|
|
112
|
+
readNetworkPolicy(namespace, name) {
|
|
113
|
+
return readOrUndefined(() => this.networking.readNamespacedNetworkPolicy({ namespace, name }));
|
|
114
|
+
}
|
|
115
|
+
deleteNetworkPolicy(namespace, name) {
|
|
116
|
+
return deleteIgnoringMissing(() => this.networking.deleteNamespacedNetworkPolicy({ namespace, name }));
|
|
117
|
+
}
|
|
118
|
+
readService(namespace, name) {
|
|
119
|
+
return readOrUndefined(() => this.core.readNamespacedService({ namespace, name }));
|
|
120
|
+
}
|
|
121
|
+
readEndpoints(namespace, name) {
|
|
122
|
+
return readOrUndefined(() => this.core.readNamespacedEndpoints({ namespace, name }));
|
|
123
|
+
}
|
|
124
|
+
async readApiServerVersion() {
|
|
125
|
+
return (await this.version.getCode()).gitVersion;
|
|
126
|
+
}
|
|
127
|
+
async readNamespace(name) {
|
|
128
|
+
return (await readOrUndefined(() => this.core.readNamespace({ name }))) !== undefined;
|
|
129
|
+
}
|
|
130
|
+
exec(namespace, pod, container, command, options) {
|
|
131
|
+
// stderr is requested but discarded so it never reaches wardby's logs.
|
|
132
|
+
const stderr = new Writable({
|
|
133
|
+
write(_chunk, _encoding, callback) {
|
|
134
|
+
callback();
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
let settled = false;
|
|
139
|
+
let socket;
|
|
140
|
+
const settle = (outcome) => {
|
|
141
|
+
if (settled)
|
|
142
|
+
return;
|
|
143
|
+
settled = true;
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
outcome();
|
|
146
|
+
};
|
|
147
|
+
const timer = setTimeout(() => {
|
|
148
|
+
settle(() => reject(new Error("kubernetes_exec_timeout")));
|
|
149
|
+
socket?.close();
|
|
150
|
+
}, options.timeoutMs);
|
|
151
|
+
new Exec(this.config)
|
|
152
|
+
.exec(namespace, pod, container, command, options.stdout ?? null, stderr, options.stdin ?? null, false, (status) => settle(() => resolve(exitCodeFromStatus(status))))
|
|
153
|
+
.then((ws) => {
|
|
154
|
+
socket = ws;
|
|
155
|
+
// Timed out while still connecting: close the late socket.
|
|
156
|
+
if (settled) {
|
|
157
|
+
socket.close();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// The status callback fires before the library closes the socket, so a close
|
|
161
|
+
// that arrives first means the command's exit status was never reported.
|
|
162
|
+
socket.addEventListener("close", () => settle(() => reject(new Error("kubernetes_exec_closed_without_status"))));
|
|
163
|
+
},
|
|
164
|
+
// A failed WebSocket handshake rejects with a ws ErrorEvent, not an Error.
|
|
165
|
+
(error) => settle(() => reject(error instanceof Error ? error : new Error("kubernetes_exec_connect_failed", { cause: error }))));
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
readLogTail(namespace, pod, container, tailLines, limitBytes) {
|
|
169
|
+
return this.core.readNamespacedPodLog({ namespace, name: pod, container, tailLines, limitBytes });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { KubernetesPlatform } from "./kubernetes-platform.js";
|
|
2
|
+
export interface PodMutation {
|
|
3
|
+
op: "add" | "replace" | "remove";
|
|
4
|
+
/** JSON Pointer (RFC 6901): "/" separated, with ~1 for "/" and ~0 for "~". */
|
|
5
|
+
path: string;
|
|
6
|
+
value?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface DryRunFixture {
|
|
9
|
+
capturedAt: string;
|
|
10
|
+
platform: KubernetesPlatform;
|
|
11
|
+
/** True until a real server-side dry run replaces this file. */
|
|
12
|
+
provisional: boolean;
|
|
13
|
+
source: string;
|
|
14
|
+
notes: string[];
|
|
15
|
+
mutations: PodMutation[];
|
|
16
|
+
}
|
|
17
|
+
export declare function loadDryRunFixture(): DryRunFixture;
|
|
18
|
+
/**
|
|
19
|
+
* Returns a copy of `document` with every mutation applied in order.
|
|
20
|
+
*
|
|
21
|
+
* `add` and `replace` assign at the pointer. `remove` deletes an object key,
|
|
22
|
+
* but SPLICES an array element: `delete array[2]` would leave a hole that
|
|
23
|
+
* serializes back as `null`, which would then be deep-compared against a real
|
|
24
|
+
* pod as a null list entry rather than as a removal. The differ only ever
|
|
25
|
+
* replaces arrays wholesale, so an array pointer can reach here only from a
|
|
26
|
+
* hand-written fixture entry — which is exactly when a silent `null` hole
|
|
27
|
+
* would be hardest to spot in review.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyMutations<T>(document: T, mutations: readonly PodMutation[]): T;
|
|
30
|
+
/**
|
|
31
|
+
* The mutations that turn `before` into `after`. Objects are walked key by key;
|
|
32
|
+
* anything else (including arrays) is replaced wholesale, which keeps a
|
|
33
|
+
* toleration list or a container list readable as one op.
|
|
34
|
+
*
|
|
35
|
+
* The equality check for that wholesale case goes through `canonical()` (recursive key
|
|
36
|
+
* sort), not a raw `JSON.stringify`: a captured pod's nested objects come back from a real
|
|
37
|
+
* API server with different key insertion order than `buildRunPod`'s own literals even when
|
|
38
|
+
* every value is identical, and a raw string compare would misread that as a genuine change —
|
|
39
|
+
* replacing an entire container or volume list with an opaque, unreviewable blob that a real
|
|
40
|
+
* platform rewrite could hide inside just as easily. `canonical()` makes the comparison see
|
|
41
|
+
* only actual value differences, while still emitting one "replace" op per array so the fixture
|
|
42
|
+
* stays readable.
|
|
43
|
+
*
|
|
44
|
+
* Sorted by code point, not `localeCompare`: the result is committed to a file
|
|
45
|
+
* and reviewed as a diff, so the order must not depend on the capturing
|
|
46
|
+
* machine's locale (an ICU collation folds case together and orders
|
|
47
|
+
* `/spec/Foo` and `/spec/foo` by tie-break rules that differ between hosts).
|
|
48
|
+
*/
|
|
49
|
+
export declare function diffMutations(before: unknown, after: unknown, prefix?: string): PodMutation[];
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The recorded Autopilot admission mutations, and the two pure functions that
|
|
3
|
+
* produce and replay them.
|
|
4
|
+
*
|
|
5
|
+
* The fixture stores a *mutation list*, not a pair of whole pods, for two
|
|
6
|
+
* reasons: a reviewer can read the entire set of differences the platform
|
|
7
|
+
* introduces in a few lines, and the test rebuilds the submitted pod from the
|
|
8
|
+
* live builder rather than replaying a stored one, so the fixture can never
|
|
9
|
+
* carry a stale *pod*.
|
|
10
|
+
*
|
|
11
|
+
* That is a narrower guarantee than it sounds, and the difference matters.
|
|
12
|
+
* Because the attestation test derives BOTH operands from the same builder, it
|
|
13
|
+
* is structurally blind to a weakening of the builder itself: measured on this
|
|
14
|
+
* branch, setting `automountServiceAccountToken: true` or halving the worker's
|
|
15
|
+
* CPU leaves the whole suite green. Three other sabotages — dropping
|
|
16
|
+
* `runtimeClassName`, dropping the pod `seccompProfile`, `hostPID: true` — do
|
|
17
|
+
* fail it, but only incidentally: those exact fields appear in the "rejects one
|
|
18
|
+
* more mutation" tamper list, so a builder that already emits the tampered
|
|
19
|
+
* value turns that tamper into a no-op. That is luck, not coverage, and it is
|
|
20
|
+
* not a property to rely on.
|
|
21
|
+
*
|
|
22
|
+
* What this file guards is that the *platform's* mutation set is still exactly
|
|
23
|
+
* the set the profile forgives. The builder's own fields are guarded by
|
|
24
|
+
* kubernetes-isolation.test.ts and kubernetes-platform.test.ts, and must stay
|
|
25
|
+
* that way.
|
|
26
|
+
*
|
|
27
|
+
* `provisional` is true while the file is written from documentation rather
|
|
28
|
+
* than captured from a cluster. The attestation suite names that state in its
|
|
29
|
+
* report, so a forgotten capture cannot pass unnoticed.
|
|
30
|
+
*
|
|
31
|
+
* This is development data reviewed in a diff, never a runtime authority: the
|
|
32
|
+
* launcher never asks a cluster what it is allowed to change.
|
|
33
|
+
*/
|
|
34
|
+
import { readFileSync } from "node:fs";
|
|
35
|
+
import { canonical } from "./kubernetes-isolation.js";
|
|
36
|
+
const FIXTURE_URL = new URL("./fixtures/gke-autopilot-dry-run.json", import.meta.url);
|
|
37
|
+
export function loadDryRunFixture() {
|
|
38
|
+
return JSON.parse(readFileSync(FIXTURE_URL, "utf8"));
|
|
39
|
+
}
|
|
40
|
+
function segments(path) {
|
|
41
|
+
if (path === "" || !path.startsWith("/"))
|
|
42
|
+
throw new Error(`dry_run_fixture_path: ${path}`);
|
|
43
|
+
return path
|
|
44
|
+
.slice(1)
|
|
45
|
+
.split("/")
|
|
46
|
+
.map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~"));
|
|
47
|
+
}
|
|
48
|
+
function container(root, path) {
|
|
49
|
+
let node = root;
|
|
50
|
+
for (const key of path) {
|
|
51
|
+
if (node === null || typeof node !== "object")
|
|
52
|
+
throw new Error(`dry_run_fixture_path: /${path.join("/")}`);
|
|
53
|
+
node = node[key];
|
|
54
|
+
}
|
|
55
|
+
if (node === null || typeof node !== "object")
|
|
56
|
+
throw new Error(`dry_run_fixture_path: /${path.join("/")}`);
|
|
57
|
+
return node;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns a copy of `document` with every mutation applied in order.
|
|
61
|
+
*
|
|
62
|
+
* `add` and `replace` assign at the pointer. `remove` deletes an object key,
|
|
63
|
+
* but SPLICES an array element: `delete array[2]` would leave a hole that
|
|
64
|
+
* serializes back as `null`, which would then be deep-compared against a real
|
|
65
|
+
* pod as a null list entry rather than as a removal. The differ only ever
|
|
66
|
+
* replaces arrays wholesale, so an array pointer can reach here only from a
|
|
67
|
+
* hand-written fixture entry — which is exactly when a silent `null` hole
|
|
68
|
+
* would be hardest to spot in review.
|
|
69
|
+
*/
|
|
70
|
+
export function applyMutations(document, mutations) {
|
|
71
|
+
const copy = structuredClone(document);
|
|
72
|
+
for (const mutation of mutations) {
|
|
73
|
+
const path = segments(mutation.path);
|
|
74
|
+
// `segments` always yields at least one element for a pointer starting with "/", so this
|
|
75
|
+
// never throws in practice; it narrows the type without a non-null assertion.
|
|
76
|
+
const key = path.pop();
|
|
77
|
+
if (key === undefined)
|
|
78
|
+
throw new Error(`dry_run_fixture_path: ${mutation.path}`);
|
|
79
|
+
const parent = container(copy, path);
|
|
80
|
+
if (Array.isArray(parent) && mutation.op === "remove") {
|
|
81
|
+
const index = /^(0|[1-9][0-9]*)$/.test(key) ? Number(key) : -1;
|
|
82
|
+
if (index < 0 || index >= parent.length)
|
|
83
|
+
throw new Error(`dry_run_fixture_path: ${mutation.path}`);
|
|
84
|
+
parent.splice(index, 1);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const bag = parent;
|
|
88
|
+
if (mutation.op === "remove")
|
|
89
|
+
delete bag[key];
|
|
90
|
+
else
|
|
91
|
+
bag[key] = structuredClone(mutation.value);
|
|
92
|
+
}
|
|
93
|
+
return copy;
|
|
94
|
+
}
|
|
95
|
+
function escape(key) {
|
|
96
|
+
return key.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The mutations that turn `before` into `after`. Objects are walked key by key;
|
|
100
|
+
* anything else (including arrays) is replaced wholesale, which keeps a
|
|
101
|
+
* toleration list or a container list readable as one op.
|
|
102
|
+
*
|
|
103
|
+
* The equality check for that wholesale case goes through `canonical()` (recursive key
|
|
104
|
+
* sort), not a raw `JSON.stringify`: a captured pod's nested objects come back from a real
|
|
105
|
+
* API server with different key insertion order than `buildRunPod`'s own literals even when
|
|
106
|
+
* every value is identical, and a raw string compare would misread that as a genuine change —
|
|
107
|
+
* replacing an entire container or volume list with an opaque, unreviewable blob that a real
|
|
108
|
+
* platform rewrite could hide inside just as easily. `canonical()` makes the comparison see
|
|
109
|
+
* only actual value differences, while still emitting one "replace" op per array so the fixture
|
|
110
|
+
* stays readable.
|
|
111
|
+
*
|
|
112
|
+
* Sorted by code point, not `localeCompare`: the result is committed to a file
|
|
113
|
+
* and reviewed as a diff, so the order must not depend on the capturing
|
|
114
|
+
* machine's locale (an ICU collation folds case together and orders
|
|
115
|
+
* `/spec/Foo` and `/spec/foo` by tie-break rules that differ between hosts).
|
|
116
|
+
*/
|
|
117
|
+
export function diffMutations(before, after, prefix = "") {
|
|
118
|
+
const plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
119
|
+
if (!plain(before) || !plain(after)) {
|
|
120
|
+
return JSON.stringify(canonical(before)) === JSON.stringify(canonical(after))
|
|
121
|
+
? []
|
|
122
|
+
: [{ op: "replace", path: prefix, value: after }];
|
|
123
|
+
}
|
|
124
|
+
const left = before;
|
|
125
|
+
const right = after;
|
|
126
|
+
const mutations = [];
|
|
127
|
+
for (const key of new Set([...Object.keys(left), ...Object.keys(right)])) {
|
|
128
|
+
const path = `${prefix}/${escape(key)}`;
|
|
129
|
+
if (!(key in right) || right[key] === undefined) {
|
|
130
|
+
if (left[key] !== undefined)
|
|
131
|
+
mutations.push({ op: "remove", path });
|
|
132
|
+
}
|
|
133
|
+
else if (!(key in left) || left[key] === undefined) {
|
|
134
|
+
mutations.push({ op: "add", path, value: right[key] });
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
mutations.push(...diffMutations(left[key], right[key], path));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return mutations.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
141
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { V1NetworkPolicy, V1Pod, V1Secret } from "@kubernetes/client-node";
|
|
2
|
+
import type { JobSpec } from "./types.js";
|
|
3
|
+
import { type KubernetesPlatform } from "./kubernetes-platform.js";
|
|
4
|
+
export declare const KUBERNETES_ISOLATION_ERROR = "kubernetes_isolation_unsupported";
|
|
5
|
+
export declare const KUBERNETES_PROVIDER_UNSUPPORTED = "kubernetes_provider_unsupported";
|
|
6
|
+
/** Extra seconds past timeoutSec before Kubernetes kills the pod (keeper included). */
|
|
7
|
+
export declare const POD_DEADLINE_GRACE_SECONDS = 300;
|
|
8
|
+
export declare const KEEPER_CONTAINER = "keeper";
|
|
9
|
+
export declare const STORAGE_INIT_CONTAINER = "storage-init";
|
|
10
|
+
export declare const WORKER_CONTAINER = "worker";
|
|
11
|
+
export declare const STORAGE_ROOT = "/run/wardby/storage";
|
|
12
|
+
export declare const KEEPER_SEEDED_MARKER = "/run/wardby/storage/input/.seeded";
|
|
13
|
+
export declare const PROXY_POD_LABEL: {
|
|
14
|
+
readonly "app.kubernetes.io/name": "wardby-coding-proxy";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* What every run pod carries and what the proxy's own ingress rule must admit on the deny port.
|
|
18
|
+
* Single source: `runLabels` stamps it, `readProxyWitness` checks the proxy admits it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const RUN_COMPONENT_LABEL: {
|
|
21
|
+
readonly "wardby.io/component": "coding-run";
|
|
22
|
+
};
|
|
23
|
+
/** The probe proved enforcement: the proxy port connected and the deny port was blocked. */
|
|
24
|
+
export declare const ENFORCEMENT_PROBE_PROVEN = 0;
|
|
25
|
+
/** The deny port was reachable: no policy is blocking it, or the policy is not port-scoped. */
|
|
26
|
+
export declare const ENFORCEMENT_PROBE_DENY_REACHABLE = 3;
|
|
27
|
+
/** The proxy port itself was unreachable: nothing could be witnessed (proxy down, or only the namespace default-deny is programmed). */
|
|
28
|
+
export declare const ENFORCEMENT_PROBE_PROXY_UNREACHABLE = 4;
|
|
29
|
+
/**
|
|
30
|
+
* The deny port answered with an RST: the SYN reached the destination host, so nothing is
|
|
31
|
+
* blocking the path — the port is reachable-but-unserved and witnesses nothing.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ENFORCEMENT_PROBE_DENY_REFUSED = 5;
|
|
34
|
+
export interface KubernetesRunNames {
|
|
35
|
+
token: string;
|
|
36
|
+
runSha: string;
|
|
37
|
+
pod: string;
|
|
38
|
+
policy: string;
|
|
39
|
+
record: string;
|
|
40
|
+
secret: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The per-run object names for a run token. The only place the naming scheme lives: the launcher
|
|
44
|
+
* resolves a handle's token through this, so names can never drift between creation and cleanup.
|
|
45
|
+
*/
|
|
46
|
+
export declare function kubernetesRunNamesForToken(token: string): Omit<KubernetesRunNames, "runSha">;
|
|
47
|
+
export declare function kubernetesRunNames(runId: string): KubernetesRunNames;
|
|
48
|
+
export declare function runLabels(runId: string): Record<string, string>;
|
|
49
|
+
/** Same grammar as Docker's repository digests; a cluster cannot pull a bare local image ID. */
|
|
50
|
+
export declare function isRegistryDigest(image: string): boolean;
|
|
51
|
+
export declare function validateKubernetesSpec(spec: JobSpec): void;
|
|
52
|
+
export interface RunPodOptions {
|
|
53
|
+
namespace: string;
|
|
54
|
+
proxyIp: string;
|
|
55
|
+
runtimeClassName?: string;
|
|
56
|
+
/** Which platform's admission rules the emitted resources must already satisfy. Default: "generic". */
|
|
57
|
+
platform?: KubernetesPlatform;
|
|
58
|
+
}
|
|
59
|
+
export declare function buildRunPod(spec: JobSpec, options: RunPodOptions): V1Pod;
|
|
60
|
+
export declare function buildRunNetworkPolicy(spec: JobSpec, namespace: string): V1NetworkPolicy;
|
|
61
|
+
export declare function buildCapabilitySecret(spec: JobSpec, namespace: string, capability: string): V1Secret;
|
|
62
|
+
/**
|
|
63
|
+
* A copy of `pod` with ONLY the API server's own defaulting and aliasing undone
|
|
64
|
+
* — the same `normalizeSpec` pass `assertRunPodMatches` runs, and nothing else.
|
|
65
|
+
*
|
|
66
|
+
* Exported for src/tools/capture-fixture.ts, which runs it over both the
|
|
67
|
+
* submitted and the returned pod before diffing them. Without it the diff is
|
|
68
|
+
* dominated by fields every API server fills in on every create
|
|
69
|
+
* (schedulerName, priority, terminationMessagePath on each container, probe
|
|
70
|
+
* defaults, `2048Mi` requantized to `2Gi`, ...); because the differ replaces
|
|
71
|
+
* arrays wholesale those collapse into opaque `replace /spec/containers`
|
|
72
|
+
* blobs, and a genuine platform rewrite *inside* a container would be
|
|
73
|
+
* indistinguishable from the noise.
|
|
74
|
+
*
|
|
75
|
+
* It deliberately does NOT apply `normalizePlatformMetadata`: that deletes
|
|
76
|
+
* exactly the platform-injected keys a capture exists to record. Fails closed
|
|
77
|
+
* (throws) on a spec the normalizer cannot make sense of.
|
|
78
|
+
*/
|
|
79
|
+
export declare function undoApiServerDefaults(pod: V1Pod): V1Pod;
|
|
80
|
+
/**
|
|
81
|
+
* Recursively sorts object keys and drops `undefined` values so key order and API-omitted
|
|
82
|
+
* fields never matter; array order is preserved.
|
|
83
|
+
*
|
|
84
|
+
* Exported for kubernetes-dry-run-fixture.ts's `diffMutations`: a captured pod's nested objects
|
|
85
|
+
* (container resource requests, volume definitions, host aliases, ...) come back from a real
|
|
86
|
+
* cluster with different key insertion order than `buildRunPod`'s own object literals, even when
|
|
87
|
+
* every value is identical. Without canonicalizing first, `diffMutations`' array branch treats
|
|
88
|
+
* that key-order difference as a genuine change and replaces the WHOLE array (every container,
|
|
89
|
+
* every volume) with an opaque blob — exactly the failure mode its own doc comment warns a real
|
|
90
|
+
* platform rewrite could hide inside. `assertRunPodMatches` below is unaffected by this (it
|
|
91
|
+
* already canonicalizes before comparing); only the capture tool's mutation list was at risk.
|
|
92
|
+
*/
|
|
93
|
+
export declare function canonical(value: unknown): unknown;
|
|
94
|
+
/**
|
|
95
|
+
* Deep-compares the pod the API server read back against the pod wardby built.
|
|
96
|
+
* ANY surviving difference fails the run closed.
|
|
97
|
+
*
|
|
98
|
+
* `platform` defaults to "generic" on purpose: a caller that forgets it gets the
|
|
99
|
+
* strictest behaviour, never the most permissive. A profile can only narrow what
|
|
100
|
+
* counts as an expected difference (by deleting named keys from both operands);
|
|
101
|
+
* it can never disable or short-circuit the comparison.
|
|
102
|
+
*/
|
|
103
|
+
export declare function assertRunPodMatches(actual: V1Pod, expected: V1Pod, platform?: KubernetesPlatform): void;
|
|
104
|
+
export declare function assertRunNetworkPolicyMatches(actual: V1NetworkPolicy, expected: V1NetworkPolicy): void;
|
|
105
|
+
/**
|
|
106
|
+
* A `node -e` script (argv only, never a shell) that measures BOTH of the proxy's
|
|
107
|
+
* ports in one pass, with a 3 s connect timeout each (above Linux's 1 s initial
|
|
108
|
+
* SYN retransmission, so one dropped SYN on an allowed path still connects).
|
|
109
|
+
*
|
|
110
|
+
* Measuring both is what makes the result decisive. A NetworkPolicy denial drops
|
|
111
|
+
* the packet rather than rejecting it — GKE Dataplane V2, which Autopilot runs,
|
|
112
|
+
* always drops — so "the deny port did not answer" is equally consistent with
|
|
113
|
+
* "the proxy is gone and no policy exists at all". Requiring the proxy port to
|
|
114
|
+
* connect in the *same* probe turns "something is listening" from a control-plane
|
|
115
|
+
* inference into a fact this pod just observed.
|
|
116
|
+
*
|
|
117
|
+
* That pairing alone is still not enough, and this was reproduced on a live
|
|
118
|
+
* cluster: in a namespace with NO NetworkPolicy at all, against a pod listening
|
|
119
|
+
* on 8787 and serving nothing on 8788, a probe that collapsed `error` and
|
|
120
|
+
* `timeout` into one "not reachable" exited PROVEN while it had full internet
|
|
121
|
+
* egress. The pairing only rules out "the whole proxy pod is dead"; whenever the
|
|
122
|
+
* deny *listener specifically* is unserved, "blocked" and "nothing there" are
|
|
123
|
+
* the same observation. No control-plane read fixes this — Endpoints subset
|
|
124
|
+
* ports come from the Service's numeric targetPort, not from anything actually
|
|
125
|
+
* binding — so the distinction has to be made in the dataplane, here:
|
|
126
|
+
*
|
|
127
|
+
* - deny port **times out** → the packet was dropped somewhere on the path → the only outcome
|
|
128
|
+
* that can prove a policy. Note what it does NOT prove on its own: *which* hop dropped it. That
|
|
129
|
+
* it was the run pod's own egress policy follows from the proxy admitting run pods on the deny
|
|
130
|
+
* port at its own ingress, leaving no other hop that would drop it — a precondition the
|
|
131
|
+
* preflight's `proxy-service` check now verifies (readProxyWitness), after a live cluster
|
|
132
|
+
* falsified the assumption: with the drop moved to the destination, a prober holding no policy
|
|
133
|
+
* at all and with full internet egress read PROVEN.
|
|
134
|
+
* - deny port **refused** (RST / ECONNREFUSED) → the SYN reached the destination host, so
|
|
135
|
+
* nothing blocked the path. This holds on every dataplane, drop-based ones included, because
|
|
136
|
+
* a drop cannot produce an RST. Reported as ENFORCEMENT_PROBE_DENY_REFUSED, never as proven.
|
|
137
|
+
* - proxy port must still CONNECT for anything to count at all.
|
|
138
|
+
*
|
|
139
|
+
* Consequence, intended: on a **reject-style** CNI a genuine policy denial also arrives as an
|
|
140
|
+
* RST, so such a cluster now fails closed here rather than passing vacuously. Failing closed on
|
|
141
|
+
* a cluster whose refusals are ambiguous is the correct direction — a witness that cannot tell
|
|
142
|
+
* "denied" from "unserved" is not a witness — and such a cluster needs a different one.
|
|
143
|
+
*
|
|
144
|
+
* Exit codes are ENFORCEMENT_PROBE_PROVEN / _DENY_REACHABLE / _PROXY_UNREACHABLE / _DENY_REFUSED.
|
|
145
|
+
* The IP is validated and embedded as a JSON string literal.
|
|
146
|
+
*/
|
|
147
|
+
export declare function enforcementProbeScript(proxyIp: string): string;
|