@wardby/cli 0.2.0 → 0.3.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 (491) hide show
  1. package/.env.example +34 -4
  2. package/README.md +183 -71
  3. package/dist/claude-coding-worker/driver.d.ts +7 -1
  4. package/dist/claude-coding-worker/driver.js +31 -2
  5. package/dist/claude-coding-worker/main.js +5 -2
  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 +13 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +219 -29
  12. package/dist/coding/collect-exclude.d.ts +25 -0
  13. package/dist/coding/collect-exclude.js +76 -0
  14. package/dist/coding/profile.d.ts +67 -22
  15. package/dist/coding/profile.js +58 -25
  16. package/dist/coding/protected-path-wording.d.ts +24 -0
  17. package/dist/coding/protected-path-wording.js +55 -0
  18. package/dist/coding/protected-paths.d.ts +31 -0
  19. package/dist/coding/protected-paths.js +48 -0
  20. package/dist/coding/protocol.d.ts +99 -3
  21. package/dist/coding/protocol.js +155 -4
  22. package/dist/coding/registry/adapters.d.ts +2 -0
  23. package/dist/coding/registry/adapters.js +9 -0
  24. package/dist/coding/registry/allowlist.d.ts +9 -0
  25. package/dist/coding/registry/allowlist.js +28 -0
  26. package/dist/coding/registry/json-scan.d.ts +55 -0
  27. package/dist/coding/registry/json-scan.js +191 -0
  28. package/dist/coding/registry/lockfiles.d.ts +6 -0
  29. package/dist/coding/registry/lockfiles.js +89 -0
  30. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  31. package/dist/coding/registry/npm-lockfile.js +73 -0
  32. package/dist/coding/registry/npm-plan.d.ts +29 -0
  33. package/dist/coding/registry/npm-plan.js +289 -0
  34. package/dist/coding/registry/npm.d.ts +7 -0
  35. package/dist/coding/registry/npm.js +252 -0
  36. package/dist/coding/registry/pypi.d.ts +4 -0
  37. package/dist/coding/registry/pypi.js +210 -0
  38. package/dist/coding/registry/report.d.ts +37 -0
  39. package/dist/coding/registry/report.js +31 -0
  40. package/dist/coding/registry/token.d.ts +4 -0
  41. package/dist/coding/registry/token.js +7 -0
  42. package/dist/coding/registry/types.d.ts +286 -0
  43. package/dist/coding/registry/types.js +19 -0
  44. package/dist/coding/registry/worker-config.d.ts +14 -0
  45. package/dist/coding/registry/worker-config.js +31 -0
  46. package/dist/coding/services/builtins.d.ts +22 -0
  47. package/dist/coding/services/builtins.js +85 -0
  48. package/dist/coding/services/catalog.d.ts +443 -0
  49. package/dist/coding/services/catalog.js +159 -0
  50. package/dist/coding/services/declaration.d.ts +11 -0
  51. package/dist/coding/services/declaration.js +114 -0
  52. package/dist/coding/services/note.d.ts +8 -0
  53. package/dist/coding/services/note.js +15 -0
  54. package/dist/coding/services/resolve.d.ts +25 -0
  55. package/dist/coding/services/resolve.js +50 -0
  56. package/dist/coding/services/wording.d.ts +25 -0
  57. package/dist/coding/services/wording.js +70 -0
  58. package/dist/coding-proxy/main.js +24 -4
  59. package/dist/coding-worker/artifact.d.ts +18 -0
  60. package/dist/coding-worker/artifact.js +48 -6
  61. package/dist/coding-worker/debug-trace.d.ts +37 -0
  62. package/dist/coding-worker/debug-trace.js +117 -0
  63. package/dist/coding-worker/driver.d.ts +14 -1
  64. package/dist/coding-worker/driver.js +106 -14
  65. package/dist/coding-worker/errors.d.ts +7 -0
  66. package/dist/coding-worker/errors.js +26 -0
  67. package/dist/coding-worker/main.js +13 -4
  68. package/dist/coding-worker/sdk.d.ts +2 -2
  69. package/dist/coding-worker/sdk.js +7 -2
  70. package/dist/coding-worker/types.d.ts +3 -0
  71. package/dist/config/providers.d.ts +41 -0
  72. package/dist/config/providers.js +66 -0
  73. package/dist/core/budget-groups.d.ts +109 -6
  74. package/dist/core/budget-groups.js +125 -19
  75. package/dist/core/budget-wording.d.ts +22 -0
  76. package/dist/core/budget-wording.js +55 -0
  77. package/dist/core/coding-queue.d.ts +1 -1
  78. package/dist/core/datastores.d.ts +1 -1
  79. package/dist/core/datastores.js +18 -2
  80. package/dist/core/db.d.ts +40 -2
  81. package/dist/core/db.js +75 -2
  82. package/dist/core/dispatch.d.ts +49 -7
  83. package/dist/core/dispatch.js +290 -45
  84. package/dist/core/engine-native.js +27 -10
  85. package/dist/core/grants.d.ts +86 -0
  86. package/dist/core/grants.js +126 -0
  87. package/dist/core/host-events.d.ts +49 -0
  88. package/dist/core/host-events.js +257 -0
  89. package/dist/core/host-identity-links.d.ts +54 -0
  90. package/dist/core/host-identity-links.js +189 -0
  91. package/dist/core/host-status.d.ts +61 -0
  92. package/dist/core/host-status.js +211 -0
  93. package/dist/core/http-runtime.js +3 -4
  94. package/dist/core/in-flight-runs.d.ts +8 -0
  95. package/dist/core/in-flight-runs.js +56 -0
  96. package/dist/core/lease.d.ts +1 -1
  97. package/dist/core/private-directory.d.ts +20 -0
  98. package/dist/core/private-directory.js +36 -0
  99. package/dist/core/provider-wording.d.ts +12 -0
  100. package/dist/core/provider-wording.js +44 -0
  101. package/dist/core/reconciler.d.ts +39 -3
  102. package/dist/core/reconciler.js +86 -2
  103. package/dist/core/repo-access.d.ts +99 -0
  104. package/dist/core/repo-access.js +136 -0
  105. package/dist/core/review-host-checks.d.ts +11 -0
  106. package/dist/core/review-host-checks.js +41 -0
  107. package/dist/core/review-host-tools.d.ts +47 -0
  108. package/dist/core/review-host-tools.js +347 -0
  109. package/dist/core/run-heartbeat.d.ts +27 -0
  110. package/dist/core/run-heartbeat.js +54 -0
  111. package/dist/core/runner.d.ts +31 -9
  112. package/dist/core/runner.js +237 -40
  113. package/dist/core/scheduler.d.ts +1 -1
  114. package/dist/core/secrets.d.ts +12 -3
  115. package/dist/core/secrets.js +25 -6
  116. package/dist/core/subagent-memory-tools.d.ts +2 -2
  117. package/dist/core/subagent-memory-tools.js +16 -3
  118. package/dist/core/tool-admin.d.ts +81 -0
  119. package/dist/core/tool-admin.js +129 -0
  120. package/dist/core/tool-names.d.ts +42 -0
  121. package/dist/core/tool-names.js +64 -0
  122. package/dist/core/untrusted-content.d.ts +32 -0
  123. package/dist/core/untrusted-content.js +72 -0
  124. package/dist/core/webhooks.d.ts +9 -2
  125. package/dist/core/webhooks.js +23 -2
  126. package/dist/generated/prisma/browser.d.ts +328 -0
  127. package/dist/generated/prisma/browser.js +17 -0
  128. package/dist/generated/prisma/client.d.ts +347 -0
  129. package/dist/generated/prisma/client.js +34 -0
  130. package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
  131. package/dist/generated/prisma/commonInputTypes.js +10 -0
  132. package/dist/generated/prisma/enums.d.ts +59 -0
  133. package/dist/generated/prisma/enums.js +59 -0
  134. package/dist/generated/prisma/internal/class.d.ts +605 -0
  135. package/dist/generated/prisma/internal/class.js +49 -0
  136. package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
  137. package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
  138. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
  139. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
  140. package/dist/generated/prisma/models/Agent.d.ts +3560 -0
  141. package/dist/generated/prisma/models/Agent.js +1 -0
  142. package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
  143. package/dist/generated/prisma/models/AgentDatastore.js +1 -0
  144. package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
  145. package/dist/generated/prisma/models/AgentMemory.js +1 -0
  146. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  147. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  148. package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
  149. package/dist/generated/prisma/models/AgentSecret.js +1 -0
  150. package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
  151. package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
  152. package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
  153. package/dist/generated/prisma/models/AgentTool.js +1 -0
  154. package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
  155. package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
  156. package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
  157. package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
  158. package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
  159. package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
  160. package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
  161. package/dist/generated/prisma/models/AuthSession.js +1 -0
  162. package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
  163. package/dist/generated/prisma/models/AuthUser.js +1 -0
  164. package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
  165. package/dist/generated/prisma/models/BudgetGroup.js +1 -0
  166. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
  167. package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
  168. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
  169. package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
  170. package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
  171. package/dist/generated/prisma/models/CodingProxySession.js +1 -0
  172. package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
  173. package/dist/generated/prisma/models/CodingRun.js +1 -0
  174. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  175. package/dist/generated/prisma/models/CodingService.js +1 -0
  176. package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
  177. package/dist/generated/prisma/models/Datastore.js +1 -0
  178. package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
  179. package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
  180. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  181. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  182. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  183. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  184. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  185. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  186. package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
  187. package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
  188. package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
  189. package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
  190. package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
  191. package/dist/generated/prisma/models/OAuthClient.js +1 -0
  192. package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
  193. package/dist/generated/prisma/models/OAuthFamily.js +1 -0
  194. package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
  195. package/dist/generated/prisma/models/OAuthGrant.js +1 -0
  196. package/dist/generated/prisma/models/Principal.d.ts +2245 -0
  197. package/dist/generated/prisma/models/Principal.js +1 -0
  198. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  199. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  200. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  201. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  202. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  203. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  204. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  205. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  206. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  207. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  208. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  209. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  210. package/dist/generated/prisma/models/Run.d.ts +2669 -0
  211. package/dist/generated/prisma/models/Run.js +1 -0
  212. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  213. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  214. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  215. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  216. package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
  217. package/dist/generated/prisma/models/SchedulerLease.js +1 -0
  218. package/dist/generated/prisma/models/Secret.d.ts +1399 -0
  219. package/dist/generated/prisma/models/Secret.js +1 -0
  220. package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
  221. package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
  222. package/dist/generated/prisma/models/Task.d.ts +1176 -0
  223. package/dist/generated/prisma/models/Task.js +1 -0
  224. package/dist/generated/prisma/models/Tool.d.ts +1490 -0
  225. package/dist/generated/prisma/models/Tool.js +1 -0
  226. package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
  227. package/dist/generated/prisma/models/Webhook.js +1 -0
  228. package/dist/generated/prisma/models.d.ts +45 -0
  229. package/dist/generated/prisma/models.js +1 -0
  230. package/dist/help/build.d.ts +1 -0
  231. package/dist/help/build.js +9 -0
  232. package/dist/help/catalog.d.ts +24 -0
  233. package/dist/help/catalog.js +160 -0
  234. package/dist/help/cli.d.ts +2 -0
  235. package/dist/help/cli.js +65 -0
  236. package/dist/help/runtime.d.ts +3 -0
  237. package/dist/help/runtime.js +44 -0
  238. package/dist/help/search.d.ts +9 -0
  239. package/dist/help/search.js +104 -0
  240. package/dist/help-index.json +660 -0
  241. package/dist/import/cli-args.js +3 -2
  242. package/dist/import/create.d.ts +6 -1
  243. package/dist/import/create.js +68 -14
  244. package/dist/import/index.d.ts +1 -1
  245. package/dist/import/index.js +19 -9
  246. package/dist/import/neutral-schema.d.ts +11 -11
  247. package/dist/mcp/auth/access.d.ts +70 -0
  248. package/dist/mcp/auth/access.js +58 -0
  249. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  250. package/dist/mcp/auth/grants-cli.js +518 -0
  251. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  252. package/dist/mcp/auth/host-account-cli.js +47 -0
  253. package/dist/mcp/auth/ownership.d.ts +37 -63
  254. package/dist/mcp/auth/ownership.js +19 -14
  255. package/dist/mcp/auth/principal.d.ts +1 -1
  256. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  257. package/dist/mcp/auth/repo-authorization.js +51 -0
  258. package/dist/mcp/auth/resource-server.d.ts +32 -3
  259. package/dist/mcp/auth/resource-server.js +84 -3
  260. package/dist/mcp/auth/self-hosted/browser.js +22 -8
  261. package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
  262. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  263. package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
  264. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  265. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
  266. package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
  267. package/dist/mcp/context.d.ts +28 -2
  268. package/dist/mcp/errors.d.ts +25 -6
  269. package/dist/mcp/errors.js +98 -0
  270. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  271. package/dist/mcp/host-events/github-ingress.js +92 -0
  272. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  273. package/dist/mcp/host-events/github-user-callback.js +50 -0
  274. package/dist/mcp/index.d.ts +8 -1
  275. package/dist/mcp/index.js +106 -16
  276. package/dist/mcp/server.d.ts +1 -1
  277. package/dist/mcp/server.js +27 -9
  278. package/dist/mcp/shared/page-style.d.ts +16 -0
  279. package/dist/mcp/shared/page-style.js +129 -0
  280. package/dist/mcp/tasks/manager.d.ts +1 -1
  281. package/dist/mcp/tools/agents.js +447 -49
  282. package/dist/mcp/tools/budget-groups.js +3 -3
  283. package/dist/mcp/tools/datastore.js +23 -16
  284. package/dist/mcp/tools/grants.d.ts +2 -0
  285. package/dist/mcp/tools/grants.js +239 -0
  286. package/dist/mcp/tools/help.d.ts +5 -0
  287. package/dist/mcp/tools/help.js +67 -0
  288. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  289. package/dist/mcp/tools/host-accounts.js +114 -0
  290. package/dist/mcp/tools/memory.d.ts +7 -1
  291. package/dist/mcp/tools/memory.js +5 -5
  292. package/dist/mcp/tools/repositories.d.ts +2 -0
  293. package/dist/mcp/tools/repositories.js +181 -0
  294. package/dist/mcp/tools/runs.d.ts +6 -0
  295. package/dist/mcp/tools/runs.js +60 -6
  296. package/dist/mcp/tools/scheduling.js +4 -7
  297. package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
  298. package/dist/mcp/tools/secret-elicitation-form.js +10 -28
  299. package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
  300. package/dist/mcp/tools/secrets.js +18 -6
  301. package/dist/mcp/tools/services.d.ts +2 -0
  302. package/dist/mcp/tools/services.js +222 -0
  303. package/dist/mcp/tools/subagents.js +53 -17
  304. package/dist/mcp/tools/tools.d.ts +41 -0
  305. package/dist/mcp/tools/tools.js +202 -41
  306. package/dist/mcp/tools/trigger.js +36 -6
  307. package/dist/mcp/tools/webhooks.js +15 -4
  308. package/dist/mcp/transport/streamable-http.d.ts +11 -1
  309. package/dist/mcp/transport/streamable-http.js +32 -2
  310. package/dist/mcp/unattended-schedules.d.ts +1 -1
  311. package/dist/mcp/webhooks/ingress.d.ts +1 -1
  312. package/dist/providers/auth/delegating.d.ts +19 -0
  313. package/dist/providers/auth/delegating.js +71 -0
  314. package/dist/providers/auth/index.d.ts +3 -1
  315. package/dist/providers/auth/index.js +11 -0
  316. package/dist/providers/auth/self-hosted.d.ts +4 -3
  317. package/dist/providers/auth/self-hosted.js +16 -4
  318. package/dist/providers/auth/types.d.ts +6 -0
  319. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  320. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  321. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  322. package/dist/providers/coding-proxy/metering.js +28 -2
  323. package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
  324. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  325. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  326. package/dist/providers/coding-proxy/proxy.js +479 -18
  327. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  328. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  329. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  330. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  331. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  332. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  333. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  334. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  335. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  336. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  337. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  338. package/dist/providers/coding-proxy/registry/store.js +70 -0
  339. package/dist/providers/coding-proxy/runtime.d.ts +18 -1
  340. package/dist/providers/coding-proxy/runtime.js +63 -0
  341. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  342. package/dist/providers/coding-proxy/server.d.ts +11 -0
  343. package/dist/providers/coding-proxy/server.js +163 -0
  344. package/dist/providers/coding-proxy/types.d.ts +15 -0
  345. package/dist/providers/datastore/postgres.d.ts +1 -1
  346. package/dist/providers/engine/types.d.ts +10 -0
  347. package/dist/providers/executor/build.d.ts +3 -3
  348. package/dist/providers/executor/composition.d.ts +4 -1
  349. package/dist/providers/executor/composition.js +19 -0
  350. package/dist/providers/executor/container.d.ts +106 -4
  351. package/dist/providers/executor/container.js +305 -22
  352. package/dist/providers/executor/dbos.d.ts +3 -4
  353. package/dist/providers/executor/in-process.d.ts +2 -3
  354. package/dist/providers/executor/routing.d.ts +7 -0
  355. package/dist/providers/executor/routing.js +13 -0
  356. package/dist/providers/executor/types.d.ts +22 -0
  357. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  358. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  359. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  360. package/dist/providers/jobs/collect-prune.js +27 -0
  361. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  362. package/dist/providers/jobs/docker-isolation.js +213 -22
  363. package/dist/providers/jobs/docker-services.d.ts +35 -0
  364. package/dist/providers/jobs/docker-services.js +191 -0
  365. package/dist/providers/jobs/docker.d.ts +60 -2
  366. package/dist/providers/jobs/docker.js +313 -50
  367. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  368. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  369. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  370. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  371. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  372. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  373. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  374. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  375. package/dist/providers/jobs/kubernetes.js +117 -31
  376. package/dist/providers/jobs/types.d.ts +20 -0
  377. package/dist/providers/llm/anthropic.js +6 -2
  378. package/dist/providers/llm/bedrock.js +2 -1
  379. package/dist/providers/llm/claude-messages.d.ts +5 -1
  380. package/dist/providers/llm/claude-messages.js +1 -0
  381. package/dist/providers/llm/claude-provider.d.ts +3 -1
  382. package/dist/providers/llm/claude-provider.js +5 -1
  383. package/dist/providers/llm/index.d.ts +1 -1
  384. package/dist/providers/llm/index.js +1 -1
  385. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  386. package/dist/providers/llm/pricing-anthropic.js +32 -14
  387. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  388. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  389. package/dist/providers/llm/routing.d.ts +10 -1
  390. package/dist/providers/llm/routing.js +18 -0
  391. package/dist/providers/llm/types.d.ts +12 -0
  392. package/dist/providers/llm/types.js +8 -1
  393. package/dist/providers/memory/postgres.d.ts +1 -1
  394. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  395. package/dist/providers/review-host/diff-lines.js +59 -0
  396. package/dist/providers/review-host/github-events.d.ts +6 -0
  397. package/dist/providers/review-host/github-events.js +180 -0
  398. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  399. package/dist/providers/review-host/github-user-auth.js +128 -0
  400. package/dist/providers/review-host/github.d.ts +57 -0
  401. package/dist/providers/review-host/github.js +567 -0
  402. package/dist/providers/review-host/index.d.ts +12 -0
  403. package/dist/providers/review-host/index.js +30 -0
  404. package/dist/providers/review-host/review-format.d.ts +19 -0
  405. package/dist/providers/review-host/review-format.js +59 -0
  406. package/dist/providers/review-host/types.d.ts +284 -0
  407. package/dist/providers/review-host/types.js +24 -0
  408. package/dist/providers/vcs/git.d.ts +16 -6
  409. package/dist/providers/vcs/git.js +79 -14
  410. package/dist/providers/vcs/github.d.ts +74 -3
  411. package/dist/providers/vcs/github.js +195 -15
  412. package/dist/providers/vcs/types.d.ts +42 -4
  413. package/dist/quickstart/index.js +34 -32
  414. package/dist/quickstart/migrate.d.ts +23 -0
  415. package/dist/quickstart/migrate.js +125 -0
  416. package/dist/sandbox/fetch-policy.d.ts +20 -2
  417. package/dist/sandbox/fetch-policy.js +64 -3
  418. package/dist/sandbox/host-functions.d.ts +9 -1
  419. package/dist/sandbox/host-functions.js +12 -5
  420. package/dist/serve.js +1 -1
  421. package/dist/wardby-bin.js +6 -0
  422. package/docs/README.md +30 -0
  423. package/docs/architecture-runtime.md +90 -0
  424. package/docs/assets/brand/wardby-icon-512.png +0 -0
  425. package/docs/assets/brand/wardby-icon.svg +16 -0
  426. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  427. package/docs/assets/brand/wardby-mascot.png +0 -0
  428. package/docs/assets/brand/wardby-mascot.svg +5 -0
  429. package/docs/assets/wardby-workflow.svg +106 -0
  430. package/docs/code-review-agents.md +481 -0
  431. package/docs/coding-agent-setup.md +172 -0
  432. package/docs/coding-packages.md +455 -0
  433. package/docs/coding-services.md +300 -0
  434. package/docs/coding-worker-byo-images.md +98 -0
  435. package/docs/coding-worker-isolation.md +1061 -0
  436. package/docs/getting-started-gke.md +615 -0
  437. package/docs/getting-started-identity-provider.md +308 -0
  438. package/docs/getting-started.md +133 -0
  439. package/docs/observability.md +53 -0
  440. package/docs/release-verification.md +66 -0
  441. package/docs/security-deployment.md +657 -0
  442. package/help/code-review-agents.md +31 -0
  443. package/help/coding-packages.md +31 -0
  444. package/help/coding-services.md +71 -0
  445. package/help/creating-agents.md +68 -0
  446. package/help/deploy-gke.md +39 -0
  447. package/help/deployment-targets.md +39 -0
  448. package/help/errors/budget-group-exhausted.md +25 -0
  449. package/help/errors/docker-isolation-unsupported.md +26 -0
  450. package/help/errors/protected-path.md +45 -0
  451. package/help/errors/repo-access.md +27 -0
  452. package/help/errors/service-declaration-invalid.md +27 -0
  453. package/help/errors/service-declaration-unavailable.md +25 -0
  454. package/help/errors/service-launcher-unsupported.md +30 -0
  455. package/help/errors/service-not-allowed.md +27 -0
  456. package/help/errors/service-unknown.md +23 -0
  457. package/help/errors/service-unready.md +49 -0
  458. package/help/getting-started.md +31 -0
  459. package/help/github.md +30 -0
  460. package/help/identity-and-access.md +39 -0
  461. package/help/mcp.md +30 -0
  462. package/help/native-capabilities.md +30 -0
  463. package/help/observability.md +41 -0
  464. package/help/operating-agents.md +30 -0
  465. package/help/security.md +27 -0
  466. package/help/troubleshooting/budgets.md +26 -0
  467. package/help/troubleshooting/coding-workers.md +47 -0
  468. package/help/troubleshooting/repository-access.md +27 -0
  469. package/package.json +30 -11
  470. package/prisma/migrate.config.mjs +20 -0
  471. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  472. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  473. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  474. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  475. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  476. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  477. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  478. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  479. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  480. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  481. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  482. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  483. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  484. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  485. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  486. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  487. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  488. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  489. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  490. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  491. package/prisma/schema.prisma +417 -23
