@wardby/cli 0.2.1 → 0.4.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 (555) hide show
  1. package/.env.example +53 -4
  2. package/README.md +52 -13
  3. package/dist/claude-coding-worker/driver.d.ts +6 -1
  4. package/dist/claude-coding-worker/driver.js +27 -1
  5. package/dist/claude-coding-worker/main.js +2 -0
  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 +14 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +284 -36
  12. package/dist/coding/base-commit.d.ts +6 -0
  13. package/dist/coding/base-commit.js +12 -0
  14. package/dist/coding/collect-exclude.d.ts +25 -0
  15. package/dist/coding/collect-exclude.js +76 -0
  16. package/dist/coding/profile.d.ts +67 -22
  17. package/dist/coding/profile.js +58 -25
  18. package/dist/coding/protected-path-wording.d.ts +24 -0
  19. package/dist/coding/protected-path-wording.js +55 -0
  20. package/dist/coding/protected-paths.d.ts +31 -0
  21. package/dist/coding/protected-paths.js +48 -0
  22. package/dist/coding/protocol.d.ts +91 -4
  23. package/dist/coding/protocol.js +123 -12
  24. package/dist/coding/provider.d.ts +6 -1
  25. package/dist/coding/provider.js +16 -9
  26. package/dist/coding/registry/adapters.d.ts +2 -0
  27. package/dist/coding/registry/adapters.js +9 -0
  28. package/dist/coding/registry/allowlist.d.ts +9 -0
  29. package/dist/coding/registry/allowlist.js +28 -0
  30. package/dist/coding/registry/json-scan.d.ts +55 -0
  31. package/dist/coding/registry/json-scan.js +191 -0
  32. package/dist/coding/registry/lockfiles.d.ts +6 -0
  33. package/dist/coding/registry/lockfiles.js +89 -0
  34. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  35. package/dist/coding/registry/npm-lockfile.js +73 -0
  36. package/dist/coding/registry/npm-plan.d.ts +29 -0
  37. package/dist/coding/registry/npm-plan.js +289 -0
  38. package/dist/coding/registry/npm.d.ts +7 -0
  39. package/dist/coding/registry/npm.js +252 -0
  40. package/dist/coding/registry/pypi.d.ts +4 -0
  41. package/dist/coding/registry/pypi.js +210 -0
  42. package/dist/coding/registry/report.d.ts +37 -0
  43. package/dist/coding/registry/report.js +31 -0
  44. package/dist/coding/registry/token.d.ts +4 -0
  45. package/dist/coding/registry/token.js +7 -0
  46. package/dist/coding/registry/types.d.ts +286 -0
  47. package/dist/coding/registry/types.js +19 -0
  48. package/dist/coding/registry/worker-config.d.ts +14 -0
  49. package/dist/coding/registry/worker-config.js +31 -0
  50. package/dist/coding/services/builtins.d.ts +22 -0
  51. package/dist/coding/services/builtins.js +85 -0
  52. package/dist/coding/services/catalog.d.ts +443 -0
  53. package/dist/coding/services/catalog.js +159 -0
  54. package/dist/coding/services/declaration.d.ts +11 -0
  55. package/dist/coding/services/declaration.js +114 -0
  56. package/dist/coding/services/note.d.ts +8 -0
  57. package/dist/coding/services/note.js +15 -0
  58. package/dist/coding/services/resolve.d.ts +25 -0
  59. package/dist/coding/services/resolve.js +50 -0
  60. package/dist/coding/services/wording.d.ts +25 -0
  61. package/dist/coding/services/wording.js +70 -0
  62. package/dist/coding-proxy/main.js +24 -4
  63. package/dist/coding-worker/artifact.d.ts +6 -0
  64. package/dist/coding-worker/debug-trace.d.ts +37 -0
  65. package/dist/coding-worker/debug-trace.js +117 -0
  66. package/dist/coding-worker/driver.d.ts +13 -1
  67. package/dist/coding-worker/driver.js +102 -13
  68. package/dist/coding-worker/errors.js +8 -0
  69. package/dist/coding-worker/main.js +10 -2
  70. package/dist/coding-worker/sdk.d.ts +2 -2
  71. package/dist/coding-worker/sdk.js +7 -2
  72. package/dist/coding-worker/types.d.ts +3 -0
  73. package/dist/config/providers.d.ts +66 -0
  74. package/dist/config/providers.js +137 -0
  75. package/dist/core/attribution.d.ts +101 -0
  76. package/dist/core/attribution.js +208 -0
  77. package/dist/core/budget-groups.d.ts +120 -10
  78. package/dist/core/budget-groups.js +133 -24
  79. package/dist/core/budget-wording.d.ts +22 -0
  80. package/dist/core/budget-wording.js +55 -0
  81. package/dist/core/coding-queue.d.ts +3 -0
  82. package/dist/core/coding-queue.js +6 -2
  83. package/dist/core/coding-service-status.d.ts +11 -0
  84. package/dist/core/coding-service-status.js +17 -0
  85. package/dist/core/cost-report.d.ts +88 -0
  86. package/dist/core/cost-report.js +248 -0
  87. package/dist/core/datastores.js +18 -2
  88. package/dist/core/db.d.ts +5 -1
  89. package/dist/core/db.js +8 -2
  90. package/dist/core/dispatch.d.ts +74 -7
  91. package/dist/core/dispatch.js +382 -47
  92. package/dist/core/engine-native.js +44 -14
  93. package/dist/core/glob.d.ts +10 -0
  94. package/dist/core/glob.js +33 -0
  95. package/dist/core/grants.d.ts +86 -0
  96. package/dist/core/grants.js +126 -0
  97. package/dist/core/host-events.d.ts +72 -0
  98. package/dist/core/host-events.js +597 -0
  99. package/dist/core/host-identity-links.d.ts +54 -0
  100. package/dist/core/host-identity-links.js +189 -0
  101. package/dist/core/host-status.d.ts +78 -0
  102. package/dist/core/host-status.js +228 -0
  103. package/dist/core/in-flight-runs.d.ts +8 -0
  104. package/dist/core/in-flight-runs.js +56 -0
  105. package/dist/core/issue-bridge.d.ts +60 -0
  106. package/dist/core/issue-bridge.js +189 -0
  107. package/dist/core/issue-dedupe.d.ts +70 -0
  108. package/dist/core/issue-dedupe.js +255 -0
  109. package/dist/core/issue-events.d.ts +42 -0
  110. package/dist/core/issue-events.js +155 -0
  111. package/dist/core/issue-status.d.ts +29 -0
  112. package/dist/core/issue-status.js +241 -0
  113. package/dist/core/issue-tracker-tools.d.ts +64 -0
  114. package/dist/core/issue-tracker-tools.js +850 -0
  115. package/dist/core/model-usage.d.ts +10 -0
  116. package/dist/core/model-usage.js +24 -0
  117. package/dist/core/provider-wording.d.ts +12 -0
  118. package/dist/core/provider-wording.js +44 -0
  119. package/dist/core/reconciler.d.ts +42 -2
  120. package/dist/core/reconciler.js +97 -2
  121. package/dist/core/repo-access.d.ts +99 -0
  122. package/dist/core/repo-access.js +136 -0
  123. package/dist/core/review-host-checks.d.ts +11 -0
  124. package/dist/core/review-host-checks.js +41 -0
  125. package/dist/core/review-host-tools.d.ts +47 -0
  126. package/dist/core/review-host-tools.js +354 -0
  127. package/dist/core/run-heartbeat.d.ts +27 -0
  128. package/dist/core/run-heartbeat.js +54 -0
  129. package/dist/core/run-pricing.d.ts +61 -0
  130. package/dist/core/run-pricing.js +56 -0
  131. package/dist/core/runner.d.ts +33 -8
  132. package/dist/core/runner.js +410 -62
  133. package/dist/core/scheduler.d.ts +4 -1
  134. package/dist/core/scheduler.js +3 -2
  135. package/dist/core/secrets.d.ts +11 -2
  136. package/dist/core/secrets.js +25 -6
  137. package/dist/core/self-defects.d.ts +80 -0
  138. package/dist/core/self-defects.js +180 -0
  139. package/dist/core/subagent-memory-tools.d.ts +1 -1
  140. package/dist/core/subagent-memory-tools.js +16 -3
  141. package/dist/core/tool-admin.d.ts +81 -0
  142. package/dist/core/tool-admin.js +129 -0
  143. package/dist/core/tool-names.d.ts +42 -0
  144. package/dist/core/tool-names.js +67 -0
  145. package/dist/core/untrusted-content.d.ts +32 -0
  146. package/dist/core/untrusted-content.js +72 -0
  147. package/dist/core/webhooks.d.ts +17 -2
  148. package/dist/core/webhooks.js +42 -3
  149. package/dist/generated/prisma/browser.d.ts +166 -0
  150. package/dist/generated/prisma/client.d.ts +166 -0
  151. package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
  152. package/dist/generated/prisma/enums.d.ts +13 -0
  153. package/dist/generated/prisma/enums.js +12 -1
  154. package/dist/generated/prisma/internal/class.d.ts +242 -0
  155. package/dist/generated/prisma/internal/class.js +4 -4
  156. package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
  157. package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
  158. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
  159. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
  160. package/dist/generated/prisma/models/Agent.d.ts +682 -1
  161. package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
  162. package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
  163. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  164. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  165. package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
  166. package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
  167. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
  168. package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
  169. package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
  170. package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
  171. package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
  172. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  173. package/dist/generated/prisma/models/CodingService.js +1 -0
  174. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  175. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  176. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  177. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  178. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  179. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  180. package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
  181. package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
  182. package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
  183. package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
  184. package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
  185. package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
  186. package/dist/generated/prisma/models/Principal.d.ts +455 -0
  187. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  188. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  189. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  190. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  191. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  192. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  193. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  194. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  195. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  196. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  197. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  198. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  199. package/dist/generated/prisma/models/Run.d.ts +1402 -82
  200. package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
  201. package/dist/generated/prisma/models/RunAttribution.js +1 -0
  202. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  203. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  204. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  205. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  206. package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
  207. package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
  208. package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
  209. package/dist/generated/prisma/models/RunModelUsage.js +1 -0
  210. package/dist/generated/prisma/models/Tool.d.ts +15 -3
  211. package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
  212. package/dist/generated/prisma/models/WorkItem.js +1 -0
  213. package/dist/generated/prisma/models.d.ts +22 -0
  214. package/dist/help/build.d.ts +1 -0
  215. package/dist/help/build.js +9 -0
  216. package/dist/help/catalog.d.ts +24 -0
  217. package/dist/help/catalog.js +160 -0
  218. package/dist/help/cli.d.ts +2 -0
  219. package/dist/help/cli.js +65 -0
  220. package/dist/help/runtime.d.ts +3 -0
  221. package/dist/help/runtime.js +44 -0
  222. package/dist/help/search.d.ts +9 -0
  223. package/dist/help/search.js +104 -0
  224. package/dist/help-index.json +999 -0
  225. package/dist/import/cli-args.js +3 -2
  226. package/dist/import/create.d.ts +5 -0
  227. package/dist/import/create.js +67 -13
  228. package/dist/import/index.js +19 -9
  229. package/dist/import/neutral-schema.d.ts +18 -18
  230. package/dist/knowledge/check.d.ts +13 -0
  231. package/dist/knowledge/check.js +69 -0
  232. package/dist/knowledge/cli.d.ts +14 -0
  233. package/dist/knowledge/cli.js +67 -0
  234. package/dist/knowledge/concept.d.ts +54 -0
  235. package/dist/knowledge/concept.js +78 -0
  236. package/dist/knowledge/note.d.ts +11 -0
  237. package/dist/knowledge/note.js +39 -0
  238. package/dist/knowledge/relevance.d.ts +11 -0
  239. package/dist/knowledge/relevance.js +14 -0
  240. package/dist/knowledge/span-hash.d.ts +3 -0
  241. package/dist/knowledge/span-hash.js +16 -0
  242. package/dist/mcp/auth/access.d.ts +72 -0
  243. package/dist/mcp/auth/access.js +58 -0
  244. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  245. package/dist/mcp/auth/grants-cli.js +518 -0
  246. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  247. package/dist/mcp/auth/host-account-cli.js +47 -0
  248. package/dist/mcp/auth/ownership.d.ts +25 -51
  249. package/dist/mcp/auth/ownership.js +19 -14
  250. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  251. package/dist/mcp/auth/repo-authorization.js +51 -0
  252. package/dist/mcp/auth/resource-server.d.ts +33 -2
  253. package/dist/mcp/auth/resource-server.js +99 -3
  254. package/dist/mcp/auth/self-hosted/browser.js +2 -2
  255. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  256. package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
  257. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  258. package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
  259. package/dist/mcp/context.d.ts +30 -1
  260. package/dist/mcp/errors.d.ts +25 -6
  261. package/dist/mcp/errors.js +98 -0
  262. package/dist/mcp/host-events/deliveries.d.ts +9 -0
  263. package/dist/mcp/host-events/deliveries.js +17 -0
  264. package/dist/mcp/host-events/github-ingress.d.ts +38 -0
  265. package/dist/mcp/host-events/github-ingress.js +83 -0
  266. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  267. package/dist/mcp/host-events/github-user-callback.js +50 -0
  268. package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
  269. package/dist/mcp/host-events/jira-ingress.js +92 -0
  270. package/dist/mcp/index.d.ts +10 -1
  271. package/dist/mcp/index.js +187 -19
  272. package/dist/mcp/server.js +32 -11
  273. package/dist/mcp/tools/agents.js +519 -50
  274. package/dist/mcp/tools/budget-groups.js +3 -3
  275. package/dist/mcp/tools/cost-report.d.ts +8 -0
  276. package/dist/mcp/tools/cost-report.js +60 -0
  277. package/dist/mcp/tools/datastore.js +22 -15
  278. package/dist/mcp/tools/grants.d.ts +2 -0
  279. package/dist/mcp/tools/grants.js +239 -0
  280. package/dist/mcp/tools/help.d.ts +5 -0
  281. package/dist/mcp/tools/help.js +67 -0
  282. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  283. package/dist/mcp/tools/host-accounts.js +114 -0
  284. package/dist/mcp/tools/issue-projects.d.ts +2 -0
  285. package/dist/mcp/tools/issue-projects.js +238 -0
  286. package/dist/mcp/tools/memory.d.ts +7 -1
  287. package/dist/mcp/tools/memory.js +5 -5
  288. package/dist/mcp/tools/model-catalog.d.ts +22 -0
  289. package/dist/mcp/tools/model-catalog.js +423 -0
  290. package/dist/mcp/tools/repositories.d.ts +2 -0
  291. package/dist/mcp/tools/repositories.js +182 -0
  292. package/dist/mcp/tools/runs.d.ts +6 -0
  293. package/dist/mcp/tools/runs.js +60 -6
  294. package/dist/mcp/tools/scheduling.js +3 -6
  295. package/dist/mcp/tools/secrets.js +18 -6
  296. package/dist/mcp/tools/services.d.ts +2 -0
  297. package/dist/mcp/tools/services.js +222 -0
  298. package/dist/mcp/tools/subagents.js +52 -16
  299. package/dist/mcp/tools/tools.d.ts +41 -0
  300. package/dist/mcp/tools/tools.js +201 -40
  301. package/dist/mcp/tools/trigger.js +65 -7
  302. package/dist/mcp/tools/webhooks.js +15 -4
  303. package/dist/mcp/transport/streamable-http.d.ts +15 -0
  304. package/dist/mcp/transport/streamable-http.js +55 -3
  305. package/dist/mcp/webhooks/ingress.d.ts +2 -1
  306. package/dist/mcp/webhooks/ingress.js +9 -2
  307. package/dist/providers/auth/delegating.d.ts +19 -0
  308. package/dist/providers/auth/delegating.js +71 -0
  309. package/dist/providers/auth/index.d.ts +2 -0
  310. package/dist/providers/auth/index.js +11 -0
  311. package/dist/providers/auth/self-hosted.d.ts +10 -2
  312. package/dist/providers/auth/self-hosted.js +55 -6
  313. package/dist/providers/auth/types.d.ts +6 -0
  314. package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
  315. package/dist/providers/coding-proxy/memory-ledger.js +31 -3
  316. package/dist/providers/coding-proxy/metering.d.ts +6 -1
  317. package/dist/providers/coding-proxy/metering.js +41 -4
  318. package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
  319. package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
  320. package/dist/providers/coding-proxy/proxy.d.ts +25 -2
  321. package/dist/providers/coding-proxy/proxy.js +561 -38
  322. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  323. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  324. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  325. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  326. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  327. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  328. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  329. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  330. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  331. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  332. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  333. package/dist/providers/coding-proxy/registry/store.js +70 -0
  334. package/dist/providers/coding-proxy/runtime.d.ts +17 -0
  335. package/dist/providers/coding-proxy/runtime.js +63 -0
  336. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  337. package/dist/providers/coding-proxy/server.d.ts +11 -0
  338. package/dist/providers/coding-proxy/server.js +163 -0
  339. package/dist/providers/coding-proxy/types.d.ts +33 -1
  340. package/dist/providers/coding-proxy/types.js +12 -1
  341. package/dist/providers/engine/types.d.ts +29 -0
  342. package/dist/providers/executor/build.d.ts +2 -2
  343. package/dist/providers/executor/composition.d.ts +3 -0
  344. package/dist/providers/executor/composition.js +28 -1
  345. package/dist/providers/executor/container.d.ts +134 -4
  346. package/dist/providers/executor/container.js +394 -37
  347. package/dist/providers/executor/dbos.d.ts +4 -3
  348. package/dist/providers/executor/dbos.js +7 -5
  349. package/dist/providers/executor/in-process.d.ts +2 -3
  350. package/dist/providers/executor/routing.d.ts +13 -0
  351. package/dist/providers/executor/routing.js +18 -0
  352. package/dist/providers/executor/types.d.ts +34 -0
  353. package/dist/providers/issue-tracker/adf.d.ts +31 -0
  354. package/dist/providers/issue-tracker/adf.js +181 -0
  355. package/dist/providers/issue-tracker/index.d.ts +5 -0
  356. package/dist/providers/issue-tracker/index.js +12 -0
  357. package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
  358. package/dist/providers/issue-tracker/jira-client.js +151 -0
  359. package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
  360. package/dist/providers/issue-tracker/jira-events.js +98 -0
  361. package/dist/providers/issue-tracker/jira.d.ts +116 -0
  362. package/dist/providers/issue-tracker/jira.js +502 -0
  363. package/dist/providers/issue-tracker/types.d.ts +269 -0
  364. package/dist/providers/issue-tracker/types.js +16 -0
  365. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  366. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  367. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  368. package/dist/providers/jobs/collect-prune.js +27 -0
  369. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  370. package/dist/providers/jobs/docker-isolation.js +213 -22
  371. package/dist/providers/jobs/docker-services.d.ts +35 -0
  372. package/dist/providers/jobs/docker-services.js +191 -0
  373. package/dist/providers/jobs/docker.d.ts +53 -2
  374. package/dist/providers/jobs/docker.js +307 -32
  375. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  376. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  377. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  378. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  379. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  380. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  381. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  382. package/dist/providers/jobs/kubernetes.d.ts +24 -2
  383. package/dist/providers/jobs/kubernetes.js +153 -22
  384. package/dist/providers/jobs/service-state.d.ts +22 -0
  385. package/dist/providers/jobs/service-state.js +17 -0
  386. package/dist/providers/jobs/types.d.ts +18 -0
  387. package/dist/providers/llm/anthropic.d.ts +3 -3
  388. package/dist/providers/llm/anthropic.js +3 -5
  389. package/dist/providers/llm/bedrock.d.ts +3 -3
  390. package/dist/providers/llm/bedrock.js +3 -8
  391. package/dist/providers/llm/catalog-lookup.d.ts +10 -0
  392. package/dist/providers/llm/catalog-lookup.js +15 -0
  393. package/dist/providers/llm/catalog-shipped.d.ts +18 -0
  394. package/dist/providers/llm/catalog-shipped.js +197 -0
  395. package/dist/providers/llm/catalog-store.d.ts +58 -0
  396. package/dist/providers/llm/catalog-store.js +138 -0
  397. package/dist/providers/llm/catalog-types.d.ts +66 -0
  398. package/dist/providers/llm/catalog-types.js +64 -0
  399. package/dist/providers/llm/catalog.d.ts +61 -0
  400. package/dist/providers/llm/catalog.js +147 -0
  401. package/dist/providers/llm/claude-messages.d.ts +5 -1
  402. package/dist/providers/llm/claude-messages.js +1 -0
  403. package/dist/providers/llm/claude-provider.d.ts +10 -12
  404. package/dist/providers/llm/claude-provider.js +15 -6
  405. package/dist/providers/llm/index.d.ts +9 -6
  406. package/dist/providers/llm/index.js +8 -5
  407. package/dist/providers/llm/openai.d.ts +14 -5
  408. package/dist/providers/llm/openai.js +24 -14
  409. package/dist/providers/llm/pricing-core.d.ts +5 -3
  410. package/dist/providers/llm/registration.js +8 -12
  411. package/dist/providers/llm/routing.d.ts +21 -11
  412. package/dist/providers/llm/routing.js +47 -13
  413. package/dist/providers/llm/types.d.ts +12 -0
  414. package/dist/providers/llm/types.js +8 -1
  415. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  416. package/dist/providers/review-host/diff-lines.js +59 -0
  417. package/dist/providers/review-host/github-events.d.ts +6 -0
  418. package/dist/providers/review-host/github-events.js +226 -0
  419. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  420. package/dist/providers/review-host/github-user-auth.js +128 -0
  421. package/dist/providers/review-host/github.d.ts +57 -0
  422. package/dist/providers/review-host/github.js +568 -0
  423. package/dist/providers/review-host/index.d.ts +12 -0
  424. package/dist/providers/review-host/index.js +30 -0
  425. package/dist/providers/review-host/review-format.d.ts +19 -0
  426. package/dist/providers/review-host/review-format.js +59 -0
  427. package/dist/providers/review-host/types.d.ts +309 -0
  428. package/dist/providers/review-host/types.js +24 -0
  429. package/dist/providers/vcs/git.d.ts +16 -6
  430. package/dist/providers/vcs/git.js +78 -34
  431. package/dist/providers/vcs/github.d.ts +94 -3
  432. package/dist/providers/vcs/github.js +223 -17
  433. package/dist/providers/vcs/types.d.ts +48 -4
  434. package/dist/quickstart/index.d.ts +2 -0
  435. package/dist/quickstart/index.js +36 -8
  436. package/dist/sandbox/fetch-policy.d.ts +20 -2
  437. package/dist/sandbox/fetch-policy.js +64 -3
  438. package/dist/sandbox/host-functions.d.ts +9 -1
  439. package/dist/sandbox/host-functions.js +12 -5
  440. package/dist/serve.js +8 -2
  441. package/dist/viewer/api-schema.d.ts +2757 -0
  442. package/dist/viewer/api-schema.js +165 -0
  443. package/dist/viewer/build-schemas.d.ts +2 -0
  444. package/dist/viewer/build-schemas.js +18 -0
  445. package/dist/viewer/event-bus.d.ts +38 -0
  446. package/dist/viewer/event-bus.js +232 -0
  447. package/dist/viewer/graph.d.ts +40 -0
  448. package/dist/viewer/graph.js +243 -0
  449. package/dist/viewer/http.d.ts +30 -0
  450. package/dist/viewer/http.js +133 -0
  451. package/dist/viewer/run-detail.d.ts +4 -0
  452. package/dist/viewer/run-detail.js +61 -0
  453. package/dist/wardby-bin.js +11 -0
  454. package/docs/README.md +40 -0
  455. package/docs/agent-recipes.md +383 -0
  456. package/docs/architecture-runtime.md +90 -0
  457. package/docs/assets/brand/wardby-icon-512.png +0 -0
  458. package/docs/assets/brand/wardby-icon.svg +16 -0
  459. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  460. package/docs/assets/brand/wardby-mascot.png +0 -0
  461. package/docs/assets/brand/wardby-mascot.svg +5 -0
  462. package/docs/assets/wardby-workflow.svg +106 -0
  463. package/docs/code-review-agents.md +508 -0
  464. package/docs/coding-agent-setup.md +175 -0
  465. package/docs/coding-packages.md +455 -0
  466. package/docs/coding-services.md +300 -0
  467. package/docs/coding-worker-byo-images.md +98 -0
  468. package/docs/coding-worker-isolation.md +1068 -0
  469. package/docs/getting-started-gke.md +632 -0
  470. package/docs/getting-started-identity-provider.md +319 -0
  471. package/docs/getting-started.md +147 -0
  472. package/docs/jira-agents.md +649 -0
  473. package/docs/knowledge.md +387 -0
  474. package/docs/models.md +221 -0
  475. package/docs/observability.md +53 -0
  476. package/docs/release-verification.md +66 -0
  477. package/docs/security-deployment.md +667 -0
  478. package/docs/viewer-api.md +142 -0
  479. package/help/admin-viewer.md +39 -0
  480. package/help/agent-recipes.md +173 -0
  481. package/help/architecture-agent.md +189 -0
  482. package/help/builder-agent.md +80 -0
  483. package/help/code-review-agents.md +37 -0
  484. package/help/coding-packages.md +31 -0
  485. package/help/coding-services.md +71 -0
  486. package/help/cost-attribution.md +67 -0
  487. package/help/creating-agents.md +90 -0
  488. package/help/deploy-gke.md +45 -0
  489. package/help/deployment-targets.md +39 -0
  490. package/help/errors/budget-group-exhausted.md +25 -0
  491. package/help/errors/docker-isolation-unsupported.md +26 -0
  492. package/help/errors/model-unavailable.md +63 -0
  493. package/help/errors/protected-path.md +45 -0
  494. package/help/errors/repo-access.md +27 -0
  495. package/help/errors/service-declaration-invalid.md +27 -0
  496. package/help/errors/service-declaration-unavailable.md +25 -0
  497. package/help/errors/service-launcher-unsupported.md +30 -0
  498. package/help/errors/service-not-allowed.md +27 -0
  499. package/help/errors/service-unknown.md +23 -0
  500. package/help/errors/service-unready.md +49 -0
  501. package/help/getting-started.md +32 -0
  502. package/help/github.md +48 -0
  503. package/help/identity-and-access.md +44 -0
  504. package/help/jira.md +135 -0
  505. package/help/knowledge.md +47 -0
  506. package/help/mcp.md +30 -0
  507. package/help/models.md +90 -0
  508. package/help/native-capabilities.md +30 -0
  509. package/help/observability.md +41 -0
  510. package/help/operating-agents.md +36 -0
  511. package/help/security.md +27 -0
  512. package/help/troubleshooting/budgets.md +32 -0
  513. package/help/troubleshooting/coding-workers.md +47 -0
  514. package/help/troubleshooting/repository-access.md +27 -0
  515. package/package.json +14 -3
  516. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  517. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  518. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  519. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  520. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  521. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  522. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  523. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  524. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  525. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  526. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  527. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  528. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  529. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  530. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  531. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  532. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  533. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  534. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  535. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  536. package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
  537. package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
  538. package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
  539. package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
  540. package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
  541. package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
  542. package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
  543. package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
  544. package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
  545. package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
  546. package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
  547. package/prisma/schema.prisma +663 -21
  548. package/dist/mcp/tools/models.d.ts +0 -8
  549. package/dist/mcp/tools/models.js +0 -15
  550. package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
  551. package/dist/providers/llm/pricing-anthropic.js +0 -30
  552. package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
  553. package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
  554. package/dist/providers/llm/pricing.d.ts +0 -30
  555. package/dist/providers/llm/pricing.js +0 -74
