agent-relay 1.2.0 → 1.2.3

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 (540) hide show
  1. package/.gitattributes +3 -0
  2. package/.nvmrc +1 -0
  3. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +65 -0
  4. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +37 -0
  5. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +65 -0
  6. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +37 -0
  7. package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +49 -0
  8. package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +31 -0
  9. package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +113 -0
  10. package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +57 -0
  11. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +53 -0
  12. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +32 -0
  13. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +26 -0
  14. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +6 -0
  15. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +47 -0
  16. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +32 -0
  17. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +53 -0
  18. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +32 -0
  19. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +59 -0
  20. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +33 -0
  21. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +53 -0
  22. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +32 -0
  23. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +48 -0
  24. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +24 -0
  25. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +209 -0
  26. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +97 -0
  27. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +48 -0
  28. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +24 -0
  29. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +49 -0
  30. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +23 -0
  31. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +53 -0
  32. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +32 -0
  33. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +49 -0
  34. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +31 -0
  35. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +26 -0
  36. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +6 -0
  37. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +59 -0
  38. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +37 -0
  39. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +77 -0
  40. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +42 -0
  41. package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +53 -0
  42. package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +32 -0
  43. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +25 -0
  44. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +15 -0
  45. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +101 -0
  46. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +44 -0
  47. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +22 -0
  48. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +5 -0
  49. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +53 -0
  50. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +32 -0
  51. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +25 -0
  52. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +15 -0
  53. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +53 -0
  54. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +32 -0
  55. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +53 -0
  56. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +32 -0
  57. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +48 -0
  58. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +24 -0
  59. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +53 -0
  60. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +32 -0
  61. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +77 -0
  62. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +42 -0
  63. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +53 -0
  64. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +32 -0
  65. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +83 -0
  66. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +47 -0
  67. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +59 -0
  68. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +37 -0
  69. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +48 -0
  70. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +16 -0
  71. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +59 -0
  72. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +37 -0
  73. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +53 -0
  74. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +32 -0
  75. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +84 -0
  76. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +109 -0
  77. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +53 -0
  78. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +32 -0
  79. package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +71 -0
  80. package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +42 -0
  81. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +53 -0
  82. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +32 -0
  83. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +20 -0
  84. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +6 -0
  85. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +175 -0
  86. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +82 -0
  87. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +47 -0
  88. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +32 -0
  89. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +59 -0
  90. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +37 -0
  91. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +53 -0
  92. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +32 -0
  93. package/.trajectories/index.json +314 -0
  94. package/ARCHITECTURE.md +1245 -0
  95. package/TESTING.md +278 -0
  96. package/deploy/init-db.sql +5 -0
  97. package/deploy/scripts/setup-fly-workspaces.sh +69 -0
  98. package/deploy/scripts/setup-railway.sh +75 -0
  99. package/deploy/workspace/entrypoint-browser.sh +118 -0
  100. package/deploy/workspace/entrypoint.sh +348 -0
  101. package/deploy/workspace/git-credential-relay +111 -0
  102. package/dist/cli/index.js +218 -1
  103. package/dist/cloud/api/billing.js +30 -9
  104. package/dist/cloud/api/codex-auth-helper.d.ts +15 -0
  105. package/dist/cloud/api/codex-auth-helper.js +100 -0
  106. package/dist/cloud/api/git.js +24 -3
  107. package/dist/cloud/api/onboarding.js +15 -2
  108. package/dist/cloud/api/repos.d.ts +1 -0
  109. package/dist/cloud/api/repos.js +186 -0
  110. package/dist/cloud/api/webhooks.d.ts +1 -0
  111. package/dist/cloud/api/webhooks.js +149 -0
  112. package/dist/cloud/api/workspaces.js +97 -6
  113. package/dist/cloud/billing/plans.js +19 -19
  114. package/dist/cloud/provisioner/index.d.ts +32 -0
  115. package/dist/cloud/provisioner/index.js +362 -21
  116. package/dist/cloud/server.js +6 -1
  117. package/dist/cloud/services/nango.d.ts +60 -0
  118. package/dist/cloud/services/nango.js +153 -0
  119. package/dist/cloud/services/planLimits.d.ts +22 -0
  120. package/dist/cloud/services/planLimits.js +58 -5
  121. package/dist/dashboard/out/404.html +1 -1
  122. package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +1 -0
  123. package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +1 -0
  124. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-3fdfa60e53f2810d.js +1 -0
  125. package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +1 -0
  126. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-3538dfe0ffe984b8.js +1 -0
  127. package/dist/dashboard/out/_next/static/chunks/app/history/{page-56a8b4616a90dc43.js → page-abb9ab2d329f56e9.js} +1 -1
  128. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
  129. package/dist/dashboard/out/_next/static/chunks/app/login/{page-3eac37ea6f5dd153.js → page-c22d080201cbd9fb.js} +1 -1
  130. package/dist/dashboard/out/_next/static/chunks/app/metrics/{page-1081dd190a331a91.js → page-67a3e98d9a43a6ed.js} +1 -1
  131. package/dist/dashboard/out/_next/static/chunks/app/{page-daf87e86f783f980.js → page-77e9c65420a06cfb.js} +1 -1
  132. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-b08ed1c34d14434a.js +1 -0
  133. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +1 -0
  134. package/dist/dashboard/out/_next/static/chunks/app/signup/{page-fee4ed1709070bcd.js → page-68d34f50baa8ab6b.js} +1 -1
  135. package/dist/dashboard/out/_next/static/chunks/{main-app-5d692157a8eb1fd9.js → main-app-6e8e8d3ef4e0192a.js} +1 -1
  136. package/dist/dashboard/out/_next/static/chunks/{main-97850e03d723ea8c.js → main-ed4e1fb6f29c34cf.js} +1 -1
  137. package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +1 -0
  138. package/dist/dashboard/out/app/onboarding.html +1 -0
  139. package/dist/dashboard/out/app/onboarding.txt +7 -0
  140. package/dist/dashboard/out/app.html +1 -1
  141. package/dist/dashboard/out/app.txt +2 -2
  142. package/dist/dashboard/out/connect-repos.html +1 -1
  143. package/dist/dashboard/out/connect-repos.txt +2 -2
  144. package/dist/dashboard/out/history.html +1 -1
  145. package/dist/dashboard/out/history.txt +2 -2
  146. package/dist/dashboard/out/index.html +1 -1
  147. package/dist/dashboard/out/index.txt +2 -2
  148. package/dist/dashboard/out/login.html +2 -2
  149. package/dist/dashboard/out/login.txt +2 -2
  150. package/dist/dashboard/out/metrics.html +1 -1
  151. package/dist/dashboard/out/metrics.txt +2 -2
  152. package/dist/dashboard/out/pricing.html +3 -3
  153. package/dist/dashboard/out/pricing.txt +2 -2
  154. package/dist/dashboard/out/providers.html +1 -1
  155. package/dist/dashboard/out/providers.txt +2 -2
  156. package/dist/dashboard/out/signup.html +2 -2
  157. package/dist/dashboard/out/signup.txt +2 -2
  158. package/package.json +2 -11
  159. package/test-push.txt +1 -0
  160. package/bin/tmux +0 -0
  161. package/dist/bridge/config.d.ts.map +0 -1
  162. package/dist/bridge/config.js.map +0 -1
  163. package/dist/bridge/index.d.ts.map +0 -1
  164. package/dist/bridge/index.js.map +0 -1
  165. package/dist/bridge/multi-project-client.d.ts.map +0 -1
  166. package/dist/bridge/multi-project-client.js.map +0 -1
  167. package/dist/bridge/shadow-cli.d.ts.map +0 -1
  168. package/dist/bridge/shadow-cli.js.map +0 -1
  169. package/dist/bridge/shadow-config.d.ts.map +0 -1
  170. package/dist/bridge/shadow-config.js.map +0 -1
  171. package/dist/bridge/spawner.d.ts.map +0 -1
  172. package/dist/bridge/spawner.js.map +0 -1
  173. package/dist/bridge/teams-config.d.ts.map +0 -1
  174. package/dist/bridge/teams-config.js.map +0 -1
  175. package/dist/bridge/types.d.ts.map +0 -1
  176. package/dist/bridge/types.js.map +0 -1
  177. package/dist/bridge/utils.d.ts.map +0 -1
  178. package/dist/bridge/utils.js.map +0 -1
  179. package/dist/cli/index.d.ts.map +0 -1
  180. package/dist/cli/index.js.map +0 -1
  181. package/dist/cloud/api/auth.d.ts.map +0 -1
  182. package/dist/cloud/api/auth.js.map +0 -1
  183. package/dist/cloud/api/billing.d.ts.map +0 -1
  184. package/dist/cloud/api/billing.js.map +0 -1
  185. package/dist/cloud/api/cli-pty-runner.d.ts.map +0 -1
  186. package/dist/cloud/api/cli-pty-runner.js.map +0 -1
  187. package/dist/cloud/api/coordinators.d.ts.map +0 -1
  188. package/dist/cloud/api/coordinators.js.map +0 -1
  189. package/dist/cloud/api/daemons.d.ts.map +0 -1
  190. package/dist/cloud/api/daemons.js.map +0 -1
  191. package/dist/cloud/api/generic-webhooks.d.ts.map +0 -1
  192. package/dist/cloud/api/generic-webhooks.js.map +0 -1
  193. package/dist/cloud/api/git.d.ts.map +0 -1
  194. package/dist/cloud/api/git.js.map +0 -1
  195. package/dist/cloud/api/github-app.d.ts.map +0 -1
  196. package/dist/cloud/api/github-app.js.map +0 -1
  197. package/dist/cloud/api/middleware/planLimits.d.ts.map +0 -1
  198. package/dist/cloud/api/middleware/planLimits.js.map +0 -1
  199. package/dist/cloud/api/monitoring.d.ts.map +0 -1
  200. package/dist/cloud/api/monitoring.js.map +0 -1
  201. package/dist/cloud/api/nango-auth.d.ts.map +0 -1
  202. package/dist/cloud/api/nango-auth.js.map +0 -1
  203. package/dist/cloud/api/onboarding.d.ts.map +0 -1
  204. package/dist/cloud/api/onboarding.js.map +0 -1
  205. package/dist/cloud/api/policy.d.ts.map +0 -1
  206. package/dist/cloud/api/policy.js.map +0 -1
  207. package/dist/cloud/api/providers.d.ts.map +0 -1
  208. package/dist/cloud/api/providers.js.map +0 -1
  209. package/dist/cloud/api/repos.d.ts.map +0 -1
  210. package/dist/cloud/api/repos.js.map +0 -1
  211. package/dist/cloud/api/teams.d.ts.map +0 -1
  212. package/dist/cloud/api/teams.js.map +0 -1
  213. package/dist/cloud/api/test-helpers.d.ts.map +0 -1
  214. package/dist/cloud/api/test-helpers.js.map +0 -1
  215. package/dist/cloud/api/usage.d.ts.map +0 -1
  216. package/dist/cloud/api/usage.js.map +0 -1
  217. package/dist/cloud/api/webhooks.d.ts.map +0 -1
  218. package/dist/cloud/api/webhooks.js.map +0 -1
  219. package/dist/cloud/api/workspaces.d.ts.map +0 -1
  220. package/dist/cloud/api/workspaces.js.map +0 -1
  221. package/dist/cloud/billing/index.d.ts.map +0 -1
  222. package/dist/cloud/billing/index.js.map +0 -1
  223. package/dist/cloud/billing/plans.d.ts.map +0 -1
  224. package/dist/cloud/billing/plans.js.map +0 -1
  225. package/dist/cloud/billing/service.d.ts.map +0 -1
  226. package/dist/cloud/billing/service.js.map +0 -1
  227. package/dist/cloud/billing/types.d.ts.map +0 -1
  228. package/dist/cloud/billing/types.js.map +0 -1
  229. package/dist/cloud/config.d.ts.map +0 -1
  230. package/dist/cloud/config.js.map +0 -1
  231. package/dist/cloud/db/drizzle.d.ts.map +0 -1
  232. package/dist/cloud/db/drizzle.js.map +0 -1
  233. package/dist/cloud/db/index.d.ts.map +0 -1
  234. package/dist/cloud/db/index.js.map +0 -1
  235. package/dist/cloud/db/schema.d.ts.map +0 -1
  236. package/dist/cloud/db/schema.js.map +0 -1
  237. package/dist/cloud/index.d.ts.map +0 -1
  238. package/dist/cloud/index.js.map +0 -1
  239. package/dist/cloud/provisioner/index.d.ts.map +0 -1
  240. package/dist/cloud/provisioner/index.js.map +0 -1
  241. package/dist/cloud/server.d.ts.map +0 -1
  242. package/dist/cloud/server.js.map +0 -1
  243. package/dist/cloud/services/auto-scaler.d.ts.map +0 -1
  244. package/dist/cloud/services/auto-scaler.js.map +0 -1
  245. package/dist/cloud/services/capacity-manager.d.ts.map +0 -1
  246. package/dist/cloud/services/capacity-manager.js.map +0 -1
  247. package/dist/cloud/services/ci-agent-spawner.d.ts.map +0 -1
  248. package/dist/cloud/services/ci-agent-spawner.js.map +0 -1
  249. package/dist/cloud/services/coordinator.d.ts.map +0 -1
  250. package/dist/cloud/services/coordinator.js.map +0 -1
  251. package/dist/cloud/services/index.d.ts.map +0 -1
  252. package/dist/cloud/services/index.js.map +0 -1
  253. package/dist/cloud/services/mention-handler.d.ts.map +0 -1
  254. package/dist/cloud/services/mention-handler.js.map +0 -1
  255. package/dist/cloud/services/nango.d.ts.map +0 -1
  256. package/dist/cloud/services/nango.js.map +0 -1
  257. package/dist/cloud/services/persistence.d.ts.map +0 -1
  258. package/dist/cloud/services/persistence.js.map +0 -1
  259. package/dist/cloud/services/planLimits.d.ts.map +0 -1
  260. package/dist/cloud/services/planLimits.js.map +0 -1
  261. package/dist/cloud/services/scaling-orchestrator.d.ts.map +0 -1
  262. package/dist/cloud/services/scaling-orchestrator.js.map +0 -1
  263. package/dist/cloud/services/scaling-policy.d.ts.map +0 -1
  264. package/dist/cloud/services/scaling-policy.js.map +0 -1
  265. package/dist/cloud/vault/index.d.ts.map +0 -1
  266. package/dist/cloud/vault/index.js.map +0 -1
  267. package/dist/cloud/webhooks/index.d.ts.map +0 -1
  268. package/dist/cloud/webhooks/index.js.map +0 -1
  269. package/dist/cloud/webhooks/parsers/github.d.ts.map +0 -1
  270. package/dist/cloud/webhooks/parsers/github.js.map +0 -1
  271. package/dist/cloud/webhooks/parsers/index.d.ts.map +0 -1
  272. package/dist/cloud/webhooks/parsers/index.js.map +0 -1
  273. package/dist/cloud/webhooks/parsers/linear.d.ts.map +0 -1
  274. package/dist/cloud/webhooks/parsers/linear.js.map +0 -1
  275. package/dist/cloud/webhooks/parsers/slack.d.ts.map +0 -1
  276. package/dist/cloud/webhooks/parsers/slack.js.map +0 -1
  277. package/dist/cloud/webhooks/responders/github.d.ts.map +0 -1
  278. package/dist/cloud/webhooks/responders/github.js.map +0 -1
  279. package/dist/cloud/webhooks/responders/index.d.ts.map +0 -1
  280. package/dist/cloud/webhooks/responders/index.js.map +0 -1
  281. package/dist/cloud/webhooks/responders/linear.d.ts.map +0 -1
  282. package/dist/cloud/webhooks/responders/linear.js.map +0 -1
  283. package/dist/cloud/webhooks/responders/slack.d.ts.map +0 -1
  284. package/dist/cloud/webhooks/responders/slack.js.map +0 -1
  285. package/dist/cloud/webhooks/router.d.ts.map +0 -1
  286. package/dist/cloud/webhooks/router.js.map +0 -1
  287. package/dist/cloud/webhooks/rules-engine.d.ts.map +0 -1
  288. package/dist/cloud/webhooks/rules-engine.js.map +0 -1
  289. package/dist/cloud/webhooks/types.d.ts.map +0 -1
  290. package/dist/cloud/webhooks/types.js.map +0 -1
  291. package/dist/continuity/formatter.d.ts.map +0 -1
  292. package/dist/continuity/formatter.js.map +0 -1
  293. package/dist/continuity/handoff-store.d.ts.map +0 -1
  294. package/dist/continuity/handoff-store.js.map +0 -1
  295. package/dist/continuity/index.d.ts.map +0 -1
  296. package/dist/continuity/index.js.map +0 -1
  297. package/dist/continuity/ledger-store.d.ts.map +0 -1
  298. package/dist/continuity/ledger-store.js.map +0 -1
  299. package/dist/continuity/manager.d.ts.map +0 -1
  300. package/dist/continuity/manager.js.map +0 -1
  301. package/dist/continuity/parser.d.ts.map +0 -1
  302. package/dist/continuity/parser.js.map +0 -1
  303. package/dist/continuity/types.d.ts.map +0 -1
  304. package/dist/continuity/types.js.map +0 -1
  305. package/dist/daemon/agent-manager.d.ts.map +0 -1
  306. package/dist/daemon/agent-manager.js.map +0 -1
  307. package/dist/daemon/agent-registry.d.ts.map +0 -1
  308. package/dist/daemon/agent-registry.js.map +0 -1
  309. package/dist/daemon/api.d.ts.map +0 -1
  310. package/dist/daemon/api.js.map +0 -1
  311. package/dist/daemon/auth.d.ts.map +0 -1
  312. package/dist/daemon/auth.js.map +0 -1
  313. package/dist/daemon/cli-auth.d.ts.map +0 -1
  314. package/dist/daemon/cli-auth.js.map +0 -1
  315. package/dist/daemon/cloud-sync.d.ts.map +0 -1
  316. package/dist/daemon/cloud-sync.js.map +0 -1
  317. package/dist/daemon/connection.d.ts.map +0 -1
  318. package/dist/daemon/connection.js.map +0 -1
  319. package/dist/daemon/index.d.ts.map +0 -1
  320. package/dist/daemon/index.js.map +0 -1
  321. package/dist/daemon/orchestrator.d.ts.map +0 -1
  322. package/dist/daemon/orchestrator.js.map +0 -1
  323. package/dist/daemon/registry.d.ts.map +0 -1
  324. package/dist/daemon/registry.js.map +0 -1
  325. package/dist/daemon/router.d.ts.map +0 -1
  326. package/dist/daemon/router.js.map +0 -1
  327. package/dist/daemon/server.d.ts.map +0 -1
  328. package/dist/daemon/server.js.map +0 -1
  329. package/dist/daemon/services/browser-testing.d.ts.map +0 -1
  330. package/dist/daemon/services/browser-testing.js.map +0 -1
  331. package/dist/daemon/services/container-spawner.d.ts.map +0 -1
  332. package/dist/daemon/services/container-spawner.js.map +0 -1
  333. package/dist/daemon/types.d.ts.map +0 -1
  334. package/dist/daemon/types.js.map +0 -1
  335. package/dist/daemon/workspace-manager.d.ts.map +0 -1
  336. package/dist/daemon/workspace-manager.js.map +0 -1
  337. package/dist/dashboard/out/_next/static/chunks/480-2d4111711d4e473c.js +0 -1
  338. package/dist/dashboard/out/_next/static/chunks/766-c3a14283c88d815b.js +0 -1
  339. package/dist/dashboard/out/_next/static/chunks/app/app/page-7120be68bea622f3.js +0 -1
  340. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-dc2e3a1a22478efc.js +0 -1
  341. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
  342. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-4d72d5a5d8a9b618.js +0 -1
  343. package/dist/dashboard/out/_next/static/chunks/app/providers/page-b68a681526eb145e.js +0 -1
  344. package/dist/dashboard/out/_next/static/css/411ce23ffeae9f76.css +0 -1
  345. package/dist/dashboard-server/metrics.d.ts.map +0 -1
  346. package/dist/dashboard-server/metrics.js.map +0 -1
  347. package/dist/dashboard-server/needs-attention.d.ts.map +0 -1
  348. package/dist/dashboard-server/needs-attention.js.map +0 -1
  349. package/dist/dashboard-server/server.d.ts.map +0 -1
  350. package/dist/dashboard-server/server.js.map +0 -1
  351. package/dist/dashboard-server/start.d.ts.map +0 -1
  352. package/dist/dashboard-server/start.js.map +0 -1
  353. package/dist/hooks/emitter.d.ts.map +0 -1
  354. package/dist/hooks/emitter.js.map +0 -1
  355. package/dist/hooks/inbox-check/hook.d.ts.map +0 -1
  356. package/dist/hooks/inbox-check/hook.js.map +0 -1
  357. package/dist/hooks/inbox-check/index.d.ts.map +0 -1
  358. package/dist/hooks/inbox-check/index.js.map +0 -1
  359. package/dist/hooks/inbox-check/types.d.ts.map +0 -1
  360. package/dist/hooks/inbox-check/types.js.map +0 -1
  361. package/dist/hooks/inbox-check/utils.d.ts.map +0 -1
  362. package/dist/hooks/inbox-check/utils.js.map +0 -1
  363. package/dist/hooks/index.d.ts.map +0 -1
  364. package/dist/hooks/index.js.map +0 -1
  365. package/dist/hooks/registry.d.ts.map +0 -1
  366. package/dist/hooks/registry.js.map +0 -1
  367. package/dist/hooks/trajectory-hooks.d.ts.map +0 -1
  368. package/dist/hooks/trajectory-hooks.js.map +0 -1
  369. package/dist/hooks/types.d.ts.map +0 -1
  370. package/dist/hooks/types.js.map +0 -1
  371. package/dist/index.d.ts.map +0 -1
  372. package/dist/index.js.map +0 -1
  373. package/dist/memory/adapters/index.d.ts.map +0 -1
  374. package/dist/memory/adapters/index.js.map +0 -1
  375. package/dist/memory/adapters/inmemory.d.ts.map +0 -1
  376. package/dist/memory/adapters/inmemory.js.map +0 -1
  377. package/dist/memory/adapters/supermemory.d.ts.map +0 -1
  378. package/dist/memory/adapters/supermemory.js.map +0 -1
  379. package/dist/memory/factory.d.ts.map +0 -1
  380. package/dist/memory/factory.js.map +0 -1
  381. package/dist/memory/index.d.ts.map +0 -1
  382. package/dist/memory/index.js.map +0 -1
  383. package/dist/memory/memory-hooks.d.ts.map +0 -1
  384. package/dist/memory/memory-hooks.js.map +0 -1
  385. package/dist/memory/service.d.ts.map +0 -1
  386. package/dist/memory/service.js.map +0 -1
  387. package/dist/memory/types.d.ts.map +0 -1
  388. package/dist/memory/types.js.map +0 -1
  389. package/dist/policy/agent-policy.d.ts.map +0 -1
  390. package/dist/policy/agent-policy.js.map +0 -1
  391. package/dist/policy/cloud-policy-fetcher.d.ts.map +0 -1
  392. package/dist/policy/cloud-policy-fetcher.js.map +0 -1
  393. package/dist/protocol/framing.d.ts.map +0 -1
  394. package/dist/protocol/framing.js.map +0 -1
  395. package/dist/protocol/index.d.ts.map +0 -1
  396. package/dist/protocol/index.js.map +0 -1
  397. package/dist/protocol/types.d.ts.map +0 -1
  398. package/dist/protocol/types.js.map +0 -1
  399. package/dist/resiliency/context-persistence.d.ts.map +0 -1
  400. package/dist/resiliency/context-persistence.js.map +0 -1
  401. package/dist/resiliency/crash-insights.d.ts.map +0 -1
  402. package/dist/resiliency/crash-insights.js.map +0 -1
  403. package/dist/resiliency/gossip-health.d.ts.map +0 -1
  404. package/dist/resiliency/gossip-health.js.map +0 -1
  405. package/dist/resiliency/health-monitor.d.ts.map +0 -1
  406. package/dist/resiliency/health-monitor.js.map +0 -1
  407. package/dist/resiliency/index.d.ts.map +0 -1
  408. package/dist/resiliency/index.js.map +0 -1
  409. package/dist/resiliency/leader-watchdog.d.ts.map +0 -1
  410. package/dist/resiliency/leader-watchdog.js.map +0 -1
  411. package/dist/resiliency/logger.d.ts.map +0 -1
  412. package/dist/resiliency/logger.js.map +0 -1
  413. package/dist/resiliency/memory-monitor.d.ts.map +0 -1
  414. package/dist/resiliency/memory-monitor.js.map +0 -1
  415. package/dist/resiliency/metrics.d.ts.map +0 -1
  416. package/dist/resiliency/metrics.js.map +0 -1
  417. package/dist/resiliency/provider-context.d.ts.map +0 -1
  418. package/dist/resiliency/provider-context.js.map +0 -1
  419. package/dist/resiliency/stateless-lead.d.ts.map +0 -1
  420. package/dist/resiliency/stateless-lead.js.map +0 -1
  421. package/dist/resiliency/supervisor.d.ts.map +0 -1
  422. package/dist/resiliency/supervisor.js.map +0 -1
  423. package/dist/shared/cli-auth-config.d.ts.map +0 -1
  424. package/dist/shared/cli-auth-config.js.map +0 -1
  425. package/dist/state/agent-state.d.ts.map +0 -1
  426. package/dist/state/agent-state.js.map +0 -1
  427. package/dist/storage/adapter.d.ts.map +0 -1
  428. package/dist/storage/adapter.js.map +0 -1
  429. package/dist/storage/sqlite-adapter.d.ts.map +0 -1
  430. package/dist/storage/sqlite-adapter.js.map +0 -1
  431. package/dist/trajectory/config.d.ts.map +0 -1
  432. package/dist/trajectory/config.js.map +0 -1
  433. package/dist/trajectory/index.d.ts.map +0 -1
  434. package/dist/trajectory/index.js.map +0 -1
  435. package/dist/trajectory/integration.d.ts.map +0 -1
  436. package/dist/trajectory/integration.js.map +0 -1
  437. package/dist/utils/agent-config.d.ts.map +0 -1
  438. package/dist/utils/agent-config.js.map +0 -1
  439. package/dist/utils/command-resolver.d.ts.map +0 -1
  440. package/dist/utils/command-resolver.js.map +0 -1
  441. package/dist/utils/index.d.ts.map +0 -1
  442. package/dist/utils/index.js.map +0 -1
  443. package/dist/utils/logger.d.ts.map +0 -1
  444. package/dist/utils/logger.js.map +0 -1
  445. package/dist/utils/name-generator.d.ts.map +0 -1
  446. package/dist/utils/name-generator.js.map +0 -1
  447. package/dist/utils/project-namespace.d.ts.map +0 -1
  448. package/dist/utils/project-namespace.js.map +0 -1
  449. package/dist/utils/tmux-resolver.d.ts.map +0 -1
  450. package/dist/utils/tmux-resolver.js.map +0 -1
  451. package/dist/utils/update-checker.d.ts.map +0 -1
  452. package/dist/utils/update-checker.js.map +0 -1
  453. package/dist/wrapper/client.d.ts.map +0 -1
  454. package/dist/wrapper/client.js.map +0 -1
  455. package/dist/wrapper/inbox.d.ts.map +0 -1
  456. package/dist/wrapper/inbox.js.map +0 -1
  457. package/dist/wrapper/index.d.ts.map +0 -1
  458. package/dist/wrapper/index.js.map +0 -1
  459. package/dist/wrapper/parser.d.ts.map +0 -1
  460. package/dist/wrapper/parser.js.map +0 -1
  461. package/dist/wrapper/pty-wrapper.d.ts.map +0 -1
  462. package/dist/wrapper/pty-wrapper.js.map +0 -1
  463. package/dist/wrapper/shared.d.ts.map +0 -1
  464. package/dist/wrapper/shared.js.map +0 -1
  465. package/dist/wrapper/tmux-wrapper.d.ts.map +0 -1
  466. package/dist/wrapper/tmux-wrapper.js.map +0 -1
  467. package/docs/AGENTS.md +0 -513
  468. package/docs/ARCHITECTURE_DECISIONS.md +0 -175
  469. package/docs/CLOUD-ARCHITECTURE.md +0 -804
  470. package/docs/CLOUD-ONBOARDING-DESIGN.md +0 -1983
  471. package/docs/CONTRIBUTING.md +0 -151
  472. package/docs/HOOKS_API.md +0 -394
  473. package/docs/INTEGRATION-GUIDE.md +0 -926
  474. package/docs/PROTOCOL.md +0 -325
  475. package/docs/WRAPPER_EVENTS.md +0 -358
  476. package/docs/agent-policy-snippet.md +0 -40
  477. package/docs/agent-relay-protocol.md +0 -238
  478. package/docs/agent-relay-snippet.md +0 -174
  479. package/docs/archive/CHANGELOG.md +0 -11
  480. package/docs/archive/CLI-SIMPLIFICATION-COMPLETE.md +0 -48
  481. package/docs/archive/DESIGN_BRIDGE_STAFFING.md +0 -878
  482. package/docs/archive/DESIGN_V2.md +0 -1079
  483. package/docs/archive/EXECUTIVE_SUMMARY.md +0 -358
  484. package/docs/archive/MONETIZATION.md +0 -1679
  485. package/docs/archive/PROPOSAL-trajectories.md +0 -1582
  486. package/docs/archive/ROADMAP.md +0 -329
  487. package/docs/archive/SCALING_ANALYSIS.md +0 -280
  488. package/docs/archive/TESTING_PRESENCE_FEATURES.md +0 -327
  489. package/docs/archive/TMUX_IMPLEMENTATION_NOTES.md +0 -364
  490. package/docs/archive/TMUX_IMPROVEMENTS.md +0 -968
  491. package/docs/archive/dashboard-v2-plan.md +0 -179
  492. package/docs/archive/removable-code-analysis.md +0 -24
  493. package/docs/competitive/GASTOWN.md +0 -451
  494. package/docs/competitive/MCP_AGENT_MAIL.md +0 -389
  495. package/docs/competitive/OVERVIEW.md +0 -898
  496. package/docs/competitive/README.md +0 -34
  497. package/docs/competitive/TMUX_ORCHESTRATOR.md +0 -605
  498. package/docs/dashboard.png +0 -0
  499. package/docs/design/ci-failure-webhooks.md +0 -812
  500. package/docs/design/comprehensive-integrations.md +0 -238
  501. package/docs/design/e2b-sandbox-integration.md +0 -504
  502. package/docs/design/github-app-permissions.md +0 -264
  503. package/docs/guides/CLOUD.md +0 -236
  504. package/docs/guides/LOCAL.md +0 -535
  505. package/docs/guides/SELF-HOSTED.md +0 -494
  506. package/docs/local-testing.md +0 -428
  507. package/docs/proposals/continuous-claude-integration.md +0 -622
  508. package/docs/proposals/custom-commands.md +0 -368
  509. package/docs/proposals/shadow-as-subagent.md +0 -765
  510. package/docs/proposals/slack-bot-integration.md +0 -1457
  511. package/docs/tasks/global-skills-system.tasks.md +0 -230
  512. package/docs/tasks/webhook-integrations.tasks.md +0 -184
  513. package/docs/tasks/workspace-capabilities.tasks.md +0 -121
  514. package/docs/testing/RESILIENCY-TEST-PLAN-2026-01-01.md +0 -366
  515. package/scripts/cloud-setup.sh +0 -96
  516. package/scripts/dev/PUBLIC_RELEASE_PLAN.md +0 -88
  517. package/scripts/dev/dev-team-setup.sh +0 -431
  518. package/scripts/e2e-test.sh +0 -119
  519. package/scripts/games/game-protocol.md +0 -79
  520. package/scripts/games/hearts-setup.sh +0 -264
  521. package/scripts/manual-qa.sh +0 -293
  522. package/scripts/run-cloud-qa.sh +0 -220
  523. package/scripts/test-cli-auth/Dockerfile +0 -44
  524. package/scripts/test-cli-auth/Dockerfile.real +0 -79
  525. package/scripts/test-cli-auth/README.md +0 -286
  526. package/scripts/test-cli-auth/ci-test-real-clis.ts +0 -251
  527. package/scripts/test-cli-auth/ci-test-runner.ts +0 -263
  528. package/scripts/test-cli-auth/mock-cli.sh +0 -147
  529. package/scripts/test-cli-auth/package.json +0 -14
  530. package/scripts/test-cli-auth/test-oauth-flow.ts +0 -220
  531. package/scripts/test-pty-input-auto.js +0 -222
  532. package/scripts/test-pty-input.js +0 -150
  533. package/scripts/tictactoe-setup.sh +0 -181
  534. /package/dist/dashboard/out/_next/static/chunks/{117-b100311aff8d5c61.js → 117-f7b8ab0809342e77.js} +0 -0
  535. /package/dist/dashboard/out/_next/static/chunks/{648-a13d3c2b1be45466.js → 648-5cc6e1921389a58a.js} +0 -0
  536. /package/dist/dashboard/out/_next/static/chunks/{724-73c1ee5f60abe860.js → 724-2dae7627550ab88f.js} +0 -0
  537. /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-a4973f3e3c82fb67.js → page-53b8a69f76db17d0.js} +0 -0
  538. /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-bf46c09eb57e019c.js → fd9d1056-609918ca7b6280bb.js} +0 -0
  539. /package/dist/dashboard/out/_next/static/{H5aWG0udPB4iOUIl_gytz → wPgKJtcOmTFLpUncDg16A}/_buildManifest.js +0 -0
  540. /package/dist/dashboard/out/_next/static/{H5aWG0udPB4iOUIl_gytz → wPgKJtcOmTFLpUncDg16A}/_ssgManifest.js +0 -0
