@wardby/cli 0.2.1 → 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 +41 -4
- 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 +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 +75 -4
- package/dist/coding/protocol.js +107 -7
- 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 +41 -0
- package/dist/config/providers.js +66 -0
- package/dist/core/budget-groups.d.ts +108 -5
- 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/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 +33 -6
- package/dist/core/dispatch.js +260 -43
- 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/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +38 -2
- 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 +30 -8
- package/dist/core/runner.js +237 -40
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- 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 +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 +8 -1
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +100 -0
- package/dist/generated/prisma/client.d.ts +100 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +30 -0
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +6 -1
- package/dist/generated/prisma/internal/class.d.ts +143 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1746 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +179 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +186 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +179 -6
- package/dist/generated/prisma/models/Agent.d.ts +294 -1
- 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 +123 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1276 -95
- 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/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 +389 -1
- 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/Tool.d.ts +15 -3
- package/dist/generated/prisma/models.d.ts +13 -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 +5 -0
- package/dist/import/create.js +67 -13
- 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 +26 -52
- 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 +31 -2
- package/dist/mcp/auth/resource-server.js +84 -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 +20 -2
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -1
- package/dist/mcp/context.d.ts +27 -1
- 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.js +27 -9
- package/dist/mcp/tools/agents.js +446 -48
- package/dist/mcp/tools/budget-groups.js +3 -3
- 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/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 +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 +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +10 -0
- package/dist/mcp/transport/streamable-http.js +32 -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 +2 -1
- 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 +17 -0
- 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 +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 +15 -0
- package/dist/providers/engine/types.d.ts +10 -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 +19 -0
- package/dist/providers/executor/container.d.ts +105 -3
- package/dist/providers/executor/container.js +296 -20
- package/dist/providers/executor/dbos.d.ts +2 -3
- 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 +49 -2
- package/dist/providers/jobs/docker.js +279 -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 +21 -2
- package/dist/providers/jobs/kubernetes.js +112 -21
- package/dist/providers/jobs/types.d.ts +18 -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/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 +77 -13
- 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 +13 -2
- 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 +12 -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/schema.prisma +407 -21
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lockfile verification ("approve a plan"): the registry core's check of a
|
|
3
|
+
* lockfile a run submits to `POST /registry/<ecosystem>/-/plan`. Nothing the
|
|
4
|
+
* lockfile claims is trusted:
|
|
5
|
+
*
|
|
6
|
+
* - every entry's integrity must equal the registry's own for that exact
|
|
7
|
+
* version (a per-version fact read from the registry, stored forever);
|
|
8
|
+
* - edges come from the registry too: a parent version's declared
|
|
9
|
+
* dependencies, resolved to lockfile entries by the client's own rule
|
|
10
|
+
* (npm: nearest ancestor node_modules), and kept only when the entry is
|
|
11
|
+
* the declared package at a version inside the declared range;
|
|
12
|
+
* - reachability starts at the projects' dependencies that are allowlist
|
|
13
|
+
* roots (at a version inside the root's range) and follows only verified
|
|
14
|
+
* edges, through versions old enough and without a HIGH/CRITICAL advisory.
|
|
15
|
+
*
|
|
16
|
+
* A refused entry refuses itself and whatever is reachable only through it;
|
|
17
|
+
* everything else is approved, as exact name@version pairs. The work is
|
|
18
|
+
* bounded: facts are fetched only for entries reached (so a lockfile of
|
|
19
|
+
* unrelated packages costs nothing upstream), with bounded concurrency and a
|
|
20
|
+
* deadline.
|
|
21
|
+
*/
|
|
22
|
+
import { matchRoot } from "../../../coding/registry/allowlist.js";
|
|
23
|
+
import { RegistryError, } from "../../../coding/registry/types.js";
|
|
24
|
+
import { versionKey } from "./audit.js";
|
|
25
|
+
export const DEFAULT_PLAN_MAX_ENTRIES = 5000;
|
|
26
|
+
export const DEFAULT_PLAN_TIMEOUT_MS = 120_000;
|
|
27
|
+
/** Per-version documents fetched at once (each is a few KB). */
|
|
28
|
+
export const DEFAULT_PLAN_FACT_CONCURRENCY = 16;
|
|
29
|
+
/** Full package documents streamed at once for publish times (each may be
|
|
30
|
+
* tens of MB, never held). */
|
|
31
|
+
export const DEFAULT_PLAN_TIME_CONCURRENCY = 8;
|
|
32
|
+
/** Largest (decompressed) package document streamed for publish times. */
|
|
33
|
+
export const DEFAULT_PLAN_MAX_DOCUMENT_BYTES = 256 * 1024 * 1024;
|
|
34
|
+
/** Runs `task` over `items`, at most `concurrency` at once. */
|
|
35
|
+
async function eachLimited(items, concurrency, task) {
|
|
36
|
+
let cursor = 0;
|
|
37
|
+
const worker = async () => {
|
|
38
|
+
while (cursor < items.length)
|
|
39
|
+
await task(items[cursor++]);
|
|
40
|
+
};
|
|
41
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));
|
|
42
|
+
}
|
|
43
|
+
/** A failed registry read is retried once before the entry is refused. */
|
|
44
|
+
async function withRetry(signal, read) {
|
|
45
|
+
try {
|
|
46
|
+
return await read();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (signal.aborted)
|
|
50
|
+
throw error;
|
|
51
|
+
return read();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function verifyLockfilePlan(input) {
|
|
55
|
+
const { adapter, support, lockfile, signal } = input;
|
|
56
|
+
const refusals = new Map();
|
|
57
|
+
for (const entry of lockfile.entries) {
|
|
58
|
+
if (entry.kind === "unsupported")
|
|
59
|
+
refusals.set(entry, {
|
|
60
|
+
code: "wardby_lockfile_entry_unsupported",
|
|
61
|
+
reason: entry.unsupportedReason ?? "cannot be verified against the registry",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Starting points: what the projects themselves declare, when it is an
|
|
65
|
+
// allowlist root at a version inside the root's range.
|
|
66
|
+
const starts = new Set();
|
|
67
|
+
for (const project of lockfile.projects) {
|
|
68
|
+
for (const dependency of project.dependencies) {
|
|
69
|
+
const entry = lockfile.resolve(project.path, dependency.key);
|
|
70
|
+
if (!entry || entry.kind !== "registry" || entry.name !== dependency.name)
|
|
71
|
+
continue;
|
|
72
|
+
const root = matchRoot(input.roots, adapter.normalizeName(entry.name));
|
|
73
|
+
if (!root)
|
|
74
|
+
continue;
|
|
75
|
+
if (root.range && !safeSatisfies(adapter, entry.version, root.range))
|
|
76
|
+
continue;
|
|
77
|
+
starts.add(entry);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Breadth first over verified edges, fetching facts one layer at a time
|
|
81
|
+
// (only for entries actually reached).
|
|
82
|
+
const outcomes = new Map();
|
|
83
|
+
const edges = new Map();
|
|
84
|
+
const candidates = new Set();
|
|
85
|
+
const visited = new Set(starts);
|
|
86
|
+
let layer = [...starts];
|
|
87
|
+
while (layer.length > 0) {
|
|
88
|
+
signal.throwIfAborted();
|
|
89
|
+
await loadFacts(input, layer, outcomes);
|
|
90
|
+
const next = [];
|
|
91
|
+
for (const entry of layer) {
|
|
92
|
+
const outcome = outcomes.get(versionKey(entry.name, entry.version));
|
|
93
|
+
if ("refusal" in outcome) {
|
|
94
|
+
refusals.set(entry, outcome.refusal);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const { fact } = outcome;
|
|
98
|
+
if (entry.integrity === null || entry.integrity.trim() !== fact.integrity) {
|
|
99
|
+
refusals.set(entry, {
|
|
100
|
+
code: "wardby_lockfile_integrity_mismatch",
|
|
101
|
+
reason: `the lockfile's integrity is not the registry's for ${entry.name}@${entry.version}`,
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (fact.publishedAt.getTime() > input.cutoff.getTime()) {
|
|
106
|
+
refusals.set(entry, {
|
|
107
|
+
code: "wardby_version_filtered",
|
|
108
|
+
reason: `published ${fact.publishedAt.toISOString()}, newer than the release-age limit`,
|
|
109
|
+
intrinsic: true,
|
|
110
|
+
publishedAt: fact.publishedAt,
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
candidates.add(entry);
|
|
115
|
+
const children = [];
|
|
116
|
+
for (const dependency of fact.dependencies) {
|
|
117
|
+
const child = lockfile.resolve(entry.path, dependency.key);
|
|
118
|
+
if (!child || child.kind !== "registry" || child.name !== dependency.name)
|
|
119
|
+
continue;
|
|
120
|
+
if (!support.edgeSatisfies(child.version, dependency.range))
|
|
121
|
+
continue;
|
|
122
|
+
children.push(child);
|
|
123
|
+
if (!visited.has(child)) {
|
|
124
|
+
visited.add(child);
|
|
125
|
+
next.push(child);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
edges.set(entry, children);
|
|
129
|
+
}
|
|
130
|
+
layer = next;
|
|
131
|
+
}
|
|
132
|
+
// One advisory lookup for every candidate version.
|
|
133
|
+
const withheld = await input.audit([...new Map([...candidates].map((entry) => [versionKey(entry.name, entry.version), entry])).values()].map(({ name, version }) => ({ name, version })), signal);
|
|
134
|
+
for (const entry of candidates) {
|
|
135
|
+
const ids = withheld.get(versionKey(entry.name, entry.version));
|
|
136
|
+
if (ids && ids.length > 0) {
|
|
137
|
+
candidates.delete(entry);
|
|
138
|
+
refusals.set(entry, {
|
|
139
|
+
code: "wardby_version_filtered",
|
|
140
|
+
reason: `withheld by advisory ${ids.join(", ")} (high severity or malware)`,
|
|
141
|
+
intrinsic: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Reachability again, now only through entries that passed every check:
|
|
146
|
+
// a refused entry refuses whatever is reachable only through it.
|
|
147
|
+
const approvedEntries = new Set();
|
|
148
|
+
const queue = [...starts].filter((entry) => candidates.has(entry));
|
|
149
|
+
for (const entry of queue)
|
|
150
|
+
approvedEntries.add(entry);
|
|
151
|
+
while (queue.length > 0) {
|
|
152
|
+
const entry = queue.shift();
|
|
153
|
+
for (const child of edges.get(entry) ?? []) {
|
|
154
|
+
if (!candidates.has(child) || approvedEntries.has(child))
|
|
155
|
+
continue;
|
|
156
|
+
approvedEntries.add(child);
|
|
157
|
+
queue.push(child);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const approved = new Map();
|
|
161
|
+
for (const entry of approvedEntries) {
|
|
162
|
+
const outcome = outcomes.get(versionKey(entry.name, entry.version));
|
|
163
|
+
if (outcome && "fact" in outcome)
|
|
164
|
+
approved.set(versionKey(entry.name, entry.version), {
|
|
165
|
+
name: entry.name,
|
|
166
|
+
version: entry.version,
|
|
167
|
+
integrity: outcome.fact.integrity,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const refused = new Map();
|
|
171
|
+
for (const entry of lockfile.entries) {
|
|
172
|
+
if (entry.kind === "bundled")
|
|
173
|
+
continue;
|
|
174
|
+
const key = versionKey(entry.name, entry.version);
|
|
175
|
+
if (approved.has(key) || refused.has(key))
|
|
176
|
+
continue;
|
|
177
|
+
const refusal = refusals.get(entry) ?? {
|
|
178
|
+
code: "wardby_package_not_allowed",
|
|
179
|
+
reason: "not reachable from this agent's allowlist through dependencies the registry confirms",
|
|
180
|
+
};
|
|
181
|
+
refused.set(key, { name: entry.name, version: entry.version, ...refusal });
|
|
182
|
+
}
|
|
183
|
+
// An entry refused at one place in the tree but approved at another (the
|
|
184
|
+
// same name@version) is approved: approvals are per exact version.
|
|
185
|
+
return { approved: [...approved.values()], refused: [...refused.values()] };
|
|
186
|
+
}
|
|
187
|
+
function safeSatisfies(adapter, version, range) {
|
|
188
|
+
try {
|
|
189
|
+
return adapter.satisfies(version, range);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Facts for every entry of `layer` not already known: stored facts first,
|
|
196
|
+
* then the registry (per-version documents, then publish times streamed
|
|
197
|
+
* once per package), storing each complete fact for reuse. */
|
|
198
|
+
async function loadFacts(input, layer, outcomes) {
|
|
199
|
+
const { support, signal } = input;
|
|
200
|
+
const wanted = [
|
|
201
|
+
...new Map(layer
|
|
202
|
+
.filter((entry) => !outcomes.has(versionKey(entry.name, entry.version)))
|
|
203
|
+
.map((entry) => [versionKey(entry.name, entry.version), { name: entry.name, version: entry.version }])).values(),
|
|
204
|
+
];
|
|
205
|
+
if (wanted.length === 0)
|
|
206
|
+
return;
|
|
207
|
+
for (const fact of await input.facts.getVersionFacts(wanted)) {
|
|
208
|
+
// A stored fact whose download host is not (or no longer) allowed is
|
|
209
|
+
// refused below like a fresh one would be.
|
|
210
|
+
outcomes.set(versionKey(fact.name, fact.version), checkedFact(input, fact));
|
|
211
|
+
}
|
|
212
|
+
const missing = wanted.filter(({ name, version }) => !outcomes.has(versionKey(name, version)));
|
|
213
|
+
if (missing.length === 0)
|
|
214
|
+
return;
|
|
215
|
+
const fresh = new Map();
|
|
216
|
+
await eachLimited(missing, input.factConcurrency ?? DEFAULT_PLAN_FACT_CONCURRENCY, async ({ name, version }) => {
|
|
217
|
+
const key = versionKey(name, version);
|
|
218
|
+
try {
|
|
219
|
+
const fact = await withRetry(signal, () => support.fetchVersionFact(name, version, input.upstream, signal));
|
|
220
|
+
if (!fact) {
|
|
221
|
+
outcomes.set(key, {
|
|
222
|
+
refusal: {
|
|
223
|
+
code: "wardby_package_not_found",
|
|
224
|
+
reason: `the registry has no ${name}@${version}`,
|
|
225
|
+
intrinsic: true,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
fresh.set(key, fact);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (signal.aborted)
|
|
234
|
+
throw error;
|
|
235
|
+
outcomes.set(key, { refusal: upstreamRefusal(error) });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
signal.throwIfAborted();
|
|
239
|
+
const byName = new Map();
|
|
240
|
+
for (const fact of fresh.values())
|
|
241
|
+
byName.set(fact.name, [...(byName.get(fact.name) ?? []), fact.version]);
|
|
242
|
+
const complete = [];
|
|
243
|
+
await eachLimited([...byName.entries()], input.timeConcurrency ?? DEFAULT_PLAN_TIME_CONCURRENCY, async ([name, versions]) => {
|
|
244
|
+
let times;
|
|
245
|
+
try {
|
|
246
|
+
times = await withRetry(signal, () => support.fetchPublishTimes(name, versions, input.streamUpstream, {
|
|
247
|
+
signal,
|
|
248
|
+
maxBytes: input.maxDocumentBytes ?? DEFAULT_PLAN_MAX_DOCUMENT_BYTES,
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
if (signal.aborted)
|
|
253
|
+
throw error;
|
|
254
|
+
for (const version of versions)
|
|
255
|
+
outcomes.set(versionKey(name, version), { refusal: upstreamRefusal(error) });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
for (const version of versions) {
|
|
259
|
+
const key = versionKey(name, version);
|
|
260
|
+
const publishedAt = times.get(version);
|
|
261
|
+
if (!publishedAt) {
|
|
262
|
+
// Unknown publish time counts as too new; not stored, so a later
|
|
263
|
+
// plan reads it again.
|
|
264
|
+
outcomes.set(key, {
|
|
265
|
+
refusal: { code: "wardby_version_filtered", reason: "the registry records no publish time for it" },
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const fact = { ...fresh.get(key), publishedAt };
|
|
270
|
+
complete.push(fact);
|
|
271
|
+
outcomes.set(key, checkedFact(input, fact));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
signal.throwIfAborted();
|
|
275
|
+
await input.facts.putVersionFacts(complete);
|
|
276
|
+
}
|
|
277
|
+
/** A fact is usable only when the registry gave an integrity to check
|
|
278
|
+
* downloads against and a download on this adapter's own upstream hosts. */
|
|
279
|
+
function checkedFact(input, fact) {
|
|
280
|
+
if (fact.integrity === "")
|
|
281
|
+
return {
|
|
282
|
+
refusal: {
|
|
283
|
+
code: "wardby_registry_integrity_missing",
|
|
284
|
+
reason: "the registry publishes no integrity to check it against",
|
|
285
|
+
intrinsic: true,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
if (!input.hostAllowed(fact.downloadUrl))
|
|
289
|
+
return {
|
|
290
|
+
refusal: {
|
|
291
|
+
code: "wardby_upstream_host_not_allowed",
|
|
292
|
+
reason: `hosted outside the ${input.adapter.id} registry's upstreams`,
|
|
293
|
+
intrinsic: true,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
return { fact };
|
|
297
|
+
}
|
|
298
|
+
function upstreamRefusal(error) {
|
|
299
|
+
if (error instanceof RegistryError && error.code === "wardby_metadata_too_large")
|
|
300
|
+
return { code: "wardby_metadata_too_large", reason: error.message };
|
|
301
|
+
if (error instanceof RegistryError && error.code === "wardby_package_not_found")
|
|
302
|
+
return { code: "wardby_package_not_found", reason: error.message };
|
|
303
|
+
return { code: "wardby_upstream_error", reason: "the registry could not be read for it; try again" };
|
|
304
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PrismaClient } from "#prisma";
|
|
2
|
+
import type { ApprovedVersion, PlanRefusedVersion, RegistryFetchRecord, RegistryRunContext, RegistryStore, StoredVersionFact } from "./store.js";
|
|
3
|
+
export declare class PrismaRegistryStore implements RegistryStore {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: PrismaClient);
|
|
6
|
+
findRunByRegistryTokenHash(hash: string, now: Date): Promise<RegistryRunContext | null>;
|
|
7
|
+
isAllowedDependency(runId: string, ecosystem: string, name: string): Promise<boolean>;
|
|
8
|
+
addAllowances(runId: string, ecosystem: string, names: readonly string[]): Promise<void>;
|
|
9
|
+
recordFetch(record: RegistryFetchRecord): Promise<void>;
|
|
10
|
+
usage(runId: string): Promise<{
|
|
11
|
+
files: number;
|
|
12
|
+
bytes: number;
|
|
13
|
+
}>;
|
|
14
|
+
refusalCount(runId: string): Promise<number>;
|
|
15
|
+
listFetches(runId: string): Promise<(RegistryFetchRecord & {
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
})[]>;
|
|
18
|
+
getVersionFacts(ecosystem: string, versions: readonly {
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
}[]): Promise<StoredVersionFact[]>;
|
|
22
|
+
putVersionFacts(ecosystem: string, facts: readonly StoredVersionFact[]): Promise<void>;
|
|
23
|
+
approveVersions(runId: string, ecosystem: string, versions: readonly ApprovedVersion[]): Promise<void>;
|
|
24
|
+
findApprovedVersion(runId: string, ecosystem: string, name: string, version: string): Promise<{
|
|
25
|
+
integrity: string;
|
|
26
|
+
downloadUrl: string;
|
|
27
|
+
} | null>;
|
|
28
|
+
refusePlanVersions(runId: string, ecosystem: string, versions: readonly PlanRefusedVersion[]): Promise<void>;
|
|
29
|
+
findPlanRefusal(runId: string, ecosystem: string, name: string, version: string): Promise<{
|
|
30
|
+
code: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
publishedAt: Date | null;
|
|
33
|
+
} | null>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** Versions looked up per query, so a 5000-entry lockfile is a handful of
|
|
2
|
+
* bounded queries rather than one enormous OR. */
|
|
3
|
+
const FACT_QUERY_CHUNK = 500;
|
|
4
|
+
export class PrismaRegistryStore {
|
|
5
|
+
db;
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
}
|
|
9
|
+
async findRunByRegistryTokenHash(hash, now) {
|
|
10
|
+
const session = await this.db.codingProxySession.findUnique({
|
|
11
|
+
where: { registryTokenHash: hash },
|
|
12
|
+
select: {
|
|
13
|
+
runId: true,
|
|
14
|
+
status: true,
|
|
15
|
+
deadlineAt: true,
|
|
16
|
+
run: { select: { packageAllowlist: true, packagePolicy: true } },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
if (!session || session.status !== "active" || session.deadlineAt <= now)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
runId: session.runId,
|
|
23
|
+
deadlineAt: session.deadlineAt,
|
|
24
|
+
allowlist: (session.run.packageAllowlist ?? {}),
|
|
25
|
+
policy: session.run.packagePolicy,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async isAllowedDependency(runId, ecosystem, name) {
|
|
29
|
+
const row = await this.db.registryAllowance.findUnique({
|
|
30
|
+
where: { runId_ecosystem_name: { runId, ecosystem, name } },
|
|
31
|
+
select: { runId: true },
|
|
32
|
+
});
|
|
33
|
+
return row !== null;
|
|
34
|
+
}
|
|
35
|
+
async addAllowances(runId, ecosystem, names) {
|
|
36
|
+
if (names.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
await this.db.registryAllowance.createMany({
|
|
39
|
+
data: [...new Set(names)].map((name) => ({ runId, ecosystem, name })),
|
|
40
|
+
skipDuplicates: true,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async recordFetch(record) {
|
|
44
|
+
await this.db.registryFetch.create({ data: record });
|
|
45
|
+
}
|
|
46
|
+
async usage(runId) {
|
|
47
|
+
const totals = await this.db.registryFetch.aggregate({
|
|
48
|
+
where: { runId, outcome: "served" },
|
|
49
|
+
_count: { _all: true },
|
|
50
|
+
_sum: { sizeBytes: true },
|
|
51
|
+
});
|
|
52
|
+
return { files: totals._count._all, bytes: totals._sum.sizeBytes ?? 0 };
|
|
53
|
+
}
|
|
54
|
+
async refusalCount(runId) {
|
|
55
|
+
return this.db.registryFetch.count({ where: { runId, outcome: "refused" } });
|
|
56
|
+
}
|
|
57
|
+
async listFetches(runId) {
|
|
58
|
+
const rows = await this.db.registryFetch.findMany({ where: { runId }, orderBy: { createdAt: "asc" } });
|
|
59
|
+
return rows.map((row) => ({
|
|
60
|
+
runId: row.runId,
|
|
61
|
+
ecosystem: row.ecosystem,
|
|
62
|
+
name: row.name,
|
|
63
|
+
version: row.version ?? undefined,
|
|
64
|
+
filename: row.filename ?? undefined,
|
|
65
|
+
integrity: row.integrity ?? undefined,
|
|
66
|
+
sizeBytes: row.sizeBytes ?? undefined,
|
|
67
|
+
outcome: row.outcome,
|
|
68
|
+
reason: row.reason ?? undefined,
|
|
69
|
+
createdAt: row.createdAt,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
async getVersionFacts(ecosystem, versions) {
|
|
73
|
+
const facts = [];
|
|
74
|
+
for (let start = 0; start < versions.length; start += FACT_QUERY_CHUNK) {
|
|
75
|
+
const chunk = versions.slice(start, start + FACT_QUERY_CHUNK);
|
|
76
|
+
const rows = await this.db.registryVersionFact.findMany({
|
|
77
|
+
where: { OR: chunk.map(({ name, version }) => ({ ecosystem, name, version })) },
|
|
78
|
+
});
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
facts.push({
|
|
81
|
+
name: row.name,
|
|
82
|
+
version: row.version,
|
|
83
|
+
publishedAt: row.publishedAt,
|
|
84
|
+
integrity: row.integrity,
|
|
85
|
+
downloadUrl: row.downloadUrl,
|
|
86
|
+
dependencies: row.dependencies,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return facts;
|
|
91
|
+
}
|
|
92
|
+
async putVersionFacts(ecosystem, facts) {
|
|
93
|
+
if (facts.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
await this.db.registryVersionFact.createMany({
|
|
96
|
+
data: facts.map((fact) => ({
|
|
97
|
+
ecosystem,
|
|
98
|
+
name: fact.name,
|
|
99
|
+
version: fact.version,
|
|
100
|
+
publishedAt: fact.publishedAt,
|
|
101
|
+
integrity: fact.integrity,
|
|
102
|
+
downloadUrl: fact.downloadUrl,
|
|
103
|
+
dependencies: fact.dependencies.map(({ key, name, range }) => ({ key, name, range })),
|
|
104
|
+
})),
|
|
105
|
+
skipDuplicates: true,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async approveVersions(runId, ecosystem, versions) {
|
|
109
|
+
if (versions.length === 0)
|
|
110
|
+
return;
|
|
111
|
+
await this.db.registryApprovedVersion.createMany({
|
|
112
|
+
data: versions.map(({ name, version, integrity }) => ({ runId, ecosystem, name, version, integrity })),
|
|
113
|
+
skipDuplicates: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async findApprovedVersion(runId, ecosystem, name, version) {
|
|
117
|
+
const approval = await this.db.registryApprovedVersion.findUnique({
|
|
118
|
+
where: { runId_ecosystem_name_version: { runId, ecosystem, name, version } },
|
|
119
|
+
select: { integrity: true },
|
|
120
|
+
});
|
|
121
|
+
if (!approval)
|
|
122
|
+
return null;
|
|
123
|
+
const fact = await this.db.registryVersionFact.findUnique({
|
|
124
|
+
where: { ecosystem_name_version: { ecosystem, name, version } },
|
|
125
|
+
select: { downloadUrl: true },
|
|
126
|
+
});
|
|
127
|
+
return fact ? { integrity: approval.integrity, downloadUrl: fact.downloadUrl } : null;
|
|
128
|
+
}
|
|
129
|
+
async refusePlanVersions(runId, ecosystem, versions) {
|
|
130
|
+
if (versions.length === 0)
|
|
131
|
+
return;
|
|
132
|
+
await this.db.registryPlanRefusal.createMany({
|
|
133
|
+
data: versions.map(({ name, version, code, reason, publishedAt }) => ({
|
|
134
|
+
runId,
|
|
135
|
+
ecosystem,
|
|
136
|
+
name,
|
|
137
|
+
version,
|
|
138
|
+
code,
|
|
139
|
+
reason,
|
|
140
|
+
publishedAt: publishedAt ?? null,
|
|
141
|
+
})),
|
|
142
|
+
skipDuplicates: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async findPlanRefusal(runId, ecosystem, name, version) {
|
|
146
|
+
return this.db.registryPlanRefusal.findUnique({
|
|
147
|
+
where: { runId_ecosystem_name_version: { runId, ecosystem, name, version } },
|
|
148
|
+
select: { code: true, reason: true, publishedAt: true },
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|