@wardby/cli 0.7.0 → 0.9.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 +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +64 -0
- package/dist/claude-plugin/command.js +379 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +71 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +3 -9
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +39 -11
- package/dist/core/engine-native.d.ts +2 -0
- package/dist/core/engine-native.js +13 -0
- package/dist/core/error-code.js +2 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/listen-address.d.ts +10 -0
- package/dist/core/listen-address.js +14 -0
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/power-idle.d.ts +8 -2
- package/dist/core/power-idle.js +17 -3
- package/dist/core/power.d.ts +5 -0
- package/dist/core/power.js +5 -0
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/retention-days.d.ts +5 -0
- package/dist/core/retention-days.js +14 -0
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/run-steps/config.d.ts +25 -0
- package/dist/core/run-steps/config.js +40 -0
- package/dist/core/run-steps/control-chars.d.ts +16 -0
- package/dist/core/run-steps/control-chars.js +26 -0
- package/dist/core/run-steps/detail.d.ts +32 -0
- package/dist/core/run-steps/detail.js +240 -0
- package/dist/core/run-steps/extract.d.ts +52 -0
- package/dist/core/run-steps/extract.js +467 -0
- package/dist/core/run-steps/ingress.d.ts +51 -0
- package/dist/core/run-steps/ingress.js +160 -0
- package/dist/core/run-steps/last-step.d.ts +15 -0
- package/dist/core/run-steps/last-step.js +35 -0
- package/dist/core/run-steps/native.d.ts +48 -0
- package/dist/core/run-steps/native.js +164 -0
- package/dist/core/run-steps/seen.d.ts +16 -0
- package/dist/core/run-steps/seen.js +35 -0
- package/dist/core/run-steps/summarize.d.ts +57 -0
- package/dist/core/run-steps/summarize.js +304 -0
- package/dist/core/run-steps/sweep.d.ts +21 -0
- package/dist/core/run-steps/sweep.js +41 -0
- package/dist/core/run-steps/types.d.ts +89 -0
- package/dist/core/run-steps/types.js +39 -0
- package/dist/core/run-steps/wire.d.ts +35 -0
- package/dist/core/run-steps/wire.js +47 -0
- package/dist/core/run-steps/writer.d.ts +49 -0
- package/dist/core/run-steps/writer.js +279 -0
- package/dist/core/runner.d.ts +70 -16
- package/dist/core/runner.js +296 -74
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +39 -0
- package/dist/generated/prisma/client.d.ts +39 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +5 -0
- package/dist/generated/prisma/internal/class.d.ts +77 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
- package/dist/generated/prisma/models/Agent.d.ts +372 -1
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +503 -1
- package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
- package/dist/generated/prisma/models/RunStep.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +7 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +812 -52
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/tools/agents.js +135 -20
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/power.js +2 -2
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/run-access.d.ts +7 -0
- package/dist/mcp/tools/run-access.js +14 -0
- package/dist/mcp/tools/run-steps.d.ts +16 -0
- package/dist/mcp/tools/run-steps.js +85 -0
- package/dist/mcp/tools/runs.js +186 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +18 -1
- package/dist/native-worker/gateway-server.js +38 -9
- package/dist/native-worker/gateway.d.ts +5 -0
- package/dist/native-worker/gateway.js +13 -1
- package/dist/native-worker/http-server.d.ts +6 -4
- package/dist/native-worker/http-server.js +53 -8
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +16 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
- package/dist/providers/coding-proxy/proxy.d.ts +33 -1
- package/dist/providers/coding-proxy/proxy.js +266 -75
- package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
- package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
- package/dist/providers/coding-proxy/runtime.d.ts +13 -0
- package/dist/providers/coding-proxy/runtime.js +87 -4
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +18 -0
- package/dist/providers/engine/types.d.ts +26 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +47 -1
- package/dist/providers/executor/container.js +84 -8
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +13 -0
- package/dist/providers/jobs/kubernetes.js +28 -5
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/serve.js +26 -18
- package/dist/viewer/api-schema.d.ts +66 -0
- package/dist/viewer/api-schema.js +31 -0
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -1
- package/dist/viewer/run-detail.d.ts +2 -0
- package/dist/viewer/run-detail.js +26 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +208 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +94 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +94 -7
- package/docs/debugging-agent-runs.md +354 -0
- package/docs/getting-started-gke.md +33 -11
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +263 -0
- package/docs/native-sandbox.md +31 -9
- package/docs/security-deployment.md +161 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +27 -12
- package/docs/viewer-api.openapi.yml +78 -2
- package/help/admin-viewer.md +5 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +63 -0
- package/help/deploy-gke.md +5 -0
- package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/low-power-mode.md +19 -8
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +9 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +7 -0
- package/help/run-steps.md +140 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
- package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
- package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
- package/prisma/schema.prisma +211 -14
- package/scripts/git-askpass.sh +1 -1
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `wardby claude-plugin install | uninstall | status`: sets up the wardby-runs
|
|
3
|
+
* Claude Code plugin (plugins/claude-code) on this machine. Install adds the
|
|
4
|
+
* plugin from a marketplace (wardby/wardby by default, or an operator's own),
|
|
5
|
+
* can register the wardby MCP server, and offers the allow rules auto mode
|
|
6
|
+
* needs for the pane's read-only tools. Uninstall removes the plugin and only
|
|
7
|
+
* the settings install added. Status reports all three.
|
|
8
|
+
*
|
|
9
|
+
* Runs without a database or any server configuration. It prints the commands
|
|
10
|
+
* it runs and their outcome, never a token: the MCP server's sign-in is Claude
|
|
11
|
+
* Code's own, at first use.
|
|
12
|
+
*/
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { createInterface } from "node:readline";
|
|
18
|
+
import { parseArgs } from "node:util";
|
|
19
|
+
export const PLUGIN = "wardby-runs";
|
|
20
|
+
export const DEFAULT_MARKETPLACE = "wardby/wardby";
|
|
21
|
+
export const MCP_SERVER = "wardby";
|
|
22
|
+
/**
|
|
23
|
+
* The pane's read-only tools on a server: what auto mode needs allowed. stop_run is never added: it
|
|
24
|
+
* changes a run. get_run_steps backs Recent steps and Steps; against an older server the rule is harmless.
|
|
25
|
+
*/
|
|
26
|
+
export function paneTools(server) {
|
|
27
|
+
return ["list_runs", "list_agents", "get_run", "get_run_transcript", "get_run_steps"].map((tool) => `mcp__${server}__${tool}`);
|
|
28
|
+
}
|
|
29
|
+
export const PANE_TOOLS = paneTools(MCP_SERVER);
|
|
30
|
+
/** How long one `claude` call may take: `claude mcp get` can check its server, which shouldn't stall doctor. */
|
|
31
|
+
const CLAUDE_TIMEOUT_MS = 20_000;
|
|
32
|
+
export const CLAUDE_PLUGIN_USAGE = `usage: wardby claude-plugin install [--marketplace <owner/repo|path>] [--server-url <url>]
|
|
33
|
+
[--allow-tools | --no-allow-tools] [--fullscreen] [--yes]
|
|
34
|
+
wardby claude-plugin uninstall
|
|
35
|
+
wardby claude-plugin status
|
|
36
|
+
|
|
37
|
+
Set up the wardby-runs Claude Code plugin: a pane listing your wardby runs.
|
|
38
|
+
|
|
39
|
+
--server-name <name> The MCP server the pane uses, as /mcp lists it (default ${MCP_SERVER}); its
|
|
40
|
+
allow rules follow the name
|
|
41
|
+
--marketplace <src> Where to install it from (default ${DEFAULT_MARKETPLACE}; an operator's own
|
|
42
|
+
marketplace repository or a local folder works too)
|
|
43
|
+
--server-url <url> Register the wardby MCP server at this URL first (for example
|
|
44
|
+
https://wardby.example.com/mcp) when Claude Code has none named ${MCP_SERVER}
|
|
45
|
+
--allow-tools Allow the pane's read-only wardby tools for auto mode without asking
|
|
46
|
+
--no-allow-tools Leave permission rules alone
|
|
47
|
+
--fullscreen Also switch Claude Code's terminal to its fullscreen view, where the pane
|
|
48
|
+
is a side bar that takes clicks (applies to every session)
|
|
49
|
+
--yes, -y Answer yes to every question`;
|
|
50
|
+
/** The real machine: spawns `claude`, asks on the terminal, uses CLAUDE_CONFIG_DIR or ~/.claude. */
|
|
51
|
+
export function claudePluginDeps() {
|
|
52
|
+
return {
|
|
53
|
+
run: (command, args) => {
|
|
54
|
+
const result = spawnSync(command, args, { encoding: "utf8", timeout: CLAUDE_TIMEOUT_MS });
|
|
55
|
+
return {
|
|
56
|
+
status: result.error ? 127 : (result.status ?? 1),
|
|
57
|
+
stdout: result.stdout ?? "",
|
|
58
|
+
stderr: result.stderr ?? "",
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
confirm: async (question) => {
|
|
62
|
+
if (!process.stdin.isTTY)
|
|
63
|
+
return false;
|
|
64
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
65
|
+
try {
|
|
66
|
+
const answer = await new Promise((resolve) => rl.question(`${question} [y/N] `, resolve));
|
|
67
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
rl.close();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
claudeConfigDir: process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude"),
|
|
74
|
+
wardbyDir: join(homedir(), ".wardby"),
|
|
75
|
+
out: (line) => process.stdout.write(`${line}\n`),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const recordPath = (deps) => join(deps.wardbyDir, "claude-plugin.json");
|
|
79
|
+
/** A settings file as an object: {} when absent; refuses a file that isn't a JSON object rather than overwrite it. */
|
|
80
|
+
function readSettings(path) {
|
|
81
|
+
if (!existsSync(path))
|
|
82
|
+
return {};
|
|
83
|
+
const text = readFileSync(path, "utf8");
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(text);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new Error(`${path} isn't valid JSON; fix it, then run this again. Nothing was changed.`);
|
|
90
|
+
}
|
|
91
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
92
|
+
throw new Error(`${path} isn't a JSON object; nothing was changed.`);
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
/** Writes a settings file whole, through a temporary file, so a failure never leaves half a file. */
|
|
97
|
+
function writeSettings(path, settings) {
|
|
98
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
99
|
+
const temp = `${path}.wardby-${process.pid}.tmp`;
|
|
100
|
+
writeFileSync(temp, `${JSON.stringify(settings, null, 2)}\n`, { mode: 0o600 });
|
|
101
|
+
renameSync(temp, path);
|
|
102
|
+
}
|
|
103
|
+
function allowList(settings) {
|
|
104
|
+
const permissions = settings.permissions;
|
|
105
|
+
if (typeof permissions !== "object" || permissions === null)
|
|
106
|
+
return [];
|
|
107
|
+
const allow = permissions.allow;
|
|
108
|
+
return Array.isArray(allow) ? allow.filter((rule) => typeof rule === "string") : [];
|
|
109
|
+
}
|
|
110
|
+
function withAllowList(settings, allow) {
|
|
111
|
+
const permissions = typeof settings.permissions === "object" && settings.permissions !== null
|
|
112
|
+
? settings.permissions
|
|
113
|
+
: {};
|
|
114
|
+
return { ...settings, permissions: { ...permissions, allow } };
|
|
115
|
+
}
|
|
116
|
+
function readRecord(deps) {
|
|
117
|
+
try {
|
|
118
|
+
const record = JSON.parse(readFileSync(recordPath(deps), "utf8"));
|
|
119
|
+
return typeof record.settingsPath === "string" && Array.isArray(record.rules) ? record : null;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function claudeAvailable(deps) {
|
|
126
|
+
return deps.run("claude", ["--version"]).status === 0;
|
|
127
|
+
}
|
|
128
|
+
function hasMcpServer(deps, server = MCP_SERVER) {
|
|
129
|
+
return deps.run("claude", ["mcp", "get", server]).status === 0;
|
|
130
|
+
}
|
|
131
|
+
function pluginInstalled(deps) {
|
|
132
|
+
const list = deps.run("claude", ["plugin", "list"]);
|
|
133
|
+
return list.status === 0 && new RegExp(`\\b${PLUGIN}@`).test(list.stdout);
|
|
134
|
+
}
|
|
135
|
+
/** The first line of a failed command's output, for the message. */
|
|
136
|
+
const firstLine = (result) => (result.stderr || result.stdout).trim().split("\n")[0]?.slice(0, 300) ?? "";
|
|
137
|
+
export async function installPlugin(options, deps) {
|
|
138
|
+
if (!claudeAvailable(deps)) {
|
|
139
|
+
deps.out("Claude Code (claude) wasn't found on PATH. Install it, then run this again.");
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
const server = options.serverName ?? MCP_SERVER;
|
|
143
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(server)) {
|
|
144
|
+
deps.out(`--server-name "${server}" isn't a server name (letters, digits, - and _).`);
|
|
145
|
+
return 1;
|
|
146
|
+
}
|
|
147
|
+
const tools = paneTools(server);
|
|
148
|
+
// Read the settings file before changing anything, so a broken one stops the install cleanly.
|
|
149
|
+
const settingsPath = join(deps.claudeConfigDir, "settings.json");
|
|
150
|
+
let settings;
|
|
151
|
+
try {
|
|
152
|
+
settings = readSettings(settingsPath);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
deps.out(error instanceof Error ? error.message : String(error));
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
// 1. The MCP server the pane talks to.
|
|
159
|
+
if (hasMcpServer(deps, server)) {
|
|
160
|
+
deps.out(`✓ Claude Code has an MCP server named ${server}.`);
|
|
161
|
+
}
|
|
162
|
+
else if (options.serverUrl) {
|
|
163
|
+
let url;
|
|
164
|
+
try {
|
|
165
|
+
url = new URL(options.serverUrl);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
deps.out(`--server-url "${options.serverUrl}" isn't a URL.`);
|
|
169
|
+
return 1;
|
|
170
|
+
}
|
|
171
|
+
if (url.protocol !== "https:" &&
|
|
172
|
+
!(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
|
|
173
|
+
deps.out("--server-url must be https (or http on localhost).");
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
const added = deps.run("claude", ["mcp", "add", "--transport", "http", "--scope", "user", server, url.href]);
|
|
177
|
+
if (added.status !== 0) {
|
|
178
|
+
deps.out(`Adding the ${server} MCP server failed: ${firstLine(added)}`);
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
deps.out(`✓ Added the ${server} MCP server (${url.href}). Sign in with /mcp in Claude Code.`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
deps.out(`! Claude Code has no MCP server named ${server}. The pane needs one: run this again with ` +
|
|
185
|
+
"--server-url <your wardby URL>/mcp, or run `wardby quickstart`.");
|
|
186
|
+
}
|
|
187
|
+
// 2. The plugin itself.
|
|
188
|
+
const installed = deps.run("claude", ["plugin", "install", PLUGIN, "--marketplace", options.marketplace]);
|
|
189
|
+
if (installed.status !== 0) {
|
|
190
|
+
deps.out(`Installing the ${PLUGIN} plugin from ${options.marketplace} failed: ${firstLine(installed)}`);
|
|
191
|
+
return 1;
|
|
192
|
+
}
|
|
193
|
+
deps.out(`✓ Installed the ${PLUGIN} plugin from ${options.marketplace}.`);
|
|
194
|
+
// 3. Allow rules for auto mode, and the fullscreen view: each only when asked for or agreed to.
|
|
195
|
+
const record = readRecord(deps) ?? { settingsPath, rules: [], tui: false };
|
|
196
|
+
let changed = false;
|
|
197
|
+
const allowTools = options.allowTools ??
|
|
198
|
+
(options.yes ||
|
|
199
|
+
(await deps.confirm(`Allow the pane's read-only wardby tools (${tools.map((t) => t.replace(`mcp__${server}__`, "")).join(", ")}) ` +
|
|
200
|
+
`in ${settingsPath}? Auto mode refuses the pane's calls without them.`)));
|
|
201
|
+
if (allowTools) {
|
|
202
|
+
const current = allowList(settings);
|
|
203
|
+
const missing = tools.filter((rule) => !current.includes(rule));
|
|
204
|
+
if (missing.length > 0) {
|
|
205
|
+
settings = withAllowList(settings, [...current, ...missing]);
|
|
206
|
+
record.rules = [...new Set([...record.rules, ...missing])];
|
|
207
|
+
changed = true;
|
|
208
|
+
deps.out(`✓ Allowed ${missing.join(", ")}.`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
deps.out("✓ The pane's tools are already allowed.");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
deps.out(`Left permission rules alone. Under auto mode, allow ${tools.join(", ")} in /permissions.`);
|
|
216
|
+
}
|
|
217
|
+
if (options.fullscreen && settings.tui !== "fullscreen") {
|
|
218
|
+
settings = { ...settings, tui: "fullscreen" };
|
|
219
|
+
record.tui = true;
|
|
220
|
+
changed = true;
|
|
221
|
+
deps.out("✓ Switched Claude Code to its fullscreen view (restart Claude Code to see it).");
|
|
222
|
+
}
|
|
223
|
+
if (changed) {
|
|
224
|
+
writeSettings(settingsPath, settings);
|
|
225
|
+
mkdirSync(deps.wardbyDir, { recursive: true });
|
|
226
|
+
writeFileSync(recordPath(deps), `${JSON.stringify({ ...record, settingsPath }, null, 2)}\n`, { mode: 0o600 });
|
|
227
|
+
}
|
|
228
|
+
deps.out("Open the pane in Claude Code with /wardby-runs (in a new session, or after /reload-plugins).");
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
export async function uninstallPlugin(deps) {
|
|
232
|
+
if (!claudeAvailable(deps)) {
|
|
233
|
+
deps.out("Claude Code (claude) wasn't found on PATH.");
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
const removed = deps.run("claude", ["plugin", "uninstall", PLUGIN]);
|
|
237
|
+
if (removed.status === 0)
|
|
238
|
+
deps.out(`✓ Uninstalled the ${PLUGIN} plugin.`);
|
|
239
|
+
else
|
|
240
|
+
deps.out(`The ${PLUGIN} plugin wasn't uninstalled: ${firstLine(removed)}`);
|
|
241
|
+
// Only what install added: rules it added, and the fullscreen view if it switched it on. The file is
|
|
242
|
+
// written only when one of those is still there; a broken file is reported, not rewritten.
|
|
243
|
+
const record = readRecord(deps);
|
|
244
|
+
if (record && existsSync(record.settingsPath)) {
|
|
245
|
+
let settings;
|
|
246
|
+
try {
|
|
247
|
+
settings = readSettings(record.settingsPath);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
deps.out(`${error instanceof Error ? error.message : String(error)} Remove ${record.rules.join(", ")} yourself.`);
|
|
251
|
+
return 1;
|
|
252
|
+
}
|
|
253
|
+
const current = allowList(settings);
|
|
254
|
+
const kept = current.filter((rule) => !record.rules.includes(rule));
|
|
255
|
+
let changed = false;
|
|
256
|
+
if (kept.length !== current.length) {
|
|
257
|
+
settings = withAllowList(settings, kept);
|
|
258
|
+
changed = true;
|
|
259
|
+
deps.out(`✓ Removed the rules install added: ${current.filter((rule) => !kept.includes(rule)).join(", ")}.`);
|
|
260
|
+
}
|
|
261
|
+
if (record.tui && settings.tui === "fullscreen") {
|
|
262
|
+
const { tui: _tui, ...rest } = settings;
|
|
263
|
+
settings = rest;
|
|
264
|
+
changed = true;
|
|
265
|
+
deps.out("✓ Switched the fullscreen view back off.");
|
|
266
|
+
}
|
|
267
|
+
if (changed)
|
|
268
|
+
writeSettings(record.settingsPath, settings);
|
|
269
|
+
}
|
|
270
|
+
if (record)
|
|
271
|
+
writeFileSync(recordPath(deps), "{}\n", { mode: 0o600 });
|
|
272
|
+
deps.out(`The ${MCP_SERVER} MCP server was left in place (remove it with: claude mcp remove ${MCP_SERVER} -s user).`);
|
|
273
|
+
return removed.status === 0 ? 0 : 1;
|
|
274
|
+
}
|
|
275
|
+
export function pluginStatus(deps, serverName = MCP_SERVER) {
|
|
276
|
+
if (!claudeAvailable(deps)) {
|
|
277
|
+
deps.out("✗ Claude Code (claude) wasn't found on PATH.");
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
const plugin = pluginInstalled(deps);
|
|
281
|
+
const server = hasMcpServer(deps, serverName);
|
|
282
|
+
const settingsPath = join(deps.claudeConfigDir, "settings.json");
|
|
283
|
+
let allowed = [];
|
|
284
|
+
try {
|
|
285
|
+
allowed = allowList(readSettings(settingsPath));
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
deps.out(`! ${error instanceof Error ? error.message : String(error)}`);
|
|
289
|
+
}
|
|
290
|
+
const missing = paneTools(serverName).filter((rule) => !allowed.includes(rule));
|
|
291
|
+
deps.out(`${plugin ? "✓" : "✗"} ${PLUGIN} plugin ${plugin ? "installed" : "not installed"}`);
|
|
292
|
+
deps.out(`${server ? "✓" : "✗"} ${serverName} MCP server ${server ? "configured" : "not configured"}`);
|
|
293
|
+
deps.out(missing.length === 0
|
|
294
|
+
? `✓ the pane's tools are allowed in ${settingsPath}`
|
|
295
|
+
: `! not allowed in ${settingsPath} (auto mode refuses them): ${missing.join(", ")}. Re-run \`wardby claude-plugin install --allow-tools\` to add them.`);
|
|
296
|
+
return plugin && server ? 0 : 1;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Quickstart's offer of the pane, once it connected Claude Code: asks unless a flag answered it,
|
|
300
|
+
* never in a non-interactive run, and installs on a yes. A failed install is a warning, not a stop.
|
|
301
|
+
*/
|
|
302
|
+
export async function quickstartPaneStep(options, steps) {
|
|
303
|
+
if (!options.mcpConfigured || (options.client !== "claude" && options.client !== "both"))
|
|
304
|
+
return "skipped";
|
|
305
|
+
const wanted = options.flag ??
|
|
306
|
+
(!options.nonInteractive && (await steps.ask("Add the wardby runs pane to Claude Code (the wardby-runs plugin)?")));
|
|
307
|
+
if (!wanted)
|
|
308
|
+
return "skipped";
|
|
309
|
+
const status = await steps.install({ marketplace: DEFAULT_MARKETPLACE, fullscreen: false, yes: options.yes });
|
|
310
|
+
if (status === 0)
|
|
311
|
+
return "installed";
|
|
312
|
+
steps.warn("! The runs pane wasn't installed; try again with: npx @wardby/cli@latest claude-plugin install");
|
|
313
|
+
return "failed";
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* `wardby doctor`'s lines for the pane: none without Claude Code. The pane is optional, so nothing
|
|
317
|
+
* here is a failure (✗): a missing plugin is "○", a missing server or rule a warning ("!").
|
|
318
|
+
*/
|
|
319
|
+
export function claudePluginDoctorLines(deps, serverName = MCP_SERVER) {
|
|
320
|
+
if (!claudeAvailable(deps))
|
|
321
|
+
return [];
|
|
322
|
+
if (!pluginInstalled(deps)) {
|
|
323
|
+
return [`○ Claude Code runs pane not installed (optional: npx @wardby/cli@latest claude-plugin install)`];
|
|
324
|
+
}
|
|
325
|
+
const lines = [`✓ ${PLUGIN} Claude Code plugin installed`];
|
|
326
|
+
lines.push(hasMcpServer(deps, serverName)
|
|
327
|
+
? `✓ ${serverName} MCP server configured in Claude Code`
|
|
328
|
+
: `! Claude Code has no MCP server named ${serverName}: the pane has nothing to show (claude-plugin install --server-url <url>)`);
|
|
329
|
+
const settingsPath = join(deps.claudeConfigDir, "settings.json");
|
|
330
|
+
let missing = paneTools(serverName);
|
|
331
|
+
try {
|
|
332
|
+
const allowed = allowList(readSettings(settingsPath));
|
|
333
|
+
missing = paneTools(serverName).filter((rule) => !allowed.includes(rule));
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// An unreadable settings file reads as no rules: the warning below says what to allow.
|
|
337
|
+
}
|
|
338
|
+
lines.push(missing.length === 0
|
|
339
|
+
? "✓ the pane's tools are allowed for auto mode"
|
|
340
|
+
: `! not allowed for auto mode in ${settingsPath}: ${missing.join(", ")} (needed only in auto mode; re-run \`claude-plugin install --allow-tools\` to add them)`);
|
|
341
|
+
return lines;
|
|
342
|
+
}
|
|
343
|
+
export async function claudePluginCommand(args, deps = claudePluginDeps()) {
|
|
344
|
+
const [sub, ...rest] = args;
|
|
345
|
+
if (sub === undefined || sub === "--help" || sub === "-h") {
|
|
346
|
+
deps.out(CLAUDE_PLUGIN_USAGE);
|
|
347
|
+
return sub === undefined ? 1 : 0;
|
|
348
|
+
}
|
|
349
|
+
if (sub === "install") {
|
|
350
|
+
const { values } = parseArgs({
|
|
351
|
+
args: rest,
|
|
352
|
+
allowNegative: true,
|
|
353
|
+
options: {
|
|
354
|
+
marketplace: { type: "string" },
|
|
355
|
+
"server-name": { type: "string" },
|
|
356
|
+
"server-url": { type: "string" },
|
|
357
|
+
"allow-tools": { type: "boolean" },
|
|
358
|
+
fullscreen: { type: "boolean" },
|
|
359
|
+
yes: { type: "boolean", short: "y" },
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
return installPlugin({
|
|
363
|
+
marketplace: values.marketplace ?? DEFAULT_MARKETPLACE,
|
|
364
|
+
serverName: values["server-name"],
|
|
365
|
+
serverUrl: values["server-url"],
|
|
366
|
+
allowTools: values["allow-tools"],
|
|
367
|
+
fullscreen: values.fullscreen ?? false,
|
|
368
|
+
yes: values.yes ?? false,
|
|
369
|
+
}, deps);
|
|
370
|
+
}
|
|
371
|
+
if (sub === "uninstall")
|
|
372
|
+
return uninstallPlugin(deps);
|
|
373
|
+
if (sub === "status") {
|
|
374
|
+
const { values } = parseArgs({ args: rest, options: { "server-name": { type: "string" } } });
|
|
375
|
+
return pluginStatus(deps, values["server-name"] ?? MCP_SERVER);
|
|
376
|
+
}
|
|
377
|
+
deps.out(`Unknown claude-plugin command "${sub}".\n${CLAUDE_PLUGIN_USAGE}`);
|
|
378
|
+
return 1;
|
|
379
|
+
}
|
package/dist/cli-help.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_USAGE = "usage:\n wardby quickstart [--provider openai|anthropic] [--model <m>] [--budget <usd>] [--client none|codex|claude|both] [--skip-demo] [--non-interactive --yes]\n wardby doctor\n wardby status\n wardby logs [--tail N] [--follow]\n wardby down [--volumes]\n wardby knowledge check [dir] [--root <dir>] [--strict] [--json] Validate a knowledge bundle (offline)\n wardby resume - [--dir <path> | --here] [--force] [--print] Continue a coding run's Claude Code session locally (paste the get_run_transcript link)\n wardby help [list]\n wardby help search <terms>\n wardby help open <article-id>\n wardby agent create --name <n> --model <m> --prompt <p> --budget <usd> [--schedule \"<cron>\"] [--timezone <tz>] [--max-turns <n>] [--owner <subject>] [--public] [--native-execution-mode control-plane|sandbox]\n (owned by --owner or LOCAL_PRINCIPAL; --public shares it with everyone at execute;\n sandbox needs NATIVE_SANDBOX_LAUNCHER set in this environment)\n wardby agent list\n wardby agent mode <name> control-plane|sandbox (native agents; applies to runs created after the change)\n wardby agent schedule <name> --cron \"<expr>\" [--timezone <tz>] [--disable]\n wardby tool create --name <n> --description <d> --params <file> --code <file>\n wardby tool attach <tool-name|tool-id> <agent-name> (grants the capabilities in the agent owner's name)\n wardby tool detach <tool-name|tool-id> <agent-name>\n wardby tool update <tool-name|tool-id> [--description <d>] [--params <file>] [--code <file>]\n wardby tool delete <tool-name|tool-id> [--detach]\n wardby tool list [--agent <name>]\n wardby run <name>\n wardby runs [--agent <name>] [--limit N] [--status <s>]\n wardby coding preflight (JOB_LAUNCHER=docker or kubernetes)\n wardby coding cleanup --run-id <id>\n wardby scheduler [--scope default]\n wardby native-gateway (the native sandbox gateway only; NATIVE_GATEWAY_LISTEN, default 0.0.0.0:8790)\n wardby mcp (MCP_TRANSPORT=stdio|http selects the transport)\n wardby serve [--scope default] (mcp + scheduler + reconciler in one process; http only)\n wardby grants migration-report [--json]\n wardby grants adopt-public --owner <subject> [--dry-run] [--keep-everyone-execute]\n wardby grants prune-bindings [--dry-run]\n wardby import <bundle-dir> [--owner <subject>] [--public] [--include-secrets --transfer-key <pem>] [--default-budget <usd>] [--dry-run] [--prefix <p>] [--on-conflict fail|skip|rename] [--allow-open-fetch]\n\noptions:\n -h, --help Show this help\n -v, --version Show the installed Wardby version";
|
|
1
|
+
export declare const CLI_USAGE = "usage:\n wardby quickstart [--provider openai|anthropic] [--model <m>] [--budget <usd>] [--client none|codex|claude|both] [--claude-plugin | --no-claude-plugin] [--skip-demo] [--non-interactive --yes]\n wardby doctor\n wardby status\n wardby logs [--tail N] [--follow]\n wardby down [--volumes]\n wardby knowledge check [dir] [--root <dir>] [--strict] [--json] Validate a knowledge bundle (offline)\n wardby resume - [--dir <path> | --here] [--force] [--print] Continue a coding run's Claude Code session locally (paste the get_run_transcript link)\n wardby claude-plugin install|uninstall|status [options] Set up the wardby-runs Claude Code pane (plugin, MCP server, allow rules)\n wardby help [list]\n wardby help search <terms>\n wardby help open <article-id>\n wardby agent create --name <n> --model <m> --prompt <p> --budget <usd> [--schedule \"<cron>\"] [--timezone <tz>] [--max-turns <n>] [--owner <subject>] [--public] [--native-execution-mode control-plane|sandbox]\n (owned by --owner or LOCAL_PRINCIPAL; --public shares it with everyone at execute;\n sandbox needs NATIVE_SANDBOX_LAUNCHER set in this environment)\n wardby agent list\n wardby agent mode <name> control-plane|sandbox (native agents; applies to runs created after the change)\n wardby agent schedule <name> --cron \"<expr>\" [--timezone <tz>] [--disable]\n wardby tool create --name <n> --description <d> --params <file> --code <file>\n wardby tool attach <tool-name|tool-id> <agent-name> (grants the capabilities in the agent owner's name)\n wardby tool detach <tool-name|tool-id> <agent-name>\n wardby tool update <tool-name|tool-id> [--description <d>] [--params <file>] [--code <file>]\n wardby tool delete <tool-name|tool-id> [--detach]\n wardby tool list [--agent <name>]\n wardby run <name>\n wardby runs [--agent <name>] [--limit N] [--status <s>]\n wardby coding preflight (JOB_LAUNCHER=docker or kubernetes)\n wardby coding cleanup --run-id <id>\n wardby scheduler [--scope default]\n wardby native-gateway (the native sandbox gateway only; NATIVE_GATEWAY_LISTEN, default 0.0.0.0:8790)\n wardby mcp (MCP_TRANSPORT=stdio|http selects the transport)\n wardby serve [--scope default] (mcp + scheduler + reconciler in one process; http only)\n wardby grants migration-report [--json]\n wardby grants adopt-public --owner <subject> [--dry-run] [--keep-everyone-execute]\n wardby grants prune-bindings [--dry-run]\n wardby import <bundle-dir> [--owner <subject>] [--public] [--include-secrets --transfer-key <pem>] [--default-budget <usd>] [--dry-run] [--prefix <p>] [--on-conflict fail|skip|rename] [--allow-open-fetch]\n\noptions:\n -h, --help Show this help\n -v, --version Show the installed Wardby version";
|
package/dist/cli-help.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export const CLI_USAGE = `usage:
|
|
2
|
-
wardby quickstart [--provider openai|anthropic] [--model <m>] [--budget <usd>] [--client none|codex|claude|both] [--skip-demo] [--non-interactive --yes]
|
|
2
|
+
wardby quickstart [--provider openai|anthropic] [--model <m>] [--budget <usd>] [--client none|codex|claude|both] [--claude-plugin | --no-claude-plugin] [--skip-demo] [--non-interactive --yes]
|
|
3
3
|
wardby doctor
|
|
4
4
|
wardby status
|
|
5
5
|
wardby logs [--tail N] [--follow]
|
|
6
6
|
wardby down [--volumes]
|
|
7
7
|
wardby knowledge check [dir] [--root <dir>] [--strict] [--json] Validate a knowledge bundle (offline)
|
|
8
8
|
wardby resume - [--dir <path> | --here] [--force] [--print] Continue a coding run's Claude Code session locally (paste the get_run_transcript link)
|
|
9
|
+
wardby claude-plugin install|uninstall|status [options] Set up the wardby-runs Claude Code pane (plugin, MCP server, allow rules)
|
|
9
10
|
wardby help [list]
|
|
10
11
|
wardby help search <terms>
|
|
11
12
|
wardby help open <article-id>
|
package/dist/cli.js
CHANGED
|
@@ -53,6 +53,8 @@ import { scheduleStartFields } from "./core/schedule-start.js";
|
|
|
53
53
|
import { assertAgentModelAvailable } from "./core/run-pricing.js";
|
|
54
54
|
import { markRunFailedFromExecutorError } from "./core/dispatch.js";
|
|
55
55
|
import { startScheduler } from "./core/scheduler.js";
|
|
56
|
+
import { RunStepSweeper } from "./core/run-steps/sweep.js";
|
|
57
|
+
import { buildRunStepWriter } from "./core/run-steps/writer.js";
|
|
56
58
|
import { startReconciler } from "./core/reconciler.js";
|
|
57
59
|
import { startNotifications } from "./core/notifications.js";
|
|
58
60
|
import { NativeEngine } from "./core/engine-native.js";
|
|
@@ -60,9 +62,13 @@ import { logger } from "./core/logger.js";
|
|
|
60
62
|
import { buildPower } from "./core/power-composition.js";
|
|
61
63
|
import { deriveJsonSchema } from "./sandbox/zod-params.js";
|
|
62
64
|
import { toolSubject, zod3ToolWarnings } from "./core/zod3-window.js";
|
|
63
|
-
import { findSameNamedAttachedTool, reservedToolNameReason, resolveToolRef } from "./core/tool-names.js";
|
|
65
|
+
import { findSameNamedAttachedTool, findSameNamedMcpTool, reservedToolNameReason, resolveToolRef, sameNamedMcpToolMessage, } from "./core/tool-names.js";
|
|
64
66
|
import { ToolAttachedError, deleteToolGuarded, formatAttachedAgents, formatToolLine, hasToolChanges, prepareToolUpdate, updateToolGuarded, } from "./core/tool-admin.js";
|
|
65
67
|
import { ToolCapabilitiesPatchSchema } from "./sandbox/tool-capabilities.js";
|
|
68
|
+
import { toolTimeoutProblem } from "./sandbox/tool-timeout.js";
|
|
69
|
+
import { loadToolMaxTimeoutMs } from "./config/tool-timeout.js";
|
|
70
|
+
import { loadMcpServersMode } from "./config/mcp-servers.js";
|
|
71
|
+
import { loadMcpMaxCallsPerRun } from "./config/mcp-call-limit.js";
|
|
66
72
|
import { startMcp, warmUpExecutor } from "./mcp/index.js";
|
|
67
73
|
import { startServe } from "./serve.js";
|
|
68
74
|
import { authCommand } from "./mcp/auth/self-hosted/cli.js";
|
|
@@ -114,6 +120,19 @@ function buildDatastore(cipher) {
|
|
|
114
120
|
function buildMemory(cipher) {
|
|
115
121
|
return guardAgentMemory(new PostgresAgentMemory(prisma), createDbSecretWriteGuard(prisma, cipher));
|
|
116
122
|
}
|
|
123
|
+
/** The run-steps writers this process built; each is flushed before the database disconnects. */
|
|
124
|
+
const runStepWriters = [];
|
|
125
|
+
/** The process's run-steps writer; an invalid WARDBY_RUN_STEPS or RUN_STEP_RETENTION_DAYS stops the command with the loader's message. */
|
|
126
|
+
function buildRunSteps(cipher) {
|
|
127
|
+
try {
|
|
128
|
+
const writer = buildRunStepWriter(prisma, cipher);
|
|
129
|
+
runStepWriters.push(writer);
|
|
130
|
+
return writer;
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
117
136
|
function buildSecrets() {
|
|
118
137
|
const config = loadProviderConfig();
|
|
119
138
|
try {
|
|
@@ -226,6 +245,21 @@ async function agentCreate(args) {
|
|
|
226
245
|
});
|
|
227
246
|
console.log(agent.id);
|
|
228
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* `--timeout-ms <ms>`: the tool's own time limit, up to WARDBY_TOOL_MAX_TIMEOUT_MS. On update,
|
|
250
|
+
* `--timeout-ms default` clears it (back to the default limit).
|
|
251
|
+
*/
|
|
252
|
+
function cliToolTimeout(raw, allowClear) {
|
|
253
|
+
if (raw === undefined)
|
|
254
|
+
return undefined;
|
|
255
|
+
if (allowClear && raw === "default")
|
|
256
|
+
return null;
|
|
257
|
+
const value = /^\d+$/.test(raw.trim()) ? Number(raw.trim()) : NaN;
|
|
258
|
+
const problem = toolTimeoutProblem(value, loadToolMaxTimeoutMs());
|
|
259
|
+
if (problem)
|
|
260
|
+
fail(`invalid --timeout-ms: ${problem}`);
|
|
261
|
+
return value;
|
|
262
|
+
}
|
|
229
263
|
async function toolCreate(args) {
|
|
230
264
|
const { values } = parseArgs({
|
|
231
265
|
args,
|
|
@@ -234,6 +268,7 @@ async function toolCreate(args) {
|
|
|
234
268
|
description: { type: "string" },
|
|
235
269
|
params: { type: "string" },
|
|
236
270
|
code: { type: "string" },
|
|
271
|
+
"timeout-ms": { type: "string" },
|
|
237
272
|
},
|
|
238
273
|
});
|
|
239
274
|
if (!values.name || !values.description || !values.params || !values.code) {
|
|
@@ -242,6 +277,7 @@ async function toolCreate(args) {
|
|
|
242
277
|
const reserved = reservedToolNameReason(values.name);
|
|
243
278
|
if (reserved)
|
|
244
279
|
fail(reserved);
|
|
280
|
+
const timeoutMs = cliToolTimeout(values["timeout-ms"], false);
|
|
245
281
|
let paramsZod;
|
|
246
282
|
let code;
|
|
247
283
|
try {
|
|
@@ -277,6 +313,7 @@ async function toolCreate(args) {
|
|
|
277
313
|
paramsZod: paramsZod,
|
|
278
314
|
jsonSchema: schemaResult.value,
|
|
279
315
|
code: code,
|
|
316
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
280
317
|
},
|
|
281
318
|
});
|
|
282
319
|
for (const warning of await zod3ToolWarnings(toolSubject(tool.name), paramsZod))
|
|
@@ -336,6 +373,9 @@ async function toolAttach(args, detach) {
|
|
|
336
373
|
if (clash) {
|
|
337
374
|
fail(`agent "${agentName}" already has a different tool named "${tool.name}" attached (${clash.id}); detach it first.`);
|
|
338
375
|
}
|
|
376
|
+
const mcpClash = await findSameNamedMcpTool(prisma, agent.id, tool.name);
|
|
377
|
+
if (mcpClash)
|
|
378
|
+
fail(sameNamedMcpToolMessage(agentName, tool.name, mcpClash.serverName));
|
|
339
379
|
const patch = ToolCapabilitiesPatchSchema.safeParse({
|
|
340
380
|
allowedSecrets: values["allow-secret"],
|
|
341
381
|
allowedDatastorePrefixes: values["allow-datastore-prefix"],
|
|
@@ -422,6 +462,7 @@ async function toolUpdate(args) {
|
|
|
422
462
|
description: { type: "string" },
|
|
423
463
|
params: { type: "string" },
|
|
424
464
|
code: { type: "string" },
|
|
465
|
+
"timeout-ms": { type: "string" },
|
|
425
466
|
},
|
|
426
467
|
});
|
|
427
468
|
const [ref] = positionals;
|
|
@@ -441,9 +482,10 @@ async function toolUpdate(args) {
|
|
|
441
482
|
description: values.description,
|
|
442
483
|
paramsZod: readFile("--params", values.params),
|
|
443
484
|
code: readFile("--code", values.code),
|
|
485
|
+
timeoutMs: cliToolTimeout(values["timeout-ms"], true),
|
|
444
486
|
};
|
|
445
487
|
if (!hasToolChanges(changes)) {
|
|
446
|
-
fail("tool update requires at least one of --description, --params <file>, --code <file
|
|
488
|
+
fail("tool update requires at least one of --description, --params <file>, --code <file>, --timeout-ms.");
|
|
447
489
|
}
|
|
448
490
|
const resolved = await resolveToolRef(prisma, ref);
|
|
449
491
|
if (!resolved.ok)
|
|
@@ -597,6 +639,7 @@ async function run(name) {
|
|
|
597
639
|
const secrets = buildSecrets();
|
|
598
640
|
const datastore = buildDatastore(secrets);
|
|
599
641
|
const memory = buildMemory(secrets);
|
|
642
|
+
const runSteps = buildRunSteps(secrets);
|
|
600
643
|
let run;
|
|
601
644
|
// Ctrl-C (or a SIGTERM) records the run as cancelled rather than leaving a
|
|
602
645
|
// `running` row behind.
|
|
@@ -610,6 +653,7 @@ async function run(name) {
|
|
|
610
653
|
memory,
|
|
611
654
|
reviewHosts: buildReviewHosts(process.env, prisma),
|
|
612
655
|
issueTrackers: buildIssueTrackers(),
|
|
656
|
+
runSteps,
|
|
613
657
|
}, prisma, (delta) => {
|
|
614
658
|
process.stdout.write(delta);
|
|
615
659
|
}, (created) => {
|
|
@@ -766,6 +810,7 @@ async function scheduler(args) {
|
|
|
766
810
|
const secrets = buildSecrets();
|
|
767
811
|
const datastore = buildDatastore(secrets);
|
|
768
812
|
const memory = buildMemory(secrets);
|
|
813
|
+
const runSteps = buildRunSteps(secrets);
|
|
769
814
|
const reviewHosts = buildReviewHosts(process.env, prisma);
|
|
770
815
|
const issueTrackers = buildIssueTrackers();
|
|
771
816
|
const chat = buildChatProviders();
|
|
@@ -783,6 +828,7 @@ async function scheduler(args) {
|
|
|
783
828
|
reviewHosts,
|
|
784
829
|
issueTrackers,
|
|
785
830
|
repoAccess,
|
|
831
|
+
runSteps,
|
|
786
832
|
};
|
|
787
833
|
const nativeExecutor = buildExecutor(config, nativeProviders, prisma);
|
|
788
834
|
const power = buildPower({ env: process.env, db: prisma });
|
|
@@ -792,6 +838,10 @@ async function scheduler(args) {
|
|
|
792
838
|
cliLog.warn(`low_power_scale_forbidden: missing ${missing.join(", ")}`);
|
|
793
839
|
}, (err) => cliLog.warn({ err }, "low_power_access_check_failed"));
|
|
794
840
|
}
|
|
841
|
+
// Read again at each tool call; checked here so a bad value stops start-up, not every run.
|
|
842
|
+
loadToolMaxTimeoutMs();
|
|
843
|
+
loadMcpServersMode();
|
|
844
|
+
loadMcpMaxCallsPerRun();
|
|
795
845
|
const executor = buildConfiguredExecutor({
|
|
796
846
|
native: nativeExecutor,
|
|
797
847
|
db: prisma,
|
|
@@ -814,6 +864,7 @@ async function scheduler(args) {
|
|
|
814
864
|
});
|
|
815
865
|
const notifications = await startNotifications({ db: prisma, chat });
|
|
816
866
|
const selfDefects = { db: prisma, issueTrackers };
|
|
867
|
+
const runStepSweeper = new RunStepSweeper(prisma);
|
|
817
868
|
const sched = startScheduler({
|
|
818
869
|
executor,
|
|
819
870
|
db: prisma,
|
|
@@ -829,6 +880,7 @@ async function scheduler(args) {
|
|
|
829
880
|
onStartFailed: (runId, err) => markRunFailedFromExecutorError(prisma, runId, err, selfDefects),
|
|
830
881
|
});
|
|
831
882
|
await power.sleepIfIdle().catch((err) => cliLog.warn({ err }, "power_sleep_failed"));
|
|
883
|
+
await runStepSweeper.maybeSweep();
|
|
832
884
|
},
|
|
833
885
|
});
|
|
834
886
|
console.log(`wardby scheduler started (scope "${scope}"). Press Ctrl+C to stop.`);
|
|
@@ -858,6 +910,14 @@ async function scheduler(args) {
|
|
|
858
910
|
*/
|
|
859
911
|
async function nativeGateway() {
|
|
860
912
|
const listen = parseGatewayListen(process.env.NATIVE_GATEWAY_LISTEN) ?? { host: "0.0.0.0", port: 8790 };
|
|
913
|
+
try {
|
|
914
|
+
loadToolMaxTimeoutMs();
|
|
915
|
+
loadMcpServersMode();
|
|
916
|
+
loadMcpMaxCallsPerRun();
|
|
917
|
+
}
|
|
918
|
+
catch (err) {
|
|
919
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
920
|
+
}
|
|
861
921
|
let modelCatalog;
|
|
862
922
|
try {
|
|
863
923
|
modelCatalog = await startModelCatalog(prisma);
|
|
@@ -877,8 +937,14 @@ async function nativeGateway() {
|
|
|
877
937
|
issueTrackers: buildIssueTrackers(),
|
|
878
938
|
repoAccess: createRepoAccessGate({ db: prisma, hosts: reviewHosts }),
|
|
879
939
|
executor: new DeferredExecutor(prisma),
|
|
940
|
+
runSteps: buildRunSteps(secrets),
|
|
880
941
|
};
|
|
881
|
-
const server = await startGatewayServer(listen, {
|
|
942
|
+
const server = await startGatewayServer(listen, {
|
|
943
|
+
db: prisma,
|
|
944
|
+
providers,
|
|
945
|
+
// Coding runs' MCP calls, forwarded by the coding proxy's /mcp route (its own token auth).
|
|
946
|
+
codingMcp: { db: prisma, cipher: secrets, log: logger.child({ module: "coding-mcp" }) },
|
|
947
|
+
});
|
|
882
948
|
// The deny port (NATIVE_GATEWAY_DENY_PORT, default 8791) serves nothing: a worker that can reach the
|
|
883
949
|
// gateway port but not this one proves its NetworkPolicy is programmed and port-scoped.
|
|
884
950
|
const denyPort = Number(process.env.NATIVE_GATEWAY_DENY_PORT ?? NATIVE_GATEWAY_DENY_PORT);
|
|
@@ -1037,6 +1103,8 @@ async function main() {
|
|
|
1037
1103
|
}
|
|
1038
1104
|
}
|
|
1039
1105
|
finally {
|
|
1106
|
+
// A run's last steps are written before the connection goes (bounded: flush never rejects).
|
|
1107
|
+
await Promise.all(runStepWriters.map((writer) => writer.flush()));
|
|
1040
1108
|
await prisma.$disconnect();
|
|
1041
1109
|
}
|
|
1042
1110
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Whether `name` may be registered: it must also be what a `<name>:<workspace>/<repo>` id resolves to. */
|
|
2
|
+
export declare function isValidHostProviderName(name: string): boolean;
|
|
3
|
+
/** The provider that owns an already-normalized repository id. */
|
|
4
|
+
export declare function codeProviderOf(repository: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* The canonical id of a repository on a host with pull requests: GitHub names,
|
|
7
|
+
* or `<provider>:<workspace>/<repo>` lower-cased. Throws for anything else,
|
|
8
|
+
* local repositories included, as normalizeGitHubRepository always has.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeHostedRepository(value: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* A pull request's web link, known here only for GitHub. A GitHub name never
|
|
13
|
+
* has a ":": anything that does gets no link, so no other host's id, valid or
|
|
14
|
+
* not, reaches a github.com URL.
|
|
15
|
+
*/
|
|
16
|
+
export declare function pullRequestWebUrl(provider: string, repository: string, number: number): string | null;
|