@wardby/cli 0.2.1 → 0.4.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 +53 -4
- package/README.md +52 -13
- package/dist/claude-coding-worker/driver.d.ts +6 -1
- package/dist/claude-coding-worker/driver.js +27 -1
- package/dist/claude-coding-worker/main.js +2 -0
- 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 +14 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +284 -36
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- 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 +91 -4
- package/dist/coding/protocol.js +123 -12
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- 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 +6 -0
- 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 +13 -1
- package/dist/coding-worker/driver.js +102 -13
- package/dist/coding-worker/errors.js +8 -0
- package/dist/coding-worker/main.js +10 -2
- 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 +66 -0
- package/dist/config/providers.js +137 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +120 -10
- package/dist/core/budget-groups.js +133 -24
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +8 -2
- package/dist/core/dispatch.d.ts +74 -7
- package/dist/core/dispatch.js +382 -47
- package/dist/core/engine-native.js +44 -14
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +72 -0
- package/dist/core/host-events.js +597 -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 +78 -0
- package/dist/core/host-status.js +228 -0
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +42 -2
- package/dist/core/reconciler.js +97 -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 +354 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +33 -8
- package/dist/core/runner.js +410 -62
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- 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 +67 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +17 -2
- package/dist/core/webhooks.js +42 -3
- package/dist/generated/prisma/browser.d.ts +166 -0
- package/dist/generated/prisma/client.d.ts +166 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
- package/dist/generated/prisma/enums.d.ts +13 -0
- package/dist/generated/prisma/enums.js +12 -1
- package/dist/generated/prisma/internal/class.d.ts +242 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
- package/dist/generated/prisma/models/Agent.d.ts +682 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.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/AgentTool.d.ts +95 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.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/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/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +455 -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 +1402 -82
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.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/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +15 -3
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +22 -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 +999 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +5 -0
- package/dist/import/create.js +67 -13
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +18 -18
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +72 -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 +25 -51
- package/dist/mcp/auth/ownership.js +19 -14
- 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 +33 -2
- package/dist/mcp/auth/resource-server.js +99 -3
- package/dist/mcp/auth/self-hosted/browser.js +2 -2
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
- package/dist/mcp/context.d.ts +30 -1
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +38 -0
- package/dist/mcp/host-events/github-ingress.js +83 -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/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +10 -1
- package/dist/mcp/index.js +187 -19
- package/dist/mcp/server.js +32 -11
- package/dist/mcp/tools/agents.js +519 -50
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/datastore.js +22 -15
- 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/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +182 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +3 -6
- 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 +52 -16
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +201 -40
- package/dist/mcp/tools/trigger.js +65 -7
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +15 -0
- package/dist/mcp/transport/streamable-http.js +55 -3
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +2 -0
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +10 -2
- package/dist/providers/auth/self-hosted.js +55 -6
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
- package/dist/providers/coding-proxy/memory-ledger.js +31 -3
- package/dist/providers/coding-proxy/metering.d.ts +6 -1
- package/dist/providers/coding-proxy/metering.js +41 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
- package/dist/providers/coding-proxy/proxy.d.ts +25 -2
- package/dist/providers/coding-proxy/proxy.js +561 -38
- 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 +17 -0
- 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 +33 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +29 -0
- package/dist/providers/executor/build.d.ts +2 -2
- package/dist/providers/executor/composition.d.ts +3 -0
- package/dist/providers/executor/composition.js +28 -1
- package/dist/providers/executor/container.d.ts +134 -4
- package/dist/providers/executor/container.js +394 -37
- package/dist/providers/executor/dbos.d.ts +4 -3
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +13 -0
- package/dist/providers/executor/routing.js +18 -0
- package/dist/providers/executor/types.d.ts +34 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -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 +53 -2
- package/dist/providers/jobs/docker.js +307 -32
- 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 +24 -2
- package/dist/providers/jobs/kubernetes.js +153 -22
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/jobs/types.d.ts +18 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -5
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -8
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- 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 +10 -12
- package/dist/providers/llm/claude-provider.js +15 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +21 -11
- package/dist/providers/llm/routing.js +47 -13
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -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 +226 -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 +568 -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 +309 -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 +78 -34
- package/dist/providers/vcs/github.d.ts +94 -3
- package/dist/providers/vcs/github.js +223 -17
- package/dist/providers/vcs/types.d.ts +48 -4
- package/dist/quickstart/index.d.ts +2 -0
- package/dist/quickstart/index.js +36 -8
- 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 +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +11 -0
- package/docs/README.md +40 -0
- package/docs/agent-recipes.md +383 -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 +508 -0
- package/docs/coding-agent-setup.md +175 -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 +1068 -0
- package/docs/getting-started-gke.md +632 -0
- package/docs/getting-started-identity-provider.md +319 -0
- package/docs/getting-started.md +147 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +667 -0
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +37 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +90 -0
- package/help/deploy-gke.md +45 -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/model-unavailable.md +63 -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 +32 -0
- package/help/github.md +48 -0
- package/help/identity-and-access.md +44 -0
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/mcp.md +30 -0
- package/help/models.md +90 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +36 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +32 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +14 -3
- 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/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +663 -21
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
- package/dist/providers/llm/pricing-anthropic.js +0 -30
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Minimal CLI.
|
|
4
4
|
*
|
|
5
|
-
* wardby agent create --name <n> --model <m> --prompt <p> --budget <usd> [--schedule "<cron>"] [--timezone <tz>] [--memory-enabled]
|
|
5
|
+
* wardby agent create --name <n> --model <m> --prompt <p> --budget <usd> [--schedule "<cron>"] [--timezone <tz>] [--memory-enabled] [--effort <level>]
|
|
6
6
|
* wardby agent list
|
|
7
7
|
* wardby agent schedule <name> --cron "<expr>" [--timezone <tz>] [--disable]
|
|
8
8
|
* wardby run <name>
|
|
@@ -20,31 +20,43 @@ import { readFileSync } from "node:fs";
|
|
|
20
20
|
import { execFile as execFileCallback } from "node:child_process";
|
|
21
21
|
import { parseArgs } from "node:util";
|
|
22
22
|
import { promisify } from "node:util";
|
|
23
|
-
import { loadCodingConcurrencyConfig, loadContainerExecutorConfig, loadKubernetesJobConfig, loadProviderConfig, } from "./config/providers.js";
|
|
23
|
+
import { loadCodingConcurrencyConfig, loadContainerExecutorConfig, loadKubernetesJobConfig, loadMcpConfig, loadProviderConfig, } from "./config/providers.js";
|
|
24
24
|
import { drainCodingQueue } from "./core/coding-queue.js";
|
|
25
25
|
import { isImmutableDockerImage } from "./providers/jobs/docker-isolation.js";
|
|
26
26
|
import { ClientNodeKubernetesApi } from "./providers/jobs/kubernetes-client.js";
|
|
27
27
|
import { describePreflightFailure, kubernetesPreflight } from "./providers/jobs/kubernetes-preflight.js";
|
|
28
|
-
import { RoutingLlmProvider, resolveLlmRegistrations } from "./providers/llm/index.js";
|
|
28
|
+
import { RoutingLlmProvider, isLlmEffort, modelSupportedEfforts, resolveLlmRegistrations, } from "./providers/llm/index.js";
|
|
29
|
+
import { startModelCatalog } from "./providers/llm/catalog-store.js";
|
|
29
30
|
import { buildConfiguredExecutor, buildExecutor } from "./providers/executor/index.js";
|
|
30
31
|
import { PostgresDatastore } from "./providers/datastore/index.js";
|
|
31
32
|
import { PostgresAgentMemory } from "./providers/memory/index.js";
|
|
32
33
|
import { buildSecretCipher } from "./providers/secrets/index.js";
|
|
33
34
|
import { prisma } from "./core/db.js";
|
|
34
35
|
import { runAgent } from "./core/runner.js";
|
|
36
|
+
import { cancelRunOnSignal } from "./core/run-heartbeat.js";
|
|
37
|
+
import { buildIssueTrackers } from "./providers/issue-tracker/index.js";
|
|
38
|
+
import { buildReviewHosts } from "./providers/review-host/index.js";
|
|
39
|
+
import { createRepoAccessGate } from "./core/repo-access.js";
|
|
35
40
|
import { validateCronExpression } from "./core/cron.js";
|
|
41
|
+
import { assertAgentModelAvailable } from "./core/run-pricing.js";
|
|
36
42
|
import { startScheduler } from "./core/scheduler.js";
|
|
37
43
|
import { startReconciler } from "./core/reconciler.js";
|
|
38
44
|
import { NativeEngine } from "./core/engine-native.js";
|
|
39
45
|
import { logger } from "./core/logger.js";
|
|
40
46
|
import { deriveJsonSchema } from "./sandbox/zod-params.js";
|
|
47
|
+
import { findSameNamedAttachedTool, reservedToolNameReason, resolveToolRef } from "./core/tool-names.js";
|
|
48
|
+
import { ToolAttachedError, deleteToolGuarded, formatAttachedAgents, formatToolLine, hasToolChanges, prepareToolUpdate, updateToolGuarded, } from "./core/tool-admin.js";
|
|
41
49
|
import { ToolCapabilitiesPatchSchema } from "./sandbox/tool-capabilities.js";
|
|
42
50
|
import { startMcp } from "./mcp/index.js";
|
|
43
51
|
import { startServe } from "./serve.js";
|
|
44
52
|
import { authCommand } from "./mcp/auth/self-hosted/cli.js";
|
|
53
|
+
import { hostAccountCommand } from "./mcp/auth/host-account-cli.js";
|
|
54
|
+
import { grantsCommand, resolveCliAgentOwner } from "./mcp/auth/grants-cli.js";
|
|
55
|
+
import { everyoneGrantData } from "./core/grants.js";
|
|
45
56
|
import { parseImportArgs } from "./import/cli-args.js";
|
|
46
57
|
import { runImport } from "./import/index.js";
|
|
47
58
|
import { CLI_USAGE } from "./cli-help.js";
|
|
59
|
+
import { helpCommand } from "./help/cli.js";
|
|
48
60
|
const cliLog = logger.child({ module: "cli" });
|
|
49
61
|
const RUN_STATUSES = [
|
|
50
62
|
"pending",
|
|
@@ -106,6 +118,9 @@ async function agentCreate(args) {
|
|
|
106
118
|
timezone: { type: "string" },
|
|
107
119
|
"max-turns": { type: "string" },
|
|
108
120
|
"memory-enabled": { type: "boolean" },
|
|
121
|
+
effort: { type: "string" },
|
|
122
|
+
owner: { type: "string" },
|
|
123
|
+
public: { type: "boolean" },
|
|
109
124
|
},
|
|
110
125
|
});
|
|
111
126
|
if (!values.name || !values.model || !values.prompt || !values.budget) {
|
|
@@ -128,17 +143,58 @@ async function agentCreate(args) {
|
|
|
128
143
|
fail(`invalid --schedule/--timezone: ${err instanceof Error ? err.message : String(err)}`);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
// Started before validating the model so an admin override recorded in
|
|
147
|
+
// ModelCatalogEntry (a disabled model, a narrowed effort list) is
|
|
148
|
+
// respected here too, not just at run time.
|
|
149
|
+
let modelCatalog;
|
|
150
|
+
try {
|
|
151
|
+
modelCatalog = await startModelCatalog(prisma);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
assertAgentModelAvailable(values.model);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
161
|
+
}
|
|
162
|
+
if (values.effort !== undefined) {
|
|
163
|
+
const accepted = modelSupportedEfforts(values.model);
|
|
164
|
+
if (!isLlmEffort(values.effort) || !accepted.includes(values.effort)) {
|
|
165
|
+
fail(`model "${values.model}" does not accept --effort "${values.effort}"` +
|
|
166
|
+
(accepted.length > 0 ? ` (accepted: ${accepted.join(", ")}).` : " (it accepts no effort setting)."));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
modelCatalog.close();
|
|
170
|
+
// Owned by --owner or the local operator; never owner-less (resource-
|
|
171
|
+
// sharing grants spec §3.8). --public shares it with everyone at execute.
|
|
172
|
+
let ownerId;
|
|
173
|
+
try {
|
|
174
|
+
ownerId = await resolveCliAgentOwner(prisma, values.owner, loadMcpConfig().localPrincipal);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
178
|
+
}
|
|
179
|
+
const agent = await prisma.$transaction(async (tx) => {
|
|
180
|
+
const created = await tx.agent.create({
|
|
181
|
+
data: {
|
|
182
|
+
name: values.name,
|
|
183
|
+
model: values.model,
|
|
184
|
+
systemPrompt: values.prompt,
|
|
185
|
+
budgetUsd,
|
|
186
|
+
schedule: values.schedule ?? null,
|
|
187
|
+
timezone,
|
|
188
|
+
maxTurns,
|
|
189
|
+
memoryEnabled: values["memory-enabled"] ?? false,
|
|
190
|
+
effort: values.effort ?? null,
|
|
191
|
+
ownerId: ownerId,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
if (values.public) {
|
|
195
|
+
await tx.resourceGrant.create({ data: everyoneGrantData("agent", created.id, "execute", "operator") });
|
|
196
|
+
}
|
|
197
|
+
return created;
|
|
142
198
|
});
|
|
143
199
|
console.log(agent.id);
|
|
144
200
|
}
|
|
@@ -155,6 +211,9 @@ async function toolCreate(args) {
|
|
|
155
211
|
if (!values.name || !values.description || !values.params || !values.code) {
|
|
156
212
|
fail("tool create requires --name, --description, --params <file>, and --code <file>.");
|
|
157
213
|
}
|
|
214
|
+
const reserved = reservedToolNameReason(values.name);
|
|
215
|
+
if (reserved)
|
|
216
|
+
fail(reserved);
|
|
158
217
|
let paramsZod;
|
|
159
218
|
let code;
|
|
160
219
|
try {
|
|
@@ -170,12 +229,19 @@ async function toolCreate(args) {
|
|
|
170
229
|
fail(`could not read --code file "${values.code}": ${err instanceof Error ? err.message : String(err)}`);
|
|
171
230
|
}
|
|
172
231
|
// Fails at registration, not at call time: a malformed schema never gets
|
|
173
|
-
// persisted. The derived schema is cached on the row
|
|
174
|
-
//
|
|
232
|
+
// persisted. The derived schema is cached on the row rather than
|
|
233
|
+
// re-derived per run; MCP update_tool re-derives it whenever paramsZod
|
|
234
|
+
// changes, so it never goes stale.
|
|
175
235
|
const schemaResult = await deriveJsonSchema(paramsZod);
|
|
176
236
|
if (!schemaResult.ok) {
|
|
177
237
|
fail(`invalid --params schema: ${schemaResult.errorMessage}`);
|
|
178
238
|
}
|
|
239
|
+
// CLI tools are public (no owner), and the (ownerId, name) unique index
|
|
240
|
+
// treats NULL owners as distinct, so it can't stop a second public tool
|
|
241
|
+
// with this name -- check here instead.
|
|
242
|
+
if (await prisma.tool.findFirst({ where: { ownerId: null, name: values.name } })) {
|
|
243
|
+
fail(`a public tool named "${values.name}" already exists.`);
|
|
244
|
+
}
|
|
179
245
|
const tool = await prisma.tool.create({
|
|
180
246
|
data: {
|
|
181
247
|
name: values.name,
|
|
@@ -203,11 +269,14 @@ async function toolAttach(args, detach) {
|
|
|
203
269
|
});
|
|
204
270
|
const [toolName, agentName] = positionals;
|
|
205
271
|
if (!toolName || !agentName) {
|
|
206
|
-
fail(`tool ${detach ? "detach" : "attach"} requires <tool-name> <agent-name>.`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
272
|
+
fail(`tool ${detach ? "detach" : "attach"} requires <tool-name|tool-id> <agent-name>.`);
|
|
273
|
+
}
|
|
274
|
+
// Tool names are unique per owner, not globally: an ambiguous name fails
|
|
275
|
+
// with the candidate ids, and an id is accepted in its place.
|
|
276
|
+
const resolved = await resolveToolRef(prisma, toolName);
|
|
277
|
+
if (!resolved.ok)
|
|
278
|
+
fail(resolved.error);
|
|
279
|
+
const tool = resolved.tool;
|
|
211
280
|
const agent = await prisma.agent.findUnique({ where: { name: agentName } });
|
|
212
281
|
if (!agent)
|
|
213
282
|
fail(`unknown agent "${agentName}".`);
|
|
@@ -229,6 +298,14 @@ async function toolAttach(args, detach) {
|
|
|
229
298
|
(allowedSharedDatastorePrefixes[boundName] ??= []).push(prefix);
|
|
230
299
|
}
|
|
231
300
|
}
|
|
301
|
+
// Same guard as MCP attach_tool: one agent never holds two tools with the
|
|
302
|
+
// same name, since the runtime dispatches by name. Operator-only, so unlike
|
|
303
|
+
// attach_tool this check-then-upsert is not one Serializable transaction
|
|
304
|
+
// (the runner's load-time duplicate check still catches a race).
|
|
305
|
+
const clash = await findSameNamedAttachedTool(prisma, agent.id, tool);
|
|
306
|
+
if (clash) {
|
|
307
|
+
fail(`agent "${agentName}" already has a different tool named "${tool.name}" attached (${clash.id}); detach it first.`);
|
|
308
|
+
}
|
|
232
309
|
const patch = ToolCapabilitiesPatchSchema.safeParse({
|
|
233
310
|
allowedSecrets: values["allow-secret"],
|
|
234
311
|
allowedDatastorePrefixes: values["allow-datastore-prefix"],
|
|
@@ -238,6 +315,10 @@ async function toolAttach(args, detach) {
|
|
|
238
315
|
if (!patch.success) {
|
|
239
316
|
fail(`invalid tool capabilities: ${patch.error.issues.map((i) => i.message).join("; ")}`);
|
|
240
317
|
}
|
|
318
|
+
// The operator is trusted with the database, so it grants these
|
|
319
|
+
// capabilities in the agent owner's name: the runner honours them only
|
|
320
|
+
// with the owner's consent stamp (resource-sharing grants spec §3.4.2).
|
|
321
|
+
// An owner-less agent has no owner to stamp: its capabilities stay inert.
|
|
241
322
|
await prisma.agentTool.upsert({
|
|
242
323
|
where: { agentId_toolId: { agentId: agent.id, toolId: tool.id } },
|
|
243
324
|
create: {
|
|
@@ -247,8 +328,10 @@ async function toolAttach(args, detach) {
|
|
|
247
328
|
allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes ?? [],
|
|
248
329
|
allowedHosts: patch.data.allowedHosts ?? [],
|
|
249
330
|
allowedSharedDatastorePrefixes: patch.data.allowedSharedDatastorePrefixes ?? {},
|
|
331
|
+
capabilitiesGrantedById: agent.ownerId,
|
|
250
332
|
},
|
|
251
333
|
update: {
|
|
334
|
+
capabilitiesGrantedById: agent.ownerId,
|
|
252
335
|
...(patch.data.allowedSecrets !== undefined ? { allowedSecrets: patch.data.allowedSecrets } : {}),
|
|
253
336
|
...(patch.data.allowedDatastorePrefixes !== undefined
|
|
254
337
|
? { allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes }
|
|
@@ -273,16 +356,116 @@ async function toolList(args) {
|
|
|
273
356
|
return;
|
|
274
357
|
}
|
|
275
358
|
for (const a of attached)
|
|
276
|
-
console.log(
|
|
359
|
+
console.log(formatToolLine(a.tool));
|
|
277
360
|
return;
|
|
278
361
|
}
|
|
279
|
-
const tools = await prisma.tool.findMany({ orderBy: { name: "asc" } });
|
|
362
|
+
const tools = await prisma.tool.findMany({ orderBy: [{ name: "asc" }, { id: "asc" }] });
|
|
280
363
|
if (tools.length === 0) {
|
|
281
364
|
console.log("no tools registered.");
|
|
282
365
|
return;
|
|
283
366
|
}
|
|
284
367
|
for (const t of tools)
|
|
285
|
-
console.log(
|
|
368
|
+
console.log(formatToolLine(t));
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* The operator may change any tool, public ones included; only a row that
|
|
372
|
+
* vanished since it was resolved is refused. Thrown rather than fail()ed:
|
|
373
|
+
* it runs inside the transaction, which must roll back, not exit mid-way.
|
|
374
|
+
*/
|
|
375
|
+
function operatorAuthorize(ref) {
|
|
376
|
+
return (tool) => {
|
|
377
|
+
if (!tool)
|
|
378
|
+
throw new Error(`unknown tool "${ref}".`);
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* `wardby tool update <tool-name|tool-id>` -- the operator's counterpart of
|
|
383
|
+
* MCP update_tool, and the only way to change a public tool. Same
|
|
384
|
+
* validation (jsonSchema re-derived from --params) and the same attachment
|
|
385
|
+
* rule, judged against the tool's owner (core/tool-admin.ts).
|
|
386
|
+
*/
|
|
387
|
+
async function toolUpdate(args) {
|
|
388
|
+
const { values, positionals } = parseArgs({
|
|
389
|
+
args,
|
|
390
|
+
allowPositionals: true,
|
|
391
|
+
options: {
|
|
392
|
+
description: { type: "string" },
|
|
393
|
+
params: { type: "string" },
|
|
394
|
+
code: { type: "string" },
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
const [ref] = positionals;
|
|
398
|
+
if (!ref)
|
|
399
|
+
fail("tool update requires <tool-name|tool-id>.");
|
|
400
|
+
const readFile = (flag, path) => {
|
|
401
|
+
if (path === undefined)
|
|
402
|
+
return undefined;
|
|
403
|
+
try {
|
|
404
|
+
return readFileSync(path, "utf8");
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
fail(`could not read ${flag} file "${path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
const changes = {
|
|
411
|
+
description: values.description,
|
|
412
|
+
paramsZod: readFile("--params", values.params),
|
|
413
|
+
code: readFile("--code", values.code),
|
|
414
|
+
};
|
|
415
|
+
if (!hasToolChanges(changes)) {
|
|
416
|
+
fail("tool update requires at least one of --description, --params <file>, --code <file>.");
|
|
417
|
+
}
|
|
418
|
+
const resolved = await resolveToolRef(prisma, ref);
|
|
419
|
+
if (!resolved.ok)
|
|
420
|
+
fail(resolved.error);
|
|
421
|
+
const prepared = await prepareToolUpdate(changes);
|
|
422
|
+
if (!prepared.ok)
|
|
423
|
+
fail(`invalid --params schema: ${prepared.errorMessage}`);
|
|
424
|
+
try {
|
|
425
|
+
await updateToolGuarded(prisma, resolved.tool.id, prepared.data, operatorAuthorize(ref));
|
|
426
|
+
}
|
|
427
|
+
catch (err) {
|
|
428
|
+
if (err instanceof ToolAttachedError) {
|
|
429
|
+
fail(`tool "${ref}" is attached to agents with a different owner: ${formatAttachedAgents(err.agents)}. Detach it from those agents first, or create a new tool.`);
|
|
430
|
+
}
|
|
431
|
+
throw err;
|
|
432
|
+
}
|
|
433
|
+
console.log(`updated tool ${resolved.tool.id}.`);
|
|
434
|
+
}
|
|
435
|
+
/** `wardby tool delete <tool-name|tool-id> [--detach]` -- MCP delete_tool's rules, public tools included. */
|
|
436
|
+
async function toolDelete(args) {
|
|
437
|
+
const { values, positionals } = parseArgs({
|
|
438
|
+
args,
|
|
439
|
+
allowPositionals: true,
|
|
440
|
+
options: { detach: { type: "boolean" } },
|
|
441
|
+
});
|
|
442
|
+
const [ref] = positionals;
|
|
443
|
+
if (!ref)
|
|
444
|
+
fail("tool delete requires <tool-name|tool-id>.");
|
|
445
|
+
const resolved = await resolveToolRef(prisma, ref);
|
|
446
|
+
if (!resolved.ok)
|
|
447
|
+
fail(resolved.error);
|
|
448
|
+
let detachedFrom;
|
|
449
|
+
try {
|
|
450
|
+
detachedFrom = await deleteToolGuarded(prisma, resolved.tool.id, {
|
|
451
|
+
detach: values.detach ?? false,
|
|
452
|
+
authorize: operatorAuthorize(ref),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
if (!(err instanceof ToolAttachedError))
|
|
457
|
+
throw err;
|
|
458
|
+
if (err.reason === "other_owners") {
|
|
459
|
+
fail(`tool "${ref}" is attached to agents with a different owner: ${formatAttachedAgents(err.agents)}. Detach it from those agents first (wardby tool detach).`);
|
|
460
|
+
}
|
|
461
|
+
if (err.reason === "needs_detach") {
|
|
462
|
+
fail(`tool "${ref}" is still attached to ${formatAttachedAgents(err.agents)}. Pass --detach to detach it and delete it.`);
|
|
463
|
+
}
|
|
464
|
+
fail(`tool "${ref}" is still attached to an agent; nothing was deleted.`);
|
|
465
|
+
}
|
|
466
|
+
console.log(detachedFrom.length > 0
|
|
467
|
+
? `deleted tool ${resolved.tool.id} (detached from ${detachedFrom.join(", ")}).`
|
|
468
|
+
: `deleted tool ${resolved.tool.id}.`);
|
|
286
469
|
}
|
|
287
470
|
async function agentSchedule(args) {
|
|
288
471
|
const [name, ...rest] = args;
|
|
@@ -347,20 +530,36 @@ async function run(name) {
|
|
|
347
530
|
if (agent.kind === "coding") {
|
|
348
531
|
fail("Coding agents are MCP-first: use trigger_agent so task input and ownership are recorded safely.");
|
|
349
532
|
}
|
|
533
|
+
let modelCatalog;
|
|
534
|
+
try {
|
|
535
|
+
modelCatalog = await startModelCatalog(prisma);
|
|
536
|
+
}
|
|
537
|
+
catch (err) {
|
|
538
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
539
|
+
}
|
|
350
540
|
const llm = buildLlmProvider();
|
|
351
541
|
const engine = buildEngine();
|
|
352
542
|
const secrets = buildSecrets();
|
|
353
543
|
const datastore = buildDatastore(secrets);
|
|
354
544
|
const memory = buildMemory();
|
|
355
545
|
let run;
|
|
546
|
+
// Ctrl-C (or a SIGTERM) records the run as cancelled rather than leaving a
|
|
547
|
+
// `running` row behind.
|
|
548
|
+
let removeSignalHandlers;
|
|
356
549
|
try {
|
|
357
|
-
run = await runAgent(name, { llm, engine, datastore, secrets, memory }, prisma, (delta) => {
|
|
550
|
+
run = await runAgent(name, { llm, engine, datastore, secrets, memory, reviewHosts: buildReviewHosts(), issueTrackers: buildIssueTrackers() }, prisma, (delta) => {
|
|
358
551
|
process.stdout.write(delta);
|
|
552
|
+
}, (created) => {
|
|
553
|
+
removeSignalHandlers = cancelRunOnSignal(prisma, created.id);
|
|
359
554
|
});
|
|
360
555
|
}
|
|
361
556
|
catch (err) {
|
|
362
557
|
fail(err instanceof Error ? err.message : String(err));
|
|
363
558
|
}
|
|
559
|
+
finally {
|
|
560
|
+
removeSignalHandlers?.();
|
|
561
|
+
modelCatalog.close();
|
|
562
|
+
}
|
|
364
563
|
process.stdout.write("\n");
|
|
365
564
|
const completedAgent = await prisma.agent.findUnique({ where: { id: run.agentId } });
|
|
366
565
|
const budgetUsd = completedAgent ? Number(completedAgent.budgetUsd) : NaN;
|
|
@@ -486,22 +685,35 @@ async function scheduler(args) {
|
|
|
486
685
|
// Parsed before anything starts, so a malformed CODING_MAX_CONCURRENT or
|
|
487
686
|
// CODING_QUEUE_TIMEOUT_SEC fails fast.
|
|
488
687
|
const concurrency = loadCodingConcurrencyConfig();
|
|
688
|
+
let modelCatalog;
|
|
689
|
+
try {
|
|
690
|
+
modelCatalog = await startModelCatalog(prisma);
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
694
|
+
}
|
|
489
695
|
const config = loadProviderConfig();
|
|
490
696
|
const llm = buildLlmProvider();
|
|
491
697
|
const engine = buildEngine();
|
|
492
698
|
const secrets = buildSecrets();
|
|
493
699
|
const datastore = buildDatastore(secrets);
|
|
494
700
|
const memory = buildMemory();
|
|
495
|
-
const
|
|
496
|
-
const
|
|
701
|
+
const reviewHosts = buildReviewHosts();
|
|
702
|
+
const issueTrackers = buildIssueTrackers();
|
|
703
|
+
// One repository-access gate (and cache) for native repo_* calls and coding runs.
|
|
704
|
+
const repoAccess = createRepoAccessGate({ db: prisma, hosts: reviewHosts });
|
|
705
|
+
const nativeExecutor = buildExecutor(config, { llm, engine, datastore, secrets, memory, reviewHosts, issueTrackers, repoAccess }, prisma);
|
|
706
|
+
const executor = buildConfiguredExecutor({ native: nativeExecutor, db: prisma, providerConfig: config, repoAccess });
|
|
497
707
|
await executor.launch?.();
|
|
498
|
-
const reconciler = startReconciler({ db: prisma, executor });
|
|
708
|
+
const reconciler = startReconciler({ db: prisma, executor, reviewHosts, issueTrackers });
|
|
709
|
+
const selfDefects = { db: prisma, issueTrackers };
|
|
499
710
|
const sched = startScheduler({
|
|
500
711
|
executor,
|
|
501
712
|
db: prisma,
|
|
502
713
|
scope,
|
|
714
|
+
selfDefects,
|
|
503
715
|
onLeaderTick: async () => {
|
|
504
|
-
await drainCodingQueue({ db: prisma, executor, ...concurrency });
|
|
716
|
+
await drainCodingQueue({ db: prisma, executor, ...concurrency, selfDefects });
|
|
505
717
|
},
|
|
506
718
|
});
|
|
507
719
|
console.log(`wardby scheduler started (scope "${scope}"). Press Ctrl+C to stop.`);
|
|
@@ -512,7 +724,10 @@ async function scheduler(args) {
|
|
|
512
724
|
reconciler.stop();
|
|
513
725
|
void Promise.resolve(executor.close?.())
|
|
514
726
|
.catch((err) => cliLog.warn({ err }, "executor close failed during scheduler shutdown"))
|
|
515
|
-
.finally(
|
|
727
|
+
.finally(() => {
|
|
728
|
+
modelCatalog.close();
|
|
729
|
+
resolve();
|
|
730
|
+
});
|
|
516
731
|
};
|
|
517
732
|
process.once("SIGINT", shutdown);
|
|
518
733
|
process.once("SIGTERM", shutdown);
|
|
@@ -560,19 +775,46 @@ async function serve(args) {
|
|
|
560
775
|
}
|
|
561
776
|
async function importCommand(rest) {
|
|
562
777
|
const opts = parseImportArgs(rest);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
778
|
+
// runImport builds an LLM provider and classifies each agent's model as
|
|
779
|
+
// routable/unroutable from currentModelCatalog() — without a store
|
|
780
|
+
// started here first, that falls back to the shipped catalog, so an
|
|
781
|
+
// admin-disabled model would be (wrongly) imported as routable and a
|
|
782
|
+
// DB-only custom model would be (wrongly) imported as disabled.
|
|
783
|
+
let modelCatalog;
|
|
784
|
+
try {
|
|
785
|
+
modelCatalog = await startModelCatalog(prisma);
|
|
786
|
+
}
|
|
787
|
+
catch (err) {
|
|
788
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
const { report, result } = await runImport({ ...opts, db: prisma, env: process.env });
|
|
792
|
+
console.log(report);
|
|
793
|
+
if (result) {
|
|
794
|
+
for (const w of result.webhookSecrets)
|
|
795
|
+
console.log(`webhook secret (${w.agentName}): ${w.secret}`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
finally {
|
|
799
|
+
modelCatalog.close();
|
|
568
800
|
}
|
|
569
801
|
}
|
|
570
802
|
async function main() {
|
|
571
803
|
const [command, ...rest] = process.argv.slice(2);
|
|
572
804
|
try {
|
|
573
|
-
if (command === "auth") {
|
|
805
|
+
if (command === "auth" && rest[0] === "host-account") {
|
|
806
|
+
// Both auth modes: keyed on Principal, not the self-hosted AuthUser.
|
|
807
|
+
await hostAccountCommand(rest.slice(1), prisma);
|
|
808
|
+
}
|
|
809
|
+
else if (command === "grants") {
|
|
810
|
+
await grantsCommand(rest, prisma);
|
|
811
|
+
}
|
|
812
|
+
else if (command === "auth") {
|
|
574
813
|
await authCommand(rest, prisma, process.env.AUTH_CREDENTIAL_HASH_KEY ?? "");
|
|
575
814
|
}
|
|
815
|
+
else if (command === "help") {
|
|
816
|
+
await helpCommand(rest);
|
|
817
|
+
}
|
|
576
818
|
else if (command === "agent" && rest[0] === "create") {
|
|
577
819
|
await agentCreate(rest.slice(1));
|
|
578
820
|
}
|
|
@@ -591,6 +833,12 @@ async function main() {
|
|
|
591
833
|
else if (command === "tool" && rest[0] === "detach") {
|
|
592
834
|
await toolAttach(rest.slice(1), true);
|
|
593
835
|
}
|
|
836
|
+
else if (command === "tool" && rest[0] === "update") {
|
|
837
|
+
await toolUpdate(rest.slice(1));
|
|
838
|
+
}
|
|
839
|
+
else if (command === "tool" && rest[0] === "delete") {
|
|
840
|
+
await toolDelete(rest.slice(1));
|
|
841
|
+
}
|
|
594
842
|
else if (command === "tool" && rest[0] === "list") {
|
|
595
843
|
await toolList(rest.slice(1));
|
|
596
844
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding workspace has no git metadata, so the worker cannot learn which
|
|
3
|
+
* commit it is looking at. The executor appends it to the task when it writes
|
|
4
|
+
* the worker input (after cloning). Never grows a task past the limit.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withBaseCommit(task: string, baseCommit: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { byteLength, MAX_CODING_TASK_BYTES } from "./protocol.js";
|
|
2
|
+
/**
|
|
3
|
+
* The coding workspace has no git metadata, so the worker cannot learn which
|
|
4
|
+
* commit it is looking at. The executor appends it to the task when it writes
|
|
5
|
+
* the worker input (after cloning). Never grows a task past the limit.
|
|
6
|
+
*/
|
|
7
|
+
export function withBaseCommit(task, baseCommit) {
|
|
8
|
+
if (!/^[0-9a-f]{40}$/.test(baseCommit))
|
|
9
|
+
return task;
|
|
10
|
+
const withLine = `${task}\n\nBase commit: ${baseCommit} (the commit this workspace was checked out at; the workspace has no git metadata).`;
|
|
11
|
+
return byteLength(withLine) <= MAX_CODING_TASK_BYTES ? withLine : task;
|
|
12
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paths a coding run's workspace never sends back: dependency and cache folders
|
|
3
|
+
* (matched by name at any depth) plus per-agent repository-relative paths. The
|
|
4
|
+
* same set is rendered three ways so the Kubernetes keeper's tar, the Docker
|
|
5
|
+
* staging prune, and Git staging all agree on what "excluded" means.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BUILTIN_COLLECT_EXCLUDE_NAMES: readonly ["node_modules", ".venv", "venv", "__pycache__", ".pytest_cache", ".ruff_cache", ".mypy_cache", ".tox", ".vite", ".cache"];
|
|
8
|
+
export declare const MAX_COLLECT_EXCLUDE_PATHS = 64;
|
|
9
|
+
export interface CollectExclusions {
|
|
10
|
+
names: readonly string[];
|
|
11
|
+
paths: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function validateCollectExcludePath(value: string): string;
|
|
14
|
+
export declare function collectExclusions(paths: readonly string[]): CollectExclusions;
|
|
15
|
+
/** For values read back from the database or a job record, which are untrusted shapes. */
|
|
16
|
+
export declare function normalizeCollectExclusions(value: unknown): CollectExclusions;
|
|
17
|
+
export declare function isCollectExcluded(relativePath: string, exclusions: CollectExclusions): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* GNU tar options for `tar -C <workspace> -cf - .`, whose member names start with
|
|
20
|
+
* "./". Names are unanchored (they match any path component); paths are anchored
|
|
21
|
+
* to the archive root. An excluded directory's contents are skipped with it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function tarExcludeArgs(exclusions: CollectExclusions): string[];
|
|
24
|
+
/** Git exclude pathspecs, appended after ":/" when staging. */
|
|
25
|
+
export declare function gitExcludePathspecs(exclusions: CollectExclusions): string[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paths a coding run's workspace never sends back: dependency and cache folders
|
|
3
|
+
* (matched by name at any depth) plus per-agent repository-relative paths. The
|
|
4
|
+
* same set is rendered three ways so the Kubernetes keeper's tar, the Docker
|
|
5
|
+
* staging prune, and Git staging all agree on what "excluded" means.
|
|
6
|
+
*/
|
|
7
|
+
export const BUILTIN_COLLECT_EXCLUDE_NAMES = [
|
|
8
|
+
"node_modules",
|
|
9
|
+
".venv",
|
|
10
|
+
"venv",
|
|
11
|
+
"__pycache__",
|
|
12
|
+
".pytest_cache",
|
|
13
|
+
".ruff_cache",
|
|
14
|
+
".mypy_cache",
|
|
15
|
+
".tox",
|
|
16
|
+
".vite",
|
|
17
|
+
".cache",
|
|
18
|
+
];
|
|
19
|
+
export const MAX_COLLECT_EXCLUDE_PATHS = 64;
|
|
20
|
+
const MAX_PATH_BYTES = 512;
|
|
21
|
+
const CONTROL = /[\u0000-\u001F\u007F]/;
|
|
22
|
+
const GLOB = /[*?[\]]/;
|
|
23
|
+
export function validateCollectExcludePath(value) {
|
|
24
|
+
const path = value.trim();
|
|
25
|
+
const valid = path.length > 0 &&
|
|
26
|
+
Buffer.byteLength(path, "utf8") <= MAX_PATH_BYTES &&
|
|
27
|
+
!CONTROL.test(path) &&
|
|
28
|
+
!GLOB.test(path) &&
|
|
29
|
+
!path.includes("\\") &&
|
|
30
|
+
!path.startsWith("/") &&
|
|
31
|
+
!path.startsWith("./") &&
|
|
32
|
+
!path.split("/").some((part) => part === "" || part === "." || part === "..");
|
|
33
|
+
if (!valid)
|
|
34
|
+
throw new Error("collect_exclude_path_invalid");
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
export function collectExclusions(paths) {
|
|
38
|
+
if (paths.length > MAX_COLLECT_EXCLUDE_PATHS)
|
|
39
|
+
throw new Error("collect_exclude_path_invalid");
|
|
40
|
+
return { names: [...BUILTIN_COLLECT_EXCLUDE_NAMES], paths: [...new Set(paths.map(validateCollectExcludePath))] };
|
|
41
|
+
}
|
|
42
|
+
/** For values read back from the database or a job record, which are untrusted shapes. */
|
|
43
|
+
export function normalizeCollectExclusions(value) {
|
|
44
|
+
if (value === undefined || value === null)
|
|
45
|
+
return collectExclusions([]);
|
|
46
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
|
|
47
|
+
throw new Error("collect_exclude_invalid");
|
|
48
|
+
}
|
|
49
|
+
return collectExclusions(value);
|
|
50
|
+
}
|
|
51
|
+
export function isCollectExcluded(relativePath, exclusions) {
|
|
52
|
+
const segments = relativePath.split("/");
|
|
53
|
+
if (segments.some((segment) => exclusions.names.includes(segment)))
|
|
54
|
+
return true;
|
|
55
|
+
return exclusions.paths.some((path) => relativePath === path || relativePath.startsWith(`${path}/`));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* GNU tar options for `tar -C <workspace> -cf - .`, whose member names start with
|
|
59
|
+
* "./". Names are unanchored (they match any path component); paths are anchored
|
|
60
|
+
* to the archive root. An excluded directory's contents are skipped with it.
|
|
61
|
+
*/
|
|
62
|
+
export function tarExcludeArgs(exclusions) {
|
|
63
|
+
return [
|
|
64
|
+
"--no-anchored",
|
|
65
|
+
...exclusions.names.map((name) => `--exclude=${name}`),
|
|
66
|
+
"--anchored",
|
|
67
|
+
...exclusions.paths.map((path) => `--exclude=./${path}`),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
/** Git exclude pathspecs, appended after ":/" when staging. */
|
|
71
|
+
export function gitExcludePathspecs(exclusions) {
|
|
72
|
+
return [
|
|
73
|
+
...exclusions.names.flatMap((name) => [`:(exclude,glob)**/${name}`, `:(exclude,glob)**/${name}/**`]),
|
|
74
|
+
...exclusions.paths.map((path) => `:(exclude,literal)${path}`),
|
|
75
|
+
];
|
|
76
|
+
}
|