@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
@@ -1,4 +1,6 @@
1
- import type { Prisma, PrismaClient } from "@prisma/client";
1
+ import type { Prisma, PrismaClient } from "#prisma";
2
+ /** Rejects unknown role names loudly; returns the deduplicated list. */
3
+ export declare function validateRoles(roles: readonly string[]): string[];
2
4
  export type AuthDb = Prisma.TransactionClient;
3
5
  export declare const DAY = 86400000;
4
6
  export declare function decodeKey(value: string, name: string): Buffer;
@@ -26,19 +28,34 @@ export declare function lockUser(tx: AuthDb, id: string): Promise<{
26
28
  createdAt: Date;
27
29
  updatedAt: Date;
28
30
  principalId: string;
31
+ roles: string[];
29
32
  displayName: string | null;
30
33
  }>;
31
34
  export declare class IdentityService {
32
35
  readonly db: PrismaClient;
33
36
  readonly credentials: Credentials;
34
37
  constructor(db: PrismaClient, hashKey: string);
35
- createUser(subject: string): Promise<{
38
+ createUser(subject: string, roles?: readonly string[]): Promise<{
36
39
  userId: string;
37
40
  subject: string;
41
+ roles: string[];
38
42
  loginKey: string;
39
43
  }>;
40
44
  createKey(subject: string): Promise<string>;
41
45
  disableUser(subject: string): Promise<void>;
46
+ /**
47
+ * Adds and/or removes roles on an existing user (never creates one). Any
48
+ * actual change signs the user out in the same transaction: their OAuth
49
+ * grant families, refresh grants, browser sessions and unexchanged
50
+ * authorization codes are revoked, and they sign in and authorize afresh.
51
+ * (Roles are also read live on every request, so even a direct database
52
+ * edit applies to live tokens at once.) A no-op change revokes nothing.
53
+ */
54
+ changeRoles(subject: string, add: readonly string[], remove: readonly string[]): Promise<{
55
+ subject: string;
56
+ roles: string[];
57
+ revokedGrants: boolean;
58
+ }>;
42
59
  revokeKey(keyId: string): Promise<void>;
43
60
  listUsers(): Prisma.PrismaPromise<{
44
61
  status: string;
@@ -46,13 +63,14 @@ export declare class IdentityService {
46
63
  subject: string;
47
64
  };
48
65
  id: string;
66
+ roles: string[];
49
67
  displayName: string | null;
50
68
  }[]>;
51
69
  listKeys(subject: string): Prisma.PrismaPromise<{
52
70
  createdAt: Date;
53
71
  keyId: string;
54
- lastUsedAt: Date | null;
55
72
  expiresAt: Date;
73
+ lastUsedAt: Date | null;
56
74
  revokedAt: Date | null;
57
75
  }[]>;
58
76
  }
@@ -1,5 +1,13 @@
1
1
  import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
2
2
  import { requireSubject } from "../../../providers/auth/subject.js";
3
+ import { ROLE_NAMES, isRoleName } from "../resource-server.js";
4
+ /** Rejects unknown role names loudly; returns the deduplicated list. */
5
+ export function validateRoles(roles) {
6
+ for (const r of roles)
7
+ if (!isRoleName(r))
8
+ throw new Error(`Unknown role "${r}" (known roles: ${ROLE_NAMES.join(", ")}).`);
9
+ return [...new Set(roles)];
10
+ }
3
11
  export const DAY = 86_400_000;
4
12
  export function decodeKey(value, name) {
5
13
  if (!/^[a-f\d]{64}$/i.test(value))
@@ -43,16 +51,17 @@ export class IdentityService {
43
51
  this.db = db;
44
52
  this.credentials = new Credentials(hashKey);
45
53
  }
46
- async createUser(subject) {
54
+ async createUser(subject, roles = []) {
47
55
  requireSubject(subject);
56
+ const granted = validateRoles(roles);
48
57
  return this.db.$transaction(async (tx) => {
49
58
  const principal = await tx.principal.upsert({ where: { subject }, create: { subject }, update: {} });
50
- const user = await tx.authUser.create({ data: { principalId: principal.id } });
59
+ const user = await tx.authUser.create({ data: { principalId: principal.id, roles: granted } });
51
60
  const key = this.credentials.create("rvk");
52
61
  await tx.authLoginKey.create({
53
62
  data: { keyId: key.id, userId: user.id, secretHash: key.hash, expiresAt: new Date(Date.now() + 365 * DAY) },
54
63
  });
55
- return { userId: user.id, subject, loginKey: key.token };
64
+ return { userId: user.id, subject, roles: granted, loginKey: key.token };
56
65
  });
57
66
  }
58
67
  async createKey(subject) {
@@ -81,6 +90,49 @@ export class IdentityService {
81
90
  await tx.oAuthFamily.updateMany({ where: { userId: user.id }, data: { revokedAt: now } });
82
91
  });
83
92
  }
93
+ /**
94
+ * Adds and/or removes roles on an existing user (never creates one). Any
95
+ * actual change signs the user out in the same transaction: their OAuth
96
+ * grant families, refresh grants, browser sessions and unexchanged
97
+ * authorization codes are revoked, and they sign in and authorize afresh.
98
+ * (Roles are also read live on every request, so even a direct database
99
+ * edit applies to live tokens at once.) A no-op change revokes nothing.
100
+ */
101
+ async changeRoles(subject, add, remove) {
102
+ const adding = validateRoles(add);
103
+ const removing = validateRoles(remove);
104
+ const both = adding.filter((r) => removing.includes(r));
105
+ if (both.length)
106
+ throw new Error(`Cannot both grant and revoke: ${both.join(", ")}.`);
107
+ const user = await this.db.authUser.findFirst({ where: { principal: { subject: requireSubject(subject) } } });
108
+ if (!user)
109
+ throw new Error("Unknown user.");
110
+ return this.db.$transaction(async (tx) => {
111
+ await tx.$queryRaw `SELECT "id" FROM "AuthUser" WHERE "id" = ${user.id} FOR UPDATE`;
112
+ const current = (await tx.authUser.findUniqueOrThrow({ where: { id: user.id } })).roles ?? [];
113
+ const roles = ROLE_NAMES.filter((r) => (current.includes(r) || adding.includes(r)) && !removing.includes(r));
114
+ await tx.authUser.update({ where: { id: user.id }, data: { roles } });
115
+ // ANY change signs the user out (families, refresh grants, sessions,
116
+ // unexchanged codes): a revoked role must never come back through an
117
+ // old token, and a granted role must never silently widen a grant the
118
+ // user consented to while it was inert — they authorize afresh.
119
+ const changed = roles.length !== current.length || roles.some((r) => !current.includes(r));
120
+ if (changed) {
121
+ const now = new Date();
122
+ await tx.oAuthAuthorizationCode.updateMany({
123
+ where: { userId: user.id, consumedAt: null },
124
+ data: { consumedAt: now },
125
+ });
126
+ await tx.authSession.updateMany({ where: { userId: user.id, revokedAt: null }, data: { revokedAt: now } });
127
+ await tx.oAuthFamily.updateMany({ where: { userId: user.id, revokedAt: null }, data: { revokedAt: now } });
128
+ await tx.oAuthGrant.updateMany({
129
+ where: { family: { userId: user.id }, revokedAt: null },
130
+ data: { revokedAt: now },
131
+ });
132
+ }
133
+ return { subject, roles, revokedGrants: changed };
134
+ });
135
+ }
84
136
  async revokeKey(keyId) {
85
137
  const key = await this.db.authLoginKey.findUnique({ where: { keyId } });
86
138
  if (!key)
@@ -94,7 +146,13 @@ export class IdentityService {
94
146
  }
95
147
  listUsers() {
96
148
  return this.db.authUser.findMany({
97
- select: { id: true, status: true, displayName: true, principal: { select: { subject: true } } },
149
+ select: {
150
+ id: true,
151
+ status: true,
152
+ roles: true,
153
+ displayName: true,
154
+ principal: { select: { subject: true } },
155
+ },
98
156
  });
99
157
  }
100
158
  listKeys(subject) {
@@ -1,4 +1,4 @@
1
- import type { PrismaClient } from "@prisma/client";
1
+ import type { PrismaClient } from "#prisma";
2
2
  import type { Credentials } from "./credentials.js";
3
3
  export interface RateLimiter {
4
4
  check(bucket: string, identity: string, limit: number): Promise<void>;
@@ -1,4 +1,4 @@
1
- import type { PrismaClient } from "@prisma/client";
1
+ import type { PrismaClient } from "#prisma";
2
2
  import type { Credentials } from "./credentials.js";
3
3
  import { type AuthDb } from "./credentials.js";
4
4
  export declare class Sessions {
@@ -21,13 +21,14 @@ export declare class Sessions {
21
21
  createdAt: Date;
22
22
  updatedAt: Date;
23
23
  principalId: string;
24
+ roles: string[];
24
25
  displayName: string | null;
25
26
  };
26
27
  } & {
27
28
  createdAt: Date;
29
+ expiresAt: Date;
28
30
  userId: string;
29
31
  secretHash: string;
30
- expiresAt: Date;
31
32
  revokedAt: Date | null;
32
33
  sessionId: string;
33
34
  lastSeenAt: Date;
@@ -7,13 +7,39 @@
7
7
  * registry also carries jobs/email/auth/storage seams no Phase 4 tool
8
8
  * needs and this plan never builds real adapters for.
9
9
  */
10
- import type { Principal, PrismaClient } from "@prisma/client";
10
+ import type { Principal, PrismaClient } from "#prisma";
11
11
  import type { RequestStateAccessor } from "@modelcontextprotocol/server";
12
12
  import type { ProviderRegistry } from "../providers/index.js";
13
- export type McpProviders = Pick<ProviderRegistry, "llm" | "engine" | "datastore" | "secrets" | "executor" | "memory">;
13
+ import type { RepoAccessGate } from "../core/repo-access.js";
14
+ import type { HostUserAuthorizerRegistry, ReviewHostRegistry } from "../providers/review-host/types.js";
15
+ export type McpProviders = Pick<ProviderRegistry, "llm" | "engine" | "datastore" | "secrets" | "executor" | "memory"> & {
16
+ reviewHosts?: ReviewHostRegistry;
17
+ /** Identity linking (link_host_account); empty or absent = linking disabled. */
18
+ hostUserAuthorizers?: HostUserAuthorizerRegistry;
19
+ /**
20
+ * Repository authorization (link_repository, coding profiles, host events).
21
+ * Absent = nothing can be authorized through host access (fail closed);
22
+ * only admin approvals still work.
23
+ */
24
+ repoAccess?: RepoAccessGate;
25
+ };
14
26
  export interface McpRequestContext {
15
27
  principal: Principal;
16
28
  scopes: Set<string>;
29
+ /**
30
+ * The caller's wardby roles (resource-server.ts ROLE_PERMISSIONS), resolved
31
+ * per request, never cached: a privileged scope is honoured only when one
32
+ * of these grants it (see requireScope). Absent/empty = member.
33
+ */
34
+ roles?: readonly string[];
35
+ /**
36
+ * The stdio local operator (set only by index.ts localOperatorContext):
37
+ * owner of every resource for access checks, unfiltered listings. Depends
38
+ * on the transport, never on the principal, so an HTTP caller whose
39
+ * subject equals LOCAL_PRINCIPAL never gets it (A10). It never bypasses
40
+ * the binding rules (auth/access.ts requireBindingOwner).
41
+ */
42
+ operator?: boolean;
17
43
  /**
18
44
  * The server's resource identifier, for tool handlers that need a
19
45
  * field-level scope step-up beyond the tool's own declared scope (e.g.
@@ -1,9 +1,3 @@
1
- /**
2
- * MCP-layer errors that carry an HTTP status + WWW-Authenticate challenge —
3
- * the transport (Task 7) maps these to the actual HTTP response; JSON-RPC
4
- * dispatch (Task 6) maps them to a JSON-RPC error object. One error type
5
- * for both call sites avoids two parallel error hierarchies.
6
- */
7
1
  export declare class McpError extends Error {
8
2
  readonly httpStatus: number;
9
3
  readonly wwwAuthenticate?: string;
@@ -11,3 +5,28 @@ export declare class McpError extends Error {
11
5
  }
12
6
  export declare function unauthorized(message: string, resourceMetadataUrl: string): McpError;
13
7
  export declare function insufficientScope(scopes: string[], resourceMetadataUrl: string): McpError;
8
+ /**
9
+ * The one place a Prisma error becomes a client-facing McpError, applied at
10
+ * both MCP dispatch sites (server.ts) so no handler leaks the raw
11
+ * "Invalid `prisma.x.create()` invocation" text. A handler that already
12
+ * maps an error to its own, more specific McpError keeps that message: an
13
+ * McpError passes through here unchanged.
14
+ *
15
+ * - P2002 (unique violation) -> 409 naming the model and the unique fields.
16
+ * An `ownerId` component is described ("for this owner") rather than
17
+ * named, since per-owner uniques are the common case.
18
+ * - P2003 (foreign key) -> 409, worded for both directions: a delete blocked
19
+ * by a reference (ON DELETE RESTRICT), or an insert/update pointing at a
20
+ * row that no longer exists (e.g. attach_tool racing delete_tool).
21
+ * - A serialization failure or deadlock in one of the Serializable
22
+ * transactions, in any of the shapes isSerializationConflict knows (P2034,
23
+ * a raw-statement P2010, or an unwrapped driver adapter error at COMMIT)
24
+ * -> 409 asking the client to retry.
25
+ * - Any other Prisma client error -> a generic 500 carrying only a
26
+ * reference id. Its message ("Invalid `tx.tool.update()` invocation in
27
+ * /path/to/file.ts:…") can include server source paths and query detail,
28
+ * so it is logged here, under that id, and never sent.
29
+ * - Anything else (an McpError, a plain Error) is returned unchanged for the
30
+ * caller to rethrow.
31
+ */
32
+ export declare function mapPrismaError(err: unknown): unknown;
@@ -4,6 +4,10 @@
4
4
  * dispatch (Task 6) maps them to a JSON-RPC error object. One error type
5
5
  * for both call sites avoids two parallel error hierarchies.
6
6
  */
7
+ import { randomUUID } from "node:crypto";
8
+ import { Prisma } from "#prisma";
9
+ import { isSerializationConflict } from "../core/dispatch.js";
10
+ import { logger } from "../core/logger.js";
7
11
  export class McpError extends Error {
8
12
  httpStatus;
9
13
  wwwAuthenticate;
@@ -20,3 +24,97 @@ export function unauthorized(message, resourceMetadataUrl) {
20
24
  export function insufficientScope(scopes, resourceMetadataUrl) {
21
25
  return new McpError(403, `Insufficient scope; this operation requires: ${scopes.join(" ")}`, `Bearer resource_metadata="${resourceMetadataUrl}", scope="${scopes.join(" ")}"`);
22
26
  }
27
+ /** "BudgetGroup" -> "budget group". */
28
+ function humanizeModel(modelName) {
29
+ return modelName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
30
+ }
31
+ function withArticle(noun) {
32
+ return `${/^[aeiou]/.test(noun) ? "An" : "A"} ${noun}`;
33
+ }
34
+ /**
35
+ * The fields behind a P2002, from whichever shape reported them: a classic
36
+ * `meta.target`, the driver adapter's `constraint.fields`, or -- what
37
+ * @prisma/adapter-pg actually reports, since PostgreSQL always names the
38
+ * violated constraint -- the index name, which follows Prisma's default
39
+ * `<Model>_<field>[_<field>...]_key` naming for every @unique/@@unique in
40
+ * schema.prisma. Pinned against a real database in
41
+ * core/prisma-adapter.database.test.ts.
42
+ */
43
+ function uniqueFields(meta, modelName) {
44
+ const target = meta?.target;
45
+ if (Array.isArray(target) && target.every((t) => typeof t === "string"))
46
+ return target;
47
+ if (typeof target === "string")
48
+ return [target];
49
+ const adapterError = meta?.driverAdapterError;
50
+ const constraint = adapterError?.cause?.constraint;
51
+ if (Array.isArray(constraint?.fields) && constraint.fields.every((f) => typeof f === "string")) {
52
+ return constraint.fields;
53
+ }
54
+ const index = constraint?.index;
55
+ if (typeof index === "string" && modelName && index.startsWith(`${modelName}_`) && index.endsWith("_key")) {
56
+ return index.slice(modelName.length + 1, -"_key".length).split("_");
57
+ }
58
+ return [];
59
+ }
60
+ /**
61
+ * The one place a Prisma error becomes a client-facing McpError, applied at
62
+ * both MCP dispatch sites (server.ts) so no handler leaks the raw
63
+ * "Invalid `prisma.x.create()` invocation" text. A handler that already
64
+ * maps an error to its own, more specific McpError keeps that message: an
65
+ * McpError passes through here unchanged.
66
+ *
67
+ * - P2002 (unique violation) -> 409 naming the model and the unique fields.
68
+ * An `ownerId` component is described ("for this owner") rather than
69
+ * named, since per-owner uniques are the common case.
70
+ * - P2003 (foreign key) -> 409, worded for both directions: a delete blocked
71
+ * by a reference (ON DELETE RESTRICT), or an insert/update pointing at a
72
+ * row that no longer exists (e.g. attach_tool racing delete_tool).
73
+ * - A serialization failure or deadlock in one of the Serializable
74
+ * transactions, in any of the shapes isSerializationConflict knows (P2034,
75
+ * a raw-statement P2010, or an unwrapped driver adapter error at COMMIT)
76
+ * -> 409 asking the client to retry.
77
+ * - Any other Prisma client error -> a generic 500 carrying only a
78
+ * reference id. Its message ("Invalid `tx.tool.update()` invocation in
79
+ * /path/to/file.ts:…") can include server source paths and query detail,
80
+ * so it is logged here, under that id, and never sent.
81
+ * - Anything else (an McpError, a plain Error) is returned unchanged for the
82
+ * caller to rethrow.
83
+ */
84
+ export function mapPrismaError(err) {
85
+ if (isSerializationConflict(err)) {
86
+ return new McpError(409, "A concurrent change conflicted with this one; retry the request.");
87
+ }
88
+ if (!isPrismaClientError(err))
89
+ return err;
90
+ if (!(err instanceof Prisma.PrismaClientKnownRequestError))
91
+ return internalDatabaseError(err);
92
+ const meta = err.meta;
93
+ const modelName = typeof meta?.modelName === "string" ? meta.modelName : undefined;
94
+ const noun = modelName ? humanizeModel(modelName) : undefined;
95
+ if (err.code === "P2002") {
96
+ const fields = uniqueFields(meta, modelName);
97
+ const named = fields.filter((f) => f !== "ownerId");
98
+ if (!noun || named.length === 0)
99
+ return new McpError(409, "A record with those values already exists.");
100
+ const scope = named.length < fields.length ? " for this owner" : "";
101
+ return new McpError(409, `${withArticle(noun)} with that ${named.join(" and ")} already exists${scope}.`);
102
+ }
103
+ if (err.code === "P2003") {
104
+ return new McpError(409, `The ${noun ?? "record"} conflicts with a related record: it is still referenced by another record, or refers to one that no longer exists.`);
105
+ }
106
+ return internalDatabaseError(err);
107
+ }
108
+ function isPrismaClientError(err) {
109
+ return ((err instanceof Error && err.name === "DriverAdapterError") ||
110
+ err instanceof Prisma.PrismaClientKnownRequestError ||
111
+ err instanceof Prisma.PrismaClientUnknownRequestError ||
112
+ err instanceof Prisma.PrismaClientValidationError ||
113
+ err instanceof Prisma.PrismaClientInitializationError ||
114
+ err instanceof Prisma.PrismaClientRustPanicError);
115
+ }
116
+ function internalDatabaseError(err) {
117
+ const reference = randomUUID();
118
+ logger.error({ err, reference }, "database error in an MCP handler; the client sees only the reference");
119
+ return new McpError(500, `Internal database error (reference: ${reference}).`);
120
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * GitHub App webhook ingress — framework-agnostic, mounted unauthenticated
3
+ * (by OAuth) in streamable-http.ts at /hosts/github/events. Authenticated by
4
+ * the App's webhook secret (X-Hub-Signature-256) over the raw body.
5
+ * Nothing from the body is logged.
6
+ */
7
+ import { type PrismaClient } from "#prisma";
8
+ import type { HostEventDb } from "../../core/host-events.js";
9
+ import type { Executor } from "../../providers/executor/types.js";
10
+ import type { ReviewHostRegistry } from "../../providers/review-host/types.js";
11
+ import type { RepoAccessGate } from "../../core/repo-access.js";
12
+ export interface GitHubIngressDeps {
13
+ db: HostEventDb & Pick<PrismaClient, "hostEventDelivery">;
14
+ executor: Executor;
15
+ hosts: ReviewHostRegistry;
16
+ /** Repository authorization for every dispatch, and the mention author's permission. */
17
+ repoAccess: RepoAccessGate;
18
+ webhookSecret: string | undefined;
19
+ appIdentity: () => Promise<{
20
+ id: number;
21
+ slug: string;
22
+ }>;
23
+ now?: () => Date;
24
+ }
25
+ export interface GitHubIngressResult {
26
+ status: number;
27
+ body: Record<string, unknown>;
28
+ /** Cosmetic follow-ups (reactions) to run after the response is sent. */
29
+ afterResponse?: () => Promise<void>;
30
+ }
31
+ export declare function handleGitHubEventIngress(req: {
32
+ headers: Record<string, string | undefined>;
33
+ rawBody: string;
34
+ }, deps: GitHubIngressDeps): Promise<GitHubIngressResult>;
35
+ /** Test-only: reset the prune clock. */
36
+ export declare function resetPruneClockForTests(): void;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * GitHub App webhook ingress — framework-agnostic, mounted unauthenticated
3
+ * (by OAuth) in streamable-http.ts at /hosts/github/events. Authenticated by
4
+ * the App's webhook secret (X-Hub-Signature-256) over the raw body.
5
+ * Nothing from the body is logged.
6
+ */
7
+ import { Prisma } from "#prisma";
8
+ import { routeHostEvent } from "../../core/host-events.js";
9
+ import { logger } from "../../core/logger.js";
10
+ import { normalizeGitHubEvent, verifyGitHubSignature } from "../../providers/review-host/github-events.js";
11
+ const log = logger.child({ module: "github-ingress" });
12
+ const DELIVERY_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
13
+ const PRUNE_INTERVAL_MS = 60 * 60 * 1000;
14
+ const SAFE_DELIVERY = /^[A-Za-z0-9-]{1,100}$/;
15
+ let lastPruneAt = 0;
16
+ export async function handleGitHubEventIngress(req, deps) {
17
+ if (!deps.webhookSecret)
18
+ return { status: 404, body: { error: "not_found" } };
19
+ if (!verifyGitHubSignature(req.rawBody, req.headers["x-hub-signature-256"], deps.webhookSecret)) {
20
+ return { status: 401, body: { error: "invalid_signature" } };
21
+ }
22
+ const deliveryId = req.headers["x-github-delivery"];
23
+ const eventName = req.headers["x-github-event"];
24
+ if (!deliveryId || !SAFE_DELIVERY.test(deliveryId) || !eventName) {
25
+ return { status: 400, body: { error: "missing_delivery_headers" } };
26
+ }
27
+ const now = (deps.now ?? (() => new Date()))();
28
+ let payload;
29
+ try {
30
+ payload = JSON.parse(req.rawBody);
31
+ }
32
+ catch {
33
+ return { status: 400, body: { error: "invalid_json" } };
34
+ }
35
+ const app = await deps.appIdentity();
36
+ const event = normalizeGitHubEvent(eventName, payload, app);
37
+ // An event we don't act on must never claim the delivery id: recording it
38
+ // here would let a later, real delivery of the same id (GitHub does reuse
39
+ // ids across distinct redeliveries of what it considers the same event)
40
+ // be silently swallowed as a "duplicate" of something we never routed.
41
+ if (!event)
42
+ return { status: 202, body: { ignored: true } };
43
+ try {
44
+ await deps.db.hostEventDelivery.create({ data: { provider: "github", deliveryId } });
45
+ }
46
+ catch (err) {
47
+ if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
48
+ return { status: 202, body: { duplicate: true } };
49
+ }
50
+ throw err;
51
+ }
52
+ if (now.getTime() - lastPruneAt > PRUNE_INTERVAL_MS) {
53
+ lastPruneAt = now.getTime();
54
+ await deps.db.hostEventDelivery
55
+ .deleteMany({ where: { receivedAt: { lt: new Date(now.getTime() - DELIVERY_RETENTION_MS) } } })
56
+ .catch((err) => log.warn({ err }, "delivery prune failed"));
57
+ }
58
+ // The delivery row is recorded before routing so a duplicate can never
59
+ // race past it, but that means a transient failure inside routeHostEvent
60
+ // must not leave a delivery permanently marked "done" — GitHub's redelivery
61
+ // would then hit our P2002 dedup check and the event would be lost for
62
+ // good. On failure here, un-record the delivery and rethrow so the retry
63
+ // (ours or GitHub's redelivery) is routed for real, not reported duplicate.
64
+ try {
65
+ const routed = await routeHostEvent(event, {
66
+ db: deps.db,
67
+ executor: deps.executor,
68
+ hosts: deps.hosts,
69
+ repoAccess: deps.repoAccess,
70
+ mentionHandle: app.slug,
71
+ });
72
+ log.info({ event: event.kind, repository: event.repository, deliveryId, runIds: routed.runIds }, "host event routed");
73
+ return {
74
+ status: 202,
75
+ body: { runIds: routed.runIds },
76
+ afterResponse: async () => {
77
+ for (const followUp of routed.followUps)
78
+ await followUp();
79
+ },
80
+ };
81
+ }
82
+ catch (err) {
83
+ await deps.db.hostEventDelivery
84
+ .deleteMany({ where: { provider: "github", deliveryId } })
85
+ .catch((delErr) => log.warn({ err: delErr }, "could not roll back the delivery record after a routing failure"));
86
+ throw err;
87
+ }
88
+ }
89
+ /** Test-only: reset the prune clock. */
90
+ export function resetPruneClockForTests() {
91
+ lastPruneAt = 0;
92
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The browser end of link_host_account: GitHub redirects here after the user
3
+ * authorizes the App (GET /hosts/github/user-callback?code&state). Mounted in
4
+ * streamable-http.ts behind its canonical-Host check, and only when the App's
5
+ * OAuth client credentials are configured. It shows the one-time
6
+ * confirmation code the initiating principal must submit over MCP, never a
7
+ * token. No script, no forms, no framing, nothing cached, no referrer.
8
+ */
9
+ import type { ServerResponse } from "node:http";
10
+ import type { PrismaClient } from "#prisma";
11
+ import type { HostUserAuthorizer } from "../../providers/review-host/types.js";
12
+ export interface HostUserCallbackDeps {
13
+ db: Pick<PrismaClient, "hostIdentityLinkRequest">;
14
+ authorizer: HostUserAuthorizer;
15
+ /** Exactly the redirect_uri the authorize URL carried (the App's registered Callback URL). */
16
+ redirectUri: string;
17
+ }
18
+ export declare function handleHostUserCallback(url: URL, res: ServerResponse, deps: HostUserCallbackDeps): Promise<void>;
@@ -0,0 +1,50 @@
1
+ import { completeHostIdentityCallback } from "../../core/host-identity-links.js";
2
+ import { PAGE_STYLE } from "../shared/page-style.js";
3
+ function escape(value) {
4
+ return value.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
5
+ }
6
+ function page(res, status, title, body) {
7
+ res.setHeader("content-security-policy", "default-src 'none'; style-src 'unsafe-inline'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'");
8
+ res.setHeader("cache-control", "no-store");
9
+ res.setHeader("referrer-policy", "no-referrer");
10
+ res.setHeader("x-frame-options", "DENY");
11
+ res.setHeader("x-content-type-options", "nosniff");
12
+ res
13
+ .writeHead(status, { "content-type": "text/html; charset=utf-8" })
14
+ .end(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` +
15
+ `<title>wardby: ${escape(title)}</title><style>${PAGE_STYLE}</style><body><div class="card"><div class="kicker">wardby</div>` +
16
+ `<h1>${escape(title)}</h1>${body}</div></body></html>`);
17
+ }
18
+ export async function handleHostUserCallback(url, res, deps) {
19
+ const error = url.searchParams.get("error");
20
+ if (error !== null) {
21
+ if (error === "access_denied") {
22
+ page(res, 200, "Linking cancelled", "<p>You cancelled on GitHub. Nothing was linked; you can close this page.</p>");
23
+ }
24
+ else {
25
+ page(res, 400, "Linking failed", "<p>GitHub did not authorize the link. Start again from your MCP client.</p>");
26
+ }
27
+ return;
28
+ }
29
+ const outcome = await completeHostIdentityCallback({
30
+ db: deps.db,
31
+ authorizer: deps.authorizer,
32
+ state: url.searchParams.get("state"),
33
+ code: url.searchParams.get("code"),
34
+ redirectUri: deps.redirectUri,
35
+ });
36
+ if (outcome.kind === "invalid") {
37
+ page(res, 400, "Link not valid", "<p>This link is invalid or has expired. Start again from your MCP client with <code>link_host_account</code>.</p>");
38
+ return;
39
+ }
40
+ if (outcome.kind === "failed") {
41
+ page(res, 502, "Linking failed", "<p>GitHub did not complete the sign-in. Start again from your MCP client.</p>");
42
+ return;
43
+ }
44
+ page(res, 200, "Confirm the link", `<p>Signed in to GitHub as <strong>@${escape(outcome.login)}</strong>, to be linked to the wardby account ` +
45
+ `<strong>${escape(outcome.subject)}</strong>.</p>` +
46
+ `<p>Give your MCP client this code (call <code>link_host_account</code> with <code>confirmationCode</code>):</p>` +
47
+ `<p style="font-size:2em;font-family:monospace;letter-spacing:.1em"><strong>${escape(outcome.code)}</strong></p>` +
48
+ `<p>It works once, within 10 minutes. <strong>If you did not start this, close this page</strong> and do not ` +
49
+ `share the code: someone may be trying to use your GitHub account's access.</p>`);
50
+ }
@@ -1,6 +1,13 @@
1
1
  import { type WardbyMcpServer } from "./server.js";
2
- import type { McpProviders } from "./context.js";
2
+ import type { McpProviders, McpRequestContext } from "./context.js";
3
+ import type { Principal, PrismaClient } from "#prisma";
3
4
  import { type SecretElicitationUrlBuilder } from "./tools/secrets.js";
5
+ /**
6
+ * stdio's one fixed identity: the local operator (LOCAL_PRINCIPAL), trusted
7
+ * with every supported scope and every role — whoever can start the
8
+ * process already holds the database and its keys.
9
+ */
10
+ export declare function localOperatorContext(principal: Principal, providers: McpProviders, db: PrismaClient): McpRequestContext;
4
11
  /** Registers the full Phase 4 tool surface — every module, in one place. */
5
12
  export declare function registerAllTools(mcp: WardbyMcpServer, opts: {
6
13
  secretElicitationUrl: SecretElicitationUrlBuilder;