@@ -6,15 +6,34 @@
6
6
  */
7
7
  import { createHash } from "node:crypto";
8
8
  import { isIP } from "node:net";
9
- import { CODING_PROXY_ALIAS, CODING_PROXY_DENY_PORT, CODING_PROXY_PORT, CODING_WORKER_GID, CODING_WORKER_UID, WORKER_STOP_GRACE_SECONDS, isRepositoryDigest, } from "./docker-isolation.js";
10
- import { GVISOR_RUNTIME_CLASS, KubernetesPlatformError, STORAGE_INIT_EPHEMERAL_MIB, WORKER_EPHEMERAL_MIB, conformResources, describeMib, normalizePlatformMetadata, platformProfile, podEphemeralStorageMib, } from "./kubernetes-platform.js";
9
+ import { StoredCodingServicesSchema } from "../../coding/services/catalog.js";
10
+ import { CLAUDE_TOOL_SETUP_ENV, CODING_PROXY_ALIAS, CODING_PROXY_DENY_PORT, CODING_PROXY_PORT, CODING_WORKER_GID, CODING_WORKER_UID, WORKER_STOP_GRACE_SECONDS, claudeToolLimits, isRepositoryDigest, } from "./docker-isolation.js";
11
+ import { GVISOR_RUNTIME_CLASS, KubernetesPlatformError, STORAGE_INIT_EPHEMERAL_MIB, TOOL_RUNNER_EPHEMERAL_MIB, WORKER_EPHEMERAL_MIB, conformResources, describeMib, normalizePlatformMetadata, platformProfile, podEphemeralStorageMib, } from "./kubernetes-platform.js";
11
12
  export const KUBERNETES_ISOLATION_ERROR = "kubernetes_isolation_unsupported";
