@wardby/cli 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +53 -4
- package/README.md +52 -13
- package/dist/claude-coding-worker/driver.d.ts +6 -1
- package/dist/claude-coding-worker/driver.js +27 -1
- package/dist/claude-coding-worker/main.js +2 -0
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +14 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +284 -36
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +91 -4
- package/dist/coding/protocol.js +123 -12
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +6 -0
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +13 -1
- package/dist/coding-worker/driver.js +102 -13
- package/dist/coding-worker/errors.js +8 -0
- package/dist/coding-worker/main.js +10 -2
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +66 -0
- package/dist/config/providers.js +137 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +120 -10
- package/dist/core/budget-groups.js +133 -24
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +8 -2
- package/dist/core/dispatch.d.ts +74 -7
- package/dist/core/dispatch.js +382 -47
- package/dist/core/engine-native.js +44 -14
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +72 -0
- package/dist/core/host-events.js +597 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +78 -0
- package/dist/core/host-status.js +228 -0
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +42 -2
- package/dist/core/reconciler.js +97 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +354 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +33 -8
- package/dist/core/runner.js +410 -62
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +67 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +17 -2
- package/dist/core/webhooks.js +42 -3
- package/dist/generated/prisma/browser.d.ts +166 -0
- package/dist/generated/prisma/client.d.ts +166 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
- package/dist/generated/prisma/enums.d.ts +13 -0
- package/dist/generated/prisma/enums.js +12 -1
- package/dist/generated/prisma/internal/class.d.ts +242 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
- package/dist/generated/prisma/models/Agent.d.ts +682 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +455 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +1402 -82
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +15 -3
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +22 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +999 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +5 -0
- package/dist/import/create.js +67 -13
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +18 -18
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +72 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +25 -51
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +33 -2
- package/dist/mcp/auth/resource-server.js +99 -3
- package/dist/mcp/auth/self-hosted/browser.js +2 -2
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
- package/dist/mcp/context.d.ts +30 -1
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +38 -0
- package/dist/mcp/host-events/github-ingress.js +83 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +10 -1
- package/dist/mcp/index.js +187 -19
- package/dist/mcp/server.js +32 -11
- package/dist/mcp/tools/agents.js +519 -50
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/datastore.js +22 -15
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +182 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +3 -6
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +52 -16
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +201 -40
- package/dist/mcp/tools/trigger.js +65 -7
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +15 -0
- package/dist/mcp/transport/streamable-http.js +55 -3
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +2 -0
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +10 -2
- package/dist/providers/auth/self-hosted.js +55 -6
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
- package/dist/providers/coding-proxy/memory-ledger.js +31 -3
- package/dist/providers/coding-proxy/metering.d.ts +6 -1
- package/dist/providers/coding-proxy/metering.js +41 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
- package/dist/providers/coding-proxy/proxy.d.ts +25 -2
- package/dist/providers/coding-proxy/proxy.js +561 -38
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +17 -0
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +33 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +29 -0
- package/dist/providers/executor/build.d.ts +2 -2
- package/dist/providers/executor/composition.d.ts +3 -0
- package/dist/providers/executor/composition.js +28 -1
- package/dist/providers/executor/container.d.ts +134 -4
- package/dist/providers/executor/container.js +394 -37
- package/dist/providers/executor/dbos.d.ts +4 -3
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +13 -0
- package/dist/providers/executor/routing.js +18 -0
- package/dist/providers/executor/types.d.ts +34 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +53 -2
- package/dist/providers/jobs/docker.js +307 -32
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +24 -2
- package/dist/providers/jobs/kubernetes.js +153 -22
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/jobs/types.d.ts +18 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -5
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -8
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +10 -12
- package/dist/providers/llm/claude-provider.js +15 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +21 -11
- package/dist/providers/llm/routing.js +47 -13
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +226 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +568 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +309 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +78 -34
- package/dist/providers/vcs/github.d.ts +94 -3
- package/dist/providers/vcs/github.js +223 -17
- package/dist/providers/vcs/types.d.ts +48 -4
- package/dist/quickstart/index.d.ts +2 -0
- package/dist/quickstart/index.js +36 -8
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +11 -0
- package/docs/README.md +40 -0
- package/docs/agent-recipes.md +383 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +508 -0
- package/docs/coding-agent-setup.md +175 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1068 -0
- package/docs/getting-started-gke.md +632 -0
- package/docs/getting-started-identity-provider.md +319 -0
- package/docs/getting-started.md +147 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +667 -0
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +37 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +90 -0
- package/help/deploy-gke.md +45 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/model-unavailable.md +63 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +32 -0
- package/help/github.md +48 -0
- package/help/identity-and-access.md +44 -0
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/mcp.md +30 -0
- package/help/models.md +90 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +36 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +32 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +14 -3
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +663 -21
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
- package/dist/providers/llm/pricing-anthropic.js +0 -30
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Local coding-agent setup
|
|
2
|
+
|
|
3
|
+
This setup starts a dedicated, trusted coding proxy while keeping the coding
|
|
4
|
+
worker untrusted and network-isolated. The proxy has the selected provider
|
|
5
|
+
credential and database access. A worker receives only a one-run capability
|
|
6
|
+
and can reach only the proxy on its internal Docker network. Claude Code uses
|
|
7
|
+
a second, credential-free tool-runner container for repository access; it
|
|
8
|
+
shares the run's network (so its package installs reach the proxy's
|
|
9
|
+
registry) but never holds the run capability.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- Docker Desktop is running.
|
|
14
|
+
- `.env.local` contains `DATABASE_URL`, `SECRET_APP_KEY`, and the credential
|
|
15
|
+
for each enabled coding provider: `OPENAI_API_KEY` and/or
|
|
16
|
+
`ANTHROPIC_API_KEY`.
|
|
17
|
+
- The local database has the current Prisma migrations applied.
|
|
18
|
+
- A GitHub App is installed on only the repository to be exercised. Grant it
|
|
19
|
+
`Contents: Read and write` and `Pull requests: Read and write`; set
|
|
20
|
+
`GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY` in `.env.local`.
|
|
21
|
+
|
|
22
|
+
GitHub documents the available App permissions in its [permissions guide](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app).
|
|
23
|
+
The same App can also run native agents as automated PR reviewers; see
|
|
24
|
+
[code-review-agents.md](code-review-agents.md) for the extra webhook
|
|
25
|
+
permissions and setup.
|
|
26
|
+
|
|
27
|
+
The GitHub App installation is the only step that cannot be created from this
|
|
28
|
+
repository. It is intentionally scoped to the test repository because a coding
|
|
29
|
+
run can push a branch and create a draft pull request.
|
|
30
|
+
|
|
31
|
+
Coding runs can also have services such as a PostgreSQL database next to them,
|
|
32
|
+
for Codex and Claude Code runs on the Kubernetes and Docker launchers; see
|
|
33
|
+
[coding-services.md](coding-services.md).
|
|
34
|
+
|
|
35
|
+
## Repository authorization
|
|
36
|
+
|
|
37
|
+
A coding agent's `codingProfile.repository` must be authorized for the agent's
|
|
38
|
+
owner: `create_agent` and `update_agent` (when the repository changes) check
|
|
39
|
+
that the owner's linked GitHub account has **write** access to it, or record
|
|
40
|
+
an explicit admin approval (`repositoryAdminOverride: true`, `admin` role
|
|
41
|
+
only). Link your GitHub account once with `link_host_account` first; see
|
|
42
|
+
[code-review-agents.md](code-review-agents.md#who-may-give-an-agent-a-repository),
|
|
43
|
+
which also covers the App's callback URL and client credentials
|
|
44
|
+
(`GITHUB_APP_CLIENT_ID`, `GITHUB_APP_CLIENT_SECRET`).
|
|
45
|
+
|
|
46
|
+
Every run checks again, before its workspace is prepared, against the agent's
|
|
47
|
+
current owner. A run whose owner has lost write access, unlinked their GitHub
|
|
48
|
+
account, or whose repository was never authorized ends `refused` with failure
|
|
49
|
+
category `repo_access` and nothing cloned. The check is repeated (usually from
|
|
50
|
+
the 5-minute cache) right before the run pushes: access lost while it worked
|
|
51
|
+
fails the run (`repo_access`) with nothing pushed. For a run already under way,
|
|
52
|
+
a transient GitHub error during either check is retried once; if it persists
|
|
53
|
+
the run fails with category `repo_access_unavailable` (GitHub could not be
|
|
54
|
+
asked — not a lost permission; re-run it later). `make_owner` to a different
|
|
55
|
+
owner turns an admin or grandfathered approval into a check of the new owner's
|
|
56
|
+
access. Coding agents must have an owner.
|
|
57
|
+
Over stdio, the local operator holds every role, so `repositoryAdminOverride`
|
|
58
|
+
is the way to approve a repository there.
|
|
59
|
+
|
|
60
|
+
## Start the trusted proxy
|
|
61
|
+
|
|
62
|
+
Run these commands from the repository root:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
npm run coding:local:up
|
|
66
|
+
npm run worker:image:local
|
|
67
|
+
npm run claude:images:local
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The image commands create local tags. Resolve every enabled image with
|
|
71
|
+
`docker image inspect --format '{{.Id}}' <tag>` and put the resulting immutable
|
|
72
|
+
`sha256:...` ID in `.env.local`; do not use the mutable tag at runtime. Add the
|
|
73
|
+
following values, replacing image IDs and GitHub values:
|
|
74
|
+
|
|
75
|
+
```dotenv
|
|
76
|
+
# Leave this as local until every value below is set and reviewed.
|
|
77
|
+
JOB_LAUNCHER=local
|
|
78
|
+
CODING_WORKER_IMAGE=sha256:replace-with-worker-image-id
|
|
79
|
+
CODING_CLAUDE_WORKER_IMAGE=sha256:replace-with-claude-worker-image-id
|
|
80
|
+
CODING_CLAUDE_TOOL_RUNNER_IMAGE=sha256:replace-with-claude-tool-runner-image-id
|
|
81
|
+
# Only for Claude Code agents on the node-python toolchain (version 3.12):
|
|
82
|
+
CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12=sha256:replace-with-claude-tool-runner-node-python-image-id
|
|
83
|
+
CODING_PROXY_CONTAINER=wardby-coding-proxy
|
|
84
|
+
VCS_WORK_ROOT=/tmp/wardby-vcs
|
|
85
|
+
CODING_JOB_STATE_ROOT=/tmp/wardby-docker-jobs
|
|
86
|
+
CODING_ARTIFACT_ROOT=/tmp/wardby-coding-artifacts
|
|
87
|
+
CODING_OPENAI_CREDENTIAL_REF=env:OPENAI_API_KEY
|
|
88
|
+
CODING_ANTHROPIC_CREDENTIAL_REF=env:ANTHROPIC_API_KEY
|
|
89
|
+
GITHUB_APP_ID=replace-with-app-id
|
|
90
|
+
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`npm run claude:images:local` builds the Claude agent image and both tool-runner
|
|
94
|
+
images (`wardby-claude-tool-runner:phase5`, and
|
|
95
|
+
`wardby-claude-tool-runner:phase5-node-python` for the `node-python` toolchain).
|
|
96
|
+
This local, immutable-`sha256:` ID form of the `CODING_CLAUDE_*` images is only
|
|
97
|
+
accepted by the Docker launcher.
|
|
98
|
+
`JOB_LAUNCHER=kubernetes` needs both images pushed to a registry and set as
|
|
99
|
+
`repo@sha256:<64 hex>` registry digests instead — the control plane refuses
|
|
100
|
+
to start otherwise — which `deploy/gke/up.sh` and `deploy/kind-coding/up.sh`
|
|
101
|
+
build, push, and pin for you.
|
|
102
|
+
|
|
103
|
+
Once the GitHub App values are set and the target repository has been reviewed,
|
|
104
|
+
change `JOB_LAUNCHER=docker` and run:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
npm run cli -- coding preflight
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The preflight checks that Docker can inspect the immutable worker image. A real
|
|
111
|
+
run additionally verifies the proxy's isolated-network attachment immediately
|
|
112
|
+
before launching the worker.
|
|
113
|
+
|
|
114
|
+
Run the no-paid-service verification gates before an opt-in live smoke:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npm run verify:phase5
|
|
118
|
+
npm run test:phase5:database
|
|
119
|
+
npm run test:docker-isolation
|
|
120
|
+
npm run test:docker-job
|
|
121
|
+
npm run verify:claude-code
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The live smoke remains manual because it spends provider credit and can create
|
|
125
|
+
a GitHub branch and draft pull request. Keep its budget deliberately small.
|
|
126
|
+
|
|
127
|
+
After the smoke completes, verify the run ID, terminal result, pull-request
|
|
128
|
+
URL, and final cost. Close the fixture PR and delete its `wardby/run-*` branch.
|
|
129
|
+
The worker's volume, artifact, and trusted checkout should be removed by
|
|
130
|
+
terminal cleanup; investigate any retained resource as a cleanup failure.
|
|
131
|
+
|
|
132
|
+
See [release verification](release-verification.md) for the complete gate.
|
|
133
|
+
|
|
134
|
+
If the repository has `docs/knowledge/index.md`, every coding run's prompt
|
|
135
|
+
includes it. See [Architecture knowledge bundles](knowledge.md).
|
|
136
|
+
|
|
137
|
+
## Budgets
|
|
138
|
+
|
|
139
|
+
A coding run's budget is reserved when it is dispatched: the agent's own
|
|
140
|
+
`budgetUsd`, tightened to whatever its budget group has left for each
|
|
141
|
+
configured period and, for a sub-agent, whatever its run tree has left. The
|
|
142
|
+
worker can never spend more than that reservation. When a group or run tree
|
|
143
|
+
has nothing left, the run is recorded as `refused` with the error
|
|
144
|
+
`budget_group_exhausted:<day|week|month>` or `run_tree_exhausted`, and no
|
|
145
|
+
container starts (`trigger_agent` returns that status and error directly).
|
|
146
|
+
|
|
147
|
+
Every live run still pending or running holds its unspent reservation
|
|
148
|
+
(reservation minus cost so far) against its group; a native run holds its
|
|
149
|
+
agent's per-run `budgetUsd`. So overlapping scheduled, webhook and manual runs
|
|
150
|
+
share one cap rather than each seeing the full remainder, and
|
|
151
|
+
`get_budget_group` reports those holds as `reservedUsd` next to `spentUsd`.
|
|
152
|
+
Native runs are served first come, first served: a native run only counts the
|
|
153
|
+
holds of runs that started before it, so members dispatched on the same tick
|
|
154
|
+
don't starve each other.
|
|
155
|
+
|
|
156
|
+
A hold lapses by itself when the run stops showing signs of life, so a crashed
|
|
157
|
+
process or an interrupted `wardby run` cannot pin a group for the rest of the
|
|
158
|
+
period. A run holds while its last heartbeat (or its start, before the first
|
|
159
|
+
beat) is under 60 seconds old (the reconciler's heartbeat timeout plus one
|
|
160
|
+
reconcile interval). Managed runs, `wardby run` and native sub-agent children
|
|
161
|
+
all beat every 10 seconds. A coding run also holds until its
|
|
162
|
+
`CODING_QUEUE_TIMEOUT_SEC` + its `timeoutSec` + 60 seconds have passed since
|
|
163
|
+
dispatch, because it does not beat while it waits in the coding queue. Its
|
|
164
|
+
recorded cost always counts. `wardby run` records the run as `cancelled` on
|
|
165
|
+
Ctrl-C or SIGTERM. If a row is still stuck in `running` for another reason,
|
|
166
|
+
only its real cost counts once its hold lapses.
|
|
167
|
+
|
|
168
|
+
## Stop the setup
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npm run coding:local:down
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
This stops the local database and proxy but leaves the named Postgres volume in
|
|
175
|
+
place. It does not alter agent records, GitHub branches, or pull requests.
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Installing packages in coding runs
|
|
2
|
+
|
|
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` and `pip install`
|
|
5
|
+
anyway, by routing those requests through the proxy to a per-agent allowlist,
|
|
6
|
+
with supply-chain safeguards and a full record of what was fetched.
|
|
7
|
+
|
|
8
|
+
**Registry mode works for both providers.** Claude Code runs every shell
|
|
9
|
+
command inside a separate, credential-free tool-runner container that shares
|
|
10
|
+
the run's proxy network with the agent, so it can reach the proxy the same
|
|
11
|
+
way Codex's driver does; it never receives the run's model capability, only
|
|
12
|
+
the registry-only settings the trusted launcher builds for it (delivered as
|
|
13
|
+
`WARDBY_TOOL_SETUP`). Both providers support the `node` toolchain (npm) and
|
|
14
|
+
the `node-python` toolchain (npm and pip). See [Claude Code](#claude-code)
|
|
15
|
+
below.
|
|
16
|
+
|
|
17
|
+
## Claude Code
|
|
18
|
+
|
|
19
|
+
`npm install` and, on the `node-python` toolchain, `pip install` run inside
|
|
20
|
+
Claude Code's tool-runner container, the same container that mounts the
|
|
21
|
+
workspace and runs every other shell command — the agent container never runs
|
|
22
|
+
one. The toolchain picks the tool-runner image: `node` uses
|
|
23
|
+
`CODING_CLAUDE_TOOL_RUNNER_IMAGE`, and `node-python` with toolchain version
|
|
24
|
+
`3.12` uses `CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12` (Python with
|
|
25
|
+
pytest and ruff, the same packages as the Codex `node-python` worker). As with
|
|
26
|
+
Codex, pip installs go into a virtual environment
|
|
27
|
+
(`python -m venv --system-site-packages .venv`), and pip there reaches only the
|
|
28
|
+
registry. The npm lockfile check
|
|
29
|
+
(the shim on `PATH` that plans `npm ci`/`npm install` against the proxy
|
|
30
|
+
before it runs; see "Lockfile installs: verified, then approved exactly"
|
|
31
|
+
below) applies there exactly as it does for Codex. Before a Claude Code run's
|
|
32
|
+
changes are committed, the control plane rewrites any collected lockfile's
|
|
33
|
+
proxy URLs back to the ecosystem's real public registry URL (for example
|
|
34
|
+
`https://registry.npmjs.org/...`), so a lockfile in the resulting pull
|
|
35
|
+
request never names the internal proxy.
|
|
36
|
+
|
|
37
|
+
## Three ways to get dependencies into a run
|
|
38
|
+
|
|
39
|
+
1. **The standard worker image plus a package allowlist (recommended
|
|
40
|
+
default).** Use wardby's stock `node` or `node-python` worker image and
|
|
41
|
+
enable the packages your agent needs on its allowlist (below). No custom
|
|
42
|
+
image to build or maintain; the agent installs what it needs at run time.
|
|
43
|
+
2. **A custom `workerImageRef` with dependencies baked in.** Still supported:
|
|
44
|
+
point the agent's profile at your own image, built on wardby's driver base
|
|
45
|
+
image (see [Bring-your-own worker images](coding-worker-byo-images.md)),
|
|
46
|
+
with everything preinstalled. Setting `workerImageRef` needs `agents:admin`
|
|
47
|
+
and the admin role, and the image must be pinned by digest.
|
|
48
|
+
3. **Both together.** Use a custom image for a toolchain or system libraries
|
|
49
|
+
the registry can't provide (a compiler, a non-Node/Python runtime, apt
|
|
50
|
+
packages), and the registry for the project-level npm/PyPI packages your
|
|
51
|
+
agent adds during the run.
|
|
52
|
+
|
|
53
|
+
npm and pip in every coding run are always pointed at the proxy — this is not
|
|
54
|
+
optional per run. If an agent has no allowlist entries for an ecosystem (the
|
|
55
|
+
default for a new agent), an install in that ecosystem gets a clear refusal
|
|
56
|
+
(`403 wardby_package_not_allowed`) rather than silently failing or reaching
|
|
57
|
+
the real registry.
|
|
58
|
+
|
|
59
|
+
## Enabling packages on an agent
|
|
60
|
+
|
|
61
|
+
Package permissions live on the coding profile's `packageAllowlist` field,
|
|
62
|
+
changed with `update_agent`. Changing `packageAllowlist` or `packagePolicy`
|
|
63
|
+
needs the `packages:approve` scope (or `agents:admin`) plus a role that grants it,
|
|
64
|
+
`admin` or `package-approver` (see
|
|
65
|
+
[roles and privileged operations](security-deployment.md#roles-and-privileged-operations)) — holding only
|
|
66
|
+
`agents:write` lets you manage everything else about the agent, but a
|
|
67
|
+
`packageAllowlist`/`packagePolicy` change from that caller is refused, since
|
|
68
|
+
it widens what the agent can download.
|
|
69
|
+
|
|
70
|
+
`packageAllowlist` is keyed by ecosystem (`npm`, `pypi`), each an array of
|
|
71
|
+
approved top-level entries. An entry is a bare package name, a name with a
|
|
72
|
+
version range in that ecosystem's own syntax, or (npm only) a scope wildcard:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"codingProfile": {
|
|
77
|
+
"packageAllowlist": {
|
|
78
|
+
"npm": ["react@^19", "@testing-library/*"],
|
|
79
|
+
"pypi": ["flask>=3"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `react@^19` — npm semver range syntax.
|
|
86
|
+
- `@testing-library/*` — every package under that npm scope.
|
|
87
|
+
- `flask>=3` — PEP 440 specifier syntax for PyPI.
|
|
88
|
+
- A bare name with no range (`"lodash"`) allows any version, subject to the
|
|
89
|
+
other safeguards below.
|
|
90
|
+
- npm names are matched exactly, including case. npm treats some legacy
|
|
91
|
+
capitalized names as distinct packages (`JSONStream` is not `jsonstream`),
|
|
92
|
+
so allowing one never allows the other; write the name as npm spells it.
|
|
93
|
+
(npm scopes are always lower case.) PyPI names are compared after PEP 503
|
|
94
|
+
normalization, so `Flask`, `flask` and `FLASK` are the same entry.
|
|
95
|
+
|
|
96
|
+
An absent or empty allowlist leaves registry mode off for that agent (the
|
|
97
|
+
default). Only _top-level_ entries need to be on the allowlist — once a
|
|
98
|
+
version is approved, the proxy reads its dependency graph from registry
|
|
99
|
+
metadata and grows the run's allowance automatically as npm or pip requests
|
|
100
|
+
each dependency's metadata, so you don't have to enumerate transitive
|
|
101
|
+
dependencies yourself.
|
|
102
|
+
|
|
103
|
+
### Lockfile installs: verified, then approved exactly
|
|
104
|
+
|
|
105
|
+
`npm ci` (or `npm install` with a complete `package-lock.json`) skips
|
|
106
|
+
metadata and requests each tarball directly, at the
|
|
107
|
+
`https://registry.npmjs.org/...` URL the lockfile records, which npm rewrites
|
|
108
|
+
to the proxy. Before that, the coding worker's `npm` verifies the lockfile:
|
|
109
|
+
the driver image puts a small shim first on the agent's `PATH`
|
|
110
|
+
(`/opt/wardby/bin/npm`, also restored for login shells). For `npm ci`,
|
|
111
|
+
`npm install`/`i`/`add` (and their aliases) in a project with a
|
|
112
|
+
`package-lock.json` or `npm-shrinkwrap.json`, it sends the lockfile to the
|
|
113
|
+
proxy's `POST /registry/npm/-/plan` (with the run's registry token), prints a
|
|
114
|
+
one-line summary and any refusals to stderr, and then runs the real npm with
|
|
115
|
+
the same arguments. Anything else runs npm directly, and a plan that fails
|
|
116
|
+
still runs npm: the install then goes through the usual checks below. An npm
|
|
117
|
+
started by another npm (a lifecycle script's) doesn't plan again; its
|
|
118
|
+
installs also go through the usual checks.
|
|
119
|
+
|
|
120
|
+
The proxy trusts nothing the lockfile claims. For lockfileVersion 2 or 3
|
|
121
|
+
(version 1 is refused with `400 wardby_lockfile_unsupported`; regenerate it
|
|
122
|
+
with npm 7 or later):
|
|
123
|
+
|
|
124
|
+
- **Integrity.** For each entry `name@version` it reads the registry's own
|
|
125
|
+
record of that exact version (`registry.npmjs.org/<name>/<version>`, a few
|
|
126
|
+
KB). The lockfile's `integrity` must equal the registry's `dist.integrity`,
|
|
127
|
+
or the entry is refused with `wardby_lockfile_integrity_mismatch`.
|
|
128
|
+
- **Edges.** Dependencies come from the registry's record too (dependencies,
|
|
129
|
+
optional and peer dependencies, npm aliases resolved), never from the
|
|
130
|
+
lockfile. Each one is matched to the lockfile entry npm would use (the
|
|
131
|
+
nearest enclosing package's `node_modules/<name>`), and counts only when
|
|
132
|
+
that entry is the declared package at a version inside the declared range
|
|
133
|
+
(strict semver, as the walk matches).
|
|
134
|
+
- **Reachability.** It starts from the project's own dependencies (and
|
|
135
|
+
workspaces') that are allowlist entries, at a version inside the
|
|
136
|
+
allowlisted range; a scope wildcard such as `@heroui/*` counts here, since
|
|
137
|
+
the lockfile names the exact package. From there it follows only verified
|
|
138
|
+
edges. An entry nothing reaches is refused with `wardby_package_not_allowed`.
|
|
139
|
+
- **Release age and advisories.** Each version's publish time (read once by
|
|
140
|
+
streaming the package's full document for its `time` field, never holding
|
|
141
|
+
it) must be older than `minReleaseAgeDays`, and one OSV `querybatch`
|
|
142
|
+
request covers every version (1000 per request), with each advisory's
|
|
143
|
+
severity read once (a version's results are cached for an hour, so a
|
|
144
|
+
repeated plan doesn't query again). Too new, or a HIGH/CRITICAL or
|
|
145
|
+
malware advisory: `wardby_version_filtered`. If OSV can't be reached, the whole plan fails
|
|
146
|
+
closed with `503 wardby_audit_unavailable` (unless
|
|
147
|
+
`REGISTRY_AUDIT_FAIL_OPEN` is set).
|
|
148
|
+
- **Other sources.** An entry installed from git, a URL or a path is refused
|
|
149
|
+
with `wardby_lockfile_entry_unsupported`; a bundled dependency is part of
|
|
150
|
+
its parent's tarball and needs nothing.
|
|
151
|
+
|
|
152
|
+
A refused entry refuses only itself and whatever is reachable only through
|
|
153
|
+
it; everything else is approved, as exact `name@version` pairs for the run.
|
|
154
|
+
The response is `{ "approved": <count>, "refused": [{ "name", "version",
|
|
155
|
+
"code", "reason" }] }`, and each refusal is recorded like any other (within
|
|
156
|
+
the 500-per-run cap). A tarball request for an approved `name@version` is then served directly,
|
|
157
|
+
checked against the approved integrity, with no graph walk.
|
|
158
|
+
|
|
159
|
+
A version the plan refused for a reason about that `name@version` itself is
|
|
160
|
+
answered straight from the plan, with no walk either: `403` with the plan's
|
|
161
|
+
code and reason. That is a version too new (`wardby_version_filtered`, until
|
|
162
|
+
it is old enough: the age is re-checked at each request), one with a
|
|
163
|
+
HIGH/CRITICAL or malware advisory (`wardby_version_filtered`, naming it), one
|
|
164
|
+
npm doesn't have (`wardby_package_not_found`), one npm publishes no integrity
|
|
165
|
+
for (`wardby_registry_integrity_missing`), and one hosted outside npm
|
|
166
|
+
(`wardby_upstream_host_not_allowed`). A refusal that depends on the lockfile
|
|
167
|
+
(`wardby_package_not_allowed`, `wardby_lockfile_integrity_mismatch`,
|
|
168
|
+
`wardby_lockfile_entry_unsupported`) is reported in the plan's response, but
|
|
169
|
+
isn't used to answer downloads: the same version reached another way (an
|
|
170
|
+
`overrides` entry, a later `npm install`) goes through the usual checks, and
|
|
171
|
+
a hostile lockfile entry can't block a package by claiming its name. Neither
|
|
172
|
+
does a version the registry couldn't be read for, nor one the plan never
|
|
173
|
+
mentioned (a package added with `npm install <new-package>`, an install
|
|
174
|
+
without a lockfile): they go through the allowlist and the dependency-graph
|
|
175
|
+
walk below.
|
|
176
|
+
|
|
177
|
+
The per-version facts (integrity, publish time, dependencies, download URL)
|
|
178
|
+
never change, so they are stored in the database and reused by every later
|
|
179
|
+
plan; decisions are always recomputed from the run's own allowlist. A plan is
|
|
180
|
+
bounded by `REGISTRY_PLAN_MAX_ENTRIES` entries (`413
|
|
181
|
+
wardby_lockfile_too_large`, as is a lockfile over 20 MiB) and
|
|
182
|
+
`REGISTRY_PLAN_TIMEOUT_MS` (`503 wardby_plan_incomplete`; what was verified is
|
|
183
|
+
kept, so a retry resumes). The proxy checks the registry token and waits for
|
|
184
|
+
one of two plan slots before it reads the lockfile at all, so an
|
|
185
|
+
unauthenticated or queued plan holds none of it; a run may run one plan at a
|
|
186
|
+
time and make `REGISTRY_PLAN_MAX_PER_RUN` in all (`429 wardby_plan_limit`).
|
|
187
|
+
It only reads the registry for entries it reaches, so a lockfile full of
|
|
188
|
+
unrelated packages costs nothing upstream. Measured
|
|
189
|
+
against real npm and OSV with a 0-day release age, the knock-knock `web/`
|
|
190
|
+
lockfile (216 entries: React 19, HeroUI 3, Vite 8, Vitest 5, jsdom) was
|
|
191
|
+
verified in 9.5 s cold and 0.7 s with stored facts, and its whole `npm ci`
|
|
192
|
+
took 12 s cold and 3 s warm, with the proxy at 183 MiB peak RSS (30 MiB
|
|
193
|
+
heap). At the default 3 days the same plan
|
|
194
|
+
took 8 s and refused 53 entries: vite 8.3.1 and vitest 5.0.2 were days old,
|
|
195
|
+
and those two are answered from the plan, but the 51 entries refused only as
|
|
196
|
+
unreachable through them are lockfile-dependent verdicts, so npm's requests
|
|
197
|
+
for them went to the graph walk, and the failing `npm ci` took 547 s with the
|
|
198
|
+
proxy at 1604 MiB peak RSS.
|
|
199
|
+
|
|
200
|
+
### Lockfile installs without a plan: the dependency-graph walk
|
|
201
|
+
|
|
202
|
+
When a tarball request names a package the run hasn't approved or seen yet,
|
|
203
|
+
the proxy resolves the approved dependency graph on demand. The graph is
|
|
204
|
+
range-aware:
|
|
205
|
+
|
|
206
|
+
- It starts from the allowlisted packages, at their kept versions: those
|
|
207
|
+
inside the allowlisted range, past the minimum release age, and not
|
|
208
|
+
withheld by the vulnerability audit.
|
|
209
|
+
- From each kept version it follows every declared dependency `name@range`
|
|
210
|
+
only into that dependency's kept versions that **satisfy the declared
|
|
211
|
+
range** (npm semver), and continues only from those versions. A
|
|
212
|
+
dependency's newer major, or an old version outside the range, never
|
|
213
|
+
contributes its own dependencies.
|
|
214
|
+
- Every in-range kept version counts, not just the newest one, so a
|
|
215
|
+
lockfile pinned to an older version inside the range still gets that
|
|
216
|
+
version's dependencies.
|
|
217
|
+
- A spec that isn't a range (a dist-tag such as `latest`, an empty spec)
|
|
218
|
+
counts every kept version. A range no kept version satisfies contributes
|
|
219
|
+
nothing.
|
|
220
|
+
- A package joins the graph, and is recorded as allowed, once one of its
|
|
221
|
+
kept versions satisfies a range that reached it. The walk stops as soon as
|
|
222
|
+
it finds the requested package.
|
|
223
|
+
|
|
224
|
+
The walk is done at most once per run (concurrent and later misses share its
|
|
225
|
+
result), and it is bounded by `REGISTRY_MAX_GRAPH_PACKAGES` and
|
|
226
|
+
`REGISTRY_GRAPH_TIMEOUT_MS` (see [Operator limits](#operator-limits)).
|
|
227
|
+
Allowances are per package name, so once a package is allowed any of its
|
|
228
|
+
kept versions can be downloaded. Serving a package's metadata directly (a
|
|
229
|
+
plain `npm install`) still allows the dependencies of all its kept versions,
|
|
230
|
+
because the proxy doesn't record which ranges a package was allowed under.
|
|
231
|
+
A scope wildcard such as `@testing-library/*` can't be enumerated, so it is
|
|
232
|
+
never a starting point of the walk. A package allowed _only_ by a scope
|
|
233
|
+
wildcard is installable, but under a lockfile its own dependencies are not
|
|
234
|
+
found by the walk (it never expands that package unless an exact allowlist
|
|
235
|
+
entry's graph reaches it). When npm reads that package's metadata, as a plain
|
|
236
|
+
`npm install` does, its dependencies are allowed as usual. With `npm ci`, give
|
|
237
|
+
its dependencies their own allowlist entries, or also list the package itself
|
|
238
|
+
by exact name. PyPI's index carries no dependencies (pip reads them from each
|
|
239
|
+
wheel), so there is no walk for PyPI.
|
|
240
|
+
|
|
241
|
+
Dependencies are followed by the package they install: an npm alias such as
|
|
242
|
+
`"string-width-cjs": "npm:string-width@^4"` allows `string-width`, not
|
|
243
|
+
`string-width-cjs`, and follows it under the alias's own range (`^4`).
|
|
244
|
+
Dependency specs that aren't fetched from the registry
|
|
245
|
+
(`file:`, `link:`, local paths, git URLs and `github:`/`user/repo`
|
|
246
|
+
shorthands, `http(s):` tarball URLs, `workspace:`) allow nothing.
|
|
247
|
+
|
|
248
|
+
If an upstream (the npm registry or the OSV audit) fails while the walk reads
|
|
249
|
+
part of the graph, that part is retried: once more straight away, then again
|
|
250
|
+
on later requests, up to four attempts per package per run. Until it can be
|
|
251
|
+
read, a package that wasn't found is answered with a "could not be checked…
|
|
252
|
+
try again" error (`502 wardby_upstream_error`, or
|
|
253
|
+
`503 wardby_audit_unavailable` if it was the audit), not with
|
|
254
|
+
`wardby_package_not_allowed`.
|
|
255
|
+
|
|
256
|
+
Likewise, if the walk is cut short before it reaches the requested package,
|
|
257
|
+
the package isn't proven absent, so the answer is never
|
|
258
|
+
`wardby_package_not_allowed`. After `REGISTRY_GRAPH_TIMEOUT_MS` the answer is
|
|
259
|
+
`503 wardby_graph_incomplete`: the next request resumes the walk where it
|
|
260
|
+
stopped (npm retries a 5xx on its own), so a retry can succeed. After
|
|
261
|
+
`REGISTRY_MAX_GRAPH_PACKAGES` trips, the bound is permanent for the run, so
|
|
262
|
+
the answer is a definitive `403 wardby_graph_limit` that clients don't retry:
|
|
263
|
+
allowlist the package directly or raise the bound.
|
|
264
|
+
|
|
265
|
+
The walk reads full npm packuments (the largest are tens of MB) but keeps
|
|
266
|
+
only what it needs from each: per version, its dependency specs, publish
|
|
267
|
+
time, tarball URL and integrity. A packument is garbage as soon as that is
|
|
268
|
+
extracted, and the rendered form npm is served is built only when a client
|
|
269
|
+
asks for a package's metadata. The walk's state is dropped when the run's
|
|
270
|
+
deadline passes. Even so, a lockfile install of a large graph is the proxy's
|
|
271
|
+
largest memory user: a measured `npm ci` of a ~220-package lockfile (React
|
|
272
|
+
19, Vite 8, Vitest 5, jsdom) peaked at 1752 MiB RSS (549 MiB heap), which is
|
|
273
|
+
why the GKE overlay gives the proxy 3Gi. A lockfile the shim verified
|
|
274
|
+
avoids the walk for every version the plan approved, and for every version it
|
|
275
|
+
refused for a reason about the version itself (too new, an advisory, ...).
|
|
276
|
+
|
|
277
|
+
## What the agent can then run
|
|
278
|
+
|
|
279
|
+
Inside the run, `npm install <package>` and `pip install <package>` (into
|
|
280
|
+
`/workspace/.venv`) work exactly as they would against the real registries,
|
|
281
|
+
for anything reachable from the allowlist. Installed `node_modules`, the
|
|
282
|
+
`.venv`, and package-manager caches are never collected: they don't count
|
|
283
|
+
toward workspace size or checks, and they're never part of the resulting
|
|
284
|
+
commit or diff. The agent's shells get `TMPDIR` pointed at the workspace's
|
|
285
|
+
`.cache/tmp` rather than the sandbox's small in-memory `/tmp`, since an
|
|
286
|
+
install like `pip install -e '.[test]'` unpacks and builds in `TMPDIR` and
|
|
287
|
+
can otherwise run out of space on a large package; that directory counts
|
|
288
|
+
toward the run's `workspaceDiskMb` and, like the rest of `.cache`, is never
|
|
289
|
+
collected either.
|
|
290
|
+
|
|
291
|
+
## Safeguards
|
|
292
|
+
|
|
293
|
+
- **Dependency graph only.** Only packages on the allowlist, or reachable
|
|
294
|
+
through the dependency graph of an allowlisted package, can be installed —
|
|
295
|
+
an agent cannot fetch an arbitrary unrelated package just because _some_
|
|
296
|
+
package is allowed.
|
|
297
|
+
- **Minimum release age.** A version has to be at least a few days old before
|
|
298
|
+
it can be installed, so a newly published (and potentially not-yet-flagged)
|
|
299
|
+
malicious release is excluded by default. The default is 3 days; a
|
|
300
|
+
profile's `packagePolicy.minReleaseAgeDays` can override it to any integer
|
|
301
|
+
0–30. A version with no publish timestamp is treated as too new to install.
|
|
302
|
+
For PyPI the age applies to each file on its own upload time: a wheel added
|
|
303
|
+
to an old release yesterday is hidden from the index and refused
|
|
304
|
+
(`404 wardby_version_filtered`) until it is old enough, while the release's
|
|
305
|
+
older wheels are served. npm versions are immutable, so each version's
|
|
306
|
+
tarball has the version's publish time.
|
|
307
|
+
- **OSV vulnerability audit.** Every package version is checked against the
|
|
308
|
+
OSV database; versions affected by a **high** or **critical** severity
|
|
309
|
+
advisory, or by a **malware** advisory (an OSV `MAL-…` entry, which has no
|
|
310
|
+
severity, one aliasing or importing one, or a CWE-506 "embedded malicious
|
|
311
|
+
code" advisory), are withheld, and lower-severity advisories are allowed but
|
|
312
|
+
reported. A version is affected when an advisory entry for that exact
|
|
313
|
+
package (same ecosystem and name) lists it, or when it falls inside one
|
|
314
|
+
of the entry's version ranges, compared with the ecosystem's own version
|
|
315
|
+
rules (semver for npm, PEP 440 for PyPI). A version the audit cannot
|
|
316
|
+
parse counts as affected. If OSV can't be reached, installs in that request fail closed
|
|
317
|
+
(`503 wardby_audit_unavailable`) rather than skipping the check — an
|
|
318
|
+
operator can opt out of fail-closed with `REGISTRY_AUDIT_FAIL_OPEN=true`.
|
|
319
|
+
- **Wheels only for Python.** PyPI source distributions (sdists), which can
|
|
320
|
+
run arbitrary code at install/build time, are never served — only wheels.
|
|
321
|
+
- **npm install scripts are off, but only by configuration.** The proxy sets
|
|
322
|
+
`npm_config_ignore_scripts=true` in the agent's environment, which disables
|
|
323
|
+
npm's install-time script hooks. This is a configuration default, not
|
|
324
|
+
something the proxy can enforce on the downloaded tarball itself: the
|
|
325
|
+
proxy cannot strip scripts from a package, so an agent that deliberately
|
|
326
|
+
overrode this setting could still run one. A script that ran would still be
|
|
327
|
+
confined to the same sandbox — no network, no credentials, only the
|
|
328
|
+
workspace writable — but this is a documented limit, not a guarantee.
|
|
329
|
+
|
|
330
|
+
## Operator limits
|
|
331
|
+
|
|
332
|
+
These per-run limits protect the proxy process and are configured with
|
|
333
|
+
environment variables (defaults shown); they're enforced by the single proxy
|
|
334
|
+
process handling the run, counting files and bytes currently in flight as well
|
|
335
|
+
as what's already been recorded, so parallel downloads (for example npm's
|
|
336
|
+
default concurrent connections) can't add up to more than the limit before any
|
|
337
|
+
one of them finishes:
|
|
338
|
+
|
|
339
|
+
| Setting | Default | Meaning |
|
|
340
|
+
| ------------------------------ | ------- | ------------------------------------------------------------- |
|
|
341
|
+
| `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file. |
|
|
342
|
+
| `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run. |
|
|
343
|
+
| `REGISTRY_MAX_FILES` | 5000 | Total files served in one run. |
|
|
344
|
+
| `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download. |
|
|
345
|
+
| `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable. |
|
|
346
|
+
| `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included. |
|
|
347
|
+
| `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads. |
|
|
348
|
+
| `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 | Packages the on-demand graph walk may expand in one run. |
|
|
349
|
+
| `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time allowed for one on-demand graph walk. |
|
|
350
|
+
| `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool. |
|
|
351
|
+
| `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests). |
|
|
352
|
+
| `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have. |
|
|
353
|
+
| `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan. |
|
|
354
|
+
| `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make. |
|
|
355
|
+
|
|
356
|
+
Metadata is cached for five minutes in a bounded cache (500 packages and
|
|
357
|
+
64 MiB of trimmed metadata, least recently used evicted first), and
|
|
358
|
+
concurrent requests for the same package share one upstream fetch.
|
|
359
|
+
|
|
360
|
+
At most 500 refusals are recorded per run. Refusals past that still return
|
|
361
|
+
their normal error to npm or pip; they just aren't added to the run's record,
|
|
362
|
+
so a worker retrying refused names in a loop can't grow it without bound.
|
|
363
|
+
|
|
364
|
+
## Integrity
|
|
365
|
+
|
|
366
|
+
Every file the proxy resolves from its own copy of the upstream metadata
|
|
367
|
+
(never a client-supplied URL) is streamed to the agent with its checksum
|
|
368
|
+
verified while downloading, aborting on a mismatch — but only when the
|
|
369
|
+
ecosystem actually publishes one. npm packages carry `dist.integrity` or
|
|
370
|
+
`dist.shasum`, and PyPI wheels carry a SHA-256 hash, so both are verified
|
|
371
|
+
today. An ecosystem whose files sometimes ship with no published checksum
|
|
372
|
+
(for example some Composer archives, if that ecosystem is added later) is
|
|
373
|
+
streamed unverified for those files — there is nothing to verify against.
|
|
374
|
+
|
|
375
|
+
## What the reviewer sees
|
|
376
|
+
|
|
377
|
+
Every package the proxy served during a run is recorded, and so is every
|
|
378
|
+
refusal up to the 500-per-run cap above. `get_run` returns `packages` and
|
|
379
|
+
`packageRefusals` for a coding run, each deduplicated (a retried download is
|
|
380
|
+
one package):
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"packages": [
|
|
385
|
+
{ "ecosystem": "npm", "name": "@heroui/react", "version": "3.2.6", "size": 482113 },
|
|
386
|
+
{ "ecosystem": "pypi", "name": "flask", "version": "3.0.0", "size": 101817 }
|
|
387
|
+
],
|
|
388
|
+
"packageRefusals": [{ "ecosystem": "npm", "name": "left-pad", "reason": "wardby_package_not_allowed" }]
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
`size` is the number of bytes served, or `null` if none was recorded.
|
|
393
|
+
`get_run` also returns `packagePlan: { "approved": <n>, "refused": <n> }`:
|
|
394
|
+
the exact versions lockfile plans approved for the run, and the distinct
|
|
395
|
+
entries they refused (each refusal is also in `packageRefusals`).
|
|
396
|
+
|
|
397
|
+
The pull request finalization also appends a collapsed **Packages installed
|
|
398
|
+
during this run** section listing the same information, so a reviewer
|
|
399
|
+
doesn't have to ask the agent what it added. It lists at most 100 packages
|
|
400
|
+
and 100 refusals, followed by "…and N more — see get_run for the full list";
|
|
401
|
+
`get_run` always has the complete list. If the report can't be loaded or
|
|
402
|
+
rendered, the section is left out and the pull request is still opened.
|
|
403
|
+
|
|
404
|
+
## Error codes
|
|
405
|
+
|
|
406
|
+
npm and pip print the proxy's error body verbatim, so these are what you'll
|
|
407
|
+
see on a failed install:
|
|
408
|
+
|
|
409
|
+
| Code | Status | Meaning / what to do |
|
|
410
|
+
| ------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
411
|
+
| `wardby_package_not_allowed` | 403 | The package isn't on the allowlist and isn't reachable from an allowlisted package's dependency graph. Add it (or its top-level dependent) to `packageAllowlist`. |
|
|
412
|
+
| `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. |
|
|
413
|
+
| `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. |
|
|
414
|
+
| `wardby_package_not_found` | 404 | The upstream registry has no such package name. Check the spelling (npm names are case-sensitive). |
|
|
415
|
+
| `wardby_package_too_large` | 413 | The file exceeds `REGISTRY_MAX_FILE_MB`. Ask the operator to raise it if the file is legitimately larger. |
|
|
416
|
+
| `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. |
|
|
417
|
+
| `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. |
|
|
418
|
+
| `wardby_graph_incomplete` | 503 | A lockfile install requested a package the on-demand dependency-graph walk hadn't reached when it was cut short by `REGISTRY_GRAPH_TIMEOUT_MS`. Retry: the walk resumes where it stopped (npm retries 5xx on its own). Never means the package is outside the graph. |
|
|
419
|
+
| `wardby_graph_limit` | 403 | The walk hit `REGISTRY_MAX_GRAPH_PACKAGES` for this run before reaching the package. Permanent for the run, so retrying won't help: allowlist the package directly, or ask the operator to raise the limit. Never means the package is outside the graph. |
|
|
420
|
+
| `wardby_lockfile_unsupported` | 400 | The lockfile sent to `POST /-/plan` isn't lockfileVersion 2 or 3 (or has no `packages`). Regenerate it with npm 7 or later. The install still runs, through the walk. |
|
|
421
|
+
| `wardby_lockfile_too_large` | 413 | The lockfile is over 20 MiB or has more than `REGISTRY_PLAN_MAX_ENTRIES` entries. The install still runs, through the walk. |
|
|
422
|
+
| `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). Regenerate the lockfile entry; never edit integrity by hand. |
|
|
423
|
+
| `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. |
|
|
424
|
+
| `wardby_plan_limit` | 429 | The run has made `REGISTRY_PLAN_MAX_PER_RUN` lockfile plans. The install still runs, through the usual checks. |
|
|
425
|
+
| `wardby_registry_integrity_missing` | — | A plan refusal: npm publishes no integrity for this version, so a download of it couldn't be checked. |
|
|
426
|
+
| `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, through the walk. |
|
|
427
|
+
| `wardby_plan_incomplete` | 503 | The plan didn't finish within `REGISTRY_PLAN_TIMEOUT_MS` (or the client went away). What was verified is kept, so a retry resumes; the install still runs, through the walk. |
|
|
428
|
+
| `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. |
|
|
429
|
+
| `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. |
|
|
430
|
+
| `wardby_upstream_unavailable` | 504 | The upstream registry didn't answer a metadata request within `REGISTRY_METADATA_TIMEOUT_MS`. Retry. |
|
|
431
|
+
| `wardby_metadata_too_large` | 502 | The package's metadata document is larger than `REGISTRY_MAX_METADATA_MB`. Ask the operator to raise it. |
|
|
432
|
+
| `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. |
|
|
433
|
+
| `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. |
|
|
434
|
+
|
|
435
|
+
## Adding an ecosystem
|
|
436
|
+
|
|
437
|
+
npm and PyPI are the two ecosystems registry mode supports today; the adapter
|
|
438
|
+
interface is designed to add more (Composer, RubyGems, Go modules are the
|
|
439
|
+
known candidates) without changing the schema or the allowlist format. Adding
|
|
440
|
+
one means:
|
|
441
|
+
|
|
442
|
+
1. Implement `RegistryAdapter` and add it to `REGISTRY_ADAPTERS`. No schema
|
|
443
|
+
change is needed.
|
|
444
|
+
2. Set `upstreamHosts` to the smallest set of hosts its downloads use.
|
|
445
|
+
3. Mark `allowed: false` on file types that run code at install time where the
|
|
446
|
+
proxy can tell them apart, and use `workerConfig` to disable install-time
|
|
447
|
+
code where only the client can.
|
|
448
|
+
4. Record upstream fixtures, and add a real-client integration test.
|
|
449
|
+
5. Provide a worker image with the language runtime.
|
|
450
|
+
6. Document every safeguard the ecosystem cannot enforce.
|
|
451
|
+
|
|
452
|
+
For example, Composer downloads from GitHub and GitLab archive hosts, often
|
|
453
|
+
with `integrity: null`, and needs `--no-plugins` and `--no-scripts` set
|
|
454
|
+
through a `COMPOSER_HOME/config.json` file from `workerConfig`. Go fits most
|
|
455
|
+
directly, because `GOPROXY` is designed for this kind of proxy.
|