@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,73 @@
1
+ /**
2
+ * npm lockfile normaliser. The npm adapter serves metadata whose tarball
3
+ * links point at the proxy, so `npm install` in the worker records those
4
+ * proxy URLs as `resolved` in package-lock.json / npm-shrinkwrap.json. They
5
+ * only resolve inside the sandbox, so before the workspace is collected every
6
+ * `resolved` value under the proxy's npm route is rewritten to the canonical
7
+ * public registry URL for the same package and version. `integrity` is left
8
+ * as is: it covers the same tarball bytes. Only the matched values change;
9
+ * the rest of the file stays byte-for-byte identical (no JSON round trip).
10
+ *
11
+ * Kept free of imports from npm.ts, which imports this module.
12
+ */
13
+ import semver from "semver";
14
+ const PUBLIC_REGISTRY = "https://registry.npmjs.org/";
15
+ /** Same shape as the adapter's allowlist name check (scoped names are lower case). */
16
+ export const PACKAGE_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/[a-z0-9-~][a-z0-9-._~]*|[A-Za-z0-9-~][A-Za-z0-9-._~]*)$/;
17
+ /** A `"resolved": "<value>"` pair whose value has no JSON escapes. */
18
+ const RESOLVED = /("resolved"\s*:\s*")([^"\\]*)(")/g;
19
+ function decode(value) {
20
+ try {
21
+ return decodeURIComponent(value);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ function unscoped(name) {
28
+ return name.slice(name.indexOf("/") + 1);
29
+ }
30
+ /** The package and version a path under the proxy's npm base downloads:
31
+ * `-/tarball/<encoded name>/<version>` (the proxy's own route) or the
32
+ * standard `<name>/-/<unscoped>-<version>.tgz`, with the scope separator
33
+ * literal or percent-encoded. Null for anything else. */
34
+ function parseProxyPath(path) {
35
+ let name;
36
+ let version;
37
+ const own = path.match(/^-\/tarball\/([^/]+)\/([^/]+)$/);
38
+ if (own) {
39
+ name = decode(own[1]);
40
+ version = decode(own[2]);
41
+ }
42
+ else {
43
+ const standard = path.match(/^(.+)\/-\/([^/]+)\.tgz$/);
44
+ if (!standard)
45
+ return null;
46
+ name = decode(standard[1]);
47
+ const file = decode(standard[2]);
48
+ if (!name || !file)
49
+ return null;
50
+ const prefix = `${unscoped(name)}-`;
51
+ version = file.startsWith(prefix) ? file.slice(prefix.length) : null;
52
+ }
53
+ if (!name || !version || !PACKAGE_NAME.test(name) || semver.valid(version) !== version)
54
+ return null;
55
+ return { name, version };
56
+ }
57
+ /** `https://registry.npmjs.org/@scope/pkg/-/pkg-1.0.0.tgz` */
58
+ export function publicNpmTarballUrl(name, version) {
59
+ return `${PUBLIC_REGISTRY}${name}/-/${unscoped(name)}-${version}.tgz`;
60
+ }
61
+ export function normalizeNpmLockfile(content, registryUrl) {
62
+ const base = registryUrl.endsWith("/") ? registryUrl : `${registryUrl}/`;
63
+ return content.replace(RESOLVED, (match, open, url, close) => {
64
+ if (!url.startsWith(base))
65
+ return match;
66
+ const target = parseProxyPath(url.slice(base.length));
67
+ return target ? `${open}${publicNpmTarballUrl(target.name, target.version)}${close}` : match;
68
+ });
69
+ }
70
+ export const npmLockfiles = {
71
+ names: ["package-lock.json", "npm-shrinkwrap.json"],
72
+ normalize: normalizeNpmLockfile,
73
+ };
@@ -0,0 +1,29 @@
1
+ import { type DeclaredDependency, type LockfilePlanSupport, type ParsedLockfile, type UpstreamFetch, type VersionFact } from "./types.js";
2
+ /** The registry dependencies a manifest's dependency map declares: each
3
+ * one's install folder (`key`), the registry package it installs (an
4
+ * alias `"string-width-cjs": "npm:string-width@^4"` installs its target,
5
+ * under the alias's own range) and its range. A spec that isn't fetched
6
+ * from the registry at all (`file:`, `link:`, a path, `git`/`git+…`, an
7
+ * `http(s):` tarball, `github:`/`user/repo` shorthands, `workspace:`)
8
+ * contributes nothing: every such spec contains a `:` or a `/`, which no
9
+ * semver range or dist-tag does. */
10
+ export declare function registryDependencyEdges(deps: unknown): DeclaredDependency[];
11
+ /** Every registry dependency a manifest declares, in npm's install order
12
+ * (dependencies, optionalDependencies, peerDependencies), once per
13
+ * install folder: npm lists an optional dependency under `dependencies`
14
+ * too, and the first declaration of a folder is the one npm uses. */
15
+ export declare function declaredDependencies(manifest: Record<string, unknown>, fields: readonly string[]): DeclaredDependency[];
16
+ export declare function parseNpmLockfile(body: string, options: {
17
+ maxEntries: number;
18
+ proxyRegistryUrl: string;
19
+ }): ParsedLockfile;
20
+ export declare function fetchNpmVersionFact(name: string, version: string, upstream: UpstreamFetch, signal?: AbortSignal): Promise<VersionFact | null>;
21
+ export declare function fetchNpmPublishTimes(name: string, versions: readonly string[], upstream: UpstreamFetch, options: {
22
+ maxBytes: number;
23
+ signal: AbortSignal;
24
+ }): Promise<Map<string, Date>>;
25
+ /** Whether a locked version satisfies a declared range: `*` and dist-tags
26
+ * accept any version (as npm's own dep-valid does), otherwise strict semver,
27
+ * the same match the graph walk uses. */
28
+ export declare function npmEdgeSatisfies(version: string, range: string): boolean;
29
+ export declare const npmLockfilePlan: LockfilePlanSupport;
@@ -0,0 +1,289 @@
1
+ /**
2
+ * npm lockfile verification hooks (`POST /registry/npm/-/plan`): parsing a
3
+ * package-lock.json (lockfileVersion 2 or 3) into entries and npm's own
4
+ * node_modules resolution rule, and reading the registry's per-version
5
+ * facts and publish times. Nothing here decides anything: the registry core
6
+ * verifies every claim against these registry reads (plan.ts).
7
+ *
8
+ * Kept free of imports from npm.ts, which imports this module.
9
+ */
10
+ import semver from "semver";
11
+ import { PACKAGE_NAME } from "./npm-lockfile.js";
12
+ import { JsonMemberTooLarge, scanTopLevelMember } from "./json-scan.js";
13
+ import { RegistryError, } from "./types.js";
14
+ const UPSTREAM = "https://registry.npmjs.org/";
15
+ const NODE_MODULES = "node_modules/";
16
+ /** The largest `time` object kept from a packument (a package with tens of
17
+ * thousands of versions has a few MB of it). */
18
+ const MAX_TIME_TEXT = 16 * 1024 * 1024;
19
+ /** A declared semver range as written, or `"*"` for anything that is not
20
+ * a valid range (a dist-tag such as `latest`, an empty spec): npm
21
+ * resolves those to some published version, so any version counts. */
22
+ function rangeOf(spec) {
23
+ return spec !== "" && semver.validRange(spec) ? spec : "*";
24
+ }
25
+ /** The registry dependencies a manifest's dependency map declares: each
26
+ * one's install folder (`key`), the registry package it installs (an
27
+ * alias `"string-width-cjs": "npm:string-width@^4"` installs its target,
28
+ * under the alias's own range) and its range. A spec that isn't fetched
29
+ * from the registry at all (`file:`, `link:`, a path, `git`/`git+…`, an
30
+ * `http(s):` tarball, `github:`/`user/repo` shorthands, `workspace:`)
31
+ * contributes nothing: every such spec contains a `:` or a `/`, which no
32
+ * semver range or dist-tag does. */
33
+ export function registryDependencyEdges(deps) {
34
+ const edges = [];
35
+ if (!deps || typeof deps !== "object")
36
+ return edges;
37
+ for (const [key, rawSpec] of Object.entries(deps)) {
38
+ const spec = typeof rawSpec === "string" ? rawSpec.trim() : "";
39
+ if (spec.startsWith("npm:")) {
40
+ const target = spec.slice("npm:".length);
41
+ const at = target.indexOf("@", 1);
42
+ const name = at > 0 ? target.slice(0, at) : target;
43
+ if (PACKAGE_NAME.test(name))
44
+ edges.push({ key, name, range: rangeOf(at > 0 ? target.slice(at + 1) : "") });
45
+ continue;
46
+ }
47
+ if (/[:/]/.test(spec))
48
+ continue;
49
+ edges.push({ key, name: key, range: rangeOf(spec) });
50
+ }
51
+ return edges;
52
+ }
53
+ /** Every registry dependency a manifest declares, in npm's install order
54
+ * (dependencies, optionalDependencies, peerDependencies), once per
55
+ * install folder: npm lists an optional dependency under `dependencies`
56
+ * too, and the first declaration of a folder is the one npm uses. */
57
+ export function declaredDependencies(manifest, fields) {
58
+ const byKey = new Map();
59
+ for (const field of fields) {
60
+ for (const edge of registryDependencyEdges(manifest[field]))
61
+ if (!byKey.has(edge.key))
62
+ byKey.set(edge.key, edge);
63
+ }
64
+ return [...byKey.values()];
65
+ }
66
+ const PUBLISHED_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
67
+ /** A project's own declarations, dev dependencies included (they are
68
+ * installed for the project, never for a dependency). */
69
+ const PROJECT_FIELDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
70
+ function badRequest(message) {
71
+ return new RegistryError(400, "wardby_bad_request", message);
72
+ }
73
+ /** The folder a `node_modules/...` path installs, e.g. `@s/a` for
74
+ * `node_modules/x/node_modules/@s/a`; undefined for a path outside any
75
+ * node_modules folder (a workspace project). */
76
+ function installFolder(path) {
77
+ const at = path.lastIndexOf(NODE_MODULES);
78
+ if (at < 0 || (at > 0 && path[at - 1] !== "/"))
79
+ return undefined;
80
+ return path.slice(at + NODE_MODULES.length);
81
+ }
82
+ /** Whether `resolved` names a registry download: the public registry, or
83
+ * the proxy's own npm route (a lockfile written through the proxy before
84
+ * it is normalized at collection). A missing `resolved` is a registry
85
+ * entry too: npm omits it for one it will fetch from the registry. */
86
+ function fromRegistry(resolved, proxyRegistryUrl) {
87
+ if (resolved === undefined)
88
+ return true;
89
+ if (typeof resolved !== "string")
90
+ return false;
91
+ return resolved.startsWith(UPSTREAM) || resolved.startsWith(proxyRegistryUrl);
92
+ }
93
+ export function parseNpmLockfile(body, options) {
94
+ let document;
95
+ try {
96
+ document = JSON.parse(body);
97
+ }
98
+ catch {
99
+ throw badRequest("the lockfile is not valid JSON");
100
+ }
101
+ if (!document || typeof document !== "object" || Array.isArray(document))
102
+ throw badRequest("the lockfile is not a JSON object");
103
+ const lock = document;
104
+ if (lock.lockfileVersion !== 2 && lock.lockfileVersion !== 3) {
105
+ throw new RegistryError(400, "wardby_lockfile_unsupported", `lockfileVersion ${JSON.stringify(lock.lockfileVersion ?? null)} is not supported; regenerate the lockfile with npm 7 or later (lockfileVersion 2 or 3)`);
106
+ }
107
+ if (!lock.packages || typeof lock.packages !== "object" || Array.isArray(lock.packages))
108
+ throw new RegistryError(400, "wardby_lockfile_unsupported", "the lockfile has no `packages` section");
109
+ const packages = lock.packages;
110
+ const count = Object.keys(packages).length - ("" in packages ? 1 : 0);
111
+ if (count > options.maxEntries) {
112
+ throw new RegistryError(413, "wardby_lockfile_too_large", `the lockfile has ${count} entries; at most ${options.maxEntries} are verified (REGISTRY_PLAN_MAX_ENTRIES)`);
113
+ }
114
+ const entries = [];
115
+ const byPath = new Map();
116
+ /** Paths of link entries (symlinks to local projects): a dependency
117
+ * resolving to one is local, not a registry entry. */
118
+ const links = new Set();
119
+ const projects = [];
120
+ for (const [path, raw] of Object.entries(packages)) {
121
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
122
+ throw badRequest(`lockfile entry "${path}" is not an object`);
123
+ const value = raw;
124
+ const folder = installFolder(path);
125
+ if (path === "" || folder === undefined) {
126
+ // The root project, or a workspace project's own folder.
127
+ projects.push({ path, dependencies: declaredDependencies(value, PROJECT_FIELDS) });
128
+ continue;
129
+ }
130
+ if (value.link === true) {
131
+ links.add(path);
132
+ continue;
133
+ }
134
+ const name = typeof value.name === "string" ? value.name : folder;
135
+ const version = typeof value.version === "string" ? value.version : "";
136
+ const integrity = typeof value.integrity === "string" ? value.integrity : null;
137
+ let entry;
138
+ if (value.inBundle === true) {
139
+ entry = { path, name, version, integrity, kind: "bundled" };
140
+ }
141
+ else if (!PACKAGE_NAME.test(name) || semver.valid(version) !== version) {
142
+ entry = {
143
+ path,
144
+ name,
145
+ version,
146
+ integrity,
147
+ kind: "unsupported",
148
+ unsupportedReason: "not a registry package name and exact version",
149
+ };
150
+ }
151
+ else if (!fromRegistry(value.resolved, options.proxyRegistryUrl)) {
152
+ entry = {
153
+ path,
154
+ name,
155
+ version,
156
+ integrity,
157
+ kind: "unsupported",
158
+ unsupportedReason: "installed from outside the npm registry",
159
+ };
160
+ }
161
+ else {
162
+ entry = { path, name, version, integrity, kind: "registry" };
163
+ }
164
+ entries.push(entry);
165
+ byPath.set(path, entry);
166
+ }
167
+ const resolve = (fromPath, key) => {
168
+ // npm searches the package's own node_modules, then each enclosing
169
+ // package's (never a bare `node_modules` or `@scope` folder), then the
170
+ // root's; a workspace project's enclosing package is the root.
171
+ let directory = fromPath;
172
+ for (;;) {
173
+ const candidate = `${directory === "" ? "" : `${directory}/`}${NODE_MODULES}${key}`;
174
+ if (links.has(candidate))
175
+ return undefined;
176
+ const found = byPath.get(candidate);
177
+ if (found)
178
+ return found;
179
+ if (directory === "")
180
+ return undefined;
181
+ const enclosing = directory.lastIndexOf(`/${NODE_MODULES}`);
182
+ directory = enclosing < 0 ? "" : directory.slice(0, enclosing);
183
+ }
184
+ };
185
+ return { entries, projects, resolve };
186
+ }
187
+ function encodedName(name) {
188
+ return name.replaceAll("/", "%2f");
189
+ }
190
+ /** npm's SRI integrity for a dist record: `integrity` as published, or,
191
+ * for a version published before npm recorded one, its sha1 `shasum` in
192
+ * SRI form (what npm itself records in a lockfile for such a version). */
193
+ function distIntegrity(dist) {
194
+ if (!dist || typeof dist !== "object")
195
+ return "";
196
+ const { integrity, shasum } = dist;
197
+ if (typeof integrity === "string" && integrity.trim() !== "")
198
+ return integrity.trim();
199
+ if (typeof shasum === "string" && /^[a-f0-9]{40}$/.test(shasum))
200
+ return `sha1-${Buffer.from(shasum, "hex").toString("base64")}`;
201
+ return "";
202
+ }
203
+ export async function fetchNpmVersionFact(name, version, upstream, signal) {
204
+ const response = await upstream(`${UPSTREAM}${encodedName(name)}/${encodeURIComponent(version)}`, {
205
+ accept: "application/json",
206
+ signal,
207
+ });
208
+ if (response.status === 404) {
209
+ await response.body?.cancel().catch(() => undefined);
210
+ return null;
211
+ }
212
+ if (!response.ok)
213
+ throw new RegistryError(502, "wardby_upstream_error", `npm returned ${response.status}`);
214
+ const doc = (await response.json());
215
+ // The registry also answers a dist-tag here (`/react/latest`), and a
216
+ // lockfile version is exact, so the answer must be that exact version.
217
+ if (doc.name !== name || doc.version !== version)
218
+ return null;
219
+ const dist = doc.dist;
220
+ return {
221
+ name,
222
+ version,
223
+ publishedAt: null,
224
+ integrity: distIntegrity(dist),
225
+ downloadUrl: typeof dist?.tarball === "string" ? dist.tarball : "",
226
+ dependencies: declaredDependencies(doc, PUBLISHED_FIELDS),
227
+ };
228
+ }
229
+ export async function fetchNpmPublishTimes(name, versions, upstream, options) {
230
+ const response = await upstream(`${UPSTREAM}${encodedName(name)}`, {
231
+ accept: "application/json",
232
+ acceptEncoding: "gzip",
233
+ signal: options.signal,
234
+ });
235
+ if (!response.ok || !response.body) {
236
+ await response.body?.cancel().catch(() => undefined);
237
+ throw new RegistryError(response.status === 404 ? 404 : 502, response.status === 404 ? "wardby_package_not_found" : "wardby_upstream_error", `npm returned ${response.status} for "${name}"`);
238
+ }
239
+ const encoding = response.headers.get("content-encoding")?.trim().toLowerCase();
240
+ let body = response.body;
241
+ if (encoding === "gzip")
242
+ body = body.pipeThrough(new DecompressionStream("gzip"));
243
+ else if (encoding && encoding !== "identity") {
244
+ await body.cancel().catch(() => undefined);
245
+ throw new RegistryError(502, "wardby_upstream_error", `npm answered with an unsupported content-encoding`);
246
+ }
247
+ let time;
248
+ try {
249
+ // maxBytes bounds the decompressed document, so a small compressed
250
+ // body cannot expand without limit.
251
+ time = await scanTopLevelMember(body, "time", { maxBytes: options.maxBytes, maxValueLength: MAX_TIME_TEXT });
252
+ }
253
+ catch (error) {
254
+ if (error instanceof JsonMemberTooLarge)
255
+ throw new RegistryError(502, "wardby_metadata_too_large", `npm metadata for "${name}" is too large`);
256
+ throw error;
257
+ }
258
+ const times = new Map();
259
+ if (!time || typeof time !== "object")
260
+ return times;
261
+ for (const version of versions) {
262
+ const value = time[version];
263
+ if (typeof value !== "string")
264
+ continue;
265
+ const date = new Date(value);
266
+ if (!Number.isNaN(date.getTime()))
267
+ times.set(version, date);
268
+ }
269
+ return times;
270
+ }
271
+ /** Whether a locked version satisfies a declared range: `*` and dist-tags
272
+ * accept any version (as npm's own dep-valid does), otherwise strict semver,
273
+ * the same match the graph walk uses. */
274
+ export function npmEdgeSatisfies(version, range) {
275
+ if (range === "*" || range === "")
276
+ return true;
277
+ try {
278
+ return semver.satisfies(version, range);
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ }
284
+ export const npmLockfilePlan = {
285
+ parse: parseNpmLockfile,
286
+ fetchVersionFact: fetchNpmVersionFact,
287
+ fetchPublishTimes: fetchNpmPublishTimes,
288
+ edgeSatisfies: npmEdgeSatisfies,
289
+ };
@@ -0,0 +1,7 @@
1
+ import { type DependencySpec, type RegistryAdapter } from "./types.js";
2
+ /** The registry package each dependency entry installs, and the range it
3
+ * asks for (aliases resolved, non-registry specs dropped; see
4
+ * registryDependencyEdges). This is the single place both the metadata
5
+ * path and the graph walk get dependency names from. */
6
+ export declare function registryDependencies(deps: Record<string, string> | undefined): DependencySpec[];
7
+ export declare const npmAdapter: RegistryAdapter;
@@ -0,0 +1,252 @@
1
+ /**
2
+ * npm registry adapter: parses npm allowlist syntax and semver ranges,
3
+ * routes proxy requests against the npm HTTP protocol (packuments,
4
+ * `-/tarball/<name>/<version>` downloads, and the standard
5
+ * `<name>/-/<unscoped>-<version>.tgz` path lockfiles record), and configures npm/npmrc for
6
+ * the sandboxed worker to use the proxy with install-time scripts disabled.
7
+ */
8
+ import semver from "semver";
9
+ import { npmLockfiles } from "./npm-lockfile.js";
10
+ import { npmLockfilePlan, registryDependencyEdges } from "./npm-plan.js";
11
+ import { AllowlistEntryError, RegistryError, } from "./types.js";
12
+ /** Scoped names are lower case (npm never allowed capitals in them); an
13
+ * unscoped legacy name may contain capitals, and npm treats it as a
14
+ * different package from its lower-case spelling (`JSONStream` is not
15
+ * `jsonstream`), so names are never case-folded anywhere. */
16
+ const NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/[a-z0-9-~][a-z0-9-._~]*|[A-Za-z0-9-~][A-Za-z0-9-._~]*)$/;
17
+ const SCOPE_WILDCARD = /^(@[a-z0-9-~][a-z0-9-._~]*)\/\*$/;
18
+ const UPSTREAM = "https://registry.npmjs.org/";
19
+ /** The per-version fields npm's installer reads (the same set as npm's
20
+ * abbreviated "install-v1" manifest). Only these are kept from an
21
+ * upstream packument, so the cached metadata never holds readmes,
22
+ * descriptions, maintainers and other bulk the proxy does not render. */
23
+ const MANIFEST_FIELDS = [
24
+ "name",
25
+ "version",
26
+ "dist",
27
+ "dependencies",
28
+ "optionalDependencies",
29
+ "devDependencies",
30
+ "peerDependencies",
31
+ "peerDependenciesMeta",
32
+ "bundleDependencies",
33
+ "bundledDependencies",
34
+ "acceptDependencies",
35
+ "bin",
36
+ "directories",
37
+ "engines",
38
+ "os",
39
+ "cpu",
40
+ "libc",
41
+ "deprecated",
42
+ "hasInstallScript",
43
+ "_hasShrinkwrap",
44
+ "license",
45
+ "funding",
46
+ ];
47
+ function abbreviated(manifest) {
48
+ const source = manifest;
49
+ const kept = Object.fromEntries(MANIFEST_FIELDS.filter((field) => field in source).map((f) => [f, source[f]]));
50
+ return kept;
51
+ }
52
+ function integrityOf(dist) {
53
+ const sri = dist.integrity?.match(/^(sha512|sha384|sha256)-([A-Za-z0-9+/=]+)$/);
54
+ if (sri)
55
+ return { algorithm: sri[1], hex: Buffer.from(sri[2], "base64").toString("hex") };
56
+ if (dist.shasum && /^[a-f0-9]{40}$/.test(dist.shasum))
57
+ return { algorithm: "sha1", hex: dist.shasum };
58
+ return null;
59
+ }
60
+ /** The registry package each dependency entry installs, and the range it
61
+ * asks for (aliases resolved, non-registry specs dropped; see
62
+ * registryDependencyEdges). This is the single place both the metadata
63
+ * path and the graph walk get dependency names from. */
64
+ export function registryDependencies(deps) {
65
+ return registryDependencyEdges(deps).map(({ name, range }) => ({ name, range }));
66
+ }
67
+ /** Maps `<name>/-/<unscoped>-<version>.tgz` (already percent-decoded) to
68
+ * the same download route as `-/tarball/<name>/<version>`, or null. The
69
+ * filename must name this exact package (case-sensitive, as npm names
70
+ * are) and an exact semver version, so it can never smuggle a path
71
+ * separator or `..` through: neither can appear in a valid version. */
72
+ function standardTarball(name, filename) {
73
+ if (!NAME.test(name))
74
+ return null;
75
+ const unscoped = name.slice(name.indexOf("/") + 1);
76
+ const prefix = `${unscoped}-`;
77
+ if (!filename.startsWith(prefix) || !filename.endsWith(".tgz"))
78
+ return null;
79
+ const version = filename.slice(prefix.length, -".tgz".length);
80
+ if (semver.valid(version) !== version)
81
+ return null;
82
+ return { kind: "download", name, version, filename: `${version}.tgz` };
83
+ }
84
+ function validName(name) {
85
+ if (!NAME.test(name))
86
+ throw new AllowlistEntryError(`"${name}" is not a valid npm package name`);
87
+ return name;
88
+ }
89
+ /** Per-version fixed cost, in bytes, of the trimmed form (Map entry,
90
+ * VersionInfo, FileRef, Date, arrays), used only for the metadata cache's
91
+ * byte budget. Deliberately generous. */
92
+ const VERSION_OVERHEAD_BYTES = 400;
93
+ const DEPENDENCY_OVERHEAD_BYTES = 80;
94
+ /** Extracts what the proxy uses from a full packument, copying every value
95
+ * it keeps, so the document itself is never retained: per version its
96
+ * dependency specs, publish time, tarball URL and integrity (all the graph
97
+ * walk, the version filter and downloads read), plus, with `render`, the
98
+ * data `renderMetadata` serves. */
99
+ function trimPackument(name, doc, render) {
100
+ const versions = new Map();
101
+ const renderData = render
102
+ ? { name: String(doc.name), distTags: { ...(doc["dist-tags"] ?? {}) }, manifests: new Map(), times: new Map() }
103
+ : undefined;
104
+ let approxBytes = 256 + name.length;
105
+ for (const [version, info] of Object.entries(doc.versions ?? {})) {
106
+ const published = doc.time?.[version];
107
+ const dependencySpecs = [info.dependencies, info.optionalDependencies, info.peerDependencies].flatMap((deps) => registryDependencies(deps));
108
+ const dependencies = [...new Set(dependencySpecs.map((spec) => spec.name))];
109
+ const file = {
110
+ filename: `${version}.tgz`,
111
+ version,
112
+ upstreamUrl: String(info.dist.tarball),
113
+ integrity: integrityOf(info.dist),
114
+ sizeBytes: null,
115
+ allowed: true,
116
+ publishedAt: published ? new Date(published) : null,
117
+ };
118
+ versions.set(version, { version, dependencies, dependencySpecs, files: [file] });
119
+ approxBytes += VERSION_OVERHEAD_BYTES + 2 * version.length + file.upstreamUrl.length;
120
+ approxBytes += file.integrity?.hex.length ?? 0;
121
+ for (const spec of dependencySpecs)
122
+ approxBytes += DEPENDENCY_OVERHEAD_BYTES + spec.name.length + spec.range.length;
123
+ if (renderData) {
124
+ const manifest = JSON.stringify(abbreviated(info));
125
+ renderData.manifests.set(version, manifest);
126
+ approxBytes += manifest.length + 64;
127
+ if (published) {
128
+ renderData.times.set(version, String(published));
129
+ approxBytes += published.length + 64;
130
+ }
131
+ }
132
+ }
133
+ return { name, versions, raw: renderData, approxBytes };
134
+ }
135
+ export const npmAdapter = {
136
+ id: "npm",
137
+ osvEcosystem: "npm",
138
+ upstreamHosts: ["registry.npmjs.org"],
139
+ collectExclude: ["node_modules"],
140
+ dependenciesInMetadata: true,
141
+ lockfiles: npmLockfiles,
142
+ lockfilePlan: npmLockfilePlan,
143
+ parseAllowlistEntry(raw) {
144
+ const value = raw.trim();
145
+ // Scopes are always lower case on npm, so folding one cannot select a
146
+ // different package; package names are kept exactly as written.
147
+ const scope = value.toLowerCase().match(SCOPE_WILDCARD);
148
+ if (scope)
149
+ return { name: `${scope[1]}/`, wildcard: true };
150
+ const at = value.indexOf("@", 1);
151
+ const name = validName(at > 0 ? value.slice(0, at) : value);
152
+ if (at < 0)
153
+ return { name, wildcard: false };
154
+ const range = value.slice(at + 1);
155
+ if (!semver.validRange(range))
156
+ throw new AllowlistEntryError(`"${range}" is not a valid npm version range`);
157
+ return { name, wildcard: false, range };
158
+ },
159
+ normalizeName: (name) => name,
160
+ satisfies: (version, range) => semver.satisfies(version, range),
161
+ compareVersions(a, b) {
162
+ if (!semver.valid(a) || !semver.valid(b))
163
+ throw new Error(`not a semver version: "${a}" / "${b}"`);
164
+ return semver.compare(a, b);
165
+ },
166
+ route(method, subpath) {
167
+ if (method !== "GET" && method !== "HEAD")
168
+ return null;
169
+ const tarball = subpath.match(/^-\/tarball\/([^/]+)\/([^/]+)$/);
170
+ // The standard upstream tarball path, `<name>/-/<unscoped>-<version>.tgz`,
171
+ // which a lockfile's `resolved` URLs point at once npm rewrites their
172
+ // host to the configured registry (replace-registry-host). The name is
173
+ // one segment (`react`, `@scope%2fpkg`) or a literal scope plus name
174
+ // (`@scope/pkg`); the filename is one raw segment.
175
+ const standard = subpath.match(/^((?:@[^/]+\/)?[^/]+)\/-\/([^/]+)$/);
176
+ try {
177
+ if (tarball) {
178
+ const name = decodeURIComponent(tarball[1]);
179
+ const version = decodeURIComponent(tarball[2]);
180
+ if (!NAME.test(name) || !semver.valid(version))
181
+ return null;
182
+ return { kind: "download", name, version, filename: `${version}.tgz` };
183
+ }
184
+ if (standard)
185
+ return standardTarball(decodeURIComponent(standard[1]), decodeURIComponent(standard[2]));
186
+ const name = decodeURIComponent(subpath);
187
+ return NAME.test(name) ? { kind: "metadata", name } : null;
188
+ }
189
+ catch {
190
+ throw new RegistryError(400, "wardby_bad_request", "malformed percent-encoding in the registry path");
191
+ }
192
+ },
193
+ async fetchMetadata(name, upstream, options = {}) {
194
+ const response = await upstream(`${UPSTREAM}${name.replaceAll("/", "%2f")}`, { accept: "application/json" });
195
+ if (response.status === 404)
196
+ throw new RegistryError(404, "wardby_package_not_found", `npm has no package "${name}"`);
197
+ if (!response.ok)
198
+ throw new RegistryError(502, "wardby_upstream_error", `npm returned ${response.status}`);
199
+ // The full packument is only ever a local of `trimPackument`: nothing
200
+ // returned from here references it, so it is garbage as soon as the
201
+ // trimmed form is built (a large one is tens of MB parsed).
202
+ return trimPackument(name, (await response.json()), options.render === true);
203
+ },
204
+ renderMetadata(meta, keep, keptFiles, proxyBase) {
205
+ const raw = meta.raw;
206
+ if (!raw)
207
+ throw new Error(`npm metadata for "${meta.name}" was fetched without render data`);
208
+ const versions = {};
209
+ const time = {};
210
+ for (const version of keep) {
211
+ const manifest = raw.manifests.get(version);
212
+ if (manifest === undefined || !keptFiles.has(`${version}.tgz`))
213
+ continue;
214
+ const info = JSON.parse(manifest);
215
+ versions[version] = {
216
+ ...info,
217
+ dist: { ...info.dist, tarball: `${proxyBase}-/tarball/${encodeURIComponent(meta.name)}/${version}` },
218
+ };
219
+ const published = raw.times.get(version);
220
+ if (published !== undefined)
221
+ time[version] = published;
222
+ }
223
+ const kept = Object.keys(versions);
224
+ const tags = Object.fromEntries(Object.entries(raw.distTags).filter(([, version]) => kept.includes(version)));
225
+ if (!tags.latest && kept.length > 0)
226
+ tags.latest = semver.maxSatisfying(kept, "*") ?? kept[kept.length - 1];
227
+ return {
228
+ contentType: "application/json",
229
+ body: JSON.stringify({ name: raw.name, "dist-tags": tags, time, versions }),
230
+ };
231
+ },
232
+ resolveDownload(route, meta) {
233
+ return meta.versions.get(route.version)?.files.find((file) => file.filename === route.filename) ?? null;
234
+ },
235
+ workerConfig({ registryUrl, token, cacheDir }) {
236
+ const npmrc = `${cacheDir}/npmrc`;
237
+ return {
238
+ env: {
239
+ npm_config_registry: registryUrl,
240
+ npm_config_userconfig: npmrc,
241
+ npm_config_ignore_scripts: "true",
242
+ npm_config_cache: cacheDir,
243
+ npm_config_audit: "false",
244
+ npm_config_fund: "false",
245
+ npm_config_update_notifier: "false",
246
+ // The only registry the npm shim (npm-shim.mjs) sends a lockfile to.
247
+ WARDBY_NPM_PLAN_REGISTRY: registryUrl,
248
+ },
249
+ files: [{ path: npmrc, content: `${registryUrl.replace(/^https?:/, "")}:_authToken=${token}\n`, mode: 0o600 }],
250
+ };
251
+ },
252
+ };
@@ -0,0 +1,4 @@
1
+ import { type RegistryAdapter } from "./types.js";
2
+ export declare function normalizePypiName(name: string): string;
3
+ export declare function requiresDist(metadataText: string): string[];
4
+ export declare const pypiAdapter: RegistryAdapter;