agent-relay 1.0.22 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (613) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge/shadow-cli.d.ts +17 -0
  3. package/dist/bridge/shadow-cli.d.ts.map +1 -0
  4. package/dist/bridge/shadow-cli.js +75 -0
  5. package/dist/bridge/shadow-cli.js.map +1 -0
  6. package/dist/bridge/shadow-config.d.ts +87 -0
  7. package/dist/bridge/shadow-config.d.ts.map +1 -0
  8. package/dist/bridge/shadow-config.js +134 -0
  9. package/dist/bridge/shadow-config.js.map +1 -0
  10. package/dist/bridge/spawner.d.ts +68 -1
  11. package/dist/bridge/spawner.d.ts.map +1 -1
  12. package/dist/bridge/spawner.js +360 -16
  13. package/dist/bridge/spawner.js.map +1 -1
  14. package/dist/bridge/types.d.ts +67 -0
  15. package/dist/bridge/types.d.ts.map +1 -1
  16. package/dist/cli/index.js +1196 -15
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cloud/api/auth.d.ts +20 -0
  19. package/dist/cloud/api/auth.d.ts.map +1 -0
  20. package/dist/cloud/api/auth.js +128 -0
  21. package/dist/cloud/api/auth.js.map +1 -0
  22. package/dist/cloud/api/billing.d.ts +17 -0
  23. package/dist/cloud/api/billing.d.ts.map +1 -0
  24. package/dist/cloud/api/billing.js +353 -0
  25. package/dist/cloud/api/billing.js.map +1 -0
  26. package/dist/cloud/api/cli-pty-runner.d.ts +54 -0
  27. package/dist/cloud/api/cli-pty-runner.d.ts.map +1 -0
  28. package/dist/cloud/api/cli-pty-runner.js +119 -0
  29. package/dist/cloud/api/cli-pty-runner.js.map +1 -0
  30. package/dist/cloud/api/coordinators.d.ts +8 -0
  31. package/dist/cloud/api/coordinators.d.ts.map +1 -0
  32. package/dist/cloud/api/coordinators.js +347 -0
  33. package/dist/cloud/api/coordinators.js.map +1 -0
  34. package/dist/cloud/api/daemons.d.ts +12 -0
  35. package/dist/cloud/api/daemons.d.ts.map +1 -0
  36. package/dist/cloud/api/daemons.js +320 -0
  37. package/dist/cloud/api/daemons.js.map +1 -0
  38. package/dist/cloud/api/generic-webhooks.d.ts +8 -0
  39. package/dist/cloud/api/generic-webhooks.d.ts.map +1 -0
  40. package/dist/cloud/api/generic-webhooks.js +129 -0
  41. package/dist/cloud/api/generic-webhooks.js.map +1 -0
  42. package/dist/cloud/api/git.d.ts +8 -0
  43. package/dist/cloud/api/git.d.ts.map +1 -0
  44. package/dist/cloud/api/git.js +131 -0
  45. package/dist/cloud/api/git.js.map +1 -0
  46. package/dist/cloud/api/github-app.d.ts +11 -0
  47. package/dist/cloud/api/github-app.d.ts.map +1 -0
  48. package/dist/cloud/api/github-app.js +189 -0
  49. package/dist/cloud/api/github-app.js.map +1 -0
  50. package/dist/cloud/api/middleware/planLimits.d.ts +43 -0
  51. package/dist/cloud/api/middleware/planLimits.d.ts.map +1 -0
  52. package/dist/cloud/api/middleware/planLimits.js +202 -0
  53. package/dist/cloud/api/middleware/planLimits.js.map +1 -0
  54. package/dist/cloud/api/monitoring.d.ts +11 -0
  55. package/dist/cloud/api/monitoring.d.ts.map +1 -0
  56. package/dist/cloud/api/monitoring.js +578 -0
  57. package/dist/cloud/api/monitoring.js.map +1 -0
  58. package/dist/cloud/api/nango-auth.d.ts +9 -0
  59. package/dist/cloud/api/nango-auth.d.ts.map +1 -0
  60. package/dist/cloud/api/nango-auth.js +377 -0
  61. package/dist/cloud/api/nango-auth.js.map +1 -0
  62. package/dist/cloud/api/onboarding.d.ts +15 -0
  63. package/dist/cloud/api/onboarding.d.ts.map +1 -0
  64. package/dist/cloud/api/onboarding.js +588 -0
  65. package/dist/cloud/api/onboarding.js.map +1 -0
  66. package/dist/cloud/api/policy.d.ts +8 -0
  67. package/dist/cloud/api/policy.d.ts.map +1 -0
  68. package/dist/cloud/api/policy.js +229 -0
  69. package/dist/cloud/api/policy.js.map +1 -0
  70. package/dist/cloud/api/providers.d.ts +7 -0
  71. package/dist/cloud/api/providers.d.ts.map +1 -0
  72. package/dist/cloud/api/providers.js +507 -0
  73. package/dist/cloud/api/providers.js.map +1 -0
  74. package/dist/cloud/api/repos.d.ts +7 -0
  75. package/dist/cloud/api/repos.d.ts.map +1 -0
  76. package/dist/cloud/api/repos.js +314 -0
  77. package/dist/cloud/api/repos.js.map +1 -0
  78. package/dist/cloud/api/teams.d.ts +7 -0
  79. package/dist/cloud/api/teams.d.ts.map +1 -0
  80. package/dist/cloud/api/teams.js +279 -0
  81. package/dist/cloud/api/teams.js.map +1 -0
  82. package/dist/cloud/api/test-helpers.d.ts +10 -0
  83. package/dist/cloud/api/test-helpers.d.ts.map +1 -0
  84. package/dist/cloud/api/test-helpers.js +575 -0
  85. package/dist/cloud/api/test-helpers.js.map +1 -0
  86. package/dist/cloud/api/usage.d.ts +7 -0
  87. package/dist/cloud/api/usage.d.ts.map +1 -0
  88. package/dist/cloud/api/usage.js +98 -0
  89. package/dist/cloud/api/usage.js.map +1 -0
  90. package/dist/cloud/api/webhooks.d.ts +7 -0
  91. package/dist/cloud/api/webhooks.d.ts.map +1 -0
  92. package/dist/cloud/api/webhooks.js +496 -0
  93. package/dist/cloud/api/webhooks.js.map +1 -0
  94. package/dist/cloud/api/workspaces.d.ts +7 -0
  95. package/dist/cloud/api/workspaces.d.ts.map +1 -0
  96. package/dist/cloud/api/workspaces.js +727 -0
  97. package/dist/cloud/api/workspaces.js.map +1 -0
  98. package/dist/cloud/billing/index.d.ts +9 -0
  99. package/dist/cloud/billing/index.d.ts.map +1 -0
  100. package/dist/cloud/billing/index.js +9 -0
  101. package/dist/cloud/billing/index.js.map +1 -0
  102. package/dist/cloud/billing/plans.d.ts +39 -0
  103. package/dist/cloud/billing/plans.d.ts.map +1 -0
  104. package/dist/cloud/billing/plans.js +245 -0
  105. package/dist/cloud/billing/plans.js.map +1 -0
  106. package/dist/cloud/billing/service.d.ts +80 -0
  107. package/dist/cloud/billing/service.d.ts.map +1 -0
  108. package/dist/cloud/billing/service.js +388 -0
  109. package/dist/cloud/billing/service.js.map +1 -0
  110. package/dist/cloud/billing/types.d.ts +141 -0
  111. package/dist/cloud/billing/types.d.ts.map +1 -0
  112. package/dist/cloud/billing/types.js +7 -0
  113. package/dist/cloud/billing/types.js.map +1 -0
  114. package/dist/cloud/config.d.ts +66 -0
  115. package/dist/cloud/config.d.ts.map +1 -0
  116. package/dist/cloud/config.js +92 -0
  117. package/dist/cloud/config.js.map +1 -0
  118. package/dist/cloud/db/drizzle.d.ts +215 -0
  119. package/dist/cloud/db/drizzle.d.ts.map +1 -0
  120. package/dist/cloud/db/drizzle.js +1083 -0
  121. package/dist/cloud/db/drizzle.js.map +1 -0
  122. package/dist/cloud/db/index.d.ts +35 -0
  123. package/dist/cloud/db/index.d.ts.map +1 -0
  124. package/dist/cloud/db/index.js +52 -0
  125. package/dist/cloud/db/index.js.map +1 -0
  126. package/dist/cloud/db/schema.d.ts +4519 -0
  127. package/dist/cloud/db/schema.d.ts.map +1 -0
  128. package/dist/cloud/db/schema.js +547 -0
  129. package/dist/cloud/db/schema.js.map +1 -0
  130. package/dist/cloud/index.d.ts +12 -0
  131. package/dist/cloud/index.d.ts.map +1 -0
  132. package/dist/cloud/index.js +39 -0
  133. package/dist/cloud/index.js.map +1 -0
  134. package/dist/cloud/provisioner/index.d.ts +75 -0
  135. package/dist/cloud/provisioner/index.d.ts.map +1 -0
  136. package/dist/cloud/provisioner/index.js +977 -0
  137. package/dist/cloud/provisioner/index.js.map +1 -0
  138. package/dist/cloud/server.d.ts +17 -0
  139. package/dist/cloud/server.d.ts.map +1 -0
  140. package/dist/cloud/server.js +534 -0
  141. package/dist/cloud/server.js.map +1 -0
  142. package/dist/cloud/services/auto-scaler.d.ts +152 -0
  143. package/dist/cloud/services/auto-scaler.d.ts.map +1 -0
  144. package/dist/cloud/services/auto-scaler.js +439 -0
  145. package/dist/cloud/services/auto-scaler.js.map +1 -0
  146. package/dist/cloud/services/capacity-manager.d.ts +148 -0
  147. package/dist/cloud/services/capacity-manager.d.ts.map +1 -0
  148. package/dist/cloud/services/capacity-manager.js +449 -0
  149. package/dist/cloud/services/capacity-manager.js.map +1 -0
  150. package/dist/cloud/services/ci-agent-spawner.d.ts +49 -0
  151. package/dist/cloud/services/ci-agent-spawner.d.ts.map +1 -0
  152. package/dist/cloud/services/ci-agent-spawner.js +373 -0
  153. package/dist/cloud/services/ci-agent-spawner.js.map +1 -0
  154. package/dist/cloud/services/coordinator.d.ts +62 -0
  155. package/dist/cloud/services/coordinator.d.ts.map +1 -0
  156. package/dist/cloud/services/coordinator.js +389 -0
  157. package/dist/cloud/services/coordinator.js.map +1 -0
  158. package/dist/cloud/services/index.d.ts +12 -0
  159. package/dist/cloud/services/index.d.ts.map +1 -0
  160. package/dist/cloud/services/index.js +15 -0
  161. package/dist/cloud/services/index.js.map +1 -0
  162. package/dist/cloud/services/mention-handler.d.ts +65 -0
  163. package/dist/cloud/services/mention-handler.d.ts.map +1 -0
  164. package/dist/cloud/services/mention-handler.js +405 -0
  165. package/dist/cloud/services/mention-handler.js.map +1 -0
  166. package/dist/cloud/services/nango.d.ts +126 -0
  167. package/dist/cloud/services/nango.d.ts.map +1 -0
  168. package/dist/cloud/services/nango.js +191 -0
  169. package/dist/cloud/services/nango.js.map +1 -0
  170. package/dist/cloud/services/persistence.d.ts +131 -0
  171. package/dist/cloud/services/persistence.d.ts.map +1 -0
  172. package/dist/cloud/services/persistence.js +200 -0
  173. package/dist/cloud/services/persistence.js.map +1 -0
  174. package/dist/cloud/services/planLimits.d.ts +125 -0
  175. package/dist/cloud/services/planLimits.d.ts.map +1 -0
  176. package/dist/cloud/services/planLimits.js +282 -0
  177. package/dist/cloud/services/planLimits.js.map +1 -0
  178. package/dist/cloud/services/scaling-orchestrator.d.ts +159 -0
  179. package/dist/cloud/services/scaling-orchestrator.d.ts.map +1 -0
  180. package/dist/cloud/services/scaling-orchestrator.js +502 -0
  181. package/dist/cloud/services/scaling-orchestrator.js.map +1 -0
  182. package/dist/cloud/services/scaling-policy.d.ts +121 -0
  183. package/dist/cloud/services/scaling-policy.d.ts.map +1 -0
  184. package/dist/cloud/services/scaling-policy.js +415 -0
  185. package/dist/cloud/services/scaling-policy.js.map +1 -0
  186. package/dist/cloud/vault/index.d.ts +76 -0
  187. package/dist/cloud/vault/index.d.ts.map +1 -0
  188. package/dist/cloud/vault/index.js +219 -0
  189. package/dist/cloud/vault/index.js.map +1 -0
  190. package/dist/cloud/webhooks/index.d.ts +24 -0
  191. package/dist/cloud/webhooks/index.d.ts.map +1 -0
  192. package/dist/cloud/webhooks/index.js +29 -0
  193. package/dist/cloud/webhooks/index.js.map +1 -0
  194. package/dist/cloud/webhooks/parsers/github.d.ts +8 -0
  195. package/dist/cloud/webhooks/parsers/github.d.ts.map +1 -0
  196. package/dist/cloud/webhooks/parsers/github.js +234 -0
  197. package/dist/cloud/webhooks/parsers/github.js.map +1 -0
  198. package/dist/cloud/webhooks/parsers/index.d.ts +23 -0
  199. package/dist/cloud/webhooks/parsers/index.d.ts.map +1 -0
  200. package/dist/cloud/webhooks/parsers/index.js +30 -0
  201. package/dist/cloud/webhooks/parsers/index.js.map +1 -0
  202. package/dist/cloud/webhooks/parsers/linear.d.ts +9 -0
  203. package/dist/cloud/webhooks/parsers/linear.d.ts.map +1 -0
  204. package/dist/cloud/webhooks/parsers/linear.js +258 -0
  205. package/dist/cloud/webhooks/parsers/linear.js.map +1 -0
  206. package/dist/cloud/webhooks/parsers/slack.d.ts +9 -0
  207. package/dist/cloud/webhooks/parsers/slack.d.ts.map +1 -0
  208. package/dist/cloud/webhooks/parsers/slack.js +214 -0
  209. package/dist/cloud/webhooks/parsers/slack.js.map +1 -0
  210. package/dist/cloud/webhooks/responders/github.d.ts +8 -0
  211. package/dist/cloud/webhooks/responders/github.d.ts.map +1 -0
  212. package/dist/cloud/webhooks/responders/github.js +73 -0
  213. package/dist/cloud/webhooks/responders/github.js.map +1 -0
  214. package/dist/cloud/webhooks/responders/index.d.ts +23 -0
  215. package/dist/cloud/webhooks/responders/index.d.ts.map +1 -0
  216. package/dist/cloud/webhooks/responders/index.js +30 -0
  217. package/dist/cloud/webhooks/responders/index.js.map +1 -0
  218. package/dist/cloud/webhooks/responders/linear.d.ts +9 -0
  219. package/dist/cloud/webhooks/responders/linear.d.ts.map +1 -0
  220. package/dist/cloud/webhooks/responders/linear.js +149 -0
  221. package/dist/cloud/webhooks/responders/linear.js.map +1 -0
  222. package/dist/cloud/webhooks/responders/slack.d.ts +20 -0
  223. package/dist/cloud/webhooks/responders/slack.d.ts.map +1 -0
  224. package/dist/cloud/webhooks/responders/slack.js +178 -0
  225. package/dist/cloud/webhooks/responders/slack.js.map +1 -0
  226. package/dist/cloud/webhooks/router.d.ts +25 -0
  227. package/dist/cloud/webhooks/router.d.ts.map +1 -0
  228. package/dist/cloud/webhooks/router.js +504 -0
  229. package/dist/cloud/webhooks/router.js.map +1 -0
  230. package/dist/cloud/webhooks/rules-engine.d.ts +24 -0
  231. package/dist/cloud/webhooks/rules-engine.d.ts.map +1 -0
  232. package/dist/cloud/webhooks/rules-engine.js +287 -0
  233. package/dist/cloud/webhooks/rules-engine.js.map +1 -0
  234. package/dist/cloud/webhooks/types.d.ts +186 -0
  235. package/dist/cloud/webhooks/types.d.ts.map +1 -0
  236. package/dist/cloud/webhooks/types.js +8 -0
  237. package/dist/cloud/webhooks/types.js.map +1 -0
  238. package/dist/continuity/formatter.d.ts +51 -0
  239. package/dist/continuity/formatter.d.ts.map +1 -0
  240. package/dist/continuity/formatter.js +313 -0
  241. package/dist/continuity/formatter.js.map +1 -0
  242. package/dist/continuity/handoff-store.d.ts +67 -0
  243. package/dist/continuity/handoff-store.d.ts.map +1 -0
  244. package/dist/continuity/handoff-store.js +472 -0
  245. package/dist/continuity/handoff-store.js.map +1 -0
  246. package/dist/continuity/index.d.ts +45 -0
  247. package/dist/continuity/index.d.ts.map +1 -0
  248. package/dist/continuity/index.js +48 -0
  249. package/dist/continuity/index.js.map +1 -0
  250. package/dist/continuity/ledger-store.d.ts +110 -0
  251. package/dist/continuity/ledger-store.d.ts.map +1 -0
  252. package/dist/continuity/ledger-store.js +500 -0
  253. package/dist/continuity/ledger-store.js.map +1 -0
  254. package/dist/continuity/manager.d.ts +178 -0
  255. package/dist/continuity/manager.d.ts.map +1 -0
  256. package/dist/continuity/manager.js +562 -0
  257. package/dist/continuity/manager.js.map +1 -0
  258. package/dist/continuity/parser.d.ts +76 -0
  259. package/dist/continuity/parser.d.ts.map +1 -0
  260. package/dist/continuity/parser.js +579 -0
  261. package/dist/continuity/parser.js.map +1 -0
  262. package/dist/continuity/types.d.ts +180 -0
  263. package/dist/continuity/types.d.ts.map +1 -0
  264. package/dist/continuity/types.js +9 -0
  265. package/dist/continuity/types.js.map +1 -0
  266. package/dist/daemon/agent-manager.d.ts +114 -0
  267. package/dist/daemon/agent-manager.d.ts.map +1 -0
  268. package/dist/daemon/agent-manager.js +513 -0
  269. package/dist/daemon/agent-manager.js.map +1 -0
  270. package/dist/daemon/agent-registry.d.ts +34 -0
  271. package/dist/daemon/agent-registry.d.ts.map +1 -1
  272. package/dist/daemon/agent-registry.js +45 -2
  273. package/dist/daemon/agent-registry.js.map +1 -1
  274. package/dist/daemon/api.d.ts +81 -0
  275. package/dist/daemon/api.d.ts.map +1 -0
  276. package/dist/daemon/api.js +554 -0
  277. package/dist/daemon/api.js.map +1 -0
  278. package/dist/daemon/cli-auth.d.ts +67 -0
  279. package/dist/daemon/cli-auth.d.ts.map +1 -0
  280. package/dist/daemon/cli-auth.js +537 -0
  281. package/dist/daemon/cli-auth.js.map +1 -0
  282. package/dist/daemon/cloud-sync.d.ts +101 -0
  283. package/dist/daemon/cloud-sync.d.ts.map +1 -0
  284. package/dist/daemon/cloud-sync.js +263 -0
  285. package/dist/daemon/cloud-sync.js.map +1 -0
  286. package/dist/daemon/index.d.ts +4 -0
  287. package/dist/daemon/index.d.ts.map +1 -1
  288. package/dist/daemon/index.js +6 -0
  289. package/dist/daemon/index.js.map +1 -1
  290. package/dist/daemon/orchestrator.d.ts +155 -0
  291. package/dist/daemon/orchestrator.d.ts.map +1 -0
  292. package/dist/daemon/orchestrator.js +766 -0
  293. package/dist/daemon/orchestrator.js.map +1 -0
  294. package/dist/daemon/router.d.ts +29 -0
  295. package/dist/daemon/router.d.ts.map +1 -1
  296. package/dist/daemon/router.js +143 -21
  297. package/dist/daemon/router.js.map +1 -1
  298. package/dist/daemon/server.d.ts +42 -0
  299. package/dist/daemon/server.d.ts.map +1 -1
  300. package/dist/daemon/server.js +199 -16
  301. package/dist/daemon/server.js.map +1 -1
  302. package/dist/daemon/services/browser-testing.d.ts +88 -0
  303. package/dist/daemon/services/browser-testing.d.ts.map +1 -0
  304. package/dist/daemon/services/browser-testing.js +244 -0
  305. package/dist/daemon/services/browser-testing.js.map +1 -0
  306. package/dist/daemon/services/container-spawner.d.ts +135 -0
  307. package/dist/daemon/services/container-spawner.d.ts.map +1 -0
  308. package/dist/daemon/services/container-spawner.js +313 -0
  309. package/dist/daemon/services/container-spawner.js.map +1 -0
  310. package/dist/daemon/types.d.ts +131 -0
  311. package/dist/daemon/types.d.ts.map +1 -0
  312. package/dist/daemon/types.js +6 -0
  313. package/dist/daemon/types.js.map +1 -0
  314. package/dist/daemon/workspace-manager.d.ts +75 -0
  315. package/dist/daemon/workspace-manager.d.ts.map +1 -0
  316. package/dist/daemon/workspace-manager.js +289 -0
  317. package/dist/daemon/workspace-manager.js.map +1 -0
  318. package/dist/dashboard/out/404.html +1 -1
  319. package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +1 -0
  320. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
  321. package/dist/dashboard/out/_next/static/chunks/480-2d4111711d4e473c.js +1 -0
  322. package/dist/dashboard/out/_next/static/chunks/724-73c1ee5f60abe860.js +9 -0
  323. package/dist/dashboard/out/_next/static/chunks/766-c3a14283c88d815b.js +1 -0
  324. package/dist/dashboard/out/_next/static/chunks/app/app/page-7120be68bea622f3.js +1 -0
  325. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-dc2e3a1a22478efc.js +1 -0
  326. package/dist/dashboard/out/_next/static/chunks/app/history/page-56a8b4616a90dc43.js +1 -0
  327. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +1 -0
  328. package/dist/dashboard/out/_next/static/chunks/app/login/page-3eac37ea6f5dd153.js +1 -0
  329. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-1081dd190a331a91.js +1 -0
  330. package/dist/dashboard/out/_next/static/chunks/app/page-daf87e86f783f980.js +1 -0
  331. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-4d72d5a5d8a9b618.js +1 -0
  332. package/dist/dashboard/out/_next/static/chunks/app/providers/page-b68a681526eb145e.js +1 -0
  333. package/dist/dashboard/out/_next/static/chunks/app/signup/page-fee4ed1709070bcd.js +1 -0
  334. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
  335. package/dist/dashboard/out/_next/static/chunks/{main-e0a1f53fe0617a63.js → main-97850e03d723ea8c.js} +1 -1
  336. package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +1 -0
  337. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
  338. package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +1 -0
  339. package/dist/dashboard/out/_next/static/css/411ce23ffeae9f76.css +1 -0
  340. package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
  341. package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
  342. package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
  343. package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
  344. package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
  345. package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +45 -0
  346. package/dist/dashboard/out/alt-logos/logo.svg +38 -0
  347. package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
  348. package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
  349. package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
  350. package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
  351. package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
  352. package/dist/dashboard/out/alt-logos/monogram-logo.svg +38 -0
  353. package/dist/dashboard/out/app.html +1 -0
  354. package/dist/dashboard/out/app.txt +7 -0
  355. package/dist/dashboard/out/connect-repos.html +1 -0
  356. package/dist/dashboard/out/connect-repos.txt +7 -0
  357. package/dist/dashboard/out/history.html +1 -0
  358. package/dist/dashboard/out/history.txt +7 -0
  359. package/dist/dashboard/out/index.html +1 -1
  360. package/dist/dashboard/out/index.txt +2 -2
  361. package/dist/dashboard/out/login.html +6 -0
  362. package/dist/dashboard/out/login.txt +7 -0
  363. package/dist/dashboard/out/metrics.html +1 -515
  364. package/dist/dashboard/out/metrics.txt +2 -2
  365. package/dist/dashboard/out/pricing.html +13 -0
  366. package/dist/dashboard/out/pricing.txt +7 -0
  367. package/dist/dashboard/out/providers.html +1 -0
  368. package/dist/dashboard/out/providers.txt +7 -0
  369. package/dist/dashboard/out/signup.html +6 -0
  370. package/dist/dashboard/out/signup.txt +7 -0
  371. package/dist/dashboard-server/metrics.d.ts.map +1 -1
  372. package/dist/dashboard-server/metrics.js +3 -2
  373. package/dist/dashboard-server/metrics.js.map +1 -1
  374. package/dist/dashboard-server/server.d.ts.map +1 -1
  375. package/dist/dashboard-server/server.js +2653 -130
  376. package/dist/dashboard-server/server.js.map +1 -1
  377. package/dist/hooks/emitter.d.ts +40 -0
  378. package/dist/hooks/emitter.d.ts.map +1 -0
  379. package/dist/hooks/emitter.js +63 -0
  380. package/dist/hooks/emitter.js.map +1 -0
  381. package/dist/hooks/index.d.ts +3 -0
  382. package/dist/hooks/index.d.ts.map +1 -1
  383. package/dist/hooks/index.js +3 -0
  384. package/dist/hooks/index.js.map +1 -1
  385. package/dist/hooks/registry.d.ts +173 -0
  386. package/dist/hooks/registry.d.ts.map +1 -0
  387. package/dist/hooks/registry.js +476 -0
  388. package/dist/hooks/registry.js.map +1 -0
  389. package/dist/hooks/trajectory-hooks.d.ts +52 -0
  390. package/dist/hooks/trajectory-hooks.d.ts.map +1 -0
  391. package/dist/hooks/trajectory-hooks.js +183 -0
  392. package/dist/hooks/trajectory-hooks.js.map +1 -0
  393. package/dist/hooks/types.d.ts +141 -0
  394. package/dist/hooks/types.d.ts.map +1 -1
  395. package/dist/index.d.ts +2 -0
  396. package/dist/index.d.ts.map +1 -1
  397. package/dist/index.js +3 -0
  398. package/dist/index.js.map +1 -1
  399. package/dist/memory/adapters/index.d.ts +8 -0
  400. package/dist/memory/adapters/index.d.ts.map +1 -0
  401. package/dist/memory/adapters/index.js +8 -0
  402. package/dist/memory/adapters/index.js.map +1 -0
  403. package/dist/memory/adapters/inmemory.d.ts +59 -0
  404. package/dist/memory/adapters/inmemory.d.ts.map +1 -0
  405. package/dist/memory/adapters/inmemory.js +195 -0
  406. package/dist/memory/adapters/inmemory.js.map +1 -0
  407. package/dist/memory/adapters/supermemory.d.ts +71 -0
  408. package/dist/memory/adapters/supermemory.d.ts.map +1 -0
  409. package/dist/memory/adapters/supermemory.js +338 -0
  410. package/dist/memory/adapters/supermemory.js.map +1 -0
  411. package/dist/memory/factory.d.ts +48 -0
  412. package/dist/memory/factory.d.ts.map +1 -0
  413. package/dist/memory/factory.js +143 -0
  414. package/dist/memory/factory.js.map +1 -0
  415. package/dist/memory/index.d.ts +32 -0
  416. package/dist/memory/index.d.ts.map +1 -0
  417. package/dist/memory/index.js +32 -0
  418. package/dist/memory/index.js.map +1 -0
  419. package/dist/memory/memory-hooks.d.ts +60 -0
  420. package/dist/memory/memory-hooks.d.ts.map +1 -0
  421. package/dist/memory/memory-hooks.js +313 -0
  422. package/dist/memory/memory-hooks.js.map +1 -0
  423. package/dist/memory/service.d.ts +49 -0
  424. package/dist/memory/service.d.ts.map +1 -0
  425. package/dist/memory/service.js +146 -0
  426. package/dist/memory/service.js.map +1 -0
  427. package/dist/memory/types.d.ts +195 -0
  428. package/dist/memory/types.d.ts.map +1 -0
  429. package/dist/memory/types.js +8 -0
  430. package/dist/memory/types.js.map +1 -0
  431. package/dist/policy/agent-policy.d.ts +225 -0
  432. package/dist/policy/agent-policy.d.ts.map +1 -0
  433. package/dist/policy/agent-policy.js +665 -0
  434. package/dist/policy/agent-policy.js.map +1 -0
  435. package/dist/policy/cloud-policy-fetcher.d.ts +12 -0
  436. package/dist/policy/cloud-policy-fetcher.d.ts.map +1 -0
  437. package/dist/policy/cloud-policy-fetcher.js +64 -0
  438. package/dist/policy/cloud-policy-fetcher.js.map +1 -0
  439. package/dist/protocol/types.d.ts +10 -1
  440. package/dist/protocol/types.d.ts.map +1 -1
  441. package/dist/resiliency/context-persistence.d.ts +140 -0
  442. package/dist/resiliency/context-persistence.d.ts.map +1 -0
  443. package/dist/resiliency/context-persistence.js +397 -0
  444. package/dist/resiliency/context-persistence.js.map +1 -0
  445. package/dist/resiliency/crash-insights.d.ts +156 -0
  446. package/dist/resiliency/crash-insights.d.ts.map +1 -0
  447. package/dist/resiliency/crash-insights.js +492 -0
  448. package/dist/resiliency/crash-insights.js.map +1 -0
  449. package/dist/resiliency/gossip-health.d.ts +137 -0
  450. package/dist/resiliency/gossip-health.d.ts.map +1 -0
  451. package/dist/resiliency/gossip-health.js +241 -0
  452. package/dist/resiliency/gossip-health.js.map +1 -0
  453. package/dist/resiliency/health-monitor.d.ts +97 -0
  454. package/dist/resiliency/health-monitor.d.ts.map +1 -0
  455. package/dist/resiliency/health-monitor.js +291 -0
  456. package/dist/resiliency/health-monitor.js.map +1 -0
  457. package/dist/resiliency/index.d.ts +68 -0
  458. package/dist/resiliency/index.d.ts.map +1 -0
  459. package/dist/resiliency/index.js +68 -0
  460. package/dist/resiliency/index.js.map +1 -0
  461. package/dist/resiliency/leader-watchdog.d.ts +109 -0
  462. package/dist/resiliency/leader-watchdog.d.ts.map +1 -0
  463. package/dist/resiliency/leader-watchdog.js +189 -0
  464. package/dist/resiliency/leader-watchdog.js.map +1 -0
  465. package/dist/resiliency/logger.d.ts +114 -0
  466. package/dist/resiliency/logger.d.ts.map +1 -0
  467. package/dist/resiliency/logger.js +250 -0
  468. package/dist/resiliency/logger.js.map +1 -0
  469. package/dist/resiliency/memory-monitor.d.ts +172 -0
  470. package/dist/resiliency/memory-monitor.d.ts.map +1 -0
  471. package/dist/resiliency/memory-monitor.js +593 -0
  472. package/dist/resiliency/memory-monitor.js.map +1 -0
  473. package/dist/resiliency/metrics.d.ts +115 -0
  474. package/dist/resiliency/metrics.d.ts.map +1 -0
  475. package/dist/resiliency/metrics.js +239 -0
  476. package/dist/resiliency/metrics.js.map +1 -0
  477. package/dist/resiliency/provider-context.d.ts +100 -0
  478. package/dist/resiliency/provider-context.d.ts.map +1 -0
  479. package/dist/resiliency/provider-context.js +360 -0
  480. package/dist/resiliency/provider-context.js.map +1 -0
  481. package/dist/resiliency/stateless-lead.d.ts +149 -0
  482. package/dist/resiliency/stateless-lead.d.ts.map +1 -0
  483. package/dist/resiliency/stateless-lead.js +308 -0
  484. package/dist/resiliency/stateless-lead.js.map +1 -0
  485. package/dist/resiliency/supervisor.d.ts +147 -0
  486. package/dist/resiliency/supervisor.d.ts.map +1 -0
  487. package/dist/resiliency/supervisor.js +459 -0
  488. package/dist/resiliency/supervisor.js.map +1 -0
  489. package/dist/shared/cli-auth-config.d.ts +91 -0
  490. package/dist/shared/cli-auth-config.d.ts.map +1 -0
  491. package/dist/shared/cli-auth-config.js +264 -0
  492. package/dist/shared/cli-auth-config.js.map +1 -0
  493. package/dist/storage/adapter.d.ts +3 -1
  494. package/dist/storage/adapter.d.ts.map +1 -1
  495. package/dist/storage/adapter.js +12 -2
  496. package/dist/storage/adapter.js.map +1 -1
  497. package/dist/storage/sqlite-adapter.d.ts.map +1 -1
  498. package/dist/storage/sqlite-adapter.js +18 -14
  499. package/dist/storage/sqlite-adapter.js.map +1 -1
  500. package/dist/trajectory/config.d.ts +84 -0
  501. package/dist/trajectory/config.d.ts.map +1 -0
  502. package/dist/trajectory/config.js +163 -0
  503. package/dist/trajectory/config.js.map +1 -0
  504. package/dist/trajectory/index.d.ts +8 -0
  505. package/dist/trajectory/index.d.ts.map +1 -0
  506. package/dist/trajectory/index.js +8 -0
  507. package/dist/trajectory/index.js.map +1 -0
  508. package/dist/trajectory/integration.d.ts +292 -0
  509. package/dist/trajectory/integration.d.ts.map +1 -0
  510. package/dist/trajectory/integration.js +834 -0
  511. package/dist/trajectory/integration.js.map +1 -0
  512. package/dist/utils/index.d.ts +1 -0
  513. package/dist/utils/index.d.ts.map +1 -1
  514. package/dist/utils/index.js +1 -0
  515. package/dist/utils/index.js.map +1 -1
  516. package/dist/utils/logger.d.ts +40 -0
  517. package/dist/utils/logger.d.ts.map +1 -0
  518. package/dist/utils/logger.js +84 -0
  519. package/dist/utils/logger.js.map +1 -0
  520. package/dist/utils/project-namespace.d.ts +24 -0
  521. package/dist/utils/project-namespace.d.ts.map +1 -1
  522. package/dist/utils/project-namespace.js +84 -0
  523. package/dist/utils/project-namespace.js.map +1 -1
  524. package/dist/wrapper/client.d.ts +16 -1
  525. package/dist/wrapper/client.d.ts.map +1 -1
  526. package/dist/wrapper/client.js +32 -1
  527. package/dist/wrapper/client.js.map +1 -1
  528. package/dist/wrapper/parser.d.ts +13 -0
  529. package/dist/wrapper/parser.d.ts.map +1 -1
  530. package/dist/wrapper/parser.js +217 -47
  531. package/dist/wrapper/parser.js.map +1 -1
  532. package/dist/wrapper/pty-wrapper.d.ts +219 -17
  533. package/dist/wrapper/pty-wrapper.d.ts.map +1 -1
  534. package/dist/wrapper/pty-wrapper.js +1050 -104
  535. package/dist/wrapper/pty-wrapper.js.map +1 -1
  536. package/dist/wrapper/shared.d.ts +165 -0
  537. package/dist/wrapper/shared.d.ts.map +1 -0
  538. package/dist/wrapper/shared.js +270 -0
  539. package/dist/wrapper/shared.js.map +1 -0
  540. package/dist/wrapper/tmux-wrapper.d.ts +78 -11
  541. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
  542. package/dist/wrapper/tmux-wrapper.js +567 -106
  543. package/dist/wrapper/tmux-wrapper.js.map +1 -1
  544. package/docs/CLOUD-ARCHITECTURE.md +804 -0
  545. package/docs/CLOUD-ONBOARDING-DESIGN.md +1983 -0
  546. package/docs/HOOKS_API.md +394 -0
  547. package/docs/WRAPPER_EVENTS.md +358 -0
  548. package/docs/agent-policy-snippet.md +40 -0
  549. package/docs/agent-relay-protocol.md +238 -0
  550. package/docs/agent-relay-snippet.md +115 -6
  551. package/docs/archive/EXECUTIVE_SUMMARY.md +358 -0
  552. package/docs/archive/ROADMAP.md +329 -0
  553. package/docs/archive/TESTING_PRESENCE_FEATURES.md +327 -0
  554. package/docs/competitive/GASTOWN.md +451 -0
  555. package/docs/{COMPETITIVE_ANALYSIS.md → competitive/OVERVIEW.md} +1 -0
  556. package/docs/competitive/README.md +34 -0
  557. package/docs/competitive/TMUX_ORCHESTRATOR.md +605 -0
  558. package/docs/dashboard.png +0 -0
  559. package/docs/design/ci-failure-webhooks.md +812 -0
  560. package/docs/design/comprehensive-integrations.md +238 -0
  561. package/docs/design/e2b-sandbox-integration.md +504 -0
  562. package/docs/design/github-app-permissions.md +264 -0
  563. package/docs/guides/CLOUD.md +236 -0
  564. package/docs/guides/LOCAL.md +535 -0
  565. package/docs/guides/SELF-HOSTED.md +494 -0
  566. package/docs/local-testing.md +428 -0
  567. package/docs/proposals/continuous-claude-integration.md +622 -0
  568. package/docs/proposals/custom-commands.md +368 -0
  569. package/docs/proposals/shadow-as-subagent.md +765 -0
  570. package/docs/proposals/slack-bot-integration.md +1457 -0
  571. package/docs/tasks/global-skills-system.tasks.md +230 -0
  572. package/docs/tasks/webhook-integrations.tasks.md +184 -0
  573. package/docs/tasks/workspace-capabilities.tasks.md +121 -0
  574. package/docs/testing/RESILIENCY-TEST-PLAN-2026-01-01.md +366 -0
  575. package/package.json +45 -7
  576. package/scripts/cloud-setup.sh +96 -0
  577. package/scripts/manual-qa.sh +293 -0
  578. package/scripts/postinstall.js +60 -0
  579. package/scripts/run-cloud-qa.sh +220 -0
  580. package/scripts/test-cli-auth/Dockerfile +44 -0
  581. package/scripts/test-cli-auth/Dockerfile.real +79 -0
  582. package/scripts/test-cli-auth/README.md +286 -0
  583. package/scripts/test-cli-auth/ci-test-real-clis.ts +251 -0
  584. package/scripts/test-cli-auth/ci-test-runner.ts +263 -0
  585. package/scripts/test-cli-auth/mock-cli.sh +147 -0
  586. package/scripts/test-cli-auth/package.json +14 -0
  587. package/scripts/test-cli-auth/test-oauth-flow.ts +220 -0
  588. package/scripts/test-pty-input-auto.js +222 -0
  589. package/scripts/test-pty-input.js +150 -0
  590. package/dist/dashboard/out/_next/static/chunks/app/layout-c9d8c5d95e48c6bf.js +0 -1
  591. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-8aa9936bc6c771ab.js +0 -1
  592. package/dist/dashboard/out/_next/static/chunks/app/page-4498be09a5157759.js +0 -1
  593. package/dist/dashboard/out/_next/static/chunks/main-app-bae2e535de00de50.js +0 -1
  594. package/dist/dashboard/out/_next/static/chunks/webpack-c81f7fd28659d64f.js +0 -1
  595. package/dist/dashboard/out/_next/static/css/50ed6996e3df7bdd.css +0 -1
  596. /package/dist/dashboard/out/_next/static/{DXFA-jj8wb3PcY5DX2xcU → H5aWG0udPB4iOUIl_gytz}/_buildManifest.js +0 -0
  597. /package/dist/dashboard/out/_next/static/{DXFA-jj8wb3PcY5DX2xcU → H5aWG0udPB4iOUIl_gytz}/_ssgManifest.js +0 -0
  598. /package/dist/dashboard/out/_next/static/chunks/{117-3bef7b19f3e60751.js → 117-b100311aff8d5c61.js} +0 -0
  599. /package/dist/dashboard/out/_next/static/chunks/{648-6cf686106c891ad3.js → 648-a13d3c2b1be45466.js} +0 -0
  600. /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-8ff6572bc7c9bc61.js → page-a4973f3e3c82fb67.js} +0 -0
  601. /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-26bd8d656b496dba.js → fd9d1056-bf46c09eb57e019c.js} +0 -0
  602. /package/docs/{CHANGELOG.md → archive/CHANGELOG.md} +0 -0
  603. /package/docs/{CLI-SIMPLIFICATION-COMPLETE.md → archive/CLI-SIMPLIFICATION-COMPLETE.md} +0 -0
  604. /package/docs/{DESIGN_BRIDGE_STAFFING.md → archive/DESIGN_BRIDGE_STAFFING.md} +0 -0
  605. /package/docs/{DESIGN_V2.md → archive/DESIGN_V2.md} +0 -0
  606. /package/docs/{MONETIZATION.md → archive/MONETIZATION.md} +0 -0
  607. /package/docs/{PROPOSAL-trajectories.md → archive/PROPOSAL-trajectories.md} +0 -0
  608. /package/docs/{SCALING_ANALYSIS.md → archive/SCALING_ANALYSIS.md} +0 -0
  609. /package/docs/{TMUX_IMPLEMENTATION_NOTES.md → archive/TMUX_IMPLEMENTATION_NOTES.md} +0 -0
  610. /package/docs/{TMUX_IMPROVEMENTS.md → archive/TMUX_IMPROVEMENTS.md} +0 -0
  611. /package/docs/{dashboard-v2-plan.md → archive/dashboard-v2-plan.md} +0 -0
  612. /package/docs/{removable-code-analysis.md → archive/removable-code-analysis.md} +0 -0
  613. /package/docs/{competitive-analysis-mcp-agent-mail.md → competitive/MCP_AGENT_MAIL.md} +0 -0