12
- export const KUBERNETES_PROVIDER_UNSUPPORTED = "kubernetes_provider_unsupported";
13
13
  /** Extra seconds past timeoutSec before Kubernetes kills the pod (keeper included). */
14
14
  export const POD_DEADLINE_GRACE_SECONDS = 300;
15
15
  export const KEEPER_CONTAINER = "keeper";
16
16
  export const STORAGE_INIT_CONTAINER = "storage-init";
17
17
  export const WORKER_CONTAINER = "worker";
18
+ /** Claude's credential-free command runner: a native sidecar in the run pod. */
19
+ export const TOOL_RUNNER_CONTAINER = "tool-runner";
20
+ /** The run Secret's key holding the tool runner's setup (claude-tool-setup.ts). */
21
+ export const TOOL_SETUP_SECRET_KEY = "tool-setup";
22
+ /** Where the tool runner listens and the agent's relay connects (claude-tool-runner/main.mjs). */
23
+ export const TOOL_SOCKET_PATH = "/run/wardby/tool/runner.sock";
24
+ const CODEX_ENTRYPOINT = "/opt/wardby/coding-worker/main.js";
25
+ const CLAUDE_ENTRYPOINT = "/opt/wardby/claude-coding-worker/main.js";
26
+ /** Every service sidecar's container name starts with this (service-postgres, service-redis, ...). */
27
+ export const SERVICE_CONTAINER_PREFIX = "service-";
28
+ /** Disk-backed scratch for each of a service's writablePaths (a socket directory, /tmp). */
29
+ export const SERVICE_SCRATCH_MIB = 64;
30
+ export function serviceContainerName(name) {
31
+ return `${SERVICE_CONTAINER_PREFIX}${name}`;
32
+ }
33
+ /** What a service sidecar reserves of the pod's ephemeral storage: its data volume plus its scratch volumes. */
34
+ export function serviceEphemeralStorageMib(service) {
35
+ return service.resources.diskMib + SERVICE_SCRATCH_MIB * service.writablePaths.length;
36
+ }
18
37
  export const STORAGE_ROOT = "/run/wardby/storage";
