@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
@@ -0,0 +1,455 @@
1
+ # Installing packages in coding runs
2
+
3
+ A coding agent's worker has no direct network access — it can reach only the
4
+ coding proxy. Registry mode lets it run `npm install` and `pip install`
5
+ anyway, by routing those requests through the proxy to a per-agent allowlist,
6
+ with supply-chain safeguards and a full record of what was fetched.
7
+
8
+ **Registry mode works for both providers.** Claude Code runs every shell
9
+ command inside a separate, credential-free tool-runner container that shares
10
+ the run's proxy network with the agent, so it can reach the proxy the same
11
+ way Codex's driver does; it never receives the run's model capability, only
12
+ the registry-only settings the trusted launcher builds for it (delivered as
13
+ `WARDBY_TOOL_SETUP`). Both providers support the `node` toolchain (npm) and
14
+ the `node-python` toolchain (npm and pip). See [Claude Code](#claude-code)
15
+ below.
16
+
17
+ ## Claude Code
18
+
19
+ `npm install` and, on the `node-python` toolchain, `pip install` run inside
20
+ Claude Code's tool-runner container, the same container that mounts the
21
+ workspace and runs every other shell command — the agent container never runs
22
+ one. The toolchain picks the tool-runner image: `node` uses
23
+ `CODING_CLAUDE_TOOL_RUNNER_IMAGE`, and `node-python` with toolchain version
24
+ `3.12` uses `CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12` (Python with
25
+ pytest and ruff, the same packages as the Codex `node-python` worker). As with
26
+ Codex, pip installs go into a virtual environment
27
+ (`python -m venv --system-site-packages .venv`), and pip there reaches only the
28
+ registry. The npm lockfile check
29
+ (the shim on `PATH` that plans `npm ci`/`npm install` against the proxy
30
+ before it runs; see "Lockfile installs: verified, then approved exactly"
31
+ below) applies there exactly as it does for Codex. Before a Claude Code run's
32
+ changes are committed, the control plane rewrites any collected lockfile's
33
+ proxy URLs back to the ecosystem's real public registry URL (for example
34
+ `https://registry.npmjs.org/...`), so a lockfile in the resulting pull
35
+ request never names the internal proxy.
36
+
37
+ ## Three ways to get dependencies into a run
38
+
39
+ 1. **The standard worker image plus a package allowlist (recommended
40
+ default).** Use wardby's stock `node` or `node-python` worker image and
41
+ enable the packages your agent needs on its allowlist (below). No custom
42
+ image to build or maintain; the agent installs what it needs at run time.
43
+ 2. **A custom `workerImageRef` with dependencies baked in.** Still supported:
44
+ point the agent's profile at your own image, built on wardby's driver base
45
+ image (see [Bring-your-own worker images](coding-worker-byo-images.md)),
46
+ with everything preinstalled. Setting `workerImageRef` needs `agents:admin`
47
+ and the admin role, and the image must be pinned by digest.
48
+ 3. **Both together.** Use a custom image for a toolchain or system libraries
49
+ the registry can't provide (a compiler, a non-Node/Python runtime, apt
50
+ packages), and the registry for the project-level npm/PyPI packages your
51
+ agent adds during the run.
52
+
53
+ npm and pip in every coding run are always pointed at the proxy — this is not
54
+ optional per run. If an agent has no allowlist entries for an ecosystem (the
55
+ default for a new agent), an install in that ecosystem gets a clear refusal
56
+ (`403 wardby_package_not_allowed`) rather than silently failing or reaching
57
+ the real registry.
58
+
59
+ ## Enabling packages on an agent
60
+
61
+ Package permissions live on the coding profile's `packageAllowlist` field,
62
+ changed with `update_agent`. Changing `packageAllowlist` or `packagePolicy`
63
+ needs the `packages:approve` scope (or `agents:admin`) plus a role that grants it,
64
+ `admin` or `package-approver` (see
65
+ [roles and privileged operations](security-deployment.md#roles-and-privileged-operations)) — holding only
66
+ `agents:write` lets you manage everything else about the agent, but a
67
+ `packageAllowlist`/`packagePolicy` change from that caller is refused, since
68
+ it widens what the agent can download.
69
+
70
+ `packageAllowlist` is keyed by ecosystem (`npm`, `pypi`), each an array of
71
+ approved top-level entries. An entry is a bare package name, a name with a
72
+ version range in that ecosystem's own syntax, or (npm only) a scope wildcard:
73
+
74
+ ```json
75
+ {
76
+ "codingProfile": {
77
+ "packageAllowlist": {
78
+ "npm": ["react@^19", "@testing-library/*"],
79
+ "pypi": ["flask>=3"]
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ - `react@^19` — npm semver range syntax.
86
+ - `@testing-library/*` — every package under that npm scope.
87
+ - `flask>=3` — PEP 440 specifier syntax for PyPI.
88
+ - A bare name with no range (`"lodash"`) allows any version, subject to the
89
+ other safeguards below.
90
+ - npm names are matched exactly, including case. npm treats some legacy
91
+ capitalized names as distinct packages (`JSONStream` is not `jsonstream`),
92
+ so allowing one never allows the other; write the name as npm spells it.
93
+ (npm scopes are always lower case.) PyPI names are compared after PEP 503
94
+ normalization, so `Flask`, `flask` and `FLASK` are the same entry.
95
+
96
+ An absent or empty allowlist leaves registry mode off for that agent (the
97
+ default). Only _top-level_ entries need to be on the allowlist — once a
98
+ version is approved, the proxy reads its dependency graph from registry
99
+ metadata and grows the run's allowance automatically as npm or pip requests
100
+ each dependency's metadata, so you don't have to enumerate transitive
101
+ dependencies yourself.
102
+
103
+ ### Lockfile installs: verified, then approved exactly
104
+
105
+ `npm ci` (or `npm install` with a complete `package-lock.json`) skips
106
+ metadata and requests each tarball directly, at the
107
+ `https://registry.npmjs.org/...` URL the lockfile records, which npm rewrites
108
+ to the proxy. Before that, the coding worker's `npm` verifies the lockfile:
109
+ the driver image puts a small shim first on the agent's `PATH`
110
+ (`/opt/wardby/bin/npm`, also restored for login shells). For `npm ci`,
111
+ `npm install`/`i`/`add` (and their aliases) in a project with a
112
+ `package-lock.json` or `npm-shrinkwrap.json`, it sends the lockfile to the
113
+ proxy's `POST /registry/npm/-/plan` (with the run's registry token), prints a
114
+ one-line summary and any refusals to stderr, and then runs the real npm with
115
+ the same arguments. Anything else runs npm directly, and a plan that fails
116
+ still runs npm: the install then goes through the usual checks below. An npm
117
+ started by another npm (a lifecycle script's) doesn't plan again; its
118
+ installs also go through the usual checks.
119
+
120
+ The proxy trusts nothing the lockfile claims. For lockfileVersion 2 or 3
121
+ (version 1 is refused with `400 wardby_lockfile_unsupported`; regenerate it
122
+ with npm 7 or later):
123
+
124
+ - **Integrity.** For each entry `name@version` it reads the registry's own
125
+ record of that exact version (`registry.npmjs.org/<name>/<version>`, a few
126
+ KB). The lockfile's `integrity` must equal the registry's `dist.integrity`,
127
+ or the entry is refused with `wardby_lockfile_integrity_mismatch`.
128
+ - **Edges.** Dependencies come from the registry's record too (dependencies,
129
+ optional and peer dependencies, npm aliases resolved), never from the
130
+ lockfile. Each one is matched to the lockfile entry npm would use (the
131
+ nearest enclosing package's `node_modules/<name>`), and counts only when
132
+ that entry is the declared package at a version inside the declared range
133
+ (strict semver, as the walk matches).
134
+ - **Reachability.** It starts from the project's own dependencies (and
135
+ workspaces') that are allowlist entries, at a version inside the
136
+ allowlisted range; a scope wildcard such as `@heroui/*` counts here, since
137
+ the lockfile names the exact package. From there it follows only verified
138
+ edges. An entry nothing reaches is refused with `wardby_package_not_allowed`.
139
+ - **Release age and advisories.** Each version's publish time (read once by
140
+ streaming the package's full document for its `time` field, never holding
141
+ it) must be older than `minReleaseAgeDays`, and one OSV `querybatch`
142
+ request covers every version (1000 per request), with each advisory's
143
+ severity read once (a version's results are cached for an hour, so a
144
+ repeated plan doesn't query again). Too new, or a HIGH/CRITICAL or
145
+ malware advisory: `wardby_version_filtered`. If OSV can't be reached, the whole plan fails
146
+ closed with `503 wardby_audit_unavailable` (unless
147
+ `REGISTRY_AUDIT_FAIL_OPEN` is set).
148
+ - **Other sources.** An entry installed from git, a URL or a path is refused
149
+ with `wardby_lockfile_entry_unsupported`; a bundled dependency is part of
150
+ its parent's tarball and needs nothing.
151
+
152
+ A refused entry refuses only itself and whatever is reachable only through
153
+ it; everything else is approved, as exact `name@version` pairs for the run.
154
+ The response is `{ "approved": <count>, "refused": [{ "name", "version",
155
+ "code", "reason" }] }`, and each refusal is recorded like any other (within
156
+ the 500-per-run cap). A tarball request for an approved `name@version` is then served directly,
157
+ checked against the approved integrity, with no graph walk.
158
+
159
+ A version the plan refused for a reason about that `name@version` itself is
160
+ answered straight from the plan, with no walk either: `403` with the plan's
161
+ code and reason. That is a version too new (`wardby_version_filtered`, until
162
+ it is old enough: the age is re-checked at each request), one with a
163
+ HIGH/CRITICAL or malware advisory (`wardby_version_filtered`, naming it), one
164
+ npm doesn't have (`wardby_package_not_found`), one npm publishes no integrity
165
+ for (`wardby_registry_integrity_missing`), and one hosted outside npm
166
+ (`wardby_upstream_host_not_allowed`). A refusal that depends on the lockfile
167
+ (`wardby_package_not_allowed`, `wardby_lockfile_integrity_mismatch`,
168
+ `wardby_lockfile_entry_unsupported`) is reported in the plan's response, but
169
+ isn't used to answer downloads: the same version reached another way (an
170
+ `overrides` entry, a later `npm install`) goes through the usual checks, and
171
+ a hostile lockfile entry can't block a package by claiming its name. Neither
172
+ does a version the registry couldn't be read for, nor one the plan never
173
+ mentioned (a package added with `npm install <new-package>`, an install
174
+ without a lockfile): they go through the allowlist and the dependency-graph
175
+ walk below.
176
+
177
+ The per-version facts (integrity, publish time, dependencies, download URL)
178
+ never change, so they are stored in the database and reused by every later
179
+ plan; decisions are always recomputed from the run's own allowlist. A plan is
180
+ bounded by `REGISTRY_PLAN_MAX_ENTRIES` entries (`413
181
+ wardby_lockfile_too_large`, as is a lockfile over 20 MiB) and
182
+ `REGISTRY_PLAN_TIMEOUT_MS` (`503 wardby_plan_incomplete`; what was verified is
183
+ kept, so a retry resumes). The proxy checks the registry token and waits for
184
+ one of two plan slots before it reads the lockfile at all, so an
185
+ unauthenticated or queued plan holds none of it; a run may run one plan at a
186
+ time and make `REGISTRY_PLAN_MAX_PER_RUN` in all (`429 wardby_plan_limit`).
187
+ It only reads the registry for entries it reaches, so a lockfile full of
188
+ unrelated packages costs nothing upstream. Measured
189
+ against real npm and OSV with a 0-day release age, the knock-knock `web/`
190
+ lockfile (216 entries: React 19, HeroUI 3, Vite 8, Vitest 5, jsdom) was
191
+ verified in 9.5 s cold and 0.7 s with stored facts, and its whole `npm ci`
192
+ took 12 s cold and 3 s warm, with the proxy at 183 MiB peak RSS (30 MiB
193
+ heap). At the default 3 days the same plan
194
+ took 8 s and refused 53 entries: vite 8.3.1 and vitest 5.0.2 were days old,
195
+ and those two are answered from the plan, but the 51 entries refused only as
196
+ unreachable through them are lockfile-dependent verdicts, so npm's requests
197
+ for them went to the graph walk, and the failing `npm ci` took 547 s with the
198
+ proxy at 1604 MiB peak RSS.
199
+
200
+ ### Lockfile installs without a plan: the dependency-graph walk
201
+
202
+ When a tarball request names a package the run hasn't approved or seen yet,
203
+ the proxy resolves the approved dependency graph on demand. The graph is
204
+ range-aware:
205
+
206
+ - It starts from the allowlisted packages, at their kept versions: those
207
+ inside the allowlisted range, past the minimum release age, and not
208
+ withheld by the vulnerability audit.
209
+ - From each kept version it follows every declared dependency `name@range`
210
+ only into that dependency's kept versions that **satisfy the declared
211
+ range** (npm semver), and continues only from those versions. A
212
+ dependency's newer major, or an old version outside the range, never
213
+ contributes its own dependencies.
214
+ - Every in-range kept version counts, not just the newest one, so a
215
+ lockfile pinned to an older version inside the range still gets that
216
+ version's dependencies.
217
+ - A spec that isn't a range (a dist-tag such as `latest`, an empty spec)
218
+ counts every kept version. A range no kept version satisfies contributes
219
+ nothing.
220
+ - A package joins the graph, and is recorded as allowed, once one of its
221
+ kept versions satisfies a range that reached it. The walk stops as soon as
222
+ it finds the requested package.
223
+
224
+ The walk is done at most once per run (concurrent and later misses share its
225
+ result), and it is bounded by `REGISTRY_MAX_GRAPH_PACKAGES` and
226
+ `REGISTRY_GRAPH_TIMEOUT_MS` (see [Operator limits](#operator-limits)).
227
+ Allowances are per package name, so once a package is allowed any of its
228
+ kept versions can be downloaded. Serving a package's metadata directly (a
229
+ plain `npm install`) still allows the dependencies of all its kept versions,
230
+ because the proxy doesn't record which ranges a package was allowed under.
231
+ A scope wildcard such as `@testing-library/*` can't be enumerated, so it is
232
+ never a starting point of the walk. A package allowed _only_ by a scope
233
+ wildcard is installable, but under a lockfile its own dependencies are not
234
+ found by the walk (it never expands that package unless an exact allowlist
235
+ entry's graph reaches it). When npm reads that package's metadata, as a plain
236
+ `npm install` does, its dependencies are allowed as usual. With `npm ci`, give
237
+ its dependencies their own allowlist entries, or also list the package itself
238
+ by exact name. PyPI's index carries no dependencies (pip reads them from each
239
+ wheel), so there is no walk for PyPI.
240
+
241
+ Dependencies are followed by the package they install: an npm alias such as
242
+ `"string-width-cjs": "npm:string-width@^4"` allows `string-width`, not
243
+ `string-width-cjs`, and follows it under the alias's own range (`^4`).
244
+ Dependency specs that aren't fetched from the registry
245
+ (`file:`, `link:`, local paths, git URLs and `github:`/`user/repo`
246
+ shorthands, `http(s):` tarball URLs, `workspace:`) allow nothing.
247
+
248
+ If an upstream (the npm registry or the OSV audit) fails while the walk reads
249
+ part of the graph, that part is retried: once more straight away, then again
250
+ on later requests, up to four attempts per package per run. Until it can be
251
+ read, a package that wasn't found is answered with a "could not be checked…
252
+ try again" error (`502 wardby_upstream_error`, or
253
+ `503 wardby_audit_unavailable` if it was the audit), not with
254
+ `wardby_package_not_allowed`.
255
+
256
+ Likewise, if the walk is cut short before it reaches the requested package,
257
+ the package isn't proven absent, so the answer is never
258
+ `wardby_package_not_allowed`. After `REGISTRY_GRAPH_TIMEOUT_MS` the answer is
259
+ `503 wardby_graph_incomplete`: the next request resumes the walk where it
260
+ stopped (npm retries a 5xx on its own), so a retry can succeed. After
261
+ `REGISTRY_MAX_GRAPH_PACKAGES` trips, the bound is permanent for the run, so
262
+ the answer is a definitive `403 wardby_graph_limit` that clients don't retry:
263
+ allowlist the package directly or raise the bound.
264
+
265
+ The walk reads full npm packuments (the largest are tens of MB) but keeps
266
+ only what it needs from each: per version, its dependency specs, publish
267
+ time, tarball URL and integrity. A packument is garbage as soon as that is
268
+ extracted, and the rendered form npm is served is built only when a client
269
+ asks for a package's metadata. The walk's state is dropped when the run's
270
+ deadline passes. Even so, a lockfile install of a large graph is the proxy's
271
+ largest memory user: a measured `npm ci` of a ~220-package lockfile (React
272
+ 19, Vite 8, Vitest 5, jsdom) peaked at 1752 MiB RSS (549 MiB heap), which is
273
+ why the GKE overlay gives the proxy 3Gi. A lockfile the shim verified
274
+ avoids the walk for every version the plan approved, and for every version it
275
+ refused for a reason about the version itself (too new, an advisory, ...).
276
+
277
+ ## What the agent can then run
278
+
279
+ Inside the run, `npm install <package>` and `pip install <package>` (into
280
+ `/workspace/.venv`) work exactly as they would against the real registries,
281
+ for anything reachable from the allowlist. Installed `node_modules`, the
282
+ `.venv`, and package-manager caches are never collected: they don't count
283
+ toward workspace size or checks, and they're never part of the resulting
284
+ commit or diff. The agent's shells get `TMPDIR` pointed at the workspace's
285
+ `.cache/tmp` rather than the sandbox's small in-memory `/tmp`, since an
286
+ install like `pip install -e '.[test]'` unpacks and builds in `TMPDIR` and
287
+ can otherwise run out of space on a large package; that directory counts
288
+ toward the run's `workspaceDiskMb` and, like the rest of `.cache`, is never
289
+ collected either.
290
+
291
+ ## Safeguards
292
+
293
+ - **Dependency graph only.** Only packages on the allowlist, or reachable
294
+ through the dependency graph of an allowlisted package, can be installed —
295
+ an agent cannot fetch an arbitrary unrelated package just because _some_
296
+ package is allowed.
297
+ - **Minimum release age.** A version has to be at least a few days old before
298
+ it can be installed, so a newly published (and potentially not-yet-flagged)
299
+ malicious release is excluded by default. The default is 3 days; a
300
+ profile's `packagePolicy.minReleaseAgeDays` can override it to any integer
301
+ 0–30. A version with no publish timestamp is treated as too new to install.
302
+ For PyPI the age applies to each file on its own upload time: a wheel added
303
+ to an old release yesterday is hidden from the index and refused
304
+ (`404 wardby_version_filtered`) until it is old enough, while the release's
305
+ older wheels are served. npm versions are immutable, so each version's
306
+ tarball has the version's publish time.
307
+ - **OSV vulnerability audit.** Every package version is checked against the
308
+ OSV database; versions affected by a **high** or **critical** severity
309
+ advisory, or by a **malware** advisory (an OSV `MAL-…` entry, which has no
310
+ severity, one aliasing or importing one, or a CWE-506 "embedded malicious
311
+ code" advisory), are withheld, and lower-severity advisories are allowed but
312
+ reported. A version is affected when an advisory entry for that exact
313
+ package (same ecosystem and name) lists it, or when it falls inside one
314
+ of the entry's version ranges, compared with the ecosystem's own version
315
+ rules (semver for npm, PEP 440 for PyPI). A version the audit cannot
316
+ parse counts as affected. If OSV can't be reached, installs in that request fail closed
317
+ (`503 wardby_audit_unavailable`) rather than skipping the check — an
318
+ operator can opt out of fail-closed with `REGISTRY_AUDIT_FAIL_OPEN=true`.
319
+ - **Wheels only for Python.** PyPI source distributions (sdists), which can
320
+ run arbitrary code at install/build time, are never served — only wheels.
321
+ - **npm install scripts are off, but only by configuration.** The proxy sets
322
+ `npm_config_ignore_scripts=true` in the agent's environment, which disables
323
+ npm's install-time script hooks. This is a configuration default, not
324
+ something the proxy can enforce on the downloaded tarball itself: the
325
+ proxy cannot strip scripts from a package, so an agent that deliberately
326
+ overrode this setting could still run one. A script that ran would still be
327
+ confined to the same sandbox — no network, no credentials, only the
328
+ workspace writable — but this is a documented limit, not a guarantee.
329
+
330
+ ## Operator limits
331
+
332
+ These per-run limits protect the proxy process and are configured with
333
+ environment variables (defaults shown); they're enforced by the single proxy
334
+ process handling the run, counting files and bytes currently in flight as well
335
+ as what's already been recorded, so parallel downloads (for example npm's
336
+ default concurrent connections) can't add up to more than the limit before any
337
+ one of them finishes:
338
+
339
+ | Setting | Default | Meaning |
340
+ | ------------------------------ | ------- | ------------------------------------------------------------- |
341
+ | `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file. |
342
+ | `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run. |
343
+ | `REGISTRY_MAX_FILES` | 5000 | Total files served in one run. |
344
+ | `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download. |
345
+ | `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable. |
346
+ | `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included. |
347
+ | `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads. |
348
+ | `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 | Packages the on-demand graph walk may expand in one run. |
349
+ | `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time allowed for one on-demand graph walk. |
350
+ | `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool. |
351
+ | `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests). |
352
+ | `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have. |
353
+ | `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan. |
354
+ | `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make. |
355
+
356
+ Metadata is cached for five minutes in a bounded cache (500 packages and
357
+ 64 MiB of trimmed metadata, least recently used evicted first), and
358
+ concurrent requests for the same package share one upstream fetch.
359
+
360
+ At most 500 refusals are recorded per run. Refusals past that still return
361
+ their normal error to npm or pip; they just aren't added to the run's record,
362
+ so a worker retrying refused names in a loop can't grow it without bound.
363
+
364
+ ## Integrity
365
+
366
+ Every file the proxy resolves from its own copy of the upstream metadata
367
+ (never a client-supplied URL) is streamed to the agent with its checksum
368
+ verified while downloading, aborting on a mismatch — but only when the
369
+ ecosystem actually publishes one. npm packages carry `dist.integrity` or
370
+ `dist.shasum`, and PyPI wheels carry a SHA-256 hash, so both are verified
371
+ today. An ecosystem whose files sometimes ship with no published checksum
372
+ (for example some Composer archives, if that ecosystem is added later) is
373
+ streamed unverified for those files — there is nothing to verify against.
374
+
375
+ ## What the reviewer sees
376
+
377
+ Every package the proxy served during a run is recorded, and so is every
378
+ refusal up to the 500-per-run cap above. `get_run` returns `packages` and
379
+ `packageRefusals` for a coding run, each deduplicated (a retried download is
380
+ one package):
381
+
382
+ ```json
383
+ {
384
+ "packages": [
385
+ { "ecosystem": "npm", "name": "@heroui/react", "version": "3.2.6", "size": 482113 },
386
+ { "ecosystem": "pypi", "name": "flask", "version": "3.0.0", "size": 101817 }
387
+ ],
388
+ "packageRefusals": [{ "ecosystem": "npm", "name": "left-pad", "reason": "wardby_package_not_allowed" }]
389
+ }
390
+ ```
391
+
392
+ `size` is the number of bytes served, or `null` if none was recorded.
393
+ `get_run` also returns `packagePlan: { "approved": <n>, "refused": <n> }`:
394
+ the exact versions lockfile plans approved for the run, and the distinct
395
+ entries they refused (each refusal is also in `packageRefusals`).
396
+
397
+ The pull request finalization also appends a collapsed **Packages installed
398
+ during this run** section listing the same information, so a reviewer
399
+ doesn't have to ask the agent what it added. It lists at most 100 packages
400
+ and 100 refusals, followed by "…and N more — see get_run for the full list";
401
+ `get_run` always has the complete list. If the report can't be loaded or
402
+ rendered, the section is left out and the pull request is still opened.
403
+
404
+ ## Error codes
405
+
406
+ npm and pip print the proxy's error body verbatim, so these are what you'll
407
+ see on a failed install:
408
+
409
+ | Code | Status | Meaning / what to do |
410
+ | ------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
411
+ | `wardby_package_not_allowed` | 403 | The package isn't on the allowlist and isn't reachable from an allowlisted package's dependency graph. Add it (or its top-level dependent) to `packageAllowlist`. |
412
+ | `wardby_file_not_allowed` | 403 | The specific file type is never served for this ecosystem (for example a PyPI sdist). Nothing to configure; use a wheel. |
413
+ | `wardby_version_filtered` | 404 | Every matching version is too new (younger than `minReleaseAgeDays`) or withheld by the vulnerability audit. Wait for it to age past the threshold, or lower `minReleaseAgeDays` if you understand the risk. |
414
+ | `wardby_package_not_found` | 404 | The upstream registry has no such package name. Check the spelling (npm names are case-sensitive). |
415
+ | `wardby_package_too_large` | 413 | The file exceeds `REGISTRY_MAX_FILE_MB`. Ask the operator to raise it if the file is legitimately larger. |
416
+ | `wardby_package_limit` | 429 | The run hit `REGISTRY_MAX_FILES` or `REGISTRY_MAX_TOTAL_MB`. Trim what the run installs, or ask the operator to raise the limit. |
417
+ | `wardby_audit_unavailable` | 503 | OSV couldn't be reached and `REGISTRY_AUDIT_FAIL_OPEN` isn't set. Retry, or have the operator set that flag if the outage is expected to be long. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the audit failed while resolving a lockfile install's dependency graph. |
418
+ | `wardby_graph_incomplete` | 503 | A lockfile install requested a package the on-demand dependency-graph walk hadn't reached when it was cut short by `REGISTRY_GRAPH_TIMEOUT_MS`. Retry: the walk resumes where it stopped (npm retries 5xx on its own). Never means the package is outside the graph. |
419
+ | `wardby_graph_limit` | 403 | The walk hit `REGISTRY_MAX_GRAPH_PACKAGES` for this run before reaching the package. Permanent for the run, so retrying won't help: allowlist the package directly, or ask the operator to raise the limit. Never means the package is outside the graph. |
420
+ | `wardby_lockfile_unsupported` | 400 | The lockfile sent to `POST /-/plan` isn't lockfileVersion 2 or 3 (or has no `packages`). Regenerate it with npm 7 or later. The install still runs, through the walk. |
421
+ | `wardby_lockfile_too_large` | 413 | The lockfile is over 20 MiB or has more than `REGISTRY_PLAN_MAX_ENTRIES` entries. The install still runs, through the walk. |
422
+ | `wardby_lockfile_integrity_mismatch` | — | A plan refusal: the lockfile's `integrity` for this entry isn't the registry's own (or the registry publishes none). Regenerate the lockfile entry; never edit integrity by hand. |
423
+ | `wardby_lockfile_entry_unsupported` | — | A plan refusal: the entry is installed from git, a URL or a path, or isn't an exact registry version. Only registry packages can be approved. |
424
+ | `wardby_plan_limit` | 429 | The run has made `REGISTRY_PLAN_MAX_PER_RUN` lockfile plans. The install still runs, through the usual checks. |
425
+ | `wardby_registry_integrity_missing` | — | A plan refusal: npm publishes no integrity for this version, so a download of it couldn't be checked. |
426
+ | `wardby_plan_in_progress` | 429 | A lockfile plan is already running for this run (one at a time). Wait for it; the install still runs, through the walk. |
427
+ | `wardby_plan_incomplete` | 503 | The plan didn't finish within `REGISTRY_PLAN_TIMEOUT_MS` (or the client went away). What was verified is kept, so a retry resumes; the install still runs, through the walk. |
428
+ | `wardby_bad_request` | 400 | The request path is malformed (bad percent-encoding, or not a valid package name). A client or agent bug, not a package choice. |
429
+ | `wardby_upstream_error` | 502 | The upstream registry answered with an error, or the download failed partway. Retry. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the registry failed while resolving a lockfile install's dependency graph. |
430
+ | `wardby_upstream_unavailable` | 504 | The upstream registry didn't answer a metadata request within `REGISTRY_METADATA_TIMEOUT_MS`. Retry. |
431
+ | `wardby_metadata_too_large` | 502 | The package's metadata document is larger than `REGISTRY_MAX_METADATA_MB`. Ask the operator to raise it. |
432
+ | `wardby_upstream_host_not_allowed` | 502 | The file's download URL points outside that ecosystem's own upstream hosts, so the proxy won't fetch it. |
433
+ | `invalid_capability` | 401 | The run's registry token doesn't match a live session (the run has ended or the token is malformed). Not something a package choice can fix. |
434
+
435
+ ## Adding an ecosystem
436
+
437
+ npm and PyPI are the two ecosystems registry mode supports today; the adapter
438
+ interface is designed to add more (Composer, RubyGems, Go modules are the
439
+ known candidates) without changing the schema or the allowlist format. Adding
440
+ one means:
441
+
442
+ 1. Implement `RegistryAdapter` and add it to `REGISTRY_ADAPTERS`. No schema
443
+ change is needed.
444
+ 2. Set `upstreamHosts` to the smallest set of hosts its downloads use.
445
+ 3. Mark `allowed: false` on file types that run code at install time where the
446
+ proxy can tell them apart, and use `workerConfig` to disable install-time
447
+ code where only the client can.
448
+ 4. Record upstream fixtures, and add a real-client integration test.
449
+ 5. Provide a worker image with the language runtime.
450
+ 6. Document every safeguard the ecosystem cannot enforce.
451
+
452
+ For example, Composer downloads from GitHub and GitLab archive hosts, often
453
+ with `integrity: null`, and needs `--no-plugins` and `--no-scripts` set
454
+ through a `COMPOSER_HOME/config.json` file from `workerConfig`. Go fits most
455
+ directly, because `GOPROXY` is designed for this kind of proxy.