9router 0.3.47 → 0.3.49
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 +42 -42
- package/app/.next/build-manifest.json +2 -2
- 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_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 +3 -3
- package/app/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/app/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- 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 +1 -1
- 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_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.js +2 -2
- package/app/.next/server/app/api/oauth/cursor/auto-import/route.js.nft.json +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_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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/app/.next/server/app/api/tunnel/disable/route.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.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.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.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.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.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.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.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.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.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.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.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 +3 -3
- package/app/.next/server/app/callback.segments/_full.segment.rsc +3 -3
- package/app/.next/server/app/callback.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/callback.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/callback.segments/_tree.segment.rsc +1 -1
- 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 +5 -5
- 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 +5 -5
- 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 +3 -3
- package/app/.next/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/combos.html +1 -1
- package/app/.next/server/app/dashboard/combos.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/combos.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/combos.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/combos.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/endpoint.html +1 -1
- package/app/.next/server/app/dashboard/endpoint.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/endpoint.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/endpoint.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/endpoint.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/mitm.html +1 -1
- package/app/.next/server/app/dashboard/mitm.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/mitm.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/mitm.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/mitm.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/profile.html +1 -1
- package/app/.next/server/app/dashboard/profile.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/profile.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/profile.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/profile.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers/new.html +1 -1
- package/app/.next/server/app/dashboard/providers/new.rsc +5 -5
- 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 +5 -5
- 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 +3 -3
- package/app/.next/server/app/dashboard/providers/new.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers.html +1 -1
- package/app/.next/server/app/dashboard/providers.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/providers.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/providers.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/providers.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.html +1 -1
- package/app/.next/server/app/dashboard/proxy-pools.rsc +5 -5
- 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 +5 -5
- 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 +3 -3
- package/app/.next/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/quota.html +2 -2
- package/app/.next/server/app/dashboard/quota.rsc +4 -4
- package/app/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +1 -1
- 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 +4 -4
- package/app/.next/server/app/dashboard/quota.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/quota.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/quota.segments/_tree.segment.rsc +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 +3 -3
- package/app/.next/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/app/.next/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +1 -1
- 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 +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/translator.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/translator.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/translator.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/usage.html +1 -1
- package/app/.next/server/app/dashboard/usage.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard/usage.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard/usage.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard/usage.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/dashboard.html +1 -1
- package/app/.next/server/app/dashboard.rsc +5 -5
- 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 +5 -5
- package/app/.next/server/app/dashboard.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/dashboard.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/dashboard.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/index.html +1 -1
- package/app/.next/server/app/index.rsc +3 -3
- package/app/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/app/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/app/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +3 -3
- package/app/.next/server/app/landing.segments/_full.segment.rsc +3 -3
- package/app/.next/server/app/landing.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/landing.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/landing.segments/_tree.segment.rsc +1 -1
- package/app/.next/server/app/landing.segments/landing/__PAGE__.segment.rsc +1 -1
- 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 +4 -4
- package/app/.next/server/app/login.segments/_full.segment.rsc +4 -4
- package/app/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/app/.next/server/app/login.segments/_index.segment.rsc +3 -3
- package/app/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- 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_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 +42 -42
- package/app/.next/server/chunks/1114.js +1 -1
- package/app/.next/server/chunks/1424.js +1 -1
- package/app/.next/server/chunks/289.js +1 -1
- package/app/.next/server/chunks/318.js +6 -6
- package/app/.next/server/chunks/4989.js +2 -2
- package/app/.next/server/chunks/5297.js +1 -1
- package/app/.next/server/chunks/6182.js +4 -4
- package/app/.next/server/chunks/8590.js +1 -1
- package/app/.next/server/chunks/8895.js +3 -3
- package/app/.next/server/chunks/9201.js +1 -1
- package/app/.next/server/chunks/9609.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/{5497-80f3e016aa6ffe90.js → 5497-612d09a8bc2fb0ac.js} +1 -1
- package/app/.next/static/chunks/{619-4c970716a5102dc3.js → 619-c4c101a6a78feeb4.js} +1 -1
- package/app/.next/static/chunks/app/{layout-3d619e2f57919f2c.js → layout-facb48b90c716570.js} +1 -1
- package/app/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
- package/app/node_modules/better-sqlite3/lib/database.js +90 -0
- package/app/node_modules/better-sqlite3/lib/index.js +3 -0
- package/app/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
- package/app/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
- package/app/node_modules/better-sqlite3/lib/methods/function.js +31 -0
- package/app/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
- package/app/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
- package/app/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
- package/app/node_modules/better-sqlite3/lib/methods/table.js +189 -0
- package/app/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
- package/app/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
- package/app/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
- package/app/node_modules/better-sqlite3/lib/util.js +12 -0
- package/app/node_modules/better-sqlite3/package.json +59 -0
- package/app/node_modules/bindings/bindings.js +221 -0
- package/app/node_modules/bindings/package.json +28 -0
- package/app/node_modules/file-uri-to-path/index.js +66 -0
- package/app/node_modules/file-uri-to-path/package.json +32 -0
- package/app/package.json +2 -1
- package/app/src/mitm/manager.js +59 -0
- package/app/src/mitm/server.js +1 -1
- package/app/src/mitm/server2.js +890 -0
- package/cli.js +97 -56
- package/package.json +1 -1
- /package/app/.next/static/{cc9J-XDw_OEWOXEASQdi9 → I7Z75b-mEq-yr9pvEanD-}/_buildManifest.js +0 -0
- /package/app/.next/static/{cc9J-XDw_OEWOXEASQdi9 → I7Z75b-mEq-yr9pvEanD-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,890 @@
|
|
|
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
|
+
/**
|
|
79
|
+
* Decode Connect-RPC frame: [1 byte flags][4 bytes length][protobuf body]
|
|
80
|
+
* Then walk raw protobuf fields and extract readable strings/numbers.
|
|
81
|
+
*/
|
|
82
|
+
function decodeConnectFrame(buf) {
|
|
83
|
+
if (!buf || buf.length < 5) return null;
|
|
84
|
+
const msgLen = buf.readUInt32BE(1);
|
|
85
|
+
if (buf.length < 5 + msgLen) return null;
|
|
86
|
+
return decodeProtoRaw(buf.slice(5, 5 + msgLen));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function decodeProtoRaw(buf, depth = 0) {
|
|
90
|
+
if (depth > 6) return {};
|
|
91
|
+
const result = {};
|
|
92
|
+
let offset = 0;
|
|
93
|
+
try {
|
|
94
|
+
while (offset < buf.length) {
|
|
95
|
+
const tag = readVarint(buf, offset);
|
|
96
|
+
if (!tag) break;
|
|
97
|
+
offset += tag.bytes;
|
|
98
|
+
const fieldNum = tag.value >> 3;
|
|
99
|
+
const wireType = tag.value & 0x7;
|
|
100
|
+
|
|
101
|
+
if (wireType === 0) {
|
|
102
|
+
// varint
|
|
103
|
+
const v = readVarint(buf, offset);
|
|
104
|
+
if (!v) break;
|
|
105
|
+
offset += v.bytes;
|
|
106
|
+
result[`f${fieldNum}`] = v.value;
|
|
107
|
+
} else if (wireType === 2) {
|
|
108
|
+
// length-delimited: string, bytes, or nested message
|
|
109
|
+
const lenV = readVarint(buf, offset);
|
|
110
|
+
if (!lenV) break;
|
|
111
|
+
offset += lenV.bytes;
|
|
112
|
+
const data = buf.slice(offset, offset + lenV.value);
|
|
113
|
+
offset += lenV.value;
|
|
114
|
+
// Try as UTF-8 string first
|
|
115
|
+
const str = data.toString("utf8");
|
|
116
|
+
const isPrintable = /^[\x20-\x7E\n\r\t]*$/.test(str) && str.length > 0;
|
|
117
|
+
if (isPrintable) {
|
|
118
|
+
result[`f${fieldNum}`] = str;
|
|
119
|
+
} else if (data.length > 0 && data.length < 4096) {
|
|
120
|
+
// Try nested decode
|
|
121
|
+
const nested = decodeProtoRaw(data, depth + 1);
|
|
122
|
+
result[`f${fieldNum}`] = Object.keys(nested).length ? nested : data.toString("hex").substring(0, 64);
|
|
123
|
+
}
|
|
124
|
+
} else if (wireType === 5) {
|
|
125
|
+
offset += 4;
|
|
126
|
+
} else if (wireType === 1) {
|
|
127
|
+
offset += 8;
|
|
128
|
+
} else {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch { /* best effort */ }
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function readVarint(buf, offset) {
|
|
137
|
+
let value = 0, shift = 0, bytes = 0;
|
|
138
|
+
while (offset + bytes < buf.length) {
|
|
139
|
+
const b = buf[offset + bytes++];
|
|
140
|
+
value |= (b & 0x7f) << shift;
|
|
141
|
+
shift += 7;
|
|
142
|
+
if (!(b & 0x80)) return { value, bytes };
|
|
143
|
+
if (shift >= 28) break;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const zlib = require("zlib");
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Deep decode protobuf buffer — returns structured object.
|
|
152
|
+
* Tries to interpret each length-delimited field as:
|
|
153
|
+
* 1. UTF-8 string (if fully printable)
|
|
154
|
+
* 2. Nested proto message (recursive)
|
|
155
|
+
* 3. Hex string fallback
|
|
156
|
+
*/
|
|
157
|
+
function deepDecodeProto(buf, depth = 0) {
|
|
158
|
+
if (depth > 10 || buf.length === 0) return null;
|
|
159
|
+
const result = {};
|
|
160
|
+
let off = 0;
|
|
161
|
+
try {
|
|
162
|
+
while (off < buf.length) {
|
|
163
|
+
// Read varint tag
|
|
164
|
+
let tag = 0, shift = 0;
|
|
165
|
+
while (off < buf.length) {
|
|
166
|
+
const b = buf[off++];
|
|
167
|
+
tag |= (b & 0x7f) << shift;
|
|
168
|
+
shift += 7;
|
|
169
|
+
if (!(b & 0x80)) break;
|
|
170
|
+
if (shift > 28) break;
|
|
171
|
+
}
|
|
172
|
+
if (tag === 0) break;
|
|
173
|
+
const field = tag >> 3;
|
|
174
|
+
const wire = tag & 7;
|
|
175
|
+
const key = `f${field}`;
|
|
176
|
+
|
|
177
|
+
if (wire === 0) {
|
|
178
|
+
// varint
|
|
179
|
+
let v = 0, s = 0;
|
|
180
|
+
while (off < buf.length) {
|
|
181
|
+
const b = buf[off++];
|
|
182
|
+
v |= (b & 0x7f) << s;
|
|
183
|
+
s += 7;
|
|
184
|
+
if (!(b & 0x80)) break;
|
|
185
|
+
}
|
|
186
|
+
result[key] = v;
|
|
187
|
+
} else if (wire === 2) {
|
|
188
|
+
// length-delimited
|
|
189
|
+
let len = 0, s = 0;
|
|
190
|
+
while (off < buf.length) {
|
|
191
|
+
const b = buf[off++];
|
|
192
|
+
len |= (b & 0x7f) << s;
|
|
193
|
+
s += 7;
|
|
194
|
+
if (!(b & 0x80)) break;
|
|
195
|
+
}
|
|
196
|
+
if (off + len > buf.length || len < 0) break;
|
|
197
|
+
const data = buf.slice(off, off + len);
|
|
198
|
+
off += len;
|
|
199
|
+
|
|
200
|
+
if (len === 0) {
|
|
201
|
+
result[key] = "";
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Try UTF-8 string
|
|
206
|
+
const str = data.toString("utf8");
|
|
207
|
+
const isPrintable = str.length > 0 && !/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(str);
|
|
208
|
+
if (isPrintable) {
|
|
209
|
+
// Accumulate repeated fields as array
|
|
210
|
+
if (key in result) {
|
|
211
|
+
result[key] = [].concat(result[key], str);
|
|
212
|
+
} else {
|
|
213
|
+
result[key] = str;
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Try nested proto
|
|
219
|
+
if (len >= 2 && len <= 500000) {
|
|
220
|
+
const nested = deepDecodeProto(data, depth + 1);
|
|
221
|
+
if (nested && Object.keys(nested).length > 0) {
|
|
222
|
+
if (key in result) {
|
|
223
|
+
result[key] = [].concat(result[key], [nested]);
|
|
224
|
+
} else {
|
|
225
|
+
result[key] = nested;
|
|
226
|
+
}
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Fallback: hex (truncate at 64 bytes)
|
|
232
|
+
result[key] = data.slice(0, 64).toString("hex");
|
|
233
|
+
} else if (wire === 5) {
|
|
234
|
+
off += 4;
|
|
235
|
+
} else if (wire === 1) {
|
|
236
|
+
off += 8;
|
|
237
|
+
} else {
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} catch { /* best effort */ }
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ── Proto helpers ──────────────────────────────────────────────
|
|
246
|
+
function protoReadVarint(buf, off) {
|
|
247
|
+
let v = 0, shift = 0;
|
|
248
|
+
while (off < buf.length) {
|
|
249
|
+
const b = buf[off++];
|
|
250
|
+
v |= (b & 0x7f) << shift;
|
|
251
|
+
shift += 7;
|
|
252
|
+
if (!(b & 0x80)) return { v, off };
|
|
253
|
+
if (shift > 49) break;
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function protoWalkFields(buf) {
|
|
259
|
+
const fields = [];
|
|
260
|
+
let off = 0;
|
|
261
|
+
while (off < buf.length) {
|
|
262
|
+
const t = protoReadVarint(buf, off);
|
|
263
|
+
if (!t || t.v === 0) break;
|
|
264
|
+
off = t.off;
|
|
265
|
+
const field = t.v >> 3, wire = t.v & 7;
|
|
266
|
+
if (wire === 0) {
|
|
267
|
+
const r = protoReadVarint(buf, off);
|
|
268
|
+
if (!r) break;
|
|
269
|
+
off = r.off;
|
|
270
|
+
fields.push({ field, wire, value: r.v });
|
|
271
|
+
} else if (wire === 2) {
|
|
272
|
+
const r = protoReadVarint(buf, off);
|
|
273
|
+
if (!r) break;
|
|
274
|
+
off = r.off;
|
|
275
|
+
if (off + r.v > buf.length) break;
|
|
276
|
+
fields.push({ field, wire, data: buf.slice(off, off + r.v) });
|
|
277
|
+
off += r.v;
|
|
278
|
+
} else if (wire === 5) { off += 4; }
|
|
279
|
+
else if (wire === 1) { off += 8; }
|
|
280
|
+
else break;
|
|
281
|
+
}
|
|
282
|
+
return fields;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function protoStr(buf) {
|
|
286
|
+
if (!buf || buf.length === 0) return null;
|
|
287
|
+
const s = buf.toString("utf8");
|
|
288
|
+
return /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(s) ? null : s;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ── Schema-aware decoders ──────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
// BidiAppendRequest: f1=data(hex-encoded AgentClientMessage), f2=requestId{f1=uuid}, f3=seqno
|
|
294
|
+
function decodeBidiAppend(buf) {
|
|
295
|
+
const fields = protoWalkFields(buf);
|
|
296
|
+
const result = {};
|
|
297
|
+
for (const f of fields) {
|
|
298
|
+
if (f.field === 1 && f.data) {
|
|
299
|
+
// f1.data is ASCII hex string → decode to actual proto bytes
|
|
300
|
+
try {
|
|
301
|
+
const hexStr = f.data.toString("utf8");
|
|
302
|
+
const protoBytes = Buffer.from(hexStr, "hex");
|
|
303
|
+
result.agentMsg = decodeAgentClientMsg(protoBytes);
|
|
304
|
+
} catch { result.agentMsg = { type: "hex_decode_error" }; }
|
|
305
|
+
} else if (f.field === 2 && f.data) {
|
|
306
|
+
const inner = protoWalkFields(f.data);
|
|
307
|
+
const id = inner.find(x => x.field === 1 && x.data);
|
|
308
|
+
result.requestId = id ? protoStr(id.data) : null;
|
|
309
|
+
} else if (f.field === 3) result.seqno = f.value;
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// AgentClientMessage oneof: f1=AgentRunRequest, f2=ExecClientMessage, f3=KvClientMessage, f4=ConversationAction, f5=ExecClientControlMessage, f6=InteractionResponse, f7=ClientHeartbeat
|
|
315
|
+
function decodeAgentClientMsg(buf) {
|
|
316
|
+
const fields = protoWalkFields(buf);
|
|
317
|
+
for (const f of fields) {
|
|
318
|
+
// f7=ClientHeartbeat: wire-type 2 but empty (len=0), data is Buffer(0)
|
|
319
|
+
if (f.field === 7) return { type: "ClientHeartbeat" };
|
|
320
|
+
if (f.data === undefined || f.data === null) continue;
|
|
321
|
+
if (f.field === 1 && f.data.length > 0) return { type: "AgentRunRequest", ...decodeAgentRunRequest(f.data) };
|
|
322
|
+
if (f.field === 2 && f.data.length > 0) return { type: "ExecClientMessage", ...decodeExecClientMessage(f.data) };
|
|
323
|
+
if (f.field === 3) return { type: "KvClientMessage" };
|
|
324
|
+
if (f.field === 4 && f.data.length > 0) return { type: "ConversationAction", ...decodeConversationAction(f.data) };
|
|
325
|
+
if (f.field === 5) return { type: "ExecClientControlMessage" };
|
|
326
|
+
if (f.field === 6) return { type: "InteractionResponse" };
|
|
327
|
+
}
|
|
328
|
+
return { type: "unknown" };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// AgentRunRequest: f1=conversation_state, f2=action, f3=model_details, f4=mcp_tools, f5=conversation_id, f6=mcp_file_system_options, f7=skill_options
|
|
332
|
+
function decodeAgentRunRequest(buf) {
|
|
333
|
+
const fields = protoWalkFields(buf);
|
|
334
|
+
const result = {};
|
|
335
|
+
|
|
336
|
+
for (const f of fields) {
|
|
337
|
+
if (f.field === 1 && f.data) result.state = decodeConversationState(f.data);
|
|
338
|
+
if (f.field === 2 && f.data) result.action = decodeConversationAction(f.data);
|
|
339
|
+
if (f.field === 3 && f.data) result.model = decodeModelDetails(f.data);
|
|
340
|
+
if (f.field === 4 && f.data) result.mcpTools = decodeMcpTools(f.data);
|
|
341
|
+
if (f.field === 5 && f.data) result.conversationId = protoStr(f.data);
|
|
342
|
+
}
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// McpTools: f1=repeated McpDescriptor (f1=serverName, f2=repeated tools)
|
|
347
|
+
function decodeMcpTools(buf) {
|
|
348
|
+
const fields = protoWalkFields(buf);
|
|
349
|
+
const descriptors = [];
|
|
350
|
+
for (const f of fields) {
|
|
351
|
+
if (f.field === 1 && f.data) {
|
|
352
|
+
const inner = protoWalkFields(f.data);
|
|
353
|
+
const name = inner.find(x => x.field === 1 && x.data);
|
|
354
|
+
const tools = inner.filter(x => x.field === 2 && x.data).map(t => {
|
|
355
|
+
const tf = protoWalkFields(t.data);
|
|
356
|
+
const tname = tf.find(x => x.field === 1 && x.data);
|
|
357
|
+
return tname ? protoStr(tname.data) : null;
|
|
358
|
+
}).filter(Boolean);
|
|
359
|
+
descriptors.push({ server: name ? protoStr(name.data) : null, tools });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return descriptors;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ExecClientMessage: f1=id(uint32), f2=shellResult, f3=writeResult, etc.
|
|
366
|
+
function decodeExecClientMessage(buf) {
|
|
367
|
+
const fields = protoWalkFields(buf);
|
|
368
|
+
const result = { id: null, execId: null, resultType: "unknown" };
|
|
369
|
+
for (const f of fields) {
|
|
370
|
+
if (f.field === 1 && f.value !== undefined) result.id = f.value;
|
|
371
|
+
if (f.field === 15 && f.data) result.execId = protoStr(f.data);
|
|
372
|
+
// Detect result type by field number (oneof)
|
|
373
|
+
const resultTypes = { 2: "shellResult", 3: "writeResult", 4: "deleteResult", 5: "grepResult", 7: "readResult", 8: "lsResult", 9: "diagnosticsResult", 10: "requestContextResult", 11: "mcpResult", 14: "shellStream", 16: "backgroundShellSpawnResult" };
|
|
374
|
+
if (f.data && resultTypes[f.field]) result.resultType = resultTypes[f.field];
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ModelDetails — scan all string fields
|
|
380
|
+
function decodeModelDetails(buf) {
|
|
381
|
+
const fields = protoWalkFields(buf);
|
|
382
|
+
const result = {};
|
|
383
|
+
for (const f of fields) {
|
|
384
|
+
if (f.data) {
|
|
385
|
+
const s = protoStr(f.data);
|
|
386
|
+
if (s && s.length > 0) result[`f${f.field}`] = s;
|
|
387
|
+
} else if (f.value !== undefined) {
|
|
388
|
+
result[`f${f.field}`] = f.value;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ConversationAction: f1=StartChatAction → f1=userMsg{f1=text,f2=uuid,f8=lexical}, f2=history{f2=contextFiles,f7=tools}
|
|
395
|
+
function decodeConversationAction(buf) {
|
|
396
|
+
const result = {};
|
|
397
|
+
const topFields = protoWalkFields(buf);
|
|
398
|
+
|
|
399
|
+
// action.f1 = StartChatAction wrapper
|
|
400
|
+
const f1 = topFields.find(f => f.field === 1 && f.data && f.data.length > 0);
|
|
401
|
+
if (!f1) {
|
|
402
|
+
// No StartChatAction — show which fields are present for debugging
|
|
403
|
+
const presentFields = topFields.map(f => `f${f.field}`);
|
|
404
|
+
if (presentFields.length > 0) result._fields = presentFields;
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
const f1Fields = protoWalkFields(f1.data);
|
|
408
|
+
|
|
409
|
+
// ── User message: action.f1.f1 ──────────────────────────────
|
|
410
|
+
const f1f1 = f1Fields.find(f => f.field === 1 && f.data);
|
|
411
|
+
if (f1f1) {
|
|
412
|
+
for (const f of protoWalkFields(f1f1.data)) {
|
|
413
|
+
if (!f.data) continue;
|
|
414
|
+
const s = protoStr(f.data);
|
|
415
|
+
if (f.field === 1 && s) result.text = s;
|
|
416
|
+
if (f.field === 2 && s) result.messageId = s;
|
|
417
|
+
if (f.field === 8 && s) {
|
|
418
|
+
try {
|
|
419
|
+
const lex = JSON.parse(s);
|
|
420
|
+
const texts = [];
|
|
421
|
+
function extractLexText(node) {
|
|
422
|
+
if (node?.text) texts.push(node.text);
|
|
423
|
+
(node?.children || []).forEach(extractLexText);
|
|
424
|
+
}
|
|
425
|
+
extractLexText(lex?.root);
|
|
426
|
+
result.textFull = texts.join("");
|
|
427
|
+
} catch { result.lexical = s.substring(0, 300); }
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ── Conversation history: action.f1.f2 ──────────────────────
|
|
433
|
+
const f1f2 = f1Fields.find(f => f.field === 2 && f.data);
|
|
434
|
+
if (f1f2) {
|
|
435
|
+
const histFields = protoWalkFields(f1f2.data);
|
|
436
|
+
|
|
437
|
+
// f2 repeated = context files (rules, open files...)
|
|
438
|
+
result.contextFiles = histFields
|
|
439
|
+
.filter(f => f.field === 2 && f.data)
|
|
440
|
+
.map(t => {
|
|
441
|
+
const tf = protoWalkFields(t.data);
|
|
442
|
+
const name = tf.find(f => f.field === 1 && f.data);
|
|
443
|
+
const content = tf.find(f => f.field === 2 && f.data);
|
|
444
|
+
return {
|
|
445
|
+
path: name ? protoStr(name.data) : null,
|
|
446
|
+
content: content ? protoStr(content.data)?.substring(0, 500) : null,
|
|
447
|
+
};
|
|
448
|
+
})
|
|
449
|
+
.filter(x => x.path);
|
|
450
|
+
|
|
451
|
+
// f7 repeated = MCP tools
|
|
452
|
+
result.tools = histFields
|
|
453
|
+
.filter(f => f.field === 7 && f.data)
|
|
454
|
+
.map(t => {
|
|
455
|
+
const tf = protoWalkFields(t.data);
|
|
456
|
+
const name = tf.find(f => f.field === 1 && f.data);
|
|
457
|
+
const desc = tf.find(f => f.field === 2 && f.data);
|
|
458
|
+
return {
|
|
459
|
+
name: name ? protoStr(name.data) : null,
|
|
460
|
+
description: desc ? protoStr(desc.data) : null,
|
|
461
|
+
};
|
|
462
|
+
})
|
|
463
|
+
.filter(x => x.name);
|
|
464
|
+
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Recursively decode any proto buffer into a plain JS object for debugging.
|
|
472
|
+
* Each field: try UTF-8 string → nested proto → hex fallback.
|
|
473
|
+
* Repeated fields become arrays.
|
|
474
|
+
*/
|
|
475
|
+
function protoTreeToObj(buf, depth = 0) {
|
|
476
|
+
if (depth > 12 || !buf || buf.length === 0) return null;
|
|
477
|
+
const result = {};
|
|
478
|
+
const fields = protoWalkFields(buf);
|
|
479
|
+
for (const f of fields) {
|
|
480
|
+
const key = `f${f.field}`;
|
|
481
|
+
let val;
|
|
482
|
+
if (f.data !== undefined) {
|
|
483
|
+
const s = protoStr(f.data);
|
|
484
|
+
if (s !== null) {
|
|
485
|
+
// Detect hex-encoded proto (BidiAppend.data field)
|
|
486
|
+
if (/^[0-9a-f]{20,}$/i.test(s)) {
|
|
487
|
+
try {
|
|
488
|
+
const dec = protoTreeToObj(Buffer.from(s, "hex"), depth + 1);
|
|
489
|
+
val = (dec && Object.keys(dec).length > 0) ? dec : s;
|
|
490
|
+
} catch { val = s; }
|
|
491
|
+
} else {
|
|
492
|
+
try { val = JSON.parse(s); } catch { val = s; }
|
|
493
|
+
}
|
|
494
|
+
} else if (f.data.length > 0) {
|
|
495
|
+
const nested = protoTreeToObj(f.data, depth + 1);
|
|
496
|
+
val = (nested && Object.keys(nested).length > 0)
|
|
497
|
+
? nested
|
|
498
|
+
: f.data.toString("hex");
|
|
499
|
+
} else {
|
|
500
|
+
val = "";
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
val = f.value;
|
|
504
|
+
}
|
|
505
|
+
// Accumulate repeated fields as array
|
|
506
|
+
if (key in result) {
|
|
507
|
+
result[key] = [].concat(result[key], [val]);
|
|
508
|
+
} else {
|
|
509
|
+
result[key] = val;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ConversationStateStructure: f1=root_prompt_messages_json(encrypted bytes), f3=todos, f8=turns, f10=mode
|
|
516
|
+
function decodeConversationState(buf) {
|
|
517
|
+
const fields = protoWalkFields(buf);
|
|
518
|
+
const turns = [], todos = [];
|
|
519
|
+
let mode = null, messageCount = 0;
|
|
520
|
+
for (const f of fields) {
|
|
521
|
+
if (f.field === 10 && f.value !== undefined) {
|
|
522
|
+
const modes = ["unspecified", "agent", "ask", "plan", "debug", "triage"];
|
|
523
|
+
mode = modes[f.value] || `mode_${f.value}`;
|
|
524
|
+
}
|
|
525
|
+
if (!f.data) continue;
|
|
526
|
+
if (f.field === 1) {
|
|
527
|
+
// Encrypted bytes — just count, cannot decode
|
|
528
|
+
messageCount++;
|
|
529
|
+
} else if (f.field === 8) {
|
|
530
|
+
// f8 = repeated bytes (ConversationTurn OR encrypted blob)
|
|
531
|
+
// Encrypted turns: binary blobs that don't parse as valid proto strings
|
|
532
|
+
if (f.data.length === 32) continue; // 32-byte encrypted blob
|
|
533
|
+
const inner = protoWalkFields(f.data);
|
|
534
|
+
const strs = inner.filter(x => x.data).map(x => protoStr(x.data)).filter(s => s && s.length > 3);
|
|
535
|
+
if (strs.length) turns.push(strs.slice(0, 3));
|
|
536
|
+
} else if (f.field === 3) {
|
|
537
|
+
// f3 = repeated bytes (TodoItem OR encrypted blob)
|
|
538
|
+
// Encrypted: 32-byte binary (shows as 64-char hex in protoStr-reject path)
|
|
539
|
+
// TodoItem: f1=id(str), f2=content(str), f3=status(varint)
|
|
540
|
+
if (f.data.length === 32) {
|
|
541
|
+
// 32 bytes = encrypted blob, skip
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const inner = protoWalkFields(f.data);
|
|
545
|
+
const id = inner.find(x => x.field === 1 && x.data);
|
|
546
|
+
const content = inner.find(x => x.field === 2 && x.data);
|
|
547
|
+
const status = inner.find(x => x.field === 3 && x.value !== undefined);
|
|
548
|
+
const idStr = id ? protoStr(id.data) : null;
|
|
549
|
+
const contentStr = content ? protoStr(content.data) : null;
|
|
550
|
+
// If neither id nor content is readable string, it's encrypted
|
|
551
|
+
if (!idStr && !contentStr) continue;
|
|
552
|
+
const statusMap = ["unspecified", "pending", "in_progress", "completed", "cancelled"];
|
|
553
|
+
todos.push({
|
|
554
|
+
id: idStr,
|
|
555
|
+
content: contentStr,
|
|
556
|
+
status: status ? (statusMap[status.value] || `status_${status.value}`) : null,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return { mode, messageCount, turns: turns.slice(0, 5), todos: todos.slice(0, 20) };
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// ── Save per-chat JSON file ────────────────────────────────────
|
|
564
|
+
function saveCursorLogFull(url, headers, buf) {
|
|
565
|
+
try {
|
|
566
|
+
if (buf.length < 5) return;
|
|
567
|
+
|
|
568
|
+
const isBidiAppend = url.includes("BidiAppend");
|
|
569
|
+
const isRunPoll = url.includes("RunPoll");
|
|
570
|
+
if (!isBidiAppend && !isRunPoll) return;
|
|
571
|
+
|
|
572
|
+
// Decompress if gzip, otherwise use raw
|
|
573
|
+
let raw;
|
|
574
|
+
const isGzip = buf[0] === 0x1f && buf[1] === 0x8b;
|
|
575
|
+
if (isGzip) {
|
|
576
|
+
raw = zlib.gunzipSync(buf);
|
|
577
|
+
} else {
|
|
578
|
+
// Try strip Connect-RPC envelope if present (flag byte + 4-byte length)
|
|
579
|
+
raw = (buf[0] === 0x00 || buf[0] === 0x01) ? buf.slice(5) : buf;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const endpoint = isBidiAppend ? "BidiAppend" : "RunPoll";
|
|
583
|
+
let decoded = {};
|
|
584
|
+
let userMessage = null;
|
|
585
|
+
|
|
586
|
+
if (isBidiAppend) {
|
|
587
|
+
decoded = decodeBidiAppend(raw);
|
|
588
|
+
const action = decoded?.agentMsg?.action || {};
|
|
589
|
+
userMessage = action.textFull || action.text || null;
|
|
590
|
+
} else {
|
|
591
|
+
// RunPoll: BidiPollRequest — f1=requestId{f1=uuid}, f2=startRequest
|
|
592
|
+
const fields = protoWalkFields(raw);
|
|
593
|
+
for (const f of fields) {
|
|
594
|
+
if (f.field === 1 && f.data) {
|
|
595
|
+
const inner = protoWalkFields(f.data);
|
|
596
|
+
const id = inner.find(x => x.field === 1 && x.data);
|
|
597
|
+
decoded.requestId = id ? protoStr(id.data) : null;
|
|
598
|
+
}
|
|
599
|
+
if (f.field === 2) decoded.startRequest = f.value === 1;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Fetch conversation history from local Cursor DB when AgentRunRequest detected
|
|
604
|
+
const conversationId = decoded?.agentMsg?.conversationId || null;
|
|
605
|
+
let conversation = null;
|
|
606
|
+
if (conversationId && decoded?.agentMsg?.type === "AgentRunRequest") {
|
|
607
|
+
conversation = readConversationFromDb(conversationId);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
611
|
+
const base = path.join(CURSOR_LOG_DIR, `${ts}_${endpoint}`);
|
|
612
|
+
|
|
613
|
+
// _chat.json — structured decoded
|
|
614
|
+
fs.writeFileSync(`${base}_chat.json`, JSON.stringify({
|
|
615
|
+
ts: new Date().toISOString(),
|
|
616
|
+
endpoint,
|
|
617
|
+
version: headers["x-cursor-client-version"] || null,
|
|
618
|
+
encoding: isGzip ? "gzip" : "raw",
|
|
619
|
+
sizeIn: buf.length,
|
|
620
|
+
sizeRaw: raw.length,
|
|
621
|
+
userMessage,
|
|
622
|
+
decoded,
|
|
623
|
+
conversation,
|
|
624
|
+
}, null, 2));
|
|
625
|
+
|
|
626
|
+
// _decoded.json — full proto tree, no filtering
|
|
627
|
+
const tree = protoTreeToObj(raw);
|
|
628
|
+
fs.writeFileSync(`${base}_decoded.json`, JSON.stringify(tree, null, 2));
|
|
629
|
+
|
|
630
|
+
const agentType = decoded?.agentMsg?.type || decoded?.requestId || "(none)";
|
|
631
|
+
const convName = conversation?.name ? ` | conv="${conversation.name}" (${conversation.totalMessages} msgs)` : "";
|
|
632
|
+
console.log(`[CURSOR] ${endpoint} | type=${agentType} | msg=${userMessage || "(none)"}${convName} | ${isGzip ? "gzip" : "raw"} ${buf.length}B`);
|
|
633
|
+
} catch (e) {
|
|
634
|
+
console.error(`[CURSOR] decode error: ${e.message}`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (!API_KEY) {
|
|
639
|
+
console.error("❌ ROUTER_API_KEY required");
|
|
640
|
+
process.exit(1);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const { getCertForDomain } = require("./cert/generate");
|
|
644
|
+
|
|
645
|
+
// Certificate cache for performance
|
|
646
|
+
const certCache = new Map();
|
|
647
|
+
|
|
648
|
+
// SNI callback for dynamic certificate generation
|
|
649
|
+
function sniCallback(servername, cb) {
|
|
650
|
+
try {
|
|
651
|
+
// Check cache first
|
|
652
|
+
if (certCache.has(servername)) {
|
|
653
|
+
const cached = certCache.get(servername);
|
|
654
|
+
return cb(null, cached);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Generate new cert for this domain
|
|
658
|
+
const certData = getCertForDomain(servername);
|
|
659
|
+
if (!certData) {
|
|
660
|
+
return cb(new Error(`Failed to generate cert for ${servername}`));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Create secure context
|
|
664
|
+
const ctx = require("tls").createSecureContext({
|
|
665
|
+
key: certData.key,
|
|
666
|
+
cert: certData.cert
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// Cache it
|
|
670
|
+
certCache.set(servername, ctx);
|
|
671
|
+
console.log(`✅ Generated cert for: ${servername}`);
|
|
672
|
+
|
|
673
|
+
cb(null, ctx);
|
|
674
|
+
} catch (error) {
|
|
675
|
+
console.error(`❌ SNI error for ${servername}:`, error.message);
|
|
676
|
+
cb(error);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Load Root CA for default context
|
|
681
|
+
const certDir = MITM_DIR;
|
|
682
|
+
const rootCAKeyPath = path.join(certDir, "rootCA.key");
|
|
683
|
+
const rootCACertPath = path.join(certDir, "rootCA.crt");
|
|
684
|
+
|
|
685
|
+
let sslOptions;
|
|
686
|
+
try {
|
|
687
|
+
sslOptions = {
|
|
688
|
+
key: fs.readFileSync(rootCAKeyPath),
|
|
689
|
+
cert: fs.readFileSync(rootCACertPath),
|
|
690
|
+
SNICallback: sniCallback
|
|
691
|
+
};
|
|
692
|
+
} catch (e) {
|
|
693
|
+
console.error(`❌ Root CA not found in ${certDir}: ${e.message}`);
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Antigravity: Gemini generateContent endpoints
|
|
698
|
+
const ANTIGRAVITY_URL_PATTERNS = [":generateContent", ":streamGenerateContent"];
|
|
699
|
+
// Copilot: OpenAI-compatible + Anthropic endpoints
|
|
700
|
+
const COPILOT_URL_PATTERNS = ["/chat/completions", "/v1/messages", "/responses"];
|
|
701
|
+
|
|
702
|
+
const LOG_DIR = path.join(__dirname, "../../logs/mitm");
|
|
703
|
+
if (ENABLE_FILE_LOG && !fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
704
|
+
|
|
705
|
+
function saveRequestLog(url, bodyBuffer) {
|
|
706
|
+
if (!ENABLE_FILE_LOG) return;
|
|
707
|
+
try {
|
|
708
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
709
|
+
const urlSlug = url.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 60);
|
|
710
|
+
const filePath = path.join(LOG_DIR, `${ts}_${urlSlug}.json`);
|
|
711
|
+
const body = JSON.parse(bodyBuffer.toString());
|
|
712
|
+
fs.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
713
|
+
} catch { /* ignore */ }
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const cachedTargetIPs = {};
|
|
717
|
+
async function resolveTargetIP(hostname) {
|
|
718
|
+
if (cachedTargetIPs[hostname]) return cachedTargetIPs[hostname];
|
|
719
|
+
const resolver = new dns.Resolver();
|
|
720
|
+
resolver.setServers(["8.8.8.8"]);
|
|
721
|
+
const resolve4 = promisify(resolver.resolve4.bind(resolver));
|
|
722
|
+
const addresses = await resolve4(hostname);
|
|
723
|
+
cachedTargetIPs[hostname] = addresses[0];
|
|
724
|
+
return cachedTargetIPs[hostname];
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function collectBodyRaw(req) {
|
|
728
|
+
return new Promise((resolve, reject) => {
|
|
729
|
+
const chunks = [];
|
|
730
|
+
req.on("data", chunk => chunks.push(chunk));
|
|
731
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
732
|
+
req.on("error", reject);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Extract model from URL path (Gemini) or body (OpenAI/Anthropic)
|
|
737
|
+
function extractModel(url, body) {
|
|
738
|
+
const urlMatch = url.match(/\/models\/([^/:]+)/);
|
|
739
|
+
if (urlMatch) return urlMatch[1];
|
|
740
|
+
try { return JSON.parse(body.toString()).model || null; } catch { return null; }
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function getMappedModel(tool, model) {
|
|
744
|
+
if (!model) return null;
|
|
745
|
+
try {
|
|
746
|
+
if (!fs.existsSync(DB_FILE)) return null;
|
|
747
|
+
const db = JSON.parse(fs.readFileSync(DB_FILE, "utf-8"));
|
|
748
|
+
return db.mitmAlias?.[tool]?.[model] || null;
|
|
749
|
+
} catch {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Determine which tool this request belongs to based on hostname
|
|
756
|
+
*/
|
|
757
|
+
function getToolForHost(host) {
|
|
758
|
+
const h = (host || "").split(":")[0];
|
|
759
|
+
if (h === "api.individual.githubcopilot.com") return "copilot";
|
|
760
|
+
if (h === "daily-cloudcode-pa.googleapis.com" || h === "cloudcode-pa.googleapis.com") return "antigravity";
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
async function passthrough(req, res, bodyBuffer) {
|
|
765
|
+
const targetHost = (req.headers.host || TARGET_HOSTS[0]).split(":")[0];
|
|
766
|
+
const targetIP = await resolveTargetIP(targetHost);
|
|
767
|
+
|
|
768
|
+
const forwardReq = https.request({
|
|
769
|
+
hostname: targetIP,
|
|
770
|
+
port: 443,
|
|
771
|
+
path: req.url,
|
|
772
|
+
method: req.method,
|
|
773
|
+
headers: { ...req.headers, host: targetHost },
|
|
774
|
+
servername: targetHost,
|
|
775
|
+
rejectUnauthorized: false
|
|
776
|
+
}, (forwardRes) => {
|
|
777
|
+
res.writeHead(forwardRes.statusCode, forwardRes.headers);
|
|
778
|
+
forwardRes.pipe(res);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
forwardReq.on("error", (err) => {
|
|
782
|
+
console.error(`❌ Passthrough error: ${err.message}`);
|
|
783
|
+
if (!res.headersSent) res.writeHead(502);
|
|
784
|
+
res.end("Bad Gateway");
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
if (bodyBuffer.length > 0) forwardReq.write(bodyBuffer);
|
|
788
|
+
forwardReq.end();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async function intercept(req, res, bodyBuffer, mappedModel) {
|
|
792
|
+
try {
|
|
793
|
+
const body = JSON.parse(bodyBuffer.toString());
|
|
794
|
+
body.model = mappedModel;
|
|
795
|
+
|
|
796
|
+
const response = await fetch(ROUTER_URL, {
|
|
797
|
+
method: "POST",
|
|
798
|
+
headers: {
|
|
799
|
+
"Content-Type": "application/json",
|
|
800
|
+
"Authorization": `Bearer ${API_KEY}`
|
|
801
|
+
},
|
|
802
|
+
body: JSON.stringify(body)
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
if (!response.ok) {
|
|
806
|
+
const errText = await response.text().catch(() => "");
|
|
807
|
+
throw new Error(`9Router ${response.status}: ${errText}`);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const ct = response.headers.get("content-type") || "application/json";
|
|
811
|
+
const resHeaders = { "Content-Type": ct, "Cache-Control": "no-cache", "Connection": "keep-alive" };
|
|
812
|
+
if (ct.includes("text/event-stream")) resHeaders["X-Accel-Buffering"] = "no";
|
|
813
|
+
res.writeHead(200, resHeaders);
|
|
814
|
+
|
|
815
|
+
const reader = response.body.getReader();
|
|
816
|
+
const decoder = new TextDecoder();
|
|
817
|
+
while (true) {
|
|
818
|
+
const { done, value } = await reader.read();
|
|
819
|
+
if (done) { res.end(); break; }
|
|
820
|
+
res.write(decoder.decode(value, { stream: true }));
|
|
821
|
+
}
|
|
822
|
+
} catch (error) {
|
|
823
|
+
console.error(`❌ ${error.message}`);
|
|
824
|
+
if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
|
|
825
|
+
res.end(JSON.stringify({ error: { message: error.message, type: "mitm_error" } }));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const server = https.createServer(sslOptions, async (req, res) => {
|
|
830
|
+
if (req.url === "/_mitm_health") {
|
|
831
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
832
|
+
res.end(JSON.stringify({ ok: true, pid: process.pid }));
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
console.log(`[MITM] ${req.method} https://${req.headers.host}${req.url}`);
|
|
837
|
+
|
|
838
|
+
const bodyBuffer = await collectBodyRaw(req);
|
|
839
|
+
if (bodyBuffer.length > 0) saveRequestLog(req.url, bodyBuffer);
|
|
840
|
+
|
|
841
|
+
// Decode and log Cursor protobuf endpoints
|
|
842
|
+
if (CURSOR_ENDPOINTS.some(e => req.url.includes(e))) {
|
|
843
|
+
saveCursorLogFull(req.url, req.headers, bodyBuffer);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Anti-loop: requests originating from 9Router bypass interception
|
|
847
|
+
if (req.headers[INTERNAL_REQUEST_HEADER.name] === INTERNAL_REQUEST_HEADER.value) {
|
|
848
|
+
return passthrough(req, res, bodyBuffer);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const tool = getToolForHost(req.headers.host);
|
|
852
|
+
if (!tool) return passthrough(req, res, bodyBuffer);
|
|
853
|
+
|
|
854
|
+
// Check if this URL should be intercepted based on tool
|
|
855
|
+
const isChat = tool === "antigravity"
|
|
856
|
+
? ANTIGRAVITY_URL_PATTERNS.some(p => req.url.includes(p))
|
|
857
|
+
: COPILOT_URL_PATTERNS.some(p => req.url.includes(p));
|
|
858
|
+
|
|
859
|
+
if (!isChat) return passthrough(req, res, bodyBuffer);
|
|
860
|
+
|
|
861
|
+
const model = extractModel(req.url, bodyBuffer);
|
|
862
|
+
console.log("Extracted model:", model)
|
|
863
|
+
const mappedModel = getMappedModel(tool, model);
|
|
864
|
+
|
|
865
|
+
if (!mappedModel) return passthrough(req, res, bodyBuffer);
|
|
866
|
+
|
|
867
|
+
return intercept(req, res, bodyBuffer, mappedModel);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
server.listen(LOCAL_PORT, () => {
|
|
871
|
+
console.log(`🚀 MITM ready on :${LOCAL_PORT}`);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
server.on("error", (error) => {
|
|
875
|
+
if (error.code === "EADDRINUSE") {
|
|
876
|
+
console.error(`❌ Port ${LOCAL_PORT} already in use`);
|
|
877
|
+
} else if (error.code === "EACCES") {
|
|
878
|
+
console.error(`❌ Permission denied for port ${LOCAL_PORT}`);
|
|
879
|
+
} else {
|
|
880
|
+
console.error(`❌ ${error.message}`);
|
|
881
|
+
}
|
|
882
|
+
process.exit(1);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
const shutdown = () => { server.close(() => process.exit(0)); };
|
|
886
|
+
process.on("SIGTERM", shutdown);
|
|
887
|
+
process.on("SIGINT", shutdown);
|
|
888
|
+
if (process.platform === "win32") {
|
|
889
|
+
process.on("SIGBREAK", shutdown);
|
|
890
|
+
}
|