package/dist/cli/index.js CHANGED
@@ -17,6 +17,8 @@ import { RelayClient } from '../wrapper/client.js';
17
17
  import { generateAgentName } from '../utils/name-generator.js';
18
18
  import { getTmuxPath } from '../utils/tmux-resolver.js';
19
19
  import { readWorkersMetadata, getWorkerLogsDir } from '../bridge/spawner.js';
20
+ import { getShadowForAgent } from '../bridge/shadow-config.js';
21
+ import { selectShadowCli } from '../bridge/shadow-cli.js';
20
22
  import { checkForUpdatesInBackground, checkForUpdates } from '../utils/update-checker.js';
21
23
  import fs from 'node:fs';
22
24
  import path from 'node:path';
@@ -53,6 +55,9 @@ program
53
55
  .option('-n, --name <name>', 'Agent name (auto-generated if not set)')
54
56
  .option('-q, --quiet', 'Disable debug output', false)
55
57
  .option('--prefix <pattern>', 'Relay prefix pattern (default: ->relay:)')
58
+ .option('--dashboard-port <port>', 'Dashboard port for spawn/release API (auto-detected if not set)')
59
+ .option('--shadow <name>', 'Spawn a shadow agent with this name that monitors the primary')
60
+ .option('--shadow-role <role>', 'Shadow role: reviewer, auditor, or triggers (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
56
61
  .argument('[command...]', 'Command to wrap (e.g., claude)')
57
62
  .action(async (commandParts, options) => {
58
63
  // If no command provided, show help
@@ -81,8 +86,31 @@ program
81
86
  }
82
87
  const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
83
88
  const { AgentSpawner } = await import('../bridge/spawner.js');
84
- // Create spawner so any agent can spawn workers
85
- const spawner = new AgentSpawner(paths.projectRoot);
89
+ // Determine dashboard port for spawn/release API
90
+ // Priority: CLI flag > env var > auto-detect default port
91
+ let dashboardPort;
92
+ if (options.dashboardPort) {
93
+ dashboardPort = parseInt(options.dashboardPort, 10);
94
+ }
95
+ else {
96
+ // Try to detect if dashboard is running at default port
97
+ const defaultPort = parseInt(DEFAULT_DASHBOARD_PORT, 10);
98
+ try {
99
+ const response = await fetch(`http://localhost:${defaultPort}/api/status`, {
100
+ method: 'GET',
101
+ signal: AbortSignal.timeout(500), // Quick timeout for detection
102
+ });
103
+ if (response.ok) {
104
+ dashboardPort = defaultPort;
105
+ console.error(`Dashboard detected: http://localhost:${dashboardPort}`);
106
+ }
107
+ }
108
+ catch {
109
+ // Dashboard not running - spawn/release will use fallback callbacks
110
+ }
111
+ }
112
+ // Create spawner as fallback for direct spawn (if dashboard API not available)
113
+ const spawner = new AgentSpawner(paths.projectRoot, undefined, dashboardPort);
86
114
  const wrapper = new TmuxWrapper({
87
115
  name: agentName,
88
116
  command: mainCommand,
@@ -92,7 +120,9 @@ program
92
120
  relayPrefix: options.prefix,
93
121
  useInbox: true,
94
122
  inboxDir: paths.dataDir, // Use the project-specific data directory for the inbox
95
- // Wire up spawn/release callbacks so any agent can spawn workers
123
+ // Use dashboard API for spawn/release when available (preferred - works from any context)
124
+ dashboardPort,
125
+ // Wire up spawn/release callbacks as fallback (if no dashboardPort)
96
126
  onSpawn: async (workerName, workerCli, task) => {
97
127
  console.error(`[${agentName}] Spawning ${workerName} (${workerCli})...`);
98
128
  const result = await spawner.spawn({
@@ -121,10 +151,80 @@ program
121
151
  });
122
152
  process.on('SIGINT', async () => {
123
153
  await spawner.releaseAll();
124
- wrapper.stop();
154
+ await wrapper.stop();
125
155
  process.exit(0);
126
156
  });
127
157
  await wrapper.start();
158
+ let shadowName;
159
+ let shadowRole;
160
+ let speakOn;
161
+ let shadowCli;
162
+ let shadowPrompt;
163
+ const rolePresets = {
164
+ reviewer: ['CODE_WRITTEN', 'REVIEW_REQUEST', 'EXPLICIT_ASK'],
165
+ auditor: ['SESSION_END', 'EXPLICIT_ASK'],
166
+ active: ['ALL_MESSAGES'],
167
+ };
168
+ if (options.shadow) {
169
+ // CLI flags provided
170
+ shadowName = options.shadow;
171
+ const role = options.shadowRole || 'EXPLICIT_ASK';
172
+ shadowRole = role;
173
+ if (rolePresets[role.toLowerCase()]) {
174
+ speakOn = rolePresets[role.toLowerCase()];
175
+ }
176
+ else {
177
+ speakOn = role.split(',').map((s) => s.trim().toUpperCase());
178
+ }
179
+ }
180
+ else {
181
+ // Check config file for shadow configuration
182
+ const shadowConfig = getShadowForAgent(paths.projectRoot, agentName);
183
+ if (shadowConfig) {
184
+ shadowName = shadowConfig.shadowName;
185
+ shadowRole = shadowConfig.roleName;
186
+ speakOn = shadowConfig.speakOn;
187
+ shadowCli = shadowConfig.cli;
188
+ shadowPrompt = shadowConfig.prompt;
189
+ console.error(`Shadow config: ${shadowName} (from .agent-relay.json)`);
190
+ }
191
+ }
192
+ // Spawn shadow if configured
193
+ if (shadowName && speakOn) {
194
+ // Decide how to run the shadow (subagent for Claude/OpenCode primaries)
195
+ let shadowSelection = null;
196
+ try {
197
+ shadowSelection = await selectShadowCli(mainCommand, { preferredShadowCli: shadowCli });
198
+ console.error(`[shadow] Mode: ${shadowSelection.mode} via ${shadowSelection.command || shadowSelection.cli} (primary: ${mainCommand})`);
199
+ }
200
+ catch (err) {
201
+ console.error(`[shadow] Shadow CLI selection failed: ${err.message}`);
202
+ }
203
+ // Subagent mode: do not spawn a separate shadow process
204
+ if (shadowSelection?.mode === 'subagent') {
205
+ console.error(`[shadow] ${shadowName} will run as ${shadowSelection.cli} subagent inside ${agentName}; no separate process spawned`);
206
+ return;
207
+ }
208
+ console.error(`Shadow: ${shadowName} (shadowing ${agentName}, speakOn: ${speakOn.join(',')})`);
209
+ // Wait for primary to register before spawning shadow
210
+ await new Promise(r => setTimeout(r, 3000));
211
+ // Build shadow task prompt
212
+ const defaultPrompt = `You are a shadow agent monitoring "${agentName}". You receive copies of their messages. Your role: ${shadowRole || 'observer'}. Stay passive unless your triggers activate.`;
213
+ const shadowTask = shadowPrompt || defaultPrompt;
214
+ const result = await spawner.spawn({
215
+ name: shadowName,
216
+ cli: shadowSelection?.command || shadowCli || mainCommand,
217
+ task: shadowTask,
218
+ shadowOf: agentName,
219
+ shadowSpeakOn: speakOn,
220
+ });
221
+ if (result.success) {
222
+ console.error(`Shadow ${shadowName} started [pid: ${result.pid}]`);
223
+ }
224
+ else {
225
+ console.error(`Failed to spawn shadow ${shadowName}: ${result.error}`);
226
+ }
227
+ }
128
228
  });
129
229
  // up - Start daemon + dashboard
130
230
  program
@@ -134,7 +234,67 @@ program
134
234
  .option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
135
235
  .option('--spawn', 'Force spawn all agents from teams.json')
136
236
  .option('--no-spawn', 'Do not auto-spawn agents (just start daemon)')
237
+ .option('--watch', 'Auto-restart daemon on crash (supervisor mode)')
238
+ .option('--max-restarts <n>', 'Max restarts in 60s before giving up (default: 5)', '5')
137
239
  .action(async (options) => {
240
+ // If --watch is specified, run in supervisor mode
241
+ if (options.watch) {
242
+ const { spawn } = await import('node:child_process');
243
+ const maxRestarts = parseInt(options.maxRestarts, 10) || 5;
244
+ const restartWindow = 60_000; // 60 seconds
245
+ const restartTimes = [];
246
+ let child = null;
247
+ let shuttingDown = false;
248
+ const startDaemon = () => {
249
+ // Build args without --watch to prevent infinite recursion
250
+ const args = ['up'];
251
+ if (options.dashboard === false)
252
+ args.push('--no-dashboard');
253
+ if (options.port)
254
+ args.push('--port', options.port);
255
+ if (options.spawn === true)
256
+ args.push('--spawn');
257
+ if (options.spawn === false)
258
+ args.push('--no-spawn');
259
+ console.log(`[supervisor] Starting daemon...`);
260
+ child = spawn(process.execPath, [process.argv[1], ...args], {
261
+ stdio: 'inherit',
262
+ env: { ...process.env, AGENT_RELAY_SUPERVISED: '1' },
263
+ });
264
+ child.on('exit', (code, signal) => {
265
+ if (shuttingDown) {
266
+ process.exit(0);
267
+ return;
268
+ }
269
+ const now = Date.now();
270
+ restartTimes.push(now);
271
+ // Remove restarts outside the window
272
+ while (restartTimes.length > 0 && restartTimes[0] < now - restartWindow) {
273
+ restartTimes.shift();
274
+ }
275
+ if (restartTimes.length >= maxRestarts) {
276
+ console.error(`[supervisor] Daemon crashed ${maxRestarts} times in ${restartWindow / 1000}s, giving up`);
277
+ process.exit(1);
278
+ }
279
+ const exitReason = signal ? `signal ${signal}` : `code ${code}`;
280
+ console.log(`[supervisor] Daemon exited (${exitReason}), restarting in 2s... (${restartTimes.length}/${maxRestarts} restarts)`);
281
+ setTimeout(startDaemon, 2000);
282
+ });
283
+ };
284
+ process.on('SIGINT', () => {
285
+ console.log('\n[supervisor] Stopping...');
286
+ shuttingDown = true;
287
+ if (child)
288
+ child.kill('SIGINT');
289
+ });
290
+ process.on('SIGTERM', () => {
291
+ shuttingDown = true;
292
+ if (child)
293
+ child.kill('SIGTERM');
294
+ });
295
+ startDaemon();
296
+ return;
297
+ }
138
298
  const { ensureProjectDir } = await import('../utils/project-namespace.js');
139
299
  const { loadTeamsConfig } = await import('../bridge/teams-config.js');
140
300
  const { AgentSpawner } = await import('../bridge/spawner.js');
@@ -157,6 +317,34 @@ program
157
317
  });
158
318
  // Create spawner for auto-spawn (will be initialized after dashboard starts)
159
319
  let spawner = null;
320
+ // Track if we're already shutting down to prevent double-cleanup
321
+ let isShuttingDown = false;
322
+ const gracefulShutdown = async (reason) => {
323
+ if (isShuttingDown)
324
+ return;
325
+ isShuttingDown = true;
326
+ console.log(`\n[daemon] ${reason}, shutting down...`);
327
+ try {
328
+ if (spawner)
329
+ await spawner.releaseAll();
330
+ await daemon.stop();
331
+ }
332
+ catch (err) {
333
+ console.error('[daemon] Error during shutdown:', err);
334
+ }
335
+ process.exit(1);
336
+ };
337
+ // Handle uncaught exceptions - log and exit (supervisor will restart)
338
+ process.on('uncaughtException', (err) => {
339
+ console.error('[daemon] Uncaught exception:', err);
340
+ gracefulShutdown('Uncaught exception');
341
+ });
342
+ // Handle unhandled promise rejections
343
+ process.on('unhandledRejection', (reason, promise) => {
344
+ console.error('[daemon] Unhandled rejection at:', promise, 'reason:', reason);
345
+ // Don't exit on unhandled rejections - just log them
346
+ // Most are recoverable (e.g., failed message delivery)
347
+ });
160
348
  process.on('SIGINT', async () => {
161
349
  console.log('\nStopping...');
162
350
  if (spawner) {
@@ -189,6 +377,13 @@ program
189
377
  projectRoot: paths.projectRoot,
190
378
  });
191
379
  console.log(`Dashboard: http://localhost:${dashboardPort}`);
380
+ // Hook daemon log output to dashboard WebSocket
381
+ daemon.onLogOutput = (agentName, data, _timestamp) => {
382
+ const broadcast = global.__broadcastLogOutput;
383
+ if (broadcast) {
384
+ broadcast(agentName, data);
385
+ }
386
+ };
192
387
  }
193
388
  // Determine if we should auto-spawn agents
194
389
  // --spawn: force spawn
@@ -288,9 +483,11 @@ program
288
483
  .command('agents')
289
484
  .description('List connected agents and spawned workers')
290
485
  .option('--all', 'Include internal/CLI agents')
486
+ .option('--remote', 'Include agents from other linked machines (requires cloud link)')
291
487
  .option('--json', 'Output as JSON')
292
488
  .action(async (options) => {
293
489
  const { getProjectPaths } = await import('../utils/project-namespace.js');
490
+ const os = await import('node:os');
294
491
  const paths = getProjectPaths();
295
492
  const agentsPath = path.join(paths.teamDir, 'agents.json');
296
493
  // Load registered agents
@@ -311,6 +508,7 @@ program
311
508
  lastSeen: agent.lastSeen,
312
509
  team: worker?.team,
313
510
  pid: worker?.pid,
511
+ location: 'local',
314
512
  });
315
513
  });
316
514
  // Add workers not in registry (orphaned or not yet registered)
@@ -323,9 +521,51 @@ program
323
521
  cli: worker.cli || '-',
324
522
  team: worker.team,
325
523
  pid: worker.pid,
524
+ location: 'local',
326
525
  });
