@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
@@ -8,6 +8,18 @@ export interface PullRequestTestResult {
8
8
  command: string;
9
9
  outcome: "passed" | "failed" | "skipped";
10
10
  }
11
+ /** One package the registry proxy served during the run (RegistryFetch outcome "served"). */
12
+ export interface PackageReport {
13
+ ecosystem: string;
14
+ name: string;
15
+ version: string;
16
+ }
17
+ /** One package the registry proxy refused during the run (RegistryFetch outcome "refused"). */
18
+ export interface PackageRefusal {
19
+ ecosystem: string;
20
+ name: string;
21
+ reason: string;
22
+ }
11
23
  export interface PullRequestInput {
12
24
  runId: string;
13
25
  repository: string;
@@ -17,6 +29,17 @@ export interface PullRequestInput {
17
29
  summary?: string;
18
30
  tests?: readonly PullRequestTestResult[];
19
31
  tag?: string;
32
+ /** Deduplicated by the caller isn't required — pullRequestBody dedupes itself (ecosystem+name+version / +reason). */
33
+ packages?: readonly PackageReport[];
34
+ packageRefusals?: readonly PackageRefusal[];
35
+ /**
36
+ * A continuation re-finds the PR its root run opened, and a person may
37
+ * have marked that PR ready for review since. The push has already updated
38
+ * it, so it is accepted as found instead of failing the run as
39
+ * `github_pull_request_not_draft`. A PR this client creates is always a
40
+ * draft either way; unset (a fresh run), only a draft PR is accepted.
41
+ */
42
+ acceptReadyForReview?: boolean;
20
43
  }
21
44
  export interface PullRequestResult {
22
45
  number: number;
@@ -42,6 +65,15 @@ export interface ContinuationCheckRunInput {
42
65
  export interface ContinuationCheckRunCompleteInput extends ContinuationCheckRunInput {
43
66
  outcome: "succeeded" | "failed";
44
67
  }
68
+ /** One file at one ref, read through the API without a clone. */
69
+ export interface RepositoryFileInput {
70
+ repository: string;
71
+ ref: string;
72
+ /** Repository-relative POSIX path. */
73
+ path: string;
74
+ /** Refused above this many bytes. */
75
+ maxBytes: number;
76
+ }
45
77
  export interface GitHubRepositoryAccess {
46
78
  withRepositoryToken<T>(repository: string, action: (token: string) => Promise<T>): Promise<T>;
47
79
  createOrFindDraftPullRequest(input: PullRequestInput): Promise<PullRequestResult>;
@@ -53,10 +85,22 @@ export interface GitHubRepositoryAccess {
53
85
  createContinuationCheckRun(input: ContinuationCheckRunInput): Promise<void>;
54
86
  /** Completes the check run if one exists for this run+commit; a no-op otherwise (never creates). */
55
87
  completeContinuationCheckRun(input: ContinuationCheckRunCompleteInput): Promise<void>;
88
+ /**
89
+ * The raw text of one file at a ref, or null when it does not exist (a
90
+ * Contents: read token). Optional so test doubles and non-GitHub providers
91
+ * can omit it: coding-run services then read no declaration.
92
+ */
93
+ readFileAtRef?(input: RepositoryFileInput): Promise<string | null>;
56
94
  }
57
95
  type Fetch = typeof globalThis.fetch;
58
96
  /** Accepts bounded opaque installation tokens without allowing control characters. */
59
97
  export declare function isSafeGitHubInstallationToken(value: unknown): value is string;
98
+ /** At most this many packages, and separately this many refusals, are listed
99
+ * in a PR body; the rest are counted in a "…and N more" line pointing at
100
+ * get_run, which returns the full list. */
101
+ export declare const MAX_PR_PACKAGE_LINES = 100;
102
+ /** The hidden run marker stays first and unconditional: createOrFindDraftPullRequest's idempotent lookup depends on it. */
103
+ export declare function pullRequestBody(input: PullRequestInput): string;
60
104
  export declare class GitHubAppClient implements GitHubRepositoryAccess {
61
105
  private readonly config;
62
106
  private readonly fetchImpl;
@@ -78,6 +122,28 @@ export declare class GitHubAppClient implements GitHubRepositoryAccess {
78
122
  * apparently isn't gated the same way a genuine issue comment might be.)
79
123
  */
80
124
  private withChecksToken;
125
+ /**
126
+ * Mints an installation token for exactly `permissions` on one repository,
127
+ * runs `action`, and always revokes. The building block for the
128
+ * code-review host (providers/review-host/github.ts), whose calls each
129
+ * request only what they need.
130
+ */
131
+ withScopedToken<T>(repository: string, permissions: Record<string, "read" | "write">, action: (token: string) => Promise<T>): Promise<T>;
132
+ /**
133
+ * One file's raw text at a ref, without a clone (a Contents: read token).
134
+ * Requests the raw media type; GitHub honors it for an ordinary file but
135
+ * falls back to a JSON metadata response for anything else the raw type
136
+ * doesn't apply to -- a directory listing (a JSON array) or a symlink /
137
+ * submodule (a JSON object whose `type` isn't "file"). Either of those, or
138
+ * a 404, means there's no file text to return.
139
+ */
140
+ readFileAtRef(input: RepositoryFileInput): Promise<string | null>;
141
+ private identity?;
142
+ /** The App's id and slug (its @-mention handle), read once with the App JWT. */
143
+ appIdentity(): Promise<{
144
+ id: number;
145
+ slug: string;
146
+ }>;
81
147
  upsertContinuationStatusComment(input: ContinuationStatusCommentInput): Promise<void>;
82
148
  updateContinuationStatusComment(input: ContinuationStatusCommentInput): Promise<void>;
83
149
  createContinuationCheckRun(input: ContinuationCheckRunInput): Promise<void>;
@@ -85,13 +151,12 @@ export declare class GitHubAppClient implements GitHubRepositoryAccess {
85
151
  createOrFindDraftPullRequest(input: PullRequestInput): Promise<PullRequestResult>;
86
152
  private appJwt;
87
153
  private mintRepositoryToken;
88
- private mintChecksToken;
89
154
  /**
90
155
  * Shared installation-token mint: requests exactly `permissions`, then
91
156
  * verifies the response granted exactly that (no more, no less, modulo
92
157
  * the always-implicit `metadata: read`) before trusting the token. Each
93
158
  * caller requests the narrowest permission set it needs — see
94
- * withIssuesToken/withChecksToken for why these are minted separately
159
+ * withScopedToken/withChecksToken for why these are minted separately
95
160
  * from withRepositoryToken rather than requesting a union of everything.
96
161
  */
97
162
  private mintScopedToken;
@@ -100,6 +165,12 @@ export declare class GitHubAppClient implements GitHubRepositoryAccess {
100
165
  private parsePullRequest;
101
166
  private findStatusComment;
102
167
  private findCheckRun;
103
- private requestJson;
168
+ /** Public for the code-review host, which issues its own REST calls with tokens minted here. */
169
+ requestJson(path: string, bearer: string, init?: RequestInit, expectedStatuses?: number[]): Promise<Response>;
170
+ /**
171
+ * Public for the code-review host: one GraphQL request, for what REST lacks
172
+ * (review threads). A response carrying `errors` fails like a REST error.
173
+ */
174
+ graphql(bearer: string, query: string, variables: Record<string, unknown>): Promise<Record<string, unknown>>;
104
175
  }
105
176
  export {};
@@ -11,6 +11,7 @@ const SAFE_PULL_REQUEST_TAG = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,31}$/;
11
11
  /** Mirrors the runId shape validated elsewhere (coding/protocol.ts, git.ts) — validated independently here too. */
12
12
  const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
13
13
  const SAFE_COMMIT_SHA = /^[0-9a-f]{40}$/;
14
+ const SAFE_FILE_PATH = /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/;
14
15
  function record(value) {
15
16
  if (!value || typeof value !== "object" || Array.isArray(value))
16
17
  throw new Error("github_api_invalid_response");
@@ -35,14 +36,88 @@ export function isSafeGitHubInstallationToken(value) {
35
36
  function pullRequestTitle(input) {
36
37
  return input.tag ? `[${input.tag}] Wardby run ${input.runId}` : `Wardby run ${input.runId}`;
37
38
  }
39
+ /**
40
+ * Package names/versions/reasons come from registry metadata, not from
41
+ * anything Wardby validated, and land inside inline code spans in the PR
42
+ * body. A backtick or newline in one of those fields could break out of the
43
+ * span or the line structure, so any entry containing either is dropped
44
+ * rather than escaped (npm/PyPI names can't contain backticks in practice,
45
+ * so this should never trigger — it's a cheap belt-and-suspenders guard).
46
+ */
47
+ function isSafeMarkdownFragment(value) {
48
+ return !value.includes("`") && !value.includes("\n") && !value.includes("\r");
49
+ }
50
+ /** At most this many packages, and separately this many refusals, are listed
51
+ * in a PR body; the rest are counted in a "…and N more" line pointing at
52
+ * get_run, which returns the full list. */
53
+ export const MAX_PR_PACKAGE_LINES = 100;
54
+ /** A hard ceiling on each list's rendered size, whatever the entries
55
+ * contain, so the section can never push the body past GitHub's limit. */
56
+ const MAX_PR_PACKAGE_LIST_CHARS = 16 * 1024;
57
+ /** Renders at most MAX_PR_PACKAGE_LINES lines (and at most
58
+ * MAX_PR_PACKAGE_LIST_CHARS characters) of `lines`, then a count of the rest. */
59
+ function cappedList(lines) {
60
+ const shown = [];
61
+ let chars = 0;
62
+ for (const line of lines.slice(0, MAX_PR_PACKAGE_LINES)) {
63
+ if (chars + line.length + 1 > MAX_PR_PACKAGE_LIST_CHARS)
64
+ break;
65
+ shown.push(line);
66
+ chars += line.length + 1;
67
+ }
68
+ const more = lines.length - shown.length;
69
+ return more > 0 ? [...shown, `- …and ${more} more — see get_run for the full list`] : shown;
70
+ }
71
+ function packagesSection(input) {
72
+ const packages = [
73
+ ...new Map((input.packages ?? [])
74
+ .filter((pkg) => isSafeMarkdownFragment(pkg.ecosystem) &&
75
+ isSafeMarkdownFragment(pkg.name) &&
76
+ isSafeMarkdownFragment(pkg.version))
77
+ .map((pkg) => [`${pkg.ecosystem}\0${pkg.name}\0${pkg.version}`, pkg])).values(),
78
+ ];
79
+ const refusals = [
80
+ ...new Map((input.packageRefusals ?? [])
81
+ .filter((refusal) => isSafeMarkdownFragment(refusal.ecosystem) &&
82
+ isSafeMarkdownFragment(refusal.name) &&
83
+ isSafeMarkdownFragment(refusal.reason))
84
+ .map((refusal) => [`${refusal.ecosystem}\0${refusal.name}\0${refusal.reason}`, refusal])).values(),
85
+ ];
86
+ if (packages.length === 0 && refusals.length === 0)
87
+ return undefined;
88
+ return [
89
+ "<details>",
90
+ `<summary>Packages installed during this run (${packages.length})</summary>`,
91
+ "",
92
+ ...cappedList(packages.map((pkg) => `- ${pkg.ecosystem} \`${pkg.name}@${pkg.version}\``)),
93
+ ...(refusals.length > 0
94
+ ? [
95
+ "",
96
+ "**Refused:**",
97
+ ...cappedList(refusals.map((refusal) => `- ${refusal.ecosystem} \`${refusal.name}\`: ${refusal.reason}`)),
98
+ ]
99
+ : []),
100
+ "</details>",
101
+ ].join("\n");
102
+ }
38
103
  /** The hidden run marker stays first and unconditional: createOrFindDraftPullRequest's idempotent lookup depends on it. */
39
- function pullRequestBody(input) {
104
+ export function pullRequestBody(input) {
40
105
  const sections = [`${RUN_MARKER_PREFIX}${input.runId} -->`];
41
106
  if (input.summary)
42
107
  sections.push(input.summary);
43
108
  if (input.tests?.length) {
44
109
  sections.push(["**Tests:**", ...input.tests.map((test) => `- \`${test.command}\`: ${test.outcome}`)].join("\n"));
45
110
  }
111
+ // The packages section is informational: a failure rendering it (e.g. a
112
+ // malformed report) omits the section and never fails finalization.
113
+ try {
114
+ const packages = packagesSection(input);
115
+ if (packages)
116
+ sections.push(packages);
117
+ }
118
+ catch {
119
+ // omitted
120
+ }
46
121
  return sections.join("\n\n");
47
122
  }
48
123
  /** The hidden marker stays first and unconditional: findStatusComment's lookup depends on it. */
@@ -108,8 +183,17 @@ export class GitHubAppClient {
108
183
  * apparently isn't gated the same way a genuine issue comment might be.)
109
184
  */
110
185
  async withChecksToken(repository, action) {
186
+ return this.withScopedToken(repository, { checks: "write" }, action);
187
+ }
188
+ /**
189
+ * Mints an installation token for exactly `permissions` on one repository,
190
+ * runs `action`, and always revokes. The building block for the
191
+ * code-review host (providers/review-host/github.ts), whose calls each
192
+ * request only what they need.
193
+ */
194
+ async withScopedToken(repository, permissions, action) {
111
195
  const normalized = normalizeGitHubRepository(repository);
112
- const token = await this.mintChecksToken(normalized);
196
+ const token = await this.mintScopedToken(normalized, permissions, (name, level) => (name === "metadata" && level === "read") || permissions[name] === level);
113
197
  try {
114
198
  return await action(token);
115
199
  }
@@ -117,6 +201,74 @@ export class GitHubAppClient {
117
201
  await this.revokeToken(token).catch(() => undefined);
118
202
  }
119
203
  }
204
+ /**
205
+ * One file's raw text at a ref, without a clone (a Contents: read token).
206
+ * Requests the raw media type; GitHub honors it for an ordinary file but
207
+ * falls back to a JSON metadata response for anything else the raw type
208
+ * doesn't apply to -- a directory listing (a JSON array) or a symlink /
209
+ * submodule (a JSON object whose `type` isn't "file"). Either of those, or
210
+ * a 404, means there's no file text to return.
211
+ */
212
+ async readFileAtRef(input) {
213
+ if (!SAFE_FILE_PATH.test(input.path) || input.path.split("/").some((part) => part === "." || part === "..")) {
214
+ throw new Error("github_file_path_invalid");
215
+ }
216
+ const repository = normalizeGitHubRepository(input.repository);
217
+ const ref = normalizeGitRef(input.ref);
218
+ const [owner, name] = repository.split("/");
219
+ const path = input.path.split("/").map(encodeURIComponent).join("/");
220
+ return this.withScopedToken(repository, { contents: "read" }, async (token) => {
221
+ const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/contents/${path}?ref=${encodeURIComponent(ref)}`, token, { headers: { accept: "application/vnd.github.raw+json" } }, [200, 404]);
222
+ if (response.status === 404)
223
+ return null;
224
+ const declared = Number(response.headers.get("content-length") ?? Number.NaN);
225
+ if (Number.isFinite(declared) && declared > input.maxBytes)
226
+ throw new Error("github_file_too_large");
227
+ const bytes = new Uint8Array(await response.arrayBuffer());
228
+ if (bytes.byteLength > input.maxBytes)
229
+ throw new Error("github_file_too_large");
230
+ let text;
231
+ try {
232
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
233
+ }
234
+ catch {
235
+ throw new Error("github_file_not_utf8");
236
+ }
237
+ // Not raw content check: try to read the body as GitHub's own
238
+ // metadata shape rather than trusting the content-type header, since
239
+ // the raw media type itself is "application/vnd.github.raw+json" --
240
+ // a substring check for "json" would misfire on every real file.
241
+ let metadata;
242
+ try {
243
+ metadata = JSON.parse(text);
244
+ }
245
+ catch {
246
+ return text;
247
+ }
248
+ if (Array.isArray(metadata))
249
+ throw new Error("github_file_not_a_file");
250
+ if (metadata && typeof metadata === "object" && "type" in metadata && metadata.type !== "file") {
251
+ throw new Error("github_file_not_a_file");
252
+ }
253
+ return text;
254
+ });
255
+ }
256
+ identity;
257
+ /** The App's id and slug (its @-mention handle), read once with the App JWT. */
258
+ appIdentity() {
259
+ this.identity ??= (async () => {
260
+ const response = await this.requestJson("/app", await this.appJwt());
261
+ const payload = record(await response.json());
262
+ if (typeof payload.slug !== "string" || !/^[a-z0-9-]{1,64}$/.test(payload.slug)) {
263
+ throw new Error("github_api_invalid_response");
264
+ }
265
+ return { id: positiveInteger(payload.id), slug: payload.slug };
266
+ })().catch((err) => {
267
+ this.identity = undefined;
268
+ throw err;
269
+ });
270
+ return this.identity;
271
+ }
120
272
  async upsertContinuationStatusComment(input) {
121
273
  const repository = normalizeGitHubRepository(input.repository);
122
274
  const baseRef = normalizeGitRef(input.baseRef);
@@ -125,7 +277,13 @@ export class GitHubAppClient {
125
277
  throw new Error("github_status_comment_input_invalid");
126
278
  }
127
279
  await this.withRepositoryToken(repository, async (token) => {
128
- const pr = await this.findPullRequest(token, { runId: input.rootRunId, repository, baseRef, headRef });
280
+ const pr = await this.findPullRequest(token, {
281
+ runId: input.rootRunId,
282
+ repository,
283
+ baseRef,
284
+ headRef,
285
+ acceptReadyForReview: true,
286
+ });
129
287
  if (!pr)
130
288
  return;
131
289
  const existing = await this.findStatusComment(token, repository, pr.number, input.runId);
@@ -147,7 +305,13 @@ export class GitHubAppClient {
147
305
  throw new Error("github_status_comment_input_invalid");
148
306
  }
149
307
  await this.withRepositoryToken(repository, async (token) => {
150
- const pr = await this.findPullRequest(token, { runId: input.rootRunId, repository, baseRef, headRef });
308
+ const pr = await this.findPullRequest(token, {
309
+ runId: input.rootRunId,
310
+ repository,
311
+ baseRef,
312
+ headRef,
313
+ acceptReadyForReview: true,
314
+ });
151
315
  if (!pr)
152
316
  return;
153
317
  const existing = await this.findStatusComment(token, repository, pr.number, input.runId);
@@ -253,24 +417,27 @@ export class GitHubAppClient {
253
417
  (name === "pull_requests" && level === "write") ||
254
418
  (name === "metadata" && level === "read"));
255
419
  }
256
- async mintChecksToken(repository) {
257
- return this.mintScopedToken(repository, { checks: "write" }, (name, level) => (name === "checks" && level === "write") || (name === "metadata" && level === "read"));
258
- }
259
420
  /**
260
421
  * Shared installation-token mint: requests exactly `permissions`, then
261
422
  * verifies the response granted exactly that (no more, no less, modulo
262
423
  * the always-implicit `metadata: read`) before trusting the token. Each
263
424
  * caller requests the narrowest permission set it needs — see
264
- * withIssuesToken/withChecksToken for why these are minted separately
425
+ * withScopedToken/withChecksToken for why these are minted separately
265
426
  * from withRepositoryToken rather than requesting a union of everything.
266
427
  */
267
428
  async mintScopedToken(repository, permissions, isAllowedPermission) {
268
429
  const [owner, name] = repository.split("/");
269
430
  const appJwt = await this.appJwt();
270
- const installationResponse = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/installation`, appJwt);
431
+ const installationResponse = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/installation`, appJwt, {}, [200, 404]);
432
+ if (installationResponse.status === 404)
433
+ throw new Error("github_app_not_installed");
271
434
  const installation = record(await installationResponse.json());
272
435
  const installationId = positiveInteger(installation.id);
273
- const tokenResponse = await this.requestJson(`/app/installations/${installationId}/access_tokens`, appJwt, { method: "POST", body: JSON.stringify({ repositories: [name], permissions }) }, [201]);
436
+ const tokenResponse = await this.requestJson(`/app/installations/${installationId}/access_tokens`, appJwt, { method: "POST", body: JSON.stringify({ repositories: [name], permissions }) }, [201, 422]);
437
+ // 422: the installation has not granted a requested permission (e.g. a
438
+ // new permission the owner has not accepted yet).
439
+ if (tokenResponse.status === 422)
440
+ throw new Error("github_installation_token_scope_invalid");
274
441
  const payload = record(await tokenResponse.json());
275
442
  const grantedPermissions = record(payload.permissions);
276
443
  const repositories = Array.isArray(payload.repositories) ? payload.repositories.map(record) : [];
@@ -307,17 +474,15 @@ export class GitHubAppClient {
307
474
  for (const item of payload) {
308
475
  const candidate = record(item);
309
476
  if (typeof candidate.body === "string" && candidate.body.includes(marker)) {
310
- if (candidate.draft !== true)
311
- throw new Error("github_pull_request_not_draft");
312
- return this.parsePullRequest(candidate, input.repository);
477
+ return this.parsePullRequest(candidate, input.repository, input.acceptReadyForReview === true);
313
478
  }
314
479
  }
315
480
  return null;
316
481
  }
317
- parsePullRequest(value, repository) {
482
+ parsePullRequest(value, repository, acceptReadyForReview = false) {
318
483
  const payload = record(value);
319
484
  const number = positiveInteger(payload.number);
320
- if (payload.draft !== true)
485
+ if (payload.draft !== true && !acceptReadyForReview)
321
486
  throw new Error("github_pull_request_not_draft");
322
487
  const expectedUrl = `https://github.com/${repository}/pull/${number}`;
323
488
  if (typeof payload.html_url !== "string")
@@ -367,6 +532,7 @@ export class GitHubAppClient {
367
532
  }
368
533
  return null;
369
534
  }
535
+ /** Public for the code-review host, which issues its own REST calls with tokens minted here. */
370
536
  async requestJson(path, bearer, init = {}, expectedStatuses = [200]) {
371
537
  let response;
372
538
  try {
@@ -391,4 +557,18 @@ export class GitHubAppClient {
391
557
  throw safeApiError(response);
392
558
  return response;
393
559
  }
560
+ /**
561
+ * Public for the code-review host: one GraphQL request, for what REST lacks
562
+ * (review threads). A response carrying `errors` fails like a REST error.
563
+ */
564
+ async graphql(bearer, query, variables) {
565
+ const response = await this.requestJson("/graphql", bearer, {
566
+ method: "POST",
567
+ body: JSON.stringify({ query, variables }),
568
+ });
569
+ const payload = record(await response.json());
570
+ if (Array.isArray(payload.errors) && payload.errors.length > 0)
571
+ throw new Error("github_graphql_error");
572
+ return record(payload.data);
573
+ }
394
574
  }
@@ -1,9 +1,12 @@
1
+ import type { RepositoryFileInput } from "./github.js";
1
2
  export interface VcsPrepareInput {
2
3
  runId: string;
3
4
  repository: string;
4
5
  baseRef: string;
5
6
  headRef: string;
6
7
  protectedPaths: string[];
8
+ /** Repository-relative paths never collected, in addition to the built-in names. */
9
+ collectExclude?: string[];
7
10
  /**
8
11
  * Revision-in-place: set when headRef is an EXISTING branch to continue
9
12
  * rather than a fresh one
@@ -38,6 +41,7 @@ export interface PreparedWorkspace {
38
41
  workspacePath: string;
39
42
  gitMetadataPath: string;
40
43
  protectedPaths: string[];
44
+ collectExclude: string[];
41
45
  continuation?: {
42
46
  rootRunId: string;
43
47
  };
@@ -50,6 +54,17 @@ export interface FinalizeChangesDetails {
50
54
  outcome: "passed" | "failed" | "skipped";
51
55
  }[];
52
56
  tag?: string;
57
+ /** Packages the registry proxy served/refused during this run (RegistryFetch), for the PR body. */
58
+ packages?: readonly {
59
+ ecosystem: string;
60
+ name: string;
61
+ version: string;
62
+ }[];
63
+ packageRefusals?: readonly {
64
+ ecosystem: string;
65
+ name: string;
66
+ reason: string;
67
+ }[];
53
68
  }
54
69
  export type FinalizeChangesResult = {
55
70
  outcome: "no_changes";
@@ -110,9 +125,32 @@ export interface VcsProvider {
110
125
  * (the same text that goes in the PR body -- see
111
126
  * `FinalizeChangesDetails.summary`) so the "done" status reflects what
112
127
  * actually happened instead of a caller having to go find out.
128
+ * `budget_exhausted` is a failure that ran out of budget;
129
+ * `details.budgetSentence` then says how (see core/budget-wording.ts).
130
+ * A `failed` run the model provider refused carries `details.providerSentence`.
131
+ * A `failed` run whose changes touched a protected path carries
132
+ * `details.protectedPathSentence` (see coding/protected-path-wording.ts).
113
133
  */
114
- notifyContinuationFinished?(workspace: PreparedWorkspace, outcome: "succeeded" | "failed", details?: {
115
- summary?: string;
116
- agentName?: string;
117
- }): Promise<void>;
134
+ notifyContinuationFinished?(workspace: PreparedWorkspace, outcome: ContinuationOutcome, details?: ContinuationFinishedDetails): Promise<void>;
135
+ /**
136
+ * The raw text of one repository file at a ref, read through the host's API
137
+ * without a clone, or null when it does not exist. Dispatch reads a coding
138
+ * run's .wardby/services.yaml from its base branch with it
139
+ * (docs/coding-services.md). Optional, mirroring notifyContinuationStarted: a
140
+ * provider (or test double) without it gives runs no services.
141
+ */
142
+ readRepositoryFile?(input: RepositoryFileInput): Promise<string | null>;
143
+ }
144
+ export type ContinuationOutcome = "succeeded" | "failed" | "budget_exhausted";
145
+ export interface ContinuationFinishedDetails {
146
+ summary?: string;
147
+ agentName?: string;
148
+ /** Host-safe sentence naming the run's budget; used with `budget_exhausted`. */
149
+ budgetSentence?: string;
150
+ /** Host-safe sentence naming a model-provider refusal (see core/provider-wording.ts); used with `failed`. */
151
+ providerSentence?: string;
152
+ /** Host-safe sentence naming a coding-run service that never became ready (coding/services/wording.ts); used with `failed`. */
153
+ serviceSentence?: string;
154
+ /** Host-safe sentence naming a protected path the run's changes touched (coding/protected-path-wording.ts); used with `failed`. */
155
+ protectedPathSentence?: string;
118
156
  }
@@ -1,5 +1,4 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { createRequire } from "node:module";
3
2
  import { createServer } from "node:net";
4
3
  import { readFileSync, statSync } from "node:fs";
5
4
  import { join } from "node:path";
@@ -7,12 +6,11 @@ import { fileURLToPath } from "node:url";
7
6
  import { parseArgs } from "node:util";
8
7
  import { createInterface } from "node:readline/promises";
9
8
  import { composeProjectName, defaultModel, ensureWardbyIgnored, generatedSecret, providerKeyName, quickstartPaths, readQuickstartEnv, readQuickstartState, resolveProjectDir, writeQuickstartEnv, writeQuickstartState, } from "./config.js";
9
+ import { FETCH_NOTICE, migrateFailureReason, prismaMigrateInvocation, REGISTRY_FAILURE_MESSAGE, runPrismaMigrateInvocation, } from "./migrate.js";
10
10
  const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
11
11
  const composeFile = join(packageRoot, "deploy/local/docker-compose.yml");
12
- const schemaFile = join(packageRoot, "prisma/schema.prisma");
13
12
  const wardbyBin = join(packageRoot, "bin/wardby.js");
14
13
  const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
15
- const require = createRequire(import.meta.url);
16
14
  const DEMO_AGENT = "hello-wardby";
17
15
  const DEMO_PROMPT = "You are the Wardby quickstart agent. Reply with a short greeting, then explain in one sentence that Wardby admitted this run against an explicit maximum budget. Do not call tools.";
18
16
  function runCommand(command, args, options = {}) {
@@ -200,23 +198,27 @@ async function chooseProvider(requested, previous, existing, nonInteractive) {
200
198
  return "anthropic";
201
199
  throw new Error(`Unknown provider selection "${answer}".`);
202
200
  }
203
- function prismaCliPath() {
204
- return require.resolve("prisma/build/index.js");
205
- }
206
201
  function applyMigrations(paths) {
207
- const result = runCommand(process.execPath, [prismaCliPath(), "migrate", "deploy", "--schema", schemaFile], {
208
- cwd: paths.projectDir,
209
- env: runtimeEnv(paths),
210
- });
202
+ const invocation = prismaMigrateInvocation("deploy", runtimeEnv(paths));
203
+ if (!invocation.local)
204
+ console.log(FETCH_NOTICE);
205
+ const result = runPrismaMigrateInvocation(invocation, paths.projectDir);
206
+ if (result.registryFailure) {
207
+ const detail = /^npm (?:error|ERR!) code .*$/m.exec(result.stderr)?.[0] ?? result.stderr.trim().split("\n")[0];
208
+ throw new Error(`${REGISTRY_FAILURE_MESSAGE}${detail ? ` (${detail})` : ""}`);
209
+ }
211
210
  if (result.status !== 0)
212
211
  commandFailure("Database migration", result);
213
212
  }
214
213
  async function seedDemo(paths, state, budget) {
215
- const previousDatabaseUrl = process.env.DATABASE_URL;
216
- process.env.DATABASE_URL = readQuickstartEnv(paths).DATABASE_URL;
217
- const { PrismaClient } = await import("@prisma/client");
218
- const db = new PrismaClient();
214
+ const { createPrismaClient } = await import("../core/db.js");
215
+ const { resolvePrincipal } = await import("../mcp/auth/principal.js");
216
+ const env = readQuickstartEnv(paths);
217
+ const db = createPrismaClient(env.DATABASE_URL);
219
218
  try {
219
+ // The demo agent belongs to the local operator (LOCAL_PRINCIPAL) and is
220
+ // private: agents are never owner-less (resource-sharing grants).
221
+ const owner = await resolvePrincipal(env.LOCAL_PRINCIPAL || "local", db);
220
222
  const existing = await db.agent.findUnique({ where: { name: DEMO_AGENT } });
221
223
  if (existing && existing.systemPrompt !== DEMO_PROMPT) {
222
224
  throw new Error(`An agent named "${DEMO_AGENT}" already exists and was not created by quickstart; rename it or use --skip-demo.`);
@@ -224,7 +226,12 @@ async function seedDemo(paths, state, budget) {
224
226
  if (existing) {
225
227
  await db.agent.update({
226
228
  where: { name: DEMO_AGENT },
227
- data: { model: state.model, budgetUsd: budget, maxTurns: 2 },
229
+ data: {
230
+ model: state.model,
231
+ budgetUsd: budget,
232
+ maxTurns: 2,
233
+ ...(existing.ownerId ? {} : { ownerId: owner.id }),
234
+ },
228
235
  });
229
236
  }
230
237
  else {
@@ -235,16 +242,13 @@ async function seedDemo(paths, state, budget) {
235
242
  budgetUsd: budget,
236
243
  maxTurns: 2,
237
244
  systemPrompt: DEMO_PROMPT,
245
+ ownerId: owner.id,
238
246
  },
239
247
  });
240
248
  }
241
249
  }
242
250
  finally {
243
251
  await db.$disconnect();
244
- if (previousDatabaseUrl === undefined)
245
- delete process.env.DATABASE_URL;
246
- else
247
- process.env.DATABASE_URL = previousDatabaseUrl;
248
252
  }
249
253
  }
250
254
  function runDemo(paths) {
@@ -419,10 +423,8 @@ async function databaseHealthy(paths) {
419
423
  const env = readQuickstartEnv(paths);
420
424
  if (!env.DATABASE_URL)
421
425
  return false;
422
- const previous = process.env.DATABASE_URL;
423
- process.env.DATABASE_URL = env.DATABASE_URL;
424
- const { PrismaClient } = await import("@prisma/client");
425
- const db = new PrismaClient();
426
+ const { createPrismaClient } = await import("../core/db.js");
427
+ const db = createPrismaClient(env.DATABASE_URL);
426
428
  try {
427
429
  await db.$queryRawUnsafe("SELECT 1");
428
430
  return true;
@@ -432,10 +434,6 @@ async function databaseHealthy(paths) {
432
434
  }
433
435
  finally {
434
436
  await db.$disconnect();
435
- if (previous === undefined)
436
- delete process.env.DATABASE_URL;
437
- else
438
- process.env.DATABASE_URL = previous;
439
437
  }
440
438
  }
441
439
  export async function doctorCommand(args) {
@@ -468,11 +466,15 @@ export async function doctorCommand(args) {
468
466
  const ps = runCompose(paths, state, ["ps", "--status", "running", "--quiet"]);
469
467
  checks.push(["PostgreSQL container", ps.status === 0 && ps.stdout.trim().length > 0]);
470
468
  checks.push(["Database connection", await databaseHealthy(paths)]);
471
- const migrations = runCommand(process.execPath, [prismaCliPath(), "migrate", "status", "--schema", schemaFile], {
472
- cwd: paths.projectDir,
473
- env: runtimeEnv(paths),
474
- });
475
- checks.push(["Database migrations", migrations.status === 0]);
469
+ const invocation = prismaMigrateInvocation("status", runtimeEnv(paths));
470
+ if (!invocation.local)
471
+ console.log(FETCH_NOTICE);
472
+ const migrations = runPrismaMigrateInvocation(invocation, paths.projectDir);
473
+ checks.push([
474
+ "Database migrations",
475
+ migrations.status === 0,
476
+ migrations.status === 0 ? undefined : migrateFailureReason(migrations),
477
+ ]);
476
478
  }
477
479
  let failed = false;
478
480
  for (const [name, ok, detail] of checks) {
@@ -0,0 +1,23 @@
1
+ export declare const PRISMA_CLI_VERSION = "7.10.0";
2
+ export declare const FETCH_NOTICE = "Fetching the Prisma CLI (prisma@7.10.0)\u2026";
3
+ export declare const REGISTRY_FAILURE_MESSAGE: string;
4
+ export declare const migrateConfigFile: string;
5
+ export interface MigrateResult {
6
+ status: number;
7
+ stdout: string;
8
+ stderr: string;
9
+ registryFailure?: boolean;
10
+ }
11
+ export interface MigrateInvocation {
12
+ command: string;
13
+ args: string[];
14
+ env: NodeJS.ProcessEnv;
15
+ local: boolean;
16
+ }
17
+ export declare function parseLocalPrismaVersion(stdout: string): string | undefined;
18
+ export declare function probeLocalPrismaVersion(env: NodeJS.ProcessEnv): string | undefined;
19
+ export declare function prismaMigrateInvocation(subcommand: "deploy" | "status", env: NodeJS.ProcessEnv, probe?: (env: NodeJS.ProcessEnv) => string | undefined): MigrateInvocation;
20
+ export declare function classifyMigrateResult(result: MigrateResult, spawnError?: Error, local?: boolean): MigrateResult;
21
+ export declare function migrateFailureReason(result: MigrateResult): string;
22
+ export declare function runPrismaMigrateInvocation(invocation: MigrateInvocation, cwd: string): MigrateResult;
23
+ export declare function runPrismaMigrate(subcommand: "deploy" | "status", env: NodeJS.ProcessEnv, cwd: string, probe?: (env: NodeJS.ProcessEnv) => string | undefined): MigrateResult;