@aoagents/ao-web 0.2.4 → 0.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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +296 -223
- package/.next/app-path-routes-manifest.json +16 -8
- package/.next/build-manifest.json +14 -14
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +31 -31
- package/.next/react-loadable-manifest.json +18 -18
- package/.next/required-server-files.json +9 -4
- package/.next/routes-manifest.json +33 -0
- package/.next/server/app/_not-found/page.js +2 -2
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +15 -15
- package/.next/server/app/api/backlog/route.js +1 -1
- package/.next/server/app/api/backlog/route.js.nft.json +1 -1
- package/.next/server/app/api/backlog/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/browse-directory/route.js +1 -0
- package/.next/server/app/api/browse-directory/route.js.nft.json +1 -0
- package/.next/server/app/api/browse-directory/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/events/route.js +3 -3
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/filesystem/browse/route.js +1 -0
- package/.next/server/app/api/filesystem/browse/route.js.nft.json +1 -0
- package/.next/server/app/api/filesystem/browse/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/issues/route.js +1 -1
- package/.next/server/app/api/issues/route.js.nft.json +1 -1
- package/.next/server/app/api/issues/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/observability/route.js +1 -1
- package/.next/server/app/api/observability/route.js.nft.json +1 -1
- package/.next/server/app/api/observability/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/orchestrators/route.js +1 -1
- package/.next/server/app/api/orchestrators/route.js.nft.json +1 -1
- package/.next/server/app/api/orchestrators/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/projects/[id]/route.js +1 -0
- package/.next/server/app/api/projects/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/projects/reload/route.js +1 -0
- package/.next/server/app/api/projects/reload/route.js.nft.json +1 -0
- package/.next/server/app/api/projects/reload/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/projects/route.js +1 -1
- package/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/prs/[id]/merge/route.js +1 -1
- package/.next/server/app/api/prs/[id]/merge/route.js.nft.json +1 -1
- package/.next/server/app/api/prs/[id]/merge/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/runtime/terminal/route.js +1 -1
- package/.next/server/app/api/runtime/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/runtime/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/kill/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/kill/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/message/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/message/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/message/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/remap/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/remap/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/remap/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/restore/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/send/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/patches/route.js +1 -0
- package/.next/server/app/api/sessions/patches/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/patches/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/setup-labels/route.js +1 -1
- package/.next/server/app/api/setup-labels/route.js.nft.json +1 -1
- package/.next/server/app/api/setup-labels/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/spawn/route.js +1 -1
- package/.next/server/app/api/spawn/route.js.nft.json +1 -1
- package/.next/server/app/api/spawn/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verify/route.js +1 -1
- package/.next/server/app/api/verify/route.js.nft.json +1 -1
- package/.next/server/app/api/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/webhooks/[...slug]/route.js +1 -1
- package/.next/server/app/api/webhooks/[...slug]/route.js.nft.json +1 -1
- package/.next/server/app/api/webhooks/[...slug]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/apple-icon/route.js +1 -1
- package/.next/server/app/apple-icon/route.js.nft.json +1 -1
- package/.next/server/app/apple-icon/route_client-reference-manifest.js +1 -1
- package/.next/server/app/dev/terminal-test/page.js +3 -3
- package/.next/server/app/dev/terminal-test/page.js.nft.json +1 -1
- package/.next/server/app/dev/terminal-test/page_client-reference-manifest.js +1 -1
- package/.next/server/app/dev/terminal-test.html +1 -1
- package/.next/server/app/dev/terminal-test.rsc +17 -17
- package/.next/server/app/icon/route.js +1 -1
- package/.next/server/app/icon/route.js.nft.json +1 -1
- package/.next/server/app/icon/route_client-reference-manifest.js +1 -1
- package/.next/server/app/icon-192/route.js +1 -1
- package/.next/server/app/icon-192/route.js.nft.json +1 -1
- package/.next/server/app/icon-192/route_client-reference-manifest.js +1 -1
- package/.next/server/app/icon-512/route.js +1 -1
- package/.next/server/app/icon-512/route.js.nft.json +1 -1
- package/.next/server/app/icon-512/route_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest.body +1 -1
- package/.next/server/app/orchestrators/page.js +2 -2
- package/.next/server/app/orchestrators/page.js.nft.json +1 -1
- package/.next/server/app/orchestrators/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/projects/[projectId]/page.js +2 -0
- package/.next/server/app/projects/[projectId]/page.js.nft.json +1 -0
- package/.next/server/app/projects/[projectId]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/projects/[projectId]/sessions/[id]/page.js +2 -0
- package/.next/server/app/projects/[projectId]/sessions/[id]/page.js.nft.json +1 -0
- package/.next/server/app/projects/[projectId]/sessions/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/projects/[projectId]/settings/page.js +2 -0
- package/.next/server/app/projects/[projectId]/settings/page.js.nft.json +1 -0
- package/.next/server/app/projects/[projectId]/settings/page_client-reference-manifest.js +1 -0
- package/.next/server/app/prs/page.js +2 -2
- package/.next/server/app/prs/page.js.nft.json +1 -1
- package/.next/server/app/prs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/[id]/page.js +2 -12
- package/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/test-direct/page.js +2 -2
- package/.next/server/app/test-direct/page.js.nft.json +1 -1
- package/.next/server/app/test-direct/page_client-reference-manifest.js +1 -1
- package/.next/server/app/test-direct.html +1 -1
- package/.next/server/app/test-direct.rsc +17 -17
- package/.next/server/app-paths-manifest.json +16 -8
- package/.next/server/chunks/1172.js +1 -0
- package/.next/server/chunks/1271.js +1 -0
- package/.next/server/chunks/2106.js +1 -0
- package/.next/server/chunks/252.js +11 -0
- package/.next/server/chunks/2810.js +1 -0
- package/.next/server/chunks/3602.js +382 -0
- package/.next/server/chunks/3667.js +277 -0
- package/.next/server/chunks/3714.js +1 -0
- package/.next/server/chunks/4520.js +1 -0
- package/.next/server/chunks/6086.js +25 -0
- package/.next/server/chunks/6172.js +9 -0
- package/.next/server/chunks/8367.js +3 -0
- package/.next/server/chunks/8386.js +1 -0
- package/.next/server/chunks/8803.js +6 -0
- package/.next/server/chunks/9561.js +22 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_app.js +1 -1
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +1 -1
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js +3 -3
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/webpack-runtime.js +1 -1
- package/.next/static/chunks/1461-af7c54935f21d56d.js +1 -0
- package/.next/static/chunks/1d2d5650.1ef8611b5325bd83.js +18 -0
- package/.next/static/chunks/{9393.acf1934a190d793b.js → 2529.32352c1ce5253e3e.js} +1 -1
- package/.next/static/chunks/3697.4d6d86c5f0caf73e.js +1 -0
- package/.next/static/chunks/4465-aaba60a6355de914.js +1 -0
- package/.next/static/chunks/{7411.ecda44797fb514a0.js → 5491.fd98884d48631149.js} +1 -1
- package/.next/static/chunks/6078.47ce88bee96cfaee.js +1 -0
- package/.next/static/chunks/6607-405ce4d15e595f4a.js +1 -0
- package/.next/static/chunks/7008-71ebb186f0549f41.js +1 -0
- package/.next/static/chunks/7317.685aa5231218e8d3.js +1 -0
- package/.next/static/chunks/8713-d3d663f55dc00e48.js +1 -0
- package/.next/static/chunks/88a6fc35-f836b4b72df5eafa.js +1 -0
- package/.next/static/chunks/9331-fcdd652218ac6f68.js +1 -0
- package/.next/static/chunks/app/_not-found/page-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/backlog/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/browse-directory/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/events/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/filesystem/browse/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/issues/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/observability/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/orchestrators/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/projects/[id]/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/projects/reload/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/projects/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/prs/[id]/merge/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/runtime/terminal/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/kill/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/message/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/remap/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/restore/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/send/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/patches/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/setup-labels/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/spawn/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/verify/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/api/webhooks/[...slug]/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/apple-icon/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/dev/terminal-test/page-5765f0465db56fed.js +1 -0
- package/.next/static/chunks/app/error-670f1d8bf2b6859c.js +1 -0
- package/.next/static/chunks/app/{global-error-7b5c8ae45329c659.js → global-error-ca06d2b1be2d4ae0.js} +1 -1
- package/.next/static/chunks/app/icon/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/icon-192/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/icon-512/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/layout-ddf79478d9a673bb.js +1 -0
- package/.next/static/chunks/app/loading-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/{not-found-3772c2e09c29d80f.js → not-found-824d5d3c6e296eeb.js} +1 -1
- package/.next/static/chunks/app/orchestrators/page-4c190484788aaaa3.js +1 -0
- package/.next/static/chunks/app/page-d3b83ad5f09b6ec7.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/loading-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/page-9f3dfbea006747cf.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/sessions/[id]/page-a7090a06948f9a6b.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/settings/page-219df6dcbc6d1107.js +1 -0
- package/.next/static/chunks/app/prs/page-a285e930235a23af.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/error-eb0973907da68a37.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/loading-e2dea9178b4af8db.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/{not-found-3772c2e09c29d80f.js → not-found-824d5d3c6e296eeb.js} +1 -1
- package/.next/static/chunks/app/sessions/[id]/page-98f398b822092218.js +1 -0
- package/.next/static/chunks/app/test-direct/page-f9bff7d7b4dfe728.js +1 -0
- package/.next/static/chunks/framework-7060e2ac4971c604.js +1 -0
- package/.next/static/chunks/main-app-690acf9d5d2050c9.js +1 -0
- package/.next/static/chunks/main-ed1610689fbd6f0d.js +1 -0
- package/.next/static/chunks/pages/_app-f4baf4dbe88f6f54.js +1 -0
- package/.next/static/chunks/pages/_error-a7f6723f42093f29.js +1 -0
- package/.next/static/chunks/{webpack-e12ceebeb7a1cc7e.js → webpack-d4ff5ed5e153ca3e.js} +1 -1
- package/.next/static/css/577ea7f5ad4f0576.css +1 -0
- package/.next/static/css/659eccb5db697b76.css +1 -0
- package/.next/static/q6yh3n8jgvyI9JIbexzuH/_buildManifest.js +1 -0
- package/dist-server/direct-terminal-ws.js +33 -236
- package/dist-server/mux-websocket.js +516 -0
- package/dist-server/start-all.js +0 -2
- package/next.config.js +5 -0
- package/package.json +18 -15
- package/.next/server/chunks/27.js +0 -438
- package/.next/server/chunks/377.js +0 -1
- package/.next/server/chunks/393.js +0 -1
- package/.next/server/chunks/627.js +0 -391
- package/.next/server/chunks/639.js +0 -6
- package/.next/server/chunks/693.js +0 -1
- package/.next/server/chunks/705.js +0 -22
- package/.next/server/chunks/787.js +0 -29
- package/.next/server/chunks/796.js +0 -1
- package/.next/server/chunks/934.js +0 -1
- package/.next/server/chunks/956.js +0 -9
- package/.next/static/WS8DHHp9haunZGhtE8L33/_buildManifest.js +0 -1
- package/.next/static/chunks/1250-e7cf6b069fbb03ed.js +0 -1
- package/.next/static/chunks/2205.498806f73783aa54.js +0 -1
- package/.next/static/chunks/3698-9c12c45b8184022c.js +0 -1
- package/.next/static/chunks/6381.1541d5695a727108.js +0 -1
- package/.next/static/chunks/7505-2d2422d31862995f.js +0 -1
- package/.next/static/chunks/8597-1385f90ec1cebf47.js +0 -1
- package/.next/static/chunks/8762.f3d526855363db16.js +0 -1
- package/.next/static/chunks/a51c26f2-a21e680a5df6764e.js +0 -1
- package/.next/static/chunks/app/_not-found/page-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/backlog/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/events/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/issues/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/observability/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/orchestrators/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/projects/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/prs/[id]/merge/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/runtime/terminal/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/kill/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/message/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/remap/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/restore/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/send/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/setup-labels/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/spawn/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/verify/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/api/webhooks/[...slug]/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/apple-icon/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/dev/terminal-test/page-ac0ce5b046fcad82.js +0 -1
- package/.next/static/chunks/app/error-4896c9d3b7681a80.js +0 -1
- package/.next/static/chunks/app/icon/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/icon-192/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/icon-512/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/layout-023f2083204e4f68.js +0 -1
- package/.next/static/chunks/app/loading-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/orchestrators/page-df85236df674fc5a.js +0 -1
- package/.next/static/chunks/app/page-e97da633b7ef25f2.js +0 -1
- package/.next/static/chunks/app/prs/page-8cc0fce584d238c5.js +0 -1
- package/.next/static/chunks/app/sessions/[id]/error-90bc99b777fecabf.js +0 -1
- package/.next/static/chunks/app/sessions/[id]/loading-2224bc1d3dce1b3e.js +0 -1
- package/.next/static/chunks/app/sessions/[id]/page-c9c95a8604786cff.js +0 -1
- package/.next/static/chunks/app/test-direct/page-16ceeb9f7664394a.js +0 -1
- package/.next/static/chunks/df4ed4d4.6a752eba3933e9a8.js +0 -3
- package/.next/static/chunks/framework-f8b8ba0f71d38056.js +0 -1
- package/.next/static/chunks/main-2bc85c765bf1fc49.js +0 -1
- package/.next/static/chunks/main-app-93fb36c3bd1a6739.js +0 -1
- package/.next/static/chunks/pages/_app-a0b975794f15bd68.js +0 -1
- package/.next/static/chunks/pages/_error-5f4e3b5eea57917d.js +0 -1
- package/.next/static/css/6ef2fa08dd043252.css +0 -1
- package/.next/static/css/908f93fdd7ffba42.css +0 -1
- package/dist-server/terminal-websocket.js +0 -375
- /package/.next/static/{WS8DHHp9haunZGhtE8L33 → q6yh3n8jgvyI9JIbexzuH}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiplexed WebSocket server for terminal multiplexing.
|
|
3
|
+
* Manages multiple terminal connections over a single persistent WebSocket.
|
|
4
|
+
*
|
|
5
|
+
* Session updates are delivered via a single shared SSE connection from this
|
|
6
|
+
* process to Next.js /api/events, then broadcast to all subscribed clients.
|
|
7
|
+
* This replaces per-client HTTP polling and makes session updates event-driven.
|
|
8
|
+
*/
|
|
9
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
10
|
+
import { homedir, userInfo } from "node:os";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { findTmux, resolveTmuxSession, validateSessionId } from "./tmux-utils.js";
|
|
13
|
+
/**
|
|
14
|
+
* Manages a single shared SSE connection to Next.js /api/events.
|
|
15
|
+
* Broadcasts session patches to all subscribed callbacks.
|
|
16
|
+
* Lazily connects on first subscriber, disconnects when the last one leaves.
|
|
17
|
+
*/
|
|
18
|
+
export class SessionBroadcaster {
|
|
19
|
+
subscribers = new Set();
|
|
20
|
+
abortController = null;
|
|
21
|
+
reconnectTimer = null;
|
|
22
|
+
baseUrl;
|
|
23
|
+
constructor(nextPort) {
|
|
24
|
+
this.baseUrl = `http://localhost:${nextPort}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Subscribe to session patches. Returns an unsubscribe function.
|
|
28
|
+
* Sends an immediate snapshot to the new subscriber, then live SSE pushes.
|
|
29
|
+
*/
|
|
30
|
+
subscribe(callback) {
|
|
31
|
+
const wasEmpty = this.subscribers.size === 0;
|
|
32
|
+
this.subscribers.add(callback);
|
|
33
|
+
// Immediately send a one-off snapshot to just this new subscriber
|
|
34
|
+
void this.fetchSnapshot().then((sessions) => {
|
|
35
|
+
if (sessions && this.subscribers.has(callback)) {
|
|
36
|
+
callback(sessions);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// Start the shared SSE connection if this is the first subscriber
|
|
40
|
+
if (wasEmpty) {
|
|
41
|
+
void this.connect();
|
|
42
|
+
}
|
|
43
|
+
return () => {
|
|
44
|
+
this.subscribers.delete(callback);
|
|
45
|
+
if (this.subscribers.size === 0) {
|
|
46
|
+
this.disconnect();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
broadcast(sessions) {
|
|
51
|
+
for (const callback of this.subscribers) {
|
|
52
|
+
try {
|
|
53
|
+
callback(sessions);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error("[MuxServer] Session broadcast subscriber threw:", err);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** One-shot HTTP fetch of the current session list for immediate delivery. */
|
|
61
|
+
async fetchSnapshot() {
|
|
62
|
+
try {
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
const timeoutId = setTimeout(() => controller.abort(), 4000);
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(`${this.baseUrl}/api/sessions/patches`, {
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
});
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
if (!res.ok)
|
|
71
|
+
return null;
|
|
72
|
+
const data = (await res.json());
|
|
73
|
+
return data.sessions ?? null;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
clearTimeout(timeoutId);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Open a persistent SSE connection and stream events to all subscribers. */
|
|
85
|
+
async connect() {
|
|
86
|
+
if (this.abortController)
|
|
87
|
+
return;
|
|
88
|
+
const controller = new AbortController();
|
|
89
|
+
this.abortController = controller;
|
|
90
|
+
const { signal } = controller;
|
|
91
|
+
try {
|
|
92
|
+
const res = await fetch(`${this.baseUrl}/api/events`, {
|
|
93
|
+
signal,
|
|
94
|
+
headers: { Accept: "text/event-stream" },
|
|
95
|
+
});
|
|
96
|
+
if (!res.ok || !res.body) {
|
|
97
|
+
throw new Error(`SSE connect failed: ${res.status}`);
|
|
98
|
+
}
|
|
99
|
+
const reader = res.body.getReader();
|
|
100
|
+
const decoder = new TextDecoder();
|
|
101
|
+
let buffer = "";
|
|
102
|
+
while (!signal.aborted) {
|
|
103
|
+
const { done, value } = await reader.read();
|
|
104
|
+
if (done)
|
|
105
|
+
break;
|
|
106
|
+
buffer += decoder.decode(value, { stream: true });
|
|
107
|
+
const lines = buffer.split("\n");
|
|
108
|
+
buffer = lines.pop() ?? "";
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
if (!line.startsWith("data: "))
|
|
111
|
+
continue;
|
|
112
|
+
try {
|
|
113
|
+
const event = JSON.parse(line.slice(6));
|
|
114
|
+
if (event.type === "snapshot" && event.sessions) {
|
|
115
|
+
this.broadcast(event.sessions);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// ignore malformed events
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (signal.aborted)
|
|
126
|
+
return; // intentional disconnect, not an error
|
|
127
|
+
console.warn("[MuxServer] SSE connection lost:", err instanceof Error ? err.message : err);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
// Only clear our own controller — a concurrent connect() may have already
|
|
131
|
+
// set a new one (e.g. disconnect() → subscribe() → connect() in the same tick).
|
|
132
|
+
if (this.abortController === controller) {
|
|
133
|
+
this.abortController = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Reconnect with backoff if there are still subscribers
|
|
137
|
+
if (this.subscribers.size > 0) {
|
|
138
|
+
console.log("[MuxServer] SSE reconnecting in 5s");
|
|
139
|
+
this.reconnectTimer = setTimeout(() => {
|
|
140
|
+
this.reconnectTimer = null;
|
|
141
|
+
if (this.subscribers.size > 0)
|
|
142
|
+
void this.connect();
|
|
143
|
+
}, 5000);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
disconnect() {
|
|
147
|
+
if (this.reconnectTimer) {
|
|
148
|
+
clearTimeout(this.reconnectTimer);
|
|
149
|
+
this.reconnectTimer = null;
|
|
150
|
+
}
|
|
151
|
+
this.abortController?.abort();
|
|
152
|
+
this.abortController = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let ptySpawn;
|
|
156
|
+
/* eslint-enable @typescript-eslint/consistent-type-imports */
|
|
157
|
+
try {
|
|
158
|
+
const nodePty = await import("node-pty");
|
|
159
|
+
ptySpawn = nodePty.spawn;
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
console.warn("[MuxServer] node-pty not available — mux server will be disabled.", err);
|
|
163
|
+
}
|
|
164
|
+
const RING_BUFFER_MAX = 50 * 1024; // 50KB max per terminal
|
|
165
|
+
const MAX_REATTACH_ATTEMPTS = 3;
|
|
166
|
+
/**
|
|
167
|
+
* TerminalManager manages PTY processes independently of WebSocket connections.
|
|
168
|
+
* A single manager instance is shared across all mux connections.
|
|
169
|
+
*/
|
|
170
|
+
class TerminalManager {
|
|
171
|
+
terminals = new Map();
|
|
172
|
+
TMUX;
|
|
173
|
+
constructor(tmuxPath) {
|
|
174
|
+
this.TMUX = tmuxPath ?? findTmux();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Open/attach to a terminal. If already open, just return.
|
|
178
|
+
* If has subscribers but PTY crashed, re-attach.
|
|
179
|
+
*/
|
|
180
|
+
open(id) {
|
|
181
|
+
// Validate and resolve
|
|
182
|
+
if (!validateSessionId(id)) {
|
|
183
|
+
throw new Error(`Invalid session ID: ${id}`);
|
|
184
|
+
}
|
|
185
|
+
const tmuxSessionId = resolveTmuxSession(id, this.TMUX);
|
|
186
|
+
if (!tmuxSessionId) {
|
|
187
|
+
throw new Error(`Session not found: ${id}`);
|
|
188
|
+
}
|
|
189
|
+
// Get or create terminal entry
|
|
190
|
+
let terminal = this.terminals.get(id);
|
|
191
|
+
if (!terminal) {
|
|
192
|
+
terminal = {
|
|
193
|
+
id,
|
|
194
|
+
tmuxSessionId,
|
|
195
|
+
pty: null,
|
|
196
|
+
subscribers: new Set(),
|
|
197
|
+
exitCallbacks: new Set(),
|
|
198
|
+
buffer: [],
|
|
199
|
+
bufferBytes: 0,
|
|
200
|
+
reattachAttempts: 0,
|
|
201
|
+
};
|
|
202
|
+
this.terminals.set(id, terminal);
|
|
203
|
+
}
|
|
204
|
+
// If PTY is already attached, we're done
|
|
205
|
+
if (terminal.pty) {
|
|
206
|
+
return tmuxSessionId;
|
|
207
|
+
}
|
|
208
|
+
// Enable mouse mode
|
|
209
|
+
const mouseProc = spawn(this.TMUX, ["set-option", "-t", tmuxSessionId, "mouse", "on"]);
|
|
210
|
+
mouseProc.on("error", (err) => {
|
|
211
|
+
console.error(`[MuxServer] Failed to set mouse mode for ${tmuxSessionId}:`, err.message);
|
|
212
|
+
});
|
|
213
|
+
// Hide the status bar
|
|
214
|
+
const statusProc = spawn(this.TMUX, ["set-option", "-t", tmuxSessionId, "status", "off"]);
|
|
215
|
+
statusProc.on("error", (err) => {
|
|
216
|
+
console.error(`[MuxServer] Failed to hide status bar for ${tmuxSessionId}:`, err.message);
|
|
217
|
+
});
|
|
218
|
+
// Build environment
|
|
219
|
+
const homeDir = process.env.HOME || homedir();
|
|
220
|
+
const currentUser = process.env.USER || userInfo().username;
|
|
221
|
+
const env = {
|
|
222
|
+
HOME: homeDir,
|
|
223
|
+
SHELL: process.env.SHELL || "/bin/bash",
|
|
224
|
+
USER: currentUser,
|
|
225
|
+
PATH: process.env.PATH || "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin",
|
|
226
|
+
TERM: "xterm-256color",
|
|
227
|
+
LANG: process.env.LANG || "en_US.UTF-8",
|
|
228
|
+
TMPDIR: process.env.TMPDIR || "/tmp",
|
|
229
|
+
};
|
|
230
|
+
if (!ptySpawn) {
|
|
231
|
+
throw new Error("node-pty not available");
|
|
232
|
+
}
|
|
233
|
+
// Spawn PTY
|
|
234
|
+
const pty = ptySpawn(this.TMUX, ["attach-session", "-t", tmuxSessionId], {
|
|
235
|
+
name: "xterm-256color",
|
|
236
|
+
cols: 80,
|
|
237
|
+
rows: 24,
|
|
238
|
+
cwd: homeDir,
|
|
239
|
+
env,
|
|
240
|
+
});
|
|
241
|
+
terminal.pty = pty;
|
|
242
|
+
// Wire up data events
|
|
243
|
+
pty.onData((data) => {
|
|
244
|
+
// Push to all subscribers — isolate each callback so a throw in one
|
|
245
|
+
// (e.g. a closed ws.send) doesn't abort the loop or skip the buffer.
|
|
246
|
+
for (const callback of terminal.subscribers) {
|
|
247
|
+
try {
|
|
248
|
+
callback(data);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
console.error("[MuxServer] Subscriber callback threw:", err);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Append to ring buffer
|
|
255
|
+
terminal.buffer.push(data);
|
|
256
|
+
terminal.bufferBytes += Buffer.byteLength(data, "utf8");
|
|
257
|
+
// Trim buffer if over limit
|
|
258
|
+
while (terminal.bufferBytes > RING_BUFFER_MAX && terminal.buffer.length > 0) {
|
|
259
|
+
const removed = terminal.buffer.shift() ?? "";
|
|
260
|
+
terminal.bufferBytes -= Buffer.byteLength(removed, "utf8");
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
// Handle PTY exit
|
|
264
|
+
pty.onExit(({ exitCode }) => {
|
|
265
|
+
console.log(`[MuxServer] PTY exited for ${id} with code ${exitCode}`);
|
|
266
|
+
terminal.pty = null;
|
|
267
|
+
// Re-attach if subscribers are still present, up to MAX_REATTACH_ATTEMPTS.
|
|
268
|
+
// The cap prevents an unbounded respawn loop when the PTY crashes immediately
|
|
269
|
+
// after every attach (e.g. resource exhaustion or a broken tmux session).
|
|
270
|
+
if (terminal.subscribers.size > 0 && terminal.reattachAttempts < MAX_REATTACH_ATTEMPTS) {
|
|
271
|
+
terminal.reattachAttempts += 1;
|
|
272
|
+
console.log(`[MuxServer] Re-attaching to ${id} (attempt ${terminal.reattachAttempts}/${MAX_REATTACH_ATTEMPTS})`);
|
|
273
|
+
try {
|
|
274
|
+
this.open(id);
|
|
275
|
+
terminal.reattachAttempts = 0; // reset on successful attach
|
|
276
|
+
return; // re-attached — don't notify exit
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
console.error(`[MuxServer] Failed to re-attach ${id}:`, err);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else if (terminal.reattachAttempts >= MAX_REATTACH_ATTEMPTS) {
|
|
283
|
+
console.error(`[MuxServer] Max re-attach attempts reached for ${id}, giving up`);
|
|
284
|
+
}
|
|
285
|
+
// Notify subscribers that the terminal has exited (re-attach failed or no subscribers)
|
|
286
|
+
for (const cb of terminal.exitCallbacks) {
|
|
287
|
+
cb(exitCode);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
console.log(`[MuxServer] Opened terminal ${id} (tmux: ${tmuxSessionId})`);
|
|
291
|
+
return tmuxSessionId;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Write data to the PTY if attached
|
|
295
|
+
*/
|
|
296
|
+
write(id, data) {
|
|
297
|
+
const terminal = this.terminals.get(id);
|
|
298
|
+
if (terminal?.pty) {
|
|
299
|
+
terminal.pty.write(data);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Resize the PTY if attached
|
|
304
|
+
*/
|
|
305
|
+
resize(id, cols, rows) {
|
|
306
|
+
const terminal = this.terminals.get(id);
|
|
307
|
+
if (terminal?.pty) {
|
|
308
|
+
terminal.pty.resize(cols, rows);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Subscribe to terminal data. Returns unsubscribe function.
|
|
313
|
+
* Automatically opens the terminal if needed.
|
|
314
|
+
* @param onExit - called when the PTY exits and cannot be re-attached
|
|
315
|
+
*/
|
|
316
|
+
subscribe(id, callback, onExit) {
|
|
317
|
+
// Ensure terminal is open
|
|
318
|
+
this.open(id);
|
|
319
|
+
const terminal = this.terminals.get(id);
|
|
320
|
+
if (!terminal) {
|
|
321
|
+
throw new Error(`Failed to open terminal: ${id}`);
|
|
322
|
+
}
|
|
323
|
+
// Add subscriber
|
|
324
|
+
terminal.subscribers.add(callback);
|
|
325
|
+
if (onExit)
|
|
326
|
+
terminal.exitCallbacks.add(onExit);
|
|
327
|
+
// Return unsubscribe function
|
|
328
|
+
return () => {
|
|
329
|
+
terminal.subscribers.delete(callback);
|
|
330
|
+
if (onExit)
|
|
331
|
+
terminal.exitCallbacks.delete(onExit);
|
|
332
|
+
// Kill PTY and clean up when the last subscriber leaves
|
|
333
|
+
if (terminal.subscribers.size === 0) {
|
|
334
|
+
if (terminal.pty) {
|
|
335
|
+
terminal.pty.kill();
|
|
336
|
+
terminal.pty = null;
|
|
337
|
+
}
|
|
338
|
+
this.terminals.delete(id);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get buffered data for a terminal
|
|
344
|
+
*/
|
|
345
|
+
getBuffer(id) {
|
|
346
|
+
const terminal = this.terminals.get(id);
|
|
347
|
+
if (!terminal)
|
|
348
|
+
return "";
|
|
349
|
+
return terminal.buffer.join("");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Create a mux WebSocket server (noServer mode).
|
|
354
|
+
* Returns the WebSocketServer instance for manual upgrade routing.
|
|
355
|
+
*/
|
|
356
|
+
export function createMuxWebSocket(tmuxPath) {
|
|
357
|
+
if (!ptySpawn) {
|
|
358
|
+
console.warn("[MuxServer] node-pty not available — mux WebSocket will be disabled");
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
const terminalManager = new TerminalManager(tmuxPath);
|
|
362
|
+
const nextPort = process.env.PORT || "3000";
|
|
363
|
+
const broadcaster = new SessionBroadcaster(nextPort);
|
|
364
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
365
|
+
wss.on("connection", (ws) => {
|
|
366
|
+
console.log("[MuxServer] New mux connection");
|
|
367
|
+
const subscriptions = new Map();
|
|
368
|
+
let sessionUnsubscribe = null;
|
|
369
|
+
let missedPongs = 0;
|
|
370
|
+
const MAX_MISSED_PONGS = 3;
|
|
371
|
+
// Heartbeat: send native WebSocket ping every 15s.
|
|
372
|
+
// Browsers automatically respond to native pings with pong frames —
|
|
373
|
+
// no application-level code is needed on the client side.
|
|
374
|
+
const heartbeatInterval = setInterval(() => {
|
|
375
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
376
|
+
// Send the ping first so it counts as a sent-but-unanswered probe
|
|
377
|
+
ws.ping();
|
|
378
|
+
missedPongs += 1;
|
|
379
|
+
if (missedPongs >= MAX_MISSED_PONGS) {
|
|
380
|
+
console.log("[MuxServer] Too many missed pongs, terminating connection");
|
|
381
|
+
ws.terminate();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}, 15_000);
|
|
385
|
+
// Native pong resets the missed counter
|
|
386
|
+
ws.on("pong", () => {
|
|
387
|
+
missedPongs = 0;
|
|
388
|
+
});
|
|
389
|
+
/**
|
|
390
|
+
* Handle incoming messages
|
|
391
|
+
*/
|
|
392
|
+
ws.on("message", (data) => {
|
|
393
|
+
try {
|
|
394
|
+
const msg = JSON.parse(data.toString("utf8"));
|
|
395
|
+
if (msg.ch === "system") {
|
|
396
|
+
if (msg.type === "ping") {
|
|
397
|
+
const pong = { ch: "system", type: "pong" };
|
|
398
|
+
ws.send(JSON.stringify(pong));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else if (msg.ch === "terminal") {
|
|
402
|
+
const { id, type } = msg;
|
|
403
|
+
try {
|
|
404
|
+
if (type === "open") {
|
|
405
|
+
// Validate session exists
|
|
406
|
+
terminalManager.open(id);
|
|
407
|
+
// Send opened confirmation (idempotent — safe to send on re-open)
|
|
408
|
+
const openedMsg = { ch: "terminal", id, type: "opened" };
|
|
409
|
+
ws.send(JSON.stringify(openedMsg));
|
|
410
|
+
// Subscribe and send history buffer only for new subscribers.
|
|
411
|
+
// Skipping the buffer on re-open prevents duplicate output when
|
|
412
|
+
// MuxProvider re-sends open for all terminals on reconnect.
|
|
413
|
+
if (!subscriptions.has(id)) {
|
|
414
|
+
// Send buffered history to catch up the new subscriber
|
|
415
|
+
const buffer = terminalManager.getBuffer(id);
|
|
416
|
+
if (buffer) {
|
|
417
|
+
const bufferMsg = {
|
|
418
|
+
ch: "terminal",
|
|
419
|
+
id,
|
|
420
|
+
type: "data",
|
|
421
|
+
data: buffer,
|
|
422
|
+
};
|
|
423
|
+
ws.send(JSON.stringify(bufferMsg));
|
|
424
|
+
}
|
|
425
|
+
const unsub = terminalManager.subscribe(id, (data) => {
|
|
426
|
+
const dataMsg = {
|
|
427
|
+
ch: "terminal",
|
|
428
|
+
id,
|
|
429
|
+
type: "data",
|
|
430
|
+
data,
|
|
431
|
+
};
|
|
432
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
433
|
+
ws.send(JSON.stringify(dataMsg));
|
|
434
|
+
}
|
|
435
|
+
}, (exitCode) => {
|
|
436
|
+
const exitedMsg = { ch: "terminal", id, type: "exited", code: exitCode };
|
|
437
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
438
|
+
ws.send(JSON.stringify(exitedMsg));
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
subscriptions.set(id, unsub);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else if (type === "data" && "data" in msg) {
|
|
445
|
+
terminalManager.write(id, msg.data);
|
|
446
|
+
}
|
|
447
|
+
else if (type === "resize" && "cols" in msg && "rows" in msg) {
|
|
448
|
+
terminalManager.resize(id, msg.cols, msg.rows);
|
|
449
|
+
}
|
|
450
|
+
else if (type === "close") {
|
|
451
|
+
// Unsubscribe this client only — TerminalManager is shared across
|
|
452
|
+
// all mux connections so we must not kill the PTY here.
|
|
453
|
+
const unsub = subscriptions.get(id);
|
|
454
|
+
if (unsub) {
|
|
455
|
+
unsub();
|
|
456
|
+
subscriptions.delete(id);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
462
|
+
const errorMsg = {
|
|
463
|
+
ch: "terminal",
|
|
464
|
+
id,
|
|
465
|
+
type: "error",
|
|
466
|
+
message: err instanceof Error ? err.message : String(err),
|
|
467
|
+
};
|
|
468
|
+
ws.send(JSON.stringify(errorMsg));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else if (msg.ch === "subscribe") {
|
|
473
|
+
if (msg.topics.includes("sessions") && !sessionUnsubscribe) {
|
|
474
|
+
sessionUnsubscribe = broadcaster.subscribe((sessions) => {
|
|
475
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
476
|
+
const snapMsg = { ch: "sessions", type: "snapshot", sessions };
|
|
477
|
+
ws.send(JSON.stringify(snapMsg));
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
console.error("[MuxServer] Failed to parse message:", err);
|
|
485
|
+
const errorMsg = {
|
|
486
|
+
ch: "system",
|
|
487
|
+
type: "error",
|
|
488
|
+
message: "Invalid message format",
|
|
489
|
+
};
|
|
490
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
491
|
+
ws.send(JSON.stringify(errorMsg));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
/**
|
|
496
|
+
* Handle connection close
|
|
497
|
+
*/
|
|
498
|
+
ws.on("close", () => {
|
|
499
|
+
console.log("[MuxServer] Mux connection closed");
|
|
500
|
+
clearInterval(heartbeatInterval);
|
|
501
|
+
sessionUnsubscribe?.();
|
|
502
|
+
sessionUnsubscribe = null;
|
|
503
|
+
for (const unsub of subscriptions.values()) {
|
|
504
|
+
unsub();
|
|
505
|
+
}
|
|
506
|
+
subscriptions.clear();
|
|
507
|
+
});
|
|
508
|
+
// In the ws library, "error" is always followed by "close", so the close
|
|
509
|
+
// handler below handles all cleanup. Log the error here and nothing more.
|
|
510
|
+
ws.on("error", (err) => {
|
|
511
|
+
console.error("[MuxServer] WebSocket error:", err.message);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
console.log("[MuxServer] Mux WebSocket server created (noServer mode)");
|
|
515
|
+
return wss;
|
|
516
|
+
}
|
package/dist-server/start-all.js
CHANGED
|
@@ -77,8 +77,6 @@ function resolveNextBin() {
|
|
|
77
77
|
// Start Next.js production server
|
|
78
78
|
const port = process.env["PORT"] || "3000";
|
|
79
79
|
spawnProcess("next", resolveNextBin(), ["start", "-p", port]);
|
|
80
|
-
// Start terminal WebSocket server (auto-restart on crash)
|
|
81
|
-
spawnProcess("terminal", "node", [resolve(__dirname, "terminal-websocket.js")], { restart: true });
|
|
82
80
|
// Start direct terminal WebSocket server (auto-restart on crash)
|
|
83
81
|
spawnProcess("direct-terminal", "node", [resolve(__dirname, "direct-terminal-ws.js")], { restart: true });
|
|
84
82
|
// Graceful shutdown — send SIGTERM to children and wait for them to exit
|
package/next.config.js
CHANGED
|
@@ -3,6 +3,7 @@ const nextConfig = {
|
|
|
3
3
|
transpilePackages: [
|
|
4
4
|
"@aoagents/ao-core",
|
|
5
5
|
"@aoagents/ao-plugin-agent-claude-code",
|
|
6
|
+
"@aoagents/ao-plugin-agent-codex",
|
|
6
7
|
"@aoagents/ao-plugin-agent-opencode",
|
|
7
8
|
"@aoagents/ao-plugin-runtime-tmux",
|
|
8
9
|
"@aoagents/ao-plugin-scm-github",
|
|
@@ -10,6 +11,10 @@ const nextConfig = {
|
|
|
10
11
|
"@aoagents/ao-plugin-tracker-linear",
|
|
11
12
|
"@aoagents/ao-plugin-workspace-worktree",
|
|
12
13
|
],
|
|
14
|
+
serverExternalPackages: [
|
|
15
|
+
"yaml",
|
|
16
|
+
"zod",
|
|
17
|
+
],
|
|
13
18
|
async headers() {
|
|
14
19
|
return [
|
|
15
20
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aoagents/ao-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Web dashboard for agent-orchestrator",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -14,21 +14,23 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@xterm/addon-fit": "^0.11.0",
|
|
16
16
|
"@xterm/addon-web-links": "^0.12.0",
|
|
17
|
+
"@xterm/xterm": "^6.0.0",
|
|
17
18
|
"next": "^15.1.0",
|
|
18
19
|
"next-themes": "^0.4.6",
|
|
19
20
|
"react": "^19.0.0",
|
|
20
21
|
"react-dom": "^19.0.0",
|
|
21
22
|
"server-only": "^0.0.1",
|
|
22
23
|
"ws": "^8.19.0",
|
|
23
|
-
"
|
|
24
|
-
"@aoagents/ao-
|
|
25
|
-
"@aoagents/ao-plugin-
|
|
26
|
-
"@aoagents/ao-plugin-
|
|
27
|
-
"@aoagents/ao-plugin-
|
|
28
|
-
"@aoagents/ao-plugin-
|
|
29
|
-
"@aoagents/ao-plugin-scm-github": "0.
|
|
30
|
-
"@aoagents/ao-plugin-
|
|
31
|
-
"@aoagents/ao-plugin-
|
|
24
|
+
"@aoagents/ao-core": "0.3.0",
|
|
25
|
+
"@aoagents/ao-plugin-agent-claude-code": "0.3.0",
|
|
26
|
+
"@aoagents/ao-plugin-agent-codex": "0.3.0",
|
|
27
|
+
"@aoagents/ao-plugin-agent-opencode": "0.3.0",
|
|
28
|
+
"@aoagents/ao-plugin-agent-cursor": "0.1.1",
|
|
29
|
+
"@aoagents/ao-plugin-runtime-tmux": "0.3.0",
|
|
30
|
+
"@aoagents/ao-plugin-scm-github": "0.3.0",
|
|
31
|
+
"@aoagents/ao-plugin-tracker-github": "0.3.0",
|
|
32
|
+
"@aoagents/ao-plugin-tracker-linear": "0.3.0",
|
|
33
|
+
"@aoagents/ao-plugin-workspace-worktree": "0.3.0"
|
|
32
34
|
},
|
|
33
35
|
"optionalDependencies": {
|
|
34
36
|
"node-pty": "^1.1.0"
|
|
@@ -47,24 +49,25 @@
|
|
|
47
49
|
"jsdom": "^25.0.0",
|
|
48
50
|
"node-gyp": "^12.2.0",
|
|
49
51
|
"playwright": "^1.49.0",
|
|
52
|
+
"rimraf": "^6.0.0",
|
|
50
53
|
"tailwindcss": "^4.0.0",
|
|
51
54
|
"tsx": "^4.19.0",
|
|
52
55
|
"typescript": "^5.7.0",
|
|
53
56
|
"vitest": "^2.1.0"
|
|
54
57
|
},
|
|
55
58
|
"scripts": {
|
|
56
|
-
"dev": "concurrently \"npm:dev:next\" \"npm:dev:
|
|
59
|
+
"dev": "concurrently \"npm:dev:next\" \"npm:dev:direct-terminal\"",
|
|
57
60
|
"dev:next": "next dev -p ${PORT:-3000}",
|
|
58
|
-
"dev:terminal": "
|
|
59
|
-
"
|
|
61
|
+
"dev:direct-terminal": "node scripts/dev-direct-terminal.mjs",
|
|
62
|
+
"prebuild": "rimraf .next dist-server",
|
|
60
63
|
"build": "next build && tsc -p tsconfig.server.json",
|
|
61
64
|
"start": "next start",
|
|
62
65
|
"start:all": "node dist-server/start-all.js",
|
|
63
|
-
"dev:optimized": "next build && tsc -p tsconfig.server.json && node dist-server/start-all.js",
|
|
66
|
+
"dev:optimized": "rimraf .next dist-server && next build && tsc -p tsconfig.server.json && node dist-server/start-all.js",
|
|
64
67
|
"typecheck": "tsc --noEmit",
|
|
65
68
|
"test": "vitest run",
|
|
66
69
|
"test:watch": "vitest",
|
|
67
|
-
"clean": "
|
|
70
|
+
"clean": "rimraf .next dist-server",
|
|
68
71
|
"screenshot": "tsx e2e/screenshot.ts",
|
|
69
72
|
"screenshot:install": "npx playwright install chromium"
|
|
70
73
|
}
|