agent-relay 1.3.2 → 1.5.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.
- package/README.md +130 -158
- package/bin/relay-pty +0 -0
- package/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/deploy/workspace/entrypoint.sh +9 -0
- package/dist/bridge/spawner.d.ts +4 -4
- package/dist/bridge/spawner.js +58 -92
- package/dist/cli/index.d.ts +8 -6
- package/dist/cli/index.js +282 -47
- package/dist/cloud/api/daemons.js +13 -32
- package/dist/cloud/api/onboarding.js +2 -4
- package/dist/cloud/api/providers.js +6 -0
- package/dist/cloud/config.d.ts +1 -0
- package/dist/cloud/config.js +2 -0
- package/dist/cloud/db/bulk-ingest.d.ts +2 -1
- package/dist/cloud/db/drizzle.d.ts +21 -26
- package/dist/cloud/db/drizzle.js +87 -100
- package/dist/cloud/db/index.d.ts +6 -5
- package/dist/cloud/db/index.js +9 -8
- package/dist/cloud/db/schema.d.ts +1049 -1076
- package/dist/cloud/db/schema.js +59 -71
- package/dist/cloud/server.js +854 -18
- package/dist/cloud/services/persistence.d.ts +15 -15
- package/dist/cloud/services/persistence.js +14 -14
- package/dist/daemon/agent-manager.d.ts +6 -5
- package/dist/daemon/agent-manager.js +12 -8
- package/dist/daemon/channel-membership-store.d.ts +48 -0
- package/dist/daemon/channel-membership-store.js +149 -0
- package/dist/daemon/cloud-sync.d.ts +2 -0
- package/dist/daemon/cloud-sync.js +4 -0
- package/dist/daemon/connection.js +17 -9
- package/dist/daemon/router.d.ts +37 -0
- package/dist/daemon/router.js +318 -79
- package/dist/daemon/server.d.ts +15 -0
- package/dist/daemon/server.js +141 -3
- package/dist/dashboard/out/404.html +1 -0
- package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_buildManifest.js +1 -0
- package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_ssgManifest.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/116-eacf84a131b80db9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/117-c8afed19e821a35d.js +2 -0
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
- package/dist/dashboard/out/_next/static/chunks/64-87ab9cd6bcf2f737.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/766-aa7c8c9900ff5f53.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/891-a024fbe4b619cf6f.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-f746f29e01fffc43.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/page-ffad986adfcc8b31.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-cfeb437f08a12ed9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-03ac6f35a6654ea6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/history/page-240f91e8b06ba8ac.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/login/page-6ec54eee75877971.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-82938ab8fcf44694.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/page-671037943b2f2e43.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-0efa024c28ba4597.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-57cbd738c6a73859.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-5ab0854472b402b0.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-18a4665665f6be11.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/main-app-6e8e8d3ef4e0192a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
- package/dist/dashboard/out/_next/static/css/4034f236dd1a3178.css +1 -0
- package/dist/dashboard/out/_next/static/css/8f9ed310f454e5a5.css +1 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +45 -0
- package/dist/dashboard/out/alt-logos/logo.svg +38 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +38 -0
- package/dist/dashboard/out/app/onboarding.html +1 -0
- package/dist/dashboard/out/app/onboarding.txt +7 -0
- package/dist/dashboard/out/app.html +1 -0
- package/dist/dashboard/out/app.txt +7 -0
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/cloud/link.html +1 -0
- package/dist/dashboard/out/cloud/link.txt +7 -0
- package/dist/dashboard/out/connect-repos.html +1 -0
- package/dist/dashboard/out/connect-repos.txt +7 -0
- package/dist/dashboard/out/history.html +1 -0
- package/dist/dashboard/out/history.txt +7 -0
- package/dist/dashboard/out/index.html +1 -0
- package/dist/dashboard/out/index.txt +7 -0
- package/dist/dashboard/out/login.html +5 -0
- package/dist/dashboard/out/login.txt +7 -0
- package/dist/dashboard/out/metrics.html +1 -0
- package/dist/dashboard/out/metrics.txt +7 -0
- package/dist/dashboard/out/pricing.html +13 -0
- package/dist/dashboard/out/pricing.txt +7 -0
- package/dist/dashboard/out/providers/setup/claude.html +1 -0
- package/dist/dashboard/out/providers/setup/claude.txt +8 -0
- package/dist/dashboard/out/providers/setup/codex.html +1 -0
- package/dist/dashboard/out/providers/setup/codex.txt +8 -0
- package/dist/dashboard/out/providers.html +1 -0
- package/dist/dashboard/out/providers.txt +7 -0
- package/dist/dashboard/out/signup.html +6 -0
- package/dist/dashboard/out/signup.txt +7 -0
- package/dist/dashboard-server/metrics.d.ts +105 -0
- package/dist/dashboard-server/metrics.js +193 -0
- package/dist/dashboard-server/needs-attention.d.ts +24 -0
- package/dist/dashboard-server/needs-attention.js +78 -0
- package/dist/dashboard-server/server.d.ts +15 -0
- package/dist/dashboard-server/server.js +4753 -0
- package/dist/dashboard-server/start.d.ts +6 -0
- package/dist/dashboard-server/start.js +13 -0
- package/dist/dashboard-server/user-bridge.d.ts +132 -0
- package/dist/dashboard-server/user-bridge.js +317 -0
- package/dist/protocol/channels.d.ts +14 -8
- package/dist/protocol/channels.js +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/relay-pty-schemas.d.ts +209 -0
- package/dist/protocol/relay-pty-schemas.js +60 -0
- package/dist/wrapper/auth-detection.js +8 -1
- package/dist/wrapper/base-wrapper.d.ts +11 -1
- package/dist/wrapper/base-wrapper.js +67 -6
- package/dist/wrapper/client.d.ts +49 -1
- package/dist/wrapper/client.js +167 -0
- package/dist/wrapper/parser.d.ts +0 -4
- package/dist/wrapper/parser.js +38 -10
- package/dist/wrapper/pty-wrapper.d.ts +12 -1
- package/dist/wrapper/pty-wrapper.js +104 -5
- package/dist/wrapper/relay-pty-orchestrator.d.ts +270 -0
- package/dist/wrapper/relay-pty-orchestrator.js +970 -0
- package/dist/wrapper/shared.d.ts +1 -1
- package/dist/wrapper/shared.js +14 -4
- package/dist/wrapper/tmux-wrapper.d.ts +13 -1
- package/dist/wrapper/tmux-wrapper.js +143 -29
- package/package.json +9 -4
- package/scripts/postinstall.js +101 -11
- package/.trajectories/active/traj_3yx9dy148mge.json +0 -42
- package/.trajectories/agent-relay-322-324.md +0 -17
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +0 -49
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +0 -31
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +0 -125
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +0 -62
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +0 -65
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +0 -37
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +0 -49
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +0 -31
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +0 -65
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +0 -37
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +0 -49
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +0 -31
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +0 -113
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +0 -57
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +0 -53
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +0 -32
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +0 -49
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +0 -31
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +0 -26
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +0 -6
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +0 -47
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +0 -32
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +0 -53
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +0 -32
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +0 -49
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +0 -31
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +0 -77
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +0 -42
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +0 -59
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +0 -33
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +0 -53
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +0 -32
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +0 -48
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +0 -24
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +0 -77
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +0 -42
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +0 -109
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +0 -77
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +0 -42
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +0 -209
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +0 -97
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +0 -66
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +0 -36
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +0 -48
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +0 -24
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +0 -49
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +0 -31
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +0 -49
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +0 -23
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +0 -40
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +0 -22
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +0 -66
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +0 -36
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +0 -49
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +0 -31
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +0 -65
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +0 -37
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +0 -53
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +0 -32
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +0 -49
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +0 -31
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +0 -26
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +0 -6
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +0 -121
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +0 -29
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +0 -59
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +0 -37
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +0 -36
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +0 -21
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +0 -53
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +0 -32
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +0 -101
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +0 -52
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +0 -77
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +0 -42
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +0 -53
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +0 -32
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +0 -25
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +0 -15
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +0 -101
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +0 -44
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +0 -101
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +0 -52
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +0 -49
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +0 -31
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +0 -65
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +0 -37
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +0 -22
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +0 -5
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +0 -53
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +0 -32
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +0 -61
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +0 -36
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +0 -49
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +0 -31
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +0 -25
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +0 -15
- package/.trajectories/completed/2026-01/traj_multi_server_arch.md +0 -101
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +0 -53
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +0 -32
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +0 -53
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +0 -32
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +0 -73
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +0 -41
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +0 -48
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +0 -24
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +0 -53
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +0 -32
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +0 -27
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +0 -14
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +0 -77
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +0 -42
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +0 -77
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +0 -42
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +0 -53
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +0 -32
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +0 -83
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +0 -47
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +0 -59
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +0 -37
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +0 -109
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +0 -56
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +0 -48
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +0 -16
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +0 -59
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +0 -37
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +0 -53
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +0 -32
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +0 -84
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +0 -109
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +0 -53
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +0 -32
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +0 -53
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +0 -32
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +0 -186
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +0 -86
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +0 -77
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +0 -42
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +0 -71
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +0 -42
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +0 -89
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +0 -47
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +0 -53
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +0 -32
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +0 -20
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +0 -6
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +0 -113
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +0 -57
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +0 -61
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +0 -36
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +0 -175
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +0 -82
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +0 -65
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +0 -37
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +0 -49
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +0 -31
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +0 -49
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +0 -31
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +0 -47
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +0 -32
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +0 -59
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +0 -37
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +0 -49
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +0 -31
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +0 -53
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +0 -32
- package/.trajectories/consolidate-settings-panel.md +0 -24
- package/.trajectories/gh-cli-user-token.md +0 -26
- package/.trajectories/index.json +0 -607
package/dist/cli/index.js
CHANGED
|
@@ -3,17 +3,21 @@
|
|
|
3
3
|
* Agent Relay CLI
|
|
4
4
|
*
|
|
5
5
|
* Commands:
|
|
6
|
-
* relay
|
|
7
|
-
* relay -
|
|
8
|
-
* relay
|
|
9
|
-
* relay
|
|
10
|
-
* relay
|
|
11
|
-
* relay
|
|
6
|
+
* relay claude - Start daemon + Dashboard coordinator with Claude
|
|
7
|
+
* relay codex - Start daemon + Dasbboard coordinator with Codex
|
|
8
|
+
* relay create-agent <cmd> - Wrap agent with real-time messaging
|
|
9
|
+
* relay create-agent -n Name cmd - Wrap with specific agent name
|
|
10
|
+
* relay up - Start daemon + dashboard
|
|
11
|
+
* relay read <id> - Read full message by ID
|
|
12
|
+
* relay agents - List connected agents
|
|
13
|
+
* relay who - Show currently active agents
|
|
12
14
|
*/
|
|
13
15
|
import { Command } from 'commander';
|
|
14
16
|
import { config as dotenvConfig } from 'dotenv';
|
|
15
17
|
import { Daemon } from '../daemon/server.js';
|
|
16
18
|
import { RelayClient } from '../wrapper/client.js';
|
|
19
|
+
import { RelayPtyOrchestrator } from '../wrapper/relay-pty-orchestrator.js';
|
|
20
|
+
import { AgentSpawner } from '../bridge/spawner.js';
|
|
17
21
|
import { generateAgentName } from '../utils/name-generator.js';
|
|
18
22
|
import { getTmuxPath } from '../utils/tmux-resolver.js';
|
|
19
23
|
import { readWorkersMetadata, getWorkerLogsDir } from '../bridge/spawner.js';
|
|
@@ -26,6 +30,7 @@ import { promisify } from 'node:util';
|
|
|
26
30
|
import { exec } from 'node:child_process';
|
|
27
31
|
import { fileURLToPath } from 'node:url';
|
|
28
32
|
dotenvConfig();
|
|
33
|
+
const DEFAULT_DASHBOARD_PORT = process.env.AGENT_RELAY_DASHBOARD_PORT || '3888';
|
|
29
34
|
// Read version from package.json
|
|
30
35
|
const __filename = fileURLToPath(import.meta.url);
|
|
31
36
|
const __dirname = path.dirname(__filename);
|
|
@@ -35,7 +40,7 @@ const VERSION = packageJson.version;
|
|
|
35
40
|
const execAsync = promisify(exec);
|
|
36
41
|
// Check for updates in background (non-blocking)
|
|
37
42
|
// Only show notification for interactive commands, not when wrapping agents or running update
|
|
38
|
-
const interactiveCommands = ['up', 'down', 'status', 'agents', 'who', 'version', '--version', '-V', '--help', '-h'];
|
|
43
|
+
const interactiveCommands = ['up', 'down', 'status', 'agents', 'who', 'version', '--version', '-V', '--help', '-h', 'create-agent', 'claude', 'codex'];
|
|
39
44
|
const shouldCheckUpdates = process.argv.length > 2 &&
|
|
40
45
|
interactiveCommands.includes(process.argv[2]);
|
|
41
46
|
if (shouldCheckUpdates) {
|
|
@@ -49,20 +54,19 @@ program
|
|
|
49
54
|
.name('agent-relay')
|
|
50
55
|
.description('Agent-to-agent messaging')
|
|
51
56
|
.version(VERSION, '-V, --version', 'Output the version number');
|
|
52
|
-
//
|
|
57
|
+
// create-agent - Wrap agent with real-time messaging
|
|
53
58
|
program
|
|
59
|
+
.command('create-agent')
|
|
60
|
+
.description('Wrap an agent with real-time messaging')
|
|
54
61
|
.option('-n, --name <name>', 'Agent name (auto-generated if not set)')
|
|
55
|
-
.option('-
|
|
62
|
+
.option('-d, --debug', 'Enable debug output')
|
|
56
63
|
.option('--prefix <pattern>', 'Relay prefix pattern (default: ->relay:)')
|
|
64
|
+
.option('--dashboard-port <port>', 'Dashboard port for spawn/release API (auto-detected if not set)')
|
|
57
65
|
.option('--shadow <name>', 'Spawn a shadow agent with this name that monitors the primary')
|
|
58
66
|
.option('--shadow-role <role>', 'Shadow role: reviewer, auditor, or triggers (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
|
|
59
|
-
.
|
|
67
|
+
.option('--skip-instructions', 'Skip initial instruction injection (use with --append-system-prompt)')
|
|
68
|
+
.argument('<command...>', 'Command to wrap (e.g., claude)')
|
|
60
69
|
.action(async (commandParts, options) => {
|
|
61
|
-
// If no command provided, show help
|
|
62
|
-
if (!commandParts || commandParts.length === 0) {
|
|
63
|
-
program.help();
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
70
|
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
67
71
|
const { findAgentConfig, isClaudeCli, buildClaudeArgs } = await import('../utils/agent-config.js');
|
|
68
72
|
const paths = getProjectPaths();
|
|
@@ -82,20 +86,52 @@ program
|
|
|
82
86
|
finalArgs = buildClaudeArgs(agentName, commandArgs, paths.projectRoot);
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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 common ports
|
|
97
|
+
const portsToTry = [
|
|
98
|
+
parseInt(DEFAULT_DASHBOARD_PORT, 10),
|
|
99
|
+
3889, 3890, 3891, // Common fallback ports when default is in use
|
|
100
|
+
];
|
|
101
|
+
for (const port of portsToTry) {
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(`http://localhost:${port}/api/health`, {
|
|
104
|
+
method: 'GET',
|
|
105
|
+
signal: AbortSignal.timeout(300), // Quick timeout for detection
|
|
106
|
+
});
|
|
107
|
+
if (response.ok) {
|
|
108
|
+
const health = await response.json();
|
|
109
|
+
if (health.status === 'healthy') {
|
|
110
|
+
dashboardPort = port;
|
|
111
|
+
console.error(`Dashboard detected: http://localhost:${dashboardPort}`);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Try next port
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Create spawner as fallback for direct spawn (if dashboard API not available)
|
|
122
|
+
const spawner = new AgentSpawner(paths.projectRoot, undefined, dashboardPort);
|
|
123
|
+
const wrapper = new RelayPtyOrchestrator({
|
|
90
124
|
name: agentName,
|
|
91
125
|
command: mainCommand,
|
|
92
126
|
args: finalArgs,
|
|
93
127
|
socketPath: paths.socketPath,
|
|
94
|
-
|
|
128
|
+
cwd: paths.projectRoot,
|
|
95
129
|
relayPrefix: options.prefix,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
130
|
+
skipInstructions: options.skipInstructions,
|
|
131
|
+
streamLogs: true,
|
|
132
|
+
// Use dashboard API for spawn/release when available (preferred - works from any context)
|
|
133
|
+
dashboardPort,
|
|
134
|
+
// Wire up spawn/release callbacks as fallback (if no dashboardPort)
|
|
99
135
|
onSpawn: async (workerName, workerCli, task) => {
|
|
100
136
|
console.error(`[${agentName}] Spawning ${workerName} (${workerCli})...`);
|
|
101
137
|
const result = await spawner.spawn({
|
|
@@ -199,10 +235,12 @@ program
|
|
|
199
235
|
}
|
|
200
236
|
}
|
|
201
237
|
});
|
|
202
|
-
// up - Start daemon
|
|
238
|
+
// up - Start daemon + dashboard
|
|
203
239
|
program
|
|
204
240
|
.command('up')
|
|
205
|
-
.description('Start daemon')
|
|
241
|
+
.description('Start daemon + dashboard')
|
|
242
|
+
.option('--no-dashboard', 'Disable web dashboard')
|
|
243
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
206
244
|
.option('--spawn', 'Force spawn all agents from teams.json')
|
|
207
245
|
.option('--no-spawn', 'Do not auto-spawn agents (just start daemon)')
|
|
208
246
|
.option('--watch', 'Auto-restart daemon on crash (supervisor mode)')
|
|
@@ -219,6 +257,10 @@ program
|
|
|
219
257
|
const startDaemon = () => {
|
|
220
258
|
// Build args without --watch to prevent infinite recursion
|
|
221
259
|
const args = ['up'];
|
|
260
|
+
if (options.dashboard === false)
|
|
261
|
+
args.push('--no-dashboard');
|
|
262
|
+
if (options.port)
|
|
263
|
+
args.push('--port', options.port);
|
|
222
264
|
if (options.spawn === true)
|
|
223
265
|
args.push('--spawn');
|
|
224
266
|
if (options.spawn === false)
|
|
@@ -330,6 +372,28 @@ program
|
|
|
330
372
|
try {
|
|
331
373
|
await daemon.start();
|
|
332
374
|
console.log('Daemon started.');
|
|
375
|
+
let dashboardPort;
|
|
376
|
+
// Dashboard starts by default (use --no-dashboard to disable)
|
|
377
|
+
if (options.dashboard !== false) {
|
|
378
|
+
const port = parseInt(options.port, 10);
|
|
379
|
+
const { startDashboard } = await import('../dashboard-server/server.js');
|
|
380
|
+
dashboardPort = await startDashboard({
|
|
381
|
+
port,
|
|
382
|
+
dataDir: paths.dataDir,
|
|
383
|
+
teamDir: paths.teamDir,
|
|
384
|
+
dbPath,
|
|
385
|
+
enableSpawner: true,
|
|
386
|
+
projectRoot: paths.projectRoot,
|
|
387
|
+
});
|
|
388
|
+
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
389
|
+
// Hook daemon log output to dashboard WebSocket
|
|
390
|
+
daemon.onLogOutput = (agentName, data, _timestamp) => {
|
|
391
|
+
const broadcast = global.__broadcastLogOutput;
|
|
392
|
+
if (broadcast) {
|
|
393
|
+
broadcast(agentName, data);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
333
397
|
// Determine if we should auto-spawn agents
|
|
334
398
|
// --spawn: force spawn
|
|
335
399
|
// --no-spawn: never spawn
|
|
@@ -342,7 +406,7 @@ program
|
|
|
342
406
|
if (shouldSpawn && teamsConfig && teamsConfig.agents.length > 0) {
|
|
343
407
|
console.log('');
|
|
344
408
|
console.log('Auto-spawning agents from teams.json...');
|
|
345
|
-
spawner = new AgentSpawner(paths.projectRoot);
|
|
409
|
+
spawner = new AgentSpawner(paths.projectRoot, undefined, dashboardPort);
|
|
346
410
|
for (const agent of teamsConfig.agents) {
|
|
347
411
|
console.log(` Spawning ${agent.name} (${agent.cli})...`);
|
|
348
412
|
const result = await spawner.spawn({
|
|
@@ -393,6 +457,153 @@ program
|
|
|
393
457
|
console.log('Cleaned up stale pid');
|
|
394
458
|
}
|
|
395
459
|
});
|
|
460
|
+
// System prompt for Dashboard agent - plain text to avoid shell escaping issues
|
|
461
|
+
const MEGA_SYSTEM_PROMPT = [
|
|
462
|
+
'You are Dashboard, a lead coordinator in agent-relay.',
|
|
463
|
+
'Your PRIMARY job is to delegate - you should almost NEVER do implementation work yourself.',
|
|
464
|
+
'ALWAYS SPAWN AGENTS: For any non-trivial task, spawn specialized workers.',
|
|
465
|
+
].join(' ');
|
|
466
|
+
// Helper function for starting Dashboard coordinator with a specific provider
|
|
467
|
+
async function startDashboardCoordinator(operator) {
|
|
468
|
+
const { spawn } = await import('node:child_process');
|
|
469
|
+
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
470
|
+
const paths = getProjectPaths();
|
|
471
|
+
console.log(`Starting Dashboard with ${operator}...`);
|
|
472
|
+
console.log(`Project: ${paths.projectRoot}`);
|
|
473
|
+
// Step 1: Check if daemon is already running, start if needed
|
|
474
|
+
console.log('\n[1/3] Checking daemon...');
|
|
475
|
+
// Check if socket exists (daemon running)
|
|
476
|
+
const socketExists = fs.existsSync(paths.socketPath);
|
|
477
|
+
// Ports to try for dashboard detection
|
|
478
|
+
const portsToTry = [
|
|
479
|
+
parseInt(DEFAULT_DASHBOARD_PORT, 10),
|
|
480
|
+
3889, 3890, 3891,
|
|
481
|
+
];
|
|
482
|
+
// Check if dashboard is responding for THIS project
|
|
483
|
+
let dashboardReady = false;
|
|
484
|
+
let detectedPort;
|
|
485
|
+
// Helper to check health at a port
|
|
486
|
+
const checkPort = async (port) => {
|
|
487
|
+
try {
|
|
488
|
+
const response = await fetch(`http://localhost:${port}/api/health`, {
|
|
489
|
+
signal: AbortSignal.timeout(500),
|
|
490
|
+
});
|
|
491
|
+
if (response.ok) {
|
|
492
|
+
const health = await response.json();
|
|
493
|
+
return health.status === 'healthy';
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Port not responding
|
|
498
|
+
}
|
|
499
|
+
return false;
|
|
500
|
+
};
|
|
501
|
+
if (socketExists) {
|
|
502
|
+
for (const port of portsToTry) {
|
|
503
|
+
if (await checkPort(port)) {
|
|
504
|
+
dashboardReady = true;
|
|
505
|
+
detectedPort = port;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (dashboardReady && detectedPort) {
|
|
511
|
+
console.log(`Daemon already running at port ${detectedPort}, reusing...`);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
console.log('Starting daemon...');
|
|
515
|
+
const daemonProc = spawn(process.execPath, [process.argv[1], 'up'], {
|
|
516
|
+
stdio: 'ignore',
|
|
517
|
+
detached: true,
|
|
518
|
+
});
|
|
519
|
+
daemonProc.unref();
|
|
520
|
+
// Wait for dashboard to be ready (up to 10 seconds)
|
|
521
|
+
const maxWait = 10000;
|
|
522
|
+
const startTime = Date.now();
|
|
523
|
+
while (Date.now() - startTime < maxWait) {
|
|
524
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
525
|
+
for (const port of portsToTry) {
|
|
526
|
+
if (await checkPort(port)) {
|
|
527
|
+
dashboardReady = true;
|
|
528
|
+
detectedPort = port;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (dashboardReady)
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
if (!dashboardReady) {
|
|
536
|
+
console.error('Warning: Dashboard may not be fully ready. Spawn might not work.');
|
|
537
|
+
detectedPort = parseInt(DEFAULT_DASHBOARD_PORT, 10); // Fallback
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const dashboardPort = detectedPort || parseInt(DEFAULT_DASHBOARD_PORT, 10);
|
|
541
|
+
// Step 2: Install prpm snippet via npx
|
|
542
|
+
console.log('[2/3] Installing agent-relay snippet...');
|
|
543
|
+
const prpmArgs = operator.toLowerCase() === 'claude'
|
|
544
|
+
? ['prpm', 'install', '@agent-relay/agent-relay-snippet', '--location', 'CLAUDE.md']
|
|
545
|
+
: ['prpm', 'install', '@agent-relay/agent-relay-snippet'];
|
|
546
|
+
try {
|
|
547
|
+
await new Promise((resolve, reject) => {
|
|
548
|
+
const prpmProc = spawn('npx', prpmArgs, {
|
|
549
|
+
stdio: 'inherit',
|
|
550
|
+
});
|
|
551
|
+
prpmProc.on('close', (code) => {
|
|
552
|
+
if (code === 0)
|
|
553
|
+
resolve();
|
|
554
|
+
else
|
|
555
|
+
reject(new Error(`npx prpm exited with code ${code}`));
|
|
556
|
+
});
|
|
557
|
+
prpmProc.on('error', reject);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
console.warn(`Warning: prpm install failed: ${err.message}`);
|
|
562
|
+
console.warn('Continuing without snippet installation...');
|
|
563
|
+
}
|
|
564
|
+
// Step 3: Start Dashboard agent with system prompt
|
|
565
|
+
console.log(`[3/3] Starting Dashboard agent with ${operator}...`);
|
|
566
|
+
console.log('');
|
|
567
|
+
const op = operator.toLowerCase();
|
|
568
|
+
// Build CLI-specific arguments for system prompt
|
|
569
|
+
// These args go AFTER the operator command, passed through to the CLI
|
|
570
|
+
let cliArgs = [];
|
|
571
|
+
if (op === 'claude') {
|
|
572
|
+
// Claude: --append-system-prompt <content> (takes content directly, not file)
|
|
573
|
+
cliArgs = ['--append-system-prompt', MEGA_SYSTEM_PROMPT];
|
|
574
|
+
}
|
|
575
|
+
else if (op === 'codex') {
|
|
576
|
+
// Codex: --config developer_instructions="<content>"
|
|
577
|
+
cliArgs = ['--config', `developer_instructions=${MEGA_SYSTEM_PROMPT}`];
|
|
578
|
+
}
|
|
579
|
+
// Use '--' to separate agent-relay options from the command + its args
|
|
580
|
+
// Format: agent-relay create-agent -n Dashboard --skip-instructions --dashboard-port <port> -- claude --append-system-prompt "..."
|
|
581
|
+
const agentProc = spawn(process.execPath, [process.argv[1], 'create-agent', '-n', 'Dashboard', '--skip-instructions', '--dashboard-port', String(dashboardPort), '--', operator, ...cliArgs], { stdio: 'inherit' });
|
|
582
|
+
// Forward signals to agent process
|
|
583
|
+
process.on('SIGINT', () => {
|
|
584
|
+
agentProc.kill('SIGINT');
|
|
585
|
+
});
|
|
586
|
+
process.on('SIGTERM', () => {
|
|
587
|
+
agentProc.kill('SIGTERM');
|
|
588
|
+
});
|
|
589
|
+
agentProc.on('close', (code) => {
|
|
590
|
+
process.exit(code ?? 0);
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
// claude - Start daemon and spawn Dashboard coordinator with Claude
|
|
594
|
+
program
|
|
595
|
+
.command('claude')
|
|
596
|
+
.description('Start daemon and Dashboard coordinator with Claude')
|
|
597
|
+
.action(async () => {
|
|
598
|
+
await startDashboardCoordinator('claude');
|
|
599
|
+
});
|
|
600
|
+
// codex - Start daemon and spawn Dashboard coordinator with Codex
|
|
601
|
+
program
|
|
602
|
+
.command('codex')
|
|
603
|
+
.description('Start daemon and Dashboard coordinator with Codex')
|
|
604
|
+
.action(async () => {
|
|
605
|
+
await startDashboardCoordinator('codex');
|
|
606
|
+
});
|
|
396
607
|
// status - Check daemon status
|
|
397
608
|
program
|
|
398
609
|
.command('status')
|
|
@@ -435,6 +646,7 @@ program
|
|
|
435
646
|
const os = await import('node:os');
|
|
436
647
|
const paths = getProjectPaths();
|
|
437
648
|
const agentsPath = path.join(paths.teamDir, 'agents.json');
|
|
649
|
+
const connectedAgentsPath = path.join(paths.teamDir, 'connected-agents.json');
|
|
438
650
|
// Load registered agents
|
|
439
651
|
const allAgents = loadAgents(agentsPath);
|
|
440
652
|
const agents = options.all
|
|
@@ -442,13 +654,41 @@ program
|
|
|
442
654
|
: allAgents.filter(isVisibleAgent);
|
|
443
655
|
// Load spawned workers
|
|
444
656
|
const workers = readWorkersMetadata(paths.projectRoot);
|
|
657
|
+
// Load currently connected agents (agents with active socket connections)
|
|
658
|
+
let connectedAgentNames = new Set();
|
|
659
|
+
let connectedUpdatedAt = 0;
|
|
660
|
+
try {
|
|
661
|
+
if (fs.existsSync(connectedAgentsPath)) {
|
|
662
|
+
const connectedData = JSON.parse(fs.readFileSync(connectedAgentsPath, 'utf-8'));
|
|
663
|
+
connectedAgentNames = new Set([
|
|
664
|
+
...(connectedData.agents || []),
|
|
665
|
+
...(connectedData.users || []),
|
|
666
|
+
]);
|
|
667
|
+
connectedUpdatedAt = connectedData.updatedAt || 0;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
// If file doesn't exist or is invalid, fall back to registry-based status
|
|
672
|
+
}
|
|
673
|
+
// Check if connected-agents.json is fresh (within 30 seconds)
|
|
674
|
+
const isConnectedAgentsStale = Date.now() - connectedUpdatedAt > 30_000;
|
|
445
675
|
const combined = [];
|
|
446
676
|
// Add registered agents
|
|
447
677
|
agents.forEach((agent) => {
|
|
448
678
|
const worker = workers.find(w => w.name === agent.name);
|
|
679
|
+
// Use connected-agents.json if fresh, otherwise fall back to registry lastSeen
|
|
680
|
+
let status;
|
|
681
|
+
if (!isConnectedAgentsStale && connectedAgentNames.size > 0) {
|
|
682
|
+
// We have fresh connected-agents data - use actual connection status
|
|
683
|
+
status = connectedAgentNames.has(agent.name ?? '') ? 'ONLINE' : 'OFFLINE';
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
// Fall back to registry-based status
|
|
687
|
+
status = getAgentStatus(agent);
|
|
688
|
+
}
|
|
449
689
|
combined.push({
|
|
450
690
|
name: agent.name ?? 'unknown',
|
|
451
|
-
status
|
|
691
|
+
status,
|
|
452
692
|
cli: agent.cli ?? '-',
|
|
453
693
|
lastSeen: agent.lastSeen,
|
|
454
694
|
team: worker?.team,
|
|
@@ -856,7 +1096,6 @@ program
|
|
|
856
1096
|
// Spawn architect agent if --architect flag is set
|
|
857
1097
|
let architectWrapper = null;
|
|
858
1098
|
if (options.architect !== undefined) {
|
|
859
|
-
const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
|
|
860
1099
|
// Determine CLI to use (default to claude)
|
|
861
1100
|
const architectCli = typeof options.architect === 'string' ? options.architect : 'claude';
|
|
862
1101
|
// Use first project as the base for the architect
|
|
@@ -923,14 +1162,13 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
923
1162
|
command = architectCli;
|
|
924
1163
|
}
|
|
925
1164
|
try {
|
|
926
|
-
architectWrapper = new
|
|
1165
|
+
architectWrapper = new RelayPtyOrchestrator({
|
|
927
1166
|
name: 'Architect',
|
|
928
1167
|
command,
|
|
929
1168
|
args,
|
|
930
1169
|
socketPath: basePaths.socketPath,
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
inboxDir: basePaths.dataDir,
|
|
1170
|
+
cwd: baseProject.path,
|
|
1171
|
+
streamLogs: true,
|
|
934
1172
|
});
|
|
935
1173
|
await architectWrapper.start();
|
|
936
1174
|
// Wait for agent to be ready, then inject the prompt
|
|
@@ -938,7 +1176,6 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
938
1176
|
try {
|
|
939
1177
|
await architectWrapper.injectMessage(architectPrompt);
|
|
940
1178
|
console.log('Architect agent started and initialized.');
|
|
941
|
-
console.log('Attach to session: tmux attach -t relay-Architect');
|
|
942
1179
|
console.log('');
|
|
943
1180
|
}
|
|
944
1181
|
catch (err) {
|
|
@@ -1293,7 +1530,7 @@ program
|
|
|
1293
1530
|
.argument('<name>', 'Agent name')
|
|
1294
1531
|
.argument('<cli>', 'CLI to use (claude, codex, gemini, etc.)')
|
|
1295
1532
|
.argument('[task]', 'Task description (can also be piped via stdin)')
|
|
1296
|
-
.option('--port <port>', 'Dashboard port',
|
|
1533
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1297
1534
|
.option('--team <team>', 'Team name for the agent')
|
|
1298
1535
|
.option('--spawner <name>', 'Name of the agent requesting the spawn (for policy enforcement)')
|
|
1299
1536
|
.option('--interactive', 'Disable auto-accept of permission prompts (for auth setup flows)')
|
|
@@ -1304,7 +1541,7 @@ program
|
|
|
1304
1541
|
.option('--shadow-triggers <triggers>', 'When to trigger shadow (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
|
|
1305
1542
|
.option('--shadow-speak-on <triggers>', 'When shadow should speak (comma-separated, same values as --shadow-triggers)')
|
|
1306
1543
|
.action(async (name, cli, task, options) => {
|
|
1307
|
-
const port = options.port ||
|
|
1544
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1308
1545
|
// Read task from stdin if not provided as argument
|
|
1309
1546
|
let finalTask = task;
|
|
1310
1547
|
if (!finalTask && !process.stdin.isTTY) {
|
|
@@ -1390,9 +1627,9 @@ program
|
|
|
1390
1627
|
.command('release')
|
|
1391
1628
|
.description('Release a spawned agent via API (no terminal required)')
|
|
1392
1629
|
.argument('<name>', 'Agent name to release')
|
|
1393
|
-
.option('--port <port>', 'Dashboard port',
|
|
1630
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1394
1631
|
.action(async (name, options) => {
|
|
1395
|
-
const port = options.port ||
|
|
1632
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1396
1633
|
try {
|
|
1397
1634
|
const response = await fetch(`http://localhost:${port}/api/spawned/${encodeURIComponent(name)}`, {
|
|
1398
1635
|
method: 'DELETE',
|
|
@@ -1960,12 +2197,12 @@ program
|
|
|
1960
2197
|
.command('metrics')
|
|
1961
2198
|
.description('Show agent memory metrics and resource usage')
|
|
1962
2199
|
.option('--agent <name>', 'Show metrics for specific agent')
|
|
1963
|
-
.option('--port <port>', 'Dashboard port',
|
|
2200
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1964
2201
|
.option('--json', 'Output as JSON')
|
|
1965
2202
|
.option('--watch', 'Continuously update metrics')
|
|
1966
2203
|
.option('--interval <ms>', 'Update interval for watch mode', '5000')
|
|
1967
2204
|
.action(async (options) => {
|
|
1968
|
-
const port = options.port ||
|
|
2205
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1969
2206
|
const fetchMetrics = async () => {
|
|
1970
2207
|
try {
|
|
1971
2208
|
const response = await fetch(`http://localhost:${port}/api/metrics/agents`);
|
|
@@ -2079,12 +2316,12 @@ program
|
|
|
2079
2316
|
program
|
|
2080
2317
|
.command('health')
|
|
2081
2318
|
.description('Show system health, crash insights, and recommendations')
|
|
2082
|
-
.option('--port <port>', 'Dashboard port',
|
|
2319
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
2083
2320
|
.option('--json', 'Output as JSON')
|
|
2084
2321
|
.option('--crashes', 'Show recent crash history')
|
|
2085
2322
|
.option('--alerts', 'Show unacknowledged alerts')
|
|
2086
2323
|
.action(async (options) => {
|
|
2087
|
-
const port = options.port ||
|
|
2324
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
2088
2325
|
try {
|
|
2089
2326
|
const response = await fetch(`http://localhost:${port}/api/metrics/health`);
|
|
2090
2327
|
if (!response.ok) {
|
|
@@ -2227,16 +2464,14 @@ program
|
|
|
2227
2464
|
console.log('');
|
|
2228
2465
|
// Use the regular wrapper but with profiling environment
|
|
2229
2466
|
const paths = getProjectPaths();
|
|
2230
|
-
const
|
|
2231
|
-
const wrapper = new TmuxWrapper({
|
|
2467
|
+
const wrapper = new RelayPtyOrchestrator({
|
|
2232
2468
|
name: agentName,
|
|
2233
2469
|
command: cmd,
|
|
2234
2470
|
args,
|
|
2235
2471
|
socketPath: paths.socketPath,
|
|
2236
|
-
|
|
2472
|
+
cwd: paths.projectRoot,
|
|
2237
2473
|
env: profileEnv,
|
|
2238
|
-
|
|
2239
|
-
inboxDir: paths.dataDir,
|
|
2474
|
+
streamLogs: true,
|
|
2240
2475
|
});
|
|
2241
2476
|
// Start memory sampling
|
|
2242
2477
|
const sampleInterval = setInterval(() => {
|
|
@@ -161,6 +161,8 @@ daemonsRouter.get('/workspace/:workspaceId/agents', requireAuth, async (req, res
|
|
|
161
161
|
name: agent.name,
|
|
162
162
|
status: agent.status,
|
|
163
163
|
isLocal: true,
|
|
164
|
+
isHuman: agent.isHuman,
|
|
165
|
+
avatarUrl: agent.avatarUrl,
|
|
164
166
|
daemonId: daemon.id,
|
|
165
167
|
daemonName: daemon.name,
|
|
166
168
|
daemonStatus: daemon.status,
|
|
@@ -394,22 +396,8 @@ daemonsRouter.get('/messages', requireDaemonAuth, async (req, res) => {
|
|
|
394
396
|
});
|
|
395
397
|
/**
|
|
396
398
|
* POST /api/daemons/messages/sync
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* Accepts batches of messages and stores them in agent_messages table.
|
|
400
|
-
* Uses upsert logic to handle duplicates (based on workspace_id + original_id).
|
|
401
|
-
*
|
|
402
|
-
* Request body:
|
|
403
|
-
* {
|
|
404
|
-
* messages: SyncMessageInput[]
|
|
405
|
-
* }
|
|
406
|
-
*
|
|
407
|
-
* Response:
|
|
408
|
-
* {
|
|
409
|
-
* success: true,
|
|
410
|
-
* synced: number, // Count of messages synced
|
|
411
|
-
* duplicates: number // Count of messages skipped (already existed)
|
|
412
|
-
* }
|
|
399
|
+
* Bulk sync messages from local daemon to cloud storage.
|
|
400
|
+
* Messages are stored for backup/history - local SQLite is used for display.
|
|
413
401
|
*/
|
|
414
402
|
daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
|
|
415
403
|
const daemon = req.daemon;
|
|
@@ -420,7 +408,6 @@ daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
|
|
|
420
408
|
if (messages.length === 0) {
|
|
421
409
|
return res.json({ success: true, synced: 0, duplicates: 0 });
|
|
422
410
|
}
|
|
423
|
-
// Limit batch size to prevent abuse
|
|
424
411
|
if (messages.length > 500) {
|
|
425
412
|
return res.status(400).json({ error: 'Maximum batch size is 500 messages' });
|
|
426
413
|
}
|
|
@@ -444,19 +431,15 @@ daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
|
|
|
444
431
|
return res.status(400).json({ error: hint });
|
|
445
432
|
}
|
|
446
433
|
try {
|
|
447
|
-
// Get user plan to determine retention policy
|
|
448
434
|
const user = await db.users.findById(daemon.userId);
|
|
449
435
|
const plan = user?.plan || 'free';
|
|
450
|
-
// Calculate expires_at based on plan
|
|
451
436
|
let expiresAt = null;
|
|
452
437
|
if (plan === 'free') {
|
|
453
|
-
expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
438
|
+
expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
454
439
|
}
|
|
455
440
|
else if (plan === 'pro') {
|
|
456
|
-
expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
|
|
441
|
+
expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
|
|
457
442
|
}
|
|
458
|
-
// Enterprise: null (never expires)
|
|
459
|
-
// Transform to NewAgentMessage format
|
|
460
443
|
const dbMessages = messages.map((msg) => ({
|
|
461
444
|
workspaceId,
|
|
462
445
|
daemonId: daemon.id,
|
|
@@ -483,11 +466,7 @@ daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
|
|
|
483
466
|
const synced = result.inserted;
|
|
484
467
|
const duplicates = result.duplicates;
|
|
485
468
|
console.log(`[message-sync] Synced ${synced} messages for daemon ${daemon.id}, ${duplicates} duplicates skipped (${result.durationMs}ms)`);
|
|
486
|
-
res.json({
|
|
487
|
-
success: true,
|
|
488
|
-
synced,
|
|
489
|
-
duplicates,
|
|
490
|
-
});
|
|
469
|
+
res.json({ success: true, synced, duplicates });
|
|
491
470
|
}
|
|
492
471
|
catch (error) {
|
|
493
472
|
console.error('Error syncing messages:', error);
|
|
@@ -496,7 +475,7 @@ daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
|
|
|
496
475
|
});
|
|
497
476
|
/**
|
|
498
477
|
* GET /api/daemons/messages/stats
|
|
499
|
-
* Get message sync statistics
|
|
478
|
+
* Get message sync statistics and database health.
|
|
500
479
|
*/
|
|
501
480
|
daemonsRouter.get('/messages/stats', requireDaemonAuth, async (req, res) => {
|
|
502
481
|
const daemon = req.daemon;
|
|
@@ -504,12 +483,14 @@ daemonsRouter.get('/messages/stats', requireDaemonAuth, async (req, res) => {
|
|
|
504
483
|
return res.status(400).json({ error: 'Daemon must be linked to a workspace' });
|
|
505
484
|
}
|
|
506
485
|
try {
|
|
507
|
-
// Get message count and pool health in parallel
|
|
508
|
-
const
|
|
509
|
-
|
|
486
|
+
// Get message count via raw query and pool health in parallel
|
|
487
|
+
const pool = db.getRawPool();
|
|
488
|
+
const [countResult, poolHealth, poolStats] = await Promise.all([
|
|
489
|
+
pool.query('SELECT COUNT(*) FROM agent_messages WHERE workspace_id = $1', [daemon.workspaceId]),
|
|
510
490
|
db.bulk.checkHealth(),
|
|
511
491
|
Promise.resolve(db.bulk.getPoolStats()),
|
|
512
492
|
]);
|
|
493
|
+
const count = parseInt(countResult.rows[0]?.count || '0', 10);
|
|
513
494
|
res.json({
|
|
514
495
|
workspaceId: daemon.workspaceId,
|
|
515
496
|
messageCount: count,
|
|
@@ -637,10 +637,8 @@ async function validateProviderToken(provider, token) {
|
|
|
637
637
|
},
|
|
638
638
|
},
|
|
639
639
|
google: {
|
|
640
|
-
url:
|
|
641
|
-
headers: {
|
|
642
|
-
Authorization: `Bearer ${token}`,
|
|
643
|
-
},
|
|
640
|
+
url: `https://generativelanguage.googleapis.com/v1/models?key=${encodeURIComponent(token)}`,
|
|
641
|
+
headers: {},
|
|
644
642
|
},
|
|
645
643
|
};
|
|
646
644
|
const config = endpoints[provider];
|
|
@@ -298,6 +298,12 @@ providersRouter.post('/:provider/api-key', async (req, res) => {
|
|
|
298
298
|
// 200 = valid, 401 = invalid key, 400/other = might still be valid key
|
|
299
299
|
isValid = testRes.status !== 401;
|
|
300
300
|
}
|
|
301
|
+
else if (provider === 'google') {
|
|
302
|
+
// Test Google/Gemini API key (uses query param auth, not Bearer token)
|
|
303
|
+
const testRes = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${encodeURIComponent(apiKey)}`);
|
|
304
|
+
// 200 = valid, 400/401/403 = invalid key
|
|
305
|
+
isValid = testRes.status === 200;
|
|
306
|
+
}
|
|
301
307
|
else {
|
|
302
308
|
// For other providers, just accept the key
|
|
303
309
|
isValid = true;
|
package/dist/cloud/config.d.ts
CHANGED
package/dist/cloud/config.js
CHANGED
|
@@ -17,6 +17,8 @@ export function loadConfig() {
|
|
|
17
17
|
publicUrl: process.env.PUBLIC_URL || 'http://localhost:4567',
|
|
18
18
|
appUrl: process.env.APP_URL || process.env.PUBLIC_URL || 'http://localhost:4567',
|
|
19
19
|
sessionSecret: requireEnv('SESSION_SECRET'),
|
|
20
|
+
// Local dashboard for channel API (auto-detected if not set)
|
|
21
|
+
localDashboardUrl: optionalEnv('LOCAL_DASHBOARD_URL'),
|
|
20
22
|
databaseUrl: requireEnv('DATABASE_URL'),
|
|
21
23
|
redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
|
|
22
24
|
github: {
|