@@ -8,9 +8,46 @@ import { getConfig } from '../config.js';
8
8
  import { db } from '../db/index.js';
9
9
  import { vault } from '../vault/index.js';
10
10
  import { nangoService } from '../services/nango.js';
11
+ import { canAutoScale, canScaleToTier, getResourceTierForPlan, } from '../services/planLimits.js';
11
12
  const WORKSPACE_PORT = 3888;
12
13
  const FETCH_TIMEOUT_MS = 10_000;
13
14
  const WORKSPACE_IMAGE = process.env.WORKSPACE_IMAGE || 'ghcr.io/agentworkforce/relay-workspace:latest';
15
+ // In-memory tracker for provisioning progress (workspace ID -> progress)
16
+ const provisioningProgress = new Map();
17
+ /**
18
+ * Update the provisioning stage for a workspace
19
+ */
20
+ function updateProvisioningStage(workspaceId, stage) {
21
+ const existing = provisioningProgress.get(workspaceId);
22
+ provisioningProgress.set(workspaceId, {
23
+ stage,
24
+ startedAt: existing?.startedAt ?? Date.now(),
25
+ updatedAt: Date.now(),
26
+ });
27
+ console.log(`[provisioner] Workspace ${workspaceId.substring(0, 8)} stage: ${stage}`);
28
+ }
29
+ /**
30
+ * Get the current provisioning stage for a workspace
31
+ */
32
+ export function getProvisioningStage(workspaceId) {
33
+ return provisioningProgress.get(workspaceId) ?? null;
34
+ }
35
+ /**
36
+ * Clear provisioning progress (call when complete or failed)
37
+ */
38
+ function clearProvisioningProgress(workspaceId) {
39
+ provisioningProgress.delete(workspaceId);
40
+ }
41
+ /**
42
+ * Schedule cleanup of provisioning progress after a delay
43
+ * This gives the frontend time to poll and see the 'complete' stage
44
+ */
45
+ function scheduleProgressCleanup(workspaceId, delayMs = 30_000) {
46
+ setTimeout(() => {
47
+ clearProvisioningProgress(workspaceId);
48
+ console.log(`[provisioner] Cleaned up provisioning progress for ${workspaceId.substring(0, 8)}`);
49
+ }, delayMs);
50
+ }
14
51
  /**
15
52
  * Get a fresh GitHub App installation token from Nango.
16
53
  * Looks up the user's connected repositories to find a valid Nango connection.
@@ -88,11 +125,95 @@ async function softHealthCheck(url) {
88
125
  console.warn(`[health] Failed to reach ${url}/health`, error);
89
126
  }
90
127
  }
128
+ /**
129
+ * Wait for machine to be in "started" state using Fly.io's /wait endpoint
130
+ * This is more efficient than polling - the API blocks until the state is reached
131
+ * @see https://fly.io/docs/machines/api/machines-resource/#wait-for-a-machine-to-reach-a-specific-state
132
+ */
133
+ async function waitForMachineStarted(apiToken, appName, machineId, timeoutSeconds = 120) {
134
+ console.log(`[provisioner] Waiting for machine ${machineId} to start (timeout: ${timeoutSeconds}s)...`);
135
+ try {
136
+ // Use Fly.io's /wait endpoint - blocks until machine reaches target state
137
+ const res = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machineId}/wait?state=started&timeout=${timeoutSeconds}`, {
138
+ headers: { Authorization: `Bearer ${apiToken}` },
139
+ });
140
+ if (res.ok) {
141
+ console.log(`[provisioner] Machine ${machineId} is now started`);
142
+ return;
143
+ }
144
+ // 408 = timeout, machine didn't reach state in time
145
+ if (res.status === 408) {
146
+ // Get current state for error message
147
+ const stateRes = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${machineId}`, { headers: { Authorization: `Bearer ${apiToken}` } });
148
+ const machine = stateRes.ok ? (await stateRes.json()) : { state: 'unknown' };
149
+ throw new Error(`Machine ${machineId} did not start within ${timeoutSeconds}s (last state: ${machine.state})`);
150
+ }
151
+ // Other error
152
+ const errorText = await res.text();
153
+ throw new Error(`Wait for machine failed: ${res.status} ${errorText}`);
154
+ }
155
+ catch (error) {
156
+ if (error instanceof Error && error.message.includes('did not start')) {
157
+ throw error;
158
+ }
159
+ console.warn(`[provisioner] Error waiting for machine:`, error);
160
+ throw new Error(`Failed to wait for machine ${machineId}: ${error.message}`);
161
+ }
162
+ }
163
+ /**
164
+ * Wait for health check to pass (with DNS propagation time)
165
+ * Tries internal Fly network first if available, then falls back to public URL
166
+ */
167
+ async function waitForHealthy(url, appName, maxWaitMs = 90_000) {
168
+ const startTime = Date.now();
169
+ // Build list of URLs to try - internal first (faster, more reliable from inside Fly)
170
+ const urlsToTry = [];
171
+ // If running on Fly and app name provided, try internal network first
172
+ const isOnFly = !!process.env.FLY_APP_NAME;
173
+ if (isOnFly && appName) {
174
+ urlsToTry.push(`http://${appName}.internal:8080/health`);
175
+ }
176
+ // Always add the public URL as fallback
177
+ urlsToTry.push(`${url.replace(/\/$/, '')}/health`);
178
+ console.log(`[provisioner] Waiting for workspace to become healthy (trying: ${urlsToTry.join(', ')})...`);
179
+ while (Date.now() - startTime < maxWaitMs) {
180
+ // Try each URL in order
181
+ for (const healthUrl of urlsToTry) {
182
+ try {
183
+ const controller = new AbortController();
184
+ const timer = setTimeout(() => controller.abort(), 5_000);
185
+ const res = await fetch(healthUrl, {
186
+ method: 'GET',
187
+ signal: controller.signal,
188
+ });
189
+ clearTimeout(timer);
190
+ if (res.ok) {
191
+ console.log(`[provisioner] Health check passed via ${healthUrl}`);
192
+ return;
193
+ }
194
+ console.log(`[provisioner] Health check to ${healthUrl} returned ${res.status}`);
195
+ }
196
+ catch (error) {
197
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
198
+ const errMsg = error.message;
199
+ // Only log detailed error for last URL attempt
200
+ if (healthUrl === urlsToTry[urlsToTry.length - 1]) {
201
+ console.log(`[provisioner] Health check failed (${elapsed}s elapsed): ${errMsg}`);
202
+ }
203
+ }
204
+ }
205
+ await wait(3000);
206
+ }
207
+ // Don't throw - workspace is provisioned, health check is best-effort
208
+ console.warn(`[provisioner] Health check did not pass within ${maxWaitMs}ms, continuing anyway`);
209
+ }
210
+ // Resource tiers sized for Claude Code agents (~1-2GB RAM per agent)
211
+ // cpuKind: 'shared' = cheaper but can be throttled, 'performance' = dedicated
91
212
  export const RESOURCE_TIERS = {
92
- small: { name: 'small', cpuCores: 1, memoryMb: 512, maxAgents: 5 },
93
- medium: { name: 'medium', cpuCores: 2, memoryMb: 1024, maxAgents: 10 },
94
- large: { name: 'large', cpuCores: 4, memoryMb: 2048, maxAgents: 20 },
95
- xlarge: { name: 'xlarge', cpuCores: 8, memoryMb: 4096, maxAgents: 50 },
213
+ small: { name: 'small', cpuCores: 2, memoryMb: 2048, maxAgents: 2, cpuKind: 'shared' },
214
+ medium: { name: 'medium', cpuCores: 2, memoryMb: 4096, maxAgents: 5, cpuKind: 'shared' },
215
+ large: { name: 'large', cpuCores: 4, memoryMb: 8192, maxAgents: 10, cpuKind: 'performance' },
216
+ xlarge: { name: 'xlarge', cpuCores: 8, memoryMb: 16384, maxAgents: 20, cpuKind: 'performance' },
96
217
  };
