@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
@@ -10,12 +10,13 @@ import { simulatorGitEnvironment } from "../../config/test-simulators.js";
10
10
  import { globRegex } from "../../core/glob.js";
11
11
  import { redactExactSecretValues } from "../../core/secret-spans.js";
12
12
  import { MAX_REDACTED_SPAN, redactTokenShapedValues, normalizeGitRef } from "../../coding/protocol.js";
13
- import { isSafeGitHubInstallationToken } from "./github.js";
13
+ import { isSafeGitAuthToken } from "./remote.js";
14
14
  export const DEFAULT_MAX_CHANGED_FILES = 1_000;
15
15
  export const DEFAULT_MAX_DIFF_BYTES = 1024 * 1024;
16
16
  const MAX_GIT_OUTPUT_BYTES = 2 * 1024 * 1024;
17
17
  const MAX_WORKSPACE_ENTRIES = 100_000;
18
18
  const SAFE_COMMIT_SHA = /^[0-9a-f]{40}$/;
19
+ const SAFE_GIT_USERNAME = /^[A-Za-z0-9._-]{1,64}$/;
19
20
  const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
20
21
  /** Where a continuation's target branch is fetched to (never under refs/remotes, so baseCommit's ref is untouched). */
21
22
  const MERGE_TARGET_REF = "refs/wardby/merge-target";
@@ -82,9 +83,12 @@ export class NodeGitCommandRunner {
82
83
  options.askPassPath ?? fileURLToPath(new URL("../../../scripts/git-askpass.sh", import.meta.url));
83
84
  }
