@wardby/cli 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (555) hide show
  1. package/.env.example +53 -4
  2. package/README.md +52 -13
  3. package/dist/claude-coding-worker/driver.d.ts +6 -1
  4. package/dist/claude-coding-worker/driver.js +27 -1
  5. package/dist/claude-coding-worker/main.js +2 -0
  6. package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
  7. package/dist/claude-coding-worker/tool-socket.js +26 -0
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +14 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +284 -36
  12. package/dist/coding/base-commit.d.ts +6 -0
  13. package/dist/coding/base-commit.js +12 -0
  14. package/dist/coding/collect-exclude.d.ts +25 -0
  15. package/dist/coding/collect-exclude.js +76 -0
  16. package/dist/coding/profile.d.ts +67 -22
  17. package/dist/coding/profile.js +58 -25
  18. package/dist/coding/protected-path-wording.d.ts +24 -0
  19. package/dist/coding/protected-path-wording.js +55 -0
  20. package/dist/coding/protected-paths.d.ts +31 -0
  21. package/dist/coding/protected-paths.js +48 -0
  22. package/dist/coding/protocol.d.ts +91 -4
  23. package/dist/coding/protocol.js +123 -12
  24. package/dist/coding/provider.d.ts +6 -1
  25. package/dist/coding/provider.js +16 -9
  26. package/dist/coding/registry/adapters.d.ts +2 -0
  27. package/dist/coding/registry/adapters.js +9 -0
  28. package/dist/coding/registry/allowlist.d.ts +9 -0
  29. package/dist/coding/registry/allowlist.js +28 -0
  30. package/dist/coding/registry/json-scan.d.ts +55 -0
  31. package/dist/coding/registry/json-scan.js +191 -0
  32. package/dist/coding/registry/lockfiles.d.ts +6 -0
  33. package/dist/coding/registry/lockfiles.js +89 -0
  34. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  35. package/dist/coding/registry/npm-lockfile.js +73 -0
  36. package/dist/coding/registry/npm-plan.d.ts +29 -0
  37. package/dist/coding/registry/npm-plan.js +289 -0
  38. package/dist/coding/registry/npm.d.ts +7 -0
  39. package/dist/coding/registry/npm.js +252 -0
  40. package/dist/coding/registry/pypi.d.ts +4 -0
  41. package/dist/coding/registry/pypi.js +210 -0
  42. package/dist/coding/registry/report.d.ts +37 -0
  43. package/dist/coding/registry/report.js +31 -0
  44. package/dist/coding/registry/token.d.ts +4 -0
  45. package/dist/coding/registry/token.js +7 -0
  46. package/dist/coding/registry/types.d.ts +286 -0
  47. package/dist/coding/registry/types.js +19 -0
  48. package/dist/coding/registry/worker-config.d.ts +14 -0
  49. package/dist/coding/registry/worker-config.js +31 -0
  50. package/dist/coding/services/builtins.d.ts +22 -0
  51. package/dist/coding/services/builtins.js +85 -0
  52. package/dist/coding/services/catalog.d.ts +443 -0
  53. package/dist/coding/services/catalog.js +159 -0
  54. package/dist/coding/services/declaration.d.ts +11 -0
  55. package/dist/coding/services/declaration.js +114 -0
  56. package/dist/coding/services/note.d.ts +8 -0
  57. package/dist/coding/services/note.js +15 -0
  58. package/dist/coding/services/resolve.d.ts +25 -0
  59. package/dist/coding/services/resolve.js +50 -0
  60. package/dist/coding/services/wording.d.ts +25 -0
  61. package/dist/coding/services/wording.js +70 -0
  62. package/dist/coding-proxy/main.js +24 -4
  63. package/dist/coding-worker/artifact.d.ts +6 -0
  64. package/dist/coding-worker/debug-trace.d.ts +37 -0
  65. package/dist/coding-worker/debug-trace.js +117 -0
  66. package/dist/coding-worker/driver.d.ts +13 -1
  67. package/dist/coding-worker/driver.js +102 -13
  68. package/dist/coding-worker/errors.js +8 -0
  69. package/dist/coding-worker/main.js +10 -2
  70. package/dist/coding-worker/sdk.d.ts +2 -2
  71. package/dist/coding-worker/sdk.js +7 -2
  72. package/dist/coding-worker/types.d.ts +3 -0
  73. package/dist/config/providers.d.ts +66 -0
  74. package/dist/config/providers.js +137 -0
  75. package/dist/core/attribution.d.ts +101 -0
  76. package/dist/core/attribution.js +208 -0
  77. package/dist/core/budget-groups.d.ts +120 -10
  78. package/dist/core/budget-groups.js +133 -24
  79. package/dist/core/budget-wording.d.ts +22 -0
  80. package/dist/core/budget-wording.js +55 -0
  81. package/dist/core/coding-queue.d.ts +3 -0
  82. package/dist/core/coding-queue.js +6 -2
  83. package/dist/core/coding-service-status.d.ts +11 -0
  84. package/dist/core/coding-service-status.js +17 -0
  85. package/dist/core/cost-report.d.ts +88 -0
  86. package/dist/core/cost-report.js +248 -0
  87. package/dist/core/datastores.js +18 -2
  88. package/dist/core/db.d.ts +5 -1
  89. package/dist/core/db.js +8 -2
  90. package/dist/core/dispatch.d.ts +74 -7
  91. package/dist/core/dispatch.js +382 -47
  92. package/dist/core/engine-native.js +44 -14
  93. package/dist/core/glob.d.ts +10 -0
  94. package/dist/core/glob.js +33 -0
  95. package/dist/core/grants.d.ts +86 -0
  96. package/dist/core/grants.js +126 -0
  97. package/dist/core/host-events.d.ts +72 -0
  98. package/dist/core/host-events.js +597 -0
  99. package/dist/core/host-identity-links.d.ts +54 -0
  100. package/dist/core/host-identity-links.js +189 -0
  101. package/dist/core/host-status.d.ts +78 -0
  102. package/dist/core/host-status.js +228 -0
  103. package/dist/core/in-flight-runs.d.ts +8 -0
  104. package/dist/core/in-flight-runs.js +56 -0
  105. package/dist/core/issue-bridge.d.ts +60 -0
  106. package/dist/core/issue-bridge.js +189 -0
  107. package/dist/core/issue-dedupe.d.ts +70 -0
  108. package/dist/core/issue-dedupe.js +255 -0
  109. package/dist/core/issue-events.d.ts +42 -0
  110. package/dist/core/issue-events.js +155 -0
  111. package/dist/core/issue-status.d.ts +29 -0
  112. package/dist/core/issue-status.js +241 -0
  113. package/dist/core/issue-tracker-tools.d.ts +64 -0
  114. package/dist/core/issue-tracker-tools.js +850 -0
  115. package/dist/core/model-usage.d.ts +10 -0
  116. package/dist/core/model-usage.js +24 -0
  117. package/dist/core/provider-wording.d.ts +12 -0
  118. package/dist/core/provider-wording.js +44 -0
  119. package/dist/core/reconciler.d.ts +42 -2
  120. package/dist/core/reconciler.js +97 -2
  121. package/dist/core/repo-access.d.ts +99 -0
  122. package/dist/core/repo-access.js +136 -0
  123. package/dist/core/review-host-checks.d.ts +11 -0
  124. package/dist/core/review-host-checks.js +41 -0
  125. package/dist/core/review-host-tools.d.ts +47 -0
  126. package/dist/core/review-host-tools.js +354 -0
  127. package/dist/core/run-heartbeat.d.ts +27 -0
  128. package/dist/core/run-heartbeat.js +54 -0
  129. package/dist/core/run-pricing.d.ts +61 -0
  130. package/dist/core/run-pricing.js +56 -0
  131. package/dist/core/runner.d.ts +33 -8
  132. package/dist/core/runner.js +410 -62
  133. package/dist/core/scheduler.d.ts +4 -1
  134. package/dist/core/scheduler.js +3 -2
  135. package/dist/core/secrets.d.ts +11 -2
  136. package/dist/core/secrets.js +25 -6
  137. package/dist/core/self-defects.d.ts +80 -0
  138. package/dist/core/self-defects.js +180 -0
  139. package/dist/core/subagent-memory-tools.d.ts +1 -1
  140. package/dist/core/subagent-memory-tools.js +16 -3
  141. package/dist/core/tool-admin.d.ts +81 -0
  142. package/dist/core/tool-admin.js +129 -0
  143. package/dist/core/tool-names.d.ts +42 -0
  144. package/dist/core/tool-names.js +67 -0
  145. package/dist/core/untrusted-content.d.ts +32 -0
  146. package/dist/core/untrusted-content.js +72 -0
  147. package/dist/core/webhooks.d.ts +17 -2
  148. package/dist/core/webhooks.js +42 -3
  149. package/dist/generated/prisma/browser.d.ts +166 -0
  150. package/dist/generated/prisma/client.d.ts +166 -0
  151. package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
  152. package/dist/generated/prisma/enums.d.ts +13 -0
  153. package/dist/generated/prisma/enums.js +12 -1
  154. package/dist/generated/prisma/internal/class.d.ts +242 -0
  155. package/dist/generated/prisma/internal/class.js +4 -4
  156. package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
  157. package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
  158. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
  159. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
  160. package/dist/generated/prisma/models/Agent.d.ts +682 -1
  161. package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
  162. package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
  163. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  164. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  165. package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
  166. package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
  167. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
  168. package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
  169. package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
  170. package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
  171. package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
  172. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  173. package/dist/generated/prisma/models/CodingService.js +1 -0
  174. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  175. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  176. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  177. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  178. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  179. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  180. package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
  181. package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
  182. package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
  183. package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
  184. package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
  185. package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
  186. package/dist/generated/prisma/models/Principal.d.ts +455 -0
  187. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  188. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  189. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  190. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  191. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  192. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  193. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  194. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  195. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  196. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  197. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  198. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  199. package/dist/generated/prisma/models/Run.d.ts +1402 -82
  200. package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
  201. package/dist/generated/prisma/models/RunAttribution.js +1 -0
  202. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  203. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  204. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  205. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  206. package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
  207. package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
  208. package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
  209. package/dist/generated/prisma/models/RunModelUsage.js +1 -0
  210. package/dist/generated/prisma/models/Tool.d.ts +15 -3
  211. package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
  212. package/dist/generated/prisma/models/WorkItem.js +1 -0
  213. package/dist/generated/prisma/models.d.ts +22 -0
  214. package/dist/help/build.d.ts +1 -0
  215. package/dist/help/build.js +9 -0
  216. package/dist/help/catalog.d.ts +24 -0
  217. package/dist/help/catalog.js +160 -0
  218. package/dist/help/cli.d.ts +2 -0
  219. package/dist/help/cli.js +65 -0
  220. package/dist/help/runtime.d.ts +3 -0
  221. package/dist/help/runtime.js +44 -0
  222. package/dist/help/search.d.ts +9 -0
  223. package/dist/help/search.js +104 -0
  224. package/dist/help-index.json +999 -0
  225. package/dist/import/cli-args.js +3 -2
  226. package/dist/import/create.d.ts +5 -0
  227. package/dist/import/create.js +67 -13
  228. package/dist/import/index.js +19 -9
  229. package/dist/import/neutral-schema.d.ts +18 -18
  230. package/dist/knowledge/check.d.ts +13 -0
  231. package/dist/knowledge/check.js +69 -0
  232. package/dist/knowledge/cli.d.ts +14 -0
  233. package/dist/knowledge/cli.js +67 -0
  234. package/dist/knowledge/concept.d.ts +54 -0
  235. package/dist/knowledge/concept.js +78 -0
  236. package/dist/knowledge/note.d.ts +11 -0
  237. package/dist/knowledge/note.js +39 -0
  238. package/dist/knowledge/relevance.d.ts +11 -0
  239. package/dist/knowledge/relevance.js +14 -0
  240. package/dist/knowledge/span-hash.d.ts +3 -0
  241. package/dist/knowledge/span-hash.js +16 -0
  242. package/dist/mcp/auth/access.d.ts +72 -0
  243. package/dist/mcp/auth/access.js +58 -0
  244. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  245. package/dist/mcp/auth/grants-cli.js +518 -0
  246. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  247. package/dist/mcp/auth/host-account-cli.js +47 -0
  248. package/dist/mcp/auth/ownership.d.ts +25 -51
  249. package/dist/mcp/auth/ownership.js +19 -14
  250. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  251. package/dist/mcp/auth/repo-authorization.js +51 -0
  252. package/dist/mcp/auth/resource-server.d.ts +33 -2
  253. package/dist/mcp/auth/resource-server.js +99 -3
  254. package/dist/mcp/auth/self-hosted/browser.js +2 -2
  255. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  256. package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
  257. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  258. package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
  259. package/dist/mcp/context.d.ts +30 -1
  260. package/dist/mcp/errors.d.ts +25 -6
  261. package/dist/mcp/errors.js +98 -0
  262. package/dist/mcp/host-events/deliveries.d.ts +9 -0
  263. package/dist/mcp/host-events/deliveries.js +17 -0
  264. package/dist/mcp/host-events/github-ingress.d.ts +38 -0
  265. package/dist/mcp/host-events/github-ingress.js +83 -0
  266. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  267. package/dist/mcp/host-events/github-user-callback.js +50 -0
  268. package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
  269. package/dist/mcp/host-events/jira-ingress.js +92 -0
  270. package/dist/mcp/index.d.ts +10 -1
  271. package/dist/mcp/index.js +187 -19
  272. package/dist/mcp/server.js +32 -11
  273. package/dist/mcp/tools/agents.js +519 -50
  274. package/dist/mcp/tools/budget-groups.js +3 -3
  275. package/dist/mcp/tools/cost-report.d.ts +8 -0
  276. package/dist/mcp/tools/cost-report.js +60 -0
  277. package/dist/mcp/tools/datastore.js +22 -15
  278. package/dist/mcp/tools/grants.d.ts +2 -0
  279. package/dist/mcp/tools/grants.js +239 -0
  280. package/dist/mcp/tools/help.d.ts +5 -0
  281. package/dist/mcp/tools/help.js +67 -0
  282. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  283. package/dist/mcp/tools/host-accounts.js +114 -0
  284. package/dist/mcp/tools/issue-projects.d.ts +2 -0
  285. package/dist/mcp/tools/issue-projects.js +238 -0
  286. package/dist/mcp/tools/memory.d.ts +7 -1
  287. package/dist/mcp/tools/memory.js +5 -5
  288. package/dist/mcp/tools/model-catalog.d.ts +22 -0
  289. package/dist/mcp/tools/model-catalog.js +423 -0
  290. package/dist/mcp/tools/repositories.d.ts +2 -0
  291. package/dist/mcp/tools/repositories.js +182 -0
  292. package/dist/mcp/tools/runs.d.ts +6 -0
  293. package/dist/mcp/tools/runs.js +60 -6
  294. package/dist/mcp/tools/scheduling.js +3 -6
  295. package/dist/mcp/tools/secrets.js +18 -6
  296. package/dist/mcp/tools/services.d.ts +2 -0
  297. package/dist/mcp/tools/services.js +222 -0
  298. package/dist/mcp/tools/subagents.js +52 -16
  299. package/dist/mcp/tools/tools.d.ts +41 -0
  300. package/dist/mcp/tools/tools.js +201 -40
  301. package/dist/mcp/tools/trigger.js +65 -7
  302. package/dist/mcp/tools/webhooks.js +15 -4
  303. package/dist/mcp/transport/streamable-http.d.ts +15 -0
  304. package/dist/mcp/transport/streamable-http.js +55 -3
  305. package/dist/mcp/webhooks/ingress.d.ts +2 -1
  306. package/dist/mcp/webhooks/ingress.js +9 -2
  307. package/dist/providers/auth/delegating.d.ts +19 -0
  308. package/dist/providers/auth/delegating.js +71 -0
  309. package/dist/providers/auth/index.d.ts +2 -0
  310. package/dist/providers/auth/index.js +11 -0
  311. package/dist/providers/auth/self-hosted.d.ts +10 -2
  312. package/dist/providers/auth/self-hosted.js +55 -6
  313. package/dist/providers/auth/types.d.ts +6 -0
  314. package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
  315. package/dist/providers/coding-proxy/memory-ledger.js +31 -3
  316. package/dist/providers/coding-proxy/metering.d.ts +6 -1
  317. package/dist/providers/coding-proxy/metering.js +41 -4
  318. package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
  319. package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
  320. package/dist/providers/coding-proxy/proxy.d.ts +25 -2
  321. package/dist/providers/coding-proxy/proxy.js +561 -38
  322. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  323. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  324. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  325. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  326. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  327. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  328. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  329. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  330. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  331. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  332. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  333. package/dist/providers/coding-proxy/registry/store.js +70 -0
  334. package/dist/providers/coding-proxy/runtime.d.ts +17 -0
  335. package/dist/providers/coding-proxy/runtime.js +63 -0
  336. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  337. package/dist/providers/coding-proxy/server.d.ts +11 -0
  338. package/dist/providers/coding-proxy/server.js +163 -0
  339. package/dist/providers/coding-proxy/types.d.ts +33 -1
  340. package/dist/providers/coding-proxy/types.js +12 -1
  341. package/dist/providers/engine/types.d.ts +29 -0
  342. package/dist/providers/executor/build.d.ts +2 -2
  343. package/dist/providers/executor/composition.d.ts +3 -0
  344. package/dist/providers/executor/composition.js +28 -1
  345. package/dist/providers/executor/container.d.ts +134 -4
  346. package/dist/providers/executor/container.js +394 -37
  347. package/dist/providers/executor/dbos.d.ts +4 -3
  348. package/dist/providers/executor/dbos.js +7 -5
  349. package/dist/providers/executor/in-process.d.ts +2 -3
  350. package/dist/providers/executor/routing.d.ts +13 -0
  351. package/dist/providers/executor/routing.js +18 -0
  352. package/dist/providers/executor/types.d.ts +34 -0
  353. package/dist/providers/issue-tracker/adf.d.ts +31 -0
  354. package/dist/providers/issue-tracker/adf.js +181 -0
  355. package/dist/providers/issue-tracker/index.d.ts +5 -0
  356. package/dist/providers/issue-tracker/index.js +12 -0
  357. package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
  358. package/dist/providers/issue-tracker/jira-client.js +151 -0
  359. package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
  360. package/dist/providers/issue-tracker/jira-events.js +98 -0
  361. package/dist/providers/issue-tracker/jira.d.ts +116 -0
  362. package/dist/providers/issue-tracker/jira.js +502 -0
  363. package/dist/providers/issue-tracker/types.d.ts +269 -0
  364. package/dist/providers/issue-tracker/types.js +16 -0
  365. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  366. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  367. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  368. package/dist/providers/jobs/collect-prune.js +27 -0
  369. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  370. package/dist/providers/jobs/docker-isolation.js +213 -22
  371. package/dist/providers/jobs/docker-services.d.ts +35 -0
  372. package/dist/providers/jobs/docker-services.js +191 -0
  373. package/dist/providers/jobs/docker.d.ts +53 -2
  374. package/dist/providers/jobs/docker.js +307 -32
  375. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  376. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  377. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  378. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  379. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  380. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  381. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  382. package/dist/providers/jobs/kubernetes.d.ts +24 -2
  383. package/dist/providers/jobs/kubernetes.js +153 -22
  384. package/dist/providers/jobs/service-state.d.ts +22 -0
  385. package/dist/providers/jobs/service-state.js +17 -0
  386. package/dist/providers/jobs/types.d.ts +18 -0
  387. package/dist/providers/llm/anthropic.d.ts +3 -3
  388. package/dist/providers/llm/anthropic.js +3 -5
  389. package/dist/providers/llm/bedrock.d.ts +3 -3
  390. package/dist/providers/llm/bedrock.js +3 -8
  391. package/dist/providers/llm/catalog-lookup.d.ts +10 -0
  392. package/dist/providers/llm/catalog-lookup.js +15 -0
  393. package/dist/providers/llm/catalog-shipped.d.ts +18 -0
  394. package/dist/providers/llm/catalog-shipped.js +197 -0
  395. package/dist/providers/llm/catalog-store.d.ts +58 -0
  396. package/dist/providers/llm/catalog-store.js +138 -0
  397. package/dist/providers/llm/catalog-types.d.ts +66 -0
  398. package/dist/providers/llm/catalog-types.js +64 -0
  399. package/dist/providers/llm/catalog.d.ts +61 -0
  400. package/dist/providers/llm/catalog.js +147 -0
  401. package/dist/providers/llm/claude-messages.d.ts +5 -1
  402. package/dist/providers/llm/claude-messages.js +1 -0
  403. package/dist/providers/llm/claude-provider.d.ts +10 -12
  404. package/dist/providers/llm/claude-provider.js +15 -6
  405. package/dist/providers/llm/index.d.ts +9 -6
  406. package/dist/providers/llm/index.js +8 -5
  407. package/dist/providers/llm/openai.d.ts +14 -5
  408. package/dist/providers/llm/openai.js +24 -14
  409. package/dist/providers/llm/pricing-core.d.ts +5 -3
  410. package/dist/providers/llm/registration.js +8 -12
  411. package/dist/providers/llm/routing.d.ts +21 -11
  412. package/dist/providers/llm/routing.js +47 -13
  413. package/dist/providers/llm/types.d.ts +12 -0
  414. package/dist/providers/llm/types.js +8 -1
  415. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  416. package/dist/providers/review-host/diff-lines.js +59 -0
  417. package/dist/providers/review-host/github-events.d.ts +6 -0
  418. package/dist/providers/review-host/github-events.js +226 -0
  419. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  420. package/dist/providers/review-host/github-user-auth.js +128 -0
  421. package/dist/providers/review-host/github.d.ts +57 -0
  422. package/dist/providers/review-host/github.js +568 -0
  423. package/dist/providers/review-host/index.d.ts +12 -0
  424. package/dist/providers/review-host/index.js +30 -0
  425. package/dist/providers/review-host/review-format.d.ts +19 -0
  426. package/dist/providers/review-host/review-format.js +59 -0
  427. package/dist/providers/review-host/types.d.ts +309 -0
  428. package/dist/providers/review-host/types.js +24 -0
  429. package/dist/providers/vcs/git.d.ts +16 -6
  430. package/dist/providers/vcs/git.js +78 -34
  431. package/dist/providers/vcs/github.d.ts +94 -3
  432. package/dist/providers/vcs/github.js +223 -17
  433. package/dist/providers/vcs/types.d.ts +48 -4
  434. package/dist/quickstart/index.d.ts +2 -0
  435. package/dist/quickstart/index.js +36 -8
  436. package/dist/sandbox/fetch-policy.d.ts +20 -2
  437. package/dist/sandbox/fetch-policy.js +64 -3
  438. package/dist/sandbox/host-functions.d.ts +9 -1
  439. package/dist/sandbox/host-functions.js +12 -5
  440. package/dist/serve.js +8 -2
  441. package/dist/viewer/api-schema.d.ts +2757 -0
  442. package/dist/viewer/api-schema.js +165 -0
  443. package/dist/viewer/build-schemas.d.ts +2 -0
  444. package/dist/viewer/build-schemas.js +18 -0
  445. package/dist/viewer/event-bus.d.ts +38 -0
  446. package/dist/viewer/event-bus.js +232 -0
  447. package/dist/viewer/graph.d.ts +40 -0
  448. package/dist/viewer/graph.js +243 -0
  449. package/dist/viewer/http.d.ts +30 -0
  450. package/dist/viewer/http.js +133 -0
  451. package/dist/viewer/run-detail.d.ts +4 -0
  452. package/dist/viewer/run-detail.js +61 -0
  453. package/dist/wardby-bin.js +11 -0
  454. package/docs/README.md +40 -0
  455. package/docs/agent-recipes.md +383 -0
  456. package/docs/architecture-runtime.md +90 -0
  457. package/docs/assets/brand/wardby-icon-512.png +0 -0
  458. package/docs/assets/brand/wardby-icon.svg +16 -0
  459. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  460. package/docs/assets/brand/wardby-mascot.png +0 -0
  461. package/docs/assets/brand/wardby-mascot.svg +5 -0
  462. package/docs/assets/wardby-workflow.svg +106 -0
  463. package/docs/code-review-agents.md +508 -0
  464. package/docs/coding-agent-setup.md +175 -0
  465. package/docs/coding-packages.md +455 -0
  466. package/docs/coding-services.md +300 -0
  467. package/docs/coding-worker-byo-images.md +98 -0
  468. package/docs/coding-worker-isolation.md +1068 -0
  469. package/docs/getting-started-gke.md +632 -0
  470. package/docs/getting-started-identity-provider.md +319 -0
  471. package/docs/getting-started.md +147 -0
  472. package/docs/jira-agents.md +649 -0
  473. package/docs/knowledge.md +387 -0
  474. package/docs/models.md +221 -0
  475. package/docs/observability.md +53 -0
  476. package/docs/release-verification.md +66 -0
  477. package/docs/security-deployment.md +667 -0
  478. package/docs/viewer-api.md +142 -0
  479. package/help/admin-viewer.md +39 -0
  480. package/help/agent-recipes.md +173 -0
  481. package/help/architecture-agent.md +189 -0
  482. package/help/builder-agent.md +80 -0
  483. package/help/code-review-agents.md +37 -0
  484. package/help/coding-packages.md +31 -0
  485. package/help/coding-services.md +71 -0
  486. package/help/cost-attribution.md +67 -0
  487. package/help/creating-agents.md +90 -0
  488. package/help/deploy-gke.md +45 -0
  489. package/help/deployment-targets.md +39 -0
  490. package/help/errors/budget-group-exhausted.md +25 -0
  491. package/help/errors/docker-isolation-unsupported.md +26 -0
  492. package/help/errors/model-unavailable.md +63 -0
  493. package/help/errors/protected-path.md +45 -0
  494. package/help/errors/repo-access.md +27 -0
  495. package/help/errors/service-declaration-invalid.md +27 -0
  496. package/help/errors/service-declaration-unavailable.md +25 -0
  497. package/help/errors/service-launcher-unsupported.md +30 -0
  498. package/help/errors/service-not-allowed.md +27 -0
  499. package/help/errors/service-unknown.md +23 -0
  500. package/help/errors/service-unready.md +49 -0
  501. package/help/getting-started.md +32 -0
  502. package/help/github.md +48 -0
  503. package/help/identity-and-access.md +44 -0
  504. package/help/jira.md +135 -0
  505. package/help/knowledge.md +47 -0
  506. package/help/mcp.md +30 -0
  507. package/help/models.md +90 -0
  508. package/help/native-capabilities.md +30 -0
  509. package/help/observability.md +41 -0
  510. package/help/operating-agents.md +36 -0
  511. package/help/security.md +27 -0
  512. package/help/troubleshooting/budgets.md +32 -0
  513. package/help/troubleshooting/coding-workers.md +47 -0
  514. package/help/troubleshooting/repository-access.md +27 -0
  515. package/package.json +14 -3
  516. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  517. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  518. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  519. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  520. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  521. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  522. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  523. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  524. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  525. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  526. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  527. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  528. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  529. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  530. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  531. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  532. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  533. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  534. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  535. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  536. package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
  537. package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
  538. package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
  539. package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
  540. package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
  541. package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
  542. package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
  543. package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
  544. package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
  545. package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
  546. package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
  547. package/prisma/schema.prisma +663 -21
  548. package/dist/mcp/tools/models.d.ts +0 -8
  549. package/dist/mcp/tools/models.js +0 -15
  550. package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
  551. package/dist/providers/llm/pricing-anthropic.js +0 -30
  552. package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
  553. package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
  554. package/dist/providers/llm/pricing.d.ts +0 -30
  555. package/dist/providers/llm/pricing.js +0 -74
