@wardby/cli 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +33 -0
- package/README.md +25 -14
- package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/context.d.ts +22 -0
- package/dist/claude-coding-worker/context.js +92 -0
- package/dist/claude-coding-worker/driver.d.ts +27 -3
- package/dist/claude-coding-worker/driver.js +161 -20
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +25 -5
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +6 -2
- package/dist/cli-native-mode.d.ts +9 -0
- package/dist/cli-native-mode.js +20 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +161 -11
- package/dist/coding/claude-context.d.ts +45 -0
- package/dist/coding/claude-context.js +434 -0
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +50 -118
- package/dist/coding/profile.js +51 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +292 -270
- package/dist/coding/protocol.js +322 -15
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +12 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +51 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +16 -3
- package/dist/coding-worker/artifact.js +5 -3
- package/dist/coding-worker/debug-trace.d.ts +6 -5
- package/dist/coding-worker/debug-trace.js +13 -18
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +118 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/sdk.d.ts +6 -0
- package/dist/coding-worker/sdk.js +1 -0
- package/dist/coding-worker/skills.d.ts +23 -0
- package/dist/coding-worker/skills.js +117 -0
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +13 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/github-allowed-accounts.d.ts +15 -0
- package/dist/config/github-allowed-accounts.js +36 -0
- package/dist/config/low-power.d.ts +12 -0
- package/dist/config/low-power.js +18 -0
- package/dist/config/mobile.d.ts +12 -0
- package/dist/config/mobile.js +18 -0
- package/dist/config/providers.d.ts +65 -3
- package/dist/config/providers.js +147 -11
- package/dist/config/test-simulators.d.ts +82 -0
- package/dist/config/test-simulators.js +305 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/db.d.ts +16 -6
- package/dist/core/db.js +12 -10
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/dispatch.d.ts +53 -0
- package/dist/core/dispatch.js +167 -57
- package/dist/core/engine-native.js +10 -6
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +168 -0
- package/dist/core/grants.d.ts +19 -6
- package/dist/core/grants.js +55 -6
- package/dist/core/host-events.d.ts +67 -5
- package/dist/core/host-events.js +571 -37
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +298 -20
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +151 -20
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +16 -11
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +68 -29
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +130 -0
- package/dist/core/merge-order-check.js +343 -0
- package/dist/core/notification-dispatcher.d.ts +70 -0
- package/dist/core/notification-dispatcher.js +368 -0
- package/dist/core/notification-templates.d.ts +19 -0
- package/dist/core/notification-templates.js +139 -0
- package/dist/core/notifications.d.ts +10 -0
- package/dist/core/notifications.js +38 -0
- package/dist/core/pg-auth.d.ts +22 -0
- package/dist/core/pg-auth.js +152 -0
- package/dist/core/power-composition.d.ts +16 -0
- package/dist/core/power-composition.js +37 -0
- package/dist/core/power-idle.d.ts +14 -0
- package/dist/core/power-idle.js +30 -0
- package/dist/core/power.d.ts +71 -0
- package/dist/core/power.js +202 -0
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +8 -5
- package/dist/core/reconciler.js +37 -6
- package/dist/core/related-pull-requests.d.ts +59 -7
- package/dist/core/related-pull-requests.js +140 -25
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/repo-access.d.ts +15 -0
- package/dist/core/repo-access.js +66 -0
- package/dist/core/review-fix.d.ts +38 -1
- package/dist/core/review-fix.js +209 -7
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +23 -0
- package/dist/core/review-host-tools.js +156 -14
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +384 -5
- package/dist/core/runner.js +1244 -372
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +39 -0
- package/dist/core/secret-broker-config.js +112 -0
- package/dist/core/secret-guard.d.ts +46 -0
- package/dist/core/secret-guard.js +104 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +148 -40
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-event-recorder.d.ts +14 -0
- package/dist/core/workflow-event-recorder.js +120 -0
- package/dist/core/workflow-events.d.ts +94 -0
- package/dist/core/workflow-events.js +48 -0
- package/dist/core/workflow-run-events.d.ts +18 -0
- package/dist/core/workflow-run-events.js +75 -0
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +92 -3
- package/dist/generated/prisma/client.d.ts +92 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +364 -54
- package/dist/generated/prisma/enums.d.ts +37 -0
- package/dist/generated/prisma/enums.js +31 -0
- package/dist/generated/prisma/internal/class.d.ts +132 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1332 -142
- package/dist/generated/prisma/internal/prismaNamespace.js +227 -5
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +234 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +227 -5
- package/dist/generated/prisma/models/Agent.d.ts +464 -7
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +301 -1
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +1841 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1713 -0
- package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1551 -0
- package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
- package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
- package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
- package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
- package/dist/generated/prisma/models/NotificationChannel.js +1 -0
- package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
- package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
- package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
- package/dist/generated/prisma/models/NotificationThread.js +1 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +1619 -58
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +37 -1
- package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
- package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
- package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
- package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
- package/dist/generated/prisma/models.d.ts +12 -0
- package/dist/help/build.js +4 -2
- package/dist/help/catalog.d.ts +1 -1
- package/dist/help/catalog.js +31 -6
- package/dist/help-index.json +2584 -146
- package/dist/import/create.js +49 -3
- package/dist/import/neutral-schema.d.ts +50 -277
- package/dist/import/neutral-schema.js +7 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +11 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/repo-authorization.js +14 -0
- package/dist/mcp/auth/resource-server.d.ts +2 -1
- package/dist/mcp/auth/resource-server.js +7 -2
- package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/browser.js +19 -6
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/host-events/github-ingress.d.ts +25 -1
- package/dist/mcp/host-events/github-ingress.js +70 -1
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.js +117 -20
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +277 -24
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +9 -3
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.d.ts +2 -0
- package/dist/mcp/tools/notification-channels.js +293 -0
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/power.d.ts +2 -0
- package/dist/mcp/tools/power.js +77 -0
- package/dist/mcp/tools/repositories.js +18 -5
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.d.ts +0 -6
- package/dist/mcp/tools/runs.js +41 -14
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
- package/dist/mcp/tools/secret-broker-compat.js +23 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
- package/dist/mcp/tools/secret-elicitation-form.js +205 -10
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -54
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
- package/dist/mcp/tools/secret-elicitation.js +110 -12
- package/dist/mcp/tools/secrets.js +186 -9
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +2 -2
- package/dist/mcp/tools/tools.js +47 -11
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/native-worker/composition.d.ts +23 -0
- package/dist/native-worker/composition.js +77 -0
- package/dist/native-worker/docker-isolation.d.ts +65 -0
- package/dist/native-worker/docker-isolation.js +183 -0
- package/dist/native-worker/docker-launcher.d.ts +110 -0
- package/dist/native-worker/docker-launcher.js +297 -0
- package/dist/native-worker/gateway-server.d.ts +77 -0
- package/dist/native-worker/gateway-server.js +412 -0
- package/dist/native-worker/gateway.d.ts +70 -0
- package/dist/native-worker/gateway.js +263 -0
- package/dist/native-worker/http-server.d.ts +24 -0
- package/dist/native-worker/http-server.js +113 -0
- package/dist/native-worker/http-transport.d.ts +17 -0
- package/dist/native-worker/http-transport.js +88 -0
- package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
- package/dist/native-worker/kubernetes-isolation.js +218 -0
- package/dist/native-worker/kubernetes-launcher.d.ts +106 -0
- package/dist/native-worker/kubernetes-launcher.js +375 -0
- package/dist/native-worker/launch.d.ts +62 -0
- package/dist/native-worker/launch.js +49 -0
- package/dist/native-worker/ledger.d.ts +125 -0
- package/dist/native-worker/ledger.js +245 -0
- package/dist/native-worker/loopback.d.ts +12 -0
- package/dist/native-worker/loopback.js +44 -0
- package/dist/native-worker/main.d.ts +8 -0
- package/dist/native-worker/main.js +85 -0
- package/dist/native-worker/protocol.d.ts +296 -0
- package/dist/native-worker/protocol.js +246 -0
- package/dist/native-worker/sandbox-executor.d.ts +83 -0
- package/dist/native-worker/sandbox-executor.js +284 -0
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +29 -0
- package/dist/native-worker/stdio.js +189 -0
- package/dist/native-worker/warm-delivery.d.ts +16 -0
- package/dist/native-worker/warm-delivery.js +33 -0
- package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
- package/dist/native-worker/warm-pool-ledger.js +73 -0
- package/dist/native-worker/warm-pool.d.ts +107 -0
- package/dist/native-worker/warm-pool.js +283 -0
- package/dist/native-worker/worker.d.ts +17 -0
- package/dist/native-worker/worker.js +141 -0
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/authorization-server.d.ts +3 -0
- package/dist/providers/auth/index.js +2 -0
- package/dist/providers/auth/self-hosted.d.ts +14 -2
- package/dist/providers/auth/self-hosted.js +54 -4
- package/dist/providers/chat/fake.d.ts +46 -0
- package/dist/providers/chat/fake.js +39 -0
- package/dist/providers/chat/index.d.ts +4 -0
- package/dist/providers/chat/index.js +8 -0
- package/dist/providers/chat/slack.d.ts +37 -0
- package/dist/providers/chat/slack.js +105 -0
- package/dist/providers/chat/types.d.ts +42 -0
- package/dist/providers/chat/types.js +19 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/memory-ledger.js +84 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
- package/dist/providers/coding-proxy/proxy.d.ts +14 -1
- package/dist/providers/coding-proxy/proxy.js +166 -14
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +4 -0
- package/dist/providers/coding-proxy/runtime.js +78 -16
- package/dist/providers/coding-proxy/server.d.ts +6 -1
- package/dist/providers/coding-proxy/server.js +45 -1
- package/dist/providers/coding-proxy/types.d.ts +65 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +36 -2
- package/dist/providers/engine/types.js +39 -0
- package/dist/providers/executor/composition.d.ts +31 -1
- package/dist/providers/executor/composition.js +81 -8
- package/dist/providers/executor/container.d.ts +385 -9
- package/dist/providers/executor/container.js +1234 -99
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +91 -0
- package/dist/providers/executor/dbos.js +319 -26
- package/dist/providers/executor/deferred.d.ts +44 -0
- package/dist/providers/executor/deferred.js +100 -0
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +39 -10
- package/dist/providers/executor/routing.js +77 -32
- package/dist/providers/executor/types.d.ts +25 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +8 -0
- package/dist/providers/jobs/docker-isolation.js +17 -0
- package/dist/providers/jobs/docker.d.ts +1 -0
- package/dist/providers/jobs/docker.js +29 -4
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +23 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +45 -1
- package/dist/providers/jobs/kubernetes-api.d.ts +25 -1
- package/dist/providers/jobs/kubernetes-api.js +17 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
- package/dist/providers/jobs/kubernetes-client.js +61 -3
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
- package/dist/providers/jobs/kubernetes-isolation.js +23 -1
- package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
- package/dist/providers/jobs/kubernetes-platform.js +38 -1
- package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
- package/dist/providers/jobs/kubernetes-preflight.js +4 -2
- package/dist/providers/jobs/kubernetes.d.ts +42 -2
- package/dist/providers/jobs/kubernetes.js +178 -19
- package/dist/providers/jobs/metadata-probes.d.ts +11 -0
- package/dist/providers/jobs/metadata-probes.js +13 -0
- package/dist/providers/jobs/types.d.ts +42 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +39 -2
- package/dist/providers/review-host/github.js +274 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +157 -2
- package/dist/providers/review-host/types.js +8 -0
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +33 -2
- package/dist/providers/vcs/git.js +324 -22
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +246 -12
- package/dist/providers/vcs/github.js +521 -61
- package/dist/providers/vcs/index.js +8 -1
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +33 -1
- package/dist/providers/vcs/routing.d.ts +4 -2
- package/dist/providers/vcs/routing.js +6 -0
- package/dist/providers/vcs/types.d.ts +80 -3
- package/dist/quickstart/coding-seed.d.ts +21 -3
- package/dist/quickstart/coding-seed.js +283 -3
- package/dist/quickstart/images.d.ts +2 -0
- package/dist/quickstart/images.js +7 -1
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/brokered-fetch.d.ts +23 -0
- package/dist/sandbox/brokered-fetch.js +37 -0
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +35 -0
- package/dist/sandbox/host-functions.js +150 -91
- package/dist/sandbox/prelude.js +7 -1
- package/dist/sandbox/run-in-sandbox.d.ts +19 -0
- package/dist/sandbox/run-in-sandbox.js +28 -10
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +3 -1
- package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
- package/dist/sandbox/secret-broker-sigv4.js +49 -0
- package/dist/sandbox/secret-broker.d.ts +34 -0
- package/dist/sandbox/secret-broker.js +161 -0
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/user-tool.d.ts +14 -0
- package/dist/sandbox/user-tool.js +32 -0
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +25 -0
- package/dist/viewer/api-schema.d.ts +372 -2454
- package/dist/viewer/api-schema.js +29 -0
- package/dist/viewer/build-schemas.js +10 -3
- package/dist/viewer/event-bus.d.ts +6 -0
- package/dist/viewer/event-bus.js +16 -7
- package/dist/viewer/graph.d.ts +5 -2
- package/dist/viewer/graph.js +7 -2
- package/dist/viewer/http.d.ts +0 -1
- package/dist/viewer/http.js +2 -2
- package/dist/viewer/infra.js +25 -2
- package/dist/viewer/limits.d.ts +4 -0
- package/dist/viewer/limits.js +4 -0
- package/dist/viewer/openapi.d.ts +15 -0
- package/dist/viewer/openapi.js +170 -0
- package/dist/wardby-bin.js +10 -0
- package/docs/README.md +7 -0
- package/docs/agent-recipes.md +114 -27
- package/docs/architecture-runtime.md +16 -1
- package/docs/code-review-agents.md +689 -45
- package/docs/coding-agent-setup.md +602 -12
- package/docs/coding-packages.md +1037 -92
- package/docs/coding-services.md +30 -13
- package/docs/coding-worker-byo-images.md +218 -6
- package/docs/coding-worker-isolation.md +173 -44
- package/docs/getting-started-gke.md +332 -129
- package/docs/getting-started-identity-provider.md +31 -11
- package/docs/getting-started.md +14 -4
- package/docs/jira-agents.md +22 -9
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/native-sandbox.md +731 -0
- package/docs/observability.md +4 -0
- package/docs/release-verification.md +9 -0
- package/docs/security-deployment.md +146 -14
- package/docs/slack-notifications.md +268 -0
- package/docs/tool-secrets.md +284 -0
- package/docs/viewer-api.md +46 -5
- package/docs/viewer-api.openapi.yml +1404 -0
- package/help/admin-viewer.md +29 -3
- package/help/agent-recipes.md +27 -0
- package/help/architecture-agent.md +11 -6
- package/help/brokered-secrets.md +79 -0
- package/help/build-claude-code-image.md +131 -0
- package/help/build-worker-image.md +6 -7
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +243 -10
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +23 -9
- package/help/deployment-targets.md +25 -14
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-provider-not-configured.md +2 -2
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/database-auth-invalid.md +28 -0
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +29 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/low-power-disabled.md +29 -0
- package/help/errors/low-power-scale-forbidden.md +35 -0
- package/help/errors/low-power-wake-timeout.md +30 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/mobile-disabled.md +31 -0
- package/help/errors/native-sandbox-capacity.md +22 -0
- package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
- package/help/errors/native-sandbox-docker-failed.md +20 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
- package/help/errors/native-sandbox-image-not-pinned.md +19 -0
- package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-network-unenforced.md +22 -0
- package/help/errors/native-sandbox-requires-catalog.md +31 -0
- package/help/errors/native-sandbox-unavailable.md +19 -0
- package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +21 -0
- package/help/errors/native-sandbox-worker-lost.md +20 -0
- package/help/errors/native-sandbox-worker-unready.md +22 -0
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-broker-body-invalid.md +26 -0
- package/help/errors/secret-broker-changed.md +20 -0
- package/help/errors/secret-broker-config-invalid.md +30 -0
- package/help/errors/secret-broker-conflict.md +27 -0
- package/help/errors/secret-broker-destination-denied.md +28 -0
- package/help/errors/secret-broker-value-invalid.md +28 -0
- package/help/errors/secret-brokered.md +24 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/secret-not-brokered.md +25 -0
- package/help/errors/slack-auth-failed.md +48 -0
- package/help/errors/slack-channel-unreachable.md +39 -0
- package/help/errors/slack-not-configured.md +33 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/getting-started.md +4 -5
- package/help/github-app-setup.md +132 -0
- package/help/github.md +40 -1
- package/help/identity-and-access.md +7 -1
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +3 -4
- package/help/low-power-mode.md +131 -0
- package/help/merge-order-check.md +122 -0
- package/help/mobile-app.md +54 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +150 -0
- package/help/observability.md +8 -0
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +80 -16
- package/help/repo-instructions-and-skills.md +91 -0
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +43 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +76 -0
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +99 -7
- package/package.json +27 -13
- package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
- package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
- package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
- package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
- package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
- package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
- package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
- package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
- package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
- package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
- package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
- package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
- package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
- package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/schema.prisma +631 -146
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createPrivateKey } from "node:crypto";
|
|
2
2
|
import { importPKCS8, SignJWT } from "jose";
|
|
3
|
-
import {
|
|
3
|
+
import { isGitHubLogin } from "../../config/github-allowed-accounts.js";
|
|
4
|
+
import { testSimulatorBase } from "../../config/test-simulators.js";
|
|
5
|
+
import { ACCEPTED_ADVISORY_ID } from "../../coding/registry/allowlist.js";
|
|
6
|
+
import { packageAuditRows, packageAuditWording, summarizePackageAudit, } from "../../coding/registry/audit-mode.js";
|
|
7
|
+
import { refusalMeaning } from "../../coding/registry/refusal-hints.js";
|
|
8
|
+
import { DEPENDENCY_CACHE_PATH, normalizeGitHubRepository, normalizeGitRef, truncateText, } from "../../coding/protocol.js";
|
|
9
|
+
import { LATEST_RUN_MARKER_PREFIX, RELATED_SECTION_END, RELATED_SECTION_START, assessFinalMessage, descriptionAppliedComment, descriptionFallbackComment, descriptionSha256, extractRelatedBlock, findDescriptionComments, hasClosingKeyword, isUneditedDescription, supersededDescriptionComment, } from "./pr-description.js";
|
|
10
|
+
export { RELATED_SECTION_END, RELATED_SECTION_START };
|
|
4
11
|
const DEFAULT_API_BASE_URL = "https://api.github.com";
|
|
5
12
|
const DEFAULT_API_VERSION = "2026-03-10";
|
|
6
13
|
const RUN_MARKER_PREFIX = "<!-- wardby:";
|
|
@@ -39,6 +46,8 @@ export function isSafeGitHubInstallationToken(value) {
|
|
|
39
46
|
* executor validates with the shared one.
|
|
40
47
|
*/
|
|
41
48
|
export const PR_ISSUE_KEY = /^[A-Z][A-Z0-9_]{0,254}-[1-9]\d{0,9}$/;
|
|
49
|
+
/** Same shape as GITHUB_ISSUE_KEY (issue-tracker/types.ts), duplicated likewise. Only the Related section shows one. */
|
|
50
|
+
export const PR_GITHUB_ISSUE_KEY = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?\/[a-z0-9._-]{1,100}#[1-9]\d{0,9}$/;
|
|
42
51
|
/** The issue key when it is well-formed; a malformed one is dropped everywhere it would be rendered. */
|
|
43
52
|
function validIssueKey(input) {
|
|
44
53
|
const key = input.issue?.key;
|
|
@@ -49,22 +58,29 @@ export function pullRequestTitle(input) {
|
|
|
49
58
|
const key = validIssueKey(input);
|
|
50
59
|
return key ? `[${key}] ${base}` : base;
|
|
51
60
|
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Validated issue parts; the tracker prefix is "" or "<Name> ". A GitHub issue
|
|
63
|
+
* key is accepted only with `orGitHub` (the Related section): the title and
|
|
64
|
+
* the body's issue line stay tracker-only.
|
|
65
|
+
*/
|
|
66
|
+
function issueParts(issue, orGitHub = false) {
|
|
54
67
|
const key = issue?.key;
|
|
55
|
-
if (typeof key !== "string" || !PR_ISSUE_KEY.test(key))
|
|
68
|
+
if (typeof key !== "string" || !(PR_ISSUE_KEY.test(key) || (orGitHub && PR_GITHUB_ISSUE_KEY.test(key)))) {
|
|
56
69
|
return undefined;
|
|
70
|
+
}
|
|
57
71
|
const url = issue?.url;
|
|
58
72
|
const safeUrl = typeof url === "string" && url.startsWith("https://") && !/[\s<>()]/.test(url) ? url : undefined;
|
|
59
73
|
const name = issue?.trackerName;
|
|
60
74
|
const tracker = typeof name === "string" && /^[A-Za-z][A-Za-z0-9 .-]{0,39}$/.test(name) ? `${name.trim()} ` : "";
|
|
61
75
|
return { key, ...(safeUrl ? { url: safeUrl } : {}), tracker };
|
|
62
76
|
}
|
|
77
|
+
/** The body's issue line: a tracker issue's link, else the same-repository issue the run came from. */
|
|
63
78
|
function issueLine(input) {
|
|
64
79
|
const parts = issueParts(input.issue);
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
67
|
-
|
|
80
|
+
if (parts?.url)
|
|
81
|
+
return `Resolves ${parts.tracker}issue [${parts.key}](${parts.url})`;
|
|
82
|
+
const number = input.issueNumber;
|
|
83
|
+
return typeof number === "number" && Number.isSafeInteger(number) && number > 0 ? `From issue #${number}` : undefined;
|
|
68
84
|
}
|
|
69
85
|
/**
|
|
70
86
|
* Package names/versions/reasons come from registry metadata, not from
|
|
@@ -77,8 +93,17 @@ function issueLine(input) {
|
|
|
77
93
|
function isSafeMarkdownFragment(value) {
|
|
78
94
|
return !value.includes("`") && !value.includes("\n") && !value.includes("\r");
|
|
79
95
|
}
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
/** Whether a comment was written by this GitHub App (its bot user, through this App's id). */
|
|
97
|
+
function isThisAppsComment(comment, appId) {
|
|
98
|
+
const user = comment.user;
|
|
99
|
+
const app = comment.performed_via_github_app;
|
|
100
|
+
return (!!user &&
|
|
101
|
+
typeof user === "object" &&
|
|
102
|
+
user.type === "Bot" &&
|
|
103
|
+
!!app &&
|
|
104
|
+
typeof app === "object" &&
|
|
105
|
+
app.id === appId);
|
|
106
|
+
}
|
|
82
107
|
/** At most this many pull requests are listed; the rest are counted. */
|
|
83
108
|
export const MAX_RELATED_PULL_REQUESTS = 20;
|
|
84
109
|
/** GitHub rejects a pull request body longer than this. */
|
|
@@ -110,43 +135,82 @@ const isDone = (entry) => entry.state === "merged" || entry.state === "closed";
|
|
|
110
135
|
/**
|
|
111
136
|
* The "Related pull requests" block, markers included, built only from
|
|
112
137
|
* stored rows (never agent text). Open (and draft, and not-yet-known) pull
|
|
113
|
-
* requests form the
|
|
114
|
-
*
|
|
115
|
-
*
|
|
138
|
+
* requests form the merge order; merged and closed ones follow as context.
|
|
139
|
+
* Undefined when no other valid pull request is left after validation: a
|
|
140
|
+
* set of one is not a set.
|
|
141
|
+
*
|
|
142
|
+
* When any valid entry (open, merged or closed) carries a `mergeOrder` (set
|
|
143
|
+
* by the delegating agent), steps are counted over all of them, so a
|
|
144
|
+
* label never changes as pull requests merge: the open entries that have
|
|
145
|
+
* one are numbered under "Merge order" and labelled with their step (equal
|
|
146
|
+
* `mergeOrder` values share a step label), any open entry without one is
|
|
147
|
+
* listed after under "Not ordered:", and a merged or closed entry that has
|
|
148
|
+
* one carries its step label in the context list too. Otherwise every open
|
|
149
|
+
* entry is numbered in the unordered "Suggested merge order" list.
|
|
116
150
|
*/
|
|
117
151
|
export function renderRelatedSection(input) {
|
|
118
152
|
const valid = input.entries.filter((entry) => relatedLine(entry, "-") !== undefined);
|
|
119
153
|
if (!valid.some((entry) => !entry.self))
|
|
120
154
|
return undefined;
|
|
121
|
-
const issue = issueParts(input.issue);
|
|
155
|
+
const issue = issueParts(input.issue, true);
|
|
122
156
|
const issueText = issue ? ` for ${issue.tracker}issue ${issue.url ? `[${issue.key}](${issue.url})` : issue.key}` : "";
|
|
123
157
|
const open = valid.filter((entry) => !isDone(entry)).slice(0, MAX_RELATED_PULL_REQUESTS);
|
|
124
158
|
const done = valid.filter(isDone).slice(0, MAX_RELATED_PULL_REQUESTS - open.length);
|
|
125
159
|
const more = valid.length - open.length - done.length;
|
|
160
|
+
const isNumber = (value) => Number.isSafeInteger(value);
|
|
161
|
+
// Over every valid entry, done ones included, so "step 2 of 3" stays the
|
|
162
|
+
// same label after step 1 merges.
|
|
163
|
+
const steps = [...new Set(valid.map((entry) => entry.mergeOrder).filter(isNumber))].sort((a, b) => a - b);
|
|
164
|
+
const stepLabel = (entry) => isNumber(entry.mergeOrder) ? ` — step ${steps.indexOf(entry.mergeOrder) + 1} of ${steps.length}` : "";
|
|
165
|
+
// Stable-sorted by mergeOrder so numbering and step labels are correct
|
|
166
|
+
// regardless of the caller's input order; unordered entries keep input order.
|
|
167
|
+
const ordered = steps.length > 0
|
|
168
|
+
? open.filter((entry) => isNumber(entry.mergeOrder)).sort((a, b) => a.mergeOrder - b.mergeOrder)
|
|
169
|
+
: [];
|
|
170
|
+
const unordered = steps.length > 0 ? open.filter((entry) => !isNumber(entry.mergeOrder)) : [];
|
|
126
171
|
return [
|
|
127
172
|
RELATED_SECTION_START,
|
|
128
173
|
"**Related pull requests**",
|
|
129
174
|
"",
|
|
130
175
|
`Wardby opened these pull requests for the same request${issueText}.`,
|
|
131
|
-
...(
|
|
176
|
+
...(steps.length > 0
|
|
177
|
+
? [
|
|
178
|
+
...(ordered.length > 0
|
|
179
|
+
? [
|
|
180
|
+
"",
|
|
181
|
+
"Merge order (set by the delegating agent; equal steps can merge in either order):",
|
|
182
|
+
"",
|
|
183
|
+
...ordered.map((entry, index) => `${relatedLine(entry, `${index + 1}.`)}${stepLabel(entry)}`),
|
|
184
|
+
]
|
|
185
|
+
: []),
|
|
186
|
+
...(unordered.length > 0
|
|
187
|
+
? ["", "Not ordered:", "", ...unordered.map((entry) => relatedLine(entry, "-"))]
|
|
188
|
+
: []),
|
|
189
|
+
]
|
|
190
|
+
: open.length > 0
|
|
191
|
+
? [
|
|
192
|
+
"",
|
|
193
|
+
"Suggested merge order (the order Wardby's agent opened them in; not a guarantee, so check dependencies before merging):",
|
|
194
|
+
"",
|
|
195
|
+
...open.map((entry, index) => relatedLine(entry, `${index + 1}.`)),
|
|
196
|
+
]
|
|
197
|
+
: []),
|
|
198
|
+
...(done.length > 0
|
|
132
199
|
? [
|
|
133
200
|
"",
|
|
134
|
-
"
|
|
201
|
+
"Already merged or closed (context only):",
|
|
135
202
|
"",
|
|
136
|
-
...
|
|
203
|
+
...done.map((entry) => `${relatedLine(entry, "-")}${stepLabel(entry)}`),
|
|
137
204
|
]
|
|
138
205
|
: []),
|
|
139
|
-
...(done.length > 0
|
|
140
|
-
? ["", "Already merged or closed (context only):", "", ...done.map((entry) => relatedLine(entry, "-"))]
|
|
141
|
-
: []),
|
|
142
206
|
...(more > 0 ? ["", `…and ${more} more`] : []),
|
|
143
207
|
"",
|
|
144
208
|
"<sub>Written by Wardby from its run records; this section is replaced when the request's runs finish.</sub>",
|
|
145
209
|
RELATED_SECTION_END,
|
|
146
210
|
].join("\n");
|
|
147
211
|
}
|
|
148
|
-
/** The run marker, then optionally blank lines and the "Resolves …"
|
|
149
|
-
const BODY_HEAD = /^[^\S\n]*<!-- (?:wardby|reevo-run):\S+ -->[^\S\n]*(?:\n|$)(?:[^\S\n]*\n)*(?:Resolves [^\n]*(?:\n|$))?/;
|
|
212
|
+
/** The run marker, then optionally blank lines and the issue line ("Resolves … issue …" or "From issue #N"): the section goes right after. */
|
|
213
|
+
const BODY_HEAD = /^[^\S\n]*<!-- (?:wardby|reevo-run):\S+ -->[^\S\n]*(?:\n|$)(?:[^\S\n]*\n)*(?:(?:Resolves |From issue #\d)[^\n]*(?:\n|$))?/;
|
|
150
214
|
/**
|
|
151
215
|
* The body with `block` in place of the existing marked section, or inserted
|
|
152
216
|
* after the run marker (and the issue line) when there is none. Null — do not
|
|
@@ -183,13 +247,16 @@ export const MAX_PR_PACKAGE_LINES = 100;
|
|
|
183
247
|
/** A hard ceiling on each list's rendered size, whatever the entries
|
|
184
248
|
* contain, so the section can never push the body past GitHub's limit. */
|
|
185
249
|
const MAX_PR_PACKAGE_LIST_CHARS = 16 * 1024;
|
|
186
|
-
/**
|
|
187
|
-
*
|
|
188
|
-
|
|
250
|
+
/** The accepted-advisories list's ceiling: smaller, so adding it keeps the
|
|
251
|
+
* whole section's worst case well inside the body limit. */
|
|
252
|
+
const MAX_PR_WAIVER_LIST_CHARS = 8 * 1024;
|
|
253
|
+
/** Renders at most MAX_PR_PACKAGE_LINES lines (and at most `maxChars`
|
|
254
|
+
* characters) of `lines`, then a count of the rest. */
|
|
255
|
+
function cappedList(lines, maxChars = MAX_PR_PACKAGE_LIST_CHARS) {
|
|
189
256
|
const shown = [];
|
|
190
257
|
let chars = 0;
|
|
191
258
|
for (const line of lines.slice(0, MAX_PR_PACKAGE_LINES)) {
|
|
192
|
-
if (chars + line.length + 1 >
|
|
259
|
+
if (chars + line.length + 1 > maxChars)
|
|
193
260
|
break;
|
|
194
261
|
shown.push(line);
|
|
195
262
|
chars += line.length + 1;
|
|
@@ -197,6 +264,14 @@ function cappedList(lines) {
|
|
|
197
264
|
const more = lines.length - shown.length;
|
|
198
265
|
return more > 0 ? [...shown, `- …and ${more} more — see get_run for the full list`] : shown;
|
|
199
266
|
}
|
|
267
|
+
const MAX_DEPENDENCY_CACHE_FOLDERS = 4;
|
|
268
|
+
function dependencyCacheLine(folders) {
|
|
269
|
+
const shown = [...new Set(folders ?? [])]
|
|
270
|
+
.filter((folder) => DEPENDENCY_CACHE_PATH.test(folder))
|
|
271
|
+
.slice(0, MAX_DEPENDENCY_CACHE_FOLDERS)
|
|
272
|
+
.map((folder) => (folder === "." ? "the repo root" : `\`${folder}\``));
|
|
273
|
+
return shown.length > 0 ? `Dependency cache: restored \`node_modules\` for ${shown.join(", ")}.` : undefined;
|
|
274
|
+
}
|
|
200
275
|
function packagesSection(input) {
|
|
201
276
|
const packages = [
|
|
202
277
|
...new Map((input.packages ?? [])
|
|
@@ -212,11 +287,23 @@ function packagesSection(input) {
|
|
|
212
287
|
isSafeMarkdownFragment(refusal.reason))
|
|
213
288
|
.map((refusal) => [`${refusal.ecosystem}\0${refusal.name}\0${refusal.reason}`, refusal])).values(),
|
|
214
289
|
];
|
|
290
|
+
const restored = dependencyCacheLine(input.restoredDependencies);
|
|
215
291
|
if (packages.length === 0 && refusals.length === 0)
|
|
216
|
-
return
|
|
292
|
+
return restored;
|
|
293
|
+
// Only ids safe in a Markdown line; a package whose ids are all unsafe is
|
|
294
|
+
// not listed here (it is still listed as installed).
|
|
295
|
+
const waivers = packages.flatMap((pkg) => {
|
|
296
|
+
// Ids come from OSV (a waiver can match via alias), so keep only the
|
|
297
|
+
// policy id shape and render each in a code span.
|
|
298
|
+
const ids = (pkg.acceptedAdvisories ?? []).filter((id) => ACCEPTED_ADVISORY_ID.test(id));
|
|
299
|
+
return ids.length > 0 ? [`- \`${pkg.name}@${pkg.version}\`: ${ids.map((id) => `\`${id}\``).join(", ")}`] : [];
|
|
300
|
+
});
|
|
217
301
|
return [
|
|
302
|
+
...(restored ? [restored, ""] : []),
|
|
218
303
|
"<details>",
|
|
219
|
-
|
|
304
|
+
input.packagesScope === "pull_request"
|
|
305
|
+
? `<summary>Packages installed by wardby's runs on this pull request (${packages.length})</summary>`
|
|
306
|
+
: `<summary>Packages installed during this run (${packages.length})</summary>`,
|
|
220
307
|
"",
|
|
221
308
|
...cappedList(packages.map((pkg) => `- ${pkg.ecosystem} \`${pkg.name}@${pkg.version}\``)),
|
|
222
309
|
...(refusals.length > 0
|
|
@@ -226,6 +313,13 @@ function packagesSection(input) {
|
|
|
226
313
|
...cappedList(refusals.map((refusal) => `- ${refusal.ecosystem} \`${refusal.name}\`: ${refusal.reason}`)),
|
|
227
314
|
]
|
|
228
315
|
: []),
|
|
316
|
+
...(waivers.length > 0
|
|
317
|
+
? [
|
|
318
|
+
"",
|
|
319
|
+
"**Accepted advisories** (waived by this agent's package policy):",
|
|
320
|
+
...cappedList(waivers, MAX_PR_WAIVER_LIST_CHARS),
|
|
321
|
+
]
|
|
322
|
+
: []),
|
|
229
323
|
"</details>",
|
|
230
324
|
].join("\n");
|
|
231
325
|
}
|
|
@@ -242,15 +336,6 @@ const LOCKFILE_NAMES = new Set([
|
|
|
242
336
|
export function isLockfilePath(path) {
|
|
243
337
|
return LOCKFILE_NAMES.has(path.slice(path.lastIndexOf("/") + 1));
|
|
244
338
|
}
|
|
245
|
-
/** What each refusal code means to someone reading the PR, without the registry's internals. */
|
|
246
|
-
const REFUSAL_MEANING = {
|
|
247
|
-
wardby_package_not_allowed: "not on the allowlist, or only reachable through a refused package",
|
|
248
|
-
wardby_version_filtered: "withheld: newer than the release-age limit, or a high-severity advisory",
|
|
249
|
-
wardby_graph_incomplete: "its dependencies could not be confirmed (usually transient)",
|
|
250
|
-
wardby_lockfile_integrity_mismatch: "the lock file's integrity does not match the registry's",
|
|
251
|
-
wardby_upstream_error: "the upstream registry failed (transient)",
|
|
252
|
-
wardby_audit_unavailable: "the advisory check was unavailable (transient)",
|
|
253
|
-
};
|
|
254
339
|
/** At most this many refused packages are named in the warning; the full list stays in the details. */
|
|
255
340
|
const MAX_WARNING_NAMES = 8;
|
|
256
341
|
/**
|
|
@@ -280,7 +365,7 @@ function refusalWarning(input) {
|
|
|
280
365
|
const listed = [...names];
|
|
281
366
|
const shown = listed.slice(0, MAX_WARNING_NAMES).join(", ");
|
|
282
367
|
const more = listed.length > MAX_WARNING_NAMES ? ` and ${listed.length - MAX_WARNING_NAMES} more` : "";
|
|
283
|
-
const meaning =
|
|
368
|
+
const meaning = refusalMeaning(reason);
|
|
284
369
|
lines.push(`> - ${shown}${more}: \`${reason}\`${meaning ? ` (${meaning})` : ""}`);
|
|
285
370
|
}
|
|
286
371
|
const lockfiles = (input.changedLockfiles ?? []).filter(isSafeMarkdownFragment);
|
|
@@ -289,8 +374,51 @@ function refusalWarning(input) {
|
|
|
289
374
|
}
|
|
290
375
|
return lines.join("\n");
|
|
291
376
|
}
|
|
377
|
+
/** The banner when the audit report can't be rendered: fixed text, no record data. */
|
|
378
|
+
const PACKAGE_AUDIT_FALLBACK_BANNER = "> [!CAUTION]\n> **Package audit mode was on for this run.** Wardby's registry may have installed packages outside this agent's allowlist; see `get_run` (`packageAudit`) before merging.";
|
|
379
|
+
/**
|
|
380
|
+
* The package audit block: built only from registry records (names and
|
|
381
|
+
* versions through codeSpan, advisory ids by their policy shape), so neither
|
|
382
|
+
* the model nor a package's metadata can remove or soften it.
|
|
383
|
+
*/
|
|
384
|
+
function packageAuditSection(input) {
|
|
385
|
+
if (!input.packageAudit)
|
|
386
|
+
return undefined;
|
|
387
|
+
const summary = summarizePackageAudit(packageAuditRows(input.packageAudit.entries));
|
|
388
|
+
return packageAuditWording(input.packageAudit.mode, summary, { format: "pr" });
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The pull request body, within GitHub's MAX_PULL_REQUEST_BODY_CHARS. When it
|
|
392
|
+
* would be longer, the informational Packages section goes first; if that is
|
|
393
|
+
* not enough, the agent's summary is truncated to fit (fitSummary keeps its
|
|
394
|
+
* closing-keyword line). The other sections are bounded and stay whole.
|
|
395
|
+
*/
|
|
396
|
+
export function pullRequestBody(input, options = {}) {
|
|
397
|
+
const body = buildPullRequestBody(input, options);
|
|
398
|
+
if (body.length <= MAX_PULL_REQUEST_BODY_CHARS)
|
|
399
|
+
return body;
|
|
400
|
+
const withoutPackages = options.omitPackages ? body : buildPullRequestBody(input, { omitPackages: true });
|
|
401
|
+
if (withoutPackages.length <= MAX_PULL_REQUEST_BODY_CHARS || !input.summary)
|
|
402
|
+
return withoutPackages;
|
|
403
|
+
const room = input.summary.length - (withoutPackages.length - MAX_PULL_REQUEST_BODY_CHARS);
|
|
404
|
+
return buildPullRequestBody({ ...input, summary: fitSummary(input.summary, room) }, { omitPackages: true });
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* `summary` cut to `max` characters. A last line with a closing keyword
|
|
408
|
+
* (`Resolves #12`) is kept after the cut, so merging still closes the issue.
|
|
409
|
+
*/
|
|
410
|
+
function fitSummary(summary, max) {
|
|
411
|
+
const measure = (value) => value.length;
|
|
412
|
+
const lines = summary.trimEnd().split("\n");
|
|
413
|
+
const last = lines[lines.length - 1];
|
|
414
|
+
if (lines.length > 1 && last.length <= 200 && hasClosingKeyword(last)) {
|
|
415
|
+
const kept = truncateText(lines.slice(0, -1).join("\n"), max - last.length - 2, measure);
|
|
416
|
+
return `${kept}\n\n${last}`;
|
|
417
|
+
}
|
|
418
|
+
return truncateText(summary, max, measure);
|
|
419
|
+
}
|
|
292
420
|
/** The hidden run marker stays first and unconditional: createOrFindDraftPullRequest's idempotent lookup depends on it. */
|
|
293
|
-
|
|
421
|
+
function buildPullRequestBody(input, options) {
|
|
294
422
|
const sections = [`${RUN_MARKER_PREFIX}${input.runId} -->`];
|
|
295
423
|
// Hidden marker stays first; the issue link is the first visible line.
|
|
296
424
|
const issue = issueLine(input);
|
|
@@ -312,6 +440,17 @@ export function pullRequestBody(input) {
|
|
|
312
440
|
catch {
|
|
313
441
|
// omitted, like the packages section
|
|
314
442
|
}
|
|
443
|
+
try {
|
|
444
|
+
const audit = packageAuditSection(input);
|
|
445
|
+
if (audit)
|
|
446
|
+
sections.push(audit);
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
// Unlike the packages section, the audit banner fails closed: a report
|
|
450
|
+
// that can't be rendered still says the mode was on.
|
|
451
|
+
if (input.packageAudit)
|
|
452
|
+
sections.push(PACKAGE_AUDIT_FALLBACK_BANNER);
|
|
453
|
+
}
|
|
315
454
|
if (input.summary)
|
|
316
455
|
sections.push(input.summary);
|
|
317
456
|
if (input.tests?.length) {
|
|
@@ -320,13 +459,16 @@ export function pullRequestBody(input) {
|
|
|
320
459
|
// The packages section is informational: a failure rendering it (e.g. a
|
|
321
460
|
// malformed report) omits the section and never fails finalization.
|
|
322
461
|
try {
|
|
323
|
-
const packages = packagesSection(input);
|
|
462
|
+
const packages = options.omitPackages ? undefined : packagesSection(input);
|
|
324
463
|
if (packages)
|
|
325
464
|
sections.push(packages);
|
|
326
465
|
}
|
|
327
466
|
catch {
|
|
328
467
|
// omitted
|
|
329
468
|
}
|
|
469
|
+
if (input.latestRunId && SAFE_RUN_ID.test(input.latestRunId)) {
|
|
470
|
+
sections.push(`${LATEST_RUN_MARKER_PREFIX}${input.latestRunId} -->`);
|
|
471
|
+
}
|
|
330
472
|
return sections.join("\n\n");
|
|
331
473
|
}
|
|
332
474
|
/** The hidden marker stays first and unconditional: findStatusComment's lookup depends on it. */
|
|
@@ -364,7 +506,9 @@ export class GitHubAppClient {
|
|
|
364
506
|
throw new Error("github_app_id_invalid");
|
|
365
507
|
if (!config.privateKey.trim())
|
|
366
508
|
throw new Error("github_app_private_key_invalid");
|
|
367
|
-
|
|
509
|
+
// WARDBY_TEST_SIMULATORS (test only, refused with production configuration): every call
|
|
510
|
+
// goes to the simulator instead, all or nothing.
|
|
511
|
+
this.apiBaseUrl = testSimulatorBase("github-api") ?? normalizeApiBaseUrl(config.apiBaseUrl ?? DEFAULT_API_BASE_URL);
|
|
368
512
|
this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
|
|
369
513
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(this.apiVersion))
|
|
370
514
|
throw new Error("github_api_version_invalid");
|
|
@@ -463,20 +607,38 @@ export class GitHubAppClient {
|
|
|
463
607
|
});
|
|
464
608
|
}
|
|
465
609
|
identity;
|
|
466
|
-
/**
|
|
467
|
-
|
|
610
|
+
/**
|
|
611
|
+
* The App's id, slug (its @-mention handle), and owner account, read once
|
|
612
|
+
* with the App JWT and memoized. `fresh` re-reads GET /app and replaces the
|
|
613
|
+
* memo (used before a destructive action, since the owner can change).
|
|
614
|
+
* A rejection is never cached.
|
|
615
|
+
*/
|
|
616
|
+
appIdentity(opts) {
|
|
617
|
+
if (opts?.fresh)
|
|
618
|
+
this.identity = undefined;
|
|
468
619
|
this.identity ??= (async () => {
|
|
469
620
|
const response = await this.requestJson("/app", await this.appJwt());
|
|
470
621
|
const payload = record(await response.json());
|
|
471
622
|
if (typeof payload.slug !== "string" || !/^[a-z0-9-]{1,64}$/.test(payload.slug)) {
|
|
472
623
|
throw new Error("github_api_invalid_response");
|
|
473
624
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
625
|
+
const ownerLogin = record(payload.owner).login;
|
|
626
|
+
if (!isGitHubLogin(ownerLogin))
|
|
627
|
+
throw new Error("github_api_invalid_response");
|
|
628
|
+
return { id: positiveInteger(payload.id), slug: payload.slug, ownerLogin };
|
|
629
|
+
})();
|
|
630
|
+
const pending = this.identity;
|
|
631
|
+
void pending.catch(() => {
|
|
632
|
+
// Only clear our own attempt: a newer (fresh) read may have replaced it.
|
|
633
|
+
if (this.identity === pending)
|
|
634
|
+
this.identity = undefined;
|
|
478
635
|
});
|
|
479
|
-
return
|
|
636
|
+
return pending;
|
|
637
|
+
}
|
|
638
|
+
/** Removes this App from an installation (an account outside the trusted set). */
|
|
639
|
+
async deleteInstallation(installationId) {
|
|
640
|
+
const id = positiveInteger(installationId);
|
|
641
|
+
await this.requestJson(`/app/installations/${id}`, await this.appJwt(), { method: "DELETE" }, [204]);
|
|
480
642
|
}
|
|
481
643
|
async upsertContinuationStatusComment(input) {
|
|
482
644
|
const repository = normalizeGitHubRepository(input.repository);
|
|
@@ -553,11 +715,14 @@ export class GitHubAppClient {
|
|
|
553
715
|
}
|
|
554
716
|
async completeContinuationCheckRun(input) {
|
|
555
717
|
const repository = normalizeGitHubRepository(input.repository);
|
|
556
|
-
if (!SAFE_RUN_ID.test(input.runId) || !SAFE_COMMIT_SHA.test(input.headSha)) {
|
|
718
|
+
if (!SAFE_RUN_ID.test(input.runId) || (input.headSha !== undefined && !SAFE_COMMIT_SHA.test(input.headSha))) {
|
|
557
719
|
throw new Error("github_check_run_input_invalid");
|
|
558
720
|
}
|
|
721
|
+
const ref = input.headSha ?? (input.headRef === undefined ? undefined : normalizeGitRef(input.headRef));
|
|
722
|
+
if (!ref)
|
|
723
|
+
throw new Error("github_check_run_input_invalid");
|
|
559
724
|
await this.withChecksToken(repository, async (token) => {
|
|
560
|
-
const existing = await this.findCheckRun(token, repository,
|
|
725
|
+
const existing = await this.findCheckRun(token, repository, ref, input.runId);
|
|
561
726
|
if (!existing)
|
|
562
727
|
return;
|
|
563
728
|
const [owner, name] = repository.split("/");
|
|
@@ -595,8 +760,14 @@ export class GitHubAppClient {
|
|
|
595
760
|
draft: true,
|
|
596
761
|
}),
|
|
597
762
|
}, [201, 422]);
|
|
598
|
-
if (response.status === 201)
|
|
599
|
-
|
|
763
|
+
if (response.status === 201) {
|
|
764
|
+
const payload = record(await response.json());
|
|
765
|
+
const created = this.parsePullRequest(payload, repository);
|
|
766
|
+
// Hash what GitHub stored, not what was sent.
|
|
767
|
+
return typeof payload.body === "string"
|
|
768
|
+
? { ...created, descriptionSha256: descriptionSha256(payload.body) }
|
|
769
|
+
: created;
|
|
770
|
+
}
|
|
600
771
|
const raced = await this.findPullRequest(token, { ...input, repository, baseRef, headRef });
|
|
601
772
|
if (raced)
|
|
602
773
|
return raced;
|
|
@@ -617,6 +788,270 @@ export class GitHubAppClient {
|
|
|
617
788
|
throw new Error("github_pull_request_input_invalid");
|
|
618
789
|
return this.withRepositoryToken(repository, (token) => this.findPullRequest(token, { runId: input.runId, repository, baseRef, headRef, acceptReadyForReview: true }));
|
|
619
790
|
}
|
|
791
|
+
/** See GitHubRepositoryAccess.openPullRequestDraft: GitHub's own `draft` flag on the marked open PR. */
|
|
792
|
+
async openPullRequestDraft(input) {
|
|
793
|
+
const repository = normalizeGitHubRepository(input.repository);
|
|
794
|
+
const baseRef = normalizeGitRef(input.baseRef);
|
|
795
|
+
const headRef = normalizeGitRef(input.headRef);
|
|
796
|
+
if (!SAFE_RUN_ID.test(input.runId))
|
|
797
|
+
throw new Error("github_pull_request_input_invalid");
|
|
798
|
+
return this.withRepositoryToken(repository, async (token) => {
|
|
799
|
+
const found = await this.findMarkedPullRequest(token, {
|
|
800
|
+
runId: input.runId,
|
|
801
|
+
repository,
|
|
802
|
+
baseRef,
|
|
803
|
+
headRef,
|
|
804
|
+
acceptReadyForReview: true,
|
|
805
|
+
});
|
|
806
|
+
return found ? found.draft : null;
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
/** See GitHubRepositoryAccess.openPullRequestBase: the marked open PR's current base, same repository only. */
|
|
810
|
+
async openPullRequestBase(input) {
|
|
811
|
+
const repository = normalizeGitHubRepository(input.repository);
|
|
812
|
+
const baseRef = normalizeGitRef(input.baseRef);
|
|
813
|
+
const headRef = normalizeGitRef(input.headRef);
|
|
814
|
+
if (!SAFE_RUN_ID.test(input.runId))
|
|
815
|
+
throw new Error("github_pull_request_input_invalid");
|
|
816
|
+
return this.withRepositoryToken(repository, async (token) => {
|
|
817
|
+
const found = await this.findMarkedPullRequest(token, {
|
|
818
|
+
runId: input.runId,
|
|
819
|
+
repository,
|
|
820
|
+
baseRef,
|
|
821
|
+
headRef,
|
|
822
|
+
acceptReadyForReview: true,
|
|
823
|
+
});
|
|
824
|
+
if (!found)
|
|
825
|
+
return null;
|
|
826
|
+
const base = record(found.raw.base);
|
|
827
|
+
const head = record(found.raw.head);
|
|
828
|
+
const sameRepository = (side) => {
|
|
829
|
+
const name = side.repo && typeof side.repo === "object" ? side.repo.full_name : null;
|
|
830
|
+
return typeof name === "string" && name.toLowerCase() === repository.toLowerCase();
|
|
831
|
+
};
|
|
832
|
+
// A fork's branch is never fetched: wardby's own heads live in the PR's repository.
|
|
833
|
+
if (!sameRepository(base) || !sameRepository(head))
|
|
834
|
+
return null;
|
|
835
|
+
if (typeof base.ref !== "string")
|
|
836
|
+
throw new Error("github_api_invalid_response");
|
|
837
|
+
try {
|
|
838
|
+
return { number: found.pr.number, baseRef: normalizeGitRef(base.ref) };
|
|
839
|
+
}
|
|
840
|
+
catch (error) {
|
|
841
|
+
throw new Error("github_api_invalid_response", { cause: error });
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* A fix round's description update (see GitHubRepositoryAccess). One
|
|
847
|
+
* repository token; every failure becomes `skipped:error`.
|
|
848
|
+
*/
|
|
849
|
+
async updatePullRequestDescription(input) {
|
|
850
|
+
try {
|
|
851
|
+
const target = this.descriptionTarget(input);
|
|
852
|
+
return await this.withRepositoryToken(target.repository, async (token) => {
|
|
853
|
+
const state = await this.descriptionState(token, input, target);
|
|
854
|
+
if (!state)
|
|
855
|
+
return { kind: "skipped", reason: "not_open" };
|
|
856
|
+
const { pr, verdict, full, proposed } = state;
|
|
857
|
+
const { repository } = target;
|
|
858
|
+
if (!state.writable) {
|
|
859
|
+
// The comment truncates a body too long for it, so the full one goes in.
|
|
860
|
+
const commentPosted = await this.upsertDescriptionComment(token, repository, pr.number, input, full, state)
|
|
861
|
+
.then(() => true)
|
|
862
|
+
.catch(() => false);
|
|
863
|
+
return { kind: "edited_by_hand", commentPosted };
|
|
864
|
+
}
|
|
865
|
+
if (proposed === undefined)
|
|
866
|
+
return { kind: "skipped", reason: "too_long" };
|
|
867
|
+
const agentTextRunId = verdict.ok ? input.runId : input.baseline.agentTextRunId;
|
|
868
|
+
const outcome = verdict.ok
|
|
869
|
+
? { kind: "replaced" }
|
|
870
|
+
: { kind: "refreshed", reason: verdict.reason };
|
|
871
|
+
const proposedSha256 = descriptionSha256(proposed);
|
|
872
|
+
if (proposedSha256 === input.baseline.recordedSha256) {
|
|
873
|
+
return { kind: "unchanged", sha256: proposedSha256, agentTextRunId: input.baseline.agentTextRunId };
|
|
874
|
+
}
|
|
875
|
+
const [owner, name] = repository.split("/");
|
|
876
|
+
// Known, accepted race: a person editing between our read and this PATCH is overwritten (GitHub has no If-Match); the store's compare-and-set only covers wardby runs racing each other.
|
|
877
|
+
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls/${pr.number}`, token, { method: "PATCH", body: JSON.stringify({ body: proposed }) }, [200]);
|
|
878
|
+
const written = record(await response.json()).body;
|
|
879
|
+
if (typeof written !== "string")
|
|
880
|
+
throw new Error("github_api_invalid_response");
|
|
881
|
+
// A proposal left from an earlier round is stale now; a failure here never undoes the write.
|
|
882
|
+
await this.markDescriptionCommentApplied(token, repository, pr.number, input.runId, state).catch(() => undefined);
|
|
883
|
+
return { ...outcome, sha256: descriptionSha256(written), agentTextRunId };
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
catch {
|
|
887
|
+
return { kind: "skipped", reason: "error" };
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
/** See GitHubRepositoryAccess.proposeDescriptionComment. */
|
|
891
|
+
async proposeDescriptionComment(input) {
|
|
892
|
+
try {
|
|
893
|
+
if (!input.headSha || !/^[0-9a-f]{40}$/.test(input.headSha))
|
|
894
|
+
return false;
|
|
895
|
+
const target = this.descriptionTarget(input);
|
|
896
|
+
return await this.withRepositoryToken(target.repository, async (token) => {
|
|
897
|
+
const state = await this.descriptionState(token, input, target);
|
|
898
|
+
if (!state || state.writable)
|
|
899
|
+
return false;
|
|
900
|
+
await this.upsertDescriptionComment(token, target.repository, state.pr.number, input, state.full, state);
|
|
901
|
+
return true;
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
/** The validated repository and refs; throws unless this is wardby's own pull request branch. */
|
|
909
|
+
descriptionTarget(input) {
|
|
910
|
+
const repository = normalizeGitHubRepository(input.repository);
|
|
911
|
+
const baseRef = normalizeGitRef(input.baseRef);
|
|
912
|
+
const headRef = normalizeGitRef(input.headRef);
|
|
913
|
+
if (!SAFE_RUN_ID.test(input.runId) ||
|
|
914
|
+
!SAFE_RUN_ID.test(input.rootRunId) ||
|
|
915
|
+
!SAFE_RUN_ID.test(input.baseline.agentTextRunId) ||
|
|
916
|
+
(input.headSha !== undefined && !/^[0-9a-f]{40}$/.test(input.headSha)) ||
|
|
917
|
+
// Only wardby's own pull request: its branch is the root run's.
|
|
918
|
+
headRef !== `wardby/run-${input.rootRunId}`) {
|
|
919
|
+
throw new Error("github_pull_request_description_input_invalid");
|
|
920
|
+
}
|
|
921
|
+
return { repository, baseRef, headRef };
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* The open pull request, the description this run proposes, and whether
|
|
925
|
+
* wardby may write it directly: it is unedited (recorded or rebuilt hash),
|
|
926
|
+
* or a person copied wardby's latest proposal in (taken back). Null when
|
|
927
|
+
* the pull request isn't open. `comments` is set when it was read.
|
|
928
|
+
*/
|
|
929
|
+
async descriptionState(token, input, target) {
|
|
930
|
+
const { repository, baseRef, headRef } = target;
|
|
931
|
+
const found = await this.findMarkedPullRequest(token, {
|
|
932
|
+
runId: input.rootRunId,
|
|
933
|
+
repository,
|
|
934
|
+
baseRef,
|
|
935
|
+
headRef,
|
|
936
|
+
acceptReadyForReview: true,
|
|
937
|
+
});
|
|
938
|
+
if (!found)
|
|
939
|
+
return null;
|
|
940
|
+
const { pr, body: currentBody } = found;
|
|
941
|
+
const verdict = assessFinalMessage(input.summary, input.baseline.previousAgentText);
|
|
942
|
+
const summary = verdict.ok ? input.summary : input.baseline.previousAgentText;
|
|
943
|
+
const { full, proposed } = this.proposedDescription(input, repository, baseRef, headRef, summary, currentBody);
|
|
944
|
+
const unedited = isUneditedDescription({
|
|
945
|
+
currentBody,
|
|
946
|
+
rootRunId: input.rootRunId,
|
|
947
|
+
recordedSha256: input.baseline.recordedSha256,
|
|
948
|
+
...(input.baseline.legacySha256 ? { legacySha256: input.baseline.legacySha256 } : {}),
|
|
949
|
+
previousAgentText: input.baseline.previousAgentText,
|
|
950
|
+
});
|
|
951
|
+
if (unedited)
|
|
952
|
+
return { pr, currentBody, verdict, full, proposed, writable: true };
|
|
953
|
+
const comments = await this.findDescriptionComments(token, repository, pr.number);
|
|
954
|
+
const latest = comments.newest?.parsed.proposed;
|
|
955
|
+
// The author copied wardby's latest proposal in: the description is wardby's again.
|
|
956
|
+
const takenBack = latest !== undefined &&
|
|
957
|
+
isUneditedDescription({
|
|
958
|
+
currentBody,
|
|
959
|
+
rootRunId: input.rootRunId,
|
|
960
|
+
recordedSha256: descriptionSha256(latest),
|
|
961
|
+
});
|
|
962
|
+
return { pr, currentBody, verdict, full, proposed, writable: takenBack, comments };
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* The rebuilt description: the generated sections from this run with
|
|
966
|
+
* `summary` as the agent text, the latest-run marker last, and the current
|
|
967
|
+
* body's Related block re-inserted verbatim. `proposed` drops the Packages
|
|
968
|
+
* section when `full` exceeds GitHub's limit, and is undefined when it still
|
|
969
|
+
* would.
|
|
970
|
+
*/
|
|
971
|
+
proposedDescription(input, repository, baseRef, headRef, summary, currentBody) {
|
|
972
|
+
const related = extractRelatedBlock(currentBody.replace(/\r\n/g, "\n"));
|
|
973
|
+
const build = (omitPackages) => {
|
|
974
|
+
const body = pullRequestBody({
|
|
975
|
+
repository,
|
|
976
|
+
baseRef,
|
|
977
|
+
headRef,
|
|
978
|
+
runId: input.rootRunId,
|
|
979
|
+
latestRunId: input.runId,
|
|
980
|
+
summary,
|
|
981
|
+
tests: input.tests,
|
|
982
|
+
tag: input.tag,
|
|
983
|
+
packages: input.packages,
|
|
984
|
+
packagesScope: "pull_request",
|
|
985
|
+
packageRefusals: input.packageRefusals,
|
|
986
|
+
packageAudit: input.packageAudit,
|
|
987
|
+
restoredDependencies: input.restoredDependencies,
|
|
988
|
+
changedLockfiles: input.changedLockfiles,
|
|
989
|
+
issue: input.issue,
|
|
990
|
+
issueNumber: input.issueNumber,
|
|
991
|
+
}, { omitPackages });
|
|
992
|
+
return related ? (upsertRelatedSection(body, related) ?? body) : body;
|
|
993
|
+
};
|
|
994
|
+
const full = build(false);
|
|
995
|
+
if (full.length <= MAX_PULL_REQUEST_BODY_CHARS)
|
|
996
|
+
return { full, proposed: full };
|
|
997
|
+
const trimmed = build(true);
|
|
998
|
+
return { full, proposed: trimmed.length <= MAX_PULL_REQUEST_BODY_CHARS ? trimmed : undefined };
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Creates the pull request's one description comment, or edits the newest
|
|
1002
|
+
* one this App wrote (unchanged text is not re-sent); older duplicates are
|
|
1003
|
+
* rewritten to point at it. A comment anyone else wrote is never edited.
|
|
1004
|
+
*/
|
|
1005
|
+
async upsertDescriptionComment(token, repository, pullRequestNumber, input, proposedBody, state) {
|
|
1006
|
+
const body = descriptionFallbackComment(input.runId, proposedBody, input.headSha);
|
|
1007
|
+
const { newest, older } = state.comments ?? (await this.findDescriptionComments(token, repository, pullRequestNumber));
|
|
1008
|
+
const [owner, name] = repository.split("/");
|
|
1009
|
+
const commentPath = (id) => `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/comments/${id}`;
|
|
1010
|
+
let url = newest?.url;
|
|
1011
|
+
if (newest) {
|
|
1012
|
+
if (newest.body.replace(/\r\n/g, "\n") !== body) {
|
|
1013
|
+
await this.requestJson(commentPath(newest.id), token, { method: "PATCH", body: JSON.stringify({ body }) }, [200]);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments`, token, { method: "POST", body: JSON.stringify({ body }) }, [201]);
|
|
1018
|
+
const created = record(await response.json()).html_url;
|
|
1019
|
+
url = typeof created === "string" ? created : undefined;
|
|
1020
|
+
}
|
|
1021
|
+
await this.supersedeDescriptionComments(token, commentPath, older, url);
|
|
1022
|
+
}
|
|
1023
|
+
/** Once a run applied its description, a pending proposal in the description comment is marked applied. */
|
|
1024
|
+
async markDescriptionCommentApplied(token, repository, pullRequestNumber, runId, state) {
|
|
1025
|
+
const { newest, older } = state.comments ?? (await this.findDescriptionComments(token, repository, pullRequestNumber));
|
|
1026
|
+
if (!newest)
|
|
1027
|
+
return;
|
|
1028
|
+
const [owner, name] = repository.split("/");
|
|
1029
|
+
const commentPath = (id) => `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/comments/${id}`;
|
|
1030
|
+
if (!newest.parsed.applied) {
|
|
1031
|
+
await this.requestJson(commentPath(newest.id), token, { method: "PATCH", body: JSON.stringify({ body: descriptionAppliedComment(runId) }) }, [200]);
|
|
1032
|
+
}
|
|
1033
|
+
await this.supersedeDescriptionComments(token, commentPath, older, newest.url);
|
|
1034
|
+
}
|
|
1035
|
+
/** Rewrites duplicate description comments to a pointer at the one in use. */
|
|
1036
|
+
async supersedeDescriptionComments(token, commentPath, older, url) {
|
|
1037
|
+
for (const duplicate of older) {
|
|
1038
|
+
await this.requestJson(commentPath(duplicate.id), token, { method: "PATCH", body: JSON.stringify({ body: supersededDescriptionComment(url) }) }, [200]);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
/** The description comments this App wrote (see findDescriptionComments; the reviewer reads them the same way). */
|
|
1042
|
+
async findDescriptionComments(token, repository, pullRequestNumber) {
|
|
1043
|
+
const { id: appId } = await this.appIdentity();
|
|
1044
|
+
const [owner, name] = repository.split("/");
|
|
1045
|
+
return findDescriptionComments(async (page) => {
|
|
1046
|
+
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments?per_page=100&page=${page}`, token);
|
|
1047
|
+
const payload = await response.json();
|
|
1048
|
+
if (!Array.isArray(payload))
|
|
1049
|
+
throw new Error("github_api_invalid_response");
|
|
1050
|
+
return payload;
|
|
1051
|
+
},
|
|
1052
|
+
// Only this App's own comments: never a person's, nor another App's or bot's.
|
|
1053
|
+
(comment) => isThisAppsComment(comment, appId));
|
|
1054
|
+
}
|
|
620
1055
|
async appJwt() {
|
|
621
1056
|
let keyObject;
|
|
622
1057
|
try {
|
|
@@ -682,11 +1117,19 @@ export class GitHubAppClient {
|
|
|
682
1117
|
await this.requestJson("/installation/token", token, { method: "DELETE" }, [204]);
|
|
683
1118
|
}
|
|
684
1119
|
async findPullRequest(token, input) {
|
|
1120
|
+
return (await this.findMarkedPullRequest(token, input))?.pr ?? null;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* findPullRequest, plus the matched pull request's raw body and payload. Not
|
|
1124
|
+
* filtered by base: a person may retarget the PR, and the head branch plus the
|
|
1125
|
+
* run marker already identify it. When several open PRs match, the one on
|
|
1126
|
+
* `baseRef` wins.
|
|
1127
|
+
*/
|
|
1128
|
+
async findMarkedPullRequest(token, input) {
|
|
685
1129
|
const [owner, name] = input.repository.split("/");
|
|
686
1130
|
const query = new URLSearchParams({
|
|
687
1131
|
state: "open",
|
|
688
1132
|
head: `${owner}:${input.headRef}`,
|
|
689
|
-
base: input.baseRef,
|
|
690
1133
|
per_page: "100",
|
|
691
1134
|
});
|
|
692
1135
|
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls?${query.toString()}`, token);
|
|
@@ -694,13 +1137,26 @@ export class GitHubAppClient {
|
|
|
694
1137
|
if (!Array.isArray(payload))
|
|
695
1138
|
throw new Error("github_api_invalid_response");
|
|
696
1139
|
const marker = `${RUN_MARKER_PREFIX}${input.runId} -->`;
|
|
1140
|
+
let first;
|
|
697
1141
|
for (const item of payload) {
|
|
698
1142
|
const candidate = record(item);
|
|
699
|
-
if (typeof candidate.body
|
|
700
|
-
|
|
1143
|
+
if (typeof candidate.body !== "string" || !candidate.body.includes(marker))
|
|
1144
|
+
continue;
|
|
1145
|
+
const base = candidate.base && typeof candidate.base === "object" ? candidate.base : {};
|
|
1146
|
+
if (base.ref === input.baseRef) {
|
|
1147
|
+
first = candidate;
|
|
1148
|
+
break;
|
|
701
1149
|
}
|
|
1150
|
+
first ??= candidate;
|
|
702
1151
|
}
|
|
703
|
-
|
|
1152
|
+
if (!first)
|
|
1153
|
+
return null;
|
|
1154
|
+
return {
|
|
1155
|
+
pr: this.parsePullRequest(first, input.repository, input.acceptReadyForReview === true),
|
|
1156
|
+
body: first.body,
|
|
1157
|
+
draft: first.draft === true,
|
|
1158
|
+
raw: first,
|
|
1159
|
+
};
|
|
704
1160
|
}
|
|
705
1161
|
parsePullRequest(value, repository, acceptReadyForReview = false) {
|
|
706
1162
|
const payload = record(value);
|
|
@@ -730,23 +1186,27 @@ export class GitHubAppClient {
|
|
|
730
1186
|
return { number, url: expectedUrl };
|
|
731
1187
|
}
|
|
732
1188
|
async findStatusComment(token, repository, pullRequestNumber, runId) {
|
|
1189
|
+
const marker = `${STATUS_COMMENT_MARKER_PREFIX}${runId} -->`;
|
|
1190
|
+
return this.findMarkedComment(token, repository, pullRequestNumber, (body) => body.includes(marker));
|
|
1191
|
+
}
|
|
1192
|
+
/** The first comment on the pull request (first page) whose body `matches`. */
|
|
1193
|
+
async findMarkedComment(token, repository, pullRequestNumber, matches) {
|
|
733
1194
|
const [owner, name] = repository.split("/");
|
|
734
1195
|
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments?per_page=100`, token);
|
|
735
1196
|
const payload = await response.json();
|
|
736
1197
|
if (!Array.isArray(payload))
|
|
737
1198
|
throw new Error("github_api_invalid_response");
|
|
738
|
-
const marker = `${STATUS_COMMENT_MARKER_PREFIX}${runId} -->`;
|
|
739
1199
|
for (const item of payload) {
|
|
740
1200
|
const candidate = record(item);
|
|
741
|
-
if (typeof candidate.body
|
|
742
|
-
|
|
743
|
-
}
|
|
1201
|
+
if (typeof candidate.body !== "string" || !matches(candidate.body))
|
|
1202
|
+
continue;
|
|
1203
|
+
return { id: positiveInteger(candidate.id), body: candidate.body };
|
|
744
1204
|
}
|
|
745
1205
|
return null;
|
|
746
1206
|
}
|
|
747
|
-
async findCheckRun(token, repository,
|
|
1207
|
+
async findCheckRun(token, repository, ref, runId) {
|
|
748
1208
|
const [owner, name] = repository.split("/");
|
|
749
|
-
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/commits/${
|
|
1209
|
+
const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/commits/${encodeURIComponent(ref)}/check-runs?per_page=100`, token);
|
|
750
1210
|
const payload = record(await response.json());
|
|
751
1211
|
const checkRuns = Array.isArray(payload.check_runs) ? payload.check_runs.map(record) : [];
|
|
752
1212
|
for (const candidate of checkRuns) {
|