@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
@@ -6,7 +6,7 @@
6
6
  * schema.prisma). Every other operation goes through the normal Prisma
7
7
  * Client since it never touches `contentTsv`.
8
8
  */
9
- import type { PrismaClient } from "@prisma/client";
9
+ import type { PrismaClient } from "#prisma";
10
10
  import type { AgentMemorySearchHit, AgentMemoryStore } from "./types.js";
11
11
  export type AgentMemoryDb = Pick<PrismaClient, "agentMemory" | "$executeRaw" | "$queryRaw">;
12
12
  export declare class PostgresAgentMemory implements AgentMemoryStore {
@@ -0,0 +1,16 @@
1
+ import type { InlineComment } from "./types.js";
2
+ /**
3
+ * The lines a host accepts inline comments on for one file's unified patch:
4
+ * added and context lines on the RIGHT (new) side, removed and context lines
5
+ * on the LEFT (old) side. Hosts reject comments anywhere else, which is the
6
+ * usual way review bots break; callers move those comments into the summary.
7
+ */
8
+ export declare function commentableLines(patch: string): {
9
+ right: Set<number>;
10
+ left: Set<number>;
11
+ };
12
+ /** Splits comments into those the host will accept inline and those that must go in the summary. */
13
+ export declare function partitionComments(comments: readonly InlineComment[], patches: ReadonlyMap<string, string | undefined>): {
14
+ inline: InlineComment[];
15
+ outside: InlineComment[];
16
+ };
@@ -0,0 +1,59 @@
1
+ const HUNK_HEADER = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
2
+ /**
3
+ * The lines a host accepts inline comments on for one file's unified patch:
4
+ * added and context lines on the RIGHT (new) side, removed and context lines
5
+ * on the LEFT (old) side. Hosts reject comments anywhere else, which is the
6
+ * usual way review bots break; callers move those comments into the summary.
7
+ */
8
+ export function commentableLines(patch) {
9
+ const right = new Set();
10
+ const left = new Set();
11
+ let oldLine = 0;
12
+ let newLine = 0;
13
+ let inHunk = false;
14
+ for (const raw of patch.split("\n")) {
15
+ if (!raw)
16
+ continue; // Skip empty segments from trailing newlines
17
+ const header = HUNK_HEADER.exec(raw);
18
+ if (header) {
19
+ oldLine = Number(header[1]);
20
+ newLine = Number(header[2]);
21
+ inHunk = true;
22
+ continue;
23
+ }
24
+ if (!inHunk || raw.startsWith("\\"))
25
+ continue;
26
+ if (raw.startsWith("+")) {
27
+ right.add(newLine++);
28
+ }
29
+ else if (raw.startsWith("-")) {
30
+ left.add(oldLine++);
31
+ }
32
+ else {
33
+ right.add(newLine++);
34
+ left.add(oldLine++);
35
+ }
36
+ }
37
+ return { right, left };
38
+ }
39
+ /** Splits comments into those the host will accept inline and those that must go in the summary. */
40
+ export function partitionComments(comments, patches) {
41
+ const cache = new Map();
42
+ const inline = [];
43
+ const outside = [];
44
+ for (const comment of comments) {
45
+ const patch = patches.get(comment.path);
46
+ if (!patch) {
47
+ outside.push(comment);
48
+ continue;
49
+ }
50
+ let lines = cache.get(comment.path);
51
+ if (!lines) {
52
+ lines = commentableLines(patch);
53
+ cache.set(comment.path, lines);
54
+ }
55
+ const allowed = comment.side === "LEFT" ? lines.left : lines.right;
56
+ (allowed.has(comment.line) ? inline : outside).push(comment);
57
+ }
58
+ return { inline, outside };
59
+ }
@@ -0,0 +1,6 @@
1
+ import type { HostEvent } from "./types.js";
2
+ export declare function verifyGitHubSignature(rawBody: string, header: string | undefined, secret: string): boolean;
3
+ export declare function normalizeGitHubEvent(eventName: string, payload: unknown, app: {
4
+ id: number;
5
+ slug: string;
6
+ }): HostEvent | null;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * GitHub App webhook → host-neutral HostEvent. Pure: no I/O. Signature
3
+ * verification is over the exact request body text GitHub signed.
4
+ */
5
+ import { createHmac, timingSafeEqual } from "node:crypto";
6
+ import { normalizeGitHubRepository } from "../../coding/protocol.js";
7
+ const PR_ACTIONS = new Set(["opened", "synchronize", "reopened", "ready_for_review"]);
8
+ const TRUSTED_ASSOCIATIONS = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
9
+ const SAFE_SHA = /^[0-9a-f]{40}$/;
10
+ /** Mirrors the runId shape validated in providers/vcs (github.ts, git.ts). */
11
+ const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
12
+ /** The hidden marker a coding run writes at the top of every PR it opens (providers/vcs/github.ts); legacy prefix too. */
13
+ const RUN_MARKER = /^\s*<!-- (?:wardby|reevo-run):(\S+) -->/;
14
+ export function verifyGitHubSignature(rawBody, header, secret) {
15
+ if (!header || !/^sha256=[0-9a-f]{64}$/.test(header))
16
+ return false;
17
+ const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest();
18
+ const presented = Buffer.from(header.slice("sha256=".length), "hex");
19
+ return presented.length === expected.length && timingSafeEqual(presented, expected);
20
+ }
21
+ const obj = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
22
+ const int = (value) => Number.isSafeInteger(value) && value > 0 ? value : null;
23
+ function repositoryOf(payload) {
24
+ const name = obj(payload.repository)?.full_name;
25
+ if (typeof name !== "string")
26
+ return null;
27
+ try {
28
+ return normalizeGitHubRepository(name);
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ function mentions(body, slug) {
35
+ const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36
+ return new RegExp(`(^|[^\\w@.-])@${escaped}(?![\\w-])`, "i").test(body);
37
+ }
38
+ const text = (value) => (typeof value === "string" ? value : "");
39
+ function subjectOf(item) {
40
+ return typeof item?.title === "string" ? { title: item.title, body: text(item.body) } : undefined;
41
+ }
42
+ /**
43
+ * The run that opened this PR. Only a PR the App itself authored counts:
44
+ * anyone can write the marker into their own PR's description. Only an open
45
+ * PR counts too: a merged or closed PR's branch is stale (a squash merge leaves
46
+ * its commits behind), so a follow-up there starts from the default branch.
47
+ */
48
+ function priorRunIdOf(pr, slug) {
49
+ if (pr?.state !== "open")
50
+ return undefined;
51
+ const author = obj(pr?.user);
52
+ if (author?.type !== "Bot" || typeof author.login !== "string")
53
+ return undefined;
54
+ if (author.login.toLowerCase() !== `${slug}[bot]`.toLowerCase())
55
+ return undefined;
56
+ const id = RUN_MARKER.exec(text(pr?.body))?.[1];
57
+ return id && SAFE_RUN_ID.test(id) ? id : undefined;
58
+ }
59
+ /**
60
+ * A cheap pre-filter only: author_association is not a permission (MEMBER is
61
+ * any org member, COLLABORATOR includes read and triage). The real gate is the
62
+ * author's live repository permission, checked by id in core/host-events.ts.
63
+ */
64
+ function trustedHuman(association, user) {
65
+ return (typeof association === "string" &&
66
+ TRUSTED_ASSOCIATIONS.has(association) &&
67
+ user?.type === "User" &&
68
+ typeof user.login === "string" &&
69
+ int(user.id) !== null);
70
+ }
71
+ export function normalizeGitHubEvent(eventName, payload, app) {
72
+ const p = obj(payload);
73
+ if (!p)
74
+ return null;
75
+ const repository = repositoryOf(p);
76
+ if (!repository)
77
+ return null;
78
+ if (eventName === "pull_request") {
79
+ if (typeof p.action !== "string" || !PR_ACTIONS.has(p.action))
80
+ return null;
81
+ const pr = obj(p.pull_request);
82
+ const head = obj(pr?.head);
83
+ const prNumber = int(pr?.number);
84
+ const headSha = head?.sha;
85
+ if (!prNumber || typeof headSha !== "string" || !SAFE_SHA.test(headSha))
86
+ return null;
87
+ const headRepo = obj(head?.repo)?.full_name;
88
+ const isFork = typeof headRepo !== "string" || headRepo.toLowerCase() !== repository;
89
+ return { kind: "pr_updated", provider: "github", repository, prNumber, headSha, isFork };
90
+ }
91
+ if (eventName === "check_run") {
92
+ if (p.action !== "rerequested")
93
+ return null;
94
+ const check = obj(p.check_run);
95
+ if (int(obj(check?.app)?.id) !== app.id)
96
+ return null;
97
+ const prNumber = int(obj(Array.isArray(check?.pull_requests) ? check.pull_requests[0] : null)?.number);
98
+ const headSha = check?.head_sha;
99
+ const checkName = check?.name;
100
+ if (!prNumber || typeof headSha !== "string" || !SAFE_SHA.test(headSha) || typeof checkName !== "string")
101
+ return null;
102
+ return { kind: "check_rerun", provider: "github", repository, prNumber, headSha, checkName };
103
+ }
104
+ if (eventName === "issues") {
105
+ if (p.action !== "opened" && p.action !== "edited")
106
+ return null;
107
+ const issue = obj(p.issue);
108
+ const number = int(issue?.number);
109
+ const title = issue?.title;
110
+ if (!number || typeof title !== "string")
111
+ return null;
112
+ const body = text(issue?.body);
113
+ const user = obj(issue?.user);
114
+ if (!trustedHuman(issue?.author_association, user))
115
+ return null;
116
+ if (!mentions(title, app.slug) && !mentions(body, app.slug))
117
+ return null;
118
+ if (p.action === "edited") {
119
+ // Only the author's own edit, and only one that newly adds the mention — not every later edit.
120
+ const sender = obj(p.sender);
121
+ if (sender?.type !== "User" || sender.login !== user.login)
122
+ return null;
123
+ const changes = obj(p.changes);
124
+ const oldTitle = obj(changes?.title)?.from;
125
+ const oldBody = obj(changes?.body)?.from;
126
+ if (typeof oldTitle !== "string" && typeof oldBody !== "string")
127
+ return null;
128
+ const before = [typeof oldTitle === "string" ? oldTitle : title, typeof oldBody === "string" ? oldBody : body];
129
+ if (before.some((t) => mentions(t, app.slug)))
130
+ return null;
131
+ }
132
+ return {
133
+ kind: "mention",
134
+ provider: "github",
135
+ repository,
136
+ number,
137
+ isPullRequest: obj(issue?.pull_request) !== null,
138
+ comment: { kind: "subject", id: String(number) },
139
+ body,
140
+ author: user.login,
141
+ authorId: String(user.id),
142
+ subject: { title, body },
143
+ };
144
+ }
145
+ if (eventName === "issue_comment" || eventName === "pull_request_review_comment") {
146
+ if (p.action !== "created")
147
+ return null;
148
+ const comment = obj(p.comment);
149
+ const user = obj(comment?.user);
150
+ const body = comment?.body;
151
+ const commentId = int(comment?.id);
152
+ if (typeof body !== "string" || !commentId || !mentions(body, app.slug))
153
+ return null;
154
+ if (!trustedHuman(comment?.author_association, user))
155
+ return null;
156
+ const isInline = eventName === "pull_request_review_comment";
157
+ const parent = obj(isInline ? p.pull_request : p.issue);
158
+ const number = int(parent?.number);
159
+ if (!number)
160
+ return null;
161
+ const isPullRequest = isInline || obj(parent?.pull_request) !== null;
162
+ const subject = subjectOf(parent);
163
+ const priorRunId = isPullRequest ? priorRunIdOf(parent, app.slug) : undefined;
164
+ return {
165
+ kind: "mention",
166
+ provider: "github",
167
+ repository,
168
+ number,
169
+ isPullRequest,
170
+ comment: { kind: isInline ? "inline" : "conversation", id: String(commentId) },
171
+ ...(isInline ? { replyToReviewCommentId: String(commentId) } : {}),
172
+ body,
173
+ author: user.login,
174
+ authorId: String(user.id),
175
+ ...(subject ? { subject } : {}),
176
+ ...(priorRunId ? { priorRunId } : {}),
177
+ };
178
+ }
179
+ return null;
180
+ }
@@ -0,0 +1,38 @@
1
+ import type { GitHubAppClient } from "../vcs/github.js";
2
+ import { type HostUserAuthorizer } from "./types.js";
3
+ type Fetch = typeof globalThis.fetch;
4
+ export interface GitHubUserAuthorizerOptions {
5
+ /** The App's API client; `GET /user` and the revocation go through its requestJson. */
6
+ client: Pick<GitHubAppClient, "requestJson">;
7
+ /** The App's OAuth Client ID (not the numeric App ID). */
8
+ clientId: string;
9
+ clientSecret: string;
10
+ /** Tests only: the web host that serves /login/oauth/*. */
11
+ webBaseUrl?: string;
12
+ /** Tests only: the fetch used for the web host. */
13
+ fetch?: Fetch;
14
+ }
15
+ export declare class GitHubUserAuthorizer implements HostUserAuthorizer {
16
+ private readonly options;
17
+ readonly provider: "github";
18
+ private readonly webBaseUrl;
19
+ private readonly fetchImpl;
20
+ constructor(options: GitHubUserAuthorizerOptions);
21
+ authorizeUrl(input: {
22
+ state: string;
23
+ codeChallenge: string;
24
+ redirectUri: string;
25
+ }): string;
26
+ complete(input: {
27
+ code: string;
28
+ codeVerifier: string;
29
+ redirectUri: string;
30
+ }): Promise<{
31
+ hostUserId: string;
32
+ login: string;
33
+ }>;
34
+ private exchange;
35
+ /** DELETE /applications/{client_id}/token (Basic auth with the client credentials). Never throws. */
36
+ private revoke;
37
+ }
38
+ export {};
@@ -0,0 +1,128 @@
1
+ /**
2
+ * GitHub App user authorization (the OAuth web flow), used only to learn a
3
+ * wardby principal's GitHub identity for link_host_account. The user token is
4
+ * never stored, logged, or returned: `complete` exchanges the code (with the
5
+ * PKCE verifier and the App's client secret), reads `GET /user`, and revokes
6
+ * the token immediately, on every path. Repository permission checks use the
7
+ * App's installation token instead (GitHubReviewHost.repositoryPermission).
8
+ * See docs/private/2026-09-26-repo-access-authorization-spec-and-plan.md §3.
9
+ */
10
+ import { logger } from "../../core/logger.js";
11
+ import { ReviewHostError } from "./types.js";
12
+ const log = logger.child({ module: "github-user-auth" });
13
+ const DEFAULT_WEB_BASE_URL = "https://github.com";
14
+ const GITHUB_LOGIN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
15
+ /** GitHub user-to-server tokens (ghu_…) are opaque; accept a bounded, header-safe shape only. */
16
+ const SAFE_TOKEN = /^[A-Za-z0-9_.-]{20,512}$/;
17
+ const SAFE_ERROR_CODE = /^[a-z_]{1,64}$/;
18
+ export class GitHubUserAuthorizer {
19
+ options;
20
+ provider = "github";
21
+ webBaseUrl;
22
+ fetchImpl;
23
+ constructor(options) {
24
+ this.options = options;
25
+ const web = new URL(options.webBaseUrl ?? DEFAULT_WEB_BASE_URL);
26
+ if (web.protocol !== "https:" || web.pathname !== "/" || web.search || web.username || web.password) {
27
+ throw new Error("github_web_base_url_invalid");
28
+ }
29
+ this.webBaseUrl = web.origin;
30
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
31
+ }
32
+ authorizeUrl(input) {
33
+ const url = new URL("/login/oauth/authorize", this.webBaseUrl);
34
+ url.search = new URLSearchParams({
35
+ client_id: this.options.clientId,
36
+ redirect_uri: input.redirectUri,
37
+ state: input.state,
38
+ code_challenge: input.codeChallenge,
39
+ code_challenge_method: "S256",
40
+ // The link is for an existing GitHub account, never a new one.
41
+ allow_signup: "false",
42
+ // Lets someone signed in to several accounts pick the right one.
43
+ prompt: "select_account",
44
+ }).toString();
45
+ return url.toString();
46
+ }
47
+ async complete(input) {
48
+ const token = await this.exchange(input);
49
+ try {
50
+ const response = await this.options.client.requestJson("/user", token);
51
+ const user = (await response.json());
52
+ const id = user?.id;
53
+ const login = user?.login;
54
+ if (user?.type !== "User" ||
55
+ !Number.isSafeInteger(id) ||
56
+ id <= 0 ||
57
+ typeof login !== "string" ||
58
+ !GITHUB_LOGIN.test(login)) {
59
+ throw new ReviewHostError("host_invalid_response", "github_user_invalid");
60
+ }
61
+ return { hostUserId: String(id), login };
62
+ }
63
+ catch (err) {
64
+ if (err instanceof ReviewHostError)
65
+ throw err;
66
+ const message = err instanceof Error && /^github_[a-z_]+(:\d{3})?/.test(err.message) ? err.message : "";
67
+ throw new ReviewHostError("host_api_error", message.split(":").slice(0, 2).join(":") || "github_user_failed");
68
+ }
69
+ finally {
70
+ await this.revoke(token);
71
+ }
72
+ }
73
+ async exchange(input) {
74
+ let response;
75
+ try {
76
+ response = await this.fetchImpl(`${this.webBaseUrl}/login/oauth/access_token`, {
77
+ method: "POST",
78
+ headers: { accept: "application/json", "content-type": "application/json", "user-agent": "wardby" },
79
+ body: JSON.stringify({
80
+ client_id: this.options.clientId,
81
+ client_secret: this.options.clientSecret,
82
+ code: input.code,
83
+ code_verifier: input.codeVerifier,
84
+ redirect_uri: input.redirectUri,
85
+ }),
86
+ });
87
+ }
88
+ catch {
89
+ throw new ReviewHostError("host_api_error", "github_oauth_unavailable");
90
+ }
91
+ if (response.status !== 200) {
92
+ throw new ReviewHostError("host_api_error", `github_oauth_exchange_failed:${response.status}`);
93
+ }
94
+ let payload = null;
95
+ try {
96
+ payload = (await response.json());
97
+ }
98
+ catch {
99
+ // handled below
100
+ }
101
+ // GitHub reports a refused exchange (bad or expired code, wrong verifier) as a 200 with `error`.
102
+ if (payload && typeof payload.error === "string") {
103
+ const code = SAFE_ERROR_CODE.test(payload.error) ? payload.error : "unknown";
104
+ throw new ReviewHostError("host_api_error", `github_oauth_exchange_failed:${code}`);
105
+ }
106
+ const token = payload?.access_token;
107
+ if (typeof token !== "string" || !SAFE_TOKEN.test(token)) {
108
+ throw new ReviewHostError("host_invalid_response", "github_oauth_token_invalid");
109
+ }
110
+ return token;
111
+ }
112
+ /** DELETE /applications/{client_id}/token (Basic auth with the client credentials). Never throws. */
113
+ async revoke(token) {
114
+ const basic = Buffer.from(`${this.options.clientId}:${this.options.clientSecret}`).toString("base64");
115
+ try {
116
+ await this.options.client.requestJson(`/applications/${encodeURIComponent(this.options.clientId)}/token`,
117
+ // requestJson always sets a bearer; the explicit header below replaces it.
118
+ "revocation", {
119
+ method: "DELETE",
120
+ headers: { authorization: `Basic ${basic}` },
121
+ body: JSON.stringify({ access_token: token }),
122
+ }, [204]);
123
+ }
124
+ catch (err) {
125
+ log.warn({ reason: err instanceof Error ? err.message.split(":").slice(0, 2).join(":") : "unknown" }, "could not revoke a GitHub user token; it expires on its own");
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * GitHub implementation of CodeReviewHost, acting as the wardby GitHub App.
3
+ * Each method mints its own installation token scoped to one repository and
4
+ * the narrowest permission set that call needs (GitHubAppClient.withScopedToken),
5
+ * and revokes it afterwards. See docs/private/2026-09-25-code-review-host-design.md §5.1.
6
+ */
7
+ import type { GitHubAppClient } from "../vcs/github.js";
8
+ import { ReviewHostError, type CodeReviewHost, type CommentInput, type CommentRef, type CompleteCheckInput, type EditCommentInput, type FileListView, type FileReadResult, type HostPermission, type HostUser, type PublishReviewInput, type PublishReviewResult, type PullRequestHead, type PullRequestView, type StartCheckInput } from "./types.js";
9
+ type Json = Record<string, unknown>;
10
+ /** Maps GitHubAppClient's fixed error messages onto host-neutral codes; keeps the body-free message. */
11
+ export declare function toReviewHostError(err: unknown): ReviewHostError;
12
+ type GraphQl = (query: string, variables: Record<string, unknown>) => Promise<Json>;
13
+ export declare class GitHubReviewHost implements CodeReviewHost {
14
+ private readonly client;
15
+ readonly provider: "github";
16
+ constructor(client: GitHubAppClient);
17
+ /** Runs `action` with a token for exactly `permissions`, translating failures to ReviewHostError. */
18
+ protected withToken<T>(repository: string, permissions: Record<string, "read" | "write">, action: (get: (path: string, init?: RequestInit, expected?: number[]) => Promise<Response>, graphql: GraphQl) => Promise<T>): Promise<T>;
19
+ repositoryPermission(repository: string, user: HostUser): Promise<{
20
+ level: HostPermission;
21
+ login: string;
22
+ }>;
23
+ pullRequestHead(repository: string, prNumber: number): Promise<PullRequestHead>;
24
+ private head;
25
+ readPullRequest(repository: string, prNumber: number, opts: {
26
+ sinceSha?: string;
27
+ maxPatchChars: number;
28
+ agentMarker: string;
29
+ }): Promise<PullRequestView>;
30
+ readFile(repository: string, path: string, ref: string | undefined, window: {
31
+ startLine: number;
32
+ maxLines: number;
33
+ }): Promise<FileReadResult>;
34
+ listFiles(repository: string, ref: string | undefined, pathPrefix?: string): Promise<FileListView>;
35
+ publishReview(repository: string, input: PublishReviewInput): Promise<PublishReviewResult>;
36
+ /**
37
+ * Resolves the requested threads that are still this agent's own open
38
+ * threads on the PR, each with its own GraphQL call; everything else is
39
+ * skipped. Never throws: the review is already published, so a refusal
40
+ * (for example an App without Contents write) only skips.
41
+ */
42
+ private resolveOwnThreads;
43
+ private publishOnly;
44
+ private findSummaryComment;
45
+ private patchCheck;
46
+ comment(repository: string, input: CommentInput): Promise<{
47
+ url: string;
48
+ id: string;
49
+ }>;
50
+ editComment(repository: string, input: EditCommentInput): Promise<void>;
51
+ acknowledge(repository: string, target: CommentRef): Promise<void>;
52
+ startCheck(repository: string, input: StartCheckInput): Promise<{
53
+ checkId: string;
54
+ }>;
55
+ completeCheck(repository: string, input: CompleteCheckInput): Promise<void>;
56
+ }
57
+ export {};