agent-relay 1.1.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 (567) 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/README.md +1 -1
  96. package/TESTING.md +278 -0
  97. package/deploy/init-db.sql +5 -0
  98. package/deploy/scripts/setup-fly-workspaces.sh +69 -0
  99. package/deploy/scripts/setup-railway.sh +75 -0
  100. package/deploy/workspace/entrypoint-browser.sh +118 -0
  101. package/deploy/workspace/entrypoint.sh +348 -0
  102. package/deploy/workspace/git-credential-relay +111 -0
  103. package/dist/bridge/spawner.d.ts +53 -0
  104. package/dist/bridge/spawner.js +203 -19
  105. package/dist/bridge/types.d.ts +12 -0
  106. package/dist/cli/index.js +618 -5
  107. package/dist/cloud/api/auth.d.ts +3 -2
  108. package/dist/cloud/api/auth.js +10 -98
  109. package/dist/cloud/api/billing.js +30 -9
  110. package/dist/cloud/api/cli-pty-runner.d.ts +54 -0
  111. package/dist/cloud/api/cli-pty-runner.js +119 -0
  112. package/dist/cloud/api/codex-auth-helper.d.ts +15 -0
  113. package/dist/cloud/api/codex-auth-helper.js +100 -0
  114. package/dist/cloud/api/generic-webhooks.d.ts +8 -0
  115. package/dist/cloud/api/generic-webhooks.js +129 -0
  116. package/dist/cloud/api/git.d.ts +8 -0
  117. package/dist/cloud/api/git.js +152 -0
  118. package/dist/cloud/api/github-app.d.ts +11 -0
  119. package/dist/cloud/api/github-app.js +189 -0
  120. package/dist/cloud/api/middleware/planLimits.d.ts +7 -0
  121. package/dist/cloud/api/middleware/planLimits.js +39 -1
  122. package/dist/cloud/api/monitoring.d.ts +11 -0
  123. package/dist/cloud/api/monitoring.js +578 -0
  124. package/dist/cloud/api/nango-auth.d.ts +9 -0
  125. package/dist/cloud/api/nango-auth.js +377 -0
  126. package/dist/cloud/api/onboarding.d.ts +8 -1
  127. package/dist/cloud/api/onboarding.js +313 -119
  128. package/dist/cloud/api/policy.d.ts +8 -0
  129. package/dist/cloud/api/policy.js +229 -0
  130. package/dist/cloud/api/providers.js +114 -42
  131. package/dist/cloud/api/repos.d.ts +1 -0
  132. package/dist/cloud/api/repos.js +186 -0
  133. package/dist/cloud/api/test-helpers.d.ts +10 -0
  134. package/dist/cloud/api/test-helpers.js +575 -0
  135. package/dist/cloud/api/webhooks.d.ts +8 -0
  136. package/dist/cloud/api/webhooks.js +645 -0
  137. package/dist/cloud/api/workspaces.js +320 -12
  138. package/dist/cloud/billing/plans.js +32 -19
  139. package/dist/cloud/billing/types.d.ts +9 -3
  140. package/dist/cloud/config.d.ts +9 -2
  141. package/dist/cloud/config.js +13 -4
  142. package/dist/cloud/db/drizzle.d.ts +84 -1
  143. package/dist/cloud/db/drizzle.js +470 -0
  144. package/dist/cloud/db/index.d.ts +9 -4
  145. package/dist/cloud/db/index.js +11 -3
  146. package/dist/cloud/db/schema.d.ts +3283 -556
  147. package/dist/cloud/db/schema.js +314 -1
  148. package/dist/cloud/index.d.ts +1 -0
  149. package/dist/cloud/index.js +2 -0
  150. package/dist/cloud/provisioner/index.d.ts +56 -0
  151. package/dist/cloud/provisioner/index.js +676 -34
  152. package/dist/cloud/server.d.ts +1 -0
  153. package/dist/cloud/server.js +362 -13
  154. package/dist/cloud/services/auto-scaler.d.ts +152 -0
  155. package/dist/cloud/services/auto-scaler.js +439 -0
  156. package/dist/cloud/services/capacity-manager.d.ts +148 -0
  157. package/dist/cloud/services/capacity-manager.js +449 -0
  158. package/dist/cloud/services/ci-agent-spawner.d.ts +49 -0
  159. package/dist/cloud/services/ci-agent-spawner.js +373 -0
  160. package/dist/cloud/services/index.d.ts +12 -0
  161. package/dist/cloud/services/index.js +15 -0
  162. package/dist/cloud/services/mention-handler.d.ts +65 -0
  163. package/dist/cloud/services/mention-handler.js +405 -0
  164. package/dist/cloud/services/nango.d.ts +186 -0
  165. package/dist/cloud/services/nango.js +344 -0
  166. package/dist/cloud/services/persistence.d.ts +131 -0
  167. package/dist/cloud/services/persistence.js +200 -0
  168. package/dist/cloud/services/planLimits.d.ts +37 -0
  169. package/dist/cloud/services/planLimits.js +86 -5
  170. package/dist/cloud/services/scaling-orchestrator.d.ts +159 -0
  171. package/dist/cloud/services/scaling-orchestrator.js +502 -0
  172. package/dist/cloud/services/scaling-policy.d.ts +121 -0
  173. package/dist/cloud/services/scaling-policy.js +415 -0
  174. package/dist/cloud/vault/index.js +1 -1
  175. package/dist/cloud/webhooks/index.d.ts +24 -0
  176. package/dist/cloud/webhooks/index.js +29 -0
  177. package/dist/cloud/webhooks/parsers/github.d.ts +8 -0
  178. package/dist/cloud/webhooks/parsers/github.js +234 -0
  179. package/dist/cloud/webhooks/parsers/index.d.ts +23 -0
  180. package/dist/cloud/webhooks/parsers/index.js +30 -0
  181. package/dist/cloud/webhooks/parsers/linear.d.ts +9 -0
  182. package/dist/cloud/webhooks/parsers/linear.js +258 -0
  183. package/dist/cloud/webhooks/parsers/slack.d.ts +9 -0
  184. package/dist/cloud/webhooks/parsers/slack.js +214 -0
  185. package/dist/cloud/webhooks/responders/github.d.ts +8 -0
  186. package/dist/cloud/webhooks/responders/github.js +73 -0
  187. package/dist/cloud/webhooks/responders/index.d.ts +23 -0
  188. package/dist/cloud/webhooks/responders/index.js +30 -0
  189. package/dist/cloud/webhooks/responders/linear.d.ts +9 -0
  190. package/dist/cloud/webhooks/responders/linear.js +149 -0
  191. package/dist/cloud/webhooks/responders/slack.d.ts +20 -0
  192. package/dist/cloud/webhooks/responders/slack.js +178 -0
  193. package/dist/cloud/webhooks/router.d.ts +25 -0
  194. package/dist/cloud/webhooks/router.js +504 -0
  195. package/dist/cloud/webhooks/rules-engine.d.ts +24 -0
  196. package/dist/cloud/webhooks/rules-engine.js +287 -0
  197. package/dist/cloud/webhooks/types.d.ts +186 -0
  198. package/dist/cloud/webhooks/types.js +8 -0
  199. package/dist/continuity/formatter.d.ts +51 -0
  200. package/dist/continuity/formatter.js +313 -0
  201. package/dist/continuity/handoff-store.d.ts +67 -0
  202. package/dist/continuity/handoff-store.js +472 -0
  203. package/dist/continuity/index.d.ts +45 -0
  204. package/dist/continuity/index.js +48 -0
  205. package/dist/continuity/ledger-store.d.ts +110 -0
  206. package/dist/continuity/ledger-store.js +500 -0
  207. package/dist/continuity/manager.d.ts +178 -0
  208. package/dist/continuity/manager.js +562 -0
  209. package/dist/continuity/parser.d.ts +76 -0
  210. package/dist/continuity/parser.js +579 -0
  211. package/dist/continuity/types.d.ts +180 -0
  212. package/dist/continuity/types.js +9 -0
  213. package/dist/daemon/agent-manager.d.ts +27 -0
  214. package/dist/daemon/agent-manager.js +107 -6
  215. package/dist/daemon/agent-registry.d.ts +32 -0
  216. package/dist/daemon/agent-registry.js +42 -2
  217. package/dist/daemon/api.d.ts +12 -0
  218. package/dist/daemon/api.js +131 -2
  219. package/dist/daemon/cli-auth.d.ts +67 -0
  220. package/dist/daemon/cli-auth.js +537 -0
  221. package/dist/daemon/cloud-sync.js +9 -7
  222. package/dist/daemon/orchestrator.js +30 -0
  223. package/dist/daemon/router.d.ts +5 -0
  224. package/dist/daemon/router.js +78 -26
  225. package/dist/daemon/server.d.ts +5 -0
  226. package/dist/daemon/server.js +9 -1
  227. package/dist/daemon/services/browser-testing.d.ts +88 -0
  228. package/dist/daemon/services/browser-testing.js +244 -0
  229. package/dist/daemon/services/container-spawner.d.ts +135 -0
  230. package/dist/daemon/services/container-spawner.js +313 -0
  231. package/dist/daemon/types.d.ts +5 -1
  232. package/dist/dashboard/out/404.html +1 -1
  233. package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +1 -0
  234. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
  235. package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +1 -0
  236. package/dist/dashboard/out/_next/static/chunks/724-2dae7627550ab88f.js +9 -0
  237. package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +1 -0
  238. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-3fdfa60e53f2810d.js +1 -0
  239. package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +1 -0
  240. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-3538dfe0ffe984b8.js +1 -0
  241. package/dist/dashboard/out/_next/static/chunks/app/history/{page-b6edd4dde8d08194.js → page-abb9ab2d329f56e9.js} +1 -1
  242. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
  243. package/dist/dashboard/out/_next/static/chunks/app/login/page-c22d080201cbd9fb.js +1 -0
  244. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-67a3e98d9a43a6ed.js +1 -0
  245. package/dist/dashboard/out/_next/static/chunks/app/page-77e9c65420a06cfb.js +1 -0
  246. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-b08ed1c34d14434a.js +1 -0
  247. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +1 -0
  248. package/dist/dashboard/out/_next/static/chunks/app/signup/page-68d34f50baa8ab6b.js +1 -0
  249. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
  250. package/dist/dashboard/out/_next/static/chunks/{main-app-5d692157a8eb1fd9.js → main-app-6e8e8d3ef4e0192a.js} +1 -1
  251. package/dist/dashboard/out/_next/static/chunks/{main-c2f423b9c9f4591b.js → main-ed4e1fb6f29c34cf.js} +1 -1
  252. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
  253. package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +1 -0
  254. package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +1 -0
  255. package/dist/dashboard/out/app/onboarding.html +1 -0
  256. package/dist/dashboard/out/app/onboarding.txt +7 -0
  257. package/dist/dashboard/out/app.html +1 -14
  258. package/dist/dashboard/out/app.txt +2 -2
  259. package/dist/dashboard/out/connect-repos.html +1 -0
  260. package/dist/dashboard/out/connect-repos.txt +7 -0
  261. package/dist/dashboard/out/history.html +1 -1
  262. package/dist/dashboard/out/history.txt +2 -2
  263. package/dist/dashboard/out/index.html +1 -1
  264. package/dist/dashboard/out/index.txt +2 -2
  265. package/dist/dashboard/out/login.html +6 -0
  266. package/dist/dashboard/out/login.txt +7 -0
  267. package/dist/dashboard/out/metrics.html +1 -1
  268. package/dist/dashboard/out/metrics.txt +2 -2
  269. package/dist/dashboard/out/pricing.html +3 -3
  270. package/dist/dashboard/out/pricing.txt +2 -2
  271. package/dist/dashboard/out/providers.html +1 -0
  272. package/dist/dashboard/out/providers.txt +7 -0
  273. package/dist/dashboard/out/signup.html +6 -0
  274. package/dist/dashboard/out/signup.txt +7 -0
  275. package/dist/dashboard-server/server.js +1308 -8
  276. package/dist/hooks/emitter.d.ts +40 -0
  277. package/dist/hooks/emitter.js +63 -0
  278. package/dist/hooks/index.d.ts +3 -0
  279. package/dist/hooks/index.js +3 -0
  280. package/dist/hooks/registry.d.ts +173 -0
  281. package/dist/hooks/registry.js +476 -0
  282. package/dist/hooks/trajectory-hooks.d.ts +52 -0
  283. package/dist/hooks/trajectory-hooks.js +183 -0
  284. package/dist/hooks/types.d.ts +141 -0
  285. package/dist/index.d.ts +2 -0
  286. package/dist/index.js +3 -0
  287. package/dist/memory/adapters/index.d.ts +8 -0
  288. package/dist/memory/adapters/index.js +8 -0
  289. package/dist/memory/adapters/inmemory.d.ts +59 -0
  290. package/dist/memory/adapters/inmemory.js +195 -0
  291. package/dist/memory/adapters/supermemory.d.ts +71 -0
  292. package/dist/memory/adapters/supermemory.js +338 -0
  293. package/dist/memory/factory.d.ts +48 -0
  294. package/dist/memory/factory.js +143 -0
  295. package/dist/memory/index.d.ts +32 -0
  296. package/dist/memory/index.js +32 -0
  297. package/dist/memory/memory-hooks.d.ts +60 -0
  298. package/dist/memory/memory-hooks.js +313 -0
  299. package/dist/memory/service.d.ts +49 -0
  300. package/dist/memory/service.js +146 -0
  301. package/dist/memory/types.d.ts +195 -0
  302. package/dist/memory/types.js +8 -0
  303. package/dist/policy/agent-policy.d.ts +225 -0
  304. package/dist/policy/agent-policy.js +665 -0
  305. package/dist/policy/cloud-policy-fetcher.d.ts +12 -0
  306. package/dist/policy/cloud-policy-fetcher.js +64 -0
  307. package/dist/resiliency/crash-insights.d.ts +156 -0
  308. package/dist/resiliency/crash-insights.js +492 -0
  309. package/dist/resiliency/gossip-health.d.ts +137 -0
  310. package/dist/resiliency/gossip-health.js +241 -0
  311. package/dist/resiliency/index.d.ts +5 -0
  312. package/dist/resiliency/index.js +5 -0
  313. package/dist/resiliency/leader-watchdog.d.ts +109 -0
  314. package/dist/resiliency/leader-watchdog.js +189 -0
  315. package/dist/resiliency/memory-monitor.d.ts +172 -0
  316. package/dist/resiliency/memory-monitor.js +593 -0
  317. package/dist/resiliency/stateless-lead.d.ts +149 -0
  318. package/dist/resiliency/stateless-lead.js +308 -0
  319. package/dist/resiliency/supervisor.d.ts +38 -0
  320. package/dist/resiliency/supervisor.js +122 -0
  321. package/dist/shared/cli-auth-config.d.ts +91 -0
  322. package/dist/shared/cli-auth-config.js +264 -0
  323. package/dist/storage/adapter.d.ts +1 -1
  324. package/dist/trajectory/config.d.ts +84 -0
  325. package/dist/trajectory/config.js +163 -0
  326. package/dist/trajectory/index.d.ts +8 -0
  327. package/dist/trajectory/index.js +8 -0
  328. package/dist/trajectory/integration.d.ts +292 -0
  329. package/dist/trajectory/integration.js +834 -0
  330. package/dist/utils/logger.js +1 -1
  331. package/dist/utils/project-namespace.d.ts +24 -0
  332. package/dist/utils/project-namespace.js +84 -0
  333. package/dist/wrapper/parser.d.ts +10 -0
  334. package/dist/wrapper/parser.js +100 -33
  335. package/dist/wrapper/pty-wrapper.d.ts +197 -16
  336. package/dist/wrapper/pty-wrapper.js +943 -106
  337. package/dist/wrapper/shared.d.ts +165 -0
  338. package/dist/wrapper/shared.js +270 -0
  339. package/dist/wrapper/tmux-wrapper.d.ts +73 -11
  340. package/dist/wrapper/tmux-wrapper.js +541 -120
  341. package/package.json +16 -16
  342. package/scripts/postinstall.js +60 -0
  343. package/test-push.txt +1 -0
  344. package/bin/tmux +0 -0
  345. package/dist/bridge/config.d.ts.map +0 -1
  346. package/dist/bridge/config.js.map +0 -1
  347. package/dist/bridge/index.d.ts.map +0 -1
  348. package/dist/bridge/index.js.map +0 -1
  349. package/dist/bridge/multi-project-client.d.ts.map +0 -1
  350. package/dist/bridge/multi-project-client.js.map +0 -1
  351. package/dist/bridge/shadow-cli.d.ts.map +0 -1
  352. package/dist/bridge/shadow-cli.js.map +0 -1
  353. package/dist/bridge/shadow-config.d.ts.map +0 -1
  354. package/dist/bridge/shadow-config.js.map +0 -1
  355. package/dist/bridge/spawner.d.ts.map +0 -1
  356. package/dist/bridge/spawner.js.map +0 -1
  357. package/dist/bridge/teams-config.d.ts.map +0 -1
  358. package/dist/bridge/teams-config.js.map +0 -1
  359. package/dist/bridge/types.d.ts.map +0 -1
  360. package/dist/bridge/types.js.map +0 -1
  361. package/dist/bridge/utils.d.ts.map +0 -1
  362. package/dist/bridge/utils.js.map +0 -1
  363. package/dist/cli/index.d.ts.map +0 -1
  364. package/dist/cli/index.js.map +0 -1
  365. package/dist/cloud/api/auth.d.ts.map +0 -1
  366. package/dist/cloud/api/auth.js.map +0 -1
  367. package/dist/cloud/api/billing.d.ts.map +0 -1
  368. package/dist/cloud/api/billing.js.map +0 -1
  369. package/dist/cloud/api/coordinators.d.ts.map +0 -1
  370. package/dist/cloud/api/coordinators.js.map +0 -1
  371. package/dist/cloud/api/daemons.d.ts.map +0 -1
  372. package/dist/cloud/api/daemons.js.map +0 -1
  373. package/dist/cloud/api/middleware/planLimits.d.ts.map +0 -1
  374. package/dist/cloud/api/middleware/planLimits.js.map +0 -1
  375. package/dist/cloud/api/onboarding.d.ts.map +0 -1
  376. package/dist/cloud/api/onboarding.js.map +0 -1
  377. package/dist/cloud/api/providers.d.ts.map +0 -1
  378. package/dist/cloud/api/providers.js.map +0 -1
  379. package/dist/cloud/api/repos.d.ts.map +0 -1
  380. package/dist/cloud/api/repos.js.map +0 -1
  381. package/dist/cloud/api/teams.d.ts.map +0 -1
  382. package/dist/cloud/api/teams.js.map +0 -1
  383. package/dist/cloud/api/usage.d.ts.map +0 -1
  384. package/dist/cloud/api/usage.js.map +0 -1
  385. package/dist/cloud/api/workspaces.d.ts.map +0 -1
  386. package/dist/cloud/api/workspaces.js.map +0 -1
  387. package/dist/cloud/billing/index.d.ts.map +0 -1
  388. package/dist/cloud/billing/index.js.map +0 -1
  389. package/dist/cloud/billing/plans.d.ts.map +0 -1
  390. package/dist/cloud/billing/plans.js.map +0 -1
  391. package/dist/cloud/billing/service.d.ts.map +0 -1
  392. package/dist/cloud/billing/service.js.map +0 -1
  393. package/dist/cloud/billing/types.d.ts.map +0 -1
  394. package/dist/cloud/billing/types.js.map +0 -1
  395. package/dist/cloud/config.d.ts.map +0 -1
  396. package/dist/cloud/config.js.map +0 -1
  397. package/dist/cloud/db/drizzle.d.ts.map +0 -1
  398. package/dist/cloud/db/drizzle.js.map +0 -1
  399. package/dist/cloud/db/index.d.ts.map +0 -1
  400. package/dist/cloud/db/index.js.map +0 -1
  401. package/dist/cloud/db/schema.d.ts.map +0 -1
  402. package/dist/cloud/db/schema.js.map +0 -1
  403. package/dist/cloud/index.d.ts.map +0 -1
  404. package/dist/cloud/index.js.map +0 -1
  405. package/dist/cloud/provisioner/index.d.ts.map +0 -1
  406. package/dist/cloud/provisioner/index.js.map +0 -1
  407. package/dist/cloud/server.d.ts.map +0 -1
  408. package/dist/cloud/server.js.map +0 -1
  409. package/dist/cloud/services/coordinator.d.ts.map +0 -1
  410. package/dist/cloud/services/coordinator.js.map +0 -1
  411. package/dist/cloud/services/planLimits.d.ts.map +0 -1
  412. package/dist/cloud/services/planLimits.js.map +0 -1
  413. package/dist/cloud/vault/index.d.ts.map +0 -1
  414. package/dist/cloud/vault/index.js.map +0 -1
  415. package/dist/daemon/agent-manager.d.ts.map +0 -1
  416. package/dist/daemon/agent-manager.js.map +0 -1
  417. package/dist/daemon/agent-registry.d.ts.map +0 -1
  418. package/dist/daemon/agent-registry.js.map +0 -1
  419. package/dist/daemon/api.d.ts.map +0 -1
  420. package/dist/daemon/api.js.map +0 -1
  421. package/dist/daemon/auth.d.ts.map +0 -1
  422. package/dist/daemon/auth.js.map +0 -1
  423. package/dist/daemon/cloud-sync.d.ts.map +0 -1
  424. package/dist/daemon/cloud-sync.js.map +0 -1
  425. package/dist/daemon/connection.d.ts.map +0 -1
  426. package/dist/daemon/connection.js.map +0 -1
  427. package/dist/daemon/index.d.ts.map +0 -1
  428. package/dist/daemon/index.js.map +0 -1
  429. package/dist/daemon/orchestrator.d.ts.map +0 -1
  430. package/dist/daemon/orchestrator.js.map +0 -1
  431. package/dist/daemon/registry.d.ts.map +0 -1
  432. package/dist/daemon/registry.js.map +0 -1
  433. package/dist/daemon/router.d.ts.map +0 -1
  434. package/dist/daemon/router.js.map +0 -1
  435. package/dist/daemon/server.d.ts.map +0 -1
  436. package/dist/daemon/server.js.map +0 -1
  437. package/dist/daemon/types.d.ts.map +0 -1
  438. package/dist/daemon/types.js.map +0 -1
  439. package/dist/daemon/workspace-manager.d.ts.map +0 -1
  440. package/dist/daemon/workspace-manager.js.map +0 -1
  441. package/dist/dashboard/out/_next/static/chunks/693-7b3301d8f6bc5014.js +0 -1
  442. package/dist/dashboard/out/_next/static/chunks/713-f78477eb185f1f4d.js +0 -1
  443. package/dist/dashboard/out/_next/static/chunks/766-e53e1cfe39b0b5b5.js +0 -1
  444. package/dist/dashboard/out/_next/static/chunks/900-037c64bfd797fb2a.js +0 -1
  445. package/dist/dashboard/out/_next/static/chunks/app/app/page-e3d9e1f4466b9bae.js +0 -1
  446. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
  447. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-e68825a81db67ba1.js +0 -1
  448. package/dist/dashboard/out/_next/static/chunks/app/page-cc108bf68c8a657f.js +0 -1
  449. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-d80e03a5297f95b6.js +0 -1
  450. package/dist/dashboard/out/_next/static/chunks/webpack-a5acc2831d094776.js +0 -1
  451. package/dist/dashboard/out/_next/static/css/79b80143647a07d7.css +0 -1
  452. package/dist/dashboard/out/_next/static/css/8cf277370ad48cfe.css +0 -1
  453. package/dist/dashboard-server/metrics.d.ts.map +0 -1
  454. package/dist/dashboard-server/metrics.js.map +0 -1
  455. package/dist/dashboard-server/needs-attention.d.ts.map +0 -1
  456. package/dist/dashboard-server/needs-attention.js.map +0 -1
  457. package/dist/dashboard-server/server.d.ts.map +0 -1
  458. package/dist/dashboard-server/server.js.map +0 -1
  459. package/dist/dashboard-server/start.d.ts.map +0 -1
  460. package/dist/dashboard-server/start.js.map +0 -1
  461. package/dist/hooks/inbox-check/hook.d.ts.map +0 -1
  462. package/dist/hooks/inbox-check/hook.js.map +0 -1
  463. package/dist/hooks/inbox-check/index.d.ts.map +0 -1
  464. package/dist/hooks/inbox-check/index.js.map +0 -1
  465. package/dist/hooks/inbox-check/types.d.ts.map +0 -1
  466. package/dist/hooks/inbox-check/types.js.map +0 -1
  467. package/dist/hooks/inbox-check/utils.d.ts.map +0 -1
  468. package/dist/hooks/inbox-check/utils.js.map +0 -1
  469. package/dist/hooks/index.d.ts.map +0 -1
  470. package/dist/hooks/index.js.map +0 -1
  471. package/dist/hooks/types.d.ts.map +0 -1
  472. package/dist/hooks/types.js.map +0 -1
  473. package/dist/index.d.ts.map +0 -1
  474. package/dist/index.js.map +0 -1
  475. package/dist/protocol/framing.d.ts.map +0 -1
  476. package/dist/protocol/framing.js.map +0 -1
  477. package/dist/protocol/index.d.ts.map +0 -1
  478. package/dist/protocol/index.js.map +0 -1
  479. package/dist/protocol/types.d.ts.map +0 -1
  480. package/dist/protocol/types.js.map +0 -1
  481. package/dist/resiliency/context-persistence.d.ts.map +0 -1
  482. package/dist/resiliency/context-persistence.js.map +0 -1
  483. package/dist/resiliency/health-monitor.d.ts.map +0 -1
  484. package/dist/resiliency/health-monitor.js.map +0 -1
  485. package/dist/resiliency/index.d.ts.map +0 -1
  486. package/dist/resiliency/index.js.map +0 -1
  487. package/dist/resiliency/logger.d.ts.map +0 -1
  488. package/dist/resiliency/logger.js.map +0 -1
  489. package/dist/resiliency/metrics.d.ts.map +0 -1
  490. package/dist/resiliency/metrics.js.map +0 -1
  491. package/dist/resiliency/provider-context.d.ts.map +0 -1
  492. package/dist/resiliency/provider-context.js.map +0 -1
  493. package/dist/resiliency/supervisor.d.ts.map +0 -1
  494. package/dist/resiliency/supervisor.js.map +0 -1
  495. package/dist/state/agent-state.d.ts.map +0 -1
  496. package/dist/state/agent-state.js.map +0 -1
  497. package/dist/storage/adapter.d.ts.map +0 -1
  498. package/dist/storage/adapter.js.map +0 -1
  499. package/dist/storage/sqlite-adapter.d.ts.map +0 -1
  500. package/dist/storage/sqlite-adapter.js.map +0 -1
  501. package/dist/utils/agent-config.d.ts.map +0 -1
  502. package/dist/utils/agent-config.js.map +0 -1
  503. package/dist/utils/command-resolver.d.ts.map +0 -1
  504. package/dist/utils/command-resolver.js.map +0 -1
  505. package/dist/utils/index.d.ts.map +0 -1
  506. package/dist/utils/index.js.map +0 -1
  507. package/dist/utils/logger.d.ts.map +0 -1
  508. package/dist/utils/logger.js.map +0 -1
  509. package/dist/utils/name-generator.d.ts.map +0 -1
  510. package/dist/utils/name-generator.js.map +0 -1
  511. package/dist/utils/project-namespace.d.ts.map +0 -1
  512. package/dist/utils/project-namespace.js.map +0 -1
  513. package/dist/utils/tmux-resolver.d.ts.map +0 -1
  514. package/dist/utils/tmux-resolver.js.map +0 -1
  515. package/dist/utils/update-checker.d.ts.map +0 -1
  516. package/dist/utils/update-checker.js.map +0 -1
  517. package/dist/wrapper/client.d.ts.map +0 -1
  518. package/dist/wrapper/client.js.map +0 -1
  519. package/dist/wrapper/inbox.d.ts.map +0 -1
  520. package/dist/wrapper/inbox.js.map +0 -1
  521. package/dist/wrapper/index.d.ts.map +0 -1
  522. package/dist/wrapper/index.js.map +0 -1
  523. package/dist/wrapper/parser.d.ts.map +0 -1
  524. package/dist/wrapper/parser.js.map +0 -1
  525. package/dist/wrapper/pty-wrapper.d.ts.map +0 -1
  526. package/dist/wrapper/pty-wrapper.js.map +0 -1
  527. package/dist/wrapper/tmux-wrapper.d.ts.map +0 -1
  528. package/dist/wrapper/tmux-wrapper.js.map +0 -1
  529. package/docs/AGENTS.md +0 -513
  530. package/docs/ARCHITECTURE_DECISIONS.md +0 -175
  531. package/docs/CHANGELOG.md +0 -11
  532. package/docs/CLI-SIMPLIFICATION-COMPLETE.md +0 -48
  533. package/docs/CLOUD-ARCHITECTURE.md +0 -652
  534. package/docs/CLOUD-ONBOARDING-DESIGN.md +0 -1983
  535. package/docs/COMPETITIVE_ANALYSIS.md +0 -897
  536. package/docs/CONTRIBUTING.md +0 -151
  537. package/docs/DESIGN_BRIDGE_STAFFING.md +0 -878
  538. package/docs/DESIGN_V2.md +0 -1079
  539. package/docs/INTEGRATION-GUIDE.md +0 -926
  540. package/docs/MONETIZATION.md +0 -1679
  541. package/docs/PROPOSAL-trajectories.md +0 -1582
  542. package/docs/PROTOCOL.md +0 -325
  543. package/docs/SCALING_ANALYSIS.md +0 -280
  544. package/docs/TESTING_PRESENCE_FEATURES.md +0 -327
  545. package/docs/TMUX_IMPLEMENTATION_NOTES.md +0 -364
  546. package/docs/TMUX_IMPROVEMENTS.md +0 -968
  547. package/docs/agent-relay-snippet.md +0 -168
  548. package/docs/competitive-analysis-mcp-agent-mail.md +0 -389
  549. package/docs/dashboard-v2-plan.md +0 -179
  550. package/docs/guides/CLOUD.md +0 -236
  551. package/docs/guides/LOCAL.md +0 -535
  552. package/docs/guides/SELF-HOSTED.md +0 -494
  553. package/docs/proposals/shadow-as-subagent.md +0 -765
  554. package/docs/proposals/slack-bot-integration.md +0 -1457
  555. package/docs/removable-code-analysis.md +0 -24
  556. package/scripts/dev/PUBLIC_RELEASE_PLAN.md +0 -88
  557. package/scripts/dev/dev-team-setup.sh +0 -431
  558. package/scripts/e2e-test.sh +0 -119
  559. package/scripts/games/game-protocol.md +0 -79
  560. package/scripts/games/hearts-setup.sh +0 -264
  561. package/scripts/tictactoe-setup.sh +0 -181
  562. /package/dist/dashboard/out/_next/static/chunks/{117-b2cd8d6485aacf2b.js → 117-f7b8ab0809342e77.js} +0 -0
  563. /package/dist/dashboard/out/_next/static/chunks/{648-8f3f26864ce515e5.js → 648-5cc6e1921389a58a.js} +0 -0
  564. /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-0b990dbb71d72a98.js → page-53b8a69f76db17d0.js} +0 -0
  565. /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-bf46c09eb57e019c.js → fd9d1056-609918ca7b6280bb.js} +0 -0
  566. /package/dist/dashboard/out/_next/static/{6HHWb2ZmnJ4OSm0zUP7h4 → wPgKJtcOmTFLpUncDg16A}/_buildManifest.js +0 -0
  567. /package/dist/dashboard/out/_next/static/{6HHWb2ZmnJ4OSm0zUP7h4 → wPgKJtcOmTFLpUncDg16A}/_ssgManifest.js +0 -0