327
526
  }
328
527
  });
528
+ // Include remote agents if --remote flag is set
529
+ if (options.remote) {
530
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
531
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
532
+ const configPath = path.join(dataDir, 'cloud-config.json');
533
+ if (fs.existsSync(configPath)) {
534
+ try {
535
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
536
+ const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
537
+ method: 'POST',
538
+ headers: {
539
+ 'Authorization': `Bearer ${config.apiKey}`,
540
+ 'Content-Type': 'application/json',
541
+ },
542
+ body: JSON.stringify({ agents: [] }),
543
+ });
544
+ if (response.ok) {
545
+ const data = await response.json();
546
+ // Add remote agents (exclude local ones by name)
547
+ const localNames = new Set(combined.map(a => a.name));
548
+ for (const agent of data.allAgents) {
549
+ if (!localNames.has(agent.name)) {
550
+ combined.push({
551
+ name: agent.name,
552
+ status: agent.status.toUpperCase(),
553
+ cli: '-',
554
+ location: agent.daemonName,
555
+ daemonId: agent.daemonId,
556
+ });
557
+ }
558
+ }
559
+ }
560
+ }
561
+ catch (err) {
562
+ console.error('[warn] Failed to fetch remote agents:', err.message);
563
+ }
564
+ }
565
+ else {
566
+ console.error('[warn] Cloud not linked. Run `agent-relay cloud link` to see remote agents.');
567
+ }
568
+ }
329
569
  if (options.json) {
330
570
  console.log(JSON.stringify(combined, null, 2));
331
571
  return;
@@ -335,21 +575,39 @@ program
335
575
  console.log(`No agents found. Ensure the daemon is running and agents are connected${hint}.`);
336
576
  return;
337
577
  }
