@wardby/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +34 -4
- package/README.md +183 -71
- package/dist/claude-coding-worker/driver.d.ts +7 -1
- package/dist/claude-coding-worker/driver.js +31 -2
- package/dist/claude-coding-worker/main.js +5 -2
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +13 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +219 -29
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +99 -3
- package/dist/coding/protocol.js +155 -4
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +18 -0
- package/dist/coding-worker/artifact.js +48 -6
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +14 -1
- package/dist/coding-worker/driver.js +106 -14
- package/dist/coding-worker/errors.d.ts +7 -0
- package/dist/coding-worker/errors.js +26 -0
- package/dist/coding-worker/main.js +13 -4
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +41 -0
- package/dist/config/providers.js +66 -0
- package/dist/core/budget-groups.d.ts +109 -6
- package/dist/core/budget-groups.js +125 -19
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +1 -1
- package/dist/core/datastores.d.ts +1 -1
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +40 -2
- package/dist/core/db.js +75 -2
- package/dist/core/dispatch.d.ts +49 -7
- package/dist/core/dispatch.js +290 -45
- package/dist/core/engine-native.js +27 -10
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +49 -0
- package/dist/core/host-events.js +257 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +61 -0
- package/dist/core/host-status.js +211 -0
- package/dist/core/http-runtime.js +3 -4
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/lease.d.ts +1 -1
- package/dist/core/private-directory.d.ts +20 -0
- package/dist/core/private-directory.js +36 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +39 -3
- package/dist/core/reconciler.js +86 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +347 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/runner.d.ts +31 -9
- package/dist/core/runner.js +237 -40
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secrets.d.ts +12 -3
- package/dist/core/secrets.js +25 -6
- package/dist/core/subagent-memory-tools.d.ts +2 -2
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +64 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +9 -2
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +328 -0
- package/dist/generated/prisma/browser.js +17 -0
- package/dist/generated/prisma/client.d.ts +347 -0
- package/dist/generated/prisma/client.js +34 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
- package/dist/generated/prisma/commonInputTypes.js +10 -0
- package/dist/generated/prisma/enums.d.ts +59 -0
- package/dist/generated/prisma/enums.js +59 -0
- package/dist/generated/prisma/internal/class.d.ts +605 -0
- package/dist/generated/prisma/internal/class.js +49 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
- package/dist/generated/prisma/models/Agent.d.ts +3560 -0
- package/dist/generated/prisma/models/Agent.js +1 -0
- package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
- package/dist/generated/prisma/models/AgentDatastore.js +1 -0
- package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
- package/dist/generated/prisma/models/AgentMemory.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
- package/dist/generated/prisma/models/AgentSecret.js +1 -0
- package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
- package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
- package/dist/generated/prisma/models/AgentTool.js +1 -0
- package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
- package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
- package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
- package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
- package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
- package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
- package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
- package/dist/generated/prisma/models/AuthSession.js +1 -0
- package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
- package/dist/generated/prisma/models/AuthUser.js +1 -0
- package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
- package/dist/generated/prisma/models/BudgetGroup.js +1 -0
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
- package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
- package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
- package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
- package/dist/generated/prisma/models/CodingProxySession.js +1 -0
- package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
- package/dist/generated/prisma/models/CodingRun.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
- package/dist/generated/prisma/models/Datastore.js +1 -0
- package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
- package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
- package/dist/generated/prisma/models/OAuthClient.js +1 -0
- package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
- package/dist/generated/prisma/models/OAuthFamily.js +1 -0
- package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
- package/dist/generated/prisma/models/OAuthGrant.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +2245 -0
- package/dist/generated/prisma/models/Principal.js +1 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +2669 -0
- package/dist/generated/prisma/models/Run.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
- package/dist/generated/prisma/models/SchedulerLease.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +1399 -0
- package/dist/generated/prisma/models/Secret.js +1 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
- package/dist/generated/prisma/models/Task.d.ts +1176 -0
- package/dist/generated/prisma/models/Task.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +1490 -0
- package/dist/generated/prisma/models/Tool.js +1 -0
- package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
- package/dist/generated/prisma/models/Webhook.js +1 -0
- package/dist/generated/prisma/models.d.ts +45 -0
- package/dist/generated/prisma/models.js +1 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +660 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +6 -1
- package/dist/import/create.js +68 -14
- package/dist/import/index.d.ts +1 -1
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +11 -11
- package/dist/mcp/auth/access.d.ts +70 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +37 -63
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/principal.d.ts +1 -1
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +32 -3
- package/dist/mcp/auth/resource-server.js +84 -3
- package/dist/mcp/auth/self-hosted/browser.js +22 -8
- package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
- package/dist/mcp/context.d.ts +28 -2
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/github-ingress.d.ts +36 -0
- package/dist/mcp/host-events/github-ingress.js +92 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/index.d.ts +8 -1
- package/dist/mcp/index.js +106 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/shared/page-style.d.ts +16 -0
- package/dist/mcp/shared/page-style.js +129 -0
- package/dist/mcp/tasks/manager.d.ts +1 -1
- package/dist/mcp/tools/agents.js +447 -49
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/datastore.js +23 -16
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +181 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +4 -7
- package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
- package/dist/mcp/tools/secret-elicitation-form.js +10 -28
- package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +53 -17
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +202 -41
- package/dist/mcp/tools/trigger.js +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +11 -1
- package/dist/mcp/transport/streamable-http.js +32 -2
- package/dist/mcp/unattended-schedules.d.ts +1 -1
- package/dist/mcp/webhooks/ingress.d.ts +1 -1
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +3 -1
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +4 -3
- package/dist/providers/auth/self-hosted.js +16 -4
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
- package/dist/providers/coding-proxy/memory-ledger.js +21 -2
- package/dist/providers/coding-proxy/metering.d.ts +4 -0
- package/dist/providers/coding-proxy/metering.js +28 -2
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
- package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
- package/dist/providers/coding-proxy/proxy.d.ts +13 -0
- package/dist/providers/coding-proxy/proxy.js +479 -18
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -1
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +15 -0
- package/dist/providers/datastore/postgres.d.ts +1 -1
- package/dist/providers/engine/types.d.ts +10 -0
- package/dist/providers/executor/build.d.ts +3 -3
- package/dist/providers/executor/composition.d.ts +4 -1
- package/dist/providers/executor/composition.js +19 -0
- package/dist/providers/executor/container.d.ts +106 -4
- package/dist/providers/executor/container.js +305 -22
- package/dist/providers/executor/dbos.d.ts +3 -4
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +7 -0
- package/dist/providers/executor/routing.js +13 -0
- package/dist/providers/executor/types.d.ts +22 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +60 -2
- package/dist/providers/jobs/docker.js +313 -50
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +21 -2
- package/dist/providers/jobs/kubernetes.js +117 -31
- package/dist/providers/jobs/types.d.ts +20 -0
- package/dist/providers/llm/anthropic.js +6 -2
- package/dist/providers/llm/bedrock.js +2 -1
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +3 -1
- package/dist/providers/llm/claude-provider.js +5 -1
- package/dist/providers/llm/index.d.ts +1 -1
- package/dist/providers/llm/index.js +1 -1
- package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
- package/dist/providers/llm/pricing-anthropic.js +32 -14
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
- package/dist/providers/llm/routing.d.ts +10 -1
- package/dist/providers/llm/routing.js +18 -0
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/memory/postgres.d.ts +1 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +180 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +567 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +284 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +79 -14
- package/dist/providers/vcs/github.d.ts +74 -3
- package/dist/providers/vcs/github.js +195 -15
- package/dist/providers/vcs/types.d.ts +42 -4
- package/dist/quickstart/index.js +34 -32
- package/dist/quickstart/migrate.d.ts +23 -0
- package/dist/quickstart/migrate.js +125 -0
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +1 -1
- package/dist/wardby-bin.js +6 -0
- package/docs/README.md +30 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +481 -0
- package/docs/coding-agent-setup.md +172 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1061 -0
- package/docs/getting-started-gke.md +615 -0
- package/docs/getting-started-identity-provider.md +308 -0
- package/docs/getting-started.md +133 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +657 -0
- package/help/code-review-agents.md +31 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/creating-agents.md +68 -0
- package/help/deploy-gke.md +39 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +31 -0
- package/help/github.md +30 -0
- package/help/identity-and-access.md +39 -0
- package/help/mcp.md +30 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +30 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +26 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +30 -11
- package/prisma/migrate.config.mjs +20 -0
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- package/prisma/schema.prisma +417 -23
package/dist/coding/protocol.js
CHANGED
|
@@ -6,6 +6,26 @@ export const MAX_CODING_SUMMARY_BYTES = 8 * 1024;
|
|
|
6
6
|
export const MAX_CODING_TESTS = 64;
|
|
7
7
|
export const MAX_CODING_TEST_COMMAND_BYTES = 2 * 1024;
|
|
8
8
|
export const MAX_TAG_BYTES = 32;
|
|
9
|
+
/**
|
|
10
|
+
* Coding-run services (docs/coding-services.md): the catalog name and version
|
|
11
|
+
* grammar, shared by the repository's .wardby/services.yaml, the catalog, and
|
|
12
|
+
* the worker input.
|
|
13
|
+
*/
|
|
14
|
+
export const CODING_SERVICE_NAME = /^[a-z][a-z0-9-]{0,39}$/;
|
|
15
|
+
export const CODING_SERVICE_VERSION = /^[0-9A-Za-z][0-9A-Za-z._-]{0,19}$/;
|
|
16
|
+
export const MAX_CODING_SERVICES = 5;
|
|
17
|
+
/** A variable a service hands the agent's shells (DATABASE_URL, PGHOST, REDIS_URL, ...). */
|
|
18
|
+
export const CODING_SERVICE_ENV_NAME = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
19
|
+
export const MAX_CODING_SERVICE_ENV = 16;
|
|
20
|
+
export const MAX_CODING_SERVICE_ENV_VALUE_BYTES = 1024;
|
|
21
|
+
/**
|
|
22
|
+
* A worker's output-schema failure, reduced to where and how it failed, never
|
|
23
|
+
* what the model wrote: `<path>:<zod issue code>`, where the path is dot-joined
|
|
24
|
+
* schema keys and array indices (`$` for the root). Unrecognized keys the model
|
|
25
|
+
* invented are carried only as the `unrecognized_keys` code, never by name.
|
|
26
|
+
*/
|
|
27
|
+
export const MAX_CODING_OUTPUT_ISSUES = 8;
|
|
28
|
+
export const SAFE_CODING_OUTPUT_ISSUE = /^(?:\$|[a-z][A-Za-z]{0,31}(?:\.(?:[a-z][A-Za-z]{0,31}|\d{1,3})){0,7}):[a-z_]{1,32}$/;
|
|
9
29
|
const MAX_REPOSITORY_INPUT_BYTES = 512;
|
|
10
30
|
const MAX_REF_BYTES = 255;
|
|
11
31
|
const MAX_MODEL_BYTES = 128;
|
|
@@ -13,8 +33,8 @@ const MAX_RUN_ID_BYTES = 128;
|
|
|
13
33
|
const MAX_COST_USD = 1_000_000;
|
|
14
34
|
const MAX_JSON_NESTING_DEPTH = 64;
|
|
15
35
|
const INVALID_MULTILINE_CONTROL = /[\u0000\u000B\u000C\u000E-\u001F\u007F]/;
|
|
16
|
-
const INVALID_SINGLE_LINE_CONTROL = /[\u0000-\u001F\u007F]/;
|
|
17
|
-
function byteLength(value) {
|
|
36
|
+
export const INVALID_SINGLE_LINE_CONTROL = /[\u0000-\u001F\u007F]/;
|
|
37
|
+
export function byteLength(value) {
|
|
18
38
|
return Buffer.byteLength(value, "utf8");
|
|
19
39
|
}
|
|
20
40
|
function boundedText(maxBytes, singleLine = false) {
|
|
@@ -25,6 +45,72 @@ function boundedText(maxBytes, singleLine = false) {
|
|
|
25
45
|
.refine((value) => !invalidControl.test(value), "must not contain control characters")
|
|
26
46
|
.refine((value) => value.trim().length > 0, "must not be blank");
|
|
27
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Names the worker owns: its shell basics, anything that would redirect a
|
|
50
|
+
* process's loader or network proxy, and the registry proxy's and Wardby's own
|
|
51
|
+
* settings. No service may set one, whatever its catalog entry says.
|
|
52
|
+
*/
|
|
53
|
+
const RESERVED_SERVICE_ENV_NAMES = new Set([
|
|
54
|
+
"HOME",
|
|
55
|
+
"LANG",
|
|
56
|
+
"LC_ALL",
|
|
57
|
+
"PATH",
|
|
58
|
+
"SHELL",
|
|
59
|
+
"TMPDIR",
|
|
60
|
+
"USER",
|
|
61
|
+
"LD_PRELOAD",
|
|
62
|
+
"LD_LIBRARY_PATH",
|
|
63
|
+
"NODE_OPTIONS",
|
|
64
|
+
"NODE_PATH",
|
|
65
|
+
"PYTHONPATH",
|
|
66
|
+
"PYTHONHOME",
|
|
67
|
+
"HTTP_PROXY",
|
|
68
|
+
"HTTPS_PROXY",
|
|
69
|
+
"NO_PROXY",
|
|
70
|
+
"ALL_PROXY",
|
|
71
|
+
"SSL_CERT_FILE",
|
|
72
|
+
"SSL_CERT_DIR",
|
|
73
|
+
]);
|
|
74
|
+
const RESERVED_SERVICE_ENV_PREFIXES = [
|
|
75
|
+
"WARDBY_",
|
|
76
|
+
"CODEX_",
|
|
77
|
+
"OPENAI_",
|
|
78
|
+
"ANTHROPIC_",
|
|
79
|
+
"PIP_",
|
|
80
|
+
"NPM_CONFIG_",
|
|
81
|
+
"UV_",
|
|
82
|
+
"GIT_",
|
|
83
|
+
];
|
|
84
|
+
export function isReservedServiceEnvName(name) {
|
|
85
|
+
return (RESERVED_SERVICE_ENV_NAMES.has(name) || RESERVED_SERVICE_ENV_PREFIXES.some((prefix) => name.startsWith(prefix)));
|
|
86
|
+
}
|
|
87
|
+
const serviceEnvValueSchema = z
|
|
88
|
+
.string()
|
|
89
|
+
.refine((value) => byteLength(value) <= MAX_CODING_SERVICE_ENV_VALUE_BYTES, `must be at most ${MAX_CODING_SERVICE_ENV_VALUE_BYTES} UTF-8 bytes`)
|
|
90
|
+
.refine((value) => !INVALID_SINGLE_LINE_CONTROL.test(value), "must not contain control characters");
|
|
91
|
+
/** The variables one service hands the agent's shells: upper-case names wardby does not reserve. */
|
|
92
|
+
export const CodingServiceTestEnvSchema = z.record(z.string(), serviceEnvValueSchema).superRefine((env, ctx) => {
|
|
93
|
+
const names = Object.keys(env);
|
|
94
|
+
if (names.length > MAX_CODING_SERVICE_ENV) {
|
|
95
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `must have at most ${MAX_CODING_SERVICE_ENV} variables` });
|
|
96
|
+
}
|
|
97
|
+
for (const name of names) {
|
|
98
|
+
if (!CODING_SERVICE_ENV_NAME.test(name) || isReservedServiceEnvName(name)) {
|
|
99
|
+
ctx.addIssue({
|
|
100
|
+
code: z.ZodIssueCode.custom,
|
|
101
|
+
path: [name],
|
|
102
|
+
message: "must be an upper-case variable name wardby does not reserve",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const workerServiceSchema = z
|
|
108
|
+
.object({
|
|
109
|
+
name: z.string().regex(CODING_SERVICE_NAME),
|
|
110
|
+
version: z.string().regex(CODING_SERVICE_VERSION),
|
|
111
|
+
testEnv: CodingServiceTestEnvSchema,
|
|
112
|
+
})
|
|
113
|
+
.strict();
|
|
28
114
|
function repositoryParts(value) {
|
|
29
115
|
if (byteLength(value) > MAX_REPOSITORY_INPUT_BYTES || INVALID_SINGLE_LINE_CONTROL.test(value)) {
|
|
30
116
|
throw new Error("invalid GitHub repository");
|
|
@@ -108,6 +194,25 @@ const repositorySchema = z
|
|
|
108
194
|
.transform(normalizeGitHubRepository);
|
|
109
195
|
export const CodingBaseRefSchema = z.string().refine(isGitRef, "must be a safe branch ref").transform(normalizeGitRef);
|
|
110
196
|
export const CodingTaskOverrideSchema = boundedText(MAX_CODING_TASK_BYTES);
|
|
197
|
+
/**
|
|
198
|
+
* A coding worker receives only its task text, so a coding agent's own
|
|
199
|
+
* instructions (its systemPrompt) travel inside that text, ahead of the
|
|
200
|
+
* request, followed by any note wardby generates for the run (the services it
|
|
201
|
+
* started: src/coding/services/note.ts). Blank instructions and no note leave
|
|
202
|
+
* the task unchanged. The combination must still fit MAX_CODING_TASK_BYTES;
|
|
203
|
+
* exceeding it is an error naming both parts rather than a silent truncation.
|
|
204
|
+
*/
|
|
205
|
+
export function composeCodingTask(instructions, task, note) {
|
|
206
|
+
const standing = [instructions?.trim(), note?.trim()].filter((part) => Boolean(part)).join("\n\n");
|
|
207
|
+
if (!standing)
|
|
208
|
+
return task;
|
|
209
|
+
const composed = `Standing instructions for this coding agent:\n${standing}\n\nRequest:\n${task}`;
|
|
210
|
+
if (byteLength(composed) > MAX_CODING_TASK_BYTES) {
|
|
211
|
+
throw new Error(`The coding agent's instructions (${byteLength(standing)} bytes) plus this task (${byteLength(task)} bytes) ` +
|
|
212
|
+
`exceed the ${MAX_CODING_TASK_BYTES}-byte coding task limit; shorten one of them.`);
|
|
213
|
+
}
|
|
214
|
+
return composed;
|
|
215
|
+
}
|
|
111
216
|
const runIdSchema = boundedText(MAX_RUN_ID_BYTES, true).refine((value) => /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value), "must be an opaque identifier");
|
|
112
217
|
const modelSchema = boundedText(MAX_MODEL_BYTES, true).refine((value) => /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value), "must be a model identifier");
|
|
113
218
|
const usageSchema = z
|
|
@@ -117,6 +222,30 @@ const usageSchema = z
|
|
|
117
222
|
costUsd: z.number().finite().nonnegative().max(MAX_COST_USD),
|
|
118
223
|
})
|
|
119
224
|
.strict();
|
|
225
|
+
const SAFE_TAG = new RegExp(`^[A-Za-z0-9][A-Za-z0-9._/-]{0,${MAX_TAG_BYTES - 1}}$`);
|
|
226
|
+
/**
|
|
227
|
+
* The tag only labels a pull request title, so a model's malformed tag must not
|
|
228
|
+
* sink an otherwise finished run. A tag that already passes is kept as-is; any
|
|
229
|
+
* other string becomes a lowercase slug (runs of disallowed characters to "-",
|
|
230
|
+
* no leading or trailing punctuation, at most MAX_TAG_BYTES), and one with
|
|
231
|
+
* nothing usable left, or a non-string, becomes null. GitHub finalization still
|
|
232
|
+
* re-validates whatever survives.
|
|
233
|
+
*/
|
|
234
|
+
export function normalizeCodingTag(value) {
|
|
235
|
+
if (value === undefined || value === null)
|
|
236
|
+
return value;
|
|
237
|
+
if (typeof value !== "string")
|
|
238
|
+
return null;
|
|
239
|
+
if (SAFE_TAG.test(value))
|
|
240
|
+
return value;
|
|
241
|
+
const slug = value
|
|
242
|
+
.toLowerCase()
|
|
243
|
+
.replace(/[^a-z0-9._/-]+/g, "-")
|
|
244
|
+
.replace(/^[^a-z0-9]+/, "")
|
|
245
|
+
.slice(0, MAX_TAG_BYTES)
|
|
246
|
+
.replace(/[^a-z0-9]+$/, "");
|
|
247
|
+
return SAFE_TAG.test(slug) ? slug : null;
|
|
248
|
+
}
|
|
120
249
|
/**
|
|
121
250
|
* A short, caller-visible reference (e.g. a ticket ID) surfaced in the PR title. Never free text.
|
|
122
251
|
* Accepts null as well as undefined: OpenAI's strict Structured Outputs mode requires every
|
|
@@ -126,7 +255,7 @@ const usageSchema = z
|
|
|
126
255
|
*/
|
|
127
256
|
const tagSchema = z
|
|
128
257
|
.string()
|
|
129
|
-
.regex(
|
|
258
|
+
.regex(SAFE_TAG, "must be a short, safe tag")
|
|
130
259
|
.nullable()
|
|
131
260
|
.optional()
|
|
132
261
|
.transform((value) => value ?? undefined);
|
|
@@ -136,6 +265,8 @@ const testResultSchema = z
|
|
|
136
265
|
outcome: z.enum(["passed", "failed", "skipped"]),
|
|
137
266
|
})
|
|
138
267
|
.strict();
|
|
268
|
+
/** Longest line a debug-traced worker writes to its log (src/coding-worker/debug-trace.ts). */
|
|
269
|
+
export const MAX_DEBUG_TRACE_LINE_BYTES = 16 * 1024;
|
|
139
270
|
export const CodingTaskInputSchema = z
|
|
140
271
|
.object({
|
|
141
272
|
schemaVersion: z.literal(CODING_PROTOCOL_VERSION),
|
|
@@ -156,6 +287,21 @@ export const CodingTaskInputSchema = z
|
|
|
156
287
|
* only enforces that headRef is internally consistent with it.
|
|
157
288
|
*/
|
|
158
289
|
continuationOf: z.object({ runId: runIdSchema }).strict().optional(),
|
|
290
|
+
/**
|
|
291
|
+
* Admin-requested debug trace (CodingAgentProfile.debugTraceUntil): the
|
|
292
|
+
* worker writes its full stream trace to its own log. Absent means off;
|
|
293
|
+
* the control plane writes the key only when true, so an input for an
|
|
294
|
+
* untraced run is unchanged for workers that predate it.
|
|
295
|
+
*/
|
|
296
|
+
debugTrace: z.boolean().optional(),
|
|
297
|
+
/**
|
|
298
|
+
* Coding-run services started next to this run (docs/coding-services.md):
|
|
299
|
+
* each one's catalog name and version, and the variables the agent's shells
|
|
300
|
+
* receive. Absent means none; the control plane writes the key only when a
|
|
301
|
+
* run has services, so every other input is unchanged for workers that
|
|
302
|
+
* predate it.
|
|
303
|
+
*/
|
|
304
|
+
services: z.array(workerServiceSchema).min(1).max(MAX_CODING_SERVICES).optional(),
|
|
159
305
|
})
|
|
160
306
|
.strict()
|
|
161
307
|
.superRefine((value, ctx) => {
|
|
@@ -167,6 +313,10 @@ export const CodingTaskInputSchema = z
|
|
|
167
313
|
message: "must match wardby/run-<runId> (or wardby/run-<continuationOf.runId>)",
|
|
168
314
|
});
|
|
169
315
|
}
|
|
316
|
+
const names = (value.services ?? []).map((service) => service.name);
|
|
317
|
+
if (new Set(names).size !== names.length) {
|
|
318
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["services"], message: "must name each service once" });
|
|
319
|
+
}
|
|
170
320
|
});
|
|
171
321
|
export const CodingAgentOutputSchema = z
|
|
172
322
|
.object({
|
|
@@ -175,7 +325,8 @@ export const CodingAgentOutputSchema = z
|
|
|
175
325
|
outcome: z.enum(["changes_ready", "no_changes", "budget_exhausted"]),
|
|
176
326
|
summary: boundedText(MAX_CODING_SUMMARY_BYTES),
|
|
177
327
|
tests: z.array(testResultSchema).max(MAX_CODING_TESTS),
|
|
178
|
-
|
|
328
|
+
// Model-written, so normalized rather than rejected; see normalizeCodingTag.
|
|
329
|
+
tag: z.preprocess(normalizeCodingTag, tagSchema),
|
|
179
330
|
})
|
|
180
331
|
.strict()
|
|
181
332
|
.transform((value) => ({
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry adapter map: every ecosystem the coding package registry
|
|
3
|
+
* proxies, keyed by its EcosystemId. Adding a new ecosystem means adding it
|
|
4
|
+
* here (and to the allowlist/profile validation that reads this map) --
|
|
5
|
+
* nothing else in the registry core hardcodes "npm" or "pypi".
|
|
6
|
+
*/
|
|
7
|
+
import { npmAdapter } from "./npm.js";
|
|
8
|
+
import { pypiAdapter } from "./pypi.js";
|
|
9
|
+
export const REGISTRY_ADAPTERS = new Map([npmAdapter, pypiAdapter].map((adapter) => [adapter.id, adapter]));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AllowlistEntry, RegistryAdapter } from "./types.js";
|
|
2
|
+
export type PackageAllowlist = Readonly<Record<string, readonly string[]>>;
|
|
3
|
+
export interface PackagePolicy {
|
|
4
|
+
minReleaseAgeDays: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const DEFAULT_MIN_RELEASE_AGE_DAYS = 3;
|
|
7
|
+
export declare function resolvePolicy(value: unknown): PackagePolicy;
|
|
8
|
+
export declare function parseAllowlist(allowlist: PackageAllowlist, adapters: ReadonlyMap<string, RegistryAdapter>): Map<string, AllowlistEntry[]>;
|
|
9
|
+
export declare function matchRoot(entries: readonly AllowlistEntry[], normalizedName: string): AllowlistEntry | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-agent package allowlist and policy model: parsing raw allowlist
|
|
3
|
+
* strings (via each ecosystem's adapter) into `AllowlistEntry` values, and
|
|
4
|
+
* matching a normalized package name against them.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export const DEFAULT_MIN_RELEASE_AGE_DAYS = 3;
|
|
8
|
+
const PolicySchema = z
|
|
9
|
+
.object({ minReleaseAgeDays: z.number().int().min(0).max(30).default(DEFAULT_MIN_RELEASE_AGE_DAYS) })
|
|
10
|
+
.strict();
|
|
11
|
+
export function resolvePolicy(value) {
|
|
12
|
+
return PolicySchema.parse(value ?? {});
|
|
13
|
+
}
|
|
14
|
+
export function parseAllowlist(allowlist, adapters) {
|
|
15
|
+
const parsed = new Map();
|
|
16
|
+
for (const [ecosystem, entries] of Object.entries(allowlist)) {
|
|
17
|
+
const adapter = adapters.get(ecosystem);
|
|
18
|
+
if (!adapter)
|
|
19
|
+
throw new Error(`unknown package ecosystem "${ecosystem}"`);
|
|
20
|
+
parsed.set(ecosystem, entries.map((entry) => adapter.parseAllowlistEntry(entry)));
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
export function matchRoot(entries, normalizedName) {
|
|
25
|
+
return entries.find((entry) => entry.wildcard
|
|
26
|
+
? normalizedName.startsWith(entry.name) && normalizedName.length > entry.name.length
|
|
27
|
+
: entry.name === normalizedName);
|
|
28
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A streaming scan for one top-level member of a JSON object document,
|
|
3
|
+
* without ever holding the document: only the wanted member's value text is
|
|
4
|
+
* kept (bounded), and every other value, however large, is skipped as it
|
|
5
|
+
* streams past. Used to read an npm packument's `time` (per-version publish
|
|
6
|
+
* times), which comes after `versions` and can follow tens of MB of it.
|
|
7
|
+
*
|
|
8
|
+
* It is a tokenizer, not a validator: it tracks strings, escapes and
|
|
9
|
+
* nesting depth, which is all that locating a top-level member needs. The
|
|
10
|
+
* captured value is then parsed with JSON.parse, so a malformed value
|
|
11
|
+
* throws there. A document that is not an object, or that ends before the
|
|
12
|
+
* member closes, yields null.
|
|
13
|
+
*/
|
|
14
|
+
export declare class JsonMemberTooLarge extends Error {
|
|
15
|
+
}
|
|
16
|
+
export declare class TopLevelMemberScanner {
|
|
17
|
+
private readonly wanted;
|
|
18
|
+
private readonly maxValueLength;
|
|
19
|
+
private depth;
|
|
20
|
+
private inString;
|
|
21
|
+
private escape;
|
|
22
|
+
/** At depth 1, the next string is a key. */
|
|
23
|
+
private expectKey;
|
|
24
|
+
private collectingKey;
|
|
25
|
+
private key;
|
|
26
|
+
private lastKey;
|
|
27
|
+
/** After `"<wanted>":`, waiting for the value's first character. */
|
|
28
|
+
private pending;
|
|
29
|
+
private capturing;
|
|
30
|
+
private captured;
|
|
31
|
+
private capturedLength;
|
|
32
|
+
private finished;
|
|
33
|
+
private result;
|
|
34
|
+
private started;
|
|
35
|
+
constructor(wanted: string, maxValueLength: number);
|
|
36
|
+
/** True once the member was captured (or proven absent or not an
|
|
37
|
+
* object), so the caller may stop reading. */
|
|
38
|
+
get done(): boolean;
|
|
39
|
+
/** The member's value, parsed; undefined while scanning or when absent. */
|
|
40
|
+
get value(): unknown;
|
|
41
|
+
/** Feeds the next decoded text. Throws JsonMemberTooLarge when the
|
|
42
|
+
* member's value exceeds its bound. */
|
|
43
|
+
push(text: string): void;
|
|
44
|
+
private appendKey;
|
|
45
|
+
private capture;
|
|
46
|
+
private finish;
|
|
47
|
+
}
|
|
48
|
+
/** Reads `stream` (UTF-8 JSON) until the top-level member `wanted` has
|
|
49
|
+
* been captured, then cancels the rest. Returns its parsed value, or
|
|
50
|
+
* undefined when the document has no such object-valued member. At most
|
|
51
|
+
* `maxBytes` of the stream are read; more throws JsonMemberTooLarge. */
|
|
52
|
+
export declare function scanTopLevelMember(stream: ReadableStream<Uint8Array>, wanted: string, limits: {
|
|
53
|
+
maxBytes: number;
|
|
54
|
+
maxValueLength: number;
|
|
55
|
+
}): Promise<unknown>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A streaming scan for one top-level member of a JSON object document,
|
|
3
|
+
* without ever holding the document: only the wanted member's value text is
|
|
4
|
+
* kept (bounded), and every other value, however large, is skipped as it
|
|
5
|
+
* streams past. Used to read an npm packument's `time` (per-version publish
|
|
6
|
+
* times), which comes after `versions` and can follow tens of MB of it.
|
|
7
|
+
*
|
|
8
|
+
* It is a tokenizer, not a validator: it tracks strings, escapes and
|
|
9
|
+
* nesting depth, which is all that locating a top-level member needs. The
|
|
10
|
+
* captured value is then parsed with JSON.parse, so a malformed value
|
|
11
|
+
* throws there. A document that is not an object, or that ends before the
|
|
12
|
+
* member closes, yields null.
|
|
13
|
+
*/
|
|
14
|
+
/** Longest top-level key compared; a longer one cannot be the wanted key. */
|
|
15
|
+
const MAX_KEY_LENGTH = 256;
|
|
16
|
+
export class JsonMemberTooLarge extends Error {
|
|
17
|
+
}
|
|
18
|
+
export class TopLevelMemberScanner {
|
|
19
|
+
wanted;
|
|
20
|
+
maxValueLength;
|
|
21
|
+
depth = 0;
|
|
22
|
+
inString = false;
|
|
23
|
+
escape = false;
|
|
24
|
+
/** At depth 1, the next string is a key. */
|
|
25
|
+
expectKey = false;
|
|
26
|
+
collectingKey = false;
|
|
27
|
+
key = "";
|
|
28
|
+
lastKey;
|
|
29
|
+
/** After `"<wanted>":`, waiting for the value's first character. */
|
|
30
|
+
pending = false;
|
|
31
|
+
capturing = false;
|
|
32
|
+
captured = [];
|
|
33
|
+
capturedLength = 0;
|
|
34
|
+
finished = false;
|
|
35
|
+
result = undefined;
|
|
36
|
+
started = false;
|
|
37
|
+
constructor(wanted, maxValueLength) {
|
|
38
|
+
this.wanted = wanted;
|
|
39
|
+
this.maxValueLength = maxValueLength;
|
|
40
|
+
}
|
|
41
|
+
/** True once the member was captured (or proven absent or not an
|
|
42
|
+
* object), so the caller may stop reading. */
|
|
43
|
+
get done() {
|
|
44
|
+
return this.finished;
|
|
45
|
+
}
|
|
46
|
+
/** The member's value, parsed; undefined while scanning or when absent. */
|
|
47
|
+
get value() {
|
|
48
|
+
return this.result;
|
|
49
|
+
}
|
|
50
|
+
/** Feeds the next decoded text. Throws JsonMemberTooLarge when the
|
|
51
|
+
* member's value exceeds its bound. */
|
|
52
|
+
push(text) {
|
|
53
|
+
if (this.finished)
|
|
54
|
+
return;
|
|
55
|
+
let segmentStart = this.capturing ? 0 : -1;
|
|
56
|
+
const length = text.length;
|
|
57
|
+
let i = 0;
|
|
58
|
+
while (i < length) {
|
|
59
|
+
if (this.inString) {
|
|
60
|
+
if (this.escape) {
|
|
61
|
+
this.escape = false;
|
|
62
|
+
if (this.collectingKey)
|
|
63
|
+
this.appendKey(text[i]);
|
|
64
|
+
i += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Skip ahead to the next quote or backslash: the bulk of a
|
|
68
|
+
// packument is string content, never looked at char by char.
|
|
69
|
+
const quote = text.indexOf('"', i);
|
|
70
|
+
const backslash = text.indexOf("\\", i);
|
|
71
|
+
const stop = quote < 0 ? backslash : backslash < 0 ? quote : Math.min(quote, backslash);
|
|
72
|
+
if (stop < 0) {
|
|
73
|
+
if (this.collectingKey)
|
|
74
|
+
this.appendKey(text.slice(i));
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (this.collectingKey)
|
|
78
|
+
this.appendKey(text.slice(i, stop));
|
|
79
|
+
if (stop === backslash) {
|
|
80
|
+
this.escape = true;
|
|
81
|
+
if (this.collectingKey)
|
|
82
|
+
this.appendKey("\\");
|
|
83
|
+
i = stop + 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
this.inString = false;
|
|
87
|
+
if (this.collectingKey) {
|
|
88
|
+
this.collectingKey = false;
|
|
89
|
+
this.lastKey = this.key;
|
|
90
|
+
}
|
|
91
|
+
i = stop + 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const c = text[i];
|
|
95
|
+
if (c === " " || c === "\n" || c === "\r" || c === "\t") {
|
|
96
|
+
i += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (!this.started) {
|
|
100
|
+
this.started = true;
|
|
101
|
+
if (c !== "{")
|
|
102
|
+
return this.finish(undefined);
|
|
103
|
+
}
|
|
104
|
+
if (this.pending) {
|
|
105
|
+
this.pending = false;
|
|
106
|
+
if (c !== "{")
|
|
107
|
+
return this.finish(undefined);
|
|
108
|
+
this.capturing = true;
|
|
109
|
+
segmentStart = i;
|
|
110
|
+
}
|
|
111
|
+
if (c === '"') {
|
|
112
|
+
this.inString = true;
|
|
113
|
+
if (this.depth === 1 && this.expectKey) {
|
|
114
|
+
this.collectingKey = true;
|
|
115
|
+
this.key = "";
|
|
116
|
+
this.expectKey = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (c === "{" || c === "[") {
|
|
120
|
+
this.depth += 1;
|
|
121
|
+
if (this.depth === 1)
|
|
122
|
+
this.expectKey = true;
|
|
123
|
+
}
|
|
124
|
+
else if (c === "}" || c === "]") {
|
|
125
|
+
this.depth -= 1;
|
|
126
|
+
if (this.capturing && this.depth === 1) {
|
|
127
|
+
this.capture(text.slice(segmentStart, i + 1));
|
|
128
|
+
return this.finish(JSON.parse(this.captured.join("")));
|
|
129
|
+
}
|
|
130
|
+
if (this.depth === 0)
|
|
131
|
+
return this.finish(undefined);
|
|
132
|
+
}
|
|
133
|
+
else if (c === ":") {
|
|
134
|
+
if (this.depth === 1 && this.lastKey === this.wanted)
|
|
135
|
+
this.pending = true;
|
|
136
|
+
}
|
|
137
|
+
else if (c === ",") {
|
|
138
|
+
if (this.depth === 1) {
|
|
139
|
+
this.expectKey = true;
|
|
140
|
+
this.lastKey = undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
i += 1;
|
|
144
|
+
}
|
|
145
|
+
if (this.capturing && segmentStart >= 0)
|
|
146
|
+
this.capture(text.slice(segmentStart));
|
|
147
|
+
}
|
|
148
|
+
appendKey(part) {
|
|
149
|
+
if (this.key.length <= MAX_KEY_LENGTH)
|
|
150
|
+
this.key += part.slice(0, MAX_KEY_LENGTH + 1 - this.key.length);
|
|
151
|
+
}
|
|
152
|
+
capture(part) {
|
|
153
|
+
this.capturedLength += part.length;
|
|
154
|
+
if (this.capturedLength > this.maxValueLength)
|
|
155
|
+
throw new JsonMemberTooLarge(`"${this.wanted}" is too large`);
|
|
156
|
+
this.captured.push(part);
|
|
157
|
+
}
|
|
158
|
+
finish(value) {
|
|
159
|
+
this.finished = true;
|
|
160
|
+
this.result = value;
|
|
161
|
+
this.captured = [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Reads `stream` (UTF-8 JSON) until the top-level member `wanted` has
|
|
165
|
+
* been captured, then cancels the rest. Returns its parsed value, or
|
|
166
|
+
* undefined when the document has no such object-valued member. At most
|
|
167
|
+
* `maxBytes` of the stream are read; more throws JsonMemberTooLarge. */
|
|
168
|
+
export async function scanTopLevelMember(stream, wanted, limits) {
|
|
169
|
+
const scanner = new TopLevelMemberScanner(wanted, limits.maxValueLength);
|
|
170
|
+
const decoder = new TextDecoder("utf-8");
|
|
171
|
+
const reader = stream.getReader();
|
|
172
|
+
let bytes = 0;
|
|
173
|
+
try {
|
|
174
|
+
for (;;) {
|
|
175
|
+
const { done, value } = await reader.read();
|
|
176
|
+
if (done) {
|
|
177
|
+
scanner.push(decoder.decode());
|
|
178
|
+
return scanner.value;
|
|
179
|
+
}
|
|
180
|
+
bytes += value.byteLength;
|
|
181
|
+
if (bytes > limits.maxBytes)
|
|
182
|
+
throw new JsonMemberTooLarge(`the document is larger than ${limits.maxBytes} bytes`);
|
|
183
|
+
scanner.push(decoder.decode(value, { stream: true }));
|
|
184
|
+
if (scanner.done)
|
|
185
|
+
return scanner.value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
await reader.cancel().catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrites the proxy download URLs a package manager records in lockfiles
|
|
3
|
+
* back to public ones, for every adapter with a `lockfiles` hook. The driver
|
|
4
|
+
* runs this on the workspace before it is collected, so a committed lockfile
|
|
5
|
+
* never points at the sandbox-only proxy host.
|
|
6
|
+
*
|
|
7
|
+
* Best effort by design: a file that cannot be read or written safely is
|
|
8
|
+
* skipped, never a reason to fail the run. The walk never follows symlinks
|
|
9
|
+
* and skips the folders collection excludes by name (dependency and cache
|
|
10
|
+
* folders), which are never sent back anyway.
|
|
11
|
+
*/
|
|
12
|
+
import { constants } from "node:fs";
|
|
13
|
+
import { open, readdir } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { BUILTIN_COLLECT_EXCLUDE_NAMES } from "../collect-exclude.js";
|
|
16
|
+
import { REGISTRY_ADAPTERS } from "./adapters.js";
|
|
17
|
+
import { registryUrlFor } from "./worker-config.js";
|
|
18
|
+
const MAX_LOCKFILE_BYTES = 64 * 1024 * 1024;
|
|
19
|
+
const MAX_DEPTH = 32;
|
|
20
|
+
const SKIPPED_DIRECTORIES = new Set([...BUILTIN_COLLECT_EXCLUDE_NAMES, ".git"]);
|
|
21
|
+
/** Rewrites one file in place through a single descriptor (no symlinks, no
|
|
22
|
+
* path re-resolution between the checks and the write). Returns whether it
|
|
23
|
+
* changed; any failure leaves the file as it was found or skips it. */
|
|
24
|
+
async function normalizeFile(path, normalize) {
|
|
25
|
+
let file;
|
|
26
|
+
try {
|
|
27
|
+
file = await open(path, constants.O_RDWR | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const metadata = await file.stat();
|
|
34
|
+
if (!metadata.isFile() || metadata.size > MAX_LOCKFILE_BYTES)
|
|
35
|
+
return false;
|
|
36
|
+
const before = (await file.readFile()).toString("utf8");
|
|
37
|
+
const after = normalize(before);
|
|
38
|
+
if (after === before)
|
|
39
|
+
return false;
|
|
40
|
+
await file.truncate(0);
|
|
41
|
+
await file.write(after, 0, "utf8");
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await file.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Normalises every recognised lockfile under `workspace`; returns the
|
|
52
|
+
* workspace-relative paths it rewrote, sorted. */
|
|
53
|
+
export async function normalizeRegistryLockfiles(input) {
|
|
54
|
+
const byName = new Map();
|
|
55
|
+
for (const adapter of REGISTRY_ADAPTERS.values()) {
|
|
56
|
+
const lockfiles = adapter.lockfiles;
|
|
57
|
+
if (!lockfiles)
|
|
58
|
+
continue;
|
|
59
|
+
const registryUrl = registryUrlFor(input.proxyBaseUrl, adapter.id);
|
|
60
|
+
for (const name of lockfiles.names)
|
|
61
|
+
byName.set(name, (content) => lockfiles.normalize(content, registryUrl));
|
|
62
|
+
}
|
|
63
|
+
const rewritten = [];
|
|
64
|
+
const walk = async (relative, depth) => {
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = await readdir(join(input.workspace, relative), { withFileTypes: true });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const path = relative ? `${relative}/${entry.name}` : entry.name;
|
|
74
|
+
// Dirent types come from lstat semantics: a symlink is neither a directory nor a file here.
|
|
75
|
+
if (entry.isDirectory()) {
|
|
76
|
+
if (depth < MAX_DEPTH && !SKIPPED_DIRECTORIES.has(entry.name))
|
|
77
|
+
await walk(path, depth + 1);
|
|
78
|
+
}
|
|
79
|
+
else if (entry.isFile()) {
|
|
80
|
+
const normalize = byName.get(entry.name);
|
|
81
|
+
if (normalize && (await normalizeFile(join(input.workspace, path), normalize)))
|
|
82
|
+
rewritten.push(path);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
if (byName.size > 0)
|
|
87
|
+
await walk("", 0);
|
|
88
|
+
return rewritten.sort();
|
|
89
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Same shape as the adapter's allowlist name check (scoped names are lower case). */
|
|
2
|
+
export declare const PACKAGE_NAME: RegExp;
|
|
3
|
+
/** `https://registry.npmjs.org/@scope/pkg/-/pkg-1.0.0.tgz` */
|
|
4
|
+
export declare function publicNpmTarballUrl(name: string, version: string): string;
|
|
5
|
+
export declare function normalizeNpmLockfile(content: string, registryUrl: string): string;
|
|
6
|
+
export declare const npmLockfiles: {
|
|
7
|
+
readonly names: readonly ["package-lock.json", "npm-shrinkwrap.json"];
|
|
8
|
+
readonly normalize: typeof normalizeNpmLockfile;
|
|
9
|
+
};
|