@made-by-moonlight/athene-web 0.9.2
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 -0
- package/.next/app-build-manifest.json +448 -0
- package/.next/app-path-routes-manifest.json +47 -0
- package/.next/build-manifest.json +33 -0
- package/.next/export-marker.json +6 -0
- package/.next/images-manifest.json +58 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +172 -0
- package/.next/react-loadable-manifest.json +61 -0
- package/.next/required-server-files.json +335 -0
- package/.next/routes-manifest.json +234 -0
- package/.next/server/app/_not-found/page.js +2 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +8 -0
- package/.next/server/app/_not-found.rsc +24 -0
- package/.next/server/app/api/backlog/route.js +1 -0
- package/.next/server/app/api/backlog/route.js.nft.json +1 -0
- package/.next/server/app/api/backlog/route_client-reference-manifest.js +1 -0
- 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/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 -0
- package/.next/server/app/api/issues/route.js.nft.json +1 -0
- package/.next/server/app/api/issues/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/observability/route.js +1 -0
- package/.next/server/app/api/observability/route.js.nft.json +1 -0
- package/.next/server/app/api/observability/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/orchestrators/route.js +1 -0
- package/.next/server/app/api/orchestrators/route.js.nft.json +1 -0
- package/.next/server/app/api/orchestrators/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/projects/[id]/route.js +5 -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 -0
- package/.next/server/app/api/projects/route.js.nft.json +1 -0
- package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/prs/[id]/merge/route.js +1 -0
- package/.next/server/app/api/prs/[id]/merge/route.js.nft.json +1 -0
- package/.next/server/app/api/prs/[id]/merge/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/reviews/execute/route.js +1 -0
- package/.next/server/app/api/reviews/execute/route.js.nft.json +1 -0
- package/.next/server/app/api/reviews/execute/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/reviews/findings/route.js +1 -0
- package/.next/server/app/api/reviews/findings/route.js.nft.json +1 -0
- package/.next/server/app/api/reviews/findings/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/reviews/route.js +1 -0
- package/.next/server/app/api/reviews/route.js.nft.json +1 -0
- package/.next/server/app/api/reviews/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/reviews/send/route.js +1 -0
- package/.next/server/app/api/reviews/send/route.js.nft.json +1 -0
- package/.next/server/app/api/reviews/send/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/runtime/terminal/route.js +1 -0
- package/.next/server/app/api/runtime/terminal/route.js.nft.json +1 -0
- package/.next/server/app/api/runtime/terminal/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/kill/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/kill/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/message/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/message/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/message/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/remap/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/remap/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/remap/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/send/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/send/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/send/route_client-reference-manifest.js +1 -0
- 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 -0
- package/.next/server/app/api/sessions/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/setup-labels/route.js +1 -0
- package/.next/server/app/api/setup-labels/route.js.nft.json +1 -0
- package/.next/server/app/api/setup-labels/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/spawn/route.js +1 -0
- package/.next/server/app/api/spawn/route.js.nft.json +1 -0
- package/.next/server/app/api/spawn/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/update/route.js +1 -0
- package/.next/server/app/api/update/route.js.nft.json +1 -0
- package/.next/server/app/api/update/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/verify/route.js +1 -0
- package/.next/server/app/api/verify/route.js.nft.json +1 -0
- package/.next/server/app/api/verify/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/version/route.js +1 -0
- package/.next/server/app/api/version/route.js.nft.json +1 -0
- package/.next/server/app/api/version/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/webhooks/[...slug]/route.js +1 -0
- package/.next/server/app/api/webhooks/[...slug]/route.js.nft.json +1 -0
- package/.next/server/app/api/webhooks/[...slug]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/apple-icon/route.js +1 -0
- package/.next/server/app/apple-icon/route.js.nft.json +1 -0
- package/.next/server/app/apple-icon/route_client-reference-manifest.js +1 -0
- package/.next/server/app/apple-icon.body +0 -0
- package/.next/server/app/apple-icon.meta +1 -0
- package/.next/server/app/dev/terminal-test/page.js +15 -0
- package/.next/server/app/dev/terminal-test/page.js.nft.json +1 -0
- package/.next/server/app/dev/terminal-test/page_client-reference-manifest.js +1 -0
- package/.next/server/app/dev/terminal-test.html +1 -0
- package/.next/server/app/dev/terminal-test.meta +7 -0
- package/.next/server/app/dev/terminal-test.rsc +28 -0
- package/.next/server/app/icon/route.js +1 -0
- package/.next/server/app/icon/route.js.nft.json +1 -0
- package/.next/server/app/icon/route_client-reference-manifest.js +1 -0
- package/.next/server/app/icon-192/route.js +1 -0
- package/.next/server/app/icon-192/route.js.nft.json +1 -0
- package/.next/server/app/icon-192/route_client-reference-manifest.js +1 -0
- package/.next/server/app/icon-512/route.js +1 -0
- package/.next/server/app/icon-512/route.js.nft.json +1 -0
- package/.next/server/app/icon-512/route_client-reference-manifest.js +1 -0
- package/.next/server/app/icon.body +0 -0
- package/.next/server/app/icon.meta +1 -0
- package/.next/server/app/manifest.webmanifest/route.js +16 -0
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -0
- package/.next/server/app/manifest.webmanifest.body +1 -0
- package/.next/server/app/manifest.webmanifest.meta +1 -0
- package/.next/server/app/page.js +2 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- 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 -0
- package/.next/server/app/prs/page.js.nft.json +1 -0
- package/.next/server/app/prs/page_client-reference-manifest.js +1 -0
- package/.next/server/app/review/page.js +2 -0
- package/.next/server/app/review/page.js.nft.json +1 -0
- package/.next/server/app/review/page_client-reference-manifest.js +1 -0
- package/.next/server/app/reviews/page.js +2 -0
- package/.next/server/app/reviews/page.js.nft.json +1 -0
- package/.next/server/app/reviews/page_client-reference-manifest.js +1 -0
- package/.next/server/app/sessions/[id]/page.js +2 -0
- package/.next/server/app/sessions/[id]/page.js.nft.json +1 -0
- package/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/test-direct/page.js +2 -0
- package/.next/server/app/test-direct/page.js.nft.json +1 -0
- package/.next/server/app/test-direct/page_client-reference-manifest.js +1 -0
- package/.next/server/app/test-direct.html +1 -0
- package/.next/server/app/test-direct.meta +7 -0
- package/.next/server/app/test-direct.rsc +28 -0
- package/.next/server/app-paths-manifest.json +47 -0
- package/.next/server/chunks/1215.js +1 -0
- package/.next/server/chunks/1271.js +1 -0
- package/.next/server/chunks/2106.js +1 -0
- package/.next/server/chunks/2347.js +3 -0
- package/.next/server/chunks/2899.js +1 -0
- package/.next/server/chunks/2914.js +1 -0
- package/.next/server/chunks/4033.js +1 -0
- package/.next/server/chunks/4148.js +942 -0
- package/.next/server/chunks/4422.js +32 -0
- package/.next/server/chunks/5053.js +1 -0
- package/.next/server/chunks/5689.js +1 -0
- package/.next/server/chunks/6148.js +9 -0
- package/.next/server/chunks/6582.js +25 -0
- package/.next/server/chunks/7002.js +1 -0
- package/.next/server/chunks/7486.js +9 -0
- package/.next/server/chunks/8803.js +6 -0
- package/.next/server/chunks/9472.js +847 -0
- package/.next/server/chunks/9493.js +1 -0
- package/.next/server/chunks/9561.js +22 -0
- package/.next/server/chunks/966.js +1 -0
- package/.next/server/functions-config-manifest.json +4 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages/_app.js +1 -0
- package/.next/server/pages/_app.js.nft.json +1 -0
- package/.next/server/pages/_document.js +1 -0
- package/.next/server/pages/_document.js.nft.json +1 -0
- package/.next/server/pages/_error.js +19 -0
- package/.next/server/pages/_error.js.nft.json +1 -0
- package/.next/server/pages-manifest.json +6 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/chunks/0f200859.359176a445d2791f.js +10 -0
- package/.next/static/chunks/1089-c6d7995c7c19039a.js +1 -0
- package/.next/static/chunks/1461-af7c54935f21d56d.js +1 -0
- package/.next/static/chunks/2073-4192de0bb00cc993.js +1 -0
- package/.next/static/chunks/3764.4c736d9a181489a4.js +1 -0
- package/.next/static/chunks/4115-1a4fa80ec67a29d3.js +1 -0
- package/.next/static/chunks/4751.6c71bfc0c4520627.js +1 -0
- package/.next/static/chunks/503.f7ff284457dd9c40.js +1 -0
- package/.next/static/chunks/5204.7de7e266895bced7.js +1 -0
- package/.next/static/chunks/6835.7a29fa4b665b7c2f.js +1 -0
- package/.next/static/chunks/7008-0d9bee1bf4bfcbea.js +1 -0
- package/.next/static/chunks/7317.685aa5231218e8d3.js +1 -0
- package/.next/static/chunks/8204-7c7837ed694da99c.js +1 -0
- package/.next/static/chunks/8713-d3d663f55dc00e48.js +1 -0
- package/.next/static/chunks/8759-490573536f93f85c.js +1 -0
- package/.next/static/chunks/88a6fc35-f836b4b72df5eafa.js +1 -0
- package/.next/static/chunks/9531-a5175e55fa0db48d.js +1 -0
- package/.next/static/chunks/9876-de0c5a1a319b4e8e.js +1 -0
- package/.next/static/chunks/app/_not-found/page-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/backlog/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/browse-directory/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/filesystem/browse/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/issues/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/observability/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/orchestrators/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/projects/[id]/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/projects/reload/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/projects/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/prs/[id]/merge/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/reviews/execute/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/reviews/findings/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/reviews/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/reviews/send/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/runtime/terminal/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/kill/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/message/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/remap/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/restore/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/send/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/patches/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/setup-labels/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/spawn/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/update/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/verify/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/version/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/api/webhooks/[...slug]/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/apple-icon/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/dev/terminal-test/page-d0132109f9d8524e.js +1 -0
- package/.next/static/chunks/app/error-d632d0714b987864.js +1 -0
- package/.next/static/chunks/app/global-error-f6bef179169bcdae.js +1 -0
- package/.next/static/chunks/app/icon/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/icon-192/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/icon-512/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/layout-5cac6fe817194d7a.js +1 -0
- package/.next/static/chunks/app/loading-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/not-found-cba3f587e1f98dcb.js +1 -0
- package/.next/static/chunks/app/page-cf7bccb75990950d.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/layout-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/loading-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/page-039a93b16089ed57.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/sessions/[id]/page-043b525bedb8f0f7.js +1 -0
- package/.next/static/chunks/app/projects/[projectId]/settings/page-63572555892d7a61.js +1 -0
- package/.next/static/chunks/app/projects/layout-d43b2e38d46221bd.js +1 -0
- package/.next/static/chunks/app/prs/page-e447852fbe0c6ee4.js +1 -0
- package/.next/static/chunks/app/review/page-022b2d2c326ff413.js +1 -0
- package/.next/static/chunks/app/reviews/page-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/error-67c0d27f977a1cc1.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/loading-6add9dacf1870b4b.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/not-found-cba3f587e1f98dcb.js +1 -0
- package/.next/static/chunks/app/sessions/[id]/page-863cf8dd2c76d06d.js +1 -0
- package/.next/static/chunks/app/test-direct/page-8b80ed180c0f2f42.js +1 -0
- package/.next/static/chunks/e12b4bb0.8742134e1ac0263f.js +58 -0
- package/.next/static/chunks/framework-7060e2ac4971c604.js +1 -0
- package/.next/static/chunks/main-app-162601c3f1c01b19.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/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-f9566aaa604a1b07.js +1 -0
- package/.next/static/css/0c9b7451c2ce7c02.css +1 -0
- package/.next/static/css/7ca4e3753c1c7833.css +1 -0
- package/.next/static/css/b4a15f23f468892a.css +1 -0
- package/.next/static/css/d06ee45019116677.css +1 -0
- package/.next/static/media/91601dd83defba07-s.p.woff2 +0 -0
- package/.next/static/media/aa8291c31d4e9e54-s.p.woff2 +0 -0
- package/.next/static/pROr0laPuZIdA4NYNygMD/_buildManifest.js +1 -0
- package/.next/static/pROr0laPuZIdA4NYNygMD/_ssgManifest.js +1 -0
- package/LICENSE +22 -0
- package/dist-server/direct-terminal-ws.js +109 -0
- package/dist-server/mux-websocket.js +1162 -0
- package/dist-server/single-port-server.js +305 -0
- package/dist-server/start-all.js +150 -0
- package/dist-server/terminal-observability.js +16 -0
- package/dist-server/tmux-utils.js +301 -0
- package/next.config.js +71 -0
- package/package.json +97 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-port server (opt-in) — a thin HTTP + WebSocket proxy that puts
|
|
3
|
+
* Next.js and the `/ao-terminal-mux` WebSocket upgrade on the same public
|
|
4
|
+
* port. Spawned by start-all.ts when AO_PATH_BASED_MUX=1, in front of a
|
|
5
|
+
* Next.js process that has shifted to an internal port.
|
|
6
|
+
*
|
|
7
|
+
* ┌──────────────────────┐ HTTP ┌──────────────────────┐
|
|
8
|
+
* │ proxy on PORT │───────▶│ next start │
|
|
9
|
+
* │ (this file) │ │ on NEXT_INTERNAL_PORT │
|
|
10
|
+
* │ │ └──────────────────────┘
|
|
11
|
+
* │ │ WS upgrade /ao-terminal-mux
|
|
12
|
+
* │ │───────▶┌──────────────────────┐
|
|
13
|
+
* │ │ │ direct-terminal-ws │
|
|
14
|
+
* │ │ │ on DIRECT_TERMINAL │
|
|
15
|
+
* │ │ └──────────────────────┘
|
|
16
|
+
* └──────────────────────┘
|
|
17
|
+
*
|
|
18
|
+
* The default flow (AO_PATH_BASED_MUX unset) is unchanged: Next.js runs on
|
|
19
|
+
* PORT directly, direct-terminal-ws runs on DIRECT_TERMINAL_PORT, and the
|
|
20
|
+
* dashboard JS picks one of three URLs at connection time
|
|
21
|
+
* (see `packages/web/src/providers/MuxProvider.tsx`):
|
|
22
|
+
*
|
|
23
|
+
* 1. proxyWsPath (TERMINAL_WS_PATH) — explicit path-based routing
|
|
24
|
+
* 2. standard port (loc.port "" / 443 / 80) — `/ao-terminal-mux` on same host
|
|
25
|
+
* 3. fallback — direct connection to `:DIRECT_TERMINAL_PORT/mux`
|
|
26
|
+
*
|
|
27
|
+
* Path #1 and #3 require the operator to do something at the proxy layer
|
|
28
|
+
* (path rewrite or per-port routing). Path #2 only works if *something* is
|
|
29
|
+
* listening for the `/ao-terminal-mux` upgrade on the dashboard port. Until
|
|
30
|
+
* now, nothing was — Next.js doesn't handle upgrades, so the request fell
|
|
31
|
+
* through to its 404 handler. This server is that something.
|
|
32
|
+
*
|
|
33
|
+
* Use this when the reverse proxy in front of AO can only forward one
|
|
34
|
+
* hostname:port pair upstream (e.g. Cloudflare Tunnel pointed at one
|
|
35
|
+
* `service:` URL with no path-based ingress). With this enabled, a single
|
|
36
|
+
* proxy rule pointing at PORT is sufficient — the WS path is multiplexed
|
|
37
|
+
* onto the same TCP port and demuxed here.
|
|
38
|
+
*
|
|
39
|
+
* `createSinglePortServer()` is exported so the proxy behaviour can be
|
|
40
|
+
* exercised in tests; the bottom-of-file entrypoint wires it to env vars and
|
|
41
|
+
* process signals when this file is run directly (as start-all.ts spawns it).
|
|
42
|
+
*/
|
|
43
|
+
import { createServer, request as httpRequest, } from "node:http";
|
|
44
|
+
import { realpathSync } from "node:fs";
|
|
45
|
+
import { fileURLToPath } from "node:url";
|
|
46
|
+
const MUX_PATH = "/ao-terminal-mux";
|
|
47
|
+
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
48
|
+
/**
|
|
49
|
+
* Hop-by-hop headers (RFC 9110 §7.6.1) are meaningful only on a single
|
|
50
|
+
* transport connection and must not be forwarded by an intermediary.
|
|
51
|
+
* Forwarding e.g. a client's `Connection: close` would tear down the
|
|
52
|
+
* keep-alive socket to the upstream; a stray `Transfer-Encoding` would
|
|
53
|
+
* desync framing once the body is re-encoded.
|
|
54
|
+
*/
|
|
55
|
+
const HOP_BY_HOP = new Set([
|
|
56
|
+
"connection",
|
|
57
|
+
"keep-alive",
|
|
58
|
+
"proxy-authenticate",
|
|
59
|
+
"proxy-authorization",
|
|
60
|
+
"te",
|
|
61
|
+
"trailer",
|
|
62
|
+
"transfer-encoding",
|
|
63
|
+
"upgrade",
|
|
64
|
+
]);
|
|
65
|
+
/**
|
|
66
|
+
* Build the header set for an upstream request: strip hop-by-hop headers
|
|
67
|
+
* (including any extra ones named in the client's `Connection` header) and
|
|
68
|
+
* append the standard `X-Forwarded-*` trio so the upstream still sees the
|
|
69
|
+
* real client IP / proto / host instead of `127.0.0.1`.
|
|
70
|
+
*
|
|
71
|
+
* On the WebSocket upgrade path, `keepUpgrade` retains `Connection` and
|
|
72
|
+
* `Upgrade` — the handshake is exactly the case where those headers are
|
|
73
|
+
* load-bearing rather than hop-by-hop noise.
|
|
74
|
+
*/
|
|
75
|
+
function buildUpstreamHeaders(req, opts) {
|
|
76
|
+
const drop = new Set(HOP_BY_HOP);
|
|
77
|
+
const connection = req.headers.connection;
|
|
78
|
+
if (connection) {
|
|
79
|
+
const tokens = Array.isArray(connection) ? connection : connection.split(",");
|
|
80
|
+
for (const token of tokens) {
|
|
81
|
+
const name = token.trim().toLowerCase();
|
|
82
|
+
if (name)
|
|
83
|
+
drop.add(name);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (opts.keepUpgrade) {
|
|
87
|
+
drop.delete("connection");
|
|
88
|
+
drop.delete("upgrade");
|
|
89
|
+
}
|
|
90
|
+
const headers = {};
|
|
91
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
92
|
+
if (value === undefined)
|
|
93
|
+
continue;
|
|
94
|
+
if (drop.has(key.toLowerCase()))
|
|
95
|
+
continue;
|
|
96
|
+
headers[key] = value;
|
|
97
|
+
}
|
|
98
|
+
// X-Forwarded-*: preserve anything an outer proxy already set, then add ours.
|
|
99
|
+
const clientIp = req.socket.remoteAddress ?? "";
|
|
100
|
+
const priorFor = headers["x-forwarded-for"];
|
|
101
|
+
headers["x-forwarded-for"] = priorFor
|
|
102
|
+
? `${Array.isArray(priorFor) ? priorFor.join(", ") : String(priorFor)}, ${clientIp}`
|
|
103
|
+
: clientIp;
|
|
104
|
+
// This proxy itself terminates plain HTTP; an outer TLS proxy would have
|
|
105
|
+
// set x-forwarded-proto already, so only fill it in when absent.
|
|
106
|
+
if (headers["x-forwarded-proto"] === undefined) {
|
|
107
|
+
headers["x-forwarded-proto"] = "http";
|
|
108
|
+
}
|
|
109
|
+
if (headers["x-forwarded-host"] === undefined && req.headers.host) {
|
|
110
|
+
headers["x-forwarded-host"] = req.headers.host;
|
|
111
|
+
}
|
|
112
|
+
return headers;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Drop hop-by-hop headers from an upstream *response* before relaying it to
|
|
116
|
+
* the client. Without this the upstream's `Connection`/`Keep-Alive` would
|
|
117
|
+
* override the proxy↔client connection's own semantics — e.g. forwarding the
|
|
118
|
+
* upstream's `Connection: keep-alive` ignores a client that asked for `close`.
|
|
119
|
+
* Framing headers (`transfer-encoding`) are dropped here too so the client's
|
|
120
|
+
* `ServerResponse` re-derives framing for its own hop.
|
|
121
|
+
*/
|
|
122
|
+
function filterResponseHeaders(headers) {
|
|
123
|
+
const out = {};
|
|
124
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
125
|
+
if (value === undefined)
|
|
126
|
+
continue;
|
|
127
|
+
if (HOP_BY_HOP.has(key.toLowerCase()))
|
|
128
|
+
continue;
|
|
129
|
+
out[key] = value;
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
function tunnelUpgrade(req, clientSocket, clientHead, target) {
|
|
134
|
+
const proxyReq = httpRequest({
|
|
135
|
+
host: target.host,
|
|
136
|
+
port: target.port,
|
|
137
|
+
method: "GET",
|
|
138
|
+
path: target.path,
|
|
139
|
+
headers: buildUpstreamHeaders(req, { keepUpgrade: true }),
|
|
140
|
+
});
|
|
141
|
+
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
|
|
142
|
+
const lines = [
|
|
143
|
+
`HTTP/1.1 ${proxyRes.statusCode ?? 101} ${proxyRes.statusMessage ?? "Switching Protocols"}`,
|
|
144
|
+
];
|
|
145
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
146
|
+
if (value === undefined)
|
|
147
|
+
continue;
|
|
148
|
+
lines.push(`${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
149
|
+
}
|
|
150
|
+
lines.push("\r\n");
|
|
151
|
+
clientSocket.write(lines.join("\r\n"));
|
|
152
|
+
if (proxyHead.length > 0)
|
|
153
|
+
clientSocket.write(proxyHead);
|
|
154
|
+
if (clientHead.length > 0)
|
|
155
|
+
proxySocket.write(clientHead);
|
|
156
|
+
clientSocket.pipe(proxySocket);
|
|
157
|
+
proxySocket.pipe(clientSocket);
|
|
158
|
+
const teardown = () => {
|
|
159
|
+
clientSocket.destroy();
|
|
160
|
+
proxySocket.destroy();
|
|
161
|
+
};
|
|
162
|
+
proxySocket.on("error", teardown);
|
|
163
|
+
proxySocket.on("close", teardown);
|
|
164
|
+
clientSocket.on("error", teardown);
|
|
165
|
+
clientSocket.on("close", teardown);
|
|
166
|
+
});
|
|
167
|
+
// Upstream answered the upgrade with an ordinary response (404, 502,
|
|
168
|
+
// mid-restart, path not in its allow-list, …) instead of a 101. Without
|
|
169
|
+
// this handler the `upgrade` event never fires and the client socket
|
|
170
|
+
// hangs until its TCP timeout. Relay the response and close cleanly.
|
|
171
|
+
proxyReq.on("response", (proxyRes) => {
|
|
172
|
+
if (clientSocket.writableEnded || clientSocket.destroyed) {
|
|
173
|
+
proxyRes.destroy();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const lines = [`HTTP/1.1 ${proxyRes.statusCode ?? 502} ${proxyRes.statusMessage ?? ""}`];
|
|
177
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
178
|
+
if (value === undefined)
|
|
179
|
+
continue;
|
|
180
|
+
const lower = key.toLowerCase();
|
|
181
|
+
// Body is delimited by connection close below, so drop framing headers.
|
|
182
|
+
if (HOP_BY_HOP.has(lower) || lower === "content-length")
|
|
183
|
+
continue;
|
|
184
|
+
lines.push(`${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
185
|
+
}
|
|
186
|
+
lines.push("connection: close");
|
|
187
|
+
lines.push("\r\n");
|
|
188
|
+
clientSocket.write(lines.join("\r\n"));
|
|
189
|
+
proxyRes.pipe(clientSocket);
|
|
190
|
+
proxyRes.on("end", () => clientSocket.end());
|
|
191
|
+
});
|
|
192
|
+
proxyReq.on("error", (err) => {
|
|
193
|
+
console.error(`[single-port] upstream upgrade error (${target.host}:${target.port}${target.path}): ${err.message}`);
|
|
194
|
+
clientSocket.destroy();
|
|
195
|
+
});
|
|
196
|
+
proxyReq.end();
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Create the single-port proxy. The returned server is not yet listening —
|
|
200
|
+
* call `listen()`.
|
|
201
|
+
*/
|
|
202
|
+
export function createSinglePortServer(config) {
|
|
203
|
+
const { port, nextInternalPort, directTerminalPort } = config;
|
|
204
|
+
// Sockets handed off via the 'upgrade' event are no longer tracked by the
|
|
205
|
+
// HTTP server, so `server.closeAllConnections()` does not destroy them and
|
|
206
|
+
// `server.close()`'s callback would wait on them forever. Track them here.
|
|
207
|
+
const upgradedSockets = new Set();
|
|
208
|
+
const server = createServer((req, res) => {
|
|
209
|
+
const proxyReq = httpRequest({
|
|
210
|
+
host: "127.0.0.1",
|
|
211
|
+
port: nextInternalPort,
|
|
212
|
+
method: req.method,
|
|
213
|
+
path: req.url,
|
|
214
|
+
headers: buildUpstreamHeaders(req, { keepUpgrade: false }),
|
|
215
|
+
}, (proxyRes) => {
|
|
216
|
+
res.writeHead(proxyRes.statusCode ?? 502, filterResponseHeaders(proxyRes.headers));
|
|
217
|
+
proxyRes.pipe(res);
|
|
218
|
+
});
|
|
219
|
+
proxyReq.on("error", (err) => {
|
|
220
|
+
if (!res.headersSent) {
|
|
221
|
+
res.writeHead(502, { "content-type": "text/plain" });
|
|
222
|
+
}
|
|
223
|
+
res.end(`Bad gateway: ${err.message}`);
|
|
224
|
+
});
|
|
225
|
+
req.pipe(proxyReq);
|
|
226
|
+
});
|
|
227
|
+
server.on("upgrade", (req, socket, head) => {
|
|
228
|
+
const clientSocket = socket;
|
|
229
|
+
upgradedSockets.add(clientSocket);
|
|
230
|
+
clientSocket.once("close", () => upgradedSockets.delete(clientSocket));
|
|
231
|
+
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
232
|
+
const target = pathname === MUX_PATH
|
|
233
|
+
? { host: "127.0.0.1", port: directTerminalPort, path: "/mux" }
|
|
234
|
+
: { host: "127.0.0.1", port: nextInternalPort, path: req.url ?? "/" };
|
|
235
|
+
tunnelUpgrade(req, clientSocket, head, target);
|
|
236
|
+
});
|
|
237
|
+
return {
|
|
238
|
+
server,
|
|
239
|
+
listen() {
|
|
240
|
+
return new Promise((resolve) => server.listen(port, () => resolve()));
|
|
241
|
+
},
|
|
242
|
+
shutdown() {
|
|
243
|
+
return new Promise((resolve) => {
|
|
244
|
+
server.close(() => resolve());
|
|
245
|
+
// closeAllConnections() handles keep-alive HTTP sockets; the upgraded
|
|
246
|
+
// WS tunnels are tracked separately and destroyed here. Destroying the
|
|
247
|
+
// client socket triggers tunnelUpgrade's teardown for the upstream side.
|
|
248
|
+
server.closeAllConnections();
|
|
249
|
+
for (const socket of upgradedSockets)
|
|
250
|
+
socket.destroy();
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/** Parse and validate the proxy config from env vars, exiting on bad input. */
|
|
256
|
+
function configFromEnv() {
|
|
257
|
+
const port = parseInt(process.env.PORT ?? "3000", 10);
|
|
258
|
+
const directTerminalPort = parseInt(process.env.DIRECT_TERMINAL_PORT ?? "14801", 10);
|
|
259
|
+
const nextInternalPort = parseInt(process.env.NEXT_INTERNAL_PORT ?? "0", 10);
|
|
260
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
|
261
|
+
console.error(`[single-port] Invalid PORT: ${process.env.PORT}`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
if (!Number.isInteger(directTerminalPort) ||
|
|
265
|
+
directTerminalPort < 1 ||
|
|
266
|
+
directTerminalPort > 65_535) {
|
|
267
|
+
console.error(`[single-port] Invalid DIRECT_TERMINAL_PORT: ${process.env.DIRECT_TERMINAL_PORT}`);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
if (!Number.isInteger(nextInternalPort) ||
|
|
271
|
+
nextInternalPort < 1 ||
|
|
272
|
+
nextInternalPort > 65_535 ||
|
|
273
|
+
nextInternalPort === port) {
|
|
274
|
+
console.error(`[single-port] Invalid NEXT_INTERNAL_PORT (must differ from PORT): ${process.env.NEXT_INTERNAL_PORT}`);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
return { port, nextInternalPort, directTerminalPort };
|
|
278
|
+
}
|
|
279
|
+
function main() {
|
|
280
|
+
const config = configFromEnv();
|
|
281
|
+
const proxy = createSinglePortServer(config);
|
|
282
|
+
void proxy.listen().then(() => {
|
|
283
|
+
console.log(`[single-port] listening on ${config.port}; HTTP → 127.0.0.1:${config.nextInternalPort}; ${MUX_PATH} → 127.0.0.1:${config.directTerminalPort}/mux`);
|
|
284
|
+
});
|
|
285
|
+
const onSignal = () => {
|
|
286
|
+
void proxy.shutdown().then(() => process.exit(0));
|
|
287
|
+
setTimeout(() => process.exit(1), SHUTDOWN_TIMEOUT_MS).unref();
|
|
288
|
+
};
|
|
289
|
+
process.on("SIGINT", onSignal);
|
|
290
|
+
process.on("SIGTERM", onSignal);
|
|
291
|
+
}
|
|
292
|
+
/** True when this file was run directly (`node single-port-server.js`). */
|
|
293
|
+
function isMainModule() {
|
|
294
|
+
if (!process.argv[1])
|
|
295
|
+
return false;
|
|
296
|
+
try {
|
|
297
|
+
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (isMainModule()) {
|
|
304
|
+
main();
|
|
305
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production entry point — starts Next.js + terminal servers.
|
|
3
|
+
* Used by `athene start` when running from an npm install (no monorepo).
|
|
4
|
+
* Replaces the dev-only `concurrently` setup.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve, dirname } from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { isWindows, killProcessTree, markDaemonShutdownHandlerInstalled, spawnManagedDaemonChild, } from "@made-by-moonlight/athene-core";
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
// Resolve paths relative to the package root (one level up from dist-server/)
|
|
14
|
+
const pkgRoot = resolve(__dirname, "..");
|
|
15
|
+
const children = [];
|
|
16
|
+
markDaemonShutdownHandlerInstalled();
|
|
17
|
+
function log(label, msg) {
|
|
18
|
+
process.stdout.write(`[${label}] ${msg}\n`);
|
|
19
|
+
}
|
|
20
|
+
function spawnProcess(label, command, args, opts) {
|
|
21
|
+
let restarts = 0;
|
|
22
|
+
const maxRestarts = opts?.maxRestarts ?? 3;
|
|
23
|
+
let slotIndex = -1;
|
|
24
|
+
function launch() {
|
|
25
|
+
const child = spawnManagedDaemonChild(`dashboard:${label}`, command, args, {
|
|
26
|
+
cwd: pkgRoot,
|
|
27
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
28
|
+
env: process.env,
|
|
29
|
+
detached: !isWindows(),
|
|
30
|
+
});
|
|
31
|
+
child.stdout?.on("data", (data) => {
|
|
32
|
+
for (const line of data.toString().split("\n").filter(Boolean)) {
|
|
33
|
+
log(label, line);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
child.stderr?.on("data", (data) => {
|
|
37
|
+
for (const line of data.toString().split("\n").filter(Boolean)) {
|
|
38
|
+
log(label, line);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
child.on("exit", (code) => {
|
|
42
|
+
log(label, `exited with code ${code}`);
|
|
43
|
+
if (!shuttingDown && opts?.restart && code !== 0 && restarts < maxRestarts) {
|
|
44
|
+
restarts++;
|
|
45
|
+
log(label, `restarting (attempt ${restarts}/${maxRestarts})`);
|
|
46
|
+
const replacement = launch();
|
|
47
|
+
// Replace in-place — slot was assigned on first push
|
|
48
|
+
children[slotIndex] = replacement;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Only push on first launch; restarts replace the existing slot
|
|
52
|
+
if (slotIndex === -1) {
|
|
53
|
+
slotIndex = children.length;
|
|
54
|
+
children.push(child);
|
|
55
|
+
}
|
|
56
|
+
return child;
|
|
57
|
+
}
|
|
58
|
+
return launch();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the `next` CLI binary path.
|
|
62
|
+
* Tries the local .bin shim first (fast), then falls back to require.resolve (hoisted deps).
|
|
63
|
+
*/
|
|
64
|
+
function resolveNextBin() {
|
|
65
|
+
// On Windows, .bin/next is a POSIX shell shim that spawn() cannot execute.
|
|
66
|
+
// Skip it and go straight to the JS entry point.
|
|
67
|
+
if (!isWindows()) {
|
|
68
|
+
const localBin = resolve(pkgRoot, "node_modules", ".bin", "next");
|
|
69
|
+
if (existsSync(localBin))
|
|
70
|
+
return localBin;
|
|
71
|
+
}
|
|
72
|
+
// Resolve the actual Next.js CLI JS entry point
|
|
73
|
+
const require = createRequire(resolve(pkgRoot, "package.json"));
|
|
74
|
+
try {
|
|
75
|
+
const nextPkg = require.resolve("next/package.json");
|
|
76
|
+
return resolve(dirname(nextPkg), "dist", "bin", "next");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Last resort — rely on PATH
|
|
80
|
+
return "next";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Start Next.js production server
|
|
84
|
+
const port = process.env["PORT"] || "3000";
|
|
85
|
+
const pathBasedMux = process.env["AO_PATH_BASED_MUX"] === "1";
|
|
86
|
+
// When AO_PATH_BASED_MUX=1, single-port-server.js owns PORT and Next.js is
|
|
87
|
+
// shifted to PORT + 1000 (overridable via NEXT_INTERNAL_PORT). The proxy
|
|
88
|
+
// forwards HTTP to Next.js and tunnels `/ao-terminal-mux` WS upgrades to
|
|
89
|
+
// direct-terminal-ws. Default off — Next.js stays on PORT directly.
|
|
90
|
+
const NEXT_INTERNAL_OFFSET = 1000;
|
|
91
|
+
const nextPort = pathBasedMux
|
|
92
|
+
? (process.env["NEXT_INTERNAL_PORT"] ?? String(parseInt(port, 10) + NEXT_INTERNAL_OFFSET))
|
|
93
|
+
: port;
|
|
94
|
+
const nextBin = resolveNextBin();
|
|
95
|
+
if (isWindows() && nextBin !== "next") {
|
|
96
|
+
// On Windows, run the JS entry point via the current node binary.
|
|
97
|
+
// spawn() can't execute .js files directly on Windows.
|
|
98
|
+
spawnProcess("next", process.execPath, [nextBin, "start", "-p", nextPort]);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
spawnProcess("next", nextBin, ["start", "-p", nextPort]);
|
|
102
|
+
}
|
|
103
|
+
if (pathBasedMux) {
|
|
104
|
+
// Surface the internal port to the child so it doesn't have to re-derive
|
|
105
|
+
// the offset; pin it explicitly.
|
|
106
|
+
process.env["NEXT_INTERNAL_PORT"] = nextPort;
|
|
107
|
+
spawnProcess("single-port", process.execPath, [resolve(__dirname, "single-port-server.js")]);
|
|
108
|
+
}
|
|
109
|
+
// Start direct terminal WebSocket server (auto-restart on crash)
|
|
110
|
+
spawnProcess("direct-terminal", "node", [resolve(__dirname, "direct-terminal-ws.js")], {
|
|
111
|
+
restart: true,
|
|
112
|
+
});
|
|
113
|
+
// Graceful shutdown — send SIGTERM to children and wait for them to exit
|
|
114
|
+
let shuttingDown = false;
|
|
115
|
+
function cleanup() {
|
|
116
|
+
if (shuttingDown)
|
|
117
|
+
return;
|
|
118
|
+
shuttingDown = true;
|
|
119
|
+
let alive = children.length;
|
|
120
|
+
if (alive === 0) {
|
|
121
|
+
process.exit(0);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Force exit after 5s if children don't exit cleanly
|
|
125
|
+
const forceTimer = setTimeout(() => {
|
|
126
|
+
log("start-all", "Children did not exit in time, forcing shutdown");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}, 5000);
|
|
129
|
+
forceTimer.unref();
|
|
130
|
+
for (const child of children) {
|
|
131
|
+
child.on("exit", () => {
|
|
132
|
+
alive--;
|
|
133
|
+
if (alive <= 0) {
|
|
134
|
+
clearTimeout(forceTimer);
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const pid = child.pid;
|
|
139
|
+
if (pid) {
|
|
140
|
+
void killProcessTree(pid, "SIGTERM").catch(() => {
|
|
141
|
+
child.kill("SIGTERM");
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
child.kill("SIGTERM");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
process.on("SIGINT", cleanup);
|
|
150
|
+
process.on("SIGTERM", cleanup);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createProjectObserver, loadConfig, resolveProjectIdForSessionId, } from "@made-by-moonlight/athene-core";
|
|
2
|
+
export function createObserverContext(surface) {
|
|
3
|
+
try {
|
|
4
|
+
const config = loadConfig();
|
|
5
|
+
return {
|
|
6
|
+
config,
|
|
7
|
+
observer: createProjectObserver(config, surface),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { config: undefined, observer: undefined };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function inferProjectId(config, sessionId) {
|
|
15
|
+
return config ? resolveProjectIdForSessionId(config, sessionId) : undefined;
|
|
16
|
+
}
|