338
- console.log('NAME STATUS CLI TEAM');
339
- console.log('─'.repeat(50));
340
- combined.forEach((agent) => {
341
- const name = agent.name.padEnd(15);
342
- const status = agent.status.padEnd(8);
343
- const cli = agent.cli.padEnd(9);
344
- const team = agent.team ?? '-';
345
- console.log(`${name} ${status} ${cli} ${team}`);
346
- });
578
+ const hasRemote = combined.some(a => a.location !== 'local');
579
+ if (hasRemote) {
580
+ console.log('NAME STATUS CLI LOCATION');
581
+ console.log('─'.repeat(55));
582
+ combined.forEach((agent) => {
583
+ const name = agent.name.padEnd(15);
584
+ const status = agent.status.padEnd(8);
585
+ const cli = agent.cli.padEnd(9);
586
+ const location = agent.location ?? 'local';
587
+ console.log(`${name} ${status} ${cli} ${location}`);
588
+ });
589
+ }
590
+ else {
591
+ console.log('NAME STATUS CLI TEAM');
592
+ console.log('─'.repeat(50));
593
+ combined.forEach((agent) => {
594
+ const name = agent.name.padEnd(15);
595
+ const status = agent.status.padEnd(8);
596
+ const cli = agent.cli.padEnd(9);
597
+ const team = agent.team ?? '-';
598
+ console.log(`${name} ${status} ${cli} ${team}`);
599
+ });
600
+ }
347
601
  if (workers.length > 0) {
348
602
  console.log('');
349
603
  console.log('Commands:');
350
604
  console.log(' agent-relay agents:logs <name> - View spawned agent output');
351
605
  console.log(' agent-relay agents:kill <name> - Kill a spawned agent');
352
606
  }