97
218
  /**
98
219
  * Fly.io provisioner
@@ -130,6 +251,8 @@ class FlyProvisioner {
130
251
  }
131
252
  async provision(workspace, credentials) {
132
253
  const appName = `ar-${workspace.id.substring(0, 8)}`;
254
+ // Stage: Creating workspace
255
+ updateProvisioningStage(workspace.id, 'creating');
133
256
  // Create Fly app
134
257
  await fetchWithRetry('https://api.machines.dev/v1/apps', {
135
258
  method: 'POST',
@@ -142,10 +265,82 @@ class FlyProvisioner {
142
265
  org_slug: this.org,
143
266
  }),
144
267
  });
268
+ // Stage: Networking
269
+ updateProvisioningStage(workspace.id, 'networking');
270
+ // Allocate IPs for the app (required for public DNS)
271
+ // Must use GraphQL API - Machines REST API doesn't support IP allocation
272
+ // Shared IPv4 is free, IPv6 is free
273
+ console.log(`[fly] Allocating IPs for ${appName}...`);
274
+ const allocateIP = async (type) => {
275
+ try {
276
+ // Map our type to Fly GraphQL enum
277
+ const graphqlType = type === 'shared_v4' ? 'shared_v4' : 'v6';
278
+ const res = await fetchWithRetry('https://api.fly.io/graphql', {
279
+ method: 'POST',
280
+ headers: {
281
+ Authorization: `Bearer ${this.apiToken}`,
282
+ 'Content-Type': 'application/json',
283
+ },
284
+ body: JSON.stringify({
285
+ query: `
286
+ mutation AllocateIPAddress($input: AllocateIPAddressInput!) {
287
+ allocateIpAddress(input: $input) {
288
+ ipAddress {
289
+ id
290
+ address
291
+ type
292
+ }
293
+ }
294
+ }
295
+ `,
296
+ variables: {
297
+ input: {
298
+ appId: appName,
299
+ type: graphqlType,
300
+ },
301
+ },
302
+ }),
303
+ });
304
+ if (!res.ok) {
305
+ const errorText = await res.text();
306
+ console.warn(`[fly] Failed to allocate ${type}: ${res.status} ${errorText}`);
307
+ return false;
308
+ }
309
+ const data = await res.json();
310
+ if (data.errors?.length) {
311
+ // Ignore "already allocated" errors
312
+ const alreadyAllocated = data.errors.some(e => e.message.includes('already') || e.message.includes('exists'));
313
+ if (!alreadyAllocated) {
314
+ console.warn(`[fly] GraphQL error allocating ${type}: ${data.errors[0].message}`);
315
+ return false;
316
+ }
317
+ console.log(`[fly] IP ${type} already allocated`);
318
+ return true;
319
+ }
320
+ const address = data.data?.allocateIpAddress?.ipAddress?.address;
321
+ console.log(`[fly] Allocated ${type}: ${address}`);
322
+ return true;
323
+ }
324
+ catch (err) {
325
+ console.warn(`[fly] Failed to allocate ${type}: ${err.message}`);
326
+ return false;
327
+ }
328
+ };
329
+ const [sharedV4Result, v6Result] = await Promise.all([
330
+ allocateIP('shared_v4'),
331
+ allocateIP('v6'),
332
+ ]);
333
+ console.log(`[fly] IP allocation results: shared_v4=${sharedV4Result}, v6=${v6Result}`);
334
+ // Stage: Secrets
335
+ updateProvisioningStage(workspace.id, 'secrets');
145
336
  // Set secrets (provider credentials)
146
337
  const secrets = {};
147
338
  for (const [provider, token] of credentials) {
148
339
  secrets[`${provider.toUpperCase()}_TOKEN`] = token;
340
+ // Also set GH_TOKEN for gh CLI compatibility
341
+ if (provider === 'github') {
342
+ secrets['GH_TOKEN'] = token;
343
+ }
149
344
  }
150
345
  if (Object.keys(secrets).length > 0) {
151
346
  await fetchWithRetry(`https://api.machines.dev/v1/apps/${appName}/secrets`, {
@@ -164,6 +359,8 @@ class FlyProvisioner {
164
359
  if (customHostname) {
165
360
  await this.allocateCertificate(appName, customHostname);
166
361
  }
362
+ // Stage: Machine
363
+ updateProvisioningStage(workspace.id, 'machine');
167
364
  // Create machine with auto-stop/start for cost optimization
168
365
  const machineResponse = await fetchWithRetry(`https://api.machines.dev/v1/apps/${appName}/machines`, {
169
366
  method: 'POST',
@@ -198,21 +395,49 @@ class FlyProvisioner {
198
395
  services: [
199
396
  {
200
397
  ports: [
201
- { port: 443, handlers: ['tls', 'http'] },
398
+ {
399
+ port: 443,
400
+ handlers: ['tls', 'http'],
401
+ // Force HTTP/1.1 to backend for WebSocket upgrade compatibility
402
+ // HTTP/2 doesn't support traditional WebSocket upgrade mechanism
403
+ http_options: {
404
+ h2_backend: false,
405
+ },
406
+ },
202
407
  { port: 80, handlers: ['http'] },
203
408
  ],
204
409
  protocol: 'tcp',
205
410
  internal_port: WORKSPACE_PORT,
206
- // Auto-stop after 5 minutes of inactivity
207
- auto_stop_machines: true,
411
+ // Auto-stop after inactivity to reduce costs
412
+ // Fly Proxy automatically wakes machines on incoming requests
413
+ auto_stop_machines: 'stop', // stop (not suspend) for faster wake
208
414
  auto_start_machines: true,
209
415
  min_machines_running: 0,
416
+ // Idle timeout before auto-stop (in seconds)
417
+ // Longer timeout = better UX, shorter = lower costs
418
+ concurrency: {
419
+ type: 'requests',
420
+ soft_limit: 25,
421
+ hard_limit: 50,
422
+ },
210
423
  },
211
424
  ],
425
+ checks: {
426
+ health: {
427
+ type: 'http',
428
+ port: WORKSPACE_PORT,
429
+ path: '/health',
430
+ interval: '30s',
431
+ timeout: '5s',
432
+ grace_period: '10s',
433
+ },
434
+ },
435
+ // Start with small tier (shared CPUs) - scales up based on plan
436
+ // Free tier uses shared CPUs for cost efficiency
212
437
  guest: {
213
438
  cpu_kind: 'shared',
214
- cpus: 1,
215
- memory_mb: 512,
439
+ cpus: 2,
440
+ memory_mb: 2048,
216
441
  },
217
442
  },
218
443
  }),
@@ -226,7 +451,19 @@ class FlyProvisioner {
226
451
  const publicUrl = customHostname
227
452
  ? `https://${customHostname}`
228
453
  : `https://${appName}.fly.dev`;
229
- await softHealthCheck(publicUrl);
454
+ // Stage: Booting
455
+ updateProvisioningStage(workspace.id, 'booting');
456
+ // Wait for machine to be in started state
457
+ await waitForMachineStarted(this.apiToken, appName, machine.id);
458
+ // Stage: Health check
459
+ updateProvisioningStage(workspace.id, 'health');
460
+ // Wait for health check to pass (includes DNS propagation time)
461
+ // Pass appName to enable internal Fly network health checks
462
+ await waitForHealthy(publicUrl, appName);
463
+ // Stage: Complete
464
+ updateProvisioningStage(workspace.id, 'complete');
465
+ // Schedule cleanup of provisioning progress after 30s (gives frontend time to see 'complete')
466
+ scheduleProgressCleanup(workspace.id);
230
467
  return {
231
468
  computeId: machine.id,
232
469
  publicUrl,
@@ -313,7 +550,8 @@ class FlyProvisioner {
313
550
  body: JSON.stringify({
314
551
  config: {
315
552
  guest: {
316
- cpu_kind: tier.cpuCores <= 2 ? 'shared' : 'performance',
553
+ // Use tier-specific CPU type (shared for cost, performance for power)
554
+ cpu_kind: tier.cpuKind,
317
555
  cpus: tier.cpuCores,
318
556
  memory_mb: tier.memoryMb,
319
557
  },
@@ -468,6 +706,10 @@ class RailwayProvisioner {
468
706
  };
469
707
  for (const [provider, token] of credentials) {
470
708
  envVars[`${provider.toUpperCase()}_TOKEN`] = token;
709
+ // Also set GH_TOKEN for gh CLI compatibility
710
+ if (provider === 'github') {
711
+ envVars['GH_TOKEN'] = token;
712
+ }
471
713
  }
472
714
  await fetchWithRetry('https://backboard.railway.app/graphql/v2', {
473
715
  method: 'POST',
@@ -682,6 +924,10 @@ class DockerProvisioner {
682
924
  ];
683
925
  for (const [provider, token] of credentials) {
684
926
  envArgs.push(`-e ${provider.toUpperCase()}_TOKEN=${token}`);
927
+ // Also set GH_TOKEN for gh CLI compatibility
928
+ if (provider === 'github') {
929
+ envArgs.push(`-e GH_TOKEN=${token}`);
930
+ }
685
931
  }
686
932
  // Run container
687
933
  const { execSync } = await import('child_process');
@@ -791,6 +1037,7 @@ export class WorkspaceProvisioner {
791
1037
  }
792
1038
  /**
793
1039
  * Provision a new workspace (one-click)
1040
+ * Returns immediately with 'provisioning' status and runs actual provisioning in background
794
1041
  */
