@rsalmn/extremerouter 0.8.2 ā 0.8.3
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/README.md +89 -89
- package/app/.next-cli-build/BUILD_ID +1 -1
- package/app/.next-cli-build/app-path-routes-manifest.json +9 -9
- package/app/.next-cli-build/build-manifest.json +4 -4
- package/app/.next-cli-build/prerender-manifest.json +3 -3
- package/app/.next-cli-build/react-loadable-manifest.json +1 -13
- package/app/.next-cli-build/required-server-files.json +23 -23
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/alerts/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/alerts/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/alerts/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/health/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/health/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/health/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/overview/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/overview/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/overview/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/playground/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/playground/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/playground/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/proxy-pools/page.js +3 -3
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/proxy-pools/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/swarm/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/swarm/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/swarm/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/token-saver/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/token-saver/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/token-saver/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page.js +2 -2
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/_global-error/page.js +3 -3
- package/app/.next-cli-build/server/app/_global-error/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/_global-error.html +1 -1
- package/app/.next-cli-build/server/app/_global-error.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_not-found/page.js +2 -2
- package/app/.next-cli-build/server/app/_not-found/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/_not-found.html +1 -1
- package/app/.next-cli-build/server/app/_not-found.rsc +7 -6
- package/app/.next-cli-build/server/app/_not-found.segments/_full.segment.rsc +7 -6
- package/app/.next-cli-build/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_not-found.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/_not-found.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/api/alerts/test/route.js +2 -2
- package/app/.next-cli-build/server/app/api/alerts/test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/alerts/test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/login/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/login/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/logout/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/callback/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/callback/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/callback/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/start/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/start/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/start/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/test/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/oidc/test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/reset-password/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/reset-password/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/reset-password/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/status/route.js +1 -1
- package/app/.next-cli-build/server/app/api/auth/status/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/breaker/route.js +1 -1
- package/app/.next-cli-build/server/app/api/breaker/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/breaker/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cache/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cache/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cache/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/all-statuses/route.js +4 -4
- package/app/.next-cli-build/server/app/api/cli-tools/all-statuses/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/all-statuses/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/alias/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/alias/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/alias/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/claude-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/claude-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/claude-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cline-settings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cline-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cline-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/codex-settings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/codex-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/codex-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/copilot-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/copilot-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/copilot-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-registry/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-registry/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-registry/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-tools/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-tools/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-tools/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/cowork-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/deepseek-tui-settings/route.js +3 -3
- package/app/.next-cli-build/server/app/api/cli-tools/deepseek-tui-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/deepseek-tui-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/droid-settings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/droid-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/droid-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/hermes-settings/route.js +3 -3
- package/app/.next-cli-build/server/app/api/cli-tools/hermes-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/hermes-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/jcode-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/jcode-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/jcode-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/kilo-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/kilo-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/kilo-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/openclaw-settings/route.js +2 -2
- package/app/.next-cli-build/server/app/api/cli-tools/openclaw-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/openclaw-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/opencode-settings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/opencode-settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/cli-tools/opencode-settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/[id]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/[id]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/combos/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/route.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/combos/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/validate-roles/route.js +1 -1
- package/app/.next-cli-build/server/app/api/combos/validate-roles/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/combos/validate-roles/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/dashboard/stream/route.js +2 -2
- package/app/.next-cli-build/server/app/api/dashboard/stream/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/dashboard/stream/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/headroom/start/route.js +1 -1
- package/app/.next-cli-build/server/app/api/headroom/start/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/headroom/start/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/headroom/status/route.js +1 -1
- package/app/.next-cli-build/server/app/api/headroom/status/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/headroom/status/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/headroom/stop/route.js +2 -2
- package/app/.next-cli-build/server/app/api/headroom/stop/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/headroom/stop/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/health/providers/route.js +1 -1
- package/app/.next-cli-build/server/app/api/health/providers/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/health/providers/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/health/route.js +1 -1
- package/app/.next-cli-build/server/app/api/health/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/health/stream/route.js +2 -2
- package/app/.next-cli-build/server/app/api/health/stream/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/health/stream/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/init/route.js +1 -1
- package/app/.next-cli-build/server/app/api/init/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/keys/[id]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/keys/[id]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/keys/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/keys/route.js +1 -1
- package/app/.next-cli-build/server/app/api/keys/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/keys/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/locale/route.js +1 -1
- package/app/.next-cli-build/server/app/api/locale/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/locale/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/message/route.js +2 -2
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/message/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/message/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/sse/route.js +2 -2
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/sse/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/mcp/[plugin]/sse/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/deepgram/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/deepgram/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/deepgram/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/elevenlabs/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/elevenlabs/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/elevenlabs/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/inworld/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/inworld/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/inworld/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/minimax/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/minimax/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/minimax/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/media-providers/tts/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/alias/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/alias/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/alias/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/availability/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/availability/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/availability/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/custom/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/custom/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/custom/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/disabled/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/disabled/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/disabled/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/models/test/route.js +1 -1
- package/app/.next-cli-build/server/app/api/models/test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/models/test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/1min/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/1min/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/1min/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/[provider]/[action]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/[provider]/[action]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/[provider]/[action]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/bulk-import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/bulk-import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/bulk-import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/import-token/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/import-token/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/codex/import-token/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/cursor/auto-import/route.js +2 -2
- package/app/.next-cli-build/server/app/api/oauth/cursor/auto-import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/cursor/auto-import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/cursor/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/cursor/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/cursor/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/gitlab/pat/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/gitlab/pat/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/gitlab/pat/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/iflow/cookie/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/iflow/cookie/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/iflow/cookie/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/api-key/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/api-key/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/api-key/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/auto-import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/auto-import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/auto-import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import-cli-proxy/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import-cli-proxy/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/import-cli-proxy/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-authorize/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-authorize/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-authorize/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-exchange/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-exchange/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/kiro/social-exchange/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/qwen/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/qwen/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/qwen/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/wp-studio/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/wp-studio/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/wp-studio/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/zed/auto-import/route.js +38 -2
- package/app/.next-cli-build/server/app/api/oauth/zed/auto-import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/zed/auto-import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/zed/import/route.js +1 -1
- package/app/.next-cli-build/server/app/api/oauth/zed/import/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/oauth/zed/import/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/pricing/route.js +1 -1
- package/app/.next-cli-build/server/app/api/pricing/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/pricing/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/[id]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/[id]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/route.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/validate/route.js +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/validate/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/provider-nodes/validate/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/agnes-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/agnes-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/agnes-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/freebuff-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/freebuff-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/freebuff-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/health-timeline/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/health-timeline/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/health-timeline/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/inxora-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/inxora-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/inxora-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-api-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-api-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-api-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/onemin-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/qwencloud-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/qwencloud-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/qwencloud-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test-models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test-models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/test-models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/v0-profile/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/v0-profile/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/v0-profile/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/vault-pool/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/vault-pool/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/vault-pool/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/zenmux-plans/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/zenmux-plans/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/[id]/zenmux-plans/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/client/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/client/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/client/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/kilo/free-models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/kilo/free-models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/kilo/free-models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/suggested-models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/suggested-models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/suggested-models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/test-batch/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/test-batch/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/test-batch/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/validate/route.js +1 -1
- package/app/.next-cli-build/server/app/api/providers/validate/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/providers/validate/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/test/route.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/[id]/test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/cloudflare-deploy/route.js +2 -2
- package/app/.next-cli-build/server/app/api/proxy-pools/cloudflare-deploy/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/cloudflare-deploy/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/deno-deploy/route.js +2 -2
- package/app/.next-cli-build/server/app/api/proxy-pools/deno-deploy/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/deno-deploy/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/route.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/vercel-deploy/route.js +2 -2
- package/app/.next-cli-build/server/app/api/proxy-pools/vercel-deploy/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/proxy-pools/vercel-deploy/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/pxpipe/install/route.js +2 -2
- package/app/.next-cli-build/server/app/api/pxpipe/install/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/pxpipe/install/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/pxpipe/status/route.js +2 -2
- package/app/.next-cli-build/server/app/api/pxpipe/status/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/pxpipe/status/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/database/route.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/database/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/settings/database/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/proxy-test/route.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/proxy-test/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/settings/proxy-test/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/require-login/route.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/require-login/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/settings/require-login/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/settings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/shutdown/route.js +1 -1
- package/app/.next-cli-build/server/app/api/shutdown/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/swarm/active/route.js +1 -1
- package/app/.next-cli-build/server/app/api/swarm/active/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/swarm/active/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/swarm/stream/route.js +2 -2
- package/app/.next-cli-build/server/app/api/swarm/stream/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/swarm/stream/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tags/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tags/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tags/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/console-logs/route.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/console-logs/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/console-logs/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/console-logs/stream/route.js +2 -2
- package/app/.next-cli-build/server/app/api/translator/console-logs/stream/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/console-logs/stream/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/load/route.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/load/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/load/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/save/route.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/save/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/save/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/send/route.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/send/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/send/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/translate/route.js +1 -1
- package/app/.next-cli-build/server/app/api/translator/translate/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/translator/translate/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/disable/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/disable/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/disable/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/enable/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/enable/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/enable/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/status/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/status/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/status/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-check/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-check/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-check/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-disable/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-disable/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-disable/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-enable/route.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-enable/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-enable/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-install/route.js +2 -2
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-install/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/tunnel/tailscale-install/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/codex-reset-credits/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/codex-reset-credits/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/codex-reset-credits/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/[connectionId]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/chart/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/chart/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/chart/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/history/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/history/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/history/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/leaderboard/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/leaderboard/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/leaderboard/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/logs/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/logs/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/logs/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/meta/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/meta/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/meta/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/providers/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/providers/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/providers/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-details/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-details/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-details/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-logs/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-logs/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/request-logs/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/retry-stats/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/retry-stats/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/retry-stats/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/stats/route.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/stats/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/stats/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/usage/stream/route.js +2 -2
- package/app/.next-cli-build/server/app/api/usage/stream/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/usage/stream/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/api/chat/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/api/chat/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/api/chat/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/speech/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/speech/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/speech/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/transcriptions/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/transcriptions/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/transcriptions/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/voices/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/voices/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/audio/voices/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/chat/completions/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/chat/completions/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/chat/completions/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/embeddings/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/embeddings/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/embeddings/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/images/generations/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/images/generations/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/images/generations/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/count_tokens/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/count_tokens/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/count_tokens/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/messages/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/[kind]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/[kind]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/[kind]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/info/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/info/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/info/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/compact/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/compact/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/compact/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/responses/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/search/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/search/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/search/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/web/fetch/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1/web/fetch/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1/web/fetch/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/[...path]/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/[...path]/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/[...path]/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/route.js +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/v1beta/models/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/version/route.js +1 -1
- package/app/.next-cli-build/server/app/api/version/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/version/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/version/shutdown/route.js +1 -1
- package/app/.next-cli-build/server/app/api/version/shutdown/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/version/shutdown/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/api/version/update/route.js +1 -1
- package/app/.next-cli-build/server/app/api/version/update/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/api/version/update/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/callback/page.js +2 -2
- package/app/.next-cli-build/server/app/callback/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/callback/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/callback.html +1 -1
- package/app/.next-cli-build/server/app/callback.rsc +8 -7
- package/app/.next-cli-build/server/app/callback.segments/_full.segment.rsc +8 -7
- package/app/.next-cli-build/server/app/callback.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/callback.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/callback.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/callback.segments/callback/__PAGE__.segment.rsc +2 -2
- package/app/.next-cli-build/server/app/callback.segments/callback.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing/page.js +2 -2
- package/app/.next-cli-build/server/app/dashboard/settings/pricing/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.html +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.rsc +8 -7
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +8 -7
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +2 -2
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/favicon.ico/route.js +1 -1
- package/app/.next-cli-build/server/app/favicon.ico/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/index.html +1 -1
- package/app/.next-cli-build/server/app/index.rsc +7 -6
- package/app/.next-cli-build/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/index.segments/_full.segment.rsc +7 -6
- package/app/.next-cli-build/server/app/index.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/index.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/index.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/landing/page.js +2 -2
- package/app/.next-cli-build/server/app/landing/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/landing/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/landing.html +1 -1
- package/app/.next-cli-build/server/app/landing.rsc +8 -7
- package/app/.next-cli-build/server/app/landing.segments/_full.segment.rsc +8 -7
- package/app/.next-cli-build/server/app/landing.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/landing.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/landing.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/landing.segments/landing/__PAGE__.segment.rsc +2 -2
- package/app/.next-cli-build/server/app/landing.segments/landing.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/login/page.js +2 -2
- package/app/.next-cli-build/server/app/login/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/login/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/login.html +1 -1
- package/app/.next-cli-build/server/app/login.rsc +8 -7
- package/app/.next-cli-build/server/app/login.segments/_full.segment.rsc +8 -7
- package/app/.next-cli-build/server/app/login.segments/_head.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/login.segments/_index.segment.rsc +6 -6
- package/app/.next-cli-build/server/app/login.segments/_tree.segment.rsc +3 -2
- package/app/.next-cli-build/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/app/.next-cli-build/server/app/login.segments/login.segment.rsc +1 -1
- package/app/.next-cli-build/server/app/manifest.webmanifest/route.js +2 -2
- package/app/.next-cli-build/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app/page.js +2 -2
- package/app/.next-cli-build/server/app/page.js.nft.json +1 -1
- package/app/.next-cli-build/server/app/page_client-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/app-paths-manifest.json +9 -9
- package/app/.next-cli-build/server/chunks/1212.js +1 -0
- package/app/.next-cli-build/server/chunks/1241.js +1 -1
- package/app/.next-cli-build/server/chunks/1350.js +1 -1
- package/app/.next-cli-build/server/chunks/1719.js +1 -1
- package/app/.next-cli-build/server/chunks/212.js +1 -1
- package/app/.next-cli-build/server/chunks/2231.js +1 -1
- package/app/.next-cli-build/server/chunks/319.js +3 -3
- package/app/.next-cli-build/server/chunks/{2926.js ā 3437.js} +9 -9
- package/app/.next-cli-build/server/chunks/3735.js +1 -1
- package/app/.next-cli-build/server/chunks/4046.js +1 -0
- package/app/.next-cli-build/server/chunks/5330.js +66 -66
- package/app/.next-cli-build/server/chunks/5612.js +1 -1
- package/app/.next-cli-build/server/chunks/5615.js +1 -1
- package/app/.next-cli-build/server/chunks/5852.js +1 -1
- package/app/.next-cli-build/server/chunks/5961.js +1 -1
- package/app/.next-cli-build/server/chunks/6070.js +1 -1
- package/app/.next-cli-build/server/chunks/{7152.js ā 6482.js} +2 -2
- package/app/.next-cli-build/server/chunks/6686.js +1 -1
- package/app/.next-cli-build/server/chunks/6777.js +1 -1
- package/app/.next-cli-build/server/chunks/7187.js +1 -1
- package/app/.next-cli-build/server/chunks/7218.js +1 -0
- package/app/.next-cli-build/server/chunks/7410.js +1 -0
- package/app/.next-cli-build/server/chunks/7566.js +3 -0
- package/app/.next-cli-build/server/chunks/7774.js +18 -18
- package/app/.next-cli-build/server/chunks/7872.js +1 -1
- package/app/.next-cli-build/server/chunks/7965.js +1 -1
- package/app/.next-cli-build/server/chunks/8238.js +1 -1
- package/app/.next-cli-build/server/chunks/8271.js +1 -1
- package/app/.next-cli-build/server/chunks/8520.js +1 -1
- package/app/.next-cli-build/server/chunks/8802.js +2 -0
- package/app/.next-cli-build/server/chunks/8895.js +6 -6
- package/app/.next-cli-build/server/chunks/{4502.js ā 8955.js} +2 -2
- package/app/.next-cli-build/server/chunks/9422.js +1 -1
- package/app/.next-cli-build/server/chunks/9609.js +1 -1
- package/app/.next-cli-build/server/chunks/9703.js +1 -1
- package/app/.next-cli-build/server/chunks/9903.js +1 -1
- package/app/.next-cli-build/server/middleware-build-manifest.js +1 -1
- package/app/.next-cli-build/server/middleware-react-loadable-manifest.js +1 -1
- package/app/.next-cli-build/server/middleware.js +5 -5
- package/app/.next-cli-build/server/next-font-manifest.js +1 -1
- package/app/.next-cli-build/server/next-font-manifest.json +1 -1
- package/app/.next-cli-build/server/pages/404.html +1 -1
- package/app/.next-cli-build/server/pages/500.html +1 -1
- package/app/.next-cli-build/server/server-reference-manifest.js +1 -1
- package/app/.next-cli-build/server/server-reference-manifest.json +1 -1
- package/app/.next-cli-build/static/chunks/{1321-aa0d45362a7ebc08.js ā 1321-5792bb21361dcd65.js} +1 -1
- package/app/.next-cli-build/static/chunks/705-cfe5cd65ea2dc6f1.js +1 -0
- package/app/.next-cli-build/static/chunks/8105-37d0f6450b27ac6b.js +1 -0
- package/app/.next-cli-build/static/chunks/{8720-bbafc2ad7d186ebd.js ā 8982-6d1803817450bcd4.js} +2 -2
- package/app/.next-cli-build/static/chunks/981-777b2911f938c030.js +3 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/alerts/{page-920869c698a8b3cf.js ā page-c263cd784e69e6f1.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/basic-chat/{page-75247ad22e97f7cc.js ā page-ecd3a85a40e51c66.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/cli-tools/[toolId]/page-3c3074cf3f61df46.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/cli-tools/{page-8b043b8296199edb.js ā page-f7cdc992cb310077.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/combos/page-648e105b4f37fef0.js +3 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/console-log/page-959b3ee3c662de33.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/endpoint/page-5689c88f2746c37a.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/health/{page-ec1dc4e44702257d.js ā page-133cf1aa2ce53c87.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/media-providers/[kind]/[id]/{page-b635b8b051acb5a5.js ā page-3828106aa4f490c1.js} +2 -2
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/media-providers/[kind]/{page-d9958dfec1d2e28e.js ā page-e086fed7677d4852.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/media-providers/combo/[id]/{page-a36b3b632d16f4cb.js ā page-f36e5ac11783fd1e.js} +2 -2
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/media-providers/web/page-e794909abbc7a09a.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/mitm/page-1d972fd3baee7474.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/overview/page-2dae733c8e1582bf.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/page-7325c65533e4cf60.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/playground/{page-dc8e49b1fe778d9c.js ā page-e7ba910803086460.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/profile/page-0f877a8a2550a1ea.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/providers/[id]/{page-2e82495d63f499e9.js ā page-201ce34cf7a0a9ed.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/providers/new/{page-8965749fb7d0fac3.js ā page-bcf9d97ab4dc20d9.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/providers/page-5b11b61863963cad.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/proxy-pools/{page-843c810c0818ce1b.js ā page-58d8d0fa5523ff95.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/quota/{page-9a44fa1600f3ff42.js ā page-0be4cac4f06ef3e2.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/skills/{page-97c920b552343488.js ā page-a8b151d5ae9004ea.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/swarm/page-594a59006196a03a.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/token-saver/page-d57c129c03c9cb36.js +1 -0
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/translator/{page-a8af04fbd6293d2f.js ā page-61d162d7a7f88a11.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/usage/{page-bd421513fd2dc28a.js ā page-483f01fd35d212f5.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/{layout-9144cc5f6a208199.js ā layout-d62e22634bbd6c94.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/_not-found/{page-732ac0fa56005849.js ā page-0161671512f52984.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/callback/{page-4e8ab9d9128acffb.js ā page-20e990994d8da6d3.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/dashboard/settings/pricing/{page-d4eb2ba84eef2143.js ā page-bb9f307074c5c23a.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/landing/{page-ade86a48ca5749f4.js ā page-468c5cb1d127c02a.js} +1 -1
- package/app/.next-cli-build/static/chunks/app/layout-10bff7b57a2a3c2c.js +1 -0
- package/app/.next-cli-build/static/chunks/app/login/page-32ab5d7c75651107.js +1 -0
- package/app/.next-cli-build/static/chunks/main-ab3c4d38db15f747.js +5 -0
- package/app/.next-cli-build/static/chunks/main-app-f661afc91c813cfe.js +1 -0
- package/app/.next-cli-build/static/chunks/next/dist/client/components/builtin/global-error-8fecb2690f5e9ae2.js +1 -0
- package/app/.next-cli-build/static/chunks/webpack-c2bac8d80aa27536.js +1 -0
- package/app/.next-cli-build/static/css/27a3eadf22014a5b.css +1 -0
- package/app/custom-server.js +9 -0
- package/app/package.json +5 -4
- package/app/public/i18n/literals/ar.json +195 -195
- package/app/public/i18n/literals/bn.json +195 -195
- package/app/public/i18n/literals/cs.json +195 -195
- package/app/public/i18n/literals/da.json +195 -195
- package/app/public/i18n/literals/de.json +195 -195
- package/app/public/i18n/literals/el.json +195 -195
- package/app/public/i18n/literals/es.json +195 -195
- package/app/public/i18n/literals/fi.json +195 -195
- package/app/public/i18n/literals/fr.json +195 -195
- package/app/public/i18n/literals/he.json +195 -195
- package/app/public/i18n/literals/hi.json +195 -195
- package/app/public/i18n/literals/hu.json +195 -195
- package/app/public/i18n/literals/id.json +195 -195
- package/app/public/i18n/literals/it.json +195 -195
- package/app/public/i18n/literals/ja.json +195 -195
- package/app/public/i18n/literals/ko.json +195 -195
- package/app/public/i18n/literals/nl.json +195 -195
- package/app/public/i18n/literals/no.json +195 -195
- package/app/public/i18n/literals/pl.json +195 -195
- package/app/public/i18n/literals/pt-BR.json +195 -195
- package/app/public/i18n/literals/pt-PT.json +195 -195
- package/app/public/i18n/literals/ro.json +195 -195
- package/app/public/i18n/literals/ru.json +195 -195
- package/app/public/i18n/literals/sv.json +195 -195
- package/app/public/i18n/literals/th.json +195 -195
- package/app/public/i18n/literals/tl.json +195 -195
- package/app/public/i18n/literals/tr.json +195 -195
- package/app/public/i18n/literals/uk.json +195 -195
- package/app/public/i18n/literals/ur.json +195 -195
- package/app/public/i18n/literals/vi.json +195 -195
- package/app/public/i18n/literals/zh-CN.json +773 -773
- package/app/public/i18n/literals/zh-TW.json +195 -195
- package/app/public/providers/agnes-api.svg +6 -6
- package/app/public/providers/agnes-web.svg +6 -6
- package/app/public/providers/alibaba-cn.svg +11 -11
- package/app/public/providers/alibaba.svg +11 -11
- package/app/public/providers/bynara.svg +22 -22
- package/app/public/providers/devin.svg +5 -5
- package/app/public/providers/featherless.svg +8 -8
- package/app/public/providers/forge.svg +9 -9
- package/app/public/providers/freebuff-web.svg +6 -6
- package/app/public/providers/hcnsec.svg +10 -10
- package/app/public/providers/inxorastudio-web.svg +4 -4
- package/app/public/providers/inxorastudio.svg +4 -4
- package/app/public/providers/moonshot.svg +8 -8
- package/app/public/providers/openvecta.svg +7 -7
- package/app/public/providers/perplexity-agent.svg +13 -13
- package/app/public/providers/qwen-cloud.svg +1 -1
- package/app/public/providers/qwencloud.svg +6 -6
- package/app/public/providers/stepfun.svg +53 -53
- package/app/public/providers/tokenrouter.svg +15 -15
- package/app/public/providers/wp-studio.svg +11 -11
- package/app/public/providers/zed.svg +11 -11
- package/app/public/providers/zenmux-free.svg +9 -9
- package/app/server.js +1 -1
- package/app/src/lib/db/adapters/sqljsAdapter.js +115 -115
- package/app/src/lib/db/migrate.js +286 -286
- package/app/src/lib/network/pickProxyPoolId.js +38 -38
- package/app/src/lib/updater/updater.js +235 -235
- package/app/src/mitm/server.js +2 -2
- package/cli.js +830 -830
- package/package.json +66 -66
- package/src/cli/api/client.js +556 -556
- package/src/cli/menus/cliTools.js +618 -618
- package/src/cli/terminalUI.js +121 -121
- package/src/cli/tray/autostart.js +306 -306
- package/src/cli/tray/tray.js +322 -322
- package/src/cli/tray/tray.ps1 +130 -130
- package/app/.next-cli-build/server/chunks/1051.js +0 -1
- package/app/.next-cli-build/server/chunks/1216.js +0 -3
- package/app/.next-cli-build/server/chunks/541.js +0 -1
- package/app/.next-cli-build/server/chunks/6316.js +0 -2
- package/app/.next-cli-build/server/chunks/9140.js +0 -1
- package/app/.next-cli-build/server/chunks/9630.js +0 -1
- package/app/.next-cli-build/static/chunks/1696-bafe4b44a7a0439e.js +0 -3
- package/app/.next-cli-build/static/chunks/1966.b8f83f7ba376c962.js +0 -1
- package/app/.next-cli-build/static/chunks/3899.3f761f7e3a944e8c.js +0 -1
- package/app/.next-cli-build/static/chunks/705-b7d7b61f41806f0d.js +0 -1
- package/app/.next-cli-build/static/chunks/8105-204edc780a329497.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/cli-tools/[toolId]/page-cd77e53dbcd46b6c.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/combos/page-dbf52954681b093c.js +0 -3
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/console-log/page-a8c4d8c681e19868.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/endpoint/page-408845bd809806a6.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/media-providers/web/page-92a0799a23d869b3.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/mitm/page-a67cd1c5f77da355.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/overview/page-262fa6911888d4f1.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/page-f644da989b8878c3.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/profile/page-aa26b25a26b5dac8.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/providers/page-96d3eb0dfde0711b.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/swarm/page-9739651f4f19cec6.js +0 -1
- package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/token-saver/page-2f3b8761d7b32ec0.js +0 -1
- package/app/.next-cli-build/static/chunks/app/layout-16b611f4f5717ccd.js +0 -1
- package/app/.next-cli-build/static/chunks/app/login/page-0831b035aa4c0f17.js +0 -1
- package/app/.next-cli-build/static/chunks/main-28015457a328b97c.js +0 -5
- package/app/.next-cli-build/static/chunks/main-app-705d8c306f358a5f.js +0 -1
- package/app/.next-cli-build/static/chunks/next/dist/client/components/builtin/global-error-22e44aa316880902.js +0 -1
- package/app/.next-cli-build/static/chunks/webpack-a990cabb6976f421.js +0 -1
- package/app/.next-cli-build/static/css/68d66160ca549ddc.css +0 -1
- package/app/cli/.build-home/AppData/Local/npm-cache/_update-notifier-last-checked +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/.migrated-from +0 -1
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/backups/schema-3-to-4-0.8.0-20260802-152036/data.sqlite +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/backups/upgrade-0.7.8-to-0.7.9-0.7.9-20260729-181011/data.sqlite +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/backups/upgrade-0.7.9-to-0.8.0-0.8.0-20260729-213634/data.sqlite +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/backups/upgrade-0.8.0-to-0.8.1-0.8.1-20260802-152517/data.sqlite +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/data.sqlite +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/data.sqlite-shm +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/db/data.sqlite-wal +0 -0
- package/app/cli/.build-home/AppData/Roaming/extremerouter/jwt-secret +0 -1
- package/app/cli/.build-home/AppData/Roaming/extremerouter/machine-id +0 -1
- package/app/cli/.build-home/AppData/Roaming/nextjs-nodejs/Config/config.json +0 -7
- /package/app/.next-cli-build/static/{S-pbWChrUiCkwFyfx4Su3 ā j7bBgtavtSkTBPfbmetr9}/_buildManifest.js +0 -0
- /package/app/.next-cli-build/static/{S-pbWChrUiCkwFyfx4Su3 ā j7bBgtavtSkTBPfbmetr9}/_ssgManifest.js +0 -0
package/cli.js
CHANGED
|
@@ -1,830 +1,830 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { spawn, exec, execSync } = require("child_process");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
const https = require("https");
|
|
7
|
-
const os = require("os");
|
|
8
|
-
|
|
9
|
-
// Native spinner - no external dependency
|
|
10
|
-
function createSpinner(text) {
|
|
11
|
-
const frames = ["ā ", "ā ", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ", "ā "];
|
|
12
|
-
let i = 0;
|
|
13
|
-
let interval = null;
|
|
14
|
-
let currentText = text;
|
|
15
|
-
return {
|
|
16
|
-
start() {
|
|
17
|
-
if (process.stdout.isTTY) {
|
|
18
|
-
process.stdout.write(`\r${frames[0]} ${currentText}`);
|
|
19
|
-
interval = setInterval(() => {
|
|
20
|
-
process.stdout.write(`\r${frames[i++ % frames.length]} ${currentText}`);
|
|
21
|
-
}, 80);
|
|
22
|
-
}
|
|
23
|
-
return this;
|
|
24
|
-
},
|
|
25
|
-
stop() {
|
|
26
|
-
if (interval) {
|
|
27
|
-
clearInterval(interval);
|
|
28
|
-
interval = null;
|
|
29
|
-
}
|
|
30
|
-
if (process.stdout.isTTY) {
|
|
31
|
-
process.stdout.write("\r\x1b[K");
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
succeed(msg) {
|
|
35
|
-
this.stop();
|
|
36
|
-
console.log(`ā
${msg}`);
|
|
37
|
-
},
|
|
38
|
-
fail(msg) {
|
|
39
|
-
this.stop();
|
|
40
|
-
console.log(`ā ${msg}`);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const pkg = require("./package.json");
|
|
46
|
-
const { ensureSqliteRuntime, buildEnvWithRuntime } = require("./hooks/sqliteRuntime");
|
|
47
|
-
const { ensureTrayRuntime } = require("./hooks/trayRuntime");
|
|
48
|
-
const args = process.argv.slice(2);
|
|
49
|
-
|
|
50
|
-
// Self-heal SQLite runtime deps (sql.js + better-sqlite3) into ~/.extremerouter/runtime
|
|
51
|
-
// so the server can resolve them via NODE_PATH. Best-effort ā sql.js is required,
|
|
52
|
-
// better-sqlite3 is optional. Logs to stderr only on failure.
|
|
53
|
-
try { ensureSqliteRuntime({ silent: true }); } catch {}
|
|
54
|
-
|
|
55
|
-
// Self-heal tray runtime (systray for macOS/Linux only). Windows skipped.
|
|
56
|
-
try { ensureTrayRuntime({ silent: true }); } catch {}
|
|
57
|
-
|
|
58
|
-
// Configuration constants
|
|
59
|
-
const APP_NAME = pkg.name; // Use from package.json
|
|
60
|
-
const INSTALL_CMD_LATEST = `npm i -g ${APP_NAME}@latest --prefer-online`;
|
|
61
|
-
|
|
62
|
-
const DEFAULT_PORT = 20128;
|
|
63
|
-
const DEFAULT_HOST = "0.0.0.0";
|
|
64
|
-
|
|
65
|
-
// First non-internal IPv4 ā the address remote peers actually reach when bound to 0.0.0.0.
|
|
66
|
-
function getLanIp() {
|
|
67
|
-
for (const ifaces of Object.values(os.networkInterfaces())) {
|
|
68
|
-
for (const i of ifaces || []) {
|
|
69
|
-
if (i.family === "IPv4" && !i.internal) return i.address;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Local URL stays "localhost"; warn separately when bound to all interfaces (network-exposed).
|
|
76
|
-
function getDisplayHost() {
|
|
77
|
-
return host === DEFAULT_HOST ? "localhost" : host;
|
|
78
|
-
}
|
|
79
|
-
const MAX_PORT_ATTEMPTS = 10;
|
|
80
|
-
// Identifiers for killAllAppProcesses - only kill extremerouter specifically
|
|
81
|
-
const PROCESS_IDENTIFIERS = [
|
|
82
|
-
'extremerouter' // Only package name - avoid killing other apps
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
// Parse arguments
|
|
86
|
-
let port = DEFAULT_PORT;
|
|
87
|
-
let host = DEFAULT_HOST;
|
|
88
|
-
let noBrowser = false;
|
|
89
|
-
let skipUpdate = false;
|
|
90
|
-
let showLog = false;
|
|
91
|
-
let trayMode = false;
|
|
92
|
-
|
|
93
|
-
for (let i = 0; i < args.length; i++) {
|
|
94
|
-
if (args[i] === "--port" || args[i] === "-p") {
|
|
95
|
-
port = parseInt(args[i + 1], 10) || DEFAULT_PORT;
|
|
96
|
-
i++;
|
|
97
|
-
} else if (args[i] === "--host" || args[i] === "-H") {
|
|
98
|
-
host = args[i + 1] || DEFAULT_HOST;
|
|
99
|
-
i++;
|
|
100
|
-
} else if (args[i] === "--no-browser" || args[i] === "-n") {
|
|
101
|
-
noBrowser = true;
|
|
102
|
-
} else if (args[i] === "--log" || args[i] === "-l") {
|
|
103
|
-
showLog = true;
|
|
104
|
-
} else if (args[i] === "--skip-update") {
|
|
105
|
-
skipUpdate = true;
|
|
106
|
-
} else if (args[i] === "--tray" || args[i] === "-t") {
|
|
107
|
-
trayMode = true;
|
|
108
|
-
process.env.TRAY_MODE = "1";
|
|
109
|
-
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
110
|
-
console.log(`
|
|
111
|
-
Usage: ${APP_NAME} [options]
|
|
112
|
-
|
|
113
|
-
Options:
|
|
114
|
-
-p, --port <port> Port to run the server (default: ${DEFAULT_PORT})
|
|
115
|
-
-H, --host <host> Host to bind (default: ${DEFAULT_HOST})
|
|
116
|
-
-n, --no-browser Don't open browser automatically
|
|
117
|
-
-l, --log Show server logs (default: hidden)
|
|
118
|
-
-t, --tray Run in system tray mode (background)
|
|
119
|
-
--skip-update Skip auto-update check
|
|
120
|
-
-h, --help Show this help message
|
|
121
|
-
-v, --version Show version
|
|
122
|
-
`);
|
|
123
|
-
process.exit(0);
|
|
124
|
-
} else if (args[i] === "--version" || args[i] === "-v") {
|
|
125
|
-
console.log(pkg.version);
|
|
126
|
-
process.exit(0);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Auto-relaunch after update: detached process has no TTY ā fallback to tray
|
|
131
|
-
if (skipUpdate && !trayMode && !process.stdin.isTTY) {
|
|
132
|
-
trayMode = true;
|
|
133
|
-
process.env.TRAY_MODE = "1";
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Always use Node.js runtime with absolute path
|
|
137
|
-
const RUNTIME = process.execPath;
|
|
138
|
-
|
|
139
|
-
// Compare semver versions: returns 1 if a > b, -1 if a < b, 0 if equal
|
|
140
|
-
function compareVersions(a, b) {
|
|
141
|
-
const partsA = a.split(".").map(Number);
|
|
142
|
-
const partsB = b.split(".").map(Number);
|
|
143
|
-
for (let i = 0; i < 3; i++) {
|
|
144
|
-
if (partsA[i] > partsB[i]) return 1;
|
|
145
|
-
if (partsA[i] < partsB[i]) return -1;
|
|
146
|
-
}
|
|
147
|
-
return 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Get app data dir (matches app/src/lib/dataDir.js convention)
|
|
151
|
-
function getAppDataDir() {
|
|
152
|
-
return process.platform === "win32"
|
|
153
|
-
? path.join(process.env.APPDATA || "", "extremerouter")
|
|
154
|
-
: path.join(os.homedir(), ".extremerouter");
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Kill PID from file (best-effort, removes file after)
|
|
158
|
-
function killByPidFile(pidFile) {
|
|
159
|
-
try {
|
|
160
|
-
if (!fs.existsSync(pidFile)) return;
|
|
161
|
-
const pid = parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10);
|
|
162
|
-
if (!pid) return;
|
|
163
|
-
try {
|
|
164
|
-
if (process.platform === "win32") {
|
|
165
|
-
execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 3000 });
|
|
166
|
-
} else {
|
|
167
|
-
process.kill(pid, "SIGKILL");
|
|
168
|
-
}
|
|
169
|
-
} catch { }
|
|
170
|
-
try { fs.unlinkSync(pidFile); } catch { }
|
|
171
|
-
} catch { }
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Kill tunnel processes (cloudflared/tailscale) by their PID files
|
|
175
|
-
function killTunnelByPidFile() {
|
|
176
|
-
const tunnelDir = path.join(getAppDataDir(), "tunnel");
|
|
177
|
-
killByPidFile(path.join(tunnelDir, "cloudflared.pid"));
|
|
178
|
-
killByPidFile(path.join(tunnelDir, "tailscale.pid"));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Kill cloudflared whose --url targets this app's port (covers stale PID file case)
|
|
182
|
-
function killCloudflaredByAppPort(appPort) {
|
|
183
|
-
if (!appPort) return [];
|
|
184
|
-
const portMatchers = [`localhost:${appPort}`, `127.0.0.1:${appPort}`];
|
|
185
|
-
const pids = [];
|
|
186
|
-
try {
|
|
187
|
-
if (process.platform === "win32") {
|
|
188
|
-
const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command "Get-WmiObject Win32_Process -Filter 'Name=\\"cloudflared.exe\\"' | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
|
189
|
-
const output = execSync(psCmd, { encoding: "utf8", windowsHide: true, timeout: 5000 });
|
|
190
|
-
const lines = output.split("\n").slice(1).filter(l => l.trim());
|
|
191
|
-
lines.forEach(line => {
|
|
192
|
-
if (portMatchers.some(m => line.includes(m))) {
|
|
193
|
-
const match = line.match(/^"(\d+)"/);
|
|
194
|
-
if (match && match[1]) pids.push(match[1]);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
} else {
|
|
198
|
-
const output = execSync("ps -eo pid,command 2>/dev/null", { encoding: "utf8", timeout: 5000 });
|
|
199
|
-
output.split("\n").forEach(line => {
|
|
200
|
-
if (line.includes("cloudflared") && portMatchers.some(m => line.includes(m))) {
|
|
201
|
-
const parts = line.trim().split(/\s+/);
|
|
202
|
-
const pid = parts[0];
|
|
203
|
-
if (pid && !isNaN(pid)) pids.push(pid);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
} catch { }
|
|
208
|
-
return pids;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Kill all extremerouter processes
|
|
212
|
-
function killAllAppProcesses(appPort) {
|
|
213
|
-
return new Promise((resolve) => {
|
|
214
|
-
try {
|
|
215
|
-
// Kill MIT first (privileged process, needs special handling)
|
|
216
|
-
killProxyByPidFile();
|
|
217
|
-
// Kill cloudflared/tailscale by PID file (precise, only this app's tunnel)
|
|
218
|
-
killTunnelByPidFile();
|
|
219
|
-
|
|
220
|
-
const platform = process.platform;
|
|
221
|
-
let pids = [];
|
|
222
|
-
|
|
223
|
-
// Catch stale PID files: kill cloudflared bound to this app's port
|
|
224
|
-
pids.push(...killCloudflaredByAppPort(appPort));
|
|
225
|
-
|
|
226
|
-
if (platform === "win32") {
|
|
227
|
-
// Windows: use WMI to get full CommandLine (tasklist /V doesn't include it)
|
|
228
|
-
try {
|
|
229
|
-
const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command "Get-WmiObject Win32_Process -Filter 'Name=\\"node.exe\\"' | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
|
230
|
-
const output = execSync(psCmd, {
|
|
231
|
-
encoding: "utf8",
|
|
232
|
-
windowsHide: true,
|
|
233
|
-
timeout: 5000
|
|
234
|
-
});
|
|
235
|
-
const lines = output.split("\n").slice(1).filter(l => l.trim());
|
|
236
|
-
lines.forEach(line => {
|
|
237
|
-
// Whitelist: real node process running extremerouter/cli.js, or next-server.
|
|
238
|
-
// Avoids killing editors/grep/strace/cursor that just have "extremerouter" in cmdline.
|
|
239
|
-
const cmd = line.toLowerCase();
|
|
240
|
-
const isAppProcess =
|
|
241
|
-
(cmd.includes("node") && cmd.includes("extremerouter") && (cmd.includes("cli.js") || cmd.includes("\\extremerouter") || cmd.includes("/extremerouter")))
|
|
242
|
-
|| cmd.includes("next-server");
|
|
243
|
-
if (isAppProcess) {
|
|
244
|
-
const match = line.match(/^"(\d+)"/);
|
|
245
|
-
if (match && match[1] && match[1] !== process.pid.toString()) {
|
|
246
|
-
pids.push(match[1]);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
} catch (e) {
|
|
251
|
-
// No processes found or error - continue
|
|
252
|
-
}
|
|
253
|
-
} else {
|
|
254
|
-
// macOS/Linux: use ps to find all matching processes
|
|
255
|
-
try {
|
|
256
|
-
const output = execSync('ps aux 2>/dev/null', {
|
|
257
|
-
encoding: 'utf8',
|
|
258
|
-
timeout: 5000
|
|
259
|
-
});
|
|
260
|
-
const lines = output.split('\n');
|
|
261
|
-
|
|
262
|
-
lines.forEach(line => {
|
|
263
|
-
// Whitelist: real node process running extremerouter/cli.js, or next-server.
|
|
264
|
-
// Avoids killing grep/strace/editors/cursor that incidentally match "extremerouter".
|
|
265
|
-
const cmd = line.toLowerCase();
|
|
266
|
-
const isAppProcess =
|
|
267
|
-
(cmd.includes("node") && cmd.includes("extremerouter") && (cmd.includes("cli.js") || cmd.includes("/extremerouter")))
|
|
268
|
-
|| cmd.includes("next-server");
|
|
269
|
-
if (isAppProcess) {
|
|
270
|
-
const parts = line.trim().split(/\s+/);
|
|
271
|
-
const pid = parts[1];
|
|
272
|
-
if (pid && !isNaN(pid) && pid !== process.pid.toString()) {
|
|
273
|
-
pids.push(pid);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
} catch (e) {
|
|
278
|
-
// No processes found or error - continue
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Kill all found processes
|
|
283
|
-
if (pids.length > 0) {
|
|
284
|
-
pids.forEach(pid => {
|
|
285
|
-
try {
|
|
286
|
-
if (platform === "win32") {
|
|
287
|
-
execSync(`taskkill /F /PID ${pid} 2>nul`, { stdio: 'ignore', shell: true, windowsHide: true, timeout: 3000 });
|
|
288
|
-
} else {
|
|
289
|
-
execSync(`kill -9 ${pid} 2>/dev/null`, { stdio: 'ignore', timeout: 3000 });
|
|
290
|
-
}
|
|
291
|
-
} catch (err) {
|
|
292
|
-
// Process already dead or can't kill - continue
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// Wait for processes to fully terminate
|
|
297
|
-
setTimeout(() => resolve(), 1000);
|
|
298
|
-
} else {
|
|
299
|
-
resolve();
|
|
300
|
-
}
|
|
301
|
-
} catch (err) {
|
|
302
|
-
// Silent fail - continue anyway
|
|
303
|
-
resolve();
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Sleep helper using SharedArrayBuffer wait (sync, no busy-loop)
|
|
309
|
-
function sleepSync(ms) {
|
|
310
|
-
try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); } catch { /* ignore */ }
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Wait until process dies or timeout reached
|
|
314
|
-
function waitForExit(pid, timeoutMs) {
|
|
315
|
-
const deadline = Date.now() + timeoutMs;
|
|
316
|
-
while (Date.now() < deadline) {
|
|
317
|
-
try { process.kill(pid, 0); } catch { return true; }
|
|
318
|
-
sleepSync(100);
|
|
319
|
-
}
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Kill MIT server by PID file (runs privileged, needs special handling)
|
|
324
|
-
// Sends SIGTERM first so MIT can clean up host entries before dying.
|
|
325
|
-
function killProxyByPidFile() {
|
|
326
|
-
try {
|
|
327
|
-
const pidFile = path.join(getAppDataDir(), "mitm", ".mitm.pid");
|
|
328
|
-
if (!fs.existsSync(pidFile)) return;
|
|
329
|
-
const pid = parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10);
|
|
330
|
-
if (!pid) return;
|
|
331
|
-
|
|
332
|
-
if (process.platform === "win32") {
|
|
333
|
-
// Graceful first (lets server cleanup hosts), then force
|
|
334
|
-
try { execSync(`taskkill /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 2000 }); } catch { }
|
|
335
|
-
if (!waitForExit(pid, 1500)) {
|
|
336
|
-
try { execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 3000 }); } catch { }
|
|
337
|
-
}
|
|
338
|
-
// Last-resort: PowerShell Stop-Process (sometimes succeeds where taskkill fails on admin processes)
|
|
339
|
-
if (!waitForExit(pid, 500)) {
|
|
340
|
-
try { execSync(`powershell -NonInteractive -WindowStyle Hidden -Command "Stop-Process -Id ${pid} -Force"`, { stdio: "ignore", windowsHide: true, timeout: 3000 }); } catch { }
|
|
341
|
-
}
|
|
342
|
-
} else {
|
|
343
|
-
// SIGTERM via cached sudo token first
|
|
344
|
-
try { execSync(`sudo -n kill -TERM ${pid} 2>/dev/null`, { stdio: "ignore", timeout: 2000 }); }
|
|
345
|
-
catch { try { process.kill(pid, "SIGTERM"); } catch { } }
|
|
346
|
-
if (!waitForExit(pid, 1500)) {
|
|
347
|
-
try { execSync(`sudo -n kill -9 ${pid} 2>/dev/null`, { stdio: "ignore", timeout: 2000 }); }
|
|
348
|
-
catch { try { process.kill(pid, "SIGKILL"); } catch { } }
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
try { fs.unlinkSync(pidFile); } catch { }
|
|
352
|
-
} catch { }
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Kill any process on specific port
|
|
356
|
-
function killProcessOnPort(port) {
|
|
357
|
-
return new Promise((resolve) => {
|
|
358
|
-
try {
|
|
359
|
-
const platform = process.platform;
|
|
360
|
-
let pid;
|
|
361
|
-
|
|
362
|
-
if (platform === "win32") {
|
|
363
|
-
try {
|
|
364
|
-
const output = execSync(`netstat -ano | findstr :${port}`, {
|
|
365
|
-
encoding: 'utf8',
|
|
366
|
-
shell: true,
|
|
367
|
-
windowsHide: true,
|
|
368
|
-
timeout: 5000
|
|
369
|
-
}).trim();
|
|
370
|
-
const lines = output.split('\n').filter(l => l.includes('LISTENING'));
|
|
371
|
-
if (lines.length > 0) {
|
|
372
|
-
pid = lines[0].trim().split(/\s+/).pop();
|
|
373
|
-
execSync(`taskkill /F /PID ${pid} 2>nul`, { stdio: 'ignore', shell: true, windowsHide: true, timeout: 3000 });
|
|
374
|
-
}
|
|
375
|
-
} catch (e) {
|
|
376
|
-
// Port is free or error
|
|
377
|
-
}
|
|
378
|
-
} else {
|
|
379
|
-
// macOS/Linux
|
|
380
|
-
try {
|
|
381
|
-
const pidOutput = execSync(`lsof -ti:${port}`, {
|
|
382
|
-
encoding: 'utf8',
|
|
383
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
384
|
-
}).trim();
|
|
385
|
-
if (pidOutput) {
|
|
386
|
-
pid = pidOutput.split('\n')[0];
|
|
387
|
-
execSync(`kill -9 ${pid} 2>/dev/null`, { stdio: 'ignore', timeout: 3000 });
|
|
388
|
-
}
|
|
389
|
-
} catch (e) {
|
|
390
|
-
// Port is free or error
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Wait for port to be released
|
|
395
|
-
setTimeout(() => resolve(), 500);
|
|
396
|
-
} catch (err) {
|
|
397
|
-
// Silent fail - continue anyway
|
|
398
|
-
resolve();
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
// Detect if running in restricted environment (Codespaces, Docker)
|
|
405
|
-
function isRestrictedEnvironment() {
|
|
406
|
-
// Check for Codespaces
|
|
407
|
-
if (process.env.CODESPACES === "true" || process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN) {
|
|
408
|
-
return "GitHub Codespaces";
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Check for Docker
|
|
412
|
-
if (fs.existsSync("/.dockerenv") || (fs.existsSync("/proc/1/cgroup") && fs.readFileSync("/proc/1/cgroup", "utf8").includes("docker"))) {
|
|
413
|
-
return "Docker";
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return null;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Check if new version available, return latest version or null
|
|
420
|
-
function checkForUpdate() {
|
|
421
|
-
return new Promise((resolve) => {
|
|
422
|
-
if (skipUpdate) {
|
|
423
|
-
resolve(null);
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const spinner = createSpinner("Checking for updates...").start();
|
|
428
|
-
let resolved = false;
|
|
429
|
-
|
|
430
|
-
const safetyTimeout = setTimeout(() => {
|
|
431
|
-
if (!resolved) {
|
|
432
|
-
resolved = true;
|
|
433
|
-
spinner.stop();
|
|
434
|
-
resolve(null);
|
|
435
|
-
}
|
|
436
|
-
}, 8000);
|
|
437
|
-
|
|
438
|
-
const done = (version) => {
|
|
439
|
-
if (resolved) return;
|
|
440
|
-
resolved = true;
|
|
441
|
-
clearTimeout(safetyTimeout);
|
|
442
|
-
spinner.stop();
|
|
443
|
-
resolve(version);
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
const req = https.get(`https://registry.npmjs.org/${pkg.name}/latest`, { timeout: 3000 }, (res) => {
|
|
447
|
-
let data = "";
|
|
448
|
-
res.on("data", chunk => data += chunk);
|
|
449
|
-
res.on("end", () => {
|
|
450
|
-
try {
|
|
451
|
-
const latest = JSON.parse(data);
|
|
452
|
-
if (latest.version && compareVersions(latest.version, pkg.version) > 0) {
|
|
453
|
-
done(latest.version);
|
|
454
|
-
} else {
|
|
455
|
-
done(null);
|
|
456
|
-
}
|
|
457
|
-
} catch (e) {
|
|
458
|
-
done(null);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
req.on("error", () => done(null));
|
|
464
|
-
req.on("timeout", () => { req.destroy(); done(null); });
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Open browser
|
|
469
|
-
function openBrowser(url) {
|
|
470
|
-
const platform = process.platform;
|
|
471
|
-
let cmd;
|
|
472
|
-
|
|
473
|
-
if (platform === "darwin") {
|
|
474
|
-
cmd = `open "${url}"`;
|
|
475
|
-
} else if (platform === "win32") {
|
|
476
|
-
cmd = `start "" "${url}"`;
|
|
477
|
-
} else {
|
|
478
|
-
cmd = `xdg-open "${url}"`;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
exec(cmd, { windowsHide: true }, (err) => {
|
|
482
|
-
if (err) {
|
|
483
|
-
console.log(`Open browser manually: ${url}`);
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Find standalone server (bundled in bin/app for published package).
|
|
489
|
-
// Prefer custom-server.js (injects real socket IP) when present.
|
|
490
|
-
const standaloneDir = path.join(__dirname, "app");
|
|
491
|
-
const customServerPath = path.join(standaloneDir, "custom-server.js");
|
|
492
|
-
const serverPath = fs.existsSync(customServerPath)
|
|
493
|
-
? customServerPath
|
|
494
|
-
: path.join(standaloneDir, "server.js");
|
|
495
|
-
|
|
496
|
-
if (!fs.existsSync(serverPath)) {
|
|
497
|
-
console.error("Error: Standalone build not found.");
|
|
498
|
-
console.error("Please run 'npm run build:cli' first.");
|
|
499
|
-
process.exit(1);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Check for updates FIRST, then start server
|
|
503
|
-
checkForUpdate().then((latestVersion) => {
|
|
504
|
-
killAllAppProcesses(port).then(() => {
|
|
505
|
-
return killProcessOnPort(port);
|
|
506
|
-
}).then(() => {
|
|
507
|
-
startServer(latestVersion);
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
// Show interface selection menu
|
|
512
|
-
async function showInterfaceMenu(latestVersion) {
|
|
513
|
-
const { selectMenu } = require("./src/cli/utils/input");
|
|
514
|
-
const { clearScreen } = require("./src/cli/utils/display");
|
|
515
|
-
const { getEndpoint } = require("./src/cli/utils/endpoint");
|
|
516
|
-
|
|
517
|
-
clearScreen();
|
|
518
|
-
|
|
519
|
-
const displayHost = getDisplayHost();
|
|
520
|
-
|
|
521
|
-
// Detect tunnel/local mode for server URL display
|
|
522
|
-
let serverUrl;
|
|
523
|
-
try {
|
|
524
|
-
const { endpoint, tunnelEnabled } = await getEndpoint(port);
|
|
525
|
-
serverUrl = tunnelEnabled ? endpoint.replace(/\/v1$/, "") : `http://${displayHost}:${port}`;
|
|
526
|
-
} catch (e) {
|
|
527
|
-
serverUrl = `http://${displayHost}:${port}`;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
const subtitle = `š Server: \x1b[32m${serverUrl}\x1b[0m`;
|
|
531
|
-
|
|
532
|
-
const menuItems = [];
|
|
533
|
-
|
|
534
|
-
if (latestVersion) {
|
|
535
|
-
menuItems.push({ label: `Update to v${latestVersion} (current: v${pkg.version})`, icon: "ā¬" });
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
menuItems.push(
|
|
539
|
-
{ label: "Web UI (Open in Browser)", icon: "š" },
|
|
540
|
-
{ label: "Terminal UI (Interactive CLI)", icon: "š»" },
|
|
541
|
-
{ label: "Hide to Tray (Background)", icon: "š" },
|
|
542
|
-
{ label: "Exit", icon: "šŖ" }
|
|
543
|
-
);
|
|
544
|
-
|
|
545
|
-
const selected = await selectMenu(`Choose Interface (v${pkg.version})`, menuItems, 0, subtitle);
|
|
546
|
-
|
|
547
|
-
const offset = latestVersion ? 1 : 0;
|
|
548
|
-
|
|
549
|
-
if (latestVersion && selected === 0) return "update";
|
|
550
|
-
if (selected === offset) return "web";
|
|
551
|
-
if (selected === offset + 1) return "terminal";
|
|
552
|
-
if (selected === offset + 2) return "hide";
|
|
553
|
-
return "exit";
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const MAX_RESTARTS = 2;
|
|
557
|
-
const RESTART_RESET_MS = 30000; // Reset counter if alive > 30s
|
|
558
|
-
|
|
559
|
-
function startServer(latestVersion) {
|
|
560
|
-
const displayHost = getDisplayHost();
|
|
561
|
-
const url = `http://${displayHost}:${port}/dashboard`;
|
|
562
|
-
// Surface real network exposure when bound to all interfaces (default 0.0.0.0).
|
|
563
|
-
if (host === DEFAULT_HOST) {
|
|
564
|
-
const lanIp = getLanIp();
|
|
565
|
-
if (lanIp) console.log(`\x1b[33mā Network-exposed: reachable at http://${lanIp}:${port} (bound 0.0.0.0). Use --host 127.0.0.1 for local-only.\x1b[0m`);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
let restartCount = 0;
|
|
569
|
-
let serverStartTime = Date.now();
|
|
570
|
-
|
|
571
|
-
const CRASH_LOG_LINES = 50;
|
|
572
|
-
let crashLog = [];
|
|
573
|
-
|
|
574
|
-
function spawnServer() {
|
|
575
|
-
serverStartTime = Date.now();
|
|
576
|
-
crashLog = [];
|
|
577
|
-
const child = spawn(RUNTIME, ["--max-old-space-size=6144", serverPath], {
|
|
578
|
-
cwd: standaloneDir,
|
|
579
|
-
stdio: showLog ? "inherit" : ["ignore", "ignore", "pipe"],
|
|
580
|
-
detached: true,
|
|
581
|
-
windowsHide: true,
|
|
582
|
-
env: {
|
|
583
|
-
...buildEnvWithRuntime(process.env),
|
|
584
|
-
PORT: port.toString(),
|
|
585
|
-
HOSTNAME: host
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
if (!showLog && child.stderr) {
|
|
589
|
-
child.stderr.on("data", (data) => {
|
|
590
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
591
|
-
crashLog.push(...lines);
|
|
592
|
-
if (crashLog.length > CRASH_LOG_LINES) crashLog = crashLog.slice(-CRASH_LOG_LINES);
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
return child;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
let server = spawnServer();
|
|
599
|
-
|
|
600
|
-
// Cleanup function - force kill server process
|
|
601
|
-
let isCleaningUp = false;
|
|
602
|
-
function cleanup() {
|
|
603
|
-
if (isCleaningUp) return;
|
|
604
|
-
isCleaningUp = true;
|
|
605
|
-
try {
|
|
606
|
-
// Kill tray if running
|
|
607
|
-
try {
|
|
608
|
-
const { killTray } = require("./src/cli/tray/tray");
|
|
609
|
-
killTray();
|
|
610
|
-
} catch (e) { }
|
|
611
|
-
// Kill MIT server (privileged process) via PID file
|
|
612
|
-
killProxyByPidFile();
|
|
613
|
-
// Kill cloudflared/tailscale via PID file (only this app's tunnel)
|
|
614
|
-
killTunnelByPidFile();
|
|
615
|
-
// Kill server process directly
|
|
616
|
-
if (server.pid) {
|
|
617
|
-
process.kill(server.pid, "SIGKILL");
|
|
618
|
-
}
|
|
619
|
-
// Also try to kill process group
|
|
620
|
-
process.kill(-server.pid, "SIGKILL");
|
|
621
|
-
} catch (e) { }
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Suppress all errors during shutdown (systray lib throws JSON parse errors)
|
|
625
|
-
let isShuttingDown = false;
|
|
626
|
-
process.on("uncaughtException", (err) => {
|
|
627
|
-
if (isShuttingDown) return;
|
|
628
|
-
console.error("Error:", err.message);
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
// Handle all exit scenarios
|
|
632
|
-
process.on("SIGINT", () => {
|
|
633
|
-
if (isShuttingDown) return;
|
|
634
|
-
isShuttingDown = true;
|
|
635
|
-
console.log("\nExiting...");
|
|
636
|
-
cleanup();
|
|
637
|
-
setTimeout(() => process.exit(0), 100);
|
|
638
|
-
});
|
|
639
|
-
process.on("SIGTERM", () => {
|
|
640
|
-
if (isShuttingDown) return;
|
|
641
|
-
isShuttingDown = true;
|
|
642
|
-
cleanup();
|
|
643
|
-
setTimeout(() => process.exit(0), 100);
|
|
644
|
-
});
|
|
645
|
-
process.on("SIGHUP", () => {
|
|
646
|
-
if (isShuttingDown) return;
|
|
647
|
-
isShuttingDown = true;
|
|
648
|
-
cleanup();
|
|
649
|
-
setTimeout(() => process.exit(0), 100);
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
// Initialize tray icon (runs alongside TUI)
|
|
653
|
-
const initTrayIcon = () => {
|
|
654
|
-
try {
|
|
655
|
-
const { initTray } = require("./src/cli/tray/tray");
|
|
656
|
-
initTray({
|
|
657
|
-
port,
|
|
658
|
-
onQuit: () => {
|
|
659
|
-
isShuttingDown = true;
|
|
660
|
-
console.log("\nš Shutting down from tray...");
|
|
661
|
-
cleanup();
|
|
662
|
-
setTimeout(() => process.exit(0), 100);
|
|
663
|
-
},
|
|
664
|
-
onOpenDashboard: () => openBrowser(url)
|
|
665
|
-
});
|
|
666
|
-
} catch (err) {
|
|
667
|
-
// Tray not available - continue without it
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
|
|
671
|
-
// Tray-only mode: no TUI, just tray icon
|
|
672
|
-
if (trayMode) {
|
|
673
|
-
// Ignore SIGHUP so macOS terminal close doesn't kill the background tray process
|
|
674
|
-
process.removeAllListeners("SIGHUP");
|
|
675
|
-
process.on("SIGHUP", () => {});
|
|
676
|
-
|
|
677
|
-
console.log(`\nš ${pkg.name} v${pkg.version}`);
|
|
678
|
-
console.log(`Server: http://${displayHost}:${port}`);
|
|
679
|
-
|
|
680
|
-
setTimeout(() => {
|
|
681
|
-
initTrayIcon();
|
|
682
|
-
console.log("\nš” Router is now running in system tray. Close this terminal if you want.");
|
|
683
|
-
console.log(" Right-click tray icon to open dashboard or quit.\n");
|
|
684
|
-
}, 2000);
|
|
685
|
-
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Wait for server to be ready, then show interface menu loop + tray
|
|
690
|
-
setTimeout(async () => {
|
|
691
|
-
// Start tray icon alongside TUI
|
|
692
|
-
initTrayIcon();
|
|
693
|
-
|
|
694
|
-
try {
|
|
695
|
-
while (true) {
|
|
696
|
-
const choice = await showInterfaceMenu(latestVersion);
|
|
697
|
-
|
|
698
|
-
if (choice === "update") {
|
|
699
|
-
isShuttingDown = true;
|
|
700
|
-
const { clearScreen } = require("./src/cli/utils/display");
|
|
701
|
-
clearScreen();
|
|
702
|
-
console.log(`\n⬠Update v${pkg.version} ā v${latestVersion}\n`);
|
|
703
|
-
console.log(`Run this after exit:\n`);
|
|
704
|
-
console.log(` \x1b[33m${INSTALL_CMD_LATEST}\x1b[0m\n`);
|
|
705
|
-
cleanup();
|
|
706
|
-
await killAllAppProcesses(port);
|
|
707
|
-
await killProcessOnPort(port);
|
|
708
|
-
setTimeout(() => process.exit(0), 200);
|
|
709
|
-
return;
|
|
710
|
-
} else if (choice === "web") {
|
|
711
|
-
openBrowser(url);
|
|
712
|
-
// Wait for user to come back
|
|
713
|
-
const { pause } = require("./src/cli/utils/input");
|
|
714
|
-
await pause("\nPress Enter to go back to menu...");
|
|
715
|
-
} else if (choice === "terminal") {
|
|
716
|
-
// Start Terminal UI - it will return when user selects Back
|
|
717
|
-
const { startTerminalUI } = require("./src/cli/terminalUI");
|
|
718
|
-
await startTerminalUI(port);
|
|
719
|
-
// Loop continues, show menu again
|
|
720
|
-
} else if (choice === "hide") {
|
|
721
|
-
const { clearScreen } = require("./src/cli/utils/display");
|
|
722
|
-
clearScreen();
|
|
723
|
-
|
|
724
|
-
// Enable auto startup on OS boot
|
|
725
|
-
try {
|
|
726
|
-
const { enableAutoStart } = require("./src/cli/tray/autostart");
|
|
727
|
-
enableAutoStart(__filename);
|
|
728
|
-
} catch (e) { }
|
|
729
|
-
|
|
730
|
-
if (process.platform === "darwin") {
|
|
731
|
-
// macOS: keep current process alive ā spawning a detached child puts
|
|
732
|
-
// it outside the login session so NSStatusItem silently fails.
|
|
733
|
-
process.removeAllListeners("SIGHUP");
|
|
734
|
-
process.on("SIGHUP", () => {});
|
|
735
|
-
|
|
736
|
-
console.log(`\nā³ Switching to tray mode... (icon already visible in menu bar)`);
|
|
737
|
-
console.log(`š ExtremeRouter is running in tray (PID: ${process.pid})`);
|
|
738
|
-
console.log(` Server: http://${displayHost}:${port}`);
|
|
739
|
-
console.log(`\nš” You can close this terminal. Right-click tray icon to quit.\n`);
|
|
740
|
-
|
|
741
|
-
// Tray already init'd at startup ā just keep event loop alive.
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Windows/Linux: spawn detached bgProcess (systray works fine in child)
|
|
746
|
-
console.log(`\nā³ Starting background process... (tray icon will appear in ~3s)`);
|
|
747
|
-
|
|
748
|
-
const bgProcess = spawn(process.execPath, [__filename, "--tray", "--skip-update", "-p", port.toString()], {
|
|
749
|
-
detached: true,
|
|
750
|
-
stdio: "ignore",
|
|
751
|
-
windowsHide: true,
|
|
752
|
-
env: { ...process.env }
|
|
753
|
-
});
|
|
754
|
-
bgProcess.unref();
|
|
755
|
-
|
|
756
|
-
console.log(`š ExtremeRouter is now running in background (PID: ${bgProcess.pid})`);
|
|
757
|
-
console.log(` Server: http://${displayHost}:${port}`);
|
|
758
|
-
console.log(`\nš” You can close this terminal. Right-click tray icon to quit.\n`);
|
|
759
|
-
|
|
760
|
-
// cleanup() kills server so bgProcess can claim the port fresh
|
|
761
|
-
cleanup();
|
|
762
|
-
process.exit(0);
|
|
763
|
-
} else if (choice === "exit") {
|
|
764
|
-
isShuttingDown = true;
|
|
765
|
-
console.log("\nExiting...");
|
|
766
|
-
cleanup();
|
|
767
|
-
setTimeout(() => process.exit(0), 100);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
} catch (err) {
|
|
771
|
-
console.error("Error:", err.message);
|
|
772
|
-
cleanup();
|
|
773
|
-
process.exit(1);
|
|
774
|
-
}
|
|
775
|
-
}, 3000);
|
|
776
|
-
|
|
777
|
-
function attachServerEvents() {
|
|
778
|
-
server.on("error", (err) => {
|
|
779
|
-
console.error("Failed to start server:", err.message);
|
|
780
|
-
if (!isShuttingDown) tryRestart();
|
|
781
|
-
else { cleanup(); process.exit(1); }
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
server.on("close", (code) => {
|
|
785
|
-
if (isShuttingDown || code === 0) {
|
|
786
|
-
process.exit(code || 0);
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
tryRestart(code);
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
function tryRestart(code) {
|
|
794
|
-
const aliveMs = Date.now() - serverStartTime;
|
|
795
|
-
// Reset counter if last run was stable
|
|
796
|
-
if (aliveMs >= RESTART_RESET_MS) restartCount = 0;
|
|
797
|
-
|
|
798
|
-
if (restartCount >= MAX_RESTARTS) {
|
|
799
|
-
console.error(`\nā ļø Server crashed ${MAX_RESTARTS} times. Disabling MIT and restarting...`);
|
|
800
|
-
try {
|
|
801
|
-
const dbPath = path.join(os.homedir(), process.platform === "win32" ? path.join("AppData", "Roaming", "extremerouter", "db.json") : path.join(".extremerouter", "db.json"));
|
|
802
|
-
if (fs.existsSync(dbPath)) {
|
|
803
|
-
const db = JSON.parse(fs.readFileSync(dbPath, "utf-8"));
|
|
804
|
-
if (db.settings) db.settings.mitmEnabled = false;
|
|
805
|
-
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
|
806
|
-
}
|
|
807
|
-
} catch { /* best effort */ }
|
|
808
|
-
restartCount = 0;
|
|
809
|
-
server = spawnServer();
|
|
810
|
-
attachServerEvents();
|
|
811
|
-
return;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
restartCount++;
|
|
815
|
-
const delay = Math.min(1000 * restartCount, 10000);
|
|
816
|
-
console.error(`\nā ļø Server exited (code=${code ?? "unknown"}). Restarting in ${delay / 1000}s... (${restartCount}/${MAX_RESTARTS})`);
|
|
817
|
-
if (crashLog.length) {
|
|
818
|
-
console.error("\n--- Server crash log ---");
|
|
819
|
-
crashLog.forEach(l => console.error(l));
|
|
820
|
-
console.error("--- End crash log ---\n");
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
setTimeout(() => {
|
|
824
|
-
server = spawnServer();
|
|
825
|
-
attachServerEvents();
|
|
826
|
-
}, delay);
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
attachServerEvents();
|
|
830
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn, exec, execSync } = require("child_process");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
|
|
9
|
+
// Native spinner - no external dependency
|
|
10
|
+
function createSpinner(text) {
|
|
11
|
+
const frames = ["ā ", "ā ", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ", "ā "];
|
|
12
|
+
let i = 0;
|
|
13
|
+
let interval = null;
|
|
14
|
+
let currentText = text;
|
|
15
|
+
return {
|
|
16
|
+
start() {
|
|
17
|
+
if (process.stdout.isTTY) {
|
|
18
|
+
process.stdout.write(`\r${frames[0]} ${currentText}`);
|
|
19
|
+
interval = setInterval(() => {
|
|
20
|
+
process.stdout.write(`\r${frames[i++ % frames.length]} ${currentText}`);
|
|
21
|
+
}, 80);
|
|
22
|
+
}
|
|
23
|
+
return this;
|
|
24
|
+
},
|
|
25
|
+
stop() {
|
|
26
|
+
if (interval) {
|
|
27
|
+
clearInterval(interval);
|
|
28
|
+
interval = null;
|
|
29
|
+
}
|
|
30
|
+
if (process.stdout.isTTY) {
|
|
31
|
+
process.stdout.write("\r\x1b[K");
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
succeed(msg) {
|
|
35
|
+
this.stop();
|
|
36
|
+
console.log(`ā
${msg}`);
|
|
37
|
+
},
|
|
38
|
+
fail(msg) {
|
|
39
|
+
this.stop();
|
|
40
|
+
console.log(`ā ${msg}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const pkg = require("./package.json");
|
|
46
|
+
const { ensureSqliteRuntime, buildEnvWithRuntime } = require("./hooks/sqliteRuntime");
|
|
47
|
+
const { ensureTrayRuntime } = require("./hooks/trayRuntime");
|
|
48
|
+
const args = process.argv.slice(2);
|
|
49
|
+
|
|
50
|
+
// Self-heal SQLite runtime deps (sql.js + better-sqlite3) into ~/.extremerouter/runtime
|
|
51
|
+
// so the server can resolve them via NODE_PATH. Best-effort ā sql.js is required,
|
|
52
|
+
// better-sqlite3 is optional. Logs to stderr only on failure.
|
|
53
|
+
try { ensureSqliteRuntime({ silent: true }); } catch {}
|
|
54
|
+
|
|
55
|
+
// Self-heal tray runtime (systray for macOS/Linux only). Windows skipped.
|
|
56
|
+
try { ensureTrayRuntime({ silent: true }); } catch {}
|
|
57
|
+
|
|
58
|
+
// Configuration constants
|
|
59
|
+
const APP_NAME = pkg.name; // Use from package.json
|
|
60
|
+
const INSTALL_CMD_LATEST = `npm i -g ${APP_NAME}@latest --prefer-online`;
|
|
61
|
+
|
|
62
|
+
const DEFAULT_PORT = 20128;
|
|
63
|
+
const DEFAULT_HOST = "0.0.0.0";
|
|
64
|
+
|
|
65
|
+
// First non-internal IPv4 ā the address remote peers actually reach when bound to 0.0.0.0.
|
|
66
|
+
function getLanIp() {
|
|
67
|
+
for (const ifaces of Object.values(os.networkInterfaces())) {
|
|
68
|
+
for (const i of ifaces || []) {
|
|
69
|
+
if (i.family === "IPv4" && !i.internal) return i.address;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Local URL stays "localhost"; warn separately when bound to all interfaces (network-exposed).
|
|
76
|
+
function getDisplayHost() {
|
|
77
|
+
return host === DEFAULT_HOST ? "localhost" : host;
|
|
78
|
+
}
|
|
79
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
80
|
+
// Identifiers for killAllAppProcesses - only kill extremerouter specifically
|
|
81
|
+
const PROCESS_IDENTIFIERS = [
|
|
82
|
+
'extremerouter' // Only package name - avoid killing other apps
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// Parse arguments
|
|
86
|
+
let port = DEFAULT_PORT;
|
|
87
|
+
let host = DEFAULT_HOST;
|
|
88
|
+
let noBrowser = false;
|
|
89
|
+
let skipUpdate = false;
|
|
90
|
+
let showLog = false;
|
|
91
|
+
let trayMode = false;
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < args.length; i++) {
|
|
94
|
+
if (args[i] === "--port" || args[i] === "-p") {
|
|
95
|
+
port = parseInt(args[i + 1], 10) || DEFAULT_PORT;
|
|
96
|
+
i++;
|
|
97
|
+
} else if (args[i] === "--host" || args[i] === "-H") {
|
|
98
|
+
host = args[i + 1] || DEFAULT_HOST;
|
|
99
|
+
i++;
|
|
100
|
+
} else if (args[i] === "--no-browser" || args[i] === "-n") {
|
|
101
|
+
noBrowser = true;
|
|
102
|
+
} else if (args[i] === "--log" || args[i] === "-l") {
|
|
103
|
+
showLog = true;
|
|
104
|
+
} else if (args[i] === "--skip-update") {
|
|
105
|
+
skipUpdate = true;
|
|
106
|
+
} else if (args[i] === "--tray" || args[i] === "-t") {
|
|
107
|
+
trayMode = true;
|
|
108
|
+
process.env.TRAY_MODE = "1";
|
|
109
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
110
|
+
console.log(`
|
|
111
|
+
Usage: ${APP_NAME} [options]
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
-p, --port <port> Port to run the server (default: ${DEFAULT_PORT})
|
|
115
|
+
-H, --host <host> Host to bind (default: ${DEFAULT_HOST})
|
|
116
|
+
-n, --no-browser Don't open browser automatically
|
|
117
|
+
-l, --log Show server logs (default: hidden)
|
|
118
|
+
-t, --tray Run in system tray mode (background)
|
|
119
|
+
--skip-update Skip auto-update check
|
|
120
|
+
-h, --help Show this help message
|
|
121
|
+
-v, --version Show version
|
|
122
|
+
`);
|
|
123
|
+
process.exit(0);
|
|
124
|
+
} else if (args[i] === "--version" || args[i] === "-v") {
|
|
125
|
+
console.log(pkg.version);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Auto-relaunch after update: detached process has no TTY ā fallback to tray
|
|
131
|
+
if (skipUpdate && !trayMode && !process.stdin.isTTY) {
|
|
132
|
+
trayMode = true;
|
|
133
|
+
process.env.TRAY_MODE = "1";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Always use Node.js runtime with absolute path
|
|
137
|
+
const RUNTIME = process.execPath;
|
|
138
|
+
|
|
139
|
+
// Compare semver versions: returns 1 if a > b, -1 if a < b, 0 if equal
|
|
140
|
+
function compareVersions(a, b) {
|
|
141
|
+
const partsA = a.split(".").map(Number);
|
|
142
|
+
const partsB = b.split(".").map(Number);
|
|
143
|
+
for (let i = 0; i < 3; i++) {
|
|
144
|
+
if (partsA[i] > partsB[i]) return 1;
|
|
145
|
+
if (partsA[i] < partsB[i]) return -1;
|
|
146
|
+
}
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get app data dir (matches app/src/lib/dataDir.js convention)
|
|
151
|
+
function getAppDataDir() {
|
|
152
|
+
return process.platform === "win32"
|
|
153
|
+
? path.join(process.env.APPDATA || "", "extremerouter")
|
|
154
|
+
: path.join(os.homedir(), ".extremerouter");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Kill PID from file (best-effort, removes file after)
|
|
158
|
+
function killByPidFile(pidFile) {
|
|
159
|
+
try {
|
|
160
|
+
if (!fs.existsSync(pidFile)) return;
|
|
161
|
+
const pid = parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10);
|
|
162
|
+
if (!pid) return;
|
|
163
|
+
try {
|
|
164
|
+
if (process.platform === "win32") {
|
|
165
|
+
execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 3000 });
|
|
166
|
+
} else {
|
|
167
|
+
process.kill(pid, "SIGKILL");
|
|
168
|
+
}
|
|
169
|
+
} catch { }
|
|
170
|
+
try { fs.unlinkSync(pidFile); } catch { }
|
|
171
|
+
} catch { }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Kill tunnel processes (cloudflared/tailscale) by their PID files
|
|
175
|
+
function killTunnelByPidFile() {
|
|
176
|
+
const tunnelDir = path.join(getAppDataDir(), "tunnel");
|
|
177
|
+
killByPidFile(path.join(tunnelDir, "cloudflared.pid"));
|
|
178
|
+
killByPidFile(path.join(tunnelDir, "tailscale.pid"));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Kill cloudflared whose --url targets this app's port (covers stale PID file case)
|
|
182
|
+
function killCloudflaredByAppPort(appPort) {
|
|
183
|
+
if (!appPort) return [];
|
|
184
|
+
const portMatchers = [`localhost:${appPort}`, `127.0.0.1:${appPort}`];
|
|
185
|
+
const pids = [];
|
|
186
|
+
try {
|
|
187
|
+
if (process.platform === "win32") {
|
|
188
|
+
const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command "Get-WmiObject Win32_Process -Filter 'Name=\\"cloudflared.exe\\"' | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
|
189
|
+
const output = execSync(psCmd, { encoding: "utf8", windowsHide: true, timeout: 5000 });
|
|
190
|
+
const lines = output.split("\n").slice(1).filter(l => l.trim());
|
|
191
|
+
lines.forEach(line => {
|
|
192
|
+
if (portMatchers.some(m => line.includes(m))) {
|
|
193
|
+
const match = line.match(/^"(\d+)"/);
|
|
194
|
+
if (match && match[1]) pids.push(match[1]);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
const output = execSync("ps -eo pid,command 2>/dev/null", { encoding: "utf8", timeout: 5000 });
|
|
199
|
+
output.split("\n").forEach(line => {
|
|
200
|
+
if (line.includes("cloudflared") && portMatchers.some(m => line.includes(m))) {
|
|
201
|
+
const parts = line.trim().split(/\s+/);
|
|
202
|
+
const pid = parts[0];
|
|
203
|
+
if (pid && !isNaN(pid)) pids.push(pid);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
} catch { }
|
|
208
|
+
return pids;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Kill all extremerouter processes
|
|
212
|
+
function killAllAppProcesses(appPort) {
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
try {
|
|
215
|
+
// Kill MIT first (privileged process, needs special handling)
|
|
216
|
+
killProxyByPidFile();
|
|
217
|
+
// Kill cloudflared/tailscale by PID file (precise, only this app's tunnel)
|
|
218
|
+
killTunnelByPidFile();
|
|
219
|
+
|
|
220
|
+
const platform = process.platform;
|
|
221
|
+
let pids = [];
|
|
222
|
+
|
|
223
|
+
// Catch stale PID files: kill cloudflared bound to this app's port
|
|
224
|
+
pids.push(...killCloudflaredByAppPort(appPort));
|
|
225
|
+
|
|
226
|
+
if (platform === "win32") {
|
|
227
|
+
// Windows: use WMI to get full CommandLine (tasklist /V doesn't include it)
|
|
228
|
+
try {
|
|
229
|
+
const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command "Get-WmiObject Win32_Process -Filter 'Name=\\"node.exe\\"' | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation"`;
|
|
230
|
+
const output = execSync(psCmd, {
|
|
231
|
+
encoding: "utf8",
|
|
232
|
+
windowsHide: true,
|
|
233
|
+
timeout: 5000
|
|
234
|
+
});
|
|
235
|
+
const lines = output.split("\n").slice(1).filter(l => l.trim());
|
|
236
|
+
lines.forEach(line => {
|
|
237
|
+
// Whitelist: real node process running extremerouter/cli.js, or next-server.
|
|
238
|
+
// Avoids killing editors/grep/strace/cursor that just have "extremerouter" in cmdline.
|
|
239
|
+
const cmd = line.toLowerCase();
|
|
240
|
+
const isAppProcess =
|
|
241
|
+
(cmd.includes("node") && cmd.includes("extremerouter") && (cmd.includes("cli.js") || cmd.includes("\\extremerouter") || cmd.includes("/extremerouter")))
|
|
242
|
+
|| cmd.includes("next-server");
|
|
243
|
+
if (isAppProcess) {
|
|
244
|
+
const match = line.match(/^"(\d+)"/);
|
|
245
|
+
if (match && match[1] && match[1] !== process.pid.toString()) {
|
|
246
|
+
pids.push(match[1]);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
} catch (e) {
|
|
251
|
+
// No processes found or error - continue
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
// macOS/Linux: use ps to find all matching processes
|
|
255
|
+
try {
|
|
256
|
+
const output = execSync('ps aux 2>/dev/null', {
|
|
257
|
+
encoding: 'utf8',
|
|
258
|
+
timeout: 5000
|
|
259
|
+
});
|
|
260
|
+
const lines = output.split('\n');
|
|
261
|
+
|
|
262
|
+
lines.forEach(line => {
|
|
263
|
+
// Whitelist: real node process running extremerouter/cli.js, or next-server.
|
|
264
|
+
// Avoids killing grep/strace/editors/cursor that incidentally match "extremerouter".
|
|
265
|
+
const cmd = line.toLowerCase();
|
|
266
|
+
const isAppProcess =
|
|
267
|
+
(cmd.includes("node") && cmd.includes("extremerouter") && (cmd.includes("cli.js") || cmd.includes("/extremerouter")))
|
|
268
|
+
|| cmd.includes("next-server");
|
|
269
|
+
if (isAppProcess) {
|
|
270
|
+
const parts = line.trim().split(/\s+/);
|
|
271
|
+
const pid = parts[1];
|
|
272
|
+
if (pid && !isNaN(pid) && pid !== process.pid.toString()) {
|
|
273
|
+
pids.push(pid);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
} catch (e) {
|
|
278
|
+
// No processes found or error - continue
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Kill all found processes
|
|
283
|
+
if (pids.length > 0) {
|
|
284
|
+
pids.forEach(pid => {
|
|
285
|
+
try {
|
|
286
|
+
if (platform === "win32") {
|
|
287
|
+
execSync(`taskkill /F /PID ${pid} 2>nul`, { stdio: 'ignore', shell: true, windowsHide: true, timeout: 3000 });
|
|
288
|
+
} else {
|
|
289
|
+
execSync(`kill -9 ${pid} 2>/dev/null`, { stdio: 'ignore', timeout: 3000 });
|
|
290
|
+
}
|
|
291
|
+
} catch (err) {
|
|
292
|
+
// Process already dead or can't kill - continue
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Wait for processes to fully terminate
|
|
297
|
+
setTimeout(() => resolve(), 1000);
|
|
298
|
+
} else {
|
|
299
|
+
resolve();
|
|
300
|
+
}
|
|
301
|
+
} catch (err) {
|
|
302
|
+
// Silent fail - continue anyway
|
|
303
|
+
resolve();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Sleep helper using SharedArrayBuffer wait (sync, no busy-loop)
|
|
309
|
+
function sleepSync(ms) {
|
|
310
|
+
try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); } catch { /* ignore */ }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Wait until process dies or timeout reached
|
|
314
|
+
function waitForExit(pid, timeoutMs) {
|
|
315
|
+
const deadline = Date.now() + timeoutMs;
|
|
316
|
+
while (Date.now() < deadline) {
|
|
317
|
+
try { process.kill(pid, 0); } catch { return true; }
|
|
318
|
+
sleepSync(100);
|
|
319
|
+
}
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Kill MIT server by PID file (runs privileged, needs special handling)
|
|
324
|
+
// Sends SIGTERM first so MIT can clean up host entries before dying.
|
|
325
|
+
function killProxyByPidFile() {
|
|
326
|
+
try {
|
|
327
|
+
const pidFile = path.join(getAppDataDir(), "mitm", ".mitm.pid");
|
|
328
|
+
if (!fs.existsSync(pidFile)) return;
|
|
329
|
+
const pid = parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10);
|
|
330
|
+
if (!pid) return;
|
|
331
|
+
|
|
332
|
+
if (process.platform === "win32") {
|
|
333
|
+
// Graceful first (lets server cleanup hosts), then force
|
|
334
|
+
try { execSync(`taskkill /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 2000 }); } catch { }
|
|
335
|
+
if (!waitForExit(pid, 1500)) {
|
|
336
|
+
try { execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore", windowsHide: true, timeout: 3000 }); } catch { }
|
|
337
|
+
}
|
|
338
|
+
// Last-resort: PowerShell Stop-Process (sometimes succeeds where taskkill fails on admin processes)
|
|
339
|
+
if (!waitForExit(pid, 500)) {
|
|
340
|
+
try { execSync(`powershell -NonInteractive -WindowStyle Hidden -Command "Stop-Process -Id ${pid} -Force"`, { stdio: "ignore", windowsHide: true, timeout: 3000 }); } catch { }
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
// SIGTERM via cached sudo token first
|
|
344
|
+
try { execSync(`sudo -n kill -TERM ${pid} 2>/dev/null`, { stdio: "ignore", timeout: 2000 }); }
|
|
345
|
+
catch { try { process.kill(pid, "SIGTERM"); } catch { } }
|
|
346
|
+
if (!waitForExit(pid, 1500)) {
|
|
347
|
+
try { execSync(`sudo -n kill -9 ${pid} 2>/dev/null`, { stdio: "ignore", timeout: 2000 }); }
|
|
348
|
+
catch { try { process.kill(pid, "SIGKILL"); } catch { } }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
try { fs.unlinkSync(pidFile); } catch { }
|
|
352
|
+
} catch { }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Kill any process on specific port
|
|
356
|
+
function killProcessOnPort(port) {
|
|
357
|
+
return new Promise((resolve) => {
|
|
358
|
+
try {
|
|
359
|
+
const platform = process.platform;
|
|
360
|
+
let pid;
|
|
361
|
+
|
|
362
|
+
if (platform === "win32") {
|
|
363
|
+
try {
|
|
364
|
+
const output = execSync(`netstat -ano | findstr :${port}`, {
|
|
365
|
+
encoding: 'utf8',
|
|
366
|
+
shell: true,
|
|
367
|
+
windowsHide: true,
|
|
368
|
+
timeout: 5000
|
|
369
|
+
}).trim();
|
|
370
|
+
const lines = output.split('\n').filter(l => l.includes('LISTENING'));
|
|
371
|
+
if (lines.length > 0) {
|
|
372
|
+
pid = lines[0].trim().split(/\s+/).pop();
|
|
373
|
+
execSync(`taskkill /F /PID ${pid} 2>nul`, { stdio: 'ignore', shell: true, windowsHide: true, timeout: 3000 });
|
|
374
|
+
}
|
|
375
|
+
} catch (e) {
|
|
376
|
+
// Port is free or error
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
// macOS/Linux
|
|
380
|
+
try {
|
|
381
|
+
const pidOutput = execSync(`lsof -ti:${port}`, {
|
|
382
|
+
encoding: 'utf8',
|
|
383
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
384
|
+
}).trim();
|
|
385
|
+
if (pidOutput) {
|
|
386
|
+
pid = pidOutput.split('\n')[0];
|
|
387
|
+
execSync(`kill -9 ${pid} 2>/dev/null`, { stdio: 'ignore', timeout: 3000 });
|
|
388
|
+
}
|
|
389
|
+
} catch (e) {
|
|
390
|
+
// Port is free or error
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Wait for port to be released
|
|
395
|
+
setTimeout(() => resolve(), 500);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
// Silent fail - continue anyway
|
|
398
|
+
resolve();
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
// Detect if running in restricted environment (Codespaces, Docker)
|
|
405
|
+
function isRestrictedEnvironment() {
|
|
406
|
+
// Check for Codespaces
|
|
407
|
+
if (process.env.CODESPACES === "true" || process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN) {
|
|
408
|
+
return "GitHub Codespaces";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Check for Docker
|
|
412
|
+
if (fs.existsSync("/.dockerenv") || (fs.existsSync("/proc/1/cgroup") && fs.readFileSync("/proc/1/cgroup", "utf8").includes("docker"))) {
|
|
413
|
+
return "Docker";
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Check if new version available, return latest version or null
|
|
420
|
+
function checkForUpdate() {
|
|
421
|
+
return new Promise((resolve) => {
|
|
422
|
+
if (skipUpdate) {
|
|
423
|
+
resolve(null);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const spinner = createSpinner("Checking for updates...").start();
|
|
428
|
+
let resolved = false;
|
|
429
|
+
|
|
430
|
+
const safetyTimeout = setTimeout(() => {
|
|
431
|
+
if (!resolved) {
|
|
432
|
+
resolved = true;
|
|
433
|
+
spinner.stop();
|
|
434
|
+
resolve(null);
|
|
435
|
+
}
|
|
436
|
+
}, 8000);
|
|
437
|
+
|
|
438
|
+
const done = (version) => {
|
|
439
|
+
if (resolved) return;
|
|
440
|
+
resolved = true;
|
|
441
|
+
clearTimeout(safetyTimeout);
|
|
442
|
+
spinner.stop();
|
|
443
|
+
resolve(version);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const req = https.get(`https://registry.npmjs.org/${pkg.name}/latest`, { timeout: 3000 }, (res) => {
|
|
447
|
+
let data = "";
|
|
448
|
+
res.on("data", chunk => data += chunk);
|
|
449
|
+
res.on("end", () => {
|
|
450
|
+
try {
|
|
451
|
+
const latest = JSON.parse(data);
|
|
452
|
+
if (latest.version && compareVersions(latest.version, pkg.version) > 0) {
|
|
453
|
+
done(latest.version);
|
|
454
|
+
} else {
|
|
455
|
+
done(null);
|
|
456
|
+
}
|
|
457
|
+
} catch (e) {
|
|
458
|
+
done(null);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
req.on("error", () => done(null));
|
|
464
|
+
req.on("timeout", () => { req.destroy(); done(null); });
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Open browser
|
|
469
|
+
function openBrowser(url) {
|
|
470
|
+
const platform = process.platform;
|
|
471
|
+
let cmd;
|
|
472
|
+
|
|
473
|
+
if (platform === "darwin") {
|
|
474
|
+
cmd = `open "${url}"`;
|
|
475
|
+
} else if (platform === "win32") {
|
|
476
|
+
cmd = `start "" "${url}"`;
|
|
477
|
+
} else {
|
|
478
|
+
cmd = `xdg-open "${url}"`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
exec(cmd, { windowsHide: true }, (err) => {
|
|
482
|
+
if (err) {
|
|
483
|
+
console.log(`Open browser manually: ${url}`);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Find standalone server (bundled in bin/app for published package).
|
|
489
|
+
// Prefer custom-server.js (injects real socket IP) when present.
|
|
490
|
+
const standaloneDir = path.join(__dirname, "app");
|
|
491
|
+
const customServerPath = path.join(standaloneDir, "custom-server.js");
|
|
492
|
+
const serverPath = fs.existsSync(customServerPath)
|
|
493
|
+
? customServerPath
|
|
494
|
+
: path.join(standaloneDir, "server.js");
|
|
495
|
+
|
|
496
|
+
if (!fs.existsSync(serverPath)) {
|
|
497
|
+
console.error("Error: Standalone build not found.");
|
|
498
|
+
console.error("Please run 'npm run build:cli' first.");
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Check for updates FIRST, then start server
|
|
503
|
+
checkForUpdate().then((latestVersion) => {
|
|
504
|
+
killAllAppProcesses(port).then(() => {
|
|
505
|
+
return killProcessOnPort(port);
|
|
506
|
+
}).then(() => {
|
|
507
|
+
startServer(latestVersion);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Show interface selection menu
|
|
512
|
+
async function showInterfaceMenu(latestVersion) {
|
|
513
|
+
const { selectMenu } = require("./src/cli/utils/input");
|
|
514
|
+
const { clearScreen } = require("./src/cli/utils/display");
|
|
515
|
+
const { getEndpoint } = require("./src/cli/utils/endpoint");
|
|
516
|
+
|
|
517
|
+
clearScreen();
|
|
518
|
+
|
|
519
|
+
const displayHost = getDisplayHost();
|
|
520
|
+
|
|
521
|
+
// Detect tunnel/local mode for server URL display
|
|
522
|
+
let serverUrl;
|
|
523
|
+
try {
|
|
524
|
+
const { endpoint, tunnelEnabled } = await getEndpoint(port);
|
|
525
|
+
serverUrl = tunnelEnabled ? endpoint.replace(/\/v1$/, "") : `http://${displayHost}:${port}`;
|
|
526
|
+
} catch (e) {
|
|
527
|
+
serverUrl = `http://${displayHost}:${port}`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const subtitle = `š Server: \x1b[32m${serverUrl}\x1b[0m`;
|
|
531
|
+
|
|
532
|
+
const menuItems = [];
|
|
533
|
+
|
|
534
|
+
if (latestVersion) {
|
|
535
|
+
menuItems.push({ label: `Update to v${latestVersion} (current: v${pkg.version})`, icon: "ā¬" });
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
menuItems.push(
|
|
539
|
+
{ label: "Web UI (Open in Browser)", icon: "š" },
|
|
540
|
+
{ label: "Terminal UI (Interactive CLI)", icon: "š»" },
|
|
541
|
+
{ label: "Hide to Tray (Background)", icon: "š" },
|
|
542
|
+
{ label: "Exit", icon: "šŖ" }
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
const selected = await selectMenu(`Choose Interface (v${pkg.version})`, menuItems, 0, subtitle);
|
|
546
|
+
|
|
547
|
+
const offset = latestVersion ? 1 : 0;
|
|
548
|
+
|
|
549
|
+
if (latestVersion && selected === 0) return "update";
|
|
550
|
+
if (selected === offset) return "web";
|
|
551
|
+
if (selected === offset + 1) return "terminal";
|
|
552
|
+
if (selected === offset + 2) return "hide";
|
|
553
|
+
return "exit";
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const MAX_RESTARTS = 2;
|
|
557
|
+
const RESTART_RESET_MS = 30000; // Reset counter if alive > 30s
|
|
558
|
+
|
|
559
|
+
function startServer(latestVersion) {
|
|
560
|
+
const displayHost = getDisplayHost();
|
|
561
|
+
const url = `http://${displayHost}:${port}/dashboard`;
|
|
562
|
+
// Surface real network exposure when bound to all interfaces (default 0.0.0.0).
|
|
563
|
+
if (host === DEFAULT_HOST) {
|
|
564
|
+
const lanIp = getLanIp();
|
|
565
|
+
if (lanIp) console.log(`\x1b[33mā Network-exposed: reachable at http://${lanIp}:${port} (bound 0.0.0.0). Use --host 127.0.0.1 for local-only.\x1b[0m`);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
let restartCount = 0;
|
|
569
|
+
let serverStartTime = Date.now();
|
|
570
|
+
|
|
571
|
+
const CRASH_LOG_LINES = 50;
|
|
572
|
+
let crashLog = [];
|
|
573
|
+
|
|
574
|
+
function spawnServer() {
|
|
575
|
+
serverStartTime = Date.now();
|
|
576
|
+
crashLog = [];
|
|
577
|
+
const child = spawn(RUNTIME, ["--max-old-space-size=6144", serverPath], {
|
|
578
|
+
cwd: standaloneDir,
|
|
579
|
+
stdio: showLog ? "inherit" : ["ignore", "ignore", "pipe"],
|
|
580
|
+
detached: true,
|
|
581
|
+
windowsHide: true,
|
|
582
|
+
env: {
|
|
583
|
+
...buildEnvWithRuntime(process.env),
|
|
584
|
+
PORT: port.toString(),
|
|
585
|
+
HOSTNAME: host
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
if (!showLog && child.stderr) {
|
|
589
|
+
child.stderr.on("data", (data) => {
|
|
590
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
591
|
+
crashLog.push(...lines);
|
|
592
|
+
if (crashLog.length > CRASH_LOG_LINES) crashLog = crashLog.slice(-CRASH_LOG_LINES);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
return child;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
let server = spawnServer();
|
|
599
|
+
|
|
600
|
+
// Cleanup function - force kill server process
|
|
601
|
+
let isCleaningUp = false;
|
|
602
|
+
function cleanup() {
|
|
603
|
+
if (isCleaningUp) return;
|
|
604
|
+
isCleaningUp = true;
|
|
605
|
+
try {
|
|
606
|
+
// Kill tray if running
|
|
607
|
+
try {
|
|
608
|
+
const { killTray } = require("./src/cli/tray/tray");
|
|
609
|
+
killTray();
|
|
610
|
+
} catch (e) { }
|
|
611
|
+
// Kill MIT server (privileged process) via PID file
|
|
612
|
+
killProxyByPidFile();
|
|
613
|
+
// Kill cloudflared/tailscale via PID file (only this app's tunnel)
|
|
614
|
+
killTunnelByPidFile();
|
|
615
|
+
// Kill server process directly
|
|
616
|
+
if (server.pid) {
|
|
617
|
+
process.kill(server.pid, "SIGKILL");
|
|
618
|
+
}
|
|
619
|
+
// Also try to kill process group
|
|
620
|
+
process.kill(-server.pid, "SIGKILL");
|
|
621
|
+
} catch (e) { }
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Suppress all errors during shutdown (systray lib throws JSON parse errors)
|
|
625
|
+
let isShuttingDown = false;
|
|
626
|
+
process.on("uncaughtException", (err) => {
|
|
627
|
+
if (isShuttingDown) return;
|
|
628
|
+
console.error("Error:", err.message);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Handle all exit scenarios
|
|
632
|
+
process.on("SIGINT", () => {
|
|
633
|
+
if (isShuttingDown) return;
|
|
634
|
+
isShuttingDown = true;
|
|
635
|
+
console.log("\nExiting...");
|
|
636
|
+
cleanup();
|
|
637
|
+
setTimeout(() => process.exit(0), 100);
|
|
638
|
+
});
|
|
639
|
+
process.on("SIGTERM", () => {
|
|
640
|
+
if (isShuttingDown) return;
|
|
641
|
+
isShuttingDown = true;
|
|
642
|
+
cleanup();
|
|
643
|
+
setTimeout(() => process.exit(0), 100);
|
|
644
|
+
});
|
|
645
|
+
process.on("SIGHUP", () => {
|
|
646
|
+
if (isShuttingDown) return;
|
|
647
|
+
isShuttingDown = true;
|
|
648
|
+
cleanup();
|
|
649
|
+
setTimeout(() => process.exit(0), 100);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Initialize tray icon (runs alongside TUI)
|
|
653
|
+
const initTrayIcon = () => {
|
|
654
|
+
try {
|
|
655
|
+
const { initTray } = require("./src/cli/tray/tray");
|
|
656
|
+
initTray({
|
|
657
|
+
port,
|
|
658
|
+
onQuit: () => {
|
|
659
|
+
isShuttingDown = true;
|
|
660
|
+
console.log("\nš Shutting down from tray...");
|
|
661
|
+
cleanup();
|
|
662
|
+
setTimeout(() => process.exit(0), 100);
|
|
663
|
+
},
|
|
664
|
+
onOpenDashboard: () => openBrowser(url)
|
|
665
|
+
});
|
|
666
|
+
} catch (err) {
|
|
667
|
+
// Tray not available - continue without it
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
// Tray-only mode: no TUI, just tray icon
|
|
672
|
+
if (trayMode) {
|
|
673
|
+
// Ignore SIGHUP so macOS terminal close doesn't kill the background tray process
|
|
674
|
+
process.removeAllListeners("SIGHUP");
|
|
675
|
+
process.on("SIGHUP", () => {});
|
|
676
|
+
|
|
677
|
+
console.log(`\nš ${pkg.name} v${pkg.version}`);
|
|
678
|
+
console.log(`Server: http://${displayHost}:${port}`);
|
|
679
|
+
|
|
680
|
+
setTimeout(() => {
|
|
681
|
+
initTrayIcon();
|
|
682
|
+
console.log("\nš” Router is now running in system tray. Close this terminal if you want.");
|
|
683
|
+
console.log(" Right-click tray icon to open dashboard or quit.\n");
|
|
684
|
+
}, 2000);
|
|
685
|
+
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Wait for server to be ready, then show interface menu loop + tray
|
|
690
|
+
setTimeout(async () => {
|
|
691
|
+
// Start tray icon alongside TUI
|
|
692
|
+
initTrayIcon();
|
|
693
|
+
|
|
694
|
+
try {
|
|
695
|
+
while (true) {
|
|
696
|
+
const choice = await showInterfaceMenu(latestVersion);
|
|
697
|
+
|
|
698
|
+
if (choice === "update") {
|
|
699
|
+
isShuttingDown = true;
|
|
700
|
+
const { clearScreen } = require("./src/cli/utils/display");
|
|
701
|
+
clearScreen();
|
|
702
|
+
console.log(`\n⬠Update v${pkg.version} ā v${latestVersion}\n`);
|
|
703
|
+
console.log(`Run this after exit:\n`);
|
|
704
|
+
console.log(` \x1b[33m${INSTALL_CMD_LATEST}\x1b[0m\n`);
|
|
705
|
+
cleanup();
|
|
706
|
+
await killAllAppProcesses(port);
|
|
707
|
+
await killProcessOnPort(port);
|
|
708
|
+
setTimeout(() => process.exit(0), 200);
|
|
709
|
+
return;
|
|
710
|
+
} else if (choice === "web") {
|
|
711
|
+
openBrowser(url);
|
|
712
|
+
// Wait for user to come back
|
|
713
|
+
const { pause } = require("./src/cli/utils/input");
|
|
714
|
+
await pause("\nPress Enter to go back to menu...");
|
|
715
|
+
} else if (choice === "terminal") {
|
|
716
|
+
// Start Terminal UI - it will return when user selects Back
|
|
717
|
+
const { startTerminalUI } = require("./src/cli/terminalUI");
|
|
718
|
+
await startTerminalUI(port);
|
|
719
|
+
// Loop continues, show menu again
|
|
720
|
+
} else if (choice === "hide") {
|
|
721
|
+
const { clearScreen } = require("./src/cli/utils/display");
|
|
722
|
+
clearScreen();
|
|
723
|
+
|
|
724
|
+
// Enable auto startup on OS boot
|
|
725
|
+
try {
|
|
726
|
+
const { enableAutoStart } = require("./src/cli/tray/autostart");
|
|
727
|
+
enableAutoStart(__filename);
|
|
728
|
+
} catch (e) { }
|
|
729
|
+
|
|
730
|
+
if (process.platform === "darwin") {
|
|
731
|
+
// macOS: keep current process alive ā spawning a detached child puts
|
|
732
|
+
// it outside the login session so NSStatusItem silently fails.
|
|
733
|
+
process.removeAllListeners("SIGHUP");
|
|
734
|
+
process.on("SIGHUP", () => {});
|
|
735
|
+
|
|
736
|
+
console.log(`\nā³ Switching to tray mode... (icon already visible in menu bar)`);
|
|
737
|
+
console.log(`š ExtremeRouter is running in tray (PID: ${process.pid})`);
|
|
738
|
+
console.log(` Server: http://${displayHost}:${port}`);
|
|
739
|
+
console.log(`\nš” You can close this terminal. Right-click tray icon to quit.\n`);
|
|
740
|
+
|
|
741
|
+
// Tray already init'd at startup ā just keep event loop alive.
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Windows/Linux: spawn detached bgProcess (systray works fine in child)
|
|
746
|
+
console.log(`\nā³ Starting background process... (tray icon will appear in ~3s)`);
|
|
747
|
+
|
|
748
|
+
const bgProcess = spawn(process.execPath, [__filename, "--tray", "--skip-update", "-p", port.toString()], {
|
|
749
|
+
detached: true,
|
|
750
|
+
stdio: "ignore",
|
|
751
|
+
windowsHide: true,
|
|
752
|
+
env: { ...process.env }
|
|
753
|
+
});
|
|
754
|
+
bgProcess.unref();
|
|
755
|
+
|
|
756
|
+
console.log(`š ExtremeRouter is now running in background (PID: ${bgProcess.pid})`);
|
|
757
|
+
console.log(` Server: http://${displayHost}:${port}`);
|
|
758
|
+
console.log(`\nš” You can close this terminal. Right-click tray icon to quit.\n`);
|
|
759
|
+
|
|
760
|
+
// cleanup() kills server so bgProcess can claim the port fresh
|
|
761
|
+
cleanup();
|
|
762
|
+
process.exit(0);
|
|
763
|
+
} else if (choice === "exit") {
|
|
764
|
+
isShuttingDown = true;
|
|
765
|
+
console.log("\nExiting...");
|
|
766
|
+
cleanup();
|
|
767
|
+
setTimeout(() => process.exit(0), 100);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
} catch (err) {
|
|
771
|
+
console.error("Error:", err.message);
|
|
772
|
+
cleanup();
|
|
773
|
+
process.exit(1);
|
|
774
|
+
}
|
|
775
|
+
}, 3000);
|
|
776
|
+
|
|
777
|
+
function attachServerEvents() {
|
|
778
|
+
server.on("error", (err) => {
|
|
779
|
+
console.error("Failed to start server:", err.message);
|
|
780
|
+
if (!isShuttingDown) tryRestart();
|
|
781
|
+
else { cleanup(); process.exit(1); }
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
server.on("close", (code) => {
|
|
785
|
+
if (isShuttingDown || code === 0) {
|
|
786
|
+
process.exit(code || 0);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
tryRestart(code);
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function tryRestart(code) {
|
|
794
|
+
const aliveMs = Date.now() - serverStartTime;
|
|
795
|
+
// Reset counter if last run was stable
|
|
796
|
+
if (aliveMs >= RESTART_RESET_MS) restartCount = 0;
|
|
797
|
+
|
|
798
|
+
if (restartCount >= MAX_RESTARTS) {
|
|
799
|
+
console.error(`\nā ļø Server crashed ${MAX_RESTARTS} times. Disabling MIT and restarting...`);
|
|
800
|
+
try {
|
|
801
|
+
const dbPath = path.join(os.homedir(), process.platform === "win32" ? path.join("AppData", "Roaming", "extremerouter", "db.json") : path.join(".extremerouter", "db.json"));
|
|
802
|
+
if (fs.existsSync(dbPath)) {
|
|
803
|
+
const db = JSON.parse(fs.readFileSync(dbPath, "utf-8"));
|
|
804
|
+
if (db.settings) db.settings.mitmEnabled = false;
|
|
805
|
+
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
|
806
|
+
}
|
|
807
|
+
} catch { /* best effort */ }
|
|
808
|
+
restartCount = 0;
|
|
809
|
+
server = spawnServer();
|
|
810
|
+
attachServerEvents();
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
restartCount++;
|
|
815
|
+
const delay = Math.min(1000 * restartCount, 10000);
|
|
816
|
+
console.error(`\nā ļø Server exited (code=${code ?? "unknown"}). Restarting in ${delay / 1000}s... (${restartCount}/${MAX_RESTARTS})`);
|
|
817
|
+
if (crashLog.length) {
|
|
818
|
+
console.error("\n--- Server crash log ---");
|
|
819
|
+
crashLog.forEach(l => console.error(l));
|
|
820
|
+
console.error("--- End crash log ---\n");
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
setTimeout(() => {
|
|
824
|
+
server = spawnServer();
|
|
825
|
+
attachServerEvents();
|
|
826
|
+
}, delay);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
attachServerEvents();
|
|
830
|
+
}
|