@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,210 @@
1
+ /**
2
+ * PyPI registry adapter: parses PyPI allowlist syntax (PEP 503 names, PEP
3
+ * 440 specifiers), routes proxy requests against the PEP 691 JSON Simple
4
+ * API plus PEP 658 metadata files, restricts downloads to wheels, and
5
+ * configures pip for the sandboxed worker to use the proxy as its only
6
+ * index with binary-only installs.
7
+ */
8
+ import { clean as pepClean, compare as pepCompare, satisfies as pepSatisfies, validRange } from "@renovatebot/pep440";
9
+ import { unzipSync, strFromU8 } from "fflate";
10
+ import { AllowlistEntryError, RegistryError, } from "./types.js";
11
+ const UPSTREAM = "https://pypi.org/simple/";
12
+ const SIMPLE_JSON = "application/vnd.pypi.simple.v1+json";
13
+ const ENTRY = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(.*)$/;
14
+ const WHEEL_METADATA_LIMIT = 64 * 1024 * 1024;
15
+ function uploadTime(file) {
16
+ const time = file["upload-time"] ? Date.parse(file["upload-time"]) : NaN;
17
+ return Number.isNaN(time) ? null : new Date(time);
18
+ }
19
+ /** A valid PyPI project name (PEP 508): ASCII letters and digits, with
20
+ * `.`, `_` and `-` allowed only between them. */
21
+ const PROJECT_NAME = /^(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9])$/;
22
+ function decoded(segment) {
23
+ try {
24
+ return decodeURIComponent(segment);
25
+ }
26
+ catch {
27
+ throw new RegistryError(400, "wardby_bad_request", "malformed percent-encoding in the registry path");
28
+ }
29
+ }
30
+ function projectName(segment) {
31
+ const name = decoded(segment);
32
+ if (!PROJECT_NAME.test(name))
33
+ throw new RegistryError(400, "wardby_bad_request", "not a valid Python project name");
34
+ return normalizePypiName(name);
35
+ }
36
+ export function normalizePypiName(name) {
37
+ return name.toLowerCase().replace(/[-_.]+/g, "-");
38
+ }
39
+ /** Version from a wheel ("name-1.0-py3-none-any.whl") or sdist ("name-1.0.tar.gz") filename. */
40
+ function versionOf(filename) {
41
+ if (filename.endsWith(".whl"))
42
+ return filename.split("-")[1] ?? null;
43
+ const sdist = filename.match(/^.+?-(\d[^-]*)\.(?:tar\.gz|zip)$/);
44
+ return sdist ? sdist[1] : null;
45
+ }
46
+ export function requiresDist(metadataText) {
47
+ const names = new Set();
48
+ for (const line of metadataText.split(/\r?\n/)) {
49
+ if (!line.startsWith("Requires-Dist:"))
50
+ continue;
51
+ const value = line.slice("Requires-Dist:".length).trim();
52
+ if (/;\s*.*\bextra\s*==/.test(value))
53
+ continue;
54
+ const name = value.match(/^([A-Za-z0-9][A-Za-z0-9._-]*)/);
55
+ if (name)
56
+ names.add(normalizePypiName(name[1]));
57
+ }
58
+ return [...names];
59
+ }
60
+ export const pypiAdapter = {
61
+ id: "pypi",
62
+ osvEcosystem: "PyPI",
63
+ upstreamHosts: ["pypi.org", "files.pythonhosted.org"],
64
+ collectExclude: [".venv", "venv", "__pycache__"],
65
+ dependenciesInMetadata: false,
66
+ parseAllowlistEntry(raw) {
67
+ const match = raw.trim().match(ENTRY);
68
+ if (!match)
69
+ throw new AllowlistEntryError(`"${raw}" is not a valid Python package entry`);
70
+ const name = normalizePypiName(match[1]);
71
+ const range = match[2].trim();
72
+ if (!range)
73
+ return { name, wildcard: false };
74
+ if (!validRange(range))
75
+ throw new AllowlistEntryError(`"${range}" is not a valid PEP 440 specifier`);
76
+ return { name, wildcard: false, range };
77
+ },
78
+ normalizeName: normalizePypiName,
79
+ satisfies: (version, range) => pepSatisfies(version, range),
80
+ compareVersions(a, b) {
81
+ // OSV and PyPI filenames both carry non-canonical spellings ("2.0rc1",
82
+ // "1.0-post1"); normalize each to PEP 440 before comparing.
83
+ const left = pepClean(a);
84
+ const right = pepClean(b);
85
+ if (!left || !right)
86
+ throw new Error(`not a PEP 440 version: "${a}" / "${b}"`);
87
+ return pepCompare(left, right);
88
+ },
89
+ route(method, subpath) {
90
+ if (method !== "GET" && method !== "HEAD")
91
+ return null;
92
+ const simple = subpath.match(/^simple\/([^/]+)\/?$/);
93
+ if (simple)
94
+ return { kind: "metadata", name: projectName(simple[1]) };
95
+ const file = subpath.match(/^files\/([^/]+)\/([^/]+)$/);
96
+ if (!file)
97
+ return null;
98
+ const name = projectName(file[1]);
99
+ const filename = decoded(file[2]);
100
+ if (filename.endsWith(".metadata")) {
101
+ return { kind: "file-metadata", name, filename: filename.slice(0, -".metadata".length) };
102
+ }
103
+ const version = versionOf(filename);
104
+ return version ? { kind: "download", name, version, filename } : null;
105
+ },
106
+ async fetchMetadata(name, upstream) {
107
+ const response = await upstream(`${UPSTREAM}${name}/`, { accept: SIMPLE_JSON });
108
+ if (response.status === 404)
109
+ throw new RegistryError(404, "wardby_package_not_found", `PyPI has no package "${name}"`);
110
+ if (!response.ok)
111
+ throw new RegistryError(502, "wardby_upstream_error", `PyPI returned ${response.status}`);
112
+ const index = (await response.json());
113
+ const versions = new Map();
114
+ for (const file of index.files) {
115
+ const version = versionOf(file.filename);
116
+ if (!version || file.yanked)
117
+ continue;
118
+ const entry = versions.get(version) ?? { version, dependencies: [], files: [] };
119
+ entry.files.push({
120
+ filename: file.filename,
121
+ version,
122
+ upstreamUrl: file.url,
123
+ integrity: file.hashes?.sha256 ? { algorithm: "sha256", hex: file.hashes.sha256 } : null,
124
+ sizeBytes: file.size ?? null,
125
+ allowed: file.filename.endsWith(".whl"),
126
+ publishedAt: uploadTime(file),
127
+ });
128
+ versions.set(version, entry);
129
+ }
130
+ // Keep only what renderMetadata and resolveFileMetadata read: wheels,
131
+ // with the PEP 691 file fields pip uses.
132
+ const raw = {
133
+ name: index.name,
134
+ files: index.files
135
+ .filter((file) => file.filename.endsWith(".whl") && !file.yanked)
136
+ .map((file) => ({
137
+ filename: file.filename,
138
+ url: file.url,
139
+ ...(file.hashes ? { hashes: file.hashes } : {}),
140
+ ...(file["upload-time"] ? { "upload-time": file["upload-time"] } : {}),
141
+ ...(file.size !== undefined ? { size: file.size } : {}),
142
+ ...(file["core-metadata"] !== undefined ? { "core-metadata": file["core-metadata"] } : {}),
143
+ ...(file["dist-info-metadata"] !== undefined ? { "dist-info-metadata": file["dist-info-metadata"] } : {}),
144
+ ...(file["requires-python"] !== undefined ? { "requires-python": file["requires-python"] } : {}),
145
+ })),
146
+ };
147
+ return { name: normalizePypiName(index.name), versions, raw };
148
+ },
149
+ renderMetadata(meta, keep, keptFiles, proxyBase) {
150
+ const index = meta.raw;
151
+ const files = index.files
152
+ .filter((file) => file.filename.endsWith(".whl"))
153
+ .filter((file) => keep.has(versionOf(file.filename) ?? "") && keptFiles.has(file.filename))
154
+ .map((file) => ({
155
+ ...file,
156
+ url: `${proxyBase}files/${encodeURIComponent(meta.name)}/${encodeURIComponent(file.filename)}`,
157
+ }));
158
+ return {
159
+ contentType: SIMPLE_JSON,
160
+ body: JSON.stringify({ meta: { "api-version": "1.1" }, name: meta.name, files, versions: [...keep] }),
161
+ };
162
+ },
163
+ resolveDownload(route, meta) {
164
+ return meta.versions.get(route.version)?.files.find((file) => file.filename === route.filename) ?? null;
165
+ },
166
+ resolveFileMetadata(route, meta) {
167
+ const index = meta.raw;
168
+ const source = index.files.find((file) => file.filename === route.filename);
169
+ const version = versionOf(route.filename);
170
+ const declared = source?.["core-metadata"] ?? source?.["dist-info-metadata"];
171
+ if (!source || !version || !declared || !source.filename.endsWith(".whl"))
172
+ return null;
173
+ const sha256 = typeof declared === "object" ? declared.sha256 : undefined;
174
+ return {
175
+ filename: `${route.filename}.metadata`,
176
+ version,
177
+ upstreamUrl: `${source.url}.metadata`,
178
+ integrity: sha256 ? { algorithm: "sha256", hex: sha256 } : null,
179
+ sizeBytes: null,
180
+ allowed: true,
181
+ // The metadata file is as old as the wheel it describes.
182
+ publishedAt: uploadTime(source),
183
+ };
184
+ },
185
+ async dependenciesFromFile(route, body) {
186
+ if (route.kind === "file-metadata")
187
+ return requiresDist(new TextDecoder().decode(body));
188
+ if (!route.filename.endsWith(".whl") || body.byteLength > WHEEL_METADATA_LIMIT)
189
+ return [];
190
+ const entries = unzipSync(body, { filter: (file) => /\.dist-info\/METADATA$/.test(file.name) });
191
+ const metadata = Object.values(entries)[0];
192
+ return metadata ? requiresDist(strFromU8(metadata)) : [];
193
+ },
194
+ workerConfig({ registryUrl, token, cacheDir }) {
195
+ const index = new URL("simple/", registryUrl);
196
+ index.username = "wardby";
197
+ index.password = token;
198
+ return {
199
+ env: {
200
+ PIP_INDEX_URL: index.toString(),
201
+ PIP_TRUSTED_HOST: index.hostname,
202
+ PIP_ONLY_BINARY: ":all:",
203
+ PIP_CACHE_DIR: cacheDir,
204
+ PIP_DISABLE_PIP_VERSION_CHECK: "1",
205
+ PIP_NO_INPUT: "1",
206
+ },
207
+ files: [],
208
+ };
209
+ },
210
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The one summary of a run's RegistryFetch rows, shared by get_run and the
3
+ * coding executor's PR-body report so both deduplicate identically: served
4
+ * packages collapse on ecosystem + name + version (a retried download is one
5
+ * package), refusals on ecosystem + name + reason.
6
+ */
7
+ export interface RegistryFetchRow {
8
+ ecosystem: string;
9
+ name: string;
10
+ version: string | null;
11
+ outcome: string;
12
+ reason: string | null;
13
+ sizeBytes: number | null;
14
+ /** `-/plan` for a refusal a lockfile plan recorded (see PLAN_FETCH_FILENAME). */
15
+ filename?: string | null;
16
+ }
17
+ /** The `filename` a lockfile plan records its refusals under. */
18
+ export declare const PLAN_REFUSAL_FILENAME = "-/plan";
19
+ /** Distinct refusals lockfile plans recorded (ecosystem + name + version +
20
+ * reason), however often the lockfile was planned. */
21
+ export declare function countPlanRefusals(rows: readonly RegistryFetchRow[]): number;
22
+ export interface RegistryPackageSummary {
23
+ ecosystem: string;
24
+ name: string;
25
+ version: string;
26
+ /** Bytes served (RegistryFetch.sizeBytes); null when not recorded. */
27
+ size: number | null;
28
+ }
29
+ export interface RegistryRefusalSummary {
30
+ ecosystem: string;
31
+ name: string;
32
+ reason: string;
33
+ }
34
+ export declare function summarizeRegistryFetches(rows: readonly RegistryFetchRow[]): {
35
+ packages: RegistryPackageSummary[];
36
+ packageRefusals: RegistryRefusalSummary[];
37
+ };
@@ -0,0 +1,31 @@
1
+ /** The `filename` a lockfile plan records its refusals under. */
2
+ export const PLAN_REFUSAL_FILENAME = "-/plan";
3
+ /** Distinct refusals lockfile plans recorded (ecosystem + name + version +
4
+ * reason), however often the lockfile was planned. */
5
+ export function countPlanRefusals(rows) {
6
+ const keys = new Set();
7
+ for (const row of rows) {
8
+ if (row.outcome !== "refused" || row.filename !== PLAN_REFUSAL_FILENAME)
9
+ continue;
10
+ keys.add(`${row.ecosystem}\0${row.name}\0${row.version ?? ""}\0${row.reason ?? ""}`);
11
+ }
12
+ return keys.size;
13
+ }
14
+ export function summarizeRegistryFetches(rows) {
15
+ const packages = new Map();
16
+ const refusals = new Map();
17
+ for (const row of rows) {
18
+ if (row.outcome === "served" && row.version) {
19
+ const key = `${row.ecosystem}\0${row.name}\0${row.version}`;
20
+ if (!packages.has(key))
21
+ packages.set(key, { ecosystem: row.ecosystem, name: row.name, version: row.version, size: row.sizeBytes });
22
+ }
23
+ else if (row.outcome === "refused") {
24
+ const reason = row.reason ?? "refused";
25
+ const key = `${row.ecosystem}\0${row.name}\0${reason}`;
26
+ if (!refusals.has(key))
27
+ refusals.set(key, { ecosystem: row.ecosystem, name: row.name, reason });
28
+ }
29
+ }
30
+ return { packages: [...packages.values()], packageRefusals: [...refusals.values()] };
31
+ }
@@ -0,0 +1,4 @@
1
+ /** A one-way, registry-only token for npm and pip. The proxy stores its hash and
2
+ * accepts it only on /registry/ routes, so package tools never hold the model
3
+ * capability. */
4
+ export declare function deriveRegistryToken(capability: string): string;
@@ -0,0 +1,7 @@
1
+ import { createHash } from "node:crypto";
2
+ /** A one-way, registry-only token for npm and pip. The proxy stores its hash and
3
+ * accepts it only on /registry/ routes, so package tools never hold the model
4
+ * capability. */
5
+ export function deriveRegistryToken(capability) {
6
+ return `rrg_${createHash("sha256").update("wardby-registry\0").update(capability).digest("base64url")}`;
7
+ }
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Shared types for the coding package registry: the adapter contract each
3
+ * ecosystem (npm, PyPI, later Composer/RubyGems/Go) implements, and the
4
+ * allowlist entry shape adapters parse into. Everything security-relevant
5
+ * (allowance checks, version filtering, streaming, recording) lives in the
6
+ * registry core, never in an adapter, so a new adapter cannot weaken a
7
+ * safeguard by omission.
8
+ */
9
+ /**
10
+ * "npm", "pypi", later "composer", "rubygems", "go". Used as the allowlist
11
+ * key, the route segment (/registry/<id>/), and RegistryFetch.ecosystem.
12
+ */
13
+ export type EcosystemId = string;
14
+ export interface AllowlistEntry {
15
+ /** Normalized name, or a scope prefix when `wildcard` is true ("@heroui/"). */
16
+ name: string;
17
+ wildcard: boolean;
18
+ /** Optional version range in this ecosystem's syntax. */
19
+ range?: string;
20
+ }
21
+ export declare class AllowlistEntryError extends Error {
22
+ }
23
+ export declare class RegistryError extends Error {
24
+ readonly status: number;
25
+ readonly code: string;
26
+ constructor(status: number, code: string, message: string);
27
+ }
28
+ export interface Integrity {
29
+ algorithm: "sha512" | "sha384" | "sha256" | "sha1";
30
+ /** Hex-encoded digest. */
31
+ hex: string;
32
+ }
33
+ export interface FileRef {
34
+ filename: string;
35
+ version: string;
36
+ upstreamUrl: string;
37
+ /** Null when the ecosystem publishes no checksum (common for Composer archives). */
38
+ integrity: Integrity | null;
39
+ sizeBytes: number | null;
40
+ /** False for files the ecosystem's safeguards exclude, e.g. PyPI sdists. */
41
+ allowed: boolean;
42
+ /** When this file was published; null means unknown and is treated as too
43
+ * new. The minimum release age applies per file: a PyPI release can gain
44
+ * a new wheel long after its first upload. For npm every version has one
45
+ * immutable tarball, so this is the version's publish time. */
46
+ publishedAt: Date | null;
47
+ }
48
+ /** One declared dependency edge: the package it installs and the range
49
+ * it asks for, in the ecosystem's syntax. `"*"` means any version (also
50
+ * used for a spec that is not a range, such as a dist-tag). */
51
+ export interface DependencySpec {
52
+ name: string;
53
+ range: string;
54
+ }
55
+ export interface VersionInfo {
56
+ version: string;
57
+ /** Dependency names (normalized). Empty when discovered from files instead. */
58
+ dependencies: readonly string[];
59
+ /** The same dependencies with their declared ranges, for the range-aware
60
+ * graph walk. Adapters with `dependenciesInMetadata` set should fill it;
61
+ * when omitted, each name counts as `"*"`. */
62
+ dependencySpecs?: readonly DependencySpec[];
63
+ files: readonly FileRef[];
64
+ }
65
+ export interface PackageMetadata {
66
+ name: string;
67
+ /** Keyed by version string. */
68
+ versions: ReadonlyMap<string, VersionInfo>;
69
+ /** Adapter-private data renderMetadata (and resolveFileMetadata) read:
70
+ * never the upstream document itself, only what those need of it.
71
+ * `undefined` when the adapter supports metadata without render data
72
+ * (`fetchMetadata` without `render`) and it was fetched that way. */
73
+ raw?: unknown;
74
+ /** Approximate bytes this object retains, for the metadata cache's byte
75
+ * budget. The core estimates it when an adapter leaves it out. */
76
+ approxBytes?: number;
77
+ }
78
+ export interface FetchMetadataOptions {
79
+ /** Also keep what renderMetadata needs. Without it, an adapter may omit
80
+ * `raw` (npm does), since the graph walk, the version filter and
81
+ * downloads read only `versions`. */
82
+ render?: boolean;
83
+ }
84
+ export interface MetadataRoute {
85
+ kind: "metadata";
86
+ name: string;
87
+ }
88
+ export interface DownloadRoute {
89
+ kind: "download";
90
+ name: string;
91
+ version: string;
92
+ filename: string;
93
+ }
94
+ /** PEP 658 metadata file for one wheel. */
95
+ export interface FileMetadataRoute {
96
+ kind: "file-metadata";
97
+ name: string;
98
+ filename: string;
99
+ }
100
+ export type RegistryRoute = MetadataRoute | DownloadRoute | FileMetadataRoute;
101
+ export interface RenderedDocument {
102
+ contentType: string;
103
+ body: string;
104
+ }
105
+ /** The pinned upstream client: HTTPS only, adapter's upstreamHosts only,
106
+ * no redirects, no private addresses. `acceptEncoding: "gzip"` asks for a
107
+ * compressed body, which the caller then decompresses itself (the pinned
108
+ * client never does). */
109
+ export type UpstreamFetch = (url: string, init?: {
110
+ method?: "GET" | "POST";
111
+ body?: string;
112
+ accept?: string;
113
+ acceptEncoding?: "gzip";
114
+ signal?: AbortSignal;
115
+ }) => Promise<Response>;
116
+ /** One declared dependency as a lockfile or registry manifest writes it:
117
+ * the folder it installs into (`key`, an alias's own name), the registry
118
+ * package it installs (`name`, the alias target) and the range it asks
119
+ * for (`"*"` for a dist-tag or empty spec). */
120
+ export interface DeclaredDependency {
121
+ key: string;
122
+ name: string;
123
+ range: string;
124
+ }
125
+ /** One installed package a lockfile claims, where it installs it. None of
126
+ * it is trusted: the plan verifies every registry entry against the
127
+ * registry itself. */
128
+ export interface LockfileEntry {
129
+ /** Where the lockfile installs it (npm: `node_modules/a/node_modules/b`). Unique. */
130
+ path: string;
131
+ /** The registry package (an alias's target), as the lockfile claims it. */
132
+ name: string;
133
+ version: string;
134
+ /** The lockfile's integrity string, or null when it records none. */
135
+ integrity: string | null;
136
+ /** `registry`: fetched from the registry (verified, then approved or
137
+ * refused). `bundled`: shipped inside its parent's own tarball, never
138
+ * fetched, so neither. `unsupported`: fetched from somewhere other than
139
+ * the registry (git, a URL, a path) or unreadable; always refused. */
140
+ kind: "registry" | "bundled" | "unsupported";
141
+ /** Why an `unsupported` entry is refused. */
142
+ unsupportedReason?: string;
143
+ }
144
+ /** A parsed lockfile: its entries, the projects whose declared
145
+ * dependencies are where reachability starts (the root, workspaces), and
146
+ * the client's own rule for which entry a declared dependency uses. */
147
+ export interface ParsedLockfile {
148
+ entries: readonly LockfileEntry[];
149
+ projects: readonly {
150
+ path: string;
151
+ dependencies: readonly DeclaredDependency[];
152
+ }[];
153
+ /** The entry that `key`, declared by the package at `fromPath`, resolves
154
+ * to (npm: the nearest ancestor `node_modules/<key>`), or undefined
155
+ * (not installed, or a local project). */
156
+ resolve(fromPath: string, key: string): LockfileEntry | undefined;
157
+ }
158
+ /** Immutable facts about one published version, read from the registry
159
+ * itself (never from a lockfile): stored once, reused forever. */
160
+ export interface VersionFact {
161
+ name: string;
162
+ version: string;
163
+ /** Null only while being assembled: a fact is stored only with a publish time. */
164
+ publishedAt: Date | null;
165
+ /** The registry's integrity string (npm: `dist.integrity`, an SRI). */
166
+ integrity: string;
167
+ downloadUrl: string;
168
+ /** Every registry dependency the version declares (npm: dependencies,
169
+ * optionalDependencies and peerDependencies, aliases resolved). */
170
+ dependencies: readonly DeclaredDependency[];
171
+ }
172
+ /** An ecosystem's lockfile verification hooks (`POST /-/plan`): parsing and
173
+ * registry reads only. Every decision (integrity, edges, reachability,
174
+ * release age, advisories) is the registry core's. */
175
+ export interface LockfilePlanSupport {
176
+ /** Parse a lockfile. Throws a RegistryError for one it cannot verify
177
+ * (unsupported version: 400 `wardby_lockfile_unsupported`; invalid:
178
+ * 400 `wardby_bad_request`; more than `maxEntries` entries: 413
179
+ * `wardby_lockfile_too_large`). `proxyRegistryUrl` is the proxy's own
180
+ * route for this ecosystem, which a lockfile written through it records
181
+ * as the download source. */
182
+ parse(body: string, options: {
183
+ maxEntries: number;
184
+ proxyRegistryUrl: string;
185
+ }): ParsedLockfile;
186
+ /** The registry's own record of one version, without its publish time;
187
+ * null when the registry has no such version. */
188
+ fetchVersionFact(name: string, version: string, upstream: UpstreamFetch, signal?: AbortSignal): Promise<VersionFact | null>;
189
+ /** Publish times of `versions` of one package, read by streaming the
190
+ * package's full document without ever holding it. A version the
191
+ * registry has no time for is absent from the result. */
192
+ fetchPublishTimes(name: string, versions: readonly string[], upstream: UpstreamFetch, options: {
193
+ maxBytes: number;
194
+ signal: AbortSignal;
195
+ }): Promise<Map<string, Date>>;
196
+ /** Whether a dependency declared as `range` accepts `version`, as the
197
+ * client itself decides it when it reuses a locked version. */
198
+ edgeSatisfies(version: string, range: string): boolean;
199
+ }
200
+ export interface WorkerConfigInput {
201
+ /** e.g. "http://wardby-proxy:8787/registry/npm/" */
202
+ registryUrl: string;
203
+ /** The derived registry-only token (`rrg_…`), never the run capability.
204
+ * Written only under cacheDir, never elsewhere. */
205
+ token: string;
206
+ /** e.g. "/workspace/.cache/npm"; always inside a collection-excluded folder. */
207
+ cacheDir: string;
208
+ }
209
+ export interface WorkerConfig {
210
+ env: Readonly<Record<string, string>>;
211
+ /** Files the driver writes before the agent starts; paths must be under cacheDir. */
212
+ files: readonly {
213
+ path: string;
214
+ content: string;
215
+ mode: number;
216
+ }[];
217
+ }
218
+ export interface RegistryAdapter {
219
+ readonly id: EcosystemId;
220
+ /** OSV ecosystem name, e.g. "npm", "PyPI", "Packagist", "RubyGems", "Go". */
221
+ readonly osvEcosystem: string;
222
+ /** Upstream hosts this adapter may reach; added to the pinned-fetch allowlist. */
223
+ readonly upstreamHosts: readonly string[];
224
+ /** Extra folder names skipped at collection, e.g. "vendor" for Composer. */
225
+ readonly collectExclude: readonly string[];
226
+ /** Whether fetchMetadata's VersionInfo.dependencies is complete (npm's
227
+ * packuments carry every version's dependencies). When true, the core
228
+ * may resolve a run's approved dependency graph from metadata alone,
229
+ * on demand, before refusing a name (a lockfile install requests
230
+ * tarballs without first requesting each parent's metadata). False for
231
+ * ecosystems whose indexes omit dependencies (PyPI), where such a walk
232
+ * would only make upstream calls and find nothing. */
233
+ readonly dependenciesInMetadata: boolean;
234
+ /** Lockfile names this ecosystem's client writes, and a rewrite of the proxy download URLs it records in one back to
235
+ * public URLs, applied before the workspace is collected (lockfiles.ts). Omitted when the client records none. */
236
+ readonly lockfiles?: {
237
+ names: readonly string[];
238
+ normalize(content: string, registryUrl: string): string;
239
+ };
240
+ /** Lockfile verification (`POST /registry/<id>/-/plan`); omitted when the
241
+ * ecosystem has none. */
242
+ readonly lockfilePlan?: LockfilePlanSupport;
243
+ /** Parse one allowlist entry in this ecosystem's syntax. Throws an
244
+ * AllowlistEntryError with a user-facing message if it is invalid. */
245
+ parseAllowlistEntry(raw: string): AllowlistEntry;
246
+ /** Canonical name used for every comparison (npm: the name exactly as
247
+ * written, since npm names are case-sensitive; PyPI: PEP 503
248
+ * normalization). */
249
+ normalizeName(name: string): string;
250
+ /** Whether `version` satisfies `range` in this ecosystem's syntax
251
+ * (npm semver ranges, PEP 440 specifiers). */
252
+ satisfies(version: string, range: string): boolean;
253
+ /** Total order over this ecosystem's version strings (semver for npm,
254
+ * PEP 440 for PyPI, normalizing non-canonical spellings first), used to
255
+ * evaluate OSV advisory ranges. Throws on a version it cannot parse; the
256
+ * audit treats that as affected (fail closed). */
257
+ compareVersions(a: string, b: string): number;
258
+ /** Classify a request under /registry/<id>/, or null for 404. Throws a
259
+ * 400 `wardby_bad_request` RegistryError for a malformed path (bad
260
+ * percent-encoding or an invalid package name); the core records it. */
261
+ route(method: string, subpath: string, headers: Headers): RegistryRoute | null;
262
+ /** Fetch and parse upstream metadata for one package, keeping only the
263
+ * fields the proxy reads: nothing returned may reference the parsed
264
+ * upstream document, which can be tens of MB. */
265
+ fetchMetadata(name: string, upstream: UpstreamFetch, options?: FetchMetadataOptions): Promise<PackageMetadata>;
266
+ /** Build the client-facing metadata document containing only `keep`
267
+ * versions and, of their files, only the filenames in `keptFiles` (the
268
+ * core's per-file release-age filter), with every download link
269
+ * rewritten to a proxy route. */
270
+ renderMetadata(meta: PackageMetadata, keep: ReadonlySet<string>, keptFiles: ReadonlySet<string>, proxyBase: string): RenderedDocument;
271
+ /** Map a download route to a file in metadata the proxy fetched itself.
272
+ * Returns null if the route names no known file. */
273
+ resolveDownload(route: DownloadRoute, meta: PackageMetadata): FileRef | null;
274
+ /** For ecosystems whose indexes omit dependencies (PyPI): dependency
275
+ * names read from a served file or its metadata file. Omitted when
276
+ * VersionInfo.dependencies is already complete (npm). */
277
+ dependenciesFromFile?(route: DownloadRoute | FileMetadataRoute, body: Uint8Array): Promise<string[]>;
278
+ /** Map a file-metadata route (PEP 658) to the file it describes, from
279
+ * metadata the proxy fetched itself. Returns null if the route names no
280
+ * known file. */
281
+ resolveFileMetadata?(route: FileMetadataRoute, meta: PackageMetadata): FileRef | null;
282
+ /** Environment variables and files the driver writes so the package
283
+ * manager uses the proxy, authenticates, keeps caches under cacheDir,
284
+ * and disables install-time code where the client allows it. */
285
+ workerConfig(input: WorkerConfigInput): WorkerConfig;
286
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared types for the coding package registry: the adapter contract each
3
+ * ecosystem (npm, PyPI, later Composer/RubyGems/Go) implements, and the
4
+ * allowlist entry shape adapters parse into. Everything security-relevant
5
+ * (allowance checks, version filtering, streaming, recording) lives in the
6
+ * registry core, never in an adapter, so a new adapter cannot weaken a
7
+ * safeguard by omission.
8
+ */
9
+ export class AllowlistEntryError extends Error {
10
+ }
11
+ export class RegistryError extends Error {
12
+ status;
13
+ code;
14
+ constructor(status, code, message) {
15
+ super(message);
16
+ this.status = status;
17
+ this.code = code;
18
+ }
19
+ }
@@ -0,0 +1,14 @@
1
+ /** The URL the worker's package manager is pointed at for one ecosystem. */
2
+ export declare function registryUrlFor(proxyBaseUrl: string, ecosystem: string): string;
3
+ export declare function registryWorkerSetup(input: {
4
+ proxyBaseUrl: string;
5
+ capability: string;
6
+ cacheRoot: string;
7
+ }): {
8
+ env: Record<string, string>;
9
+ files: {
10
+ path: string;
11
+ content: string;
12
+ mode: number;
13
+ }[];
14
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Builds the full worker environment/file setup for every registry adapter
3
+ * at once, from the run's derived registry-only token -- never the run
4
+ * capability itself. The driver calls this once per coding run and applies
5
+ * the result before the agent starts.
6
+ */
7
+ import { REGISTRY_ADAPTERS } from "./adapters.js";
8
+ import { deriveRegistryToken } from "./token.js";
9
+ /** The URL the worker's package manager is pointed at for one ecosystem. */
10
+ export function registryUrlFor(proxyBaseUrl, ecosystem) {
11
+ return `${proxyBaseUrl.replace(/\/$/, "")}/registry/${ecosystem}/`;
12
+ }
13
+ export function registryWorkerSetup(input) {
14
+ const token = deriveRegistryToken(input.capability);
15
+ const env = {};
16
+ const files = [];
17
+ for (const adapter of REGISTRY_ADAPTERS.values()) {
18
+ const config = adapter.workerConfig({
19
+ registryUrl: registryUrlFor(input.proxyBaseUrl, adapter.id),
20
+ token,
21
+ cacheDir: `${input.cacheRoot}/${adapter.id}`,
22
+ });
23
+ Object.assign(env, config.env);
24
+ for (const file of config.files) {
25
+ if (!file.path.startsWith(`${input.cacheRoot}/`))
26
+ throw new Error("registry_worker_file_outside_cache");
27
+ files.push(file);
28
+ }
29
+ }
30
+ return { env, files };
31
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The services wardby ships in its catalog. Migration
3
+ * 20260927050000_coding_run_services seeds exactly these rows
4
+ * (builtins.database.test.ts holds the two together); a later release changes
5
+ * them with a new migration and a change here.
6
+ *
7
+ * Each image is the official Docker Hub image pinned by its multi-architecture
8
+ * index digest, resolved with
9
+ * `docker buildx imagetools inspect docker.io/library/<name>:<tag>`.
10
+ *
11
+ * A sidecar runs as the run pod's non-root uid with a read-only root
12
+ * filesystem, so each entry names every directory its image writes to: the
13
+ * data directory and, for the databases, the socket directory and /tmp (the
14
+ * official entrypoints write an nss_wrapper passwd file there for an arbitrary
15
+ * uid). Postgres keeps PGDATA one level below its volume so the entrypoint can
16
+ * create it owned by that uid. Readiness probes TCP on 127.0.0.1, not the Unix
17
+ * socket: both database entrypoints first run a temporary socket-only server to
18
+ * initialize, and only the final server listens on TCP. Variables use
19
+ * 127.0.0.1 rather than localhost so no client tries the IPv6 loopback first.
20
+ */
21
+ import type { CodingServiceDefinition } from "./catalog.js";
22
+ export declare const BUILTIN_CODING_SERVICES: readonly CodingServiceDefinition[];