795
1042
  async provision(config) {
796
1043
  // Create workspace record
@@ -814,6 +1061,22 @@ export class WorkspaceProvisioner {
814
1061
  });
815
1062
  // Auto-accept the creator's membership
816
1063
  await db.workspaceMembers.acceptInvite(workspace.id, config.userId);
1064
+ // Initialize stage tracking immediately
1065
+ updateProvisioningStage(workspace.id, 'creating');
1066
+ // Run provisioning in the background so frontend can poll for stages
1067
+ this.runProvisioningAsync(workspace, config).catch((error) => {
1068
+ console.error(`[provisioner] Background provisioning failed for ${workspace.id}:`, error);
1069
+ });
1070
+ // Return immediately with 'provisioning' status
1071
+ return {
1072
+ workspaceId: workspace.id,
1073
+ status: 'provisioning',
1074
+ };
1075
+ }
1076
+ /**
1077
+ * Run the actual provisioning work asynchronously
1078
+ */
1079
+ async runProvisioningAsync(workspace, config) {
817
1080
  // Get credentials
818
1081
  const credentials = new Map();
819
1082
  for (const provider of config.providers) {
@@ -848,22 +1111,21 @@ export class WorkspaceProvisioner {
848
1111
  computeId,
849
1112
  publicUrl,
850
1113
  });
851
- return {
852
- workspaceId: workspace.id,
853
- status: 'running',
854
- publicUrl,
855
- };
1114
+ // Schedule cleanup of provisioning progress after 30s (gives frontend time to see 'complete')
1115
+ setTimeout(() => {
1116
+ clearProvisioningProgress(workspace.id);
1117
+ console.log(`[provisioner] Cleaned up provisioning progress for ${workspace.id.substring(0, 8)}`);
1118
+ }, 30_000);
1119
+ console.log(`[provisioner] Workspace ${workspace.id} provisioned successfully at ${publicUrl}`);
856
1120
  }
