@wardby/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +34 -4
- package/README.md +183 -71
- package/dist/claude-coding-worker/driver.d.ts +7 -1
- package/dist/claude-coding-worker/driver.js +31 -2
- package/dist/claude-coding-worker/main.js +5 -2
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +13 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +219 -29
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +99 -3
- package/dist/coding/protocol.js +155 -4
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +18 -0
- package/dist/coding-worker/artifact.js +48 -6
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +14 -1
- package/dist/coding-worker/driver.js +106 -14
- package/dist/coding-worker/errors.d.ts +7 -0
- package/dist/coding-worker/errors.js +26 -0
- package/dist/coding-worker/main.js +13 -4
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +41 -0
- package/dist/config/providers.js +66 -0
- package/dist/core/budget-groups.d.ts +109 -6
- package/dist/core/budget-groups.js +125 -19
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +1 -1
- package/dist/core/datastores.d.ts +1 -1
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +40 -2
- package/dist/core/db.js +75 -2
- package/dist/core/dispatch.d.ts +49 -7
- package/dist/core/dispatch.js +290 -45
- package/dist/core/engine-native.js +27 -10
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +49 -0
- package/dist/core/host-events.js +257 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +61 -0
- package/dist/core/host-status.js +211 -0
- package/dist/core/http-runtime.js +3 -4
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/lease.d.ts +1 -1
- package/dist/core/private-directory.d.ts +20 -0
- package/dist/core/private-directory.js +36 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +39 -3
- package/dist/core/reconciler.js +86 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +347 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/runner.d.ts +31 -9
- package/dist/core/runner.js +237 -40
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secrets.d.ts +12 -3
- package/dist/core/secrets.js +25 -6
- package/dist/core/subagent-memory-tools.d.ts +2 -2
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +64 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +9 -2
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +328 -0
- package/dist/generated/prisma/browser.js +17 -0
- package/dist/generated/prisma/client.d.ts +347 -0
- package/dist/generated/prisma/client.js +34 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
- package/dist/generated/prisma/commonInputTypes.js +10 -0
- package/dist/generated/prisma/enums.d.ts +59 -0
- package/dist/generated/prisma/enums.js +59 -0
- package/dist/generated/prisma/internal/class.d.ts +605 -0
- package/dist/generated/prisma/internal/class.js +49 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
- package/dist/generated/prisma/models/Agent.d.ts +3560 -0
- package/dist/generated/prisma/models/Agent.js +1 -0
- package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
- package/dist/generated/prisma/models/AgentDatastore.js +1 -0
- package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
- package/dist/generated/prisma/models/AgentMemory.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
- package/dist/generated/prisma/models/AgentSecret.js +1 -0
- package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
- package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
- package/dist/generated/prisma/models/AgentTool.js +1 -0
- package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
- package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
- package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
- package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
- package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
- package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
- package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
- package/dist/generated/prisma/models/AuthSession.js +1 -0
- package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
- package/dist/generated/prisma/models/AuthUser.js +1 -0
- package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
- package/dist/generated/prisma/models/BudgetGroup.js +1 -0
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
- package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
- package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
- package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
- package/dist/generated/prisma/models/CodingProxySession.js +1 -0
- package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
- package/dist/generated/prisma/models/CodingRun.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
- package/dist/generated/prisma/models/Datastore.js +1 -0
- package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
- package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
- package/dist/generated/prisma/models/OAuthClient.js +1 -0
- package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
- package/dist/generated/prisma/models/OAuthFamily.js +1 -0
- package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
- package/dist/generated/prisma/models/OAuthGrant.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +2245 -0
- package/dist/generated/prisma/models/Principal.js +1 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +2669 -0
- package/dist/generated/prisma/models/Run.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
- package/dist/generated/prisma/models/SchedulerLease.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +1399 -0
- package/dist/generated/prisma/models/Secret.js +1 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
- package/dist/generated/prisma/models/Task.d.ts +1176 -0
- package/dist/generated/prisma/models/Task.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +1490 -0
- package/dist/generated/prisma/models/Tool.js +1 -0
- package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
- package/dist/generated/prisma/models/Webhook.js +1 -0
- package/dist/generated/prisma/models.d.ts +45 -0
- package/dist/generated/prisma/models.js +1 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +660 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +6 -1
- package/dist/import/create.js +68 -14
- package/dist/import/index.d.ts +1 -1
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +11 -11
- package/dist/mcp/auth/access.d.ts +70 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +37 -63
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/principal.d.ts +1 -1
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +32 -3
- package/dist/mcp/auth/resource-server.js +84 -3
- package/dist/mcp/auth/self-hosted/browser.js +22 -8
- package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
- package/dist/mcp/context.d.ts +28 -2
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/github-ingress.d.ts +36 -0
- package/dist/mcp/host-events/github-ingress.js +92 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/index.d.ts +8 -1
- package/dist/mcp/index.js +106 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/shared/page-style.d.ts +16 -0
- package/dist/mcp/shared/page-style.js +129 -0
- package/dist/mcp/tasks/manager.d.ts +1 -1
- package/dist/mcp/tools/agents.js +447 -49
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/datastore.js +23 -16
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +181 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +4 -7
- package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
- package/dist/mcp/tools/secret-elicitation-form.js +10 -28
- package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +53 -17
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +202 -41
- package/dist/mcp/tools/trigger.js +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +11 -1
- package/dist/mcp/transport/streamable-http.js +32 -2
- package/dist/mcp/unattended-schedules.d.ts +1 -1
- package/dist/mcp/webhooks/ingress.d.ts +1 -1
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +3 -1
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +4 -3
- package/dist/providers/auth/self-hosted.js +16 -4
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
- package/dist/providers/coding-proxy/memory-ledger.js +21 -2
- package/dist/providers/coding-proxy/metering.d.ts +4 -0
- package/dist/providers/coding-proxy/metering.js +28 -2
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
- package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
- package/dist/providers/coding-proxy/proxy.d.ts +13 -0
- package/dist/providers/coding-proxy/proxy.js +479 -18
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -1
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +15 -0
- package/dist/providers/datastore/postgres.d.ts +1 -1
- package/dist/providers/engine/types.d.ts +10 -0
- package/dist/providers/executor/build.d.ts +3 -3
- package/dist/providers/executor/composition.d.ts +4 -1
- package/dist/providers/executor/composition.js +19 -0
- package/dist/providers/executor/container.d.ts +106 -4
- package/dist/providers/executor/container.js +305 -22
- package/dist/providers/executor/dbos.d.ts +3 -4
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +7 -0
- package/dist/providers/executor/routing.js +13 -0
- package/dist/providers/executor/types.d.ts +22 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +60 -2
- package/dist/providers/jobs/docker.js +313 -50
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +21 -2
- package/dist/providers/jobs/kubernetes.js +117 -31
- package/dist/providers/jobs/types.d.ts +20 -0
- package/dist/providers/llm/anthropic.js +6 -2
- package/dist/providers/llm/bedrock.js +2 -1
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +3 -1
- package/dist/providers/llm/claude-provider.js +5 -1
- package/dist/providers/llm/index.d.ts +1 -1
- package/dist/providers/llm/index.js +1 -1
- package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
- package/dist/providers/llm/pricing-anthropic.js +32 -14
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
- package/dist/providers/llm/routing.d.ts +10 -1
- package/dist/providers/llm/routing.js +18 -0
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/memory/postgres.d.ts +1 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +180 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +567 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +284 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +79 -14
- package/dist/providers/vcs/github.d.ts +74 -3
- package/dist/providers/vcs/github.js +195 -15
- package/dist/providers/vcs/types.d.ts +42 -4
- package/dist/quickstart/index.js +34 -32
- package/dist/quickstart/migrate.d.ts +23 -0
- package/dist/quickstart/migrate.js +125 -0
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +1 -1
- package/dist/wardby-bin.js +6 -0
- package/docs/README.md +30 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +481 -0
- package/docs/coding-agent-setup.md +172 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1061 -0
- package/docs/getting-started-gke.md +615 -0
- package/docs/getting-started-identity-provider.md +308 -0
- package/docs/getting-started.md +133 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +657 -0
- package/help/code-review-agents.md +31 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/creating-agents.md +68 -0
- package/help/deploy-gke.md +39 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +31 -0
- package/help/github.md +30 -0
- package/help/identity-and-access.md +39 -0
- package/help/mcp.md +30 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +30 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +26 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +30 -11
- package/prisma/migrate.config.mjs +20 -0
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- package/prisma/schema.prisma +417 -23
package/dist/mcp/tools/tools.js
CHANGED
|
@@ -9,15 +9,51 @@
|
|
|
9
9
|
* (attach/detach on an agent or tool you don't own) DO throw, same
|
|
10
10
|
* convention as every other tool in this codebase.
|
|
11
11
|
*/
|
|
12
|
-
import { Prisma } from "
|
|
12
|
+
import { Prisma } from "#prisma";
|
|
13
13
|
import { deriveJsonSchema, validateParams } from "../../sandbox/zod-params.js";
|
|
14
14
|
import { runInSandbox } from "../../sandbox/run-in-sandbox.js";
|
|
15
15
|
import { ToolCapabilitiesPatchSchema } from "../../sandbox/tool-capabilities.js";
|
|
16
16
|
import { buildSharedDatastoreAccessor } from "../../core/datastores.js";
|
|
17
|
-
import {
|
|
17
|
+
import { findSameNamedAttachedTool, reservedToolNameReason } from "../../core/tool-names.js";
|
|
18
|
+
import { ToolAttachedError, deleteToolGuarded, hasToolChanges, prepareToolUpdate, updateToolGuarded, } from "../../core/tool-admin.js";
|
|
18
19
|
import { McpError } from "../errors.js";
|
|
19
|
-
import {
|
|
20
|
+
import { requireOwnedTool, requireStrictlyOwnedTool, assertStrictlyOwnedTool, visibleToPrincipal, canRead, } from "../auth/ownership.js";
|
|
21
|
+
import { agentAccessResolver, assertAgentAccess, requireAgentAccess } from "../auth/access.js";
|
|
22
|
+
import { atLeast } from "../../core/grants.js";
|
|
20
23
|
import { textResult } from "./text-result.js";
|
|
24
|
+
/**
|
|
25
|
+
* Names the agents the caller can read (its own and ones granted to it)
|
|
26
|
+
* and only counts the rest, so a refusal never reveals another principal's
|
|
27
|
+
* agent names or ids.
|
|
28
|
+
*/
|
|
29
|
+
async function describeAgents(agents, ctx) {
|
|
30
|
+
const accessOf = await agentAccessResolver(ctx);
|
|
31
|
+
const readable = agents.filter((agent) => atLeast("agent", accessOf(agent), "read"));
|
|
32
|
+
const hidden = agents.length - readable.length;
|
|
33
|
+
const parts = readable.map((agent) => `"${agent.name}" (${agent.id})`);
|
|
34
|
+
if (hidden > 0)
|
|
35
|
+
parts.push(`${hidden} agent(s) owned by other principals`);
|
|
36
|
+
return parts.join(", ");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A tool row as a caller who doesn't own it may see it: name and
|
|
40
|
+
* description, never code or paramsZod (A7). `public` tells an owner-less
|
|
41
|
+
* tool apart from the caller's own same-named one (names are unique per
|
|
42
|
+
* owner, not globally). Shared by list_tools and get_agent.
|
|
43
|
+
*/
|
|
44
|
+
export function projectTool(tool, principalId) {
|
|
45
|
+
if (tool.ownerId !== null && tool.ownerId === principalId)
|
|
46
|
+
return tool;
|
|
47
|
+
return tool.ownerId === null
|
|
48
|
+
? { id: tool.id, name: tool.name, description: tool.description, public: true }
|
|
49
|
+
: { id: tool.id, name: tool.name, description: tool.description, public: false, ownerIsCaller: false };
|
|
50
|
+
}
|
|
51
|
+
/** Refuses a name one of the runner's built-ins would shadow (see core/tool-names.ts). */
|
|
52
|
+
function assertToolNameAllowed(name) {
|
|
53
|
+
const reason = reservedToolNameReason(name);
|
|
54
|
+
if (reason)
|
|
55
|
+
throw new McpError(400, reason);
|
|
56
|
+
}
|
|
21
57
|
export function registerToolAuthoringTools(mcp) {
|
|
22
58
|
mcp.registerTool({
|
|
23
59
|
name: "create_tool",
|
|
@@ -33,24 +69,108 @@ export function registerToolAuthoringTools(mcp) {
|
|
|
33
69
|
required: ["name", "description", "paramsZod", "code"],
|
|
34
70
|
},
|
|
35
71
|
handler: async (args, ctx) => {
|
|
36
|
-
|
|
37
|
-
throw new McpError(400, `Tool name "${args.name}" is reserved for the built-in agent-memory tools.`);
|
|
38
|
-
}
|
|
72
|
+
assertToolNameAllowed(args.name);
|
|
39
73
|
const schemaResult = await deriveJsonSchema(args.paramsZod);
|
|
40
74
|
if (!schemaResult.ok) {
|
|
41
75
|
return textResult({ ok: false, errorKind: schemaResult.errorKind, errorMessage: schemaResult.errorMessage });
|
|
42
76
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
try {
|
|
78
|
+
const tool = await ctx.db.tool.create({
|
|
79
|
+
data: {
|
|
80
|
+
name: args.name,
|
|
81
|
+
description: args.description,
|
|
82
|
+
paramsZod: args.paramsZod,
|
|
83
|
+
code: args.code,
|
|
84
|
+
jsonSchema: schemaResult.value,
|
|
85
|
+
ownerId: ctx.principal.id,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return textResult(tool);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
|
|
92
|
+
throw new McpError(409, `A tool named "${args.name}" already exists for your principal.`);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
mcp.registerTool({
|
|
99
|
+
name: "update_tool",
|
|
100
|
+
scope: "tools:write",
|
|
101
|
+
description: "Replaces a tool's description, paramsZod and/or code in place (its name can't change: agents' prompts call it by name). Owner-only: public tools can't be changed over MCP. Refused while the tool is attached to any agent you don't own, including owner-less ones and agents shared with you, since the change would reach another owner's agent; detach it there first or create a new tool. Runs already started keep the version they loaded for their whole lifetime; the next run picks up the new one.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
properties: {
|
|
106
|
+
toolId: { type: "string" },
|
|
107
|
+
description: { type: "string" },
|
|
108
|
+
paramsZod: { type: "string" },
|
|
109
|
+
code: { type: "string" },
|
|
110
|
+
},
|
|
111
|
+
required: ["toolId"],
|
|
112
|
+
},
|
|
113
|
+
handler: async (args, ctx) => {
|
|
114
|
+
const changes = { description: args.description, paramsZod: args.paramsZod, code: args.code };
|
|
115
|
+
if (!hasToolChanges(changes)) {
|
|
116
|
+
throw new McpError(400, "update_tool needs at least one of description, paramsZod or code.");
|
|
117
|
+
}
|
|
118
|
+
// Checked once up front so a non-owner never gets as far as a sandbox
|
|
119
|
+
// compile, and again (with the attachments) inside the transaction.
|
|
120
|
+
await requireStrictlyOwnedTool(ctx.db, args.toolId, ctx.principal.id);
|
|
121
|
+
const prepared = await prepareToolUpdate(changes);
|
|
122
|
+
if (!prepared.ok) {
|
|
123
|
+
return textResult({ ok: false, errorKind: prepared.errorKind, errorMessage: prepared.errorMessage });
|
|
124
|
+
}
|
|
125
|
+
// Any attachment to an agent the caller doesn't own (an owner-less one
|
|
126
|
+
// included) blocks every field; see core/tool-admin.ts for why, and
|
|
127
|
+
// for the Serializable transaction that keeps a concurrent attach from
|
|
128
|
+
// landing between the check and the write.
|
|
129
|
+
try {
|
|
130
|
+
const tool = await updateToolGuarded(ctx.db, args.toolId, prepared.data, (row) => assertStrictlyOwnedTool(row, args.toolId, ctx.principal.id));
|
|
131
|
+
return textResult(tool);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (err instanceof ToolAttachedError) {
|
|
135
|
+
throw new McpError(409, `Tool "${args.toolId}" is attached to agents you don't own: ${await describeAgents(err.agents, ctx)}. Detach it from those agents first (or ask their owners to), or create a new tool instead.`);
|
|
136
|
+
}
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
mcp.registerTool({
|
|
142
|
+
name: "delete_tool",
|
|
143
|
+
scope: "tools:write",
|
|
144
|
+
description: "Deletes a tool you own. Refused while it is attached to any agent; pass detach: true to detach it from your own agents first. It is never detached from an agent you don't own -- use detach_tool where you hold write on the agent, or ask its owner. Past runs are unaffected, and a run already under way keeps the version it loaded.",
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: "object",
|
|
147
|
+
additionalProperties: false,
|
|
148
|
+
properties: { toolId: { type: "string" }, detach: { type: "boolean" } },
|
|
149
|
+
required: ["toolId"],
|
|
150
|
+
},
|
|
151
|
+
handler: async (args, ctx) => {
|
|
152
|
+
try {
|
|
153
|
+
const detachedFrom = await deleteToolGuarded(ctx.db, args.toolId, {
|
|
154
|
+
detach: args.detach === true,
|
|
155
|
+
authorize: (row) => assertStrictlyOwnedTool(row, args.toolId, ctx.principal.id),
|
|
156
|
+
});
|
|
157
|
+
return textResult({ deleted: args.toolId, detachedFrom });
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (!(err instanceof ToolAttachedError))
|
|
161
|
+
throw err;
|
|
162
|
+
const agents = await describeAgents(err.agents, ctx);
|
|
163
|
+
switch (err.reason) {
|
|
164
|
+
// Public agents are included: pulling a tool from a shared agent
|
|
165
|
+
// should be an explicit detach_tool.
|
|
166
|
+
case "other_owners":
|
|
167
|
+
throw new McpError(409, `Tool "${args.toolId}" is still attached to ${agents}. Only your own agents can be detached here; use detach_tool where you hold write on the agent, or ask the other owners to.`);
|
|
168
|
+
case "needs_detach":
|
|
169
|
+
throw new McpError(409, `Tool "${args.toolId}" is still attached to ${agents}. Pass detach: true to detach it from these agents and delete it.`);
|
|
170
|
+
case "race":
|
|
171
|
+
throw new McpError(409, `Tool "${args.toolId}" is still attached to an agent; nothing was deleted.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
54
174
|
},
|
|
55
175
|
});
|
|
56
176
|
mcp.registerTool({
|
|
@@ -112,6 +232,7 @@ export function registerToolAuthoringTools(mcp) {
|
|
|
112
232
|
mcp.registerTool({
|
|
113
233
|
name: "attach_tool",
|
|
114
234
|
scope: "tools:write",
|
|
235
|
+
description: "Attaches a tool to an agent (needs write on the agent). The four capability fields (allowedSecrets, allowedDatastorePrefixes, allowedHosts, allowedSharedDatastorePrefixes) are the agent owner's to grant: anyone else passing one gets 403, and their attachment runs with none until the owner re-runs attach_tool with the capabilities. When the owner grants an attachment it had not granted before (someone else attached it, or the agent changed owner), every capability not passed is reset to empty, so state each one you want. Granting capabilities to a tool someone else owns vouches for code you can't read (only its owner sees it); the tool's owner can't change it while it's attached to your agent, and detaching drops your grant.",
|
|
115
236
|
inputSchema: {
|
|
116
237
|
type: "object",
|
|
117
238
|
properties: {
|
|
@@ -141,35 +262,72 @@ export function registerToolAuthoringTools(mcp) {
|
|
|
141
262
|
if (!patch.success) {
|
|
142
263
|
throw new McpError(400, `Invalid tool capabilities: ${patch.error.issues.map((i) => i.message).join("; ")}`);
|
|
143
264
|
}
|
|
265
|
+
const capabilitiesPassed = Object.values(patch.data).some((value) => value !== undefined);
|
|
144
266
|
await ctx.db.$transaction(async (tx) => {
|
|
145
|
-
const agent = await tx.agent.findUnique({ where: { id: args.agentId } });
|
|
146
|
-
if (!agent)
|
|
147
|
-
throw new McpError(404, `Agent "${args.agentId}" not found.`);
|
|
148
|
-
assertCanMutate(agent.ownerId, ctx.principal.id, `Agent "${args.agentId}" is not owned by the caller.`);
|
|
267
|
+
const { agent } = await assertAgentAccess(ctx, await tx.agent.findUnique({ where: { id: args.agentId } }), args.agentId, "write", tx);
|
|
149
268
|
if (agent.kind === "coding") {
|
|
150
269
|
throw new McpError(400, "Native sandbox tools cannot be attached to coding agents.");
|
|
151
270
|
}
|
|
152
|
-
|
|
271
|
+
// Capabilities carry the owner's consent (resource-sharing grants
|
|
272
|
+
// spec §3.4.2). Strict ownership: neither a write-grantee nor the
|
|
273
|
+
// stdio operator may hand the owner's secrets, datastores or
|
|
274
|
+
// network to code on the agent.
|
|
275
|
+
const isOwner = agent.ownerId !== null && agent.ownerId === ctx.principal.id;
|
|
276
|
+
if (capabilitiesPassed && !isOwner) {
|
|
277
|
+
throw new McpError(403, "Only the agent's owner can grant capabilities to an attachment; ask them to re-run attach_tool with the capabilities.");
|
|
278
|
+
}
|
|
279
|
+
const existing = await tx.agentTool.findUnique({
|
|
280
|
+
where: { agentId_toolId: { agentId: args.agentId, toolId: args.toolId } },
|
|
281
|
+
});
|
|
282
|
+
// Creating an attachment needs a tool the caller may attach (Phase
|
|
283
|
+
// 1: its own or an owner-less one). Re-attaching an existing one
|
|
284
|
+
// only changes its capabilities: that is how the agent's owner
|
|
285
|
+
// grants them to a write-grantee's tool, or re-grants them after a
|
|
286
|
+
// make_owner transfer, so the tool check does not apply.
|
|
287
|
+
const tool = existing
|
|
288
|
+
? await tx.tool.findUniqueOrThrow({ where: { id: args.toolId } })
|
|
289
|
+
: await requireOwnedTool(tx, args.toolId, ctx.principal.id);
|
|
290
|
+
// The runtime dispatches by name, and names are only unique per
|
|
291
|
+
// owner: a second same-named tool on one agent would be a
|
|
292
|
+
// duplicate tool name to the model and silently shadow one of them.
|
|
293
|
+
const clash = await findSameNamedAttachedTool(tx, args.agentId, tool);
|
|
294
|
+
if (clash) {
|
|
295
|
+
// The clashing tool may be another principal's private one (a
|
|
296
|
+
// write-grantee's, or one left from before a transfer): name its
|
|
297
|
+
// id only when the caller could read it anyway.
|
|
298
|
+
const which = canRead(clash.ownerId, ctx.principal.id)
|
|
299
|
+
? ` (${clash.id})`
|
|
300
|
+
: " (a tool owned by another principal)";
|
|
301
|
+
throw new McpError(409, `Agent "${args.agentId}" already has a different tool named "${tool.name}" attached${which}; detach it first.`);
|
|
302
|
+
}
|
|
303
|
+
// Re-granting an attachment this owner never vouched for: what is
|
|
304
|
+
// not restated is reset, never silently adopted.
|
|
305
|
+
const keepUnstated = existing?.capabilitiesGrantedById === agent.ownerId;
|
|
306
|
+
const field = (key, empty) => patch.data[key] !== undefined ? { [key]: patch.data[key] } : keepUnstated ? {} : { [key]: empty };
|
|
307
|
+
const ownerGrant = isOwner
|
|
308
|
+
? {
|
|
309
|
+
...field("allowedSecrets", []),
|
|
310
|
+
...field("allowedDatastorePrefixes", []),
|
|
311
|
+
...field("allowedHosts", []),
|
|
312
|
+
...field("allowedSharedDatastorePrefixes", {}),
|
|
313
|
+
capabilitiesGrantedById: agent.ownerId,
|
|
314
|
+
}
|
|
315
|
+
: {};
|
|
153
316
|
await tx.agentTool.upsert({
|
|
154
317
|
where: { agentId_toolId: { agentId: args.agentId, toolId: args.toolId } },
|
|
155
318
|
create: {
|
|
156
319
|
agentId: args.agentId,
|
|
157
320
|
toolId: args.toolId,
|
|
158
|
-
allowedSecrets: patch.data.allowedSecrets
|
|
159
|
-
allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes
|
|
160
|
-
allowedHosts: patch.data.allowedHosts
|
|
161
|
-
allowedSharedDatastorePrefixes: patch.data.allowedSharedDatastorePrefixes
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
...(patch.data.allowedSecrets !== undefined ? { allowedSecrets: patch.data.allowedSecrets } : {}),
|
|
165
|
-
...(patch.data.allowedDatastorePrefixes !== undefined
|
|
166
|
-
? { allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes }
|
|
167
|
-
: {}),
|
|
168
|
-
...(patch.data.allowedHosts !== undefined ? { allowedHosts: patch.data.allowedHosts } : {}),
|
|
169
|
-
...(patch.data.allowedSharedDatastorePrefixes !== undefined
|
|
170
|
-
? { allowedSharedDatastorePrefixes: patch.data.allowedSharedDatastorePrefixes }
|
|
171
|
-
: {}),
|
|
321
|
+
allowedSecrets: (isOwner && patch.data.allowedSecrets) || [],
|
|
322
|
+
allowedDatastorePrefixes: (isOwner && patch.data.allowedDatastorePrefixes) || [],
|
|
323
|
+
allowedHosts: (isOwner && patch.data.allowedHosts) || [],
|
|
324
|
+
allowedSharedDatastorePrefixes: (isOwner && patch.data.allowedSharedDatastorePrefixes) || {},
|
|
325
|
+
attachedById: ctx.principal.id,
|
|
326
|
+
capabilitiesGrantedById: isOwner ? agent.ownerId : null,
|
|
172
327
|
},
|
|
328
|
+
// A non-owner's re-attach leaves an existing attachment's
|
|
329
|
+
// capabilities and consent stamp untouched.
|
|
330
|
+
update: ownerGrant,
|
|
173
331
|
});
|
|
174
332
|
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
175
333
|
return textResult({ attached: true });
|
|
@@ -184,8 +342,9 @@ export function registerToolAuthoringTools(mcp) {
|
|
|
184
342
|
required: ["agentId", "toolId"],
|
|
185
343
|
},
|
|
186
344
|
handler: async (args, ctx) => {
|
|
187
|
-
|
|
188
|
-
|
|
345
|
+
// Write on the agent is enough, whoever owns the tool: an agent's
|
|
346
|
+
// owner (or write-grantee) can always pull a tool off it.
|
|
347
|
+
await requireAgentAccess(ctx, args.agentId, "write");
|
|
189
348
|
await ctx.db.agentTool.deleteMany({ where: { agentId: args.agentId, toolId: args.toolId } });
|
|
190
349
|
return textResult({ detached: true });
|
|
191
350
|
},
|
|
@@ -195,13 +354,15 @@ export function registerToolAuthoringTools(mcp) {
|
|
|
195
354
|
scope: "tools:write",
|
|
196
355
|
inputSchema: { type: "object", properties: { agentId: { type: "string" } } },
|
|
197
356
|
handler: async (args, ctx) => {
|
|
198
|
-
const project = (tool) => tool
|
|
357
|
+
const project = (tool) => projectTool(tool, ctx.principal.id);
|
|
199
358
|
if (args.agentId) {
|
|
200
|
-
await
|
|
359
|
+
const { access } = await requireAgentAccess(ctx, args.agentId, "read");
|
|
201
360
|
const rows = await ctx.db.agentTool.findMany({ where: { agentId: args.agentId }, include: { tool: true } });
|
|
361
|
+
// The agent's owner sees every tool on it (projected: code only for
|
|
362
|
+
// the tool's own owner); anyone else only tools they could see anyway.
|
|
202
363
|
return textResult(rows
|
|
203
364
|
.map((r) => r.tool)
|
|
204
|
-
.filter((tool) => canRead(tool.ownerId, ctx.principal.id))
|
|
365
|
+
.filter((tool) => access === "owner" || canRead(tool.ownerId, ctx.principal.id))
|
|
205
366
|
.map(project));
|
|
206
367
|
}
|
|
207
368
|
const tools = await ctx.db.tool.findMany({ where: visibleToPrincipal(ctx.principal.id) });
|
|
@@ -33,7 +33,8 @@ import { CodingBaseRefSchema, CodingTaskOverrideSchema } from "../../coding/prot
|
|
|
33
33
|
import { z } from "zod";
|
|
34
34
|
import { McpError } from "../errors.js";
|
|
35
35
|
import { createTaskResult, getTask, cancelTask } from "../tasks/manager.js";
|
|
36
|
-
import {
|
|
36
|
+
import { requireOwnedTask } from "../auth/ownership.js";
|
|
37
|
+
import { assertAgentAccess } from "../auth/access.js";
|
|
37
38
|
import { textResult } from "./text-result.js";
|
|
38
39
|
const DEFAULT_TASK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
39
40
|
const TriggerAgentSchema = z
|
|
@@ -50,6 +51,29 @@ function parseTriggerArgs(args) {
|
|
|
50
51
|
const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ");
|
|
51
52
|
throw new McpError(400, `Invalid trigger_agent arguments: ${details}`);
|
|
52
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* A non-owner's coding task or baseRef would steer what the owner's
|
|
56
|
+
* repository authority does, so it is allowed only when the owner opted in
|
|
57
|
+
* to non-owner task text (codingProfile.allowWebhookTaskOverride, the same
|
|
58
|
+
* opt-in webhooks use). Otherwise a non-owner run uses the owner's
|
|
59
|
+
* defaultTask. Resource-sharing grants spec §3.4.5.
|
|
60
|
+
*/
|
|
61
|
+
function assertOverrideAllowed(principalId, agent, args) {
|
|
62
|
+
// Strictly the owner: the stdio operator's owner-level access doesn't make
|
|
63
|
+
// it the owner whose repository authority the task steers (review M8).
|
|
64
|
+
const isOwner = agent.ownerId !== null && agent.ownerId === principalId;
|
|
65
|
+
if (isOwner || (args.task === undefined && args.baseRef === undefined))
|
|
66
|
+
return;
|
|
67
|
+
if (!agent.codingProfile?.allowWebhookTaskOverride) {
|
|
68
|
+
throw new McpError(403, `Agent "${agent.id}": only its owner can pass a task or baseRef, unless the owner allows it (codingProfile.allowWebhookTaskOverride).`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** execute on the agent, plus the override rule; `db` is the transaction when re-checking. */
|
|
72
|
+
async function requireTriggerable(ctx, row, id, args, db = ctx.db) {
|
|
73
|
+
const { agent } = await assertAgentAccess(ctx, row, id, "execute", db);
|
|
74
|
+
assertOverrideAllowed(ctx.principal.id, agent, args);
|
|
75
|
+
return agent;
|
|
76
|
+
}
|
|
53
77
|
export function registerTriggerTool(mcp) {
|
|
54
78
|
mcp.registerTool({
|
|
55
79
|
name: "trigger_agent",
|
|
@@ -62,7 +86,7 @@ export function registerTriggerTool(mcp) {
|
|
|
62
86
|
},
|
|
63
87
|
handler: async (rawArgs, ctx) => {
|
|
64
88
|
const args = parseTriggerArgs(rawArgs);
|
|
65
|
-
const agent = await
|
|
89
|
+
const agent = await requireTriggerable(ctx, await ctx.db.agent.findUnique({ where: { id: args.agentId }, include: { codingProfile: true } }), args.agentId, args);
|
|
66
90
|
if (agent.kind !== "coding" && (args.task !== undefined || args.baseRef !== undefined)) {
|
|
67
91
|
throw new McpError(400, "Task and baseRef overrides are only valid for coding agents.");
|
|
68
92
|
}
|
|
@@ -74,10 +98,11 @@ export function registerTriggerTool(mcp) {
|
|
|
74
98
|
codingTask: args.task,
|
|
75
99
|
codingBaseRef: args.baseRef,
|
|
76
100
|
task: ctx.clientSupportsTasks ? { principalId: ctx.principal.id, ttlMs: DEFAULT_TASK_TTL_MS } : undefined,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
101
|
+
triggeredById: ctx.principal.id,
|
|
102
|
+
// Re-checked through the transaction: a concurrent revoke or
|
|
103
|
+
// make_owner conflicts instead of racing the run in.
|
|
104
|
+
beforePersist: async (tx, current) => {
|
|
105
|
+
await requireTriggerable(ctx, current, agent.id, args, tx);
|
|
81
106
|
return true;
|
|
82
107
|
},
|
|
83
108
|
});
|
|
@@ -88,6 +113,11 @@ export function registerTriggerTool(mcp) {
|
|
|
88
113
|
throw new Error("Run task was not persisted.");
|
|
89
114
|
return textResult(createTaskResult(dispatched.task, DEFAULT_TASK_TTL_MS));
|
|
90
115
|
}
|
|
116
|
+
// A run refused at dispatch (its budget group or run tree is spent) is
|
|
117
|
+
// already terminal: say so now rather than only through get_run.
|
|
118
|
+
if (dispatched.run.status === "refused") {
|
|
119
|
+
return textResult({ runId: dispatched.run.id, status: "refused", error: dispatched.run.error });
|
|
120
|
+
}
|
|
91
121
|
return textResult({ runId: dispatched.run.id });
|
|
92
122
|
},
|
|
93
123
|
});
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { createWebhook, listWebhooks, deleteWebhook } from "../../core/webhooks.js";
|
|
2
|
-
import {
|
|
2
|
+
import { McpError } from "../errors.js";
|
|
3
|
+
import { requireAgentAccess } from "../auth/access.js";
|
|
3
4
|
import { textResult } from "./text-result.js";
|
|
4
5
|
export function registerWebhookTools(mcp) {
|
|
5
6
|
mcp.registerTool({
|
|
6
7
|
name: "create_webhook",
|
|
7
8
|
scope: "webhooks:write",
|
|
9
|
+
description: "Creates a standing trigger for an agent (needs write on it) and returns its secret once. It fires only while you still own the agent or hold execute on it.",
|
|
8
10
|
inputSchema: { type: "object", properties: { agentId: { type: "string" } }, required: ["agentId"] },
|
|
9
11
|
handler: async (args, ctx) => {
|
|
10
|
-
|
|
12
|
+
// A standing trigger that can also carry task text for a native agent:
|
|
13
|
+
// write, not just execute (resource-sharing grants spec §3.10).
|
|
14
|
+
await requireAgentAccess(ctx, args.agentId, "write");
|
|
11
15
|
const created = await createWebhook(args.agentId, ctx.principal.id, ctx.db);
|
|
12
16
|
return textResult(created);
|
|
13
17
|
},
|
|
@@ -15,18 +19,25 @@ export function registerWebhookTools(mcp) {
|
|
|
15
19
|
mcp.registerTool({
|
|
16
20
|
name: "list_webhooks",
|
|
17
21
|
scope: "webhooks:write",
|
|
22
|
+
description: "Lists webhooks you created, and every webhook on an agent you own.",
|
|
18
23
|
inputSchema: { type: "object", properties: {} },
|
|
19
24
|
handler: async (_args, ctx) => {
|
|
20
|
-
const webhooks = await listWebhooks(ctx.principal.id, ctx.db);
|
|
25
|
+
const webhooks = await listWebhooks(ctx.principal.id, ctx.db, { all: ctx.operator === true });
|
|
21
26
|
return textResult(webhooks);
|
|
22
27
|
},
|
|
23
28
|
});
|
|
24
29
|
mcp.registerTool({
|
|
25
30
|
name: "delete_webhook",
|
|
26
31
|
scope: "webhooks:write",
|
|
32
|
+
description: "Deletes a webhook you created, or any webhook on an agent you own.",
|
|
27
33
|
inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
|
|
28
34
|
handler: async (args, ctx) => {
|
|
29
|
-
await
|
|
35
|
+
const webhook = await ctx.db.webhook.findUnique({ where: { id: args.id }, include: { agent: true } });
|
|
36
|
+
const principalId = ctx.principal.id;
|
|
37
|
+
const allowed = webhook !== null &&
|
|
38
|
+
(ctx.operator === true || webhook.ownerId === principalId || webhook.agent.ownerId === principalId);
|
|
39
|
+
if (!allowed)
|
|
40
|
+
throw new McpError(403, `Webhook "${args.id}" is not owned by the caller.`);
|
|
30
41
|
await deleteWebhook(args.id, ctx.db);
|
|
31
42
|
return textResult({ deleted: args.id });
|
|
32
43
|
},
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ServerResponse } from "node:http";
|
|
2
2
|
import { type PerRequestResponseMode } from "@modelcontextprotocol/server";
|
|
3
|
-
import type { PrismaClient } from "
|
|
3
|
+
import type { PrismaClient } from "#prisma";
|
|
4
4
|
import type { AuthProvider } from "../../providers/auth/types.js";
|
|
5
5
|
import type { SelfHostedAuthProvider } from "../../providers/auth/self-hosted.js";
|
|
6
6
|
import type { WardbyMcpServer } from "../server.js";
|
|
7
7
|
import type { McpProviders } from "../context.js";
|
|
8
|
+
import { type GitHubIngressDeps } from "../host-events/github-ingress.js";
|
|
9
|
+
import { type HostUserCallbackDeps } from "../host-events/github-user-callback.js";
|
|
8
10
|
export interface HttpServerConfig {
|
|
9
11
|
canonicalUri: string;
|
|
10
12
|
httpBind: {
|
|
@@ -25,6 +27,14 @@ export interface StartHttpServerOptions {
|
|
|
25
27
|
providers: McpProviders;
|
|
26
28
|
};
|
|
27
29
|
selfHosted?: SelfHostedAuthProvider;
|
|
30
|
+
/** Code-review host webhooks; absent = the endpoints answer 404. */
|
|
31
|
+
hostEvents?: {
|
|
32
|
+
github?: GitHubIngressDeps;
|
|
33
|
+
};
|
|
34
|
+
/** The host identity-link browser callback (link_host_account); absent = 404. */
|
|
35
|
+
hostUserAuth?: {
|
|
36
|
+
github?: HostUserCallbackDeps;
|
|
37
|
+
};
|
|
28
38
|
}
|
|
29
39
|
export interface HttpServerHandle {
|
|
30
40
|
port: number;
|
|
@@ -5,6 +5,8 @@ import { toNodeHandler, hostHeaderValidation } from "@modelcontextprotocol/node"
|
|
|
5
5
|
import { authenticate, protectedResourceMetadata } from "../auth/resource-server.js";
|
|
6
6
|
import { McpError } from "../errors.js";
|
|
7
7
|
import { handleWebhookIngress } from "../webhooks/ingress.js";
|
|
8
|
+
import { handleGitHubEventIngress } from "../host-events/github-ingress.js";
|
|
9
|
+
import { handleHostUserCallback } from "../host-events/github-user-callback.js";
|
|
8
10
|
import { canonicalUrl, HTTP_LIMITS, HttpBoundaryError, parseBody, readBody } from "./http-limits.js";
|
|
9
11
|
import { browserHandler } from "../auth/self-hosted/browser.js";
|
|
10
12
|
import { handleSecretElicitationForm, SECRET_ELICITATION_PATH } from "../tools/secret-elicitation-form.js";
|
|
@@ -73,6 +75,7 @@ export async function startHttpServer(opts) {
|
|
|
73
75
|
const controller = new AbortController();
|
|
74
76
|
const timer = setTimeout(() => controller.abort(), HTTP_LIMITS.requestMs);
|
|
75
77
|
let body;
|
|
78
|
+
let rawBody;
|
|
76
79
|
try {
|
|
77
80
|
const hasBody = req.method === "POST" ||
|
|
78
81
|
req.method === "PUT" ||
|
|
@@ -80,8 +83,11 @@ export async function startHttpServer(opts) {
|
|
|
80
83
|
req.headers["content-length"] ||
|
|
81
84
|
req.headers["transfer-encoding"];
|
|
82
85
|
if (hasBody) {
|
|
83
|
-
const limit = url.pathname === "/mcp" || url.pathname.startsWith("/webhooks/")
|
|
86
|
+
const limit = url.pathname === "/mcp" || url.pathname.startsWith("/webhooks/") || url.pathname.startsWith("/hosts/")
|
|
87
|
+
? HTTP_LIMITS.json
|
|
88
|
+
: HTTP_LIMITS.auth;
|
|
84
89
|
const raw = await readBody(req, limit, controller.signal);
|
|
90
|
+
rawBody = raw;
|
|
85
91
|
body = parseBody(raw, req.headers["content-type"]);
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -117,6 +123,30 @@ export async function startHttpServer(opts) {
|
|
|
117
123
|
sendJson(res, result.status, result.body);
|
|
118
124
|
return;
|
|
119
125
|
}
|
|
126
|
+
if (url.pathname === "/hosts/github/events" && req.method === "POST") {
|
|
127
|
+
if (!opts.hostEvents?.github) {
|
|
128
|
+
sendJson(res, 404, { error: "not_found" });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const headers = Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v]));
|
|
132
|
+
const result = await handleGitHubEventIngress({ headers, rawBody: rawBody ?? "" }, opts.hostEvents.github);
|
|
133
|
+
sendJson(res, result.status, result.body);
|
|
134
|
+
if (result.afterResponse) {
|
|
135
|
+
void result.afterResponse().catch((err) => httpLog.warn({ err }, "host event follow-up failed"));
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (url.pathname === "/hosts/github/user-callback" && req.method === "GET") {
|
|
140
|
+
// Unauthenticated by design (a browser redirect from GitHub): the
|
|
141
|
+
// single-use, hashed OAuth state is what binds it to a link request,
|
|
142
|
+
// and the canonical-Host check above has already run.
|
|
143
|
+
if (!opts.hostUserAuth?.github) {
|
|
144
|
+
sendJson(res, 404, { error: "not_found" });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
await handleHostUserCallback(url, res, opts.hostUserAuth.github);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
120
150
|
if (url.pathname === "/mcp") {
|
|
121
151
|
if (req.method === "POST" && (!body || body instanceof URLSearchParams))
|
|
122
152
|
throw new HttpBoundaryError(415, "expected_json");
|
|
@@ -125,7 +155,7 @@ export async function startHttpServer(opts) {
|
|
|
125
155
|
token: "",
|
|
126
156
|
clientId: ctx.principal.subject,
|
|
127
157
|
scopes: [...ctx.scopes],
|
|
128
|
-
extra: { principal: ctx.principal },
|
|
158
|
+
extra: { principal: ctx.principal, roles: ctx.roles },
|
|
129
159
|
};
|
|
130
160
|
await nodeHandler(req, res, body);
|
|
131
161
|
return;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* route in streamable-http.ts, matching the design's "route is
|
|
5
5
|
* unauthenticated by OAuth" invariant.
|
|
6
6
|
*/
|
|
7
|
-
import type { PrismaClient } from "
|
|
7
|
+
import type { PrismaClient } from "#prisma";
|
|
8
8
|
import type { Executor } from "../../providers/executor/types.js";
|
|
9
9
|
export interface WebhookIngressRequest {
|
|
10
10
|
headers: Record<string, string | undefined>;
|
|
@@ -13,7 +13,25 @@ export interface DelegatingAuthConfig {
|
|
|
13
13
|
issuer?: string;
|
|
14
14
|
audience?: string;
|
|
15
15
|
jwksUri?: string;
|
|
16
|
+
/**
|
|
17
|
+
* AUTH_ROLE_CLAIM: the access-token claim carrying the IdP's roles/groups —
|
|
18
|
+
* an exact top-level claim name first (e.g. Auth0's namespaced
|
|
19
|
+
* `https://wardby.example/roles`, Okta `groups`, FusionAuth `roles`), else
|
|
20
|
+
* a dotted path into nested objects (Keycloak `realm_access.roles`).
|
|
21
|
+
*/
|
|
22
|
+
roleClaim?: string;
|
|
23
|
+
/**
|
|
24
|
+
* AUTH_ROLE_MAP: comma-separated `idpValue=wardbyRole` pairs, e.g.
|
|
25
|
+
* `wardby-admin=admin,wardby-packages=package-approver,wardby-services=service-manager`.
|
|
26
|
+
*/
|
|
27
|
+
roleMap?: string;
|
|
16
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Parses AUTH_ROLE_MAP. Whitespace around entries and around `=` is ignored;
|
|
31
|
+
* an unknown wardby role, or a malformed or empty entry, is a startup error.
|
|
32
|
+
* The LAST `=` splits an entry (wardby role names never contain one).
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseRoleMap(value: string): Map<string, string[]>;
|
|
17
35
|
/**
|
|
18
36
|
* Every spelling of the configured audience a token may legitimately carry.
|
|
19
37
|
*
|
|
@@ -35,6 +53,7 @@ export declare class DelegatingAuthProvider implements AuthProvider {
|
|
|
35
53
|
private readonly config;
|
|
36
54
|
private readonly audiences;
|
|
37
55
|
private readonly jwks;
|
|
56
|
+
private readonly roleMap?;
|
|
38
57
|
constructor(config: DelegatingAuthConfig, jwks?: JWTVerifyGetKey);
|
|
39
58
|
verifyBearer(token: string): Promise<VerifiedToken>;
|
|
40
59
|
profile(idToken: string): Promise<AuthProfile>;
|