@wardby/cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/driver.d.ts +21 -3
- package/dist/claude-coding-worker/driver.js +125 -17
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +12 -1
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +1 -0
- package/dist/cli.js +24 -10
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +46 -126
- package/dist/coding/profile.js +45 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +264 -346
- package/dist/coding/protocol.js +235 -12
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +12 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +51 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +8 -3
- package/dist/coding-worker/artifact.js +3 -1
- package/dist/coding-worker/debug-trace.d.ts +6 -5
- package/dist/coding-worker/debug-trace.js +13 -18
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +116 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +11 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/providers.d.ts +12 -3
- package/dist/config/providers.js +55 -5
- package/dist/config/test-simulators.d.ts +82 -0
- package/dist/config/test-simulators.js +305 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/dispatch.d.ts +42 -0
- package/dist/core/dispatch.js +131 -56
- package/dist/core/engine-native.js +1 -1
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +168 -0
- package/dist/core/grants.d.ts +19 -6
- package/dist/core/grants.js +55 -6
- package/dist/core/host-events.d.ts +18 -2
- package/dist/core/host-events.js +284 -24
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +298 -20
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +124 -33
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +4 -10
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +54 -26
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +2 -1
- package/dist/core/merge-order-check.js +8 -1
- package/dist/core/notification-templates.d.ts +1 -7
- package/dist/core/notification-templates.js +36 -2
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +4 -3
- package/dist/core/reconciler.js +8 -1
- package/dist/core/related-pull-requests.d.ts +11 -3
- package/dist/core/related-pull-requests.js +27 -14
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/review-fix.d.ts +1 -1
- package/dist/core/review-fix.js +2 -1
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +3 -0
- package/dist/core/review-host-tools.js +99 -13
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +80 -34
- package/dist/core/runner.js +439 -144
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +10 -74
- package/dist/core/secret-guard.d.ts +46 -0
- package/dist/core/secret-guard.js +104 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-events.d.ts +14 -2
- package/dist/core/workflow-events.js +4 -0
- package/dist/core/workflow-run-events.d.ts +5 -3
- package/dist/core/workflow-run-events.js +43 -8
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +34 -3
- package/dist/generated/prisma/client.d.ts +34 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +220 -60
- package/dist/generated/prisma/enums.d.ts +10 -0
- package/dist/generated/prisma/enums.js +8 -0
- package/dist/generated/prisma/internal/class.d.ts +44 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +428 -9
- package/dist/generated/prisma/internal/prismaNamespace.js +92 -1
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +95 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +92 -1
- package/dist/generated/prisma/models/Agent.d.ts +106 -8
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +229 -1
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +1637 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +738 -2
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models.d.ts +4 -0
- package/dist/help-index.json +1320 -69
- package/dist/import/create.js +32 -3
- package/dist/import/neutral-schema.d.ts +50 -281
- package/dist/import/neutral-schema.js +5 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +10 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/host-events/github-ingress.d.ts +2 -0
- package/dist/mcp/host-events/github-ingress.js +2 -1
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +58 -10
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +198 -16
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +9 -3
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.js +2 -2
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/repositories.js +18 -5
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.js +34 -12
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-elicitation-form.js +5 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -61
- package/dist/mcp/tools/secrets.js +2 -2
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +1 -1
- package/dist/mcp/tools/tools.js +25 -9
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/native-worker/docker-launcher.d.ts +38 -2
- package/dist/native-worker/docker-launcher.js +65 -5
- package/dist/native-worker/gateway-server.d.ts +41 -3
- package/dist/native-worker/gateway-server.js +124 -27
- package/dist/native-worker/gateway.d.ts +27 -1
- package/dist/native-worker/gateway.js +70 -4
- package/dist/native-worker/http-server.d.ts +3 -1
- package/dist/native-worker/http-server.js +11 -7
- package/dist/native-worker/http-transport.js +1 -1
- package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
- package/dist/native-worker/kubernetes-launcher.js +43 -3
- package/dist/native-worker/launch.d.ts +18 -1
- package/dist/native-worker/launch.js +6 -1
- package/dist/native-worker/ledger.d.ts +18 -2
- package/dist/native-worker/ledger.js +57 -4
- package/dist/native-worker/loopback.js +3 -8
- package/dist/native-worker/main.d.ts +2 -1
- package/dist/native-worker/main.js +14 -3
- package/dist/native-worker/protocol.d.ts +129 -577
- package/dist/native-worker/protocol.js +47 -1
- package/dist/native-worker/sandbox-executor.d.ts +17 -2
- package/dist/native-worker/sandbox-executor.js +78 -4
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +1 -1
- package/dist/native-worker/stdio.js +6 -5
- package/dist/native-worker/warm-pool.d.ts +2 -2
- package/dist/native-worker/warm-pool.js +4 -4
- package/dist/native-worker/worker.js +28 -2
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/self-hosted.d.ts +1 -1
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/memory-ledger.js +84 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
- package/dist/providers/coding-proxy/proxy.d.ts +14 -1
- package/dist/providers/coding-proxy/proxy.js +166 -14
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +4 -0
- package/dist/providers/coding-proxy/runtime.js +78 -16
- package/dist/providers/coding-proxy/server.d.ts +6 -1
- package/dist/providers/coding-proxy/server.js +45 -1
- package/dist/providers/coding-proxy/types.d.ts +65 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +17 -2
- package/dist/providers/engine/types.js +17 -0
- package/dist/providers/executor/composition.d.ts +10 -3
- package/dist/providers/executor/composition.js +47 -7
- package/dist/providers/executor/container.d.ts +361 -8
- package/dist/providers/executor/container.js +1136 -91
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +50 -1
- package/dist/providers/executor/dbos.js +207 -20
- package/dist/providers/executor/deferred.d.ts +12 -24
- package/dist/providers/executor/deferred.js +10 -29
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +2 -0
- package/dist/providers/executor/routing.js +4 -0
- package/dist/providers/executor/types.d.ts +13 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +8 -0
- package/dist/providers/jobs/docker-isolation.js +17 -0
- package/dist/providers/jobs/docker.d.ts +1 -0
- package/dist/providers/jobs/docker.js +27 -2
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
- package/dist/providers/jobs/kubernetes-api.js +10 -0
- package/dist/providers/jobs/kubernetes-client.js +10 -1
- package/dist/providers/jobs/kubernetes-isolation.d.ts +2 -0
- package/dist/providers/jobs/kubernetes-isolation.js +17 -1
- package/dist/providers/jobs/kubernetes.d.ts +6 -1
- package/dist/providers/jobs/kubernetes.js +105 -16
- package/dist/providers/jobs/types.d.ts +33 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +29 -2
- package/dist/providers/review-host/github.js +224 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +134 -2
- package/dist/providers/review-host/types.js +8 -0
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +33 -2
- package/dist/providers/vcs/git.js +324 -22
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +214 -6
- package/dist/providers/vcs/github.js +447 -45
- package/dist/providers/vcs/index.js +8 -1
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +33 -1
- package/dist/providers/vcs/routing.d.ts +4 -2
- package/dist/providers/vcs/routing.js +6 -0
- package/dist/providers/vcs/types.d.ts +80 -3
- package/dist/quickstart/coding-seed.d.ts +14 -3
- package/dist/quickstart/coding-seed.js +213 -4
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +1 -0
- package/dist/sandbox/host-functions.js +6 -11
- package/dist/sandbox/secret-broker.js +3 -4
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +8 -1
- package/dist/viewer/api-schema.d.ts +349 -2627
- package/dist/viewer/api-schema.js +7 -0
- package/dist/viewer/build-schemas.js +6 -2
- package/dist/viewer/openapi.js +2 -4
- package/dist/wardby-bin.js +10 -0
- package/docs/agent-recipes.md +58 -20
- package/docs/code-review-agents.md +551 -41
- package/docs/coding-agent-setup.md +489 -7
- package/docs/coding-packages.md +1033 -90
- package/docs/coding-services.md +27 -12
- package/docs/coding-worker-byo-images.md +20 -0
- package/docs/coding-worker-isolation.md +105 -30
- package/docs/getting-started-gke.md +124 -137
- package/docs/getting-started-identity-provider.md +10 -1
- package/docs/getting-started.md +9 -0
- package/docs/jira-agents.md +17 -8
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/native-sandbox.md +119 -23
- package/docs/observability.md +4 -0
- package/docs/security-deployment.md +33 -2
- package/docs/slack-notifications.md +35 -19
- package/docs/tool-secrets.md +42 -10
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +24 -2
- package/help/architecture-agent.md +11 -6
- package/help/build-worker-image.md +3 -1
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +177 -6
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +15 -8
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +4 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +3 -2
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/github-app-setup.md +132 -0
- package/help/github.md +22 -0
- package/help/identity-and-access.md +2 -0
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +1 -1
- package/help/low-power-mode.md +7 -1
- package/help/merge-order-check.md +5 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +15 -1
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +10 -7
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +33 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +19 -5
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +78 -7
- package/package.json +17 -10
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/schema.prisma +311 -97
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
|
@@ -2,29 +2,24 @@
|
|
|
2
2
|
* PyPI registry adapter: parses PyPI allowlist syntax (PEP 503 names, PEP
|
|
3
3
|
* 440 specifiers), routes proxy requests against the PEP 691 JSON Simple
|
|
4
4
|
* API plus PEP 658 metadata files, restricts downloads to wheels, and
|
|
5
|
-
* configures pip for the sandboxed worker to use the proxy as
|
|
6
|
-
* index with binary-only installs.
|
|
5
|
+
* configures pip and uv for the sandboxed worker to use the proxy as their
|
|
6
|
+
* only index with binary-only installs.
|
|
7
7
|
*/
|
|
8
8
|
import { clean as pepClean, compare as pepCompare, satisfies as pepSatisfies, validRange } from "@renovatebot/pep440";
|
|
9
9
|
import { unzipSync, strFromU8 } from "fflate";
|
|
10
|
+
import { normalizeUvLock, pypiLockfilePlan } from "./pypi-plan.js";
|
|
11
|
+
import { isPypiProjectName, MAX_EXTRAS, normalizePypiName, parseExtras, requiredDists, requiresDist, } from "./pypi-requirements.js";
|
|
10
12
|
import { AllowlistEntryError, RegistryError, } from "./types.js";
|
|
11
13
|
const UPSTREAM = "https://pypi.org/simple/";
|
|
12
14
|
const SIMPLE_JSON = "application/vnd.pypi.simple.v1+json";
|
|
13
15
|
/** name, an optional `[extras]` group (checked separately), then the specifier. */
|
|
14
16
|
const ENTRY = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(?:\[([^\]]*)\])?\s*(.*)$/;
|
|
15
|
-
/** The most extras one allowlist entry or dependency line may name. */
|
|
16
|
-
export const MAX_EXTRAS = 32;
|
|
17
17
|
const WHEEL_METADATA_LIMIT = 64 * 1024 * 1024;
|
|
18
|
+
export { isPypiProjectName, MAX_EXTRAS, normalizePypiName, parseExtras, requiredDists, requiresDist };
|
|
18
19
|
function uploadTime(file) {
|
|
19
20
|
const time = file["upload-time"] ? Date.parse(file["upload-time"]) : NaN;
|
|
20
21
|
return Number.isNaN(time) ? null : new Date(time);
|
|
21
22
|
}
|
|
22
|
-
/** A valid PyPI project name (PEP 508): ASCII letters and digits, with
|
|
23
|
-
* `.`, `_` and `-` allowed only between them. */
|
|
24
|
-
const PROJECT_NAME = /^(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9])$/;
|
|
25
|
-
export function isPypiProjectName(name) {
|
|
26
|
-
return PROJECT_NAME.test(name);
|
|
27
|
-
}
|
|
28
23
|
function decoded(segment) {
|
|
29
24
|
try {
|
|
30
25
|
return decodeURIComponent(segment);
|
|
@@ -35,12 +30,12 @@ function decoded(segment) {
|
|
|
35
30
|
}
|
|
36
31
|
function projectName(segment) {
|
|
37
32
|
const name = decoded(segment);
|
|
38
|
-
if (!
|
|
33
|
+
if (!isPypiProjectName(name))
|
|
39
34
|
throw new RegistryError(400, "wardby_bad_request", "not a valid Python project name");
|
|
40
35
|
return normalizePypiName(name);
|
|
41
36
|
}
|
|
42
|
-
|
|
43
|
-
return
|
|
37
|
+
function isSdist(filename) {
|
|
38
|
+
return /\.(?:tar\.gz|zip)$/.test(filename);
|
|
44
39
|
}
|
|
45
40
|
/** Version from a wheel ("name-1.0-py3-none-any.whl") or sdist ("name-1.0.tar.gz") filename. */
|
|
46
41
|
function versionOf(filename) {
|
|
@@ -49,139 +44,15 @@ function versionOf(filename) {
|
|
|
49
44
|
const sdist = filename.match(/^.+?-(\d[^-]*)\.(?:tar\.gz|zip)$/);
|
|
50
45
|
return sdist ? sdist[1] : null;
|
|
51
46
|
}
|
|
52
|
-
/** Normalized (PEP 685), sorted, de-duplicated extras from the text inside
|
|
53
|
-
* `[...]`, or null when any is not a valid extra name (PEP 508: the same
|
|
54
|
-
* shape as a project name), the list is empty, or it names too many. */
|
|
55
|
-
export function parseExtras(list) {
|
|
56
|
-
const items = list.split(",").map((item) => item.trim());
|
|
57
|
-
if (items.length > MAX_EXTRAS || items.some((item) => !PROJECT_NAME.test(item)))
|
|
58
|
-
return null;
|
|
59
|
-
return [...new Set(items.map(normalizePypiName))].sort();
|
|
60
|
-
}
|
|
61
|
-
/** The string literals a marker compares `extra` to with `==` or `===`
|
|
62
|
-
* (either operand order), normalized; null when the marker never mentions
|
|
63
|
-
* `extra`. Tokenized, so a quoted value never counts as a clause. Fails
|
|
64
|
-
* closed: when `extra` appears in any shape this does not fully recognise
|
|
65
|
-
* (`extra in "a b"`, `extra.x`, a bare `extra`, an unbalanced quote), the
|
|
66
|
-
* line is gated on no extra at all, so it is never followed. */
|
|
67
|
-
function markerExtras(marker) {
|
|
68
|
-
if (!/\bextra\b/.test(marker))
|
|
69
|
-
return null;
|
|
70
|
-
const tokens = marker.match(/"[^"]*"|'[^']*'|===|==|!=|<=|>=|~=|[A-Za-z_][A-Za-z0-9_.]*|\S/g) ?? [];
|
|
71
|
-
const isLiteral = (token) => token !== undefined && token.length >= 2 && (token[0] === '"' || token[0] === "'");
|
|
72
|
-
const isEquals = (token) => token === "==" || token === "===";
|
|
73
|
-
// A lone quote (unterminated string) means the marker cannot be read.
|
|
74
|
-
if (tokens.some((token) => token === '"' || token === "'"))
|
|
75
|
-
return [];
|
|
76
|
-
const found = [];
|
|
77
|
-
let recognised = 0;
|
|
78
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
79
|
-
if (tokens[i] !== "extra")
|
|
80
|
-
continue;
|
|
81
|
-
let value = null;
|
|
82
|
-
if (isEquals(tokens[i + 1]) && isLiteral(tokens[i + 2]))
|
|
83
|
-
value = tokens[i + 2].slice(1, -1);
|
|
84
|
-
else if (isEquals(tokens[i - 1]) && isLiteral(tokens[i - 2]))
|
|
85
|
-
value = tokens[i - 2].slice(1, -1);
|
|
86
|
-
if (value === null)
|
|
87
|
-
return [];
|
|
88
|
-
recognised++;
|
|
89
|
-
if (PROJECT_NAME.test(value))
|
|
90
|
-
found.push(normalizePypiName(value));
|
|
91
|
-
}
|
|
92
|
-
// `extra` only inside quoted values: not a clause, but not one this reads either.
|
|
93
|
-
return recognised === 0 ? [] : found;
|
|
94
|
-
}
|
|
95
|
-
const REQUIREMENT = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(?:\[([^\]]*)\])?/;
|
|
96
|
-
// Splits each Requires-Dist value only at its first `;` (the marker), which is
|
|
97
|
-
// all wheel METADATA needs: no `name @ url` with a `;` in the URL, no comments
|
|
98
|
-
// or line continuations as in a requirements file.
|
|
99
|
-
function requirementLines(metadataText) {
|
|
100
|
-
let self = null;
|
|
101
|
-
const lines = [];
|
|
102
|
-
for (const line of metadataText.split(/\r?\n/)) {
|
|
103
|
-
// The headers end at the first truly empty line; the description
|
|
104
|
-
// follows. A whitespace-only line is a folded header's continuation.
|
|
105
|
-
if (line === "")
|
|
106
|
-
break;
|
|
107
|
-
if (line.startsWith("Name:")) {
|
|
108
|
-
const name = line.slice("Name:".length).trim();
|
|
109
|
-
if (self === null && PROJECT_NAME.test(name))
|
|
110
|
-
self = normalizePypiName(name);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (!line.startsWith("Requires-Dist:"))
|
|
114
|
-
continue;
|
|
115
|
-
const value = line.slice("Requires-Dist:".length).trim();
|
|
116
|
-
const match = value.match(REQUIREMENT);
|
|
117
|
-
if (!match || !PROJECT_NAME.test(match[1]))
|
|
118
|
-
continue;
|
|
119
|
-
const semicolon = value.indexOf(";", match[0].length);
|
|
120
|
-
lines.push({
|
|
121
|
-
name: normalizePypiName(match[1]),
|
|
122
|
-
// A malformed extras list still installs the package, just none of its extras.
|
|
123
|
-
extras: match[2] === undefined ? [] : (parseExtras(match[2]) ?? []),
|
|
124
|
-
gate: semicolon === -1 ? null : markerExtras(value.slice(semicolon + 1)),
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return { self, lines };
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* The dependencies a wheel's METADATA declares for an install of the package
|
|
131
|
-
* with `extras`: every line without an `extra == "…"` marker clause, plus the
|
|
132
|
-
* lines whose marker names one of `extras` (other marker clauses are ignored,
|
|
133
|
-
* so a requested extra's lines are included on every platform). A line gated
|
|
134
|
-
* on any other extra is skipped. Each dependency carries the extras its line
|
|
135
|
-
* asks of it (`uvicorn[standard]`); a line naming the package itself
|
|
136
|
-
* (`celery[redis]; extra == "all"`) adds those extras to this install
|
|
137
|
-
* instead. Names and extras are validated and normalized.
|
|
138
|
-
*
|
|
139
|
-
* Policy, not pip: a plain install (no `extras`) follows no extras at all,
|
|
140
|
-
* neither its own (a self-referencing line) nor its dependencies' — each
|
|
141
|
-
* dependency comes back bare, so extras are followed only along a chain that
|
|
142
|
-
* starts at an allowlist entry naming extras.
|
|
143
|
-
*/
|
|
144
|
-
export function requiredDists(metadataText, extras = []) {
|
|
145
|
-
const { self, lines } = requirementLines(metadataText);
|
|
146
|
-
const requested = new Set(extras.map(normalizePypiName));
|
|
147
|
-
const plain = requested.size === 0;
|
|
148
|
-
const included = (line) => line.gate === null || line.gate.some((extra) => requested.has(extra));
|
|
149
|
-
for (let grew = !plain; grew;) {
|
|
150
|
-
grew = false;
|
|
151
|
-
for (const line of lines) {
|
|
152
|
-
if (line.name !== self || !included(line))
|
|
153
|
-
continue;
|
|
154
|
-
for (const extra of line.extras) {
|
|
155
|
-
if (requested.size >= MAX_EXTRAS || requested.has(extra))
|
|
156
|
-
continue;
|
|
157
|
-
requested.add(extra);
|
|
158
|
-
grew = true;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const out = new Map();
|
|
163
|
-
for (const line of lines) {
|
|
164
|
-
if (line.name === self || !included(line))
|
|
165
|
-
continue;
|
|
166
|
-
const merged = out.get(line.name) ?? new Set();
|
|
167
|
-
if (!plain)
|
|
168
|
-
for (const extra of line.extras)
|
|
169
|
-
if (merged.size < MAX_EXTRAS)
|
|
170
|
-
merged.add(extra);
|
|
171
|
-
out.set(line.name, merged);
|
|
172
|
-
}
|
|
173
|
-
return [...out].map(([name, merged]) => ({ name, extras: [...merged].sort() }));
|
|
174
|
-
}
|
|
175
|
-
/** Dependency names a METADATA declares for a plain install (no extras). */
|
|
176
|
-
export function requiresDist(metadataText) {
|
|
177
|
-
return requiredDists(metadataText).map((dependency) => dependency.name);
|
|
178
|
-
}
|
|
179
47
|
export const pypiAdapter = {
|
|
180
48
|
id: "pypi",
|
|
181
49
|
osvEcosystem: "PyPI",
|
|
182
50
|
upstreamHosts: ["pypi.org", "files.pythonhosted.org"],
|
|
183
51
|
collectExclude: [".venv", "venv", "__pycache__"],
|
|
184
52
|
dependenciesInMetadata: false,
|
|
53
|
+
// uv records the proxy's URLs in a uv.lock it writes through it.
|
|
54
|
+
lockfiles: { names: ["uv.lock"], normalize: (content, registryUrl) => normalizeUvLock(content, registryUrl) },
|
|
55
|
+
lockfilePlan: pypiLockfilePlan,
|
|
185
56
|
parseAllowlistEntry(raw) {
|
|
186
57
|
const match = raw.trim().match(ENTRY);
|
|
187
58
|
if (!match)
|
|
@@ -251,29 +122,37 @@ export const pypiAdapter = {
|
|
|
251
122
|
});
|
|
252
123
|
versions.set(version, entry);
|
|
253
124
|
}
|
|
254
|
-
// Keep only what renderMetadata and resolveFileMetadata read: wheels
|
|
255
|
-
// with the PEP 691 file fields pip
|
|
125
|
+
// Keep only what renderMetadata and resolveFileMetadata read: wheels and
|
|
126
|
+
// sdists, with the PEP 691 file fields pip and uv use. An sdist is listed
|
|
127
|
+
// (so a uv.lock written through the proxy keeps it, as one written
|
|
128
|
+
// against PyPI does) but never served: downloads are wheels only, and
|
|
129
|
+
// PIP_ONLY_BINARY / UV_NO_BUILD keep both clients off it. Its metadata
|
|
130
|
+
// file is never offered.
|
|
256
131
|
const raw = {
|
|
257
132
|
name: index.name,
|
|
258
133
|
files: index.files
|
|
259
|
-
.filter((file) => file.filename.endsWith(".whl") && !file.yanked)
|
|
260
|
-
.map((file) =>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
134
|
+
.filter((file) => (file.filename.endsWith(".whl") || isSdist(file.filename)) && !file.yanked)
|
|
135
|
+
.map((file) => {
|
|
136
|
+
const wheel = file.filename.endsWith(".whl");
|
|
137
|
+
return {
|
|
138
|
+
filename: file.filename,
|
|
139
|
+
url: file.url,
|
|
140
|
+
...(file.hashes ? { hashes: file.hashes } : {}),
|
|
141
|
+
...(file["upload-time"] ? { "upload-time": file["upload-time"] } : {}),
|
|
142
|
+
...(file.size !== undefined ? { size: file.size } : {}),
|
|
143
|
+
...(wheel && file["core-metadata"] !== undefined ? { "core-metadata": file["core-metadata"] } : {}),
|
|
144
|
+
...(wheel && file["dist-info-metadata"] !== undefined
|
|
145
|
+
? { "dist-info-metadata": file["dist-info-metadata"] }
|
|
146
|
+
: {}),
|
|
147
|
+
...(file["requires-python"] !== undefined ? { "requires-python": file["requires-python"] } : {}),
|
|
148
|
+
};
|
|
149
|
+
}),
|
|
270
150
|
};
|
|
271
151
|
return { name: normalizePypiName(index.name), versions, raw };
|
|
272
152
|
},
|
|
273
153
|
renderMetadata(meta, keep, keptFiles, proxyBase) {
|
|
274
154
|
const index = meta.raw;
|
|
275
155
|
const files = index.files
|
|
276
|
-
.filter((file) => file.filename.endsWith(".whl"))
|
|
277
156
|
.filter((file) => keep.has(versionOf(file.filename) ?? "") && keptFiles.has(file.filename))
|
|
278
157
|
.map((file) => ({
|
|
279
158
|
...file,
|
|
@@ -327,6 +206,13 @@ export const pypiAdapter = {
|
|
|
327
206
|
PIP_CACHE_DIR: cacheDir,
|
|
328
207
|
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
329
208
|
PIP_NO_INPUT: "1",
|
|
209
|
+
// uv reads none of pip's settings: without these it goes straight to pypi.org,
|
|
210
|
+
// which the sandbox can't reach. UV_NO_BUILD refuses sdists from the index (the
|
|
211
|
+
// wheels-only rule) and still builds the project itself.
|
|
212
|
+
UV_DEFAULT_INDEX: index.toString(),
|
|
213
|
+
UV_NO_BUILD: "1",
|
|
214
|
+
UV_CACHE_DIR: `${cacheDir}/uv`,
|
|
215
|
+
UV_PYTHON_DOWNLOADS: "never",
|
|
330
216
|
},
|
|
331
217
|
files: [],
|
|
332
218
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared wording for package refusals served by the registry proxy. Pure (no provider imports) so the
|
|
3
|
+
* proxy, the run report, and the PR banner all read one table.
|
|
4
|
+
*/
|
|
5
|
+
export declare const PACKAGE_NOT_ALLOWED = "wardby_package_not_allowed";
|
|
6
|
+
export declare const PACKAGE_NOT_ALLOWED_PENDING = "wardby_package_not_allowed_pending";
|
|
7
|
+
export declare const GRAPH_LIMIT = "wardby_graph_limit";
|
|
8
|
+
export declare const GRAPH_INCOMPLETE = "wardby_graph_incomplete";
|
|
9
|
+
export interface WalkProgress {
|
|
10
|
+
walked: number;
|
|
11
|
+
queued: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function notAllowedMessage(name: string, ecosystem: string, hasAllowlist: boolean): string;
|
|
14
|
+
export declare function notAllowedPendingMessage(name: string, ecosystem: string, progress: WalkProgress): string;
|
|
15
|
+
export declare function graphLimitMessage(name: string, ecosystem: string, limit: number): string;
|
|
16
|
+
export declare function graphIncompleteMessage(name: string, ecosystem: string, progress: WalkProgress): string;
|
|
17
|
+
/** Static guidance for a stored refusal code (get_run `hint`); undefined for unknown codes. */
|
|
18
|
+
export declare function refusalHint(code: string): string | undefined;
|
|
19
|
+
/** Short parenthetical for the PR banner; undefined for unknown codes. */
|
|
20
|
+
export declare function refusalMeaning(code: string): string | undefined;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared wording for package refusals served by the registry proxy. Pure (no provider imports) so the
|
|
3
|
+
* proxy, the run report, and the PR banner all read one table.
|
|
4
|
+
*/
|
|
5
|
+
export const PACKAGE_NOT_ALLOWED = "wardby_package_not_allowed";
|
|
6
|
+
export const PACKAGE_NOT_ALLOWED_PENDING = "wardby_package_not_allowed_pending";
|
|
7
|
+
export const GRAPH_LIMIT = "wardby_graph_limit";
|
|
8
|
+
export const GRAPH_INCOMPLETE = "wardby_graph_incomplete";
|
|
9
|
+
function packages(count) {
|
|
10
|
+
return `${count.toLocaleString("en-US")} package${count === 1 ? "" : "s"}`;
|
|
11
|
+
}
|
|
12
|
+
export function notAllowedMessage(name, ecosystem, hasAllowlist) {
|
|
13
|
+
const why = hasAllowlist
|
|
14
|
+
? `${name} (${ecosystem}) is not on this agent's allowlist, and no allowlisted package this run resolved depends on it (a package added to the project directly must itself be allowlisted)`
|
|
15
|
+
: `${name} (${ecosystem}) is not allowed: this agent has no allowlist for ${ecosystem}`;
|
|
16
|
+
return `${why}. The agent's owner must allowlist it; retrying will not help.`;
|
|
17
|
+
}
|
|
18
|
+
export function notAllowedPendingMessage(name, ecosystem, progress) {
|
|
19
|
+
return (`${name} (${ecosystem}) is not on this agent's allowlist, and was not found among the allowlisted packages' dependencies after ` +
|
|
20
|
+
`${packages(progress.walked)}, at least ${progress.queued.toLocaleString("en-US")} more queued. ` +
|
|
21
|
+
`It may still be reachable, but the walk was cut short; retrying rarely helps. ` +
|
|
22
|
+
`The agent's owner must allowlist ${name} directly.`);
|
|
23
|
+
}
|
|
24
|
+
export function graphLimitMessage(name, ecosystem, limit) {
|
|
25
|
+
return (`${name} (${ecosystem}) wasn't reached before this run's dependency graph walk hit its limit of ${packages(limit)} ` +
|
|
26
|
+
`(REGISTRY_MAX_GRAPH_PACKAGES); retrying will not help. ` +
|
|
27
|
+
`The agent's owner must allowlist ${name} directly, or the operator can raise the limit.`);
|
|
28
|
+
}
|
|
29
|
+
export function graphIncompleteMessage(name, ecosystem, progress) {
|
|
30
|
+
return (`${name} (${ecosystem}) is still being checked against the allowed packages' dependency graph after ${packages(progress.walked)}, ` +
|
|
31
|
+
`${progress.queued.toLocaleString("en-US")} still queued; the walk timed out before checking it (usually a slow upstream for that package, or another request's walk running out of time). Please try again shortly.`);
|
|
32
|
+
}
|
|
33
|
+
const OWNER = "The agent's owner must allowlist the package directly; retrying will not help.";
|
|
34
|
+
const HINTS = {
|
|
35
|
+
[PACKAGE_NOT_ALLOWED]: `The package is not on the agent's allowlist and no allowlisted package the run resolved depends on it. ${OWNER}`,
|
|
36
|
+
[PACKAGE_NOT_ALLOWED_PENDING]: `The dependency walk ended before reaching the package. Retrying rarely helps. ${OWNER}`,
|
|
37
|
+
[GRAPH_LIMIT]: `The run's dependency graph walk hit REGISTRY_MAX_GRAPH_PACKAGES before reaching the package, so it may not be in the graph at all. Retrying will not help. The agent's owner must allowlist the package directly, or the operator can raise the limit.`,
|
|
38
|
+
[GRAPH_INCOMPLETE]: "The walk reached the package but timed out before checking its versions, usually a slow upstream. Try again shortly.",
|
|
39
|
+
wardby_version_filtered: "Every version the agent may use is newer than the release-age limit, outside the allowlisted range, or has a blocking advisory. Pin an older version, or the agent's owner can change the allowlist or policy.",
|
|
40
|
+
wardby_file_not_allowed: "The registry serves wheels only, and the allowed versions of this package publish only source distributions. Use a version or package that ships a wheel, or ask the agent's owner.",
|
|
41
|
+
};
|
|
42
|
+
/** What each refusal code means to someone reading the PR, without the registry's internals. */
|
|
43
|
+
const MEANINGS = {
|
|
44
|
+
[PACKAGE_NOT_ALLOWED]: "not on the allowlist, or only reachable through a refused package; the owner must allowlist it",
|
|
45
|
+
[PACKAGE_NOT_ALLOWED_PENDING]: "not reached before the dependency walk stopped; the owner must allowlist it",
|
|
46
|
+
[GRAPH_LIMIT]: "not reached before the run's dependency graph walk hit its package limit; the owner must allowlist it directly",
|
|
47
|
+
[GRAPH_INCOMPLETE]: "still being checked against the allowed packages' dependency graph; retry shortly",
|
|
48
|
+
wardby_version_filtered: "withheld: newer than the release-age limit, or a high-severity advisory",
|
|
49
|
+
wardby_file_not_allowed: "no wheel to install: only source distributions, which the registry does not serve",
|
|
50
|
+
wardby_lockfile_integrity_mismatch: "the lock file's integrity does not match the registry's",
|
|
51
|
+
wardby_upstream_error: "the upstream registry failed (transient)",
|
|
52
|
+
wardby_audit_unavailable: "the advisory check was unavailable (transient)",
|
|
53
|
+
};
|
|
54
|
+
/** Static guidance for a stored refusal code (get_run `hint`); undefined for unknown codes. */
|
|
55
|
+
export function refusalHint(code) {
|
|
56
|
+
return Object.hasOwn(HINTS, code) ? HINTS[code] : undefined;
|
|
57
|
+
}
|
|
58
|
+
/** Short parenthetical for the PR banner; undefined for unknown codes. */
|
|
59
|
+
export function refusalMeaning(code) {
|
|
60
|
+
return Object.hasOwn(MEANINGS, code) ? MEANINGS[code] : undefined;
|
|
61
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RegistryRefusalSummary } from "./report.js";
|
|
2
|
+
/** Untrusted names stay bounded code spans with mentions and markup defused. */
|
|
3
|
+
export declare function codeSpan(value: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* The ⛔ blocked status (or, with `blocked: false`, the ⚠️ warning) for a run's refusals, as GitHub/Jira
|
|
6
|
+
* Markdown or, with `slack`, as Slack mrkdwn (already escaped).
|
|
7
|
+
*/
|
|
8
|
+
export declare function packageRefusalWording(refusals: readonly RegistryRefusalSummary[], opts?: {
|
|
9
|
+
blocked?: boolean;
|
|
10
|
+
slack?: boolean;
|
|
11
|
+
}): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Fixed operator guidance from structured registry codes, never registry free text. */
|
|
2
|
+
import { GRAPH_INCOMPLETE, GRAPH_LIMIT, PACKAGE_NOT_ALLOWED, PACKAGE_NOT_ALLOWED_PENDING } from "./refusal-hints.js";
|
|
3
|
+
const HELP_URL = "https://github.com/wardby/wardby/blob/main/help/coding-packages.md";
|
|
4
|
+
const HELP_LABEL = "Help: package allowlist";
|
|
5
|
+
const ALLOWLIST = new Set([PACKAGE_NOT_ALLOWED, PACKAGE_NOT_ALLOWED_PENDING, GRAPH_LIMIT]);
|
|
6
|
+
/** Upstream or check failures that usually pass: a later run may succeed unchanged. */
|
|
7
|
+
const TRANSIENT = new Set([
|
|
8
|
+
GRAPH_INCOMPLETE,
|
|
9
|
+
"wardby_upstream_error",
|
|
10
|
+
"wardby_upstream_unavailable",
|
|
11
|
+
"wardby_audit_unavailable",
|
|
12
|
+
"wardby_download_idle",
|
|
13
|
+
]);
|
|
14
|
+
/** Untrusted names stay bounded code spans with mentions and markup defused. */
|
|
15
|
+
export function codeSpan(value) {
|
|
16
|
+
// eslint-disable-next-line no-control-regex -- stripping untrusted control characters.
|
|
17
|
+
const withoutControls = value.replace(/[`\u0000-\u001f\u007f<>]/g, " ");
|
|
18
|
+
const safe = withoutControls.replace(/\s+/g, " ").replace(/@/g, "@").slice(0, 120);
|
|
19
|
+
return `\`${safe}\``;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The ⛔ blocked status (or, with `blocked: false`, the ⚠️ warning) for a run's refusals, as GitHub/Jira
|
|
23
|
+
* Markdown or, with `slack`, as Slack mrkdwn (already escaped).
|
|
24
|
+
*/
|
|
25
|
+
export function packageRefusalWording(refusals, opts = {}) {
|
|
26
|
+
const blocked = opts.blocked ?? true;
|
|
27
|
+
const bold = (text) => (opts.slack ? `*${text}*` : `**${text}**`);
|
|
28
|
+
const distinct = [...new Map(refusals.map((r) => [JSON.stringify([r.ecosystem, r.name, r.reason]), r])).values()];
|
|
29
|
+
const lines = distinct.slice(0, 10).map((r) => {
|
|
30
|
+
const code = /^wardby_[a-z_]+$/.exec(r.reason)?.[0] === r.reason ? ` (\`${r.reason}\`)` : "";
|
|
31
|
+
const guidance = ALLOWLIST.has(r.reason)
|
|
32
|
+
? "not on this agent's allowlist; add it to `codingProfile.packageAllowlist`"
|
|
33
|
+
: r.reason === "wardby_version_filtered"
|
|
34
|
+
? "no version passed the release-age, advisory or malware checks; wait or adjust `packagePolicy.minReleaseAgeDays`, accept an advisory in `packagePolicy.acceptedAdvisories`, or upgrade. Never bypass malware checks"
|
|
35
|
+
: r.reason === "wardby_lockfile_entry_unsupported"
|
|
36
|
+
? "the lock file installs it from git or a URL, which the registry cannot verify; depend on a registry version instead"
|
|
37
|
+
: TRANSIENT.has(r.reason)
|
|
38
|
+
? "the registry could not finish checking it (a slow or failing upstream); mention `@wardby` again to retry"
|
|
39
|
+
: "refused by the package registry; review the package policy and registry error before retrying";
|
|
40
|
+
return `- ${codeSpan(r.ecosystem)} ${codeSpan(r.name)}: ${guidance}${code}`;
|
|
41
|
+
});
|
|
42
|
+
if (distinct.length > 10)
|
|
43
|
+
lines.push(`- ${distinct.length - 10} more refused packages.`);
|
|
44
|
+
const text = [
|
|
45
|
+
blocked
|
|
46
|
+
? `⛔ ${bold("Blocked by the package registry: no changes were made.")} Coding sub-runs couldn't install:`
|
|
47
|
+
: `⚠️ ${bold("Dependency install incomplete.")} Coding sub-runs couldn't install:`,
|
|
48
|
+
...lines,
|
|
49
|
+
"",
|
|
50
|
+
"To unblock: use `update_agent` to update `codingProfile.packageAllowlist` or `codingProfile.packagePolicy` as appropriate (needs `packages:approve` or `agents:admin`), then mention `@wardby` again. ",
|
|
51
|
+
].join("\n");
|
|
52
|
+
// Slack's three control characters; the names cannot hold < or > (codeSpan), but may hold &.
|
|
53
|
+
return opts.slack
|
|
54
|
+
? `${text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}<${HELP_URL}|${HELP_LABEL}>`
|
|
55
|
+
: `${text}[${HELP_LABEL}](${HELP_URL})`;
|
|
56
|
+
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one summary of a run's RegistryFetch rows, shared by get_run and the
|
|
3
|
-
* coding executor's PR-body report so both deduplicate identically: served
|
|
4
|
-
* packages collapse on ecosystem + name + version (a retried download is one
|
|
5
|
-
* package), refusals on ecosystem + name + reason.
|
|
6
|
-
*/
|
|
7
1
|
export interface RegistryFetchRow {
|
|
8
2
|
ecosystem: string;
|
|
9
3
|
name: string;
|
|
@@ -13,24 +7,92 @@ export interface RegistryFetchRow {
|
|
|
13
7
|
sizeBytes: number | null;
|
|
14
8
|
/** `-/plan` for a refusal a lockfile plan recorded (see PLAN_FETCH_FILENAME). */
|
|
15
9
|
filename?: string | null;
|
|
10
|
+
/** The coding run that recorded it; needed when rows of several runs are summarized together. */
|
|
11
|
+
runId?: string;
|
|
16
12
|
}
|
|
17
13
|
/** The `filename` a lockfile plan records its refusals under. */
|
|
18
14
|
export declare const PLAN_REFUSAL_FILENAME = "-/plan";
|
|
15
|
+
/**
|
|
16
|
+
* The `filename` of a refusal recorded only from an index listing: the
|
|
17
|
+
* registry answered a metadata request whose kept versions have no file the
|
|
18
|
+
* client may download (a PyPI project published only as an sdist, or whose
|
|
19
|
+
* every wheel is too new). The installer may only have looked at the name
|
|
20
|
+
* (a resolver exploring candidates, a dependency under a marker that does not
|
|
21
|
+
* apply), so such a refusal is weaker evidence than a refused download.
|
|
22
|
+
*
|
|
23
|
+
* The rule, the same for every ecosystem: a listing-only refusal is cleared
|
|
24
|
+
* like any refusal of a whole name, by any version of that name served after
|
|
25
|
+
* it in the same run (summarizeRegistryFetches). One left standing (nothing
|
|
26
|
+
* of that name was served) is marked `listingOnly`. It is reported to the
|
|
27
|
+
* agent (`wardby-packages`, GET /registry/-/refusals) and in get_run, and:
|
|
28
|
+
* - a coding sub-run failed: it counts toward ⛔ "Blocked by the package
|
|
29
|
+
* registry" and Slack `run_blocked` like any refusal (blockingRefusals in
|
|
30
|
+
* core/host-status.ts);
|
|
31
|
+
* - the run made no changes (and none failed): it is a ⚠️ line on the status,
|
|
32
|
+
* never ⛔ or `run_blocked` (a package whose only release has nothing but
|
|
33
|
+
* too-new wheels can be why);
|
|
34
|
+
* - a pull request resulted: it adds nothing, neither to the description's
|
|
35
|
+
* install warning and package section nor to the status (warningRefusals).
|
|
36
|
+
*/
|
|
37
|
+
export declare const LISTING_REFUSAL_FILENAME = "-/listing";
|
|
38
|
+
/** The refusals a pull request description or a not-blocked status warns
|
|
39
|
+
* about: all but listing-only ones (see LISTING_REFUSAL_FILENAME). */
|
|
40
|
+
export declare function warningRefusals<T extends {
|
|
41
|
+
listingOnly?: boolean;
|
|
42
|
+
}>(refusals: readonly T[]): T[];
|
|
19
43
|
/** Distinct refusals lockfile plans recorded (ecosystem + name + version +
|
|
20
44
|
* reason), however often the lockfile was planned. */
|
|
21
45
|
export declare function countPlanRefusals(rows: readonly RegistryFetchRow[]): number;
|
|
46
|
+
/** A served row's `reason` when serving it waived blocking advisories the
|
|
47
|
+
* run's packagePolicy.acceptedAdvisories lists: this prefix, then the
|
|
48
|
+
* waived advisory ids joined by ", ". */
|
|
49
|
+
export declare const ACCEPTED_ADVISORIES_PREFIX = "accepted advisories: ";
|
|
50
|
+
/** The served-row reason recording these waived advisory ids, or undefined
|
|
51
|
+
* when none were waived (the row then has no reason, as before). */
|
|
52
|
+
export declare function acceptedAdvisoriesReason(ids: readonly string[]): string | undefined;
|
|
53
|
+
/** The waived advisory ids a served row's reason records (empty for any
|
|
54
|
+
* other reason, or none). */
|
|
55
|
+
export declare function parseAcceptedAdvisories(reason: string | null | undefined): string[];
|
|
22
56
|
export interface RegistryPackageSummary {
|
|
23
57
|
ecosystem: string;
|
|
24
58
|
name: string;
|
|
25
59
|
version: string;
|
|
26
60
|
/** Bytes served (RegistryFetch.sizeBytes); null when not recorded. */
|
|
27
61
|
size: number | null;
|
|
62
|
+
/** Blocking advisories waived to serve it (the run's accepted set);
|
|
63
|
+
* absent when none were. */
|
|
64
|
+
acceptedAdvisories?: string[];
|
|
28
65
|
}
|
|
29
|
-
export
|
|
66
|
+
export type RegistryRefusalSummary = {
|
|
30
67
|
ecosystem: string;
|
|
31
68
|
name: string;
|
|
32
69
|
reason: string;
|
|
33
|
-
|
|
70
|
+
/** Static guidance for the code (shared with the proxy message and the PR banner); absent for unknown codes. */
|
|
71
|
+
hint?: string;
|
|
72
|
+
/** Every row behind it was an index listing's (LISTING_REFUSAL_FILENAME); absent otherwise. */
|
|
73
|
+
listingOnly?: true;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Rows must be in the order they were recorded (createdAt ascending): a
|
|
77
|
+
* refusal followed by a served row for the same package in the same run
|
|
78
|
+
* (`runId`; rows without one count as one run) no longer counts, since that
|
|
79
|
+
* run's install went on to get it (npm retrying a temporary
|
|
80
|
+
* `wardby_upstream_error`, an audit that was briefly unavailable). A package
|
|
81
|
+
* served to a sibling sub-run never clears another run's refusal. A refusal
|
|
82
|
+
* of one version is cleared only by that version being served; a refusal of
|
|
83
|
+
* the whole name (no version) by any version of it. A refusal recorded after
|
|
84
|
+
* the package was served still counts. Cleared refusals are left out of every
|
|
85
|
+
* surface (get_run, the PR warning, the status comments, Slack), because each
|
|
86
|
+
* of them tells the user the install did not finish, which is then untrue.
|
|
87
|
+
*
|
|
88
|
+
* Known limits: order is by record time, and a served row is recorded when
|
|
89
|
+
* its download finishes (no start time is stored), so a download that started
|
|
90
|
+
* before a concurrent refusal of the same version in the same run, and
|
|
91
|
+
* finished after it, clears it; rows in the same millisecond have no
|
|
92
|
+
* tiebreaker. Matching is per version, not per file, so serving any file of
|
|
93
|
+
* a PyPI release clears a refused file of that release (another platform's
|
|
94
|
+
* too-new wheel).
|
|
95
|
+
*/
|
|
34
96
|
export declare function summarizeRegistryFetches(rows: readonly RegistryFetchRow[]): {
|
|
35
97
|
packages: RegistryPackageSummary[];
|
|
36
98
|
packageRefusals: RegistryRefusalSummary[];
|
|
@@ -1,5 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one summary of a run's RegistryFetch rows, shared by get_run and the
|
|
3
|
+
* coding executor's PR-body report so both deduplicate identically: served
|
|
4
|
+
* packages collapse on ecosystem + name + version (a retried download is one
|
|
5
|
+
* package), refusals on ecosystem + name + reason.
|
|
6
|
+
*/
|
|
7
|
+
import { ACCEPTED_ADVISORY_ID } from "./allowlist.js";
|
|
8
|
+
import { refusalHint } from "./refusal-hints.js";
|
|
1
9
|
/** The `filename` a lockfile plan records its refusals under. */
|
|
2
10
|
export const PLAN_REFUSAL_FILENAME = "-/plan";
|
|
11
|
+
/**
|
|
12
|
+
* The `filename` of a refusal recorded only from an index listing: the
|
|
13
|
+
* registry answered a metadata request whose kept versions have no file the
|
|
14
|
+
* client may download (a PyPI project published only as an sdist, or whose
|
|
15
|
+
* every wheel is too new). The installer may only have looked at the name
|
|
16
|
+
* (a resolver exploring candidates, a dependency under a marker that does not
|
|
17
|
+
* apply), so such a refusal is weaker evidence than a refused download.
|
|
18
|
+
*
|
|
19
|
+
* The rule, the same for every ecosystem: a listing-only refusal is cleared
|
|
20
|
+
* like any refusal of a whole name, by any version of that name served after
|
|
21
|
+
* it in the same run (summarizeRegistryFetches). One left standing (nothing
|
|
22
|
+
* of that name was served) is marked `listingOnly`. It is reported to the
|
|
23
|
+
* agent (`wardby-packages`, GET /registry/-/refusals) and in get_run, and:
|
|
24
|
+
* - a coding sub-run failed: it counts toward ⛔ "Blocked by the package
|
|
25
|
+
* registry" and Slack `run_blocked` like any refusal (blockingRefusals in
|
|
26
|
+
* core/host-status.ts);
|
|
27
|
+
* - the run made no changes (and none failed): it is a ⚠️ line on the status,
|
|
28
|
+
* never ⛔ or `run_blocked` (a package whose only release has nothing but
|
|
29
|
+
* too-new wheels can be why);
|
|
30
|
+
* - a pull request resulted: it adds nothing, neither to the description's
|
|
31
|
+
* install warning and package section nor to the status (warningRefusals).
|
|
32
|
+
*/
|
|
33
|
+
export const LISTING_REFUSAL_FILENAME = "-/listing";
|
|
34
|
+
/** The refusals a pull request description or a not-blocked status warns
|
|
35
|
+
* about: all but listing-only ones (see LISTING_REFUSAL_FILENAME). */
|
|
36
|
+
export function warningRefusals(refusals) {
|
|
37
|
+
return refusals.filter((refusal) => refusal.listingOnly !== true);
|
|
38
|
+
}
|
|
3
39
|
/** Distinct refusals lockfile plans recorded (ecosystem + name + version +
|
|
4
40
|
* reason), however often the lockfile was planned. */
|
|
5
41
|
export function countPlanRefusals(rows) {
|
|
@@ -11,21 +47,92 @@ export function countPlanRefusals(rows) {
|
|
|
11
47
|
}
|
|
12
48
|
return keys.size;
|
|
13
49
|
}
|
|
50
|
+
/** A served row's `reason` when serving it waived blocking advisories the
|
|
51
|
+
* run's packagePolicy.acceptedAdvisories lists: this prefix, then the
|
|
52
|
+
* waived advisory ids joined by ", ". */
|
|
53
|
+
export const ACCEPTED_ADVISORIES_PREFIX = "accepted advisories: ";
|
|
54
|
+
/** The served-row reason recording these waived advisory ids, or undefined
|
|
55
|
+
* when none were waived (the row then has no reason, as before). */
|
|
56
|
+
export function acceptedAdvisoriesReason(ids) {
|
|
57
|
+
return ids.length > 0 ? `${ACCEPTED_ADVISORIES_PREFIX}${ids.join(", ")}` : undefined;
|
|
58
|
+
}
|
|
59
|
+
/** The waived advisory ids a served row's reason records (empty for any
|
|
60
|
+
* other reason, or none). */
|
|
61
|
+
export function parseAcceptedAdvisories(reason) {
|
|
62
|
+
if (!reason?.startsWith(ACCEPTED_ADVISORIES_PREFIX))
|
|
63
|
+
return [];
|
|
64
|
+
return reason
|
|
65
|
+
.slice(ACCEPTED_ADVISORIES_PREFIX.length)
|
|
66
|
+
.split(",")
|
|
67
|
+
.map((id) => id.trim())
|
|
68
|
+
.filter((id) => ACCEPTED_ADVISORY_ID.test(id));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Rows must be in the order they were recorded (createdAt ascending): a
|
|
72
|
+
* refusal followed by a served row for the same package in the same run
|
|
73
|
+
* (`runId`; rows without one count as one run) no longer counts, since that
|
|
74
|
+
* run's install went on to get it (npm retrying a temporary
|
|
75
|
+
* `wardby_upstream_error`, an audit that was briefly unavailable). A package
|
|
76
|
+
* served to a sibling sub-run never clears another run's refusal. A refusal
|
|
77
|
+
* of one version is cleared only by that version being served; a refusal of
|
|
78
|
+
* the whole name (no version) by any version of it. A refusal recorded after
|
|
79
|
+
* the package was served still counts. Cleared refusals are left out of every
|
|
80
|
+
* surface (get_run, the PR warning, the status comments, Slack), because each
|
|
81
|
+
* of them tells the user the install did not finish, which is then untrue.
|
|
82
|
+
*
|
|
83
|
+
* Known limits: order is by record time, and a served row is recorded when
|
|
84
|
+
* its download finishes (no start time is stored), so a download that started
|
|
85
|
+
* before a concurrent refusal of the same version in the same run, and
|
|
86
|
+
* finished after it, clears it; rows in the same millisecond have no
|
|
87
|
+
* tiebreaker. Matching is per version, not per file, so serving any file of
|
|
88
|
+
* a PyPI release clears a refused file of that release (another platform's
|
|
89
|
+
* too-new wheel).
|
|
90
|
+
*/
|
|
14
91
|
export function summarizeRegistryFetches(rows) {
|
|
15
92
|
const packages = new Map();
|
|
93
|
+
// Each refusal with the run + version pairs it still stands for (version "" = the whole name).
|
|
16
94
|
const refusals = new Map();
|
|
17
95
|
for (const row of rows) {
|
|
18
96
|
if (row.outcome === "served" && row.version) {
|
|
19
97
|
const key = `${row.ecosystem}\0${row.name}\0${row.version}`;
|
|
20
|
-
|
|
21
|
-
|
|
98
|
+
let summary = packages.get(key);
|
|
99
|
+
if (!summary) {
|
|
100
|
+
summary = { ecosystem: row.ecosystem, name: row.name, version: row.version, size: row.sizeBytes };
|
|
101
|
+
packages.set(key, summary);
|
|
102
|
+
}
|
|
103
|
+
// Every file of the version (a retry, a wheel and its metadata) may
|
|
104
|
+
// record the waiver; the package lists each advisory once.
|
|
105
|
+
const accepted = parseAcceptedAdvisories(row.reason);
|
|
106
|
+
if (accepted.length > 0)
|
|
107
|
+
summary.acceptedAdvisories = [...new Set([...(summary.acceptedAdvisories ?? []), ...accepted])];
|
|
108
|
+
for (const [refusalKey, refusal] of refusals) {
|
|
109
|
+
if (refusal.summary.ecosystem !== row.ecosystem || refusal.summary.name !== row.name)
|
|
110
|
+
continue;
|
|
111
|
+
refusal.versions.delete(`${row.runId ?? ""}\0`);
|
|
112
|
+
refusal.versions.delete(`${row.runId ?? ""}\0${row.version}`);
|
|
113
|
+
if (refusal.versions.size === 0)
|
|
114
|
+
refusals.delete(refusalKey);
|
|
115
|
+
}
|
|
22
116
|
}
|
|
23
117
|
else if (row.outcome === "refused") {
|
|
24
118
|
const reason = row.reason ?? "refused";
|
|
25
119
|
const key = `${row.ecosystem}\0${row.name}\0${reason}`;
|
|
26
|
-
|
|
27
|
-
|
|
120
|
+
let refusal = refusals.get(key);
|
|
121
|
+
if (!refusal) {
|
|
122
|
+
const hint = refusalHint(reason);
|
|
123
|
+
refusal = {
|
|
124
|
+
summary: { ecosystem: row.ecosystem, name: row.name, reason, ...(hint ? { hint } : {}) },
|
|
125
|
+
versions: new Set(),
|
|
126
|
+
listing: true,
|
|
127
|
+
};
|
|
128
|
+
refusals.set(key, refusal);
|
|
129
|
+
}
|
|
130
|
+
refusal.versions.add(`${row.runId ?? ""}\0${row.version ?? ""}`);
|
|
131
|
+
refusal.listing &&= row.filename === LISTING_REFUSAL_FILENAME;
|
|
28
132
|
}
|
|
29
133
|
}
|
|
30
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
packages: [...packages.values()],
|
|
136
|
+
packageRefusals: [...refusals.values()].map(({ summary, listing }) => listing ? { ...summary, listingOnly: true } : summary),
|
|
137
|
+
};
|
|
31
138
|
}
|