857
1121
  catch (error) {
858
1122
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
859
1123
  await db.workspaces.updateStatus(workspace.id, 'error', {
860
1124
  errorMessage,
861
1125
  });
862
- return {
863
- workspaceId: workspace.id,
864
- status: 'error',
865
- error: errorMessage,
866
- };
1126
+ // Clear provisioning progress on error
1127
+ clearProvisioningProgress(workspace.id);
1128
+ console.error(`[provisioner] Workspace ${workspace.id} provisioning failed:`, errorMessage);
867
1129
  }
868
1130
  }
869
1131
  /**
@@ -885,6 +1147,11 @@ export class WorkspaceProvisioner {
885
1147
  if (!workspace) {
886
1148
  throw new Error('Workspace not found');
887
1149
  }
1150
+ // During early provisioning, computeId isn't set yet
1151
+ // Return the database status instead of querying the provider
1152
+ if (!workspace.computeId && workspace.status === 'provisioning') {
1153
+ return 'provisioning';
1154
+ }
888
1155
  const status = await this.provisioner.getStatus(workspace);
889
1156
  // Update database if status changed
890
1157
  if (status !== workspace.status) {
@@ -965,6 +1232,80 @@ export class WorkspaceProvisioner {
965
1232
  const tierName = workspace.config.resourceTier || 'small';
966
1233
  return RESOURCE_TIERS[tierName] || RESOURCE_TIERS.small;
967
1234
  }
1235
+ /**
1236
+ * Get recommended tier based on agent count
1237
+ * Uses 1.5-2GB per agent as baseline for Claude Code
1238
+ */
1239
+ getRecommendedTier(agentCount) {
1240
+ // Find the smallest tier that supports this agent count
1241
+ const tiers = Object.values(RESOURCE_TIERS).sort((a, b) => a.maxAgents - b.maxAgents);
1242
+ for (const tier of tiers) {
1243
+ if (tier.maxAgents >= agentCount) {
1244
+ return tier;
1245
+ }
1246
+ }
1247
+ // If agent count exceeds all tiers, return the largest
1248
+ return RESOURCE_TIERS.xlarge;
1249
+ }
1250
+ /**
1251
+ * Auto-scale workspace based on current agent count
1252
+ * Respects plan limits - free tier cannot scale, others have max tier limits
1253
+ * Returns { scaled: boolean, reason?: string }
1254
+ */
1255
+ async autoScale(workspaceId, currentAgentCount) {
1256
+ const workspace = await db.workspaces.findById(workspaceId);
1257
+ if (!workspace) {
1258
+ throw new Error('Workspace not found');
1259
+ }
1260
+ // Get user's plan
1261
+ const user = await db.users.findById(workspace.userId);
1262
+ const plan = user?.plan || 'free';
1263
+ // Check if plan allows auto-scaling
1264
+ if (!canAutoScale(plan)) {
1265
+ return {
1266
+ scaled: false,
1267
+ reason: 'Auto-scaling requires Pro plan or higher',
1268
+ };
1269
+ }
1270
+ const currentTier = await this.getCurrentTier(workspaceId);
1271
+ const recommendedTier = this.getRecommendedTier(currentAgentCount);
1272
+ // Only scale UP, never down (to avoid disruption)
1273
+ if (recommendedTier.memoryMb <= currentTier.memoryMb) {
1274
+ return {
1275
+ scaled: false,
1276
+ currentTier: currentTier.name,
1277
+ };
1278
+ }
1279
+ // Check if plan allows scaling to the recommended tier
1280
+ if (!canScaleToTier(plan, recommendedTier.name)) {
1281
+ // Find the max tier allowed for this plan
1282
+ const maxTierName = getResourceTierForPlan(plan);
1283
+ const maxTier = RESOURCE_TIERS[maxTierName];
1284
+ if (maxTier.memoryMb <= currentTier.memoryMb) {
1285
+ return {
1286
+ scaled: false,
1287
+ reason: `Already at max tier (${currentTier.name}) for ${plan} plan`,
1288
+ currentTier: currentTier.name,
1289
+ };
1290
+ }
1291
+ // Scale to max allowed tier instead
1292
+ console.log(`[provisioner] Auto-scaling workspace ${workspaceId.substring(0, 8)} from ${currentTier.name} to ${maxTierName} (max for ${plan} plan)`);
1293
+ await this.resize(workspaceId, maxTier);
1294
+ return {
1295
+ scaled: true,
1296
+ currentTier: currentTier.name,
1297
+ targetTier: maxTierName,
1298
+ reason: `Scaled to max tier for ${plan} plan`,
1299
+ };
1300
+ }
1301
+ console.log(`[provisioner] Auto-scaling workspace ${workspaceId.substring(0, 8)} from ${currentTier.name} to ${recommendedTier.name} (${currentAgentCount} agents)`);
1302
+ await this.resize(workspaceId, recommendedTier);
1303
+ return {
1304
+ scaled: true,
1305
+ currentTier: currentTier.name,
1306
+ targetTier: recommendedTier.name,
1307
+ };
1308
+ }
968
1309
  }