@@ -5,6 +5,7 @@ import { Express } from 'express';
5
5
  declare module 'express-session' {
6
6
  interface SessionData {
7
7
  csrfToken?: string;
8
+ userId?: string;
8
9
  }
9
10
  }
10
11
  export interface CloudServer {
@@ -7,14 +7,18 @@ import cors from 'cors';
7
7
  import helmet from 'helmet';
8
8
  import crypto from 'crypto';
9
9
  import path from 'node:path';
10
+ import http from 'node:http';
10
11
  import { fileURLToPath } from 'node:url';
11
12
  import { createClient } from 'redis';
12
13
  import { RedisStore } from 'connect-redis';
14
+ import { WebSocketServer, WebSocket } from 'ws';
13
15
  import { getConfig } from './config.js';
16
+ import { runMigrations } from './db/index.js';
17
+ import { getScalingOrchestrator } from './services/index.js';
14
18
  const __filename = fileURLToPath(import.meta.url);
15
19
  const __dirname = path.dirname(__filename);
16
20
  // API routers
17
- import { authRouter } from './api/auth.js';
21
+ import { authRouter, requireAuth } from './api/auth.js';
18
22
  import { providersRouter } from './api/providers.js';
19
23
  import { workspacesRouter } from './api/workspaces.js';
20
24
  import { reposRouter } from './api/repos.js';
@@ -23,6 +27,43 @@ import { teamsRouter } from './api/teams.js';
23
27
  import { billingRouter } from './api/billing.js';
24
28
  import { usageRouter } from './api/usage.js';
25
29
  import { coordinatorsRouter } from './api/coordinators.js';
30
+ import { daemonsRouter } from './api/daemons.js';
31
+ import { monitoringRouter } from './api/monitoring.js';
32
+ import { testHelpersRouter } from './api/test-helpers.js';
33
+ import { webhooksRouter } from './api/webhooks.js';
34
+ import { githubAppRouter } from './api/github-app.js';
35
+ import { nangoAuthRouter } from './api/nango-auth.js';
36
+ import { gitRouter } from './api/git.js';
37
+ import { codexAuthHelperRouter } from './api/codex-auth-helper.js';
38
+ import { db } from './db/index.js';
39
+ /**
40
+ * Proxy a request to the user's primary running workspace
41
+ */
42
+ async function proxyToUserWorkspace(req, res, path) {
43
+ const userId = req.session.userId;
44
+ if (!userId) {
45
+ res.status(401).json({ error: 'Unauthorized' });
46
+ return;
47
+ }
48
+ try {
49
+ // Find user's running workspace
50
+ const workspaces = await db.workspaces.findByUserId(userId);
51
+ const runningWorkspace = workspaces.find(w => w.status === 'running' && w.publicUrl);
52
+ if (!runningWorkspace || !runningWorkspace.publicUrl) {
53
+ res.status(404).json({ error: 'No running workspace found', success: false });
54
+ return;
55
+ }
56
+ // Proxy to workspace
57
+ const targetUrl = `${runningWorkspace.publicUrl}${path}`;
58
+ const proxyRes = await fetch(targetUrl);
59
+ const data = await proxyRes.json();
60
+ res.status(proxyRes.status).json(data);
61
+ }
62
+ catch (error) {
63
+ console.error('[trajectory-proxy] Error:', error);
64
+ res.status(500).json({ error: 'Failed to proxy request to workspace', success: false });
65
+ }
66
+ }
26
67
  export async function createServer() {
27
68
  const config = getConfig();
28
69
  const app = express();
@@ -37,16 +78,19 @@ export async function createServer() {
37
78
  });
38
79
  await redisClient.connect();
39
80
  // Middleware
40
- // Configure helmet to allow Next.js inline scripts
81
+ // Configure helmet to allow Next.js inline scripts and Nango Connect UI
41
82
  app.use(helmet({
42
83
  contentSecurityPolicy: {
43
84
  directives: {
44
85
  defaultSrc: ["'self'"],
45
- scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
46
- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
47
- fontSrc: ["'self'", "https://fonts.gstatic.com"],
48
- imgSrc: ["'self'", "data:", "https:"],
49
- connectSrc: ["'self'", "wss:", "ws:", "https:"],
86
+ scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://connect.nango.dev"],
87
+ styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://connect.nango.dev"],
88
+ fontSrc: ["'self'", "https://fonts.gstatic.com", "data:"],
89
+ imgSrc: ["'self'", "data:", "https:", "blob:"],
90
+ connectSrc: ["'self'", "wss:", "ws:", "https:", "https://api.nango.dev", "https://connect.nango.dev"],
91
+ frameSrc: ["'self'", "https://connect.nango.dev", "https://github.com"],
92
+ childSrc: ["'self'", "https://connect.nango.dev", "blob:"],
93
+ workerSrc: ["'self'", "blob:"],
50
94
  },
51
95
  },
52
96
  }));
@@ -54,7 +98,15 @@ export async function createServer() {
54
98
  origin: config.publicUrl,
55
99
  credentials: true,
56
100
  }));
