@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
@@ -10,6 +10,63 @@
10
10
  import { createRemoteJWKSet, jwtVerify, errors as joseErrors } from "jose";
11
11
  import { AudienceError, NotSupportedError } from "./types.js";
12
12
  import { requireSubject } from "./subject.js";
13
+ import { ROLE_NAMES, isRoleName } from "../../mcp/auth/resource-server.js";
14
+ /**
15
+ * Parses AUTH_ROLE_MAP. Whitespace around entries and around `=` is ignored;
16
+ * an unknown wardby role, or a malformed or empty entry, is a startup error.
17
+ * The LAST `=` splits an entry (wardby role names never contain one).
18
+ */
19
+ export function parseRoleMap(value) {
20
+ const map = new Map();
21
+ const entries = value.split(",").map((e) => e.trim());
22
+ if (entries.every((e) => !e))
23
+ throw new Error("AUTH_ROLE_MAP is empty.");
24
+ for (const entry of entries) {
25
+ if (!entry)
26
+ continue;
27
+ const at = entry.lastIndexOf("=");
28
+ const idpValue = at > 0 ? entry.slice(0, at).trim() : "";
29
+ const role = at > 0 ? entry.slice(at + 1).trim() : "";
30
+ if (!idpValue || !role)
31
+ throw new Error(`AUTH_ROLE_MAP entry "${entry}" must look like idpValue=wardbyRole.`);
32
+ if (!isRoleName(role))
33
+ throw new Error(`AUTH_ROLE_MAP maps "${idpValue}" to unknown role "${role}" (known: ${ROLE_NAMES.join(", ")}).`);
34
+ map.set(idpValue, [...new Set([...(map.get(idpValue) ?? []), role])]);
35
+ }
36
+ return map;
37
+ }
38
+ /**
39
+ * Resolves `claim` in a verified payload: an exact top-level name wins (claim
40
+ * names may themselves contain dots and slashes), otherwise a dotted path.
41
+ */
42
+ function resolveClaim(payload, claim) {
43
+ if (Object.prototype.hasOwnProperty.call(payload, claim))
44
+ return payload[claim];
45
+ if (!claim.includes("."))
46
+ return undefined;
47
+ let value = payload;
48
+ for (const part of claim.split(".")) {
49
+ if (typeof value !== "object" || value === null || Array.isArray(value))
50
+ return undefined;
51
+ if (!Object.prototype.hasOwnProperty.call(value, part))
52
+ return undefined;
53
+ value = value[part];
54
+ }
55
+ return value;
56
+ }
57
+ /**
58
+ * Maps a resolved claim (a string, or an array of strings) through the role
59
+ * map; unmapped values are ignored, and any other shape yields no roles.
60
+ */
61
+ function rolesFromClaim(value, map) {
62
+ const values = typeof value === "string" ? [value] : Array.isArray(value) ? value : [];
63
+ const roles = new Set();
64
+ for (const v of values)
65
+ if (typeof v === "string")
66
+ for (const r of map.get(v) ?? [])
67
+ roles.add(r);
68
+ return [...roles];
69
+ }
13
70
  /**
14
71
  * Every spelling of the configured audience a token may legitimately carry.
15
72
  *
@@ -53,9 +110,16 @@ export class DelegatingAuthProvider {
53
110
  config;
54
111
  audiences;
55
112
  jwks;
113
+ roleMap;
56
114
  constructor(config, jwks) {
57
115
  if (!config.issuer || !config.audience)
58
116
  throw new Error("AUTH_ISSUER and AUTH_AUDIENCE are required.");
117
+ // Half a configuration would silently give everyone no roles while
118
+ // looking configured — refuse to start instead.
119
+ if ((config.roleClaim !== undefined || config.roleMap !== undefined) && (!config.roleClaim || !config.roleMap))
120
+ throw new Error("AUTH_ROLE_CLAIM and AUTH_ROLE_MAP must be set together (or both left unset).");
121
+ if (config.roleMap)
122
+ this.roleMap = parseRoleMap(config.roleMap);
59
123
  this.config = config;
60
124
  this.audiences = audienceCandidates(config.audience);
61
125
  if (jwks) {
@@ -80,6 +144,13 @@ export class DelegatingAuthProvider {
80
144
  email: typeof payload.email === "string" ? payload.email : undefined,
81
145
  roles: Array.isArray(payload.roles) ? payload.roles.map(String) : [],
82
146
  scopes: scopesFromClaim(payload.scope ?? payload.scp),
147
+ // Read from the bearer token validated above for this issuer and
148
+ // audience (signature, expiry too); the IdP should mint that audience
149
+ // only into access tokens. Unconfigured, every caller has no roles
150
+ // and privileged operations are refused.
151
+ wardbyRoles: this.config.roleClaim && this.roleMap
152
+ ? rolesFromClaim(resolveClaim(payload, this.config.roleClaim), this.roleMap)
153
+ : [],
83
154
  };
84
155
  }
85
156
  catch (err) {
@@ -3,9 +3,11 @@
3
3
  * kind. Both adapters implement the same AuthProvider (incl. verifyBearer),
4
4
  * so the resource-server middleware and tool handlers never branch on mode.
5
5
  */
