@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,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tmux utilities for terminal servers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from direct-terminal-ws.ts and terminal-websocket.ts
|
|
5
|
+
* so the logic can be properly unit tested.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
8
|
+
import { readdirSync, existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
import { isWindows } from "@made-by-moonlight/athene-core";
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
/** Session ID validation regex — alphanumeric, hyphens, underscores only */
|
|
15
|
+
export const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
16
|
+
/** Hash prefix pattern — 12-char lowercase hex, as generated by generateConfigHash */
|
|
17
|
+
const HASH_PREFIX_PATTERN = /^[a-f0-9]{12}-/;
|
|
18
|
+
/**
|
|
19
|
+
* StorageKey pattern — either bare 12-hex hash or `{hash}-{projectName}`
|
|
20
|
+
* wrapped form. The wrapped suffix comes from `basename(projectPath)` on
|
|
21
|
+
* disk so it can contain any character a filesystem path component allows
|
|
22
|
+
* (spaces, unicode, etc.); we only require that something non-empty
|
|
23
|
+
* follows the `-` separator. Security: storageKey is passed to
|
|
24
|
+
* `execFileSync` which bypasses the shell, so arbitrary characters are
|
|
25
|
+
* safe in the argv.
|
|
26
|
+
*/
|
|
27
|
+
const STORAGE_KEY_PATTERN = /^[a-f0-9]{12}(-.+)?$/;
|
|
28
|
+
const defaultFs = {
|
|
29
|
+
// Only return subdirectory names. `readdirSync` without withFileTypes
|
|
30
|
+
// includes plain files, so a stray file like `aabbccddeef0` would pass
|
|
31
|
+
// STORAGE_KEY_PATTERN and trigger an unnecessary existsSync probe.
|
|
32
|
+
readdir: (p) => readdirSync(p, { withFileTypes: true })
|
|
33
|
+
.filter((e) => e.isDirectory())
|
|
34
|
+
.map((e) => e.name),
|
|
35
|
+
exists: (p) => existsSync(p),
|
|
36
|
+
homedir,
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Find every storageKey that owns a given sessionId by scanning the AO
|
|
40
|
+
* base directory for projects whose `sessions/{sessionId}` file exists.
|
|
41
|
+
*
|
|
42
|
+
* This is the authoritative disambiguation step: tmux names alone are
|
|
43
|
+
* ambiguous when storageKey can take either the bare-hash or wrapped
|
|
44
|
+
* `{hash}-{projectName}` form. The on-disk session record is unique per
|
|
45
|
+
* storageKey so it tells us exactly which tmux name to expect.
|
|
46
|
+
*
|
|
47
|
+
* Returns all candidates (not just the first). Multiple projects can
|
|
48
|
+
* share a sessionId like `app-1`, and the caller must probe each until
|
|
49
|
+
* it finds a live tmux session — otherwise a stale metadata dir from
|
|
50
|
+
* one project could shadow the live session of another.
|
|
51
|
+
*/
|
|
52
|
+
function findStorageKeysForSession(sessionId, fs, projectId) {
|
|
53
|
+
const aoBase = join(fs.homedir(), ".agent-orchestrator");
|
|
54
|
+
let entries;
|
|
55
|
+
try {
|
|
56
|
+
entries = fs.readdir(aoBase);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const matches = [];
|
|
62
|
+
const projectMatches = [];
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (!STORAGE_KEY_PATTERN.test(entry))
|
|
65
|
+
continue;
|
|
66
|
+
const sessionFile = join(aoBase, entry, "sessions", sessionId);
|
|
67
|
+
if (fs.exists(sessionFile)) {
|
|
68
|
+
const unwrappedProjectId = entry.slice(13); // Strip "{hash}-" prefix when present.
|
|
69
|
+
if (projectId && (entry === projectId || unwrappedProjectId === projectId)) {
|
|
70
|
+
projectMatches.push(entry);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
matches.push(entry);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [...projectMatches, ...matches];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate a session ID format.
|
|
81
|
+
* Prevents path traversal, shell injection, and other attacks.
|
|
82
|
+
*/
|
|
83
|
+
export function validateSessionId(sessionId) {
|
|
84
|
+
return SESSION_ID_PATTERN.test(sessionId);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find full path to tmux binary.
|
|
88
|
+
*
|
|
89
|
+
* Checks common installation locations because node-pty's posix_spawnp
|
|
90
|
+
* doesn't reliably inherit PATH, and some Node.js environments (e.g.,
|
|
91
|
+
* launched from GUI apps) have minimal PATH.
|
|
92
|
+
*
|
|
93
|
+
* @param execFn - Injectable execFileSync for testing. Defaults to child_process.execFileSync.
|
|
94
|
+
*/
|
|
95
|
+
export function findTmux(execFn = execFileSync) {
|
|
96
|
+
if (isWindows())
|
|
97
|
+
return null;
|
|
98
|
+
const candidates = [
|
|
99
|
+
"/opt/homebrew/bin/tmux", // macOS ARM (Homebrew)
|
|
100
|
+
"/usr/local/bin/tmux", // macOS Intel (Homebrew)
|
|
101
|
+
"/usr/bin/tmux", // Linux
|
|
102
|
+
];
|
|
103
|
+
for (const p of candidates) {
|
|
104
|
+
try {
|
|
105
|
+
execFn(p, ["-V"], { timeout: 5000 });
|
|
106
|
+
return p;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return "tmux"; // Fall back to bare name
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check whether a tmux session with the given name exists.
|
|
116
|
+
*
|
|
117
|
+
* Uses `=` exact-match prefix so the lookup never falls back to tmux's
|
|
118
|
+
* default prefix matching (where "ao-1" would match "ao-15"). The caller
|
|
119
|
+
* must already have the canonical tmux session name (typically the value
|
|
120
|
+
* returned by `resolveTmuxSession`).
|
|
121
|
+
*
|
|
122
|
+
* Async: this runs from inside node-pty's `onExit` callback on every agent
|
|
123
|
+
* exit, and the WebSocket server is single-threaded. A synchronous
|
|
124
|
+
* `execFileSync` here would block the event loop — and every WebSocket
|
|
125
|
+
* connection, HTTP request, and in-flight terminal — for up to the 5 s
|
|
126
|
+
* subprocess timeout when tmux is slow to respond. Use the promisified
|
|
127
|
+
* `execFile` form instead.
|
|
128
|
+
*
|
|
129
|
+
* @returns true if the session exists, false otherwise (including tmux
|
|
130
|
+
* not running, no sessions, or any unexpected error)
|
|
131
|
+
*/
|
|
132
|
+
export async function tmuxHasSession(tmuxPath, tmuxSessionName, execFn = execFileAsync) {
|
|
133
|
+
if (!tmuxPath)
|
|
134
|
+
return false;
|
|
135
|
+
try {
|
|
136
|
+
await execFn(tmuxPath, ["has-session", "-t", `=${tmuxSessionName}`], { timeout: 5000 });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Resolve a user-facing session ID to its actual tmux session name.
|
|
145
|
+
*
|
|
146
|
+
* ao-core names tmux sessions as `{storageKey}-{sessionId}`, where
|
|
147
|
+
* storageKey is either `{12-hex}` (bare hash) or `{12-hex}-{projectName}`
|
|
148
|
+
* (legacy wrapped format). This function:
|
|
149
|
+
*
|
|
150
|
+
* 1. Tries exact match using tmux's `=` prefix syntax to prevent
|
|
151
|
+
* prefix matching (where "ao-1" would incorrectly match "ao-15").
|
|
152
|
+
* 2. Looks up the storageKey owning this sessionId on disk (under
|
|
153
|
+
* `~/.agent-orchestrator/{storageKey}/sessions/{sessionId}`) and asks
|
|
154
|
+
* tmux whether the exact `{storageKey}-{sessionId}` session exists.
|
|
155
|
+
* The on-disk check is authoritative — it avoids ambiguous suffix
|
|
156
|
+
* matches where a bare session like `{hash}-my-app-1` could be
|
|
157
|
+
* mistaken for a lookup of `app-1`.
|
|
158
|
+
* 3. Falls back to listing sessions and matching a hash-prefixed name
|
|
159
|
+
* whose remainder equals the sessionId (bare-hash only), so behavior
|
|
160
|
+
* stays correct even if the on-disk session record is absent.
|
|
161
|
+
*
|
|
162
|
+
* @param sessionId - User-facing session ID (e.g., "ao-15")
|
|
163
|
+
* @param tmuxPath - Full path to tmux binary
|
|
164
|
+
* @param execFn - Injectable execFileSync for testing. Defaults to child_process.execFileSync.
|
|
165
|
+
* @param fs - Injectable filesystem adapter for testing.
|
|
166
|
+
* @returns The actual tmux session name, or null if not found
|
|
167
|
+
*/
|
|
168
|
+
export function resolveTmuxSession(sessionId, tmuxPath, execFn = execFileSync, fs = defaultFs, projectId) {
|
|
169
|
+
if (!tmuxPath)
|
|
170
|
+
return null;
|
|
171
|
+
// Try exact match first using = prefix for exact matching (e.g., "ao-orchestrator")
|
|
172
|
+
// Without =, tmux uses prefix matching: "ao-1" would match "ao-15"
|
|
173
|
+
try {
|
|
174
|
+
execFn(tmuxPath, ["has-session", "-t", `=${sessionId}`], { timeout: 5000 });
|
|
175
|
+
return sessionId;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Not an exact match
|
|
179
|
+
}
|
|
180
|
+
// Authoritative path: find candidate storageKeys on disk, then verify
|
|
181
|
+
// each exact tmux session name with has-session. This is unambiguous
|
|
182
|
+
// even when the storageKey is wrapped (`{hash}-{projectName}`). Walk
|
|
183
|
+
// every candidate so a stale metadata dir in one project can't shadow
|
|
184
|
+
// the live session of another project with the same sessionId.
|
|
185
|
+
for (const storageKey of findStorageKeysForSession(sessionId, fs, projectId)) {
|
|
186
|
+
const tmuxName = `${storageKey}-${sessionId}`;
|
|
187
|
+
try {
|
|
188
|
+
execFn(tmuxPath, ["has-session", "-t", `=${tmuxName}`], { timeout: 5000 });
|
|
189
|
+
return tmuxName;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Session dir exists but tmux session doesn't — try next candidate
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Fallback: list sessions and match the bare-hash form only. We
|
|
196
|
+
// intentionally do NOT match by trailing suffix here — that would cause
|
|
197
|
+
// `app-1` to falsely resolve a distinct session `{hash}-my-app-1`. If a
|
|
198
|
+
// wrapped-storageKey session isn't findable on disk above, it's safer
|
|
199
|
+
// to return null than to guess.
|
|
200
|
+
try {
|
|
201
|
+
const output = execFn(tmuxPath, ["list-sessions", "-F", "#{session_name}"], {
|
|
202
|
+
timeout: 5000,
|
|
203
|
+
encoding: "utf8",
|
|
204
|
+
});
|
|
205
|
+
const sessions = output.split("\n").filter(Boolean);
|
|
206
|
+
const match = sessions.find((s) => HASH_PREFIX_PATTERN.test(s) && s.substring(13) === sessionId);
|
|
207
|
+
if (match) {
|
|
208
|
+
return match;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// tmux not running or no sessions
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Resolve a user-facing session ID to its Windows named pipe path.
|
|
218
|
+
*
|
|
219
|
+
* V2 layout (current): JSON metadata at
|
|
220
|
+
* `~/.agent-orchestrator/projects/{projectId}/sessions/{sessionId}.json`
|
|
221
|
+
* with `runtimeHandle.data.pipePath` as a top-level field.
|
|
222
|
+
*
|
|
223
|
+
* V1 layout (legacy fallback): line-delimited key=value at
|
|
224
|
+
* `~/.agent-orchestrator/{storageKey}/sessions/{sessionId}` where
|
|
225
|
+
* storageKey is bare 12-hex or `{hash}-{projectName}`. Kept so users
|
|
226
|
+
* who haven't run `athene migrate-storage` still see live sessions.
|
|
227
|
+
*
|
|
228
|
+
* When `projectId` is provided, only that project's metadata file is read.
|
|
229
|
+
* Without it (legacy callers), walks all projects and returns the first
|
|
230
|
+
* matching pipePath — which can collide when two projects share a sessionId.
|
|
231
|
+
*
|
|
232
|
+
* @returns Full pipe path (e.g., "\\\\.\\pipe\\ao-pty-win1-orchestrator"), or null
|
|
233
|
+
*/
|
|
234
|
+
export function resolvePipePath(sessionId, projectId, fs = defaultFs) {
|
|
235
|
+
if (!isWindows())
|
|
236
|
+
return null;
|
|
237
|
+
const readFile = fs.readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
238
|
+
const aoBase = join(fs.homedir(), ".agent-orchestrator");
|
|
239
|
+
const readPipeFromV2 = (project) => {
|
|
240
|
+
const sessionFile = join(aoBase, "projects", project, "sessions", `${sessionId}.json`);
|
|
241
|
+
if (!fs.exists(sessionFile))
|
|
242
|
+
return null;
|
|
243
|
+
try {
|
|
244
|
+
const meta = JSON.parse(readFile(sessionFile));
|
|
245
|
+
const pipePath = meta.runtimeHandle?.data?.pipePath;
|
|
246
|
+
return typeof pipePath === "string" && pipePath.length > 0 ? pipePath : null;
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// V2: prefer the caller's projectId when provided; otherwise walk all projects
|
|
253
|
+
const projectsDir = join(aoBase, "projects");
|
|
254
|
+
if (projectId) {
|
|
255
|
+
const pipe = readPipeFromV2(projectId);
|
|
256
|
+
if (pipe)
|
|
257
|
+
return pipe;
|
|
258
|
+
}
|
|
259
|
+
else if (fs.exists(projectsDir)) {
|
|
260
|
+
let projects;
|
|
261
|
+
try {
|
|
262
|
+
projects = fs.readdir(projectsDir);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
projects = [];
|
|
266
|
+
}
|
|
267
|
+
for (const project of projects) {
|
|
268
|
+
const pipe = readPipeFromV2(project);
|
|
269
|
+
if (pipe)
|
|
270
|
+
return pipe;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// V1 fallback: line-delimited key=value under {storageKey}/sessions/{sessionId}
|
|
274
|
+
for (const storageKey of findStorageKeysForSession(sessionId, {
|
|
275
|
+
readdir: fs.readdir,
|
|
276
|
+
exists: fs.exists,
|
|
277
|
+
homedir: fs.homedir,
|
|
278
|
+
})) {
|
|
279
|
+
const sessionFile = join(aoBase, storageKey, "sessions", sessionId);
|
|
280
|
+
let content;
|
|
281
|
+
try {
|
|
282
|
+
content = readFile(sessionFile);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const match = content.match(/^runtimeHandle=(.+)$/m);
|
|
288
|
+
if (!match)
|
|
289
|
+
continue;
|
|
290
|
+
try {
|
|
291
|
+
const handle = JSON.parse(match[1]);
|
|
292
|
+
const pipePath = handle.data?.pipePath;
|
|
293
|
+
if (pipePath && pipePath.length > 0)
|
|
294
|
+
return pipePath;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
package/next.config.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
/** @type {import('next').NextConfig} */
|
|
8
|
+
const homeDir = os.homedir().replace(/\\/g, "/");
|
|
9
|
+
const nextConfig = {
|
|
10
|
+
outputFileTracingRoot: path.join(__dirname, "../.."),
|
|
11
|
+
transpilePackages: [
|
|
12
|
+
"@made-by-moonlight/athene-plugin-agent-claude-code",
|
|
13
|
+
"@made-by-moonlight/athene-plugin-agent-codex",
|
|
14
|
+
"@made-by-moonlight/athene-plugin-agent-opencode",
|
|
15
|
+
"@made-by-moonlight/athene-plugin-runtime-tmux",
|
|
16
|
+
"@made-by-moonlight/athene-plugin-scm-github",
|
|
17
|
+
"@made-by-moonlight/athene-plugin-tracker-github",
|
|
18
|
+
"@made-by-moonlight/athene-plugin-tracker-linear",
|
|
19
|
+
"@made-by-moonlight/athene-plugin-workspace-worktree",
|
|
20
|
+
],
|
|
21
|
+
serverExternalPackages: [
|
|
22
|
+
"yaml",
|
|
23
|
+
"zod",
|
|
24
|
+
"@made-by-moonlight/athene-core",
|
|
25
|
+
"better-sqlite3",
|
|
26
|
+
],
|
|
27
|
+
webpack: (config, { isServer }) => {
|
|
28
|
+
if (process.platform === "win32") {
|
|
29
|
+
config.snapshot = {
|
|
30
|
+
...config.snapshot,
|
|
31
|
+
managedPaths: [/^(.+?[\\/]node_modules[\\/])/],
|
|
32
|
+
};
|
|
33
|
+
// Prevent nft from globbing the home directory during server file tracing.
|
|
34
|
+
// ao-core resolves paths like ~/.agent-orchestrator at runtime; nft tries to
|
|
35
|
+
// scan them at build time and hits EPERM on Windows junction points
|
|
36
|
+
// (e.g. C:\Users\<user>\Application Data).
|
|
37
|
+
if (isServer) {
|
|
38
|
+
const tracePlugin = config.plugins.find(
|
|
39
|
+
(p) => p.constructor?.name === "TraceEntryPointsPlugin"
|
|
40
|
+
);
|
|
41
|
+
if (tracePlugin) {
|
|
42
|
+
tracePlugin.traceIgnores = [
|
|
43
|
+
...(tracePlugin.traceIgnores ?? []),
|
|
44
|
+
`${homeDir}/**`,
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return config;
|
|
50
|
+
},
|
|
51
|
+
async headers() {
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
source: "/sw.js",
|
|
55
|
+
headers: [
|
|
56
|
+
{ key: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
|
|
57
|
+
{ key: "Service-Worker-Allowed", value: "/" },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Only load bundle analyzer when ANALYZE=true (dev-only dependency)
|
|
65
|
+
let config = nextConfig;
|
|
66
|
+
if (process.env.ANALYZE === "true") {
|
|
67
|
+
const { default: bundleAnalyzer } = await import("@next/bundle-analyzer");
|
|
68
|
+
config = bundleAnalyzer({ enabled: true })(nextConfig);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default config;
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@made-by-moonlight/athene-web",
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"description": "Web dashboard for Athene",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/slievr/Athene.git",
|
|
10
|
+
"directory": "packages/web"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/slievr/Athene",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/slievr/Athene/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.18.3"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
".next/server",
|
|
24
|
+
".next/static",
|
|
25
|
+
".next/*.json",
|
|
26
|
+
".next/BUILD_ID",
|
|
27
|
+
"dist-server",
|
|
28
|
+
"next.config.js"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@xterm/addon-fit": "0.12.0-beta.256",
|
|
32
|
+
"@xterm/addon-unicode11": "0.10.0-beta.256",
|
|
33
|
+
"@xterm/addon-web-links": "0.13.0-beta.256",
|
|
34
|
+
"@xterm/addon-webgl": "0.20.0-beta.255",
|
|
35
|
+
"@xterm/xterm": "6.1.0-beta.256",
|
|
36
|
+
"next": "^15.1.0",
|
|
37
|
+
"next-themes": "^0.4.6",
|
|
38
|
+
"react": "^19.0.0",
|
|
39
|
+
"react-dom": "^19.0.0",
|
|
40
|
+
"server-only": "^0.0.1",
|
|
41
|
+
"ws": "^8.19.0",
|
|
42
|
+
"@made-by-moonlight/athene-core": "0.9.1",
|
|
43
|
+
"@made-by-moonlight/athene-plugin-agent-claude-code": "0.9.1",
|
|
44
|
+
"@made-by-moonlight/athene-plugin-agent-codex": "0.9.1",
|
|
45
|
+
"@made-by-moonlight/athene-plugin-agent-grok": "0.1.3",
|
|
46
|
+
"@made-by-moonlight/athene-plugin-agent-cursor": "0.9.1",
|
|
47
|
+
"@made-by-moonlight/athene-plugin-agent-kimicode": "0.9.1",
|
|
48
|
+
"@made-by-moonlight/athene-plugin-agent-opencode": "0.9.1",
|
|
49
|
+
"@made-by-moonlight/athene-plugin-runtime-tmux": "0.9.1",
|
|
50
|
+
"@made-by-moonlight/athene-plugin-scm-github": "0.9.1",
|
|
51
|
+
"@made-by-moonlight/athene-plugin-tracker-linear": "0.9.1",
|
|
52
|
+
"@made-by-moonlight/athene-plugin-workspace-worktree": "0.9.1",
|
|
53
|
+
"@made-by-moonlight/athene-plugin-tracker-github": "0.9.1",
|
|
54
|
+
"@made-by-moonlight/athene-plugin-runtime-process": "0.9.1"
|
|
55
|
+
},
|
|
56
|
+
"optionalDependencies": {
|
|
57
|
+
"node-pty": "^1.1.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@next/bundle-analyzer": "^15.1.0",
|
|
61
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
62
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
63
|
+
"@testing-library/react": "^16.1.0",
|
|
64
|
+
"@types/react": "^19.0.0",
|
|
65
|
+
"@types/react-dom": "^19.0.0",
|
|
66
|
+
"@types/ws": "^8.18.1",
|
|
67
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
68
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
69
|
+
"concurrently": "^9.2.1",
|
|
70
|
+
"jsdom": "^25.0.0",
|
|
71
|
+
"node-gyp": "^12.2.0",
|
|
72
|
+
"playwright": "^1.49.0",
|
|
73
|
+
"rimraf": "^6.0.0",
|
|
74
|
+
"tailwindcss": "^4.0.0",
|
|
75
|
+
"tsx": "^4.19.0",
|
|
76
|
+
"typescript": "^5.7.0",
|
|
77
|
+
"vite": "^6.4.2",
|
|
78
|
+
"vitest": "^3.2.4"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"dev": "concurrently \"npm:dev:next\" \"npm:dev:direct-terminal\"",
|
|
82
|
+
"dev:next": "next dev -p ${PORT:-3000}",
|
|
83
|
+
"dev:direct-terminal": "node scripts/dev-direct-terminal.mjs",
|
|
84
|
+
"prebuild": "node scripts/guard-production-artifact-clean.mjs && rimraf .next dist-server",
|
|
85
|
+
"build": "next build && tsc -p tsconfig.server.json && node scripts/stamp-version.js",
|
|
86
|
+
"start": "next start",
|
|
87
|
+
"start:all": "node dist-server/start-all.js",
|
|
88
|
+
"dev:optimized": "rimraf .next dist-server && next build && tsc -p tsconfig.server.json && node dist-server/start-all.js",
|
|
89
|
+
"typecheck": "tsc --noEmit",
|
|
90
|
+
"test": "vitest run",
|
|
91
|
+
"test:e2e:review": "tsx e2e/review-board.e2e.ts",
|
|
92
|
+
"test:watch": "vitest",
|
|
93
|
+
"clean": "node scripts/guard-production-artifact-clean.mjs && rimraf .next dist-server",
|
|
94
|
+
"screenshot": "tsx e2e/screenshot.ts",
|
|
95
|
+
"screenshot:install": "npx playwright install chromium"
|
|
96
|
+
}
|
|
97
|
+
}
|