57
- app.use(express.json());
101
+ // Custom JSON parser that preserves raw body for webhook signature verification
102
+ // Increase limit to 10mb for base64 image uploads (screenshots)
103
+ app.use(express.json({
104
+ limit: '10mb',
105
+ verify: (req, _res, buf) => {
106
+ // Store raw body for webhook signature verification
107
+ req.rawBody = buf.toString();
108
+ },
109
+ }));
58
110
  // Session middleware
59
111
  app.use(session({
60
112
  store: new RedisStore({ client: redisClient }),
@@ -80,9 +132,17 @@ export async function createServer() {
80
132
  });
81
133
  // Simple in-memory rate limiting per IP
82
134
  const RATE_LIMIT_WINDOW_MS = 60_000;
83
- const RATE_LIMIT_MAX = 300;
135
+ // Higher limit in development mode
136
+ const RATE_LIMIT_MAX = process.env.NODE_ENV === 'development' ? 1000 : 300;
84
137
  const rateLimits = new Map();
85
138
  app.use((req, res, next) => {
139
+ // Skip rate limiting for localhost in development
140
+ if (process.env.NODE_ENV === 'development') {
141
+ const ip = req.ip || '';
142
+ if (ip === '127.0.0.1' || ip === '::1' || ip === '::ffff:127.0.0.1') {
143
+ return next();
144
+ }
145
+ }
86
146
  const now = Date.now();
87
147
  const key = req.ip || 'unknown';
88
148
  const entry = rateLimits.get(key);
@@ -111,18 +171,49 @@ export async function createServer() {
111
171
  });
112
172
  // Lightweight CSRF protection using session token
113
173
  const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
174
+ // Paths exempt from CSRF (webhooks from external services, workspace proxy, local auth callbacks)
175
+ const CSRF_EXEMPT_PATHS = [
176
+ '/api/webhooks/',
177
+ '/api/auth/nango/webhook',
178
+ '/api/auth/codex-helper/callback',
179
+ ];
180
+ // Additional pattern for workspace proxy routes (contains /proxy/)
181
+ const isWorkspaceProxyRoute = (path) => /^\/api\/workspaces\/[^/]+\/proxy\//.test(path);
114
182
  app.use((req, res, next) => {
183
+ // Skip CSRF for webhook endpoints and workspace proxy routes
184
+ if (CSRF_EXEMPT_PATHS.some(path => req.path.startsWith(path)) || isWorkspaceProxyRoute(req.path)) {
185
+ return next();
186
+ }
115
187
  if (!req.session)
116
188
  return res.status(500).json({ error: 'Session unavailable' });
189
+ // Generate CSRF token if not present
190
+ // Use session.save() to ensure the session is persisted even for unauthenticated users
191
+ // This is necessary because saveUninitialized: false won't auto-save new sessions
117
192
  if (!req.session.csrfToken) {
118
193
  req.session.csrfToken = crypto.randomBytes(32).toString('hex');
194
+ // Explicitly save session to persist the CSRF token
195
+ req.session.save((err) => {
196
+ if (err) {
197
+ console.error('[csrf] Failed to save session:', err);
198
+ }
199
+ });
119
200
  }
120
201
  res.setHeader('X-CSRF-Token', req.session.csrfToken);
121
202
  if (SAFE_METHODS.has(req.method.toUpperCase())) {
122
203
  return next();
123
204
  }
205
+ // Skip CSRF for Bearer-authenticated endpoints (daemon API, test helpers)
206
+ const authHeader = req.get('authorization');
207
+ if (authHeader?.startsWith('Bearer ')) {
208
+ return next();
209
+ }
210
+ // Skip CSRF for test endpoints in non-production
211
+ if (process.env.NODE_ENV !== 'production' && req.path.startsWith('/api/test/')) {
212
+ return next();
213
+ }
124
214
  const token = req.get('x-csrf-token');
125
215
  if (!token || token !== req.session.csrfToken) {
216
+ console.log(`[csrf] Token mismatch: received=${token?.substring(0, 8)}... expected=${req.session.csrfToken?.substring(0, 8)}...`);
126
217
  return res.status(403).json({
127
218
  error: 'CSRF token invalid or missing',
128
219
  code: 'CSRF_MISMATCH',
@@ -144,12 +235,39 @@ export async function createServer() {
144
235
  app.use('/api/billing', billingRouter);
145
236
  app.use('/api/usage', usageRouter);
146
237
  app.use('/api/project-groups', coordinatorsRouter);
147
- // TODO: Add authenticated agent/daemon channels when remote sockets are supported
238
+ app.use('/api/daemons', daemonsRouter);
239
+ app.use('/api/monitoring', monitoringRouter);
240
+ app.use('/api/webhooks', webhooksRouter);
241
+ app.use('/api/github-app', githubAppRouter);
242
+ app.use('/api/auth/nango', nangoAuthRouter);
243
+ app.use('/api/auth/codex-helper', codexAuthHelperRouter);
244
+ app.use('/api/git', gitRouter);
245
+ // Test helper routes (only available in non-production)
246
+ if (process.env.NODE_ENV !== 'production') {
247
+ app.use('/api/test', testHelpersRouter);
248
+ console.log('[cloud] Test helper routes enabled (non-production mode)');
249
+ }
250
+ // Trajectory proxy routes - auto-detect user's workspace and forward
251
+ // These are convenience routes so the dashboard doesn't need to know the workspace ID
252
+ app.get('/api/trajectory', requireAuth, async (req, res) => {
253
+ await proxyToUserWorkspace(req, res, '/api/trajectory');
254
+ });
255
+ app.get('/api/trajectory/steps', requireAuth, async (req, res) => {
256
+ const queryString = req.query.trajectoryId
257
+ ? `?trajectoryId=${encodeURIComponent(req.query.trajectoryId)}`
258
+ : '';
259
+ await proxyToUserWorkspace(req, res, `/api/trajectory/steps${queryString}`);
260
+ });
261
+ app.get('/api/trajectory/history', requireAuth, async (req, res) => {
262
+ await proxyToUserWorkspace(req, res, '/api/trajectory/history');
263
+ });
148
264
  // Serve static dashboard files (Next.js static export)
149
265
  // Path: dist/cloud/server.js -> ../../src/dashboard/out
150
266
  const dashboardPath = path.join(__dirname, '../../src/dashboard/out');
151
- app.use(express.static(dashboardPath));
152
- // SPA fallback - serve index.html for all non-API routes
267
+ // Serve static files with .html extension fallback for clean URLs
268
+ // e.g., /signup will try /signup.html
269
+ app.use(express.static(dashboardPath, { extensions: ['html'] }));
270
+ // SPA fallback - serve index.html for all non-API routes that don't match static files
153
271
  // Express 5 requires named wildcard params instead of bare '*'
154
272
  app.get('/{*splat}', (req, res, next) => {
155
273
  // Don't serve index.html for API routes
@@ -168,18 +286,249 @@ export async function createServer() {
168
286
  });
169
287
  // Server lifecycle
170
288
  let server = null;
289
+ let scalingOrchestrator = null;
290
+ // Create HTTP server for WebSocket upgrade handling
291
+ const httpServer = http.createServer(app);
292
+ // ===== Presence WebSocket =====
293
+ const wssPresence = new WebSocketServer({
294
+ noServer: true,
295
+ perMessageDeflate: false,
296
+ maxPayload: 1024 * 1024, // 1MB - presence messages are small
297
+ });
298
+ const onlineUsers = new Map();
299
+ // Validation helpers
300
+ const isValidUsername = (username) => {
301
+ if (typeof username !== 'string')
302
+ return false;
303
+ return /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/.test(username);
304
+ };
305
+ const isValidAvatarUrl = (url) => {
306
+ if (url === undefined || url === null)
307
+ return true;
308
+ if (typeof url !== 'string')
309
+ return false;
310
+ try {
311
+ const parsed = new URL(url);
312
+ return parsed.protocol === 'https:' &&
313
+ (parsed.hostname === 'avatars.githubusercontent.com' ||
314
+ parsed.hostname === 'github.com' ||
315
+ parsed.hostname.endsWith('.githubusercontent.com'));
316
+ }
317
+ catch {
318
+ return false;
319
+ }
320
+ };
321
+ // Handle HTTP upgrade for WebSocket
322
+ httpServer.on('upgrade', (request, socket, head) => {
323
+ const pathname = new URL(request.url || '', `http://${request.headers.host}`).pathname;
324
+ if (pathname === '/ws/presence') {
325
+ wssPresence.handleUpgrade(request, socket, head, (ws) => {
326
+ wssPresence.emit('connection', ws, request);
327
+ });
328
+ }
329
+ else {
330
+ // Unknown WebSocket path - destroy socket
331
+ socket.destroy();
332
+ }
333
+ });
334
+ // Broadcast to all presence clients
335
+ const broadcastPresence = (message, exclude) => {
336
+ const payload = JSON.stringify(message);
337
+ wssPresence.clients.forEach((client) => {
338
+ if (client !== exclude && client.readyState === WebSocket.OPEN) {
339
+ client.send(payload);
340
+ }
341
+ });
342
+ };
343
+ // Get online users list
344
+ const getOnlineUsersList = () => {
345
+ return Array.from(onlineUsers.values()).map((state) => state.info);
346
+ };
347
+ // Heartbeat interval to detect dead connections (30 seconds)
348
+ const PRESENCE_HEARTBEAT_INTERVAL = 30000;
349
+ const _PRESENCE_HEARTBEAT_TIMEOUT = 35000; // Allow 5s grace period (reserved for future use)
350
+ // Track connection health for heartbeat
351
+ const connectionHealth = new WeakMap();
352
+ // Heartbeat interval to clean up dead connections
353
+ const presenceHeartbeat = setInterval(() => {
354
+ const now = Date.now();
355
+ wssPresence.clients.forEach((ws) => {
356
+ const health = connectionHealth.get(ws);
357
+ if (!health) {
358
+ // New connection without health tracking - initialize it
359
+ connectionHealth.set(ws, { isAlive: true, lastPing: now });
360
+ return;
361
+ }
362
+ if (!health.isAlive) {
363
+ // Connection didn't respond to last ping - terminate it
364
+ ws.terminate();
365
+ return;
366
+ }
367
+ // Mark as not alive until we get a pong
368
+ health.isAlive = false;
369
+ health.lastPing = now;
370
+ ws.ping();
371
+ });
372
+ }, PRESENCE_HEARTBEAT_INTERVAL);
373
+ // Clean up interval on server close
374
+ wssPresence.on('close', () => {
375
+ clearInterval(presenceHeartbeat);
376
+ });
377
+ // Handle presence connections
378
+ wssPresence.on('connection', (ws) => {
379
+ // Initialize health tracking (no log - too noisy)
380
+ connectionHealth.set(ws, { isAlive: true, lastPing: Date.now() });
381
+ // Handle pong responses (heartbeat)
382
+ ws.on('pong', () => {
383
+ const health = connectionHealth.get(ws);
384
+ if (health) {
385
+ health.isAlive = true;
386
+ }
387
+ });
388
+ let clientUsername;
389
+ ws.on('message', (data) => {
390
+ try {
391
+ const msg = JSON.parse(data.toString());
392
+ if (msg.type === 'presence') {
393
+ if (msg.action === 'join' && msg.user?.username) {
394
+ const username = msg.user.username;
395
+ const avatarUrl = msg.user.avatarUrl;
396
+ if (!isValidUsername(username)) {
397
+ console.warn(`[cloud] Invalid username rejected: ${username}`);
398
+ return;
399
+ }
400
+ if (!isValidAvatarUrl(avatarUrl)) {
401
+ console.warn(`[cloud] Invalid avatar URL rejected for user ${username}`);
402
+ return;
403
+ }
404
+ clientUsername = username;
405
+ const now = new Date().toISOString();
406
+ const existing = onlineUsers.get(username);
407
+ if (existing) {
408
+ existing.connections.add(ws);
409
+ existing.info.lastSeen = now;
410
+ // Only log at milestones to reduce noise
411
+ const count = existing.connections.size;
412
+ if (count === 2 || count === 5 || count === 10 || count % 50 === 0) {
413
+ console.log(`[cloud] User ${username} has ${count} connections`);
414
+ }
415
+ }
416
+ else {
417
+ onlineUsers.set(username, {
418
+ info: { username, avatarUrl, connectedAt: now, lastSeen: now },
419
+ connections: new Set([ws]),
420
+ });
421
+ console.log(`[cloud] User ${username} came online`);
422
+ broadcastPresence({
423
+ type: 'presence_join',
424
+ user: { username, avatarUrl, connectedAt: now, lastSeen: now },
425
+ }, ws);
426
+ }
427
+ ws.send(JSON.stringify({
428
+ type: 'presence_list',
429
+ users: getOnlineUsersList(),
430
+ }));
431
+ }
432
+ else if (msg.action === 'leave') {
433
+ if (!clientUsername || msg.username !== clientUsername)
434
+ return;
435
+ const userState = onlineUsers.get(clientUsername);
436
+ if (userState) {
437
+ userState.connections.delete(ws);
438
+ if (userState.connections.size === 0) {
439
+ onlineUsers.delete(clientUsername);
440
+ console.log(`[cloud] User ${clientUsername} went offline`);
441
+ broadcastPresence({ type: 'presence_leave', username: clientUsername });
442
+ }
443
+ }
444
+ }
445
+ }
446
+ else if (msg.type === 'typing') {
447
+ if (!clientUsername || msg.username !== clientUsername)
448
+ return;
449
+ const userState = onlineUsers.get(clientUsername);
450
+ if (userState) {
451
+ userState.info.lastSeen = new Date().toISOString();
452
+ }
453
+ broadcastPresence({
454
+ type: 'typing',
455
+ username: clientUsername,
456
+ avatarUrl: userState?.info.avatarUrl,
457
+ isTyping: msg.isTyping,
458
+ }, ws);
459
+ }
460
+ }
461
+ catch (err) {
462
+ console.error('[cloud] Invalid presence message:', err);
463
+ }
464
+ });
465
+ ws.on('close', () => {
466
+ if (clientUsername) {
467
+ const userState = onlineUsers.get(clientUsername);
468
+ if (userState) {
469
+ userState.connections.delete(ws);
470
+ if (userState.connections.size === 0) {
471
+ onlineUsers.delete(clientUsername);
472
+ console.log(`[cloud] User ${clientUsername} disconnected`);
473
+ broadcastPresence({ type: 'presence_leave', username: clientUsername });
474
+ }
475
+ }
476
+ }
477
+ });
478
+ ws.on('error', (err) => {
479
+ console.error('[cloud] Presence WebSocket error:', err);
480
+ });
481
+ });
482
+ wssPresence.on('error', (err) => {
483
+ console.error('[cloud] Presence WebSocket server error:', err);
484
+ });
171
485
  return {
172
486
  app,
173
487
  async start() {
488
+ // Run database migrations before accepting connections
489
+ console.log('[cloud] Running database migrations...');
490
+ await runMigrations();
491
+ // Initialize scaling orchestrator for auto-scaling
492
+ if (process.env.RELAY_CLOUD_ENABLED === 'true') {
493
+ try {
494
+ scalingOrchestrator = getScalingOrchestrator();
495
+ await scalingOrchestrator.initialize(config.redisUrl);
496
+ console.log('[cloud] Scaling orchestrator initialized');
497
+ // Log scaling events
498
+ scalingOrchestrator.on('scaling_started', (op) => {
499
+ console.log(`[scaling] Started: ${op.action} for user ${op.userId}`);
500
+ });
501
+ scalingOrchestrator.on('scaling_completed', (op) => {
502
+ console.log(`[scaling] Completed: ${op.action} for user ${op.userId}`);
503
+ });
504
+ scalingOrchestrator.on('scaling_error', ({ operation, error }) => {
505
+ console.error(`[scaling] Error: ${operation.action} for ${operation.userId}:`, error);
506
+ });
507
+ scalingOrchestrator.on('workspace_provisioned', (data) => {
508
+ console.log(`[scaling] Provisioned workspace ${data.workspaceId} for user ${data.userId}`);
509
+ });
510
+ }
511
+ catch (error) {
512
+ console.warn('[cloud] Failed to initialize scaling orchestrator:', error);
513
+ // Non-fatal - server can run without auto-scaling
514
+ }
515
+ }
174
516
  return new Promise((resolve) => {
175
- server = app.listen(config.port, () => {
517
+ server = httpServer.listen(config.port, () => {
176
518
  console.log(`Agent Relay Cloud running on port ${config.port}`);
177
519
  console.log(`Public URL: ${config.publicUrl}`);
520
+ console.log(`WebSocket: ws://localhost:${config.port}/ws/presence`);
178
521
  resolve();
179
522
  });
180
523
  });
181
524
  },
182
525
  async stop() {
526
+ // Shutdown scaling orchestrator
527
+ if (scalingOrchestrator) {
528
+ await scalingOrchestrator.shutdown();
529
+ }
530
+ // Close WebSocket server
531
+ wssPresence.close();
183
532
  if (server) {
184
533
  await new Promise((resolve) => server.close(() => resolve()));
185
534
  }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Auto-Scaler Service
3
+ *
4
+ * Monitors workspace metrics and automatically scales instances based on
5
+ * defined policies. Uses Redis pub/sub for cross-server coordination to
6
+ * ensure only one scaling operation happens at a time.
7
+ *
8
+ * Key responsibilities:
9
+ * - Subscribe to metrics updates from monitoring service
10
+ * - Evaluate scaling policies periodically
11
+ * - Coordinate scaling decisions across multiple cloud servers
12
+ * - Execute scaling actions via workspace provisioner
13
+ * - Track scaling history and pending operations
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import { ScalingDecision, UserScalingContext, WorkspaceMetrics } from './scaling-policy.js';
17
+ export interface ScalingOperation {
18
+ id: string;
19
+ userId: string;
20
+ action: 'scale_up' | 'scale_down' | 'resize_up' | 'resize_down' | 'increase_agent_limit' | 'migrate_agents' | 'rebalance';
21
+ targetWorkspaceId?: string;
22
+ targetResourceTier?: 'small' | 'medium' | 'large' | 'xlarge';
23
+ targetAgentLimit?: number;
24
+ status: 'pending' | 'in_progress' | 'completed' | 'failed';
25
+ startedAt: Date;
26
+ completedAt?: Date;
27
+ error?: string;
28
+ triggeredBy: string;
29
+ metrics: Record<string, number>;
30
+ }
31
+ export interface AutoScalerConfig {
32
+ enabled: boolean;
33
+ evaluationIntervalMs: number;
34
+ lockTimeoutMs: number;
35
+ maxConcurrentOperations: number;
36
+ redisKeyPrefix: string;
37
+ }
38
+ export interface MetricsSnapshot {
39
+ userId: string;
40
+ workspaces: WorkspaceMetrics[];
41
+ timestamp: Date;
42
+ }
43
+ export declare class AutoScaler extends EventEmitter {
44
+ private config;
45
+ private policyService;
46
+ private redis;
47
+ private subscriber;
48
+ private evaluationTimer;
49
+ private pendingOperations;
50
+ private metricsCache;
51
+ private isLeader;
52
+ private serverId;
53
+ private lastScalingActions;
54
+ constructor(config?: Partial<AutoScalerConfig>);
55
+ /**
56
+ * Initialize with Redis connection for cross-server coordination
57
+ */
58
+ initialize(redisUrl: string): Promise<void>;
59
+ /**
60
+ * Set up Redis pub/sub subscriptions
61
+ */
62
+ private setupSubscriptions;
63
+ /**
64
+ * Handle channel message
65
+ */
66
+ private handleChannelMessage;
67
+ /**
68
+ * Handle incoming pub/sub messages
69
+ */
70
+ private handlePubSubMessage;
71
+ /**
72
+ * Handle metrics update from monitoring service
73
+ */
74
+ private handleMetricsUpdate;
75
+ /**
76
+ * Handle scaling request (from any server)
77
+ */
78
+ private handleScalingRequest;
79
+ /**
80
+ * Handle scaling completion
81
+ */
82
+ private handleScalingComplete;
83
+ /**
84
+ * Handle leadership change
85
+ */
86
+ private handleLeadershipChange;
87
+ /**
88
+ * Attempt to become the leader (only leader evaluates scaling)
89
+ */
90
+ private attemptLeadership;
91
+ /**
92
+ * Schedule leadership lock renewal
93
+ */
94
+ private scheduleLeadershipRenewal;
95
+ /**
96
+ * Start the periodic evaluation loop
97
+ */
98
+ private startEvaluationLoop;
99
+ /**
100
+ * Evaluate scaling for all cached users
101
+ */
102
+ private evaluateAllUsers;
103
+ /**
104
+ * Evaluate scaling for a specific user
105
+ */
106
+ private evaluateUserScaling;
107
+ /**
108
+ * Build user context for policy evaluation
109
+ */
110
+ private buildUserContext;
111
+ /**
112
+ * Request a scaling operation
113
+ */
114
+ private requestScaling;
115
+ /**
116
+ * Execute the actual scaling operation
117
+ */
118
+ private executeScaling;
119
+ /**
120
+ * Report metrics from monitoring service
121
+ */
122
+ reportMetrics(userId: string, workspaces: WorkspaceMetrics[]): Promise<void>;
123
+ /**
124
+ * Manually trigger scaling evaluation for a user
125
+ */
126
+ triggerEvaluation(userId: string): Promise<ScalingDecision | null>;
127
+ /**
128
+ * Get current scaling status
129
+ */
130
+ getStatus(): {
131
+ enabled: boolean;
132
+ isLeader: boolean;
133
+ serverId: string;
134
+ pendingOperations: number;
135
+ cachedUsers: number;
136
+ };
137
+ /**
138
+ * Get pending operations for a user
139
+ */
140
+ getPendingOperations(userId?: string): ScalingOperation[];
141
+ /**
142
+ * Update user plan in cache
143
+ */
144
+ setUserPlan(userId: string, plan: UserScalingContext['plan']): Promise<void>;
145
+ /**
146
+ * Clean shutdown
147
+ */
148
+ shutdown(): Promise<void>;
149
+ }
150
+ export declare function getAutoScaler(): AutoScaler;
151
+ export declare function createAutoScaler(config?: Partial<AutoScalerConfig>): AutoScaler;
152
+ //# sourceMappingURL=auto-scaler.d.ts.map