969
1310
  // Singleton instance
970
1311
  let _provisioner = null;
@@ -34,6 +34,7 @@ import { webhooksRouter } from './api/webhooks.js';
34
34
  import { githubAppRouter } from './api/github-app.js';
35
35
  import { nangoAuthRouter } from './api/nango-auth.js';
36
36
  import { gitRouter } from './api/git.js';
37
+ import { codexAuthHelperRouter } from './api/codex-auth-helper.js';
37
38
  import { db } from './db/index.js';
38
39
  /**
39
40
  * Proxy a request to the user's primary running workspace
@@ -98,7 +99,9 @@ export async function createServer() {
98
99
  credentials: true,
99
100
  }));
100
101
  // Custom JSON parser that preserves raw body for webhook signature verification
102
+ // Increase limit to 10mb for base64 image uploads (screenshots)
101
103
  app.use(express.json({
104
+ limit: '10mb',
102
105
  verify: (req, _res, buf) => {
103
106
  // Store raw body for webhook signature verification
104
107
  req.rawBody = buf.toString();
@@ -168,10 +171,11 @@ export async function createServer() {
168
171
  });
169
172
  // Lightweight CSRF protection using session token
170
173
  const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
171
- // Paths exempt from CSRF (webhooks from external services, workspace proxy)
174
+ // Paths exempt from CSRF (webhooks from external services, workspace proxy, local auth callbacks)
172
175
  const CSRF_EXEMPT_PATHS = [
173
176
  '/api/webhooks/',
174
177
  '/api/auth/nango/webhook',
178
+ '/api/auth/codex-helper/callback',
175
179
  ];
176
180
  // Additional pattern for workspace proxy routes (contains /proxy/)
177
181
  const isWorkspaceProxyRoute = (path) => /^\/api\/workspaces\/[^/]+\/proxy\//.test(path);
@@ -236,6 +240,7 @@ export async function createServer() {
236
240
  app.use('/api/webhooks', webhooksRouter);
237
241
  app.use('/api/github-app', githubAppRouter);
238
242
  app.use('/api/auth/nango', nangoAuthRouter);
243
+ app.use('/api/auth/codex-helper', codexAuthHelperRouter);
239
244
  app.use('/api/git', gitRouter);
240
245
  // Test helper routes (only available in non-production)
241
246
  if (process.env.NODE_ENV !== 'production') {
@@ -33,6 +33,22 @@ declare class NangoService {
33
33
  * For API calls, use the proxy methods instead.
34
34
  */
