@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
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Webhook/Task
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Ownership decisions for the resource types that don't have grants yet:
|
|
3
|
+
* BudgetGroup/Tool/Secret/Datastore/Webhook/Task. A null owner still means
|
|
4
|
+
* "public" here: readable AND mutable by anyone. Tool and budget-group
|
|
5
|
+
* grants replace that in the resource-sharing grants Phases 2 and 3.
|
|
6
|
+
*
|
|
7
|
+
* Agents are NOT decided here any more: they use explicit grants (owner, or
|
|
8
|
+
* read < execute < write), and an owner-less agent is reachable only
|
|
9
|
+
* through its grants -- see ./access.ts and core/grants.ts. `isOwner` stays
|
|
6
10
|
* available (strict, no null-passthrough) for checks that must stay
|
|
7
|
-
* owner-only regardless of publicness
|
|
8
|
-
* feature would use it to tell "already owned" apart from "public" before
|
|
9
|
-
* deciding whether to stamp a new owner. Every tool file routes through
|
|
10
|
-
* this module instead of re-deriving the null-handling itself, so changing
|
|
11
|
-
* the policy again later is a change here, not an N-file sweep.
|
|
11
|
+
* owner-only regardless of publicness.
|
|
12
12
|
*/
|
|
13
|
-
import type { PrismaClient, Tool } from "
|
|
13
|
+
import type { PrismaClient, Tool } from "#prisma";
|
|
14
14
|
export declare function canRead(ownerId: string | null, principalId: string): boolean;
|
|
15
15
|
export declare function isOwner(ownerId: string | null, principalId: string): boolean;
|
|
16
16
|
/** Same rule as canRead: a null owner is a mutation target for anyone, same as it's a read target for anyone. */
|
|
@@ -24,90 +24,64 @@ export declare function visibleToPrincipal(principalId: string): {
|
|
|
24
24
|
ownerId: null;
|
|
25
25
|
})[];
|
|
26
26
|
};
|
|
27
|
-
export declare function
|
|
27
|
+
export declare function requireOwnedBudgetGroup(db: PrismaClient, id: string, principalId: string): Promise<{
|
|
28
28
|
name: string;
|
|
29
|
-
model: string;
|
|
30
|
-
budgetUsd: import("@prisma/client/runtime/library").Decimal;
|
|
31
29
|
id: string;
|
|
32
|
-
systemPrompt: string;
|
|
33
|
-
createdAt: Date;
|
|
34
|
-
updatedAt: Date;
|
|
35
|
-
kind: import("@prisma/client").$Enums.AgentKind;
|
|
36
|
-
schedule: string | null;
|
|
37
|
-
timezone: string;
|
|
38
|
-
scheduleEnabled: boolean;
|
|
39
|
-
lastScheduledAt: Date | null;
|
|
40
|
-
maxTurns: number;
|
|
41
30
|
ownerId: string | null;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
name: string;
|
|
47
|
-
model: string;
|
|
48
|
-
budgetUsd: import("@prisma/client/runtime/library").Decimal;
|
|
49
|
-
id: string;
|
|
50
|
-
systemPrompt: string;
|
|
31
|
+
dailyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
32
|
+
weeklyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
33
|
+
monthlyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
34
|
+
warnThresholdRatio: import("@prisma/client-runtime-utils").Decimal;
|
|
51
35
|
createdAt: Date;
|
|
52
36
|
updatedAt: Date;
|
|
53
|
-
kind: import("@prisma/client").$Enums.AgentKind;
|
|
54
|
-
schedule: string | null;
|
|
55
|
-
timezone: string;
|
|
56
|
-
scheduleEnabled: boolean;
|
|
57
|
-
lastScheduledAt: Date | null;
|
|
58
|
-
maxTurns: number;
|
|
59
|
-
ownerId: string | null;
|
|
60
|
-
budgetGroupId: string | null;
|
|
61
|
-
memoryEnabled: boolean;
|
|
62
37
|
}>;
|
|
63
|
-
export declare function
|
|
38
|
+
export declare function requireReadableBudgetGroup(db: Pick<PrismaClient, "budgetGroup">, id: string, principalId: string): Promise<{
|
|
64
39
|
name: string;
|
|
65
40
|
id: string;
|
|
66
|
-
createdAt: Date;
|
|
67
|
-
updatedAt: Date;
|
|
68
41
|
ownerId: string | null;
|
|
69
|
-
dailyBudgetUsd: import("@prisma/client
|
|
70
|
-
weeklyBudgetUsd: import("@prisma/client
|
|
71
|
-
monthlyBudgetUsd: import("@prisma/client
|
|
72
|
-
warnThresholdRatio: import("@prisma/client
|
|
73
|
-
}>;
|
|
74
|
-
export declare function requireReadableBudgetGroup(db: PrismaClient, id: string, principalId: string): Promise<{
|
|
75
|
-
name: string;
|
|
76
|
-
id: string;
|
|
42
|
+
dailyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
43
|
+
weeklyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
44
|
+
monthlyBudgetUsd: import("@prisma/client-runtime-utils").Decimal | null;
|
|
45
|
+
warnThresholdRatio: import("@prisma/client-runtime-utils").Decimal;
|
|
77
46
|
createdAt: Date;
|
|
78
47
|
updatedAt: Date;
|
|
79
|
-
ownerId: string | null;
|
|
80
|
-
dailyBudgetUsd: import("@prisma/client/runtime/library").Decimal | null;
|
|
81
|
-
weeklyBudgetUsd: import("@prisma/client/runtime/library").Decimal | null;
|
|
82
|
-
monthlyBudgetUsd: import("@prisma/client/runtime/library").Decimal | null;
|
|
83
|
-
warnThresholdRatio: import("@prisma/client/runtime/library").Decimal;
|
|
84
48
|
}>;
|
|
85
49
|
export declare function requireOwnedSecret(db: PrismaClient, id: string, principalId: string): Promise<void>;
|
|
86
50
|
export declare function requireOwnedWebhook(db: PrismaClient, id: string, principalId: string): Promise<void>;
|
|
87
51
|
export declare function requireOwnedDatastore(db: PrismaClient, id: string, principalId: string): Promise<{
|
|
88
52
|
name: string;
|
|
89
53
|
id: string;
|
|
54
|
+
ownerId: string | null;
|
|
90
55
|
createdAt: Date;
|
|
91
56
|
updatedAt: Date;
|
|
92
|
-
ownerId: string | null;
|
|
93
57
|
}>;
|
|
94
58
|
export declare function requireOwnedTool(db: Pick<PrismaClient, "tool">, id: string, principalId: string): Promise<Tool>;
|
|
59
|
+
/**
|
|
60
|
+
* Owner-only, with no null-is-public passthrough: for changing or removing
|
|
61
|
+
* a tool's code (update_tool/delete_tool), where requireOwnedTool's rule
|
|
62
|
+
* would let anyone rewrite a public tool. A public tool is readable but
|
|
63
|
+
* immutable over MCP (403) -- only an operator can change one, via the CLI.
|
|
64
|
+
* A tool the caller can't read is 404, the same as a missing one.
|
|
65
|
+
*/
|
|
66
|
+
export declare function requireStrictlyOwnedTool(db: Pick<PrismaClient, "tool">, id: string, principalId: string): Promise<Tool>;
|
|
67
|
+
/** requireStrictlyOwnedTool's rule for a row already read (e.g. inside core/tool-admin.ts's transaction). */
|
|
68
|
+
export declare function assertStrictlyOwnedTool(tool: Tool | null, id: string, principalId: string): Tool;
|
|
95
69
|
/**
|
|
96
70
|
* A Task's principalId is the caller who triggered it, not the underlying
|
|
97
71
|
* agent's owner — a public agent's runs are still private to whoever
|
|
98
72
|
* triggered them. No canRead/null=public path exists here on purpose.
|
|
99
73
|
*/
|
|
100
74
|
export declare function requireOwnedTask(db: PrismaClient, taskId: string, principalId: string): Promise<{
|
|
101
|
-
status: import("
|
|
75
|
+
status: import("#prisma").TaskStatus;
|
|
102
76
|
runId: string | null;
|
|
103
|
-
|
|
104
|
-
|
|
77
|
+
kind: import("#prisma").TaskKind;
|
|
78
|
+
error: import("@prisma/client/runtime/client").JsonValue | null;
|
|
79
|
+
result: import("@prisma/client/runtime/client").JsonValue | null;
|
|
105
80
|
id: string;
|
|
106
81
|
createdAt: Date;
|
|
107
82
|
updatedAt: Date;
|
|
108
|
-
kind: import("@prisma/client").$Enums.TaskKind;
|
|
109
83
|
principalId: string | null;
|
|
110
|
-
inputRequests: import("@prisma/client/runtime/
|
|
111
|
-
inputResponses: import("@prisma/client/runtime/
|
|
84
|
+
inputRequests: import("@prisma/client/runtime/client").JsonValue | null;
|
|
85
|
+
inputResponses: import("@prisma/client/runtime/client").JsonValue | null;
|
|
112
86
|
ttlAt: Date;
|
|
113
87
|
}>;
|
|
@@ -17,20 +17,6 @@ export function assertCanMutate(ownerId, principalId, notFoundMessage) {
|
|
|
17
17
|
export function visibleToPrincipal(principalId) {
|
|
18
18
|
return { OR: [{ ownerId: principalId }, { ownerId: null }] };
|
|
19
19
|
}
|
|
20
|
-
export async function requireOwnedAgent(db, id, principalId) {
|
|
21
|
-
const agent = await db.agent.findUnique({ where: { id } });
|
|
22
|
-
if (!agent)
|
|
23
|
-
throw new McpError(404, `Agent "${id}" not found.`);
|
|
24
|
-
assertCanMutate(agent.ownerId, principalId, `Agent "${id}" is not owned by the caller.`);
|
|
25
|
-
return agent;
|
|
26
|
-
}
|
|
27
|
-
export async function requireReadableAgent(db, id, principalId) {
|
|
28
|
-
const agent = await db.agent.findUnique({ where: { id } });
|
|
29
|
-
if (!agent || !canRead(agent.ownerId, principalId)) {
|
|
30
|
-
throw new McpError(404, `Agent "${id}" not found.`);
|
|
31
|
-
}
|
|
32
|
-
return agent;
|
|
33
|
-
}
|
|
34
20
|
export async function requireOwnedBudgetGroup(db, id, principalId) {
|
|
35
21
|
const group = await db.budgetGroup.findUnique({ where: { id } });
|
|
36
22
|
if (!group)
|
|
@@ -71,6 +57,25 @@ export async function requireOwnedTool(db, id, principalId) {
|
|
|
71
57
|
assertCanMutate(tool.ownerId, principalId, `Tool "${id}" is not owned by the caller.`);
|
|
72
58
|
return tool;
|
|
73
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Owner-only, with no null-is-public passthrough: for changing or removing
|
|
62
|
+
* a tool's code (update_tool/delete_tool), where requireOwnedTool's rule
|
|
63
|
+
* would let anyone rewrite a public tool. A public tool is readable but
|
|
64
|
+
* immutable over MCP (403) -- only an operator can change one, via the CLI.
|
|
65
|
+
* A tool the caller can't read is 404, the same as a missing one.
|
|
66
|
+
*/
|
|
67
|
+
export async function requireStrictlyOwnedTool(db, id, principalId) {
|
|
68
|
+
return assertStrictlyOwnedTool(await db.tool.findUnique({ where: { id } }), id, principalId);
|
|
69
|
+
}
|
|
70
|
+
/** requireStrictlyOwnedTool's rule for a row already read (e.g. inside core/tool-admin.ts's transaction). */
|
|
71
|
+
export function assertStrictlyOwnedTool(tool, id, principalId) {
|
|
72
|
+
if (!tool || !canRead(tool.ownerId, principalId))
|
|
73
|
+
throw new McpError(404, `Tool "${id}" not found.`);
|
|
74
|
+
if (!isOwner(tool.ownerId, principalId)) {
|
|
75
|
+
throw new McpError(403, `Tool "${id}" is public; public tools can't be changed or deleted over MCP.`);
|
|
76
|
+
}
|
|
77
|
+
return tool;
|
|
78
|
+
}
|
|
74
79
|
/**
|
|
75
80
|
* A Task's principalId is the caller who triggered it, not the underlying
|
|
76
81
|
* agent's owner — a public agent's runs are still private to whoever
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* always carries an owner, so a deployment that later moves from stdio to
|
|
6
6
|
* HTTP+OAuth doesn't orphan locally-authored agents.
|
|
7
7
|
*/
|
|
8
|
-
import type { PrismaClient, Principal } from "
|
|
8
|
+
import type { PrismaClient, Principal } from "#prisma";
|
|
9
9
|
export declare function resolvePrincipal(subject: string, db: PrismaClient): Promise<Principal>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set-time repository authorization for the MCP tools that give an agent
|
|
3
|
+
* authority over a repository (link_repository; create_agent/update_agent's
|
|
4
|
+
* codingProfile.repository). Returns the stamp to store on the granting row,
|
|
5
|
+
* or throws an actionable McpError. See core/repo-access.ts for the use-time
|
|
6
|
+
* side and the rules; the host call happens here, before any transaction.
|
|
7
|
+
*/
|
|
8
|
+
import { type AuthorizedVia, type RepoAccessKind } from "../../core/repo-access.js";
|
|
9
|
+
import type { McpRequestContext } from "../context.js";
|
|
10
|
+
export interface RepositoryAuthorization {
|
|
11
|
+
authorizedVia: AuthorizedVia;
|
|
12
|
+
authorizedById: string;
|
|
13
|
+
authorizedAt: Date;
|
|
14
|
+
}
|
|
15
|
+
export declare function authorizeRepositoryForSet(ctx: McpRequestContext, input: {
|
|
16
|
+
/** The agent's owner. The caller is always the owner here (or creating the agent). */
|
|
17
|
+
ownerId: string | null;
|
|
18
|
+
provider: string;
|
|
19
|
+
repository: string;
|
|
20
|
+
kind: RepoAccessKind;
|
|
21
|
+
adminOverride?: boolean;
|
|
22
|
+
}): Promise<RepositoryAuthorization>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set-time repository authorization for the MCP tools that give an agent
|
|
3
|
+
* authority over a repository (link_repository; create_agent/update_agent's
|
|
4
|
+
* codingProfile.repository). Returns the stamp to store on the granting row,
|
|
5
|
+
* or throws an actionable McpError. See core/repo-access.ts for the use-time
|
|
6
|
+
* side and the rules; the host call happens here, before any transaction.
|
|
7
|
+
*/
|
|
8
|
+
import { requiredLevel } from "../../core/repo-access.js";
|
|
9
|
+
import { McpError } from "../errors.js";
|
|
10
|
+
import { requireScope } from "./resource-server.js";
|
|
11
|
+
export async function authorizeRepositoryForSet(ctx, input) {
|
|
12
|
+
if (!input.ownerId) {
|
|
13
|
+
throw new McpError(400, "owner_required: agents without an owner can't be given a repository, because an owner-less agent has no owner " +
|
|
14
|
+
"whose GitHub access can be checked. An admin can assign an owner with make_owner first.");
|
|
15
|
+
}
|
|
16
|
+
const stamp = (authorizedVia) => ({
|
|
17
|
+
authorizedVia,
|
|
18
|
+
authorizedById: ctx.principal.id,
|
|
19
|
+
authorizedAt: new Date(),
|
|
20
|
+
});
|
|
21
|
+
if (input.adminOverride) {
|
|
22
|
+
// Role-gated: the scope alone never suffices (resource-server.ts).
|
|
23
|
+
requireScope(ctx, ctx.canonicalUri, "agents:admin");
|
|
24
|
+
return stamp("admin");
|
|
25
|
+
}
|
|
26
|
+
const gate = ctx.providers.repoAccess;
|
|
27
|
+
if (!gate) {
|
|
28
|
+
throw new McpError(503, "Repository access can't be checked on this deployment (no GitHub App is configured). " +
|
|
29
|
+
"An admin can approve it explicitly with adminOverride.");
|
|
30
|
+
}
|
|
31
|
+
const required = requiredLevel(input.kind);
|
|
32
|
+
const decision = await gate.authorizePrincipal({
|
|
33
|
+
principalId: input.ownerId,
|
|
34
|
+
provider: input.provider,
|
|
35
|
+
repository: input.repository,
|
|
36
|
+
required,
|
|
37
|
+
fresh: true,
|
|
38
|
+
});
|
|
39
|
+
if (decision.ok)
|
|
40
|
+
return stamp("host_permission");
|
|
41
|
+
switch (decision.reason) {
|
|
42
|
+
case "identity_not_linked":
|
|
43
|
+
throw new McpError(403, "Link your GitHub account first (link_host_account): wardby checks that your own GitHub account can " +
|
|
44
|
+
`access ${input.repository} before an agent you own may use it.`);
|
|
45
|
+
case "insufficient_permission":
|
|
46
|
+
throw new McpError(403, `Your linked GitHub account has ${decision.level ?? "no"} access to ${input.repository}; ` +
|
|
47
|
+
`this needs ${required}. Ask a repository admin for access, or a wardby admin to approve it (adminOverride).`);
|
|
48
|
+
default:
|
|
49
|
+
throw new McpError(503, `Could not verify your GitHub access to ${input.repository} right now (is the GitHub App installed on it?). Try again.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -4,12 +4,35 @@
|
|
|
4
4
|
* AuthProvider (delegating | self-hosted) is active — both implement
|
|
5
5
|
* verifyBearer, so this file never branches on auth mode.
|
|
6
6
|
*/
|
|
7
|
-
import type { PrismaClient } from "
|
|
7
|
+
import type { PrismaClient } from "#prisma";
|
|
8
8
|
import type { AuthProvider } from "../../providers/auth/types.js";
|
|
9
|
-
import { McpError } from "../errors.js";
|
|
9
|
+
import { McpError, insufficientScope } from "../errors.js";
|
|
10
10
|
import type { McpRequestContext, McpProviders } from "../context.js";
|
|
11
11
|
/** The initial capability tiers (§Scopes → capability tiers in the design doc). */
|
|
12
12
|
export declare const SCOPES_SUPPORTED: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Scopes whose operations reach beyond the caller's own (or public)
|
|
15
|
+
* resources — they double as the permission names roles grant:
|
|
16
|
+
* agents:admin reassigns ANY agent's owner (make_owner) and sets a BYO
|
|
17
|
+
* workerImageRef; packages:approve widens coding agents' package allowlists;
|
|
18
|
+
* services:manage changes the coding-run service catalog, which decides what
|
|
19
|
+
* runs next to every coding run that names an entry.
|
|
20
|
+
* A token scope only DELEGATES — it never authorizes on its own:
|
|
21
|
+
* requireScope/requireAnyScope honour one only when one of the caller's
|
|
22
|
+
* roles (McpRequestContext.roles, resolved live per request) grants it.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PRIVILEGED_SCOPES: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The built-in roles and the permissions (privileged scope names) each
|
|
27
|
+
* grants. No roles = member: every non-privileged scope, nothing privileged.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ROLE_PERMISSIONS: Readonly<Record<string, readonly string[]>>;
|
|
30
|
+
export declare const ROLE_NAMES: readonly string[];
|
|
31
|
+
export declare function isRoleName(name: string): boolean;
|
|
32
|
+
/** The permissions granted by `roles`; unknown role names grant nothing. */
|
|
33
|
+
export declare function permissionsOf(roles: readonly string[] | null | undefined): Set<string>;
|
|
34
|
+
/** Intersects a space-delimited scope string with `allowed`, deduplicated, keeping request order. */
|
|
35
|
+
export declare function limitScope(scope: string, allowed: readonly string[]): string;
|
|
13
36
|
export interface ProtectedResourceMetadata {
|
|
14
37
|
resource: string;
|
|
15
38
|
authorization_servers: string[];
|
|
@@ -44,4 +67,10 @@ export declare function authenticate(headers: AuthenticateHeaders, deps: Authent
|
|
|
44
67
|
* per missing scope.
|
|
45
68
|
*/
|
|
46
69
|
export declare function requireScope(ctx: McpRequestContext, canonicalUri: string, ...scopes: string[]): void;
|
|
47
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Like requireScope, but any ONE of `alternatives` suffices (e.g. package
|
|
72
|
+
* approval: packages:approve or agents:admin). The first alternative is the
|
|
73
|
+
* one named in the insufficient_scope challenge.
|
|
74
|
+
*/
|
|
75
|
+
export declare function requireAnyScope(ctx: McpRequestContext, canonicalUri: string, ...alternatives: string[]): void;
|
|
76
|
+
export { McpError, insufficientScope };
|
|
@@ -10,11 +10,51 @@ export const SCOPES_SUPPORTED = [
|
|
|
10
10
|
"secrets:write",
|
|
11
11
|
"webhooks:write",
|
|
12
12
|
"budget_groups:write",
|
|
13
|
+
// Approve coding agents' package allowlists (see docs/coding-packages.md).
|
|
14
|
+
"packages:approve",
|
|
15
|
+
// Create, update and delete coding-run service catalog entries (see docs/coding-services.md).
|
|
16
|
+
// Reading the catalog is agents:read.
|
|
17
|
+
"services:manage",
|
|
13
18
|
// Reassigns an agent's owner regardless of who currently owns it (or
|
|
14
19
|
// whether it's public) — a step above agents:write, which only ever lets
|
|
15
20
|
// a caller act on agents they already own or that are unowned.
|
|
16
21
|
"agents:admin",
|
|
22
|
+
// set_agent_memory / delete_agent_memory (reading memory is agents:read).
|
|
23
|
+
"memory:write",
|
|
17
24
|
];
|
|
25
|
+
/**
|
|
26
|
+
* Scopes whose operations reach beyond the caller's own (or public)
|
|
27
|
+
* resources — they double as the permission names roles grant:
|
|
28
|
+
* agents:admin reassigns ANY agent's owner (make_owner) and sets a BYO
|
|
29
|
+
* workerImageRef; packages:approve widens coding agents' package allowlists;
|
|
30
|
+
* services:manage changes the coding-run service catalog, which decides what
|
|
31
|
+
* runs next to every coding run that names an entry.
|
|
32
|
+
* A token scope only DELEGATES — it never authorizes on its own:
|
|
33
|
+
* requireScope/requireAnyScope honour one only when one of the caller's
|
|
34
|
+
* roles (McpRequestContext.roles, resolved live per request) grants it.
|
|
35
|
+
*/
|
|
36
|
+
export const PRIVILEGED_SCOPES = ["agents:admin", "packages:approve", "services:manage"];
|
|
37
|
+
/**
|
|
38
|
+
* The built-in roles and the permissions (privileged scope names) each
|
|
39
|
+
* grants. No roles = member: every non-privileged scope, nothing privileged.
|
|
40
|
+
*/
|
|
41
|
+
export const ROLE_PERMISSIONS = {
|
|
42
|
+
admin: ["agents:admin", "packages:approve", "services:manage"],
|
|
43
|
+
"package-approver": ["packages:approve"],
|
|
44
|
+
"service-manager": ["services:manage"],
|
|
45
|
+
};
|
|
46
|
+
export const ROLE_NAMES = Object.keys(ROLE_PERMISSIONS);
|
|
47
|
+
export function isRoleName(name) {
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(ROLE_PERMISSIONS, name);
|
|
49
|
+
}
|
|
50
|
+
/** The permissions granted by `roles`; unknown role names grant nothing. */
|
|
51
|
+
export function permissionsOf(roles) {
|
|
52
|
+
return new Set((roles ?? []).filter(isRoleName).flatMap((r) => ROLE_PERMISSIONS[r]));
|
|
53
|
+
}
|
|
54
|
+
/** Intersects a space-delimited scope string with `allowed`, deduplicated, keeping request order. */
|
|
55
|
+
export function limitScope(scope, allowed) {
|
|
56
|
+
return [...new Set(scope.split(/\s+/).filter((s) => allowed.includes(s)))].join(" ");
|
|
57
|
+
}
|
|
18
58
|
/** RFC 9728 §3.1: insert `/.well-known/oauth-protected-resource` before the resource's path. */
|
|
19
59
|
export function protectedResourceMetadataUrl(canonicalUri) {
|
|
20
60
|
const url = new URL(canonicalUri);
|
|
@@ -55,6 +95,9 @@ export async function authenticate(headers, deps) {
|
|
|
55
95
|
return {
|
|
56
96
|
principal,
|
|
57
97
|
scopes: new Set(verified.scopes),
|
|
98
|
+
// Decided by the provider on THIS request (self-hosted: AuthUser.roles
|
|
99
|
+
// read from the database; delegating: the configured signed role claim).
|
|
100
|
+
roles: (verified.wardbyRoles ?? []).filter(isRoleName),
|
|
58
101
|
canonicalUri: deps.canonicalUri,
|
|
59
102
|
providers: deps.providers,
|
|
60
103
|
db: deps.db,
|
|
@@ -75,8 +118,46 @@ export async function authenticate(headers, deps) {
|
|
|
75
118
|
*/
|
|
76
119
|
export function requireScope(ctx, canonicalUri, ...scopes) {
|
|
77
120
|
const held = scopes.every((s) => ctx.scopes.has(s));
|
|
78
|
-
if (held)
|
|
121
|
+
if (!held)
|
|
122
|
+
throw insufficientScope(scopes, protectedResourceMetadataUrl(canonicalUri));
|
|
123
|
+
requirePermission(ctx, scopes);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Like requireScope, but any ONE of `alternatives` suffices (e.g. package
|
|
127
|
+
* approval: packages:approve or agents:admin). The first alternative is the
|
|
128
|
+
* one named in the insufficient_scope challenge.
|
|
129
|
+
*/
|
|
130
|
+
export function requireAnyScope(ctx, canonicalUri, ...alternatives) {
|
|
131
|
+
const held = alternatives.filter((s) => ctx.scopes.has(s));
|
|
132
|
+
if (held.length === 0)
|
|
133
|
+
throw insufficientScope(alternatives.slice(0, 1), protectedResourceMetadataUrl(canonicalUri));
|
|
134
|
+
// One held alternative that is non-privileged, or that a role permits, suffices.
|
|
135
|
+
const permitted = permissionsOf(ctx.roles);
|
|
136
|
+
if (held.some((s) => !PRIVILEGED_SCOPES.includes(s) || permitted.has(s)))
|
|
79
137
|
return;
|
|
80
|
-
|
|
138
|
+
// A role DOES grant one of the unheld alternatives: re-authorizing for that
|
|
139
|
+
// scope is the fix, so say so with a scope challenge, not a role 403.
|
|
140
|
+
const reachable = alternatives.find((s) => !ctx.scopes.has(s) && permitted.has(s));
|
|
141
|
+
if (reachable)
|
|
142
|
+
throw insufficientScope([reachable], protectedResourceMetadataUrl(canonicalUri));
|
|
143
|
+
throw forbidden(held);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The one role check: a privileged scope is honoured only when one of the
|
|
147
|
+
* caller's roles grants it as a permission. This is authorization, not
|
|
148
|
+
* delegation, so the 403 carries no scope challenge — a client re-authorizing
|
|
149
|
+
* for more scopes can't fix it; an operator must grant a role.
|
|
150
|
+
*/
|
|
151
|
+
function requirePermission(ctx, scopes) {
|
|
152
|
+
const permitted = permissionsOf(ctx.roles);
|
|
153
|
+
const missing = scopes.filter((s) => PRIVILEGED_SCOPES.includes(s) && !permitted.has(s));
|
|
154
|
+
if (missing.length > 0)
|
|
155
|
+
throw forbidden(missing);
|
|
156
|
+
}
|
|
157
|
+
function forbidden(permissions) {
|
|
158
|
+
const grantedBy = ROLE_NAMES.filter((r) => permissions.some((p) => ROLE_PERMISSIONS[r].includes(p)));
|
|
159
|
+
return new McpError(403, `Forbidden: ${permissions.join(" ")} requires a role that grants it (${grantedBy.join(" or ")}); ` +
|
|
160
|
+
"the token's scope alone is not enough. An operator grants roles (self-hosted: " +
|
|
161
|
+
"`wardby auth user grant --subject <subject> --role <role>`; delegating: AUTH_ROLE_CLAIM/AUTH_ROLE_MAP).");
|
|
81
162
|
}
|
|
82
|
-
export { McpError };
|
|
163
|
+
export { McpError, insufficientScope };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID, randomBytes } from "node:crypto";
|
|
2
2
|
import { PostgresRateLimiter, RateLimitError } from "./rate-limit.js";
|
|
3
3
|
import { logger } from "../../../core/logger.js";
|
|
4
|
+
import { PAGE_STYLE } from "../../shared/page-style.js";
|
|
4
5
|
const authLog = logger.child({ module: "self-hosted-oauth" });
|
|
5
6
|
const paths = new Set([
|
|
6
7
|
"/login",
|
|
@@ -29,12 +30,21 @@ const FORM_SCRIPT = `document.addEventListener('submit', async (event) => {
|
|
|
29
30
|
});`;
|
|
30
31
|
function html(res, content) {
|
|
31
32
|
const nonce = randomBytes(18).toString("base64");
|
|
32
|
-
res.setHeader("content-security-policy",
|
|
33
|
+
res.setHeader("content-security-policy",
|
|
34
|
+
// style-src reuses the same per-request nonce as script-src rather than
|
|
35
|
+
// 'unsafe-inline', so the one inline <style> stays as tightly scoped as
|
|
36
|
+
// the inline <script>.
|
|
37
|
+
`default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'`);
|
|
33
38
|
res
|
|
34
39
|
.writeHead(200, { "content-type": "text/html; charset=utf-8" })
|
|
35
|
-
.end('<!doctype html><html lang="en"><meta charset="utf-8"><
|
|
40
|
+
.end('<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">' +
|
|
41
|
+
'<title>wardby authorization</title><style nonce="' +
|
|
42
|
+
nonce +
|
|
43
|
+
'">' +
|
|
44
|
+
PAGE_STYLE +
|
|
45
|
+
'</style><body><div class="card"><div class="kicker">wardby</div>' +
|
|
36
46
|
content +
|
|
37
|
-
'<p id="auth-error" role="alert"></p><noscript>JavaScript is required for secure form submission.</noscript><script nonce="' +
|
|
47
|
+
'<p id="auth-error" role="alert"></p></div><noscript>JavaScript is required for secure form submission.</noscript><script nonce="' +
|
|
38
48
|
nonce +
|
|
39
49
|
'">' +
|
|
40
50
|
FORM_SCRIPT +
|
|
@@ -155,7 +165,7 @@ export function browserHandler(provider) {
|
|
|
155
165
|
html(res, '<h1>Sign in to wardby</h1><form method="post" action="/login">' +
|
|
156
166
|
hidden("interaction", interaction) +
|
|
157
167
|
hidden("csrf", challenge) +
|
|
158
|
-
'<label>Login key
|
|
168
|
+
'<label>Login key<input type="password" name="login_key" required autocomplete="off"></label><button>Sign in</button></form>');
|
|
159
169
|
return true;
|
|
160
170
|
}
|
|
161
171
|
if (req.method === "POST" &&
|
|
@@ -187,18 +197,19 @@ export function browserHandler(provider) {
|
|
|
187
197
|
}
|
|
188
198
|
if (req.method === "GET" && url.pathname === "/consent") {
|
|
189
199
|
const id = url.searchParams.get("interaction") ?? "";
|
|
190
|
-
const { interaction, challenge } = await provider.consentPage(cookies.get(sessionName) ?? "", id);
|
|
200
|
+
const { interaction, challenge, scopes } = await provider.consentPage(cookies.get(sessionName) ?? "", id);
|
|
191
201
|
const name = interaction.client.metadata.client_name;
|
|
192
202
|
html(res, "<h1>Authorize " +
|
|
193
203
|
escape(name) +
|
|
194
204
|
"</h1><p>Resource: " +
|
|
195
205
|
escape(interaction.resource) +
|
|
196
206
|
"</p><p>Scopes: " +
|
|
197
|
-
escape(
|
|
207
|
+
escape(scopes.length ? scopes.join(" ") : "(none)") +
|
|
198
208
|
'</p><form action="/consent" method="post">' +
|
|
199
209
|
hidden("interaction", id) +
|
|
200
210
|
hidden("csrf", challenge) +
|
|
201
|
-
'<
|
|
211
|
+
'<div class="actions"><button name="decision" value="approve">Approve</button>' +
|
|
212
|
+
'<button name="decision" value="deny">Deny</button></div></form>');
|
|
202
213
|
return true;
|
|
203
214
|
}
|
|
204
215
|
if (req.method === "POST" && url.pathname === "/consent") {
|
|
@@ -210,7 +221,10 @@ export function browserHandler(provider) {
|
|
|
210
221
|
if (req.method === "GET" && url.pathname === "/logout") {
|
|
211
222
|
const session = await provider.sessions.get(cookies.get(sessionName) ?? "");
|
|
212
223
|
const challenge = await provider.sessions.challenge("logout", session.sessionId, "logout");
|
|
213
|
-
html(res,
|
|
224
|
+
html(res, "<h1>Sign out</h1>" +
|
|
225
|
+
'<form action="/logout" method="post">' +
|
|
226
|
+
hidden("csrf", challenge) +
|
|
227
|
+
"<button>Sign out</button></form>");
|
|
214
228
|
return true;
|
|
215
229
|
}
|
|
216
230
|
if (req.method === "POST" && url.pathname === "/logout") {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { PrismaClient } from "
|
|
1
|
+
import type { PrismaClient } from "#prisma";
|
|
2
2
|
export declare function authCommand(args: string[], db: PrismaClient, hashKey: string, output?: (value: string) => void): Promise<void>;
|
|
@@ -1,39 +1,73 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import { IdentityService } from "./credentials.js";
|
|
3
|
+
const USAGE = "Use auth user create --subject <s> [--role <role>]... | user list | " +
|
|
4
|
+
"user grant --subject <s> [--role <role>]... [--revoke-role <role>]... | user disable --subject <s> | " +
|
|
5
|
+
"key create|list --subject <s> | key revoke <key-id>.";
|
|
6
|
+
/** The flags each command accepts. Anything else is rejected, never silently ignored. */
|
|
7
|
+
const COMMANDS = {
|
|
8
|
+
"user create": ["subject", "role"],
|
|
9
|
+
"user list": [],
|
|
10
|
+
"user grant": ["subject", "role", "revoke-role"],
|
|
11
|
+
"user disable": ["subject"],
|
|
12
|
+
"key create": ["subject"],
|
|
13
|
+
"key list": ["subject"],
|
|
14
|
+
};
|
|
15
|
+
const FLAG_OPTIONS = {
|
|
16
|
+
subject: { type: "string" },
|
|
17
|
+
role: { type: "string", multiple: true },
|
|
18
|
+
"revoke-role": { type: "string", multiple: true },
|
|
19
|
+
};
|
|
3
20
|
export async function authCommand(args, db, hashKey, output = console.log) {
|
|
4
21
|
const [kind, action, ...rest] = args;
|
|
5
22
|
const service = new IdentityService(db, hashKey);
|
|
6
23
|
if (hashKey.toLowerCase() === process.env.SECRET_APP_KEY?.toLowerCase() ||
|
|
7
24
|
hashKey.toLowerCase() === process.env.AUTH_SIGNING_KEY?.toLowerCase())
|
|
8
25
|
throw new Error("Authentication keys must be independent.");
|
|
9
|
-
|
|
26
|
+
const command = `${kind} ${action}`;
|
|
27
|
+
if (command === "key revoke") {
|
|
28
|
+
if (rest.length !== 1 || rest[0].startsWith("-"))
|
|
29
|
+
throw new Error("Use auth key revoke <key-id> (no flags).");
|
|
10
30
|
await service.revokeKey(rest[0]);
|
|
11
31
|
output("Key, sessions, and grants revoked.");
|
|
12
32
|
return;
|
|
13
33
|
}
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
34
|
+
const allowed = COMMANDS[command];
|
|
35
|
+
if (!allowed)
|
|
36
|
+
throw new Error(USAGE);
|
|
37
|
+
// strict parsing throws on any flag not declared for THIS command, so a
|
|
38
|
+
// flag another command takes (e.g. `key create --role admin`) is an error.
|
|
39
|
+
const { values } = parseArgs({
|
|
40
|
+
args: rest,
|
|
41
|
+
strict: true,
|
|
42
|
+
allowPositionals: false,
|
|
43
|
+
options: Object.fromEntries(allowed.map((flag) => [flag, FLAG_OPTIONS[flag]])),
|
|
44
|
+
});
|
|
45
|
+
if (command === "user list") {
|
|
16
46
|
output(JSON.stringify(await service.listUsers(), null, 2));
|
|
17
47
|
return;
|
|
18
48
|
}
|
|
19
49
|
if (!values.subject)
|
|
20
|
-
throw new Error(
|
|
21
|
-
if (
|
|
22
|
-
output(JSON.stringify(await service.createUser(values.subject)));
|
|
50
|
+
throw new Error(`auth ${command} requires --subject.`);
|
|
51
|
+
if (command === "user create") {
|
|
52
|
+
output(JSON.stringify(await service.createUser(values.subject, values.role ?? [])));
|
|
23
53
|
return;
|
|
24
54
|
}
|
|
25
|
-
if (
|
|
55
|
+
if (command === "user grant") {
|
|
56
|
+
const add = values.role ?? [];
|
|
57
|
+
const remove = values["revoke-role"] ?? [];
|
|
58
|
+
if (add.length + remove.length === 0)
|
|
59
|
+
throw new Error("auth user grant requires --role <role> and/or --revoke-role <role> (each repeatable).");
|
|
60
|
+
output(JSON.stringify(await service.changeRoles(values.subject, add, remove)));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (command === "user disable") {
|
|
26
64
|
await service.disableUser(values.subject);
|
|
27
65
|
output("User disabled; credentials, sessions, and grants revoked.");
|
|
28
66
|
return;
|
|
29
67
|
}
|
|
30
|
-
if (
|
|
68
|
+
if (command === "key create") {
|
|
31
69
|
output(await service.createKey(values.subject));
|
|
32
70
|
return;
|
|
33
71
|
}
|
|
34
|
-
|
|
35
|
-
output(JSON.stringify(await service.listKeys(values.subject), null, 2));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
throw new Error("Use auth user create|list|disable or auth key create|list|revoke.");
|
|
72
|
+
output(JSON.stringify(await service.listKeys(values.subject), null, 2));
|
|
39
73
|
}
|