agent-relay 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +3 -0
- package/.nvmrc +1 -0
- package/.trajectories/agent-relay-322-324.md +17 -0
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +49 -0
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +31 -0
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +125 -0
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +62 -0
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +65 -0
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +37 -0
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +65 -0
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +37 -0
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +49 -0
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +31 -0
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +113 -0
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +57 -0
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +53 -0
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +32 -0
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +49 -0
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +31 -0
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +26 -0
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +6 -0
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +47 -0
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +32 -0
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +53 -0
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +32 -0
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +77 -0
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +42 -0
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +59 -0
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +33 -0
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +53 -0
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +32 -0
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +48 -0
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +24 -0
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +77 -0
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +42 -0
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +77 -0
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +42 -0
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +209 -0
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +97 -0
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +66 -0
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +36 -0
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +48 -0
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +24 -0
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +49 -0
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +23 -0
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +40 -0
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +22 -0
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +53 -0
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +32 -0
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +49 -0
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +31 -0
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +26 -0
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +6 -0
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +121 -0
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +29 -0
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +59 -0
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +37 -0
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +53 -0
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +32 -0
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +101 -0
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +52 -0
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +77 -0
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +42 -0
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +53 -0
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +32 -0
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +25 -0
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +15 -0
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +101 -0
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +44 -0
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +49 -0
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +31 -0
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +65 -0
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +37 -0
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +22 -0
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +5 -0
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +53 -0
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +32 -0
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +49 -0
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +31 -0
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +25 -0
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +15 -0
- package/.trajectories/completed/2026-01/traj_multi_server_arch.md +101 -0
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +53 -0
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +32 -0
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +53 -0
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +32 -0
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +48 -0
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +24 -0
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +53 -0
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +32 -0
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +27 -0
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +14 -0
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +77 -0
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +42 -0
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +53 -0
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +32 -0
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +83 -0
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +47 -0
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +59 -0
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +37 -0
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +48 -0
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +16 -0
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +59 -0
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +37 -0
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +53 -0
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +32 -0
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +84 -0
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +109 -0
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +53 -0
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +32 -0
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +53 -0
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +32 -0
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +186 -0
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +86 -0
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +77 -0
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +42 -0
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +71 -0
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +42 -0
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +89 -0
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +47 -0
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +53 -0
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +32 -0
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +20 -0
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +6 -0
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +175 -0
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +82 -0
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +65 -0
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +37 -0
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +31 -0
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +47 -0
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +32 -0
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +59 -0
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +37 -0
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +53 -0
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +32 -0
- package/.trajectories/consolidate-settings-panel.md +24 -0
- package/.trajectories/gh-cli-user-token.md +26 -0
- package/.trajectories/index.json +468 -0
- package/ARCHITECTURE.md +1245 -0
- package/TESTING.md +278 -0
- package/deploy/init-db.sql +5 -0
- package/deploy/scripts/setup-fly-workspaces.sh +69 -0
- package/deploy/scripts/setup-railway.sh +75 -0
- package/deploy/workspace/codex.config.toml +15 -0
- package/deploy/workspace/entrypoint-browser.sh +118 -0
- package/deploy/workspace/entrypoint.sh +508 -0
- package/deploy/workspace/git-credential-relay +126 -0
- package/dist/bridge/spawner.d.ts +7 -0
- package/dist/bridge/spawner.js +40 -9
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.js +260 -1
- package/dist/cloud/api/admin.d.ts +8 -0
- package/dist/cloud/api/admin.js +212 -0
- package/dist/cloud/api/auth.js +8 -0
- package/dist/cloud/api/billing.d.ts +0 -10
- package/dist/cloud/api/billing.js +278 -67
- package/dist/cloud/api/codex-auth-helper.d.ts +21 -0
- package/dist/cloud/api/codex-auth-helper.js +307 -0
- package/dist/cloud/api/coordinators.js +402 -0
- package/dist/cloud/api/daemons.js +15 -11
- package/dist/cloud/api/git.js +127 -19
- package/dist/cloud/api/github-app.js +42 -8
- package/dist/cloud/api/nango-auth.js +297 -16
- package/dist/cloud/api/onboarding.js +112 -35
- package/dist/cloud/api/providers.js +12 -16
- package/dist/cloud/api/repos.d.ts +1 -0
- package/dist/cloud/api/repos.js +311 -49
- package/dist/cloud/api/test-helpers.js +40 -0
- package/dist/cloud/api/usage.js +13 -0
- package/dist/cloud/api/webhooks.d.ts +1 -0
- package/dist/cloud/api/webhooks.js +149 -0
- package/dist/cloud/api/workspaces.d.ts +18 -0
- package/dist/cloud/api/workspaces.js +1042 -21
- package/dist/cloud/billing/plans.js +19 -19
- package/dist/cloud/config.d.ts +8 -0
- package/dist/cloud/config.js +15 -0
- package/dist/cloud/db/drizzle.d.ts +5 -2
- package/dist/cloud/db/drizzle.js +27 -20
- package/dist/cloud/db/schema.d.ts +19 -51
- package/dist/cloud/db/schema.js +5 -4
- package/dist/cloud/index.d.ts +0 -1
- package/dist/cloud/index.js +0 -1
- package/dist/cloud/provisioner/index.d.ts +125 -1
- package/dist/cloud/provisioner/index.js +939 -53
- package/dist/cloud/server.js +161 -16
- package/dist/cloud/services/compute-enforcement.d.ts +57 -0
- package/dist/cloud/services/compute-enforcement.js +175 -0
- package/dist/cloud/services/index.d.ts +2 -0
- package/dist/cloud/services/index.js +4 -0
- package/dist/cloud/services/intro-expiration.d.ts +55 -0
- package/dist/cloud/services/intro-expiration.js +211 -0
- package/dist/cloud/services/nango.d.ts +74 -0
- package/dist/cloud/services/nango.js +218 -5
- package/dist/cloud/services/planLimits.d.ts +22 -0
- package/dist/cloud/services/planLimits.js +58 -5
- package/dist/cloud/services/ssh-security.d.ts +31 -0
- package/dist/cloud/services/ssh-security.js +63 -0
- package/dist/continuity/manager.d.ts +5 -0
- package/dist/continuity/manager.js +56 -2
- package/dist/daemon/api.d.ts +2 -0
- package/dist/daemon/api.js +214 -5
- package/dist/daemon/cli-auth.d.ts +13 -1
- package/dist/daemon/cli-auth.js +166 -47
- package/dist/daemon/connection.d.ts +7 -1
- package/dist/daemon/connection.js +15 -0
- package/dist/daemon/orchestrator.d.ts +2 -0
- package/dist/daemon/orchestrator.js +26 -0
- package/dist/daemon/repo-manager.d.ts +116 -0
- package/dist/daemon/repo-manager.js +384 -0
- package/dist/daemon/router.d.ts +60 -1
- package/dist/daemon/router.js +281 -20
- package/dist/daemon/user-directory.d.ts +111 -0
- package/dist/daemon/user-directory.js +233 -0
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
- package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/{page-daf87e86f783f980.js → page-4a5938c18a11a654.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/{main-97850e03d723ea8c.js → main-2ee6beb2ae96d210.js} +1 -1
- package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +1 -0
- package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +1 -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 -1
- package/dist/dashboard/out/app.txt +3 -3
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +3 -3
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +3 -3
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +3 -3
- package/dist/dashboard/out/login.html +2 -2
- package/dist/dashboard/out/login.txt +3 -3
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +3 -3
- package/dist/dashboard/out/pricing.html +3 -3
- package/dist/dashboard/out/pricing.txt +3 -3
- 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 -1
- package/dist/dashboard/out/providers.txt +3 -3
- package/dist/dashboard/out/signup.html +2 -2
- package/dist/dashboard/out/signup.txt +3 -3
- package/dist/dashboard-server/server.js +316 -12
- package/dist/dashboard-server/user-bridge.d.ts +103 -0
- package/dist/dashboard-server/user-bridge.js +189 -0
- package/dist/protocol/channels.d.ts +205 -0
- package/dist/protocol/channels.js +154 -0
- package/dist/protocol/types.d.ts +13 -1
- package/dist/resiliency/provider-context.js +2 -0
- package/dist/shared/cli-auth-config.d.ts +19 -0
- package/dist/shared/cli-auth-config.js +58 -2
- package/dist/utils/agent-config.js +1 -1
- package/dist/wrapper/auth-detection.d.ts +49 -0
- package/dist/wrapper/auth-detection.js +192 -0
- package/dist/wrapper/base-wrapper.d.ts +153 -0
- package/dist/wrapper/base-wrapper.js +393 -0
- package/dist/wrapper/client.d.ts +7 -1
- package/dist/wrapper/client.js +3 -0
- package/dist/wrapper/index.d.ts +1 -0
- package/dist/wrapper/index.js +4 -3
- package/dist/wrapper/pty-wrapper.d.ts +62 -84
- package/dist/wrapper/pty-wrapper.js +154 -180
- package/dist/wrapper/tmux-wrapper.d.ts +41 -66
- package/dist/wrapper/tmux-wrapper.js +90 -134
- package/package.json +5 -12
- package/scripts/postinstall.js +11 -155
- package/scripts/test-interactive-terminal.sh +248 -0
- package/test-push.txt +1 -0
- package/bin/tmux +0 -0
- package/dist/bridge/config.d.ts.map +0 -1
- package/dist/bridge/config.js.map +0 -1
- package/dist/bridge/index.d.ts.map +0 -1
- package/dist/bridge/index.js.map +0 -1
- package/dist/bridge/multi-project-client.d.ts.map +0 -1
- package/dist/bridge/multi-project-client.js.map +0 -1
- package/dist/bridge/shadow-cli.d.ts.map +0 -1
- package/dist/bridge/shadow-cli.js.map +0 -1
- package/dist/bridge/shadow-config.d.ts.map +0 -1
- package/dist/bridge/shadow-config.js.map +0 -1
- package/dist/bridge/spawner.d.ts.map +0 -1
- package/dist/bridge/spawner.js.map +0 -1
- package/dist/bridge/teams-config.d.ts.map +0 -1
- package/dist/bridge/teams-config.js.map +0 -1
- package/dist/bridge/types.d.ts.map +0 -1
- package/dist/bridge/types.js.map +0 -1
- package/dist/bridge/utils.d.ts.map +0 -1
- package/dist/bridge/utils.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cloud/api/auth.d.ts.map +0 -1
- package/dist/cloud/api/auth.js.map +0 -1
- package/dist/cloud/api/billing.d.ts.map +0 -1
- package/dist/cloud/api/billing.js.map +0 -1
- package/dist/cloud/api/cli-pty-runner.d.ts.map +0 -1
- package/dist/cloud/api/cli-pty-runner.js.map +0 -1
- package/dist/cloud/api/coordinators.d.ts.map +0 -1
- package/dist/cloud/api/coordinators.js.map +0 -1
- package/dist/cloud/api/daemons.d.ts.map +0 -1
- package/dist/cloud/api/daemons.js.map +0 -1
- package/dist/cloud/api/generic-webhooks.d.ts.map +0 -1
- package/dist/cloud/api/generic-webhooks.js.map +0 -1
- package/dist/cloud/api/git.d.ts.map +0 -1
- package/dist/cloud/api/git.js.map +0 -1
- package/dist/cloud/api/github-app.d.ts.map +0 -1
- package/dist/cloud/api/github-app.js.map +0 -1
- package/dist/cloud/api/middleware/planLimits.d.ts.map +0 -1
- package/dist/cloud/api/middleware/planLimits.js.map +0 -1
- package/dist/cloud/api/monitoring.d.ts.map +0 -1
- package/dist/cloud/api/monitoring.js.map +0 -1
- package/dist/cloud/api/nango-auth.d.ts.map +0 -1
- package/dist/cloud/api/nango-auth.js.map +0 -1
- package/dist/cloud/api/onboarding.d.ts.map +0 -1
- package/dist/cloud/api/onboarding.js.map +0 -1
- package/dist/cloud/api/policy.d.ts.map +0 -1
- package/dist/cloud/api/policy.js.map +0 -1
- package/dist/cloud/api/providers.d.ts.map +0 -1
- package/dist/cloud/api/providers.js.map +0 -1
- package/dist/cloud/api/repos.d.ts.map +0 -1
- package/dist/cloud/api/repos.js.map +0 -1
- package/dist/cloud/api/teams.d.ts.map +0 -1
- package/dist/cloud/api/teams.js.map +0 -1
- package/dist/cloud/api/test-helpers.d.ts.map +0 -1
- package/dist/cloud/api/test-helpers.js.map +0 -1
- package/dist/cloud/api/usage.d.ts.map +0 -1
- package/dist/cloud/api/usage.js.map +0 -1
- package/dist/cloud/api/webhooks.d.ts.map +0 -1
- package/dist/cloud/api/webhooks.js.map +0 -1
- package/dist/cloud/api/workspaces.d.ts.map +0 -1
- package/dist/cloud/api/workspaces.js.map +0 -1
- package/dist/cloud/billing/index.d.ts.map +0 -1
- package/dist/cloud/billing/index.js.map +0 -1
- package/dist/cloud/billing/plans.d.ts.map +0 -1
- package/dist/cloud/billing/plans.js.map +0 -1
- package/dist/cloud/billing/service.d.ts.map +0 -1
- package/dist/cloud/billing/service.js.map +0 -1
- package/dist/cloud/billing/types.d.ts.map +0 -1
- package/dist/cloud/billing/types.js.map +0 -1
- package/dist/cloud/config.d.ts.map +0 -1
- package/dist/cloud/config.js.map +0 -1
- package/dist/cloud/db/drizzle.d.ts.map +0 -1
- package/dist/cloud/db/drizzle.js.map +0 -1
- package/dist/cloud/db/index.d.ts.map +0 -1
- package/dist/cloud/db/index.js.map +0 -1
- package/dist/cloud/db/schema.d.ts.map +0 -1
- package/dist/cloud/db/schema.js.map +0 -1
- package/dist/cloud/index.d.ts.map +0 -1
- package/dist/cloud/index.js.map +0 -1
- package/dist/cloud/provisioner/index.d.ts.map +0 -1
- package/dist/cloud/provisioner/index.js.map +0 -1
- package/dist/cloud/server.d.ts.map +0 -1
- package/dist/cloud/server.js.map +0 -1
- package/dist/cloud/services/auto-scaler.d.ts.map +0 -1
- package/dist/cloud/services/auto-scaler.js.map +0 -1
- package/dist/cloud/services/capacity-manager.d.ts.map +0 -1
- package/dist/cloud/services/capacity-manager.js.map +0 -1
- package/dist/cloud/services/ci-agent-spawner.d.ts.map +0 -1
- package/dist/cloud/services/ci-agent-spawner.js.map +0 -1
- package/dist/cloud/services/coordinator.d.ts.map +0 -1
- package/dist/cloud/services/coordinator.js.map +0 -1
- package/dist/cloud/services/index.d.ts.map +0 -1
- package/dist/cloud/services/index.js.map +0 -1
- package/dist/cloud/services/mention-handler.d.ts.map +0 -1
- package/dist/cloud/services/mention-handler.js.map +0 -1
- package/dist/cloud/services/nango.d.ts.map +0 -1
- package/dist/cloud/services/nango.js.map +0 -1
- package/dist/cloud/services/persistence.d.ts.map +0 -1
- package/dist/cloud/services/persistence.js.map +0 -1
- package/dist/cloud/services/planLimits.d.ts.map +0 -1
- package/dist/cloud/services/planLimits.js.map +0 -1
- package/dist/cloud/services/scaling-orchestrator.d.ts.map +0 -1
- package/dist/cloud/services/scaling-orchestrator.js.map +0 -1
- package/dist/cloud/services/scaling-policy.d.ts.map +0 -1
- package/dist/cloud/services/scaling-policy.js.map +0 -1
- package/dist/cloud/vault/index.d.ts +0 -76
- package/dist/cloud/vault/index.d.ts.map +0 -1
- package/dist/cloud/vault/index.js +0 -219
- package/dist/cloud/vault/index.js.map +0 -1
- package/dist/cloud/webhooks/index.d.ts.map +0 -1
- package/dist/cloud/webhooks/index.js.map +0 -1
- package/dist/cloud/webhooks/parsers/github.d.ts.map +0 -1
- package/dist/cloud/webhooks/parsers/github.js.map +0 -1
- package/dist/cloud/webhooks/parsers/index.d.ts.map +0 -1
- package/dist/cloud/webhooks/parsers/index.js.map +0 -1
- package/dist/cloud/webhooks/parsers/linear.d.ts.map +0 -1
- package/dist/cloud/webhooks/parsers/linear.js.map +0 -1
- package/dist/cloud/webhooks/parsers/slack.d.ts.map +0 -1
- package/dist/cloud/webhooks/parsers/slack.js.map +0 -1
- package/dist/cloud/webhooks/responders/github.d.ts.map +0 -1
- package/dist/cloud/webhooks/responders/github.js.map +0 -1
- package/dist/cloud/webhooks/responders/index.d.ts.map +0 -1
- package/dist/cloud/webhooks/responders/index.js.map +0 -1
- package/dist/cloud/webhooks/responders/linear.d.ts.map +0 -1
- package/dist/cloud/webhooks/responders/linear.js.map +0 -1
- package/dist/cloud/webhooks/responders/slack.d.ts.map +0 -1
- package/dist/cloud/webhooks/responders/slack.js.map +0 -1
- package/dist/cloud/webhooks/router.d.ts.map +0 -1
- package/dist/cloud/webhooks/router.js.map +0 -1
- package/dist/cloud/webhooks/rules-engine.d.ts.map +0 -1
- package/dist/cloud/webhooks/rules-engine.js.map +0 -1
- package/dist/cloud/webhooks/types.d.ts.map +0 -1
- package/dist/cloud/webhooks/types.js.map +0 -1
- package/dist/continuity/formatter.d.ts.map +0 -1
- package/dist/continuity/formatter.js.map +0 -1
- package/dist/continuity/handoff-store.d.ts.map +0 -1
- package/dist/continuity/handoff-store.js.map +0 -1
- package/dist/continuity/index.d.ts.map +0 -1
- package/dist/continuity/index.js.map +0 -1
- package/dist/continuity/ledger-store.d.ts.map +0 -1
- package/dist/continuity/ledger-store.js.map +0 -1
- package/dist/continuity/manager.d.ts.map +0 -1
- package/dist/continuity/manager.js.map +0 -1
- package/dist/continuity/parser.d.ts.map +0 -1
- package/dist/continuity/parser.js.map +0 -1
- package/dist/continuity/types.d.ts.map +0 -1
- package/dist/continuity/types.js.map +0 -1
- package/dist/daemon/agent-manager.d.ts.map +0 -1
- package/dist/daemon/agent-manager.js.map +0 -1
- package/dist/daemon/agent-registry.d.ts.map +0 -1
- package/dist/daemon/agent-registry.js.map +0 -1
- package/dist/daemon/api.d.ts.map +0 -1
- package/dist/daemon/api.js.map +0 -1
- package/dist/daemon/auth.d.ts.map +0 -1
- package/dist/daemon/auth.js.map +0 -1
- package/dist/daemon/cli-auth.d.ts.map +0 -1
- package/dist/daemon/cli-auth.js.map +0 -1
- package/dist/daemon/cloud-sync.d.ts.map +0 -1
- package/dist/daemon/cloud-sync.js.map +0 -1
- package/dist/daemon/connection.d.ts.map +0 -1
- package/dist/daemon/connection.js.map +0 -1
- package/dist/daemon/index.d.ts.map +0 -1
- package/dist/daemon/index.js.map +0 -1
- package/dist/daemon/orchestrator.d.ts.map +0 -1
- package/dist/daemon/orchestrator.js.map +0 -1
- package/dist/daemon/registry.d.ts.map +0 -1
- package/dist/daemon/registry.js.map +0 -1
- package/dist/daemon/router.d.ts.map +0 -1
- package/dist/daemon/router.js.map +0 -1
- package/dist/daemon/server.d.ts.map +0 -1
- package/dist/daemon/server.js.map +0 -1
- package/dist/daemon/services/browser-testing.d.ts.map +0 -1
- package/dist/daemon/services/browser-testing.js.map +0 -1
- package/dist/daemon/services/container-spawner.d.ts.map +0 -1
- package/dist/daemon/services/container-spawner.js.map +0 -1
- package/dist/daemon/types.d.ts.map +0 -1
- package/dist/daemon/types.js.map +0 -1
- package/dist/daemon/workspace-manager.d.ts.map +0 -1
- package/dist/daemon/workspace-manager.js.map +0 -1
- package/dist/dashboard/out/_next/static/H5aWG0udPB4iOUIl_gytz/_ssgManifest.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/480-2d4111711d4e473c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/724-73c1ee5f60abe860.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/766-c3a14283c88d815b.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-7120be68bea622f3.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-1081dd190a331a91.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-b68a681526eb145e.js +0 -1
- package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +0 -1
- package/dist/dashboard/out/_next/static/css/411ce23ffeae9f76.css +0 -1
- package/dist/dashboard-server/metrics.d.ts.map +0 -1
- package/dist/dashboard-server/metrics.js.map +0 -1
- package/dist/dashboard-server/needs-attention.d.ts.map +0 -1
- package/dist/dashboard-server/needs-attention.js.map +0 -1
- package/dist/dashboard-server/server.d.ts.map +0 -1
- package/dist/dashboard-server/server.js.map +0 -1
- package/dist/dashboard-server/start.d.ts.map +0 -1
- package/dist/dashboard-server/start.js.map +0 -1
- package/dist/hooks/emitter.d.ts.map +0 -1
- package/dist/hooks/emitter.js.map +0 -1
- package/dist/hooks/inbox-check/hook.d.ts.map +0 -1
- package/dist/hooks/inbox-check/hook.js.map +0 -1
- package/dist/hooks/inbox-check/index.d.ts.map +0 -1
- package/dist/hooks/inbox-check/index.js.map +0 -1
- package/dist/hooks/inbox-check/types.d.ts.map +0 -1
- package/dist/hooks/inbox-check/types.js.map +0 -1
- package/dist/hooks/inbox-check/utils.d.ts.map +0 -1
- package/dist/hooks/inbox-check/utils.js.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/registry.d.ts.map +0 -1
- package/dist/hooks/registry.js.map +0 -1
- package/dist/hooks/trajectory-hooks.d.ts.map +0 -1
- package/dist/hooks/trajectory-hooks.js.map +0 -1
- package/dist/hooks/types.d.ts.map +0 -1
- package/dist/hooks/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/memory/adapters/index.d.ts.map +0 -1
- package/dist/memory/adapters/index.js.map +0 -1
- package/dist/memory/adapters/inmemory.d.ts.map +0 -1
- package/dist/memory/adapters/inmemory.js.map +0 -1
- package/dist/memory/adapters/supermemory.d.ts.map +0 -1
- package/dist/memory/adapters/supermemory.js.map +0 -1
- package/dist/memory/factory.d.ts.map +0 -1
- package/dist/memory/factory.js.map +0 -1
- package/dist/memory/index.d.ts.map +0 -1
- package/dist/memory/index.js.map +0 -1
- package/dist/memory/memory-hooks.d.ts.map +0 -1
- package/dist/memory/memory-hooks.js.map +0 -1
- package/dist/memory/service.d.ts.map +0 -1
- package/dist/memory/service.js.map +0 -1
- package/dist/memory/types.d.ts.map +0 -1
- package/dist/memory/types.js.map +0 -1
- package/dist/policy/agent-policy.d.ts.map +0 -1
- package/dist/policy/agent-policy.js.map +0 -1
- package/dist/policy/cloud-policy-fetcher.d.ts.map +0 -1
- package/dist/policy/cloud-policy-fetcher.js.map +0 -1
- package/dist/protocol/framing.d.ts.map +0 -1
- package/dist/protocol/framing.js.map +0 -1
- package/dist/protocol/index.d.ts.map +0 -1
- package/dist/protocol/index.js.map +0 -1
- package/dist/protocol/types.d.ts.map +0 -1
- package/dist/protocol/types.js.map +0 -1
- package/dist/resiliency/context-persistence.d.ts.map +0 -1
- package/dist/resiliency/context-persistence.js.map +0 -1
- package/dist/resiliency/crash-insights.d.ts.map +0 -1
- package/dist/resiliency/crash-insights.js.map +0 -1
- package/dist/resiliency/gossip-health.d.ts.map +0 -1
- package/dist/resiliency/gossip-health.js.map +0 -1
- package/dist/resiliency/health-monitor.d.ts.map +0 -1
- package/dist/resiliency/health-monitor.js.map +0 -1
- package/dist/resiliency/index.d.ts.map +0 -1
- package/dist/resiliency/index.js.map +0 -1
- package/dist/resiliency/leader-watchdog.d.ts.map +0 -1
- package/dist/resiliency/leader-watchdog.js.map +0 -1
- package/dist/resiliency/logger.d.ts.map +0 -1
- package/dist/resiliency/logger.js.map +0 -1
- package/dist/resiliency/memory-monitor.d.ts.map +0 -1
- package/dist/resiliency/memory-monitor.js.map +0 -1
- package/dist/resiliency/metrics.d.ts.map +0 -1
- package/dist/resiliency/metrics.js.map +0 -1
- package/dist/resiliency/provider-context.d.ts.map +0 -1
- package/dist/resiliency/provider-context.js.map +0 -1
- package/dist/resiliency/stateless-lead.d.ts.map +0 -1
- package/dist/resiliency/stateless-lead.js.map +0 -1
- package/dist/resiliency/supervisor.d.ts.map +0 -1
- package/dist/resiliency/supervisor.js.map +0 -1
- package/dist/shared/cli-auth-config.d.ts.map +0 -1
- package/dist/shared/cli-auth-config.js.map +0 -1
- package/dist/state/agent-state.d.ts.map +0 -1
- package/dist/state/agent-state.js.map +0 -1
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/sqlite-adapter.d.ts.map +0 -1
- package/dist/storage/sqlite-adapter.js.map +0 -1
- package/dist/trajectory/config.d.ts.map +0 -1
- package/dist/trajectory/config.js.map +0 -1
- package/dist/trajectory/index.d.ts.map +0 -1
- package/dist/trajectory/index.js.map +0 -1
- package/dist/trajectory/integration.d.ts.map +0 -1
- package/dist/trajectory/integration.js.map +0 -1
- package/dist/utils/agent-config.d.ts.map +0 -1
- package/dist/utils/agent-config.js.map +0 -1
- package/dist/utils/command-resolver.d.ts.map +0 -1
- package/dist/utils/command-resolver.js.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/name-generator.d.ts.map +0 -1
- package/dist/utils/name-generator.js.map +0 -1
- package/dist/utils/project-namespace.d.ts.map +0 -1
- package/dist/utils/project-namespace.js.map +0 -1
- package/dist/utils/tmux-resolver.d.ts.map +0 -1
- package/dist/utils/tmux-resolver.js.map +0 -1
- package/dist/utils/update-checker.d.ts.map +0 -1
- package/dist/utils/update-checker.js.map +0 -1
- package/dist/wrapper/client.d.ts.map +0 -1
- package/dist/wrapper/client.js.map +0 -1
- package/dist/wrapper/inbox.d.ts.map +0 -1
- package/dist/wrapper/inbox.js.map +0 -1
- package/dist/wrapper/index.d.ts.map +0 -1
- package/dist/wrapper/index.js.map +0 -1
- package/dist/wrapper/parser.d.ts.map +0 -1
- package/dist/wrapper/parser.js.map +0 -1
- package/dist/wrapper/pty-wrapper.d.ts.map +0 -1
- package/dist/wrapper/pty-wrapper.js.map +0 -1
- package/dist/wrapper/shared.d.ts.map +0 -1
- package/dist/wrapper/shared.js.map +0 -1
- package/dist/wrapper/tmux-wrapper.d.ts.map +0 -1
- package/dist/wrapper/tmux-wrapper.js.map +0 -1
- package/docs/AGENTS.md +0 -513
- package/docs/ARCHITECTURE_DECISIONS.md +0 -175
- package/docs/CLOUD-ARCHITECTURE.md +0 -804
- package/docs/CLOUD-ONBOARDING-DESIGN.md +0 -1983
- package/docs/CONTRIBUTING.md +0 -151
- package/docs/HOOKS_API.md +0 -394
- package/docs/INTEGRATION-GUIDE.md +0 -926
- package/docs/PROTOCOL.md +0 -325
- package/docs/WRAPPER_EVENTS.md +0 -358
- package/docs/agent-policy-snippet.md +0 -40
- package/docs/agent-relay-protocol.md +0 -238
- package/docs/agent-relay-snippet.md +0 -174
- package/docs/archive/CHANGELOG.md +0 -11
- package/docs/archive/CLI-SIMPLIFICATION-COMPLETE.md +0 -48
- package/docs/archive/DESIGN_BRIDGE_STAFFING.md +0 -878
- package/docs/archive/DESIGN_V2.md +0 -1079
- package/docs/archive/EXECUTIVE_SUMMARY.md +0 -358
- package/docs/archive/MONETIZATION.md +0 -1679
- package/docs/archive/PROPOSAL-trajectories.md +0 -1582
- package/docs/archive/ROADMAP.md +0 -329
- package/docs/archive/SCALING_ANALYSIS.md +0 -280
- package/docs/archive/TESTING_PRESENCE_FEATURES.md +0 -327
- package/docs/archive/TMUX_IMPLEMENTATION_NOTES.md +0 -364
- package/docs/archive/TMUX_IMPROVEMENTS.md +0 -968
- package/docs/archive/dashboard-v2-plan.md +0 -179
- package/docs/archive/removable-code-analysis.md +0 -24
- package/docs/competitive/GASTOWN.md +0 -451
- package/docs/competitive/MCP_AGENT_MAIL.md +0 -389
- package/docs/competitive/OVERVIEW.md +0 -898
- package/docs/competitive/README.md +0 -34
- package/docs/competitive/TMUX_ORCHESTRATOR.md +0 -605
- package/docs/dashboard.png +0 -0
- package/docs/design/ci-failure-webhooks.md +0 -812
- package/docs/design/comprehensive-integrations.md +0 -238
- package/docs/design/e2b-sandbox-integration.md +0 -504
- package/docs/design/github-app-permissions.md +0 -264
- package/docs/guides/CLOUD.md +0 -236
- package/docs/guides/LOCAL.md +0 -535
- package/docs/guides/SELF-HOSTED.md +0 -494
- package/docs/local-testing.md +0 -428
- package/docs/proposals/continuous-claude-integration.md +0 -622
- package/docs/proposals/custom-commands.md +0 -368
- package/docs/proposals/shadow-as-subagent.md +0 -765
- package/docs/proposals/slack-bot-integration.md +0 -1457
- package/docs/tasks/global-skills-system.tasks.md +0 -230
- package/docs/tasks/webhook-integrations.tasks.md +0 -184
- package/docs/tasks/workspace-capabilities.tasks.md +0 -121
- package/docs/testing/RESILIENCY-TEST-PLAN-2026-01-01.md +0 -366
- package/scripts/cloud-setup.sh +0 -96
- package/scripts/dev/PUBLIC_RELEASE_PLAN.md +0 -88
- package/scripts/dev/dev-team-setup.sh +0 -431
- package/scripts/e2e-test.sh +0 -119
- package/scripts/games/game-protocol.md +0 -79
- package/scripts/games/hearts-setup.sh +0 -264
- package/scripts/manual-qa.sh +0 -293
- package/scripts/run-cloud-qa.sh +0 -220
- package/scripts/test-cli-auth/Dockerfile +0 -44
- package/scripts/test-cli-auth/Dockerfile.real +0 -79
- package/scripts/test-cli-auth/README.md +0 -286
- package/scripts/test-cli-auth/ci-test-real-clis.ts +0 -251
- package/scripts/test-cli-auth/ci-test-runner.ts +0 -263
- package/scripts/test-cli-auth/mock-cli.sh +0 -147
- package/scripts/test-cli-auth/package.json +0 -14
- package/scripts/test-cli-auth/test-oauth-flow.ts +0 -220
- package/scripts/test-pty-input-auto.js +0 -222
- package/scripts/test-pty-input.js +0 -150
- package/scripts/tictactoe-setup.sh +0 -181
- /package/dist/dashboard/out/_next/static/{H5aWG0udPB4iOUIl_gytz → T1tgCqVWHFIkV7ClEtzD7}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{117-b100311aff8d5c61.js → 117-f7b8ab0809342e77.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{648-a13d3c2b1be45466.js → 648-5cc6e1921389a58a.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-a4973f3e3c82fb67.js → page-53b8a69f76db17d0.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/connect-repos/{page-dc2e3a1a22478efc.js → page-f45ecbc3e06134fc.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/history/{page-56a8b4616a90dc43.js → page-8c8bed33beb2bf1c.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/login/{page-3eac37ea6f5dd153.js → page-16f3b49e55b1e0ed.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/pricing/{page-4d72d5a5d8a9b618.js → page-982a7000fee44014.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/signup/{page-fee4ed1709070bcd.js → page-547dd0ca55ecd0ba.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-bf46c09eb57e019c.js → fd9d1056-609918ca7b6280bb.js} +0 -0
package/dist/daemon/router.js
CHANGED
|
@@ -24,6 +24,12 @@ export class Router {
|
|
|
24
24
|
shadowsByPrimary = new Map();
|
|
25
25
|
/** Reverse lookup: shadowAgent -> primaryAgent (for cleanup) */
|
|
26
26
|
primaryByShadow = new Map();
|
|
27
|
+
/** Channel membership: channel -> Set of member names */
|
|
28
|
+
channels = new Map();
|
|
29
|
+
/** User entities (human users, not agents) */
|
|
30
|
+
users = new Map();
|
|
31
|
+
/** Reverse lookup: member name -> Set of channels they're in */
|
|
32
|
+
memberChannels = new Map();
|
|
27
33
|
/** Default timeout for processing indicator (30 seconds) */
|
|
28
34
|
static PROCESSING_TIMEOUT_MS = 30_000;
|
|
29
35
|
/** Callback when processing state changes (for real-time dashboard updates) */
|
|
@@ -47,21 +53,34 @@ export class Router {
|
|
|
47
53
|
register(connection) {
|
|
48
54
|
this.connections.set(connection.id, connection);
|
|
49
55
|
if (connection.agentName) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
const isUser = connection.entityType === 'user';
|
|
57
|
+
if (isUser) {
|
|
58
|
+
// Handle existing user connection with same name (disconnect old)
|
|
59
|
+
const existingUser = this.users.get(connection.agentName);
|
|
60
|
+
if (existingUser && existingUser.id !== connection.id) {
|
|
61
|
+
existingUser.close();
|
|
62
|
+
this.connections.delete(existingUser.id);
|
|
63
|
+
}
|
|
64
|
+
this.users.set(connection.agentName, connection);
|
|
65
|
+
routerLog.info(`User registered: ${connection.agentName}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Handle existing agent connection with same name (disconnect old)
|
|
69
|
+
const existing = this.agents.get(connection.agentName);
|
|
70
|
+
if (existing && existing.id !== connection.id) {
|
|
71
|
+
existing.close();
|
|
72
|
+
this.connections.delete(existing.id);
|
|
73
|
+
}
|
|
74
|
+
this.agents.set(connection.agentName, connection);
|
|
75
|
+
this.registry?.registerOrUpdate({
|
|
76
|
+
name: connection.agentName,
|
|
77
|
+
cli: connection.cli,
|
|
78
|
+
program: connection.program,
|
|
79
|
+
model: connection.model,
|
|
80
|
+
task: connection.task,
|
|
81
|
+
workingDirectory: connection.workingDirectory,
|
|
82
|
+
});
|
|
55
83
|
}
|
|
56
|
-
this.agents.set(connection.agentName, connection);
|
|
57
|
-
this.registry?.registerOrUpdate({
|
|
58
|
-
name: connection.agentName,
|
|
59
|
-
cli: connection.cli,
|
|
60
|
-
program: connection.program,
|
|
61
|
-
model: connection.model,
|
|
62
|
-
task: connection.task,
|
|
63
|
-
workingDirectory: connection.workingDirectory,
|
|
64
|
-
});
|
|
65
84
|
}
|
|
66
85
|
}
|
|
67
86
|
/**
|
|
@@ -70,14 +89,25 @@ export class Router {
|
|
|
70
89
|
unregister(connection) {
|
|
71
90
|
this.connections.delete(connection.id);
|
|
72
91
|
if (connection.agentName) {
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
this.
|
|
92
|
+
const isUser = connection.entityType === 'user';
|
|
93
|
+
if (isUser) {
|
|
94
|
+
const currentUser = this.users.get(connection.agentName);
|
|
95
|
+
if (currentUser?.id === connection.id) {
|
|
96
|
+
this.users.delete(connection.agentName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const current = this.agents.get(connection.agentName);
|
|
101
|
+
if (current?.id === connection.id) {
|
|
102
|
+
this.agents.delete(connection.agentName);
|
|
103
|
+
}
|
|
76
104
|
}
|
|
77
105
|
// Remove from all subscriptions
|
|
78
106
|
for (const subscribers of this.subscriptions.values()) {
|
|
79
107
|
subscribers.delete(connection.agentName);
|
|
80
108
|
}
|
|
109
|
+
// Remove from all channels and notify remaining members
|
|
110
|
+
this.removeFromAllChannels(connection.agentName);
|
|
81
111
|
// Clean up shadow relationships
|
|
82
112
|
this.unbindShadow(connection.agentName);
|
|
83
113
|
// Clear processing state
|
|
@@ -85,6 +115,25 @@ export class Router {
|
|
|
85
115
|
}
|
|
86
116
|
this.clearPendingForConnection(connection.id);
|
|
87
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Remove a member from all channels they're in.
|
|
120
|
+
*/
|
|
121
|
+
removeFromAllChannels(memberName) {
|
|
122
|
+
const memberChannelSet = this.memberChannels.get(memberName);
|
|
123
|
+
if (!memberChannelSet)
|
|
124
|
+
return;
|
|
125
|
+
for (const channelName of memberChannelSet) {
|
|
126
|
+
const members = this.channels.get(channelName);
|
|
127
|
+
if (members) {
|
|
128
|
+
members.delete(memberName);
|
|
129
|
+
// Clean up empty channels
|
|
130
|
+
if (members.size === 0) {
|
|
131
|
+
this.channels.delete(channelName);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
this.memberChannels.delete(memberName);
|
|
136
|
+
}
|
|
88
137
|
/**
|
|
89
138
|
* Subscribe an agent to a topic.
|
|
90
139
|
*/
|
|
@@ -308,7 +357,8 @@ export class Router {
|
|
|
308
357
|
* Send a direct message to a specific agent.
|
|
309
358
|
*/
|
|
310
359
|
sendDirect(from, to, envelope) {
|
|
311
|
-
const target = this.agents.get(to);
|
|
360
|
+
const target = this.agents.get(to) ?? this.users.get(to);
|
|
361
|
+
const isUserTarget = target?.entityType === 'user';
|
|
312
362
|
// If agent not found locally, check if it's on a remote machine
|
|
313
363
|
if (!target) {
|
|
314
364
|
const remoteAgent = this.crossMachineHandler?.isRemoteAgent(to);
|
|
@@ -326,8 +376,10 @@ export class Router {
|
|
|
326
376
|
if (sent) {
|
|
327
377
|
this.trackDelivery(target, deliver);
|
|
328
378
|
this.registry?.recordReceive(to);
|
|
329
|
-
//
|
|
330
|
-
|
|
379
|
+
// Only mark AI agents as processing; humans don't need processing indicators
|
|
380
|
+
if (!isUserTarget) {
|
|
381
|
+
this.setProcessing(to, deliver.id);
|
|
382
|
+
}
|
|
331
383
|
}
|
|
332
384
|
return sent;
|
|
333
385
|
}
|
|
@@ -694,5 +746,214 @@ export class Router {
|
|
|
694
746
|
}
|
|
695
747
|
}
|
|
696
748
|
}
|
|
749
|
+
// ==================== Channel Methods ====================
|
|
750
|
+
/**
|
|
751
|
+
* Handle a CHANNEL_JOIN message.
|
|
752
|
+
* Adds the member to the channel and notifies existing members.
|
|
753
|
+
*/
|
|
754
|
+
handleChannelJoin(connection, envelope) {
|
|
755
|
+
const memberName = connection.agentName;
|
|
756
|
+
if (!memberName) {
|
|
757
|
+
routerLog.warn('CHANNEL_JOIN from connection without name');
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const channel = envelope.payload.channel;
|
|
761
|
+
// Get or create channel
|
|
762
|
+
let members = this.channels.get(channel);
|
|
763
|
+
if (!members) {
|
|
764
|
+
members = new Set();
|
|
765
|
+
this.channels.set(channel, members);
|
|
766
|
+
}
|
|
767
|
+
// Check if already a member
|
|
768
|
+
if (members.has(memberName)) {
|
|
769
|
+
routerLog.debug(`${memberName} already in ${channel}`);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
// Notify existing members about the new joiner
|
|
773
|
+
for (const existingMember of members) {
|
|
774
|
+
const memberConn = this.getConnectionByName(existingMember);
|
|
775
|
+
if (memberConn) {
|
|
776
|
+
const joinNotification = {
|
|
777
|
+
v: PROTOCOL_VERSION,
|
|
778
|
+
type: 'CHANNEL_JOIN',
|
|
779
|
+
id: uuid(),
|
|
780
|
+
ts: Date.now(),
|
|
781
|
+
from: memberName,
|
|
782
|
+
payload: envelope.payload,
|
|
783
|
+
};
|
|
784
|
+
memberConn.send(joinNotification);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
// Add the new member
|
|
788
|
+
members.add(memberName);
|
|
789
|
+
// Track which channels this member is in
|
|
790
|
+
let memberChannelSet = this.memberChannels.get(memberName);
|
|
791
|
+
if (!memberChannelSet) {
|
|
792
|
+
memberChannelSet = new Set();
|
|
793
|
+
this.memberChannels.set(memberName, memberChannelSet);
|
|
794
|
+
}
|
|
795
|
+
memberChannelSet.add(channel);
|
|
796
|
+
routerLog.info(`${memberName} joined ${channel} (${members.size} members)`);
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Handle a CHANNEL_LEAVE message.
|
|
800
|
+
* Removes the member from the channel and notifies remaining members.
|
|
801
|
+
*/
|
|
802
|
+
handleChannelLeave(connection, envelope) {
|
|
803
|
+
const memberName = connection.agentName;
|
|
804
|
+
if (!memberName) {
|
|
805
|
+
routerLog.warn('CHANNEL_LEAVE from connection without name');
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const channel = envelope.payload.channel;
|
|
809
|
+
const members = this.channels.get(channel);
|
|
810
|
+
if (!members || !members.has(memberName)) {
|
|
811
|
+
routerLog.debug(`${memberName} not in ${channel}, ignoring leave`);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
// Remove from channel
|
|
815
|
+
members.delete(memberName);
|
|
816
|
+
// Remove from member's channel list
|
|
817
|
+
const memberChannelSet = this.memberChannels.get(memberName);
|
|
818
|
+
if (memberChannelSet) {
|
|
819
|
+
memberChannelSet.delete(channel);
|
|
820
|
+
if (memberChannelSet.size === 0) {
|
|
821
|
+
this.memberChannels.delete(memberName);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
// Notify remaining members
|
|
825
|
+
for (const remainingMember of members) {
|
|
826
|
+
const memberConn = this.getConnectionByName(remainingMember);
|
|
827
|
+
if (memberConn) {
|
|
828
|
+
const leaveNotification = {
|
|
829
|
+
v: PROTOCOL_VERSION,
|
|
830
|
+
type: 'CHANNEL_LEAVE',
|
|
831
|
+
id: uuid(),
|
|
832
|
+
ts: Date.now(),
|
|
833
|
+
from: memberName,
|
|
834
|
+
payload: envelope.payload,
|
|
835
|
+
};
|
|
836
|
+
memberConn.send(leaveNotification);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
// Clean up empty channels
|
|
840
|
+
if (members.size === 0) {
|
|
841
|
+
this.channels.delete(channel);
|
|
842
|
+
routerLog.debug(`Channel ${channel} deleted (empty)`);
|
|
843
|
+
}
|
|
844
|
+
routerLog.info(`${memberName} left ${channel}`);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Route a channel message to all members except the sender.
|
|
848
|
+
*/
|
|
849
|
+
routeChannelMessage(connection, envelope) {
|
|
850
|
+
const senderName = connection.agentName;
|
|
851
|
+
if (!senderName) {
|
|
852
|
+
routerLog.warn('CHANNEL_MESSAGE from connection without name');
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const channel = envelope.payload.channel;
|
|
856
|
+
const members = this.channels.get(channel);
|
|
857
|
+
if (!members) {
|
|
858
|
+
routerLog.warn(`Message to non-existent channel ${channel}`);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (!members.has(senderName)) {
|
|
862
|
+
routerLog.warn(`${senderName} not a member of ${channel}`);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
// Route to all members except sender
|
|
866
|
+
for (const memberName of members) {
|
|
867
|
+
if (memberName === senderName)
|
|
868
|
+
continue;
|
|
869
|
+
const memberConn = this.getConnectionByName(memberName);
|
|
870
|
+
if (memberConn) {
|
|
871
|
+
const deliverEnvelope = {
|
|
872
|
+
v: PROTOCOL_VERSION,
|
|
873
|
+
type: 'CHANNEL_MESSAGE',
|
|
874
|
+
id: uuid(),
|
|
875
|
+
ts: Date.now(),
|
|
876
|
+
from: senderName,
|
|
877
|
+
payload: envelope.payload,
|
|
878
|
+
};
|
|
879
|
+
memberConn.send(deliverEnvelope);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
// Persist channel message
|
|
883
|
+
this.persistChannelMessage(envelope, senderName);
|
|
884
|
+
routerLog.debug(`${senderName} -> ${channel}: ${envelope.payload.body.substring(0, 50)}`);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Persist a channel message to storage.
|
|
888
|
+
*/
|
|
889
|
+
persistChannelMessage(envelope, from) {
|
|
890
|
+
if (!this.storage)
|
|
891
|
+
return;
|
|
892
|
+
this.storage.saveMessage({
|
|
893
|
+
id: envelope.id,
|
|
894
|
+
ts: envelope.ts,
|
|
895
|
+
from,
|
|
896
|
+
to: envelope.payload.channel, // Channel name as "to"
|
|
897
|
+
topic: undefined,
|
|
898
|
+
kind: 'message',
|
|
899
|
+
body: envelope.payload.body,
|
|
900
|
+
data: {
|
|
901
|
+
...envelope.payload.data,
|
|
902
|
+
_isChannelMessage: true,
|
|
903
|
+
_channel: envelope.payload.channel,
|
|
904
|
+
_mentions: envelope.payload.mentions,
|
|
905
|
+
},
|
|
906
|
+
thread: envelope.payload.thread,
|
|
907
|
+
status: 'unread',
|
|
908
|
+
is_urgent: false,
|
|
909
|
+
is_broadcast: true, // Channel messages are effectively broadcasts
|
|
910
|
+
}).catch((err) => {
|
|
911
|
+
routerLog.error('Failed to persist channel message', { error: String(err) });
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get all members of a channel.
|
|
916
|
+
*/
|
|
917
|
+
getChannelMembers(channel) {
|
|
918
|
+
const members = this.channels.get(channel);
|
|
919
|
+
return members ? Array.from(members) : [];
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Get all channels.
|
|
923
|
+
*/
|
|
924
|
+
getChannels() {
|
|
925
|
+
return Array.from(this.channels.keys());
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get all channels a member is in.
|
|
929
|
+
*/
|
|
930
|
+
getChannelsForMember(memberName) {
|
|
931
|
+
const channels = this.memberChannels.get(memberName);
|
|
932
|
+
return channels ? Array.from(channels) : [];
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Check if a name belongs to a user (not an agent).
|
|
936
|
+
*/
|
|
937
|
+
isUser(name) {
|
|
938
|
+
return this.users.has(name);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Check if a name belongs to an agent (not a user).
|
|
942
|
+
*/
|
|
943
|
+
isAgent(name) {
|
|
944
|
+
return this.agents.has(name);
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Get list of connected user names (human users only).
|
|
948
|
+
*/
|
|
949
|
+
getUsers() {
|
|
950
|
+
return Array.from(this.users.keys());
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Get a connection by name (checks both agents and users).
|
|
954
|
+
*/
|
|
955
|
+
getConnectionByName(name) {
|
|
956
|
+
return this.agents.get(name) ?? this.users.get(name);
|
|
957
|
+
}
|
|
697
958
|
}
|
|
698
959
|
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Directory Service
|
|
3
|
+
*
|
|
4
|
+
* Manages per-user directories on workspace volumes for CLI credential storage.
|
|
5
|
+
* Each user gets their own home directory at /data/users/{userId}/ with
|
|
6
|
+
* provider-specific subdirectories for credentials.
|
|
7
|
+
*
|
|
8
|
+
* Structure:
|
|
9
|
+
* /data/
|
|
10
|
+
* └── users/
|
|
11
|
+
* ├── {userId1}/
|
|
12
|
+
* │ ├── .claude/
|
|
13
|
+
* │ │ └── .credentials.json
|
|
14
|
+
* │ ├── .codex/
|
|
15
|
+
* │ │ └── credentials.json
|
|
16
|
+
* │ └── .config/
|
|
17
|
+
* │ └── gcloud/
|
|
18
|
+
* │ └── application_default_credentials.json
|
|
19
|
+
* └── {userId2}/
|
|
20
|
+
* └── ...
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Service for managing per-user directories on workspace volumes.
|
|
24
|
+
* Enables multi-user credential storage without conflicts.
|
|
25
|
+
*/
|
|
26
|
+
export declare class UserDirectoryService {
|
|
27
|
+
private baseDir;
|
|
28
|
+
private usersDir;
|
|
29
|
+
/**
|
|
30
|
+
* Create a new UserDirectoryService.
|
|
31
|
+
* @param baseDir - Base data directory (e.g., /data)
|
|
32
|
+
*/
|
|
33
|
+
constructor(baseDir: string);
|
|
34
|
+
/**
|
|
35
|
+
* Get the home directory path for a user.
|
|
36
|
+
* Creates the directory if it doesn't exist.
|
|
37
|
+
*
|
|
38
|
+
* @param userId - User ID (UUID or similar)
|
|
39
|
+
* @returns Absolute path to user's home directory
|
|
40
|
+
* @throws Error if userId is invalid
|
|
41
|
+
*/
|
|
42
|
+
getUserHome(userId: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Ensure a provider's credential directory exists for a user.
|
|
45
|
+
*
|
|
46
|
+
* @param userId - User ID
|
|
47
|
+
* @param provider - Provider name (claude, codex, gemini, etc.)
|
|
48
|
+
* @returns Absolute path to provider directory
|
|
49
|
+
*/
|
|
50
|
+
ensureProviderDir(userId: string, provider: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Initialize a complete user environment with all provider directories.
|
|
53
|
+
*
|
|
54
|
+
* @param userId - User ID
|
|
55
|
+
* @returns User's home directory path
|
|
56
|
+
*/
|
|
57
|
+
initializeUserEnvironment(userId: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Get environment variables for spawning an agent with user-specific HOME.
|
|
60
|
+
*
|
|
61
|
+
* @param userId - User ID
|
|
62
|
+
* @returns Environment variables to merge with process.env
|
|
63
|
+
*/
|
|
64
|
+
getUserEnvironment(userId: string): Record<string, string>;
|
|
65
|
+
/**
|
|
66
|
+
* List all user IDs that have directories.
|
|
67
|
+
*
|
|
68
|
+
* @returns Array of user IDs
|
|
69
|
+
*/
|
|
70
|
+
listUsers(): string[];
|
|
71
|
+
/**
|
|
72
|
+
* Check if a user has an existing directory.
|
|
73
|
+
*
|
|
74
|
+
* @param userId - User ID
|
|
75
|
+
* @returns True if directory exists
|
|
76
|
+
*/
|
|
77
|
+
hasUserDirectory(userId: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Get the path to a provider's credentials file for a user.
|
|
80
|
+
*
|
|
81
|
+
* @param userId - User ID
|
|
82
|
+
* @param provider - Provider name
|
|
83
|
+
* @returns Absolute path to credentials file
|
|
84
|
+
*/
|
|
85
|
+
getProviderCredentialPath(userId: string, provider: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Validate a user ID to prevent path traversal and other issues.
|
|
88
|
+
*
|
|
89
|
+
* @param userId - User ID to validate
|
|
90
|
+
* @throws Error if userId is invalid
|
|
91
|
+
*/
|
|
92
|
+
private validateUserId;
|
|
93
|
+
/**
|
|
94
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
95
|
+
*/
|
|
96
|
+
private ensureDirectory;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the default data directory for user directories.
|
|
100
|
+
* Uses AGENT_RELAY_DATA_DIR if set, otherwise /data (for Fly.io volumes).
|
|
101
|
+
*/
|
|
102
|
+
export declare function getDefaultDataDir(): string;
|
|
103
|
+
/**
|
|
104
|
+
* Get the singleton UserDirectoryService instance.
|
|
105
|
+
*/
|
|
106
|
+
export declare function getUserDirectoryService(): UserDirectoryService;
|
|
107
|
+
/**
|
|
108
|
+
* Create a new UserDirectoryService for testing or custom paths.
|
|
109
|
+
*/
|
|
110
|
+
export declare function createUserDirectoryService(baseDir: string): UserDirectoryService;
|
|
111
|
+
//# sourceMappingURL=user-directory.d.ts.map
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Directory Service
|
|
3
|
+
*
|
|
4
|
+
* Manages per-user directories on workspace volumes for CLI credential storage.
|
|
5
|
+
* Each user gets their own home directory at /data/users/{userId}/ with
|
|
6
|
+
* provider-specific subdirectories for credentials.
|
|
7
|
+
*
|
|
8
|
+
* Structure:
|
|
9
|
+
* /data/
|
|
10
|
+
* └── users/
|
|
11
|
+
* ├── {userId1}/
|
|
12
|
+
* │ ├── .claude/
|
|
13
|
+
* │ │ └── .credentials.json
|
|
14
|
+
* │ ├── .codex/
|
|
15
|
+
* │ │ └── credentials.json
|
|
16
|
+
* │ └── .config/
|
|
17
|
+
* │ └── gcloud/
|
|
18
|
+
* │ └── application_default_credentials.json
|
|
19
|
+
* └── {userId2}/
|
|
20
|
+
* └── ...
|
|
21
|
+
*/
|
|
22
|
+
import * as fs from 'fs';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import { createLogger } from '../resiliency/logger.js';
|
|
25
|
+
const logger = createLogger('user-directory');
|
|
26
|
+
const PROVIDER_CONFIGS = {
|
|
27
|
+
claude: {
|
|
28
|
+
dir: '.claude',
|
|
29
|
+
credentialsFile: '.credentials.json',
|
|
30
|
+
},
|
|
31
|
+
codex: {
|
|
32
|
+
dir: '.codex',
|
|
33
|
+
credentialsFile: 'credentials.json',
|
|
34
|
+
},
|
|
35
|
+
gemini: {
|
|
36
|
+
dir: '.config/gcloud',
|
|
37
|
+
credentialsFile: 'application_default_credentials.json',
|
|
38
|
+
},
|
|
39
|
+
opencode: {
|
|
40
|
+
dir: '.opencode',
|
|
41
|
+
credentialsFile: 'credentials.json',
|
|
42
|
+
},
|
|
43
|
+
droid: {
|
|
44
|
+
dir: '.factory',
|
|
45
|
+
credentialsFile: 'credentials.json',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* All supported providers for initialization
|
|
50
|
+
*/
|
|
51
|
+
const ALL_PROVIDERS = Object.keys(PROVIDER_CONFIGS);
|
|
52
|
+
/**
|
|
53
|
+
* Service for managing per-user directories on workspace volumes.
|
|
54
|
+
* Enables multi-user credential storage without conflicts.
|
|
55
|
+
*/
|
|
56
|
+
export class UserDirectoryService {
|
|
57
|
+
baseDir;
|
|
58
|
+
usersDir;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new UserDirectoryService.
|
|
61
|
+
* @param baseDir - Base data directory (e.g., /data)
|
|
62
|
+
*/
|
|
63
|
+
constructor(baseDir) {
|
|
64
|
+
this.baseDir = baseDir;
|
|
65
|
+
this.usersDir = path.join(baseDir, 'users');
|
|
66
|
+
// Ensure users directory exists
|
|
67
|
+
this.ensureDirectory(this.usersDir);
|
|
68
|
+
logger.info(`UserDirectoryService initialized at ${this.usersDir}`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the home directory path for a user.
|
|
72
|
+
* Creates the directory if it doesn't exist.
|
|
73
|
+
*
|
|
74
|
+
* @param userId - User ID (UUID or similar)
|
|
75
|
+
* @returns Absolute path to user's home directory
|
|
76
|
+
* @throws Error if userId is invalid
|
|
77
|
+
*/
|
|
78
|
+
getUserHome(userId) {
|
|
79
|
+
this.validateUserId(userId);
|
|
80
|
+
const userHome = path.join(this.usersDir, userId);
|
|
81
|
+
this.ensureDirectory(userHome);
|
|
82
|
+
return userHome;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Ensure a provider's credential directory exists for a user.
|
|
86
|
+
*
|
|
87
|
+
* @param userId - User ID
|
|
88
|
+
* @param provider - Provider name (claude, codex, gemini, etc.)
|
|
89
|
+
* @returns Absolute path to provider directory
|
|
90
|
+
*/
|
|
91
|
+
ensureProviderDir(userId, provider) {
|
|
92
|
+
this.validateUserId(userId);
|
|
93
|
+
const config = PROVIDER_CONFIGS[provider];
|
|
94
|
+
if (!config) {
|
|
95
|
+
// For unknown providers, use .{provider} directory
|
|
96
|
+
const userHome = this.getUserHome(userId);
|
|
97
|
+
const providerDir = path.join(userHome, `.${provider}`);
|
|
98
|
+
this.ensureDirectory(providerDir);
|
|
99
|
+
return providerDir;
|
|
100
|
+
}
|
|
101
|
+
const userHome = this.getUserHome(userId);
|
|
102
|
+
const providerDir = path.join(userHome, config.dir);
|
|
103
|
+
this.ensureDirectory(providerDir);
|
|
104
|
+
return providerDir;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Initialize a complete user environment with all provider directories.
|
|
108
|
+
*
|
|
109
|
+
* @param userId - User ID
|
|
110
|
+
* @returns User's home directory path
|
|
111
|
+
*/
|
|
112
|
+
initializeUserEnvironment(userId) {
|
|
113
|
+
this.validateUserId(userId);
|
|
114
|
+
const userHome = this.getUserHome(userId);
|
|
115
|
+
// Create all provider directories
|
|
116
|
+
for (const provider of ALL_PROVIDERS) {
|
|
117
|
+
this.ensureProviderDir(userId, provider);
|
|
118
|
+
}
|
|
119
|
+
logger.info(`Initialized user environment for ${userId} at ${userHome}`);
|
|
120
|
+
return userHome;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get environment variables for spawning an agent with user-specific HOME.
|
|
124
|
+
*
|
|
125
|
+
* @param userId - User ID
|
|
126
|
+
* @returns Environment variables to merge with process.env
|
|
127
|
+
*/
|
|
128
|
+
getUserEnvironment(userId) {
|
|
129
|
+
const userHome = this.getUserHome(userId);
|
|
130
|
+
return {
|
|
131
|
+
HOME: userHome,
|
|
132
|
+
XDG_CONFIG_HOME: path.join(userHome, '.config'),
|
|
133
|
+
AGENT_RELAY_USER_ID: userId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* List all user IDs that have directories.
|
|
138
|
+
*
|
|
139
|
+
* @returns Array of user IDs
|
|
140
|
+
*/
|
|
141
|
+
listUsers() {
|
|
142
|
+
try {
|
|
143
|
+
const entries = fs.readdirSync(this.usersDir, { withFileTypes: true });
|
|
144
|
+
return entries
|
|
145
|
+
.filter((entry) => entry.isDirectory())
|
|
146
|
+
.map((entry) => entry.name);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Check if a user has an existing directory.
|
|
154
|
+
*
|
|
155
|
+
* @param userId - User ID
|
|
156
|
+
* @returns True if directory exists
|
|
157
|
+
*/
|
|
158
|
+
hasUserDirectory(userId) {
|
|
159
|
+
const userHome = path.join(this.usersDir, userId);
|
|
160
|
+
return fs.existsSync(userHome);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get the path to a provider's credentials file for a user.
|
|
164
|
+
*
|
|
165
|
+
* @param userId - User ID
|
|
166
|
+
* @param provider - Provider name
|
|
167
|
+
* @returns Absolute path to credentials file
|
|
168
|
+
*/
|
|
169
|
+
getProviderCredentialPath(userId, provider) {
|
|
170
|
+
const config = PROVIDER_CONFIGS[provider];
|
|
171
|
+
if (!config) {
|
|
172
|
+
const userHome = this.getUserHome(userId);
|
|
173
|
+
return path.join(userHome, `.${provider}`, 'credentials.json');
|
|
174
|
+
}
|
|
175
|
+
const providerDir = this.ensureProviderDir(userId, provider);
|
|
176
|
+
return path.join(providerDir, config.credentialsFile);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validate a user ID to prevent path traversal and other issues.
|
|
180
|
+
*
|
|
181
|
+
* @param userId - User ID to validate
|
|
182
|
+
* @throws Error if userId is invalid
|
|
183
|
+
*/
|
|
184
|
+
validateUserId(userId) {
|
|
185
|
+
if (!userId || userId.trim() === '') {
|
|
186
|
+
throw new Error('User ID cannot be empty');
|
|
187
|
+
}
|
|
188
|
+
// Prevent path traversal
|
|
189
|
+
if (userId.includes('..') || userId.includes('/') || userId.includes('\\')) {
|
|
190
|
+
throw new Error('User ID contains invalid characters');
|
|
191
|
+
}
|
|
192
|
+
// Ensure resolved path is within users directory
|
|
193
|
+
const resolved = path.resolve(this.usersDir, userId);
|
|
194
|
+
if (!resolved.startsWith(this.usersDir)) {
|
|
195
|
+
throw new Error('User ID would escape users directory');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
200
|
+
*/
|
|
201
|
+
ensureDirectory(dirPath) {
|
|
202
|
+
if (!fs.existsSync(dirPath)) {
|
|
203
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get the default data directory for user directories.
|
|
209
|
+
* Uses AGENT_RELAY_DATA_DIR if set, otherwise /data (for Fly.io volumes).
|
|
210
|
+
*/
|
|
211
|
+
export function getDefaultDataDir() {
|
|
212
|
+
return process.env.AGENT_RELAY_DATA_DIR || '/data';
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Singleton instance for the workspace.
|
|
216
|
+
*/
|
|
217
|
+
let _instance = null;
|
|
218
|
+
/**
|
|
219
|
+
* Get the singleton UserDirectoryService instance.
|
|
220
|
+
*/
|
|
221
|
+
export function getUserDirectoryService() {
|
|
222
|
+
if (!_instance) {
|
|
223
|
+
_instance = new UserDirectoryService(getDefaultDataDir());
|
|
224
|
+
}
|
|
225
|
+
return _instance;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Create a new UserDirectoryService for testing or custom paths.
|
|
229
|
+
*/
|
|
230
|
+
export function createUserDirectoryService(baseDir) {
|
|
231
|
+
return new UserDirectoryService(baseDir);
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=user-directory.js.map
|