84
85
  async run(args, options = {}) {
85
- if (options.authToken !== undefined && !isSafeGitHubInstallationToken(options.authToken)) {
86
+ if (options.authToken !== undefined && !isSafeGitAuthToken(options.authToken)) {
86
87
  throw new Error("git_auth_token_invalid");
87
88
  }
89
+ if (options.authUsername !== undefined && !SAFE_GIT_USERNAME.test(options.authUsername)) {
90
+ throw new Error("git_auth_username_invalid");
91
+ }
88
92
  const env = {
89
93
  PATH: this.path,
90
94
  HOME: this.options.homeDir,
@@ -97,6 +101,7 @@ export class NodeGitCommandRunner {
97
101
  GIT_TERMINAL_PROMPT: "0",
98
102
  GCM_INTERACTIVE: "never",
99
103
  GIT_ASKPASS: options.authToken ? this.askPassPath : "/bin/false",
104
+ ...(options.authToken && options.authUsername ? { WARDBY_GIT_USERNAME: options.authUsername } : {}),
100
105
  SSH_ASKPASS: "/bin/false",
101
106
  GIT_LFS_SKIP_SMUDGE: "1",
102
107
  // WARDBY_TEST_SIMULATORS (test only): the simulator's throwaway CA; empty otherwise.
@@ -293,7 +298,7 @@ export class GitVcsProvider {
293
298
  let mergeFetched = false;
294
299
  try {
295
300
  await this.remote.withAccess(normalized.repository, async (token) => {
296
- const auth = token !== undefined ? { authToken: token } : {};
301
+ const auth = this.gitAuth(token);
297
302
  if (this.remote.refNotFoundError) {
298
303
  const listed = await this.git.run([
299
304
  ...HARDENED_GIT_CONFIG,
@@ -937,10 +942,16 @@ export class GitVcsProvider {
937
942
  throw new Error("vcs_git_config_unsafe");
938
943
  }
939
944
  }
945
+ /** The git credentials for a remote's access token; none when the remote needs no token. */
946
+ gitAuth(token) {
947
+ if (token === undefined)
948
+ return {};
949
+ return { authToken: token, ...(this.remote.gitUsername ? { authUsername: this.remote.gitUsername } : {}) };
950
+ }
940
951
  async pushOnce(workspace, commitSha) {
941
952
  this.remote.assertPushRef?.(workspace.headRef);
942
953
  await this.remote.withAccess(workspace.repository, async (token) => {
943
- const auth = token !== undefined ? { authToken: token } : {};
954
+ const auth = this.gitAuth(token);
944
955
  const remote = await this.remoteHead(workspace, auth);
945
956
  if (remote === commitSha)
946
957
  return;
@@ -1,5 +1,7 @@
1
1
  import { type PackageAuditReport } from "../../coding/registry/audit-mode.js";
2
2
  import { RELATED_SECTION_END, RELATED_SECTION_START } from "./pr-description.js";
3
+ import type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestEntry, RelatedPullRequestState, RelatedPullRequestsInput, RepositoryFileInput } from "./types.js";
4
+ export type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestEntry, RelatedPullRequestState, RelatedPullRequestsInput, RepositoryFileInput, };
3
5
  export { RELATED_SECTION_END, RELATED_SECTION_START };
4
6
  export interface GitHubAppConfig {
5
7
  appId: string;
@@ -106,22 +108,6 @@ export interface PullRequestResult {
106
108
  */
107
109
  descriptionSha256?: string;
108
110
  }
109
- /** What wardby last wrote into a pull request description (control-plane records). */
110
- export interface DescriptionBaseline {
111
- /** Null = legacy pull request (no recorded write). */
112
- recordedSha256: string | null;
113
- /**
114
- * Legacy pull requests only: the hash of the body wardby would have written
115
- * for the root run, rebuilt from stored records. A current body with this
116
- * hash is unedited. Absent when the records can't rebuild it (then the
117
- * description counts as edited).
118
- */
119
- legacySha256?: string;
120
- /** Stored summary of the run whose text the description carries (root run for legacy). */
121
- previousAgentText?: string;
122
- /** That run's id. */
123
- agentTextRunId: string;
124
- }
125
111
  /**
126
112
  * A fix round's description update. `runId` is this (latest) run; the pull
127
113
  * request is re-found by `rootRunId`'s marker. `related` is ignored: the
@@ -138,23 +124,6 @@ export interface UpdateDescriptionInput extends Omit<PullRequestInput, "runId" |
138
124
  */
139
125
  headSha?: string;
140
126
  }
141
- /** `agentTextRunId` is the run whose summary the description now carries. */
142
- export type DescriptionUpdateResult = {
143
- kind: "replaced" | "unchanged";
144
- sha256: string;
145
- agentTextRunId: string;
146
- } | {
147
- kind: "refreshed";
148
- reason: "empty" | "too_short" | "missing_closing_keyword";
149
- sha256: string;
150
- agentTextRunId: string;
151
- } | {
152
- kind: "edited_by_hand";
153
- commentPosted: boolean;
154
- } | {
155
- kind: "skipped";
156
- reason: "not_open" | "too_long" | "error";
157
- };
158
127
  /** See VcsProvider.notifyContinuationStarted/Finished (types.ts) — these back that GitHub-specific mechanism. */
159
128
  export interface ContinuationStatusCommentInput {
160
129
  /** This continuation round's own run id — the comment's identity/marker key. */
@@ -185,15 +154,6 @@ export interface ContinuationCheckRunCompleteInput {
185
154
  */
186
155
  headRef?: string;
187
156
  }
188
- /** One file at one ref, read through the API without a clone. */
189
- export interface RepositoryFileInput {
190
- repository: string;
191
- ref: string;
192
- /** Repository-relative POSIX path. */
193
- path: string;
194
- /** Refused above this many bytes. */
195
- maxBytes: number;
196
- }
197
157
  export interface GitHubRepositoryAccess {
198
158
  withRepositoryToken<T>(repository: string, action: (token: string) => Promise<T>): Promise<T>;
199
159
  createOrFindDraftPullRequest(input: PullRequestInput): Promise<PullRequestResult>;
@@ -264,8 +224,6 @@ export interface GitHubRepositoryAccess {
264
224
  readFileAtRef?(input: RepositoryFileInput): Promise<string | null>;
265
225
  }
266
226
  type Fetch = typeof globalThis.fetch;
267
- /** Accepts bounded opaque installation tokens without allowing control characters. */
268
- export declare function isSafeGitHubInstallationToken(value: unknown): value is string;
269
227
  /**
270
228
  * Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
271
229
  * stays free of issue-tracker imports (a test keeps the two in step). The
@@ -279,33 +237,6 @@ export declare function pullRequestTitle(input: PullRequestInput): string;
279
237
  export declare const MAX_RELATED_PULL_REQUESTS = 20;
280
238
  /** GitHub rejects a pull request body longer than this. */
281
239
  export declare const MAX_PULL_REQUEST_BODY_CHARS = 65536;
282
- export type RelatedPullRequestState = "open" | "draft" | "merged" | "closed";
283
- /** One pull request of the request's set, from stored control-plane rows only. */
284
- export interface RelatedPullRequestEntry {
285
- repository: string;
286
- /** Absent only for `self` while the pull request is being created. */
287
- number?: number;
288
- /** Live state; absent when unknown (at creation). */
289
- state?: RelatedPullRequestState;
290
- /** The pull request whose description this section is in. */
291
- self?: boolean;
292
- /**
293
- * Step in the delegating agent's declared merge order (1-99), from
294
- * CodingRun.mergeOrder; absent = the delegating agent set no order
295
- * (an open entry is listed under "Not ordered:" when any other entry,
296
- * open or done, has one).
297
- */
298
- mergeOrder?: number;
299
- }
300
- export interface RelatedPullRequestsInput {
301
- /** In merge order: by mergeOrder when set, else the order the request's coding runs were dispatched. */
302
- entries: readonly RelatedPullRequestEntry[];
303
- issue?: {
304
- key: string;
305
- url?: string;
306
- trackerName?: string;
307
- };
308
- }
309
240
  /**
310
241
  * The "Related pull requests" block, markers included, built only from
311
242
  * stored rows (never agent text). Open (and draft, and not-yet-known) pull
@@ -2,8 +2,10 @@ import { createPrivateKey } from "node:crypto";
2
2
  import { importPKCS8, SignJWT } from "jose";
3
3
  import { isGitHubLogin } from "../../config/github-allowed-accounts.js";
4
4
  import { testSimulatorBase } from "../../config/test-simulators.js";
5
+ import { codeProviderOf, normalizeHostedRepository, pullRequestWebUrl } from "../../coding/code-host.js";
5
6
  import { ACCEPTED_ADVISORY_ID } from "../../coding/registry/allowlist.js";
6
7
  import { packageAuditRows, packageAuditWording, summarizePackageAudit, } from "../../coding/registry/audit-mode.js";
8
+ import { isSafeGitAuthToken } from "./remote.js";
7
9
  import { refusalMeaning } from "../../coding/registry/refusal-hints.js";
8
10
  import { DEPENDENCY_CACHE_PATH, normalizeGitHubRepository, normalizeGitRef, truncateText, } from "../../coding/protocol.js";
9
11
  import { LATEST_RUN_MARKER_PREFIX, RELATED_SECTION_END, RELATED_SECTION_START, assessFinalMessage, descriptionAppliedComment, descriptionFallbackComment, descriptionSha256, extractRelatedBlock, findDescriptionComments, hasClosingKeyword, isUneditedDescription, supersededDescriptionComment, } from "./pr-description.js";
@@ -33,13 +35,6 @@ function safeApiError(response) {
33
35
  const requestId = response.headers.get("x-github-request-id");
34
36
  return new Error(`github_api_error:${response.status}${requestId ? `:${requestId}` : ""}`);
35
37
  }
36
- /** Accepts bounded opaque installation tokens without allowing control characters. */
37
- export function isSafeGitHubInstallationToken(value) {
38
- return (typeof value === "string" &&
39
- value.length >= 20 &&
40
- Buffer.byteLength(value, "utf8") <= 512 &&
41
- /^[A-Za-z0-9._-]+$/.test(value));
42
- }
43
38
  /**
44
39
  * Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
45
40
  * stays free of issue-tracker imports (a test keeps the two in step). The
@@ -109,10 +104,10 @@ export const MAX_RELATED_PULL_REQUESTS = 20;
109
104
  /** GitHub rejects a pull request body longer than this. */
110
105
  export const MAX_PULL_REQUEST_BODY_CHARS = 65_536;
111
106
  const RELATED_STATES = new Set(["open", "draft", "merged", "closed"]);
112
- /** The repository normalized, or undefined when it isn't a plain owner/name safe inside a link. */
107
+ /** The repository normalized, or undefined when it isn't a hosted repository id safe inside a link. */
113
108
  function safeRepository(value) {
114
109
  try {
115
- const repository = normalizeGitHubRepository(value);
110
+ const repository = normalizeHostedRepository(value);
116
111
  return isSafeMarkdownFragment(repository) && !/[[\]()<>\s]/.test(repository) ? repository : undefined;
117
112
  }
118
113
  catch {
@@ -129,7 +124,9 @@ function relatedLine(entry, prefix) {
129
124
  return `${prefix} **This pull request**${state}`;
130
125
  if (!Number.isSafeInteger(entry.number) || (entry.number ?? 0) <= 0)
131
126
  return undefined;
132
- return `${prefix} [${repository}#${entry.number}](https://github.com/${repository}/pull/${entry.number})${state}`;
127
+ const label = `${repository}#${entry.number}`;
128
+ const url = pullRequestWebUrl(codeProviderOf(repository), repository, entry.number);
129
+ return `${prefix} ${url ? `[${label}](${url})` : label}${state}`;
133
130
  }
134
131
  const isDone = (entry) => entry.state === "merged" || entry.state === "closed";
135
132
  /**
@@ -1103,7 +1100,7 @@ export class GitHubAppClient {
1103
1100
  const expiresAt = typeof payload.expires_at === "string" ? Date.parse(payload.expires_at) : Number.NaN;
1104
1101
  const unexpectedPermission = Object.entries(grantedPermissions).some(([permissionName, level]) => !isAllowedPermission(permissionName, level));
1105
1102
  const requestedGranted = Object.entries(permissions).every(([permissionName, level]) => grantedPermissions[permissionName] === level);
1106
- if (!isSafeGitHubInstallationToken(payload.token) ||
1103
+ if (!isSafeGitAuthToken(payload.token) ||
1107
1104
  !Number.isFinite(expiresAt) ||
1108
1105
  expiresAt <= this.now().getTime() + 60_000 ||
1109
1106
  !requestedGranted ||
@@ -5,7 +5,7 @@ export { GitHubAppClient } from "./github.js";
5
5
  export { GitVcsProvider, NodeGitCommandRunner } from "./git.js";
6
6
  export { GitHubRemote } from "./github-remote.js";
7
7
  export { LocalRemote } from "./local-remote.js";
8
- export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR } from "./routing.js";
8
+ export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR, VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR, } from "./routing.js";
9
9
  export type { GitRemote, PublishInput, PublishResult } from "./remote.js";
10
10
  export interface BuildVcsProviderOptions {
11
11
  /** Startup warnings; the module logger by default. */
@@ -14,7 +14,7 @@ export { GitHubAppClient } from "./github.js";
14
14
  export { GitVcsProvider, NodeGitCommandRunner } from "./git.js";
15
15
  export { GitHubRemote } from "./github-remote.js";
16
16
  export { LocalRemote } from "./local-remote.js";
17
- export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR } from "./routing.js";
17
+ export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR, VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR, } from "./routing.js";
18
18
  /**
19
19
  * GitHub is built only with App credentials, local repositories only with
20
20
  * LOCAL_REPO_ROOTS; neither being set is allowed (the server still starts) and
@@ -1,4 +1,6 @@
1
1
  import type { DescriptionWriteOutcome, FinalizeChangesDetails, PreparedWorkspace, VcsProvider } from "./types.js";
2
+ /** Accepts bounded opaque git auth tokens without allowing control characters. */
3
+ export declare function isSafeGitAuthToken(value: unknown): value is string;
2
4
  /** What a remote needs to publish a pushed commit (GitHub: open or find the draft PR). */
3
5
  export interface PublishInput {
4
6
  /** The validated workspace whose headRef was just pushed. */
@@ -25,6 +27,8 @@ export type PublishResult = {
25
27
  * fetch-compare-push) stays in GitVcsProvider and applies to every remote.
26
28
  */
27
29
  export interface GitRemote {
30
+ /** The username git sends with the token (GitHub: unset, git-askpass.sh's default). */
31
+ readonly gitUsername?: string;
28
32
  /** Extra `-c` pairs appended after HARDENED_GIT_CONFIG for clone/push/ls-remote (GitHub: none). */
29
33
  readonly gitConfig: readonly string[];
30
34
  /** Syntactic normalization of a repository id for this remote (GitHub: owner/name; local: local:/abs/path). */
@@ -1 +1,7 @@
1
- export {};
1
+ /** Accepts bounded opaque git auth tokens without allowing control characters. */
2
+ export function isSafeGitAuthToken(value) {
3
+ return (typeof value === "string" &&
4
+ value.length >= 20 &&
5
+ Buffer.byteLength(value, "utf8") <= 512 &&
6
+ /^[A-Za-z0-9._-]+$/.test(value));
7
+ }
@@ -1,19 +1,25 @@
1
- import type { RepositoryFileInput } from "./github.js";
1
+ import type { RepositoryFileInput } from "./types.js";
2
2
  import type { ContinuationFinishedDetails, ContinuationOutcome, ContinuationTarget, DescriptionWriteOutcome, FinalizeChangesDetails, FinalizeChangesResult, PreparedWorkspace, VcsPrepareInput, VcsProvider } from "./types.js";
3
3
  /** Keeps the `vcs_github_not_configured` code prefix that error matching and categorization key on. */
4
4
  export declare const VCS_GITHUB_NOT_CONFIGURED_ERROR = "vcs_github_not_configured: set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY to run coding agents on GitHub repositories";
5
+ /** Keeps a stable code prefix for error matching. The provider name comes from a validated repository id. */
6
+ export declare const VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR = "vcs_host_provider_not_configured";
5
7
  export interface RoutingVcsOptions {
6
8
  /** Null when no GitHub App is configured. */
7
9
  github: VcsProvider | null;
8
10
  /** Null when no local repository roots are configured. */
9
11
  local: VcsProvider | null;
12
+ /** Registered host providers, by name. */
13
+ hosted?: ReadonlyMap<string, VcsProvider>;
10
14
  }
11
15
  /**
12
- * One VcsProvider over both repository kinds: `local:/abs/path` goes to the
13
- * local provider, everything else to GitHub. A kind that is not configured
14
- * fails only when a repository of that kind is actually used, so a server with
15
- * only one of the two still starts. The repository name is not re-resolved
16
- * here: the local provider verifies it against the trusted roots.
16
+ * One VcsProvider over every repository kind: `local:/abs/path` goes to the
17
+ * local provider, `<provider>:<workspace>/<repo>` to the registered host
18
+ * provider of that name, and everything else to GitHub. A kind that is not
19
+ * configured fails only when a repository of that kind is actually used, so a
20
+ * server with only some of them still starts. A hosted repository never falls
21
+ * back to GitHub. The repository name is not re-resolved here: the local
22
+ * provider verifies it against the trusted roots.
17
23
  */
18
24
  export declare class RoutingVcsProvider implements VcsProvider {
19
25
  private readonly options;
@@ -1,12 +1,17 @@
1
+ import { codeProviderOf } from "../../coding/code-host.js";
1
2
  import { LocalRepoError, isLocalRepository } from "../../coding/local-repo.js";
2
3
  /** Keeps the `vcs_github_not_configured` code prefix that error matching and categorization key on. */
3
4
  export const VCS_GITHUB_NOT_CONFIGURED_ERROR = "vcs_github_not_configured: set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY to run coding agents on GitHub repositories";
5
+ /** Keeps a stable code prefix for error matching. The provider name comes from a validated repository id. */
6
+ export const VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR = "vcs_host_provider_not_configured";
4
7
  /**
5
- * One VcsProvider over both repository kinds: `local:/abs/path` goes to the
6
- * local provider, everything else to GitHub. A kind that is not configured
7
- * fails only when a repository of that kind is actually used, so a server with
8
- * only one of the two still starts. The repository name is not re-resolved
9
- * here: the local provider verifies it against the trusted roots.
8
+ * One VcsProvider over every repository kind: `local:/abs/path` goes to the
9
+ * local provider, `<provider>:<workspace>/<repo>` to the registered host
10
+ * provider of that name, and everything else to GitHub. A kind that is not
11
+ * configured fails only when a repository of that kind is actually used, so a
12
+ * server with only some of them still starts. A hosted repository never falls
13
+ * back to GitHub. The repository name is not re-resolved here: the local
14
+ * provider verifies it against the trusted roots.
10
15
  */
11
16
  export class RoutingVcsProvider {
12
17
  options;
@@ -20,13 +25,23 @@ export class RoutingVcsProvider {
20
25
  }
21
26
  return this.options.local;
22
27
  }
28
+ const provider = codeProviderOf(repository);
29
+ if (provider !== "github") {
30
+ const hosted = this.options.hosted?.get(provider);
31
+ if (!hosted)
32
+ throw new Error(`${VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR}: no host provider named ${provider}`);
33
+ return hosted;
34
+ }
23
35
  if (!this.options.github)
24
36
  throw new Error(VCS_GITHUB_NOT_CONFIGURED_ERROR);
25
37
  return this.options.github;
26
38
  }
27
39
  /** The target when configured, else null (best-effort calls that must never throw). */
28
40
  optionalTarget(repository) {
29
- return (isLocalRepository(repository) ? this.options.local : this.options.github) ?? null;
41
+ if (isLocalRepository(repository))
42
+ return this.options.local ?? null;
43
+ const provider = codeProviderOf(repository);
44
+ return (provider === "github" ? this.options.github : this.options.hosted?.get(provider)) ?? null;
30
45
  }
31
46
  async prepareWorkspace(input) {
32
47
  return this.target(input.repository).prepareWorkspace(input);
@@ -1,5 +1,4 @@
1
1
  import type { PackageAuditReport } from "../../coding/registry/audit-mode.js";
2
- import type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestsInput, RepositoryFileInput } from "./github.js";
3
2
  /** What happened to a pull request description: wardby just wrote it (a new PR) or updated a fix round's. */
4
3
  export type DescriptionWriteOutcome = {
5
4
  kind: "written";
@@ -168,8 +167,8 @@ export interface VcsProvider {
168
167
  * Best-effort "wardby is working on this" signal for a continuation
169
168
  * a no-op when `workspace.continuation` is unset, since a fresh run has
170
169
  * no PR to attach anything to until its one commit lands. Optional
171
- * because this is a GitHub-specific concept, not a universal VCS one --
172
- * a future non-GitHub provider (or a test double) can simply omit it,
170
+ * because not every host has this signal (GitHub does; others may too) --
171
+ * a provider (or a test double) can simply omit it,
173
172
  * mirroring `Executor.resolveCodingWorkerImage?` (dispatch.ts calls it
174
173
  * with `?.()`). Implementations MUST NOT throw: this is strictly
175
174
  * observability, never allowed to affect the real coding run.
@@ -248,3 +247,72 @@ export interface ContinuationFinishedDetails {
248
247
  pullRequestNumber?: number;
249
248
  };
250
249
  }
250
+ /** What wardby last wrote into a pull request description (control-plane records). */
251
+ export interface DescriptionBaseline {
252
+ /** Null = legacy pull request (no recorded write). */
253
+ recordedSha256: string | null;
254
+ /**
255
+ * Legacy pull requests only: the hash of the body wardby would have written
256
+ * for the root run, rebuilt from stored records. A current body with this
257
+ * hash is unedited. Absent when the records can't rebuild it (then the
258
+ * description counts as edited).
259
+ */
260
+ legacySha256?: string;
261
+ /** Stored summary of the run whose text the description carries (root run for legacy). */
262
+ previousAgentText?: string;
263
+ /** That run's id. */
264
+ agentTextRunId: string;
265
+ }
266
+ /** `agentTextRunId` is the run whose summary the description now carries. */
267
+ export type DescriptionUpdateResult = {
268
+ kind: "replaced" | "unchanged";
269
+ sha256: string;
270
+ agentTextRunId: string;
271
+ } | {
272
+ kind: "refreshed";
273
+ reason: "empty" | "too_short" | "missing_closing_keyword";
274
+ sha256: string;
275
+ agentTextRunId: string;
276
+ } | {
277
+ kind: "edited_by_hand";
278
+ commentPosted: boolean;
279
+ } | {
280
+ kind: "skipped";
281
+ reason: "not_open" | "too_long" | "error";
282
+ };
283
+ /** One file at one ref, read through the API without a clone. */
284
+ export interface RepositoryFileInput {
285
+ repository: string;
286
+ ref: string;
287
+ /** Repository-relative POSIX path. */
288
+ path: string;
289
+ /** Refused above this many bytes. */
290
+ maxBytes: number;
291
+ }
292
+ export type RelatedPullRequestState = "open" | "draft" | "merged" | "closed";
293
+ /** One pull request of the request's set, from stored control-plane rows only. */
294
+ export interface RelatedPullRequestEntry {
295
+ repository: string;
296
+ /** Absent only for `self` while the pull request is being created. */
297
+ number?: number;
298
+ /** Live state; absent when unknown (at creation). */
299
+ state?: RelatedPullRequestState;
300
+ /** The pull request whose description this section is in. */
301
+ self?: boolean;
302
+ /**
303
+ * Step in the delegating agent's declared merge order (1-99), from
304
+ * CodingRun.mergeOrder; absent = the delegating agent set no order
305
+ * (an open entry is listed under "Not ordered:" when any other entry,
306
+ * open or done, has one).
307
+ */
308
+ mergeOrder?: number;
309
+ }
310
+ export interface RelatedPullRequestsInput {
311
+ /** In merge order: by mergeOrder when set, else the order the request's coding runs were dispatched. */
312
+ entries: readonly RelatedPullRequestEntry[];
313
+ issue?: {
314
+ key: string;
315
+ url?: string;
316
+ trackerName?: string;
317
+ };
318
+ }
@@ -5,6 +5,11 @@
5
5
  */
6
6
  export declare function mcpAddArgs(client: "codex" | "claude", projectDir: string, packageSpec: string): string[];
7
7
  export declare function quickstartCommand(args: string[]): Promise<void>;
8
+ /**
9
+ * Doctor's Claude Code pane section. The pane is optional, so its lines are information: a "✗"
10
+ * from them would be read as a failure, so none reaches the output.
11
+ */
12
+ export declare function claudePaneDoctorLines<D>(lines: (deps: D) => string[], deps: D): string[];
8
13
  export declare function doctorCommand(args: string[]): Promise<void>;
9
14
  export declare function statusCommand(args: string[]): Promise<void>;
10
15
  export declare function logsCommand(args: string[]): Promise<void>;
@@ -360,6 +360,7 @@ export async function quickstartCommand(args) {
360
360
  client: { type: "string" },
361
361
  "non-interactive": { type: "boolean" },
362
362
  "skip-demo": { type: "boolean" },
363
+ "claude-plugin": { type: "boolean" },
363
364
  yes: { type: "boolean", short: "y" },
364
365
  },
365
366
  });
@@ -447,6 +448,15 @@ export async function quickstartCommand(args) {
447
448
  const codingRan = coding?.seed !== undefined && coding.seed.builder.status !== "skipped" && coding.seed.reviewer.status !== "skipped";
448
449
  const client = parseClient(values.client) ?? (await chooseClient(nonInteractive));
449
450
  const mcpConfigured = client !== "none" && configureMcpClients(client, paths);
451
+ // With Claude Code connected, offer the runs pane (the wardby-runs plugin): off unless asked for.
452
+ {
453
+ const { claudePluginDeps, installPlugin, quickstartPaneStep } = await import("../claude-plugin/command.js");
454
+ await quickstartPaneStep({ client, mcpConfigured, flag: values["claude-plugin"], nonInteractive, yes: values.yes ?? false }, {
455
+ ask: (question) => promptYesNo(question, false),
456
+ install: (options) => installPlugin(options, claudePluginDeps()),
457
+ warn: (line) => console.warn(line),
458
+ });
459
+ }
450
460
  console.log("\nWardby is ready.");
451
461
  console.log(" npx @wardby/cli@latest status");
452
462
  console.log(" npx @wardby/cli@latest doctor");
@@ -471,6 +481,13 @@ async function databaseHealthy(paths) {
471
481
  await db.$disconnect();
472
482
  }
473
483
  }
484
+ /**
485
+ * Doctor's Claude Code pane section. The pane is optional, so its lines are information: a "✗"
486
+ * from them would be read as a failure, so none reaches the output.
487
+ */
488
+ export function claudePaneDoctorLines(lines, deps) {
489
+ return lines(deps).map((line) => (line.startsWith("✗") ? `!${line.slice(1)}` : line));
490
+ }
474
491
  export async function doctorCommand(args) {
475
492
  parseArgs({ args, options: {} });
476
493
  const paths = quickstartPaths(resolveProjectDir());
@@ -520,6 +537,10 @@ export async function doctorCommand(args) {
520
537
  failed ||= line.startsWith("✗");
521
538
  console.log(line);
522
539
  }
540
+ // The Claude Code runs pane is optional: its lines never fail doctor.
541
+ const { claudePluginDeps, claudePluginDoctorLines } = await import("../claude-plugin/command.js");
542
+ for (const line of claudePaneDoctorLines(claudePluginDoctorLines, claudePluginDeps()))
543
+ console.log(line);
523
544
  const base = baseImageLine(resolveQuickstartImages({ env: runtimeEnv(paths), packageRoot }));
524
545
  if (base)
525
546
  console.log(base);
@@ -1 +1 @@
1
- {"runtime":"ghcr.io/wardby/wardby/wardby-runtime@sha256:e7458c9fba898c8315f3478b1ad0585d54e099d266aad65efe13d14d000fb49c","worker":"ghcr.io/wardby/wardby/wardby-coding-worker@sha256:e0fb40eff1583a63830c2647c4397e8553f4cc1f3005dd7a745b7936cc79d18c","claudeWorker":"ghcr.io/wardby/wardby/wardby-claude-coding-worker@sha256:f6ecf8c2195f4a8b7fcd859db2e6c101f0494e5eb5447a4613030ba9e3d966f2","claudeToolRunner":"ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:efd0987be3a2ed67695fd8b4309762a33f0d0eb8dadd03bf5d6bda12108f282a","workerNodePython":"ghcr.io/wardby/wardby/wardby-coding-worker-node-python@sha256:ea42d825288b34da8d309fda3f344a8bdfb66be50c6179018daef5a98f7938f0","claudeToolRunnerNodePython":"ghcr.io/wardby/wardby/wardby-claude-tool-runner-node-python@sha256:876b387f0ada205ccf5cc5f6c0a07e5faf8d8dd47d21611df0905bfba5dd99f4","nativeWorker":"ghcr.io/wardby/wardby/wardby-native-worker@sha256:4ecf9ebf498de9f5e70f313e750e38bcba72a71c334c5f68413a71242c0bf18c","driver":"ghcr.io/wardby/wardby/wardby-coding-worker-driver@sha256:a9af4a2561c1aeb5876159fcbb28a3ae2ce386fe27c161ebb9855bd092ec3fc8"}
1
+ {"runtime":"ghcr.io/wardby/wardby/wardby-runtime@sha256:d5cb8fcfbfd43c230418374e9a5e203528b7d7fd372c01c8545284bc261edd58","worker":"ghcr.io/wardby/wardby/wardby-coding-worker@sha256:943a4023bcdc86d84fb923ebd97e3b0b0b5c0055ea81542cb9972ddc6ee36e69","claudeWorker":"ghcr.io/wardby/wardby/wardby-claude-coding-worker@sha256:861ef8f23bd70c329cde7013191371737d266b854678942d328906b29242d08f","claudeToolRunner":"ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:247a870862a3b20d02b6d0a2a87ef7b3d2ae57a30478b6d517ae58535bf54260","workerNodePython":"ghcr.io/wardby/wardby/wardby-coding-worker-node-python@sha256:9684306a84da26ea057b6ccdaaa73f7377820a74bb30fe1740afb8c734da4300","claudeToolRunnerNodePython":"ghcr.io/wardby/wardby/wardby-claude-tool-runner-node-python@sha256:71bd16fd9635eee62b561771da9568120db25ec259807aa070c2eb7865dbbf0f","nativeWorker":"ghcr.io/wardby/wardby/wardby-native-worker@sha256:33606de7284c11dee354a44b6563cc7999fd135d71545c1e63244d867f1a7b2c","driver":"ghcr.io/wardby/wardby/wardby-coding-worker-driver@sha256:281a7cabc53d6ff1977d1b68c1420b2ef598f90da3995cb13df27395a58a60ef"}
@@ -6,7 +6,7 @@
6
6
  * compilation/validation is pure computation, no host I/O at all).
7
7
  */
8
8
  import type { QuickJSContext, QuickJSRuntime } from "quickjs-emscripten";
9
- export type SandboxErrorKind = "thrown" | "timeout" | "memory" | "cpu" | "non_serializable";
9
+ export type SandboxErrorKind = "thrown" | "timeout" | "memory" | "cpu" | "non_serializable" | "cancelled";
10
10
  export type SandboxResult = {
11
11
  ok: true;
12
12
  value: unknown;
@@ -34,4 +34,14 @@ export declare function classifyEvalError(dumped: {
34
34
  name?: string;
35
35
  message?: string;
36
36
  }): SandboxResult;
37
- export declare function evalToJson(code: string, limitsOverride: Partial<SandboxLimits> | undefined, installExtras: (context: QuickJSContext, runtime: QuickJSRuntime, signal: AbortSignal) => void): Promise<SandboxResult>;
37
+ export interface EvalOptions {
38
+ /**
39
+ * Ends the evaluation early when it aborts (a stopped run): the result is `aborted(reason)`,
40
+ * and every host call still in flight is aborted with it.
41
+ */
42
+ signal?: AbortSignal;
43
+ aborted?: (reason: unknown) => SandboxResult;
44
+ /** The wall-time error's text; defaults to naming the limit. */
45
+ timeoutMessage?: (limitMs: number) => string;
46
+ }
47
+ export declare function evalToJson(code: string, limitsOverride: Partial<SandboxLimits> | undefined, installExtras: (context: QuickJSContext, runtime: QuickJSRuntime, signal: AbortSignal) => void, options?: EvalOptions): Promise<SandboxResult>;
@@ -56,9 +56,14 @@ export function classifyEvalError(dumped) {
56
56
  errorMessage: dumped.message ? `${dumped.name ?? "Error"}: ${dumped.message}` : JSON.stringify(dumped),
57
57
  };
58
58
  }
59
- export async function evalToJson(code, limitsOverride, installExtras) {
59
+ const defaultAborted = () => ({ ok: false, errorKind: "cancelled", errorMessage: "Cancelled." });
60
+ export async function evalToJson(code, limitsOverride, installExtras, options = {}) {
60
61
  const limits = { ...DEFAULT_SANDBOX_LIMITS, ...limitsOverride };
62
+ const aborted = options.aborted ?? defaultAborted;
63
+ if (options.signal?.aborted)
64
+ return aborted(options.signal.reason);
61
65
  const controller = new AbortController();
66
+ let onAbort;
62
67
  let timer;
63
68
  const deadline = Date.now() + limits.wallTimeLimitMs;
64
69
  const qjsModule = await getQuickJsModule();
@@ -68,7 +73,7 @@ export async function evalToJson(code, limitsOverride, installExtras) {
68
73
  let steps = 0;
69
74
  runtime.setInterruptHandler(() => {
70
75
  steps += 1;
71
- return steps > limits.maxInterruptChecks || Date.now() >= deadline;
76
+ return steps > limits.maxInterruptChecks || Date.now() >= deadline || options.signal?.aborted === true;
72
77
  });
73
78
  const context = runtime.newContext();
74
79
  let disposed = false;
@@ -139,10 +144,19 @@ export async function evalToJson(code, limitsOverride, installExtras) {
139
144
  timer = setTimeout(() => resolve({
140
145
  ok: false,
141
146
  errorKind: "timeout",
142
- errorMessage: `Exceeded the ${limits.wallTimeLimitMs}ms wall-time limit.`,
147
+ errorMessage: options.timeoutMessage
148
+ ? options.timeoutMessage(limits.wallTimeLimitMs)
149
+ : `Exceeded the ${limits.wallTimeLimitMs}ms wall-time limit.`,
143
150
  }), limits.wallTimeLimitMs);
144
151
  });
145
- return await Promise.race([runPromise, timeoutPromise]);
152
+ const external = options.signal;
153
+ const abortPromise = new Promise((resolve) => {
154
+ if (!external)
155
+ return;
156
+ onAbort = () => resolve(aborted(external.reason));
157
+ external.addEventListener("abort", onAbort, { once: true });
158
+ });
159
+ return await Promise.race([runPromise, timeoutPromise, abortPromise]);
146
160
  }
147
161
  catch (err) {
148
162
  return {
@@ -153,6 +167,8 @@ export async function evalToJson(code, limitsOverride, installExtras) {
153
167
  }
154
168
  finally {
155
169
  clearTimeout(timer);
170
+ if (onAbort)
171
+ options.signal?.removeEventListener("abort", onAbort);
156
172
  controller.abort();
157
173
  disposeAll();
158
174
  }
@@ -43,6 +43,8 @@ export interface HostFunctionOptions {
43
43
  redactSecretValues?: readonly string[];
44
44
  /** Overrides the network call — tests only. */
45
45
  fetchImpl?: typeof safeFetch;
46
+ /** Each fetch's timeout (fetchTimeoutFor the tool's limit); the default fetch timeout when absent. */
47
+ fetchTimeoutMs?: number;
46
48
  }
47
49
  /** One host bridge: JSON-string arguments in, a JSON-serializable value out (bridge.ts). */
48
50
  export type HostBridge = (argsJson: string) => Promise<unknown>;
@@ -63,6 +65,8 @@ export declare function createPrivilegedHost(options: PrivilegedHostOptions): Pr
63
65
  export interface SandboxApiOptions {
64
66
  privileged: PrivilegedHost;
65
67
  signal?: AbortSignal;
68
+ /** The tool's time limit, which also caps sleep(); the default wall-time cap when absent. */
69
+ wallTimeLimitMs?: number;
66
70
  /** Overrides the shared default parser-worker pool — mainly for tests. */
67
71
  parserPool?: ParserWorkerPool;
68
72
  }