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/cloud/api/repos.js
CHANGED
|
@@ -2,10 +2,62 @@
|
|
|
2
2
|
* Repos API Routes
|
|
3
3
|
*
|
|
4
4
|
* GitHub repository management - list, import, sync.
|
|
5
|
+
* Includes Nango-based GitHub permission checking for dashboard access control.
|
|
5
6
|
*/
|
|
7
|
+
import crypto from 'crypto';
|
|
6
8
|
import { Router } from 'express';
|
|
7
9
|
import { requireAuth } from './auth.js';
|
|
8
10
|
import { db } from '../db/index.js';
|
|
11
|
+
import { nangoService } from '../services/nango.js';
|
|
12
|
+
import { getConfig } from '../config.js';
|
|
13
|
+
/**
|
|
14
|
+
* Generate workspace token for API calls to workspace containers
|
|
15
|
+
*/
|
|
16
|
+
function generateWorkspaceToken(workspaceId) {
|
|
17
|
+
const config = getConfig();
|
|
18
|
+
return crypto
|
|
19
|
+
.createHmac('sha256', config.sessionSecret)
|
|
20
|
+
.update(`workspace:${workspaceId}`)
|
|
21
|
+
.digest('hex');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Call workspace API endpoint
|
|
25
|
+
*/
|
|
26
|
+
async function callWorkspaceApi(publicUrl, workspaceId, method, endpoint, body) {
|
|
27
|
+
const token = generateWorkspaceToken(workspaceId);
|
|
28
|
+
const url = `${publicUrl.replace(/\/$/, '')}${endpoint}`;
|
|
29
|
+
try {
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
method,
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Authorization': `Bearer ${token}`,
|
|
35
|
+
},
|
|
36
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
37
|
+
});
|
|
38
|
+
const data = await response.json().catch((parseError) => {
|
|
39
|
+
console.error('Failed to parse JSON from workspace response', {
|
|
40
|
+
url,
|
|
41
|
+
status: response.status,
|
|
42
|
+
error: parseError instanceof Error ? parseError.message : parseError,
|
|
43
|
+
});
|
|
44
|
+
return null;
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
ok: response.ok,
|
|
48
|
+
status: response.status,
|
|
49
|
+
data,
|
|
50
|
+
error: response.ok ? undefined : (data?.error || `HTTP ${response.status}`),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
status: 0,
|
|
57
|
+
error: err instanceof Error ? err.message : 'Network error',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
9
61
|
export const reposRouter = Router();
|
|
10
62
|
// All routes require authentication
|
|
11
63
|
reposRouter.use(requireAuth);
|
|
@@ -187,6 +239,228 @@ reposRouter.post('/bulk', async (req, res) => {
|
|
|
187
239
|
results,
|
|
188
240
|
});
|
|
189
241
|
});
|
|
242
|
+
/**
|
|
243
|
+
* GET /api/repos/accessible
|
|
244
|
+
* List all GitHub repositories the authenticated user has access to.
|
|
245
|
+
* Uses Nango proxy with user's GitHub OAuth token.
|
|
246
|
+
*/
|
|
247
|
+
reposRouter.get('/accessible', async (req, res) => {
|
|
248
|
+
const userId = req.session.userId;
|
|
249
|
+
const { page, perPage, type, sort } = req.query;
|
|
250
|
+
try {
|
|
251
|
+
// Get user's Nango connection ID
|
|
252
|
+
const user = await db.users.findById(userId);
|
|
253
|
+
if (!user) {
|
|
254
|
+
return res.status(404).json({ error: 'User not found' });
|
|
255
|
+
}
|
|
256
|
+
if (!user.nangoConnectionId) {
|
|
257
|
+
return res.status(400).json({
|
|
258
|
+
error: 'GitHub not connected via Nango',
|
|
259
|
+
code: 'NANGO_NOT_CONNECTED',
|
|
260
|
+
message: 'Please reconnect your GitHub account',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
// List accessible repos via Nango proxy
|
|
264
|
+
const result = await nangoService.listUserAccessibleRepos(user.nangoConnectionId, {
|
|
265
|
+
page: page ? parseInt(page, 10) : undefined,
|
|
266
|
+
perPage: perPage ? Math.min(parseInt(perPage, 10), 100) : undefined,
|
|
267
|
+
type: type,
|
|
268
|
+
sort: sort,
|
|
269
|
+
});
|
|
270
|
+
res.json({
|
|
271
|
+
repositories: result.repositories,
|
|
272
|
+
pagination: {
|
|
273
|
+
page: page ? parseInt(page, 10) : 1,
|
|
274
|
+
perPage: perPage ? Math.min(parseInt(perPage, 10), 100) : 100,
|
|
275
|
+
hasMore: result.hasMore,
|
|
276
|
+
},
|
|
277
|
+
checkedBy: {
|
|
278
|
+
userId: user.id,
|
|
279
|
+
githubUsername: user.githubUsername,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error('Error listing accessible repos:', error);
|
|
285
|
+
res.status(500).json({ error: 'Failed to list accessible repositories' });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
/**
|
|
289
|
+
* GET /api/repos/search
|
|
290
|
+
* Search GitHub repos by name
|
|
291
|
+
*/
|
|
292
|
+
reposRouter.get('/search', async (req, res) => {
|
|
293
|
+
const githubToken = req.session.githubToken;
|
|
294
|
+
const { q } = req.query;
|
|
295
|
+
if (!q || typeof q !== 'string') {
|
|
296
|
+
return res.status(400).json({ error: 'Search query (q) is required' });
|
|
297
|
+
}
|
|
298
|
+
if (!githubToken) {
|
|
299
|
+
return res.status(401).json({ error: 'GitHub not connected' });
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
// Search user's repos
|
|
303
|
+
const response = await fetch(`https://api.github.com/search/repositories?q=${encodeURIComponent(q)}+user:@me&sort=updated&per_page=20`, {
|
|
304
|
+
headers: {
|
|
305
|
+
Authorization: `Bearer ${githubToken}`,
|
|
306
|
+
Accept: 'application/vnd.github.v3+json',
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
throw new Error('GitHub search failed');
|
|
311
|
+
}
|
|
312
|
+
const data = await response.json();
|
|
313
|
+
res.json({
|
|
314
|
+
repositories: data.items.map((r) => ({
|
|
315
|
+
githubId: r.id,
|
|
316
|
+
fullName: r.full_name,
|
|
317
|
+
name: r.name,
|
|
318
|
+
owner: r.owner.login,
|
|
319
|
+
description: r.description,
|
|
320
|
+
defaultBranch: r.default_branch,
|
|
321
|
+
isPrivate: r.private,
|
|
322
|
+
language: r.language,
|
|
323
|
+
})),
|
|
324
|
+
total: data.total_count,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
console.error('Error searching repos:', error);
|
|
329
|
+
res.status(500).json({ error: 'Failed to search repositories' });
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
// ============================================================================
|
|
333
|
+
// Nango-based GitHub Permission APIs (for dashboard access control)
|
|
334
|
+
// ============================================================================
|
|
335
|
+
/**
|
|
336
|
+
* GET /api/repos/check-access/:owner/:repo
|
|
337
|
+
* Check if authenticated user has access to a specific GitHub repository.
|
|
338
|
+
* Uses Nango proxy with user's GitHub OAuth token.
|
|
339
|
+
*
|
|
340
|
+
* Response:
|
|
341
|
+
* - hasAccess: boolean - Whether user can access this repo
|
|
342
|
+
* - permission: 'admin' | 'write' | 'read' | 'none' - User's permission level
|
|
343
|
+
* - repository: Repository details if user has access
|
|
344
|
+
*
|
|
345
|
+
* Use this for dashboard access control - grant access if hasAccess is true.
|
|
346
|
+
*/
|
|
347
|
+
reposRouter.get('/check-access/:owner/:repo', async (req, res) => {
|
|
348
|
+
const userId = req.session.userId;
|
|
349
|
+
const { owner, repo } = req.params;
|
|
350
|
+
if (!owner || !repo) {
|
|
351
|
+
return res.status(400).json({ error: 'Owner and repo parameters are required' });
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
// Get user's Nango connection ID
|
|
355
|
+
const user = await db.users.findById(userId);
|
|
356
|
+
if (!user) {
|
|
357
|
+
return res.status(404).json({ error: 'User not found' });
|
|
358
|
+
}
|
|
359
|
+
if (!user.nangoConnectionId) {
|
|
360
|
+
return res.status(400).json({
|
|
361
|
+
error: 'GitHub not connected via Nango',
|
|
362
|
+
code: 'NANGO_NOT_CONNECTED',
|
|
363
|
+
message: 'Please reconnect your GitHub account',
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
// Check access via Nango proxy
|
|
367
|
+
const accessResult = await nangoService.checkUserRepoAccess(user.nangoConnectionId, owner, repo);
|
|
368
|
+
res.json({
|
|
369
|
+
hasAccess: accessResult.hasAccess,
|
|
370
|
+
permission: accessResult.permission,
|
|
371
|
+
repository: accessResult.repository,
|
|
372
|
+
// Include user info for dashboard context
|
|
373
|
+
checkedBy: {
|
|
374
|
+
userId: user.id,
|
|
375
|
+
githubUsername: user.githubUsername,
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
console.error('Error checking repo access:', error);
|
|
381
|
+
res.status(500).json({ error: 'Failed to check repository access' });
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
/**
|
|
385
|
+
* POST /api/repos/check-access-bulk
|
|
386
|
+
* Check access to multiple repositories at once.
|
|
387
|
+
* Useful for determining which workspaces a user can view.
|
|
388
|
+
*
|
|
389
|
+
* Body:
|
|
390
|
+
* - repositories: Array of "owner/repo" strings
|
|
391
|
+
*
|
|
392
|
+
* Response:
|
|
393
|
+
* - results: Array of { fullName, hasAccess, permission }
|
|
394
|
+
*/
|
|
395
|
+
reposRouter.post('/check-access-bulk', async (req, res) => {
|
|
396
|
+
const userId = req.session.userId;
|
|
397
|
+
const { repositories } = req.body;
|
|
398
|
+
if (!repositories || !Array.isArray(repositories)) {
|
|
399
|
+
return res.status(400).json({ error: 'repositories array is required' });
|
|
400
|
+
}
|
|
401
|
+
if (repositories.length > 50) {
|
|
402
|
+
return res.status(400).json({ error: 'Maximum 50 repositories per request' });
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
// Get user's Nango connection ID
|
|
406
|
+
const user = await db.users.findById(userId);
|
|
407
|
+
if (!user) {
|
|
408
|
+
return res.status(404).json({ error: 'User not found' });
|
|
409
|
+
}
|
|
410
|
+
if (!user.nangoConnectionId) {
|
|
411
|
+
return res.status(400).json({
|
|
412
|
+
error: 'GitHub not connected via Nango',
|
|
413
|
+
code: 'NANGO_NOT_CONNECTED',
|
|
414
|
+
message: 'Please reconnect your GitHub account',
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
// Check access for each repo (in parallel with concurrency limit)
|
|
418
|
+
const results = [];
|
|
419
|
+
// Process in batches of 10 to avoid rate limiting
|
|
420
|
+
const batchSize = 10;
|
|
421
|
+
for (let i = 0; i < repositories.length; i += batchSize) {
|
|
422
|
+
const batch = repositories.slice(i, i + batchSize);
|
|
423
|
+
const batchResults = await Promise.all(batch.map(async (fullName) => {
|
|
424
|
+
try {
|
|
425
|
+
const [owner, repo] = fullName.split('/');
|
|
426
|
+
if (!owner || !repo) {
|
|
427
|
+
return { fullName, hasAccess: false, error: 'Invalid repository format' };
|
|
428
|
+
}
|
|
429
|
+
const accessResult = await nangoService.checkUserRepoAccess(user.nangoConnectionId, owner, repo);
|
|
430
|
+
return {
|
|
431
|
+
fullName,
|
|
432
|
+
hasAccess: accessResult.hasAccess,
|
|
433
|
+
permission: accessResult.permission,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
catch (_err) {
|
|
437
|
+
return { fullName, hasAccess: false, error: 'Check failed' };
|
|
438
|
+
}
|
|
439
|
+
}));
|
|
440
|
+
results.push(...batchResults);
|
|
441
|
+
}
|
|
442
|
+
const accessibleCount = results.filter(r => r.hasAccess).length;
|
|
443
|
+
res.json({
|
|
444
|
+
results,
|
|
445
|
+
summary: {
|
|
446
|
+
total: repositories.length,
|
|
447
|
+
accessible: accessibleCount,
|
|
448
|
+
denied: repositories.length - accessibleCount,
|
|
449
|
+
},
|
|
450
|
+
checkedBy: {
|
|
451
|
+
userId: user.id,
|
|
452
|
+
githubUsername: user.githubUsername,
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
console.error('Error checking bulk repo access:', error);
|
|
458
|
+
res.status(500).json({ error: 'Failed to check repository access' });
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
// ============================================================================
|
|
462
|
+
// WILDCARD ROUTES BELOW - All specific routes must be defined ABOVE this line
|
|
463
|
+
// ============================================================================
|
|
190
464
|
/**
|
|
191
465
|
* GET /api/repos/:id
|
|
192
466
|
* Get repository details
|
|
@@ -219,6 +493,9 @@ reposRouter.get('/:id', async (req, res) => {
|
|
|
219
493
|
/**
|
|
220
494
|
* POST /api/repos/:id/sync
|
|
221
495
|
* Trigger repository sync (clone/pull to workspace)
|
|
496
|
+
*
|
|
497
|
+
* Calls the workspace's /repos/sync API endpoint to clone or update the repo.
|
|
498
|
+
* This enables dynamic repo management without workspace restart.
|
|
222
499
|
*/
|
|
223
500
|
reposRouter.post('/:id/sync', async (req, res) => {
|
|
224
501
|
const userId = req.session.userId;
|
|
@@ -232,14 +509,43 @@ reposRouter.post('/:id/sync', async (req, res) => {
|
|
|
232
509
|
if (!repo.workspaceId) {
|
|
233
510
|
return res.status(400).json({ error: 'Repository not assigned to a workspace' });
|
|
234
511
|
}
|
|
512
|
+
// Get the workspace to find its public URL
|
|
513
|
+
const workspace = await db.workspaces.findById(repo.workspaceId);
|
|
514
|
+
if (!workspace) {
|
|
515
|
+
return res.status(404).json({ error: 'Workspace not found' });
|
|
516
|
+
}
|
|
517
|
+
if (workspace.status !== 'running') {
|
|
518
|
+
return res.status(400).json({
|
|
519
|
+
error: 'Workspace is not running',
|
|
520
|
+
workspaceStatus: workspace.status,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
if (!workspace.publicUrl) {
|
|
524
|
+
return res.status(400).json({ error: 'Workspace has no public URL' });
|
|
525
|
+
}
|
|
235
526
|
// Update sync status
|
|
236
527
|
await db.repositories.updateSyncStatus(id, 'syncing');
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
528
|
+
// Call the workspace's repo sync API
|
|
529
|
+
const result = await callWorkspaceApi(workspace.publicUrl, workspace.id, 'POST', '/repos/sync', { repo: repo.githubFullName });
|
|
530
|
+
if (result.ok) {
|
|
531
|
+
// Update sync status to synced
|
|
240
532
|
await db.repositories.updateSyncStatus(id, 'synced', new Date());
|
|
241
|
-
|
|
242
|
-
|
|
533
|
+
res.json({
|
|
534
|
+
message: 'Repository synced successfully',
|
|
535
|
+
syncStatus: 'synced',
|
|
536
|
+
result: result.data,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
// Update sync status to error
|
|
541
|
+
await db.repositories.updateSyncStatus(id, 'error');
|
|
542
|
+
console.error('Workspace sync failed:', result.error);
|
|
543
|
+
res.status(502).json({
|
|
544
|
+
error: 'Failed to sync repository to workspace',
|
|
545
|
+
details: result.error,
|
|
546
|
+
syncStatus: 'error',
|
|
547
|
+
});
|
|
548
|
+
}
|
|
243
549
|
}
|
|
244
550
|
catch (error) {
|
|
245
551
|
console.error('Error syncing repo:', error);
|
|
@@ -267,48 +573,4 @@ reposRouter.delete('/:id', async (req, res) => {
|
|
|
267
573
|
res.status(500).json({ error: 'Failed to delete repository' });
|
|
268
574
|
}
|
|
269
575
|
});
|
|
270
|
-
/**
|
|
271
|
-
* GET /api/repos/search
|
|
272
|
-
* Search GitHub repos by name
|
|
273
|
-
*/
|
|
274
|
-
reposRouter.get('/search', async (req, res) => {
|
|
275
|
-
const githubToken = req.session.githubToken;
|
|
276
|
-
const { q } = req.query;
|
|
277
|
-
if (!q || typeof q !== 'string') {
|
|
278
|
-
return res.status(400).json({ error: 'Search query (q) is required' });
|
|
279
|
-
}
|
|
280
|
-
if (!githubToken) {
|
|
281
|
-
return res.status(401).json({ error: 'GitHub not connected' });
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
// Search user's repos
|
|
285
|
-
const response = await fetch(`https://api.github.com/search/repositories?q=${encodeURIComponent(q)}+user:@me&sort=updated&per_page=20`, {
|
|
286
|
-
headers: {
|
|
287
|
-
Authorization: `Bearer ${githubToken}`,
|
|
288
|
-
Accept: 'application/vnd.github.v3+json',
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
if (!response.ok) {
|
|
292
|
-
throw new Error('GitHub search failed');
|
|
293
|
-
}
|
|
294
|
-
const data = await response.json();
|
|
295
|
-
res.json({
|
|
296
|
-
repositories: data.items.map((r) => ({
|
|
297
|
-
githubId: r.id,
|
|
298
|
-
fullName: r.full_name,
|
|
299
|
-
name: r.name,
|
|
300
|
-
owner: r.owner.login,
|
|
301
|
-
description: r.description,
|
|
302
|
-
defaultBranch: r.default_branch,
|
|
303
|
-
isPrivate: r.private,
|
|
304
|
-
language: r.language,
|
|
305
|
-
})),
|
|
306
|
-
total: data.total_count,
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
console.error('Error searching repos:', error);
|
|
311
|
-
res.status(500).json({ error: 'Failed to search repositories' });
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
576
|
//# sourceMappingURL=repos.js.map
|
|
@@ -227,6 +227,46 @@ testHelpersRouter.post('/create-mock-repo', async (req, res) => {
|
|
|
227
227
|
res.status(500).json({ error: 'Failed to create mock repo' });
|
|
228
228
|
}
|
|
229
229
|
});
|
|
230
|
+
/**
|
|
231
|
+
* GET /api/test/auto-login
|
|
232
|
+
* Browser-friendly auto-login - visit this URL to login and redirect
|
|
233
|
+
* Usage: /api/test/auto-login?redirect=/providers/setup/claude?workspace=xxx
|
|
234
|
+
*/
|
|
235
|
+
testHelpersRouter.get('/auto-login', async (req, res) => {
|
|
236
|
+
if (!isTestMode) {
|
|
237
|
+
return res.status(403).json({ error: 'Test endpoints disabled in production' });
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const db = getDb();
|
|
241
|
+
const redirect = req.query.redirect || '/app';
|
|
242
|
+
// Find or create test user
|
|
243
|
+
let user;
|
|
244
|
+
const existingUsers = await db.select().from(users).limit(1);
|
|
245
|
+
if (existingUsers.length > 0) {
|
|
246
|
+
user = existingUsers[0];
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const testId = `test-${randomUUID()}`;
|
|
250
|
+
const [newUser] = await db.insert(users).values({
|
|
251
|
+
email: `${testId}@test.local`,
|
|
252
|
+
githubId: testId,
|
|
253
|
+
githubUsername: 'test-user',
|
|
254
|
+
avatarUrl: null,
|
|
255
|
+
plan: 'free',
|
|
256
|
+
}).returning();
|
|
257
|
+
user = newUser;
|
|
258
|
+
}
|
|
259
|
+
// Set session and CSRF token
|
|
260
|
+
req.session.userId = user.id;
|
|
261
|
+
req.session.csrfToken = randomUUID();
|
|
262
|
+
// Redirect to requested page
|
|
263
|
+
res.redirect(redirect);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
console.error('Error in auto-login:', error);
|
|
267
|
+
res.status(500).json({ error: 'Failed to auto-login' });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
230
270
|
/**
|
|
231
271
|
* POST /api/test/login-as
|
|
232
272
|
* Quick login for testing - creates session for existing or new test user
|
package/dist/cloud/api/usage.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { Router } from 'express';
|
|
7
7
|
import { requireAuth } from './auth.js';
|
|
8
8
|
import { getRemainingQuota, getUserUsage, getPlanLimits } from '../services/planLimits.js';
|
|
9
|
+
import { getIntroStatus } from '../services/intro-expiration.js';
|
|
9
10
|
import { db } from '../db/index.js';
|
|
10
11
|
export const usageRouter = Router();
|
|
11
12
|
// All routes require authentication
|
|
@@ -23,6 +24,8 @@ usageRouter.get('/', async (req, res) => {
|
|
|
23
24
|
}
|
|
24
25
|
const plan = user.plan || 'free';
|
|
25
26
|
const quota = await getRemainingQuota(userId);
|
|
27
|
+
// Get intro period status for free tier users
|
|
28
|
+
const introStatus = getIntroStatus(user.createdAt, plan);
|
|
26
29
|
const calcPercent = (current, limit) => limit === Infinity ? 0 : Math.round((current / limit) * 100);
|
|
27
30
|
res.json({
|
|
28
31
|
plan,
|
|
@@ -51,6 +54,16 @@ usageRouter.get('/', async (req, res) => {
|
|
|
51
54
|
concurrentAgents: calcPercent(quota.usage.concurrentAgents, quota.limits.maxConcurrentAgents),
|
|
52
55
|
computeHours: calcPercent(quota.usage.computeHoursThisMonth, quota.limits.maxComputeHoursPerMonth),
|
|
53
56
|
},
|
|
57
|
+
// Intro period bonus for free tier users (2 CPU / 4GB for first 14 days)
|
|
58
|
+
introBonus: {
|
|
59
|
+
isActive: introStatus.isIntroPeriod,
|
|
60
|
+
daysRemaining: introStatus.daysRemaining,
|
|
61
|
+
totalDays: introStatus.introPeriodDays,
|
|
62
|
+
expiresAt: introStatus.expiresAt?.toISOString() || null,
|
|
63
|
+
resources: introStatus.isIntroPeriod
|
|
64
|
+
? { cpus: 2, memoryGb: 4, description: 'Pro-level resources' }
|
|
65
|
+
: { cpus: 1, memoryGb: 2, description: 'Standard free tier' },
|
|
66
|
+
},
|
|
54
67
|
});
|
|
55
68
|
}
|
|
56
69
|
catch (error) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Webhook API Routes
|
|
3
3
|
*
|
|
4
4
|
* Handles GitHub App webhooks for installation events.
|
|
5
|
+
* Also provides workspace webhook forwarding for external integrations.
|
|
5
6
|
*/
|
|
6
7
|
export declare const webhooksRouter: import("express-serve-static-core").Router;
|
|
7
8
|
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -2,12 +2,161 @@
|
|
|
2
2
|
* Webhook API Routes
|
|
3
3
|
*
|
|
4
4
|
* Handles GitHub App webhooks for installation events.
|
|
5
|
+
* Also provides workspace webhook forwarding for external integrations.
|
|
5
6
|
*/
|
|
6
7
|
import { Router } from 'express';
|
|
7
8
|
import crypto from 'crypto';
|
|
8
9
|
import { getConfig } from '../config.js';
|
|
9
10
|
import { db } from '../db/index.js';
|
|
10
11
|
export const webhooksRouter = Router();
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Workspace Webhook Forwarding
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Convert workspace public URL to internal Fly.io URL
|
|
17
|
+
*/
|
|
18
|
+
function getWorkspaceInternalUrl(publicUrl) {
|
|
19
|
+
const isOnFly = !!process.env.FLY_APP_NAME;
|
|
20
|
+
let url = publicUrl.replace(/\/$/, '');
|
|
21
|
+
if (isOnFly && url.includes('.fly.dev')) {
|
|
22
|
+
// Use Fly.io internal networking
|
|
23
|
+
// ar-583f273b.fly.dev -> http://ar-583f273b.internal:3888
|
|
24
|
+
// .internal uses IPv6 and works by default for apps in the same org
|
|
25
|
+
const appName = url.match(/https?:\/\/([^.]+)\.fly\.dev/)?.[1];
|
|
26
|
+
if (appName) {
|
|
27
|
+
url = `http://${appName}.internal:3888`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return url;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Wake a suspended Fly.io workspace machine
|
|
34
|
+
*/
|
|
35
|
+
async function wakeWorkspaceMachine(workspaceId) {
|
|
36
|
+
const workspace = await db.workspaces.findById(workspaceId);
|
|
37
|
+
if (!workspace?.computeId)
|
|
38
|
+
return false;
|
|
39
|
+
const appName = `ar-${workspaceId.substring(0, 8)}`;
|
|
40
|
+
const apiToken = process.env.FLY_API_TOKEN;
|
|
41
|
+
if (!apiToken) {
|
|
42
|
+
console.warn('[webhooks] FLY_API_TOKEN not set, cannot wake machine');
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch(`https://api.machines.dev/v1/apps/${appName}/machines/${workspace.computeId}/start`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { Authorization: `Bearer ${apiToken}` },
|
|
49
|
+
});
|
|
50
|
+
if (response.ok) {
|
|
51
|
+
console.log(`[webhooks] Started workspace machine ${workspace.computeId}`);
|
|
52
|
+
// Wait a bit for machine to start
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// 200 OK means already running
|
|
57
|
+
if (response.status === 200) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
console.warn(`[webhooks] Failed to start machine: ${response.status}`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('[webhooks] Error waking machine:', error);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* POST /api/webhooks/workspace/:workspaceId/*
|
|
70
|
+
* Forward webhooks to a specific workspace
|
|
71
|
+
*
|
|
72
|
+
* External services can send webhooks to:
|
|
73
|
+
* https://agent-relay.com/api/webhooks/workspace/{workspaceId}/your/path
|
|
74
|
+
*
|
|
75
|
+
* This will be forwarded to:
|
|
76
|
+
* http://{workspace-internal}/webhooks/your/path
|
|
77
|
+
*/
|
|
78
|
+
// Handler for workspace webhook forwarding - matches any path under /workspace/:workspaceId
|
|
79
|
+
async function _handleWorkspaceWebhook(req, res) {
|
|
80
|
+
// Extract workspaceId from URL path
|
|
81
|
+
const pathMatch = req.originalUrl.match(/\/workspace\/([^/]+)\/?(.*)$/);
|
|
82
|
+
if (!pathMatch) {
|
|
83
|
+
res.status(400).json({ error: 'Invalid workspace webhook URL' });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const workspaceId = pathMatch[1];
|
|
87
|
+
const forwardPath = pathMatch[2] || '';
|
|
88
|
+
console.log(`[webhooks] Forwarding to workspace ${workspaceId}: ${req.method} /${forwardPath}`);
|
|
89
|
+
try {
|
|
90
|
+
// Find the workspace
|
|
91
|
+
const workspace = await db.workspaces.findById(workspaceId);
|
|
92
|
+
if (!workspace) {
|
|
93
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!workspace.publicUrl) {
|
|
97
|
+
res.status(400).json({ error: 'Workspace has no public URL' });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Try to wake the machine if it might be suspended
|
|
101
|
+
if (workspace.status === 'running' || workspace.status === 'suspended') {
|
|
102
|
+
await wakeWorkspaceMachine(workspaceId);
|
|
103
|
+
}
|
|
104
|
+
// Get internal URL for server-to-server communication
|
|
105
|
+
const internalUrl = getWorkspaceInternalUrl(workspace.publicUrl);
|
|
106
|
+
const targetUrl = `${internalUrl}/webhooks/${forwardPath}`;
|
|
107
|
+
console.log(`[webhooks] Forwarding to: ${targetUrl}`);
|
|
108
|
+
// Forward the request with original headers and body
|
|
109
|
+
const forwardHeaders = {};
|
|
110
|
+
// Copy relevant headers
|
|
111
|
+
const headersToForward = [
|
|
112
|
+
'content-type',
|
|
113
|
+
'x-hub-signature-256',
|
|
114
|
+
'x-hub-signature',
|
|
115
|
+
'x-github-event',
|
|
116
|
+
'x-github-delivery',
|
|
117
|
+
'x-gitlab-token',
|
|
118
|
+
'x-gitlab-event',
|
|
119
|
+
'user-agent',
|
|
120
|
+
];
|
|
121
|
+
for (const header of headersToForward) {
|
|
122
|
+
const value = req.get(header);
|
|
123
|
+
if (value) {
|
|
124
|
+
forwardHeaders[header] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Add workspace context header
|
|
128
|
+
forwardHeaders['x-forwarded-for-workspace'] = workspaceId;
|
|
129
|
+
forwardHeaders['x-original-host'] = req.get('host') || '';
|
|
130
|
+
// Get raw body if available, otherwise use JSON stringified body
|
|
131
|
+
const rawBody = req.rawBody || JSON.stringify(req.body);
|
|
132
|
+
const response = await fetch(targetUrl, {
|
|
133
|
+
method: req.method,
|
|
134
|
+
headers: forwardHeaders,
|
|
135
|
+
body: ['GET', 'HEAD'].includes(req.method) ? undefined : rawBody,
|
|
136
|
+
});
|
|
137
|
+
// Forward response back
|
|
138
|
+
const responseBody = await response.text();
|
|
139
|
+
res.status(response.status);
|
|
140
|
+
// Copy response headers
|
|
141
|
+
response.headers.forEach((value, key) => {
|
|
142
|
+
if (!['content-encoding', 'transfer-encoding', 'content-length'].includes(key.toLowerCase())) {
|
|
143
|
+
res.set(key, value);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
res.send(responseBody);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error(`[webhooks] Error forwarding to workspace ${workspaceId}:`, error);
|
|
150
|
+
res.status(502).json({
|
|
151
|
+
error: 'Failed to forward webhook to workspace',
|
|
152
|
+
details: error.message,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// TODO: Re-enable workspace webhook forwarding once route pattern issue is resolved
|
|
157
|
+
// The newer path-to-regexp version doesn't support wildcard patterns like /workspace/*
|
|
158
|
+
// See: https://git.new/pathToRegexpError
|
|
159
|
+
// webhooksRouter.all('/workspace/*', handleWorkspaceWebhook);
|
|
11
160
|
// GitHub webhook signature verification
|
|
12
161
|
function verifyGitHubSignature(payload, signature) {
|
|
13
162
|
if (!signature)
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
* Workspaces API Routes
|
|
3
3
|
*
|
|
4
4
|
* One-click workspace provisioning and management.
|
|
5
|
+
* Includes auto-access based on GitHub repo permissions.
|
|
5
6
|
*/
|
|
7
|
+
import { Request, Response, NextFunction } from 'express';
|
|
8
|
+
/**
|
|
9
|
+
* Check if user has access to a workspace via:
|
|
10
|
+
* 1. Workspace ownership (userId matches)
|
|
11
|
+
* 2. Explicit workspace_members record
|
|
12
|
+
* 3. GitHub repo access (just-in-time check via Nango)
|
|
13
|
+
*/
|
|
14
|
+
export declare function checkWorkspaceAccess(userId: string, workspaceId: string): Promise<{
|
|
15
|
+
hasAccess: boolean;
|
|
16
|
+
accessType: 'owner' | 'member' | 'contributor' | 'none';
|
|
17
|
+
permission?: 'admin' | 'write' | 'read';
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Middleware to require workspace access.
|
|
21
|
+
* Checks ownership, membership, or GitHub repo access.
|
|
22
|
+
*/
|
|
23
|
+
export declare function requireWorkspaceAccess(req: Request, res: Response, next: NextFunction): void;
|
|
6
24
|
export declare const workspacesRouter: import("express-serve-static-core").Router;
|
|
7
25
|
//# sourceMappingURL=workspaces.d.ts.map
|