@wardby/cli 0.2.1 → 0.4.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 +53 -4
- package/README.md +52 -13
- package/dist/claude-coding-worker/driver.d.ts +6 -1
- package/dist/claude-coding-worker/driver.js +27 -1
- package/dist/claude-coding-worker/main.js +2 -0
- 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 +14 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +284 -36
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- 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 +91 -4
- package/dist/coding/protocol.js +123 -12
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- 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 +6 -0
- 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 +13 -1
- package/dist/coding-worker/driver.js +102 -13
- package/dist/coding-worker/errors.js +8 -0
- package/dist/coding-worker/main.js +10 -2
- 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 +66 -0
- package/dist/config/providers.js +137 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +120 -10
- package/dist/core/budget-groups.js +133 -24
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +8 -2
- package/dist/core/dispatch.d.ts +74 -7
- package/dist/core/dispatch.js +382 -47
- package/dist/core/engine-native.js +44 -14
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +72 -0
- package/dist/core/host-events.js +597 -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 +78 -0
- package/dist/core/host-status.js +228 -0
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +42 -2
- package/dist/core/reconciler.js +97 -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 +354 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +33 -8
- package/dist/core/runner.js +410 -62
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- 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 +67 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +17 -2
- package/dist/core/webhooks.js +42 -3
- package/dist/generated/prisma/browser.d.ts +166 -0
- package/dist/generated/prisma/client.d.ts +166 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
- package/dist/generated/prisma/enums.d.ts +13 -0
- package/dist/generated/prisma/enums.js +12 -1
- package/dist/generated/prisma/internal/class.d.ts +242 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
- package/dist/generated/prisma/models/Agent.d.ts +682 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.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/AgentTool.d.ts +95 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.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/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/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +455 -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 +1402 -82
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.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/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +15 -3
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +22 -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 +999 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +5 -0
- package/dist/import/create.js +67 -13
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +18 -18
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +72 -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 +25 -51
- package/dist/mcp/auth/ownership.js +19 -14
- 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 +33 -2
- package/dist/mcp/auth/resource-server.js +99 -3
- package/dist/mcp/auth/self-hosted/browser.js +2 -2
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
- package/dist/mcp/context.d.ts +30 -1
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +38 -0
- package/dist/mcp/host-events/github-ingress.js +83 -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/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +10 -1
- package/dist/mcp/index.js +187 -19
- package/dist/mcp/server.js +32 -11
- package/dist/mcp/tools/agents.js +519 -50
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/datastore.js +22 -15
- 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/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +182 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +3 -6
- 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 +52 -16
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +201 -40
- package/dist/mcp/tools/trigger.js +65 -7
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +15 -0
- package/dist/mcp/transport/streamable-http.js +55 -3
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +2 -0
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +10 -2
- package/dist/providers/auth/self-hosted.js +55 -6
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
- package/dist/providers/coding-proxy/memory-ledger.js +31 -3
- package/dist/providers/coding-proxy/metering.d.ts +6 -1
- package/dist/providers/coding-proxy/metering.js +41 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
- package/dist/providers/coding-proxy/proxy.d.ts +25 -2
- package/dist/providers/coding-proxy/proxy.js +561 -38
- 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 +17 -0
- 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 +33 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +29 -0
- package/dist/providers/executor/build.d.ts +2 -2
- package/dist/providers/executor/composition.d.ts +3 -0
- package/dist/providers/executor/composition.js +28 -1
- package/dist/providers/executor/container.d.ts +134 -4
- package/dist/providers/executor/container.js +394 -37
- package/dist/providers/executor/dbos.d.ts +4 -3
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +13 -0
- package/dist/providers/executor/routing.js +18 -0
- package/dist/providers/executor/types.d.ts +34 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -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 +53 -2
- package/dist/providers/jobs/docker.js +307 -32
- 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 +24 -2
- package/dist/providers/jobs/kubernetes.js +153 -22
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/jobs/types.d.ts +18 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -5
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -8
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- 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 +10 -12
- package/dist/providers/llm/claude-provider.js +15 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +21 -11
- package/dist/providers/llm/routing.js +47 -13
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -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 +226 -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 +568 -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 +309 -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 +78 -34
- package/dist/providers/vcs/github.d.ts +94 -3
- package/dist/providers/vcs/github.js +223 -17
- package/dist/providers/vcs/types.d.ts +48 -4
- package/dist/quickstart/index.d.ts +2 -0
- package/dist/quickstart/index.js +36 -8
- 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 +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +11 -0
- package/docs/README.md +40 -0
- package/docs/agent-recipes.md +383 -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 +508 -0
- package/docs/coding-agent-setup.md +175 -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 +1068 -0
- package/docs/getting-started-gke.md +632 -0
- package/docs/getting-started-identity-provider.md +319 -0
- package/docs/getting-started.md +147 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +667 -0
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +37 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +90 -0
- package/help/deploy-gke.md +45 -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/model-unavailable.md +63 -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 +32 -0
- package/help/github.md +48 -0
- package/help/identity-and-access.md +44 -0
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/mcp.md +30 -0
- package/help/models.md +90 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +36 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +32 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +14 -3
- 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/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +663 -21
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
- package/dist/providers/llm/pricing-anthropic.js +0 -30
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
import { type CollectExclusions } from "../../coding/collect-exclude.js";
|
|
2
|
+
import type { CodingProvider } from "../../coding/provider.js";
|
|
3
|
+
import { type ServiceStateReporter } from "./service-state.js";
|
|
1
4
|
import type { JobHandle, JobResult, JobSpec, JobStatus, WorkspaceJobLauncher } from "./types.js";
|
|
5
|
+
/** How long one service image pull may take before the run fails as service_unready. */
|
|
6
|
+
export declare const SERVICE_IMAGE_PULL_TIMEOUT_MS = 300000;
|
|
7
|
+
/**
|
|
8
|
+
* The default bound on a run's whole service start-up (every service's create, start, inspection and
|
|
9
|
+
* readiness probing; image pulls excluded), matching the Kubernetes launcher's pod-start bound.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_SERVICE_READY_TIMEOUT_MS = 120000;
|
|
2
12
|
export declare const SAFE_WORKER_DIAGNOSTIC: RegExp;
|
|
3
13
|
export interface WorkerDiagnostic {
|
|
4
14
|
diagnostic: string;
|
|
@@ -14,6 +24,8 @@ export declare function parseWorkerDiagnosticLine(line: string): WorkerDiagnosti
|
|
|
14
24
|
export interface DockerCommandOptions {
|
|
15
25
|
env?: Record<string, string>;
|
|
16
26
|
maxOutputBytes?: number;
|
|
27
|
+
/** Kill the Docker client and fail after this long (service readiness probes and image pulls). */
|
|
28
|
+
timeoutMs?: number;
|
|
17
29
|
}
|
|
18
30
|
export interface DockerCommandResult {
|
|
19
31
|
stdout: string;
|
|
@@ -58,7 +70,7 @@ export declare class NodeDockerCommandRunner implements DockerCommandRunner {
|
|
|
58
70
|
export interface DockerArtifactTransfer {
|
|
59
71
|
seedDirectory(sourceDirectory: string, container: string, destination: string): Promise<void>;
|
|
60
72
|
seedInput(sourceFile: string, container: string): Promise<void>;
|
|
61
|
-
materializeDirectory(container: string, source: string, destination: string, maxBytes: number): Promise<void>;
|
|
73
|
+
materializeDirectory(container: string, source: string, destination: string, maxBytes: number, exclusions: CollectExclusions): Promise<void>;
|
|
62
74
|
}
|
|
63
75
|
export declare function dockerTransferEnvironment(path: string): NodeJS.ProcessEnv;
|
|
64
76
|
/** Streams tar archives into the unprivileged keeper; no host bind mounts are used. */
|
|
@@ -68,7 +80,7 @@ export declare class NodeDockerArtifactTransfer implements DockerArtifactTransfe
|
|
|
68
80
|
constructor(dockerBinary?: string, path?: string);
|
|
69
81
|
seedDirectory(sourceDirectory: string, container: string, destination: string): Promise<void>;
|
|
70
82
|
seedInput(sourceFile: string, container: string): Promise<void>;
|
|
71
|
-
materializeDirectory(container: string, source: string, destination: string, maxBytes: number): Promise<void>;
|
|
83
|
+
materializeDirectory(container: string, source: string, destination: string, maxBytes: number, exclusions: CollectExclusions): Promise<void>;
|
|
72
84
|
}
|
|
73
85
|
export declare function validateMaterializedWorkspace(root: string, maxBytes: number): Promise<void>;
|
|
74
86
|
export interface DockerJobLauncherOptions {
|
|
@@ -81,6 +93,15 @@ export interface DockerJobLauncherOptions {
|
|
|
81
93
|
docker?: DockerCommandRunner;
|
|
82
94
|
transfer?: DockerArtifactTransfer;
|
|
83
95
|
now?: () => number;
|
|
96
|
+
/** Waits between service readiness probes; tests replace it. */
|
|
97
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* The bound on a run's whole service start-up, image pulls excluded (default
|
|
100
|
+
* DEFAULT_SERVICE_READY_TIMEOUT_MS). Start-up never runs past the run's own deadline either.
|
|
101
|
+
*/
|
|
102
|
+
serviceReadyTimeoutMs?: number;
|
|
103
|
+
/** Display-only observer of each service's start-up state (the viewer); its errors are ignored. */
|
|
104
|
+
onServiceState?: ServiceStateReporter;
|
|
84
105
|
/** Optional observer invoked after provisioning fails but before resources are cleaned up. */
|
|
85
106
|
onProvisionFailure?: (context: {
|
|
86
107
|
runId: string;
|
|
@@ -94,10 +115,14 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
|
|
|
94
115
|
private readonly docker;
|
|
95
116
|
private readonly transfer;
|
|
96
117
|
private readonly now;
|
|
118
|
+
private readonly sleep;
|
|
119
|
+
private readonly serviceReadyTimeoutMs;
|
|
97
120
|
private readonly locks;
|
|
98
121
|
private readonly timers;
|
|
99
122
|
private readonly ready;
|
|
100
123
|
constructor(options: DockerJobLauncherOptions);
|
|
124
|
+
/** Starts JobSpec.services (a network keeper plus one hardened container per service) for Codex and Claude Code runs. */
|
|
125
|
+
supportsServicesFor(provider: CodingProvider): boolean;
|
|
101
126
|
launch(spec: JobSpec): Promise<JobHandle>;
|
|
102
127
|
status(handle: JobHandle): Promise<JobStatus>;
|
|
103
128
|
collect(handle: JobHandle): Promise<JobResult>;
|
|
@@ -107,8 +132,32 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
|
|
|
107
132
|
private initialize;
|
|
108
133
|
private launchLocked;
|
|
109
134
|
private provision;
|
|
135
|
+
/**
|
|
136
|
+
* A run with services (docs/coding-services.md). The network keeper joins the run's internal
|
|
137
|
+
* network and owns the namespace; each service joins it and must pass its catalog readiness
|
|
138
|
+
* command before the next one starts, as Kubernetes starts native sidecars. The whole start-up is
|
|
139
|
+
* bounded (ServiceStartBudget): running out fails as coding_service_unready for the service being
|
|
140
|
+
* waited on. Returns the keeper's container ID, which Docker may report as the services' and
|
|
141
|
+
* worker's network mode.
|
|
142
|
+
*/
|
|
143
|
+
private startServices;
|
|
144
|
+
/**
|
|
145
|
+
* A local copy of the digest-pinned image is used as is; otherwise it is pulled, bounded by its
|
|
146
|
+
* own limit and the run's deadline. Pull time does not count toward the start-up budget.
|
|
147
|
+
*/
|
|
148
|
+
private ensureServiceImage;
|
|
149
|
+
/**
|
|
150
|
+
* The catalog readiness command, like a Kubernetes startup probe: every periodSeconds, each
|
|
151
|
+
* attempt bounded by timeoutSeconds, failing after failureThreshold consecutive failures. A
|
|
152
|
+
* service that exited or vanished fails at once, and so does one still unready when the start-up
|
|
153
|
+
* budget runs out (each probe and wait is clipped to what is left of it). Probe output is never
|
|
154
|
+
* logged or kept, so these errors carry no cause. Returns the number of probes made; records
|
|
155
|
+
* why it failed in `failure` for the display-only state report.
|
|
156
|
+
*/
|
|
157
|
+
private waitForServiceReady;
|
|
110
158
|
private refresh;
|
|
111
159
|
private stopRecord;
|
|
160
|
+
private stopContainer;
|
|
112
161
|
private sweepExpired;
|
|
113
162
|
private cleanupResources;
|
|
114
163
|
private removeContainerIfAttested;
|
|
@@ -118,6 +167,8 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
|
|
|
118
167
|
private readResultArtifact;
|
|
119
168
|
private readWorkerFailureDiagnostic;
|
|
120
169
|
private keeperName;
|
|
170
|
+
/** For a run with services: the attested network keeper's ID, which the worker's network mode may name. */
|
|
171
|
+
private attestedNetworkKeeperId;
|
|
121
172
|
private assertHost;
|
|
122
173
|
private createAndAssert;
|
|
123
174
|
private waitForStorage;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { normalizeCollectExclusions } from "../../coding/collect-exclude.js";
|
|
3
|
+
import { pruneCollectExcluded } from "./collect-prune.js";
|
|
2
4
|
import { spawn } from "node:child_process";
|
|
3
5
|
import { lstat, mkdir, mkdtemp, open, opendir, readdir, readFile, readlink, realpath, rename, rm, unlink, } from "node:fs/promises";
|
|
4
6
|
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
@@ -6,13 +8,25 @@ import { logger } from "../../core/logger.js";
|
|
|
6
8
|
import { ensurePrivateDirectory } from "../../core/private-directory.js";
|
|
7
9
|
import { readBoundedRegularFile } from "../../coding-worker/artifact.js";
|
|
8
10
|
import { parseCodingAgentOutputJson, redactAndTruncate, MAX_CODING_ARTIFACT_BYTES, MAX_CODING_OUTPUT_ISSUES, SAFE_CODING_OUTPUT_ISSUE, } from "../../coding/protocol.js";
|
|
9
|
-
import { assertDockerHostSupportsIsolation, assertClaudeAgentContainerInspection, assertClaudeToolRunnerContainerInspection, assertIsolationNetworkInspection, assertKeeperContainerInspection, assertProxyContainerInspection, assertStorageVolumeInspection, assertWorkerContainerInspection, buildDockerIsolationPlan, } from "./docker-isolation.js";
|
|
11
|
+
import { assertDockerHostSupportsIsolation, assertClaudeAgentContainerInspection, assertClaudeToolRunnerContainerInspection, assertIsolationNetworkInspection, assertKeeperContainerInspection, assertNetworkKeeperContainerInspection, assertProxyContainerInspection, assertStorageVolumeInspection, assertWorkerContainerInspection, buildDockerIsolationPlan, CLAUDE_TOOL_SETUP_ENV, dockerSupportsServicesFor, } from "./docker-isolation.js";
|
|
12
|
+
import { serviceUnreadyError } from "../../coding/services/wording.js";
|
|
13
|
+
import { assertServiceContainerInspection, buildDockerServicePlan, dockerServiceContainerNames, } from "./docker-services.js";
|
|
14
|
+
import { claudeToolSetup } from "./claude-tool-setup.js";
|
|
15
|
+
import { reportServiceState, } from "./service-state.js";
|
|
10
16
|
import { replaceDirectoryFromStaging } from "./workspace-swap.js";
|
|
11
17
|
const BACKEND = "docker";
|
|
12
18
|
const STATE_SCHEMA_VERSION = 1;
|
|
13
19
|
const MAX_DOCKER_OUTPUT_BYTES = 1024 * 1024;
|
|
14
20
|
const MAX_DOCKER_SEED_DIAGNOSTIC_BYTES = 4 * 1024;
|
|
15
21
|
const MAX_WORKSPACE_ENTRIES = 100_000;
|
|
22
|
+
/** How long one service image pull may take before the run fails as service_unready. */
|
|
23
|
+
export const SERVICE_IMAGE_PULL_TIMEOUT_MS = 300_000;
|
|
24
|
+
/**
|
|
25
|
+
* The default bound on a run's whole service start-up (every service's create, start, inspection and
|
|
26
|
+
* readiness probing; image pulls excluded), matching the Kubernetes launcher's pod-start bound.
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_SERVICE_READY_TIMEOUT_MS = 120_000;
|
|
29
|
+
const MAX_SERVICE_PROBE_OUTPUT_BYTES = 64 * 1024;
|
|
16
30
|
const TERMINAL_PHASES = new Set(["succeeded", "failed", "stopped", "lost", "removed"]);
|
|
17
31
|
export const SAFE_WORKER_DIAGNOSTIC = /^(?:worker_[a-z_]+|coding_[a-z_]+|wardby_[a-z_]+)$/;
|
|
18
32
|
/**
|
|
@@ -81,7 +95,7 @@ export class NodeDockerCommandRunner {
|
|
|
81
95
|
if (args.some((argument) => argument.includes("\0")))
|
|
82
96
|
throw new Error("docker_argument_invalid");
|
|
83
97
|
const extraEnv = options.env ?? {};
|
|
84
|
-
if (Object.keys(extraEnv).some((key) => key !== "WARDBY_RUN_CAPABILITY"))
|
|
98
|
+
if (Object.keys(extraEnv).some((key) => key !== "WARDBY_RUN_CAPABILITY" && key !== CLAUDE_TOOL_SETUP_ENV))
|
|
85
99
|
throw new Error("docker_environment_invalid");
|
|
86
100
|
const env = {
|
|
87
101
|
PATH: this.path,
|
|
@@ -100,6 +114,13 @@ export class NodeDockerCommandRunner {
|
|
|
100
114
|
rejectPromise(new DockerCommandError(null));
|
|
101
115
|
return;
|
|
102
116
|
}
|
|
117
|
+
let timedOut = false;
|
|
118
|
+
const timer = options.timeoutMs === undefined
|
|
119
|
+
? undefined
|
|
120
|
+
: setTimeout(() => {
|
|
121
|
+
timedOut = true;
|
|
122
|
+
child.kill("SIGKILL");
|
|
123
|
+
}, options.timeoutMs);
|
|
103
124
|
const stdout = [];
|
|
104
125
|
const stderr = [];
|
|
105
126
|
let bytes = 0;
|
|
@@ -120,6 +141,11 @@ export class NodeDockerCommandRunner {
|
|
|
120
141
|
spawnFailed = true;
|
|
121
142
|
});
|
|
122
143
|
child.once("close", (code) => {
|
|
144
|
+
if (timer)
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
if (timedOut) {
|
|
147
|
+
return rejectPromise(new DockerCommandError(code, false, false, { subcommand: args[0], stderr: "timed out" }));
|
|
148
|
+
}
|
|
123
149
|
if (exceeded)
|
|
124
150
|
return rejectPromise(new DockerCommandError(code, false, true));
|
|
125
151
|
if (spawnFailed)
|
|
@@ -256,7 +282,7 @@ export class NodeDockerArtifactTransfer {
|
|
|
256
282
|
});
|
|
257
283
|
await docker.run(["container", "cp", sourceFile, `${container}:/run/wardby/storage/input/input.json`]);
|
|
258
284
|
}
|
|
259
|
-
async materializeDirectory(container, source, destination, maxBytes) {
|
|
285
|
+
async materializeDirectory(container, source, destination, maxBytes, exclusions) {
|
|
260
286
|
await replaceDirectoryFromStaging(destination, maxBytes, "docker_workspace_destination_invalid", async (staging) => {
|
|
261
287
|
await new NodeDockerCommandRunner({ dockerBinary: this.dockerBinary, homeDir: "/tmp", path: this.path }).run([
|
|
262
288
|
"container",
|
|
@@ -264,6 +290,8 @@ export class NodeDockerArtifactTransfer {
|
|
|
264
290
|
`${container}:${source}/.`,
|
|
265
291
|
staging,
|
|
266
292
|
]);
|
|
293
|
+
// Before replaceDirectoryFromStaging validates: excluded folders never count.
|
|
294
|
+
await pruneCollectExcluded(staging, exclusions);
|
|
267
295
|
});
|
|
268
296
|
}
|
|
269
297
|
}
|
|
@@ -380,6 +408,42 @@ function resourceLabels(record) {
|
|
|
380
408
|
function labelsMatch(actual, expected) {
|
|
381
409
|
return Object.entries(expected).every(([key, value]) => actual?.[key] === value);
|
|
382
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* A run's service start-up budget: `budgetMs` from the start, extended by the time image pulls took,
|
|
413
|
+
* and never past the run's deadline.
|
|
414
|
+
*/
|
|
415
|
+
class ServiceStartBudget {
|
|
416
|
+
now;
|
|
417
|
+
budgetMs;
|
|
418
|
+
deadlineAt;
|
|
419
|
+
startedAt;
|
|
420
|
+
excludedMs = 0;
|
|
421
|
+
constructor(now, budgetMs, deadlineAt) {
|
|
422
|
+
this.now = now;
|
|
423
|
+
this.budgetMs = budgetMs;
|
|
424
|
+
this.deadlineAt = deadlineAt;
|
|
425
|
+
this.startedAt = now();
|
|
426
|
+
}
|
|
427
|
+
/** What is left of the budget, capped by the run's deadline; zero once either runs out. */
|
|
428
|
+
remainingMs() {
|
|
429
|
+
const now = this.now();
|
|
430
|
+
return Math.max(0, Math.min(this.startedAt + this.excludedMs + this.budgetMs - now, this.deadlineAt - now));
|
|
431
|
+
}
|
|
432
|
+
/** What is left before the run's deadline: the only bound (besides its own) on an image pull. */
|
|
433
|
+
untilDeadlineMs() {
|
|
434
|
+
return Math.max(0, this.deadlineAt - this.now());
|
|
435
|
+
}
|
|
436
|
+
exclude(milliseconds) {
|
|
437
|
+
this.excludedMs += Math.max(0, milliseconds);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/** `coding_service_unready:<name>` (the executor's service_unready), keeping the Docker failure as the operator's cause. */
|
|
441
|
+
function serviceUnready(name, cause) {
|
|
442
|
+
const error = serviceUnreadyError(name);
|
|
443
|
+
if (cause !== undefined)
|
|
444
|
+
error.cause = cause;
|
|
445
|
+
return error;
|
|
446
|
+
}
|
|
383
447
|
function capabilityFromInspection(container) {
|
|
384
448
|
return container.Config?.Env?.find((value) => value.startsWith("WARDBY_RUN_CAPABILITY="))?.slice("WARDBY_RUN_CAPABILITY=".length);
|
|
385
449
|
}
|
|
@@ -417,6 +481,8 @@ export class DockerJobLauncher {
|
|
|
417
481
|
docker;
|
|
418
482
|
transfer;
|
|
419
483
|
now;
|
|
484
|
+
sleep;
|
|
485
|
+
serviceReadyTimeoutMs;
|
|
420
486
|
locks = new Map();
|
|
421
487
|
timers = new Map();
|
|
422
488
|
ready;
|
|
@@ -429,8 +495,17 @@ export class DockerJobLauncher {
|
|
|
429
495
|
this.docker = options.docker ?? new NodeDockerCommandRunner({ homeDir: join(this.stateRoot, ".home") });
|
|
430
496
|
this.transfer = options.transfer ?? new NodeDockerArtifactTransfer();
|
|
431
497
|
this.now = options.now ?? Date.now;
|
|
498
|
+
this.sleep =
|
|
499
|
+
options.sleep ?? ((milliseconds) => new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds)));
|
|
500
|
+
this.serviceReadyTimeoutMs = options.serviceReadyTimeoutMs ?? DEFAULT_SERVICE_READY_TIMEOUT_MS;
|
|
501
|
+
if (!Number.isFinite(this.serviceReadyTimeoutMs) || this.serviceReadyTimeoutMs <= 0)
|
|
502
|
+
throw new Error("docker_launcher_service_timeout_invalid");
|
|
432
503
|
this.ready = this.initialize();
|
|
433
504
|
}
|
|
505
|
+
/** Starts JobSpec.services (a network keeper plus one hardened container per service) for Codex and Claude Code runs. */
|
|
506
|
+
supportsServicesFor(provider) {
|
|
507
|
+
return dockerSupportsServicesFor(provider);
|
|
508
|
+
}
|
|
434
509
|
async launch(spec) {
|
|
435
510
|
await this.ready;
|
|
436
511
|
return this.withRun(spec.runId, async () => this.launchLocked(spec));
|
|
@@ -493,7 +568,9 @@ export class DockerJobLauncher {
|
|
|
493
568
|
if (resolve(destination) !== expected || !expected.startsWith(`${this.workspaceRoot}${sep}`)) {
|
|
494
569
|
throw new Error("docker_workspace_destination_invalid");
|
|
495
570
|
}
|
|
496
|
-
await this.transfer.materializeDirectory(this.keeperName(current), "/run/wardby/storage/workspace", expected, current.spec.limits.diskMb * 1024 * 1024
|
|
571
|
+
await this.transfer.materializeDirectory(this.keeperName(current), "/run/wardby/storage/workspace", expected, current.spec.limits.diskMb * 1024 * 1024,
|
|
572
|
+
// Names are always the current built-in list; only the paths come from the spec.
|
|
573
|
+
normalizeCollectExclusions(current.spec.collectExclude?.paths));
|
|
497
574
|
});
|
|
498
575
|
}
|
|
499
576
|
async stop(handle, _reason) {
|
|
@@ -557,6 +634,7 @@ export class DockerJobLauncher {
|
|
|
557
634
|
}
|
|
558
635
|
async launchLocked(spec) {
|
|
559
636
|
const plan = buildDockerIsolationPlan(spec, this.options.proxyContainer);
|
|
637
|
+
const services = buildDockerServicePlan(spec);
|
|
560
638
|
const specHash = stableSpecHash(spec);
|
|
561
639
|
const existing = await this.readRecord(spec.runId);
|
|
562
640
|
if (existing) {
|
|
@@ -581,7 +659,7 @@ export class DockerJobLauncher {
|
|
|
581
659
|
await this.refresh(existing);
|
|
582
660
|
return clone(existing.handle);
|
|
583
661
|
}
|
|
584
|
-
return this.provision(existing, plan, capability);
|
|
662
|
+
return this.provision(existing, plan, services, capability);
|
|
585
663
|
}
|
|
586
664
|
await this.validateInputArtifact(spec.inputArtifact);
|
|
587
665
|
const capability = ensureCapability(await this.options.resolveCapability(spec.runId));
|
|
@@ -600,7 +678,7 @@ export class DockerJobLauncher {
|
|
|
600
678
|
// This durable record is the idempotency fence before any Docker side effect.
|
|
601
679
|
await this.writeRecord(record);
|
|
602
680
|
try {
|
|
603
|
-
return await this.provision(record, plan, capability);
|
|
681
|
+
return await this.provision(record, plan, services, capability);
|
|
604
682
|
}
|
|
605
683
|
catch (error) {
|
|
606
684
|
await this.options
|
|
@@ -613,7 +691,7 @@ export class DockerJobLauncher {
|
|
|
613
691
|
throw error;
|
|
614
692
|
}
|
|
615
693
|
}
|
|
616
|
-
async provision(record, plan, capability) {
|
|
694
|
+
async provision(record, plan, services, capability) {
|
|
617
695
|
await this.assertHost();
|
|
618
696
|
const labels = resourceLabels(record);
|
|
619
697
|
await this.createAndAssert(appendLabels(plan.networkCreateArgs, labels), ["network", "inspect", plan.names.network], (value) => {
|
|
@@ -640,13 +718,21 @@ export class DockerJobLauncher {
|
|
|
640
718
|
await this.run(plan.proxyNetworkConnectArgs);
|
|
641
719
|
const proxy = await this.inspect(["container", "inspect", this.options.proxyContainer]);
|
|
642
720
|
assertProxyContainerInspection(proxy, record.runId);
|
|
721
|
+
// Built before anything starts, so a setup past the tool runner's bounds fails the launch early.
|
|
722
|
+
const toolSetup = record.spec.provider === "claude-code" ? claudeToolSetup(record.spec, capability) : undefined;
|
|
723
|
+
// Services first (a run with services only): the Codex worker and Claude Code's tool runner join
|
|
724
|
+
// the network keeper's namespace, so it must exist, and every service must pass readiness before
|
|
725
|
+
// anything that runs the run's commands starts.
|
|
726
|
+
const networkKeeperId = plan.networkKeeperCreateArgs
|
|
727
|
+
? await this.startServices(record, plan.networkKeeperCreateArgs, plan.names.networkKeeperContainer, services, labels)
|
|
728
|
+
: undefined;
|
|
643
729
|
if (plan.toolCreateArgs) {
|
|
644
730
|
await this.createAndAssert(appendLabels(plan.toolCreateArgs, labels), ["container", "inspect", plan.names.toolContainer], (value) => {
|
|
645
|
-
assertClaudeToolRunnerContainerInspection(value, record.spec);
|
|
731
|
+
assertClaudeToolRunnerContainerInspection(value, record.spec, toolSetup, networkKeeperId);
|
|
646
732
|
if (!labelsMatch(value.Config?.Labels, labels)) {
|
|
647
733
|
throw new Error("docker_resource_attestation_failed");
|
|
648
734
|
}
|
|
649
|
-
});
|
|
735
|
+
}, { env: { WARDBY_TOOL_SETUP: toolSetup } });
|
|
650
736
|
await this.startContainer(plan.names.toolContainer);
|
|
651
737
|
await this.waitForToolRunner(plan.names.toolContainer);
|
|
652
738
|
}
|
|
@@ -657,7 +743,7 @@ export class DockerJobLauncher {
|
|
|
657
743
|
assertClaudeAgentContainerInspection(inspected, record.spec, capability);
|
|
658
744
|
}
|
|
659
745
|
else {
|
|
660
|
-
assertWorkerContainerInspection(inspected, record.spec, capability);
|
|
746
|
+
assertWorkerContainerInspection(inspected, record.spec, capability, networkKeeperId);
|
|
661
747
|
}
|
|
662
748
|
if (!labelsMatch(inspected.Config?.Labels, labels))
|
|
663
749
|
throw new Error("docker_resource_attestation_failed");
|
|
@@ -668,6 +754,165 @@ export class DockerJobLauncher {
|
|
|
668
754
|
this.scheduleDeadline(record);
|
|
669
755
|
return clone(record.handle);
|
|
670
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* A run with services (docs/coding-services.md). The network keeper joins the run's internal
|
|
759
|
+
* network and owns the namespace; each service joins it and must pass its catalog readiness
|
|
760
|
+
* command before the next one starts, as Kubernetes starts native sidecars. The whole start-up is
|
|
761
|
+
* bounded (ServiceStartBudget): running out fails as coding_service_unready for the service being
|
|
762
|
+
* waited on. Returns the keeper's container ID, which Docker may report as the services' and
|
|
763
|
+
* worker's network mode.
|
|
764
|
+
*/
|
|
765
|
+
async startServices(record, keeperArgs, keeperName, services, labels) {
|
|
766
|
+
const budget = new ServiceStartBudget(this.now, this.serviceReadyTimeoutMs, record.deadlineAt);
|
|
767
|
+
const keeper = (await this.createAndAssert(appendLabels(keeperArgs, labels), ["container", "inspect", keeperName], (value) => {
|
|
768
|
+
const inspected = value;
|
|
769
|
+
assertNetworkKeeperContainerInspection(inspected, record.spec);
|
|
770
|
+
if (!labelsMatch(inspected.Config?.Labels, labels))
|
|
771
|
+
throw new Error("docker_resource_attestation_failed");
|
|
772
|
+
}));
|
|
773
|
+
const keeperId = keeper.Id;
|
|
774
|
+
if (!keeperId)
|
|
775
|
+
throw new Error("docker_resource_attestation_failed");
|
|
776
|
+
await this.startContainer(keeperName);
|
|
777
|
+
const report = (name, update) => reportServiceState(this.options.onServiceState, { runId: record.runId, name, ...update });
|
|
778
|
+
for (const planned of services)
|
|
779
|
+
await report(planned.service.name, { state: "pending" });
|
|
780
|
+
for (const planned of services) {
|
|
781
|
+
const name = planned.service.name;
|
|
782
|
+
const failure = { reason: "image_unavailable" };
|
|
783
|
+
let attempts;
|
|
784
|
+
try {
|
|
785
|
+
if (budget.remainingMs() <= 0)
|
|
786
|
+
throw serviceUnready(name);
|
|
787
|
+
await this.ensureServiceImage(planned, budget);
|
|
788
|
+
if (budget.remainingMs() <= 0)
|
|
789
|
+
throw serviceUnready(name);
|
|
790
|
+
failure.reason = "start_failed";
|
|
791
|
+
let createFailure;
|
|
792
|
+
try {
|
|
793
|
+
await this.createAndAssert(appendLabels(planned.createArgs, labels), ["container", "inspect", planned.container], (value) => {
|
|
794
|
+
const inspected = value;
|
|
795
|
+
assertServiceContainerInspection(inspected, record.spec, planned.service, keeperId);
|
|
796
|
+
if (!labelsMatch(inspected.Config?.Labels, labels))
|
|
797
|
+
throw new Error("docker_resource_attestation_failed");
|
|
798
|
+
}, undefined, (error) => {
|
|
799
|
+
createFailure = error;
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
catch (error) {
|
|
803
|
+
// createAndAssert tolerates a failed create (a crash retry may find it made), so a service
|
|
804
|
+
// Docker refused to create shows up as a failed inspection; the create's own failure is the
|
|
805
|
+
// useful cause, not that generic "not found". Attestation failures rethrow as is.
|
|
806
|
+
if (error instanceof DockerCommandError)
|
|
807
|
+
throw serviceUnready(name, createFailure ?? error);
|
|
808
|
+
throw error;
|
|
809
|
+
}
|
|
810
|
+
if (budget.remainingMs() <= 0)
|
|
811
|
+
throw serviceUnready(name);
|
|
812
|
+
try {
|
|
813
|
+
await this.startContainer(planned.container);
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
throw serviceUnready(name, error);
|
|
817
|
+
}
|
|
818
|
+
failure.reason = "probe_failed";
|
|
819
|
+
await report(name, { state: "probing", attempts: 0 });
|
|
820
|
+
attempts = await this.waitForServiceReady(planned, budget, failure, (made) => report(name, { state: "probing", attempts: made }));
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
const reason = failure.reason !== "exited" && budget.remainingMs() <= 0 ? "timed_out" : failure.reason;
|
|
824
|
+
await report(name, {
|
|
825
|
+
state: "failed",
|
|
826
|
+
reason,
|
|
827
|
+
...(failure.attempts === undefined ? {} : { attempts: failure.attempts }),
|
|
828
|
+
});
|
|
829
|
+
throw error;
|
|
830
|
+
}
|
|
831
|
+
await report(name, { state: "ready", attempts });
|
|
832
|
+
}
|
|
833
|
+
return keeperId;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* A local copy of the digest-pinned image is used as is; otherwise it is pulled, bounded by its
|
|
837
|
+
* own limit and the run's deadline. Pull time does not count toward the start-up budget.
|
|
838
|
+
*/
|
|
839
|
+
async ensureServiceImage(planned, budget) {
|
|
840
|
+
try {
|
|
841
|
+
await this.run(["image", "inspect", "--format", "{{.Id}}", planned.service.image]);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
// Missing (or unreadable): pull it by its digest below.
|
|
846
|
+
}
|
|
847
|
+
const untilDeadline = budget.untilDeadlineMs();
|
|
848
|
+
if (untilDeadline <= 0)
|
|
849
|
+
throw serviceUnready(planned.service.name);
|
|
850
|
+
const startedAt = this.now();
|
|
851
|
+
try {
|
|
852
|
+
await this.run(["image", "pull", "--quiet", planned.service.image], {
|
|
853
|
+
timeoutMs: Math.min(SERVICE_IMAGE_PULL_TIMEOUT_MS, untilDeadline),
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
throw serviceUnready(planned.service.name, error);
|
|
858
|
+
}
|
|
859
|
+
finally {
|
|
860
|
+
budget.exclude(this.now() - startedAt);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* The catalog readiness command, like a Kubernetes startup probe: every periodSeconds, each
|
|
865
|
+
* attempt bounded by timeoutSeconds, failing after failureThreshold consecutive failures. A
|
|
866
|
+
* service that exited or vanished fails at once, and so does one still unready when the start-up
|
|
867
|
+
* budget runs out (each probe and wait is clipped to what is left of it). Probe output is never
|
|
868
|
+
* logged or kept, so these errors carry no cause. Returns the number of probes made; records
|
|
869
|
+
* why it failed in `failure` for the display-only state report.
|
|
870
|
+
*/
|
|
871
|
+
async waitForServiceReady(planned, budget, failure, onProbeFailed) {
|
|
872
|
+
const { command, periodSeconds, timeoutSeconds, failureThreshold } = planned.service.readiness;
|
|
873
|
+
const name = planned.service.name;
|
|
874
|
+
let failures = 0;
|
|
875
|
+
for (;;) {
|
|
876
|
+
if (budget.remainingMs() <= 0)
|
|
877
|
+
throw serviceUnready(name);
|
|
878
|
+
let state;
|
|
879
|
+
try {
|
|
880
|
+
state = await this.inspect(["container", "inspect", planned.container]);
|
|
881
|
+
}
|
|
882
|
+
catch (error) {
|
|
883
|
+
if (error instanceof DockerCommandError) {
|
|
884
|
+
failure.reason = "exited";
|
|
885
|
+
throw serviceUnready(name);
|
|
886
|
+
}
|
|
887
|
+
throw error;
|
|
888
|
+
}
|
|
889
|
+
if (state.State?.Running !== true) {
|
|
890
|
+
failure.reason = "exited";
|
|
891
|
+
throw serviceUnready(name);
|
|
892
|
+
}
|
|
893
|
+
const remaining = budget.remainingMs();
|
|
894
|
+
if (remaining <= 0)
|
|
895
|
+
throw serviceUnready(name);
|
|
896
|
+
try {
|
|
897
|
+
await this.run(["container", "exec", planned.container, ...command], {
|
|
898
|
+
timeoutMs: Math.min(timeoutSeconds * 1_000, remaining),
|
|
899
|
+
maxOutputBytes: MAX_SERVICE_PROBE_OUTPUT_BYTES,
|
|
900
|
+
});
|
|
901
|
+
return failures + 1;
|
|
902
|
+
}
|
|
903
|
+
catch {
|
|
904
|
+
failures += 1;
|
|
905
|
+
failure.attempts = failures;
|
|
906
|
+
if (failures >= failureThreshold)
|
|
907
|
+
throw serviceUnready(name);
|
|
908
|
+
}
|
|
909
|
+
await onProbeFailed(failures);
|
|
910
|
+
const wait = Math.min(periodSeconds * 1_000, budget.remainingMs());
|
|
911
|
+
if (wait <= 0)
|
|
912
|
+
throw serviceUnready(name);
|
|
913
|
+
await this.sleep(wait);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
671
916
|
async refresh(record) {
|
|
672
917
|
if (isTerminal(record))
|
|
673
918
|
return;
|
|
@@ -701,7 +946,7 @@ export class DockerJobLauncher {
|
|
|
701
946
|
"inspect",
|
|
702
947
|
buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.toolContainer,
|
|
703
948
|
]);
|
|
704
|
-
assertClaudeToolRunnerContainerInspection(tool, record.spec);
|
|
949
|
+
assertClaudeToolRunnerContainerInspection(tool, record.spec, claudeToolSetup(record.spec, capability), await this.attestedNetworkKeeperId(record));
|
|
705
950
|
if (tool.State?.Running !== true || tool.State?.Status !== "running") {
|
|
706
951
|
record.phase = "failed";
|
|
707
952
|
record.result = { exitCode: 1, reason: "failed", diagnostic: "worker_tool_runner_failed" };
|
|
@@ -711,7 +956,7 @@ export class DockerJobLauncher {
|
|
|
711
956
|
}
|
|
712
957
|
}
|
|
713
958
|
else {
|
|
714
|
-
assertWorkerContainerInspection(inspection, record.spec, capability);
|
|
959
|
+
assertWorkerContainerInspection(inspection, record.spec, capability, await this.attestedNetworkKeeperId(record));
|
|
715
960
|
}
|
|
716
961
|
const phase = dockerState(inspection);
|
|
717
962
|
if (!phase || phase === "provisioning" || phase === "active")
|
|
@@ -724,28 +969,27 @@ export class DockerJobLauncher {
|
|
|
724
969
|
async stopRecord(record, timedOut) {
|
|
725
970
|
if (isTerminal(record))
|
|
726
971
|
return;
|
|
727
|
-
|
|
728
|
-
await this.run(["container", "stop", "--time", "10", record.handle.id]);
|
|
729
|
-
}
|
|
730
|
-
catch (error) {
|
|
731
|
-
if (!(error instanceof DockerCommandError && error.notFound))
|
|
732
|
-
throw new Error("docker_stop_failed", { cause: error });
|
|
733
|
-
}
|
|
972
|
+
await this.stopContainer(record.handle.id);
|
|
734
973
|
if (record.spec.provider === "claude-code") {
|
|
735
|
-
|
|
736
|
-
try {
|
|
737
|
-
await this.run(["container", "stop", "--time", "10", tool]);
|
|
738
|
-
}
|
|
739
|
-
catch (error) {
|
|
740
|
-
if (!(error instanceof DockerCommandError && error.notFound))
|
|
741
|
-
throw new Error("docker_stop_failed", { cause: error });
|
|
742
|
-
}
|
|
974
|
+
await this.stopContainer(buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.toolContainer);
|
|
743
975
|
}
|
|
976
|
+
// Services stop with the run; they (and the network keeper) are removed with its other resources.
|
|
977
|
+
for (const service of dockerServiceContainerNames(record.spec))
|
|
978
|
+
await this.stopContainer(service);
|
|
744
979
|
record.phase = timedOut ? "failed" : "stopped";
|
|
745
980
|
record.result = timedOut ? { exitCode: 124, reason: "timed_out" } : resultFor("stopped");
|
|
746
981
|
await this.writeRecord(record);
|
|
747
982
|
this.clearDeadline(record);
|
|
748
983
|
}
|
|
984
|
+
async stopContainer(name) {
|
|
985
|
+
try {
|
|
986
|
+
await this.run(["container", "stop", "--time", "10", name]);
|
|
987
|
+
}
|
|
988
|
+
catch (error) {
|
|
989
|
+
if (!(error instanceof DockerCommandError && error.notFound))
|
|
990
|
+
throw new Error("docker_stop_failed", { cause: error });
|
|
991
|
+
}
|
|
992
|
+
}
|
|
749
993
|
async sweepExpired(record) {
|
|
750
994
|
// The DB verifier ran immediately before this call; never sweep by label alone.
|
|
751
995
|
try {
|
|
@@ -765,17 +1009,26 @@ export class DockerJobLauncher {
|
|
|
765
1009
|
await this.removeContainerIfAttested(plan.names.workerContainer, labels);
|
|
766
1010
|
if (plan.toolCreateArgs)
|
|
767
1011
|
await this.removeContainerIfAttested(plan.names.toolContainer, labels);
|
|
1012
|
+
// Services, then the network keeper: nothing (the Codex worker, Claude's tool runner, a service)
|
|
1013
|
+
// may still share its namespace when it goes.
|
|
1014
|
+
// --volumes: an image VOLUME the attestation rejected must not outlive its container.
|
|
1015
|
+
for (const service of dockerServiceContainerNames(record.spec)) {
|
|
1016
|
+
await this.removeContainerIfAttested(service, labels, true);
|
|
1017
|
+
}
|
|
1018
|
+
if (plan.networkKeeperCreateArgs) {
|
|
1019
|
+
await this.removeContainerIfAttested(plan.names.networkKeeperContainer, labels);
|
|
1020
|
+
}
|
|
768
1021
|
await this.runIgnoreMissing(["network", "disconnect", plan.names.network, this.options.proxyContainer]);
|
|
769
1022
|
await this.removeContainerIfAttested(plan.names.keeperContainer, labels);
|
|
770
1023
|
await this.removeNetworkIfAttested(plan.names.network, labels);
|
|
771
1024
|
await this.removeVolumeIfAttested(plan.names.storageVolume, labels);
|
|
772
1025
|
}
|
|
773
|
-
async removeContainerIfAttested(name, labels) {
|
|
1026
|
+
async removeContainerIfAttested(name, labels, anonymousVolumes = false) {
|
|
774
1027
|
try {
|
|
775
1028
|
const inspected = await this.inspect(["container", "inspect", name]);
|
|
776
1029
|
if (!labelsMatch(inspected.Config?.Labels, labels))
|
|
777
1030
|
throw new Error("docker_resource_attestation_failed");
|
|
778
|
-
await this.runIgnoreMissing(["container", "rm", "--force", name]);
|
|
1031
|
+
await this.runIgnoreMissing(["container", "rm", "--force", ...(anonymousVolumes ? ["--volumes"] : []), name]);
|
|
779
1032
|
}
|
|
780
1033
|
catch (error) {
|
|
781
1034
|
if (!(error instanceof DockerCommandError && error.notFound))
|
|
@@ -852,18 +1105,40 @@ export class DockerJobLauncher {
|
|
|
852
1105
|
keeperName(record) {
|
|
853
1106
|
return buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.keeperContainer;
|
|
854
1107
|
}
|
|
1108
|
+
/** For a run with services: the attested network keeper's ID, which the worker's network mode may name. */
|
|
1109
|
+
async attestedNetworkKeeperId(record) {
|
|
1110
|
+
if (record.spec.services === undefined)
|
|
1111
|
+
return undefined;
|
|
1112
|
+
const name = buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.networkKeeperContainer;
|
|
1113
|
+
let keeper;
|
|
1114
|
+
try {
|
|
1115
|
+
keeper = await this.inspect(["container", "inspect", name]);
|
|
1116
|
+
}
|
|
1117
|
+
catch (error) {
|
|
1118
|
+
throw new Error("docker_status_unavailable", { cause: error });
|
|
1119
|
+
}
|
|
1120
|
+
assertNetworkKeeperContainerInspection(keeper, record.spec);
|
|
1121
|
+
if (!labelsMatch(keeper.Config?.Labels, resourceLabels(record)) || !keeper.Id) {
|
|
1122
|
+
throw new Error("docker_resource_attestation_failed");
|
|
1123
|
+
}
|
|
1124
|
+
return keeper.Id;
|
|
1125
|
+
}
|
|
855
1126
|
async assertHost() {
|
|
856
1127
|
const info = await this.inspect(["info", "--format", "{{json .}}"]);
|
|
857
1128
|
assertDockerHostSupportsIsolation(info);
|
|
858
1129
|
}
|
|
859
|
-
async createAndAssert(createArgs, inspectArgs, assert, options) {
|
|
1130
|
+
async createAndAssert(createArgs, inspectArgs, assert, options, onCreateFailure) {
|
|
860
1131
|
try {
|
|
861
1132
|
await this.run(createArgs, options);
|
|
862
1133
|
}
|
|
863
|
-
catch {
|
|
1134
|
+
catch (error) {
|
|
864
1135
|
// A retry may find a resource created before the previous process crashed.
|
|
1136
|
+
if (error instanceof DockerCommandError)
|
|
1137
|
+
onCreateFailure?.(error);
|
|
865
1138
|
}
|
|
866
|
-
|
|
1139
|
+
const inspected = await this.inspect(inspectArgs);
|
|
1140
|
+
assert(inspected);
|
|
1141
|
+
return inspected;
|
|
867
1142
|
}
|
|
868
1143
|
async waitForStorage(keeper) {
|
|
869
1144
|
// The keeper emits this fixed marker only after creating every storage
|