@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,11 +1,107 @@
|
|
|
1
|
-
import { fulfillSecretElicitation } from "./secret-elicitation.js";
|
|
1
|
+
import { fulfillSecretElicitation, fulfillUnbrokerElicitation, } from "./secret-elicitation.js";
|
|
2
|
+
import { listSecrets } from "../../core/secrets.js";
|
|
3
|
+
import { parseSecretBrokerConfig } from "../../core/secret-broker-config.js";
|
|
2
4
|
import { PAGE_STYLE } from "../shared/page-style.js";
|
|
3
5
|
/** The HTTP-transport route path this form is mounted at (see streamable-http.ts). */
|
|
4
6
|
export const SECRET_ELICITATION_PATH = "/elicit/secret";
|
|
5
7
|
function escape(value) {
|
|
6
8
|
return value.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7
9
|
}
|
|
8
|
-
|
|
10
|
+
const BROKER_WARNING = "If this is not brokered, any tool attached with this secret can read its value and send it anywhere that tool can fetch — including when a prompt-injected agent tells it to.";
|
|
11
|
+
const BROKER_COMPAT = 'Brokered secrets only work with tools that call <code>fetch(url, { secrets: ["NAME"] })</code>. A tool that reads the value with <code>secrets.get()</code> will fail with <code>secret_brokered</code>.';
|
|
12
|
+
/** Script-free toggle: the fields after the checkbox show only while it is checked. */
|
|
13
|
+
const BROKER_STYLE = `
|
|
14
|
+
.broker-toggle { display: inline-block; margin: 0 0 12px 6px; color: var(--ink); }
|
|
15
|
+
.broker-fields { display: none; }
|
|
16
|
+
#brokered:checked ~ .broker-fields { display: block; }
|
|
17
|
+
.warning { border-left: 3px solid #b45309; padding-left: 8px; color: var(--ink); }
|
|
18
|
+
.broker-fields input, .broker-fields textarea, .broker-fields select, input[name="confirm"] {
|
|
19
|
+
display: block;
|
|
20
|
+
width: 100%;
|
|
21
|
+
margin-top: 8px;
|
|
22
|
+
padding: 10px 12px;
|
|
23
|
+
font: inherit;
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
color: var(--ink);
|
|
26
|
+
background: var(--paper);
|
|
27
|
+
border: 1px solid var(--line);
|
|
28
|
+
border-radius: 10px;
|
|
29
|
+
}
|
|
30
|
+
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }
|
|
31
|
+
code { overflow-wrap: anywhere; }
|
|
32
|
+
pre { white-space: pre-wrap; overflow-wrap: anywhere; padding: 10px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; }
|
|
33
|
+
.hint { margin: 4px 0 12px; }
|
|
34
|
+
.broker-summary { margin: 0 0 12px; padding: 10px 10px 10px 28px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; color: var(--ink); }
|
|
35
|
+
.broker-summary li { margin: 2px 0; }
|
|
36
|
+
/* Show only the selected placement's fields; browsers without :has() show them all. */
|
|
37
|
+
@supports selector(:has(*)) {
|
|
38
|
+
.placement { display: none; }
|
|
39
|
+
.broker-fields:has(option[value="header"]:checked) .placement-header,
|
|
40
|
+
.broker-fields:has(option[value="query"]:checked) .placement-query,
|
|
41
|
+
.broker-fields:has(option[value="body"]:checked) .placement-body,
|
|
42
|
+
.broker-fields:has(option[value="aws-sigv4"]:checked) .placement-aws-sigv4 { display: block; }
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
/** The "Brokered (recommended)" checkbox, warning, compatibility note, and broker fields, pre-filled from `prefill`. */
|
|
46
|
+
export function brokerFieldset(prefill) {
|
|
47
|
+
const p = prefill?.placement;
|
|
48
|
+
const sel = (kind) => (p?.kind === kind ? " selected" : "");
|
|
49
|
+
return (`<input type="checkbox" id="brokered" name="brokered" value="1"${prefill ? " checked" : ""}>` +
|
|
50
|
+
`<label class="broker-toggle" for="brokered">Brokered (recommended)</label>` +
|
|
51
|
+
`<p class="warning">${escape(BROKER_WARNING)}</p>` +
|
|
52
|
+
`<p>${BROKER_COMPAT}</p>` +
|
|
53
|
+
`<div class="broker-fields">` +
|
|
54
|
+
`<label>Allowed hosts (one per line)<textarea name="hosts" rows="2">${escape(prefill?.hosts.join("\n") ?? "")}</textarea></label>` +
|
|
55
|
+
`<label>Path prefixes (optional, one per line)<textarea name="pathPrefixes" rows="2">${escape(prefill?.pathPrefixes?.join("\n") ?? "")}</textarea></label>` +
|
|
56
|
+
`<label>Placement<select name="placement">` +
|
|
57
|
+
`<option value="header"${sel("header")}>Header</option><option value="query"${sel("query")}>Query parameter</option>` +
|
|
58
|
+
`<option value="body"${sel("body")}>Body field</option><option value="aws-sigv4"${sel("aws-sigv4")}>AWS SigV4</option></select></label>` +
|
|
59
|
+
`<div class="placement placement-header">` +
|
|
60
|
+
`<label>Header name<input name="headerName" value="${escape(p?.kind === "header" ? p.name : "Authorization")}"></label>` +
|
|
61
|
+
`<label>Header format<input name="headerFormat" value="${escape(p?.kind === "header" ? p.format : "Bearer {value}")}"></label>` +
|
|
62
|
+
`<p class="hint"><code>{value}</code> is replaced with the secret.</p>` +
|
|
63
|
+
`</div>` +
|
|
64
|
+
`<div class="placement placement-query">` +
|
|
65
|
+
`<label>Query parameter name<input name="queryName" value="${escape(p?.kind === "query" ? p.name : "")}"></label>` +
|
|
66
|
+
`</div>` +
|
|
67
|
+
`<div class="placement placement-body">` +
|
|
68
|
+
`<label>Body field name<input name="bodyField" value="${escape(p?.kind === "body" ? p.field : "")}"></label>` +
|
|
69
|
+
`<p class="hint">A top-level field of a JSON-object or form-encoded body.</p>` +
|
|
70
|
+
`</div>` +
|
|
71
|
+
`<div class="placement placement-aws-sigv4">` +
|
|
72
|
+
`<label>AWS region<input name="awsRegion" value="${escape(p?.kind === "aws-sigv4" ? p.region : "")}"></label>` +
|
|
73
|
+
`<label>AWS service<input name="awsService" value="${escape(p?.kind === "aws-sigv4" ? p.service : "")}"></label>` +
|
|
74
|
+
`<p class="hint">For AWS SigV4 the secret value is JSON (session token optional):</p>` +
|
|
75
|
+
`<pre>{\n "accessKeyId": "…",\n "secretAccessKey": "…",\n "sessionToken": "…"\n}</pre>` +
|
|
76
|
+
`</div>` +
|
|
77
|
+
`</div>`);
|
|
78
|
+
}
|
|
79
|
+
const lines = (v) => (v ?? "")
|
|
80
|
+
.split(/\r?\n/)
|
|
81
|
+
.map((s) => s.trim())
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
/** The form's broker config: null when the box is unchecked; throws secret_broker_config_invalid when it doesn't validate. */
|
|
84
|
+
export function brokerConfigFromForm(body) {
|
|
85
|
+
if (body.get("brokered") !== "1")
|
|
86
|
+
return null;
|
|
87
|
+
const kind = body.get("placement");
|
|
88
|
+
const placement = kind === "header"
|
|
89
|
+
? { kind, name: body.get("headerName") ?? "", format: body.get("headerFormat") ?? "" }
|
|
90
|
+
: kind === "query"
|
|
91
|
+
? { kind, name: body.get("queryName") ?? "" }
|
|
92
|
+
: kind === "body"
|
|
93
|
+
? { kind, field: body.get("bodyField") ?? "" }
|
|
94
|
+
: kind === "aws-sigv4"
|
|
95
|
+
? { kind, region: body.get("awsRegion") ?? "", service: body.get("awsService") ?? "" }
|
|
96
|
+
: { kind };
|
|
97
|
+
const prefixes = lines(body.get("pathPrefixes"));
|
|
98
|
+
return parseSecretBrokerConfig({
|
|
99
|
+
hosts: lines(body.get("hosts")),
|
|
100
|
+
...(prefixes.length > 0 && { pathPrefixes: prefixes }),
|
|
101
|
+
placement,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function html(res, status, body, extraStyle = "") {
|
|
9
105
|
res.setHeader("content-security-policy", "default-src 'none'; script-src 'none'; style-src 'unsafe-inline'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'");
|
|
10
106
|
res.setHeader("cache-control", "no-store");
|
|
11
107
|
res.setHeader("x-content-type-options", "nosniff");
|
|
@@ -18,7 +114,7 @@ function html(res, status, body) {
|
|
|
18
114
|
res
|
|
19
115
|
.writeHead(status, { "content-type": "text/html; charset=utf-8" })
|
|
20
116
|
.end(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` +
|
|
21
|
-
`<title>wardby secret entry</title><style>${PAGE_STYLE}</style><body><div class="card"><div class="kicker">wardby</div>${body}</div></body></html>`);
|
|
117
|
+
`<title>wardby secret entry</title><style>${PAGE_STYLE}${extraStyle}</style><body><div class="card"><div class="kicker">wardby</div>${body}</div></body></html>`);
|
|
22
118
|
}
|
|
23
119
|
/** Handles one GET (render) or POST (submit) for the secret-entry form at the given token. */
|
|
24
120
|
export async function handleSecretElicitationForm(method, token, readFormBody, res, deps) {
|
|
@@ -27,26 +123,46 @@ export async function handleSecretElicitationForm(method, token, readFormBody, r
|
|
|
27
123
|
if (!token)
|
|
28
124
|
throw new Error("missing token");
|
|
29
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
|
+
}
|
|
30
131
|
}
|
|
31
132
|
catch {
|
|
32
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>");
|
|
33
134
|
return;
|
|
34
135
|
}
|
|
136
|
+
if ((payload.kind ?? "create") === "unbroker") {
|
|
137
|
+
await handleUnbroker(method, payload, readFormBody, res, deps);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
35
140
|
if (method === "GET") {
|
|
36
141
|
html(res, 200, `<h1>Enter secret value</h1><p>Name: <strong>${escape(payload.secretName)}</strong></p>` +
|
|
37
142
|
`<form method="post"><label>Secret value` +
|
|
38
143
|
`<input type="password" name="value" autocomplete="off" required autofocus placeholder="Paste the value here"></label>` +
|
|
39
|
-
|
|
144
|
+
brokerFieldset(payload.broker) +
|
|
145
|
+
`<button type="submit">Save</button></form>`, BROKER_STYLE);
|
|
40
146
|
return;
|
|
41
147
|
}
|
|
42
148
|
if (method === "POST") {
|
|
43
|
-
const body = await readFormBody
|
|
149
|
+
const body = await readBodyOr413(readFormBody, res);
|
|
150
|
+
if (!body)
|
|
151
|
+
return;
|
|
44
152
|
const value = body.get("value") ?? "";
|
|
45
153
|
if (!value) {
|
|
46
154
|
html(res, 400, "<h1>A value is required.</h1>");
|
|
47
155
|
return;
|
|
48
156
|
}
|
|
49
|
-
|
|
157
|
+
let broker;
|
|
158
|
+
try {
|
|
159
|
+
broker = brokerConfigFromForm(body);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
html(res, 400, `<h1>Could not save</h1><p>${escape(err instanceof Error ? err.message : String(err))}</p>`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const outcome = await fulfillSecretElicitation(payload, value, deps.secrets, deps.db, broker);
|
|
50
166
|
if (outcome.ok)
|
|
51
167
|
html(res, 200, "<h1>Saved</h1><p>You can close this tab and return to your MCP client.</p>");
|
|
52
168
|
else
|
|
@@ -55,21 +171,100 @@ export async function handleSecretElicitationForm(method, token, readFormBody, r
|
|
|
55
171
|
}
|
|
56
172
|
res.writeHead(405).end();
|
|
57
173
|
}
|
|
58
|
-
/** Reads
|
|
174
|
+
/** Reads the POST body, or answers 413 (over the cap) and returns undefined. */
|
|
175
|
+
async function readBodyOr413(readFormBody, res) {
|
|
176
|
+
try {
|
|
177
|
+
return await readFormBody();
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
if (!(err instanceof Error && err.message === "body_too_large"))
|
|
181
|
+
throw err;
|
|
182
|
+
res.setHeader("connection", "close");
|
|
183
|
+
html(res, 413, "<h1>Value too large</h1><p>The form was larger than this page accepts.</p>");
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** A readable, escaped summary of a broker config for the unbroker page. */
|
|
188
|
+
function describeBroker(broker) {
|
|
189
|
+
if (!broker)
|
|
190
|
+
return "<p>(not brokered)</p>";
|
|
191
|
+
const p = broker.placement;
|
|
192
|
+
const placement = p.kind === "header"
|
|
193
|
+
? `header <code>${escape(p.name)}: ${escape(p.format)}</code>`
|
|
194
|
+
: p.kind === "query"
|
|
195
|
+
? `query parameter <code>${escape(p.name)}</code>`
|
|
196
|
+
: p.kind === "body"
|
|
197
|
+
? `body field <code>${escape(p.field)}</code>`
|
|
198
|
+
: `AWS SigV4 (region <code>${escape(p.region)}</code>, service <code>${escape(p.service)}</code>)`;
|
|
199
|
+
const list = (items) => items.map((item) => `<code>${escape(item)}</code>`).join(", ");
|
|
200
|
+
return (`<ul class="broker-summary">` +
|
|
201
|
+
`<li>Hosts: ${list(broker.hosts)}</li>` +
|
|
202
|
+
(broker.pathPrefixes?.length ? `<li>Paths: ${list(broker.pathPrefixes)}</li>` : "") +
|
|
203
|
+
`<li>Sent as: ${placement}</li>` +
|
|
204
|
+
`</ul>`);
|
|
205
|
+
}
|
|
206
|
+
/** The unbroker link's page: show what is brokered now, and remove it only when the person types the secret's name. */
|
|
207
|
+
async function handleUnbroker(method, payload, readFormBody, res, deps) {
|
|
208
|
+
if (method === "GET") {
|
|
209
|
+
const current = (await listSecrets(payload.ownerId, deps.db)).find((s) => s.name === payload.secretName);
|
|
210
|
+
html(res, 200, `<h1>Remove brokering</h1><p>Name: <strong>${escape(payload.secretName)}</strong></p>` +
|
|
211
|
+
`<p>Currently brokered as:</p>${describeBroker(current?.broker ?? null)}` +
|
|
212
|
+
`<p class="warning">${escape(BROKER_WARNING)}</p>` +
|
|
213
|
+
`<form method="post"><label>Type the secret's name to confirm` +
|
|
214
|
+
`<input name="confirm" autocomplete="off" required autofocus></label>` +
|
|
215
|
+
`<button type="submit">Remove brokering</button></form>`, BROKER_STYLE);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (method === "POST") {
|
|
219
|
+
const body = await readBodyOr413(readFormBody, res);
|
|
220
|
+
if (!body)
|
|
221
|
+
return;
|
|
222
|
+
if (body.get("confirm") !== payload.secretName) {
|
|
223
|
+
html(res, 400, `<h1>${escape("Type the secret's name to confirm.")}</h1>`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const outcome = await fulfillUnbrokerElicitation(payload, deps.secrets, deps.db);
|
|
227
|
+
if (outcome.ok) {
|
|
228
|
+
html(res, 200, "<h1>Brokering removed</h1><p>You can close this tab and return to your MCP client.</p>");
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
html(res, 400, `<h1>Could not remove brokering</h1><p>${escape(outcome.error)}</p>`);
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
res.writeHead(405).end();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The largest form body accepted: room for the largest valid AWS SigV4 value
|
|
239
|
+
* (an 8 KiB session token, up to 3x after URL encoding) plus the broker fields.
|
|
240
|
+
*/
|
|
241
|
+
export const FORM_BODY_LIMIT = 32 * 1024;
|
|
242
|
+
/**
|
|
243
|
+
* Reads a small `application/x-www-form-urlencoded` body. Over FORM_BODY_LIMIT it
|
|
244
|
+
* rejects with `body_too_large` and discards the rest of the upload, so the
|
|
245
|
+
* handler can still answer 413 on the same connection.
|
|
246
|
+
*/
|
|
59
247
|
export function readFormBody(req) {
|
|
60
248
|
return new Promise((resolve, reject) => {
|
|
61
249
|
let size = 0;
|
|
250
|
+
let tooLarge = false;
|
|
62
251
|
const chunks = [];
|
|
63
252
|
req.on("data", (chunk) => {
|
|
253
|
+
if (tooLarge)
|
|
254
|
+
return;
|
|
64
255
|
size += chunk.length;
|
|
65
|
-
if (size >
|
|
256
|
+
if (size > FORM_BODY_LIMIT) {
|
|
257
|
+
tooLarge = true;
|
|
258
|
+
chunks.length = 0;
|
|
66
259
|
reject(new Error("body_too_large"));
|
|
67
|
-
req.destroy();
|
|
68
260
|
return;
|
|
69
261
|
}
|
|
70
262
|
chunks.push(chunk);
|
|
71
263
|
});
|
|
72
|
-
req.on("end", () =>
|
|
264
|
+
req.on("end", () => {
|
|
265
|
+
if (!tooLarge)
|
|
266
|
+
resolve(new URLSearchParams(Buffer.concat(chunks).toString("utf8")));
|
|
267
|
+
});
|
|
73
268
|
req.on("error", reject);
|
|
74
269
|
});
|
|
75
270
|
}
|
|
@@ -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,57 +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
|
-
void handleSecretElicitationForm(req.method, url.searchParams.get("t"), () => readFormBody(req), res, deps);
|
|
40
|
-
});
|
|
41
|
-
server = s;
|
|
42
|
-
await new Promise((resolve, reject) => {
|
|
43
|
-
s.once("error", reject);
|
|
44
|
-
s.listen(0, "127.0.0.1", resolve);
|
|
45
|
-
});
|
|
46
|
-
const address = s.address();
|
|
47
|
-
port = typeof address === "object" && address ? address.port : 0;
|
|
48
|
-
scheduleIdleShutdown();
|
|
49
|
-
return port;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
async urlFor(token) {
|
|
53
|
-
const p = await ensureServer();
|
|
54
|
-
return `http://127.0.0.1:${p}/secret?t=${encodeURIComponent(token)}`;
|
|
55
|
-
},
|
|
56
|
-
};
|
|
11
|
+
const host = createStdioLinkHost({ [STDIO_SECRET_PATH]: secretFormRoute(deps) });
|
|
12
|
+
return { urlFor: (token) => host.urlFor(STDIO_SECRET_PATH, token) };
|
|
57
13
|
}
|
|
@@ -14,25 +14,60 @@
|
|
|
14
14
|
* elicitation nobody completes or polls within that window is abandoned).
|
|
15
15
|
* Expiry is swept lazily on every read and write via a `deleteMany` rather
|
|
16
16
|
* than a timer, so an idle process holds no interval.
|
|
17
|
+
*
|
|
18
|
+
* Two kinds share the table: "create" (enter a secret's value, optionally
|
|
19
|
+
* brokered) and "unbroker" (a person confirms removing a secret's broker
|
|
20
|
+
* config: `set_secret_broker { broker: null }` never removes it directly).
|
|
21
|
+
* Payloads and outcomes written before `kind` existed have none and count as
|
|
22
|
+
* "create".
|
|
17
23
|
*/
|
|
18
24
|
import type { PrismaClient } from "#prisma";
|
|
19
25
|
import { type SecretMetadata } from "../../core/secrets.js";
|
|
26
|
+
import { type SecretBrokerConfig } from "../../core/secret-broker-config.js";
|
|
20
27
|
import type { SecretCipher } from "../../providers/secrets/types.js";
|
|
28
|
+
export type SecretElicitationKind = "create" | "unbroker";
|
|
21
29
|
export interface SecretElicitationPayload {
|
|
22
30
|
ownerId: string;
|
|
23
31
|
secretName: string;
|
|
32
|
+
/** Missing on links minted before brokered secrets: those are "create". */
|
|
33
|
+
kind?: SecretElicitationKind;
|
|
34
|
+
/** "create" only: the broker config to save with the value (the form pre-fills it). */
|
|
35
|
+
broker?: SecretBrokerConfig;
|
|
36
|
+
/**
|
|
37
|
+
* "unbroker" only: brokerConfigHash of the config the link was minted for. The
|
|
38
|
+
* removal is refused if the secret's config has changed since (or the link has none).
|
|
39
|
+
*/
|
|
40
|
+
brokerHash?: string;
|
|
24
41
|
}
|
|
25
42
|
export type SecretElicitationOutcome = {
|
|
26
43
|
ok: true;
|
|
44
|
+
kind: SecretElicitationKind;
|
|
27
45
|
secret: SecretMetadata;
|
|
28
46
|
} | {
|
|
29
47
|
ok: false;
|
|
48
|
+
kind: SecretElicitationKind;
|
|
30
49
|
error: string;
|
|
31
50
|
};
|
|
32
|
-
export declare function getSecretElicitationOutcome(ownerId: string, secretName: string, db: PrismaClient): Promise<SecretElicitationOutcome | undefined>;
|
|
51
|
+
export declare function getSecretElicitationOutcome(ownerId: string, secretName: string, db: PrismaClient, kind?: SecretElicitationKind): Promise<SecretElicitationOutcome | undefined>;
|
|
33
52
|
/**
|
|
34
53
|
* Called by the browser form's POST handler. Idempotent — a resubmit for
|
|
35
54
|
* the same (ownerId, secretName) that's already fulfilled returns the
|
|
36
|
-
* recorded outcome rather than writing again.
|
|
55
|
+
* recorded outcome rather than writing again. A value or broker config that
|
|
56
|
+
* fails validation is returned but not recorded, so the person can correct it. `broker` (the form's,
|
|
57
|
+
* pre-filled from the payload) makes the new secret brokered.
|
|
58
|
+
*/
|
|
59
|
+
export declare function fulfillSecretElicitation(payload: SecretElicitationPayload, value: string, cipher: SecretCipher, db: PrismaClient, broker?: SecretBrokerConfig | null): Promise<SecretElicitationOutcome>;
|
|
60
|
+
/**
|
|
61
|
+
* Called by the unbroker form's POST: removes brokering (audited, via
|
|
62
|
+
* "browser", the owner as actor). Idempotent like fulfillSecretElicitation.
|
|
63
|
+
* A link only removes the config it was minted for: if brokering changed
|
|
64
|
+
* since (or the link carries no brokerHash) it is refused without recording
|
|
65
|
+
* an outcome, so a fresh link for the current config still works.
|
|
66
|
+
*/
|
|
67
|
+
export declare function fulfillUnbrokerElicitation(payload: SecretElicitationPayload, cipher: SecretCipher, db: PrismaClient): Promise<SecretElicitationOutcome>;
|
|
68
|
+
/**
|
|
69
|
+
* Forgets a recorded unbroker outcome for this secret: called when brokering
|
|
70
|
+
* is set again, so a removal confirmed earlier can't answer (or short-circuit)
|
|
71
|
+
* a later removal request.
|
|
37
72
|
*/
|
|
38
|
-
export declare function
|
|
73
|
+
export declare function clearUnbrokerOutcome(ownerId: string, secretName: string, db: PrismaClient): Promise<void>;
|
|
@@ -1,34 +1,72 @@
|
|
|
1
|
-
import { createSecret } from "../../core/secrets.js";
|
|
1
|
+
import { createSecret, listSecrets, setSecretBroker } from "../../core/secrets.js";
|
|
2
|
+
import { parseSecretBrokerConfig, SECRET_BROKER_CHANGED, } from "../../core/secret-broker-config.js";
|
|
3
|
+
/**
|
|
4
|
+
* A link only does what it was minted for: a create link can't confirm an
|
|
5
|
+
* unbroker, nor the reverse. Refused without recording an outcome.
|
|
6
|
+
*/
|
|
7
|
+
function kindMismatch(payload, expected) {
|
|
8
|
+
const actual = payload.kind ?? "create";
|
|
9
|
+
if (actual === expected)
|
|
10
|
+
return undefined;
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
kind: expected,
|
|
14
|
+
error: `elicitation_kind_mismatch: this link is for a "${actual}" request, not "${expected}"`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** A stored outcome's kind; one written before `kind` existed is a "create". */
|
|
18
|
+
function kindOf(outcome) {
|
|
19
|
+
return outcome.kind ?? "create";
|
|
20
|
+
}
|
|
2
21
|
const ELICITATION_TTL_MS = 600_000;
|
|
3
22
|
/** Sweeps every expired entry, not just the one being looked up, so a set-and-never-polled-again entry doesn't linger forever. */
|
|
4
23
|
async function pruneExpired(db, now) {
|
|
5
24
|
await db.secretElicitationOutcome.deleteMany({ where: { expiresAt: { lte: now } } });
|
|
6
25
|
}
|
|
7
|
-
export async function getSecretElicitationOutcome(ownerId, secretName, db) {
|
|
26
|
+
export async function getSecretElicitationOutcome(ownerId, secretName, db, kind = "create") {
|
|
8
27
|
const now = new Date();
|
|
9
28
|
await pruneExpired(db, now);
|
|
10
29
|
const row = await db.secretElicitationOutcome.findUnique({ where: { ownerId_secretName: { ownerId, secretName } } });
|
|
11
|
-
|
|
30
|
+
if (!row || kindOf(row.outcome) !== kind)
|
|
31
|
+
return undefined;
|
|
32
|
+
return row.outcome;
|
|
33
|
+
}
|
|
34
|
+
async function recordOutcome(db, ownerId, secretName, outcome, now) {
|
|
35
|
+
const expiresAt = new Date(now.getTime() + ELICITATION_TTL_MS);
|
|
36
|
+
await db.secretElicitationOutcome.upsert({
|
|
37
|
+
where: { ownerId_secretName: { ownerId, secretName } },
|
|
38
|
+
create: { ownerId, secretName, outcome, expiresAt },
|
|
39
|
+
update: { outcome, expiresAt },
|
|
40
|
+
});
|
|
12
41
|
}
|
|
13
42
|
/**
|
|
14
43
|
* Called by the browser form's POST handler. Idempotent — a resubmit for
|
|
15
44
|
* the same (ownerId, secretName) that's already fulfilled returns the
|
|
16
|
-
* recorded outcome rather than writing again.
|
|
45
|
+
* recorded outcome rather than writing again. A value or broker config that
|
|
46
|
+
* fails validation is returned but not recorded, so the person can correct it. `broker` (the form's,
|
|
47
|
+
* pre-filled from the payload) makes the new secret brokered.
|
|
17
48
|
*/
|
|
18
|
-
export async function fulfillSecretElicitation(payload, value, cipher, db) {
|
|
49
|
+
export async function fulfillSecretElicitation(payload, value, cipher, db, broker) {
|
|
50
|
+
const mismatch = kindMismatch(payload, "create");
|
|
51
|
+
if (mismatch)
|
|
52
|
+
return mismatch;
|
|
19
53
|
const now = new Date();
|
|
20
54
|
await pruneExpired(db, now);
|
|
21
55
|
const { ownerId, secretName } = payload;
|
|
22
56
|
const existing = await db.secretElicitationOutcome.findUnique({
|
|
23
57
|
where: { ownerId_secretName: { ownerId, secretName } },
|
|
24
58
|
});
|
|
25
|
-
if (existing)
|
|
59
|
+
if (existing && kindOf(existing.outcome) === "create")
|
|
26
60
|
return existing.outcome;
|
|
27
61
|
let outcome;
|
|
28
62
|
try {
|
|
29
|
-
const secret = await createSecret(secretName, value, ownerId, cipher, db
|
|
63
|
+
const secret = await createSecret(secretName, value, ownerId, cipher, db, {
|
|
64
|
+
broker: broker ?? undefined,
|
|
65
|
+
via: "browser",
|
|
66
|
+
});
|
|
30
67
|
outcome = {
|
|
31
68
|
ok: true,
|
|
69
|
+
kind: "create",
|
|
32
70
|
secret: {
|
|
33
71
|
id: secret.id,
|
|
34
72
|
name: secret.name,
|
|
@@ -36,17 +74,77 @@ export async function fulfillSecretElicitation(payload, value, cipher, db) {
|
|
|
36
74
|
ownerId: secret.ownerId,
|
|
37
75
|
createdAt: secret.createdAt,
|
|
38
76
|
updatedAt: secret.updatedAt,
|
|
77
|
+
// A rotation without a broker keeps the stored config, so report what was saved.
|
|
78
|
+
broker: secret.broker === null || secret.broker === undefined ? null : parseSecretBrokerConfig(secret.broker),
|
|
39
79
|
},
|
|
40
80
|
};
|
|
41
81
|
}
|
|
42
82
|
catch (err) {
|
|
43
|
-
outcome = { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
83
|
+
outcome = { ok: false, kind: "create", error: err instanceof Error ? err.message : String(err) };
|
|
84
|
+
// A value or config the person can fix: not recorded, so the same link takes a corrected
|
|
85
|
+
// submission and create_secret keeps waiting rather than replaying this error.
|
|
86
|
+
if (isCorrectableInput(outcome.error))
|
|
87
|
+
return outcome;
|
|
44
88
|
}
|
|
45
|
-
|
|
46
|
-
|
|
89
|
+
await recordOutcome(db, ownerId, secretName, outcome, now);
|
|
90
|
+
return outcome;
|
|
91
|
+
}
|
|
92
|
+
const CORRECTABLE_ERRORS = ["secret_broker_value_invalid", "secret_broker_config_invalid", "bridge_input_limit"];
|
|
93
|
+
function isCorrectableInput(message) {
|
|
94
|
+
return CORRECTABLE_ERRORS.some((code) => message.startsWith(code));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Called by the unbroker form's POST: removes brokering (audited, via
|
|
98
|
+
* "browser", the owner as actor). Idempotent like fulfillSecretElicitation.
|
|
99
|
+
* A link only removes the config it was minted for: if brokering changed
|
|
100
|
+
* since (or the link carries no brokerHash) it is refused without recording
|
|
101
|
+
* an outcome, so a fresh link for the current config still works.
|
|
102
|
+
*/
|
|
103
|
+
export async function fulfillUnbrokerElicitation(payload, cipher, db) {
|
|
104
|
+
const mismatch = kindMismatch(payload, "unbroker");
|
|
105
|
+
if (mismatch)
|
|
106
|
+
return mismatch;
|
|
107
|
+
const changed = { ok: false, kind: "unbroker", error: SECRET_BROKER_CHANGED };
|
|
108
|
+
if (!payload.brokerHash)
|
|
109
|
+
return changed;
|
|
110
|
+
const now = new Date();
|
|
111
|
+
await pruneExpired(db, now);
|
|
112
|
+
const { ownerId, secretName } = payload;
|
|
113
|
+
const existing = await db.secretElicitationOutcome.findUnique({
|
|
47
114
|
where: { ownerId_secretName: { ownerId, secretName } },
|
|
48
|
-
create: { ownerId, secretName, outcome, expiresAt },
|
|
49
|
-
update: { outcome, expiresAt },
|
|
50
115
|
});
|
|
116
|
+
if (existing && kindOf(existing.outcome) === "unbroker")
|
|
117
|
+
return existing.outcome;
|
|
118
|
+
let outcome;
|
|
119
|
+
try {
|
|
120
|
+
await setSecretBroker(db, cipher, {
|
|
121
|
+
ownerId,
|
|
122
|
+
name: secretName,
|
|
123
|
+
broker: null,
|
|
124
|
+
actorId: ownerId,
|
|
125
|
+
via: "browser",
|
|
126
|
+
expectedBrokerHash: payload.brokerHash,
|
|
127
|
+
});
|
|
128
|
+
const secret = (await listSecrets(ownerId, db)).find((s) => s.name === secretName);
|
|
129
|
+
if (!secret)
|
|
130
|
+
throw new Error(`secret_not_found: no secret named "${secretName}"`);
|
|
131
|
+
outcome = { ok: true, kind: "unbroker", secret };
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err instanceof Error && err.message === SECRET_BROKER_CHANGED)
|
|
135
|
+
return changed;
|
|
136
|
+
outcome = { ok: false, kind: "unbroker", error: err instanceof Error ? err.message : String(err) };
|
|
137
|
+
}
|
|
138
|
+
await recordOutcome(db, ownerId, secretName, outcome, now);
|
|
51
139
|
return outcome;
|
|
52
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Forgets a recorded unbroker outcome for this secret: called when brokering
|
|
143
|
+
* is set again, so a removal confirmed earlier can't answer (or short-circuit)
|
|
144
|
+
* a later removal request.
|
|
145
|
+
*/
|
|
146
|
+
export async function clearUnbrokerOutcome(ownerId, secretName, db) {
|
|
147
|
+
await db.secretElicitationOutcome.deleteMany({
|
|
148
|
+
where: { ownerId, secretName, outcome: { path: ["kind"], equals: "unbroker" } },
|
|
149
|
+
});
|
|
150
|
+
}
|