35
35
  getGithubAppToken(connectionId: string): Promise<string>;
36
+ /**
37
+ * Retrieve the user's OAuth access token from a GitHub App OAuth connection.
38
+ * This is the user-level token (not the installation token).
39
+ * Use this for operations that require user context (e.g., gh CLI).
40
+ *
41
+ * The user token can be found in:
42
+ * 1. getToken() without installation flag
43
+ * 2. connection_config.access_token in github-app-oauth
44
+ * 3. Separate 'github' user connection
45
+ */
46
+ getGithubUserOAuthToken(connectionId: string): Promise<string>;
47
+ /**
48
+ * Retrieve the user's OAuth token from a 'github' user connection.
49
+ * This is for the separate GitHub OAuth login (not the App connection).
50
+ */
51
+ getGithubUserToken(connectionId: string): Promise<string>;
36
52
  /**
37
53
  * List repositories available to a GitHub App installation using the Nango Proxy.
38
54
  * The proxy automatically handles token injection and refresh.
@@ -114,6 +130,50 @@ declare class NangoService {
114
130
  };
115
131
  metadata?: Record<string, unknown>;
116
132
  }>;
133
+ /**
134
+ * Check if user has access to a specific GitHub repository.
135
+ * Uses the user's OAuth connection to query GitHub API.
136
+ * @param connectionId - User's Nango connection ID (github user OAuth)
137
+ * @param owner - Repository owner
138
+ * @param repo - Repository name
139
+ * @returns Access details or null if no access
140
+ */
141
+ checkUserRepoAccess(connectionId: string, owner: string, repo: string): Promise<{
142
+ hasAccess: boolean;
143
+ permission?: 'admin' | 'write' | 'read' | 'none';
144
+ repository?: {
145
+ id: number;
146
+ fullName: string;
147
+ isPrivate: boolean;
148
+ defaultBranch: string;
149
+ };
150
+ }>;
151
+ /**
152
+ * List all repositories the user has access to via their OAuth connection.
153
+ * Uses the user's personal OAuth token (not the GitHub App).
154
+ * @param connectionId - User's Nango connection ID (github user OAuth)
155
+ * @param options - Pagination and filter options
156
+ * @returns List of accessible repositories
157
+ */
158
+ listUserAccessibleRepos(connectionId: string, options?: {
159
+ page?: number;
160
+ perPage?: number;
161
+ type?: 'all' | 'owner' | 'public' | 'private' | 'member';
162
+ sort?: 'created' | 'updated' | 'pushed' | 'full_name';
163
+ }): Promise<{
164
+ repositories: Array<{
165
+ id: number;
166
+ fullName: string;
167
+ isPrivate: boolean;
168
+ defaultBranch: string;
169
+ permissions: {
170
+ admin: boolean;
171
+ push: boolean;
172
+ pull: boolean;
173
+ };
174
+ }>;
175
+ hasMore: boolean;
176
+ }>;
117
177
  /**
118
178
  * Verify webhook signature sent by Nango.
119
179
  * Uses the new verifyIncomingWebhookRequest method.