607
+ if (!options.remote) {
608
+ console.log('');
609
+ console.log('Tip: Use --remote to include agents from other linked machines.');
610
+ }
353
611
  });
354
612
  // who - Show currently active agents (online within last 30s)
355
613
  program
@@ -541,6 +799,7 @@ program
541
799
  .description('Bridge multiple projects as orchestrator')
542
800
  .argument('[projects...]', 'Project paths to bridge')
543
801
  .option('--cli <tool>', 'CLI tool override for all projects')
802
+ .option('--architect [cli]', 'Spawn an architect agent to coordinate all projects (default: claude)')
544
803
  .action(async (projectPaths, options) => {
545
804
  const { resolveProjects, validateDaemons } = await import('../bridge/config.js');
546
805
  const { MultiProjectClient } = await import('../bridge/multi-project-client.js');
@@ -646,9 +905,106 @@ program
646
905
  console.log('Connected to all projects.');
647
906
  console.log('');
648
907
  console.log('Cross-project messaging:');
649
- console.log(' @relay:projectId:agent Message');
650
- console.log(' @relay:*:lead Broadcast to all leads');
908
+ console.log(' ->relay:projectId:agent Message');
909
+ console.log(' ->relay:*:lead Broadcast to all leads');
651
910
  console.log('');
911
+ // Spawn architect agent if --architect flag is set
912
+ let architectWrapper = null;
913
+ if (options.architect !== undefined) {
914
+ const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
915
+ // Determine CLI to use (default to claude)
916
+ const architectCli = typeof options.architect === 'string' ? options.architect : 'claude';
917
+ // Use first project as the base for the architect
918
+ const baseProject = valid[0];
919
+ const basePaths = getProjectPaths(baseProject.path);
920
+ // Build project context for the architect
921
+ const projectContext = valid.map(p => `- ${p.id}: ${p.path} (Lead: ${p.leadName})`).join('\n');
922
+ // Create architect system prompt
923
+ const architectPrompt = `You are the Architect, a cross-project coordinator overseeing multiple codebases.
924
+
925
+ ## Connected Projects
926
+ ${projectContext}
927
+
928
+ ## Your Role
929
+ - Coordinate high-level work across all projects
930
+ - Assign tasks to project leads
931
+ - Ensure consistency and resolve cross-project dependencies
932
+ - Review overall architecture decisions
933
+
934
+ ## Cross-Project Messaging
935
+
936
+ Use this syntax to message agents in specific projects:
937
+
938
+ \`\`\`
939
+ ->relay:${valid[0].id}:${valid[0].leadName} <<<
940
+ Your message to this project's lead>>>
941
+
942
+ ->relay:${valid.length > 1 ? valid[1].id : valid[0].id}:* <<<
943
+ Broadcast to all agents in a project>>>
944
+
945
+ ->relay:*:* <<<
946
+ Broadcast to ALL agents in ALL projects>>>
947
+ \`\`\`
948
+
949
+ Format: \`->relay:project-id:agent-name\`
950
+
951
+ ## Getting Started
952
+ 1. Check in with each project lead to understand current status
953
+ 2. Identify cross-project dependencies
954
+ 3. Coordinate work across teams
955
+
956
+ Start by greeting the project leads and asking for status updates.`;
957
+ console.log('Spawning Architect agent...');
958
+ console.log(` CLI: ${architectCli}`);
959
+ console.log(` Base project: ${baseProject.path}`);
960
+ console.log('');
961
+ // Determine command and args based on CLI
962
+ let command;
963
+ let args = [];
964
+ if (architectCli === 'claude' || architectCli.startsWith('claude:')) {
965
+ command = 'claude';
966
+ args = ['--dangerously-skip-permissions'];
967
+ // Add model if specified (e.g., claude:opus)
968
+ if (architectCli.includes(':')) {
969
+ const model = architectCli.split(':')[1];
970
+ args.push('--model', model);
971
+ }
972
+ }
973
+ else if (architectCli === 'codex') {
974
+ command = 'codex';
975
+ args = ['--dangerously-skip-permissions'];
976
+ }
977
+ else {
978
+ command = architectCli;
979
+ }
980
+ try {
981
+ architectWrapper = new TmuxWrapper({
982
+ name: 'Architect',
983
+ command,
984
+ args,
985
+ socketPath: basePaths.socketPath,
986
+ debug: false,
987
+ useInbox: true,
988
+ inboxDir: basePaths.dataDir,
989
+ });
990
+ await architectWrapper.start();
991
+ // Wait for agent to be ready, then inject the prompt
992
+ setTimeout(async () => {
993
+ try {
994
+ await architectWrapper.injectMessage(architectPrompt);
995
+ console.log('Architect agent started and initialized.');
996
+ console.log('Attach to session: tmux attach -t relay-Architect');
997
+ console.log('');
998
+ }
999
+ catch (err) {
1000
+ console.error('Failed to inject architect prompt:', err);
1001
+ }
1002
+ }, 3000);
1003
+ }
1004
+ catch (err) {
1005
+ console.error('Failed to spawn Architect agent:', err);
1006
+ }
1007
+ }
652
1008
  // Handle messages from projects
653
1009
  client.onMessage = (projectId, from, payload, messageId) => {
654
1010
  console.log(`[${projectId}] ${from}: ${payload.body.substring(0, 80)}...`);
@@ -985,6 +1341,40 @@ program
985
1341
  }
986
1342
  }
987
1343
  });