6
- import type { PrismaClient } from "@prisma/client";
6
+ import type { PrismaClient } from "#prisma";
7
7
  import type { AuthProviderKind, AuthConfig } from "../../config/providers.js";
8
8
  import type { AuthProvider } from "./types.js";
9
9
  export { DelegatingAuthProvider } from "./delegating.js";
10
10
  export { SelfHostedAuthProvider } from "./self-hosted.js";
11
+ /** Configuration that is valid but almost certainly not what the operator meant. */
12
+ export declare function authConfigWarnings(kind: AuthProviderKind, config: AuthConfig): string[];
11
13
  export declare function buildAuthProvider(kind: AuthProviderKind, config: AuthConfig, db: PrismaClient): AuthProvider;
@@ -1,8 +1,19 @@
1
1
  import { DelegatingAuthProvider } from "./delegating.js";
2
2
  import { SelfHostedAuthProvider } from "./self-hosted.js";
3
+ import { logger } from "../../core/logger.js";
3
4
  export { DelegatingAuthProvider } from "./delegating.js";
4
5
  export { SelfHostedAuthProvider } from "./self-hosted.js";
6
+ /** Configuration that is valid but almost certainly not what the operator meant. */
7
+ export function authConfigWarnings(kind, config) {
8
+ const warnings = [];
9
+ if (kind === "self-hosted" && (config.roleClaim || config.roleMap))
10
+ warnings.push("AUTH_ROLE_CLAIM / AUTH_ROLE_MAP are set but ignored with AUTH_PROVIDER=self-hosted: self-hosted roles " +
11
+ "come from the database (`wardby auth user grant --subject <s> --role <role>`).");
12
+ return warnings;
13
+ }
5
14
  export function buildAuthProvider(kind, config, db) {
15
+ for (const warning of authConfigWarnings(kind, config))
16
+ logger.warn({ module: "auth" }, warning);
6
17
  if (kind === "self-hosted") {
7
18
  if (!config.audience || !config.signingKey || !config.credentialHashKey) {
8
19
  throw new Error("AUTH_AUDIENCE, AUTH_SIGNING_KEY, and AUTH_CREDENTIAL_HASH_KEY are required.");
@@ -1,4 +1,4 @@
1
- import type { PrismaClient } from "@prisma/client";
1
+ import type { PrismaClient } from "#prisma";
2
2
  import type { AuthProfile, AuthProvider, AuthTokens, VerifiedToken } from "./types.js";
3
3
  import { Credentials } from "../../mcp/auth/self-hosted/credentials.js";
4
4
  import { Sessions } from "../../mcp/auth/self-hosted/session.js";
@@ -34,7 +34,7 @@ export declare class SelfHostedAuthProvider implements AuthProvider {
34
34
  consentPage(sessionToken: string, interactionId: string): Promise<{
35
35
  interaction: {
36
36
  client: {
37
- metadata: import("@prisma/client/runtime/library").JsonValue;
37
+ metadata: import("@prisma/client/runtime/client").JsonValue;
38
38
  createdAt: Date;
39
39
  clientId: string;
40
40
  };
@@ -42,15 +42,16 @@ export declare class SelfHostedAuthProvider implements AuthProvider {
42
42
  id: string;
43
43
  createdAt: Date;
44
44
  state: string | null;
45
+ clientId: string;
45
46
  expiresAt: Date;
46
47
  resource: string;
47
- clientId: string;
48
48
  consumedAt: Date | null;
49
49
  redirectUri: string;
50
50
  codeChallenge: string;
51
51
  requestedScope: string;
52
52
  };
53
53
  challenge: string;
54
+ scopes: string[];
54
55
  }>;
55
56
  consent(sessionToken: string, interactionId: string, challenge: string, approve: boolean): Promise<string>;
56
57
  handleToken(params: TokenParams): Promise<TokenResult>;
@@ -3,7 +3,7 @@ import { SignJWT, jwtVerify } from "jose";
3
3
  import { z } from "zod";
4
4
  import { NotSupportedError } from "./types.js";
5
5
  import { requireSubject } from "./subject.js";
6
- import { SCOPES_SUPPORTED } from "../../mcp/auth/resource-server.js";
6
+ import { SCOPES_SUPPORTED, limitScope } from "../../mcp/auth/resource-server.js";
7
7
  import { canonicalUrl } from "../../mcp/transport/http-limits.js";
8
8
  import { Credentials, DAY, decodeKey, lockUser } from "../../mcp/auth/self-hosted/credentials.js";
9
9
  import { Sessions } from "../../mcp/auth/self-hosted/session.js";
@@ -106,7 +106,7 @@ export class SelfHostedAuthProvider {
106
106
  const metadata = client?.metadata;
107
107
  if (!metadata?.redirect_uris?.includes(p.redirectUri) || !metadata.grant_types?.includes("authorization_code"))
108
108
  throw new Error("Invalid client or redirect.");
109
- const scope = [...new Set(p.scope.split(/\s+/).filter((s) => SCOPES_SUPPORTED.includes(s)))].join(" ");
109
+ const scope = limitScope(p.scope, SCOPES_SUPPORTED);
110
110
  const request = await this.db.oAuthAuthorizationRequest.create({
111
111
  data: {
112
112
  clientId: p.clientId,
@@ -129,7 +129,10 @@ export class SelfHostedAuthProvider {
129
129
  if (!interaction || interaction.consumedAt || interaction.expiresAt.getTime() <= Date.now())
130
130
  throw new Error("Invalid interaction.");
131
131
  const challenge = await this.sessions.challenge("consent", session.sessionId, interactionId);
132
- return { interaction, challenge };
132
+ // Exactly what approving issues: the stored (supported-only) request.
133
+ // It's fixed when the interaction is created, so what the page shows
134
+ // can't drift from what consent() puts on the code.
135
+ return { interaction, challenge, scopes: interaction.requestedScope.split(" ").filter(Boolean) };
133
136
  }
134
137
  async consent(sessionToken, interactionId, challenge, approve) {
135
138
  const first = await this.sessions.get(sessionToken);
@@ -339,7 +342,16 @@ export class SelfHostedAuthProvider {
339
342
  family.clientId !== payload.client_id ||
340
343
  payload.scope !== family.scope)
341
344
  throw new Error("Invalid token.");
342
- return { subject, scopes: family.scope.split(/\s+/).filter(Boolean), roles: [] };
345
+ // Roles are read from the database on every request (no caching), so a
346
+ // grant or revocation applies to live tokens immediately. Scopes stay
347
+ // exactly what the user consented to; roles decide whether the
348
+ // privileged ones are honoured (resource-server.ts requireScope).
349
+ return {
350
+ subject,
351
+ scopes: family.scope.split(/\s+/).filter(Boolean),
352
+ roles: [],
353
+ wardbyRoles: family.user.roles ?? [],
354
+ };
343
355
  }
344
356
  async cleanup() {
345
357
  const now = new Date();
@@ -32,6 +32,12 @@ export interface VerifiedToken {
32
32
  scopes: string[];
33
33
  email?: string;
34
34
  roles?: string[];
35
+ /**
36
+ * wardby roles (not the IdP's raw `roles`), decided by the provider at
37
+ * verification time: self-hosted reads AuthUser.roles live; delegating
38
+ * maps the configured signed claim through AUTH_ROLE_MAP. Absent = none.
39
+ */
40
+ wardbyRoles?: string[];
35
41
  }
36
42
  export interface AuthTokens {
37
43
  idToken: string;
@@ -12,6 +12,9 @@ export declare class MemoryProxyLedger implements ProxyLedger {
12
12
  release(requestId: string, upstreamStatus: number): Promise<void>;
13
13
  markUncertain(requestId: string, upstreamStatus?: number): Promise<void>;
14
14
  cancelSession(sessionId: string): Promise<void>;
15
+ budgetExhausted(sessionId: string): Promise<boolean>;
16
+ recordUpstreamFailure(sessionId: string, code: string): Promise<void>;
17
+ upstreamFailure(sessionId: string): Promise<string | null>;
15
18
  getRequest(requestId: string): Promise<ProxyRequest | null>;
16
19
  private requiredRequest;
17
20
  }
@@ -1,5 +1,11 @@
1
1
  function cloneSession(session) {
2
- return { ...session, allowedModels: [...session.allowedModels], deadlineAt: new Date(session.deadlineAt) };
2
+ return {
3
+ ...session,
4
+ allowedModels: [...session.allowedModels],
5
+ deadlineAt: new Date(session.deadlineAt),
6
+ budgetExhaustedAt: session.budgetExhaustedAt ? new Date(session.budgetExhaustedAt) : null,
7
+ upstreamFailure: session.upstreamFailure ?? null,
8
+ };
3
9
  }
4
10
  function cloneRequest(request) {
5
11
  return {
@@ -48,8 +54,10 @@ export class MemoryProxyLedger {
48
54
  if (request.status === "reserved" || request.status === "uncertain")
49
55
  committedOrHeld += request.reservationUsd;
50
56
  }
51
- if (committedOrHeld + input.reservationUsd >= session.budgetUsd)
57
+ if (committedOrHeld + input.reservationUsd >= session.budgetUsd) {
58
+ session.budgetExhaustedAt ??= new Date(input.now);
52
59
  return { outcome: "budget_exhausted" };
60
+ }
53
61
  const request = { ...input, pricing: { ...input.pricing }, status: "reserved" };
54
62
  this.requests.set(request.id, request);
55
63
  this.requestBySessionKey.set(lookupKey, request.id);
@@ -90,6 +98,17 @@ export class MemoryProxyLedger {
90
98
  if (session)
91
99
  session.status = "cancelled";
92
100
  }
101
+ async budgetExhausted(sessionId) {
102
+ return Boolean(this.sessions.get(sessionId)?.budgetExhaustedAt);
103
+ }
104
+ async recordUpstreamFailure(sessionId, code) {
105
+ const session = this.sessions.get(sessionId);
106
+ if (session)
107
+ session.upstreamFailure ??= code;
108
+ }
109
+ async upstreamFailure(sessionId) {
110
+ return this.sessions.get(sessionId)?.upstreamFailure ?? null;
111
+ }
93
112
  async getRequest(requestId) {
94
113
  const request = this.requests.get(requestId);
95
114
  return request ? cloneRequest(request) : null;
@@ -8,8 +8,12 @@ export declare function fingerprintRequest(body: string): string;
8
8
  export interface TerminalUsage {
9
9
  terminal: boolean;
10
10
  usage?: ProxyUsage;
11
+ /** Set when the upstream ended the response as failed or incomplete: its error code. */
12
+ failure?: string;
11
13
  }
12
14
  export declare function terminalUsageFromSseFrame(frame: string): TerminalUsage;
15
+ /** The safe error code in an upstream error body (a stream event or a rejected response's JSON), if any. */
16
+ export declare function safeUpstreamErrorCode(record: Record<string, unknown>): string | undefined;
13
17
  /** Accumulates Anthropic's split input/output usage until message_stop. */
14
18
  export declare class AnthropicSseUsageTracker {
15
19
  private startUsage?;
@@ -74,13 +74,36 @@ export function terminalUsageFromSseFrame(frame) {
74
74
  if (!event || typeof event !== "object")
75
75
  return { terminal: false };
76
76
  const record = event;
77
- if (record.type !== "response.completed" && record.type !== "response.failed")
77
+ // A stream-level error event (quota, rate limit, server error) ends the response with no usage.
78
+ if (record.type === "error")
79
+ return { terminal: true, failure: upstreamErrorCode(record) };
80
+ const failed = record.type === "response.failed" || record.type === "response.incomplete";
81
+ if (record.type !== "response.completed" && !failed)
78
82
  return { terminal: false };
79
83
  const response = record.response;
80
84
  if (!response || typeof response !== "object")
81
85
  throw new Error("invalid_upstream_terminal_event");
82
86
  const usage = response.usage;
83
- return { terminal: true, usage: usage == null ? undefined : parseAuthoritativeUsage(usage) };
87
+ return {
88
+ terminal: true,
89
+ usage: usage == null ? undefined : parseAuthoritativeUsage(usage),
90
+ ...(failed ? { failure: upstreamErrorCode(response) } : {}),
91
+ };
92
+ }
93
+ const SAFE_ERROR_CODE = /^[A-Za-z0-9_.-]{1,64}$/;
94
+ /** The upstream's own error code, never its message: codes are fixed identifiers, messages can echo input. */
95
+ function upstreamErrorCode(record) {
96
+ return safeUpstreamErrorCode(record) ?? "unknown";
97
+ }
98
+ /** The safe error code in an upstream error body (a stream event or a rejected response's JSON), if any. */
99
+ export function safeUpstreamErrorCode(record) {
100
+ const nested = (key) => {
101
+ const value = record[key];
102
+ return value && typeof value === "object" ? value : undefined;
103
+ };
104
+ const candidates = [nested("error")?.code, nested("error")?.type, nested("incomplete_details")?.reason, record.code];
105
+ const code = candidates.find((value) => typeof value === "string" && SAFE_ERROR_CODE.test(value));
106
+ return typeof code === "string" ? code : undefined;
84
107
  }
85
108
  function sseEvent(frame) {
86
109
  const lines = frame.split("\n");
@@ -118,6 +141,9 @@ export class AnthropicSseUsageTracker {
118
141
  if (!value)
119
142
  return { terminal: false };
120
143
  switch (value.type) {
144
+ // A stream-level error event (quota, rate limit, server error) ends the response with no usage.
145
+ case "error":
146
+ return { terminal: true, failure: upstreamErrorCode(value) };
121
147
  case "message_start": {
122
148
  if (this.startUsage || !value.message || typeof value.message !== "object") {
123
149
  throw new Error("invalid_upstream_sse");
@@ -1,9 +1,23 @@
1
- import type { PrismaClient } from "@prisma/client";
1
+ import type { PrismaClient } from "#prisma";
2
2
  import type { CreateProxySessionInput, ProxyLedger, ProxyRequest, ProxySession, ProxyUsage, ReserveProxyRequestInput, ReserveProxyRequestResult } from "./types.js";
3
3
  export type PrismaProxyLedgerDb = Pick<PrismaClient, "$transaction" | "$queryRaw" | "$executeRaw">;
4
+ /** How long a ledger transaction waits for a pool connection (Prisma's
5
+ * default is 2 s). A model request fails outright when this runs out, so it
6
+ * gets more room than the default; its pool is also kept apart from the
7
+ * package registry's (coding-proxy/main.ts). */
8
+ export declare const LEDGER_TRANSACTION_MAX_WAIT_MS = 5000;
9
+ /** How long a ledger transaction may run once started (Prisma's default is
10
+ * 5 s). Each is a handful of small queries, but a proxy busy with package
11
+ * installs (the registry's dependency walk parses large npm documents on the
12
+ * same event loop) can stall between them; a live lockfile install expired
13
+ * one ("A commit cannot be executed on an expired transaction") and failed
14
+ * the model request. The row lock it holds is per session, so a longer
15
+ * window only delays that run's own next request. */
16
+ export declare const LEDGER_TRANSACTION_TIMEOUT_MS = 20000;
4
17
  export declare class PrismaProxyLedger implements ProxyLedger {
5
18
  private readonly db;
6
19
  constructor(db: PrismaProxyLedgerDb);
20
+ private transaction;
7
21
  createSession(input: CreateProxySessionInput): Promise<void>;
8
22
  findSessionByCapabilityHash(capabilityHash: string): Promise<ProxySession | null>;
9
23
  reserve(input: ReserveProxyRequestInput): Promise<ReserveProxyRequestResult>;
@@ -11,5 +25,8 @@ export declare class PrismaProxyLedger implements ProxyLedger {
11
25
  release(requestId: string, upstreamStatus: number): Promise<void>;
12
26
  markUncertain(requestId: string, upstreamStatus?: number): Promise<void>;
13
27
  cancelSession(sessionId: string): Promise<void>;
28
+ budgetExhausted(sessionId: string): Promise<boolean>;
29
+ recordUpstreamFailure(sessionId: string, code: string): Promise<void>;
30
+ upstreamFailure(sessionId: string): Promise<string | null>;
14
31
  getRequest(requestId: string): Promise<ProxyRequest | null>;
15
32
  }
@@ -1,3 +1,16 @@
1
+ /** How long a ledger transaction waits for a pool connection (Prisma's
2
+ * default is 2 s). A model request fails outright when this runs out, so it
3
+ * gets more room than the default; its pool is also kept apart from the
4
+ * package registry's (coding-proxy/main.ts). */
5
+ export const LEDGER_TRANSACTION_MAX_WAIT_MS = 5_000;
6
+ /** How long a ledger transaction may run once started (Prisma's default is
7
+ * 5 s). Each is a handful of small queries, but a proxy busy with package
8
+ * installs (the registry's dependency walk parses large npm documents on the
9
+ * same event loop) can stall between them; a live lockfile install expired
10
+ * one ("A commit cannot be executed on an expired transaction") and failed
11
+ * the model request. The row lock it holds is per session, so a longer
12
+ * window only delays that run's own next request. */
13
+ export const LEDGER_TRANSACTION_TIMEOUT_MS = 20_000;
1
14
  function sessionFromRow(row) {
2
15
  if (!Array.isArray(row.allowedModels) || !row.allowedModels.every((model) => typeof model === "string")) {
3
16
  throw new Error("invalid_proxy_session_models");
@@ -15,6 +28,9 @@ function sessionFromRow(row) {
15
28
  deadlineAt: row.deadlineAt,
16
29
  budgetUsd: Number(row.budgetUsd),
17
30
  status: row.status,
31
+ registryTokenHash: row.registryTokenHash,
32
+ budgetExhaustedAt: row.budgetExhaustedAt ?? null,
33
+ upstreamFailure: row.upstreamFailure ?? null,
18
34
  };
19
35
  }
20
36
  function requestFromRow(row) {
@@ -57,26 +73,33 @@ export class PrismaProxyLedger {
57
73
  constructor(db) {
58
74
  this.db = db;
59
75
  }
76
+ transaction(fn) {
77
+ return this.db.$transaction(fn, {
78
+ maxWait: LEDGER_TRANSACTION_MAX_WAIT_MS,
79
+ timeout: LEDGER_TRANSACTION_TIMEOUT_MS,
80
+ });
81
+ }
60
82
  async createSession(input) {
61
83
  const models = JSON.stringify(input.allowedModels);
62
84
  await this.db.$executeRaw `
63
85
  INSERT INTO "CodingProxySession"
64
86
  ("id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt",
65
- "budgetUsd", "status", "createdAt", "updatedAt")
87
+ "budgetUsd", "status", "registryTokenHash", "createdAt", "updatedAt")
66
88
  VALUES
67
89
  (${input.id}, ${input.runId}, ${input.capabilityHash}, ${input.credentialRef}, ${input.protocol}, ${models}::jsonb,
68
- ${input.deadlineAt}, ${input.budgetUsd}, 'active', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
90
+ ${input.deadlineAt}, ${input.budgetUsd}, 'active', ${input.registryTokenHash}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
69
91
  `;
70
92
  }
71
93
  async findSessionByCapabilityHash(capabilityHash) {
72
94
  const rows = await this.db.$queryRaw `
73
- SELECT "id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt", "budgetUsd", "status"
95
+ SELECT "id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt", "budgetUsd", "status", "registryTokenHash",
96
+ "budgetExhaustedAt", "upstreamFailure"
74
97
  FROM "CodingProxySession" WHERE "capabilityHash" = ${capabilityHash}
75
98
  `;
76
99
  return rows[0] ? sessionFromRow(rows[0]) : null;
77
100
  }
78
101
  async reserve(input) {
79
- return this.db.$transaction(async (tx) => {
102
+ return this.transaction(async (tx) => {
80
103
  const sessions = await tx.$queryRaw `
81
104
  SELECT "id", "runId", "capabilityHash", "credentialRef", "protocol", "allowedModels", "deadlineAt", "budgetUsd", "status"
82
105
  FROM "CodingProxySession" WHERE "id" = ${input.sessionId} FOR UPDATE
@@ -107,6 +130,10 @@ export class PrismaProxyLedger {
107
130
  FROM "CodingProxyRequest" WHERE "sessionId" = ${input.sessionId}
108
131
  `;
109
132
  if (Number(totals[0]?.held ?? 0) + input.reservationUsd >= Number(row.budgetUsd)) {
133
+ await tx.$executeRaw `
134
+ UPDATE "CodingProxySession" SET "budgetExhaustedAt" = COALESCE("budgetExhaustedAt", ${input.now})
135
+ WHERE "id" = ${input.sessionId}
136
+ `;
110
137
  return { outcome: "budget_exhausted" };
111
138
  }
112
139
  const pricing = JSON.stringify({
@@ -132,7 +159,7 @@ export class PrismaProxyLedger {
132
159
  });
133
160
  }
134
161
  async complete(requestId, usage, actualCostUsd, upstreamStatus) {
135
- return this.db.$transaction(async (tx) => {
162
+ return this.transaction(async (tx) => {
136
163
  const initial = await requestById(tx, requestId);
137
164
  if (!initial)
138
165
  throw new Error("unknown_proxy_request");
@@ -207,6 +234,24 @@ export class PrismaProxyLedger {
207
234
  WHERE "id" = ${sessionId} AND "status" = 'active'
208
235
  `;
209
236
  }
237
+ async budgetExhausted(sessionId) {
238
+ const rows = await this.db.$queryRaw `
239
+ SELECT "id" FROM "CodingProxySession" WHERE "id" = ${sessionId} AND "budgetExhaustedAt" IS NOT NULL
240
+ `;
241
+ return rows.length > 0;
242
+ }
243
+ async recordUpstreamFailure(sessionId, code) {
244
+ await this.db.$executeRaw `
245
+ UPDATE "CodingProxySession" SET "upstreamFailure" = ${code}, "updatedAt" = CURRENT_TIMESTAMP
246
+ WHERE "id" = ${sessionId} AND "upstreamFailure" IS NULL
247
+ `;
248
+ }
249
+ async upstreamFailure(sessionId) {
250
+ const rows = await this.db.$queryRaw `
251
+ SELECT "upstreamFailure" FROM "CodingProxySession" WHERE "id" = ${sessionId}
252
+ `;
253
+ return rows[0]?.upstreamFailure ?? null;
254
+ }
210
255
  getRequest(requestId) {
211
256
  return requestById(this.db, requestId);
212
257
  }
@@ -4,6 +4,12 @@ export declare const PROXY_MAX_BODY_BYTES: number;
4
4
  export declare const PROXY_MAX_OUTPUT_TOKENS = 1000000;
5
5
  export declare const PROXY_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
6
6
  export declare const CLAUDE_CODE_ANTHROPIC_BETAS: readonly ["claude-code-20250219", "context-management-2025-06-27", "effort-2025-11-24", "interleaved-thinking-2025-05-14", "mid-conversation-system-2026-04-07", "prompt-caching-scope-2026-01-05", "thinking-token-count-2026-05-13"];
7
+ /**
8
+ * The longest run_command timeout the Claude tool runner offers the model (its MAX_TIMEOUT_MS,
9
+ * src/claude-tool-runner/command.mjs; the proxy tests keep the two equal). A lower limit here let the
10
+ * command run, then refused the next request, which replays that tool call, as unsupported.
11
+ */
12
+ export declare const MAX_COMMAND_TIMEOUT_MS = 120000;
7
13
  export declare class CodingProxyError extends Error {
8
14
  readonly status: number;
9
15
  readonly code: string;
@@ -51,6 +57,7 @@ export interface CodingProxyOptions {
51
57
  pricing?: (model: string, protocol: ProxyProtocol) => ModelPricing;
52
58
  pricingVersion?: string;
53
59
  }
60
+ export declare function capabilityHash(capability: string): string;
54
61
  export declare class CodingProxy {
55
62
  private readonly ledger;
56
63
  private readonly credentials;
@@ -64,6 +71,10 @@ export declare class CodingProxy {
64
71
  constructor(options: CodingProxyOptions);
65
72
  createSession(input: CreateCodingProxySession): Promise<CreatedCodingProxySession>;
66
73
  cancelSession(sessionId: string): Promise<void>;
74
+ /** Whether this session's run was refused a model request for budget. */
75
+ budgetExhausted(sessionId: string): Promise<boolean>;
76
+ /** The code of the first upstream failure relayed for this session's run, if any. */
77
+ upstreamFailure(sessionId: string): Promise<string | null>;
67
78
  execute(input: ExecuteProxyRequest, sink: ProxyResponseSink): Promise<void>;
68
79
  private authenticate;
69
80
  private trackActive;
@@ -71,5 +82,7 @@ export declare class CodingProxy {
71
82
  private complete;
72
83
  private forwardJson;
73
84
  private forwardStream;
85
+ /** Best effort: the executor reads it to name the failure; it must never break the relay. */
86
+ private recordUpstreamFailure;
74
87
  private markUncertain;
75
88
  }