@wardby/cli 0.6.0 → 0.8.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 +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +19 -0
- package/dist/claude-coding-worker/driver.d.ts +33 -3
- package/dist/claude-coding-worker/driver.js +151 -21
- package/dist/claude-coding-worker/main.js +38 -6
- package/dist/claude-coding-worker/sdk.js +29 -2
- 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/claude-plugin/command.d.ts +61 -0
- package/dist/claude-plugin/command.js +376 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +3 -1
- package/dist/cli.js +71 -13
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -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/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -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 +237 -12
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +17 -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 +52 -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 +9 -6
- package/dist/coding-worker/debug-trace.js +16 -19
- 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 +124 -39
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- 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 +15 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/providers.d.ts +12 -3
- package/dist/config/providers.js +55 -5
- package/dist/config/test-simulators.d.ts +92 -0
- package/dist/config/test-simulators.js +333 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -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-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -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/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +48 -2
- package/dist/core/dispatch.js +166 -66
- package/dist/core/engine-native.js +1 -1
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +169 -0
- package/dist/core/grants.d.ts +46 -8
- package/dist/core/grants.js +99 -8
- package/dist/core/host-events.d.ts +18 -2
- package/dist/core/host-events.js +291 -27
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +300 -22
- 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 +58 -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/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -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 +44 -18
- package/dist/core/notification-dispatcher.js +2 -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 +49 -20
- 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 +4 -3
- 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 +102 -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 +145 -49
- package/dist/core/runner.js +719 -216
- 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 +12 -74
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +49 -0
- package/dist/core/secret-guard.js +119 -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 +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- 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 +45 -9
- 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 +65 -3
- package/dist/generated/prisma/client.d.ts +65 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +266 -106
- 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 +110 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +957 -9
- package/dist/generated/prisma/internal/prismaNamespace.js +166 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +174 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +166 -2
- package/dist/generated/prisma/models/Agent.d.ts +377 -8
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +81 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +228 -7
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +232 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1712 -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/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.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/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +77 -1
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -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/ResourceGrant.d.ts +1 -1
- 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/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +10 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +2033 -110
- package/dist/import/create.js +48 -4
- package/dist/import/neutral-schema.d.ts +51 -281
- 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 +10 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- 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 +3 -0
- 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 +69 -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 +295 -28
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +73 -29
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- 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 +65 -12
- 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 +209 -27
- 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/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +40 -6
- 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 +7 -5
- package/dist/mcp/tools/tools.js +64 -16
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- 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/mcp/webhooks/ingress.js +10 -0
- 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 +55 -3
- package/dist/native-worker/gateway-server.js +152 -34
- package/dist/native-worker/gateway.d.ts +29 -1
- package/dist/native-worker/gateway.js +74 -4
- package/dist/native-worker/http-server.d.ts +7 -1
- package/dist/native-worker/http-server.js +62 -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 +24 -4
- package/dist/native-worker/ledger.js +62 -8
- 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 +130 -577
- package/dist/native-worker/protocol.js +51 -2
- 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/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -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/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -2
- package/dist/providers/coding-proxy/memory-ledger.js +99 -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 +16 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +209 -69
- package/dist/providers/coding-proxy/proxy.d.ts +21 -2
- package/dist/providers/coding-proxy/proxy.js +300 -48
- 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 +10 -0
- package/dist/providers/coding-proxy/runtime.js +109 -16
- package/dist/providers/coding-proxy/server.d.ts +9 -1
- package/dist/providers/coding-proxy/server.js +55 -1
- package/dist/providers/coding-proxy/types.d.ts +78 -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 +52 -7
- package/dist/providers/executor/container.d.ts +394 -9
- package/dist/providers/executor/container.js +1210 -96
- 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/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -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 +10 -2
- package/dist/providers/jobs/docker-isolation.js +47 -8
- package/dist/providers/jobs/docker.d.ts +9 -0
- package/dist/providers/jobs/docker.js +60 -11
- 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 +5 -1
- package/dist/providers/jobs/kubernetes-isolation.js +32 -3
- package/dist/providers/jobs/kubernetes.d.ts +11 -1
- package/dist/providers/jobs/kubernetes.js +111 -17
- package/dist/providers/jobs/types.d.ts +39 -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 +140 -6
- package/dist/providers/review-host/types.js +9 -1
- 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 +39 -4
- package/dist/providers/vcs/git.js +339 -26
- 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 +183 -44
- package/dist/providers/vcs/github.js +455 -56
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +9 -2
- 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 +37 -1
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +16 -8
- package/dist/providers/vcs/routing.js +27 -6
- package/dist/providers/vcs/types.d.ts +150 -5
- package/dist/quickstart/coding-seed.d.ts +14 -3
- package/dist/quickstart/coding-seed.js +213 -4
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- 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/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- 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 +5 -0
- package/dist/sandbox/host-functions.js +13 -13
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +4 -5
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- 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/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -4
- package/dist/wardby-bin.js +15 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +95 -24
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +164 -0
- package/docs/code-review-agents.md +557 -41
- package/docs/coding-agent-setup.md +566 -8
- package/docs/coding-packages.md +1033 -90
- package/docs/coding-services.md +27 -12
- package/docs/coding-worker-byo-images.md +24 -2
- package/docs/coding-worker-isolation.md +183 -37
- package/docs/debugging-agent-runs.md +242 -0
- package/docs/getting-started-gke.md +129 -141
- package/docs/getting-started-identity-provider.md +53 -38
- package/docs/getting-started.md +17 -0
- package/docs/host-providers.md +294 -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/mcp-servers.md +254 -0
- package/docs/native-sandbox.md +143 -31
- package/docs/observability.md +4 -0
- package/docs/security-deployment.md +139 -29
- package/docs/slack-notifications.md +35 -19
- package/docs/tool-secrets.md +45 -10
- package/docs/viewer-api.md +6 -0
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +28 -2
- package/help/agent-recipes.md +33 -2
- package/help/architecture-agent.md +11 -6
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/build-worker-image.md +3 -1
- package/help/builder-agent.md +19 -6
- package/help/claude-code-run-pane.md +40 -0
- 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-mcp-pins-unavailable.md +35 -0
- 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 +1 -1
- 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/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +4 -3
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- 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/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -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/errors/tool-timeout.md +38 -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/host-providers.md +40 -0
- package/help/identity-and-access.md +6 -1
- 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/mcp-servers.md +73 -0
- package/help/mcp.md +7 -2
- package/help/merge-order-check.md +5 -0
- package/help/native-capabilities.md +55 -1
- package/help/native-sandbox.md +16 -2
- package/help/operating-agents.md +21 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +17 -10
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +134 -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 +86 -7
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +18 -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/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/schema.prisma +468 -109
- package/scripts/git-askpass.sh +1 -1
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
package/dist/mcp/tools/runs.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { agentAccessResolver, readableAgentsWhere, requireAgentAccess } from "../auth/access.js";
|
|
2
|
+
import { requireScope } from "../auth/resource-server.js";
|
|
1
3
|
import { McpError } from "../errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
+
import { loadGraphRuns } from "../../viewer/graph.js";
|
|
5
|
+
import { loadJiraConfig } from "../../config/providers.js";
|
|
6
|
+
import { findReadableRun } from "./run-access.js";
|
|
7
|
+
import { publicCodingRunResult, redactTokenShapedValues, sanitizeDependencyCacheReport, } from "../../coding/protocol.js";
|
|
4
8
|
import { countPlanRefusals, summarizeRegistryFetches } from "../../coding/registry/report.js";
|
|
9
|
+
import { summarizePackageAudit } from "../../coding/registry/audit-mode.js";
|
|
5
10
|
import { storedServiceLabels } from "../../coding/services/catalog.js";
|
|
6
11
|
import { textResult } from "./text-result.js";
|
|
12
|
+
import { loadBudgetEvents } from "../../core/budget-events.js";
|
|
13
|
+
import { loadAgentSecretValues, maskNamedSecrets } from "../../core/secret-guard.js";
|
|
14
|
+
import { logger } from "../../core/logger.js";
|
|
15
|
+
const runsLog = logger.child({ module: "mcp-runs" });
|
|
7
16
|
/**
|
|
8
17
|
* Run visibility (resource-sharing grants spec §3.5, A6): a run's output is
|
|
9
18
|
* the triggerer's and the agent owner's, never every reader's. The owner
|
|
@@ -15,69 +24,215 @@ function withOperatorMode(run) {
|
|
|
15
24
|
const mode = run.nativeExecutionMode;
|
|
16
25
|
return { ...run, nativeExecutionMode: mode == null ? null : mode === "sandbox" ? "sandbox" : "control-plane" };
|
|
17
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* A coding run's request as its readers may see it. It can be text a caller or an issue wrote, so
|
|
29
|
+
* the agent's secret values and token-shaped values are masked here on every read. A secret that
|
|
30
|
+
* can't be read means the text can't be checked, so it is withheld; the log carries the error name only.
|
|
31
|
+
*/
|
|
32
|
+
async function requestForReader(ctx, run, request) {
|
|
33
|
+
try {
|
|
34
|
+
const named = await loadAgentSecretValues(ctx.db, ctx.providers.secrets, run.agentId);
|
|
35
|
+
return { request: redactTokenShapedValues(maskNamedSecrets(request, named)) };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
runsLog.warn({ runId: run.id, errorName: err instanceof Error ? err.name : typeof err }, "could not read the agent's secrets to check a run's request; withholding it");
|
|
39
|
+
return { requestUnavailable: true };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** The issue-tracker sites issue links are built from (the admin viewer's own source); none when unset or invalid. */
|
|
43
|
+
function issueSites() {
|
|
44
|
+
try {
|
|
45
|
+
const jira = loadJiraConfig();
|
|
46
|
+
return jira ? { jira: jira.siteUrl } : {};
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What started each run and what it produced, in the admin viewer API's shapes (docs/viewer-api.md):
|
|
54
|
+
* `triggerInfo` (the pull request, issue, schedule or lead behind it), `outcomes` (pull requests opened,
|
|
55
|
+
* comments, checks) and `serviceReadiness` (each coding-run service's state). Only ids and links:
|
|
56
|
+
* repositories, numbers, issue keys, URLs, states and times, never free text. A service's failure
|
|
57
|
+
* `reason` is left out, since it can quote what the service printed.
|
|
58
|
+
*/
|
|
59
|
+
async function runGraphFields(ctx, runIds) {
|
|
60
|
+
const graph = await loadGraphRuns(ctx.db, runIds, issueSites());
|
|
61
|
+
return new Map(graph.map((run) => [
|
|
62
|
+
run.id,
|
|
63
|
+
{
|
|
64
|
+
triggerInfo: run.trigger,
|
|
65
|
+
outcomes: run.outcomes,
|
|
66
|
+
serviceReadiness: run.services.map(({ name, state, attempts, readyAt, failedAt }) => ({
|
|
67
|
+
name,
|
|
68
|
+
state,
|
|
69
|
+
attempts,
|
|
70
|
+
readyAt,
|
|
71
|
+
failedAt,
|
|
72
|
+
})),
|
|
73
|
+
},
|
|
74
|
+
]));
|
|
75
|
+
}
|
|
76
|
+
/** Statuses `active: true` keeps, and that `since` always keeps. */
|
|
77
|
+
const ACTIVE_STATUSES = ["pending", "running"];
|
|
78
|
+
/** Rows returned without an agentId: a default and a cap, since it spans every agent the caller can see. */
|
|
79
|
+
const ACROSS_AGENTS_DEFAULT_LIMIT = 50;
|
|
80
|
+
const ACROSS_AGENTS_MAX_LIMIT = 200;
|
|
81
|
+
/** The `active` and `since` filters, as clauses to AND onto the visibility rule. */
|
|
82
|
+
function runFilters(args) {
|
|
83
|
+
const filters = [];
|
|
84
|
+
if (args.active === true)
|
|
85
|
+
filters.push({ status: { in: ACTIVE_STATUSES } });
|
|
86
|
+
if (args.since !== undefined) {
|
|
87
|
+
const since = new Date(args.since);
|
|
88
|
+
if (Number.isNaN(since.getTime()))
|
|
89
|
+
throw new McpError(400, "since must be an ISO date or time");
|
|
90
|
+
filters.push({
|
|
91
|
+
OR: [{ startedAt: { gte: since } }, { finishedAt: { gte: since } }, { status: { in: ACTIVE_STATUSES } }],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return filters;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Runs across every agent the caller can read, under the same rule as one agent's listing: all runs of
|
|
98
|
+
* agents they own, only the runs they triggered on the rest. With `all` (admin:view, checked by the
|
|
99
|
+
* caller), every run in the deployment, as the admin viewer shows. Rows name their agent and leave
|
|
100
|
+
* out finalText, which can be long and is one get_run away.
|
|
101
|
+
*/
|
|
102
|
+
async function listRunsAcrossAgents(ctx, filters, opts) {
|
|
103
|
+
const [where, accessOf] = await Promise.all([
|
|
104
|
+
opts.all ? Promise.resolve({}) : readableAgentsWhere(ctx),
|
|
105
|
+
agentAccessResolver(ctx),
|
|
106
|
+
]);
|
|
107
|
+
const agents = await ctx.db.agent.findMany({
|
|
108
|
+
where,
|
|
109
|
+
select: { id: true, ownerId: true, name: true, budgetUsd: true },
|
|
110
|
+
});
|
|
111
|
+
const owned = agents.filter((agent) => opts.all || accessOf(agent) === "owner").map((agent) => agent.id);
|
|
112
|
+
const others = agents.filter((agent) => !opts.all && accessOf(agent) !== "owner").map((agent) => agent.id);
|
|
113
|
+
const byId = new Map(agents.map((agent) => [agent.id, agent]));
|
|
114
|
+
// A limit that isn't a finite number falls back to the default rather than reaching the query as NaN.
|
|
115
|
+
const requested = Number.isFinite(opts.limit) ? Math.floor(opts.limit) : ACROSS_AGENTS_DEFAULT_LIMIT;
|
|
116
|
+
const take = Math.min(Math.max(1, requested), ACROSS_AGENTS_MAX_LIMIT);
|
|
117
|
+
const status = opts.status;
|
|
118
|
+
const runs = await ctx.db.run.findMany({
|
|
119
|
+
where: {
|
|
120
|
+
OR: [{ agentId: { in: owned } }, { agentId: { in: others }, triggeredById: ctx.principal.id }],
|
|
121
|
+
...(status ? { status: status } : {}),
|
|
122
|
+
...(filters.length > 0 ? { AND: filters } : {}),
|
|
123
|
+
},
|
|
124
|
+
omit: { finalText: true, pricingSnapshot: true },
|
|
125
|
+
take,
|
|
126
|
+
orderBy: { startedAt: "desc" },
|
|
127
|
+
});
|
|
128
|
+
const graph = await runGraphFields(ctx, runs.map((run) => run.id));
|
|
129
|
+
return runs.map((run) => {
|
|
130
|
+
const agent = byId.get(run.agentId);
|
|
131
|
+
return {
|
|
132
|
+
...run,
|
|
133
|
+
agentName: agent?.name ?? null,
|
|
134
|
+
agentBudgetUsd: agent ? Number(agent.budgetUsd) : null,
|
|
135
|
+
...(graph.get(run.id) ?? { triggerInfo: null, outcomes: [], serviceReadiness: [] }),
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/** Same rule as get_run: a pending coding run with queuedAt is waiting for a concurrency slot (CODING_MAX_CONCURRENT), not stuck. */
|
|
140
|
+
async function withQueuedAt(ctx, runs) {
|
|
141
|
+
const pendingIds = runs.filter((run) => run.status === "pending").map((run) => run.id);
|
|
142
|
+
const queued = pendingIds.length > 0
|
|
143
|
+
? await ctx.db.codingRun.findMany({
|
|
144
|
+
where: { runId: { in: pendingIds }, queuedAt: { not: null } },
|
|
145
|
+
select: { runId: true, queuedAt: true },
|
|
146
|
+
})
|
|
147
|
+
: [];
|
|
148
|
+
const queuedAtByRun = new Map(queued.map((row) => [row.runId, row.queuedAt?.toISOString()]));
|
|
149
|
+
return runs.map((run) => {
|
|
150
|
+
const codingQueuedAt = queuedAtByRun.get(run.id);
|
|
151
|
+
return codingQueuedAt ? { ...withOperatorMode(run), codingQueuedAt } : withOperatorMode(run);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
18
154
|
export function registerRunTools(mcp) {
|
|
19
155
|
mcp.registerTool({
|
|
20
156
|
name: "list_runs",
|
|
21
157
|
scope: "agents:read",
|
|
158
|
+
description: "Lists runs, newest first. With agentId, that agent's runs; without it, runs across every agent you can see " +
|
|
159
|
+
"(each row adds agentName, agentBudgetUsd, triggerInfo, outcomes and serviceReadiness, and leaves out finalText " +
|
|
160
|
+
`and pricingSnapshot; default limit ${ACROSS_AGENTS_DEFAULT_LIMIT}, ` +
|
|
161
|
+
`at most ${ACROSS_AGENTS_MAX_LIMIT}). You see all runs of agents you own and only the runs you triggered on others. ` +
|
|
162
|
+
"active: true keeps pending and running runs; since (ISO time) keeps runs that started or finished then or later, " +
|
|
163
|
+
"plus any still pending or running. all: true (no agentId) lists every run in the deployment (admins only, admin:view). " +
|
|
164
|
+
"Use get_run for one run's detail.",
|
|
22
165
|
inputSchema: {
|
|
23
166
|
type: "object",
|
|
24
|
-
properties: {
|
|
25
|
-
|
|
167
|
+
properties: {
|
|
168
|
+
agentId: { type: "string" },
|
|
169
|
+
status: { type: "string" },
|
|
170
|
+
active: { type: "boolean" },
|
|
171
|
+
since: { type: "string", description: "ISO date or time" },
|
|
172
|
+
all: { type: "boolean" },
|
|
173
|
+
limit: { type: "number" },
|
|
174
|
+
},
|
|
26
175
|
},
|
|
27
176
|
handler: async (args, ctx) => {
|
|
177
|
+
const filters = runFilters(args);
|
|
178
|
+
if (args.all === true) {
|
|
179
|
+
if (args.agentId !== undefined)
|
|
180
|
+
throw new McpError(400, "all lists every agent's runs; leave out agentId");
|
|
181
|
+
if (!ctx.operator)
|
|
182
|
+
requireScope(ctx, ctx.canonicalUri, "admin:view");
|
|
183
|
+
}
|
|
184
|
+
if (args.agentId === undefined) {
|
|
185
|
+
const runs = await listRunsAcrossAgents(ctx, filters, {
|
|
186
|
+
status: args.status,
|
|
187
|
+
limit: args.limit,
|
|
188
|
+
all: args.all === true,
|
|
189
|
+
});
|
|
190
|
+
return textResult(await withQueuedAt(ctx, runs));
|
|
191
|
+
}
|
|
28
192
|
const { access } = await requireAgentAccess(ctx, args.agentId, "read");
|
|
29
193
|
const runs = await ctx.db.run.findMany({
|
|
30
194
|
where: {
|
|
31
195
|
agentId: args.agentId,
|
|
32
196
|
...(args.status ? { status: args.status } : {}),
|
|
33
197
|
...(access === "owner" ? {} : { triggeredById: ctx.principal.id }),
|
|
198
|
+
...(filters.length > 0 ? { AND: filters } : {}),
|
|
34
199
|
},
|
|
35
200
|
take: args.limit,
|
|
36
201
|
orderBy: { startedAt: "desc" },
|
|
37
202
|
});
|
|
38
|
-
|
|
39
|
-
// for a concurrency slot (CODING_MAX_CONCURRENT), not stuck.
|
|
40
|
-
const pendingIds = runs.filter((run) => run.status === "pending").map((run) => run.id);
|
|
41
|
-
const queued = pendingIds.length > 0
|
|
42
|
-
? await ctx.db.codingRun.findMany({
|
|
43
|
-
where: { runId: { in: pendingIds }, queuedAt: { not: null } },
|
|
44
|
-
select: { runId: true, queuedAt: true },
|
|
45
|
-
})
|
|
46
|
-
: [];
|
|
47
|
-
const queuedAtByRun = new Map(queued.map((row) => [row.runId, row.queuedAt?.toISOString()]));
|
|
48
|
-
return textResult(runs.map((run) => {
|
|
49
|
-
const codingQueuedAt = queuedAtByRun.get(run.id);
|
|
50
|
-
return codingQueuedAt ? { ...withOperatorMode(run), codingQueuedAt } : withOperatorMode(run);
|
|
51
|
-
}));
|
|
203
|
+
return textResult(await withQueuedAt(ctx, runs));
|
|
52
204
|
},
|
|
53
205
|
});
|
|
54
206
|
mcp.registerTool({
|
|
55
207
|
name: "get_run",
|
|
56
208
|
scope: "agents:read",
|
|
209
|
+
description: "One run in full: status, cost, request, result, errors and budget events, plus triggerInfo (what started it: " +
|
|
210
|
+
"a pull request review or mention, an issue, a schedule or a lead run), outcomes (pull requests opened, comments, " +
|
|
211
|
+
"checks) and serviceReadiness. A run's pull-request outcome matching another run's triggerInfo links the two.",
|
|
57
212
|
inputSchema: { type: "object", properties: { runId: { type: "string" } }, required: ["runId"] },
|
|
58
213
|
handler: async (args, ctx) => {
|
|
59
|
-
const run = await ctx.db.run.findUnique({ where: { id: args.runId } });
|
|
60
|
-
const notFound = new McpError(404, `Run "${args.runId}" not found.`);
|
|
61
|
-
if (!run)
|
|
62
|
-
throw notFound;
|
|
63
214
|
// Its triggerer keeps seeing it even after losing access to the agent;
|
|
64
215
|
// otherwise only the agent's owner. read on the agent is not enough.
|
|
65
|
-
|
|
66
|
-
const agent = await ctx.db.agent.findUnique({ where: { id: run.agentId } });
|
|
67
|
-
if (!agent || (await agentAccess(ctx, agent)) !== "owner")
|
|
68
|
-
throw notFound;
|
|
69
|
-
}
|
|
216
|
+
const run = await findReadableRun(ctx, args.runId);
|
|
70
217
|
const codingRun = await ctx.db.codingRun.findUnique({
|
|
71
218
|
where: { runId: run.id },
|
|
72
219
|
select: {
|
|
73
220
|
result: true,
|
|
221
|
+
request: true,
|
|
74
222
|
queuedAt: true,
|
|
75
223
|
failureCategory: true,
|
|
76
224
|
diagnosticId: true,
|
|
77
225
|
debugTrace: true,
|
|
226
|
+
debugTraceMaxMb: true,
|
|
78
227
|
services: true,
|
|
79
228
|
resultBranch: true,
|
|
80
229
|
baseSha: true,
|
|
230
|
+
dependencyCacheResult: true,
|
|
231
|
+
prDescriptionOutcome: true,
|
|
232
|
+
packageAuditMode: true,
|
|
233
|
+
budgetBaseUsd: true,
|
|
234
|
+
budgetReservedUsd: true,
|
|
235
|
+
budgetLimitUsd: true,
|
|
81
236
|
},
|
|
82
237
|
});
|
|
83
238
|
const localPr = await ctx.db.localPullRequest.findUnique({
|
|
@@ -102,6 +257,12 @@ export function registerRunTools(mcp) {
|
|
|
102
257
|
refused: countPlanRefusals(registryFetches),
|
|
103
258
|
}
|
|
104
259
|
: undefined;
|
|
260
|
+
// Package audit mode, fixed at dispatch: what the registry served only
|
|
261
|
+
// because of it (empty when nothing was), read only for a run that had it.
|
|
262
|
+
const packageAuditMode = codingRun?.packageAuditMode ?? null;
|
|
263
|
+
const packageAudit = packageAuditMode
|
|
264
|
+
? summarizePackageAudit(await ctx.db.registryAuditRecord.findMany({ where: { runId: run.id }, orderBy: { createdAt: "asc" } })).entries
|
|
265
|
+
: undefined;
|
|
105
266
|
// A failed coding run's error is only an opaque id; these two say which
|
|
106
267
|
// stage failed and are the key an operator searches the control-plane
|
|
107
268
|
// log for (the real reason is logged there, never persisted). Both are
|
|
@@ -110,17 +271,37 @@ export function registerRunTools(mcp) {
|
|
|
110
271
|
const diagnosticId = codingRun?.diagnosticId ?? undefined;
|
|
111
272
|
// Which services the run started with ("postgres 16"); never their images or environments.
|
|
112
273
|
const services = codingRun ? storedServiceLabels(codingRun.services) : [];
|
|
274
|
+
// The worker's dependency cache report (stable codes, folder names and numbers only), re-bounded on read.
|
|
275
|
+
const dependencyCache = codingRun?.dependencyCacheResult !== null && codingRun?.dependencyCacheResult !== undefined
|
|
276
|
+
? sanitizeDependencyCacheReport(codingRun.dependencyCacheResult)
|
|
277
|
+
: undefined;
|
|
278
|
+
// Well-formed refusals only, with group, lead and agent budget filled as the run error's are.
|
|
279
|
+
const budgetEvents = await loadBudgetEvents(ctx.db, run.id);
|
|
280
|
+
// What started the run and what it produced, as the admin viewer shows them.
|
|
281
|
+
const graph = (await runGraphFields(ctx, [run.id])).get(run.id);
|
|
282
|
+
// What the run was asked to do: the trigger's or lead's task, without the agent's standing instructions.
|
|
283
|
+
const request = codingRun?.request ? await requestForReader(ctx, run, codingRun.request) : {};
|
|
113
284
|
return textResult({
|
|
114
285
|
...withOperatorMode(run),
|
|
286
|
+
...request,
|
|
287
|
+
...(graph ?? {}),
|
|
288
|
+
...(budgetEvents.length > 0 ? { budgetEvents } : {}),
|
|
289
|
+
...(codingRun?.budgetReservedUsd != null ? { budgetReservedUsd: Number(codingRun.budgetReservedUsd) } : {}),
|
|
290
|
+
...(codingRun?.budgetBaseUsd != null ? { budgetBaseUsd: Number(codingRun.budgetBaseUsd) } : {}),
|
|
291
|
+
// The most overrun top-ups may raise budgetReservedUsd to; absent = the reservation is the limit.
|
|
292
|
+
...(codingRun?.budgetLimitUsd != null ? { budgetLimitUsd: Number(codingRun.budgetLimitUsd) } : {}),
|
|
115
293
|
...(codingResult ? { codingResult } : {}),
|
|
116
294
|
...(failureCategory ? { failureCategory } : {}),
|
|
117
295
|
...(diagnosticId ? { diagnosticId } : {}),
|
|
118
296
|
...(codingQueuedAt ? { codingQueuedAt } : {}),
|
|
119
297
|
// An admin turned on the debug trace: the worker's full trace is in its pod log.
|
|
120
298
|
...(codingRun?.debugTrace ? { debugTrace: true } : {}),
|
|
299
|
+
...(codingRun?.debugTrace && codingRun.debugTraceMaxMb ? { debugTraceMaxMb: codingRun.debugTraceMaxMb } : {}),
|
|
121
300
|
...(services.length > 0 ? { services } : {}),
|
|
122
301
|
...(codingRun?.resultBranch ? { resultBranch: codingRun.resultBranch } : {}),
|
|
123
302
|
...(codingRun?.baseSha ? { baseSha: codingRun.baseSha } : {}),
|
|
303
|
+
...(dependencyCache ? { dependencyCache } : {}),
|
|
304
|
+
...(codingRun?.prDescriptionOutcome ? { prDescriptionOutcome: codingRun.prDescriptionOutcome } : {}),
|
|
124
305
|
...(localPr
|
|
125
306
|
? {
|
|
126
307
|
review: {
|
|
@@ -137,6 +318,7 @@ export function registerRunTools(mcp) {
|
|
|
137
318
|
}
|
|
138
319
|
: {}),
|
|
139
320
|
...(codingRun ? { packages, packageRefusals, packagePlan } : {}),
|
|
321
|
+
...(packageAuditMode ? { packageAuditMode, packageAudit } : {}),
|
|
140
322
|
});
|
|
141
323
|
},
|
|
142
324
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Prisma } from "#prisma";
|
|
2
2
|
import { validateCronExpression } from "../../core/cron.js";
|
|
3
|
+
import { scheduleStartFields } from "../../core/schedule-start.js";
|
|
3
4
|
import { McpError } from "../errors.js";
|
|
4
5
|
import { assertAgentAccess, requireAgentAccess } from "../auth/access.js";
|
|
5
6
|
import { textResult } from "./text-result.js";
|
|
@@ -24,9 +25,10 @@ export function registerSchedulingTools(mcp) {
|
|
|
24
25
|
if (existing.kind === "coding" && !existing.codingProfile?.defaultTask) {
|
|
25
26
|
throw new McpError(400, "A default task is required before enabling a coding-agent schedule.");
|
|
26
27
|
}
|
|
28
|
+
const next = { schedule: args.schedule, timezone: args.timezone, scheduleEnabled: true };
|
|
27
29
|
return tx.agent.update({
|
|
28
30
|
where: { id: args.agentId },
|
|
29
|
-
data: {
|
|
31
|
+
data: { ...next, ...(await scheduleStartFields(tx, existing, next)) },
|
|
30
32
|
});
|
|
31
33
|
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
32
34
|
return textResult(agent);
|
|
@@ -123,6 +123,11 @@ export async function handleSecretElicitationForm(method, token, readFormBody, r
|
|
|
123
123
|
if (!token)
|
|
124
124
|
throw new Error("missing token");
|
|
125
125
|
payload = await deps.verify(token);
|
|
126
|
+
// Other links share the signing key (a run transcript download, a tool's
|
|
127
|
+
// requestState): only a secret link's kind opens this form.
|
|
128
|
+
if (payload.kind !== undefined && payload.kind !== "create" && payload.kind !== "unbroker") {
|
|
129
|
+
throw new Error("not a secret link");
|
|
130
|
+
}
|
|
126
131
|
}
|
|
127
132
|
catch {
|
|
128
133
|
html(res, 400, "<h1>Link expired</h1><p>This link is invalid or has expired. Return to your MCP client and try again.</p>");
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { SecretFormDeps } from "./secret-elicitation-form.js";
|
|
2
|
+
import { type StdioLinkHandler } from "../transport/stdio-link-host.js";
|
|
3
|
+
/** The loopback path the form is served at on stdio. */
|
|
4
|
+
export declare const STDIO_SECRET_PATH = "/secret";
|
|
2
5
|
export interface StdioSecretElicitationHost {
|
|
3
6
|
urlFor(token: string): Promise<string>;
|
|
4
7
|
}
|
|
8
|
+
/** The secret form as a link-host route. */
|
|
9
|
+
export declare function secretFormRoute(deps: SecretFormDeps): StdioLinkHandler;
|
|
10
|
+
/** A link host serving only the secret form (the composition root shares one host across routes). */
|
|
5
11
|
export declare function createStdioSecretElicitationHost(deps: SecretFormDeps): StdioSecretElicitationHost;
|
|
@@ -1,64 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* stdio-only host for the secret-entry form: an ephemeral, loopback-only
|
|
3
|
-
* HTTP server, started lazily on the first pending elicitation and closed
|
|
4
|
-
* after a period of inactivity. stdio's MCP process has no other listener
|
|
5
|
-
* (see mcp/transport/stdio.ts) — this one exists only while a secret entry
|
|
6
|
-
* is actually pending, consistent with stdio's "no token; the operator is
|
|
7
|
-
* trusted" model: reachable only from this same machine, and only a holder
|
|
8
|
-
* of the one-time signed URL can do anything with it.
|
|
9
|
-
*/
|
|
10
|
-
import { createServer } from "node:http";
|
|
11
1
|
import { handleSecretElicitationForm, readFormBody } from "./secret-elicitation-form.js";
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
import { createStdioLinkHost } from "../transport/stdio-link-host.js";
|
|
3
|
+
/** The loopback path the form is served at on stdio. */
|
|
4
|
+
export const STDIO_SECRET_PATH = "/secret";
|
|
5
|
+
/** The secret form as a link-host route. */
|
|
6
|
+
export function secretFormRoute(deps) {
|
|
7
|
+
return (req, res, token) => handleSecretElicitationForm(req.method, token, () => readFormBody(req), res, deps);
|
|
8
|
+
}
|
|
9
|
+
/** A link host serving only the secret form (the composition root shares one host across routes). */
|
|
14
10
|
export function createStdioSecretElicitationHost(deps) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let idleTimer;
|
|
18
|
-
function scheduleIdleShutdown() {
|
|
19
|
-
if (idleTimer)
|
|
20
|
-
clearTimeout(idleTimer);
|
|
21
|
-
idleTimer = setTimeout(() => {
|
|
22
|
-
server?.close();
|
|
23
|
-
server = undefined;
|
|
24
|
-
}, IDLE_SHUTDOWN_MS);
|
|
25
|
-
idleTimer.unref();
|
|
26
|
-
}
|
|
27
|
-
async function ensureServer() {
|
|
28
|
-
if (server) {
|
|
29
|
-
scheduleIdleShutdown();
|
|
30
|
-
return port;
|
|
31
|
-
}
|
|
32
|
-
const s = createServer((req, res) => {
|
|
33
|
-
scheduleIdleShutdown();
|
|
34
|
-
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
35
|
-
if (url.pathname !== "/secret") {
|
|
36
|
-
res.writeHead(404).end();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
// A failure here must never become an unhandled rejection: that would take
|
|
40
|
-
// down the whole stdio MCP process, not just this request.
|
|
41
|
-
handleSecretElicitationForm(req.method, url.searchParams.get("t"), () => readFormBody(req), res, deps).catch(() => {
|
|
42
|
-
if (!res.headersSent)
|
|
43
|
-
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
44
|
-
if (!res.writableEnded)
|
|
45
|
-
res.end("Internal error");
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
server = s;
|
|
49
|
-
await new Promise((resolve, reject) => {
|
|
50
|
-
s.once("error", reject);
|
|
51
|
-
s.listen(0, "127.0.0.1", resolve);
|
|
52
|
-
});
|
|
53
|
-
const address = s.address();
|
|
54
|
-
port = typeof address === "object" && address ? address.port : 0;
|
|
55
|
-
scheduleIdleShutdown();
|
|
56
|
-
return port;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
async urlFor(token) {
|
|
60
|
-
const p = await ensureServer();
|
|
61
|
-
return `http://127.0.0.1:${p}/secret?t=${encodeURIComponent(token)}`;
|
|
62
|
-
},
|
|
63
|
-
};
|
|
11
|
+
const host = createStdioLinkHost({ [STDIO_SECRET_PATH]: secretFormRoute(deps) });
|
|
12
|
+
return { urlFor: (token) => host.urlFor(STDIO_SECRET_PATH, token) };
|
|
64
13
|
}
|
|
@@ -38,6 +38,8 @@ export interface SecretElicitationPayload {
|
|
|
38
38
|
* removal is refused if the secret's config has changed since (or the link has none).
|
|
39
39
|
*/
|
|
40
40
|
brokerHash?: string;
|
|
41
|
+
/** The caller passed the hosts:admin check when the link was minted. */
|
|
42
|
+
hostManager?: boolean;
|
|
41
43
|
}
|
|
42
44
|
export type SecretElicitationOutcome = {
|
|
43
45
|
ok: true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSecret, listSecrets, setSecretBroker } from "../../core/secrets.js";
|
|
1
|
+
import { createSecret, hostProvidersUsingSecret, listSecrets, setSecretBroker, } from "../../core/secrets.js";
|
|
2
2
|
import { parseSecretBrokerConfig, SECRET_BROKER_CHANGED, } from "../../core/secret-broker-config.js";
|
|
3
3
|
/**
|
|
4
4
|
* A link only does what it was minted for: a create link can't confirm an
|
|
@@ -19,6 +19,18 @@ function kindOf(outcome) {
|
|
|
19
19
|
return outcome.kind ?? "create";
|
|
20
20
|
}
|
|
21
21
|
const ELICITATION_TTL_MS = 600_000;
|
|
22
|
+
const HOST_PROVIDER_LINK_STALE = "host_provider_secret_link_stale: a host provider now uses this secret; request a new link with hosts:admin";
|
|
23
|
+
/**
|
|
24
|
+
* A link minted before the secret joined a host provider carries no hosts:admin check.
|
|
25
|
+
* Refused without recording an outcome, so a fresh link from a host manager still works.
|
|
26
|
+
*/
|
|
27
|
+
async function isStaleForHostProvider(db, payload) {
|
|
28
|
+
if (payload.hostManager)
|
|
29
|
+
return false;
|
|
30
|
+
const { ownerId, secretName: name } = payload;
|
|
31
|
+
const secret = await db.secret.findUnique({ where: { ownerId_name: { ownerId, name } }, select: { id: true } });
|
|
32
|
+
return secret !== null && (await hostProvidersUsingSecret(db, secret.id)).providers.length > 0;
|
|
33
|
+
}
|
|
22
34
|
/** Sweeps every expired entry, not just the one being looked up, so a set-and-never-polled-again entry doesn't linger forever. */
|
|
23
35
|
async function pruneExpired(db, now) {
|
|
24
36
|
await db.secretElicitationOutcome.deleteMany({ where: { expiresAt: { lte: now } } });
|
|
@@ -58,6 +70,9 @@ export async function fulfillSecretElicitation(payload, value, cipher, db, broke
|
|
|
58
70
|
});
|
|
59
71
|
if (existing && kindOf(existing.outcome) === "create")
|
|
60
72
|
return existing.outcome;
|
|
73
|
+
if (await isStaleForHostProvider(db, payload)) {
|
|
74
|
+
return { ok: false, kind: "create", error: HOST_PROVIDER_LINK_STALE };
|
|
75
|
+
}
|
|
61
76
|
let outcome;
|
|
62
77
|
try {
|
|
63
78
|
const secret = await createSecret(secretName, value, ownerId, cipher, db, {
|
|
@@ -89,7 +104,12 @@ export async function fulfillSecretElicitation(payload, value, cipher, db, broke
|
|
|
89
104
|
await recordOutcome(db, ownerId, secretName, outcome, now);
|
|
90
105
|
return outcome;
|
|
91
106
|
}
|
|
92
|
-
const CORRECTABLE_ERRORS = [
|
|
107
|
+
const CORRECTABLE_ERRORS = [
|
|
108
|
+
"secret_broker_value_invalid",
|
|
109
|
+
"secret_broker_config_invalid",
|
|
110
|
+
"bridge_input_limit",
|
|
111
|
+
"host_provider_webhook_secret_short",
|
|
112
|
+
];
|
|
93
113
|
function isCorrectableInput(message) {
|
|
94
114
|
return CORRECTABLE_ERRORS.some((code) => message.startsWith(code));
|
|
95
115
|
}
|
|
@@ -115,6 +135,9 @@ export async function fulfillUnbrokerElicitation(payload, cipher, db) {
|
|
|
115
135
|
});
|
|
116
136
|
if (existing && kindOf(existing.outcome) === "unbroker")
|
|
117
137
|
return existing.outcome;
|
|
138
|
+
if (await isStaleForHostProvider(db, payload)) {
|
|
139
|
+
return { ok: false, kind: "unbroker", error: HOST_PROVIDER_LINK_STALE };
|
|
140
|
+
}
|
|
118
141
|
let outcome;
|
|
119
142
|
try {
|
|
120
143
|
await setSecretBroker(db, cipher, {
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* value/ciphertext at the source); this file adds ownership on top.
|
|
5
5
|
*/
|
|
6
6
|
import { inputRequired, inputResponse } from "@modelcontextprotocol/server";
|
|
7
|
-
import { createSecret, listSecrets, attachSecret, detachSecret, deleteSecret, setSecretBroker, } from "../../core/secrets.js";
|
|
7
|
+
import { createSecret, listSecrets, attachSecret, detachSecret, deleteSecret, hostProvidersUsingSecret, setSecretBroker, WEBHOOK_SECRET_ATTACHED, } from "../../core/secrets.js";
|
|
8
|
+
import { mcpServersUsingSecret } from "../../core/mcp-servers.js";
|
|
8
9
|
import { brokerConfigHash, parseSecretBrokerConfig } from "../../core/secret-broker-config.js";
|
|
9
10
|
import { McpError } from "../errors.js";
|
|
10
11
|
import { requireOwnedSecret } from "../auth/ownership.js";
|
|
11
|
-
import {
|
|
12
|
+
import { requireScope } from "../auth/resource-server.js";
|
|
13
|
+
import { requireAgentAccess, requireBindable, requireBindingOwner } from "../auth/access.js";
|
|
12
14
|
import { clearUnbrokerOutcome, getSecretElicitationOutcome, } from "./secret-elicitation.js";
|
|
13
15
|
import { allowedSecretNames, brokerCompatibilityWarnings } from "./secret-broker-compat.js";
|
|
14
16
|
import { textResult } from "./text-result.js";
|
|
@@ -27,7 +29,9 @@ function mapSecretError(err) {
|
|
|
27
29
|
const message = err instanceof Error ? err.message : String(err);
|
|
28
30
|
if (message.startsWith("secret_not_found"))
|
|
29
31
|
throw new McpError(404, message);
|
|
30
|
-
if (message.startsWith("secret_broker_config_invalid") ||
|
|
32
|
+
if (message.startsWith("secret_broker_config_invalid") ||
|
|
33
|
+
message.startsWith("secret_broker_value_invalid") ||
|
|
34
|
+
message.startsWith("host_provider_webhook_secret_short")) {
|
|
31
35
|
throw new McpError(400, message);
|
|
32
36
|
}
|
|
33
37
|
throw err;
|
|
@@ -70,11 +74,26 @@ async function findOwnSecret(ctx, name) {
|
|
|
70
74
|
throw new McpError(404, `secret_not_found: no secret named "${name}"`);
|
|
71
75
|
return secret;
|
|
72
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* A host provider's secret needs hosts:admin as well as secrets:write: replacing a credential
|
|
79
|
+
* or its broker config changes what the provider does. True when the caller passed that check,
|
|
80
|
+
* and its links say so; checked before any link is minted, and again when one is submitted.
|
|
81
|
+
*/
|
|
82
|
+
async function requireHostsAdminIfProviderSecret(ctx, name) {
|
|
83
|
+
const secret = await ctx.db.secret.findUnique({
|
|
84
|
+
where: { ownerId_name: { ownerId: ctx.principal.id, name } },
|
|
85
|
+
select: { id: true },
|
|
86
|
+
});
|
|
87
|
+
if (!secret || (await hostProvidersUsingSecret(ctx.db, secret.id)).providers.length === 0)
|
|
88
|
+
return false;
|
|
89
|
+
requireScope(ctx, ctx.canonicalUri, "hosts:admin");
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
73
92
|
export function registerSecretsTools(mcp, opts) {
|
|
74
93
|
mcp.registerTool({
|
|
75
94
|
name: "create_secret",
|
|
76
95
|
scope: "secrets:write",
|
|
77
|
-
description: 'Creates a secret. Omit "value" to enter it securely via a one-time browser link instead of passing it as a plaintext argument. Pass "broker" ({ hosts, pathPrefixes?, placement }) to make it brokered: tools can then only send it to those hosts with fetch(url, { secrets: [name] }) and can never read it. See the help article "brokered-secrets".',
|
|
96
|
+
description: 'Creates a secret. Omit "value" to enter it securely via a one-time browser link instead of passing it as a plaintext argument. Pass "broker" ({ hosts, pathPrefixes?, placement }) to make it brokered: tools can then only send it to those hosts with fetch(url, { secrets: [name] }) and can never read it. A secret a host provider uses also needs hosts:admin. See the help article "brokered-secrets".',
|
|
78
97
|
inputSchema: {
|
|
79
98
|
type: "object",
|
|
80
99
|
properties: {
|
|
@@ -85,6 +104,7 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
85
104
|
required: ["name"],
|
|
86
105
|
},
|
|
87
106
|
handler: async (args, ctx) => {
|
|
107
|
+
const hostManager = await requireHostsAdminIfProviderSecret(ctx, args.name);
|
|
88
108
|
const broker = args.broker === undefined ? undefined : parseBrokerArg(args.broker);
|
|
89
109
|
if (args.value !== undefined) {
|
|
90
110
|
let secret;
|
|
@@ -131,6 +151,7 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
131
151
|
secretName: args.name,
|
|
132
152
|
kind: "create",
|
|
133
153
|
...(broker ? { broker } : {}),
|
|
154
|
+
...(hostManager ? { hostManager } : {}),
|
|
134
155
|
};
|
|
135
156
|
const token = await mcp.mintRequestState(payload);
|
|
136
157
|
const url = await opts.buildElicitationUrl(token);
|
|
@@ -157,7 +178,7 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
157
178
|
mcp.registerTool({
|
|
158
179
|
name: "set_secret_broker",
|
|
159
180
|
scope: "secrets:write",
|
|
160
|
-
description: 'Sets or changes a secret\'s broker config ({ hosts, pathPrefixes?, placement }), so tools can only send it to those hosts with fetch(url, { secrets: [name] }) and never read it. broker: null removes brokering, which must be confirmed by a person in the browser link this returns. Every change is audited. See the help article "brokered-secrets".',
|
|
181
|
+
description: 'Sets or changes a secret\'s broker config ({ hosts, pathPrefixes?, placement }), so tools can only send it to those hosts with fetch(url, { secrets: [name] }) and never read it. broker: null removes brokering, which must be confirmed by a person in the browser link this returns. Every change is audited. A secret a host provider uses also needs hosts:admin. See the help article "brokered-secrets".',
|
|
161
182
|
inputSchema: {
|
|
162
183
|
type: "object",
|
|
163
184
|
properties: {
|
|
@@ -171,6 +192,7 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
171
192
|
},
|
|
172
193
|
handler: async (args, ctx) => {
|
|
173
194
|
const ownerId = ctx.principal.id;
|
|
195
|
+
const hostManager = await requireHostsAdminIfProviderSecret(ctx, args.name);
|
|
174
196
|
if (args.broker !== null) {
|
|
175
197
|
const broker = parseBrokerArg(args.broker);
|
|
176
198
|
await findOwnSecret(ctx, args.name);
|
|
@@ -222,6 +244,7 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
222
244
|
secretName: args.name,
|
|
223
245
|
kind: "unbroker",
|
|
224
246
|
brokerHash: brokerConfigHash(parseSecretBrokerConfig(secret.broker)),
|
|
247
|
+
...(hostManager ? { hostManager } : {}),
|
|
225
248
|
};
|
|
226
249
|
const token = await mcp.mintRequestState(payload);
|
|
227
250
|
const url = await opts.buildElicitationUrl(token);
|
|
@@ -269,13 +292,15 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
269
292
|
// (A2/S2-2): a binding hands the value to every tool the owner lets
|
|
270
293
|
// read it. Not even the stdio operator crosses owners.
|
|
271
294
|
const { agent } = await requireAgentAccess(ctx, args.agentId, "read");
|
|
272
|
-
|
|
295
|
+
await requireBindable(ctx, agent);
|
|
273
296
|
const ownerId = agent.ownerId;
|
|
274
297
|
try {
|
|
275
298
|
// Resolved among the agent owner's secrets (== the caller here).
|
|
276
299
|
await attachSecret(args.agentId, args.name, ownerId, ctx.db, args.alias ?? args.name);
|
|
277
300
|
}
|
|
278
301
|
catch (err) {
|
|
302
|
+
if (err instanceof Error && err.message === WEBHOOK_SECRET_ATTACHED)
|
|
303
|
+
throw new McpError(409, err.message);
|
|
279
304
|
throw new McpError(404, err instanceof Error ? err.message : String(err));
|
|
280
305
|
}
|
|
281
306
|
return textResult({ attached: true });
|
|
@@ -306,6 +331,15 @@ export function registerSecretsTools(mcp, opts) {
|
|
|
306
331
|
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
|
|
307
332
|
handler: async (args, ctx) => {
|
|
308
333
|
await requireOwnedSecret(ctx.db, args.id, ctx.principal.id);
|
|
334
|
+
const { providers } = await hostProvidersUsingSecret(ctx.db, args.id);
|
|
335
|
+
if (providers.length > 0) {
|
|
336
|
+
throw new McpError(409, `This secret is used by host provider ${providers.map((p) => `"${p}"`).join(", ")}; re-register it or delete_host_provider first.`);
|
|
337
|
+
}
|
|
338
|
+
const secret = await ctx.db.secret.findUnique({ where: { id: args.id }, select: { name: true } });
|
|
339
|
+
const servers = secret ? await mcpServersUsingSecret(ctx.db, ctx.principal.id, secret.name) : [];
|
|
340
|
+
if (servers.length > 0) {
|
|
341
|
+
throw new McpError(409, `Secret "${secret.name}" is used by MCP server(s) ${servers.join(", ")}; update or delete them first.`);
|
|
342
|
+
}
|
|
309
343
|
await deleteSecret(args.id, ctx.db);
|
|
310
344
|
return textResult({ deleted: args.id });
|
|
311
345
|
},
|