@wardby/cli 0.1.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 +131 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +282 -0
- package/bin/wardby.js +3 -0
- package/deploy/local/docker-compose.yml +19 -0
- package/dist/claude-coding-worker/driver.d.ts +70 -0
- package/dist/claude-coding-worker/driver.js +128 -0
- package/dist/claude-coding-worker/main.d.ts +2 -0
- package/dist/claude-coding-worker/main.js +41 -0
- package/dist/claude-coding-worker/sdk.d.ts +4 -0
- package/dist/claude-coding-worker/sdk.js +43 -0
- package/dist/claude-coding-worker/tool-relay.d.ts +1 -0
- package/dist/claude-coding-worker/tool-relay.js +9 -0
- package/dist/cli-help.d.ts +1 -0
- package/dist/cli-help.js +20 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +629 -0
- package/dist/coding/observability.d.ts +59 -0
- package/dist/coding/observability.js +77 -0
- package/dist/coding/profile.d.ts +88 -0
- package/dist/coding/profile.js +137 -0
- package/dist/coding/protocol.d.ts +345 -0
- package/dist/coding/protocol.js +445 -0
- package/dist/coding/provider.d.ts +4 -0
- package/dist/coding/provider.js +18 -0
- package/dist/coding-proxy/main.d.ts +1 -0
- package/dist/coding-proxy/main.js +51 -0
- package/dist/coding-worker/artifact.d.ts +16 -0
- package/dist/coding-worker/artifact.js +37 -0
- package/dist/coding-worker/driver.d.ts +46 -0
- package/dist/coding-worker/driver.js +158 -0
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +23 -0
- package/dist/coding-worker/isolation-probe.d.ts +1 -0
- package/dist/coding-worker/isolation-probe.js +146 -0
- package/dist/coding-worker/keeper.d.ts +4 -0
- package/dist/coding-worker/keeper.js +29 -0
- package/dist/coding-worker/main.d.ts +2 -0
- package/dist/coding-worker/main.js +43 -0
- package/dist/coding-worker/sdk.d.ts +26 -0
- package/dist/coding-worker/sdk.js +28 -0
- package/dist/coding-worker/types.d.ts +64 -0
- package/dist/coding-worker/types.js +1 -0
- package/dist/config/providers.d.ts +158 -0
- package/dist/config/providers.js +162 -0
- package/dist/core/budget-groups.d.ts +72 -0
- package/dist/core/budget-groups.js +149 -0
- package/dist/core/budget.d.ts +63 -0
- package/dist/core/budget.js +69 -0
- package/dist/core/coding-queue.d.ts +32 -0
- package/dist/core/coding-queue.js +54 -0
- package/dist/core/cron.d.ts +31 -0
- package/dist/core/cron.js +43 -0
- package/dist/core/datastores.d.ts +41 -0
- package/dist/core/datastores.js +105 -0
- package/dist/core/db.d.ts +2 -0
- package/dist/core/db.js +2 -0
- package/dist/core/dispatch.d.ts +81 -0
- package/dist/core/dispatch.js +184 -0
- package/dist/core/engine-native.d.ts +29 -0
- package/dist/core/engine-native.js +259 -0
- package/dist/core/http-runtime.d.ts +7 -0
- package/dist/core/http-runtime.js +101 -0
- package/dist/core/lease.d.ts +13 -0
- package/dist/core/lease.js +22 -0
- package/dist/core/logger.d.ts +13 -0
- package/dist/core/logger.js +45 -0
- package/dist/core/memory-tools.d.ts +11 -0
- package/dist/core/memory-tools.js +122 -0
- package/dist/core/reconciler.d.ts +45 -0
- package/dist/core/reconciler.js +152 -0
- package/dist/core/runner.d.ts +87 -0
- package/dist/core/runner.js +529 -0
- package/dist/core/scheduler.d.ts +47 -0
- package/dist/core/scheduler.js +121 -0
- package/dist/core/secrets.d.ts +46 -0
- package/dist/core/secrets.js +100 -0
- package/dist/core/subagent-memory-tools.d.ts +18 -0
- package/dist/core/subagent-memory-tools.js +121 -0
- package/dist/core/timing.d.ts +7 -0
- package/dist/core/timing.js +7 -0
- package/dist/core/webhooks.d.ts +23 -0
- package/dist/core/webhooks.js +113 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.js +13 -0
- package/dist/import/budgets.d.ts +16 -0
- package/dist/import/budgets.js +21 -0
- package/dist/import/bundle.d.ts +16 -0
- package/dist/import/bundle.js +61 -0
- package/dist/import/cli-args.d.ts +2 -0
- package/dist/import/cli-args.js +44 -0
- package/dist/import/create.d.ts +34 -0
- package/dist/import/create.js +363 -0
- package/dist/import/index.d.ts +25 -0
- package/dist/import/index.js +130 -0
- package/dist/import/model-gate.d.ts +1 -0
- package/dist/import/model-gate.js +3 -0
- package/dist/import/neutral-schema.d.ts +369 -0
- package/dist/import/neutral-schema.js +109 -0
- package/dist/import/preflight.d.ts +41 -0
- package/dist/import/preflight.js +71 -0
- package/dist/import/report.d.ts +5 -0
- package/dist/import/report.js +98 -0
- package/dist/import/tool-scan.d.ts +8 -0
- package/dist/import/tool-scan.js +20 -0
- package/dist/mcp/auth/ownership.d.ts +113 -0
- package/dist/mcp/auth/ownership.js +87 -0
- package/dist/mcp/auth/principal.d.ts +9 -0
- package/dist/mcp/auth/principal.js +9 -0
- package/dist/mcp/auth/resource-server.d.ts +47 -0
- package/dist/mcp/auth/resource-server.js +82 -0
- package/dist/mcp/auth/self-hosted/browser.d.ts +3 -0
- package/dist/mcp/auth/self-hosted/browser.js +273 -0
- package/dist/mcp/auth/self-hosted/cli.d.ts +2 -0
- package/dist/mcp/auth/self-hosted/cli.js +39 -0
- package/dist/mcp/auth/self-hosted/credentials.d.ts +58 -0
- package/dist/mcp/auth/self-hosted/credentials.js +106 -0
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +13 -0
- package/dist/mcp/auth/self-hosted/rate-limit.js +20 -0
- package/dist/mcp/auth/self-hosted/session.d.ts +36 -0
- package/dist/mcp/auth/self-hosted/session.js +110 -0
- package/dist/mcp/capabilities.d.ts +11 -0
- package/dist/mcp/capabilities.js +10 -0
- package/dist/mcp/context.d.ts +46 -0
- package/dist/mcp/context.js +1 -0
- package/dist/mcp/errors.d.ts +13 -0
- package/dist/mcp/errors.js +22 -0
- package/dist/mcp/index.d.ts +32 -0
- package/dist/mcp/index.js +187 -0
- package/dist/mcp/jsonrpc.d.ts +8 -0
- package/dist/mcp/jsonrpc.js +1 -0
- package/dist/mcp/server.d.ts +107 -0
- package/dist/mcp/server.js +203 -0
- package/dist/mcp/tasks/manager.d.ts +63 -0
- package/dist/mcp/tasks/manager.js +103 -0
- package/dist/mcp/tools/agents.d.ts +2 -0
- package/dist/mcp/tools/agents.js +375 -0
- package/dist/mcp/tools/budget-groups.d.ts +2 -0
- package/dist/mcp/tools/budget-groups.js +136 -0
- package/dist/mcp/tools/datastore.d.ts +2 -0
- package/dist/mcp/tools/datastore.js +217 -0
- package/dist/mcp/tools/memory.d.ts +3 -0
- package/dist/mcp/tools/memory.js +60 -0
- package/dist/mcp/tools/models.d.ts +8 -0
- package/dist/mcp/tools/models.js +15 -0
- package/dist/mcp/tools/runs.d.ts +2 -0
- package/dist/mcp/tools/runs.js +61 -0
- package/dist/mcp/tools/scheduling.d.ts +2 -0
- package/dist/mcp/tools/scheduling.js +48 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -0
- package/dist/mcp/tools/secret-elicitation-form.js +93 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +5 -0
- package/dist/mcp/tools/secret-elicitation-server.js +57 -0
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -0
- package/dist/mcp/tools/secret-elicitation.js +52 -0
- package/dist/mcp/tools/secrets.d.ts +13 -0
- package/dist/mcp/tools/secrets.js +124 -0
- package/dist/mcp/tools/subagents.d.ts +2 -0
- package/dist/mcp/tools/subagents.js +133 -0
- package/dist/mcp/tools/text-result.d.ts +6 -0
- package/dist/mcp/tools/text-result.js +3 -0
- package/dist/mcp/tools/tools.d.ts +2 -0
- package/dist/mcp/tools/tools.js +211 -0
- package/dist/mcp/tools/trigger.d.ts +2 -0
- package/dist/mcp/tools/trigger.js +120 -0
- package/dist/mcp/tools/webhooks.d.ts +2 -0
- package/dist/mcp/tools/webhooks.js +34 -0
- package/dist/mcp/transport/http-limits.d.ts +17 -0
- package/dist/mcp/transport/http-limits.js +92 -0
- package/dist/mcp/transport/stdio.d.ts +23 -0
- package/dist/mcp/transport/stdio.js +6 -0
- package/dist/mcp/transport/streamable-http.d.ts +34 -0
- package/dist/mcp/transport/streamable-http.js +158 -0
- package/dist/mcp/unattended-schedules.d.ts +15 -0
- package/dist/mcp/unattended-schedules.js +21 -0
- package/dist/mcp/webhooks/ingress.d.ts +17 -0
- package/dist/mcp/webhooks/ingress.js +33 -0
- package/dist/observability/config.d.ts +6 -0
- package/dist/observability/config.js +20 -0
- package/dist/observability/metrics-server.d.ts +11 -0
- package/dist/observability/metrics-server.js +47 -0
- package/dist/observability/metrics.d.ts +36 -0
- package/dist/observability/metrics.js +158 -0
- package/dist/providers/auth/authorization-server.d.ts +41 -0
- package/dist/providers/auth/authorization-server.js +1 -0
- package/dist/providers/auth/delegating.d.ts +44 -0
- package/dist/providers/auth/delegating.js +105 -0
- package/dist/providers/auth/index.d.ts +11 -0
- package/dist/providers/auth/index.js +20 -0
- package/dist/providers/auth/release-gate.d.ts +1 -0
- package/dist/providers/auth/release-gate.js +4 -0
- package/dist/providers/auth/self-hosted.d.ts +78 -0
- package/dist/providers/auth/self-hosted.js +377 -0
- package/dist/providers/auth/subject.d.ts +1 -0
- package/dist/providers/auth/subject.js +6 -0
- package/dist/providers/auth/types.d.ts +63 -0
- package/dist/providers/auth/types.js +22 -0
- package/dist/providers/coding-proxy/deny-port.d.ts +5 -0
- package/dist/providers/coding-proxy/deny-port.js +45 -0
- package/dist/providers/coding-proxy/environment-credentials.d.ts +7 -0
- package/dist/providers/coding-proxy/environment-credentials.js +16 -0
- package/dist/providers/coding-proxy/index.d.ts +7 -0
- package/dist/providers/coding-proxy/index.js +7 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/memory-ledger.js +103 -0
- package/dist/providers/coding-proxy/metering.d.ts +20 -0
- package/dist/providers/coding-proxy/metering.js +161 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +213 -0
- package/dist/providers/coding-proxy/proxy.d.ts +75 -0
- package/dist/providers/coding-proxy/proxy.js +721 -0
- package/dist/providers/coding-proxy/runtime.d.ts +18 -0
- package/dist/providers/coding-proxy/runtime.js +39 -0
- package/dist/providers/coding-proxy/secure-fetch.d.ts +9 -0
- package/dist/providers/coding-proxy/secure-fetch.js +72 -0
- package/dist/providers/coding-proxy/server.d.ts +17 -0
- package/dist/providers/coding-proxy/server.js +139 -0
- package/dist/providers/coding-proxy/types.d.ts +99 -0
- package/dist/providers/coding-proxy/types.js +1 -0
- package/dist/providers/datastore/index.d.ts +2 -0
- package/dist/providers/datastore/index.js +2 -0
- package/dist/providers/datastore/postgres.d.ts +20 -0
- package/dist/providers/datastore/postgres.js +130 -0
- package/dist/providers/datastore/scoped.d.ts +10 -0
- package/dist/providers/datastore/scoped.js +46 -0
- package/dist/providers/datastore/types.d.ts +31 -0
- package/dist/providers/datastore/types.js +7 -0
- package/dist/providers/email/types.d.ts +46 -0
- package/dist/providers/email/types.js +8 -0
- package/dist/providers/engine/index.d.ts +1 -0
- package/dist/providers/engine/index.js +1 -0
- package/dist/providers/engine/types.d.ts +67 -0
- package/dist/providers/engine/types.js +16 -0
- package/dist/providers/executor/build.d.ts +6 -0
- package/dist/providers/executor/build.js +15 -0
- package/dist/providers/executor/composition.d.ts +14 -0
- package/dist/providers/executor/composition.js +123 -0
- package/dist/providers/executor/container.d.ts +184 -0
- package/dist/providers/executor/container.js +936 -0
- package/dist/providers/executor/dbos-status.d.ts +50 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +53 -0
- package/dist/providers/executor/dbos.js +269 -0
- package/dist/providers/executor/in-process.d.ts +18 -0
- package/dist/providers/executor/in-process.js +38 -0
- package/dist/providers/executor/index.d.ts +8 -0
- package/dist/providers/executor/index.js +8 -0
- package/dist/providers/executor/routing.d.ts +48 -0
- package/dist/providers/executor/routing.js +70 -0
- package/dist/providers/executor/types.d.ts +55 -0
- package/dist/providers/executor/types.js +1 -0
- package/dist/providers/index.d.ts +43 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/jobs/docker-isolation.d.ts +166 -0
- package/dist/providers/jobs/docker-isolation.js +724 -0
- package/dist/providers/jobs/docker.d.ts +127 -0
- package/dist/providers/jobs/docker.js +1027 -0
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +98 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +142 -0
- package/dist/providers/jobs/fake.d.ts +23 -0
- package/dist/providers/jobs/fake.js +132 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +60 -0
- package/dist/providers/jobs/kubernetes-api.js +20 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +36 -0
- package/dist/providers/jobs/kubernetes-client.js +171 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +49 -0
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +141 -0
- package/dist/providers/jobs/kubernetes-isolation.d.ts +147 -0
- package/dist/providers/jobs/kubernetes-isolation.js +607 -0
- package/dist/providers/jobs/kubernetes-platform.d.ts +177 -0
- package/dist/providers/jobs/kubernetes-platform.js +280 -0
- package/dist/providers/jobs/kubernetes-preflight.d.ts +59 -0
- package/dist/providers/jobs/kubernetes-preflight.js +339 -0
- package/dist/providers/jobs/kubernetes-witness.d.ts +10 -0
- package/dist/providers/jobs/kubernetes-witness.js +193 -0
- package/dist/providers/jobs/kubernetes.d.ts +127 -0
- package/dist/providers/jobs/kubernetes.js +874 -0
- package/dist/providers/jobs/safe-extract.d.ts +9 -0
- package/dist/providers/jobs/safe-extract.js +325 -0
- package/dist/providers/jobs/types.d.ts +78 -0
- package/dist/providers/jobs/types.js +6 -0
- package/dist/providers/jobs/workspace-swap.d.ts +2 -0
- package/dist/providers/jobs/workspace-swap.js +47 -0
- package/dist/providers/llm/anthropic.d.ts +13 -0
- package/dist/providers/llm/anthropic.js +21 -0
- package/dist/providers/llm/bedrock.d.ts +19 -0
- package/dist/providers/llm/bedrock.js +33 -0
- package/dist/providers/llm/claude-messages.d.ts +106 -0
- package/dist/providers/llm/claude-messages.js +157 -0
- package/dist/providers/llm/claude-provider.d.ts +40 -0
- package/dist/providers/llm/claude-provider.js +31 -0
- package/dist/providers/llm/index.d.ts +10 -0
- package/dist/providers/llm/index.js +10 -0
- package/dist/providers/llm/openai.d.ts +23 -0
- package/dist/providers/llm/openai.js +156 -0
- package/dist/providers/llm/pricing-anthropic.d.ts +4 -0
- package/dist/providers/llm/pricing-anthropic.js +30 -0
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +12 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +37 -0
- package/dist/providers/llm/pricing-core.d.ts +26 -0
- package/dist/providers/llm/pricing-core.js +17 -0
- package/dist/providers/llm/pricing.d.ts +30 -0
- package/dist/providers/llm/pricing.js +74 -0
- package/dist/providers/llm/registration.d.ts +8 -0
- package/dist/providers/llm/registration.js +39 -0
- package/dist/providers/llm/routing.d.ts +25 -0
- package/dist/providers/llm/routing.js +32 -0
- package/dist/providers/llm/types.d.ts +85 -0
- package/dist/providers/llm/types.js +12 -0
- package/dist/providers/memory/index.d.ts +2 -0
- package/dist/providers/memory/index.js +2 -0
- package/dist/providers/memory/postgres.d.ts +20 -0
- package/dist/providers/memory/postgres.js +72 -0
- package/dist/providers/memory/types.d.ts +32 -0
- package/dist/providers/memory/types.js +17 -0
- package/dist/providers/secrets/app-key.d.ts +8 -0
- package/dist/providers/secrets/app-key.js +50 -0
- package/dist/providers/secrets/index.d.ts +10 -0
- package/dist/providers/secrets/index.js +13 -0
- package/dist/providers/secrets/transfer-envelope.d.ts +12 -0
- package/dist/providers/secrets/transfer-envelope.js +35 -0
- package/dist/providers/secrets/types.d.ts +18 -0
- package/dist/providers/secrets/types.js +12 -0
- package/dist/providers/storage/types.d.ts +18 -0
- package/dist/providers/storage/types.js +7 -0
- package/dist/providers/vcs/git.d.ts +105 -0
- package/dist/providers/vcs/git.js +710 -0
- package/dist/providers/vcs/github.d.ts +105 -0
- package/dist/providers/vcs/github.js +394 -0
- package/dist/providers/vcs/index.d.ts +6 -0
- package/dist/providers/vcs/index.js +26 -0
- package/dist/providers/vcs/types.d.ts +118 -0
- package/dist/providers/vcs/types.js +1 -0
- package/dist/sandbox/bounded-json.d.ts +2 -0
- package/dist/sandbox/bounded-json.js +36 -0
- package/dist/sandbox/bridge.d.ts +15 -0
- package/dist/sandbox/bridge.js +78 -0
- package/dist/sandbox/eval-core.d.ts +37 -0
- package/dist/sandbox/eval-core.js +159 -0
- package/dist/sandbox/fetch-policy.d.ts +24 -0
- package/dist/sandbox/fetch-policy.js +96 -0
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +5 -0
- package/dist/sandbox/generated/zod.bundle.js +1 -0
- package/dist/sandbox/host-functions.d.ts +29 -0
- package/dist/sandbox/host-functions.js +168 -0
- package/dist/sandbox/limits.d.ts +39 -0
- package/dist/sandbox/limits.js +39 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/crash.js +6 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/echo.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/reject.js +5 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.d.ts +1 -0
- package/dist/sandbox/parser-worker/__fixtures__/spin-forever.js +11 -0
- package/dist/sandbox/parser-worker/pool.d.ts +13 -0
- package/dist/sandbox/parser-worker/pool.js +111 -0
- package/dist/sandbox/parser-worker/worker.d.ts +14 -0
- package/dist/sandbox/parser-worker/worker.js +63 -0
- package/dist/sandbox/pii-redaction.d.ts +1 -0
- package/dist/sandbox/pii-redaction.js +29 -0
- package/dist/sandbox/prelude.d.ts +26 -0
- package/dist/sandbox/prelude.js +408 -0
- package/dist/sandbox/quickjs-module.d.ts +13 -0
- package/dist/sandbox/quickjs-module.js +19 -0
- package/dist/sandbox/run-in-sandbox.d.ts +34 -0
- package/dist/sandbox/run-in-sandbox.js +53 -0
- package/dist/sandbox/safe-fetch.d.ts +26 -0
- package/dist/sandbox/safe-fetch.js +140 -0
- package/dist/sandbox/tool-capabilities.d.ts +66 -0
- package/dist/sandbox/tool-capabilities.js +103 -0
- package/dist/sandbox/zod-params.d.ts +22 -0
- package/dist/sandbox/zod-params.js +60 -0
- package/dist/serve.d.ts +14 -0
- package/dist/serve.js +54 -0
- package/dist/wardby-bin.d.ts +2 -0
- package/dist/wardby-bin.js +20 -0
- package/package.json +123 -0
- package/prisma/migrations/20260905000000_init/migration.sql +34 -0
- package/prisma/migrations/20260905010000_scheduler_durability/migration.sql +31 -0
- package/prisma/migrations/20260905020000_tools_multiturn/migration.sql +47 -0
- package/prisma/migrations/20260905030000_cache_tool_json_schema/migration.sql +13 -0
- package/prisma/migrations/20260906000000_mcp_phase4/migration.sql +115 -0
- package/prisma/migrations/20260906010000_run_final_text/migration.sql +7 -0
- package/prisma/migrations/20260906020000_run_turns/migration.sql +6 -0
- package/prisma/migrations/20260906030000_task_principal_id/migration.sql +5 -0
- package/prisma/migrations/20260906040000_secure_self_hosted_oauth/migration.sql +67 -0
- package/prisma/migrations/20260906050000_phase5_coding_agents/migration.sql +52 -0
- package/prisma/migrations/20260906060000_tool_capability_scoping/migration.sql +22 -0
- package/prisma/migrations/20260906070000_datastore_pii_flag/migration.sql +6 -0
- package/prisma/migrations/20260906080000_secret_elicitation_outcomes/migration.sql +13 -0
- package/prisma/migrations/20260907010000_coding_proxy_ledger/migration.sql +57 -0
- package/prisma/migrations/20260907020000_budget_groups/migration.sql +24 -0
- package/prisma/migrations/20260907030000_coding_webhook_task_override/migration.sql +4 -0
- package/prisma/migrations/20260908010000_run_execution_backend/migration.sql +2 -0
- package/prisma/migrations/20260908020000_coding_run_audit_metadata/migration.sql +7 -0
- package/prisma/migrations/20260910010000_agent_secret_bound_name/migration.sql +16 -0
- package/prisma/migrations/20260911010000_coding_worker_toolchains/migration.sql +5 -0
- package/prisma/migrations/20260912010000_coding_provider_contract/migration.sql +24 -0
- package/prisma/migrations/20260912020000_coding_proxy_protocol/migration.sql +10 -0
- package/prisma/migrations/20260912030000_agent_memory/migration.sql +18 -0
- package/prisma/migrations/20260912040000_named_shared_datastores/migration.sql +68 -0
- package/prisma/migrations/20260913000000_run_trigger_webhook/migration.sql +2 -0
- package/prisma/migrations/20260913010000_agent_sub_agent/migration.sql +32 -0
- package/prisma/migrations/20260913020000_run_task_override/migration.sql +5 -0
- package/prisma/migrations/20260913030000_coding_run_root/migration.sql +11 -0
- package/prisma/migrations/20260922010000_coding_run_queued_at/migration.sql +8 -0
- package/prisma/migrations/20260922020000_coding_workspace_disk/migration.sql +8 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +723 -0
- package/scripts/git-askpass.sh +11 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, statSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { ManifestSchema, NeutralAgentSchema, NeutralToolSchema, NeutralAgentToolSchema, NeutralSecretSchema, NeutralAgentSecretSchema, NeutralSingleDatastoreSchema, NeutralSharedDatastoreSchema, NeutralWebhookSchema, NeutralBudgetSchema, } from "./neutral-schema.js";
|
|
5
|
+
export class BundleError extends Error {
|
|
6
|
+
}
|
|
7
|
+
function readArray(dir, rel, schema) {
|
|
8
|
+
const p = join(dir, rel);
|
|
9
|
+
if (!existsSync(p))
|
|
10
|
+
return [];
|
|
11
|
+
let raw;
|
|
12
|
+
try {
|
|
13
|
+
raw = JSON.parse(readFileSync(p, "utf8"));
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
throw new BundleError(`${rel}: invalid JSON (${e.message})`);
|
|
17
|
+
}
|
|
18
|
+
const parsed = z.array(schema).safeParse(raw);
|
|
19
|
+
if (!parsed.success)
|
|
20
|
+
throw new BundleError(`${rel}: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`);
|
|
21
|
+
return parsed.data;
|
|
22
|
+
}
|
|
23
|
+
export function openBundle(dir) {
|
|
24
|
+
let st;
|
|
25
|
+
try {
|
|
26
|
+
st = statSync(dir);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new BundleError(`no such bundle directory: ${dir}`);
|
|
30
|
+
}
|
|
31
|
+
if (!st.isDirectory())
|
|
32
|
+
throw new BundleError(`bundle path is not a directory (extract the .zip first): ${dir}`);
|
|
33
|
+
const manifestPath = join(dir, "manifest.json");
|
|
34
|
+
if (!existsSync(manifestPath))
|
|
35
|
+
throw new BundleError("manifest.json not found in bundle");
|
|
36
|
+
let raw;
|
|
37
|
+
try {
|
|
38
|
+
raw = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
throw new BundleError(`manifest.json: invalid JSON (${e.message})`);
|
|
42
|
+
}
|
|
43
|
+
const parsed = ManifestSchema.safeParse(raw);
|
|
44
|
+
if (!parsed.success)
|
|
45
|
+
throw new BundleError(`manifest.json: ${parsed.error.issues[0]?.message ?? "invalid"}`);
|
|
46
|
+
const manifest = parsed.data;
|
|
47
|
+
if (manifest.bundleVersion !== 1)
|
|
48
|
+
throw new BundleError(`unsupported bundleVersion ${manifest.bundleVersion}`);
|
|
49
|
+
return {
|
|
50
|
+
manifest,
|
|
51
|
+
readAgents: () => readArray(dir, "config/agents.json", NeutralAgentSchema),
|
|
52
|
+
readTools: () => readArray(dir, "config/tools.json", NeutralToolSchema),
|
|
53
|
+
readAgentTools: () => readArray(dir, "config/agent-tools.json", NeutralAgentToolSchema),
|
|
54
|
+
readSecrets: () => readArray(dir, "config/secrets.json", NeutralSecretSchema),
|
|
55
|
+
readAgentSecrets: () => readArray(dir, "config/agent-secrets.json", NeutralAgentSecretSchema),
|
|
56
|
+
readSingleDatastores: () => readArray(dir, "config/datastores-single.json", NeutralSingleDatastoreSchema),
|
|
57
|
+
readSharedDatastores: () => readArray(dir, "config/datastores-shared.json", NeutralSharedDatastoreSchema),
|
|
58
|
+
readWebhooks: () => readArray(dir, "config/webhooks.json", NeutralWebhookSchema),
|
|
59
|
+
readBudgets: () => readArray(dir, "capabilities/budgets.json", NeutralBudgetSchema),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
const VALID_CONFLICT_POLICIES = ["fail", "skip", "rename"];
|
|
3
|
+
export function parseImportArgs(args) {
|
|
4
|
+
const { values, positionals } = parseArgs({
|
|
5
|
+
args,
|
|
6
|
+
allowPositionals: true,
|
|
7
|
+
options: {
|
|
8
|
+
owner: { type: "string" },
|
|
9
|
+
public: { type: "boolean" },
|
|
10
|
+
"include-secrets": { type: "boolean" },
|
|
11
|
+
"transfer-key": { type: "string" },
|
|
12
|
+
"default-budget": { type: "string" },
|
|
13
|
+
"dry-run": { type: "boolean" },
|
|
14
|
+
prefix: { type: "string" },
|
|
15
|
+
"on-conflict": { type: "string" },
|
|
16
|
+
"allow-open-fetch": { type: "boolean" },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
// Require one positional: the bundle directory
|
|
20
|
+
if (positionals.length === 0) {
|
|
21
|
+
throw new Error("Missing bundle directory positional argument");
|
|
22
|
+
}
|
|
23
|
+
const dir = positionals[0];
|
|
24
|
+
// Parse owner/public
|
|
25
|
+
const isPublic = values.public ?? false;
|
|
26
|
+
const owner = isPublic ? null : (values.owner ?? null);
|
|
27
|
+
// Validate on-conflict
|
|
28
|
+
const onConflict = (values["on-conflict"] ?? "fail");
|
|
29
|
+
if (!VALID_CONFLICT_POLICIES.includes(onConflict)) {
|
|
30
|
+
throw new Error(`Invalid --on-conflict value "${onConflict}". Must be one of: ${VALID_CONFLICT_POLICIES.join(", ")}`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
dir,
|
|
34
|
+
owner,
|
|
35
|
+
isPublic,
|
|
36
|
+
includeSecrets: values["include-secrets"] ?? false,
|
|
37
|
+
transferKeyPath: values["transfer-key"],
|
|
38
|
+
defaultBudget: values["default-budget"],
|
|
39
|
+
dryRun: values["dry-run"] ?? false,
|
|
40
|
+
prefix: values.prefix ?? "imported-",
|
|
41
|
+
onConflict,
|
|
42
|
+
allowOpenFetch: values["allow-open-fetch"] ?? false,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered idempotent create step — writes Tier-1 entities (tools, agents,
|
|
3
|
+
* attachments, secrets, datastores, webhooks, budget groups) to the database
|
|
4
|
+
* from a validated bundle + reconciliation.
|
|
5
|
+
*/
|
|
6
|
+
import type { PrismaClient } from "@prisma/client";
|
|
7
|
+
import type { KeyObject } from "node:crypto";
|
|
8
|
+
import type { Bundle } from "./bundle.js";
|
|
9
|
+
import type { Reconciliation } from "./preflight.js";
|
|
10
|
+
import type { SecretCipher } from "../providers/secrets/types.js";
|
|
11
|
+
export interface CreateOptions {
|
|
12
|
+
db: PrismaClient;
|
|
13
|
+
cipher: SecretCipher;
|
|
14
|
+
ownerId: string | null;
|
|
15
|
+
defaultBudget: string;
|
|
16
|
+
secretMode: "references" | "envelope";
|
|
17
|
+
transferPrivateKey?: KeyObject;
|
|
18
|
+
allowOpenFetch: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ImportResult {
|
|
21
|
+
agentsCreated: number;
|
|
22
|
+
toolsCreated: number;
|
|
23
|
+
secretsCreated: number;
|
|
24
|
+
datastoreEntries: number;
|
|
25
|
+
datastoresSharedCreated: number;
|
|
26
|
+
budgetGroupsCreated: number;
|
|
27
|
+
webhookSecrets: {
|
|
28
|
+
agentName: string;
|
|
29
|
+
secret: string;
|
|
30
|
+
}[];
|
|
31
|
+
pendingSecretReentry: string[];
|
|
32
|
+
warnings: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function createFromBundle(bundle: Bundle, recon: Reconciliation, opts: CreateOptions): Promise<ImportResult>;
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered idempotent create step — writes Tier-1 entities (tools, agents,
|
|
3
|
+
* attachments, secrets, datastores, webhooks, budget groups) to the database
|
|
4
|
+
* from a validated bundle + reconciliation.
|
|
5
|
+
*/
|
|
6
|
+
import { Prisma } from "@prisma/client";
|
|
7
|
+
import { createSecret, attachSecret } from "../core/secrets.js";
|
|
8
|
+
import { createDatastore, attachDatastore } from "../core/datastores.js";
|
|
9
|
+
import { createWebhook } from "../core/webhooks.js";
|
|
10
|
+
import { deriveJsonSchema } from "../sandbox/zod-params.js";
|
|
11
|
+
import { ToolCapabilitiesPatchSchema, FETCH_WILDCARD } from "../sandbox/tool-capabilities.js";
|
|
12
|
+
import { PostgresDatastore } from "../providers/datastore/postgres.js";
|
|
13
|
+
import { decryptTransferEnvelope } from "../providers/secrets/transfer-envelope.js";
|
|
14
|
+
import { mapBudget } from "./budgets.js";
|
|
15
|
+
export async function createFromBundle(bundle, recon, opts) {
|
|
16
|
+
const { db, cipher, ownerId, secretMode, transferPrivateKey, allowOpenFetch } = opts;
|
|
17
|
+
const warnings = [];
|
|
18
|
+
const webhookSecrets = [];
|
|
19
|
+
const pendingSecretReentry = [];
|
|
20
|
+
const hasOwner = ownerId !== null;
|
|
21
|
+
// Helper: apply nameRemap
|
|
22
|
+
const eff = (name) => recon.nameRemap.get(name) ?? name;
|
|
23
|
+
// Helper: check if agent/tool was skipped/rejected
|
|
24
|
+
const agentSkipped = (name) => recon.agents.find((a) => a.name === name)?.skipped;
|
|
25
|
+
const agentDisabled = (name) => recon.agents.find((a) => a.name === name)?.disabled ?? false;
|
|
26
|
+
const toolRejected = (name) => recon.tools.find((t) => t.name === name)?.rejected;
|
|
27
|
+
const toolSkipped = (name) => recon.tools.find((t) => t.name === name)?.skipped;
|
|
28
|
+
// Track created entities
|
|
29
|
+
let toolsCreated = 0;
|
|
30
|
+
let agentsCreated = 0;
|
|
31
|
+
let secretsCreated = 0;
|
|
32
|
+
let datastoreEntries = 0;
|
|
33
|
+
let datastoresSharedCreated = 0;
|
|
34
|
+
let budgetGroupsCreated = 0;
|
|
35
|
+
// Step 1: Tools
|
|
36
|
+
const toolIdMap = new Map();
|
|
37
|
+
for (const t of bundle.readTools()) {
|
|
38
|
+
if (toolRejected(t.name) || toolSkipped(t.name))
|
|
39
|
+
continue;
|
|
40
|
+
const schema = await deriveJsonSchema(t.paramsZod);
|
|
41
|
+
if (!schema.ok) {
|
|
42
|
+
warnings.push(`tool ${t.name}: failed to derive JSON schema, skipping`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const tool = await db.tool.upsert({
|
|
46
|
+
where: { name: eff(t.name) },
|
|
47
|
+
create: {
|
|
48
|
+
name: eff(t.name),
|
|
49
|
+
description: t.description,
|
|
50
|
+
paramsZod: t.paramsZod,
|
|
51
|
+
code: t.code,
|
|
52
|
+
jsonSchema: schema.value,
|
|
53
|
+
ownerId,
|
|
54
|
+
},
|
|
55
|
+
update: {},
|
|
56
|
+
});
|
|
57
|
+
toolIdMap.set(t.name, tool.id);
|
|
58
|
+
toolsCreated++;
|
|
59
|
+
}
|
|
60
|
+
// Step 2: Agents
|
|
61
|
+
const agentIdMap = new Map();
|
|
62
|
+
for (const a of bundle.readAgents()) {
|
|
63
|
+
if (agentSkipped(a.name))
|
|
64
|
+
continue;
|
|
65
|
+
const scheduleEnabled = agentDisabled(a.name) ? false : a.scheduleEnabled;
|
|
66
|
+
const agent = await db.agent.upsert({
|
|
67
|
+
where: { name: eff(a.name) },
|
|
68
|
+
create: {
|
|
69
|
+
name: eff(a.name),
|
|
70
|
+
model: a.model,
|
|
71
|
+
systemPrompt: a.systemPrompt,
|
|
72
|
+
budgetUsd: opts.defaultBudget,
|
|
73
|
+
schedule: a.schedule || null,
|
|
74
|
+
timezone: a.timezone,
|
|
75
|
+
maxTurns: a.maxTurns,
|
|
76
|
+
scheduleEnabled,
|
|
77
|
+
ownerId,
|
|
78
|
+
},
|
|
79
|
+
update: {},
|
|
80
|
+
});
|
|
81
|
+
agentIdMap.set(a.name, agent.id);
|
|
82
|
+
agentsCreated++;
|
|
83
|
+
}
|
|
84
|
+
// Step 3: Attach tools
|
|
85
|
+
for (const at of bundle.readAgentTools()) {
|
|
86
|
+
const agentId = agentIdMap.get(at.agentName);
|
|
87
|
+
const toolId = toolIdMap.get(at.toolName);
|
|
88
|
+
if (!agentId || !toolId) {
|
|
89
|
+
const missing = !agentId ? `agent ${at.agentName}` : `tool ${at.toolName}`;
|
|
90
|
+
warnings.push(`agent-tool ${at.agentName}/${at.toolName}: skipped — ${missing} was not imported`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Validate capabilities (drop "*" unless allowOpenFetch)
|
|
94
|
+
let allowedHosts = at.allowedHosts;
|
|
95
|
+
if (!allowOpenFetch) {
|
|
96
|
+
allowedHosts = allowedHosts.filter((h) => h !== FETCH_WILDCARD);
|
|
97
|
+
}
|
|
98
|
+
const capsPatch = ToolCapabilitiesPatchSchema.safeParse({
|
|
99
|
+
allowedSecrets: at.allowedSecrets,
|
|
100
|
+
allowedDatastorePrefixes: at.allowedDatastorePrefixes,
|
|
101
|
+
allowedHosts,
|
|
102
|
+
allowedSharedDatastorePrefixes: at.allowedSharedDatastorePrefixes,
|
|
103
|
+
});
|
|
104
|
+
if (!capsPatch.success) {
|
|
105
|
+
warnings.push(`agent-tool ${at.agentName}/${at.toolName}: invalid capabilities, skipping attachment`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const caps = capsPatch.data;
|
|
109
|
+
await db.agentTool.upsert({
|
|
110
|
+
where: { agentId_toolId: { agentId, toolId } },
|
|
111
|
+
create: {
|
|
112
|
+
agentId,
|
|
113
|
+
toolId,
|
|
114
|
+
allowedSecrets: caps.allowedSecrets ?? [],
|
|
115
|
+
allowedDatastorePrefixes: caps.allowedDatastorePrefixes ?? [],
|
|
116
|
+
allowedHosts: caps.allowedHosts ?? [],
|
|
117
|
+
allowedSharedDatastorePrefixes: caps.allowedSharedDatastorePrefixes ?? {},
|
|
118
|
+
},
|
|
119
|
+
update: {
|
|
120
|
+
allowedSecrets: caps.allowedSecrets ?? [],
|
|
121
|
+
allowedDatastorePrefixes: caps.allowedDatastorePrefixes ?? [],
|
|
122
|
+
allowedHosts: caps.allowedHosts ?? [],
|
|
123
|
+
allowedSharedDatastorePrefixes: caps.allowedSharedDatastorePrefixes ?? {},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Step 4: Secrets — one row per base secret, under its canonical name.
|
|
128
|
+
if (secretMode === "envelope") {
|
|
129
|
+
if (!hasOwner) {
|
|
130
|
+
for (const s of bundle.readSecrets()) {
|
|
131
|
+
warnings.push(`secret ${s.name}: skipped — secrets require an owner (public import)`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (!transferPrivateKey) {
|
|
135
|
+
warnings.push("envelope mode requires transferPrivateKey, skipping secrets");
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const owner = ownerId; // hasOwner guarantees non-null
|
|
139
|
+
for (const s of bundle.readSecrets()) {
|
|
140
|
+
if (!s.ciphertext) {
|
|
141
|
+
warnings.push(`secret ${s.name}: no ciphertext in envelope mode, skipping`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
let plaintext;
|
|
145
|
+
try {
|
|
146
|
+
// AAD is ALWAYS the base secret name (s.name), never an alias.
|
|
147
|
+
plaintext = decryptTransferEnvelope(s.ciphertext, s.name, transferPrivateKey);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
warnings.push(`secret ${s.name}: failed to decrypt (${err instanceof Error ? err.message : String(err)})`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
await createSecret(s.name, plaintext, owner, cipher, db);
|
|
155
|
+
secretsCreated++; // one per distinct base secret (fixes F1 over-count)
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
warnings.push(`secret ${s.name}: failed to create (${err instanceof Error ? err.message : String(err)})`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
for (const s of bundle.readSecrets()) {
|
|
165
|
+
pendingSecretReentry.push(s.name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Step 5: Attach secrets (envelope mode only)
|
|
169
|
+
if (secretMode === "envelope" && hasOwner) {
|
|
170
|
+
const owner = ownerId;
|
|
171
|
+
for (const as of bundle.readAgentSecrets()) {
|
|
172
|
+
const agentId = agentIdMap.get(as.agentName);
|
|
173
|
+
if (!agentId)
|
|
174
|
+
continue;
|
|
175
|
+
const boundName = as.alias ?? as.secretName;
|
|
176
|
+
try {
|
|
177
|
+
await attachSecret(agentId, as.secretName, owner, db, boundName);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
|
|
181
|
+
// Either an idempotent re-run, or two secrets bound to the same
|
|
182
|
+
// point-of-use name for one agent (a bundle inconsistency). Surface it.
|
|
183
|
+
warnings.push(`agent-secret ${as.agentName}: "${boundName}" already bound — skipped (re-run or duplicate binding)`);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
warnings.push(`agent-secret ${as.agentName}/${as.secretName}: failed to attach (${err instanceof Error ? err.message : String(err)})`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Step 6: Per-agent datastore seed
|
|
191
|
+
const ds = new PostgresDatastore(db, cipher);
|
|
192
|
+
for (const d of bundle.readSingleDatastores()) {
|
|
193
|
+
const agentId = agentIdMap.get(d.agentName);
|
|
194
|
+
if (!agentId)
|
|
195
|
+
continue;
|
|
196
|
+
for (const entry of d.entries) {
|
|
197
|
+
try {
|
|
198
|
+
await ds.set(agentId, entry.key, entry.value, { pii: entry.pii });
|
|
199
|
+
datastoreEntries++;
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
warnings.push(`datastore ${d.agentName}/${entry.key}: failed to set (${err instanceof Error ? err.message : String(err)})`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Step 6.5: Shared datastores — require an owner, same rule as
|
|
207
|
+
// secrets/webhooks/budgets: skip with a warning in public-import mode.
|
|
208
|
+
if (!hasOwner) {
|
|
209
|
+
for (const sd of bundle.readSharedDatastores()) {
|
|
210
|
+
warnings.push(`shared-datastore ${sd.name}: skipped — shared datastores require an owner (public import)`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const owner = ownerId; // hasOwner guarantees non-null
|
|
215
|
+
for (const sd of bundle.readSharedDatastores()) {
|
|
216
|
+
let created;
|
|
217
|
+
try {
|
|
218
|
+
created = await createDatastore(sd.name, owner, db);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
|
|
222
|
+
// Two shared-datastore definitions in this bundle collide on
|
|
223
|
+
// (ownerId, name) — createDatastore isn't idempotent (Task 3).
|
|
224
|
+
// Surface it and move on rather than aborting the whole import.
|
|
225
|
+
warnings.push(`shared-datastore ${sd.name}: a datastore with this name already exists for this owner — skipped`);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
warnings.push(`shared-datastore ${sd.name}: failed to create (${err instanceof Error ? err.message : String(err)})`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
datastoresSharedCreated++;
|
|
232
|
+
for (const entry of sd.entries) {
|
|
233
|
+
try {
|
|
234
|
+
await db.datastoreEntry.create({
|
|
235
|
+
data: { datastoreId: created.id, key: entry.key, value: entry.value, pii: false },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
warnings.push(`shared-datastore ${sd.name}/${entry.key}: failed to seed (${err instanceof Error ? err.message : String(err)})`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const agentName of sd.attachedAgentNames) {
|
|
243
|
+
const agentId = agentIdMap.get(agentName);
|
|
244
|
+
if (!agentId)
|
|
245
|
+
continue;
|
|
246
|
+
try {
|
|
247
|
+
await attachDatastore(agentId, created.id, db, sd.name);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
|
|
251
|
+
warnings.push(`shared-datastore ${sd.name}: agent ${agentName} already binds "${sd.name}" — skipped`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
warnings.push(`shared-datastore ${sd.name}/${agentName}: failed to attach (${err instanceof Error ? err.message : String(err)})`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Step 7: Webhooks
|
|
260
|
+
if (!hasOwner) {
|
|
261
|
+
// Webhooks require an owner - skip in public import mode
|
|
262
|
+
for (const w of bundle.readWebhooks()) {
|
|
263
|
+
warnings.push(`webhook for agent ${w.agentName}: skipped — webhooks require an owner (public import)`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const owner = ownerId; // TypeScript narrowing: hasOwner guarantees non-null
|
|
268
|
+
for (const w of bundle.readWebhooks()) {
|
|
269
|
+
const agentId = agentIdMap.get(w.agentName);
|
|
270
|
+
if (!agentId)
|
|
271
|
+
continue;
|
|
272
|
+
try {
|
|
273
|
+
// Check if webhook already exists (idempotency)
|
|
274
|
+
const existing = await db.webhook.findFirst({ where: { agentId } });
|
|
275
|
+
if (existing)
|
|
276
|
+
continue;
|
|
277
|
+
const { id, secret } = await createWebhook(agentId, owner, db);
|
|
278
|
+
webhookSecrets.push({ agentName: w.agentName, secret });
|
|
279
|
+
if (!w.enabled) {
|
|
280
|
+
await db.webhook.update({
|
|
281
|
+
where: { id },
|
|
282
|
+
data: { status: "disabled" },
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
warnings.push(`webhook ${w.agentName}: failed to create (${err instanceof Error ? err.message : String(err)})`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Step 8: Budget groups
|
|
292
|
+
if (!hasOwner) {
|
|
293
|
+
// Budget groups require an owner - skip in public import mode
|
|
294
|
+
for (const b of bundle.readBudgets()) {
|
|
295
|
+
const mapped = mapBudget(b);
|
|
296
|
+
if (mapped.ok) {
|
|
297
|
+
warnings.push(`budget group ${mapped.group.name}: skipped — budget groups require an owner (public import)`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
const owner = ownerId; // TypeScript narrowing: hasOwner guarantees non-null
|
|
303
|
+
const budgetGroupMap = new Map(); // budget name -> groupId
|
|
304
|
+
const agentBudgetAssignments = new Map(); // agent name -> first budget that claimed it
|
|
305
|
+
for (const b of bundle.readBudgets()) {
|
|
306
|
+
const mapped = mapBudget(b);
|
|
307
|
+
if (!mapped.ok)
|
|
308
|
+
continue;
|
|
309
|
+
const { group, agentNames } = mapped;
|
|
310
|
+
try {
|
|
311
|
+
const bg = await db.budgetGroup.upsert({
|
|
312
|
+
where: { ownerId_name: { ownerId: owner, name: group.name } },
|
|
313
|
+
create: {
|
|
314
|
+
name: group.name,
|
|
315
|
+
ownerId: owner,
|
|
316
|
+
dailyBudgetUsd: group.dailyBudgetUsd,
|
|
317
|
+
weeklyBudgetUsd: group.weeklyBudgetUsd,
|
|
318
|
+
monthlyBudgetUsd: group.monthlyBudgetUsd,
|
|
319
|
+
warnThresholdRatio: group.warnThresholdRatio,
|
|
320
|
+
},
|
|
321
|
+
update: {},
|
|
322
|
+
});
|
|
323
|
+
budgetGroupMap.set(group.name, bg.id);
|
|
324
|
+
budgetGroupsCreated++;
|
|
325
|
+
// Attach agents (first budget wins)
|
|
326
|
+
for (const agentName of agentNames) {
|
|
327
|
+
const agentId = agentIdMap.get(agentName);
|
|
328
|
+
if (!agentId)
|
|
329
|
+
continue;
|
|
330
|
+
const existingBudget = agentBudgetAssignments.get(agentName);
|
|
331
|
+
if (existingBudget) {
|
|
332
|
+
warnings.push(`agent ${agentName}: already in budget group "${existingBudget}", ignoring "${group.name}"`);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
await db.agent.update({
|
|
337
|
+
where: { id: agentId },
|
|
338
|
+
data: { budgetGroupId: bg.id },
|
|
339
|
+
});
|
|
340
|
+
agentBudgetAssignments.set(agentName, group.name);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
warnings.push(`agent ${agentName}: failed to attach to budget group "${group.name}" (${err instanceof Error ? err.message : String(err)})`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
warnings.push(`budget group ${group.name}: failed to create (${err instanceof Error ? err.message : String(err)})`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
agentsCreated,
|
|
354
|
+
toolsCreated,
|
|
355
|
+
secretsCreated,
|
|
356
|
+
datastoreEntries,
|
|
357
|
+
datastoresSharedCreated,
|
|
358
|
+
budgetGroupsCreated,
|
|
359
|
+
webhookSecrets,
|
|
360
|
+
pendingSecretReentry,
|
|
361
|
+
warnings,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level orchestrator: opens a bundle, runs preflight reconciliation,
|
|
3
|
+
* renders the report, and (unless dry-run or fatal collision) creates entities.
|
|
4
|
+
*/
|
|
5
|
+
import type { PrismaClient } from "@prisma/client";
|
|
6
|
+
import type { ConflictPolicy } from "./preflight.js";
|
|
7
|
+
import { type ImportResult } from "./create.js";
|
|
8
|
+
export interface ImportOptions {
|
|
9
|
+
dir: string;
|
|
10
|
+
owner: string | null;
|
|
11
|
+
isPublic: boolean;
|
|
12
|
+
includeSecrets: boolean;
|
|
13
|
+
transferKeyPath?: string;
|
|
14
|
+
defaultBudget?: string;
|
|
15
|
+
dryRun: boolean;
|
|
16
|
+
prefix: string;
|
|
17
|
+
onConflict: ConflictPolicy;
|
|
18
|
+
allowOpenFetch: boolean;
|
|
19
|
+
db: PrismaClient;
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
21
|
+
}
|
|
22
|
+
export declare function runImport(opts: ImportOptions): Promise<{
|
|
23
|
+
report: string;
|
|
24
|
+
result?: ImportResult;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level orchestrator: opens a bundle, runs preflight reconciliation,
|
|
3
|
+
* renders the report, and (unless dry-run or fatal collision) creates entities.
|
|
4
|
+
*/
|
|
5
|
+
import { openBundle } from "./bundle.js";
|
|
6
|
+
import { preflight } from "./preflight.js";
|
|
7
|
+
import { renderReconciliation } from "./report.js";
|
|
8
|
+
import { createFromBundle } from "./create.js";
|
|
9
|
+
import { supportedGlobalsFromPrelude } from "./tool-scan.js";
|
|
10
|
+
import { SANDBOX_PRELUDE } from "../sandbox/prelude.js";
|
|
11
|
+
import { resolveLlmRegistrations } from "../providers/llm/registration.js";
|
|
12
|
+
import { RoutingLlmProvider } from "../providers/llm/routing.js";
|
|
13
|
+
import { loadProviderConfig } from "../config/providers.js";
|
|
14
|
+
import { buildSecretCipher } from "../providers/secrets/index.js";
|
|
15
|
+
import { resolvePrincipal } from "../mcp/auth/principal.js";
|
|
16
|
+
import { loadTransferPrivateKey, transferKeyIdOf } from "../providers/secrets/transfer-envelope.js";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
export async function runImport(opts) {
|
|
19
|
+
const env = opts.env ?? process.env;
|
|
20
|
+
// Guard: owner XOR public
|
|
21
|
+
if (opts.owner === null && !opts.isPublic) {
|
|
22
|
+
throw new Error("Must specify either --owner or --public (not neither)");
|
|
23
|
+
}
|
|
24
|
+
if (opts.owner !== null && opts.isPublic) {
|
|
25
|
+
throw new Error("Cannot specify both --owner and --public");
|
|
26
|
+
}
|
|
27
|
+
// Open bundle
|
|
28
|
+
const bundle = openBundle(opts.dir);
|
|
29
|
+
// Determine effective secret mode: envelope only if bundle is envelope AND --include-secrets
|
|
30
|
+
const effectiveSecretMode = bundle.manifest.secretMode === "envelope" && opts.includeSecrets ? "envelope" : "references";
|
|
31
|
+
// Build routableModels from LLM registrations
|
|
32
|
+
const reg = resolveLlmRegistrations(env);
|
|
33
|
+
const routableModels = reg.kind === "registrations" ? new Set(new RoutingLlmProvider(reg.registrations).listModels()) : new Set();
|
|
34
|
+
// Get supportedGlobals from prelude
|
|
35
|
+
const supportedGlobals = supportedGlobalsFromPrelude(SANDBOX_PRELUDE);
|
|
36
|
+
// Load existing names from db
|
|
37
|
+
const existingAgents = await opts.db.agent.findMany({ select: { name: true } });
|
|
38
|
+
const existingAgentNames = new Set(existingAgents.map((a) => a.name));
|
|
39
|
+
const existingTools = await opts.db.tool.findMany({ select: { name: true } });
|
|
40
|
+
const existingToolNames = new Set(existingTools.map((t) => t.name));
|
|
41
|
+
// For owner-scoped entities (secrets, budgets), load only if we have an owner
|
|
42
|
+
const ownerId = opts.owner !== null && !opts.isPublic ? (await resolvePrincipal(opts.owner, opts.db)).id : null;
|
|
43
|
+
// Tier-1 capabilities: "budgets" and "shared-datastores" are implemented
|
|
44
|
+
const capabilitiesSupported = new Set(["budgets", "shared-datastores"]);
|
|
45
|
+
// Run preflight reconciliation
|
|
46
|
+
const recon = preflight({
|
|
47
|
+
bundle,
|
|
48
|
+
routableModels,
|
|
49
|
+
supportedGlobals,
|
|
50
|
+
existingAgentNames,
|
|
51
|
+
existingToolNames,
|
|
52
|
+
capabilitiesSupported,
|
|
53
|
+
onConflict: opts.onConflict,
|
|
54
|
+
prefix: opts.prefix,
|
|
55
|
+
allowOpenFetch: opts.allowOpenFetch,
|
|
56
|
+
});
|
|
57
|
+
// Render initial report
|
|
58
|
+
let report = renderReconciliation(recon, {
|
|
59
|
+
secretMode: bundle.manifest.secretMode,
|
|
60
|
+
dryRun: opts.dryRun,
|
|
61
|
+
});
|
|
62
|
+
// Early return if dry-run or fatal collision
|
|
63
|
+
if (recon.hasFatalCollision && !opts.dryRun) {
|
|
64
|
+
report +=
|
|
65
|
+
"\n\n*** Import aborted: a name collision was found under --on-conflict fail. No changes were written. ***";
|
|
66
|
+
return { report };
|
|
67
|
+
}
|
|
68
|
+
if (opts.dryRun || recon.hasFatalCollision) {
|
|
69
|
+
return { report };
|
|
70
|
+
}
|
|
71
|
+
// Load and verify transfer key (only when actually importing secrets from envelope)
|
|
72
|
+
let transferPrivateKey = undefined;
|
|
73
|
+
if (effectiveSecretMode === "envelope") {
|
|
74
|
+
if (!opts.transferKeyPath) {
|
|
75
|
+
throw new Error("Importing secrets from an envelope bundle requires --transfer-key");
|
|
76
|
+
}
|
|
77
|
+
const keyPem = readFileSync(opts.transferKeyPath, "utf8");
|
|
78
|
+
transferPrivateKey = loadTransferPrivateKey(keyPem);
|
|
79
|
+
const keyId = transferKeyIdOf(transferPrivateKey);
|
|
80
|
+
if (keyId !== bundle.manifest.transferKeyId) {
|
|
81
|
+
throw new Error(`Transfer key ID mismatch: key has ${keyId}, bundle manifest declares ${bundle.manifest.transferKeyId}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Build secret cipher
|
|
85
|
+
const providerConfig = loadProviderConfig(env);
|
|
86
|
+
const cipher = buildSecretCipher(providerConfig, env);
|
|
87
|
+
// Run create
|
|
88
|
+
const result = await createFromBundle(bundle, recon, {
|
|
89
|
+
db: opts.db,
|
|
90
|
+
cipher,
|
|
91
|
+
ownerId,
|
|
92
|
+
defaultBudget: opts.defaultBudget ?? "10.00",
|
|
93
|
+
secretMode: effectiveSecretMode,
|
|
94
|
+
transferPrivateKey,
|
|
95
|
+
allowOpenFetch: opts.allowOpenFetch,
|
|
96
|
+
});
|
|
97
|
+
// Append final report sections
|
|
98
|
+
const finalLines = [];
|
|
99
|
+
finalLines.push("");
|
|
100
|
+
finalLines.push("--- Import Complete ---");
|
|
101
|
+
finalLines.push(`Agents created: ${result.agentsCreated}`);
|
|
102
|
+
finalLines.push(`Tools created: ${result.toolsCreated}`);
|
|
103
|
+
finalLines.push(`Secrets created: ${result.secretsCreated}`);
|
|
104
|
+
finalLines.push(`Datastore entries: ${result.datastoreEntries}`);
|
|
105
|
+
finalLines.push(`Shared datastores created: ${result.datastoresSharedCreated}`);
|
|
106
|
+
finalLines.push(`Budget groups created: ${result.budgetGroupsCreated}`);
|
|
107
|
+
if (result.webhookSecrets.length > 0) {
|
|
108
|
+
finalLines.push("");
|
|
109
|
+
finalLines.push("Webhook secrets (save these now, they won't be shown again):");
|
|
110
|
+
for (const ws of result.webhookSecrets) {
|
|
111
|
+
finalLines.push(` ${ws.agentName}: ${ws.secret}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (result.pendingSecretReentry.length > 0) {
|
|
115
|
+
finalLines.push("");
|
|
116
|
+
finalLines.push("Pending secret re-entry (references mode):");
|
|
117
|
+
for (const name of result.pendingSecretReentry) {
|
|
118
|
+
finalLines.push(` - ${name}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (result.warnings.length > 0) {
|
|
122
|
+
finalLines.push("");
|
|
123
|
+
finalLines.push("Warnings:");
|
|
124
|
+
for (const w of result.warnings) {
|
|
125
|
+
finalLines.push(` - ${w}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
report += "\n" + finalLines.join("\n");
|
|
129
|
+
return { report, result };
|
|
130
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function classifyModel(model: string, routableModels: ReadonlySet<string>): "routable" | "unroutable";
|