@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,131 @@
1
+ /**
2
+ * OSV vulnerability audit for the coding package registry. One query per
3
+ * package (cached), evaluated against every candidate version with the
4
+ * ecosystem's own version comparator:
5
+ *
6
+ * - only `affected[]` entries for the queried package count: same OSV
7
+ * ecosystem, and the same name under the adapter's normalization (PEP 503
8
+ * for PyPI, exact for npm). A GHSA advisory lists every package it touches
9
+ * (e.g. `lodash-es`, or RubyGems `lodash-rails`), and none of those may
10
+ * leak onto the one being installed;
11
+ * - a version is affected when it is listed in `versions`, or falls inside a
12
+ * SEMVER/ECOSYSTEM range (npm GHSA entries carry ranges only). Ranges are
13
+ * evaluated per the OSV schema: events sorted by version, `introduced`
14
+ * (including "0") opens, `fixed` closes (exclusive), `last_affected`
15
+ * closes (inclusive), `limit` bounds;
16
+ * - a version or event the comparator cannot parse counts as affected
17
+ * (fail closed).
18
+ */
19
+ import { type RegistryAdapter, type UpstreamFetch } from "../../../coding/registry/types.js";
20
+ export declare const DEFAULT_VULN_CACHE_ENTRIES = 10000;
21
+ /** Exact versions whose querybatch results are cached. */
22
+ export declare const DEFAULT_BATCH_CACHE_ENTRIES = 20000;
23
+ interface OsvEvent {
24
+ introduced?: string;
25
+ fixed?: string;
26
+ last_affected?: string;
27
+ limit?: string;
28
+ }
29
+ interface OsvAffected {
30
+ package?: {
31
+ name?: string;
32
+ ecosystem?: string;
33
+ };
34
+ ranges?: {
35
+ type?: string;
36
+ events?: OsvEvent[];
37
+ }[];
38
+ versions?: string[];
39
+ }
40
+ interface OsvVuln {
41
+ id: string;
42
+ aliases?: string[];
43
+ affected?: (OsvAffected & {
44
+ database_specific?: Record<string, unknown>;
45
+ })[];
46
+ database_specific?: {
47
+ severity?: string;
48
+ cwe_ids?: unknown;
49
+ [key: string]: unknown;
50
+ };
51
+ }
52
+ /** Whether an advisory reports malicious code: an OSV malicious-packages
53
+ * entry (`MAL-…`, which carries no severity at all), one that aliases or
54
+ * imports one, or a CWE-506 (embedded malicious code) advisory. Withheld
55
+ * whatever its severity says. */
56
+ export declare function isMalware(vuln: OsvVuln): boolean;
57
+ /** What the audit needs from an adapter: which OSV ecosystem to query, how
58
+ * names compare, and how versions order. */
59
+ export type AuditAdapter = Pick<RegistryAdapter, "osvEcosystem" | "normalizeName" | "compareVersions">;
60
+ export interface AdvisoryIndex {
61
+ /** Advisory ids with HIGH or CRITICAL severity that affect `version`. */
62
+ withheld(version: string): readonly string[];
63
+ /** Advisory ids of any other severity that affect `version`. */
64
+ reported(version: string): readonly string[];
65
+ }
66
+ type Compare = (a: string, b: string) => number;
67
+ /** OSV range evaluation: sort events by version, then sweep. A version or
68
+ * event the comparator cannot parse makes the version count as affected. */
69
+ export declare function inOsvRange(version: string, events: readonly OsvEvent[], compare: Compare): boolean;
70
+ export declare const DEFAULT_AUDIT_CACHE_ENTRIES = 5000;
71
+ /** One exact package version to audit. */
72
+ export interface AuditedVersion {
73
+ name: string;
74
+ version: string;
75
+ }
76
+ /** `name@version` -> the HIGH/CRITICAL advisory ids affecting it; a
77
+ * version with none is absent. */
78
+ export type WithheldVersions = Map<string, string[]>;
79
+ export declare const versionKey: (name: string, version: string) => string;
80
+ export declare class OsvAudit {
81
+ private readonly options;
82
+ /** Advisories per package, least recently used first. Bounded to
83
+ * `maxEntries`; expired entries are dropped on access and before any
84
+ * live entry is evicted. A failed query is never cached. */
85
+ private readonly cache;
86
+ /** Single-flight: concurrent audits of one package share one OSV query. */
87
+ private readonly loads;
88
+ /** Advisory severities by id, with the OSV `modified` time they were
89
+ * read at, least recently used first. */
90
+ private readonly vulns;
91
+ /** Advisory ids (and their `modified` time) per exact version from
92
+ * querybatch, for the audit TTL, least recently used first, so repeated
93
+ * plans don't re-query. */
94
+ private readonly batchCache;
95
+ private readonly now;
96
+ private readonly ttlMs;
97
+ private readonly maxEntries;
98
+ private readonly fetch;
99
+ constructor(options: {
100
+ fetch: UpstreamFetch;
101
+ failOpen: boolean;
102
+ now?: () => number;
103
+ ttlMs?: number;
104
+ /** Packages kept in the advisory cache (default 5000). */
105
+ maxEntries?: number;
106
+ /** Advisory severities kept for exact-version audits (default 10000). */
107
+ maxVulnEntries?: number;
108
+ /** Exact versions whose querybatch results are kept (default 20000). */
109
+ maxBatchEntries?: number;
110
+ /** Timeout for one OSV request, body included (default 30 s). */
111
+ timeoutMs?: number;
112
+ /** Largest OSV response read (default 64 MiB). */
113
+ maxBytes?: number;
114
+ });
115
+ audit(adapter: AuditAdapter, name: string): Promise<AdvisoryIndex>;
116
+ /** Advisories for exact versions, in as few OSV requests as possible:
117
+ * one querybatch per 1000 versions (OSV matches each version itself),
118
+ * then each affecting advisory's severity from /v1/vulns/{id}, cached by
119
+ * id and OSV's `modified` time. Fails closed like `audit`: any failure
120
+ * is a 503 wardby_audit_unavailable, unless configured to fail open. */
121
+ auditVersions(adapter: AuditAdapter, versions: readonly AuditedVersion[], signal?: AbortSignal): Promise<WithheldVersions>;
122
+ private loadVersions;
123
+ private rememberBatch;
124
+ /** Whether an advisory is HIGH or CRITICAL, cached by id while OSV's
125
+ * `modified` time for it is unchanged. */
126
+ private blocking;
127
+ private remember;
128
+ private load;
129
+ private query;
130
+ }
131
+ export {};
@@ -0,0 +1,380 @@
1
+ /**
2
+ * OSV vulnerability audit for the coding package registry. One query per
3
+ * package (cached), evaluated against every candidate version with the
4
+ * ecosystem's own version comparator:
5
+ *
6
+ * - only `affected[]` entries for the queried package count: same OSV
7
+ * ecosystem, and the same name under the adapter's normalization (PEP 503
8
+ * for PyPI, exact for npm). A GHSA advisory lists every package it touches
9
+ * (e.g. `lodash-es`, or RubyGems `lodash-rails`), and none of those may
10
+ * leak onto the one being installed;
11
+ * - a version is affected when it is listed in `versions`, or falls inside a
12
+ * SEMVER/ECOSYSTEM range (npm GHSA entries carry ranges only). Ranges are
13
+ * evaluated per the OSV schema: events sorted by version, `introduced`
14
+ * (including "0") opens, `fixed` closes (exclusive), `last_affected`
15
+ * closes (inclusive), `limit` bounds;
16
+ * - a version or event the comparator cannot parse counts as affected
17
+ * (fail closed).
18
+ */
19
+ import { RegistryError } from "../../../coding/registry/types.js";
20
+ import { boundedMetadataFetch, DEFAULT_MAX_METADATA_BYTES, DEFAULT_METADATA_TIMEOUT_MS } from "./bounded-fetch.js";
21
+ const OSV_QUERY = "https://api.osv.dev/v1/query";
22
+ const OSV_QUERY_BATCH = "https://api.osv.dev/v1/querybatch";
23
+ const OSV_VULN = "https://api.osv.dev/v1/vulns/";
24
+ /** OSV's own limit on queries per querybatch request. */
25
+ const OSV_BATCH_SIZE = 1000;
26
+ /** Rounds of per-query pagination one batch follows before giving up
27
+ * (fail closed): a package with that many pages of advisories is not a
28
+ * real case. */
29
+ const OSV_BATCH_MAX_PAGES = 20;
30
+ /** Advisory details fetched at once. */
31
+ const OSV_VULN_CONCURRENCY = 8;
32
+ export const DEFAULT_VULN_CACHE_ENTRIES = 10_000;
33
+ /** Exact versions whose querybatch results are cached. */
34
+ export const DEFAULT_BATCH_CACHE_ENTRIES = 20_000;
35
+ const BLOCKING = new Set(["HIGH", "CRITICAL"]);
36
+ const RANGE_TYPES = new Set(["SEMVER", "ECOSYSTEM"]);
37
+ /** Whether an advisory reports malicious code: an OSV malicious-packages
38
+ * entry (`MAL-…`, which carries no severity at all), one that aliases or
39
+ * imports one, or a CWE-506 (embedded malicious code) advisory. Withheld
40
+ * whatever its severity says. */
41
+ export function isMalware(vuln) {
42
+ if (vuln.id.startsWith("MAL-") || (vuln.aliases ?? []).some((alias) => alias.startsWith("MAL-")))
43
+ return true;
44
+ const specific = vuln.database_specific ?? {};
45
+ if (specific["malicious-packages-origins"] !== undefined)
46
+ return true;
47
+ if (Array.isArray(specific.cwe_ids) && specific.cwe_ids.includes("CWE-506"))
48
+ return true;
49
+ return (vuln.affected ?? []).some((affected) => {
50
+ const detail = affected.database_specific ?? {};
51
+ return (detail["malicious-packages-origins"] !== undefined ||
52
+ (typeof detail.source === "string" && detail.source.includes("/malicious-packages/")));
53
+ });
54
+ }
55
+ /** Whether an advisory withholds the versions it affects. */
56
+ function blocks(vuln) {
57
+ return BLOCKING.has(vuln.database_specific?.severity?.toUpperCase() ?? "") || isMalware(vuln);
58
+ }
59
+ function eventVersion(event) {
60
+ return event.introduced ?? event.fixed ?? event.last_affected ?? event.limit;
61
+ }
62
+ /** OSV range evaluation: sort events by version, then sweep. A version or
63
+ * event the comparator cannot parse makes the version count as affected. */
64
+ export function inOsvRange(version, events, compare) {
65
+ try {
66
+ const sorted = events
67
+ .filter((event) => eventVersion(event) !== undefined)
68
+ .sort((a, b) => {
69
+ if (a.introduced === "0")
70
+ return b.introduced === "0" ? 0 : -1;
71
+ if (b.introduced === "0")
72
+ return 1;
73
+ const order = compare(eventVersion(a), eventVersion(b));
74
+ if (order !== 0)
75
+ return order;
76
+ // Ties are resolved independent of array order: an `introduced` at
77
+ // the same version as a closing event is processed last, so that
78
+ // version counts as affected (fail closed).
79
+ return Number(a.introduced !== undefined) - Number(b.introduced !== undefined);
80
+ });
81
+ let affected = false;
82
+ for (const event of sorted) {
83
+ if (event.introduced !== undefined) {
84
+ if (event.introduced === "0" || compare(version, event.introduced) >= 0)
85
+ affected = true;
86
+ }
87
+ else if (event.fixed !== undefined) {
88
+ if (compare(version, event.fixed) >= 0)
89
+ affected = false;
90
+ }
91
+ else if (event.last_affected !== undefined) {
92
+ if (compare(version, event.last_affected) > 0)
93
+ affected = false;
94
+ }
95
+ else if (event.limit !== undefined && event.limit !== "*") {
96
+ if (compare(version, event.limit) >= 0)
97
+ affected = false;
98
+ }
99
+ }
100
+ return affected;
101
+ }
102
+ catch {
103
+ return true;
104
+ }
105
+ }
106
+ function sameVersion(a, b, compare) {
107
+ if (a === b)
108
+ return true;
109
+ try {
110
+ return compare(a, b) === 0;
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ function affects(version, affected, compare) {
117
+ if ((affected.versions ?? []).some((listed) => sameVersion(version, listed, compare)))
118
+ return true;
119
+ return (affected.ranges ?? []).some((range) => RANGE_TYPES.has(range.type ?? "") && inOsvRange(version, range.events ?? [], compare));
120
+ }
121
+ function indexOf(advisories, adapter) {
122
+ const compare = (a, b) => adapter.compareVersions(a, b);
123
+ const matching = (version, blocking) => advisories
124
+ .filter((advisory) => advisory.blocking === blocking)
125
+ .filter((advisory) => advisory.affected.some((affected) => affects(version, affected, compare)))
126
+ .map((advisory) => advisory.id);
127
+ return { withheld: (version) => matching(version, true), reported: (version) => matching(version, false) };
128
+ }
129
+ const EMPTY_INDEX = { withheld: () => [], reported: () => [] };
130
+ export const DEFAULT_AUDIT_CACHE_ENTRIES = 5000;
131
+ export const versionKey = (name, version) => `${name}@${version}`;
132
+ export class OsvAudit {
133
+ options;
134
+ /** Advisories per package, least recently used first. Bounded to
135
+ * `maxEntries`; expired entries are dropped on access and before any
136
+ * live entry is evicted. A failed query is never cached. */
137
+ cache = new Map();
138
+ /** Single-flight: concurrent audits of one package share one OSV query. */
139
+ loads = new Map();
140
+ /** Advisory severities by id, with the OSV `modified` time they were
141
+ * read at, least recently used first. */
142
+ vulns = new Map();
143
+ /** Advisory ids (and their `modified` time) per exact version from
144
+ * querybatch, for the audit TTL, least recently used first, so repeated
145
+ * plans don't re-query. */
146
+ batchCache = new Map();
147
+ now;
148
+ ttlMs;
149
+ maxEntries;
150
+ fetch;
151
+ constructor(options) {
152
+ this.options = options;
153
+ this.now = options.now ?? Date.now;
154
+ this.ttlMs = options.ttlMs ?? 3_600_000;
155
+ this.maxEntries = options.maxEntries ?? DEFAULT_AUDIT_CACHE_ENTRIES;
156
+ // Every OSV request is bounded in time and size; any failure, including
157
+ // these, makes the audit unavailable (fail closed unless configured open).
158
+ this.fetch = boundedMetadataFetch(options.fetch, {
159
+ timeoutMs: options.timeoutMs ?? DEFAULT_METADATA_TIMEOUT_MS,
160
+ maxBytes: options.maxBytes ?? DEFAULT_MAX_METADATA_BYTES,
161
+ });
162
+ }
163
+ async audit(adapter, name) {
164
+ const wanted = adapter.normalizeName(name);
165
+ const key = `${adapter.osvEcosystem}:${wanted}`;
166
+ const cached = this.cache.get(key);
167
+ if (cached) {
168
+ this.cache.delete(key);
169
+ if (cached.expires > this.now()) {
170
+ this.cache.set(key, cached); // most recently used
171
+ return indexOf(cached.advisories, adapter);
172
+ }
173
+ }
174
+ let load = this.loads.get(key);
175
+ if (!load) {
176
+ load = this.load(adapter, name, wanted)
177
+ .then((advisories) => {
178
+ this.remember(key, advisories);
179
+ return advisories;
180
+ })
181
+ .finally(() => this.loads.delete(key));
182
+ this.loads.set(key, load);
183
+ }
184
+ try {
185
+ return indexOf(await load, adapter);
186
+ }
187
+ catch {
188
+ if (this.options.failOpen)
189
+ return EMPTY_INDEX;
190
+ throw new RegistryError(503, "wardby_audit_unavailable", `the vulnerability audit for "${name}" could not reach OSV; try again later`);
191
+ }
192
+ }
193
+ /** Advisories for exact versions, in as few OSV requests as possible:
194
+ * one querybatch per 1000 versions (OSV matches each version itself),
195
+ * then each affecting advisory's severity from /v1/vulns/{id}, cached by
196
+ * id and OSV's `modified` time. Fails closed like `audit`: any failure
197
+ * is a 503 wardby_audit_unavailable, unless configured to fail open. */
198
+ async auditVersions(adapter, versions, signal) {
199
+ try {
200
+ return await this.loadVersions(adapter, versions, signal);
201
+ }
202
+ catch (error) {
203
+ if (signal?.aborted)
204
+ throw error;
205
+ if (this.options.failOpen)
206
+ return new Map();
207
+ throw new RegistryError(503, "wardby_audit_unavailable", "the vulnerability audit of the lockfile's versions could not reach OSV; try again later");
208
+ }
209
+ }
210
+ async loadVersions(adapter, versions, signal) {
211
+ const unique = [...new Map(versions.map((entry) => [versionKey(entry.name, entry.version), entry])).values()];
212
+ /** Advisory id -> its `modified` time, and the versions it affects. */
213
+ const found = new Map();
214
+ const note = (key, vuln) => {
215
+ const advisory = found.get(vuln.id) ?? { modified: vuln.modified, versions: new Set() };
216
+ advisory.versions.add(key);
217
+ found.set(vuln.id, advisory);
218
+ };
219
+ const now = this.now();
220
+ const missing = [];
221
+ for (const entry of unique) {
222
+ const key = versionKey(entry.name, entry.version);
223
+ const cacheKey = `${adapter.osvEcosystem}:${key}`;
224
+ const cached = this.batchCache.get(cacheKey);
225
+ if (cached && cached.expires > now) {
226
+ this.batchCache.delete(cacheKey);
227
+ this.batchCache.set(cacheKey, cached); // most recently used
228
+ for (const vuln of cached.vulns)
229
+ note(key, vuln);
230
+ }
231
+ else
232
+ missing.push(entry);
233
+ }
234
+ const fresh = new Map();
235
+ for (let start = 0; start < missing.length; start += OSV_BATCH_SIZE) {
236
+ const chunk = missing.slice(start, start + OSV_BATCH_SIZE);
237
+ let pending = chunk.map((entry) => ({ entry, pageToken: undefined }));
238
+ for (let page = 0; pending.length > 0; page += 1) {
239
+ if (page >= OSV_BATCH_MAX_PAGES)
240
+ throw new Error("osv_batch_pages");
241
+ const response = await this.fetch(OSV_QUERY_BATCH, {
242
+ method: "POST",
243
+ accept: "application/json",
244
+ signal,
245
+ body: JSON.stringify({
246
+ queries: pending.map(({ entry, pageToken }) => ({
247
+ package: { name: entry.name, ecosystem: adapter.osvEcosystem },
248
+ version: entry.version,
249
+ ...(pageToken ? { page_token: pageToken } : {}),
250
+ })),
251
+ }),
252
+ });
253
+ if (!response.ok)
254
+ throw new Error(`osv_status_${response.status}`);
255
+ const body = (await response.json());
256
+ const results = body.results ?? [];
257
+ // One result per query, in order: anything else cannot be matched
258
+ // to the versions it answers (fail closed).
259
+ if (results.length !== pending.length)
260
+ throw new Error("osv_batch_mismatch");
261
+ const next = [];
262
+ results.forEach((result, index) => {
263
+ const key = versionKey(pending[index].entry.name, pending[index].entry.version);
264
+ const list = fresh.get(key) ?? [];
265
+ fresh.set(key, list);
266
+ for (const vuln of result.vulns ?? []) {
267
+ if (typeof vuln.id !== "string" || vuln.id === "")
268
+ throw new Error("osv_batch_vuln_id");
269
+ const entry = { id: vuln.id, modified: typeof vuln.modified === "string" ? vuln.modified : "" };
270
+ list.push(entry);
271
+ note(key, entry);
272
+ }
273
+ if (typeof result.next_page_token === "string" && result.next_page_token !== "")
274
+ next.push({ entry: pending[index].entry, pageToken: result.next_page_token });
275
+ });
276
+ pending = next;
277
+ }
278
+ }
279
+ // Cached only once every page of every version was read.
280
+ for (const [key, vulns] of fresh)
281
+ this.rememberBatch(`${adapter.osvEcosystem}:${key}`, vulns);
282
+ const withheld = new Map();
283
+ const ids = [...found.keys()];
284
+ let cursor = 0;
285
+ const worker = async () => {
286
+ while (cursor < ids.length) {
287
+ const id = ids[cursor++];
288
+ const advisory = found.get(id);
289
+ if (!(await this.blocking(id, advisory.modified, signal)))
290
+ continue;
291
+ for (const key of advisory.versions)
292
+ withheld.set(key, [...(withheld.get(key) ?? []), id]);
293
+ }
294
+ };
295
+ await Promise.all(Array.from({ length: Math.min(OSV_VULN_CONCURRENCY, ids.length) }, worker));
296
+ return withheld;
297
+ }
298
+ rememberBatch(key, vulns) {
299
+ const now = this.now();
300
+ this.batchCache.delete(key);
301
+ const limit = this.options.maxBatchEntries ?? DEFAULT_BATCH_CACHE_ENTRIES;
302
+ while (this.batchCache.size >= limit) {
303
+ const oldest = this.batchCache.keys().next();
304
+ if (oldest.done)
305
+ break;
306
+ this.batchCache.delete(oldest.value);
307
+ }
308
+ this.batchCache.set(key, { expires: now + this.ttlMs, vulns });
309
+ }
310
+ /** Whether an advisory is HIGH or CRITICAL, cached by id while OSV's
311
+ * `modified` time for it is unchanged. */
312
+ async blocking(id, modified, signal) {
313
+ const cached = this.vulns.get(id);
314
+ if (cached && modified !== "" && cached.modified === modified) {
315
+ this.vulns.delete(id);
316
+ this.vulns.set(id, cached); // most recently used
317
+ return cached.blocking;
318
+ }
319
+ const response = await this.fetch(`${OSV_VULN}${encodeURIComponent(id)}`, { accept: "application/json", signal });
320
+ if (!response.ok)
321
+ throw new Error(`osv_status_${response.status}`);
322
+ const vuln = (await response.json());
323
+ if (typeof vuln.id !== "string")
324
+ throw new Error("osv_vuln_invalid");
325
+ const blocking = blocks(vuln);
326
+ this.vulns.delete(id);
327
+ while (this.vulns.size >= (this.options.maxVulnEntries ?? DEFAULT_VULN_CACHE_ENTRIES)) {
328
+ const oldest = this.vulns.keys().next();
329
+ if (oldest.done)
330
+ break;
331
+ this.vulns.delete(oldest.value);
332
+ }
333
+ this.vulns.set(id, { modified: typeof vuln.modified === "string" ? vuln.modified : modified, blocking });
334
+ return blocking;
335
+ }
336
+ remember(key, advisories) {
337
+ const now = this.now();
338
+ for (const [cachedKey, entry] of this.cache)
339
+ if (entry.expires <= now)
340
+ this.cache.delete(cachedKey);
341
+ this.cache.delete(key);
342
+ while (this.cache.size >= this.maxEntries) {
343
+ const oldest = this.cache.keys().next();
344
+ if (oldest.done)
345
+ break;
346
+ this.cache.delete(oldest.value);
347
+ }
348
+ this.cache.set(key, { expires: now + this.ttlMs, advisories });
349
+ }
350
+ async load(adapter, name, wanted) {
351
+ const vulns = await this.query(adapter.osvEcosystem, name);
352
+ const advisories = [];
353
+ for (const vuln of vulns) {
354
+ const affected = (vuln.affected ?? []).filter((entry) => entry.package?.ecosystem === adapter.osvEcosystem &&
355
+ typeof entry.package.name === "string" &&
356
+ adapter.normalizeName(entry.package.name) === wanted);
357
+ if (affected.length === 0)
358
+ continue;
359
+ advisories.push({ id: vuln.id, blocking: blocks(vuln), affected });
360
+ }
361
+ return advisories;
362
+ }
363
+ async query(ecosystem, name) {
364
+ const vulns = [];
365
+ let pageToken;
366
+ do {
367
+ const response = await this.fetch(OSV_QUERY, {
368
+ method: "POST",
369
+ accept: "application/json",
370
+ body: JSON.stringify({ package: { name, ecosystem }, ...(pageToken ? { page_token: pageToken } : {}) }),
371
+ });
372
+ if (!response.ok)
373
+ throw new Error(`osv_status_${response.status}`);
374
+ const page = (await response.json());
375
+ vulns.push(...(page.vulns ?? []));
376
+ pageToken = page.next_page_token;
377
+ } while (pageToken);
378
+ return vulns;
379
+ }
380
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The registry core's guard around every metadata and OSV request: a
3
+ * timeout covering the request and its whole body, and a byte cap on the
4
+ * body. The body is read here, within both limits, and handed back as an
5
+ * in-memory Response, so an adapter's (or the audit's) `response.json()`
6
+ * can neither hang nor buffer an unbounded document. Downloads do not go
7
+ * through this: they stream with their own size and idle limits.
8
+ */
9
+ import { type UpstreamFetch } from "../../../coding/registry/types.js";
10
+ export declare const DEFAULT_METADATA_TIMEOUT_MS = 30000;
11
+ export declare const DEFAULT_MAX_METADATA_BYTES: number;
12
+ export interface MetadataFetchLimits {
13
+ timeoutMs: number;
14
+ maxBytes: number;
15
+ }
16
+ export declare function boundedMetadataFetch(upstream: UpstreamFetch, limits: MetadataFetchLimits): UpstreamFetch;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The registry core's guard around every metadata and OSV request: a
3
+ * timeout covering the request and its whole body, and a byte cap on the
4
+ * body. The body is read here, within both limits, and handed back as an
5
+ * in-memory Response, so an adapter's (or the audit's) `response.json()`
6
+ * can neither hang nor buffer an unbounded document. Downloads do not go
7
+ * through this: they stream with their own size and idle limits.
8
+ */
9
+ import { RegistryError } from "../../../coding/registry/types.js";
10
+ export const DEFAULT_METADATA_TIMEOUT_MS = 30_000;
11
+ export const DEFAULT_MAX_METADATA_BYTES = 64 * 1024 * 1024;
12
+ function timedOut(signal, error) {
13
+ if (signal.aborted && signal.reason instanceof Error && signal.reason.name === "TimeoutError")
14
+ return true;
15
+ return error instanceof Error && error.name === "TimeoutError";
16
+ }
17
+ function unavailable(url) {
18
+ return new RegistryError(504, "wardby_upstream_unavailable", `${new URL(url).hostname} did not answer in time`);
19
+ }
20
+ function tooLarge(url, maxBytes) {
21
+ return new RegistryError(502, "wardby_metadata_too_large", `${new URL(url).hostname} returned more than ${Math.floor(maxBytes / (1024 * 1024))} MiB of metadata`);
22
+ }
23
+ export function boundedMetadataFetch(upstream, limits) {
24
+ return async (url, init = {}) => {
25
+ const timeout = AbortSignal.timeout(limits.timeoutMs);
26
+ const signal = init.signal ? AbortSignal.any([timeout, init.signal]) : timeout;
27
+ try {
28
+ const response = await upstream(url, { ...init, signal });
29
+ const declared = Number(response.headers.get("content-length"));
30
+ if (Number.isFinite(declared) && declared > limits.maxBytes) {
31
+ await response.body?.cancel().catch(() => undefined);
32
+ throw tooLarge(url, limits.maxBytes);
33
+ }
34
+ if (!response.body)
35
+ return response;
36
+ const chunks = [];
37
+ let total = 0;
38
+ const reader = response.body.getReader();
39
+ // The timeout also bounds a body that trickles: a stalled read is
40
+ // abandoned when the signal fires, even if the upstream ignores it.
41
+ const aborted = new Promise((_, reject) => {
42
+ if (signal.aborted)
43
+ reject(signal.reason);
44
+ signal.addEventListener("abort", () => reject(signal.reason), { once: true });
45
+ });
46
+ aborted.catch(() => undefined);
47
+ try {
48
+ for (;;) {
49
+ const { done, value } = await Promise.race([reader.read(), aborted]);
50
+ if (done)
51
+ break;
52
+ total += value.byteLength;
53
+ if (total > limits.maxBytes)
54
+ throw tooLarge(url, limits.maxBytes);
55
+ chunks.push(value);
56
+ }
57
+ }
58
+ catch (error) {
59
+ await reader.cancel().catch(() => undefined);
60
+ throw error;
61
+ }
62
+ const nullBody = [101, 204, 205, 304].includes(response.status);
63
+ return new Response(nullBody ? null : Buffer.concat(chunks), {
64
+ status: response.status,
65
+ statusText: response.statusText,
66
+ headers: response.headers,
67
+ });
68
+ }
69
+ catch (error) {
70
+ if (error instanceof RegistryError)
71
+ throw error;
72
+ if (timedOut(signal, error))
73
+ throw unavailable(url);
74
+ throw error;
75
+ }
76
+ };
77
+ }
@@ -0,0 +1,58 @@
1
+ import { type AllowlistEntry, type LockfilePlanSupport, type ParsedLockfile, type RegistryAdapter, type UpstreamFetch } from "../../../coding/registry/types.js";
2
+ import { type AuditedVersion, type WithheldVersions } from "./audit.js";
3
+ import type { ApprovedVersion, StoredVersionFact } from "./store.js";
4
+ export declare const DEFAULT_PLAN_MAX_ENTRIES = 5000;
5
+ export declare const DEFAULT_PLAN_TIMEOUT_MS = 120000;
6
+ /** Per-version documents fetched at once (each is a few KB). */
7
+ export declare const DEFAULT_PLAN_FACT_CONCURRENCY = 16;
8
+ /** Full package documents streamed at once for publish times (each may be
9
+ * tens of MB, never held). */
10
+ export declare const DEFAULT_PLAN_TIME_CONCURRENCY = 8;
11
+ /** Largest (decompressed) package document streamed for publish times. */
12
+ export declare const DEFAULT_PLAN_MAX_DOCUMENT_BYTES: number;
13
+ export interface PlanRefusal {
14
+ name: string;
15
+ version: string;
16
+ code: string;
17
+ reason: string;
18
+ /** A verdict about the registry's name@version itself (too new, an
19
+ * advisory, not published, no integrity, hosted elsewhere), true of it
20
+ * whatever lockfile names it. Only these answer later downloads; a
21
+ * lockfile-dependent verdict (unreachable, an integrity or source the
22
+ * lockfile claims) never does. */
23
+ intrinsic?: boolean;
24
+ /** For a too-new refusal: the version's publish time, so whoever answers
25
+ * from it can re-check the age against the cutoff at that time. */
26
+ publishedAt?: Date;
27
+ }
28
+ export interface PlanResult {
29
+ approved: ApprovedVersion[];
30
+ refused: PlanRefusal[];
31
+ }
32
+ export interface PlanFacts {
33
+ getVersionFacts(versions: readonly {
34
+ name: string;
35
+ version: string;
36
+ }[]): Promise<StoredVersionFact[]>;
37
+ putVersionFacts(facts: readonly StoredVersionFact[]): Promise<void>;
38
+ }
39
+ export interface PlanInput {
40
+ adapter: RegistryAdapter;
41
+ support: LockfilePlanSupport;
42
+ lockfile: ParsedLockfile;
43
+ roots: readonly AllowlistEntry[];
44
+ /** Latest publish time a version may have (the release-age cutoff). */
45
+ cutoff: Date;
46
+ facts: PlanFacts;
47
+ /** Bounded fetch for small per-version documents. */
48
+ upstream: UpstreamFetch;
49
+ /** Unbuffered fetch for streamed package documents. */
50
+ streamUpstream: UpstreamFetch;
51
+ audit(versions: readonly AuditedVersion[], signal: AbortSignal): Promise<WithheldVersions>;
52
+ hostAllowed(url: string): boolean;
53
+ signal: AbortSignal;
54
+ factConcurrency?: number;
55
+ timeConcurrency?: number;
56
+ maxDocumentBytes?: number;
57
+ }
58
+ export declare function verifyLockfilePlan(input: PlanInput): Promise<PlanResult>;