@@ -1,4 +1,14 @@
1
+ import { type CollectExclusions } from "../../coding/collect-exclude.js";
2
+ import type { CodingProvider } from "../../coding/provider.js";
3
+ import { type ServiceStateReporter } from "./service-state.js";
1
4
  import type { JobHandle, JobResult, JobSpec, JobStatus, WorkspaceJobLauncher } from "./types.js";
5
+ /** How long one service image pull may take before the run fails as service_unready. */
6
+ export declare const SERVICE_IMAGE_PULL_TIMEOUT_MS = 300000;
7
+ /**
8
+ * The default bound on a run's whole service start-up (every service's create, start, inspection and
9
+ * readiness probing; image pulls excluded), matching the Kubernetes launcher's pod-start bound.
10
+ */
11
+ export declare const DEFAULT_SERVICE_READY_TIMEOUT_MS = 120000;
2
12
  export declare const SAFE_WORKER_DIAGNOSTIC: RegExp;
3
13
  export interface WorkerDiagnostic {
4
14
  diagnostic: string;
@@ -14,6 +24,8 @@ export declare function parseWorkerDiagnosticLine(line: string): WorkerDiagnosti
14
24
  export interface DockerCommandOptions {
15
25
  env?: Record<string, string>;
16
26
  maxOutputBytes?: number;
27
+ /** Kill the Docker client and fail after this long (service readiness probes and image pulls). */
28
+ timeoutMs?: number;
17
29
  }
18
30
  export interface DockerCommandResult {
19
31
  stdout: string;
@@ -58,7 +70,7 @@ export declare class NodeDockerCommandRunner implements DockerCommandRunner {
58
70
  export interface DockerArtifactTransfer {
59
71
  seedDirectory(sourceDirectory: string, container: string, destination: string): Promise<void>;
60
72
  seedInput(sourceFile: string, container: string): Promise<void>;
61
- materializeDirectory(container: string, source: string, destination: string, maxBytes: number): Promise<void>;
73
+ materializeDirectory(container: string, source: string, destination: string, maxBytes: number, exclusions: CollectExclusions): Promise<void>;
62
74
  }
63
75
  export declare function dockerTransferEnvironment(path: string): NodeJS.ProcessEnv;
64
76
  /** Streams tar archives into the unprivileged keeper; no host bind mounts are used. */
@@ -68,7 +80,7 @@ export declare class NodeDockerArtifactTransfer implements DockerArtifactTransfe
68
80
  constructor(dockerBinary?: string, path?: string);
69
81
  seedDirectory(sourceDirectory: string, container: string, destination: string): Promise<void>;
70
82
  seedInput(sourceFile: string, container: string): Promise<void>;
71
- materializeDirectory(container: string, source: string, destination: string, maxBytes: number): Promise<void>;
83
+ materializeDirectory(container: string, source: string, destination: string, maxBytes: number, exclusions: CollectExclusions): Promise<void>;
72
84
  }
73
85
  export declare function validateMaterializedWorkspace(root: string, maxBytes: number): Promise<void>;
74
86
  export interface DockerJobLauncherOptions {
@@ -81,6 +93,15 @@ export interface DockerJobLauncherOptions {
81
93
  docker?: DockerCommandRunner;
82
94
  transfer?: DockerArtifactTransfer;
83
95
  now?: () => number;
96
+ /** Waits between service readiness probes; tests replace it. */
97
+ sleep?: (milliseconds: number) => Promise<void>;
98
+ /**
99
+ * The bound on a run's whole service start-up, image pulls excluded (default
100
+ * DEFAULT_SERVICE_READY_TIMEOUT_MS). Start-up never runs past the run's own deadline either.
101
+ */
102
+ serviceReadyTimeoutMs?: number;
103
+ /** Display-only observer of each service's start-up state (the viewer); its errors are ignored. */
104
+ onServiceState?: ServiceStateReporter;
84
105
  /** Optional observer invoked after provisioning fails but before resources are cleaned up. */
85
106
  onProvisionFailure?: (context: {
86
107
  runId: string;
@@ -94,10 +115,14 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
94
115
  private readonly docker;
95
116
  private readonly transfer;
96
117
  private readonly now;
118
+ private readonly sleep;
119
+ private readonly serviceReadyTimeoutMs;
97
120
  private readonly locks;
98
121
  private readonly timers;
99
122
  private readonly ready;
100
123
  constructor(options: DockerJobLauncherOptions);
124
+ /** Starts JobSpec.services (a network keeper plus one hardened container per service) for Codex and Claude Code runs. */
125
+ supportsServicesFor(provider: CodingProvider): boolean;
101
126
  launch(spec: JobSpec): Promise<JobHandle>;
102
127
  status(handle: JobHandle): Promise<JobStatus>;
103
128
  collect(handle: JobHandle): Promise<JobResult>;
@@ -107,8 +132,32 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
107
132
  private initialize;
108
133
  private launchLocked;
109
134
  private provision;
135
+ /**
136
+ * A run with services (docs/coding-services.md). The network keeper joins the run's internal
137
+ * network and owns the namespace; each service joins it and must pass its catalog readiness
138
+ * command before the next one starts, as Kubernetes starts native sidecars. The whole start-up is
139
+ * bounded (ServiceStartBudget): running out fails as coding_service_unready for the service being
140
+ * waited on. Returns the keeper's container ID, which Docker may report as the services' and
141
+ * worker's network mode.
142
+ */
143
+ private startServices;
144
+ /**
145
+ * A local copy of the digest-pinned image is used as is; otherwise it is pulled, bounded by its
146
+ * own limit and the run's deadline. Pull time does not count toward the start-up budget.
147
+ */
148
+ private ensureServiceImage;
149
+ /**
150
+ * The catalog readiness command, like a Kubernetes startup probe: every periodSeconds, each
151
+ * attempt bounded by timeoutSeconds, failing after failureThreshold consecutive failures. A
152
+ * service that exited or vanished fails at once, and so does one still unready when the start-up
153
+ * budget runs out (each probe and wait is clipped to what is left of it). Probe output is never
154
+ * logged or kept, so these errors carry no cause. Returns the number of probes made; records
155
+ * why it failed in `failure` for the display-only state report.
156
+ */
157
+ private waitForServiceReady;
110
158
  private refresh;
111
159
  private stopRecord;
160
+ private stopContainer;
112
161
  private sweepExpired;
113
162
  private cleanupResources;
114
163
  private removeContainerIfAttested;
@@ -118,6 +167,8 @@ export declare class DockerJobLauncher implements WorkspaceJobLauncher {
118
167
  private readResultArtifact;
119
168
  private readWorkerFailureDiagnostic;
120
169
  private keeperName;
170
+ /** For a run with services: the attested network keeper's ID, which the worker's network mode may name. */
171
+ private attestedNetworkKeeperId;
121
172
  private assertHost;
122
173
  private createAndAssert;
123
174
  private waitForStorage;
@@ -1,4 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { normalizeCollectExclusions } from "../../coding/collect-exclude.js";
3
+ import { pruneCollectExcluded } from "./collect-prune.js";
2
4
  import { spawn } from "node:child_process";
3
5
  import { lstat, mkdir, mkdtemp, open, opendir, readdir, readFile, readlink, realpath, rename, rm, unlink, } from "node:fs/promises";
4
6
  import { dirname, isAbsolute, join, resolve, sep } from "node:path";
@@ -6,13 +8,25 @@ import { logger } from "../../core/logger.js";
6
8
  import { ensurePrivateDirectory } from "../../core/private-directory.js";
7
9
  import { readBoundedRegularFile } from "../../coding-worker/artifact.js";
8
10
  import { parseCodingAgentOutputJson, redactAndTruncate, MAX_CODING_ARTIFACT_BYTES, MAX_CODING_OUTPUT_ISSUES, SAFE_CODING_OUTPUT_ISSUE, } from "../../coding/protocol.js";
9
- import { assertDockerHostSupportsIsolation, assertClaudeAgentContainerInspection, assertClaudeToolRunnerContainerInspection, assertIsolationNetworkInspection, assertKeeperContainerInspection, assertProxyContainerInspection, assertStorageVolumeInspection, assertWorkerContainerInspection, buildDockerIsolationPlan, } from "./docker-isolation.js";
11
+ import { assertDockerHostSupportsIsolation, assertClaudeAgentContainerInspection, assertClaudeToolRunnerContainerInspection, assertIsolationNetworkInspection, assertKeeperContainerInspection, assertNetworkKeeperContainerInspection, assertProxyContainerInspection, assertStorageVolumeInspection, assertWorkerContainerInspection, buildDockerIsolationPlan, CLAUDE_TOOL_SETUP_ENV, dockerSupportsServicesFor, } from "./docker-isolation.js";
12
+ import { serviceUnreadyError } from "../../coding/services/wording.js";
13
+ import { assertServiceContainerInspection, buildDockerServicePlan, dockerServiceContainerNames, } from "./docker-services.js";
14
+ import { claudeToolSetup } from "./claude-tool-setup.js";
15
+ import { reportServiceState, } from "./service-state.js";
10
16
  import { replaceDirectoryFromStaging } from "./workspace-swap.js";
11
17
  const BACKEND = "docker";
12
18
  const STATE_SCHEMA_VERSION = 1;
13
19
  const MAX_DOCKER_OUTPUT_BYTES = 1024 * 1024;
14
20
  const MAX_DOCKER_SEED_DIAGNOSTIC_BYTES = 4 * 1024;
15
21
  const MAX_WORKSPACE_ENTRIES = 100_000;
22
+ /** How long one service image pull may take before the run fails as service_unready. */
23
+ export const SERVICE_IMAGE_PULL_TIMEOUT_MS = 300_000;
24
+ /**
25
+ * The default bound on a run's whole service start-up (every service's create, start, inspection and
26
+ * readiness probing; image pulls excluded), matching the Kubernetes launcher's pod-start bound.
27
+ */
28
+ export const DEFAULT_SERVICE_READY_TIMEOUT_MS = 120_000;
29
+ const MAX_SERVICE_PROBE_OUTPUT_BYTES = 64 * 1024;
16
30
  const TERMINAL_PHASES = new Set(["succeeded", "failed", "stopped", "lost", "removed"]);
17
31
  export const SAFE_WORKER_DIAGNOSTIC = /^(?:worker_[a-z_]+|coding_[a-z_]+|wardby_[a-z_]+)$/;
18
32
  /**
@@ -81,7 +95,7 @@ export class NodeDockerCommandRunner {
81
95
  if (args.some((argument) => argument.includes("\0")))
82
96
  throw new Error("docker_argument_invalid");
83
97
  const extraEnv = options.env ?? {};
84
- if (Object.keys(extraEnv).some((key) => key !== "WARDBY_RUN_CAPABILITY"))
98
+ if (Object.keys(extraEnv).some((key) => key !== "WARDBY_RUN_CAPABILITY" && key !== CLAUDE_TOOL_SETUP_ENV))
85
99
  throw new Error("docker_environment_invalid");
86
100
  const env = {
87
101
  PATH: this.path,
@@ -100,6 +114,13 @@ export class NodeDockerCommandRunner {
100
114
  rejectPromise(new DockerCommandError(null));
101
115
  return;
102
116
  }
117
+ let timedOut = false;
118
+ const timer = options.timeoutMs === undefined
119
+ ? undefined
120
+ : setTimeout(() => {
121
+ timedOut = true;
122
+ child.kill("SIGKILL");
123
+ }, options.timeoutMs);
103
124
  const stdout = [];
104
125
  const stderr = [];
105
126
  let bytes = 0;
@@ -120,6 +141,11 @@ export class NodeDockerCommandRunner {
120
141
  spawnFailed = true;
121
142
  });
122
143
  child.once("close", (code) => {
144
+ if (timer)
145
+ clearTimeout(timer);
146
+ if (timedOut) {
147
+ return rejectPromise(new DockerCommandError(code, false, false, { subcommand: args[0], stderr: "timed out" }));
148
+ }
123
149
  if (exceeded)
124
150
  return rejectPromise(new DockerCommandError(code, false, true));
125
151
  if (spawnFailed)
@@ -256,7 +282,7 @@ export class NodeDockerArtifactTransfer {
256
282
  });
257
283
  await docker.run(["container", "cp", sourceFile, `${container}:/run/wardby/storage/input/input.json`]);
258
284
  }
259
- async materializeDirectory(container, source, destination, maxBytes) {
285
+ async materializeDirectory(container, source, destination, maxBytes, exclusions) {
260
286
  await replaceDirectoryFromStaging(destination, maxBytes, "docker_workspace_destination_invalid", async (staging) => {
261
287
  await new NodeDockerCommandRunner({ dockerBinary: this.dockerBinary, homeDir: "/tmp", path: this.path }).run([
262
288
  "container",
@@ -264,6 +290,8 @@ export class NodeDockerArtifactTransfer {
264
290
  `${container}:${source}/.`,
265
291
  staging,
266
292
  ]);
293
+ // Before replaceDirectoryFromStaging validates: excluded folders never count.
294
+ await pruneCollectExcluded(staging, exclusions);
267
295
  });
268
296
  }
269
297
  }
@@ -380,6 +408,42 @@ function resourceLabels(record) {
380
408
  function labelsMatch(actual, expected) {
381
409
  return Object.entries(expected).every(([key, value]) => actual?.[key] === value);
382
410
  }
411
+ /**
412
+ * A run's service start-up budget: `budgetMs` from the start, extended by the time image pulls took,
413
+ * and never past the run's deadline.
414
+ */
415
+ class ServiceStartBudget {
416
+ now;
417
+ budgetMs;
418
+ deadlineAt;
419
+ startedAt;
420
+ excludedMs = 0;
421
+ constructor(now, budgetMs, deadlineAt) {
422
+ this.now = now;
423
+ this.budgetMs = budgetMs;
424
+ this.deadlineAt = deadlineAt;
425
+ this.startedAt = now();
426
+ }
427
+ /** What is left of the budget, capped by the run's deadline; zero once either runs out. */
428
+ remainingMs() {
429
+ const now = this.now();
430
+ return Math.max(0, Math.min(this.startedAt + this.excludedMs + this.budgetMs - now, this.deadlineAt - now));
431
+ }
432
+ /** What is left before the run's deadline: the only bound (besides its own) on an image pull. */
433
+ untilDeadlineMs() {
434
+ return Math.max(0, this.deadlineAt - this.now());
435
+ }
436
+ exclude(milliseconds) {
437
+ this.excludedMs += Math.max(0, milliseconds);
438
+ }
439
+ }
440
+ /** `coding_service_unready:<name>` (the executor's service_unready), keeping the Docker failure as the operator's cause. */
441
+ function serviceUnready(name, cause) {
442
+ const error = serviceUnreadyError(name);
443
+ if (cause !== undefined)
444
+ error.cause = cause;
445
+ return error;
446
+ }
383
447
  function capabilityFromInspection(container) {
384
448
  return container.Config?.Env?.find((value) => value.startsWith("WARDBY_RUN_CAPABILITY="))?.slice("WARDBY_RUN_CAPABILITY=".length);
385
449
  }
@@ -417,6 +481,8 @@ export class DockerJobLauncher {
417
481
  docker;
418
482
  transfer;
419
483
  now;
484
+ sleep;
485
+ serviceReadyTimeoutMs;
420
486
  locks = new Map();
421
487
  timers = new Map();
422
488
  ready;
@@ -429,8 +495,17 @@ export class DockerJobLauncher {
429
495
  this.docker = options.docker ?? new NodeDockerCommandRunner({ homeDir: join(this.stateRoot, ".home") });
430
496
  this.transfer = options.transfer ?? new NodeDockerArtifactTransfer();
431
497
  this.now = options.now ?? Date.now;
498
+ this.sleep =
499
+ options.sleep ?? ((milliseconds) => new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds)));
500
+ this.serviceReadyTimeoutMs = options.serviceReadyTimeoutMs ?? DEFAULT_SERVICE_READY_TIMEOUT_MS;
501
+ if (!Number.isFinite(this.serviceReadyTimeoutMs) || this.serviceReadyTimeoutMs <= 0)
502
+ throw new Error("docker_launcher_service_timeout_invalid");
432
503
  this.ready = this.initialize();
433
504
  }
505
+ /** Starts JobSpec.services (a network keeper plus one hardened container per service) for Codex and Claude Code runs. */
506
+ supportsServicesFor(provider) {
507
+ return dockerSupportsServicesFor(provider);
508
+ }
434
509
  async launch(spec) {
435
510
  await this.ready;
436
511
  return this.withRun(spec.runId, async () => this.launchLocked(spec));
@@ -493,7 +568,9 @@ export class DockerJobLauncher {
493
568
  if (resolve(destination) !== expected || !expected.startsWith(`${this.workspaceRoot}${sep}`)) {
494
569
  throw new Error("docker_workspace_destination_invalid");
495
570
  }
496
- await this.transfer.materializeDirectory(this.keeperName(current), "/run/wardby/storage/workspace", expected, current.spec.limits.diskMb * 1024 * 1024);
571
+ await this.transfer.materializeDirectory(this.keeperName(current), "/run/wardby/storage/workspace", expected, current.spec.limits.diskMb * 1024 * 1024,
572
+ // Names are always the current built-in list; only the paths come from the spec.
573
+ normalizeCollectExclusions(current.spec.collectExclude?.paths));
497
574
  });
498
575
  }
499
576
  async stop(handle, _reason) {
@@ -557,6 +634,7 @@ export class DockerJobLauncher {
557
634
  }
558
635
  async launchLocked(spec) {
559
636
  const plan = buildDockerIsolationPlan(spec, this.options.proxyContainer);
637
+ const services = buildDockerServicePlan(spec);
560
638
  const specHash = stableSpecHash(spec);
561
639
  const existing = await this.readRecord(spec.runId);
562
640
  if (existing) {
@@ -581,7 +659,7 @@ export class DockerJobLauncher {
581
659
  await this.refresh(existing);
582
660
  return clone(existing.handle);
583
661
  }
584
- return this.provision(existing, plan, capability);
662
+ return this.provision(existing, plan, services, capability);
585
663
  }
586
664
  await this.validateInputArtifact(spec.inputArtifact);
587
665
  const capability = ensureCapability(await this.options.resolveCapability(spec.runId));
@@ -600,7 +678,7 @@ export class DockerJobLauncher {
600
678
  // This durable record is the idempotency fence before any Docker side effect.
601
679
  await this.writeRecord(record);
602
680
  try {
603
- return await this.provision(record, plan, capability);
681
+ return await this.provision(record, plan, services, capability);
604
682
  }
605
683
  catch (error) {
606
684
  await this.options
@@ -613,7 +691,7 @@ export class DockerJobLauncher {
613
691
  throw error;
614
692
  }
615
693
  }
616
- async provision(record, plan, capability) {
694
+ async provision(record, plan, services, capability) {
617
695
  await this.assertHost();
618
696
  const labels = resourceLabels(record);
619
697
  await this.createAndAssert(appendLabels(plan.networkCreateArgs, labels), ["network", "inspect", plan.names.network], (value) => {
@@ -640,13 +718,21 @@ export class DockerJobLauncher {
640
718
  await this.run(plan.proxyNetworkConnectArgs);
641
719
  const proxy = await this.inspect(["container", "inspect", this.options.proxyContainer]);
642
720
  assertProxyContainerInspection(proxy, record.runId);
721
+ // Built before anything starts, so a setup past the tool runner's bounds fails the launch early.
722
+ const toolSetup = record.spec.provider === "claude-code" ? claudeToolSetup(record.spec, capability) : undefined;
723
+ // Services first (a run with services only): the Codex worker and Claude Code's tool runner join
724
+ // the network keeper's namespace, so it must exist, and every service must pass readiness before
725
+ // anything that runs the run's commands starts.
726
+ const networkKeeperId = plan.networkKeeperCreateArgs
727
+ ? await this.startServices(record, plan.networkKeeperCreateArgs, plan.names.networkKeeperContainer, services, labels)
728
+ : undefined;
643
729
  if (plan.toolCreateArgs) {
644
730
  await this.createAndAssert(appendLabels(plan.toolCreateArgs, labels), ["container", "inspect", plan.names.toolContainer], (value) => {
645
- assertClaudeToolRunnerContainerInspection(value, record.spec);
731
+ assertClaudeToolRunnerContainerInspection(value, record.spec, toolSetup, networkKeeperId);
646
732
  if (!labelsMatch(value.Config?.Labels, labels)) {
647
733
  throw new Error("docker_resource_attestation_failed");
648
734
  }
649
- });
735
+ }, { env: { WARDBY_TOOL_SETUP: toolSetup } });
650
736
  await this.startContainer(plan.names.toolContainer);
651
737
  await this.waitForToolRunner(plan.names.toolContainer);
652
738
  }
@@ -657,7 +743,7 @@ export class DockerJobLauncher {
657
743
  assertClaudeAgentContainerInspection(inspected, record.spec, capability);
658
744
  }
659
745
  else {
660
- assertWorkerContainerInspection(inspected, record.spec, capability);
746
+ assertWorkerContainerInspection(inspected, record.spec, capability, networkKeeperId);
661
747
  }
662
748
  if (!labelsMatch(inspected.Config?.Labels, labels))
663
749
  throw new Error("docker_resource_attestation_failed");
@@ -668,6 +754,165 @@ export class DockerJobLauncher {
668
754
  this.scheduleDeadline(record);
669
755
  return clone(record.handle);
670
756
  }
757
+ /**
758
+ * A run with services (docs/coding-services.md). The network keeper joins the run's internal
759
+ * network and owns the namespace; each service joins it and must pass its catalog readiness
760
+ * command before the next one starts, as Kubernetes starts native sidecars. The whole start-up is
761
+ * bounded (ServiceStartBudget): running out fails as coding_service_unready for the service being
762
+ * waited on. Returns the keeper's container ID, which Docker may report as the services' and
763
+ * worker's network mode.
764
+ */
765
+ async startServices(record, keeperArgs, keeperName, services, labels) {
766
+ const budget = new ServiceStartBudget(this.now, this.serviceReadyTimeoutMs, record.deadlineAt);
767
+ const keeper = (await this.createAndAssert(appendLabels(keeperArgs, labels), ["container", "inspect", keeperName], (value) => {
768
+ const inspected = value;
769
+ assertNetworkKeeperContainerInspection(inspected, record.spec);
770
+ if (!labelsMatch(inspected.Config?.Labels, labels))
771
+ throw new Error("docker_resource_attestation_failed");
772
+ }));
773
+ const keeperId = keeper.Id;
774
+ if (!keeperId)
775
+ throw new Error("docker_resource_attestation_failed");
776
+ await this.startContainer(keeperName);
777
+ const report = (name, update) => reportServiceState(this.options.onServiceState, { runId: record.runId, name, ...update });
778
+ for (const planned of services)
779
+ await report(planned.service.name, { state: "pending" });
780
+ for (const planned of services) {
781
+ const name = planned.service.name;
782
+ const failure = { reason: "image_unavailable" };
783
+ let attempts;
784
+ try {
785
+ if (budget.remainingMs() <= 0)
786
+ throw serviceUnready(name);
787
+ await this.ensureServiceImage(planned, budget);
788
+ if (budget.remainingMs() <= 0)
789
+ throw serviceUnready(name);
790
+ failure.reason = "start_failed";
791
+ let createFailure;
792
+ try {
793
+ await this.createAndAssert(appendLabels(planned.createArgs, labels), ["container", "inspect", planned.container], (value) => {
794
+ const inspected = value;
795
+ assertServiceContainerInspection(inspected, record.spec, planned.service, keeperId);
796
+ if (!labelsMatch(inspected.Config?.Labels, labels))
797
+ throw new Error("docker_resource_attestation_failed");
798
+ }, undefined, (error) => {
799
+ createFailure = error;
800
+ });
801
+ }
802
+ catch (error) {
803
+ // createAndAssert tolerates a failed create (a crash retry may find it made), so a service
804
+ // Docker refused to create shows up as a failed inspection; the create's own failure is the
805
+ // useful cause, not that generic "not found". Attestation failures rethrow as is.
806
+ if (error instanceof DockerCommandError)
807
+ throw serviceUnready(name, createFailure ?? error);
808
+ throw error;
809
+ }
810
+ if (budget.remainingMs() <= 0)
811
+ throw serviceUnready(name);
812
+ try {
813
+ await this.startContainer(planned.container);
814
+ }
815
+ catch (error) {
816
+ throw serviceUnready(name, error);
817
+ }
818
+ failure.reason = "probe_failed";
819
+ await report(name, { state: "probing", attempts: 0 });
820
+ attempts = await this.waitForServiceReady(planned, budget, failure, (made) => report(name, { state: "probing", attempts: made }));
821
+ }
822
+ catch (error) {
823
+ const reason = failure.reason !== "exited" && budget.remainingMs() <= 0 ? "timed_out" : failure.reason;
824
+ await report(name, {
825
+ state: "failed",
826
+ reason,
827
+ ...(failure.attempts === undefined ? {} : { attempts: failure.attempts }),
828
+ });
829
+ throw error;
830
+ }
831
+ await report(name, { state: "ready", attempts });
832
+ }
833
+ return keeperId;
834
+ }
835
+ /**
836
+ * A local copy of the digest-pinned image is used as is; otherwise it is pulled, bounded by its
837
+ * own limit and the run's deadline. Pull time does not count toward the start-up budget.
838
+ */
839
+ async ensureServiceImage(planned, budget) {
840
+ try {
841
+ await this.run(["image", "inspect", "--format", "{{.Id}}", planned.service.image]);
842
+ return;
843
+ }
844
+ catch {
845
+ // Missing (or unreadable): pull it by its digest below.
846
+ }
847
+ const untilDeadline = budget.untilDeadlineMs();
848
+ if (untilDeadline <= 0)
849
+ throw serviceUnready(planned.service.name);
850
+ const startedAt = this.now();
851
+ try {
852
+ await this.run(["image", "pull", "--quiet", planned.service.image], {
853
+ timeoutMs: Math.min(SERVICE_IMAGE_PULL_TIMEOUT_MS, untilDeadline),
854
+ });
855
+ }
856
+ catch (error) {
857
+ throw serviceUnready(planned.service.name, error);
858
+ }
859
+ finally {
860
+ budget.exclude(this.now() - startedAt);
861
+ }
862
+ }
863
+ /**
864
+ * The catalog readiness command, like a Kubernetes startup probe: every periodSeconds, each
865
+ * attempt bounded by timeoutSeconds, failing after failureThreshold consecutive failures. A
866
+ * service that exited or vanished fails at once, and so does one still unready when the start-up
867
+ * budget runs out (each probe and wait is clipped to what is left of it). Probe output is never
868
+ * logged or kept, so these errors carry no cause. Returns the number of probes made; records
869
+ * why it failed in `failure` for the display-only state report.
870
+ */
871
+ async waitForServiceReady(planned, budget, failure, onProbeFailed) {
872
+ const { command, periodSeconds, timeoutSeconds, failureThreshold } = planned.service.readiness;
873
+ const name = planned.service.name;
874
+ let failures = 0;
875
+ for (;;) {
876
+ if (budget.remainingMs() <= 0)
877
+ throw serviceUnready(name);
878
+ let state;
879
+ try {
880
+ state = await this.inspect(["container", "inspect", planned.container]);
881
+ }
882
+ catch (error) {
883
+ if (error instanceof DockerCommandError) {
884
+ failure.reason = "exited";
885
+ throw serviceUnready(name);
886
+ }
887
+ throw error;
888
+ }
889
+ if (state.State?.Running !== true) {
890
+ failure.reason = "exited";
891
+ throw serviceUnready(name);
892
+ }
893
+ const remaining = budget.remainingMs();
894
+ if (remaining <= 0)
895
+ throw serviceUnready(name);
896
+ try {
897
+ await this.run(["container", "exec", planned.container, ...command], {
898
+ timeoutMs: Math.min(timeoutSeconds * 1_000, remaining),
899
+ maxOutputBytes: MAX_SERVICE_PROBE_OUTPUT_BYTES,
900
+ });
901
+ return failures + 1;
902
+ }
903
+ catch {
904
+ failures += 1;
905
+ failure.attempts = failures;
906
+ if (failures >= failureThreshold)
907
+ throw serviceUnready(name);
908
+ }
909
+ await onProbeFailed(failures);
910
+ const wait = Math.min(periodSeconds * 1_000, budget.remainingMs());
911
+ if (wait <= 0)
912
+ throw serviceUnready(name);
913
+ await this.sleep(wait);
914
+ }
915
+ }
671
916
  async refresh(record) {
672
917
  if (isTerminal(record))
673
918
  return;
@@ -701,7 +946,7 @@ export class DockerJobLauncher {
701
946
  "inspect",
702
947
  buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.toolContainer,
703
948
  ]);
704
- assertClaudeToolRunnerContainerInspection(tool, record.spec);
949
+ assertClaudeToolRunnerContainerInspection(tool, record.spec, claudeToolSetup(record.spec, capability), await this.attestedNetworkKeeperId(record));
705
950
  if (tool.State?.Running !== true || tool.State?.Status !== "running") {
706
951
  record.phase = "failed";
707
952
  record.result = { exitCode: 1, reason: "failed", diagnostic: "worker_tool_runner_failed" };
@@ -711,7 +956,7 @@ export class DockerJobLauncher {
711
956
  }
712
957
  }
713
958
  else {
714
- assertWorkerContainerInspection(inspection, record.spec, capability);
959
+ assertWorkerContainerInspection(inspection, record.spec, capability, await this.attestedNetworkKeeperId(record));
715
960
  }
716
961
  const phase = dockerState(inspection);
717
962
  if (!phase || phase === "provisioning" || phase === "active")
@@ -724,28 +969,27 @@ export class DockerJobLauncher {
724
969
  async stopRecord(record, timedOut) {
725
970
  if (isTerminal(record))
726
971
  return;
727
- try {
728
- await this.run(["container", "stop", "--time", "10", record.handle.id]);
729
- }
730
- catch (error) {
731
- if (!(error instanceof DockerCommandError && error.notFound))
732
- throw new Error("docker_stop_failed", { cause: error });
733
- }
972
+ await this.stopContainer(record.handle.id);
734
973
  if (record.spec.provider === "claude-code") {
735
- const tool = buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.toolContainer;
736
- try {
737
- await this.run(["container", "stop", "--time", "10", tool]);
738
- }
739
- catch (error) {
740
- if (!(error instanceof DockerCommandError && error.notFound))
741
- throw new Error("docker_stop_failed", { cause: error });
742
- }
974
+ await this.stopContainer(buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.toolContainer);
743
975
  }
976
+ // Services stop with the run; they (and the network keeper) are removed with its other resources.
977
+ for (const service of dockerServiceContainerNames(record.spec))
978
+ await this.stopContainer(service);
744
979
  record.phase = timedOut ? "failed" : "stopped";
745
980
  record.result = timedOut ? { exitCode: 124, reason: "timed_out" } : resultFor("stopped");
746
981
  await this.writeRecord(record);
747
982
  this.clearDeadline(record);
748
983
  }
984
+ async stopContainer(name) {
985
+ try {
986
+ await this.run(["container", "stop", "--time", "10", name]);
987
+ }
988
+ catch (error) {
989
+ if (!(error instanceof DockerCommandError && error.notFound))
990
+ throw new Error("docker_stop_failed", { cause: error });
991
+ }
992
+ }
749
993
  async sweepExpired(record) {
750
994
  // The DB verifier ran immediately before this call; never sweep by label alone.
751
995
  try {
@@ -765,17 +1009,26 @@ export class DockerJobLauncher {
765
1009
  await this.removeContainerIfAttested(plan.names.workerContainer, labels);
766
1010
  if (plan.toolCreateArgs)
767
1011
  await this.removeContainerIfAttested(plan.names.toolContainer, labels);
1012
+ // Services, then the network keeper: nothing (the Codex worker, Claude's tool runner, a service)
1013
+ // may still share its namespace when it goes.
1014
+ // --volumes: an image VOLUME the attestation rejected must not outlive its container.
1015
+ for (const service of dockerServiceContainerNames(record.spec)) {
1016
+ await this.removeContainerIfAttested(service, labels, true);
1017
+ }
1018
+ if (plan.networkKeeperCreateArgs) {
1019
+ await this.removeContainerIfAttested(plan.names.networkKeeperContainer, labels);
1020
+ }
768
1021
  await this.runIgnoreMissing(["network", "disconnect", plan.names.network, this.options.proxyContainer]);
769
1022
  await this.removeContainerIfAttested(plan.names.keeperContainer, labels);
770
1023
  await this.removeNetworkIfAttested(plan.names.network, labels);
771
1024
  await this.removeVolumeIfAttested(plan.names.storageVolume, labels);
772
1025
  }
773
- async removeContainerIfAttested(name, labels) {
1026
+ async removeContainerIfAttested(name, labels, anonymousVolumes = false) {
774
1027
  try {
775
1028
  const inspected = await this.inspect(["container", "inspect", name]);
776
1029
  if (!labelsMatch(inspected.Config?.Labels, labels))
777
1030
  throw new Error("docker_resource_attestation_failed");
778
- await this.runIgnoreMissing(["container", "rm", "--force", name]);
1031
+ await this.runIgnoreMissing(["container", "rm", "--force", ...(anonymousVolumes ? ["--volumes"] : []), name]);
779
1032
  }
780
1033
  catch (error) {
781
1034
  if (!(error instanceof DockerCommandError && error.notFound))
@@ -852,18 +1105,40 @@ export class DockerJobLauncher {
852
1105
  keeperName(record) {
853
1106
  return buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.keeperContainer;
854
1107
  }
1108
+ /** For a run with services: the attested network keeper's ID, which the worker's network mode may name. */
1109
+ async attestedNetworkKeeperId(record) {
1110
+ if (record.spec.services === undefined)
1111
+ return undefined;
1112
+ const name = buildDockerIsolationPlan(record.spec, this.options.proxyContainer).names.networkKeeperContainer;
1113
+ let keeper;
1114
+ try {
1115
+ keeper = await this.inspect(["container", "inspect", name]);
1116
+ }
1117
+ catch (error) {
1118
+ throw new Error("docker_status_unavailable", { cause: error });
1119
+ }
1120
+ assertNetworkKeeperContainerInspection(keeper, record.spec);
1121
+ if (!labelsMatch(keeper.Config?.Labels, resourceLabels(record)) || !keeper.Id) {
1122
+ throw new Error("docker_resource_attestation_failed");
1123
+ }
1124
+ return keeper.Id;
1125
+ }
855
1126
  async assertHost() {
856
1127
  const info = await this.inspect(["info", "--format", "{{json .}}"]);
857
1128
  assertDockerHostSupportsIsolation(info);
858
1129
  }
859
- async createAndAssert(createArgs, inspectArgs, assert, options) {
1130
+ async createAndAssert(createArgs, inspectArgs, assert, options, onCreateFailure) {
860
1131
  try {
861
1132
  await this.run(createArgs, options);
862
1133
  }
863
- catch {
1134
+ catch (error) {
864
1135
  // A retry may find a resource created before the previous process crashed.
1136
+ if (error instanceof DockerCommandError)
1137
+ onCreateFailure?.(error);
865
1138
  }
866
- assert(await this.inspect(inspectArgs));
1139
+ const inspected = await this.inspect(inspectArgs);
1140
+ assert(inspected);
1141
+ return inspected;
867
1142
  }
868
1143
  async waitForStorage(keeper) {
869
1144
  // The keeper emits this fixed marker only after creating every storage
@@ -8,6 +8,7 @@ export interface FakeExecCall {
8
8
  command: string[];
9
9
  stdin?: Readable;
10
10
  stdout?: Writable;
11
+ timeoutMs: number;
11
12
  }
12
13
  type Kind = "configmap" | "secret" | "pod" | "networkpolicy" | "service" | "endpoints";
13
14
  type Obj = {