9router 0.3.55 → 0.3.57
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/app/.next/BUILD_ID +1 -1
- package/app/.next/app-path-routes-manifest.json +40 -40
- package/app/.next/build-manifest.json +5 -5
- package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/mitm/page.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/_global-error.html +2 -2
- package/app/.next/server/app/_global-error.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/_not-found.html +1 -1
- package/app/.next/server/app/_not-found.rsc +4 -4
- package/app/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- package/app/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/app/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/antigravity-mitm/alias/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/antigravity-mitm/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/claude-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/codex-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/copilot-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/droid-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/openclaw-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cli-tools/opencode-settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cloud/auth/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cloud/credentials/update/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cloud/model/resolve/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/cloud/models/alias/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/combos/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/combos/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/keys/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/keys/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/locale/route.js +1 -1
- package/app/.next/server/app/api/locale/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/models/alias/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/models/route.js +1 -1
- package/app/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/models/test/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/[provider]/[action]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/cursor/auto-import/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/cursor/import/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/iflow/cookie/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/kiro/auto-import/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/kiro/import/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/kiro/social-authorize/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/oauth/kiro/social-exchange/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/provider-nodes/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/provider-nodes/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/provider-nodes/validate/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/[id]/models/route.js +1 -1
- package/app/.next/server/app/api/providers/[id]/models/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/[id]/test/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/[id]/test-models/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/client/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/route.js +1 -1
- package/app/.next/server/app/api/providers/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/test-batch/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/providers/validate/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/proxy-pools/[id]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/proxy-pools/[id]/test/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/proxy-pools/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/settings/database/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/settings/proxy-test/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/settings/require-login/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/tags/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/console-logs/route.js +1 -1
- package/app/.next/server/app/api/translator/console-logs/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/console-logs/stream/route.js +1 -1
- package/app/.next/server/app/api/translator/console-logs/stream/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/load/route.js.nft.json +1 -1
- package/app/.next/server/app/api/translator/load/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/save/route.js.nft.json +1 -1
- package/app/.next/server/app/api/translator/save/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/send/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/translator/translate/route.js +1 -1
- package/app/.next/server/app/api/translator/translate/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/tunnel/disable/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/tunnel/enable/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/tunnel/status/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/[connectionId]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/chart/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/history/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/providers/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/request-details/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/request-logs/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/stats/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/usage/stream/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/api/chat/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/chat/completions/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/embeddings/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/messages/count_tokens/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/messages/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/models/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/responses/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1beta/models/[...path]/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/v1beta/models/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/version/route.js +1 -1
- package/app/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/callback.html +1 -1
- package/app/.next/server/app/callback.rsc +4 -4
- package/app/.next/server/app/callback.segments/_full.segment.rsc +4 -4
- package/app/.next/server/app/callback.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/callback.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/callback.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/callback.segments/callback/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/callback.segments/callback.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/cli-tools.html +1 -1
- package/app/.next/server/app/dashboard/cli-tools.rsc +6 -6
- package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/cli-tools.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/cli-tools.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/cli-tools.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/combos.html +1 -1
- package/app/.next/server/app/dashboard/combos.rsc +6 -6
- package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/combos.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/combos.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/combos.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/combos.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/endpoint.html +1 -1
- package/app/.next/server/app/dashboard/endpoint.rsc +6 -6
- package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/endpoint.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/endpoint.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/endpoint.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/endpoint.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/mitm.html +1 -1
- package/app/.next/server/app/dashboard/mitm.rsc +6 -6
- package/app/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/mitm.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/mitm.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/mitm.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/mitm.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/profile.html +1 -1
- package/app/.next/server/app/dashboard/profile.rsc +6 -6
- package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/profile.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/profile.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/profile.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/profile.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers/new.html +1 -1
- package/app/.next/server/app/dashboard/providers/new.rsc +6 -6
- package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers/new.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/providers/new.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers/new.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/providers/new.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers.html +1 -1
- package/app/.next/server/app/dashboard/providers.rsc +6 -6
- package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/providers.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/providers.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/providers.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/proxy-pools.html +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.rsc +6 -6
- package/app/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/proxy-pools.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/proxy-pools.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/quota.html +2 -2
- package/app/.next/server/app/dashboard/quota.rsc +7 -7
- package/app/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/quota.segments/_full.segment.rsc +7 -7
- package/app/.next/server/app/dashboard/quota.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/quota.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/quota.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/settings/pricing/page.js +1 -1
- package/app/.next/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.html +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.rsc +5 -5
- package/app/.next/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +5 -5
- package/app/.next/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/translator.html +1 -1
- package/app/.next/server/app/dashboard/translator.rsc +6 -6
- package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/translator.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/translator.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/translator.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/translator.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/usage.html +1 -1
- package/app/.next/server/app/dashboard/usage.rsc +6 -6
- package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard/usage.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard/usage.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/usage.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard/usage.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/dashboard.html +1 -1
- package/app/.next/server/app/dashboard.rsc +6 -6
- package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
- package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
- package/app/.next/server/app/dashboard.segments/_full.segment.rsc +6 -6
- package/app/.next/server/app/dashboard.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/dashboard.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/index.html +1 -1
- package/app/.next/server/app/index.rsc +4 -4
- package/app/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/app/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/index.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/landing.html +1 -1
- package/app/.next/server/app/landing.rsc +5 -5
- package/app/.next/server/app/landing.segments/_full.segment.rsc +5 -5
- package/app/.next/server/app/landing.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/landing.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/landing.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/landing.segments/landing/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/landing.segments/landing.segment.rsc +1 -1
- package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app/login.html +1 -1
- package/app/.next/server/app/login.rsc +5 -5
- package/app/.next/server/app/login.segments/_full.segment.rsc +5 -5
- package/app/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/login.segments/_index.segment.rsc +4 -4
- package/app/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/app/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/app/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/app/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/app/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/app/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/.next/server/app-paths-manifest.json +40 -40
- package/app/.next/server/chunks/1114.js +1 -1
- package/app/.next/server/chunks/1424.js +1 -1
- package/app/.next/server/chunks/318.js +1 -1
- package/app/.next/server/chunks/3832.js +1 -1
- package/app/.next/server/chunks/412.js +1 -1
- package/app/.next/server/chunks/6182.js +1 -1
- package/app/.next/server/chunks/649.js +1 -1
- package/app/.next/server/chunks/7341.js +1 -1
- package/app/.next/server/chunks/8590.js +1 -1
- package/app/.next/server/chunks/9201.js +1 -1
- package/app/.next/server/chunks/9718.js +1 -1
- package/app/.next/server/middleware-build-manifest.js +1 -1
- package/app/.next/server/pages/404.html +1 -1
- package/app/.next/server/pages/500.html +2 -2
- package/app/.next/server/server-reference-manifest.js +1 -1
- package/app/.next/server/server-reference-manifest.json +1 -1
- package/app/.next/static/chunks/2652-51aca5cd32bd186f.js +1 -0
- package/app/.next/static/chunks/3794-23858078d298f399.js +2 -0
- package/app/.next/static/chunks/{4bd1b696-e5d7c65570c947b7.js → 4bd1b696-84e33c69f320f6a0.js} +1 -1
- package/app/.next/static/chunks/{5497-9933573cfe960e0c.js → 5497-ea854c092fddeb4d.js} +3 -3
- package/app/.next/static/chunks/{619-c4c101a6a78feeb4.js → 619-bfe7e89fff6eb942.js} +1 -1
- package/app/.next/static/chunks/{9242-3e0fdad972d9b35d.js → 9242-a85bb5812b359227.js} +3 -3
- package/app/.next/static/chunks/app/(dashboard)/dashboard/cli-tools/{page-d973cbda9e65d6ae.js → page-15fc16f73981b9be.js} +1 -1
- package/app/.next/static/chunks/app/(dashboard)/dashboard/mitm/page-fb6736d76c7a3b6a.js +1 -0
- package/app/.next/static/chunks/app/dashboard/settings/pricing/{page-4b7305b3e5d6e57b.js → page-e097c892258ad1fd.js} +1 -1
- package/app/.next/static/chunks/app/landing/{page-106f74afc6f71a31.js → page-cb9f1320605eaafc.js} +1 -1
- package/app/.next/static/chunks/app/{layout-08e56eacc7acf9e1.js → layout-095f249fd620f9cf.js} +1 -1
- package/app/.next/static/chunks/{framework-9f63a52f5ba18ac8.js → framework-6854168261815bb6.js} +1 -1
- package/app/.next/static/chunks/{main-aab9e1b069a77e0f.js → main-5b78d3de83609121.js} +2 -2
- package/app/.next/static/chunks/webpack-9bef9bba7ebc0551.js +1 -0
- package/app/.next/static/css/9dff3edaebe94c91.css +3 -0
- package/app/package.json +3 -1
- package/app/public/i18n/literals/ar.json +196 -0
- package/app/public/i18n/literals/bn.json +196 -0
- package/app/public/i18n/literals/cs.json +196 -0
- package/app/public/i18n/literals/da.json +196 -0
- package/app/public/i18n/literals/de.json +196 -0
- package/app/public/i18n/literals/el.json +196 -0
- package/app/public/i18n/literals/es.json +196 -0
- package/app/public/i18n/literals/fi.json +196 -0
- package/app/public/i18n/literals/fr.json +196 -0
- package/app/public/i18n/literals/he.json +196 -0
- package/app/public/i18n/literals/hi.json +196 -0
- package/app/public/i18n/literals/hu.json +196 -0
- package/app/public/i18n/literals/id.json +196 -0
- package/app/public/i18n/literals/it.json +196 -0
- package/app/public/i18n/literals/ja.json +196 -0
- package/app/public/i18n/literals/ko.json +196 -0
- package/app/public/i18n/literals/nl.json +196 -0
- package/app/public/i18n/literals/no.json +196 -0
- package/app/public/i18n/literals/pl.json +196 -0
- package/app/public/i18n/literals/pt-BR.json +196 -0
- package/app/public/i18n/literals/pt-PT.json +196 -0
- package/app/public/i18n/literals/ro.json +196 -0
- package/app/public/i18n/literals/ru.json +196 -0
- package/app/public/i18n/literals/sv.json +196 -0
- package/app/public/i18n/literals/th.json +196 -0
- package/app/public/i18n/literals/tl.json +196 -0
- package/app/public/i18n/literals/tr.json +196 -0
- package/app/public/i18n/literals/uk.json +196 -0
- package/app/public/i18n/literals/ur.json +196 -0
- package/app/public/i18n/literals/zh-TW.json +196 -0
- package/app/src/mitm/server.js +1 -297
- package/package.json +1 -1
- package/app/.next/static/chunks/2652-378fd4fa14847021.js +0 -1
- package/app/.next/static/chunks/3794-c67589461d449d62.js +0 -2
- package/app/.next/static/chunks/app/(dashboard)/dashboard/mitm/page-bf88929b2cc595ab.js +0 -1
- package/app/.next/static/chunks/webpack-25c43999146c934e.js +0 -1
- package/app/.next/static/css/4016074f63f34af9.css +0 -3
- package/app/src/mitm/cert/generate.js +0 -32
- package/app/src/mitm/cert/install.js +0 -176
- package/app/src/mitm/cert/rootCA.js +0 -173
- package/app/src/mitm/dns/dnsConfig.js +0 -216
- package/app/src/mitm/logger.js +0 -8
- package/app/src/mitm/manager.js +0 -602
- package/app/src/mitm/paths.js +0 -16
- package/app/src/mitm/server2.js +0 -1099
- /package/app/.next/static/{lSUZX6G8rzoxhxgvfuJ7I → k34dymG0FFBnm_nPA74jH}/_buildManifest.js +0 -0
- /package/app/.next/static/{lSUZX6G8rzoxhxgvfuJ7I → k34dymG0FFBnm_nPA74jH}/_ssgManifest.js +0 -0
package/app/src/mitm/server2.js
DELETED
|
@@ -1,1099 +0,0 @@
|
|
|
1
|
-
const https = require("https");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const dns = require("dns");
|
|
5
|
-
const { promisify } = require("util");
|
|
6
|
-
const Database = require("better-sqlite3");
|
|
7
|
-
|
|
8
|
-
const CURSOR_GLOBAL_DB = path.join(
|
|
9
|
-
process.env.HOME,
|
|
10
|
-
"Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
// Read full conversation from Cursor global DB
|
|
14
|
-
function readConversationFromDb(conversationId) {
|
|
15
|
-
try {
|
|
16
|
-
const db = new Database(CURSOR_GLOBAL_DB, { readonly: true, fileMustExist: true });
|
|
17
|
-
const stmt = db.prepare("SELECT value FROM cursorDiskKV WHERE key=?");
|
|
18
|
-
|
|
19
|
-
const row = stmt.get(`composerData:${conversationId}`);
|
|
20
|
-
if (!row) { db.close(); return null; }
|
|
21
|
-
|
|
22
|
-
const composer = JSON.parse(row.value);
|
|
23
|
-
const headers = composer.fullConversationHeadersOnly || [];
|
|
24
|
-
const name = composer.name || "";
|
|
25
|
-
|
|
26
|
-
const messages = [];
|
|
27
|
-
for (const h of headers) {
|
|
28
|
-
const brow = stmt.get(`bubbleId:${conversationId}:${h.bubbleId}`);
|
|
29
|
-
if (!brow) continue;
|
|
30
|
-
const bubble = JSON.parse(brow.value);
|
|
31
|
-
const role = h.type === 1 ? "user" : "assistant";
|
|
32
|
-
const text = (bubble.text || "").trim();
|
|
33
|
-
const ts = bubble.createdAt || null;
|
|
34
|
-
|
|
35
|
-
// Tool call bubble (no text but has toolFormerData)
|
|
36
|
-
const tool = bubble.toolFormerData && bubble.toolFormerData.name
|
|
37
|
-
? { name: bubble.toolFormerData.name, args: bubble.toolFormerData.rawArgs || null, status: bubble.toolFormerData.status || null }
|
|
38
|
-
: null;
|
|
39
|
-
|
|
40
|
-
if (!text && !tool) continue;
|
|
41
|
-
messages.push({ role, text: text || null, tool, ts });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
db.close();
|
|
45
|
-
return { name, totalMessages: headers.length, messages };
|
|
46
|
-
} catch (e) {
|
|
47
|
-
return { error: e.message };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const INTERNAL_REQUEST_HEADER = { name: "x-request-source", value: "local" };
|
|
52
|
-
|
|
53
|
-
// All intercepted domains across all tools
|
|
54
|
-
const TARGET_HOSTS = [
|
|
55
|
-
"daily-cloudcode-pa.googleapis.com",
|
|
56
|
-
"cloudcode-pa.googleapis.com",
|
|
57
|
-
"api.individual.githubcopilot.com",
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
const LOCAL_PORT = 443;
|
|
61
|
-
const ROUTER_URL = "http://localhost:20128/v1/chat/completions";
|
|
62
|
-
const API_KEY = process.env.ROUTER_API_KEY;
|
|
63
|
-
const { DATA_DIR, MITM_DIR } = require("./paths");
|
|
64
|
-
const DB_FILE = path.join(DATA_DIR, "db.json");
|
|
65
|
-
|
|
66
|
-
const ENABLE_FILE_LOG = false;
|
|
67
|
-
const CURSOR_LOG_DIR = path.join(__dirname, "../../logs/cursor");
|
|
68
|
-
if (!fs.existsSync(CURSOR_LOG_DIR)) fs.mkdirSync(CURSOR_LOG_DIR, { recursive: true });
|
|
69
|
-
const CURSOR_JSONL = path.join(CURSOR_LOG_DIR, "cursor_messages.jsonl");
|
|
70
|
-
|
|
71
|
-
const CURSOR_ENDPOINTS = [
|
|
72
|
-
"/agent.v1.AgentService/RunPoll",
|
|
73
|
-
"/aiserver.v1.BidiService/BidiAppend",
|
|
74
|
-
"/agent.v1.AgentService/Run",
|
|
75
|
-
"/agent.v1.AgentService/RunSSE",
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
// Mock rules: if last user message contains key → reply with value
|
|
79
|
-
const MOCK_RULES = {
|
|
80
|
-
"aaa": "bbb",
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// conversationId → mock reply (set when AgentRunRequest matches a rule)
|
|
84
|
-
const mockPending = new Map();
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Decode Connect-RPC frame: [1 byte flags][4 bytes length][protobuf body]
|
|
88
|
-
* Then walk raw protobuf fields and extract readable strings/numbers.
|
|
89
|
-
*/
|
|
90
|
-
function decodeConnectFrame(buf) {
|
|
91
|
-
if (!buf || buf.length < 5) return null;
|
|
92
|
-
const msgLen = buf.readUInt32BE(1);
|
|
93
|
-
if (buf.length < 5 + msgLen) return null;
|
|
94
|
-
return decodeProtoRaw(buf.slice(5, 5 + msgLen));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function decodeProtoRaw(buf, depth = 0) {
|
|
98
|
-
if (depth > 6) return {};
|
|
99
|
-
const result = {};
|
|
100
|
-
let offset = 0;
|
|
101
|
-
try {
|
|
102
|
-
while (offset < buf.length) {
|
|
103
|
-
const tag = readVarint(buf, offset);
|
|
104
|
-
if (!tag) break;
|
|
105
|
-
offset += tag.bytes;
|
|
106
|
-
const fieldNum = tag.value >> 3;
|
|
107
|
-
const wireType = tag.value & 0x7;
|
|
108
|
-
|
|
109
|
-
if (wireType === 0) {
|
|
110
|
-
// varint
|
|
111
|
-
const v = readVarint(buf, offset);
|
|
112
|
-
if (!v) break;
|
|
113
|
-
offset += v.bytes;
|
|
114
|
-
result[`f${fieldNum}`] = v.value;
|
|
115
|
-
} else if (wireType === 2) {
|
|
116
|
-
// length-delimited: string, bytes, or nested message
|
|
117
|
-
const lenV = readVarint(buf, offset);
|
|
118
|
-
if (!lenV) break;
|
|
119
|
-
offset += lenV.bytes;
|
|
120
|
-
const data = buf.slice(offset, offset + lenV.value);
|
|
121
|
-
offset += lenV.value;
|
|
122
|
-
// Try as UTF-8 string first
|
|
123
|
-
const str = data.toString("utf8");
|
|
124
|
-
const isPrintable = /^[\x20-\x7E\n\r\t]*$/.test(str) && str.length > 0;
|
|
125
|
-
if (isPrintable) {
|
|
126
|
-
result[`f${fieldNum}`] = str;
|
|
127
|
-
} else if (data.length > 0 && data.length < 4096) {
|
|
128
|
-
// Try nested decode
|
|
129
|
-
const nested = decodeProtoRaw(data, depth + 1);
|
|
130
|
-
result[`f${fieldNum}`] = Object.keys(nested).length ? nested : data.toString("hex").substring(0, 64);
|
|
131
|
-
}
|
|
132
|
-
} else if (wireType === 5) {
|
|
133
|
-
offset += 4;
|
|
134
|
-
} else if (wireType === 1) {
|
|
135
|
-
offset += 8;
|
|
136
|
-
} else {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
} catch { /* best effort */ }
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function readVarint(buf, offset) {
|
|
145
|
-
let value = 0, shift = 0, bytes = 0;
|
|
146
|
-
while (offset + bytes < buf.length) {
|
|
147
|
-
const b = buf[offset + bytes++];
|
|
148
|
-
value |= (b & 0x7f) << shift;
|
|
149
|
-
shift += 7;
|
|
150
|
-
if (!(b & 0x80)) return { value, bytes };
|
|
151
|
-
if (shift >= 28) break;
|
|
152
|
-
}
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const zlib = require("zlib");
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Deep decode protobuf buffer — returns structured object.
|
|
160
|
-
* Tries to interpret each length-delimited field as:
|
|
161
|
-
* 1. UTF-8 string (if fully printable)
|
|
162
|
-
* 2. Nested proto message (recursive)
|
|
163
|
-
* 3. Hex string fallback
|
|
164
|
-
*/
|
|
165
|
-
function deepDecodeProto(buf, depth = 0) {
|
|
166
|
-
if (depth > 10 || buf.length === 0) return null;
|
|
167
|
-
const result = {};
|
|
168
|
-
let off = 0;
|
|
169
|
-
try {
|
|
170
|
-
while (off < buf.length) {
|
|
171
|
-
// Read varint tag
|
|
172
|
-
let tag = 0, shift = 0;
|
|
173
|
-
while (off < buf.length) {
|
|
174
|
-
const b = buf[off++];
|
|
175
|
-
tag |= (b & 0x7f) << shift;
|
|
176
|
-
shift += 7;
|
|
177
|
-
if (!(b & 0x80)) break;
|
|
178
|
-
if (shift > 28) break;
|
|
179
|
-
}
|
|
180
|
-
if (tag === 0) break;
|
|
181
|
-
const field = tag >> 3;
|
|
182
|
-
const wire = tag & 7;
|
|
183
|
-
const key = `f${field}`;
|
|
184
|
-
|
|
185
|
-
if (wire === 0) {
|
|
186
|
-
// varint
|
|
187
|
-
let v = 0, s = 0;
|
|
188
|
-
while (off < buf.length) {
|
|
189
|
-
const b = buf[off++];
|
|
190
|
-
v |= (b & 0x7f) << s;
|
|
191
|
-
s += 7;
|
|
192
|
-
if (!(b & 0x80)) break;
|
|
193
|
-
}
|
|
194
|
-
result[key] = v;
|
|
195
|
-
} else if (wire === 2) {
|
|
196
|
-
// length-delimited
|
|
197
|
-
let len = 0, s = 0;
|
|
198
|
-
while (off < buf.length) {
|
|
199
|
-
const b = buf[off++];
|
|
200
|
-
len |= (b & 0x7f) << s;
|
|
201
|
-
s += 7;
|
|
202
|
-
if (!(b & 0x80)) break;
|
|
203
|
-
}
|
|
204
|
-
if (off + len > buf.length || len < 0) break;
|
|
205
|
-
const data = buf.slice(off, off + len);
|
|
206
|
-
off += len;
|
|
207
|
-
|
|
208
|
-
if (len === 0) {
|
|
209
|
-
result[key] = "";
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Try UTF-8 string
|
|
214
|
-
const str = data.toString("utf8");
|
|
215
|
-
const isPrintable = str.length > 0 && !/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(str);
|
|
216
|
-
if (isPrintable) {
|
|
217
|
-
// Accumulate repeated fields as array
|
|
218
|
-
if (key in result) {
|
|
219
|
-
result[key] = [].concat(result[key], str);
|
|
220
|
-
} else {
|
|
221
|
-
result[key] = str;
|
|
222
|
-
}
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Try nested proto
|
|
227
|
-
if (len >= 2 && len <= 500000) {
|
|
228
|
-
const nested = deepDecodeProto(data, depth + 1);
|
|
229
|
-
if (nested && Object.keys(nested).length > 0) {
|
|
230
|
-
if (key in result) {
|
|
231
|
-
result[key] = [].concat(result[key], [nested]);
|
|
232
|
-
} else {
|
|
233
|
-
result[key] = nested;
|
|
234
|
-
}
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Fallback: hex (truncate at 64 bytes)
|
|
240
|
-
result[key] = data.slice(0, 64).toString("hex");
|
|
241
|
-
} else if (wire === 5) {
|
|
242
|
-
off += 4;
|
|
243
|
-
} else if (wire === 1) {
|
|
244
|
-
off += 8;
|
|
245
|
-
} else {
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
} catch { /* best effort */ }
|
|
250
|
-
return result;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ── Proto helpers ──────────────────────────────────────────────
|
|
254
|
-
function protoReadVarint(buf, off) {
|
|
255
|
-
let v = 0, shift = 0;
|
|
256
|
-
while (off < buf.length) {
|
|
257
|
-
const b = buf[off++];
|
|
258
|
-
v |= (b & 0x7f) << shift;
|
|
259
|
-
shift += 7;
|
|
260
|
-
if (!(b & 0x80)) return { v, off };
|
|
261
|
-
if (shift > 49) break;
|
|
262
|
-
}
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function protoWalkFields(buf) {
|
|
267
|
-
const fields = [];
|
|
268
|
-
let off = 0;
|
|
269
|
-
while (off < buf.length) {
|
|
270
|
-
const t = protoReadVarint(buf, off);
|
|
271
|
-
if (!t || t.v === 0) break;
|
|
272
|
-
off = t.off;
|
|
273
|
-
const field = t.v >> 3, wire = t.v & 7;
|
|
274
|
-
if (wire === 0) {
|
|
275
|
-
const r = protoReadVarint(buf, off);
|
|
276
|
-
if (!r) break;
|
|
277
|
-
off = r.off;
|
|
278
|
-
fields.push({ field, wire, value: r.v });
|
|
279
|
-
} else if (wire === 2) {
|
|
280
|
-
const r = protoReadVarint(buf, off);
|
|
281
|
-
if (!r) break;
|
|
282
|
-
off = r.off;
|
|
283
|
-
if (off + r.v > buf.length) break;
|
|
284
|
-
fields.push({ field, wire, data: buf.slice(off, off + r.v) });
|
|
285
|
-
off += r.v;
|
|
286
|
-
} else if (wire === 5) { off += 4; }
|
|
287
|
-
else if (wire === 1) { off += 8; }
|
|
288
|
-
else break;
|
|
289
|
-
}
|
|
290
|
-
return fields;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function protoStr(buf) {
|
|
294
|
-
if (!buf || buf.length === 0) return null;
|
|
295
|
-
const s = buf.toString("utf8");
|
|
296
|
-
return /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(s) ? null : s;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// ── Schema-aware decoders ──────────────────────────────────────
|
|
300
|
-
|
|
301
|
-
// BidiAppendRequest: f1=data(hex-encoded AgentClientMessage), f2=requestId{f1=uuid}, f3=seqno
|
|
302
|
-
function decodeBidiAppend(buf) {
|
|
303
|
-
const fields = protoWalkFields(buf);
|
|
304
|
-
const result = {};
|
|
305
|
-
for (const f of fields) {
|
|
306
|
-
if (f.field === 1 && f.data) {
|
|
307
|
-
// f1.data is ASCII hex string → decode to actual proto bytes
|
|
308
|
-
try {
|
|
309
|
-
const hexStr = f.data.toString("utf8");
|
|
310
|
-
const protoBytes = Buffer.from(hexStr, "hex");
|
|
311
|
-
result.agentMsg = decodeAgentClientMsg(protoBytes);
|
|
312
|
-
} catch { result.agentMsg = { type: "hex_decode_error" }; }
|
|
313
|
-
} else if (f.field === 2 && f.data) {
|
|
314
|
-
const inner = protoWalkFields(f.data);
|
|
315
|
-
const id = inner.find(x => x.field === 1 && x.data);
|
|
316
|
-
result.requestId = id ? protoStr(id.data) : null;
|
|
317
|
-
} else if (f.field === 3) result.seqno = f.value;
|
|
318
|
-
}
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// AgentClientMessage oneof: f1=AgentRunRequest, f2=ExecClientMessage, f3=KvClientMessage, f4=ConversationAction, f5=ExecClientControlMessage, f6=InteractionResponse, f7=ClientHeartbeat
|
|
323
|
-
function decodeAgentClientMsg(buf) {
|
|
324
|
-
const fields = protoWalkFields(buf);
|
|
325
|
-
for (const f of fields) {
|
|
326
|
-
// f7=ClientHeartbeat: wire-type 2 but empty (len=0), data is Buffer(0)
|
|
327
|
-
if (f.field === 7) return { type: "ClientHeartbeat" };
|
|
328
|
-
if (f.data === undefined || f.data === null) continue;
|
|
329
|
-
if (f.field === 1 && f.data.length > 0) return { type: "AgentRunRequest", ...decodeAgentRunRequest(f.data) };
|
|
330
|
-
if (f.field === 2 && f.data.length > 0) return { type: "ExecClientMessage", ...decodeExecClientMessage(f.data) };
|
|
331
|
-
if (f.field === 3) return { type: "KvClientMessage" };
|
|
332
|
-
if (f.field === 4 && f.data.length > 0) return { type: "ConversationAction", ...decodeConversationAction(f.data) };
|
|
333
|
-
if (f.field === 5) return { type: "ExecClientControlMessage" };
|
|
334
|
-
if (f.field === 6) return { type: "InteractionResponse" };
|
|
335
|
-
}
|
|
336
|
-
return { type: "unknown" };
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// AgentRunRequest: f1=conversation_state, f2=action, f3=model_details, f4=mcp_tools, f5=conversation_id, f6=mcp_file_system_options, f7=skill_options
|
|
340
|
-
function decodeAgentRunRequest(buf) {
|
|
341
|
-
const fields = protoWalkFields(buf);
|
|
342
|
-
const result = {};
|
|
343
|
-
|
|
344
|
-
for (const f of fields) {
|
|
345
|
-
if (f.field === 1 && f.data) result.state = decodeConversationState(f.data);
|
|
346
|
-
if (f.field === 2 && f.data) result.action = decodeConversationAction(f.data);
|
|
347
|
-
if (f.field === 3 && f.data) result.model = decodeModelDetails(f.data);
|
|
348
|
-
if (f.field === 4 && f.data) result.mcpTools = decodeMcpTools(f.data);
|
|
349
|
-
if (f.field === 5 && f.data) result.conversationId = protoStr(f.data);
|
|
350
|
-
}
|
|
351
|
-
return result;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// McpTools: f1=repeated McpDescriptor (f1=serverName, f2=repeated tools)
|
|
355
|
-
function decodeMcpTools(buf) {
|
|
356
|
-
const fields = protoWalkFields(buf);
|
|
357
|
-
const descriptors = [];
|
|
358
|
-
for (const f of fields) {
|
|
359
|
-
if (f.field === 1 && f.data) {
|
|
360
|
-
const inner = protoWalkFields(f.data);
|
|
361
|
-
const name = inner.find(x => x.field === 1 && x.data);
|
|
362
|
-
const tools = inner.filter(x => x.field === 2 && x.data).map(t => {
|
|
363
|
-
const tf = protoWalkFields(t.data);
|
|
364
|
-
const tname = tf.find(x => x.field === 1 && x.data);
|
|
365
|
-
return tname ? protoStr(tname.data) : null;
|
|
366
|
-
}).filter(Boolean);
|
|
367
|
-
descriptors.push({ server: name ? protoStr(name.data) : null, tools });
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return descriptors;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// ExecClientMessage: f1=id(uint32), f2=shellResult, f3=writeResult, etc.
|
|
374
|
-
function decodeExecClientMessage(buf) {
|
|
375
|
-
const fields = protoWalkFields(buf);
|
|
376
|
-
const result = { id: null, execId: null, resultType: "unknown" };
|
|
377
|
-
for (const f of fields) {
|
|
378
|
-
if (f.field === 1 && f.value !== undefined) result.id = f.value;
|
|
379
|
-
if (f.field === 15 && f.data) result.execId = protoStr(f.data);
|
|
380
|
-
// Detect result type by field number (oneof)
|
|
381
|
-
const resultTypes = { 2: "shellResult", 3: "writeResult", 4: "deleteResult", 5: "grepResult", 7: "readResult", 8: "lsResult", 9: "diagnosticsResult", 10: "requestContextResult", 11: "mcpResult", 14: "shellStream", 16: "backgroundShellSpawnResult" };
|
|
382
|
-
if (f.data && resultTypes[f.field]) result.resultType = resultTypes[f.field];
|
|
383
|
-
}
|
|
384
|
-
return result;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// ModelDetails — scan all string fields
|
|
388
|
-
function decodeModelDetails(buf) {
|
|
389
|
-
const fields = protoWalkFields(buf);
|
|
390
|
-
const result = {};
|
|
391
|
-
for (const f of fields) {
|
|
392
|
-
if (f.data) {
|
|
393
|
-
const s = protoStr(f.data);
|
|
394
|
-
if (s && s.length > 0) result[`f${f.field}`] = s;
|
|
395
|
-
} else if (f.value !== undefined) {
|
|
396
|
-
result[`f${f.field}`] = f.value;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return result;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ConversationAction: f1=StartChatAction → f1=userMsg{f1=text,f2=uuid,f8=lexical}, f2=history{f2=contextFiles,f7=tools}
|
|
403
|
-
function decodeConversationAction(buf) {
|
|
404
|
-
const result = {};
|
|
405
|
-
const topFields = protoWalkFields(buf);
|
|
406
|
-
|
|
407
|
-
// action.f1 = StartChatAction wrapper
|
|
408
|
-
const f1 = topFields.find(f => f.field === 1 && f.data && f.data.length > 0);
|
|
409
|
-
if (!f1) {
|
|
410
|
-
// No StartChatAction — show which fields are present for debugging
|
|
411
|
-
const presentFields = topFields.map(f => `f${f.field}`);
|
|
412
|
-
if (presentFields.length > 0) result._fields = presentFields;
|
|
413
|
-
return result;
|
|
414
|
-
}
|
|
415
|
-
const f1Fields = protoWalkFields(f1.data);
|
|
416
|
-
|
|
417
|
-
// ── User message: action.f1.f1 ──────────────────────────────
|
|
418
|
-
const f1f1 = f1Fields.find(f => f.field === 1 && f.data);
|
|
419
|
-
if (f1f1) {
|
|
420
|
-
for (const f of protoWalkFields(f1f1.data)) {
|
|
421
|
-
if (!f.data) continue;
|
|
422
|
-
const s = protoStr(f.data);
|
|
423
|
-
if (f.field === 1 && s) result.text = s;
|
|
424
|
-
if (f.field === 2 && s) result.messageId = s;
|
|
425
|
-
if (f.field === 8 && s) {
|
|
426
|
-
try {
|
|
427
|
-
const lex = JSON.parse(s);
|
|
428
|
-
const texts = [];
|
|
429
|
-
function extractLexText(node) {
|
|
430
|
-
if (node?.text) texts.push(node.text);
|
|
431
|
-
(node?.children || []).forEach(extractLexText);
|
|
432
|
-
}
|
|
433
|
-
extractLexText(lex?.root);
|
|
434
|
-
result.textFull = texts.join("");
|
|
435
|
-
} catch { result.lexical = s.substring(0, 300); }
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// ── Conversation history: action.f1.f2 ──────────────────────
|
|
441
|
-
const f1f2 = f1Fields.find(f => f.field === 2 && f.data);
|
|
442
|
-
if (f1f2) {
|
|
443
|
-
const histFields = protoWalkFields(f1f2.data);
|
|
444
|
-
|
|
445
|
-
// f2 repeated = context files (rules, open files...)
|
|
446
|
-
result.contextFiles = histFields
|
|
447
|
-
.filter(f => f.field === 2 && f.data)
|
|
448
|
-
.map(t => {
|
|
449
|
-
const tf = protoWalkFields(t.data);
|
|
450
|
-
const name = tf.find(f => f.field === 1 && f.data);
|
|
451
|
-
const content = tf.find(f => f.field === 2 && f.data);
|
|
452
|
-
return {
|
|
453
|
-
path: name ? protoStr(name.data) : null,
|
|
454
|
-
content: content ? protoStr(content.data)?.substring(0, 500) : null,
|
|
455
|
-
};
|
|
456
|
-
})
|
|
457
|
-
.filter(x => x.path);
|
|
458
|
-
|
|
459
|
-
// f7 repeated = MCP tools
|
|
460
|
-
result.tools = histFields
|
|
461
|
-
.filter(f => f.field === 7 && f.data)
|
|
462
|
-
.map(t => {
|
|
463
|
-
const tf = protoWalkFields(t.data);
|
|
464
|
-
const name = tf.find(f => f.field === 1 && f.data);
|
|
465
|
-
const desc = tf.find(f => f.field === 2 && f.data);
|
|
466
|
-
return {
|
|
467
|
-
name: name ? protoStr(name.data) : null,
|
|
468
|
-
description: desc ? protoStr(desc.data) : null,
|
|
469
|
-
};
|
|
470
|
-
})
|
|
471
|
-
.filter(x => x.name);
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return result;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Recursively decode any proto buffer into a plain JS object for debugging.
|
|
480
|
-
* Each field: try UTF-8 string → nested proto → hex fallback.
|
|
481
|
-
* Repeated fields become arrays.
|
|
482
|
-
*/
|
|
483
|
-
function protoTreeToObj(buf, depth = 0) {
|
|
484
|
-
if (depth > 12 || !buf || buf.length === 0) return null;
|
|
485
|
-
const result = {};
|
|
486
|
-
const fields = protoWalkFields(buf);
|
|
487
|
-
for (const f of fields) {
|
|
488
|
-
const key = `f${f.field}`;
|
|
489
|
-
let val;
|
|
490
|
-
if (f.data !== undefined) {
|
|
491
|
-
const s = protoStr(f.data);
|
|
492
|
-
if (s !== null) {
|
|
493
|
-
// Detect hex-encoded proto (BidiAppend.data field)
|
|
494
|
-
if (/^[0-9a-f]{20,}$/i.test(s)) {
|
|
495
|
-
try {
|
|
496
|
-
const dec = protoTreeToObj(Buffer.from(s, "hex"), depth + 1);
|
|
497
|
-
val = (dec && Object.keys(dec).length > 0) ? dec : s;
|
|
498
|
-
} catch { val = s; }
|
|
499
|
-
} else {
|
|
500
|
-
try { val = JSON.parse(s); } catch { val = s; }
|
|
501
|
-
}
|
|
502
|
-
} else if (f.data.length > 0) {
|
|
503
|
-
const nested = protoTreeToObj(f.data, depth + 1);
|
|
504
|
-
val = (nested && Object.keys(nested).length > 0)
|
|
505
|
-
? nested
|
|
506
|
-
: f.data.toString("hex");
|
|
507
|
-
} else {
|
|
508
|
-
val = "";
|
|
509
|
-
}
|
|
510
|
-
} else {
|
|
511
|
-
val = f.value;
|
|
512
|
-
}
|
|
513
|
-
// Accumulate repeated fields as array
|
|
514
|
-
if (key in result) {
|
|
515
|
-
result[key] = [].concat(result[key], [val]);
|
|
516
|
-
} else {
|
|
517
|
-
result[key] = val;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
return result;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// ConversationStateStructure: f1=root_prompt_messages_json(encrypted bytes), f3=todos, f8=turns, f10=mode
|
|
524
|
-
function decodeConversationState(buf) {
|
|
525
|
-
const fields = protoWalkFields(buf);
|
|
526
|
-
const turns = [], todos = [];
|
|
527
|
-
let mode = null, messageCount = 0;
|
|
528
|
-
for (const f of fields) {
|
|
529
|
-
if (f.field === 10 && f.value !== undefined) {
|
|
530
|
-
const modes = ["unspecified", "agent", "ask", "plan", "debug", "triage"];
|
|
531
|
-
mode = modes[f.value] || `mode_${f.value}`;
|
|
532
|
-
}
|
|
533
|
-
if (!f.data) continue;
|
|
534
|
-
if (f.field === 1) {
|
|
535
|
-
// Encrypted bytes — just count, cannot decode
|
|
536
|
-
messageCount++;
|
|
537
|
-
} else if (f.field === 8) {
|
|
538
|
-
// f8 = repeated bytes (ConversationTurn OR encrypted blob)
|
|
539
|
-
// Encrypted turns: binary blobs that don't parse as valid proto strings
|
|
540
|
-
if (f.data.length === 32) continue; // 32-byte encrypted blob
|
|
541
|
-
const inner = protoWalkFields(f.data);
|
|
542
|
-
const strs = inner.filter(x => x.data).map(x => protoStr(x.data)).filter(s => s && s.length > 3);
|
|
543
|
-
if (strs.length) turns.push(strs.slice(0, 3));
|
|
544
|
-
} else if (f.field === 3) {
|
|
545
|
-
// f3 = repeated bytes (TodoItem OR encrypted blob)
|
|
546
|
-
// Encrypted: 32-byte binary (shows as 64-char hex in protoStr-reject path)
|
|
547
|
-
// TodoItem: f1=id(str), f2=content(str), f3=status(varint)
|
|
548
|
-
if (f.data.length === 32) {
|
|
549
|
-
// 32 bytes = encrypted blob, skip
|
|
550
|
-
continue;
|
|
551
|
-
}
|
|
552
|
-
const inner = protoWalkFields(f.data);
|
|
553
|
-
const id = inner.find(x => x.field === 1 && x.data);
|
|
554
|
-
const content = inner.find(x => x.field === 2 && x.data);
|
|
555
|
-
const status = inner.find(x => x.field === 3 && x.value !== undefined);
|
|
556
|
-
const idStr = id ? protoStr(id.data) : null;
|
|
557
|
-
const contentStr = content ? protoStr(content.data) : null;
|
|
558
|
-
// If neither id nor content is readable string, it's encrypted
|
|
559
|
-
if (!idStr && !contentStr) continue;
|
|
560
|
-
const statusMap = ["unspecified", "pending", "in_progress", "completed", "cancelled"];
|
|
561
|
-
todos.push({
|
|
562
|
-
id: idStr,
|
|
563
|
-
content: contentStr,
|
|
564
|
-
status: status ? (statusMap[status.value] || `status_${status.value}`) : null,
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
return { mode, messageCount, turns: turns.slice(0, 5), todos: todos.slice(0, 20) };
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// ── Save per-chat JSON file ────────────────────────────────────
|
|
572
|
-
// Only logs AgentRunRequest and ExecClientMessage — skips noise (RunPoll, KvClientMessage, ClientHeartbeat...)
|
|
573
|
-
function saveCursorLogFull(url, headers, buf) {
|
|
574
|
-
try {
|
|
575
|
-
if (buf.length < 5) return;
|
|
576
|
-
if (!url.includes("BidiAppend")) return;
|
|
577
|
-
|
|
578
|
-
const isGzip = buf[0] === 0x1f && buf[1] === 0x8b;
|
|
579
|
-
const raw = isGzip ? zlib.gunzipSync(buf) : (buf[0] === 0x00 || buf[0] === 0x01 ? buf.slice(5) : buf);
|
|
580
|
-
|
|
581
|
-
const decoded = decodeBidiAppend(raw);
|
|
582
|
-
const msgType = decoded?.agentMsg?.type;
|
|
583
|
-
|
|
584
|
-
// Only log meaningful message types
|
|
585
|
-
if (msgType !== "AgentRunRequest" && msgType !== "ExecClientMessage") return;
|
|
586
|
-
|
|
587
|
-
const action = decoded?.agentMsg?.action || {};
|
|
588
|
-
const userMessage = action.textFull || action.text || null;
|
|
589
|
-
|
|
590
|
-
// Fetch full conversation from DB for AgentRunRequest
|
|
591
|
-
const conversationId = decoded?.agentMsg?.conversationId || null;
|
|
592
|
-
let conversation = null;
|
|
593
|
-
if (conversationId && msgType === "AgentRunRequest") {
|
|
594
|
-
conversation = readConversationFromDb(conversationId);
|
|
595
|
-
|
|
596
|
-
// Check MOCK_RULES against last user message
|
|
597
|
-
const lastMsg = userMessage || "";
|
|
598
|
-
for (const [keyword, reply] of Object.entries(MOCK_RULES)) {
|
|
599
|
-
if (lastMsg.toLowerCase().includes(keyword.toLowerCase())) {
|
|
600
|
-
mockPending.set(conversationId, reply);
|
|
601
|
-
console.log(`[MOCK] Triggered rule "${keyword}" → "${reply}" for conv=${conversationId}`);
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
608
|
-
fs.writeFileSync(
|
|
609
|
-
path.join(CURSOR_LOG_DIR, `${ts}_${msgType}_chat.json`),
|
|
610
|
-
JSON.stringify({
|
|
611
|
-
ts: new Date().toISOString(),
|
|
612
|
-
type: msgType,
|
|
613
|
-
version: headers["x-cursor-client-version"] || null,
|
|
614
|
-
sizeIn: buf.length,
|
|
615
|
-
userMessage,
|
|
616
|
-
decoded,
|
|
617
|
-
conversation,
|
|
618
|
-
}, null, 2)
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
const convName = conversation?.name ? ` | conv="${conversation.name}" (${conversation.totalMessages} msgs)` : "";
|
|
622
|
-
console.log(`[CURSOR] ${msgType} | msg=${userMessage || "(none)"}${convName} | ${buf.length}B`);
|
|
623
|
-
} catch (e) {
|
|
624
|
-
console.error(`[CURSOR] decode error: ${e.message}`);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
if (!API_KEY) {
|
|
629
|
-
console.error("❌ ROUTER_API_KEY required");
|
|
630
|
-
process.exit(1);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
const { getCertForDomain } = require("./cert/generate");
|
|
634
|
-
|
|
635
|
-
// Certificate cache for performance
|
|
636
|
-
const certCache = new Map();
|
|
637
|
-
|
|
638
|
-
// SNI callback for dynamic certificate generation
|
|
639
|
-
function sniCallback(servername, cb) {
|
|
640
|
-
try {
|
|
641
|
-
// Check cache first
|
|
642
|
-
if (certCache.has(servername)) {
|
|
643
|
-
const cached = certCache.get(servername);
|
|
644
|
-
return cb(null, cached);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// Generate new cert for this domain
|
|
648
|
-
const certData = getCertForDomain(servername);
|
|
649
|
-
if (!certData) {
|
|
650
|
-
return cb(new Error(`Failed to generate cert for ${servername}`));
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Create secure context
|
|
654
|
-
const ctx = require("tls").createSecureContext({
|
|
655
|
-
key: certData.key,
|
|
656
|
-
cert: certData.cert
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
// Cache it
|
|
660
|
-
certCache.set(servername, ctx);
|
|
661
|
-
console.log(`✅ Generated cert for: ${servername}`);
|
|
662
|
-
|
|
663
|
-
cb(null, ctx);
|
|
664
|
-
} catch (error) {
|
|
665
|
-
console.error(`❌ SNI error for ${servername}:`, error.message);
|
|
666
|
-
cb(error);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Load Root CA for default context
|
|
671
|
-
const certDir = MITM_DIR;
|
|
672
|
-
const rootCAKeyPath = path.join(certDir, "rootCA.key");
|
|
673
|
-
const rootCACertPath = path.join(certDir, "rootCA.crt");
|
|
674
|
-
|
|
675
|
-
let sslOptions;
|
|
676
|
-
try {
|
|
677
|
-
sslOptions = {
|
|
678
|
-
key: fs.readFileSync(rootCAKeyPath),
|
|
679
|
-
cert: fs.readFileSync(rootCACertPath),
|
|
680
|
-
SNICallback: sniCallback
|
|
681
|
-
};
|
|
682
|
-
} catch (e) {
|
|
683
|
-
console.error(`❌ Root CA not found in ${certDir}: ${e.message}`);
|
|
684
|
-
process.exit(1);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Antigravity: Gemini generateContent endpoints
|
|
688
|
-
const ANTIGRAVITY_URL_PATTERNS = [":generateContent", ":streamGenerateContent"];
|
|
689
|
-
// Copilot: OpenAI-compatible + Anthropic endpoints
|
|
690
|
-
const COPILOT_URL_PATTERNS = ["/chat/completions", "/v1/messages", "/responses"];
|
|
691
|
-
|
|
692
|
-
const LOG_DIR = path.join(__dirname, "../../logs/mitm");
|
|
693
|
-
if (ENABLE_FILE_LOG && !fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
694
|
-
|
|
695
|
-
function saveRequestLog(url, bodyBuffer) {
|
|
696
|
-
if (!ENABLE_FILE_LOG) return;
|
|
697
|
-
try {
|
|
698
|
-
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
699
|
-
const urlSlug = url.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 60);
|
|
700
|
-
const filePath = path.join(LOG_DIR, `${ts}_${urlSlug}.json`);
|
|
701
|
-
const body = JSON.parse(bodyBuffer.toString());
|
|
702
|
-
fs.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
703
|
-
} catch { /* ignore */ }
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
const cachedTargetIPs = {};
|
|
707
|
-
async function resolveTargetIP(hostname) {
|
|
708
|
-
if (cachedTargetIPs[hostname]) return cachedTargetIPs[hostname];
|
|
709
|
-
const resolver = new dns.Resolver();
|
|
710
|
-
resolver.setServers(["8.8.8.8"]);
|
|
711
|
-
const resolve4 = promisify(resolver.resolve4.bind(resolver));
|
|
712
|
-
const addresses = await resolve4(hostname);
|
|
713
|
-
cachedTargetIPs[hostname] = addresses[0];
|
|
714
|
-
return cachedTargetIPs[hostname];
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
function collectBodyRaw(req) {
|
|
718
|
-
return new Promise((resolve, reject) => {
|
|
719
|
-
const chunks = [];
|
|
720
|
-
req.on("data", chunk => chunks.push(chunk));
|
|
721
|
-
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
722
|
-
req.on("error", reject);
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
// Extract model from URL path (Gemini) or body (OpenAI/Anthropic)
|
|
727
|
-
function extractModel(url, body) {
|
|
728
|
-
const urlMatch = url.match(/\/models\/([^/:]+)/);
|
|
729
|
-
if (urlMatch) return urlMatch[1];
|
|
730
|
-
try { return JSON.parse(body.toString()).model || null; } catch { return null; }
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function getMappedModel(tool, model) {
|
|
734
|
-
if (!model) return null;
|
|
735
|
-
try {
|
|
736
|
-
if (!fs.existsSync(DB_FILE)) return null;
|
|
737
|
-
const db = JSON.parse(fs.readFileSync(DB_FILE, "utf-8"));
|
|
738
|
-
return db.mitmAlias?.[tool]?.[model] || null;
|
|
739
|
-
} catch {
|
|
740
|
-
return null;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Determine which tool this request belongs to based on hostname
|
|
746
|
-
*/
|
|
747
|
-
function getToolForHost(host) {
|
|
748
|
-
const h = (host || "").split(":")[0];
|
|
749
|
-
if (h === "api.individual.githubcopilot.com") return "copilot";
|
|
750
|
-
if (h === "daily-cloudcode-pa.googleapis.com" || h === "cloudcode-pa.googleapis.com") return "antigravity";
|
|
751
|
-
return null;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Encode a single protobuf length-delimited field: (fieldNum << 3 | wireType) + varint(len) + bytes
|
|
756
|
-
* wireType 2 = length-delimited, wireType 0 = varint
|
|
757
|
-
*/
|
|
758
|
-
function protoEncodeField(fieldNum, wireType, data) {
|
|
759
|
-
const tag = (fieldNum << 3) | wireType;
|
|
760
|
-
const tagBuf = encodeVarint(tag);
|
|
761
|
-
if (wireType === 2) {
|
|
762
|
-
const lenBuf = encodeVarint(data.length);
|
|
763
|
-
return Buffer.concat([tagBuf, lenBuf, data]);
|
|
764
|
-
}
|
|
765
|
-
// wireType 0: data is already a varint buffer
|
|
766
|
-
return Buffer.concat([tagBuf, data]);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
function encodeVarint(n) {
|
|
770
|
-
const bufs = [];
|
|
771
|
-
while (n > 0x7f) {
|
|
772
|
-
bufs.push((n & 0x7f) | 0x80);
|
|
773
|
-
n >>>= 7;
|
|
774
|
-
}
|
|
775
|
-
bufs.push(n & 0x7f);
|
|
776
|
-
return Buffer.from(bufs);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* Build a Connect-RPC streaming frame: [flags=0][4-byte big-endian length][body]
|
|
781
|
-
*/
|
|
782
|
-
function buildConnectFrame(body) {
|
|
783
|
-
const lenBuf = Buffer.alloc(4);
|
|
784
|
-
lenBuf.writeUInt32BE(body.length, 0);
|
|
785
|
-
return Buffer.concat([Buffer.from([0x00]), lenBuf, body]);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Build one gzip-compressed Connect-RPC data frame containing a BidiPollResponse.
|
|
790
|
-
* BidiPollResponse: f1=seqno(varint), f2=data(hex string of AgentServerMessage)
|
|
791
|
-
* AgentServerMessage.f1 = InteractionUpdate.f1 = TextDeltaUpdate.f1 = text
|
|
792
|
-
*/
|
|
793
|
-
function buildFakePollFrame(text, seqno) {
|
|
794
|
-
const zlib = require("zlib");
|
|
795
|
-
|
|
796
|
-
// TextDeltaUpdate { f1: text }
|
|
797
|
-
const textDelta = protoEncodeField(1, 2, Buffer.from(text, "utf8"));
|
|
798
|
-
// InteractionUpdate { f1: TextDeltaUpdate }
|
|
799
|
-
const interactionUpdate = protoEncodeField(1, 2, textDelta);
|
|
800
|
-
// AgentServerMessage { f1: InteractionUpdate }
|
|
801
|
-
const agentMsg = protoEncodeField(1, 2, interactionUpdate);
|
|
802
|
-
|
|
803
|
-
// BidiPollResponse { f1: seqno, f2: hex(agentMsg) }
|
|
804
|
-
let pollResp = protoEncodeField(1, 0, encodeVarint(seqno));
|
|
805
|
-
pollResp = Buffer.concat([pollResp, protoEncodeField(2, 2, Buffer.from(agentMsg.toString("hex"), "utf8"))]);
|
|
806
|
-
|
|
807
|
-
// Gzip compress (flags=1)
|
|
808
|
-
const compressed = zlib.gzipSync(pollResp);
|
|
809
|
-
const lenBuf = Buffer.alloc(4);
|
|
810
|
-
lenBuf.writeUInt32BE(compressed.length, 0);
|
|
811
|
-
return Buffer.concat([Buffer.from([0x01]), lenBuf, compressed]);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
* Build Connect-RPC end-of-stream trailer frame: flags=0x02, body={}
|
|
816
|
-
*/
|
|
817
|
-
function buildTrailerFrame() {
|
|
818
|
-
const body = Buffer.from("{}", "utf8");
|
|
819
|
-
const lenBuf = Buffer.alloc(4);
|
|
820
|
-
lenBuf.writeUInt32BE(body.length, 0);
|
|
821
|
-
return Buffer.concat([Buffer.from([0x02]), lenBuf, body]);
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
* Send fake RunPoll response matching real Cursor server format
|
|
826
|
-
*/
|
|
827
|
-
function sendMockResponse(res, replyText) {
|
|
828
|
-
res.writeHead(200, {
|
|
829
|
-
"content-type": "application/connect+proto",
|
|
830
|
-
"transfer-encoding": "chunked",
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
// Send text as single frame + trailer
|
|
834
|
-
res.write(buildFakePollFrame(replyText, 1));
|
|
835
|
-
res.write(buildTrailerFrame());
|
|
836
|
-
res.end();
|
|
837
|
-
console.log(`[MOCK] RunPoll → "${replyText}"`);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
async function passthrough(req, res, bodyBuffer, captureFile = null) {
|
|
841
|
-
const targetHost = (req.headers.host || TARGET_HOSTS[0]).split(":")[0];
|
|
842
|
-
const targetIP = await resolveTargetIP(targetHost);
|
|
843
|
-
|
|
844
|
-
const forwardReq = https.request({
|
|
845
|
-
hostname: targetIP,
|
|
846
|
-
port: 443,
|
|
847
|
-
path: req.url,
|
|
848
|
-
method: req.method,
|
|
849
|
-
headers: { ...req.headers, host: targetHost },
|
|
850
|
-
servername: targetHost,
|
|
851
|
-
rejectUnauthorized: false
|
|
852
|
-
}, (forwardRes) => {
|
|
853
|
-
// Log headers + first frame raw bytes of first RunPoll response for mock calibration
|
|
854
|
-
if (req.url.includes("RunPoll") && !passthrough._loggedRunPollHeaders) {
|
|
855
|
-
passthrough._loggedRunPollHeaders = true;
|
|
856
|
-
console.log("[DBG] RunPoll headers:", JSON.stringify(forwardRes.headers));
|
|
857
|
-
const origWrite = res.write.bind(res);
|
|
858
|
-
let logged = false;
|
|
859
|
-
res.write = function(chunk) {
|
|
860
|
-
if (!logged) {
|
|
861
|
-
logged = true;
|
|
862
|
-
console.log("[DBG] RunPoll first chunk hex:", Buffer.isBuffer(chunk) ? chunk.slice(0, 80).toString("hex") : Buffer.from(chunk).slice(0, 80).toString("hex"));
|
|
863
|
-
}
|
|
864
|
-
return origWrite(chunk);
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
res.writeHead(forwardRes.statusCode, forwardRes.headers);
|
|
868
|
-
|
|
869
|
-
if (!captureFile) {
|
|
870
|
-
forwardRes.pipe(res);
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Tee response: forward to client AND decode+save to file
|
|
875
|
-
const chunks = [];
|
|
876
|
-
forwardRes.on("data", chunk => {
|
|
877
|
-
chunks.push(chunk);
|
|
878
|
-
res.write(chunk);
|
|
879
|
-
});
|
|
880
|
-
forwardRes.on("end", () => {
|
|
881
|
-
res.end();
|
|
882
|
-
try {
|
|
883
|
-
const raw = Buffer.concat(chunks);
|
|
884
|
-
// Save first RunPoll raw that has text (frames > 20)
|
|
885
|
-
if (req.url.includes("RunPoll") && !passthrough._savedTextRaw && frameCount > 20) {
|
|
886
|
-
passthrough._savedTextRaw = true;
|
|
887
|
-
fs.writeFileSync(path.join(CURSOR_LOG_DIR, "_runpoll_text_raw.hex"), raw.toString("hex"));
|
|
888
|
-
console.log("[DBG] RunPoll TEXT raw saved, len=", raw.length, "frames=", frameCount);
|
|
889
|
-
}
|
|
890
|
-
// Decode Connect-RPC streaming frames: each frame = [1B flags][4B len][proto body]
|
|
891
|
-
const textChunks = [];
|
|
892
|
-
const toolCalls = [];
|
|
893
|
-
let frameCount = 0;
|
|
894
|
-
let offset = 0;
|
|
895
|
-
|
|
896
|
-
while (offset + 5 <= raw.length) {
|
|
897
|
-
const flags = raw[offset];
|
|
898
|
-
const msgLen = raw.readUInt32BE(offset + 1);
|
|
899
|
-
if (offset + 5 + msgLen > raw.length) break;
|
|
900
|
-
let body = raw.slice(offset + 5, offset + 5 + msgLen);
|
|
901
|
-
offset += 5 + msgLen;
|
|
902
|
-
frameCount++;
|
|
903
|
-
|
|
904
|
-
// Decompress gzip frame (flags=1)
|
|
905
|
-
if (flags === 1) {
|
|
906
|
-
try { body = zlib.gunzipSync(body); } catch { continue; }
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// BidiPollResponse: f1=seqno(varint), f2=data(hex-encoded AgentServerMessage)
|
|
910
|
-
const pollFields = protoWalkFields(body);
|
|
911
|
-
const dataField = pollFields.find(f => f.field === 2 && f.data);
|
|
912
|
-
if (!dataField) continue;
|
|
913
|
-
|
|
914
|
-
// f2.data is ASCII hex string → decode to actual AgentServerMessage bytes
|
|
915
|
-
const hexStr = dataField.data.toString("utf8");
|
|
916
|
-
if (!/^[0-9a-f]+$/i.test(hexStr)) continue;
|
|
917
|
-
const agentMsg = Buffer.from(hexStr, "hex");
|
|
918
|
-
|
|
919
|
-
// AgentServerMessage: f1=InteractionUpdate, f2=ExecServerMessage
|
|
920
|
-
const agentFields = protoWalkFields(agentMsg);
|
|
921
|
-
|
|
922
|
-
// f1 = InteractionUpdate → f1=TextDeltaUpdate.f1=text
|
|
923
|
-
const interactionUpdate = agentFields.find(f => f.field === 1 && f.data);
|
|
924
|
-
if (interactionUpdate) {
|
|
925
|
-
const iuFields = protoWalkFields(interactionUpdate.data);
|
|
926
|
-
const textDelta = iuFields.find(f => f.field === 1 && f.data);
|
|
927
|
-
if (textDelta) {
|
|
928
|
-
const tdFields = protoWalkFields(textDelta.data);
|
|
929
|
-
const textField = tdFields.find(f => f.field === 1 && f.data);
|
|
930
|
-
if (textField) {
|
|
931
|
-
const text = textField.data.toString("utf8");
|
|
932
|
-
if (text.length > 0) textChunks.push(text);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// f2 = ExecServerMessage → decode tool call
|
|
938
|
-
// ExecServerMessage: f1=id(varint), f15=exec_id(str), f2=shell, f3=write, f4=delete, f5=grep, f7=read, f8=ls
|
|
939
|
-
const execMsg = agentFields.find(f => f.field === 2 && f.data);
|
|
940
|
-
if (execMsg) {
|
|
941
|
-
const execFields = protoWalkFields(execMsg.data);
|
|
942
|
-
const toolFieldMap = { 2: "shell", 3: "write", 4: "delete", 5: "grep", 7: "read", 8: "ls", 11: "mcp", 14: "shell_stream", 20: "fetch" };
|
|
943
|
-
const toolField = execFields.find(f => toolFieldMap[f.field] && f.data);
|
|
944
|
-
if (toolField) {
|
|
945
|
-
const toolName = toolFieldMap[toolField.field];
|
|
946
|
-
// Extract first string field as main arg (command/path)
|
|
947
|
-
const argFields = protoWalkFields(toolField.data);
|
|
948
|
-
const mainArg = argFields.find(f => f.field === 1 && f.data);
|
|
949
|
-
const arg = mainArg ? mainArg.data.toString("utf8") : null;
|
|
950
|
-
toolCalls.push({ tool: toolName, arg });
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
const assembled = textChunks.length > 0 ? textChunks.join("") : null;
|
|
956
|
-
// Only save file if there's actual content
|
|
957
|
-
if (assembled || toolCalls.length > 0) {
|
|
958
|
-
const entry = { ts: new Date().toISOString(), frames: frameCount };
|
|
959
|
-
if (assembled) entry.text = assembled;
|
|
960
|
-
if (toolCalls.length > 0) entry.tools = toolCalls;
|
|
961
|
-
fs.writeFileSync(captureFile, JSON.stringify(entry, null, 2));
|
|
962
|
-
const toolSummary = toolCalls.map(t => `${t.tool}(${(t.arg || "").slice(0, 40)})`).join(", ");
|
|
963
|
-
const preview = assembled ? `"${assembled.slice(0, 80)}"` : "";
|
|
964
|
-
console.log(`[CURSOR] RunPoll ← ${preview}${toolSummary ? ` [${toolSummary}]` : ""}`);
|
|
965
|
-
}
|
|
966
|
-
} catch { /* ignore */ }
|
|
967
|
-
});
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
forwardReq.on("error", (err) => {
|
|
971
|
-
console.error(`❌ Passthrough error: ${err.message}`);
|
|
972
|
-
if (!res.headersSent) res.writeHead(502);
|
|
973
|
-
res.end("Bad Gateway");
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
if (bodyBuffer.length > 0) forwardReq.write(bodyBuffer);
|
|
977
|
-
forwardReq.end();
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
async function intercept(req, res, bodyBuffer, mappedModel) {
|
|
981
|
-
try {
|
|
982
|
-
const body = JSON.parse(bodyBuffer.toString());
|
|
983
|
-
body.model = mappedModel;
|
|
984
|
-
|
|
985
|
-
const response = await fetch(ROUTER_URL, {
|
|
986
|
-
method: "POST",
|
|
987
|
-
headers: {
|
|
988
|
-
"Content-Type": "application/json",
|
|
989
|
-
"Authorization": `Bearer ${API_KEY}`
|
|
990
|
-
},
|
|
991
|
-
body: JSON.stringify(body)
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
if (!response.ok) {
|
|
995
|
-
const errText = await response.text().catch(() => "");
|
|
996
|
-
throw new Error(`9Router ${response.status}: ${errText}`);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
const ct = response.headers.get("content-type") || "application/json";
|
|
1000
|
-
const resHeaders = { "Content-Type": ct, "Cache-Control": "no-cache", "Connection": "keep-alive" };
|
|
1001
|
-
if (ct.includes("text/event-stream")) resHeaders["X-Accel-Buffering"] = "no";
|
|
1002
|
-
res.writeHead(200, resHeaders);
|
|
1003
|
-
|
|
1004
|
-
const reader = response.body.getReader();
|
|
1005
|
-
const decoder = new TextDecoder();
|
|
1006
|
-
while (true) {
|
|
1007
|
-
const { done, value } = await reader.read();
|
|
1008
|
-
if (done) { res.end(); break; }
|
|
1009
|
-
res.write(decoder.decode(value, { stream: true }));
|
|
1010
|
-
}
|
|
1011
|
-
} catch (error) {
|
|
1012
|
-
console.error(`❌ ${error.message}`);
|
|
1013
|
-
if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
|
|
1014
|
-
res.end(JSON.stringify({ error: { message: error.message, type: "mitm_error" } }));
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
const server = https.createServer(sslOptions, async (req, res) => {
|
|
1019
|
-
if (req.url === "/_mitm_health") {
|
|
1020
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1021
|
-
res.end(JSON.stringify({ ok: true, pid: process.pid }));
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
console.log(`[MITM] ${req.method} https://${req.headers.host}${req.url}`);
|
|
1026
|
-
|
|
1027
|
-
const bodyBuffer = await collectBodyRaw(req);
|
|
1028
|
-
if (bodyBuffer.length > 0) saveRequestLog(req.url, bodyBuffer);
|
|
1029
|
-
|
|
1030
|
-
// Decode and log Cursor protobuf request endpoints
|
|
1031
|
-
if (CURSOR_ENDPOINTS.some(e => req.url.includes(e))) {
|
|
1032
|
-
saveCursorLogFull(req.url, req.headers, bodyBuffer);
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// Capture response of RunPoll (server streams BidiPollResponse chunks back)
|
|
1036
|
-
const isRunPoll = req.url.includes("RunPoll");
|
|
1037
|
-
if (isRunPoll) {
|
|
1038
|
-
// Check if there's a mock rule pending for this conversation
|
|
1039
|
-
// Extract conversationId from BidiPollRequest.f1 (BidiRequestId) → need to check mockPending
|
|
1040
|
-
// Simple approach: check all pending mocks and reply for any RunPoll within same session
|
|
1041
|
-
// Since mockPending is keyed by conversationId and RunPoll doesn't carry it directly,
|
|
1042
|
-
// we intercept the first RunPoll after a matching AgentRunRequest by checking pendingQueue
|
|
1043
|
-
if (mockPending.size > 0) {
|
|
1044
|
-
// Pick the first pending mock (FIFO) — matches the most recent matching conversation
|
|
1045
|
-
const [convId, replyText] = mockPending.entries().next().value;
|
|
1046
|
-
mockPending.delete(convId);
|
|
1047
|
-
return sendMockResponse(res, replyText);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1051
|
-
const captureFile = path.join(CURSOR_LOG_DIR, `${ts}_RunPoll_response.json`);
|
|
1052
|
-
return passthrough(req, res, bodyBuffer, captureFile);
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
// Anti-loop: requests originating from 9Router bypass interception
|
|
1056
|
-
if (req.headers[INTERNAL_REQUEST_HEADER.name] === INTERNAL_REQUEST_HEADER.value) {
|
|
1057
|
-
return passthrough(req, res, bodyBuffer);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
const tool = getToolForHost(req.headers.host);
|
|
1061
|
-
if (!tool) return passthrough(req, res, bodyBuffer);
|
|
1062
|
-
|
|
1063
|
-
// Check if this URL should be intercepted based on tool
|
|
1064
|
-
const isChat = tool === "antigravity"
|
|
1065
|
-
? ANTIGRAVITY_URL_PATTERNS.some(p => req.url.includes(p))
|
|
1066
|
-
: COPILOT_URL_PATTERNS.some(p => req.url.includes(p));
|
|
1067
|
-
|
|
1068
|
-
if (!isChat) return passthrough(req, res, bodyBuffer);
|
|
1069
|
-
|
|
1070
|
-
const model = extractModel(req.url, bodyBuffer);
|
|
1071
|
-
console.log("Extracted model:", model)
|
|
1072
|
-
const mappedModel = getMappedModel(tool, model);
|
|
1073
|
-
|
|
1074
|
-
if (!mappedModel) return passthrough(req, res, bodyBuffer);
|
|
1075
|
-
|
|
1076
|
-
return intercept(req, res, bodyBuffer, mappedModel);
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
server.listen(LOCAL_PORT, () => {
|
|
1080
|
-
console.log(`🚀 MITM ready on :${LOCAL_PORT}`);
|
|
1081
|
-
});
|
|
1082
|
-
|
|
1083
|
-
server.on("error", (error) => {
|
|
1084
|
-
if (error.code === "EADDRINUSE") {
|
|
1085
|
-
console.error(`❌ Port ${LOCAL_PORT} already in use`);
|
|
1086
|
-
} else if (error.code === "EACCES") {
|
|
1087
|
-
console.error(`❌ Permission denied for port ${LOCAL_PORT}`);
|
|
1088
|
-
} else {
|
|
1089
|
-
console.error(`❌ ${error.message}`);
|
|
1090
|
-
}
|
|
1091
|
-
process.exit(1);
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
const shutdown = () => { server.close(() => process.exit(0)); };
|
|
1095
|
-
process.on("SIGTERM", shutdown);
|
|
1096
|
-
process.on("SIGINT", shutdown);
|
|
1097
|
-
if (process.platform === "win32") {
|
|
1098
|
-
process.on("SIGBREAK", shutdown);
|
|
1099
|
-
}
|