19
38
  export const KEEPER_SEEDED_MARKER = `${STORAGE_ROOT}/input/.seeded`;
20
39
  export const PROXY_POD_LABEL = { "app.kubernetes.io/name": "wardby-coding-proxy" };
@@ -36,33 +55,55 @@ export const ENFORCEMENT_PROBE_PROXY_UNREACHABLE = 4;
36
55
  export const ENFORCEMENT_PROBE_DENY_REFUSED = 5;
37
56
  const WORKER_SERVICE_ACCOUNT = "wardby-coding-worker";
38
57
  const RUN_ID = /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,199}$/;
39
- /** The worker waits for the launcher's seeded marker, then runs the image's normal entrypoint. */
40
- const WORKER_GATE = [
41
- 'const fs = require("node:fs");',
42
- 'const marker = "/run/wardby/input/.seeded";',
43
- "(function wait() {",
44
- " if (fs.existsSync(marker)) {",
45
- ' import("/opt/wardby/coding-worker/main.js").catch(() => { process.exitCode = 1; });',
46
- " } else {",
47
- " setTimeout(wait, 250);",
48
- " }",
49
- "})();",
50
- ].join("\n");
58
+ /** The worker waits for the launcher's seeded marker, then runs its provider's entrypoint. */
59
+ function workerGate(entrypoint) {
60
+ return [
61
+ 'const fs = require("node:fs");',
62
+ 'const marker = "/run/wardby/input/.seeded";',
63
+ "(function wait() {",
64
+ " if (fs.existsSync(marker)) {",
65
+ ` import(${JSON.stringify(entrypoint)}).catch(() => { process.exitCode = 1; });`,
66
+ " } else {",
67
+ " setTimeout(wait, 250);",
68
+ " }",
69
+ "})();",
70
+ ].join("\n");
71
+ }
51
72
  /**
52
73
  * Creates the worker's subPath mount sources, owned by the run uid, before any regular container
53
74
  * starts. Otherwise the kubelet creates them root-owned while setting up the worker's subPath mounts,
54
75
  * and the keeper (uid 10001, no capabilities) can't chmod them. Idempotent; no shell.
55
76
  */