1344
+ // release - Release a spawned agent via API (works from any context, no terminal required)
1345
+ program
1346
+ .command('release')
1347
+ .description('Release a spawned agent via API (no terminal required)')
1348
+ .argument('<name>', 'Agent name to release')
1349
+ .option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
1350
+ .action(async (name, options) => {
1351
+ const port = options.port || DEFAULT_DASHBOARD_PORT;
1352
+ try {
1353
+ const response = await fetch(`http://localhost:${port}/api/spawned/${encodeURIComponent(name)}`, {
1354
+ method: 'DELETE',
1355
+ });
1356
+ const result = await response.json();
1357
+ if (result.success) {
1358
+ console.log(`Released agent: ${name}`);
1359
+ process.exit(0);
1360
+ }
1361
+ else {
1362
+ console.error(`Failed to release ${name}: ${result.error || 'Unknown error'}`);
1363
+ process.exit(1);
1364
+ }
1365
+ }
1366
+ catch (err) {
1367
+ // If API call fails, try to provide helpful error message
1368
+ if (err.code === 'ECONNREFUSED') {
1369
+ console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
1370
+ console.log(`Run 'agent-relay up' to start the daemon.`);
1371
+ }
1372
+ else {
1373
+ console.error(`Failed to release ${name}: ${err.message}`);
1374
+ }
1375
+ process.exit(1);
1376
+ }
1377
+ });
988
1378
  // agents:kill - Kill a spawned agent by PID
989
1379
  program
990
1380
  .command('agents:kill')
@@ -1039,5 +1429,796 @@ program
1039
1429
  }
1040
1430
  }
1041
1431
  });