@@ -9,7 +9,7 @@ function extractCodingTask(req) {
9
9
  const parsed = CodingTaskOverrideSchema.safeParse(req.body.task);
10
10
  return parsed.success ? parsed.data : undefined;
11
11
  }
12
- export async function handleWebhookIngress(webhookId, req, db, executor) {
12
+ export async function handleWebhookIngress(webhookId, req, db, executor, issueTrackers) {
13
13
  const secret = extractSecret(req);
14
14
  if (!secret) {
15
15
  return { status: 401, body: { error: "invalid_secret", error_description: "No webhook secret presented." } };
@@ -18,11 +18,18 @@ export async function handleWebhookIngress(webhookId, req, db, executor) {
18
18
  if (req.body.task !== undefined && codingTask === undefined) {
19
19
  return { status: 400, body: { error: "invalid_task" } };
20
20
  }
21
- const result = await resolveWebhookRun(webhookId, secret, db, executor, codingTask);
21
+ // `wardbyIssue`, not `issue`: forwarded third-party payloads (GitHub, Jira)
22
+ // carry their own top-level `issue` object, which is not ours to read.
23
+ const result = await resolveWebhookRun(webhookId, secret, db, executor, codingTask, {
24
+ issue: req.body.wardbyIssue,
25
+ issueTrackers,
26
+ });
22
27
  if (result.ok) {
23
28
  return { status: 202, body: { runId: result.runId } };
24
29
  }
25
30
  switch (result.reason) {
31
+ case "invalid_issue":
32
+ return { status: 400, body: { error: "invalid_issue", error_description: result.message } };
26
33
  case "not_found":
27
34
  return { status: 404, body: { error: "not_found" } };
28
35
  case "invalid_secret":
@@ -13,7 +13,25 @@ export interface DelegatingAuthConfig {
13
13
  issuer?: string;
14
14
  audience?: string;
15
15
  jwksUri?: string;
16
+ /**
17
+ * AUTH_ROLE_CLAIM: the access-token claim carrying the IdP's roles/groups —
18
+ * an exact top-level claim name first (e.g. Auth0's namespaced
19
+ * `https://wardby.example/roles`, Okta `groups`, FusionAuth `roles`), else
20
+ * a dotted path into nested objects (Keycloak `realm_access.roles`).
21
+ */
22
+ roleClaim?: string;
23
+ /**
24
+ * AUTH_ROLE_MAP: comma-separated `idpValue=wardbyRole` pairs, e.g.
25
+ * `wardby-admin=admin,wardby-packages=package-approver,wardby-services=service-manager`.
26
+ */
27
+ roleMap?: string;
16
28
  }
29
+ /**
30
+ * Parses AUTH_ROLE_MAP. Whitespace around entries and around `=` is ignored;
31
+ * an unknown wardby role, or a malformed or empty entry, is a startup error.
32
+ * The LAST `=` splits an entry (wardby role names never contain one).
33
+ */
34
+ export declare function parseRoleMap(value: string): Map<string, string[]>;
17
35
  /**
18
36
  * Every spelling of the configured audience a token may legitimately carry.
19
37
  *
@@ -35,6 +53,7 @@ export declare class DelegatingAuthProvider implements AuthProvider {
35
53
  private readonly config;
36
54
  private readonly audiences;
37
55
  private readonly jwks;
56
+ private readonly roleMap?;
38
57
  constructor(config: DelegatingAuthConfig, jwks?: JWTVerifyGetKey);
39
58
  verifyBearer(token: string): Promise<VerifiedToken>;
40
59
  profile(idToken: string): Promise<AuthProfile>;
@@ -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) {
@@ -8,4 +8,6 @@ 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.");
@@ -4,6 +4,13 @@ import { Credentials } from "../../mcp/auth/self-hosted/credentials.js";
4
4
  import { Sessions } from "../../mcp/auth/self-hosted/session.js";
5
5
  import type { AuthorizeParams, RegisterClientParams, SelfHostedAuthConfig, TokenParams, TokenResult } from "./authorization-server.js";
6
6
  export type { AuthorizeParams, RegisterClientParams, SelfHostedAuthConfig, TokenParams, TokenResult, } from "./authorization-server.js";
7
+ /**
8
+ * Whether `requested` is one of a client's registered redirect URIs. A native
9
+ * app's loopback redirect may differ from its registration only in port: it
10
+ * listens on whatever port the OS hands it (RFC 8252 §7.3). Everything else —
11
+ * scheme, host, path, query — must match exactly, with no userinfo or fragment.
12
+ */
13
+ export declare function redirectMatches(registered: readonly string[], requested: string): boolean;
7
14
  export declare class SelfHostedAuthProvider implements AuthProvider {
8
15
  readonly db: PrismaClient;
9
16
  readonly credentials: Credentials;
@@ -39,18 +46,19 @@ export declare class SelfHostedAuthProvider implements AuthProvider {
39
46
  clientId: string;
40
47
  };
41
48
  } & {
42
- id: string;
43
49
  createdAt: Date;
50
+ id: string;
44
51
  state: string | null;
52
+ clientId: string;
45
53
  expiresAt: Date;
46
54
  resource: string;
47
- clientId: string;
48
55
  consumedAt: Date | null;
49
56
  redirectUri: string;
50
57
  codeChallenge: string;
51
58
  requestedScope: string;
52
59
  };
53
60
  challenge: string;
61
+ scopes: string[];
54
62
  }>;
55
63
  consent(sessionToken: string, interactionId: string, challenge: string, approve: boolean): Promise<string>;
56
64
  handleToken(params: TokenParams): Promise<TokenResult>;
@@ -3,17 +3,53 @@ 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";
10
+ const LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
11
+ /**
12
+ * Whether `requested` is one of a client's registered redirect URIs. A native
13
+ * app's loopback redirect may differ from its registration only in port: it
14
+ * listens on whatever port the OS hands it (RFC 8252 §7.3). Everything else —
15
+ * scheme, host, path, query — must match exactly, with no userinfo or fragment.
16
+ */
17
+ export function redirectMatches(registered, requested) {
18
+ if (registered.includes(requested))
19
+ return true;
20
+ let want;
21
+ try {
22
+ want = new URL(requested);
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ if (want.protocol !== "http:" || !LOOPBACK_HOSTS.includes(want.hostname))
28
+ return false;
29
+ return registered.some((value) => {
30
+ try {
31
+ const have = new URL(value);
32
+ return (have.protocol === "http:" &&
33
+ have.hostname === want.hostname &&
34
+ have.pathname === want.pathname &&
35
+ have.search === want.search &&
36
+ !want.username &&
37
+ !want.password &&
38
+ !requested.includes("#") // URL.hash is "" for a bare "#"
39
+ );
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ });
45
+ }
10
46
  function validRedirect(value) {
11
47
  try {
12
48
  const u = new URL(value);
13
49
  return (!value.includes("#") &&
14
50
  !u.username &&
15
51
  !u.password &&
16
- (u.protocol === "https:" || (u.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname))));
52
+ (u.protocol === "https:" || (u.protocol === "http:" && LOOPBACK_HOSTS.includes(u.hostname))));
17
53
  }
18
54
  catch {
19
55
  return false;
@@ -104,9 +140,10 @@ export class SelfHostedAuthProvider {
104
140
  throw new Error("Invalid resource.");
105
141
  const client = await this.db.oAuthClient.findUnique({ where: { clientId: p.clientId } });
106
142
  const metadata = client?.metadata;
107
- if (!metadata?.redirect_uris?.includes(p.redirectUri) || !metadata.grant_types?.includes("authorization_code"))
143
+ if (!redirectMatches(metadata?.redirect_uris ?? [], p.redirectUri) ||
144
+ !metadata?.grant_types?.includes("authorization_code"))
108
145
  throw new Error("Invalid client or redirect.");
109
- const scope = [...new Set(p.scope.split(/\s+/).filter((s) => SCOPES_SUPPORTED.includes(s)))].join(" ");
146
+ const scope = limitScope(p.scope, SCOPES_SUPPORTED);
110
147
  const request = await this.db.oAuthAuthorizationRequest.create({
111
148
  data: {
112
149
  clientId: p.clientId,
@@ -129,7 +166,10 @@ export class SelfHostedAuthProvider {
129
166
  if (!interaction || interaction.consumedAt || interaction.expiresAt.getTime() <= Date.now())
130
167
  throw new Error("Invalid interaction.");
131
168
  const challenge = await this.sessions.challenge("consent", session.sessionId, interactionId);
132
- return { interaction, challenge };
169
+ // Exactly what approving issues: the stored (supported-only) request.
170
+ // It's fixed when the interaction is created, so what the page shows
171
+ // can't drift from what consent() puts on the code.
172
+ return { interaction, challenge, scopes: interaction.requestedScope.split(" ").filter(Boolean) };
133
173
  }
134
174
  async consent(sessionToken, interactionId, challenge, approve) {
135
175
  const first = await this.sessions.get(sessionToken);
@@ -339,7 +379,16 @@ export class SelfHostedAuthProvider {
339
379
  family.clientId !== payload.client_id ||
340
380
  payload.scope !== family.scope)
341
381
  throw new Error("Invalid token.");
342
- return { subject, scopes: family.scope.split(/\s+/).filter(Boolean), roles: [] };
382
+ // Roles are read from the database on every request (no caching), so a
383
+ // grant or revocation applies to live tokens immediately. Scopes stay
384
+ // exactly what the user consented to; roles decide whether the
385
+ // privileged ones are honoured (resource-server.ts requireScope).
386
+ return {
387
+ subject,
388
+ scopes: family.scope.split(/\s+/).filter(Boolean),
389
+ roles: [],
390
+ wardbyRoles: family.user.roles ?? [],
391
+ };
343
392
  }
344
393
  async cleanup() {
345
394
  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;
@@ -1,4 +1,4 @@
1
- import type { CreateProxySessionInput, ProxyLedger, ProxyRequest, ProxySession, ProxyUsage, ReserveProxyRequestInput, ReserveProxyRequestResult } from "./types.js";
1
+ import { type CreateProxySessionInput, type ProxyLedger, type ProxyRequest, type ProxySession, type ProxyUsage, type ReserveProxyRequestInput, type ReserveProxyRequestResult } from "./types.js";
2
2
  /** Deterministic contract adapter. Sharing one instance simulates a proxy restart. */
3
3
  export declare class MemoryProxyLedger implements ProxyLedger {
4
4
  private readonly sessions;
@@ -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,15 @@
1
+ import { normalizeProxyModelTerms, } from "./types.js";
1
2
  function cloneSession(session) {
2
- return { ...session, allowedModels: [...session.allowedModels], deadlineAt: new Date(session.deadlineAt) };
3
+ return {
4
+ ...session,
5
+ allowedModels: [...session.allowedModels],
6
+ deadlineAt: new Date(session.deadlineAt),
7
+ budgetExhaustedAt: session.budgetExhaustedAt ? new Date(session.budgetExhaustedAt) : null,
8
+ upstreamFailure: session.upstreamFailure ?? null,
9
+ terms: session.terms
10
+ ? { version: session.terms.version, entry: { ...session.terms.entry, efforts: [...session.terms.entry.efforts] } }
11
+ : null,
12
+ };
3
13
  }
4
14
  function cloneRequest(request) {
5
15
  return {
@@ -20,7 +30,12 @@ export class MemoryProxyLedger {
20
30
  }
21
31
  if (this.sessionByCapability.has(input.capabilityHash))
22
32
  throw new Error("proxy_capability_collision");
23
- const session = { ...input, allowedModels: [...input.allowedModels], status: "active" };
33
+ const session = {
34
+ ...input,
35
+ allowedModels: [...input.allowedModels],
36
+ status: "active",
37
+ terms: input.terms ? normalizeProxyModelTerms(input.terms) : null,
38
+ };
24
39
  this.sessions.set(session.id, session);
25
40
  this.sessionByCapability.set(session.capabilityHash, session.id);
26
41
  }
@@ -48,8 +63,10 @@ export class MemoryProxyLedger {
48
63
  if (request.status === "reserved" || request.status === "uncertain")
49
64
  committedOrHeld += request.reservationUsd;
50
65
  }
51
- if (committedOrHeld + input.reservationUsd >= session.budgetUsd)
66
+ if (committedOrHeld + input.reservationUsd >= session.budgetUsd) {
67
+ session.budgetExhaustedAt ??= new Date(input.now);
52
68
  return { outcome: "budget_exhausted" };
69
+ }
53
70
  const request = { ...input, pricing: { ...input.pricing }, status: "reserved" };
54
71
  this.requests.set(request.id, request);
55
72
  this.requestBySessionKey.set(lookupKey, request.id);
@@ -90,6 +107,17 @@ export class MemoryProxyLedger {
90
107
  if (session)
91
108
  session.status = "cancelled";
92
109
  }
110
+ async budgetExhausted(sessionId) {
111
+ return Boolean(this.sessions.get(sessionId)?.budgetExhaustedAt);
112
+ }
113
+ async recordUpstreamFailure(sessionId, code) {
114
+ const session = this.sessions.get(sessionId);
115
+ if (session)
116
+ session.upstreamFailure ??= code;
117
+ }
118
+ async upstreamFailure(sessionId) {
119
+ return this.sessions.get(sessionId)?.upstreamFailure ?? null;
120
+ }
93
121
  async getRequest(requestId) {
94
122
  const request = this.requests.get(requestId);
95
123
  return request ? cloneRequest(request) : null;
@@ -1,4 +1,4 @@
1
- import { type ModelPricing } from "../llm/pricing.js";
1
+ import { type ModelPricing } from "../llm/pricing-core.js";
2
2
  import type { PricingSnapshot, ProxyUsage } from "./types.js";
3
3
  export declare function parseAuthoritativeUsage(value: unknown): ProxyUsage;
4
4
  export declare function parseAnthropicAuthoritativeUsage(value: unknown): ProxyUsage;
@@ -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?;
@@ -17,4 +21,5 @@ export declare class AnthropicSseUsageTracker {
17
21
  private stopped;
18
22
  consume(frame: string): TerminalUsage;
19
23
  }
24
+ /** The rates a request is billed at, tagged with their version. Picks only the price fields: a catalog entry's routing/shape fields stay out of the ledger. */
20
25
  export declare function pricingSnapshot(version: string, pricing: ModelPricing): PricingSnapshot;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { computeCost } from "../llm/pricing.js";
2
+ import { computeCost } from "../llm/pricing-core.js";
3
3
  const MAX_SAFE_TOKENS = 2_000_000_000;
4
4
  function tokenCount(value, name) {
5
5
  if (!Number.isSafeInteger(value) || value < 0 || value > MAX_SAFE_TOKENS) {
@@ -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");
@@ -156,6 +182,17 @@ export class AnthropicSseUsageTracker {
156
182
  }
157
183
  }
158
184
  }
185
+ /** The rates a request is billed at, tagged with their version. Picks only the price fields: a catalog entry's routing/shape fields stay out of the ledger. */
159
186
  export function pricingSnapshot(version, pricing) {
160
- return { version, ...pricing };
187
+ const snapshot = {
188
+ version,
189
+ encoding: pricing.encoding,
190
+ inputPerMTok: pricing.inputPerMTok,
191
+ outputPerMTok: pricing.outputPerMTok,
192
+ };
193
+ if (pricing.cachedInputPerMTok !== undefined)
194
+ snapshot.cachedInputPerMTok = pricing.cachedInputPerMTok;
195
+ if (pricing.cacheWritePerMTok !== undefined)
196
+ snapshot.cacheWritePerMTok = pricing.cacheWritePerMTok;
197
+ return snapshot;
161
198
  }
@@ -1,9 +1,23 @@
1
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
  }