56
- const STORAGE_INIT_SCRIPT = [
57
- 'const fs = require("node:fs");',
58
- 'for (const name of ["workspace", "input", "output"]) {',
59
- ` const path = ${JSON.stringify(STORAGE_ROOT)} + "/" + name;`,
60
- " fs.mkdirSync(path, { recursive: true, mode: 0o700 });",
61
- " fs.chmodSync(path, 0o700);",
62
- "}",
63
- ].join("\n");
64
- function isolationError() {
65
- return new Error(KUBERNETES_ISOLATION_ERROR);
77
+ function storageInitScript(directories) {
78
+ return [
79
+ 'const fs = require("node:fs");',
80
+ `for (const name of ${JSON.stringify(directories).replaceAll(",", ", ")}) {`,
81
+ ` const path = ${JSON.stringify(STORAGE_ROOT)} + "/" + name;`,
82
+ " fs.mkdirSync(path, { recursive: true, mode: 0o700 });",
83
+ " fs.chmodSync(path, 0o700);",
84
+ "}",
85
+ ].join("\n");
86
+ }
87
+ const STORAGE_DIRECTORIES = ["workspace", "input", "output"];
88
+ /**
89
+ * Claude's socket volume: its own memory-backed emptyDir, mounted by the worker and the tool runner
90
+ * only. Under gVisor a Unix socket bound on the disk-backed storage volume is invisible to another
91
+ * container; a memory-backed emptyDir that two containers mount is one tmpfs shared across the
92
+ * sandbox (GKE Autopilot annotates it `share: pod`), so the agent's relay can connect.
93
+ */
94
+ const TOOL_SOCKET_VOLUME = "tool-socket";
95
+ const TOOL_SOCKET_VOLUME_MIB = 1;
96
+ /**
97
+ * `reason` is a short, fixed code naming which check failed (never a value from the spec itself,
98
+ * so nothing operator-supplied — an image reference, a limit — ever ends up in the message). It's
99
+ * appended the same way kubernetes-preflight.ts's `failure(check, cause)` tags a canary failure:
100
+ * callers that only match on the bare `KUBERNETES_ISOLATION_ERROR` prefix (a substring match, e.g.
101
+ * `toThrow(KUBERNETES_ISOLATION_ERROR)` or `error.message.startsWith(...)`) are unaffected, while
102
+ * an operator reading the message (or a `this.warn(...)` of it — see kubernetes.ts's `launch()`)
103
+ * gets more than a bare code to go on.
104
+ */
105
+ function isolationError(reason) {
106
+ return new Error(reason === undefined ? KUBERNETES_ISOLATION_ERROR : `${KUBERNETES_ISOLATION_ERROR}:${reason}`);
66
107
  }
67
108
  function sha256(value) {
68
109
  return createHash("sha256").update(value).digest("hex");
@@ -105,22 +146,43 @@ function isWholeMillicores(cpus) {
105
146
  }
106
147
  export function validateKubernetesSpec(spec) {
107
148
  if (spec.kind !== "coding-agent" || !RUN_ID.test(spec.runId))
108
- throw isolationError();
109
- if (spec.provider === "claude-code")
110
- throw new Error(KUBERNETES_PROVIDER_UNSUPPORTED);
111
- if (spec.provider !== undefined && spec.provider !== "codex")
112
- throw isolationError();
113
- if (spec.toolImage !== undefined || !isRegistryDigest(spec.image))
114
- throw isolationError();
115
- const { cpus, memoryMb, pids, diskMb } = spec.limits;
116
- if (!inRange(cpus, 0.1, 32, false) ||
117
- !isWholeMillicores(cpus) ||
118
- !inRange(memoryMb, 128, 65_536, true) ||
119
- !inRange(pids, 16, 4_096, true) ||
120
- !inRange(diskMb, 64, 32_768, true) ||
121
- !inRange(spec.timeoutSec, 1, 86_400, true)) {
122
- throw isolationError();
149
+ throw isolationError("run-id");
150
+ if (spec.provider !== undefined && spec.provider !== "codex" && spec.provider !== "claude-code") {
151
+ throw isolationError("provider");
152
+ }
153
+ if (spec.provider === "claude-code") {
154
+ if (spec.toolImage === undefined || !isRegistryDigest(spec.toolImage)) {
155
+ throw isolationError("tool-image-not-registry-digest");
156
+ }
157
+ // Docker's floor for the same split (docker-isolation.ts validateSpec): the tool runner takes 0.25 CPU and ≥128 MiB.
158
+ if (spec.limits.cpus < 0.35 || spec.limits.memoryMb < 256)
159
+ throw isolationError("claude-limits-too-small");
160
+ }
161
+ else if (spec.toolImage !== undefined) {
162
+ throw isolationError("tool-image-set");
123
163
  }
164
+ if (!isRegistryDigest(spec.image))
165
+ throw isolationError("image-not-registry-digest");
166
+ if (spec.services !== undefined) {
167
+ // Re-validated here, not trusted from dispatch: every image a digest, every path and probe in bounds.
168
+ const names = spec.services.map((service) => service.name);
169
+ if (spec.services.length === 0 ||
170
+ new Set(names).size !== names.length ||
171
+ !StoredCodingServicesSchema.safeParse(spec.services).success) {
172
+ throw isolationError("services-invalid");
173
+ }
174
+ }
175
+ const { cpus, memoryMb, pids, diskMb } = spec.limits;
176
+ if (!inRange(cpus, 0.1, 32, false) || !isWholeMillicores(cpus))
177
+ throw isolationError("cpus-out-of-range");
178
+ if (!inRange(memoryMb, 128, 65_536, true))
179
+ throw isolationError("memory-out-of-range");
180
+ if (!inRange(pids, 16, 4_096, true))
181
+ throw isolationError("pids-out-of-range");
182
+ if (!inRange(diskMb, 64, 32_768, true))
183
+ throw isolationError("disk-out-of-range");
184
+ if (!inRange(spec.timeoutSec, 1, 86_400, true))
185
+ throw isolationError("timeout-out-of-range");
124
186
  }
125
187
  function containerSecurity() {
126
188
  return {
@@ -131,6 +193,56 @@ function containerSecurity() {
131
193
  capabilities: { drop: ["ALL"] },
132
194
  };
133
195
  }
196
+ /**
197
+ * The emptyDirs behind one service's mounts: its data path, then each writable path. The only place
198
+ * their names are built, so the sidecar's volumeMounts and the pod's volumes can never disagree.
199
+ */
200
+ function serviceStorage(service, index) {
201
+ return [
202
+ { name: `service-${index}-data`, mountPath: service.dataPath, sizeMib: service.resources.diskMib },
203
+ ...service.writablePaths.map((path, scratch) => ({
204
+ name: `service-${index}-scratch-${scratch}`,
205
+ mountPath: path,
206
+ sizeMib: SERVICE_SCRATCH_MIB,
207
+ })),
208
+ ];
209
+ }
210
+ /**
211
+ * One coding-run service as a native sidecar (docs/coding-services.md): an init container with
212
+ * restartPolicy Always, so the kubelet starts it before the keeper and worker and holds them until
213
+ * its startup probe (the catalog's readiness command) passes. It runs the catalog's image with its
214
+ * own entrypoint, under the same security context as every other container, with an emptyDir at its
215
+ * data path and at each writable path. Same pod, same network namespace: the worker reaches it on
216
+ * 127.0.0.1, and the run's NetworkPolicy needs no change.
217
+ */
218
+ function serviceSidecar(service, index, profile) {
219
+ const env = Object.entries(service.serviceEnv)
220
+ .sort(([left], [right]) => left.localeCompare(right))
221
+ .map(([name, value]) => ({ name, value }));
222
+ return {
223
+ name: serviceContainerName(service.name),
224
+ image: service.image,
225
+ restartPolicy: "Always",
226
+ ...(env.length > 0 ? { env } : {}),
227
+ securityContext: containerSecurity(),
228
+ resources: conformResources(profile, {
229
+ cpuMillicores: service.resources.cpuMillicores,
230
+ memoryMib: service.resources.memoryMib,
231
+ ephemeralStorageMib: serviceEphemeralStorageMib(service),
232
+ }),
233
+ startupProbe: {
234
+ exec: { command: [...service.readiness.command] },
235
+ periodSeconds: service.readiness.periodSeconds,
236
+ timeoutSeconds: service.readiness.timeoutSeconds,
237
+ failureThreshold: service.readiness.failureThreshold,
238
+ successThreshold: 1,
239
+ },
240
+ volumeMounts: serviceStorage(service, index).map(({ name, mountPath }) => ({ name, mountPath })),
241
+ };
242
+ }
243
+ function serviceVolumes(services) {
244
+ return services.flatMap((service, index) => serviceStorage(service, index).map(({ name, sizeMib }) => ({ name, emptyDir: { sizeLimit: `${sizeMib}Mi` } })));
245
+ }
134
246
  export function buildRunPod(spec, options) {
135
247
  validateKubernetesSpec(spec);
136
248
  const profile = platformProfile(options.platform ?? "generic");
@@ -144,19 +256,23 @@ export function buildRunPod(spec, options) {
144
256
  // conformResources range-checks one container at a time; only this function sees every container,
145
257
  // so the SUMMED pod total is checked here — before submission, so an over-large workspace fails
146
258
  // closed rather than being rewritten by the platform (which attestation would then reject anyway).
259
+ const services = spec.services ?? [];
147
260
  const ceiling = profile.resources.ephemeralStorageCeilingMib;
148
- const podEphemeral = podEphemeralStorageMib(spec.limits.diskMb);
261
+ const podEphemeral = podEphemeralStorageMib(spec.limits.diskMb, services.reduce((total, service) => total + serviceEphemeralStorageMib(service), 0));
149
262
  if (ceiling !== undefined && podEphemeral > ceiling) {
150
- throw new KubernetesPlatformError(`a ${spec.limits.diskMb} MiB workspace needs ${podEphemeral} MiB of pod ephemeral storage, over the ${describeMib(ceiling)} ceiling of platform ${profile.name}`);
263
+ throw new KubernetesPlatformError(`a ${spec.limits.diskMb} MiB workspace${services.length > 0 ? " and its services" : ""} need${services.length > 0 ? "" : "s"} ${podEphemeral} MiB of pod ephemeral storage, over the ${describeMib(ceiling)} ceiling of platform ${profile.name}`);
151
264
  }
152
265
  const names = kubernetesRunNames(spec.runId);
153
266
  const scratchMb = Math.max(16, Math.min(64, Math.floor(spec.limits.memoryMb / 8)));
154
267
  const sidecarCpuMillicores = 250;
155
268
  const sidecarMemoryMib = 128;
269
+ const claude = spec.provider === "claude-code";
270
+ const tool = claudeToolLimits(spec);
271
+ const toolScratchMb = Math.max(16, Math.min(64, Math.floor(tool.memoryMb / 8)));
156
272
  const storageInit = {
157
273
  name: STORAGE_INIT_CONTAINER,
158
274
  image: spec.image,
159
- command: ["node", "-e", STORAGE_INIT_SCRIPT],
275
+ command: ["node", "-e", storageInitScript(STORAGE_DIRECTORIES)],
160
276
  securityContext: containerSecurity(),
161
277
  resources: conformResources(profile, {
162
278
  cpuMillicores: sidecarCpuMillicores,
@@ -182,25 +298,73 @@ export function buildRunPod(spec, options) {
182
298
  const worker = {
183
299
  name: WORKER_CONTAINER,
184
300
  image: spec.image,
185
- command: ["node", "-e", WORKER_GATE],
301
+ command: ["node", "-e", workerGate(claude ? CLAUDE_ENTRYPOINT : CODEX_ENTRYPOINT)],
186
302
  env: [
187
303
  { name: "WARDBY_PROXY_URL", value: `http://${CODING_PROXY_ALIAS}:${CODING_PROXY_PORT}` },
188
304
  { name: "WARDBY_RUN_CAPABILITY", valueFrom: { secretKeyRef: { name: names.secret, key: "capability" } } },
189
305
  ],
190
306
  securityContext: containerSecurity(),
191
307
  resources: conformResources(profile, {
192
- cpuMillicores: Math.round(spec.limits.cpus * 1000),
193
- memoryMib: spec.limits.memoryMb,
194
- ephemeralStorageMib: WORKER_EPHEMERAL_MIB,
308
+ cpuMillicores: Math.round((claude ? spec.limits.cpus - tool.cpus : spec.limits.cpus) * 1000),
309
+ memoryMib: claude ? spec.limits.memoryMb - tool.memoryMb : spec.limits.memoryMb,
310
+ ephemeralStorageMib: claude ? WORKER_EPHEMERAL_MIB - TOOL_RUNNER_EPHEMERAL_MIB : WORKER_EPHEMERAL_MIB,
195
311
  }),
196
- volumeMounts: [
197
- { name: "storage", mountPath: "/workspace", subPath: "workspace" },
198
- { name: "storage", mountPath: "/run/wardby/input", subPath: "input", readOnly: true },
199
- { name: "storage", mountPath: "/run/wardby/output", subPath: "output" },
200
- { name: "tmp", mountPath: "/tmp" },
201
- { name: "home", mountPath: "/home/wardby" },
202
- ],
312
+ // Claude's agent never sees the repository: the tool runner does, over the socket.
313
+ volumeMounts: claude
314
+ ? [
315
+ { name: "storage", mountPath: "/run/wardby/input", subPath: "input", readOnly: true },
316
+ { name: "storage", mountPath: "/run/wardby/output", subPath: "output" },
317
+ { name: TOOL_SOCKET_VOLUME, mountPath: "/run/wardby/tool" },
318
+ { name: "tmp", mountPath: "/tmp" },
319
+ { name: "home", mountPath: "/home/wardby" },
320
+ ]
321
+ : [
322
+ { name: "storage", mountPath: "/workspace", subPath: "workspace" },
323
+ { name: "storage", mountPath: "/run/wardby/input", subPath: "input", readOnly: true },
324
+ { name: "storage", mountPath: "/run/wardby/output", subPath: "output" },
325
+ { name: "tmp", mountPath: "/tmp" },
326
+ { name: "home", mountPath: "/home/wardby" },
327
+ ],
203
328
  };
329
+ /**
330
+ * Claude's tool runner (docs/coding-worker-isolation.md): a native sidecar, so the kubelet starts it
331
+ * before the keeper and worker and holds them until its socket exists. It mounts the workspace and
332
+ * the socket, gets its registry/service setup from the run's Secret, and never the capability. It
333
+ * shares the pod's network namespace, so the run's single egress rule (the proxy) applies to it too.
334
+ */
335
+ const toolRunner = claude
336
+ ? {
337
+ name: TOOL_RUNNER_CONTAINER,
338
+ image: spec.toolImage,
339
+ command: ["node", "/opt/wardby/claude-tool-runner/main.mjs"],
340
+ restartPolicy: "Always",
341
+ env: [
342
+ {
343
+ name: CLAUDE_TOOL_SETUP_ENV,
344
+ valueFrom: { secretKeyRef: { name: names.secret, key: TOOL_SETUP_SECRET_KEY } },
345
+ },
346
+ ],
347
+ securityContext: containerSecurity(),
348
+ resources: conformResources(profile, {
349
+ cpuMillicores: Math.round(tool.cpus * 1000),
350
+ memoryMib: tool.memoryMb,
351
+ ephemeralStorageMib: TOOL_RUNNER_EPHEMERAL_MIB,
352
+ }),
353
+ startupProbe: {
354
+ exec: { command: ["test", "-S", TOOL_SOCKET_PATH] },
355
+ periodSeconds: 1,
356
+ timeoutSeconds: 1,
357
+ failureThreshold: 60,
358
+ successThreshold: 1,
359
+ },
360
+ volumeMounts: [
361
+ { name: "storage", mountPath: "/workspace", subPath: "workspace" },
362
+ { name: TOOL_SOCKET_VOLUME, mountPath: "/run/wardby/tool" },
363
+ { name: "tool-tmp", mountPath: "/tmp" },
364
+ { name: "tool-home", mountPath: "/home/wardby" },
365
+ ],
366
+ }
367
+ : undefined;
204
368
  return {
205
369
  apiVersion: "v1",
206
370
  kind: "Pod",
@@ -226,6 +390,9 @@ export function buildRunPod(spec, options) {
226
390
  dnsConfig: { nameservers: ["127.0.0.1"] },
227
391
  hostAliases: [{ ip: options.proxyIp, hostnames: [CODING_PROXY_ALIAS] }],
228
392
  ...(options.runtimeClassName ? { runtimeClassName: options.runtimeClassName } : {}),
393
+ // Part of the submitted spec, so attestation compares it like any other field; the
394
+ // `priority` and `preemptionPolicy` the API server resolves from it are normalized away.
395
+ ...(options.priorityClassName ? { priorityClassName: options.priorityClassName } : {}),
229
396
  securityContext: {
230
397
  runAsNonRoot: true,
231
398
  runAsUser: CODING_WORKER_UID,
@@ -237,8 +404,22 @@ export function buildRunPod(spec, options) {
237
404
  { name: "storage", emptyDir: { sizeLimit: `${spec.limits.diskMb}Mi` } },
238
405
  { name: "tmp", emptyDir: { medium: "Memory", sizeLimit: `${scratchMb}Mi` } },
239
406
  { name: "home", emptyDir: { medium: "Memory", sizeLimit: `${scratchMb}Mi` } },
407
+ ...(claude
408
+ ? [
409
+ { name: TOOL_SOCKET_VOLUME, emptyDir: { medium: "Memory", sizeLimit: `${TOOL_SOCKET_VOLUME_MIB}Mi` } },
410
+ { name: "tool-tmp", emptyDir: { medium: "Memory", sizeLimit: `${toolScratchMb}Mi` } },
411
+ { name: "tool-home", emptyDir: { medium: "Memory", sizeLimit: `${toolScratchMb}Mi` } },
412
+ ]
413
+ : []),
414
+ ...serviceVolumes(services),
415
+ ],
416
+ // storage-init first (it must finish before any subPath mount), then Claude's tool runner, then each
417
+ // service sidecar in declaration order; the keeper and worker start only once every sidecar started.
418
+ initContainers: [
419
+ storageInit,
420
+ ...(toolRunner ? [toolRunner] : []),
421
+ ...services.map((service, index) => serviceSidecar(service, index, profile)),
240
422
  ],
241
- initContainers: [storageInit],
242
423
  containers: [keeper, worker],
243
424
  },
244
425
  };
@@ -262,14 +443,14 @@ export function buildRunNetworkPolicy(spec, namespace) {
262
443
  },
263
444
  };
264
445
  }
265
- export function buildCapabilitySecret(spec, namespace, capability) {
446
+ export function buildCapabilitySecret(spec, namespace, capability, toolSetup) {
266
447
  const names = kubernetesRunNames(spec.runId);
267
448
  return {
268
449
  apiVersion: "v1",
269
450
  kind: "Secret",
270
451
  type: "Opaque",
271
452
  metadata: { name: names.secret, namespace, labels: runLabels(spec.runId) },
272
- stringData: { capability },
453
+ stringData: { capability, ...(toolSetup !== undefined ? { [TOOL_SETUP_SECRET_KEY]: toolSetup } : {}) },
273
454
  };
274
455
  }
275
456
  /**
@@ -41,6 +41,11 @@ export declare const GVISOR_RUNTIME_CLASS = "gvisor";
41
41
  export declare const STORAGE_INIT_EPHEMERAL_MIB = 64;
42
42
  /** Ephemeral storage reserved for the worker's writable layer (/tmp and /home are memory-backed). */
43
43
  export declare const WORKER_EPHEMERAL_MIB = 1024;
44
+ /**
45
+ * A Claude run's tool runner's share of WORKER_EPHEMERAL_MIB (its writable layer; /tmp and home are
46
+ * memory-backed). The agent gets the rest, so a Claude pod reserves exactly what a Codex pod does.
47
+ */
48
+ export declare const TOOL_RUNNER_EPHEMERAL_MIB = 256;
44
49
  export interface PlatformResourceRules {
45
50
  /** Smallest CPU request the platform accepts, in millicores. */
46
51
  cpuFloorMillicores: number;
@@ -118,11 +123,12 @@ export interface ContainerResources {
118
123
  */
119
124
  export declare function conformResources(profile: KubernetesPlatformProfile, request: ContainerResourceRequest): ContainerResources;
120
125
  /**
121
- * The pod's total ephemeral storage: max(init containers) + sum(regular containers).
122
- * storage-init's 64 MiB never exceeds keeper + worker, so the total is the keeper's
123
- * storage volume plus the worker's reservation.
126
+ * The pod's total ephemeral storage: max(init containers) + sum(regular containers and sidecars).
127
+ * storage-init's 64 MiB never exceeds the rest, so the total is the keeper's storage volume, the
128
+ * worker's reservation, and whatever the run's service sidecars reserve (their data and scratch
129
+ * emptyDirs: serviceEphemeralStorageMib in kubernetes-isolation.ts).
124
130
  */
125
- export declare function podEphemeralStorageMib(diskMb: number): number;
131
+ export declare function podEphemeralStorageMib(diskMb: number, servicesMib?: number): number;
126
132
  /**
127
133
  * Renders a MiB figure with its GiB equivalent, e.g. `10240 MiB (10 GiB)`. Both
128
134
  * places that report the ephemeral-storage ceiling use this rather than writing
@@ -12,6 +12,11 @@ export const GVISOR_RUNTIME_CLASS = "gvisor";
12
12
  export const STORAGE_INIT_EPHEMERAL_MIB = 64;
13
13
  /** Ephemeral storage reserved for the worker's writable layer (/tmp and /home are memory-backed). */
14
14
  export const WORKER_EPHEMERAL_MIB = 1024;
15
+ /**
16
+ * A Claude run's tool runner's share of WORKER_EPHEMERAL_MIB (its writable layer; /tmp and home are
17
+ * memory-backed). The agent gets the rest, so a Claude pod reserves exactly what a Codex pod does.
18
+ */
19
+ export const TOOL_RUNNER_EPHEMERAL_MIB = 256;
15
20
  /**
16
21
  * Deep-freezes a profile. An allowance list is the set of differences attestation
17
22
  * will forgive: nothing at runtime — a later module, a test helper, a mistaken
@@ -174,12 +179,13 @@ export function conformResources(profile, request) {
174
179
  return { requests, limits: { ...requests } };
175
180
  }
176
181
  /**
177
- * The pod's total ephemeral storage: max(init containers) + sum(regular containers).
178
- * storage-init's 64 MiB never exceeds keeper + worker, so the total is the keeper's
179
- * storage volume plus the worker's reservation.
182
+ * The pod's total ephemeral storage: max(init containers) + sum(regular containers and sidecars).
183
+ * storage-init's 64 MiB never exceeds the rest, so the total is the keeper's storage volume, the
184
+ * worker's reservation, and whatever the run's service sidecars reserve (their data and scratch
185
+ * emptyDirs: serviceEphemeralStorageMib in kubernetes-isolation.ts).
180
186
  */
181
- export function podEphemeralStorageMib(diskMb) {
182
- return diskMb + WORKER_EPHEMERAL_MIB;
187
+ export function podEphemeralStorageMib(diskMb, servicesMib = 0) {
188
+ return diskMb + WORKER_EPHEMERAL_MIB + servicesMib;
183
189
  }
184
190
  /**
185
191
  * Renders a MiB figure with its GiB equivalent, e.g. `10240 MiB (10 GiB)`. Both
@@ -191,6 +191,9 @@ async function runCanary(options, state, proxyIp, timeoutMs, cleanupTimeoutMs) {
191
191
  namespace,
192
192
  proxyIp,
193
193
  runtimeClassName: config.runtimeClassName,
194
+ // The canary carries the class too, so a class missing from the cluster fails the
195
+ // preflight rather than the first real run.
196
+ priorityClassName: config.priorityClassName,
194
197
  platform: config.platform,
195
198
  });
196
199
  const podSpec = pod.spec;
@@ -1,6 +1,7 @@
1
1
  import { type Readable } from "node:stream";
2
2
  import type { KubernetesJobConfig } from "../../config/providers.js";
3
3
  import { type KubernetesApi } from "./kubernetes-api.js";
4
+ import type { CodingProvider } from "../../coding/provider.js";
4
5
  import type { JobHandle, JobResult, JobSpec, JobStatus, WorkspaceJobLauncher } from "./types.js";
5
6
  export interface KubernetesClusterInfo {
6
7
  /** The proxy Service's ClusterIP: the enforcement witness's address (8787 reachable, 8788 not). */
@@ -20,8 +21,19 @@ export interface KubernetesJobLauncherOptions {
20
21
  now?: () => number;
21
22
  sleep?: (milliseconds: number) => Promise<void>;
22
23
  readyTimeoutMs?: number;
23
- /** How long to wait for the run's NetworkPolicy to be enforced before seeding. Default 30_000. */
24
+ /**
25
+ * How long to wait for the run's NetworkPolicy to be enforced before seeding. Default 30_000,
26
+ * but never *less* than `enforcementExecTimeoutMs * ENFORCEMENT_BLOCKED_STREAK`: see the
27
+ * constructor. An explicit override here is a floor raise, not an escape hatch — the gate still
28
+ * needs room for a full streak of probes at whatever the effective exec timeout is.
29
+ */
24
30
  enforcementTimeoutMs?: number;
31
+ /**
32
+ * Bound for a single enforcement probe exec. Default 10_000 (DEFAULT_ENFORCEMENT_EXEC_TIMEOUT_MS).
33
+ * Raise this on a resource-constrained cluster (e.g. `kind` on a laptop) where a healthy probe
34
+ * can legitimately take longer than 10 s to run inside the keeper's CPU/memory limits.
35
+ */
36
+ enforcementExecTimeoutMs?: number;
25
37
  createArchive?: (directory: string) => {
26
38
  stream: Readable;
27
39
  done: Promise<number>;
@@ -44,8 +56,14 @@ export declare class KubernetesJobLauncher implements WorkspaceJobLauncher {
44
56
  private readonly createArchive;
45
57
  private readonly warn;
46
58
  private readonly enforcementTimeoutMs;
59
+ private readonly enforcementExecTimeoutMs;
47
60
  private preflightResult?;
48
61
  constructor(options: KubernetesJobLauncherOptions);
62
+ /**
63
+ * Services run as native sidecars in the run pod (kubernetes-isolation.ts), for either provider:
64
+ * Claude Code's tool runner is a sidecar in the same pod, so it reaches them on 127.0.0.1 too.
65
+ */
66
+ supportsServicesFor(provider: CodingProvider): boolean;
49
67
  /**
50
68
  * Every per-run object name derives from the run id, so the handle is known before any cluster call.
51
69
  * The executor persists it first; a crash mid-launch then still has a handle to clean up with (the
@@ -104,7 +122,8 @@ export declare class KubernetesJobLauncher implements WorkspaceJobLauncher {
104
122
  * Streams the keeper's workspace through the strict extractor into `staging`.
105
123
  * The transfer is bounded by `timeoutMs` (see transferBudgetMs): on expiry the
106
124
  * extraction is aborted. On any failure the output stream this method created
107
- * is destroyed, since the exec's own timeout does not end it.
125
+ * is destroyed, since the exec's own timeout does not end it. Excluded dependency
126
+ * and cache paths never leave the pod (see src/coding/collect-exclude.ts).
108
127
  */
109
128
  private extractWorkspace;
110
129
  /**