@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
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PyPI registry adapter: parses PyPI allowlist syntax (PEP 503 names, PEP
|
|
3
|
+
* 440 specifiers), routes proxy requests against the PEP 691 JSON Simple
|
|
4
|
+
* API plus PEP 658 metadata files, restricts downloads to wheels, and
|
|
5
|
+
* configures pip for the sandboxed worker to use the proxy as its only
|
|
6
|
+
* index with binary-only installs.
|
|
7
|
+
*/
|
|
8
|
+
import { clean as pepClean, compare as pepCompare, satisfies as pepSatisfies, validRange } from "@renovatebot/pep440";
|
|
9
|
+
import { unzipSync, strFromU8 } from "fflate";
|
|
10
|
+
import { AllowlistEntryError, RegistryError, } from "./types.js";
|
|
11
|
+
const UPSTREAM = "https://pypi.org/simple/";
|
|
12
|
+
const SIMPLE_JSON = "application/vnd.pypi.simple.v1+json";
|
|
13
|
+
const ENTRY = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(.*)$/;
|
|
14
|
+
const WHEEL_METADATA_LIMIT = 64 * 1024 * 1024;
|
|
15
|
+
function uploadTime(file) {
|
|
16
|
+
const time = file["upload-time"] ? Date.parse(file["upload-time"]) : NaN;
|
|
17
|
+
return Number.isNaN(time) ? null : new Date(time);
|
|
18
|
+
}
|
|
19
|
+
/** A valid PyPI project name (PEP 508): ASCII letters and digits, with
|
|
20
|
+
* `.`, `_` and `-` allowed only between them. */
|
|
21
|
+
const PROJECT_NAME = /^(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9])$/;
|
|
22
|
+
function decoded(segment) {
|
|
23
|
+
try {
|
|
24
|
+
return decodeURIComponent(segment);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new RegistryError(400, "wardby_bad_request", "malformed percent-encoding in the registry path");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function projectName(segment) {
|
|
31
|
+
const name = decoded(segment);
|
|
32
|
+
if (!PROJECT_NAME.test(name))
|
|
33
|
+
throw new RegistryError(400, "wardby_bad_request", "not a valid Python project name");
|
|
34
|
+
return normalizePypiName(name);
|
|
35
|
+
}
|
|
36
|
+
export function normalizePypiName(name) {
|
|
37
|
+
return name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
38
|
+
}
|
|
39
|
+
/** Version from a wheel ("name-1.0-py3-none-any.whl") or sdist ("name-1.0.tar.gz") filename. */
|
|
40
|
+
function versionOf(filename) {
|
|
41
|
+
if (filename.endsWith(".whl"))
|
|
42
|
+
return filename.split("-")[1] ?? null;
|
|
43
|
+
const sdist = filename.match(/^.+?-(\d[^-]*)\.(?:tar\.gz|zip)$/);
|
|
44
|
+
return sdist ? sdist[1] : null;
|
|
45
|
+
}
|
|
46
|
+
export function requiresDist(metadataText) {
|
|
47
|
+
const names = new Set();
|
|
48
|
+
for (const line of metadataText.split(/\r?\n/)) {
|
|
49
|
+
if (!line.startsWith("Requires-Dist:"))
|
|
50
|
+
continue;
|
|
51
|
+
const value = line.slice("Requires-Dist:".length).trim();
|
|
52
|
+
if (/;\s*.*\bextra\s*==/.test(value))
|
|
53
|
+
continue;
|
|
54
|
+
const name = value.match(/^([A-Za-z0-9][A-Za-z0-9._-]*)/);
|
|
55
|
+
if (name)
|
|
56
|
+
names.add(normalizePypiName(name[1]));
|
|
57
|
+
}
|
|
58
|
+
return [...names];
|
|
59
|
+
}
|
|
60
|
+
export const pypiAdapter = {
|
|
61
|
+
id: "pypi",
|
|
62
|
+
osvEcosystem: "PyPI",
|
|
63
|
+
upstreamHosts: ["pypi.org", "files.pythonhosted.org"],
|
|
64
|
+
collectExclude: [".venv", "venv", "__pycache__"],
|
|
65
|
+
dependenciesInMetadata: false,
|
|
66
|
+
parseAllowlistEntry(raw) {
|
|
67
|
+
const match = raw.trim().match(ENTRY);
|
|
68
|
+
if (!match)
|
|
69
|
+
throw new AllowlistEntryError(`"${raw}" is not a valid Python package entry`);
|
|
70
|
+
const name = normalizePypiName(match[1]);
|
|
71
|
+
const range = match[2].trim();
|
|
72
|
+
if (!range)
|
|
73
|
+
return { name, wildcard: false };
|
|
74
|
+
if (!validRange(range))
|
|
75
|
+
throw new AllowlistEntryError(`"${range}" is not a valid PEP 440 specifier`);
|
|
76
|
+
return { name, wildcard: false, range };
|
|
77
|
+
},
|
|
78
|
+
normalizeName: normalizePypiName,
|
|
79
|
+
satisfies: (version, range) => pepSatisfies(version, range),
|
|
80
|
+
compareVersions(a, b) {
|
|
81
|
+
// OSV and PyPI filenames both carry non-canonical spellings ("2.0rc1",
|
|
82
|
+
// "1.0-post1"); normalize each to PEP 440 before comparing.
|
|
83
|
+
const left = pepClean(a);
|
|
84
|
+
const right = pepClean(b);
|
|
85
|
+
if (!left || !right)
|
|
86
|
+
throw new Error(`not a PEP 440 version: "${a}" / "${b}"`);
|
|
87
|
+
return pepCompare(left, right);
|
|
88
|
+
},
|
|
89
|
+
route(method, subpath) {
|
|
90
|
+
if (method !== "GET" && method !== "HEAD")
|
|
91
|
+
return null;
|
|
92
|
+
const simple = subpath.match(/^simple\/([^/]+)\/?$/);
|
|
93
|
+
if (simple)
|
|
94
|
+
return { kind: "metadata", name: projectName(simple[1]) };
|
|
95
|
+
const file = subpath.match(/^files\/([^/]+)\/([^/]+)$/);
|
|
96
|
+
if (!file)
|
|
97
|
+
return null;
|
|
98
|
+
const name = projectName(file[1]);
|
|
99
|
+
const filename = decoded(file[2]);
|
|
100
|
+
if (filename.endsWith(".metadata")) {
|
|
101
|
+
return { kind: "file-metadata", name, filename: filename.slice(0, -".metadata".length) };
|
|
102
|
+
}
|
|
103
|
+
const version = versionOf(filename);
|
|
104
|
+
return version ? { kind: "download", name, version, filename } : null;
|
|
105
|
+
},
|
|
106
|
+
async fetchMetadata(name, upstream) {
|
|
107
|
+
const response = await upstream(`${UPSTREAM}${name}/`, { accept: SIMPLE_JSON });
|
|
108
|
+
if (response.status === 404)
|
|
109
|
+
throw new RegistryError(404, "wardby_package_not_found", `PyPI has no package "${name}"`);
|
|
110
|
+
if (!response.ok)
|
|
111
|
+
throw new RegistryError(502, "wardby_upstream_error", `PyPI returned ${response.status}`);
|
|
112
|
+
const index = (await response.json());
|
|
113
|
+
const versions = new Map();
|
|
114
|
+
for (const file of index.files) {
|
|
115
|
+
const version = versionOf(file.filename);
|
|
116
|
+
if (!version || file.yanked)
|
|
117
|
+
continue;
|
|
118
|
+
const entry = versions.get(version) ?? { version, dependencies: [], files: [] };
|
|
119
|
+
entry.files.push({
|
|
120
|
+
filename: file.filename,
|
|
121
|
+
version,
|
|
122
|
+
upstreamUrl: file.url,
|
|
123
|
+
integrity: file.hashes?.sha256 ? { algorithm: "sha256", hex: file.hashes.sha256 } : null,
|
|
124
|
+
sizeBytes: file.size ?? null,
|
|
125
|
+
allowed: file.filename.endsWith(".whl"),
|
|
126
|
+
publishedAt: uploadTime(file),
|
|
127
|
+
});
|
|
128
|
+
versions.set(version, entry);
|
|
129
|
+
}
|
|
130
|
+
// Keep only what renderMetadata and resolveFileMetadata read: wheels,
|
|
131
|
+
// with the PEP 691 file fields pip uses.
|
|
132
|
+
const raw = {
|
|
133
|
+
name: index.name,
|
|
134
|
+
files: index.files
|
|
135
|
+
.filter((file) => file.filename.endsWith(".whl") && !file.yanked)
|
|
136
|
+
.map((file) => ({
|
|
137
|
+
filename: file.filename,
|
|
138
|
+
url: file.url,
|
|
139
|
+
...(file.hashes ? { hashes: file.hashes } : {}),
|
|
140
|
+
...(file["upload-time"] ? { "upload-time": file["upload-time"] } : {}),
|
|
141
|
+
...(file.size !== undefined ? { size: file.size } : {}),
|
|
142
|
+
...(file["core-metadata"] !== undefined ? { "core-metadata": file["core-metadata"] } : {}),
|
|
143
|
+
...(file["dist-info-metadata"] !== undefined ? { "dist-info-metadata": file["dist-info-metadata"] } : {}),
|
|
144
|
+
...(file["requires-python"] !== undefined ? { "requires-python": file["requires-python"] } : {}),
|
|
145
|
+
})),
|
|
146
|
+
};
|
|
147
|
+
return { name: normalizePypiName(index.name), versions, raw };
|
|
148
|
+
},
|
|
149
|
+
renderMetadata(meta, keep, keptFiles, proxyBase) {
|
|
150
|
+
const index = meta.raw;
|
|
151
|
+
const files = index.files
|
|
152
|
+
.filter((file) => file.filename.endsWith(".whl"))
|
|
153
|
+
.filter((file) => keep.has(versionOf(file.filename) ?? "") && keptFiles.has(file.filename))
|
|
154
|
+
.map((file) => ({
|
|
155
|
+
...file,
|
|
156
|
+
url: `${proxyBase}files/${encodeURIComponent(meta.name)}/${encodeURIComponent(file.filename)}`,
|
|
157
|
+
}));
|
|
158
|
+
return {
|
|
159
|
+
contentType: SIMPLE_JSON,
|
|
160
|
+
body: JSON.stringify({ meta: { "api-version": "1.1" }, name: meta.name, files, versions: [...keep] }),
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
resolveDownload(route, meta) {
|
|
164
|
+
return meta.versions.get(route.version)?.files.find((file) => file.filename === route.filename) ?? null;
|
|
165
|
+
},
|
|
166
|
+
resolveFileMetadata(route, meta) {
|
|
167
|
+
const index = meta.raw;
|
|
168
|
+
const source = index.files.find((file) => file.filename === route.filename);
|
|
169
|
+
const version = versionOf(route.filename);
|
|
170
|
+
const declared = source?.["core-metadata"] ?? source?.["dist-info-metadata"];
|
|
171
|
+
if (!source || !version || !declared || !source.filename.endsWith(".whl"))
|
|
172
|
+
return null;
|
|
173
|
+
const sha256 = typeof declared === "object" ? declared.sha256 : undefined;
|
|
174
|
+
return {
|
|
175
|
+
filename: `${route.filename}.metadata`,
|
|
176
|
+
version,
|
|
177
|
+
upstreamUrl: `${source.url}.metadata`,
|
|
178
|
+
integrity: sha256 ? { algorithm: "sha256", hex: sha256 } : null,
|
|
179
|
+
sizeBytes: null,
|
|
180
|
+
allowed: true,
|
|
181
|
+
// The metadata file is as old as the wheel it describes.
|
|
182
|
+
publishedAt: uploadTime(source),
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
async dependenciesFromFile(route, body) {
|
|
186
|
+
if (route.kind === "file-metadata")
|
|
187
|
+
return requiresDist(new TextDecoder().decode(body));
|
|
188
|
+
if (!route.filename.endsWith(".whl") || body.byteLength > WHEEL_METADATA_LIMIT)
|
|
189
|
+
return [];
|
|
190
|
+
const entries = unzipSync(body, { filter: (file) => /\.dist-info\/METADATA$/.test(file.name) });
|
|
191
|
+
const metadata = Object.values(entries)[0];
|
|
192
|
+
return metadata ? requiresDist(strFromU8(metadata)) : [];
|
|
193
|
+
},
|
|
194
|
+
workerConfig({ registryUrl, token, cacheDir }) {
|
|
195
|
+
const index = new URL("simple/", registryUrl);
|
|
196
|
+
index.username = "wardby";
|
|
197
|
+
index.password = token;
|
|
198
|
+
return {
|
|
199
|
+
env: {
|
|
200
|
+
PIP_INDEX_URL: index.toString(),
|
|
201
|
+
PIP_TRUSTED_HOST: index.hostname,
|
|
202
|
+
PIP_ONLY_BINARY: ":all:",
|
|
203
|
+
PIP_CACHE_DIR: cacheDir,
|
|
204
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
205
|
+
PIP_NO_INPUT: "1",
|
|
206
|
+
},
|
|
207
|
+
files: [],
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one summary of a run's RegistryFetch rows, shared by get_run and the
|
|
3
|
+
* coding executor's PR-body report so both deduplicate identically: served
|
|
4
|
+
* packages collapse on ecosystem + name + version (a retried download is one
|
|
5
|
+
* package), refusals on ecosystem + name + reason.
|
|
6
|
+
*/
|
|
7
|
+
export interface RegistryFetchRow {
|
|
8
|
+
ecosystem: string;
|
|
9
|
+
name: string;
|
|
10
|
+
version: string | null;
|
|
11
|
+
outcome: string;
|
|
12
|
+
reason: string | null;
|
|
13
|
+
sizeBytes: number | null;
|
|
14
|
+
/** `-/plan` for a refusal a lockfile plan recorded (see PLAN_FETCH_FILENAME). */
|
|
15
|
+
filename?: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** The `filename` a lockfile plan records its refusals under. */
|
|
18
|
+
export declare const PLAN_REFUSAL_FILENAME = "-/plan";
|
|
19
|
+
/** Distinct refusals lockfile plans recorded (ecosystem + name + version +
|
|
20
|
+
* reason), however often the lockfile was planned. */
|
|
21
|
+
export declare function countPlanRefusals(rows: readonly RegistryFetchRow[]): number;
|
|
22
|
+
export interface RegistryPackageSummary {
|
|
23
|
+
ecosystem: string;
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
/** Bytes served (RegistryFetch.sizeBytes); null when not recorded. */
|
|
27
|
+
size: number | null;
|
|
28
|
+
}
|
|
29
|
+
export interface RegistryRefusalSummary {
|
|
30
|
+
ecosystem: string;
|
|
31
|
+
name: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function summarizeRegistryFetches(rows: readonly RegistryFetchRow[]): {
|
|
35
|
+
packages: RegistryPackageSummary[];
|
|
36
|
+
packageRefusals: RegistryRefusalSummary[];
|
|
37
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The `filename` a lockfile plan records its refusals under. */
|
|
2
|
+
export const PLAN_REFUSAL_FILENAME = "-/plan";
|
|
3
|
+
/** Distinct refusals lockfile plans recorded (ecosystem + name + version +
|
|
4
|
+
* reason), however often the lockfile was planned. */
|
|
5
|
+
export function countPlanRefusals(rows) {
|
|
6
|
+
const keys = new Set();
|
|
7
|
+
for (const row of rows) {
|
|
8
|
+
if (row.outcome !== "refused" || row.filename !== PLAN_REFUSAL_FILENAME)
|
|
9
|
+
continue;
|
|
10
|
+
keys.add(`${row.ecosystem}\0${row.name}\0${row.version ?? ""}\0${row.reason ?? ""}`);
|
|
11
|
+
}
|
|
12
|
+
return keys.size;
|
|
13
|
+
}
|
|
14
|
+
export function summarizeRegistryFetches(rows) {
|
|
15
|
+
const packages = new Map();
|
|
16
|
+
const refusals = new Map();
|
|
17
|
+
for (const row of rows) {
|
|
18
|
+
if (row.outcome === "served" && row.version) {
|
|
19
|
+
const key = `${row.ecosystem}\0${row.name}\0${row.version}`;
|
|
20
|
+
if (!packages.has(key))
|
|
21
|
+
packages.set(key, { ecosystem: row.ecosystem, name: row.name, version: row.version, size: row.sizeBytes });
|
|
22
|
+
}
|
|
23
|
+
else if (row.outcome === "refused") {
|
|
24
|
+
const reason = row.reason ?? "refused";
|
|
25
|
+
const key = `${row.ecosystem}\0${row.name}\0${reason}`;
|
|
26
|
+
if (!refusals.has(key))
|
|
27
|
+
refusals.set(key, { ecosystem: row.ecosystem, name: row.name, reason });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { packages: [...packages.values()], packageRefusals: [...refusals.values()] };
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** A one-way, registry-only token for npm and pip. The proxy stores its hash and
|
|
3
|
+
* accepts it only on /registry/ routes, so package tools never hold the model
|
|
4
|
+
* capability. */
|
|
5
|
+
export function deriveRegistryToken(capability) {
|
|
6
|
+
return `rrg_${createHash("sha256").update("wardby-registry\0").update(capability).digest("base64url")}`;
|
|
7
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the coding package registry: the adapter contract each
|
|
3
|
+
* ecosystem (npm, PyPI, later Composer/RubyGems/Go) implements, and the
|
|
4
|
+
* allowlist entry shape adapters parse into. Everything security-relevant
|
|
5
|
+
* (allowance checks, version filtering, streaming, recording) lives in the
|
|
6
|
+
* registry core, never in an adapter, so a new adapter cannot weaken a
|
|
7
|
+
* safeguard by omission.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* "npm", "pypi", later "composer", "rubygems", "go". Used as the allowlist
|
|
11
|
+
* key, the route segment (/registry/<id>/), and RegistryFetch.ecosystem.
|
|
12
|
+
*/
|
|
13
|
+
export type EcosystemId = string;
|
|
14
|
+
export interface AllowlistEntry {
|
|
15
|
+
/** Normalized name, or a scope prefix when `wildcard` is true ("@heroui/"). */
|
|
16
|
+
name: string;
|
|
17
|
+
wildcard: boolean;
|
|
18
|
+
/** Optional version range in this ecosystem's syntax. */
|
|
19
|
+
range?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class AllowlistEntryError extends Error {
|
|
22
|
+
}
|
|
23
|
+
export declare class RegistryError extends Error {
|
|
24
|
+
readonly status: number;
|
|
25
|
+
readonly code: string;
|
|
26
|
+
constructor(status: number, code: string, message: string);
|
|
27
|
+
}
|
|
28
|
+
export interface Integrity {
|
|
29
|
+
algorithm: "sha512" | "sha384" | "sha256" | "sha1";
|
|
30
|
+
/** Hex-encoded digest. */
|
|
31
|
+
hex: string;
|
|
32
|
+
}
|
|
33
|
+
export interface FileRef {
|
|
34
|
+
filename: string;
|
|
35
|
+
version: string;
|
|
36
|
+
upstreamUrl: string;
|
|
37
|
+
/** Null when the ecosystem publishes no checksum (common for Composer archives). */
|
|
38
|
+
integrity: Integrity | null;
|
|
39
|
+
sizeBytes: number | null;
|
|
40
|
+
/** False for files the ecosystem's safeguards exclude, e.g. PyPI sdists. */
|
|
41
|
+
allowed: boolean;
|
|
42
|
+
/** When this file was published; null means unknown and is treated as too
|
|
43
|
+
* new. The minimum release age applies per file: a PyPI release can gain
|
|
44
|
+
* a new wheel long after its first upload. For npm every version has one
|
|
45
|
+
* immutable tarball, so this is the version's publish time. */
|
|
46
|
+
publishedAt: Date | null;
|
|
47
|
+
}
|
|
48
|
+
/** One declared dependency edge: the package it installs and the range
|
|
49
|
+
* it asks for, in the ecosystem's syntax. `"*"` means any version (also
|
|
50
|
+
* used for a spec that is not a range, such as a dist-tag). */
|
|
51
|
+
export interface DependencySpec {
|
|
52
|
+
name: string;
|
|
53
|
+
range: string;
|
|
54
|
+
}
|
|
55
|
+
export interface VersionInfo {
|
|
56
|
+
version: string;
|
|
57
|
+
/** Dependency names (normalized). Empty when discovered from files instead. */
|
|
58
|
+
dependencies: readonly string[];
|
|
59
|
+
/** The same dependencies with their declared ranges, for the range-aware
|
|
60
|
+
* graph walk. Adapters with `dependenciesInMetadata` set should fill it;
|
|
61
|
+
* when omitted, each name counts as `"*"`. */
|
|
62
|
+
dependencySpecs?: readonly DependencySpec[];
|
|
63
|
+
files: readonly FileRef[];
|
|
64
|
+
}
|
|
65
|
+
export interface PackageMetadata {
|
|
66
|
+
name: string;
|
|
67
|
+
/** Keyed by version string. */
|
|
68
|
+
versions: ReadonlyMap<string, VersionInfo>;
|
|
69
|
+
/** Adapter-private data renderMetadata (and resolveFileMetadata) read:
|
|
70
|
+
* never the upstream document itself, only what those need of it.
|
|
71
|
+
* `undefined` when the adapter supports metadata without render data
|
|
72
|
+
* (`fetchMetadata` without `render`) and it was fetched that way. */
|
|
73
|
+
raw?: unknown;
|
|
74
|
+
/** Approximate bytes this object retains, for the metadata cache's byte
|
|
75
|
+
* budget. The core estimates it when an adapter leaves it out. */
|
|
76
|
+
approxBytes?: number;
|
|
77
|
+
}
|
|
78
|
+
export interface FetchMetadataOptions {
|
|
79
|
+
/** Also keep what renderMetadata needs. Without it, an adapter may omit
|
|
80
|
+
* `raw` (npm does), since the graph walk, the version filter and
|
|
81
|
+
* downloads read only `versions`. */
|
|
82
|
+
render?: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface MetadataRoute {
|
|
85
|
+
kind: "metadata";
|
|
86
|
+
name: string;
|
|
87
|
+
}
|
|
88
|
+
export interface DownloadRoute {
|
|
89
|
+
kind: "download";
|
|
90
|
+
name: string;
|
|
91
|
+
version: string;
|
|
92
|
+
filename: string;
|
|
93
|
+
}
|
|
94
|
+
/** PEP 658 metadata file for one wheel. */
|
|
95
|
+
export interface FileMetadataRoute {
|
|
96
|
+
kind: "file-metadata";
|
|
97
|
+
name: string;
|
|
98
|
+
filename: string;
|
|
99
|
+
}
|
|
100
|
+
export type RegistryRoute = MetadataRoute | DownloadRoute | FileMetadataRoute;
|
|
101
|
+
export interface RenderedDocument {
|
|
102
|
+
contentType: string;
|
|
103
|
+
body: string;
|
|
104
|
+
}
|
|
105
|
+
/** The pinned upstream client: HTTPS only, adapter's upstreamHosts only,
|
|
106
|
+
* no redirects, no private addresses. `acceptEncoding: "gzip"` asks for a
|
|
107
|
+
* compressed body, which the caller then decompresses itself (the pinned
|
|
108
|
+
* client never does). */
|
|
109
|
+
export type UpstreamFetch = (url: string, init?: {
|
|
110
|
+
method?: "GET" | "POST";
|
|
111
|
+
body?: string;
|
|
112
|
+
accept?: string;
|
|
113
|
+
acceptEncoding?: "gzip";
|
|
114
|
+
signal?: AbortSignal;
|
|
115
|
+
}) => Promise<Response>;
|
|
116
|
+
/** One declared dependency as a lockfile or registry manifest writes it:
|
|
117
|
+
* the folder it installs into (`key`, an alias's own name), the registry
|
|
118
|
+
* package it installs (`name`, the alias target) and the range it asks
|
|
119
|
+
* for (`"*"` for a dist-tag or empty spec). */
|
|
120
|
+
export interface DeclaredDependency {
|
|
121
|
+
key: string;
|
|
122
|
+
name: string;
|
|
123
|
+
range: string;
|
|
124
|
+
}
|
|
125
|
+
/** One installed package a lockfile claims, where it installs it. None of
|
|
126
|
+
* it is trusted: the plan verifies every registry entry against the
|
|
127
|
+
* registry itself. */
|
|
128
|
+
export interface LockfileEntry {
|
|
129
|
+
/** Where the lockfile installs it (npm: `node_modules/a/node_modules/b`). Unique. */
|
|
130
|
+
path: string;
|
|
131
|
+
/** The registry package (an alias's target), as the lockfile claims it. */
|
|
132
|
+
name: string;
|
|
133
|
+
version: string;
|
|
134
|
+
/** The lockfile's integrity string, or null when it records none. */
|
|
135
|
+
integrity: string | null;
|
|
136
|
+
/** `registry`: fetched from the registry (verified, then approved or
|
|
137
|
+
* refused). `bundled`: shipped inside its parent's own tarball, never
|
|
138
|
+
* fetched, so neither. `unsupported`: fetched from somewhere other than
|
|
139
|
+
* the registry (git, a URL, a path) or unreadable; always refused. */
|
|
140
|
+
kind: "registry" | "bundled" | "unsupported";
|
|
141
|
+
/** Why an `unsupported` entry is refused. */
|
|
142
|
+
unsupportedReason?: string;
|
|
143
|
+
}
|
|
144
|
+
/** A parsed lockfile: its entries, the projects whose declared
|
|
145
|
+
* dependencies are where reachability starts (the root, workspaces), and
|
|
146
|
+
* the client's own rule for which entry a declared dependency uses. */
|
|
147
|
+
export interface ParsedLockfile {
|
|
148
|
+
entries: readonly LockfileEntry[];
|
|
149
|
+
projects: readonly {
|
|
150
|
+
path: string;
|
|
151
|
+
dependencies: readonly DeclaredDependency[];
|
|
152
|
+
}[];
|
|
153
|
+
/** The entry that `key`, declared by the package at `fromPath`, resolves
|
|
154
|
+
* to (npm: the nearest ancestor `node_modules/<key>`), or undefined
|
|
155
|
+
* (not installed, or a local project). */
|
|
156
|
+
resolve(fromPath: string, key: string): LockfileEntry | undefined;
|
|
157
|
+
}
|
|
158
|
+
/** Immutable facts about one published version, read from the registry
|
|
159
|
+
* itself (never from a lockfile): stored once, reused forever. */
|
|
160
|
+
export interface VersionFact {
|
|
161
|
+
name: string;
|
|
162
|
+
version: string;
|
|
163
|
+
/** Null only while being assembled: a fact is stored only with a publish time. */
|
|
164
|
+
publishedAt: Date | null;
|
|
165
|
+
/** The registry's integrity string (npm: `dist.integrity`, an SRI). */
|
|
166
|
+
integrity: string;
|
|
167
|
+
downloadUrl: string;
|
|
168
|
+
/** Every registry dependency the version declares (npm: dependencies,
|
|
169
|
+
* optionalDependencies and peerDependencies, aliases resolved). */
|
|
170
|
+
dependencies: readonly DeclaredDependency[];
|
|
171
|
+
}
|
|
172
|
+
/** An ecosystem's lockfile verification hooks (`POST /-/plan`): parsing and
|
|
173
|
+
* registry reads only. Every decision (integrity, edges, reachability,
|
|
174
|
+
* release age, advisories) is the registry core's. */
|
|
175
|
+
export interface LockfilePlanSupport {
|
|
176
|
+
/** Parse a lockfile. Throws a RegistryError for one it cannot verify
|
|
177
|
+
* (unsupported version: 400 `wardby_lockfile_unsupported`; invalid:
|
|
178
|
+
* 400 `wardby_bad_request`; more than `maxEntries` entries: 413
|
|
179
|
+
* `wardby_lockfile_too_large`). `proxyRegistryUrl` is the proxy's own
|
|
180
|
+
* route for this ecosystem, which a lockfile written through it records
|
|
181
|
+
* as the download source. */
|
|
182
|
+
parse(body: string, options: {
|
|
183
|
+
maxEntries: number;
|
|
184
|
+
proxyRegistryUrl: string;
|
|
185
|
+
}): ParsedLockfile;
|
|
186
|
+
/** The registry's own record of one version, without its publish time;
|
|
187
|
+
* null when the registry has no such version. */
|
|
188
|
+
fetchVersionFact(name: string, version: string, upstream: UpstreamFetch, signal?: AbortSignal): Promise<VersionFact | null>;
|
|
189
|
+
/** Publish times of `versions` of one package, read by streaming the
|
|
190
|
+
* package's full document without ever holding it. A version the
|
|
191
|
+
* registry has no time for is absent from the result. */
|
|
192
|
+
fetchPublishTimes(name: string, versions: readonly string[], upstream: UpstreamFetch, options: {
|
|
193
|
+
maxBytes: number;
|
|
194
|
+
signal: AbortSignal;
|
|
195
|
+
}): Promise<Map<string, Date>>;
|
|
196
|
+
/** Whether a dependency declared as `range` accepts `version`, as the
|
|
197
|
+
* client itself decides it when it reuses a locked version. */
|
|
198
|
+
edgeSatisfies(version: string, range: string): boolean;
|
|
199
|
+
}
|
|
200
|
+
export interface WorkerConfigInput {
|
|
201
|
+
/** e.g. "http://wardby-proxy:8787/registry/npm/" */
|
|
202
|
+
registryUrl: string;
|
|
203
|
+
/** The derived registry-only token (`rrg_…`), never the run capability.
|
|
204
|
+
* Written only under cacheDir, never elsewhere. */
|
|
205
|
+
token: string;
|
|
206
|
+
/** e.g. "/workspace/.cache/npm"; always inside a collection-excluded folder. */
|
|
207
|
+
cacheDir: string;
|
|
208
|
+
}
|
|
209
|
+
export interface WorkerConfig {
|
|
210
|
+
env: Readonly<Record<string, string>>;
|
|
211
|
+
/** Files the driver writes before the agent starts; paths must be under cacheDir. */
|
|
212
|
+
files: readonly {
|
|
213
|
+
path: string;
|
|
214
|
+
content: string;
|
|
215
|
+
mode: number;
|
|
216
|
+
}[];
|
|
217
|
+
}
|
|
218
|
+
export interface RegistryAdapter {
|
|
219
|
+
readonly id: EcosystemId;
|
|
220
|
+
/** OSV ecosystem name, e.g. "npm", "PyPI", "Packagist", "RubyGems", "Go". */
|
|
221
|
+
readonly osvEcosystem: string;
|
|
222
|
+
/** Upstream hosts this adapter may reach; added to the pinned-fetch allowlist. */
|
|
223
|
+
readonly upstreamHosts: readonly string[];
|
|
224
|
+
/** Extra folder names skipped at collection, e.g. "vendor" for Composer. */
|
|
225
|
+
readonly collectExclude: readonly string[];
|
|
226
|
+
/** Whether fetchMetadata's VersionInfo.dependencies is complete (npm's
|
|
227
|
+
* packuments carry every version's dependencies). When true, the core
|
|
228
|
+
* may resolve a run's approved dependency graph from metadata alone,
|
|
229
|
+
* on demand, before refusing a name (a lockfile install requests
|
|
230
|
+
* tarballs without first requesting each parent's metadata). False for
|
|
231
|
+
* ecosystems whose indexes omit dependencies (PyPI), where such a walk
|
|
232
|
+
* would only make upstream calls and find nothing. */
|
|
233
|
+
readonly dependenciesInMetadata: boolean;
|
|
234
|
+
/** Lockfile names this ecosystem's client writes, and a rewrite of the proxy download URLs it records in one back to
|
|
235
|
+
* public URLs, applied before the workspace is collected (lockfiles.ts). Omitted when the client records none. */
|
|
236
|
+
readonly lockfiles?: {
|
|
237
|
+
names: readonly string[];
|
|
238
|
+
normalize(content: string, registryUrl: string): string;
|
|
239
|
+
};
|
|
240
|
+
/** Lockfile verification (`POST /registry/<id>/-/plan`); omitted when the
|
|
241
|
+
* ecosystem has none. */
|
|
242
|
+
readonly lockfilePlan?: LockfilePlanSupport;
|
|
243
|
+
/** Parse one allowlist entry in this ecosystem's syntax. Throws an
|
|
244
|
+
* AllowlistEntryError with a user-facing message if it is invalid. */
|
|
245
|
+
parseAllowlistEntry(raw: string): AllowlistEntry;
|
|
246
|
+
/** Canonical name used for every comparison (npm: the name exactly as
|
|
247
|
+
* written, since npm names are case-sensitive; PyPI: PEP 503
|
|
248
|
+
* normalization). */
|
|
249
|
+
normalizeName(name: string): string;
|
|
250
|
+
/** Whether `version` satisfies `range` in this ecosystem's syntax
|
|
251
|
+
* (npm semver ranges, PEP 440 specifiers). */
|
|
252
|
+
satisfies(version: string, range: string): boolean;
|
|
253
|
+
/** Total order over this ecosystem's version strings (semver for npm,
|
|
254
|
+
* PEP 440 for PyPI, normalizing non-canonical spellings first), used to
|
|
255
|
+
* evaluate OSV advisory ranges. Throws on a version it cannot parse; the
|
|
256
|
+
* audit treats that as affected (fail closed). */
|
|
257
|
+
compareVersions(a: string, b: string): number;
|
|
258
|
+
/** Classify a request under /registry/<id>/, or null for 404. Throws a
|
|
259
|
+
* 400 `wardby_bad_request` RegistryError for a malformed path (bad
|
|
260
|
+
* percent-encoding or an invalid package name); the core records it. */
|
|
261
|
+
route(method: string, subpath: string, headers: Headers): RegistryRoute | null;
|
|
262
|
+
/** Fetch and parse upstream metadata for one package, keeping only the
|
|
263
|
+
* fields the proxy reads: nothing returned may reference the parsed
|
|
264
|
+
* upstream document, which can be tens of MB. */
|
|
265
|
+
fetchMetadata(name: string, upstream: UpstreamFetch, options?: FetchMetadataOptions): Promise<PackageMetadata>;
|
|
266
|
+
/** Build the client-facing metadata document containing only `keep`
|
|
267
|
+
* versions and, of their files, only the filenames in `keptFiles` (the
|
|
268
|
+
* core's per-file release-age filter), with every download link
|
|
269
|
+
* rewritten to a proxy route. */
|
|
270
|
+
renderMetadata(meta: PackageMetadata, keep: ReadonlySet<string>, keptFiles: ReadonlySet<string>, proxyBase: string): RenderedDocument;
|
|
271
|
+
/** Map a download route to a file in metadata the proxy fetched itself.
|
|
272
|
+
* Returns null if the route names no known file. */
|
|
273
|
+
resolveDownload(route: DownloadRoute, meta: PackageMetadata): FileRef | null;
|
|
274
|
+
/** For ecosystems whose indexes omit dependencies (PyPI): dependency
|
|
275
|
+
* names read from a served file or its metadata file. Omitted when
|
|
276
|
+
* VersionInfo.dependencies is already complete (npm). */
|
|
277
|
+
dependenciesFromFile?(route: DownloadRoute | FileMetadataRoute, body: Uint8Array): Promise<string[]>;
|
|
278
|
+
/** Map a file-metadata route (PEP 658) to the file it describes, from
|
|
279
|
+
* metadata the proxy fetched itself. Returns null if the route names no
|
|
280
|
+
* known file. */
|
|
281
|
+
resolveFileMetadata?(route: FileMetadataRoute, meta: PackageMetadata): FileRef | null;
|
|
282
|
+
/** Environment variables and files the driver writes so the package
|
|
283
|
+
* manager uses the proxy, authenticates, keeps caches under cacheDir,
|
|
284
|
+
* and disables install-time code where the client allows it. */
|
|
285
|
+
workerConfig(input: WorkerConfigInput): WorkerConfig;
|
|
286
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the coding package registry: the adapter contract each
|
|
3
|
+
* ecosystem (npm, PyPI, later Composer/RubyGems/Go) implements, and the
|
|
4
|
+
* allowlist entry shape adapters parse into. Everything security-relevant
|
|
5
|
+
* (allowance checks, version filtering, streaming, recording) lives in the
|
|
6
|
+
* registry core, never in an adapter, so a new adapter cannot weaken a
|
|
7
|
+
* safeguard by omission.
|
|
8
|
+
*/
|
|
9
|
+
export class AllowlistEntryError extends Error {
|
|
10
|
+
}
|
|
11
|
+
export class RegistryError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
code;
|
|
14
|
+
constructor(status, code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.code = code;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The URL the worker's package manager is pointed at for one ecosystem. */
|
|
2
|
+
export declare function registryUrlFor(proxyBaseUrl: string, ecosystem: string): string;
|
|
3
|
+
export declare function registryWorkerSetup(input: {
|
|
4
|
+
proxyBaseUrl: string;
|
|
5
|
+
capability: string;
|
|
6
|
+
cacheRoot: string;
|
|
7
|
+
}): {
|
|
8
|
+
env: Record<string, string>;
|
|
9
|
+
files: {
|
|
10
|
+
path: string;
|
|
11
|
+
content: string;
|
|
12
|
+
mode: number;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the full worker environment/file setup for every registry adapter
|
|
3
|
+
* at once, from the run's derived registry-only token -- never the run
|
|
4
|
+
* capability itself. The driver calls this once per coding run and applies
|
|
5
|
+
* the result before the agent starts.
|
|
6
|
+
*/
|
|
7
|
+
import { REGISTRY_ADAPTERS } from "./adapters.js";
|
|
8
|
+
import { deriveRegistryToken } from "./token.js";
|
|
9
|
+
/** The URL the worker's package manager is pointed at for one ecosystem. */
|
|
10
|
+
export function registryUrlFor(proxyBaseUrl, ecosystem) {
|
|
11
|
+
return `${proxyBaseUrl.replace(/\/$/, "")}/registry/${ecosystem}/`;
|
|
12
|
+
}
|
|
13
|
+
export function registryWorkerSetup(input) {
|
|
14
|
+
const token = deriveRegistryToken(input.capability);
|
|
15
|
+
const env = {};
|
|
16
|
+
const files = [];
|
|
17
|
+
for (const adapter of REGISTRY_ADAPTERS.values()) {
|
|
18
|
+
const config = adapter.workerConfig({
|
|
19
|
+
registryUrl: registryUrlFor(input.proxyBaseUrl, adapter.id),
|
|
20
|
+
token,
|
|
21
|
+
cacheDir: `${input.cacheRoot}/${adapter.id}`,
|
|
22
|
+
});
|
|
23
|
+
Object.assign(env, config.env);
|
|
24
|
+
for (const file of config.files) {
|
|
25
|
+
if (!file.path.startsWith(`${input.cacheRoot}/`))
|
|
26
|
+
throw new Error("registry_worker_file_outside_cache");
|
|
27
|
+
files.push(file);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { env, files };
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The services wardby ships in its catalog. Migration
|
|
3
|
+
* 20260927050000_coding_run_services seeds exactly these rows
|
|
4
|
+
* (builtins.database.test.ts holds the two together); a later release changes
|
|
5
|
+
* them with a new migration and a change here.
|
|
6
|
+
*
|
|
7
|
+
* Each image is the official Docker Hub image pinned by its multi-architecture
|
|
8
|
+
* index digest, resolved with
|
|
9
|
+
* `docker buildx imagetools inspect docker.io/library/<name>:<tag>`.
|
|
10
|
+
*
|
|
11
|
+
* A sidecar runs as the run pod's non-root uid with a read-only root
|
|
12
|
+
* filesystem, so each entry names every directory its image writes to: the
|
|
13
|
+
* data directory and, for the databases, the socket directory and /tmp (the
|
|
14
|
+
* official entrypoints write an nss_wrapper passwd file there for an arbitrary
|
|
15
|
+
* uid). Postgres keeps PGDATA one level below its volume so the entrypoint can
|
|
16
|
+
* create it owned by that uid. Readiness probes TCP on 127.0.0.1, not the Unix
|
|
17
|
+
* socket: both database entrypoints first run a temporary socket-only server to
|
|
18
|
+
* initialize, and only the final server listens on TCP. Variables use
|
|
19
|
+
* 127.0.0.1 rather than localhost so no client tries the IPv6 loopback first.
|
|
20
|
+
*/
|
|
21
|
+
import type { CodingServiceDefinition } from "./catalog.js";
|
|
22
|
+
export declare const BUILTIN_CODING_SERVICES: readonly CodingServiceDefinition[];
|