1432
+ // ============================================================================
1433
+ // Cloud commands
1434
+ // ============================================================================
1435
+ const cloudCommand = program
1436
+ .command('cloud')
1437
+ .description('Cloud account and sync commands');
1438
+ cloudCommand
1439
+ .command('link')
1440
+ .description('Link this machine to your Agent Relay Cloud account')
1441
+ .option('--name <name>', 'Name for this machine')
1442
+ .option('--cloud-url <url>', 'Cloud API URL', process.env.AGENT_RELAY_CLOUD_URL || 'https://api.agent-relay.com')
1443
+ .action(async (options) => {
1444
+ const os = await import('node:os');
1445
+ const crypto = await import('node:crypto');
1446
+ const readline = await import('node:readline');
1447
+ const cloudUrl = options.cloudUrl;
1448
+ const machineName = options.name || os.hostname();
1449
+ // Generate machine ID
1450
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1451
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1452
+ const machineIdPath = path.join(dataDir, 'machine-id');
1453
+ const configPath = path.join(dataDir, 'cloud-config.json');
1454
+ let machineId;
1455
+ if (fs.existsSync(machineIdPath)) {
1456
+ machineId = fs.readFileSync(machineIdPath, 'utf-8').trim();
1457
+ }
1458
+ else {
1459
+ machineId = `${os.hostname()}-${crypto.randomBytes(8).toString('hex')}`;
1460
+ fs.mkdirSync(dataDir, { recursive: true });
1461
+ fs.writeFileSync(machineIdPath, machineId);
1462
+ }
1463
+ console.log('');
1464
+ console.log('🔗 Agent Relay Cloud - Link Machine');
1465
+ console.log('');
1466
+ console.log(`Machine: ${machineName}`);
1467
+ console.log(`ID: ${machineId}`);
1468
+ console.log('');
1469
+ // Generate a temporary code for the browser auth flow
1470
+ const tempCode = crypto.randomBytes(16).toString('hex');
1471
+ // Store temp code for callback
1472
+ const tempCodePath = path.join(dataDir, '.link-code');
1473
+ fs.writeFileSync(tempCodePath, tempCode);
1474
+ const authUrl = `${cloudUrl.replace('/api', '')}/cloud/link?code=${tempCode}&machine=${encodeURIComponent(machineId)}&name=${encodeURIComponent(machineName)}`;
1475
+ console.log('Open this URL in your browser to authenticate:');
1476
+ console.log('');
1477
+ console.log(` ${authUrl}`);
1478
+ console.log('');
1479
+ // Try to open browser automatically
1480
+ try {
1481
+ const openCommand = process.platform === 'darwin' ? 'open' :
1482
+ process.platform === 'win32' ? 'start' : 'xdg-open';
1483
+ await execAsync(`${openCommand} "${authUrl}"`);
1484
+ console.log('(Browser opened automatically)');
1485
+ }
1486
+ catch {
1487
+ console.log('(Copy the URL above and paste it in your browser)');
1488
+ }
1489
+ console.log('');
1490
+ console.log('After authenticating, paste your API key here:');
1491
+ const rl = readline.createInterface({
1492
+ input: process.stdin,
1493
+ output: process.stdout,
1494
+ });
1495
+ const apiKey = await new Promise((resolve) => {
1496
+ rl.question('API Key: ', (answer) => {
1497
+ rl.close();
1498
+ resolve(answer.trim());
1499
+ });
1500
+ });
1501
+ if (!apiKey || !apiKey.startsWith('ar_live_')) {
1502
+ console.error('');
1503
+ console.error('Invalid API key format. Expected ar_live_...');
1504
+ process.exit(1);
1505
+ }
1506
+ // Verify the API key works
1507
+ console.log('');
1508
+ console.log('Verifying API key...');
1509
+ try {
1510
+ const response = await fetch(`${cloudUrl}/api/daemons/heartbeat`, {
1511
+ method: 'POST',
1512
+ headers: {
1513
+ 'Authorization': `Bearer ${apiKey}`,
1514
+ 'Content-Type': 'application/json',
1515
+ },
1516
+ body: JSON.stringify({
1517
+ agents: [],
1518
+ metrics: { linkedAt: new Date().toISOString() },
1519
+ }),
1520
+ });
1521
+ if (!response.ok) {
1522
+ const error = await response.text();
1523
+ console.error(`Failed to verify API key: ${error}`);
1524
+ process.exit(1);
1525
+ }
1526
+ // Save config
1527
+ const config = {
1528
+ apiKey,
1529
+ cloudUrl,
1530
+ machineId,
1531
+ machineName,
1532
+ linkedAt: new Date().toISOString(),
1533
+ };
1534
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
1535
+ fs.chmodSync(configPath, 0o600); // Secure the file
1536
+ // Clean up temp code
1537
+ if (fs.existsSync(tempCodePath)) {
1538
+ fs.unlinkSync(tempCodePath);
1539
+ }
1540
+ console.log('');
1541
+ console.log('✓ Machine linked successfully!');
1542
+ console.log('');
1543
+ console.log('Your daemon will now sync with Agent Relay Cloud.');
1544
+ console.log('Run `agent-relay up` to start with cloud sync enabled.');
1545
+ console.log('');
1546
+ }
1547
+ catch (err) {
1548
+ console.error(`Failed to connect to cloud: ${err.message}`);
1549
+ process.exit(1);
1550
+ }
1551
+ });
1552
+ cloudCommand
1553
+ .command('unlink')
1554
+ .description('Unlink this machine from Agent Relay Cloud')
1555
+ .action(async () => {
1556
+ const os = await import('node:os');
1557
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1558
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1559
+ const configPath = path.join(dataDir, 'cloud-config.json');
1560
+ if (!fs.existsSync(configPath)) {
1561
+ console.log('This machine is not linked to Agent Relay Cloud.');
1562
+ return;
1563
+ }
1564
+ // Read current config
1565
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1566
+ // Delete config file
1567
+ fs.unlinkSync(configPath);
1568
+ console.log('');
1569
+ console.log('✓ Machine unlinked from Agent Relay Cloud');
1570
+ console.log('');
1571
+ console.log(`Machine ID: ${config.machineId}`);
1572
+ console.log(`Was linked since: ${config.linkedAt}`);
1573
+ console.log('');
1574
+ console.log('Note: The API key has been removed locally. To fully revoke access,');
1575
+ console.log('visit your Agent Relay Cloud dashboard and remove this machine.');
1576
+ console.log('');
1577
+ });
1578
+ cloudCommand
1579
+ .command('status')
1580
+ .description('Show cloud sync status')
1581
+ .action(async () => {
1582
+ const os = await import('node:os');
1583
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1584
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1585
+ const configPath = path.join(dataDir, 'cloud-config.json');
1586
+ if (!fs.existsSync(configPath)) {
1587
+ console.log('');
1588
+ console.log('Cloud sync: Not configured');
1589
+ console.log('');
1590
+ console.log('Run `agent-relay cloud link` to connect to Agent Relay Cloud.');
1591
+ console.log('');
1592
+ return;
1593
+ }
1594
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1595
+ console.log('');
1596
+ console.log('Cloud sync: Enabled');
1597
+ console.log('');
1598
+ console.log(` Machine: ${config.machineName}`);
1599
+ console.log(` ID: ${config.machineId}`);
1600
+ console.log(` Cloud URL: ${config.cloudUrl}`);
1601
+ console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
1602
+ console.log('');
1603
+ // Check if daemon is running and connected
1604
+ const { getProjectPaths } = await import('../utils/project-namespace.js');
1605
+ const paths = getProjectPaths();
1606
+ if (fs.existsSync(paths.socketPath)) {
1607
+ console.log(' Daemon: Running');
1608
+ // Try to get cloud sync status from daemon
1609
+ try {
1610
+ const response = await fetch(`${config.cloudUrl}/api/daemons/heartbeat`, {
1611
+ method: 'POST',
1612
+ headers: {
1613
+ 'Authorization': `Bearer ${config.apiKey}`,
1614
+ 'Content-Type': 'application/json',
1615
+ },
1616
+ body: JSON.stringify({ agents: [], metrics: {} }),
1617
+ });
1618
+ if (response.ok) {
1619
+ console.log(' Cloud connection: Online');
1620
+ }
1621
+ else {
1622
+ console.log(' Cloud connection: Error (API key may be invalid)');
1623
+ }
1624
+ }
1625
+ catch (err) {
1626
+ console.log(` Cloud connection: Offline (${err.message})`);
1627
+ }
1628
+ }
1629
+ else {
1630
+ console.log(' Daemon: Not running');
1631
+ console.log(' Cloud connection: Offline (daemon not started)');
1632
+ }
1633
+ console.log('');
1634
+ });
1635
+ cloudCommand
1636
+ .command('sync')
1637
+ .description('Manually sync credentials from cloud')
1638
+ .action(async () => {
1639
+ const os = await import('node:os');
1640
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1641
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1642
+ const configPath = path.join(dataDir, 'cloud-config.json');
1643
+ if (!fs.existsSync(configPath)) {
1644
+ console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
1645
+ process.exit(1);
1646
+ }
1647
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1648
+ console.log('Syncing credentials from cloud...');
1649
+ try {
1650
+ const response = await fetch(`${config.cloudUrl}/api/daemons/credentials`, {
1651
+ headers: {
1652
+ 'Authorization': `Bearer ${config.apiKey}`,
1653
+ },
1654
+ });
1655
+ if (!response.ok) {
1656
+ const error = await response.text();
1657
+ console.error(`Failed to sync: ${error}`);
1658
+ process.exit(1);
1659
+ }
1660
+ const data = await response.json();
1661
+ console.log('');
1662
+ console.log(`Synced ${data.credentials.length} provider credentials:`);
1663
+ for (const cred of data.credentials) {
1664
+ console.log(` - ${cred.provider}`);
1665
+ }
1666
+ // Save credentials locally for daemon to use
1667
+ const credentialsPath = path.join(dataDir, 'cloud-credentials.json');
1668
+ fs.writeFileSync(credentialsPath, JSON.stringify(data.credentials, null, 2));
1669
+ fs.chmodSync(credentialsPath, 0o600);
1670
+ console.log('');
1671
+ console.log('✓ Credentials synced successfully');
1672
+ console.log('');
1673
+ }
1674
+ catch (err) {
1675
+ console.error(`Failed to sync: ${err.message}`);
1676
+ process.exit(1);
1677
+ }
1678
+ });
1679
+ // ============================================================================
1680
+ // TRAJECTORY COMMANDS (trail proxy)
1681
+ // ============================================================================
1682
+ // trail - Proxy to trail CLI for trajectory tracking
1683
+ program
1684
+ .command('trail')
1685
+ .description('Trajectory tracking commands (proxies to trail CLI)')
1686
+ .argument('[args...]', 'Arguments to pass to trail CLI')
1687
+ .allowUnknownOption()
1688
+ .action(async (args) => {
1689
+ const { spawn } = await import('node:child_process');
1690
+ const { getProjectPaths } = await import('../utils/project-namespace.js');
1691
+ const paths = getProjectPaths();
1692
+ // Check if trail is available
1693
+ const trailCheck = spawn('which', ['trail'], { stdio: 'pipe' });
1694
+ const trailExists = await new Promise((resolve) => {
1695
+ trailCheck.on('close', (code) => resolve(code === 0));
1696
+ trailCheck.on('error', () => resolve(false));
1697
+ });
1698
+ if (!trailExists) {
1699
+ console.error('trail CLI not found. Install with: npm install -g agent-trajectories');
1700
+ console.log('');
1701
+ console.log('The trail CLI provides trajectory tracking for agent work:');
1702
+ console.log(' trail start "<task>" Start tracking a new trajectory');
1703
+ console.log(' trail status Show current trajectory status');
1704
+ console.log(' trail phase <phase> Transition to PDERO phase');
1705
+ console.log(' trail decision "<choice>" Record a decision');
1706
+ console.log(' trail complete Complete the trajectory');
1707
+ console.log(' trail list List all trajectories');
1708
+ console.log('');
1709
+ console.log('PDERO phases: plan, design, execute, review, observe');
1710
+ process.exit(1);
1711
+ }
1712
+ // Spawn trail with the provided arguments
1713
+ const trailProc = spawn('trail', args, {
1714
+ cwd: paths.projectRoot,
1715
+ stdio: 'inherit',
1716
+ env: {
1717
+ ...process.env,
1718
+ TRAJECTORIES_PROJECT: paths.projectId,
1719
+ TRAJECTORIES_DATA_DIR: paths.dataDir,
1720
+ },
1721
+ });
1722
+ trailProc.on('close', (code) => {
1723
+ process.exit(code ?? 0);
1724
+ });
1725
+ trailProc.on('error', (err) => {
1726
+ console.error(`Failed to run trail: ${err.message}`);
1727
+ process.exit(1);
1728
+ });
1729
+ });
1730
+ cloudCommand
1731
+ .command('agents')
1732
+ .description('List agents across all linked machines')
1733
+ .option('--json', 'Output as JSON')
1734
+ .action(async (options) => {
1735
+ const os = await import('node:os');
1736
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1737
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1738
+ const configPath = path.join(dataDir, 'cloud-config.json');
1739
+ if (!fs.existsSync(configPath)) {
1740
+ console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
1741
+ process.exit(1);
1742
+ }
1743
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1744
+ try {
1745
+ // Get agents from cloud
1746
+ const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
1747
+ method: 'POST',
1748
+ headers: {
1749
+ 'Authorization': `Bearer ${config.apiKey}`,
1750
+ 'Content-Type': 'application/json',
1751
+ },
1752
+ body: JSON.stringify({ agents: [] }), // Report no agents, just fetch list
1753
+ });
1754
+ if (!response.ok) {
1755
+ const error = await response.text();
1756
+ console.error(`Failed to fetch agents: ${error}`);
1757
+ process.exit(1);
1758
+ }
1759
+ const data = await response.json();
1760
+ if (options.json) {
1761
+ console.log(JSON.stringify(data.allAgents, null, 2));
1762
+ return;
1763
+ }
1764
+ if (!data.allAgents.length) {
1765
+ console.log('No agents found across linked machines.');
1766
+ console.log('Make sure daemons are running on linked machines.');
1767
+ return;
1768
+ }
1769
+ console.log('');
1770
+ console.log('Agents across all linked machines:');
1771
+ console.log('');
1772
+ console.log('NAME STATUS DAEMON MACHINE');
1773
+ console.log('─'.repeat(65));
1774
+ // Group by daemon
1775
+ const byDaemon = new Map();
1776
+ for (const agent of data.allAgents) {
1777
+ const existing = byDaemon.get(agent.daemonName) || [];
1778
+ existing.push(agent);
1779
+ byDaemon.set(agent.daemonName, existing);
1780
+ }
1781
+ for (const [daemonName, agents] of byDaemon.entries()) {
1782
+ for (const agent of agents) {
1783
+ const name = agent.name.padEnd(15);
1784
+ const status = agent.status.padEnd(8);
1785
+ const daemon = daemonName.padEnd(18);
1786
+ const machine = agent.machineId.substring(0, 20);
1787
+ console.log(`${name} ${status} ${daemon} ${machine}`);
1788
+ }
1789
+ }
1790
+ console.log('');
1791
+ console.log(`Total: ${data.allAgents.length} agents on ${byDaemon.size} machines`);
1792
+ console.log('');
1793
+ }
1794
+ catch (err) {
1795
+ console.error(`Failed to fetch agents: ${err.message}`);
1796
+ process.exit(1);
1797
+ }
1798
+ });
1799
+ cloudCommand
1800
+ .command('send')
1801
+ .description('Send a message to an agent on any linked machine')
1802
+ .argument('<agent>', 'Target agent name')
1803
+ .argument('<message>', 'Message to send')
1804
+ .option('--from <name>', 'Sender name', 'cli')
1805
+ .action(async (agent, message, options) => {
1806
+ const os = await import('node:os');
1807
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1808
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1809
+ const configPath = path.join(dataDir, 'cloud-config.json');
1810
+ if (!fs.existsSync(configPath)) {
1811
+ console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
1812
+ process.exit(1);
1813
+ }
1814
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1815
+ console.log(`Sending message to ${agent}...`);
1816
+ try {
1817
+ // First, find which daemon the agent is on
1818
+ const agentsResponse = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
1819
+ method: 'POST',
1820
+ headers: {
1821
+ 'Authorization': `Bearer ${config.apiKey}`,
1822
+ 'Content-Type': 'application/json',
1823
+ },
1824
+ body: JSON.stringify({ agents: [] }),
1825
+ });
1826
+ if (!agentsResponse.ok) {
1827
+ const error = await agentsResponse.text();
1828
+ console.error(`Failed to find agent: ${error}`);
1829
+ process.exit(1);
1830
+ }
1831
+ const agentsData = await agentsResponse.json();
1832
+ const targetAgent = agentsData.allAgents.find(a => a.name === agent);
1833
+ if (!targetAgent) {
1834
+ console.error(`Agent "${agent}" not found.`);
1835
+ console.log('Available agents:');
1836
+ for (const a of agentsData.allAgents) {
1837
+ console.log(` - ${a.name} (on ${a.daemonName})`);
1838
+ }
1839
+ process.exit(1);
1840
+ }
1841
+ // Send the message
1842
+ const sendResponse = await fetch(`${config.cloudUrl}/api/daemons/message`, {
1843
+ method: 'POST',
1844
+ headers: {
1845
+ 'Authorization': `Bearer ${config.apiKey}`,
1846
+ 'Content-Type': 'application/json',
1847
+ },
1848
+ body: JSON.stringify({
1849
+ targetDaemonId: targetAgent.daemonId,
1850
+ targetAgent: agent,
1851
+ message: {
1852
+ from: options.from,
1853
+ content: message,
1854
+ },
1855
+ }),
1856
+ });
1857
+ if (!sendResponse.ok) {
1858
+ const error = await sendResponse.text();
1859
+ console.error(`Failed to send message: ${error}`);
1860
+ process.exit(1);
1861
+ }
1862
+ console.log('');
1863
+ console.log(`✓ Message sent to ${agent} on ${targetAgent.daemonName}`);
1864
+ console.log('');
1865
+ }
1866
+ catch (err) {
1867
+ console.error(`Failed to send message: ${err.message}`);
1868
+ process.exit(1);
1869
+ }
1870
+ });
1871
+ cloudCommand
1872
+ .command('daemons')
1873
+ .description('List all linked daemon instances')
1874
+ .option('--json', 'Output as JSON')
1875
+ .action(async (_options) => {
1876
+ const os = await import('node:os');
1877
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
1878
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
1879
+ const configPath = path.join(dataDir, 'cloud-config.json');
1880
+ if (!fs.existsSync(configPath)) {
1881
+ console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
1882
+ process.exit(1);
1883
+ }
1884
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1885
+ try {
1886
+ // Get daemons list (requires browser auth, so we use a workaround)
1887
+ // For now, just show what we know about our own daemon
1888
+ console.log('');
1889
+ console.log('Linked Daemon:');
1890
+ console.log('');
1891
+ console.log(` Machine: ${config.machineName}`);
1892
+ console.log(` ID: ${config.machineId}`);
1893
+ console.log(` Cloud: ${config.cloudUrl}`);
1894
+ console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
1895
+ console.log('');
1896
+ console.log('Note: To see all linked daemons, visit your cloud dashboard.');
1897
+ console.log('');
1898
+ }
1899
+ catch (err) {
1900
+ console.error(`Failed: ${err.message}`);
1901
+ process.exit(1);
1902
+ }
1903
+ });
1904
+ // ============================================================================
1905
+ // Monitoring commands (metrics, health, profiler)
1906
+ // ============================================================================
1907
+ // metrics - Show agent memory metrics
1908
+ program
1909
+ .command('metrics')
1910
+ .description('Show agent memory metrics and resource usage')
1911
+ .option('--agent <name>', 'Show metrics for specific agent')
1912
+ .option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
1913
+ .option('--json', 'Output as JSON')
1914
+ .option('--watch', 'Continuously update metrics')
1915
+ .option('--interval <ms>', 'Update interval for watch mode', '5000')
1916
+ .action(async (options) => {
1917
+ const port = options.port || DEFAULT_DASHBOARD_PORT;
1918
+ const fetchMetrics = async () => {
1919
+ try {
1920
+ const response = await fetch(`http://localhost:${port}/api/metrics/agents`);
1921
+ if (!response.ok) {
1922
+ throw new Error(`HTTP ${response.status}`);
1923
+ }
1924
+ return await response.json();
1925
+ }
1926
+ catch (err) {
1927
+ if (err.code === 'ECONNREFUSED') {
1928
+ console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
1929
+ console.log(`Run 'agent-relay up' to start the daemon.`);
1930
+ }
1931
+ else {
1932
+ console.error(`Failed to fetch metrics: ${err.message}`);
1933
+ }
1934
+ process.exit(1);
1935
+ }
1936
+ };
1937
+ const formatBytes = (bytes) => {
1938
+ if (bytes === 0)
1939
+ return '0 B';
1940
+ const k = 1024;
1941
+ const sizes = ['B', 'KB', 'MB', 'GB'];
1942
+ const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
1943
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
1944
+ };
1945
+ const formatUptime = (ms) => {
1946
+ if (ms < 60000)
1947
+ return `${Math.floor(ms / 1000)}s`;
1948
+ if (ms < 3600000)
1949
+ return `${Math.floor(ms / 60000)}m`;
1950
+ return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;
1951
+ };
1952
+ const displayMetrics = (data) => {
1953
+ let agents = data.agents;
1954
+ if (options.agent) {
1955
+ agents = agents.filter(a => a.name === options.agent);
1956
+ if (agents.length === 0) {
1957
+ console.error(`Agent "${options.agent}" not found`);
1958
+ return;
1959
+ }
1960
+ }
1961
+ if (options.json) {
1962
+ console.log(JSON.stringify({ agents, system: data.system }, null, 2));
1963
+ return;
1964
+ }
1965
+ if (options.watch) {
1966
+ // Clear screen for watch mode
1967
+ console.clear();
1968
+ console.log(`Agent Metrics (updating every ${options.interval}ms) [Ctrl+C to stop]`);
1969
+ console.log(`System: ${formatBytes(data.system.heapUsed)} heap / ${formatBytes(data.system.freeMemory)} free`);
1970
+ console.log('');
1971
+ }
1972
+ if (agents.length === 0) {
1973
+ console.log('No agents with memory metrics.');
1974
+ console.log('Ensure agents are running and memory monitoring is enabled.');
1975
+ return;
1976
+ }
1977
+ console.log('AGENT PID MEMORY CPU TREND ALERT UPTIME');
1978
+ console.log('─'.repeat(75));
1979
+ for (const agent of agents) {
1980
+ const name = agent.name.padEnd(15);
1981
+ const pid = (agent.pid?.toString() || '-').padEnd(8);
1982
+ const memory = formatBytes(agent.rssBytes || 0).padEnd(11);
1983
+ const cpu = ((agent.cpuPercent?.toFixed(1) || '0') + '%').padEnd(6);
1984
+ const trend = (agent.trend || 'unknown').padEnd(11);
1985
+ const alertColors = {
1986
+ normal: 'normal',
1987
+ warning: '\x1b[33mwarning\x1b[0m',
1988
+ critical: '\x1b[31mcritical\x1b[0m',
1989
+ oom_imminent: '\x1b[31;1mOOM!\x1b[0m',
1990
+ };
1991
+ const alert = (alertColors[agent.alertLevel || 'normal'] || agent.alertLevel || '-').padEnd(9);
1992
+ const uptime = formatUptime(agent.uptimeMs || 0);
1993
+ console.log(`${name} ${pid} ${memory} ${cpu} ${trend} ${alert} ${uptime}`);
1994
+ }
1995
+ if (!options.watch) {
1996
+ console.log('');
1997
+ console.log(`Total: ${agents.length} agent(s)`);
1998
+ if (agents.some(a => a.alertLevel && a.alertLevel !== 'normal')) {
1999
+ console.log('');
2000
+ console.log('⚠️ Some agents have elevated memory usage. Run `agent-relay health` for details.');
2001
+ }
2002
+ }
2003
+ };
2004
+ if (options.watch) {
2005
+ const interval = parseInt(options.interval || '5000', 10);
2006
+ const update = async () => {
2007
+ try {
2008
+ const data = await fetchMetrics();
2009
+ displayMetrics(data);
2010
+ }
2011
+ catch {
2012
+ // Error already logged in fetchMetrics
2013
+ }
2014
+ };
2015
+ process.on('SIGINT', () => {
2016
+ console.log('\nStopped watching metrics.');
2017
+ process.exit(0);
2018
+ });
2019
+ await update();
2020
+ setInterval(update, interval);
2021
+ }
2022
+ else {
2023
+ const data = await fetchMetrics();
2024
+ displayMetrics(data);
2025
+ }
2026
+ });
2027
+ // health - Show crash insights and system health
2028
+ program
2029
+ .command('health')
2030
+ .description('Show system health, crash insights, and recommendations')
2031
+ .option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
2032
+ .option('--json', 'Output as JSON')
2033
+ .option('--crashes', 'Show recent crash history')
2034
+ .option('--alerts', 'Show unacknowledged alerts')
2035
+ .action(async (options) => {
2036
+ const port = options.port || DEFAULT_DASHBOARD_PORT;
2037
+ try {
2038
+ const response = await fetch(`http://localhost:${port}/api/metrics/health`);
2039
+ if (!response.ok) {
2040
+ throw new Error(`HTTP ${response.status}`);
2041
+ }
2042
+ const data = await response.json();
2043
+ if (options.json) {
2044
+ console.log(JSON.stringify(data, null, 2));
2045
+ return;
2046
+ }
2047
+ // Health score with color
2048
+ const scoreColor = data.healthScore >= 80 ? '\x1b[32m' : // Green
2049
+ data.healthScore >= 50 ? '\x1b[33m' : // Yellow
2050
+ '\x1b[31m'; // Red
2051
+ const resetColor = '\x1b[0m';
2052
+ console.log('');
2053
+ console.log('═══════════════════════════════════════════════════════════════');
2054
+ console.log(` SYSTEM HEALTH: ${scoreColor}${data.healthScore}/100${resetColor}`);
2055
+ console.log('═══════════════════════════════════════════════════════════════');
2056
+ console.log('');
2057
+ console.log(` ${data.summary}`);
2058
+ console.log('');
2059
+ // Show stats
2060
+ console.log(` Agents: ${data.stats.agentCount}`);
2061
+ console.log(` Crashes (24h): ${data.stats.totalCrashes24h}`);
2062
+ console.log(` Alerts (24h): ${data.stats.totalAlerts24h}`);
2063
+ console.log('');
2064
+ // Show issues
2065
+ if (data.issues.length > 0) {
2066
+ console.log(' ISSUES:');
2067
+ for (const issue of data.issues) {
2068
+ const icon = issue.severity === 'critical' ? '🔴' :
2069
+ issue.severity === 'high' ? '🟠' :
2070
+ issue.severity === 'medium' ? '🟡' : '🔵';
2071
+ console.log(` ${icon} ${issue.message}`);
2072
+ }
2073
+ console.log('');
2074
+ }
2075
+ // Show recommendations
2076
+ if (data.recommendations.length > 0) {
2077
+ console.log(' RECOMMENDATIONS:');
2078
+ for (const rec of data.recommendations) {
2079
+ console.log(` → ${rec}`);
2080
+ }
2081
+ console.log('');
2082
+ }
2083
+ // Show crashes if requested
2084
+ if (options.crashes && data.crashes.length > 0) {
2085
+ console.log(' RECENT CRASHES:');
2086
+ console.log(' ─────────────────────────────────────────────────────────────');
2087
+ for (const crash of data.crashes.slice(0, 10)) {
2088
+ const time = new Date(crash.crashedAt).toLocaleString();
2089
+ console.log(` ${crash.agentName} - ${time}`);
2090
+ console.log(` Cause: ${crash.likelyCause} | ${crash.summary.slice(0, 60)}...`);
2091
+ }
2092
+ console.log('');
2093
+ }
2094
+ // Show alerts if requested
2095
+ if (options.alerts && data.alerts.length > 0) {
2096
+ console.log(' UNACKNOWLEDGED ALERTS:');
2097
+ console.log(' ─────────────────────────────────────────────────────────────');
2098
+ for (const alert of data.alerts.slice(0, 10)) {
2099
+ const _time = new Date(alert.createdAt).toLocaleString();
2100
+ const icon = alert.alertType === 'oom_imminent' ? '🔴' :
2101
+ alert.alertType === 'critical' ? '🟠' : '🟡';
2102
+ console.log(` ${icon} ${alert.agentName} - ${alert.alertType}`);
2103
+ console.log(` ${alert.message}`);
2104
+ }
2105
+ console.log('');
2106
+ }
2107
+ console.log('═══════════════════════════════════════════════════════════════');
2108
+ console.log('');
2109
+ if (!options.crashes && data.stats.totalCrashes24h > 0) {
2110
+ console.log(' Tip: Run `agent-relay health --crashes` to see crash details');
2111
+ }
2112
+ if (!options.alerts && data.stats.totalAlerts24h > 0) {
2113
+ console.log(' Tip: Run `agent-relay health --alerts` to see alerts');
2114
+ }
2115
+ console.log('');
2116
+ }
2117
+ catch (err) {
2118
+ if (err.code === 'ECONNREFUSED') {
2119
+ console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
2120
+ console.log(`Run 'agent-relay up' to start the daemon.`);
2121
+ }
2122
+ else {
2123
+ console.error(`Failed to fetch health data: ${err.message}`);
2124
+ }
2125
+ process.exit(1);
2126
+ }
2127
+ });
2128
+ // profile - Run agent with profiling enabled
2129
+ program
2130
+ .command('profile')
2131
+ .description('Run an agent with memory profiling enabled')
2132
+ .argument('<command...>', 'Command to profile')
2133
+ .option('-n, --name <name>', 'Agent name')
2134
+ .option('--heap-snapshot-interval <ms>', 'Take heap snapshots at interval (ms)', '60000')
2135
+ .option('--output-dir <dir>', 'Directory for profile output', './profiles')
2136
+ .option('--expose-gc', 'Expose garbage collector for manual GC')
2137
+ .action(async (commandParts, options) => {
2138
+ const { getProjectPaths } = await import('../utils/project-namespace.js');
2139
+ if (!commandParts || commandParts.length === 0) {
2140
+ console.error('No command specified');
2141
+ process.exit(1);
2142
+ }
2143
+ const [cmd, ...args] = commandParts;
2144
+ const agentName = options.name ?? generateAgentName();
2145
+ const outputDir = options.outputDir || './profiles';
2146
+ const snapshotInterval = parseInt(options.heapSnapshotInterval || '60000', 10);
2147
+ // Create output directory
2148
+ if (!fs.existsSync(outputDir)) {
2149
+ fs.mkdirSync(outputDir, { recursive: true });
2150
+ }
2151
+ console.log('');
2152
+ console.log('🔬 Agent Relay Profiler');
2153
+ console.log('');
2154
+ console.log(` Agent: ${agentName}`);
2155
+ console.log(` Command: ${cmd} ${args.join(' ')}`);
2156
+ console.log(` Output: ${outputDir}`);
2157
+ console.log(` Heap snapshots: every ${snapshotInterval}ms`);
2158
+ console.log('');
2159
+ // Build Node.js flags for profiling
2160
+ const nodeFlags = [
2161
+ '--inspect', // Enable inspector
2162
+ '--inspect-brk=0', // Don't actually break, just enable
2163
+ ];
2164
+ if (options.exposeGc) {
2165
+ nodeFlags.push('--expose-gc');
2166
+ }
2167
+ // Set environment variables for profiling
2168
+ const profileEnv = {
2169
+ ...process.env,
2170
+ NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} ${nodeFlags.join(' ')}`.trim(),
2171
+ AGENT_RELAY_PROFILE_ENABLED: '1',
2172
+ AGENT_RELAY_PROFILE_OUTPUT: outputDir,
2173
+ AGENT_RELAY_PROFILE_INTERVAL: snapshotInterval.toString(),
2174
+ };
2175
+ console.log('Starting profiled agent...');
2176
+ console.log('');
2177
+ // Use the regular wrapper but with profiling environment
2178
+ const paths = getProjectPaths();
2179
+ const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
2180
+ const wrapper = new TmuxWrapper({
2181
+ name: agentName,
2182
+ command: cmd,
2183
+ args,
2184
+ socketPath: paths.socketPath,
2185
+ debug: true,
2186
+ env: profileEnv,
2187
+ useInbox: true,
2188
+ inboxDir: paths.dataDir,
2189
+ });
2190
+ // Start memory sampling
2191
+ const sampleInterval = setInterval(() => {
2192
+ const memUsage = process.memoryUsage();
2193
+ const timestamp = new Date().toISOString();
2194
+ const sample = {
2195
+ timestamp,
2196
+ heapUsed: memUsage.heapUsed,
2197
+ heapTotal: memUsage.heapTotal,
2198
+ external: memUsage.external,
2199
+ rss: memUsage.rss,
2200
+ };
2201
+ // Append to samples file
2202
+ const samplesFile = path.join(outputDir, `${agentName}-memory.jsonl`);
2203
+ fs.appendFileSync(samplesFile, JSON.stringify(sample) + '\n');
2204
+ }, 5000);
2205
+ process.on('SIGINT', async () => {
2206
+ clearInterval(sampleInterval);
2207
+ console.log('\n');
2208
+ console.log('Profiling stopped.');
2209
+ console.log('');
2210
+ console.log(`Profile data saved to: ${outputDir}/`);
2211
+ console.log(` - ${agentName}-memory.jsonl (memory samples)`);
2212
+ console.log('');
2213
+ console.log('To analyze:');
2214
+ console.log(` 1. Open chrome://inspect in Chrome`);
2215
+ console.log(` 2. Load CPU/heap profiles from ${outputDir}/`);
2216
+ console.log('');
2217
+ wrapper.stop();
2218
+ process.exit(0);
2219
+ });
2220
+ await wrapper.start();
2221
+ console.log(`Profiling ${agentName}... Press Ctrl+C to stop.`);
2222
+ });
1042
2223
  program.parse();
1043
2224
  //# sourceMappingURL=index.js.map