@wardby/cli 0.5.3 → 0.7.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 +33 -0
- package/README.md +25 -14
- package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/context.d.ts +22 -0
- package/dist/claude-coding-worker/context.js +92 -0
- package/dist/claude-coding-worker/driver.d.ts +27 -3
- package/dist/claude-coding-worker/driver.js +161 -20
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +25 -5
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +6 -2
- package/dist/cli-native-mode.d.ts +9 -0
- package/dist/cli-native-mode.js +20 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +161 -11
- package/dist/coding/claude-context.d.ts +45 -0
- package/dist/coding/claude-context.js +434 -0
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +50 -118
- package/dist/coding/profile.js +51 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +292 -270
- package/dist/coding/protocol.js +322 -15
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +12 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +51 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +16 -3
- package/dist/coding-worker/artifact.js +5 -3
- package/dist/coding-worker/debug-trace.d.ts +6 -5
- package/dist/coding-worker/debug-trace.js +13 -18
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +118 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/sdk.d.ts +6 -0
- package/dist/coding-worker/sdk.js +1 -0
- package/dist/coding-worker/skills.d.ts +23 -0
- package/dist/coding-worker/skills.js +117 -0
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +13 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/github-allowed-accounts.d.ts +15 -0
- package/dist/config/github-allowed-accounts.js +36 -0
- package/dist/config/low-power.d.ts +12 -0
- package/dist/config/low-power.js +18 -0
- package/dist/config/mobile.d.ts +12 -0
- package/dist/config/mobile.js +18 -0
- package/dist/config/providers.d.ts +65 -3
- package/dist/config/providers.js +147 -11
- package/dist/config/test-simulators.d.ts +82 -0
- package/dist/config/test-simulators.js +305 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/db.d.ts +16 -6
- package/dist/core/db.js +12 -10
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/dispatch.d.ts +53 -0
- package/dist/core/dispatch.js +167 -57
- package/dist/core/engine-native.js +10 -6
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +168 -0
- package/dist/core/grants.d.ts +19 -6
- package/dist/core/grants.js +55 -6
- package/dist/core/host-events.d.ts +67 -5
- package/dist/core/host-events.js +571 -37
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +298 -20
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +151 -20
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +16 -11
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +68 -29
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +130 -0
- package/dist/core/merge-order-check.js +343 -0
- package/dist/core/notification-dispatcher.d.ts +70 -0
- package/dist/core/notification-dispatcher.js +368 -0
- package/dist/core/notification-templates.d.ts +19 -0
- package/dist/core/notification-templates.js +139 -0
- package/dist/core/notifications.d.ts +10 -0
- package/dist/core/notifications.js +38 -0
- package/dist/core/pg-auth.d.ts +22 -0
- package/dist/core/pg-auth.js +152 -0
- package/dist/core/power-composition.d.ts +16 -0
- package/dist/core/power-composition.js +37 -0
- package/dist/core/power-idle.d.ts +14 -0
- package/dist/core/power-idle.js +30 -0
- package/dist/core/power.d.ts +71 -0
- package/dist/core/power.js +202 -0
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +8 -5
- package/dist/core/reconciler.js +37 -6
- package/dist/core/related-pull-requests.d.ts +59 -7
- package/dist/core/related-pull-requests.js +140 -25
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/repo-access.d.ts +15 -0
- package/dist/core/repo-access.js +66 -0
- package/dist/core/review-fix.d.ts +38 -1
- package/dist/core/review-fix.js +209 -7
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +23 -0
- package/dist/core/review-host-tools.js +156 -14
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +384 -5
- package/dist/core/runner.js +1244 -372
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +39 -0
- package/dist/core/secret-broker-config.js +112 -0
- package/dist/core/secret-guard.d.ts +46 -0
- package/dist/core/secret-guard.js +104 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +148 -40
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-event-recorder.d.ts +14 -0
- package/dist/core/workflow-event-recorder.js +120 -0
- package/dist/core/workflow-events.d.ts +94 -0
- package/dist/core/workflow-events.js +48 -0
- package/dist/core/workflow-run-events.d.ts +18 -0
- package/dist/core/workflow-run-events.js +75 -0
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +92 -3
- package/dist/generated/prisma/client.d.ts +92 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +364 -54
- package/dist/generated/prisma/enums.d.ts +37 -0
- package/dist/generated/prisma/enums.js +31 -0
- package/dist/generated/prisma/internal/class.d.ts +132 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1332 -142
- package/dist/generated/prisma/internal/prismaNamespace.js +227 -5
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +234 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +227 -5
- package/dist/generated/prisma/models/Agent.d.ts +464 -7
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +301 -1
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +1841 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1713 -0
- package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1551 -0
- package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
- package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
- package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
- package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
- package/dist/generated/prisma/models/NotificationChannel.js +1 -0
- package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
- package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
- package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
- package/dist/generated/prisma/models/NotificationThread.js +1 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +1619 -58
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +37 -1
- package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
- package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
- package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
- package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
- package/dist/generated/prisma/models.d.ts +12 -0
- package/dist/help/build.js +4 -2
- package/dist/help/catalog.d.ts +1 -1
- package/dist/help/catalog.js +31 -6
- package/dist/help-index.json +2584 -146
- package/dist/import/create.js +49 -3
- package/dist/import/neutral-schema.d.ts +50 -277
- package/dist/import/neutral-schema.js +7 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +11 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/repo-authorization.js +14 -0
- package/dist/mcp/auth/resource-server.d.ts +2 -1
- package/dist/mcp/auth/resource-server.js +7 -2
- package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/browser.js +19 -6
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/host-events/github-ingress.d.ts +25 -1
- package/dist/mcp/host-events/github-ingress.js +70 -1
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.js +117 -20
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +277 -24
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +9 -3
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.d.ts +2 -0
- package/dist/mcp/tools/notification-channels.js +293 -0
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/power.d.ts +2 -0
- package/dist/mcp/tools/power.js +77 -0
- package/dist/mcp/tools/repositories.js +18 -5
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.d.ts +0 -6
- package/dist/mcp/tools/runs.js +41 -14
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
- package/dist/mcp/tools/secret-broker-compat.js +23 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
- package/dist/mcp/tools/secret-elicitation-form.js +205 -10
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -54
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
- package/dist/mcp/tools/secret-elicitation.js +110 -12
- package/dist/mcp/tools/secrets.js +186 -9
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +2 -2
- package/dist/mcp/tools/tools.js +47 -11
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/native-worker/composition.d.ts +23 -0
- package/dist/native-worker/composition.js +77 -0
- package/dist/native-worker/docker-isolation.d.ts +65 -0
- package/dist/native-worker/docker-isolation.js +183 -0
- package/dist/native-worker/docker-launcher.d.ts +110 -0
- package/dist/native-worker/docker-launcher.js +297 -0
- package/dist/native-worker/gateway-server.d.ts +77 -0
- package/dist/native-worker/gateway-server.js +412 -0
- package/dist/native-worker/gateway.d.ts +70 -0
- package/dist/native-worker/gateway.js +263 -0
- package/dist/native-worker/http-server.d.ts +24 -0
- package/dist/native-worker/http-server.js +113 -0
- package/dist/native-worker/http-transport.d.ts +17 -0
- package/dist/native-worker/http-transport.js +88 -0
- package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
- package/dist/native-worker/kubernetes-isolation.js +218 -0
- package/dist/native-worker/kubernetes-launcher.d.ts +106 -0
- package/dist/native-worker/kubernetes-launcher.js +375 -0
- package/dist/native-worker/launch.d.ts +62 -0
- package/dist/native-worker/launch.js +49 -0
- package/dist/native-worker/ledger.d.ts +125 -0
- package/dist/native-worker/ledger.js +245 -0
- package/dist/native-worker/loopback.d.ts +12 -0
- package/dist/native-worker/loopback.js +44 -0
- package/dist/native-worker/main.d.ts +8 -0
- package/dist/native-worker/main.js +85 -0
- package/dist/native-worker/protocol.d.ts +296 -0
- package/dist/native-worker/protocol.js +246 -0
- package/dist/native-worker/sandbox-executor.d.ts +83 -0
- package/dist/native-worker/sandbox-executor.js +284 -0
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +29 -0
- package/dist/native-worker/stdio.js +189 -0
- package/dist/native-worker/warm-delivery.d.ts +16 -0
- package/dist/native-worker/warm-delivery.js +33 -0
- package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
- package/dist/native-worker/warm-pool-ledger.js +73 -0
- package/dist/native-worker/warm-pool.d.ts +107 -0
- package/dist/native-worker/warm-pool.js +283 -0
- package/dist/native-worker/worker.d.ts +17 -0
- package/dist/native-worker/worker.js +141 -0
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/authorization-server.d.ts +3 -0
- package/dist/providers/auth/index.js +2 -0
- package/dist/providers/auth/self-hosted.d.ts +14 -2
- package/dist/providers/auth/self-hosted.js +54 -4
- package/dist/providers/chat/fake.d.ts +46 -0
- package/dist/providers/chat/fake.js +39 -0
- package/dist/providers/chat/index.d.ts +4 -0
- package/dist/providers/chat/index.js +8 -0
- package/dist/providers/chat/slack.d.ts +37 -0
- package/dist/providers/chat/slack.js +105 -0
- package/dist/providers/chat/types.d.ts +42 -0
- package/dist/providers/chat/types.js +19 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/memory-ledger.js +84 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
- package/dist/providers/coding-proxy/proxy.d.ts +14 -1
- package/dist/providers/coding-proxy/proxy.js +166 -14
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +4 -0
- package/dist/providers/coding-proxy/runtime.js +78 -16
- package/dist/providers/coding-proxy/server.d.ts +6 -1
- package/dist/providers/coding-proxy/server.js +45 -1
- package/dist/providers/coding-proxy/types.d.ts +65 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +36 -2
- package/dist/providers/engine/types.js +39 -0
- package/dist/providers/executor/composition.d.ts +31 -1
- package/dist/providers/executor/composition.js +81 -8
- package/dist/providers/executor/container.d.ts +385 -9
- package/dist/providers/executor/container.js +1234 -99
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +91 -0
- package/dist/providers/executor/dbos.js +319 -26
- package/dist/providers/executor/deferred.d.ts +44 -0
- package/dist/providers/executor/deferred.js +100 -0
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +39 -10
- package/dist/providers/executor/routing.js +77 -32
- package/dist/providers/executor/types.d.ts +25 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +8 -0
- package/dist/providers/jobs/docker-isolation.js +17 -0
- package/dist/providers/jobs/docker.d.ts +1 -0
- package/dist/providers/jobs/docker.js +29 -4
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +23 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +45 -1
- package/dist/providers/jobs/kubernetes-api.d.ts +25 -1
- package/dist/providers/jobs/kubernetes-api.js +17 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
- package/dist/providers/jobs/kubernetes-client.js +61 -3
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
- package/dist/providers/jobs/kubernetes-isolation.js +23 -1
- package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
- package/dist/providers/jobs/kubernetes-platform.js +38 -1
- package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
- package/dist/providers/jobs/kubernetes-preflight.js +4 -2
- package/dist/providers/jobs/kubernetes.d.ts +42 -2
- package/dist/providers/jobs/kubernetes.js +178 -19
- package/dist/providers/jobs/metadata-probes.d.ts +11 -0
- package/dist/providers/jobs/metadata-probes.js +13 -0
- package/dist/providers/jobs/types.d.ts +42 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +39 -2
- package/dist/providers/review-host/github.js +274 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +157 -2
- package/dist/providers/review-host/types.js +8 -0
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +33 -2
- package/dist/providers/vcs/git.js +324 -22
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +246 -12
- package/dist/providers/vcs/github.js +521 -61
- package/dist/providers/vcs/index.js +8 -1
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +33 -1
- package/dist/providers/vcs/routing.d.ts +4 -2
- package/dist/providers/vcs/routing.js +6 -0
- package/dist/providers/vcs/types.d.ts +80 -3
- package/dist/quickstart/coding-seed.d.ts +21 -3
- package/dist/quickstart/coding-seed.js +283 -3
- package/dist/quickstart/images.d.ts +2 -0
- package/dist/quickstart/images.js +7 -1
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/brokered-fetch.d.ts +23 -0
- package/dist/sandbox/brokered-fetch.js +37 -0
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +35 -0
- package/dist/sandbox/host-functions.js +150 -91
- package/dist/sandbox/prelude.js +7 -1
- package/dist/sandbox/run-in-sandbox.d.ts +19 -0
- package/dist/sandbox/run-in-sandbox.js +28 -10
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +3 -1
- package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
- package/dist/sandbox/secret-broker-sigv4.js +49 -0
- package/dist/sandbox/secret-broker.d.ts +34 -0
- package/dist/sandbox/secret-broker.js +161 -0
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/user-tool.d.ts +14 -0
- package/dist/sandbox/user-tool.js +32 -0
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +25 -0
- package/dist/viewer/api-schema.d.ts +372 -2454
- package/dist/viewer/api-schema.js +29 -0
- package/dist/viewer/build-schemas.js +10 -3
- package/dist/viewer/event-bus.d.ts +6 -0
- package/dist/viewer/event-bus.js +16 -7
- package/dist/viewer/graph.d.ts +5 -2
- package/dist/viewer/graph.js +7 -2
- package/dist/viewer/http.d.ts +0 -1
- package/dist/viewer/http.js +2 -2
- package/dist/viewer/infra.js +25 -2
- package/dist/viewer/limits.d.ts +4 -0
- package/dist/viewer/limits.js +4 -0
- package/dist/viewer/openapi.d.ts +15 -0
- package/dist/viewer/openapi.js +170 -0
- package/dist/wardby-bin.js +10 -0
- package/docs/README.md +7 -0
- package/docs/agent-recipes.md +114 -27
- package/docs/architecture-runtime.md +16 -1
- package/docs/code-review-agents.md +689 -45
- package/docs/coding-agent-setup.md +602 -12
- package/docs/coding-packages.md +1037 -92
- package/docs/coding-services.md +30 -13
- package/docs/coding-worker-byo-images.md +218 -6
- package/docs/coding-worker-isolation.md +173 -44
- package/docs/getting-started-gke.md +332 -129
- package/docs/getting-started-identity-provider.md +31 -11
- package/docs/getting-started.md +14 -4
- package/docs/jira-agents.md +22 -9
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/native-sandbox.md +731 -0
- package/docs/observability.md +4 -0
- package/docs/release-verification.md +9 -0
- package/docs/security-deployment.md +146 -14
- package/docs/slack-notifications.md +268 -0
- package/docs/tool-secrets.md +284 -0
- package/docs/viewer-api.md +46 -5
- package/docs/viewer-api.openapi.yml +1404 -0
- package/help/admin-viewer.md +29 -3
- package/help/agent-recipes.md +27 -0
- package/help/architecture-agent.md +11 -6
- package/help/brokered-secrets.md +79 -0
- package/help/build-claude-code-image.md +131 -0
- package/help/build-worker-image.md +6 -7
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +243 -10
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +23 -9
- package/help/deployment-targets.md +25 -14
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-provider-not-configured.md +2 -2
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/database-auth-invalid.md +28 -0
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +29 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/low-power-disabled.md +29 -0
- package/help/errors/low-power-scale-forbidden.md +35 -0
- package/help/errors/low-power-wake-timeout.md +30 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/mobile-disabled.md +31 -0
- package/help/errors/native-sandbox-capacity.md +22 -0
- package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
- package/help/errors/native-sandbox-docker-failed.md +20 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
- package/help/errors/native-sandbox-image-not-pinned.md +19 -0
- package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-network-unenforced.md +22 -0
- package/help/errors/native-sandbox-requires-catalog.md +31 -0
- package/help/errors/native-sandbox-unavailable.md +19 -0
- package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +21 -0
- package/help/errors/native-sandbox-worker-lost.md +20 -0
- package/help/errors/native-sandbox-worker-unready.md +22 -0
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-broker-body-invalid.md +26 -0
- package/help/errors/secret-broker-changed.md +20 -0
- package/help/errors/secret-broker-config-invalid.md +30 -0
- package/help/errors/secret-broker-conflict.md +27 -0
- package/help/errors/secret-broker-destination-denied.md +28 -0
- package/help/errors/secret-broker-value-invalid.md +28 -0
- package/help/errors/secret-brokered.md +24 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/secret-not-brokered.md +25 -0
- package/help/errors/slack-auth-failed.md +48 -0
- package/help/errors/slack-channel-unreachable.md +39 -0
- package/help/errors/slack-not-configured.md +33 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/getting-started.md +4 -5
- package/help/github-app-setup.md +132 -0
- package/help/github.md +40 -1
- package/help/identity-and-access.md +7 -1
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +3 -4
- package/help/low-power-mode.md +131 -0
- package/help/merge-order-check.md +122 -0
- package/help/mobile-app.md +54 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +150 -0
- package/help/observability.md +8 -0
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +80 -16
- package/help/repo-instructions-and-skills.md +91 -0
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +43 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +76 -0
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +99 -7
- package/package.json +27 -13
- package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
- package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
- package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
- package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
- package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
- package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
- package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
- package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
- package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
- package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
- package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
- package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
- package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
- package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/schema.prisma +631 -146
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
package/docs/coding-packages.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Installing packages in coding runs
|
|
2
2
|
|
|
3
3
|
A coding agent's worker has no direct network access — it can reach only the
|
|
4
|
-
coding proxy. Registry mode lets it run `npm install`
|
|
5
|
-
anyway, by routing those requests through the proxy to a per-agent allowlist,
|
|
4
|
+
coding proxy. Registry mode lets it run `npm install` (or pnpm or yarn) and
|
|
5
|
+
`pip install` anyway, by routing those requests through the proxy to a per-agent allowlist,
|
|
6
6
|
with supply-chain safeguards and a full record of what was fetched.
|
|
7
7
|
|
|
8
8
|
**Registry mode works for both providers.** Claude Code runs every shell
|
|
@@ -25,10 +25,11 @@ one. The toolchain picks the tool-runner image: `node` uses
|
|
|
25
25
|
pytest and ruff, the same packages as the Codex `node-python` worker). As with
|
|
26
26
|
Codex, pip installs go into a virtual environment
|
|
27
27
|
(`python -m venv --system-site-packages .venv`), and pip there reaches only the
|
|
28
|
-
registry. The
|
|
29
|
-
(the shim on `PATH` that plans `npm ci`/`npm install
|
|
30
|
-
before
|
|
31
|
-
below) applies there exactly as it does for
|
|
28
|
+
registry. The lockfile check
|
|
29
|
+
(the shim on `PATH` that plans `npm ci`/`npm install`, and the pnpm and yarn
|
|
30
|
+
installs, against the proxy before they run; see "Lockfile installs:
|
|
31
|
+
verified, then approved exactly" below) applies there exactly as it does for
|
|
32
|
+
Codex. Before a Claude Code run's
|
|
32
33
|
changes are committed, the control plane rewrites any collected lockfile's
|
|
33
34
|
proxy URLs back to the ecosystem's real public registry URL (for example
|
|
34
35
|
`https://registry.npmjs.org/...`), so a lockfile in the resulting pull
|
|
@@ -42,16 +43,19 @@ request never names the internal proxy.
|
|
|
42
43
|
image to build or maintain; the agent installs what it needs at run time.
|
|
43
44
|
2. **A custom `workerImageRef` with dependencies baked in.** Still supported:
|
|
44
45
|
point the agent's profile at your own image, built on wardby's driver base
|
|
45
|
-
image
|
|
46
|
-
|
|
46
|
+
image for a Codex agent, or `FROM` the same release's tool runner image for
|
|
47
|
+
a Claude Code agent (see
|
|
48
|
+
[Bring-your-own worker images](coding-worker-byo-images.md)), with
|
|
49
|
+
everything preinstalled. Setting `workerImageRef` needs `agents:admin`
|
|
47
50
|
and the admin role, and the image must be pinned by digest.
|
|
48
51
|
3. **Both together.** Use a custom image for a toolchain or system libraries
|
|
49
52
|
the registry can't provide (a compiler, a non-Node/Python runtime, apt
|
|
50
53
|
packages), and the registry for the project-level npm/PyPI packages your
|
|
51
54
|
agent adds during the run.
|
|
52
55
|
|
|
53
|
-
npm
|
|
54
|
-
optional per run
|
|
56
|
+
npm, pnpm, yarn, pip and uv in every coding run are always pointed at the proxy —
|
|
57
|
+
this is not optional per run (see [pnpm and yarn](#pnpm-and-yarn) for how
|
|
58
|
+
each client is configured). If an agent has no allowlist entries for an ecosystem (the
|
|
55
59
|
default for a new agent), an install in that ecosystem gets a clear refusal
|
|
56
60
|
(`403 wardby_package_not_allowed`) rather than silently failing or reaching
|
|
57
61
|
the real registry.
|
|
@@ -102,7 +106,11 @@ default). Only _top-level_ entries need to be on the allowlist — once a
|
|
|
102
106
|
version is approved, the proxy reads its dependency graph from registry
|
|
103
107
|
metadata and grows the run's allowance automatically as npm or pip requests
|
|
104
108
|
each dependency's metadata, so you don't have to enumerate transitive
|
|
105
|
-
dependencies yourself.
|
|
109
|
+
dependencies yourself. A package the project adds as a new _top-level_
|
|
110
|
+
dependency, though, must be on the allowlist itself (or already in the
|
|
111
|
+
lockfile the run verified), even if some allowlisted package's dependency
|
|
112
|
+
graph contains it: see
|
|
113
|
+
[Metadata requests are answered at once](#metadata-requests-are-answered-at-once).
|
|
106
114
|
|
|
107
115
|
### PyPI extras
|
|
108
116
|
|
|
@@ -177,6 +185,54 @@ entries first.
|
|
|
177
185
|
Every safeguard below (release age, advisories, wheels only, the record of
|
|
178
186
|
what was fetched) applies to packages reached through an extra.
|
|
179
187
|
|
|
188
|
+
### Accepting advisories
|
|
189
|
+
|
|
190
|
+
A version withheld only because of a **high** or **critical** advisory can be
|
|
191
|
+
served for one agent once an approver accepts that advisory. List the advisory
|
|
192
|
+
ids in the coding profile's `packagePolicy.acceptedAdvisories` with
|
|
193
|
+
`create_agent` or `update_agent` (up to 32 unique ids; needs `packages:approve`
|
|
194
|
+
or `agents:admin`, like any other package-policy change):
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"codingProfile": {
|
|
199
|
+
"packagePolicy": {
|
|
200
|
+
"minReleaseAgeDays": 3,
|
|
201
|
+
"acceptedAdvisories": ["GHSA-xxxx-xxxx-xxxx"]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`update_agent` replaces `packagePolicy` as a whole, so always send the full
|
|
208
|
+
object: leaving out `minReleaseAgeDays` resets it to the default of 3. To
|
|
209
|
+
remove a waiver, send the whole object again without that id.
|
|
210
|
+
|
|
211
|
+
Upgrade the coding proxy together with the control plane before setting
|
|
212
|
+
`acceptedAdvisories`: an older proxy rejects the unknown policy key and fails
|
|
213
|
+
package requests for that run (it fails closed).
|
|
214
|
+
|
|
215
|
+
- Ids are matched exactly as OSV prints them (case-sensitive, for example
|
|
216
|
+
`GHSA-…`, `CVE-…`, `PYSEC-…`) against an advisory's id and its aliases, so
|
|
217
|
+
accepting a `CVE-…` id also waives the `GHSA-…` advisory that aliases it.
|
|
218
|
+
- The registry serves a version only when **every** advisory blocking it is
|
|
219
|
+
accepted. Otherwise it is still refused with `wardby_version_filtered`, and
|
|
220
|
+
the refusal names only the advisories still blocking.
|
|
221
|
+
- **Malware is never waivable.** `MAL-…` ids are rejected when you set the
|
|
222
|
+
policy, and an advisory that is malware (a `MAL-…` id or alias, or CWE-506)
|
|
223
|
+
is still withheld even if its id is listed.
|
|
224
|
+
- Each served version that needed a waiver is recorded with the reason
|
|
225
|
+
`accepted advisories: <ids>`. `get_run` shows `acceptedAdvisories` per
|
|
226
|
+
package, and the pull request body gets an **Accepted advisories** section
|
|
227
|
+
listing `name@version: ids` (only when a waiver was used).
|
|
228
|
+
- The policy is fixed when a run starts; a change applies to the next run.
|
|
229
|
+
|
|
230
|
+
To find the id, take the package named in the refusal and look it up at
|
|
231
|
+
`https://osv.dev/list?q=<package>`, or run `npm audit`. Accept an advisory only
|
|
232
|
+
after reading it and judging whether the vulnerable code is actually exposed
|
|
233
|
+
(for example, a dependency used only at build time). Remove the entry once a
|
|
234
|
+
fixed version ships, so the waiver does not outlive its reason.
|
|
235
|
+
|
|
180
236
|
### The quickstart offers a repository's declared packages
|
|
181
237
|
|
|
182
238
|
The quickstart's coding step (see
|
|
@@ -211,9 +267,16 @@ the driver image puts a small shim first on the agent's `PATH`
|
|
|
211
267
|
proxy's `POST /registry/npm/-/plan` (with the run's registry token), prints a
|
|
212
268
|
one-line summary and any refusals to stderr, and then runs the real npm with
|
|
213
269
|
the same arguments. Anything else runs npm directly, and a plan that fails
|
|
214
|
-
still runs npm: the install then goes through the usual checks below
|
|
215
|
-
|
|
216
|
-
|
|
270
|
+
still runs npm: the install then goes through the usual checks below, and
|
|
271
|
+
for the rest of the run metadata requests the proxy can't otherwise answer
|
|
272
|
+
fall back to the slower dependency-graph walk (see
|
|
273
|
+
[Metadata requests are answered at once](#metadata-requests-are-answered-at-once)).
|
|
274
|
+
A lockfileVersion 1 lockfile can't be planned (`400
|
|
275
|
+
wardby_lockfile_unsupported`), so regenerate it with a current npm before the
|
|
276
|
+
run to avoid that fallback. An npm started by another npm (a lifecycle
|
|
277
|
+
script's) doesn't plan again; its installs also go through the usual
|
|
278
|
+
checks. Python lockfiles are verified the same way, with differences listed
|
|
279
|
+
under [Python installers](#python-installers-pip-uv-and-poetry).
|
|
217
280
|
|
|
218
281
|
The proxy trusts nothing the lockfile claims. For lockfileVersion 2 or 3
|
|
219
282
|
(version 1 is refused with `400 wardby_lockfile_unsupported`; regenerate it
|
|
@@ -229,11 +292,20 @@ with npm 7 or later):
|
|
|
229
292
|
nearest enclosing package's `node_modules/<name>`), and counts only when
|
|
230
293
|
that entry is the declared package at a version inside the declared range
|
|
231
294
|
(strict semver, as the walk matches).
|
|
232
|
-
- **Reachability.** It starts from
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
the lockfile names the exact package. From
|
|
236
|
-
|
|
295
|
+
- **Reachability.** It starts from every lockfile entry that is an allowlist
|
|
296
|
+
entry, at a version inside the allowlisted range, wherever it sits in the
|
|
297
|
+
lockfile and whether or not a project declares it; a scope wildcard such as
|
|
298
|
+
`@heroui/*` counts here, since the lockfile names the exact package. From
|
|
299
|
+
there it follows only verified edges, each inside the declared range. An
|
|
300
|
+
entry nothing reaches is refused with `wardby_package_not_allowed`.
|
|
301
|
+
- **Overrides.** An npm `overrides` entry can install a version that its parent's
|
|
302
|
+
declared range doesn't allow, so the plan can't reach it through that
|
|
303
|
+
parent. It's approved, with its own dependencies, when the overridden
|
|
304
|
+
package's name is on the allowlist (and the version is inside the
|
|
305
|
+
allowlisted range); otherwise it's refused with
|
|
306
|
+
`wardby_package_not_allowed`. Allowlist the target of each override your
|
|
307
|
+
repository uses. Release age, advisories and integrity are checked as for
|
|
308
|
+
any other entry.
|
|
237
309
|
- **Release age and advisories.** Each version's publish time (read once by
|
|
238
310
|
streaming the package's full document for its `time` field, never holding
|
|
239
311
|
it) must be older than `minReleaseAgeDays`, and one OSV `querybatch`
|
|
@@ -248,9 +320,13 @@ with npm 7 or later):
|
|
|
248
320
|
its parent's tarball and needs nothing.
|
|
249
321
|
|
|
250
322
|
A refused entry refuses only itself and whatever is reachable only through
|
|
251
|
-
it; everything else is approved, as exact `name@version` pairs for the run
|
|
323
|
+
it; everything else is approved, as exact `name@version` pairs for the run,
|
|
324
|
+
and each approved package name is allowed for the run, so npm can read its
|
|
325
|
+
metadata again (as `npm install <new-package>` does for the packages already in
|
|
326
|
+
the lockfile).
|
|
252
327
|
The response is `{ "approved": <count>, "refused": [{ "name", "version",
|
|
253
|
-
"code", "reason" }] }
|
|
328
|
+
"code", "reason" }] }` (plus `"audited": <count>` in a
|
|
329
|
+
[package audit mode](#package-audit-mode) run), and each refusal is recorded like any other (within
|
|
254
330
|
the 500-per-run cap). A tarball request for an approved `name@version` is then served directly,
|
|
255
331
|
checked against the approved integrity, with no graph walk.
|
|
256
332
|
|
|
@@ -269,8 +345,10 @@ isn't used to answer downloads: the same version reached another way (an
|
|
|
269
345
|
a hostile lockfile entry can't block a package by claiming its name. Neither
|
|
270
346
|
does a version the registry couldn't be read for, nor one the plan never
|
|
271
347
|
mentioned (a package added with `npm install <new-package>`, an install
|
|
272
|
-
without a lockfile):
|
|
273
|
-
|
|
348
|
+
without a lockfile): their metadata requests go through the allowlist and the
|
|
349
|
+
run's allowances (see
|
|
350
|
+
[Metadata requests are answered at once](#metadata-requests-are-answered-at-once)),
|
|
351
|
+
and their downloads also through the dependency-graph walk below.
|
|
274
352
|
|
|
275
353
|
The per-version facts (integrity, publish time, dependencies, download URL)
|
|
276
354
|
never change, so they are stored in the database and reused by every later
|
|
@@ -283,22 +361,164 @@ one of two plan slots before it reads the lockfile at all, so an
|
|
|
283
361
|
unauthenticated or queued plan holds none of it; a run may run one plan at a
|
|
284
362
|
time and make `REGISTRY_PLAN_MAX_PER_RUN` in all (`429 wardby_plan_limit`).
|
|
285
363
|
It only reads the registry for entries it reaches, so a lockfile full of
|
|
286
|
-
unrelated packages costs nothing upstream.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
and
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
364
|
+
unrelated packages costs nothing upstream. As a rough guide, a web-app
|
|
365
|
+
lockfile of about 200 entries verifies in seconds the first time and in under a
|
|
366
|
+
second once its package facts are stored. When the release-age policy refuses
|
|
367
|
+
some entries, packages that are refused only because they're reachable through
|
|
368
|
+
those entries can't be answered from the plan, so npm's requests for them fall
|
|
369
|
+
back to the dependency-graph walk below (after a plan with any refusal, this
|
|
370
|
+
applies to metadata requests as well as downloads). That can make a failing install take
|
|
371
|
+
minutes and use much more proxy memory; fix the refusals (wait out the release
|
|
372
|
+
age or adjust the agent's policy) rather than retrying.
|
|
373
|
+
|
|
374
|
+
### pnpm and yarn
|
|
375
|
+
|
|
376
|
+
pnpm and yarn install from the same npm allowlist and the same registry
|
|
377
|
+
route as npm, with the same safeguards. The worker configures each of them
|
|
378
|
+
to reach only the proxy, with the run's registry token:
|
|
379
|
+
|
|
380
|
+
| Client | How the worker points it at the proxy |
|
|
381
|
+
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
382
|
+
| npm | `npm_config_registry`, and an npmrc (`npm_config_userconfig`) holding the token. Install scripts, the update notifier, audit and fund are off. |
|
|
383
|
+
| pnpm | npm's settings above (pnpm reads the same ones), so install scripts and the update check are off too. The shim puts its store and cache under the workspace's `.cache` (`npm_config_store_dir` and `npm_config_cache_dir`, set for pnpm alone: npm warns about settings it doesn't know). |
|
|
384
|
+
| yarn classic (1.x) | npm's registry and npmrc. yarn classic sends the token only with `always-auth`, which npm warns about, so the shim sets `npm_config_always_auth` for yarn alone. Install scripts are off with `YARN_IGNORE_SCRIPTS` (yarn classic ignores `npm_config_ignore_scripts`); the shim puts its cache under `.cache` (`npm_config_cache_folder`, for yarn alone). |
|
|
385
|
+
| yarn Berry (2 or later) | Berry reads none of npm's settings: `YARN_NPM_REGISTRY_SERVER`, `YARN_NPM_AUTH_TOKEN` with `YARN_NPM_ALWAYS_AUTH`, and the proxy's host in `YARN_UNSAFE_HTTP_WHITELIST` when the proxy is plain HTTP (Berry refuses an `http:` registry otherwise). Telemetry and scripts are off (`YARN_ENABLE_TELEMETRY=0`, `YARN_ENABLE_SCRIPTS=false`); its global folder is under `.cache`. |
|
|
386
|
+
| corepack | `COREPACK_NPM_REGISTRY` and `COREPACK_NPM_TOKEN`, so it downloads a pinned pnpm or yarn release from the proxy; no download prompt, no `packageManager` pinning, no lookup of the latest release. |
|
|
387
|
+
|
|
388
|
+
Berry's per-project `cacheFolder` is left alone, so a repository that
|
|
389
|
+
commits its cache (zero-install) keeps using it. A service's `testEnv` can't
|
|
390
|
+
set `YARN_*`, `PNPM_*` or `COREPACK_*` variables.
|
|
391
|
+
|
|
392
|
+
**Getting pnpm or yarn Berry into the run.** The stock worker images ship
|
|
393
|
+
npm, yarn classic and corepack, not pnpm or yarn Berry. The shim runs
|
|
394
|
+
`pnpm`, and `yarn` in a project whose `package.json` (or, for a workspace
|
|
395
|
+
package, the workspace root's) pins yarn 2 or later
|
|
396
|
+
(`"packageManager": "yarn@4.x"`), through corepack, which downloads that
|
|
397
|
+
release through the registry like any other package: allowlist `pnpm` for
|
|
398
|
+
pnpm, `@yarnpkg/cli-dist` for yarn 2 or later, or `yarn` for a pinned yarn
|
|
399
|
+
1.x other than the image's. The release-age policy applies to it too, so a
|
|
400
|
+
release pinned the day it shipped is refused until it is old enough.
|
|
401
|
+
corepack reads one version's document at
|
|
402
|
+
`/registry/npm/<name>/<version>`, which the proxy serves with the same checks
|
|
403
|
+
as the package's whole document. A repository that commits its yarn release
|
|
404
|
+
(`yarnPath` in `.yarnrc.yml`) needs none of this. Alternatively, bake the
|
|
405
|
+
tool into a custom worker image.
|
|
406
|
+
|
|
407
|
+
**Lockfile plans.** The shim acts as `pnpm` and `yarn` too: before
|
|
408
|
+
`pnpm install`/`i`/`add` it sends `pnpm-lock.yaml`, and before a bare
|
|
409
|
+
`yarn`, `yarn install`, `add`, `remove`, `upgrade`, `upgrade-interactive`
|
|
410
|
+
or `up` (also as `yarn workspace <name> <command>`) it sends `yarn.lock` (the nearest one
|
|
411
|
+
from the project up, so a workspace root's lockfile counts), to the same
|
|
412
|
+
`POST /registry/npm/-/plan`, which tells the formats apart by content.
|
|
413
|
+
Supported:
|
|
414
|
+
|
|
415
|
+
| Lockfile | Versions | Integrity checked against the registry | How a dependency is matched to its entry |
|
|
416
|
+
| ---------------------------- | ------------------------------------------ | -------------------------------------- | --------------------------------------------------------------------------------- |
|
|
417
|
+
| `package-lock.json` | lockfileVersion 2 and 3 | `integrity` | npm's: the nearest enclosing `node_modules/<name>` |
|
|
418
|
+
| `pnpm-lock.yaml` | lockfileVersion 6 (pnpm 8) and 9 (pnpm 9+) | `resolution.integrity` | the reference the parent's entry records (`ms: 2.1.3`, an alias's `name@version`) |
|
|
419
|
+
| `yarn.lock`, yarn classic | `# yarn lockfile v1` | `integrity` | the parent's `name@range` descriptor |
|
|
420
|
+
| `yarn.lock`, yarn 2 or later | `__metadata.version` 4 or later | none: see below | the parent's `name@range` descriptor |
|
|
421
|
+
|
|
422
|
+
Every other check is the one described above: reachability from allowlist
|
|
423
|
+
entries over dependencies the registry declares, inside their declared
|
|
424
|
+
ranges, release age, advisories, the plan's refusal records and allowances,
|
|
425
|
+
and the fallback to the walk after a failed or partial plan. Workspace
|
|
426
|
+
packages and `link:`, `portal:`, `file:`, `exec:` and `patch:` entries are
|
|
427
|
+
local and skipped (a yarn patch applies to an npm entry the lockfile lists
|
|
428
|
+
on its own); git and URL entries are refused with
|
|
429
|
+
`wardby_lockfile_entry_unsupported`. pnpm's older `lockfileVersion` 5.x
|
|
430
|
+
(pnpm 7) is refused with `400 wardby_lockfile_unsupported`: regenerate it
|
|
431
|
+
with pnpm 8 or later. The YAML lockfiles are read as plain strings, and one
|
|
432
|
+
using YAML anchors or aliases (which pnpm and yarn never write) is refused.
|
|
433
|
+
The proxy reads a `pnpm-lock.yaml` or `yarn.lock` in a separate worker
|
|
434
|
+
thread, never on the thread that serves every run's registry and model
|
|
435
|
+
traffic. The read is bounded to 15 seconds and to 384 MiB of memory for
|
|
436
|
+
every 5000 entries `REGISTRY_PLAN_MAX_ENTRIES` allows (384 MiB at the
|
|
437
|
+
default). A lockfile not read in time is answered with
|
|
438
|
+
`503 wardby_plan_incomplete`, and one that needs more memory with
|
|
439
|
+
`413 wardby_lockfile_too_large`; a retry of the same lockfile fails the same
|
|
440
|
+
way, and the install falls back to the walk, as for any failed plan. A
|
|
441
|
+
reader that fails for any other reason is the proxy's own failure: it is
|
|
442
|
+
logged (`registry.failed`) and answered with `502 wardby_upstream_error`.
|
|
443
|
+
A realistic lockfile at the default entry bound (a 3.4 MiB
|
|
444
|
+
`pnpm-lock.yaml`) reads in under a second using about 160 MiB.
|
|
445
|
+
|
|
446
|
+
**Proxy memory.** Each lockfile plan being read can use that bound on top of
|
|
447
|
+
the proxy's own memory, and two plans are read at once, so allow about
|
|
448
|
+
800 MiB of headroom for lockfile reads at the default settings (more if you
|
|
449
|
+
raise `REGISTRY_PLAN_MAX_ENTRIES`).
|
|
450
|
+
|
|
451
|
+
**yarn Berry's `checksum` is not npm's integrity.** It is yarn's own hash
|
|
452
|
+
of its cache archive, so the plan can't compare it with the registry's.
|
|
453
|
+
A Berry plan approves exact `name@version` pairs, and each download is then
|
|
454
|
+
checked against the registry's own integrity for that version, as every
|
|
455
|
+
download is. A tampered tarball is still refused (`wardby_integrity_mismatch`);
|
|
456
|
+
what a Berry plan can't catch is a lockfile entry whose checksum was edited,
|
|
457
|
+
which yarn itself rejects at install.
|
|
458
|
+
|
|
459
|
+
**yarn Berry adds `node-gyp`.** Berry gives every package with an install
|
|
460
|
+
script (such as `fsevents`) an implicit dependency on `node-gyp`, which no
|
|
461
|
+
registry metadata declares. Allowlist `node-gyp` for a Berry project whose
|
|
462
|
+
lockfile has it, or the install is refused at `node-gyp`.
|
|
463
|
+
|
|
464
|
+
**yarn classic downloads from the URLs in yarn.lock**, never from its
|
|
465
|
+
configured registry, so a frozen install can't reach the proxy on its own.
|
|
466
|
+
For the install, the shim points the lockfile's `registry.yarnpkg.com` and
|
|
467
|
+
`registry.npmjs.org` URLs at the proxy, and afterwards puts the file back
|
|
468
|
+
byte for byte; if yarn rewrote the lockfile (`yarn add`), each entry's
|
|
469
|
+
proxy URL goes back to the host that entry had (a new entry gets the host
|
|
470
|
+
most of the lockfile uses). The shim does this for every command above. A yarn classic install that
|
|
471
|
+
bypasses the shim (the real `yarn` called by its full path) gets neither this
|
|
472
|
+
nor the token, and fails.
|
|
473
|
+
|
|
474
|
+
**What a run commits.** The proxy serves each download at the registry's
|
|
475
|
+
usual path (`<proxy>/registry/npm/<name>/-/<name>-<version>.tgz`), and pnpm
|
|
476
|
+
and yarn 2 or later record a download URL only when it isn't the usual one,
|
|
477
|
+
so their lockfiles, yarn's `.pnp.cjs` and install state, and the names in
|
|
478
|
+
`.yarn/cache` name nothing of the proxy: a zero-install repository can
|
|
479
|
+
commit them as they are. npm and yarn classic record every download URL, so
|
|
480
|
+
before a run's changes are collected Wardby rewrites their `resolved` URLs
|
|
481
|
+
to public registry URLs (yarn classic's on `registry.yarnpkg.com`). It
|
|
482
|
+
also drops a pnpm `tarball` or a yarn 2+ `__archiveUrl` that still names the
|
|
483
|
+
proxy (from an older Wardby). A committed lockfile never names the proxy or
|
|
484
|
+
the token.
|
|
485
|
+
|
|
486
|
+
### Metadata requests are answered at once
|
|
487
|
+
|
|
488
|
+
When a package manager resolves a package, it reads the package's metadata
|
|
489
|
+
(npm's packument) first. The proxy serves it if the name is allowlisted, was
|
|
490
|
+
declared as a dependency (regular, optional or peer) by a package whose metadata
|
|
491
|
+
was already served in this run, or was approved by the run's lockfile plan.
|
|
492
|
+
Package managers resolve top-down, reading a parent's metadata before asking for
|
|
493
|
+
its dependencies, so every genuine dependency is already allowed when it is
|
|
494
|
+
requested. Any other name is refused at once with
|
|
495
|
+
`403 wardby_package_not_allowed`, with no dependency-graph walk.
|
|
496
|
+
|
|
497
|
+
The exception is a run whose lockfile plan failed (any error answer, such as
|
|
498
|
+
an unsupported lockfileVersion 1, `wardby_lockfile_too_large`,
|
|
499
|
+
`wardby_plan_incomplete`, `wardby_plan_limit` or `wardby_plan_in_progress`)
|
|
500
|
+
or refused an entry. Then some of the lockfile's packages may have no
|
|
501
|
+
allowance, and npm may read their metadata in no particular order (for
|
|
502
|
+
example, when it upgrades a lockfileVersion 1 lockfile or re-reads the
|
|
503
|
+
lockfile's packages on `npm install <new-package>`). For the rest of that run
|
|
504
|
+
and ecosystem, a metadata request for an unknown name goes through the
|
|
505
|
+
dependency-graph walk below, exactly as a download does, even if a later plan
|
|
506
|
+
succeeds. The proxy keeps this per run in memory, like the walk itself.
|
|
507
|
+
|
|
508
|
+
As a consequence, a **new top-level package must be allowlisted** (or already
|
|
509
|
+
be in the lockfile the run's plan approved), even if some allowlisted
|
|
510
|
+
package's dependency graph contains it. For example, if `framework` is
|
|
511
|
+
allowlisted and depends on `helper`, `npm install framework` installs `helper`
|
|
512
|
+
as its dependency, but `npm install helper` (adding it as a top-level
|
|
513
|
+
dependency) is refused until `helper` is allowlisted too.
|
|
297
514
|
|
|
298
515
|
### Lockfile installs without a plan: the dependency-graph walk
|
|
299
516
|
|
|
300
|
-
When a tarball request names a package the run hasn't approved or seen yet
|
|
301
|
-
|
|
517
|
+
When a tarball request names a package the run hasn't approved or seen yet
|
|
518
|
+
(a lockfile install requests tarballs without first reading each parent's
|
|
519
|
+
metadata), the proxy resolves the approved dependency graph on demand. A
|
|
520
|
+
metadata request starts this walk only after a lockfile plan of the run failed
|
|
521
|
+
or refused an entry (see above). The graph is
|
|
302
522
|
range-aware:
|
|
303
523
|
|
|
304
524
|
- It starts from the allowlisted packages, at their kept versions: those
|
|
@@ -351,14 +571,45 @@ try again" error (`502 wardby_upstream_error`, or
|
|
|
351
571
|
`503 wardby_audit_unavailable` if it was the audit), not with
|
|
352
572
|
`wardby_package_not_allowed`.
|
|
353
573
|
|
|
354
|
-
|
|
355
|
-
the package
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
the
|
|
361
|
-
|
|
574
|
+
The walk expands the requested name first, so a request is answered as soon as
|
|
575
|
+
the walk reaches it. It groups all of a package's ranges together, so each
|
|
576
|
+
package is expanded once however many dependents ask for it, and it reuses
|
|
577
|
+
already expanded packages through a bounded memo (entries are per run, kept for 15
|
|
578
|
+
minutes; its 32 MiB budget is shared by the whole proxy process). Because it follows every kept version inside each range, plus
|
|
579
|
+
optional and peer dependencies, its graph is usually several times larger than
|
|
580
|
+
the graph of one install. The walk runs only while a request waits for it; its
|
|
581
|
+
state is per run, so it resumes within the run and starts again in a new run.
|
|
582
|
+
Each time a walk ends, the proxy logs `registry.graph_walk` with how far it
|
|
583
|
+
got. (A run in [package audit mode](#package-audit-mode) never walks; it logs
|
|
584
|
+
`registry.audited` for each package it installs outside the allowlist.)
|
|
585
|
+
|
|
586
|
+
A request the walk could not settle gets one of four answers (a metadata
|
|
587
|
+
request gets the last three only after a failed or partial lockfile plan,
|
|
588
|
+
since otherwise it never walks):
|
|
589
|
+
|
|
590
|
+
- `403 wardby_package_not_allowed`: final. The walk finished and the package is
|
|
591
|
+
not on the allowlist, or is only reachable through a refused package. The
|
|
592
|
+
agent's owner must allowlist it.
|
|
593
|
+
- `403 wardby_package_not_allowed_pending`: the walk stopped (at
|
|
594
|
+
`REGISTRY_GRAPH_TIMEOUT_MS`) before reaching the name, so it may still be
|
|
595
|
+
reachable. The message shows how many packages were walked and how many were
|
|
596
|
+
queued. It is not transient: the agent should stop, because retrying rarely
|
|
597
|
+
helps, and the owner should allowlist the package directly.
|
|
598
|
+
- `403 wardby_graph_limit`: this run's walk reached `REGISTRY_MAX_GRAPH_PACKAGES`
|
|
599
|
+
(counting package names) before it reached the name, so the name may not be in
|
|
600
|
+
the graph at all. The bound is permanent for the run: the agent's owner can
|
|
601
|
+
allowlist the package directly, or the operator can raise the limit.
|
|
602
|
+
- `503 wardby_graph_incomplete`: only for a name that is still being checked.
|
|
603
|
+
The walk reached the name (an expanded version declares it) but timed out
|
|
604
|
+
(`REGISTRY_GRAPH_TIMEOUT_MS`) before the name's own versions were checked,
|
|
605
|
+
usually a slow or failing upstream for that package, or another request's walk
|
|
606
|
+
running out of time. Retry shortly (npm retries a 5xx on its own). When a
|
|
607
|
+
completed walk has failed nodes, the answer is `wardby_upstream_error` or
|
|
608
|
+
`wardby_audit_unavailable` instead.
|
|
609
|
+
|
|
610
|
+
Large frameworks (for example Expo) can exceed the graph limit, because the
|
|
611
|
+
walk follows every in-range version plus optional and peer dependencies.
|
|
612
|
+
Allowlist the packages the project needs directly.
|
|
362
613
|
|
|
363
614
|
The walk reads full npm packuments (the largest are tens of MB) but keeps
|
|
364
615
|
only what it needs from each: per version, its dependency specs, publish
|
|
@@ -372,6 +623,533 @@ why the GKE overlay gives the proxy 3Gi. A lockfile the shim verified
|
|
|
372
623
|
avoids the walk for every version the plan approved, and for every version it
|
|
373
624
|
refused for a reason about the version itself (too new, an advisory, ...).
|
|
374
625
|
|
|
626
|
+
## Package audit mode
|
|
627
|
+
|
|
628
|
+
From Wardby 0.7.0, an agent's owner can switch its coding runs' registry into
|
|
629
|
+
package audit mode for a limited time. A package outside the allowlist is then
|
|
630
|
+
installed instead of refused, and recorded so it can be approved afterwards.
|
|
631
|
+
Use it to find out which packages a task actually needs (a new framework, a
|
|
632
|
+
large toolchain) without a round of refusals for each one, then approve them
|
|
633
|
+
and let the mode lapse.
|
|
634
|
+
|
|
635
|
+
### What each mode does
|
|
636
|
+
|
|
637
|
+
"Outside the allowlist" means a name that is neither an allowlist entry nor
|
|
638
|
+
allowed for the run as a dependency of one.
|
|
639
|
+
|
|
640
|
+
| Situation | Off (default) | `audit` | `full` |
|
|
641
|
+
| ------------------------------------------------------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
642
|
+
| Metadata for a package outside the allowlist | `403 wardby_package_not_allowed` | served; the package is recorded as `top-level` (`unknown` after a failed or partial lockfile plan) | same as `audit` |
|
|
643
|
+
| Download of a package outside the allowlist | lockfile plan, graph walk, refusal | served with no walk; recorded as `unknown` unless its metadata or a plan said more | same as `audit` |
|
|
644
|
+
| A dependency an audited package declares | not applicable | allowed for the run; recorded as `transitive`, with the top-level audited package that brought it (`via`) | same as `audit` |
|
|
645
|
+
| A version outside an allowlist entry's own range (`foo@^1`) | `wardby_version_filtered` | unchanged: an entry's range is the owner's explicit restriction | unchanged |
|
|
646
|
+
| A high or critical advisory, not accepted | withheld | withheld | served, and every blocking advisory recorded (also for an allowlisted package) |
|
|
647
|
+
| An advisory in `packagePolicy.acceptedAdvisories` | waived, recorded on the served row | same | same |
|
|
648
|
+
| Malware (`MAL-…`, an alias of one, CWE-506) | withheld | withheld | withheld |
|
|
649
|
+
| Release age (`minReleaseAgeDays`, per file) | enforced | enforced | enforced |
|
|
650
|
+
| OSV unreachable | `503 wardby_audit_unavailable` | same | same |
|
|
651
|
+
| Source distributions, size and file limits, integrity, hosts | enforced | enforced | enforced |
|
|
652
|
+
|
|
653
|
+
No graph walk runs in an audit-mode run, so `wardby_graph_limit`,
|
|
654
|
+
`wardby_graph_incomplete` and `wardby_package_not_allowed_pending` can't
|
|
655
|
+
occur there.
|
|
656
|
+
|
|
657
|
+
### Turning it on
|
|
658
|
+
|
|
659
|
+
The agent's owner sets it with `update_agent`. It needs `packages:approve` or
|
|
660
|
+
`agents:admin` (the gate for `packageAllowlist`), and `create_agent` doesn't
|
|
661
|
+
accept it:
|
|
662
|
+
|
|
663
|
+
```json
|
|
664
|
+
{ "id": "<agent id>", "codingProfile": { "packageAuditMode": "audit", "packageAuditMinutes": 1440 } }
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
| Input | Effect |
|
|
668
|
+
| --------------------------------------------- | -------------------------------------------------------------- |
|
|
669
|
+
| `packageAuditMode: "audit"` or `"full"` | on for 24 hours |
|
|
670
|
+
| …with `packageAuditMinutes: <m>` (1 to 10080) | on for `m` minutes (at most 7 days) |
|
|
671
|
+
| `packageAuditMinutes: <m>` alone | re-arms the stored mode for `m` minutes (an error without one) |
|
|
672
|
+
| `packageAuditMode: null` | off |
|
|
673
|
+
|
|
674
|
+
Each change is logged as `coding.package_audit.set` with the agent, mode,
|
|
675
|
+
end time and who set it. `get_agent` and `update_agent` return
|
|
676
|
+
`codingProfile.packageAuditMode`, `packageAuditUntil` and
|
|
677
|
+
`packageAuditActive`, plus a warning while the window is open. An expired
|
|
678
|
+
window keeps the stored mode but is inactive.
|
|
679
|
+
|
|
680
|
+
A coding run takes the mode when it is dispatched and keeps it for its whole
|
|
681
|
+
life, queued or long-running. A run dispatched just before the window ends
|
|
682
|
+
runs with the mode for up to the agent's `timeoutSec`. Continuations and
|
|
683
|
+
review fix rounds are new dispatches and read the window again.
|
|
684
|
+
|
|
685
|
+
### What is recorded, and where
|
|
686
|
+
|
|
687
|
+
Each package version served only because of the mode (or, in `full`, served
|
|
688
|
+
despite a blocking advisory) is recorded once per run, when its first file
|
|
689
|
+
finishes downloading. A HEAD request, a metadata request or a refusal records
|
|
690
|
+
nothing. If the record can't be written, the download fails instead of being
|
|
691
|
+
installed unrecorded. The record shows up in:
|
|
692
|
+
|
|
693
|
+
- `get_run`: `packageAuditMode` and `packageAudit` (an empty list when the mode
|
|
694
|
+
was on and nothing was recorded; both absent when it was off).
|
|
695
|
+
- The pull request body: a `[!CAUTION]` banner above the agent's summary,
|
|
696
|
+
**Packages outside the allowlist** (the `top-level` names to approve, each
|
|
697
|
+
with a count of its dependencies), a separate **Relation unknown** list (see
|
|
698
|
+
below), and in `full` mode **Served despite advisories**. When
|
|
699
|
+
the mode was on and nothing was recorded, the banner says so in one line. A
|
|
700
|
+
later run on the same pull request merges the lists across its runs, and the
|
|
701
|
+
banner is kept when the **Packages** section is dropped for size.
|
|
702
|
+
- The run's GitHub or Jira status comment: a short block above the run line,
|
|
703
|
+
with `unknown` names in their own labelled sentence.
|
|
704
|
+
- Slack: a `packages_audited` event for the top-level run (see
|
|
705
|
+
[Slack notifications](slack-notifications.md)).
|
|
706
|
+
- The proxy log: `registry.audited`, one line per version, with the run,
|
|
707
|
+
ecosystem, name, version, size, relation, `via`, mode and advisory ids.
|
|
708
|
+
|
|
709
|
+
```json
|
|
710
|
+
{
|
|
711
|
+
"packageAuditMode": "full",
|
|
712
|
+
"packageAudit": [
|
|
713
|
+
{ "ecosystem": "npm", "name": "vite-plugin-pwa", "version": "1.0.3", "size": 41210, "relation": "top-level" },
|
|
714
|
+
{
|
|
715
|
+
"ecosystem": "npm",
|
|
716
|
+
"name": "workbox-build",
|
|
717
|
+
"version": "7.3.0",
|
|
718
|
+
"size": 90231,
|
|
719
|
+
"relation": "transitive",
|
|
720
|
+
"via": "vite-plugin-pwa"
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
"ecosystem": "npm",
|
|
724
|
+
"name": "lodash",
|
|
725
|
+
"version": "4.17.20",
|
|
726
|
+
"size": 1200,
|
|
727
|
+
"relation": "allowed",
|
|
728
|
+
"advisories": [{ "id": "GHSA-35jh-r3h4-6jhm", "severity": "HIGH", "summary": "Command injection in lodash" }]
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
`relation` is best effort. `unknown` means the registry couldn't tell how the
|
|
735
|
+
package reached the run (a direct tarball download, or a metadata request
|
|
736
|
+
after a lockfile plan fell back). A dependency that an audited package
|
|
737
|
+
declared first stays recorded as audited even if an allowlisted package
|
|
738
|
+
declares it later. Both errors over-report, never under-report. Because an
|
|
739
|
+
`unknown` package may really be a dependency of an allowlisted package that
|
|
740
|
+
was installed without its parent's metadata, every surface lists `unknown`
|
|
741
|
+
names apart, labelled "Relation unknown — may be a dependency; approve only
|
|
742
|
+
if needed directly", and they aren't approved unless you name them.
|
|
743
|
+
|
|
744
|
+
### Lockfile plans and the dependency cache
|
|
745
|
+
|
|
746
|
+
In audit mode a lockfile plan (`POST /registry/<ecosystem>/-/plan`) also
|
|
747
|
+
approves the lockfile entries the allowlist doesn't reach, when they pass
|
|
748
|
+
every other check. An entry a lockfile project declares is `top-level`, one
|
|
749
|
+
an audited top-level entry reaches is `transitive`, and anything else is
|
|
750
|
+
`unknown`. The projects are the root and workspaces a `package-lock.json`,
|
|
751
|
+
`pnpm-lock.yaml` or yarn 2+ `yarn.lock` lists. A yarn classic `yarn.lock`
|
|
752
|
+
lists none, so there a package is `top-level` when no other entry depends on
|
|
753
|
+
it at the same range; one that another package also requires at that range
|
|
754
|
+
shows as `transitive` (or `unknown` when nothing audited reaches it). An allowlisted name at a version outside its entry's range is never
|
|
755
|
+
approved this way. The plan's response adds `audited`, the number of
|
|
756
|
+
approvals carrying an audit record, and each download of such an approval is
|
|
757
|
+
recorded like any other audited package.
|
|
758
|
+
|
|
759
|
+
A plan that audited anything never records its lockfile for the
|
|
760
|
+
[dependency cache](#dependency-cache). A restore installs packages without
|
|
761
|
+
downloading them, so they would go unrecorded. The run installs normally
|
|
762
|
+
instead, and no snapshot of that lockfile is saved from it.
|
|
763
|
+
|
|
764
|
+
### Promoting audited packages
|
|
765
|
+
|
|
766
|
+
Add what a run recorded to the allowlist with `approve_audited_packages`
|
|
767
|
+
(`{ "runId": "<run id>" }`, optionally `"names": [...]`). Without `names` it
|
|
768
|
+
adds the `top-level` names as bare entries to the agent's `packageAllowlist`;
|
|
769
|
+
their dependencies then follow through the allowlist graph. It adds an
|
|
770
|
+
`unknown` name only when `names` lists it, and otherwise returns those names
|
|
771
|
+
as `unknown` for you to judge. For a parent run, each coding sub-run's names go to that sub-run's own
|
|
772
|
+
agent. It needs the same access as `packageAllowlist` (`packages:approve` or
|
|
773
|
+
`agents:admin`, owner only), skips names already allowed, keeps the 256-entry
|
|
774
|
+
limit per ecosystem, and is logged as `coding.package_audit.promoted`. It
|
|
775
|
+
checks every agent before writing any, so a refusal (another owner's agent, a
|
|
776
|
+
full allowlist) changes nothing. It
|
|
777
|
+
doesn't turn the mode off. You can also edit `packageAllowlist` with
|
|
778
|
+
`update_agent` yourself, for example to pin a range.
|
|
779
|
+
|
|
780
|
+
### Risks
|
|
781
|
+
|
|
782
|
+
Package audit mode weakens the supply-chain guard while it is on:
|
|
783
|
+
|
|
784
|
+
- `audit` keeps the malware, advisory and release-age checks. `full` keeps
|
|
785
|
+
only malware and release age. A confused or prompt-injected agent can
|
|
786
|
+
install a lookalike package that has no advisory yet and is older than the
|
|
787
|
+
release age.
|
|
788
|
+
- npm runs with `--ignore-scripts` and pip installs only wheels, so nothing
|
|
789
|
+
runs at install time, but the run's tests and builds still execute what was
|
|
790
|
+
installed.
|
|
791
|
+
- A scheduled agent can run many times inside one window. Keep windows short,
|
|
792
|
+
turn the mode off once you've approved what you need, and read the caution
|
|
793
|
+
banner on every affected pull request before merging.
|
|
794
|
+
|
|
795
|
+
The mode is never on by default, needs an approver's scope and the agent's
|
|
796
|
+
owner, lasts at most 7 days, is logged when set, and is reported on every
|
|
797
|
+
run it affected.
|
|
798
|
+
|
|
799
|
+
### Upgrading
|
|
800
|
+
|
|
801
|
+
The coding proxy reads each run's mode from `CodingRun.packageAuditMode` and
|
|
802
|
+
writes `RegistryAuditRecord` rows. The GKE `up.sh` applies these grants from
|
|
803
|
+
`deploy/gke/database-grants.sql` after its migrations. If you manage the proxy
|
|
804
|
+
database role yourself, grant it `SELECT` on `CodingRun.packageAuditMode` and
|
|
805
|
+
`SELECT, INSERT` on `RegistryAuditRecord` before upgrading the proxy.
|
|
806
|
+
Without them, every registry request fails with `502 wardby_upstream_error`
|
|
807
|
+
and the proxy logs `registry.failed` with a permission error.
|
|
808
|
+
|
|
809
|
+
## Dependency cache
|
|
810
|
+
|
|
811
|
+
Installing a large `node_modules` on every run is slow. With the dependency
|
|
812
|
+
cache on, Wardby prepares an npm project's `node_modules` **before the agent
|
|
813
|
+
starts**, from a snapshot kept in a blob store, and tells the agent not to
|
|
814
|
+
reinstall it. It is off by default and is turned on per agent.
|
|
815
|
+
|
|
816
|
+
### What it does
|
|
817
|
+
|
|
818
|
+
For each eligible npm project the worker prepares `node_modules` in one of
|
|
819
|
+
these ways, reported per project in [`get_run`](#what-get_run-reports):
|
|
820
|
+
|
|
821
|
+
- **`hit`**: a snapshot for exactly this lockfile exists. It is downloaded,
|
|
822
|
+
verified, and put in place. Nothing is installed. A snapshot that fails to
|
|
823
|
+
download or verify is not used: the project is installed `cold` instead.
|
|
824
|
+
- **`nearest`**: no exact snapshot, but one exists for the same repository,
|
|
825
|
+
folder and worker image with a different lockfile. It is
|
|
826
|
+
restored, `npm install --no-save` brings it to the committed lockfile
|
|
827
|
+
(usually a small change), the committed `package.json` and
|
|
828
|
+
`package-lock.json` are put back byte for byte, and a new snapshot is
|
|
829
|
+
stored.
|
|
830
|
+
- **`cold`**: no usable snapshot. `npm ci` runs and its result is stored.
|
|
831
|
+
- **`skipped`** or **`refused`**: nothing was prepared and the agent installs
|
|
832
|
+
for itself as it always did. A skip never fails the run.
|
|
833
|
+
|
|
834
|
+
Snapshots are keyed by repository, project folder, lockfile hash and worker
|
|
835
|
+
image (the Claude Code tool-runner image for Claude Code runs), all derived on
|
|
836
|
+
the server. Changing the image starts a new key; changing the lockfile falls
|
|
837
|
+
back to `nearest`. The agent's package policy is not part of the key: a
|
|
838
|
+
snapshot holds exactly the lockfile's integrity-checked packages, so agents on
|
|
839
|
+
the same repository share snapshots, and each restore is still gated by the
|
|
840
|
+
restoring agent's own current policy (see
|
|
841
|
+
[A restore needs the plan first](#the-trust-model)).
|
|
842
|
+
|
|
843
|
+
### The trust model
|
|
844
|
+
|
|
845
|
+
A snapshot holds only what Wardby's own code installed, from packages the
|
|
846
|
+
uploading run's policy approved, before the run's agent existed; and a run restores
|
|
847
|
+
one only after the proxy re-approves the committed lockfile for that run. The
|
|
848
|
+
packages themselves are whatever your allowlist and policy approve: the cache
|
|
849
|
+
doesn't vet their contents beyond what a normal install through the registry
|
|
850
|
+
does. A `nearest` base may come from another agent on the same repository and
|
|
851
|
+
contain packages outside this agent's policy; the delta install prunes them,
|
|
852
|
+
and the consistency check verifies that the tree matches the approved lockfile
|
|
853
|
+
before the agent starts.
|
|
854
|
+
|
|
855
|
+
- **Only Wardby builds snapshots, and only before the agent exists.** The
|
|
856
|
+
worker uploads a snapshot as part of its own preparation step, then seals the
|
|
857
|
+
run's upload window with the proxy. The proxy also seals the window itself on
|
|
858
|
+
the run's first model request: the worker sends none before its preparation
|
|
859
|
+
step is over, so the window is closed before the agent can act even when the
|
|
860
|
+
worker never sealed it. After the seal the proxy refuses every lookup,
|
|
861
|
+
download and upload for the run, and bytes of an upload that finish after the
|
|
862
|
+
seal are discarded. A proxy-side deadline
|
|
863
|
+
(`WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS` after the run's proxy session
|
|
864
|
+
starts) is the last backstop. A run uploads at most once per project.
|
|
865
|
+
- **A restore needs the plan first.** The worker sends the committed lockfile
|
|
866
|
+
to the proxy's lockfile plan (see
|
|
867
|
+
[Lockfile installs: verified, then approved exactly](#lockfile-installs-verified-then-approved-exactly)).
|
|
868
|
+
The proxy records the lockfile's hash only if the plan approved every package
|
|
869
|
+
under the agent's _current_ allowlist, release-age rule and advisory policy,
|
|
870
|
+
and serves a snapshot only for a recorded hash. If the policy now refuses
|
|
871
|
+
something, the project is `refused` and no snapshot is used.
|
|
872
|
+
- **Keys are server-derived.** The worker supplies only the project folder and
|
|
873
|
+
the lockfile hash. The repository and image come from the run's own
|
|
874
|
+
record, and an id that is neither the run's own key nor the recomputed
|
|
875
|
+
nearest snapshot for the same scope is answered 404.
|
|
876
|
+
- **Integrity is checked on download.** The proxy stores each snapshot's
|
|
877
|
+
SHA-256 and size and sends them with the download; the worker checks both
|
|
878
|
+
while streaming. A blob whose bytes no longer match its record is deleted by
|
|
879
|
+
the proxy when it is served, so the next run rebuilds it. The worker then
|
|
880
|
+
scans the archive before writing anything (regular files, folders and
|
|
881
|
+
relative links inside `node_modules` only; no absolute or `..` paths, hard
|
|
882
|
+
links, devices, duplicate paths, including by case or Unicode form, or links
|
|
883
|
+
that leave `node_modules`), extracts it into a private temporary folder,
|
|
884
|
+
re-verifies the tree on disk, compares the recorded package metadata (npm's
|
|
885
|
+
hidden lockfile and each package's `package.json` version) with the committed
|
|
886
|
+
lockfile, and only then renames it into place. The integrity of the bytes
|
|
887
|
+
comes from the checksum the proxy holds, not from this comparison. Any
|
|
888
|
+
failure removes the temporary tree, and the project is installed `cold`
|
|
889
|
+
instead (except when the run's time is up or the tree would not fit the
|
|
890
|
+
run's disk). The worker records that scan's result with each snapshot it
|
|
891
|
+
uploads; a restore whose downloaded bytes match the recorded digest skips
|
|
892
|
+
the separate scan, and extraction still enforces every archive rule above
|
|
893
|
+
and the disk budget.
|
|
894
|
+
- **Install scripts are not cached.** Every preparation step runs npm with
|
|
895
|
+
`--ignore-scripts`. After the upload window is sealed, and only for a project
|
|
896
|
+
whose committed lockfile lists a package with an install script, the worker
|
|
897
|
+
runs `npm rebuild` so native add-ons (esbuild, sharp and the like) work. It
|
|
898
|
+
runs with a minimal environment (`PATH`, `HOME`, `LANG`, `TMPDIR` and the
|
|
899
|
+
registry settings, never the run's credentials), starts only after the proxy
|
|
900
|
+
confirmed the seal, and nothing it writes can be uploaded.
|
|
901
|
+
|
|
902
|
+
### When it applies
|
|
903
|
+
|
|
904
|
+
- npm only: a `package.json` and a `package-lock.json` with `lockfileVersion`
|
|
905
|
+
2 or 3, at the repository root and/or in other folders up to **3 levels
|
|
906
|
+
deep**, at most **4 projects** in all (the root first, then the others in
|
|
907
|
+
path order; any further ones are skipped `too_many_projects`).
|
|
908
|
+
- Folders named `node_modules`, folders whose name starts with a dot or has a
|
|
909
|
+
space or other characters outside letters, digits and `_@+-.`, and symlinked
|
|
910
|
+
folders or lockfiles are not searched or used.
|
|
911
|
+
- npm workspaces monorepos are not supported. A project that declares
|
|
912
|
+
`workspaces`, and any project covered by another project's `workspaces`
|
|
913
|
+
globs, is skipped, and so is a folder with an `npm-shrinkwrap.json`.
|
|
914
|
+
- A folder that already has a `node_modules` is left alone.
|
|
915
|
+
- The agent needs a package allowlist (registry mode): the restore is approved
|
|
916
|
+
by the lockfile plan.
|
|
917
|
+
- Codex and Claude Code runs on the `node` and `node-python` toolchains, with
|
|
918
|
+
worker images from a Wardby release that includes the cache. See
|
|
919
|
+
[Worker images](#worker-images) before you turn it on.
|
|
920
|
+
|
|
921
|
+
### Worker images
|
|
922
|
+
|
|
923
|
+
Use the worker images from the same release as the control plane before you
|
|
924
|
+
turn `dependencyCache` on for any agent: the Codex worker images
|
|
925
|
+
(`CODING_WORKER_IMAGE` and its toolchain variants such as
|
|
926
|
+
`CODING_WORKER_IMAGE_NODE_PYTHON_3_12`), the Claude Code worker image
|
|
927
|
+
(`CODING_CLAUDE_WORKER_IMAGE`) and the Claude Code tool runner images
|
|
928
|
+
(`CODING_CLAUDE_TOOL_RUNNER_IMAGE` and its variants). An older Codex or
|
|
929
|
+
Claude Code worker image validates its input strictly and rejects a run whose
|
|
930
|
+
agent has the cache on: the run fails with `worker_input_failed`. A
|
|
931
|
+
`workerImageRef` image must also be rebuilt for this release: `FROM` this
|
|
932
|
+
release's driver image for a Codex agent, or `FROM` this release's tool runner
|
|
933
|
+
image for a Claude Code agent. `create_agent` and `update_agent` return a
|
|
934
|
+
warning when an agent has both `dependencyCache` and a `workerImageRef`;
|
|
935
|
+
Wardby can't tell how old a custom image is. A Claude Code tool runner without
|
|
936
|
+
the cache doesn't fail the run: its projects report `tool_unavailable`. See
|
|
937
|
+
[Bring-Your-Own Coding-Worker Images](coding-worker-byo-images.md#the-dependency-cache-needs-a-current-driver).
|
|
938
|
+
|
|
939
|
+
### Enable it
|
|
940
|
+
|
|
941
|
+
1. **Deployment.** On the **coding proxy**, which is the only component that
|
|
942
|
+
touches the store, set `WARDBY_DEPENDENCY_CACHE=true` and a blob store
|
|
943
|
+
(`BLOB_STORE` plus its per-store variables: `BLOB_STORE=gcs` with
|
|
944
|
+
`BLOB_STORE_GCS_BUCKET`, `BLOB_STORE=s3` with `BLOB_STORE_S3_BUCKET` and
|
|
945
|
+
`BLOB_STORE_S3_REGION`, or `BLOB_STORE=local` with `BLOB_STORE_LOCAL_DIR`).
|
|
946
|
+
With the switch on but no blob store the cache stays off and the proxy logs
|
|
947
|
+
a warning. Per target:
|
|
948
|
+
- **GKE**: set `dependency_cache_enabled = true` and
|
|
949
|
+
`dependency_cache_bucket_name` in `terraform.tfvars`, then run
|
|
950
|
+
`deploy/gke/up.sh` with `WARDBY_DEPENDENCY_CACHE=true`. See
|
|
951
|
+
[`deploy/gke/README.md`](../deploy/gke/README.md#run-dependency-cache).
|
|
952
|
+
- **AWS (EKS)**: set the Terraform variables of the same names, and give the
|
|
953
|
+
proxy container `BLOB_STORE=s3`, `BLOB_STORE_S3_BUCKET` (the
|
|
954
|
+
`dependency_cache_bucket` output), `BLOB_STORE_S3_REGION` and
|
|
955
|
+
`WARDBY_DEPENDENCY_CACHE=true`. See
|
|
956
|
+
[`deploy/aws/README.md`](../deploy/aws/README.md#run-dependency-cache).
|
|
957
|
+
- **kind harness**:
|
|
958
|
+
`WARDBY_DEPENDENCY_CACHE=true bash deploy/kind-coding/up.sh`. The store is
|
|
959
|
+
the proxy pod's `emptyDir`, so snapshots are lost when the pod restarts.
|
|
960
|
+
- **Docker quickstart**: add `WARDBY_DEPENDENCY_CACHE=true` to
|
|
961
|
+
`.wardby/.env` and restart the stack. Snapshots live on a named volume
|
|
962
|
+
that `docker compose down -v` removes. This requires wardby 0.7.0 or
|
|
963
|
+
later (earlier runtime images can't write to the volume).
|
|
964
|
+
2. **Database grants.** Where the coding proxy has its own restricted database
|
|
965
|
+
role (GKE), it needs access to `DependencySnapshot` and a few `CodingRun`
|
|
966
|
+
columns. `deploy/gke/up.sh` applies them before the rollout; see
|
|
967
|
+
[Upgrading an existing deployment](getting-started-gke.md#upgrading-an-existing-deployment).
|
|
968
|
+
3. **Per agent.** Once the [worker images](#worker-images) are current, set
|
|
969
|
+
`codingProfile.dependencyCache` to `true` with `create_agent` or
|
|
970
|
+
`update_agent`. Like the package allowlist, it needs
|
|
971
|
+
`packages:approve` or `agents:admin`. The setting is read when a run
|
|
972
|
+
starts.
|
|
973
|
+
|
|
974
|
+
### Dependency cache limits
|
|
975
|
+
|
|
976
|
+
These variables are read by the coding proxy, and only when
|
|
977
|
+
`WARDBY_DEPENDENCY_CACHE=true`. Each limit must be a positive integer; the
|
|
978
|
+
proxy refuses to start otherwise.
|
|
979
|
+
|
|
980
|
+
| Variable | Default | Meaning |
|
|
981
|
+
| ----------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
982
|
+
| `WARDBY_DEPENDENCY_CACHE` | `false` | Master switch (`true` or `false`). Also needs `BLOB_STORE`. |
|
|
983
|
+
| `WARDBY_DEPENDENCY_CACHE_MAX_SNAPSHOT_MB` | 1024 | Largest snapshot accepted. A bigger one is not stored. |
|
|
984
|
+
| `WARDBY_DEPENDENCY_CACHE_REPO_CAP_MB` | 4096 | Snapshot bytes kept per repository. Least recently used go first. |
|
|
985
|
+
| `WARDBY_DEPENDENCY_CACHE_TOTAL_CAP_MB` | 20480 | Snapshot bytes kept in all. Least recently used go first. |
|
|
986
|
+
| `WARDBY_DEPENDENCY_CACHE_MAX_AGE_DAYS` | 14 | A snapshot's lifetime, counted from creation rather than last use, so even a constantly used snapshot is rebuilt periodically. |
|
|
987
|
+
| `WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS` | 900 | How long after a run's proxy session starts the proxy still accepts an upload. |
|
|
988
|
+
|
|
989
|
+
The proxy evicts hourly: snapshots past the maximum age, then least recently
|
|
990
|
+
used ones over the per-repository cap, then over the total cap. It deletes the
|
|
991
|
+
stored object before its database record. On GKE and AWS the bucket also
|
|
992
|
+
deletes objects older than `dependency_cache_max_age_days + 7` days, which
|
|
993
|
+
removes objects that were never recorded; keep that Terraform variable equal
|
|
994
|
+
to `WARDBY_DEPENDENCY_CACHE_MAX_AGE_DAYS` by hand. The kind harness and
|
|
995
|
+
Docker quickstart stores are local and have no such rule.
|
|
996
|
+
|
|
997
|
+
Other bounds:
|
|
998
|
+
|
|
999
|
+
- The cache works within a disk budget: the run's workspace disk
|
|
1000
|
+
(`workspaceDiskMb`) less what the workspace already uses and a safety
|
|
1001
|
+
margin. If that can't be measured, or leaves under 64 MiB, the cache is
|
|
1002
|
+
skipped (`budget_unknown`, `budget_too_small`). Restores and the snapshot
|
|
1003
|
+
archive are held to the budget while they are written; an install is only
|
|
1004
|
+
checked before it starts, since npm's own writes can't be bounded while it
|
|
1005
|
+
runs.
|
|
1006
|
+
- A restore must fit: the downloaded archive plus its unpacked tree. A
|
|
1007
|
+
`nearest` snapshot is restored only when one and a half times its
|
|
1008
|
+
unpacked size fits, leaving room for the update and the new archive.
|
|
1009
|
+
- An install (`cold`) starts only when at least 1 GiB is left, or twice
|
|
1010
|
+
the project's tree size when a snapshot of it was already seen. Otherwise
|
|
1011
|
+
the project is skipped `budget_too_small` and the workspace is left as it
|
|
1012
|
+
was, so the agent installs for itself. A tree much larger than that can
|
|
1013
|
+
still fill the disk during the install, as it would for the agent.
|
|
1014
|
+
- After its own install, the worker deletes npm's download cache (the
|
|
1015
|
+
`_cacache` folder under the npm cache folder in the workspace's `.cache`;
|
|
1016
|
+
the registry settings beside it stay). It is about as large as
|
|
1017
|
+
`node_modules` again, and the agent's npm downloads what it needs.
|
|
1018
|
+
- Before archiving a tree for upload, the worker measures the workspace
|
|
1019
|
+
again. It doesn't write the archive when the estimated size (the tree
|
|
1020
|
+
at its last snapshot's compression, or a third of the tree) doesn't fit
|
|
1021
|
+
what is left, and stops and deletes an archive that outgrows it while
|
|
1022
|
+
being written. The installed tree stays and the project is still
|
|
1023
|
+
`cold` or `nearest`; only the upload is skipped. The worker log records
|
|
1024
|
+
this as `snapshot_skipped` with reason `budget` (it isn't a `get_run`
|
|
1025
|
+
reason).
|
|
1026
|
+
- The worker gives the lockfile plan up to 10 minutes (a proxy with a cold
|
|
1027
|
+
package cache checks every package of a large lockfile first), and each
|
|
1028
|
+
other proxy call up to 2 minutes. When the proxy stops at its own
|
|
1029
|
+
`REGISTRY_PLAN_TIMEOUT_MS` (`503 wardby_plan_incomplete`), the worker sends
|
|
1030
|
+
the plan again after 2 seconds and the proxy resumes from what it already
|
|
1031
|
+
verified, until the 10 minutes or 5 attempts are up (`timeout`). Each
|
|
1032
|
+
attempt counts toward `REGISTRY_PLAN_MAX_PER_RUN`; the cache sends at most
|
|
1033
|
+
8 plans per run, leaving the rest for the agent's own installs (a project
|
|
1034
|
+
past that is `plan_failed`). Raising `REGISTRY_PLAN_TIMEOUT_MS` is optional
|
|
1035
|
+
tuning: it means fewer, longer attempts.
|
|
1036
|
+
- The whole preparation, measuring the disk included, is bounded to 20
|
|
1037
|
+
minutes or half the time left before the run's deadline, whichever is
|
|
1038
|
+
sooner, so a short run keeps most of its time for the agent. A rebuild is
|
|
1039
|
+
bounded to 5 minutes, and the worker stops uploading once the proxy's upload
|
|
1040
|
+
window is nearly over.
|
|
1041
|
+
- Packing a snapshot (`tar` and gzip) gets 5 minutes plus 0.5 seconds per MiB
|
|
1042
|
+
of the installed tree (a 1 GiB tree gets about 13.5 minutes), and never runs
|
|
1043
|
+
past the upload window: a snapshot that couldn't be uploaded isn't packed.
|
|
1044
|
+
When a step runs out of time, the worker log records `snapshot_skipped` with
|
|
1045
|
+
reason `timeout`, the `stage` (`archive`, `scan` or `upload`),
|
|
1046
|
+
`writtenBytes`, `estimateBytes`, `elapsedMs`, and the `limit` that fired
|
|
1047
|
+
with its `limitMs`. No snapshot is stored, so every run on that lockfile
|
|
1048
|
+
installs again. What to raise depends on `limit`:
|
|
1049
|
+
- `pack`: the pod is short on CPU for gzip; raise `CODING_CPUS`.
|
|
1050
|
+
- `upload_window`: the install took most of the window; raise
|
|
1051
|
+
`WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS`, or `CODING_CPUS` to install
|
|
1052
|
+
faster.
|
|
1053
|
+
- `upload`: the blob store is slow to accept the archive; check the proxy's
|
|
1054
|
+
path to it.
|
|
1055
|
+
- `deadline`: the whole preparation ran out (20 minutes, or half the time
|
|
1056
|
+
left before the run's deadline); raise `CODING_CPUS` or the agent's
|
|
1057
|
+
`timeoutSec`.
|
|
1058
|
+
- A large tree also needs memory: an npm install of about 1 GiB and 400,000
|
|
1059
|
+
files can need more than the default 2048 MiB under gVisor, and a pod that
|
|
1060
|
+
runs out is killed before anything is cached. Raise `CODING_MEMORY_MB` (6144
|
|
1061
|
+
is enough for a tree that size).
|
|
1062
|
+
- On Docker the workspace can be a tmpfs, whose inode limit can bind before
|
|
1063
|
+
its byte limit on a very large restore. The cache is then skipped
|
|
1064
|
+
(`internal_error`) and the run continues.
|
|
1065
|
+
|
|
1066
|
+
### What the agent sees
|
|
1067
|
+
|
|
1068
|
+
When at least one project was restored or installed, the agent's task carries a
|
|
1069
|
+
note naming the folders: "Dependencies are pre-installed from the committed
|
|
1070
|
+
lockfile in: the repo root, web. Don't run `npm ci` there; add packages with a
|
|
1071
|
+
single install command." `npm ci` deletes `node_modules` and installs
|
|
1072
|
+
everything again, which throws away the prepared tree and the time saved,
|
|
1073
|
+
whereas `npm install <package>` works on top of it. A pull request Wardby opens
|
|
1074
|
+
gets a line, "Dependency cache: restored `node_modules` for the repo root,
|
|
1075
|
+
`web`.", naming the folders restored from a snapshot (`hit` or `nearest`).
|
|
1076
|
+
|
|
1077
|
+
### What `get_run` reports
|
|
1078
|
+
|
|
1079
|
+
`get_run` returns `dependencyCache` for a run that used the cache:
|
|
1080
|
+
|
|
1081
|
+
```json
|
|
1082
|
+
{
|
|
1083
|
+
"dependencyCache": {
|
|
1084
|
+
"projects": [
|
|
1085
|
+
{ "path": ".", "result": "hit", "key": "9f2c…", "sizeBytes": 148000000, "restoreMs": 4100, "rebuild": "ok" },
|
|
1086
|
+
{ "path": "web", "result": "cold", "installMs": 61000, "sizeBytes": 91000000 },
|
|
1087
|
+
{ "path": "tools", "result": "skipped", "reason": "workspaces" }
|
|
1088
|
+
]
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
- `result` is `hit`, `nearest`, `cold`, `skipped` or `refused`.
|
|
1094
|
+
- `key`, `sizeBytes`, `restoreMs` and `installMs` appear where they apply.
|
|
1095
|
+
- `reason` explains a `skipped` or `refused` project, or a `cold` project whose
|
|
1096
|
+
tree was kept but not stored (`inconsistent`).
|
|
1097
|
+
- `rebuild` is `ok`, `failed`, `timeout`, `skipped` (no install scripts) or
|
|
1098
|
+
`unsealed` (the proxy did not confirm the seal, so no script ran).
|
|
1099
|
+
- A top-level `reason` with an empty `projects` list means the whole step
|
|
1100
|
+
failed or was skipped. `omitted` counts projects that couldn't be listed.
|
|
1101
|
+
|
|
1102
|
+
The field is absent for a run that didn't use the cache, and for one that
|
|
1103
|
+
failed before its result was collected.
|
|
1104
|
+
|
|
1105
|
+
### Cache reason codes
|
|
1106
|
+
|
|
1107
|
+
A skipped project never fails the run. Look the `reason` up here. A snapshot
|
|
1108
|
+
that a `hit` or `nearest` restore rejects (`download_failed`, `size_mismatch`,
|
|
1109
|
+
`sha_mismatch`, `too_large`, `unsafe_archive`, `inconsistent`) is not reported
|
|
1110
|
+
as the project's reason: the project installs `cold` instead, and a traced
|
|
1111
|
+
Codex run's worker log records the code as `hit_rejected` or
|
|
1112
|
+
`nearest_rejected`. The same log times each restore's stages as
|
|
1113
|
+
`restore_phase` events (`stage` is `download`, `scan`, `extract`,
|
|
1114
|
+
`verify_links` or `check_consistency`, with `ms`), for finding a slow restore.
|
|
1115
|
+
|
|
1116
|
+
| Reason | Meaning and what to do |
|
|
1117
|
+
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1118
|
+
| `no_lockfile`, `lockfile_unreadable`, `no_package_json` | Nothing eligible in that folder. Expected for repositories without npm. |
|
|
1119
|
+
| `lockfile_version` | The lockfile is below `lockfileVersion` 2. Regenerate it with npm 7 or later. |
|
|
1120
|
+
| `workspaces`, `workspace_member`, `shrinkwrap_present` | An npm workspaces root, a folder inside one, or an `npm-shrinkwrap.json`: not supported. The agent installs for itself. |
|
|
1121
|
+
| `node_modules_present` | The checkout already has a `node_modules` there. It is left alone. |
|
|
1122
|
+
| `no_dependencies` | The lockfile installs nothing. |
|
|
1123
|
+
| `too_many_projects` | More than 4 projects were found. The rest are skipped. |
|
|
1124
|
+
| `cache_dir_unsafe` | `.cache` in the checkout isn't a plain folder (for example a committed symlink). Remove it from the repository. |
|
|
1125
|
+
| `no_registry` | The worker got no registry settings: the agent has no package allowlist. Enable registry mode first. |
|
|
1126
|
+
| `plan_failed` | The lockfile plan didn't answer properly. See [Error codes](#error-codes); retry. |
|
|
1127
|
+
| `plan_refused` (result `refused`) | The plan refused at least one package under the agent's current policy (not allowlisted, too new, advisory). Fix the allowlist or policy; no snapshot is used. |
|
|
1128
|
+
| `cache_unavailable` | The proxy has no cache for this run: the switch is off, there is no blob store, or the run's flag or image is missing. Check `WARDBY_DEPENDENCY_CACHE` and `BLOB_STORE` on the proxy. |
|
|
1129
|
+
| `plan_required`, `sealed` | The proxy refused a lookup because the lockfile wasn't approved by a plan, or the window was already sealed. Usually the preparation was cut short. |
|
|
1130
|
+
| `lookup_failed`, `download_failed` | The proxy or the blob store answered unexpectedly. Check the proxy log and the store's permissions. |
|
|
1131
|
+
| `size_mismatch`, `sha_mismatch` | The download didn't match its recorded size or digest. It is not used, and the proxy deletes the damaged snapshot. Repeats point at the blob store. |
|
|
1132
|
+
| `too_large` | A snapshot is larger than the maximum the proxy reported. Raise `WARDBY_DEPENDENCY_CACHE_MAX_SNAPSHOT_MB` if that is intended. |
|
|
1133
|
+
| `unsafe_archive` | The archive failed a safety rule or its extracted tree didn't verify. Runs install `cold` instead until it ages out and is rebuilt. A package shipping two files that differ only in case is not cached. |
|
|
1134
|
+
| `over_budget` | The unpacked snapshot is larger than the disk budget. Raise the agent's `workspaceDiskMb`. |
|
|
1135
|
+
| `inconsistent` | The tree doesn't match the committed lockfile. A restored one is discarded and the project installs `cold`; a `cold` one is kept for the agent but not stored. |
|
|
1136
|
+
| `install_failed` | `npm ci`, or the `npm install` that updates a nearest snapshot, exited non-zero. The agent installs for itself and sees the real error. |
|
|
1137
|
+
| `budget_unknown`, `budget_too_small` | The run's workspace disk couldn't be measured, under 64 MiB would remain, or too little is left for an install (see "Other bounds" above). The workspace is unchanged. Raise `workspaceDiskMb`. |
|
|
1138
|
+
| `timeout`, `cancelled` | A step or the preparation's bound (20 minutes, or half the time left before the run's deadline) ran out, or the run was cancelled. |
|
|
1139
|
+
| `internal_error`, `worker_error` | Something unexpected in the worker, including a full disk or inode table. The run continues without the cache; check the worker's log. |
|
|
1140
|
+
| `tool_unavailable` | Claude Code only: the tool-runner image predates the cache or is a custom image without it. Rebuild it `FROM` the current tool-runner image. The proxy still seals the window on the run's first model request. |
|
|
1141
|
+
| `report_invalid` | The worker's report was malformed and was replaced by this code. |
|
|
1142
|
+
| `already_started` | A command ran before preparation finished. Not expected in normal operation. |
|
|
1143
|
+
| `invalid_deadline` | Claude Code only: the worker passed the tool runner a deadline it couldn't read. Not expected; the worker and tool runner images are from different releases. |
|
|
1144
|
+
| `worker_input_failed` (the run's error, not a project reason) | The run failed before the agent started: a Codex or Claude Code worker image older than the cache, or a `workerImageRef` image not rebuilt from a release that includes it. See [Worker images](#worker-images). |
|
|
1145
|
+
|
|
1146
|
+
For `rebuild`, `failed`, `timeout` and `unsealed` mean native add-ons may be
|
|
1147
|
+
unbuilt; the agent can run `npm rebuild` itself.
|
|
1148
|
+
|
|
1149
|
+
The proxy answers its `/cache/npm/` routes with `{"error": "<code>"}` and a
|
|
1150
|
+
`404 not_found` when the cache is off; those codes show up as the reasons
|
|
1151
|
+
above, never to the agent.
|
|
1152
|
+
|
|
375
1153
|
## What the agent can then run
|
|
376
1154
|
|
|
377
1155
|
Inside the run, `npm install <package>` and `pip install <package>` (into
|
|
@@ -386,6 +1164,120 @@ can otherwise run out of space on a large package; that directory counts
|
|
|
386
1164
|
toward the run's `workspaceDiskMb` and, like the rest of `.cache`, is never
|
|
387
1165
|
collected either.
|
|
388
1166
|
|
|
1167
|
+
### Python installers: pip, uv and Poetry
|
|
1168
|
+
|
|
1169
|
+
The `node-python` worker images ship pip (inside a virtual environment) and
|
|
1170
|
+
uv. The worker points both at the registry for every run:
|
|
1171
|
+
|
|
1172
|
+
| Setting | Client | What it does |
|
|
1173
|
+
| ------------------------------- | ------ | ----------------------------------------------------------------------------- |
|
|
1174
|
+
| `PIP_INDEX_URL` | pip | The registry, with the run's registry-only token, as the only index |
|
|
1175
|
+
| `PIP_ONLY_BINARY=:all:` | pip | Wheels only; source distributions are never installed |
|
|
1176
|
+
| `UV_DEFAULT_INDEX` | uv | The same registry URL as `PIP_INDEX_URL` (uv reads none of pip's settings) |
|
|
1177
|
+
| `UV_NO_BUILD=1` | uv | Wheels only from the index; the project itself still builds |
|
|
1178
|
+
| `PIP_CACHE_DIR`, `UV_CACHE_DIR` | both | Caches under the workspace's `.cache`, never collected |
|
|
1179
|
+
| `UV_PYTHON_DOWNLOADS=never` | uv | uv never tries to download a Python interpreter (the sandbox can't reach one) |
|
|
1180
|
+
|
|
1181
|
+
These are set by the worker, not by an operator, and an agent's or
|
|
1182
|
+
service's own environment can't override `PIP_*` or `UV_*` names.
|
|
1183
|
+
|
|
1184
|
+
What works, and what doesn't yet:
|
|
1185
|
+
|
|
1186
|
+
- **`pip install <package>`, `pip install -r requirements.txt` with
|
|
1187
|
+
top-level requirements, and `uv pip install …`** resolve top-down, so
|
|
1188
|
+
every dependency is allowed by the time it is requested.
|
|
1189
|
+
- **A fully pinned requirements file** (`pip-compile`, `uv pip compile` or
|
|
1190
|
+
`pip freeze` output, with or without `--hash`) lists transitive packages
|
|
1191
|
+
as top-level lines, which pip and uv request before reading any parent's
|
|
1192
|
+
metadata. Verify it first, as npm lockfiles are: `uv pip install -r` and
|
|
1193
|
+
`uv pip sync` do this themselves (see below); for pip, run
|
|
1194
|
+
`wardby-packages plan requirements.txt` before `pip install -r
|
|
1195
|
+
requirements.txt` (pip has no hook, and in the worker it only exists
|
|
1196
|
+
inside a virtual environment). The worker tells the agent so.
|
|
1197
|
+
- **`uv sync`, `uv run`, `uv add`, `uv remove` and `uv lock` with a
|
|
1198
|
+
committed `uv.lock`** work: the worker's `uv` is a small shim
|
|
1199
|
+
(`/opt/wardby/bin/uv`) that verifies the lock first. uv installs a locked
|
|
1200
|
+
project from the file URLs the lock records, never from its index, so for
|
|
1201
|
+
the command the shim also points the lock's PyPI URLs at the registry
|
|
1202
|
+
(without credentials; uv sends the index's), and afterwards puts the file
|
|
1203
|
+
back: exactly as it was when uv left it alone, otherwise with PyPI's URLs
|
|
1204
|
+
(a file the lock already had keeps its URL; a new one gets PyPI's
|
|
1205
|
+
redirecting `files.pythonhosted.org/packages/<tag>/<letter>/<name>/<file>`
|
|
1206
|
+
path). Before the workspace is collected, any registry URL left in a
|
|
1207
|
+
`uv.lock` is rewritten the same way, so a committed lock never names the
|
|
1208
|
+
registry.
|
|
1209
|
+
- **`pip install .`, `uv sync`, `uv run` and other project installs** build
|
|
1210
|
+
the project, which installs its build backend (`[build-system] requires`:
|
|
1211
|
+
`hatchling`, `setuptools`, `poetry-core`, …) and whatever the backend asks
|
|
1212
|
+
for while building (hatchling asks for `editables` for an editable install,
|
|
1213
|
+
which `uv sync` and `uv run` do). A lockfile doesn't record these, so
|
|
1214
|
+
allowlist them like any other package.
|
|
1215
|
+
- **Poetry** is not supported in the sandbox: it can't be pointed at the
|
|
1216
|
+
registry by environment alone (it needs a primary source in
|
|
1217
|
+
`pyproject.toml` plus credentials), and a `poetry.lock` (or a
|
|
1218
|
+
`pylock.toml`) is refused by the plan with `wardby_lockfile_unsupported`.
|
|
1219
|
+
Export a pinned requirements file, or use uv.
|
|
1220
|
+
|
|
1221
|
+
A Python lockfile plan (`POST /registry/pypi/-/plan`) trusts nothing the
|
|
1222
|
+
file claims, like an npm one, with these differences:
|
|
1223
|
+
|
|
1224
|
+
- **Entries.** A `uv.lock` (`version = 1`): every package from PyPI (or the
|
|
1225
|
+
registry). Its own projects (editable, virtual and directory sources) are
|
|
1226
|
+
where the lock starts; a package from git, a URL, a path or another index
|
|
1227
|
+
is refused with `wardby_lockfile_entry_unsupported`. A requirements file:
|
|
1228
|
+
every exact `name==version` line (options, `-r`/`-c` includes, local
|
|
1229
|
+
projects and unpinned lines are not entries; pip resolves those the usual
|
|
1230
|
+
way, so plan an included file separately); a `name @ url` line is refused.
|
|
1231
|
+
- **Facts.** Each pinned release is read from PyPI's JSON API
|
|
1232
|
+
(`pypi.org/pypi/<name>/<version>/json`, stored for reuse): its dependency
|
|
1233
|
+
lines (`requires_dist`) and its wheels. A release with no wheel is refused
|
|
1234
|
+
with `wardby_file_not_allowed`; its publish time, for the release-age
|
|
1235
|
+
check, is its earliest wheel's. Hashes a lockfile records are not compared
|
|
1236
|
+
(pip and uv check them on install).
|
|
1237
|
+
- **Edges and extras.** A dependency line counts when the locked version is
|
|
1238
|
+
inside its specifier (a pinned pre-release included). Extras follow the
|
|
1239
|
+
same rule as the dependency walk: a line gated on an extra (`extra ==
|
|
1240
|
+
"test"`) is followed only for an install with that extra, and extras are
|
|
1241
|
+
followed only along a chain that starts at an allowlist entry naming them
|
|
1242
|
+
(`psycopg[binary]`).
|
|
1243
|
+
- **Approvals are names.** An approved package is allowed by name for the
|
|
1244
|
+
run; its downloads still go through every usual check (wheels only, each
|
|
1245
|
+
file's own release age, the index's sha256, advisories). A pin refused as
|
|
1246
|
+
too new or with no wheel is named in the plan's output with its reason.
|
|
1247
|
+
|
|
1248
|
+
**Why an install was refused.** npm prints the registry's refusal body, so
|
|
1249
|
+
the agent sees the code and the hint. pip doesn't (a refused package shows
|
|
1250
|
+
as `from versions: none`), and uv shows only `403 Forbidden`. So the worker
|
|
1251
|
+
tells the agent to run `wardby-packages` after an install that failed
|
|
1252
|
+
without saying why: it lists the packages the registry refused for the run
|
|
1253
|
+
in the last 15 minutes, each with its code and what to do. For a Claude Code
|
|
1254
|
+
agent, the tool runner also adds those lines to any failed command's output
|
|
1255
|
+
by itself. The command reads the run's registry-only token from the worker's
|
|
1256
|
+
environment and sends it only to the registry.
|
|
1257
|
+
|
|
1258
|
+
The registry also records what it hides from a Python index, which pip and
|
|
1259
|
+
uv report only as "no versions": a package whose allowed versions publish
|
|
1260
|
+
only source distributions (`wardby_file_not_allowed`), or whose every wheel
|
|
1261
|
+
is newer than the release-age limit (`wardby_version_filtered`). One case
|
|
1262
|
+
stays unrecorded: a single pinned version withheld while others are listed
|
|
1263
|
+
(`jinja2==2.10` with a blocking advisory), since the registry never sees
|
|
1264
|
+
the client's pin.
|
|
1265
|
+
|
|
1266
|
+
An installer often reads an index it doesn't need (a resolver weighing
|
|
1267
|
+
candidates, a dependency behind a platform marker), so a refusal recorded
|
|
1268
|
+
only from an index weighs less than a refused download. Once any version of
|
|
1269
|
+
that name is served later in the same coding run, it is dropped like any
|
|
1270
|
+
other refusal the run got past. If nothing of that name was served,
|
|
1271
|
+
`wardby-packages` and `get_run` list it (`get_run` marks it
|
|
1272
|
+
`"listingOnly": true`), and the status shows it according to the outcome:
|
|
1273
|
+
|
|
1274
|
+
- a coding sub-run failed: it counts toward ⛔ blocked, like any refusal;
|
|
1275
|
+
- the run made no changes: it is a ⚠️ dependency install line, not ⛔ (for
|
|
1276
|
+
example, a package whose only release has nothing but too-new wheels);
|
|
1277
|
+
- a pull request was opened or updated: it adds no **Dependency install
|
|
1278
|
+
incomplete** warning, no ⚠️ line, and no entry in the pull request's
|
|
1279
|
+
package section.
|
|
1280
|
+
|
|
389
1281
|
## Safeguards
|
|
390
1282
|
|
|
391
1283
|
- **Dependency graph only.** Only packages on the allowlist, or reachable
|
|
@@ -414,16 +1306,26 @@ collected either.
|
|
|
414
1306
|
parse counts as affected. If OSV can't be reached, installs in that request fail closed
|
|
415
1307
|
(`503 wardby_audit_unavailable`) rather than skipping the check — an
|
|
416
1308
|
operator can opt out of fail-closed with `REGISTRY_AUDIT_FAIL_OPEN=true`.
|
|
1309
|
+
An approver can accept specific advisories for one agent with
|
|
1310
|
+
`packagePolicy.acceptedAdvisories` (see
|
|
1311
|
+
[Accepting advisories](#accepting-advisories)); malware is never waivable,
|
|
1312
|
+
and every waiver is reported in the run's packages and in the pull request.
|
|
417
1313
|
- **Wheels only for Python.** PyPI source distributions (sdists), which can
|
|
418
1314
|
run arbitrary code at install/build time, are never served — only wheels.
|
|
1315
|
+
The index still lists them, so a `uv.lock` uv writes in a run keeps them
|
|
1316
|
+
as one written against PyPI does, but a download of one is refused with
|
|
1317
|
+
`wardby_file_not_allowed`.
|
|
419
1318
|
- **npm install scripts are off, but only by configuration.** The proxy sets
|
|
420
1319
|
`npm_config_ignore_scripts=true` in the agent's environment, which disables
|
|
421
|
-
npm's install-time script hooks
|
|
1320
|
+
npm's (and pnpm's) install-time script hooks; `YARN_IGNORE_SCRIPTS` and
|
|
1321
|
+
`YARN_ENABLE_SCRIPTS=false` do the same for yarn classic and yarn Berry. This is a configuration default, not
|
|
422
1322
|
something the proxy can enforce on the downloaded tarball itself: the
|
|
423
1323
|
proxy cannot strip scripts from a package, so an agent that deliberately
|
|
424
1324
|
overrode this setting could still run one. A script that ran would still be
|
|
425
1325
|
confined to the same sandbox — no network, no credentials, only the
|
|
426
1326
|
workspace writable — but this is a documented limit, not a guarantee.
|
|
1327
|
+
The one exception is the [dependency cache](#dependency-cache)'s
|
|
1328
|
+
`npm rebuild`, which Wardby runs itself before the agent starts.
|
|
427
1329
|
|
|
428
1330
|
## Operator limits
|
|
429
1331
|
|
|
@@ -434,22 +1336,28 @@ as what's already been recorded, so parallel downloads (for example npm's
|
|
|
434
1336
|
default concurrent connections) can't add up to more than the limit before any
|
|
435
1337
|
one of them finishes:
|
|
436
1338
|
|
|
437
|
-
| Setting | Default | Meaning
|
|
438
|
-
| ------------------------------ | ------- |
|
|
439
|
-
| `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file.
|
|
440
|
-
| `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run.
|
|
441
|
-
| `REGISTRY_MAX_FILES` | 5000 | Total files served in one run.
|
|
442
|
-
| `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download.
|
|
443
|
-
| `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable.
|
|
444
|
-
| `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included.
|
|
445
|
-
| `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads.
|
|
446
|
-
| `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 |
|
|
447
|
-
| `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time
|
|
448
|
-
| `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool.
|
|
449
|
-
| `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests).
|
|
450
|
-
| `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have.
|
|
451
|
-
| `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan.
|
|
452
|
-
| `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make.
|
|
1339
|
+
| Setting | Default | Meaning |
|
|
1340
|
+
| ------------------------------ | ------- | -------------------------------------------------------------- |
|
|
1341
|
+
| `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file. |
|
|
1342
|
+
| `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run. |
|
|
1343
|
+
| `REGISTRY_MAX_FILES` | 5000 | Total files served in one run. |
|
|
1344
|
+
| `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download. |
|
|
1345
|
+
| `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable. |
|
|
1346
|
+
| `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included. |
|
|
1347
|
+
| `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads. |
|
|
1348
|
+
| `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 | Package names the graph walk may expand per run and ecosystem. |
|
|
1349
|
+
| `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time one download's graph walk may take. |
|
|
1350
|
+
| `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool. |
|
|
1351
|
+
| `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests). |
|
|
1352
|
+
| `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have. |
|
|
1353
|
+
| `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan. |
|
|
1354
|
+
| `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make. |
|
|
1355
|
+
|
|
1356
|
+
A large allowlist can reach the package bound: the walk follows every kept
|
|
1357
|
+
version in each range plus optional and peer dependencies, so a graph is
|
|
1358
|
+
usually several times larger than one install. Allowlist the packages a
|
|
1359
|
+
project needs directly, or raise `REGISTRY_MAX_GRAPH_PACKAGES` (which costs
|
|
1360
|
+
proxy memory and walk time).
|
|
453
1361
|
|
|
454
1362
|
Metadata is cached for five minutes in a bounded cache (500 packages and
|
|
455
1363
|
64 MiB of trimmed metadata, least recently used evicted first), and
|
|
@@ -483,14 +1391,34 @@ one package):
|
|
|
483
1391
|
{ "ecosystem": "npm", "name": "@heroui/react", "version": "3.2.6", "size": 482113 },
|
|
484
1392
|
{ "ecosystem": "pypi", "name": "flask", "version": "3.0.0", "size": 101817 }
|
|
485
1393
|
],
|
|
486
|
-
"packageRefusals": [
|
|
1394
|
+
"packageRefusals": [
|
|
1395
|
+
{
|
|
1396
|
+
"ecosystem": "npm",
|
|
1397
|
+
"name": "left-pad",
|
|
1398
|
+
"reason": "wardby_package_not_allowed",
|
|
1399
|
+
"hint": "The package is not on the agent's allowlist and no allowlisted package the run resolved depends on it. The agent's owner must allowlist the package directly; retrying will not help."
|
|
1400
|
+
}
|
|
1401
|
+
]
|
|
487
1402
|
}
|
|
488
1403
|
```
|
|
489
1404
|
|
|
1405
|
+
`hint` says what the refusal code means and what to do about it; it is
|
|
1406
|
+
present for the allowlist codes below and omitted for codes without guidance.
|
|
490
1407
|
`size` is the number of bytes served, or `null` if none was recorded.
|
|
1408
|
+
A refusal the run got past is left out of `packageRefusals`, and so out of
|
|
1409
|
+
the pull request warning, the status comments and Slack: the registry later
|
|
1410
|
+
served the same package to the same coding run (for a refusal of one
|
|
1411
|
+
version, that version), for example after npm retried a temporary
|
|
1412
|
+
`wardby_upstream_error`. A package served to a different coding sub-run
|
|
1413
|
+
does not clear it, and a refusal recorded after the package was served
|
|
1414
|
+
still counts.
|
|
491
1415
|
`get_run` also returns `packagePlan: { "approved": <n>, "refused": <n> }`:
|
|
492
1416
|
the exact versions lockfile plans approved for the run, and the distinct
|
|
493
|
-
entries they refused (each refusal is also in `packageRefusals
|
|
1417
|
+
entries they refused (each refusal is also in `packageRefusals`, unless that
|
|
1418
|
+
version was served later). A run with
|
|
1419
|
+
[package audit mode](#package-audit-mode) on also returns `packageAuditMode`
|
|
1420
|
+
and `packageAudit`, and its pull request opens with a caution banner listing
|
|
1421
|
+
the packages installed outside the allowlist.
|
|
494
1422
|
|
|
495
1423
|
The pull request finalization also appends a collapsed **Packages installed
|
|
496
1424
|
during this run** section listing the same information, so a reviewer
|
|
@@ -499,10 +1427,17 @@ and 100 refusals, followed by "…and N more — see get_run for the full list";
|
|
|
499
1427
|
`get_run` always has the complete list. If the report can't be loaded or
|
|
500
1428
|
rendered, the section is left out and the pull request is still opened.
|
|
501
1429
|
|
|
1430
|
+
A later run on the same pull request (a review fix round, a mention
|
|
1431
|
+
follow-up, a continuation) rewrites the section as **Packages installed by
|
|
1432
|
+
wardby's runs on this pull request**: every package installed by the runs
|
|
1433
|
+
that pushed to the pull request plus the latest run's, each package and
|
|
1434
|
+
version once. Refusals, and the warning below, cover the latest run only. See
|
|
1435
|
+
[How the pull request description is kept current](code-review-agents.md#how-the-pull-request-description-is-kept-current).
|
|
1436
|
+
|
|
502
1437
|
When the registry refused anything during the run, the pull request also
|
|
503
1438
|
opens with a **Dependency install incomplete** warning, above the agent's
|
|
504
1439
|
summary. It names the refused packages grouped by refusal code, says what
|
|
505
|
-
each code means, and notes that failed checks under **Tests** may come from
|
|
1440
|
+
each code means (in the same terms as `hint`), and notes that failed checks under **Tests** may come from
|
|
506
1441
|
the sandbox rather than the change. If the run changed a lock file
|
|
507
1442
|
(`package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`, `pnpm-lock.yaml`,
|
|
508
1443
|
`poetry.lock`, `uv.lock` or `Pipfile.lock`), the warning names it: the lock
|
|
@@ -515,6 +1450,12 @@ Review agents see the pull request's CI results through `repo_pr_read`
|
|
|
515
1450
|
failure; see
|
|
516
1451
|
[code-review-agents.md](code-review-agents.md#the-repo_-tools).
|
|
517
1452
|
|
|
1453
|
+
GitHub and Jira status comments show a package-registry blocked message only
|
|
1454
|
+
when no pull request resulted and a coding child reported no changes or failed.
|
|
1455
|
+
Otherwise, recorded refusals add a dependency install warning while preserving
|
|
1456
|
+
the agent's reply or answer, or the pull request link. A recorded refusal alone
|
|
1457
|
+
does not mean the final install or the run failed.
|
|
1458
|
+
|
|
518
1459
|
A package that is only reachable through a refused one is refused too, as
|
|
519
1460
|
`wardby_package_not_allowed`. When a dependency deep in a toolchain has a
|
|
520
1461
|
high-severity advisory and no fixed version, every run that installs that
|
|
@@ -522,34 +1463,38 @@ tree is refused at the same place; approving more packages won't help.
|
|
|
522
1463
|
|
|
523
1464
|
## Error codes
|
|
524
1465
|
|
|
525
|
-
npm
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
|
532
|
-
|
|
|
533
|
-
| `
|
|
534
|
-
| `
|
|
535
|
-
| `
|
|
536
|
-
| `
|
|
537
|
-
| `
|
|
538
|
-
| `
|
|
539
|
-
| `
|
|
540
|
-
| `
|
|
541
|
-
| `
|
|
542
|
-
| `
|
|
543
|
-
| `
|
|
544
|
-
| `
|
|
545
|
-
| `
|
|
546
|
-
| `
|
|
547
|
-
| `
|
|
548
|
-
| `
|
|
549
|
-
| `
|
|
550
|
-
| `
|
|
551
|
-
| `
|
|
552
|
-
| `
|
|
1466
|
+
npm prints the proxy's error body verbatim, so these are what you'll see on
|
|
1467
|
+
a failed npm install. pip and uv don't print it: `wardby-packages` lists the
|
|
1468
|
+
same codes for the run (see
|
|
1469
|
+
[Python installers](#python-installers-pip-uv-and-poetry)), and so do
|
|
1470
|
+
`get_run` and the pull request.
|
|
1471
|
+
|
|
1472
|
+
| Code | Status | Meaning / what to do |
|
|
1473
|
+
| ------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1474
|
+
| `wardby_package_not_allowed` | 403 | The package isn't on the allowlist and wasn't declared by a package already resolved in the run (a new top-level package must be allowlisted itself), or is only reachable through a refused package. Final: the agent's owner must allowlist it. For a package the project adds directly, allowlist the package itself; allowlisting a dependent only helps for a package something already in the project depends on. For a PyPI package an extra pulls in (`psycopg-binary` for `psycopg[binary]`), name the extra on the entry instead. |
|
|
1475
|
+
| `wardby_package_not_allowed_pending` | 403 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: the dependency walk stopped before reaching the package, so it may still be reachable. Not transient and retrying rarely helps: the owner should allowlist the package directly. |
|
|
1476
|
+
| `wardby_file_not_allowed` | 403 | The specific file type is never served for this ecosystem (for example a PyPI sdist). Nothing to configure; use a wheel. |
|
|
1477
|
+
| `wardby_version_filtered` | 404 | Every matching version is too new (younger than `minReleaseAgeDays`) or withheld by the vulnerability audit. Wait for it to age past the threshold, or lower `minReleaseAgeDays` if you understand the risk. |
|
|
1478
|
+
| `wardby_package_not_found` | 404 | The upstream registry has no such package name. Check the spelling (npm names are case-sensitive). |
|
|
1479
|
+
| `wardby_package_too_large` | 413 | The file exceeds `REGISTRY_MAX_FILE_MB`. Ask the operator to raise it if the file is legitimately larger. |
|
|
1480
|
+
| `wardby_package_limit` | 429 | The run hit `REGISTRY_MAX_FILES` or `REGISTRY_MAX_TOTAL_MB`. Trim what the run installs, or ask the operator to raise the limit. |
|
|
1481
|
+
| `wardby_audit_unavailable` | 503 | OSV couldn't be reached and `REGISTRY_AUDIT_FAIL_OPEN` isn't set. Retry, or have the operator set that flag if the outage is expected to be long. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the audit failed while resolving a lockfile install's dependency graph. |
|
|
1482
|
+
| `wardby_graph_incomplete` | 503 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: a dependency the walk reached but timed out (`REGISTRY_GRAPH_TIMEOUT_MS`) before checking its versions, usually a slow or failing upstream for that package. Retry shortly; npm retries 5xx on its own. Never means the package is outside the graph. |
|
|
1483
|
+
| `wardby_graph_limit` | 403 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: the dependency graph is over `REGISTRY_MAX_GRAPH_PACKAGES` for this run. This run's walk reached the limit before the package, so it may not be in the graph. Permanent for the run, so retrying won't help: the owner can allowlist the package directly, or the operator can raise the limit. |
|
|
1484
|
+
| `wardby_lockfile_unsupported` | 400 | The lockfile sent to `POST /-/plan` isn't one the plan reads: a `package-lock.json` with lockfileVersion 2 or 3 (and a `packages` section), a `pnpm-lock.yaml` with lockfileVersion 6 or 9, or a yarn classic or yarn 2+ `yarn.lock`; for PyPI, a `uv.lock` (version 1) or a pinned requirements file (a Poetry `poetry.lock` or a `pylock.toml` is refused). Regenerate it with a current client. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the slower walk. |
|
|
1485
|
+
| `wardby_lockfile_too_large` | 413 | The lockfile is over 20 MiB or has more than `REGISTRY_PLAN_MAX_ENTRIES` entries. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
|
|
1486
|
+
| `wardby_lockfile_integrity_mismatch` | — | A plan refusal: the lockfile's `integrity` for this entry isn't the registry's own (or the registry publishes none, or the lockfile records none; a yarn 2+ `yarn.lock` is never checked this way, see [pnpm and yarn](#pnpm-and-yarn)). Regenerate the lockfile entry; never edit integrity by hand. |
|
|
1487
|
+
| `wardby_lockfile_entry_unsupported` | — | A plan refusal: the entry is installed from git, a URL or a path, or isn't an exact registry version. Only registry packages can be approved. |
|
|
1488
|
+
| `wardby_plan_limit` | 429 | The run has made `REGISTRY_PLAN_MAX_PER_RUN` lockfile plans. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
|
|
1489
|
+
| `wardby_registry_integrity_missing` | — | A plan refusal: npm publishes no integrity for this version, so a download of it couldn't be checked. |
|
|
1490
|
+
| `wardby_plan_in_progress` | 429 | A lockfile plan is already running for this run (one at a time). Wait for it; the install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
|
|
1491
|
+
| `wardby_plan_incomplete` | 503 | The plan didn't finish within `REGISTRY_PLAN_TIMEOUT_MS` (or the client went away), or a `pnpm-lock.yaml` or `yarn.lock` wasn't read within 15 seconds (see [pnpm and yarn](#pnpm-and-yarn); a retry of that lockfile won't help). What was verified is kept, so a retry resumes; the install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
|
|
1492
|
+
| `wardby_bad_request` | 400 | The request path is malformed (bad percent-encoding, or not a valid package name). A client or agent bug, not a package choice. |
|
|
1493
|
+
| `wardby_upstream_error` | 502 | The upstream registry answered with an error, or the download failed partway. Retry. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the registry failed while resolving a lockfile install's dependency graph. |
|
|
1494
|
+
| `wardby_upstream_unavailable` | 504 | The upstream registry didn't answer a metadata request within `REGISTRY_METADATA_TIMEOUT_MS`. Retry. |
|
|
1495
|
+
| `wardby_metadata_too_large` | 502 | The package's metadata document is larger than `REGISTRY_MAX_METADATA_MB`. Ask the operator to raise it. |
|
|
1496
|
+
| `wardby_upstream_host_not_allowed` | 502 | The file's download URL points outside that ecosystem's own upstream hosts, so the proxy won't fetch it. |
|
|
1497
|
+
| `invalid_capability` | 401 | The run's registry token doesn't match a live session (the run has ended or the token is malformed). Not something a package choice can fix. |
|
|
553
1498
|
|
|
554
1499
|
## Adding an ecosystem
|
|
555
1500
|
|