@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.
Files changed (367) hide show
  1. package/.env.example +5 -5
  2. package/README.md +6 -1
  3. package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
  4. package/dist/claude-coding-worker/driver.d.ts +12 -0
  5. package/dist/claude-coding-worker/driver.js +29 -7
  6. package/dist/claude-coding-worker/main.js +5 -1
  7. package/dist/claude-coding-worker/sdk.js +17 -1
  8. package/dist/claude-plugin/command.d.ts +64 -0
  9. package/dist/claude-plugin/command.js +379 -0
  10. package/dist/cli-help.d.ts +1 -1
  11. package/dist/cli-help.js +2 -1
  12. package/dist/cli.js +71 -3
  13. package/dist/coding/code-host.d.ts +16 -0
  14. package/dist/coding/code-host.js +60 -0
  15. package/dist/coding/mcp-token.d.ts +12 -0
  16. package/dist/coding/mcp-token.js +26 -0
  17. package/dist/coding/protocol.js +2 -0
  18. package/dist/coding/redact.js +6 -1
  19. package/dist/coding/transcript/recorder.d.ts +5 -4
  20. package/dist/coding/transcript/recorder.js +3 -9
  21. package/dist/coding-worker/debug-trace.d.ts +3 -1
  22. package/dist/coding-worker/debug-trace.js +4 -2
  23. package/dist/coding-worker/driver.js +8 -1
  24. package/dist/coding-worker/main.js +5 -1
  25. package/dist/coding-worker/mcp-config.d.ts +27 -0
  26. package/dist/coding-worker/mcp-config.js +32 -0
  27. package/dist/coding-worker/sdk.d.ts +36 -0
  28. package/dist/coding-worker/sdk.js +28 -1
  29. package/dist/coding-worker/types.d.ts +4 -0
  30. package/dist/config/mcp-call-limit.d.ts +8 -0
  31. package/dist/config/mcp-call-limit.js +18 -0
  32. package/dist/config/mcp-servers.d.ts +8 -0
  33. package/dist/config/mcp-servers.js +12 -0
  34. package/dist/config/test-simulators.d.ts +10 -0
  35. package/dist/config/test-simulators.js +28 -0
  36. package/dist/config/tool-timeout.d.ts +1 -0
  37. package/dist/config/tool-timeout.js +18 -0
  38. package/dist/core/coding-mcp.d.ts +36 -0
  39. package/dist/core/coding-mcp.js +253 -0
  40. package/dist/core/coding-repository.d.ts +46 -0
  41. package/dist/core/coding-repository.js +92 -0
  42. package/dist/core/delegation-slots.d.ts +27 -0
  43. package/dist/core/delegation-slots.js +44 -0
  44. package/dist/core/dispatch.d.ts +6 -2
  45. package/dist/core/dispatch.js +39 -11
  46. package/dist/core/engine-native.d.ts +2 -0
  47. package/dist/core/engine-native.js +13 -0
  48. package/dist/core/error-code.js +2 -0
  49. package/dist/core/grants.d.ts +28 -3
  50. package/dist/core/grants.js +46 -4
  51. package/dist/core/host-events.js +7 -3
  52. package/dist/core/host-status.js +2 -2
  53. package/dist/core/issue-status.js +4 -3
  54. package/dist/core/listen-address.d.ts +10 -0
  55. package/dist/core/listen-address.js +14 -0
  56. package/dist/core/mark-ready.d.ts +1 -1
  57. package/dist/core/mark-ready.js +1 -1
  58. package/dist/core/mcp-client/client.d.ts +47 -0
  59. package/dist/core/mcp-client/client.js +141 -0
  60. package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
  61. package/dist/core/mcp-client/jsonrpc.js +54 -0
  62. package/dist/core/mcp-client/results.d.ts +13 -0
  63. package/dist/core/mcp-client/results.js +42 -0
  64. package/dist/core/mcp-servers.d.ts +90 -0
  65. package/dist/core/mcp-servers.js +230 -0
  66. package/dist/core/mcp-tools.d.ts +108 -0
  67. package/dist/core/mcp-tools.js +287 -0
  68. package/dist/core/merge-order-check.js +38 -19
  69. package/dist/core/notification-dispatcher.js +2 -1
  70. package/dist/core/power-idle.d.ts +8 -2
  71. package/dist/core/power-idle.js +17 -3
  72. package/dist/core/power.d.ts +5 -0
  73. package/dist/core/power.js +5 -0
  74. package/dist/core/related-pull-requests.js +26 -10
  75. package/dist/core/retention-days.d.ts +5 -0
  76. package/dist/core/retention-days.js +14 -0
  77. package/dist/core/review-fix.js +2 -2
  78. package/dist/core/review-host-tools.js +3 -1
  79. package/dist/core/run-steps/config.d.ts +25 -0
  80. package/dist/core/run-steps/config.js +40 -0
  81. package/dist/core/run-steps/control-chars.d.ts +16 -0
  82. package/dist/core/run-steps/control-chars.js +26 -0
  83. package/dist/core/run-steps/detail.d.ts +32 -0
  84. package/dist/core/run-steps/detail.js +240 -0
  85. package/dist/core/run-steps/extract.d.ts +52 -0
  86. package/dist/core/run-steps/extract.js +467 -0
  87. package/dist/core/run-steps/ingress.d.ts +51 -0
  88. package/dist/core/run-steps/ingress.js +160 -0
  89. package/dist/core/run-steps/last-step.d.ts +15 -0
  90. package/dist/core/run-steps/last-step.js +35 -0
  91. package/dist/core/run-steps/native.d.ts +48 -0
  92. package/dist/core/run-steps/native.js +164 -0
  93. package/dist/core/run-steps/seen.d.ts +16 -0
  94. package/dist/core/run-steps/seen.js +35 -0
  95. package/dist/core/run-steps/summarize.d.ts +57 -0
  96. package/dist/core/run-steps/summarize.js +304 -0
  97. package/dist/core/run-steps/sweep.d.ts +21 -0
  98. package/dist/core/run-steps/sweep.js +41 -0
  99. package/dist/core/run-steps/types.d.ts +89 -0
  100. package/dist/core/run-steps/types.js +39 -0
  101. package/dist/core/run-steps/wire.d.ts +35 -0
  102. package/dist/core/run-steps/wire.js +47 -0
  103. package/dist/core/run-steps/writer.d.ts +49 -0
  104. package/dist/core/run-steps/writer.js +279 -0
  105. package/dist/core/runner.d.ts +70 -16
  106. package/dist/core/runner.js +296 -74
  107. package/dist/core/secret-broker-config.d.ts +2 -0
  108. package/dist/core/secret-broker-config.js +1 -1
  109. package/dist/core/secret-guard.d.ts +5 -2
  110. package/dist/core/secret-guard.js +22 -7
  111. package/dist/core/secrets.d.ts +11 -1
  112. package/dist/core/secrets.js +27 -1
  113. package/dist/core/serializable-retry.d.ts +18 -0
  114. package/dist/core/serializable-retry.js +43 -0
  115. package/dist/core/tool-admin.d.ts +5 -1
  116. package/dist/core/tool-admin.js +7 -2
  117. package/dist/core/tool-names.d.ts +13 -0
  118. package/dist/core/tool-names.js +20 -0
  119. package/dist/core/webhooks.d.ts +4 -0
  120. package/dist/core/webhooks.js +68 -49
  121. package/dist/core/workflow-run-events.js +2 -1
  122. package/dist/generated/prisma/browser.d.ts +39 -0
  123. package/dist/generated/prisma/client.d.ts +39 -0
  124. package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
  125. package/dist/generated/prisma/enums.d.ts +6 -0
  126. package/dist/generated/prisma/enums.js +5 -0
  127. package/dist/generated/prisma/internal/class.d.ts +77 -0
  128. package/dist/generated/prisma/internal/class.js +4 -4
  129. package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
  130. package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
  131. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
  132. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
  133. package/dist/generated/prisma/models/Agent.d.ts +372 -1
  134. package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
  135. package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
  136. package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
  137. package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
  138. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
  139. package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
  140. package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
  141. package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
  142. package/dist/generated/prisma/models/HostProvider.js +1 -0
  143. package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
  144. package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
  145. package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
  146. package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
  147. package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
  148. package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
  149. package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
  150. package/dist/generated/prisma/models/McpServer.js +1 -0
  151. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
  152. package/dist/generated/prisma/models/Principal.d.ts +167 -0
  153. package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
  154. package/dist/generated/prisma/models/Run.d.ts +503 -1
  155. package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
  156. package/dist/generated/prisma/models/RunStep.js +1 -0
  157. package/dist/generated/prisma/models/Secret.d.ts +387 -0
  158. package/dist/generated/prisma/models/Tool.d.ts +223 -1
  159. package/dist/generated/prisma/models.d.ts +7 -0
  160. package/dist/help/search.js +12 -3
  161. package/dist/help-index.json +812 -52
  162. package/dist/import/create.js +16 -1
  163. package/dist/import/neutral-schema.d.ts +1 -0
  164. package/dist/import/neutral-schema.js +2 -0
  165. package/dist/mcp/auth/access.d.ts +1 -0
  166. package/dist/mcp/auth/grants-cli.d.ts +6 -4
  167. package/dist/mcp/auth/grants-cli.js +42 -3
  168. package/dist/mcp/auth/resource-server.d.ts +3 -1
  169. package/dist/mcp/auth/resource-server.js +17 -2
  170. package/dist/mcp/context.d.ts +6 -0
  171. package/dist/mcp/index.js +21 -0
  172. package/dist/mcp/tools/agents.js +135 -20
  173. package/dist/mcp/tools/grants.js +66 -28
  174. package/dist/mcp/tools/host-providers.d.ts +2 -0
  175. package/dist/mcp/tools/host-providers.js +215 -0
  176. package/dist/mcp/tools/mcp-servers.d.ts +26 -0
  177. package/dist/mcp/tools/mcp-servers.js +481 -0
  178. package/dist/mcp/tools/power.js +2 -2
  179. package/dist/mcp/tools/repositories.js +50 -10
  180. package/dist/mcp/tools/run-access.d.ts +7 -0
  181. package/dist/mcp/tools/run-access.js +14 -0
  182. package/dist/mcp/tools/run-steps.d.ts +16 -0
  183. package/dist/mcp/tools/run-steps.js +85 -0
  184. package/dist/mcp/tools/runs.js +186 -18
  185. package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
  186. package/dist/mcp/tools/secret-elicitation.js +25 -2
  187. package/dist/mcp/tools/secrets.js +38 -4
  188. package/dist/mcp/tools/tools.d.ts +6 -4
  189. package/dist/mcp/tools/tools.js +40 -8
  190. package/dist/mcp/tools/trigger.js +28 -6
  191. package/dist/mcp/transport/http-limits.d.ts +2 -0
  192. package/dist/mcp/transport/http-limits.js +6 -2
  193. package/dist/mcp/webhooks/ingress.js +10 -0
  194. package/dist/native-worker/gateway-server.d.ts +18 -1
  195. package/dist/native-worker/gateway-server.js +38 -9
  196. package/dist/native-worker/gateway.d.ts +5 -0
  197. package/dist/native-worker/gateway.js +13 -1
  198. package/dist/native-worker/http-server.d.ts +6 -4
  199. package/dist/native-worker/http-server.js +53 -8
  200. package/dist/native-worker/ledger.d.ts +6 -2
  201. package/dist/native-worker/ledger.js +5 -4
  202. package/dist/native-worker/protocol.d.ts +1 -0
  203. package/dist/native-worker/protocol.js +4 -1
  204. package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
  205. package/dist/providers/coding-proxy/capability-hash.js +5 -0
  206. package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
  207. package/dist/providers/coding-proxy/mcp-route.js +166 -0
  208. package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
  209. package/dist/providers/coding-proxy/memory-ledger.js +16 -0
  210. package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
  211. package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
  212. package/dist/providers/coding-proxy/proxy.d.ts +33 -1
  213. package/dist/providers/coding-proxy/proxy.js +266 -75
  214. package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
  215. package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
  216. package/dist/providers/coding-proxy/runtime.d.ts +13 -0
  217. package/dist/providers/coding-proxy/runtime.js +87 -4
  218. package/dist/providers/coding-proxy/server.d.ts +3 -0
  219. package/dist/providers/coding-proxy/server.js +10 -0
  220. package/dist/providers/coding-proxy/types.d.ts +18 -0
  221. package/dist/providers/engine/types.d.ts +26 -0
  222. package/dist/providers/executor/composition.js +5 -0
  223. package/dist/providers/executor/container.d.ts +47 -1
  224. package/dist/providers/executor/container.js +84 -8
  225. package/dist/providers/host-tools/invoke.d.ts +37 -0
  226. package/dist/providers/host-tools/invoke.js +117 -0
  227. package/dist/providers/host-tools/operations.d.ts +65 -0
  228. package/dist/providers/host-tools/operations.js +149 -0
  229. package/dist/providers/host-tools/registry.d.ts +79 -0
  230. package/dist/providers/host-tools/registry.js +362 -0
  231. package/dist/providers/host-tools/request-guard.d.ts +17 -0
  232. package/dist/providers/host-tools/request-guard.js +99 -0
  233. package/dist/providers/host-tools/self-test.d.ts +64 -0
  234. package/dist/providers/host-tools/self-test.js +142 -0
  235. package/dist/providers/jobs/docker-isolation.d.ts +2 -2
  236. package/dist/providers/jobs/docker-isolation.js +30 -8
  237. package/dist/providers/jobs/docker.d.ts +8 -0
  238. package/dist/providers/jobs/docker.js +33 -9
  239. package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
  240. package/dist/providers/jobs/kubernetes-isolation.js +15 -2
  241. package/dist/providers/jobs/kubernetes.d.ts +13 -0
  242. package/dist/providers/jobs/kubernetes.js +28 -5
  243. package/dist/providers/jobs/types.d.ts +6 -0
  244. package/dist/providers/review-host/types.d.ts +6 -4
  245. package/dist/providers/review-host/types.js +1 -1
  246. package/dist/providers/vcs/git.d.ts +6 -2
  247. package/dist/providers/vcs/git.js +15 -4
  248. package/dist/providers/vcs/github.d.ts +2 -71
  249. package/dist/providers/vcs/github.js +8 -11
  250. package/dist/providers/vcs/index.d.ts +1 -1
  251. package/dist/providers/vcs/index.js +1 -1
  252. package/dist/providers/vcs/remote.d.ts +4 -0
  253. package/dist/providers/vcs/remote.js +7 -1
  254. package/dist/providers/vcs/routing.d.ts +12 -6
  255. package/dist/providers/vcs/routing.js +21 -6
  256. package/dist/providers/vcs/types.d.ts +71 -3
  257. package/dist/quickstart/index.d.ts +5 -0
  258. package/dist/quickstart/index.js +21 -0
  259. package/dist/quickstart-images.json +1 -1
  260. package/dist/sandbox/eval-core.d.ts +12 -2
  261. package/dist/sandbox/eval-core.js +20 -4
  262. package/dist/sandbox/host-functions.d.ts +4 -0
  263. package/dist/sandbox/host-functions.js +7 -2
  264. package/dist/sandbox/limits.d.ts +4 -1
  265. package/dist/sandbox/limits.js +4 -1
  266. package/dist/sandbox/run-in-sandbox.d.ts +6 -0
  267. package/dist/sandbox/run-in-sandbox.js +14 -2
  268. package/dist/sandbox/safe-fetch.d.ts +2 -0
  269. package/dist/sandbox/safe-fetch.js +1 -1
  270. package/dist/sandbox/secret-broker.d.ts +1 -0
  271. package/dist/sandbox/secret-broker.js +1 -1
  272. package/dist/sandbox/tool-timeout.d.ts +32 -0
  273. package/dist/sandbox/tool-timeout.js +52 -0
  274. package/dist/sandbox/user-tool.d.ts +8 -1
  275. package/dist/sandbox/user-tool.js +9 -3
  276. package/dist/serve.js +26 -18
  277. package/dist/viewer/api-schema.d.ts +66 -0
  278. package/dist/viewer/api-schema.js +31 -0
  279. package/dist/viewer/graph.js +2 -1
  280. package/dist/viewer/openapi.js +2 -1
  281. package/dist/viewer/run-detail.d.ts +2 -0
  282. package/dist/viewer/run-detail.js +26 -1
  283. package/dist/wardby-bin.js +5 -0
  284. package/docs/README.md +11 -1
  285. package/docs/agent-recipes.md +37 -4
  286. package/docs/architecture-runtime.md +1 -1
  287. package/docs/claude-code-plugin.md +208 -0
  288. package/docs/code-review-agents.md +6 -0
  289. package/docs/coding-agent-setup.md +94 -1
  290. package/docs/coding-worker-byo-images.md +4 -2
  291. package/docs/coding-worker-isolation.md +94 -7
  292. package/docs/debugging-agent-runs.md +354 -0
  293. package/docs/getting-started-gke.md +33 -11
  294. package/docs/getting-started-identity-provider.md +43 -37
  295. package/docs/getting-started.md +8 -0
  296. package/docs/host-providers.md +294 -0
  297. package/docs/mcp-servers.md +263 -0
  298. package/docs/native-sandbox.md +31 -9
  299. package/docs/security-deployment.md +161 -31
  300. package/docs/tool-secrets.md +3 -0
  301. package/docs/viewer-api.md +27 -12
  302. package/docs/viewer-api.openapi.yml +78 -2
  303. package/help/admin-viewer.md +5 -0
  304. package/help/agent-recipes.md +33 -2
  305. package/help/brokered-secrets.md +3 -0
  306. package/help/build-claude-code-image.md +1 -1
  307. package/help/claude-code-run-pane.md +63 -0
  308. package/help/deploy-gke.md +5 -0
  309. package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
  310. package/help/errors/coding-mcp-pins-unavailable.md +35 -0
  311. package/help/errors/database-auth-invalid.md +1 -1
  312. package/help/errors/host-provider-error.md +40 -0
  313. package/help/errors/host-tool-failed.md +36 -0
  314. package/help/errors/mcp-call-limit.md +22 -0
  315. package/help/errors/mcp-call-timeout.md +22 -0
  316. package/help/errors/mcp-server-auth-failed.md +22 -0
  317. package/help/errors/mcp-server-credentials-unavailable.md +45 -0
  318. package/help/errors/mcp-server-error.md +28 -0
  319. package/help/errors/mcp-server-unreachable.md +22 -0
  320. package/help/errors/mcp-servers-off.md +21 -0
  321. package/help/errors/mcp-tool-not-allowed.md +26 -0
  322. package/help/errors/mcp-tool-removed.md +22 -0
  323. package/help/errors/mcp-unavailable.md +25 -0
  324. package/help/errors/native-sandbox-capacity.md +1 -1
  325. package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
  326. package/help/errors/native-sandbox-docker-failed.md +1 -1
  327. package/help/errors/native-sandbox-gateway-error.md +1 -1
  328. package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
  329. package/help/errors/native-sandbox-image-not-pinned.md +1 -1
  330. package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
  331. package/help/errors/native-sandbox-network-unenforced.md +1 -1
  332. package/help/errors/native-sandbox-requires-catalog.md +1 -1
  333. package/help/errors/native-sandbox-unavailable.md +1 -1
  334. package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
  335. package/help/errors/native-sandbox-worker-exited.md +1 -1
  336. package/help/errors/native-sandbox-worker-lost.md +1 -1
  337. package/help/errors/native-sandbox-worker-unready.md +1 -1
  338. package/help/errors/repository-not-listed.md +36 -0
  339. package/help/errors/repository-required.md +36 -0
  340. package/help/errors/tool-timeout.md +38 -0
  341. package/help/host-providers.md +40 -0
  342. package/help/identity-and-access.md +4 -1
  343. package/help/low-power-mode.md +19 -8
  344. package/help/mcp-servers.md +73 -0
  345. package/help/mcp.md +9 -2
  346. package/help/native-capabilities.md +27 -1
  347. package/help/native-sandbox.md +1 -1
  348. package/help/operating-agents.md +16 -0
  349. package/help/related-pull-requests.md +7 -3
  350. package/help/repo-instructions-and-skills.md +3 -1
  351. package/help/resume-runs-locally.md +7 -0
  352. package/help/run-steps.md +140 -0
  353. package/help/stop-run.md +1 -1
  354. package/help/troubleshooting/coding-workers.md +9 -1
  355. package/help/troubleshooting/debugging-agent-runs.md +29 -0
  356. package/package.json +2 -1
  357. package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
  358. package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
  359. package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
  360. package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
  361. package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
  362. package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
  363. package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
  364. package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
  365. package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
  366. package/prisma/schema.prisma +211 -14
  367. 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
+ }
@@ -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, { db: prisma, providers });
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;