@wardby/cli 0.2.0 → 0.3.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 +34 -4
- package/README.md +183 -71
- package/dist/claude-coding-worker/driver.d.ts +7 -1
- package/dist/claude-coding-worker/driver.js +31 -2
- package/dist/claude-coding-worker/main.js +5 -2
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +13 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +219 -29
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +99 -3
- package/dist/coding/protocol.js +155 -4
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +18 -0
- package/dist/coding-worker/artifact.js +48 -6
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +14 -1
- package/dist/coding-worker/driver.js +106 -14
- package/dist/coding-worker/errors.d.ts +7 -0
- package/dist/coding-worker/errors.js +26 -0
- package/dist/coding-worker/main.js +13 -4
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +41 -0
- package/dist/config/providers.js +66 -0
- package/dist/core/budget-groups.d.ts +109 -6
- package/dist/core/budget-groups.js +125 -19
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +1 -1
- package/dist/core/datastores.d.ts +1 -1
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +40 -2
- package/dist/core/db.js +75 -2
- package/dist/core/dispatch.d.ts +49 -7
- package/dist/core/dispatch.js +290 -45
- package/dist/core/engine-native.js +27 -10
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +49 -0
- package/dist/core/host-events.js +257 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +61 -0
- package/dist/core/host-status.js +211 -0
- package/dist/core/http-runtime.js +3 -4
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/lease.d.ts +1 -1
- package/dist/core/private-directory.d.ts +20 -0
- package/dist/core/private-directory.js +36 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +39 -3
- package/dist/core/reconciler.js +86 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +347 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/runner.d.ts +31 -9
- package/dist/core/runner.js +237 -40
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secrets.d.ts +12 -3
- package/dist/core/secrets.js +25 -6
- package/dist/core/subagent-memory-tools.d.ts +2 -2
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +64 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +9 -2
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +328 -0
- package/dist/generated/prisma/browser.js +17 -0
- package/dist/generated/prisma/client.d.ts +347 -0
- package/dist/generated/prisma/client.js +34 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
- package/dist/generated/prisma/commonInputTypes.js +10 -0
- package/dist/generated/prisma/enums.d.ts +59 -0
- package/dist/generated/prisma/enums.js +59 -0
- package/dist/generated/prisma/internal/class.d.ts +605 -0
- package/dist/generated/prisma/internal/class.js +49 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
- package/dist/generated/prisma/models/Agent.d.ts +3560 -0
- package/dist/generated/prisma/models/Agent.js +1 -0
- package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
- package/dist/generated/prisma/models/AgentDatastore.js +1 -0
- package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
- package/dist/generated/prisma/models/AgentMemory.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
- package/dist/generated/prisma/models/AgentSecret.js +1 -0
- package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
- package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
- package/dist/generated/prisma/models/AgentTool.js +1 -0
- package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
- package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
- package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
- package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
- package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
- package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
- package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
- package/dist/generated/prisma/models/AuthSession.js +1 -0
- package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
- package/dist/generated/prisma/models/AuthUser.js +1 -0
- package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
- package/dist/generated/prisma/models/BudgetGroup.js +1 -0
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
- package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
- package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
- package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
- package/dist/generated/prisma/models/CodingProxySession.js +1 -0
- package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
- package/dist/generated/prisma/models/CodingRun.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
- package/dist/generated/prisma/models/Datastore.js +1 -0
- package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
- package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
- package/dist/generated/prisma/models/OAuthClient.js +1 -0
- package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
- package/dist/generated/prisma/models/OAuthFamily.js +1 -0
- package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
- package/dist/generated/prisma/models/OAuthGrant.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +2245 -0
- package/dist/generated/prisma/models/Principal.js +1 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +2669 -0
- package/dist/generated/prisma/models/Run.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
- package/dist/generated/prisma/models/SchedulerLease.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +1399 -0
- package/dist/generated/prisma/models/Secret.js +1 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
- package/dist/generated/prisma/models/Task.d.ts +1176 -0
- package/dist/generated/prisma/models/Task.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +1490 -0
- package/dist/generated/prisma/models/Tool.js +1 -0
- package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
- package/dist/generated/prisma/models/Webhook.js +1 -0
- package/dist/generated/prisma/models.d.ts +45 -0
- package/dist/generated/prisma/models.js +1 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +660 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +6 -1
- package/dist/import/create.js +68 -14
- package/dist/import/index.d.ts +1 -1
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +11 -11
- package/dist/mcp/auth/access.d.ts +70 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +37 -63
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/principal.d.ts +1 -1
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +32 -3
- package/dist/mcp/auth/resource-server.js +84 -3
- package/dist/mcp/auth/self-hosted/browser.js +22 -8
- package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
- package/dist/mcp/context.d.ts +28 -2
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/github-ingress.d.ts +36 -0
- package/dist/mcp/host-events/github-ingress.js +92 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/index.d.ts +8 -1
- package/dist/mcp/index.js +106 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/shared/page-style.d.ts +16 -0
- package/dist/mcp/shared/page-style.js +129 -0
- package/dist/mcp/tasks/manager.d.ts +1 -1
- package/dist/mcp/tools/agents.js +447 -49
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/datastore.js +23 -16
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +181 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +4 -7
- package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
- package/dist/mcp/tools/secret-elicitation-form.js +10 -28
- package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +53 -17
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +202 -41
- package/dist/mcp/tools/trigger.js +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +11 -1
- package/dist/mcp/transport/streamable-http.js +32 -2
- package/dist/mcp/unattended-schedules.d.ts +1 -1
- package/dist/mcp/webhooks/ingress.d.ts +1 -1
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +3 -1
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +4 -3
- package/dist/providers/auth/self-hosted.js +16 -4
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
- package/dist/providers/coding-proxy/memory-ledger.js +21 -2
- package/dist/providers/coding-proxy/metering.d.ts +4 -0
- package/dist/providers/coding-proxy/metering.js +28 -2
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
- package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
- package/dist/providers/coding-proxy/proxy.d.ts +13 -0
- package/dist/providers/coding-proxy/proxy.js +479 -18
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -1
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +15 -0
- package/dist/providers/datastore/postgres.d.ts +1 -1
- package/dist/providers/engine/types.d.ts +10 -0
- package/dist/providers/executor/build.d.ts +3 -3
- package/dist/providers/executor/composition.d.ts +4 -1
- package/dist/providers/executor/composition.js +19 -0
- package/dist/providers/executor/container.d.ts +106 -4
- package/dist/providers/executor/container.js +305 -22
- package/dist/providers/executor/dbos.d.ts +3 -4
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +7 -0
- package/dist/providers/executor/routing.js +13 -0
- package/dist/providers/executor/types.d.ts +22 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +60 -2
- package/dist/providers/jobs/docker.js +313 -50
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +21 -2
- package/dist/providers/jobs/kubernetes.js +117 -31
- package/dist/providers/jobs/types.d.ts +20 -0
- package/dist/providers/llm/anthropic.js +6 -2
- package/dist/providers/llm/bedrock.js +2 -1
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +3 -1
- package/dist/providers/llm/claude-provider.js +5 -1
- package/dist/providers/llm/index.d.ts +1 -1
- package/dist/providers/llm/index.js +1 -1
- package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
- package/dist/providers/llm/pricing-anthropic.js +32 -14
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
- package/dist/providers/llm/routing.d.ts +10 -1
- package/dist/providers/llm/routing.js +18 -0
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/memory/postgres.d.ts +1 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +180 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +567 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +284 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +79 -14
- package/dist/providers/vcs/github.d.ts +74 -3
- package/dist/providers/vcs/github.js +195 -15
- package/dist/providers/vcs/types.d.ts +42 -4
- package/dist/quickstart/index.js +34 -32
- package/dist/quickstart/migrate.d.ts +23 -0
- package/dist/quickstart/migrate.js +125 -0
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +1 -1
- package/dist/wardby-bin.js +6 -0
- package/docs/README.md +30 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +481 -0
- package/docs/coding-agent-setup.md +172 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1061 -0
- package/docs/getting-started-gke.md +615 -0
- package/docs/getting-started-identity-provider.md +308 -0
- package/docs/getting-started.md +133 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +657 -0
- package/help/code-review-agents.md +31 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/creating-agents.md +68 -0
- package/help/deploy-gke.md +39 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +31 -0
- package/help/github.md +30 -0
- package/help/identity-and-access.md +39 -0
- package/help/mcp.md +30 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +30 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +26 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +30 -11
- package/prisma/migrate.config.mjs +20 -0
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- package/prisma/schema.prisma +417 -23
|
@@ -6,15 +6,34 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { isIP } from "node:net";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { StoredCodingServicesSchema } from "../../coding/services/catalog.js";
|
|
10
|
+
import { CLAUDE_TOOL_SETUP_ENV, CODING_PROXY_ALIAS, CODING_PROXY_DENY_PORT, CODING_PROXY_PORT, CODING_WORKER_GID, CODING_WORKER_UID, WORKER_STOP_GRACE_SECONDS, claudeToolLimits, isRepositoryDigest, } from "./docker-isolation.js";
|
|
11
|
+
import { GVISOR_RUNTIME_CLASS, KubernetesPlatformError, STORAGE_INIT_EPHEMERAL_MIB, TOOL_RUNNER_EPHEMERAL_MIB, WORKER_EPHEMERAL_MIB, conformResources, describeMib, normalizePlatformMetadata, platformProfile, podEphemeralStorageMib, } from "./kubernetes-platform.js";
|
|
11
12
|
export const KUBERNETES_ISOLATION_ERROR = "kubernetes_isolation_unsupported";
|
|
12
|
-
export const KUBERNETES_PROVIDER_UNSUPPORTED = "kubernetes_provider_unsupported";
|
|
13
13
|
/** Extra seconds past timeoutSec before Kubernetes kills the pod (keeper included). */
|
|
14
14
|
export const POD_DEADLINE_GRACE_SECONDS = 300;
|
|
15
15
|
export const KEEPER_CONTAINER = "keeper";
|
|
16
16
|
export const STORAGE_INIT_CONTAINER = "storage-init";
|
|
17
17
|
export const WORKER_CONTAINER = "worker";
|
|
18
|
+
/** Claude's credential-free command runner: a native sidecar in the run pod. */
|
|
19
|
+
export const TOOL_RUNNER_CONTAINER = "tool-runner";
|
|
20
|
+
/** The run Secret's key holding the tool runner's setup (claude-tool-setup.ts). */
|
|
21
|
+
export const TOOL_SETUP_SECRET_KEY = "tool-setup";
|
|
22
|
+
/** Where the tool runner listens and the agent's relay connects (claude-tool-runner/main.mjs). */
|
|
23
|
+
export const TOOL_SOCKET_PATH = "/run/wardby/tool/runner.sock";
|
|
24
|
+
const CODEX_ENTRYPOINT = "/opt/wardby/coding-worker/main.js";
|
|
25
|
+
const CLAUDE_ENTRYPOINT = "/opt/wardby/claude-coding-worker/main.js";
|
|
26
|
+
/** Every service sidecar's container name starts with this (service-postgres, service-redis, ...). */
|
|
27
|
+
export const SERVICE_CONTAINER_PREFIX = "service-";
|
|
28
|
+
/** Disk-backed scratch for each of a service's writablePaths (a socket directory, /tmp). */
|
|
29
|
+
export const SERVICE_SCRATCH_MIB = 64;
|
|
30
|
+
export function serviceContainerName(name) {
|
|
31
|
+
return `${SERVICE_CONTAINER_PREFIX}${name}`;
|
|
32
|
+
}
|
|
33
|
+
/** What a service sidecar reserves of the pod's ephemeral storage: its data volume plus its scratch volumes. */
|
|
34
|
+
export function serviceEphemeralStorageMib(service) {
|
|
35
|
+
return service.resources.diskMib + SERVICE_SCRATCH_MIB * service.writablePaths.length;
|
|
36
|
+
}
|
|
18
37
|
export const STORAGE_ROOT = "/run/wardby/storage";
|
|
19
38
|
export const KEEPER_SEEDED_MARKER = `${STORAGE_ROOT}/input/.seeded`;
|
|
20
39
|
export const PROXY_POD_LABEL = { "app.kubernetes.io/name": "wardby-coding-proxy" };
|
|
@@ -36,33 +55,55 @@ export const ENFORCEMENT_PROBE_PROXY_UNREACHABLE = 4;
|
|
|
36
55
|
export const ENFORCEMENT_PROBE_DENY_REFUSED = 5;
|
|
37
56
|
const WORKER_SERVICE_ACCOUNT = "wardby-coding-worker";
|
|
38
57
|
const RUN_ID = /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,199}$/;
|
|
39
|
-
/** The worker waits for the launcher's seeded marker, then runs
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
/** The worker waits for the launcher's seeded marker, then runs its provider's entrypoint. */
|
|
59
|
+
function workerGate(entrypoint) {
|
|
60
|
+
return [
|
|
61
|
+
'const fs = require("node:fs");',
|
|
62
|
+
'const marker = "/run/wardby/input/.seeded";',
|
|
63
|
+
"(function wait() {",
|
|
64
|
+
" if (fs.existsSync(marker)) {",
|
|
65
|
+
` import(${JSON.stringify(entrypoint)}).catch(() => { process.exitCode = 1; });`,
|
|
66
|
+
" } else {",
|
|
67
|
+
" setTimeout(wait, 250);",
|
|
68
|
+
" }",
|
|
69
|
+
"})();",
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
51
72
|
/**
|
|
52
73
|
* Creates the worker's subPath mount sources, owned by the run uid, before any regular container
|
|
53
74
|
* starts. Otherwise the kubelet creates them root-owned while setting up the worker's subPath mounts,
|
|
54
75
|
* and the keeper (uid 10001, no capabilities) can't chmod them. Idempotent; no shell.
|
|
55
76
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
function storageInitScript(directories) {
|
|
78
|
+
return [
|
|
79
|
+
'const fs = require("node:fs");',
|
|
80
|
+
`for (const name of ${JSON.stringify(directories).replaceAll(",", ", ")}) {`,
|
|
81
|
+
` const path = ${JSON.stringify(STORAGE_ROOT)} + "/" + name;`,
|
|
82
|
+
" fs.mkdirSync(path, { recursive: true, mode: 0o700 });",
|
|
83
|
+
" fs.chmodSync(path, 0o700);",
|
|
84
|
+
"}",
|
|
85
|
+
].join("\n");
|
|
86
|
+
}
|
|
87
|
+
const STORAGE_DIRECTORIES = ["workspace", "input", "output"];
|
|
88
|
+
/**
|
|
89
|
+
* Claude's socket volume: its own memory-backed emptyDir, mounted by the worker and the tool runner
|
|
90
|
+
* only. Under gVisor a Unix socket bound on the disk-backed storage volume is invisible to another
|
|
91
|
+
* container; a memory-backed emptyDir that two containers mount is one tmpfs shared across the
|
|
92
|
+
* sandbox (GKE Autopilot annotates it `share: pod`), so the agent's relay can connect.
|
|
93
|
+
*/
|
|
94
|
+
const TOOL_SOCKET_VOLUME = "tool-socket";
|
|
95
|
+
const TOOL_SOCKET_VOLUME_MIB = 1;
|
|
96
|
+
/**
|
|
97
|
+
* `reason` is a short, fixed code naming which check failed (never a value from the spec itself,
|
|
98
|
+
* so nothing operator-supplied — an image reference, a limit — ever ends up in the message). It's
|
|
99
|
+
* appended the same way kubernetes-preflight.ts's `failure(check, cause)` tags a canary failure:
|
|
100
|
+
* callers that only match on the bare `KUBERNETES_ISOLATION_ERROR` prefix (a substring match, e.g.
|
|
101
|
+
* `toThrow(KUBERNETES_ISOLATION_ERROR)` or `error.message.startsWith(...)`) are unaffected, while
|
|
102
|
+
* an operator reading the message (or a `this.warn(...)` of it — see kubernetes.ts's `launch()`)
|
|
103
|
+
* gets more than a bare code to go on.
|
|
104
|
+
*/
|
|
105
|
+
function isolationError(reason) {
|
|
106
|
+
return new Error(reason === undefined ? KUBERNETES_ISOLATION_ERROR : `${KUBERNETES_ISOLATION_ERROR}:${reason}`);
|
|
66
107
|
}
|
|
67
108
|
function sha256(value) {
|
|
68
109
|
return createHash("sha256").update(value).digest("hex");
|
|
@@ -105,22 +146,43 @@ function isWholeMillicores(cpus) {
|
|
|
105
146
|
}
|
|
106
147
|
export function validateKubernetesSpec(spec) {
|
|
107
148
|
if (spec.kind !== "coding-agent" || !RUN_ID.test(spec.runId))
|
|
108
|
-
throw isolationError();
|
|
109
|
-
if (spec.provider
|
|
110
|
-
throw
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
throw isolationError();
|
|
149
|
+
throw isolationError("run-id");
|
|
150
|
+
if (spec.provider !== undefined && spec.provider !== "codex" && spec.provider !== "claude-code") {
|
|
151
|
+
throw isolationError("provider");
|
|
152
|
+
}
|
|
153
|
+
if (spec.provider === "claude-code") {
|
|
154
|
+
if (spec.toolImage === undefined || !isRegistryDigest(spec.toolImage)) {
|
|
155
|
+
throw isolationError("tool-image-not-registry-digest");
|
|
156
|
+
}
|
|
157
|
+
// Docker's floor for the same split (docker-isolation.ts validateSpec): the tool runner takes 0.25 CPU and ≥128 MiB.
|
|
158
|
+
if (spec.limits.cpus < 0.35 || spec.limits.memoryMb < 256)
|
|
159
|
+
throw isolationError("claude-limits-too-small");
|
|
160
|
+
}
|
|
161
|
+
else if (spec.toolImage !== undefined) {
|
|
162
|
+
throw isolationError("tool-image-set");
|
|
123
163
|
}
|
|
164
|
+
if (!isRegistryDigest(spec.image))
|
|
165
|
+
throw isolationError("image-not-registry-digest");
|
|
166
|
+
if (spec.services !== undefined) {
|
|
167
|
+
// Re-validated here, not trusted from dispatch: every image a digest, every path and probe in bounds.
|
|
168
|
+
const names = spec.services.map((service) => service.name);
|
|
169
|
+
if (spec.services.length === 0 ||
|
|
170
|
+
new Set(names).size !== names.length ||
|
|
171
|
+
!StoredCodingServicesSchema.safeParse(spec.services).success) {
|
|
172
|
+
throw isolationError("services-invalid");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const { cpus, memoryMb, pids, diskMb } = spec.limits;
|
|
176
|
+
if (!inRange(cpus, 0.1, 32, false) || !isWholeMillicores(cpus))
|
|
177
|
+
throw isolationError("cpus-out-of-range");
|
|
178
|
+
if (!inRange(memoryMb, 128, 65_536, true))
|
|
179
|
+
throw isolationError("memory-out-of-range");
|
|
180
|
+
if (!inRange(pids, 16, 4_096, true))
|
|
181
|
+
throw isolationError("pids-out-of-range");
|
|
182
|
+
if (!inRange(diskMb, 64, 32_768, true))
|
|
183
|
+
throw isolationError("disk-out-of-range");
|
|
184
|
+
if (!inRange(spec.timeoutSec, 1, 86_400, true))
|
|
185
|
+
throw isolationError("timeout-out-of-range");
|
|
124
186
|
}
|
|
125
187
|
function containerSecurity() {
|
|
126
188
|
return {
|
|
@@ -131,6 +193,56 @@ function containerSecurity() {
|
|
|
131
193
|
capabilities: { drop: ["ALL"] },
|
|
132
194
|
};
|
|
133
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* The emptyDirs behind one service's mounts: its data path, then each writable path. The only place
|
|
198
|
+
* their names are built, so the sidecar's volumeMounts and the pod's volumes can never disagree.
|
|
199
|
+
*/
|
|
200
|
+
function serviceStorage(service, index) {
|
|
201
|
+
return [
|
|
202
|
+
{ name: `service-${index}-data`, mountPath: service.dataPath, sizeMib: service.resources.diskMib },
|
|
203
|
+
...service.writablePaths.map((path, scratch) => ({
|
|
204
|
+
name: `service-${index}-scratch-${scratch}`,
|
|
205
|
+
mountPath: path,
|
|
206
|
+
sizeMib: SERVICE_SCRATCH_MIB,
|
|
207
|
+
})),
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* One coding-run service as a native sidecar (docs/coding-services.md): an init container with
|
|
212
|
+
* restartPolicy Always, so the kubelet starts it before the keeper and worker and holds them until
|
|
213
|
+
* its startup probe (the catalog's readiness command) passes. It runs the catalog's image with its
|
|
214
|
+
* own entrypoint, under the same security context as every other container, with an emptyDir at its
|
|
215
|
+
* data path and at each writable path. Same pod, same network namespace: the worker reaches it on
|
|
216
|
+
* 127.0.0.1, and the run's NetworkPolicy needs no change.
|
|
217
|
+
*/
|
|
218
|
+
function serviceSidecar(service, index, profile) {
|
|
219
|
+
const env = Object.entries(service.serviceEnv)
|
|
220
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
221
|
+
.map(([name, value]) => ({ name, value }));
|
|
222
|
+
return {
|
|
223
|
+
name: serviceContainerName(service.name),
|
|
224
|
+
image: service.image,
|
|
225
|
+
restartPolicy: "Always",
|
|
226
|
+
...(env.length > 0 ? { env } : {}),
|
|
227
|
+
securityContext: containerSecurity(),
|
|
228
|
+
resources: conformResources(profile, {
|
|
229
|
+
cpuMillicores: service.resources.cpuMillicores,
|
|
230
|
+
memoryMib: service.resources.memoryMib,
|
|
231
|
+
ephemeralStorageMib: serviceEphemeralStorageMib(service),
|
|
232
|
+
}),
|
|
233
|
+
startupProbe: {
|
|
234
|
+
exec: { command: [...service.readiness.command] },
|
|
235
|
+
periodSeconds: service.readiness.periodSeconds,
|
|
236
|
+
timeoutSeconds: service.readiness.timeoutSeconds,
|
|
237
|
+
failureThreshold: service.readiness.failureThreshold,
|
|
238
|
+
successThreshold: 1,
|
|
239
|
+
},
|
|
240
|
+
volumeMounts: serviceStorage(service, index).map(({ name, mountPath }) => ({ name, mountPath })),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function serviceVolumes(services) {
|
|
244
|
+
return services.flatMap((service, index) => serviceStorage(service, index).map(({ name, sizeMib }) => ({ name, emptyDir: { sizeLimit: `${sizeMib}Mi` } })));
|
|
245
|
+
}
|
|
134
246
|
export function buildRunPod(spec, options) {
|
|
135
247
|
validateKubernetesSpec(spec);
|
|
136
248
|
const profile = platformProfile(options.platform ?? "generic");
|
|
@@ -144,19 +256,23 @@ export function buildRunPod(spec, options) {
|
|
|
144
256
|
// conformResources range-checks one container at a time; only this function sees every container,
|
|
145
257
|
// so the SUMMED pod total is checked here — before submission, so an over-large workspace fails
|
|
146
258
|
// closed rather than being rewritten by the platform (which attestation would then reject anyway).
|
|
259
|
+
const services = spec.services ?? [];
|
|
147
260
|
const ceiling = profile.resources.ephemeralStorageCeilingMib;
|
|
148
|
-
const podEphemeral = podEphemeralStorageMib(spec.limits.diskMb);
|
|
261
|
+
const podEphemeral = podEphemeralStorageMib(spec.limits.diskMb, services.reduce((total, service) => total + serviceEphemeralStorageMib(service), 0));
|
|
149
262
|
if (ceiling !== undefined && podEphemeral > ceiling) {
|
|
150
|
-
throw new KubernetesPlatformError(`a ${spec.limits.diskMb} MiB workspace
|
|
263
|
+
throw new KubernetesPlatformError(`a ${spec.limits.diskMb} MiB workspace${services.length > 0 ? " and its services" : ""} need${services.length > 0 ? "" : "s"} ${podEphemeral} MiB of pod ephemeral storage, over the ${describeMib(ceiling)} ceiling of platform ${profile.name}`);
|
|
151
264
|
}
|
|
152
265
|
const names = kubernetesRunNames(spec.runId);
|
|
153
266
|
const scratchMb = Math.max(16, Math.min(64, Math.floor(spec.limits.memoryMb / 8)));
|
|
154
267
|
const sidecarCpuMillicores = 250;
|
|
155
268
|
const sidecarMemoryMib = 128;
|
|
269
|
+
const claude = spec.provider === "claude-code";
|
|
270
|
+
const tool = claudeToolLimits(spec);
|
|
271
|
+
const toolScratchMb = Math.max(16, Math.min(64, Math.floor(tool.memoryMb / 8)));
|
|
156
272
|
const storageInit = {
|
|
157
273
|
name: STORAGE_INIT_CONTAINER,
|
|
158
274
|
image: spec.image,
|
|
159
|
-
command: ["node", "-e",
|
|
275
|
+
command: ["node", "-e", storageInitScript(STORAGE_DIRECTORIES)],
|
|
160
276
|
securityContext: containerSecurity(),
|
|
161
277
|
resources: conformResources(profile, {
|
|
162
278
|
cpuMillicores: sidecarCpuMillicores,
|
|
@@ -182,25 +298,73 @@ export function buildRunPod(spec, options) {
|
|
|
182
298
|
const worker = {
|
|
183
299
|
name: WORKER_CONTAINER,
|
|
184
300
|
image: spec.image,
|
|
185
|
-
command: ["node", "-e",
|
|
301
|
+
command: ["node", "-e", workerGate(claude ? CLAUDE_ENTRYPOINT : CODEX_ENTRYPOINT)],
|
|
186
302
|
env: [
|
|
187
303
|
{ name: "WARDBY_PROXY_URL", value: `http://${CODING_PROXY_ALIAS}:${CODING_PROXY_PORT}` },
|
|
188
304
|
{ name: "WARDBY_RUN_CAPABILITY", valueFrom: { secretKeyRef: { name: names.secret, key: "capability" } } },
|
|
189
305
|
],
|
|
190
306
|
securityContext: containerSecurity(),
|
|
191
307
|
resources: conformResources(profile, {
|
|
192
|
-
cpuMillicores: Math.round(spec.limits.cpus * 1000),
|
|
193
|
-
memoryMib: spec.limits.memoryMb,
|
|
194
|
-
ephemeralStorageMib: WORKER_EPHEMERAL_MIB,
|
|
308
|
+
cpuMillicores: Math.round((claude ? spec.limits.cpus - tool.cpus : spec.limits.cpus) * 1000),
|
|
309
|
+
memoryMib: claude ? spec.limits.memoryMb - tool.memoryMb : spec.limits.memoryMb,
|
|
310
|
+
ephemeralStorageMib: claude ? WORKER_EPHEMERAL_MIB - TOOL_RUNNER_EPHEMERAL_MIB : WORKER_EPHEMERAL_MIB,
|
|
195
311
|
}),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
312
|
+
// Claude's agent never sees the repository: the tool runner does, over the socket.
|
|
313
|
+
volumeMounts: claude
|
|
314
|
+
? [
|
|
315
|
+
{ name: "storage", mountPath: "/run/wardby/input", subPath: "input", readOnly: true },
|
|
316
|
+
{ name: "storage", mountPath: "/run/wardby/output", subPath: "output" },
|
|
317
|
+
{ name: TOOL_SOCKET_VOLUME, mountPath: "/run/wardby/tool" },
|
|
318
|
+
{ name: "tmp", mountPath: "/tmp" },
|
|
319
|
+
{ name: "home", mountPath: "/home/wardby" },
|
|
320
|
+
]
|
|
321
|
+
: [
|
|
322
|
+
{ name: "storage", mountPath: "/workspace", subPath: "workspace" },
|
|
323
|
+
{ name: "storage", mountPath: "/run/wardby/input", subPath: "input", readOnly: true },
|
|
324
|
+
{ name: "storage", mountPath: "/run/wardby/output", subPath: "output" },
|
|
325
|
+
{ name: "tmp", mountPath: "/tmp" },
|
|
326
|
+
{ name: "home", mountPath: "/home/wardby" },
|
|
327
|
+
],
|
|
203
328
|
};
|
|
329
|
+
/**
|
|
330
|
+
* Claude's tool runner (docs/coding-worker-isolation.md): a native sidecar, so the kubelet starts it
|
|
331
|
+
* before the keeper and worker and holds them until its socket exists. It mounts the workspace and
|
|
332
|
+
* the socket, gets its registry/service setup from the run's Secret, and never the capability. It
|
|
333
|
+
* shares the pod's network namespace, so the run's single egress rule (the proxy) applies to it too.
|
|
334
|
+
*/
|
|
335
|
+
const toolRunner = claude
|
|
336
|
+
? {
|
|
337
|
+
name: TOOL_RUNNER_CONTAINER,
|
|
338
|
+
image: spec.toolImage,
|
|
339
|
+
command: ["node", "/opt/wardby/claude-tool-runner/main.mjs"],
|
|
340
|
+
restartPolicy: "Always",
|
|
341
|
+
env: [
|
|
342
|
+
{
|
|
343
|
+
name: CLAUDE_TOOL_SETUP_ENV,
|
|
344
|
+
valueFrom: { secretKeyRef: { name: names.secret, key: TOOL_SETUP_SECRET_KEY } },
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
securityContext: containerSecurity(),
|
|
348
|
+
resources: conformResources(profile, {
|
|
349
|
+
cpuMillicores: Math.round(tool.cpus * 1000),
|
|
350
|
+
memoryMib: tool.memoryMb,
|
|
351
|
+
ephemeralStorageMib: TOOL_RUNNER_EPHEMERAL_MIB,
|
|
352
|
+
}),
|
|
353
|
+
startupProbe: {
|
|
354
|
+
exec: { command: ["test", "-S", TOOL_SOCKET_PATH] },
|
|
355
|
+
periodSeconds: 1,
|
|
356
|
+
timeoutSeconds: 1,
|
|
357
|
+
failureThreshold: 60,
|
|
358
|
+
successThreshold: 1,
|
|
359
|
+
},
|
|
360
|
+
volumeMounts: [
|
|
361
|
+
{ name: "storage", mountPath: "/workspace", subPath: "workspace" },
|
|
362
|
+
{ name: TOOL_SOCKET_VOLUME, mountPath: "/run/wardby/tool" },
|
|
363
|
+
{ name: "tool-tmp", mountPath: "/tmp" },
|
|
364
|
+
{ name: "tool-home", mountPath: "/home/wardby" },
|
|
365
|
+
],
|
|
366
|
+
}
|
|
367
|
+
: undefined;
|
|
204
368
|
return {
|
|
205
369
|
apiVersion: "v1",
|
|
206
370
|
kind: "Pod",
|
|
@@ -226,6 +390,9 @@ export function buildRunPod(spec, options) {
|
|
|
226
390
|
dnsConfig: { nameservers: ["127.0.0.1"] },
|
|
227
391
|
hostAliases: [{ ip: options.proxyIp, hostnames: [CODING_PROXY_ALIAS] }],
|
|
228
392
|
...(options.runtimeClassName ? { runtimeClassName: options.runtimeClassName } : {}),
|
|
393
|
+
// Part of the submitted spec, so attestation compares it like any other field; the
|
|
394
|
+
// `priority` and `preemptionPolicy` the API server resolves from it are normalized away.
|
|
395
|
+
...(options.priorityClassName ? { priorityClassName: options.priorityClassName } : {}),
|
|
229
396
|
securityContext: {
|
|
230
397
|
runAsNonRoot: true,
|
|
231
398
|
runAsUser: CODING_WORKER_UID,
|
|
@@ -237,8 +404,22 @@ export function buildRunPod(spec, options) {
|
|
|
237
404
|
{ name: "storage", emptyDir: { sizeLimit: `${spec.limits.diskMb}Mi` } },
|
|
238
405
|
{ name: "tmp", emptyDir: { medium: "Memory", sizeLimit: `${scratchMb}Mi` } },
|
|
239
406
|
{ name: "home", emptyDir: { medium: "Memory", sizeLimit: `${scratchMb}Mi` } },
|
|
407
|
+
...(claude
|
|
408
|
+
? [
|
|
409
|
+
{ name: TOOL_SOCKET_VOLUME, emptyDir: { medium: "Memory", sizeLimit: `${TOOL_SOCKET_VOLUME_MIB}Mi` } },
|
|
410
|
+
{ name: "tool-tmp", emptyDir: { medium: "Memory", sizeLimit: `${toolScratchMb}Mi` } },
|
|
411
|
+
{ name: "tool-home", emptyDir: { medium: "Memory", sizeLimit: `${toolScratchMb}Mi` } },
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
414
|
+
...serviceVolumes(services),
|
|
415
|
+
],
|
|
416
|
+
// storage-init first (it must finish before any subPath mount), then Claude's tool runner, then each
|
|
417
|
+
// service sidecar in declaration order; the keeper and worker start only once every sidecar started.
|
|
418
|
+
initContainers: [
|
|
419
|
+
storageInit,
|
|
420
|
+
...(toolRunner ? [toolRunner] : []),
|
|
421
|
+
...services.map((service, index) => serviceSidecar(service, index, profile)),
|
|
240
422
|
],
|
|
241
|
-
initContainers: [storageInit],
|
|
242
423
|
containers: [keeper, worker],
|
|
243
424
|
},
|
|
244
425
|
};
|
|
@@ -262,14 +443,14 @@ export function buildRunNetworkPolicy(spec, namespace) {
|
|
|
262
443
|
},
|
|
263
444
|
};
|
|
264
445
|
}
|
|
265
|
-
export function buildCapabilitySecret(spec, namespace, capability) {
|
|
446
|
+
export function buildCapabilitySecret(spec, namespace, capability, toolSetup) {
|
|
266
447
|
const names = kubernetesRunNames(spec.runId);
|
|
267
448
|
return {
|
|
268
449
|
apiVersion: "v1",
|
|
269
450
|
kind: "Secret",
|
|
270
451
|
type: "Opaque",
|
|
271
452
|
metadata: { name: names.secret, namespace, labels: runLabels(spec.runId) },
|
|
272
|
-
stringData: { capability },
|
|
453
|
+
stringData: { capability, ...(toolSetup !== undefined ? { [TOOL_SETUP_SECRET_KEY]: toolSetup } : {}) },
|
|
273
454
|
};
|
|
274
455
|
}
|
|
275
456
|
/**
|
|
@@ -41,6 +41,11 @@ export declare const GVISOR_RUNTIME_CLASS = "gvisor";
|
|
|
41
41
|
export declare const STORAGE_INIT_EPHEMERAL_MIB = 64;
|
|
42
42
|
/** Ephemeral storage reserved for the worker's writable layer (/tmp and /home are memory-backed). */
|
|
43
43
|
export declare const WORKER_EPHEMERAL_MIB = 1024;
|
|
44
|
+
/**
|
|
45
|
+
* A Claude run's tool runner's share of WORKER_EPHEMERAL_MIB (its writable layer; /tmp and home are
|
|
46
|
+
* memory-backed). The agent gets the rest, so a Claude pod reserves exactly what a Codex pod does.
|
|
47
|
+
*/
|
|
48
|
+
export declare const TOOL_RUNNER_EPHEMERAL_MIB = 256;
|
|
44
49
|
export interface PlatformResourceRules {
|
|
45
50
|
/** Smallest CPU request the platform accepts, in millicores. */
|
|
46
51
|
cpuFloorMillicores: number;
|
|
@@ -118,11 +123,12 @@ export interface ContainerResources {
|
|
|
118
123
|
*/
|
|
119
124
|
export declare function conformResources(profile: KubernetesPlatformProfile, request: ContainerResourceRequest): ContainerResources;
|
|
120
125
|
/**
|
|
121
|
-
* The pod's total ephemeral storage: max(init containers) + sum(regular containers).
|
|
122
|
-
* storage-init's 64 MiB never exceeds
|
|
123
|
-
*
|
|
126
|
+
* The pod's total ephemeral storage: max(init containers) + sum(regular containers and sidecars).
|
|
127
|
+
* storage-init's 64 MiB never exceeds the rest, so the total is the keeper's storage volume, the
|
|
128
|
+
* worker's reservation, and whatever the run's service sidecars reserve (their data and scratch
|
|
129
|
+
* emptyDirs: serviceEphemeralStorageMib in kubernetes-isolation.ts).
|
|
124
130
|
*/
|
|
125
|
-
export declare function podEphemeralStorageMib(diskMb: number): number;
|
|
131
|
+
export declare function podEphemeralStorageMib(diskMb: number, servicesMib?: number): number;
|
|
126
132
|
/**
|
|
127
133
|
* Renders a MiB figure with its GiB equivalent, e.g. `10240 MiB (10 GiB)`. Both
|
|
128
134
|
* places that report the ephemeral-storage ceiling use this rather than writing
|
|
@@ -12,6 +12,11 @@ export const GVISOR_RUNTIME_CLASS = "gvisor";
|
|
|
12
12
|
export const STORAGE_INIT_EPHEMERAL_MIB = 64;
|
|
13
13
|
/** Ephemeral storage reserved for the worker's writable layer (/tmp and /home are memory-backed). */
|
|
14
14
|
export const WORKER_EPHEMERAL_MIB = 1024;
|
|
15
|
+
/**
|
|
16
|
+
* A Claude run's tool runner's share of WORKER_EPHEMERAL_MIB (its writable layer; /tmp and home are
|
|
17
|
+
* memory-backed). The agent gets the rest, so a Claude pod reserves exactly what a Codex pod does.
|
|
18
|
+
*/
|
|
19
|
+
export const TOOL_RUNNER_EPHEMERAL_MIB = 256;
|
|
15
20
|
/**
|
|
16
21
|
* Deep-freezes a profile. An allowance list is the set of differences attestation
|
|
17
22
|
* will forgive: nothing at runtime — a later module, a test helper, a mistaken
|
|
@@ -174,12 +179,13 @@ export function conformResources(profile, request) {
|
|
|
174
179
|
return { requests, limits: { ...requests } };
|
|
175
180
|
}
|
|
176
181
|
/**
|
|
177
|
-
* The pod's total ephemeral storage: max(init containers) + sum(regular containers).
|
|
178
|
-
* storage-init's 64 MiB never exceeds
|
|
179
|
-
*
|
|
182
|
+
* The pod's total ephemeral storage: max(init containers) + sum(regular containers and sidecars).
|
|
183
|
+
* storage-init's 64 MiB never exceeds the rest, so the total is the keeper's storage volume, the
|
|
184
|
+
* worker's reservation, and whatever the run's service sidecars reserve (their data and scratch
|
|
185
|
+
* emptyDirs: serviceEphemeralStorageMib in kubernetes-isolation.ts).
|
|
180
186
|
*/
|
|
181
|
-
export function podEphemeralStorageMib(diskMb) {
|
|
182
|
-
return diskMb + WORKER_EPHEMERAL_MIB;
|
|
187
|
+
export function podEphemeralStorageMib(diskMb, servicesMib = 0) {
|
|
188
|
+
return diskMb + WORKER_EPHEMERAL_MIB + servicesMib;
|
|
183
189
|
}
|
|
184
190
|
/**
|
|
185
191
|
* Renders a MiB figure with its GiB equivalent, e.g. `10240 MiB (10 GiB)`. Both
|
|
@@ -191,6 +191,9 @@ async function runCanary(options, state, proxyIp, timeoutMs, cleanupTimeoutMs) {
|
|
|
191
191
|
namespace,
|
|
192
192
|
proxyIp,
|
|
193
193
|
runtimeClassName: config.runtimeClassName,
|
|
194
|
+
// The canary carries the class too, so a class missing from the cluster fails the
|
|
195
|
+
// preflight rather than the first real run.
|
|
196
|
+
priorityClassName: config.priorityClassName,
|
|
194
197
|
platform: config.platform,
|
|
195
198
|
});
|
|
196
199
|
const podSpec = pod.spec;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Readable } from "node:stream";
|
|
2
2
|
import type { KubernetesJobConfig } from "../../config/providers.js";
|
|
3
3
|
import { type KubernetesApi } from "./kubernetes-api.js";
|
|
4
|
+
import type { CodingProvider } from "../../coding/provider.js";
|
|
4
5
|
import type { JobHandle, JobResult, JobSpec, JobStatus, WorkspaceJobLauncher } from "./types.js";
|
|
5
6
|
export interface KubernetesClusterInfo {
|
|
6
7
|
/** The proxy Service's ClusterIP: the enforcement witness's address (8787 reachable, 8788 not). */
|
|
@@ -20,8 +21,19 @@ export interface KubernetesJobLauncherOptions {
|
|
|
20
21
|
now?: () => number;
|
|
21
22
|
sleep?: (milliseconds: number) => Promise<void>;
|
|
22
23
|
readyTimeoutMs?: number;
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* How long to wait for the run's NetworkPolicy to be enforced before seeding. Default 30_000,
|
|
26
|
+
* but never *less* than `enforcementExecTimeoutMs * ENFORCEMENT_BLOCKED_STREAK`: see the
|
|
27
|
+
* constructor. An explicit override here is a floor raise, not an escape hatch — the gate still
|
|
28
|
+
* needs room for a full streak of probes at whatever the effective exec timeout is.
|
|
29
|
+
*/
|
|
24
30
|
enforcementTimeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Bound for a single enforcement probe exec. Default 10_000 (DEFAULT_ENFORCEMENT_EXEC_TIMEOUT_MS).
|
|
33
|
+
* Raise this on a resource-constrained cluster (e.g. `kind` on a laptop) where a healthy probe
|
|
34
|
+
* can legitimately take longer than 10 s to run inside the keeper's CPU/memory limits.
|
|
35
|
+
*/
|
|
36
|
+
enforcementExecTimeoutMs?: number;
|
|
25
37
|
createArchive?: (directory: string) => {
|
|
26
38
|
stream: Readable;
|
|
27
39
|
done: Promise<number>;
|
|
@@ -44,8 +56,14 @@ export declare class KubernetesJobLauncher implements WorkspaceJobLauncher {
|
|
|
44
56
|
private readonly createArchive;
|
|
45
57
|
private readonly warn;
|
|
46
58
|
private readonly enforcementTimeoutMs;
|
|
59
|
+
private readonly enforcementExecTimeoutMs;
|
|
47
60
|
private preflightResult?;
|
|
48
61
|
constructor(options: KubernetesJobLauncherOptions);
|
|
62
|
+
/**
|
|
63
|
+
* Services run as native sidecars in the run pod (kubernetes-isolation.ts), for either provider:
|
|
64
|
+
* Claude Code's tool runner is a sidecar in the same pod, so it reaches them on 127.0.0.1 too.
|
|
65
|
+
*/
|
|
66
|
+
supportsServicesFor(provider: CodingProvider): boolean;
|
|
49
67
|
/**
|
|
50
68
|
* Every per-run object name derives from the run id, so the handle is known before any cluster call.
|
|
51
69
|
* The executor persists it first; a crash mid-launch then still has a handle to clean up with (the
|
|
@@ -104,7 +122,8 @@ export declare class KubernetesJobLauncher implements WorkspaceJobLauncher {
|
|
|
104
122
|
* Streams the keeper's workspace through the strict extractor into `staging`.
|
|
105
123
|
* The transfer is bounded by `timeoutMs` (see transferBudgetMs): on expiry the
|
|
106
124
|
* extraction is aborted. On any failure the output stream this method created
|
|
107
|
-
* is destroyed, since the exec's own timeout does not end it.
|
|
125
|
+
* is destroyed, since the exec's own timeout does not end it. Excluded dependency
|
|
126
|
+
* and cache paths never leave the pod (see src/coding/collect-exclude.ts).
|
|
108
127
|
*/
|
|
109
128
|
private extractWorkspace;
|
|
110
129
|
/**
|