n9router 0.3.89 → 0.3.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/mitm/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/providers/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/oauth/[provider]/[action]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/translator/send/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/translator/translate/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/[connectionId]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/chart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/history/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/logs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/request-details/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/request-logs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/stats/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/stream/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/api/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/chat/completions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/embeddings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/responses/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1beta/models/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/callback/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/callback.html +1 -1
- package/.next/standalone/.next/server/app/callback.rsc +13 -13
- package/.next/standalone/.next/server/app/callback.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/callback.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/callback.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/callback.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/callback.segments/callback/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/callback.segments/callback.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/basic-chat.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/basic-chat.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/basic-chat.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/cli-tools.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/cli-tools.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/combos.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/combos.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/combos.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/combos.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/combos.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/combos.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/endpoint.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/endpoint.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/endpoint.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/mitm.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/mitm.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/mitm.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/profile.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/profile.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/profile.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/profile.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/profile.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/providers/new.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/providers/new.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/providers/new.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/providers.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/providers.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/providers.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/providers.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/providers.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/quota.html +2 -2
- package/.next/standalone/.next/server/app/dashboard/quota.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/quota.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/quota.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/quota.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/quota.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.rsc +13 -13
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/translator.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/translator.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/translator.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/translator.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/translator.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/translator.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard/usage.html +1 -1
- package/.next/standalone/.next/server/app/dashboard/usage.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/usage.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/dashboard/usage.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard/usage.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard/usage.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/dashboard.html +1 -1
- package/.next/standalone/.next/server/app/dashboard.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/dashboard.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/dashboard.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/dashboard.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/landing/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/landing.html +1 -1
- package/.next/standalone/.next/server/app/landing.rsc +13 -13
- package/.next/standalone/.next/server/app/landing.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/landing.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/landing.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/landing.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/landing.segments/landing/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/landing.segments/landing.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login.html +1 -1
- package/.next/standalone/.next/server/app/login.rsc +13 -13
- package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.yvcis._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0148t9m._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b802lh._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kpc-o6._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lc~.sk._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lp_pm5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0pwrfp1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uw3x8s._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0y.reft._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0y5ae.7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z-kiuj._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0amvca6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0415gym._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_00c58qj._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_049j5t_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0ss815q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0tlv512._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_0_xhepz._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_shared_constants_cliTools_09iru_o.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/static/chunks/026p3rpdycbyo.css +1 -0
- package/.next/standalone/.next/static/chunks/{08kho~qudyqxf.js → 06l6ck_q1o~m9.js} +3 -3
- package/.next/standalone/.next/static/chunks/{00~ibgir0ac-0.js → 0pwbnjvdm7lt_.js} +2 -2
- package/.next/{static/chunks/0hgyabetclndg.js → standalone/.next/static/chunks/0~p0472sbmye6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0a84s1t0dt8-h.js → 148_2lu2ra629.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0f56a6ti-axtc.js → 17~_6lx17xr8z.js} +3 -3
- package/.next/standalone/mitm/server.js +8 -8
- package/.next/standalone/mitm/tokenPool.js +79 -17
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/src/app/(dashboard)/dashboard/cli-tools/components/TokenSwapPoolCard.js +331 -50
- package/.next/standalone/src/app/(dashboard)/dashboard/mitm/MitmPageClient.js +11 -4
- package/.next/standalone/src/app/api/providers/[id]/route.js +4 -0
- package/.next/standalone/src/lib/localDb.js +3 -1
- package/.next/standalone/src/mitm/server.js +8 -8
- package/.next/standalone/src/mitm/tokenPool.js +79 -17
- package/.next/standalone/tests/unit/tokenPool.test.js +55 -14
- package/.next/static/chunks/026p3rpdycbyo.css +1 -0
- package/.next/static/chunks/{08kho~qudyqxf.js → 06l6ck_q1o~m9.js} +3 -3
- package/.next/static/chunks/{00~ibgir0ac-0.js → 0pwbnjvdm7lt_.js} +2 -2
- package/.next/{standalone/.next/static/chunks/0hgyabetclndg.js → static/chunks/0~p0472sbmye6.js} +1 -1
- package/.next/static/chunks/{0a84s1t0dt8-h.js → 148_2lu2ra629.js} +1 -1
- package/.next/static/chunks/{0f56a6ti-axtc.js → 17~_6lx17xr8z.js} +3 -3
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0wr~fjn._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0z7gqwv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_12us7s0._.js +0 -3
- package/.next/standalone/.next/static/chunks/0lkbvidbb15ld.css +0 -1
- package/.next/static/chunks/0lkbvidbb15ld.css +0 -1
- /package/.next/standalone/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_ssgManifest.js +0 -0
- /package/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_buildManifest.js +0 -0
- /package/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{XSoZ0BJFg1b0W2IGyybf7 → eALv8q_pKhx9-bE-9KiDB}/_ssgManifest.js +0 -0
|
@@ -110,6 +110,52 @@ function getActiveConnections(provider) {
|
|
|
110
110
|
} catch { return []; }
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
function getConnectionById(connId) {
|
|
114
|
+
try {
|
|
115
|
+
if (!fs.existsSync(DB_FILE)) return null;
|
|
116
|
+
const db = JSON.parse(fs.readFileSync(DB_FILE, "utf-8"));
|
|
117
|
+
const connections = db.providerConnections || [];
|
|
118
|
+
return connections.find(c => c.id === connId) || null;
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function maskEmail(email) {
|
|
125
|
+
if (!email || typeof email !== "string") return email;
|
|
126
|
+
const atIndex = email.indexOf("@");
|
|
127
|
+
if (atIndex <= 0 || atIndex === email.length - 1) return email;
|
|
128
|
+
|
|
129
|
+
const local = email.slice(0, atIndex);
|
|
130
|
+
const domain = email.slice(atIndex + 1);
|
|
131
|
+
|
|
132
|
+
if (local.length === 1) return `${local[0]}**@${domain}`;
|
|
133
|
+
if (local.length === 2) return `${local[0]}**${local[1]}@${domain}`;
|
|
134
|
+
|
|
135
|
+
return `${local[0]}**${local[local.length - 1]}@${domain}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isAccountEmailMaskEnabled() {
|
|
139
|
+
try {
|
|
140
|
+
if (!fs.existsSync(DB_FILE)) return false;
|
|
141
|
+
const db = JSON.parse(fs.readFileSync(DB_FILE, "utf-8"));
|
|
142
|
+
return !!db.settings?.tokenSwapMaskEmails;
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getConnectionLabel(connection) {
|
|
149
|
+
if (!connection) return "";
|
|
150
|
+
|
|
151
|
+
const shouldMask = isAccountEmailMaskEnabled();
|
|
152
|
+
const email = shouldMask ? maskEmail(connection.email) : connection.email;
|
|
153
|
+
|
|
154
|
+
if (connection.name && email) return `${connection.name} <${email}>`;
|
|
155
|
+
if (email) return email;
|
|
156
|
+
return connection.name || connection.id.slice(0, 8);
|
|
157
|
+
}
|
|
158
|
+
|
|
113
159
|
function isTokenSwapEnabled(provider) {
|
|
114
160
|
try {
|
|
115
161
|
if (!fs.existsSync(DB_FILE)) return false;
|
|
@@ -131,13 +177,13 @@ function getNextConnection(provider) {
|
|
|
131
177
|
const connections = getActiveConnections(provider);
|
|
132
178
|
if (connections.length === 0) return null;
|
|
133
179
|
if (connections.length === 1) {
|
|
134
|
-
log(`🎯 [token-pool] selected: "${connections[0]
|
|
180
|
+
log(`🎯 [token-pool] selected: "${getConnectionLabel(connections[0])}" (only account)`);
|
|
135
181
|
return connections[0];
|
|
136
182
|
}
|
|
137
183
|
const idx = (rrState[provider] || 0) % connections.length;
|
|
138
184
|
rrState[provider] = idx + 1;
|
|
139
185
|
const selected = connections[idx];
|
|
140
|
-
log(`🎯 [token-pool] round-robin[${idx}/${connections.length}]: "${selected
|
|
186
|
+
log(`🎯 [token-pool] round-robin[${idx}/${connections.length}]: "${getConnectionLabel(selected)}"`);
|
|
141
187
|
return selected;
|
|
142
188
|
}
|
|
143
189
|
|
|
@@ -236,24 +282,38 @@ function markAccountUsed(connId) {
|
|
|
236
282
|
}
|
|
237
283
|
}
|
|
238
284
|
|
|
239
|
-
// ── Token refresh trigger (
|
|
285
|
+
// ── Token refresh trigger (refresh + reload persisted token) ─
|
|
240
286
|
// Calls 9Router's existing POST /api/providers/:id/test which
|
|
241
|
-
// checks expiry
|
|
287
|
+
// checks expiry, refreshes token automatically, then returns the
|
|
288
|
+
// latest persisted connection snapshot for the current request.
|
|
242
289
|
|
|
243
|
-
function triggerRefreshIfNeeded(connection) {
|
|
244
|
-
if (!connection
|
|
290
|
+
async function triggerRefreshIfNeeded(connection) {
|
|
291
|
+
if (!connection?.expiresAt || !connection?.refreshToken) return connection;
|
|
245
292
|
const expiresAt = new Date(connection.expiresAt).getTime();
|
|
246
|
-
if (Date.now() + TOKEN_EXPIRY_BUFFER_MS < expiresAt) return;
|
|
247
|
-
|
|
248
|
-
log(`🔄 [token-pool] near-expiry refresh → ${(connection
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
293
|
+
if (Date.now() + TOKEN_EXPIRY_BUFFER_MS < expiresAt) return connection;
|
|
294
|
+
|
|
295
|
+
log(`🔄 [token-pool] near-expiry refresh → ${getConnectionLabel(connection).slice(0, 20)}`);
|
|
296
|
+
return await new Promise((resolve) => {
|
|
297
|
+
try {
|
|
298
|
+
const req = http.request(
|
|
299
|
+
{ hostname: "127.0.0.1", port: ROUTER_PORT, path: `/api/providers/${connection.id}/test`, method: "POST" },
|
|
300
|
+
(res) => {
|
|
301
|
+
res.on("data", () => {});
|
|
302
|
+
res.on("end", () => {
|
|
303
|
+
const refreshed = getConnectionById(connection.id);
|
|
304
|
+
if (refreshed?.accessToken && refreshed.accessToken !== connection.accessToken) {
|
|
305
|
+
log(`♻️ [token-pool] refreshed token applied → ${getConnectionLabel(connection).slice(0, 20)}`);
|
|
306
|
+
}
|
|
307
|
+
resolve(refreshed || connection);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
req.on("error", () => resolve(connection));
|
|
312
|
+
req.end();
|
|
313
|
+
} catch {
|
|
314
|
+
resolve(connection);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
257
317
|
}
|
|
258
318
|
|
|
259
319
|
module.exports = {
|
|
@@ -267,4 +327,6 @@ module.exports = {
|
|
|
267
327
|
getTokenSwapStrategy,
|
|
268
328
|
parseQuotaCooldown,
|
|
269
329
|
markAccountUsed,
|
|
330
|
+
getConnectionLabel,
|
|
331
|
+
maskEmail,
|
|
270
332
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n9router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.90",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "n9router",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.90",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@monaco-editor/react": "^4.7.0",
|
package/.next/standalone/src/app/(dashboard)/dashboard/cli-tools/components/TokenSwapPoolCard.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
4
|
-
import { Card, Badge } from "@/shared/components";
|
|
4
|
+
import { Card, Badge, Toggle } from "@/shared/components";
|
|
5
5
|
import Link from "next/link";
|
|
6
|
-
import { parseQuotaData } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils";
|
|
6
|
+
import { parseQuotaData, formatResetTime } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils";
|
|
7
7
|
|
|
8
8
|
// Model to highlight in the quota summary
|
|
9
9
|
const HIGHLIGHT_MODEL = "claude-sonnet-4-6";
|
|
@@ -26,14 +26,103 @@ function getQuotaBg(pct) {
|
|
|
26
26
|
return "bg-red-500";
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function formatResetTimeDisplay(resetTime) {
|
|
30
|
+
if (!resetTime) return null;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const resetDate = new Date(resetTime);
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const isToday = resetDate.toDateString() === now.toDateString();
|
|
36
|
+
const isTomorrow = resetDate.toDateString() === new Date(now.getTime() + 86400000).toDateString();
|
|
37
|
+
|
|
38
|
+
const timeStr = resetDate.toLocaleTimeString(undefined, {
|
|
39
|
+
hour: "2-digit",
|
|
40
|
+
minute: "2-digit",
|
|
41
|
+
hour12: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (isToday) return `Today, ${timeStr}`;
|
|
45
|
+
if (isTomorrow) return `Tomorrow, ${timeStr}`;
|
|
46
|
+
|
|
47
|
+
return resetDate.toLocaleString(undefined, {
|
|
48
|
+
month: "short",
|
|
49
|
+
day: "numeric",
|
|
50
|
+
hour: "2-digit",
|
|
51
|
+
minute: "2-digit",
|
|
52
|
+
hour12: true,
|
|
53
|
+
});
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function maskEmail(email) {
|
|
60
|
+
if (!email || typeof email !== "string") return email;
|
|
61
|
+
const atIndex = email.indexOf("@");
|
|
62
|
+
if (atIndex <= 0 || atIndex === email.length - 1) return email;
|
|
63
|
+
|
|
64
|
+
const local = email.slice(0, atIndex);
|
|
65
|
+
const domain = email.slice(atIndex + 1);
|
|
66
|
+
|
|
67
|
+
if (local.length === 1) return `${local[0]}**@${domain}`;
|
|
68
|
+
if (local.length === 2) return `${local[0]}**${local[1]}@${domain}`;
|
|
69
|
+
|
|
70
|
+
return `${local[0]}**${local[local.length - 1]}@${domain}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getAccountDisplay(acc, maskEmails) {
|
|
74
|
+
if (acc.email) return maskEmails ? maskEmail(acc.email) : acc.email;
|
|
75
|
+
return acc.name || acc.id.slice(0, 16);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getStickyLimitForTool(tool) {
|
|
79
|
+
return tool?.stickyRoundRobinLimit || 3;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getPreferredAccountId(accounts, strategy, stickyLimit) {
|
|
83
|
+
if (!accounts || accounts.length === 0) return null;
|
|
84
|
+
if (accounts.length === 1) return accounts[0].id;
|
|
85
|
+
|
|
86
|
+
const byNewest = [...accounts].sort((a, b) => {
|
|
87
|
+
if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
|
|
88
|
+
if (!a.lastUsedAt) return 1;
|
|
89
|
+
if (!b.lastUsedAt) return -1;
|
|
90
|
+
return new Date(b.lastUsedAt) - new Date(a.lastUsedAt);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (strategy === "sticky") {
|
|
94
|
+
return byNewest[0]?.id || null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const current = byNewest[0];
|
|
98
|
+
const currentCount = current?.consecutiveUseCount || 0;
|
|
99
|
+
if (current?.lastUsedAt && currentCount < stickyLimit) {
|
|
100
|
+
return current.id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const byOldest = [...accounts].sort((a, b) => {
|
|
104
|
+
if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
|
|
105
|
+
if (!a.lastUsedAt) return -1;
|
|
106
|
+
if (!b.lastUsedAt) return 1;
|
|
107
|
+
return new Date(a.lastUsedAt) - new Date(b.lastUsedAt);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return byOldest[0]?.id || null;
|
|
111
|
+
}
|
|
112
|
+
|
|
29
113
|
/**
|
|
30
114
|
* Token Swap Pool Card — standalone card for token rotation mode.
|
|
31
115
|
*/
|
|
32
|
-
export default function TokenSwapPoolCard({ tool, connections = [], serverRunning, dnsActive, onToggle }) {
|
|
116
|
+
export default function TokenSwapPoolCard({ tool, connections = [], serverRunning, dnsActive, onToggle, onRefreshConnections }) {
|
|
33
117
|
const [enabled, setEnabled] = useState(false);
|
|
34
118
|
const [toggling, setToggling] = useState(false);
|
|
35
119
|
const [strategy, setStrategy] = useState("round-robin"); // "round-robin" | "sticky"
|
|
36
120
|
const [togglingStrategy, setTogglingStrategy] = useState(false);
|
|
121
|
+
const [maskEmails, setMaskEmails] = useState(false);
|
|
122
|
+
const [togglingMaskEmails, setTogglingMaskEmails] = useState(false);
|
|
123
|
+
const [togglingAccountId, setTogglingAccountId] = useState(null);
|
|
124
|
+
const [resettingAccountId, setResettingAccountId] = useState(null);
|
|
125
|
+
const [resettingAll, setResettingAll] = useState(false);
|
|
37
126
|
const [quotas, setQuotas] = useState({}); // { [connId]: { quotas: [], error: string|null, loading: bool } }
|
|
38
127
|
const quotaCacheRef = useRef({}); // { [connId]: { data: parsed, error, ts: number } }
|
|
39
128
|
|
|
@@ -44,6 +133,7 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
44
133
|
const data = await res.json();
|
|
45
134
|
setEnabled(!!data.tokenSwapEnabled);
|
|
46
135
|
setStrategy(data.tokenSwapStrategy || "round-robin");
|
|
136
|
+
setMaskEmails(!!data.tokenSwapMaskEmails);
|
|
47
137
|
}
|
|
48
138
|
} catch { /* ignore */ }
|
|
49
139
|
}, []);
|
|
@@ -149,18 +239,39 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
149
239
|
setTogglingStrategy(false);
|
|
150
240
|
};
|
|
151
241
|
|
|
152
|
-
const
|
|
153
|
-
(
|
|
242
|
+
const toggleMaskEmails = async () => {
|
|
243
|
+
if (togglingMaskEmails) return;
|
|
244
|
+
setTogglingMaskEmails(true);
|
|
245
|
+
const newVal = !maskEmails;
|
|
246
|
+
try {
|
|
247
|
+
const res = await fetch("/api/settings", {
|
|
248
|
+
method: "PATCH",
|
|
249
|
+
headers: { "Content-Type": "application/json" },
|
|
250
|
+
body: JSON.stringify({ tokenSwapMaskEmails: newVal }),
|
|
251
|
+
});
|
|
252
|
+
if (res.ok) setMaskEmails(newVal);
|
|
253
|
+
} catch { /* ignore */ }
|
|
254
|
+
setTogglingMaskEmails(false);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const providerAccounts = connections.filter(
|
|
258
|
+
(c) => c.provider === tool.tokenSwapProvider
|
|
154
259
|
);
|
|
155
|
-
const
|
|
260
|
+
const activeAccounts = providerAccounts.filter(
|
|
261
|
+
(c) => c.isActive !== false
|
|
262
|
+
);
|
|
263
|
+
const activeCount = activeAccounts.length;
|
|
264
|
+
const activeAccountsKey = activeAccounts.map((acc) => acc.id).join("|");
|
|
265
|
+
const stickyLimit = getStickyLimitForTool(tool);
|
|
266
|
+
const preferredAccountId = getPreferredAccountId(activeAccounts, strategy, stickyLimit);
|
|
156
267
|
|
|
157
268
|
// Auto-fetch quotas when enabled and accounts available
|
|
158
269
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
159
270
|
useEffect(() => {
|
|
160
271
|
if (enabled && activeCount > 0) {
|
|
161
|
-
fetchQuotas(
|
|
272
|
+
fetchQuotas(activeAccounts);
|
|
162
273
|
}
|
|
163
|
-
}, [enabled, activeCount]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
274
|
+
}, [enabled, activeCount, activeAccountsKey, fetchQuotas]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
164
275
|
|
|
165
276
|
// Prerequisites check
|
|
166
277
|
const prereqsMet = serverRunning && dnsActive;
|
|
@@ -169,31 +280,19 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
169
280
|
/**
|
|
170
281
|
* Render inline quota info for a single account
|
|
171
282
|
*/
|
|
172
|
-
const
|
|
283
|
+
const getAccountQuotaMeta = (accId) => {
|
|
173
284
|
const q = quotas[accId];
|
|
174
|
-
if (!q) return
|
|
175
|
-
if (q.loading) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
if (q.error) {
|
|
181
|
-
return (
|
|
182
|
-
<span className="text-[10px] text-red-400 shrink-0" title={q.error}>⛔ bad account</span>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (!q.quotas || q.quotas.length === 0) {
|
|
186
|
-
return (
|
|
187
|
-
<span className="text-[10px] text-text-muted shrink-0">no quota data</span>
|
|
188
|
-
);
|
|
189
|
-
}
|
|
285
|
+
if (!q) return { state: "empty" };
|
|
286
|
+
if (q.loading) return { state: "loading" };
|
|
287
|
+
if (q.error) return { state: "error", error: q.error };
|
|
288
|
+
if (!q.quotas || q.quotas.length === 0) return { state: "no-data" };
|
|
190
289
|
|
|
191
290
|
// Find highlight model, fallback to first quota with data
|
|
192
291
|
const highlight = q.quotas.find(m =>
|
|
193
292
|
m.modelKey?.includes(HIGHLIGHT_MODEL) || m.name?.toLowerCase().includes("opus")
|
|
194
293
|
) || q.quotas[0];
|
|
195
294
|
|
|
196
|
-
if (!highlight) return
|
|
295
|
+
if (!highlight) return { state: "no-data" };
|
|
197
296
|
|
|
198
297
|
const pct = highlight.remainingPercentage !== undefined
|
|
199
298
|
? Math.round(highlight.remainingPercentage)
|
|
@@ -201,19 +300,113 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
201
300
|
? Math.round(((highlight.total - highlight.used) / highlight.total) * 100)
|
|
202
301
|
: null;
|
|
203
302
|
|
|
204
|
-
|
|
303
|
+
const nextResetAt = [...q.quotas]
|
|
304
|
+
.map((quota) => quota.resetAt)
|
|
305
|
+
.filter(Boolean)
|
|
306
|
+
.filter((resetAt) => new Date(resetAt).getTime() > Date.now())
|
|
307
|
+
.sort((a, b) => new Date(a).getTime() - new Date(b).getTime())[0] || highlight.resetAt || null;
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
state: pct === null ? "no-data" : "ready",
|
|
311
|
+
highlight,
|
|
312
|
+
pct,
|
|
313
|
+
nextResetAt,
|
|
314
|
+
resetCountdown: formatResetTime(nextResetAt),
|
|
315
|
+
resetDisplay: formatResetTimeDisplay(nextResetAt),
|
|
316
|
+
};
|
|
317
|
+
};
|
|
205
318
|
|
|
319
|
+
const renderAccountQuota = (accId) => {
|
|
320
|
+
const meta = getAccountQuotaMeta(accId);
|
|
321
|
+
if (!meta || meta.state === "empty") {
|
|
322
|
+
return <span className="text-[10px] text-text-muted">Enable to load quota data</span>;
|
|
323
|
+
}
|
|
324
|
+
if (meta.state === "loading") {
|
|
325
|
+
return <span className="text-[10px] text-text-muted animate-pulse">Loading quota…</span>;
|
|
326
|
+
}
|
|
327
|
+
if (meta.state === "error") {
|
|
328
|
+
return <span className="text-[10px] text-red-400" title={meta.error}>Quota unavailable</span>;
|
|
329
|
+
}
|
|
330
|
+
if (meta.state === "no-data") {
|
|
331
|
+
return <span className="text-[10px] text-text-muted">No quota data</span>;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const { highlight, pct, resetCountdown, resetDisplay } = meta;
|
|
206
335
|
return (
|
|
207
|
-
<div className="flex
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
<div className=
|
|
336
|
+
<div className="flex flex-col gap-1.5 min-w-0">
|
|
337
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
338
|
+
<span className="text-[10px] text-text-muted truncate">{highlight.name}</span>
|
|
339
|
+
<div className="flex-1 h-1.5 rounded-full bg-surface-alt overflow-hidden min-w-[56px]">
|
|
340
|
+
<div className={`h-full rounded-full ${getQuotaBg(pct)}`} style={{ width: `${Math.min(pct, 100)}%` }} />
|
|
341
|
+
</div>
|
|
342
|
+
<span className={`text-[10px] font-medium shrink-0 ${getQuotaColor(pct)}`}>{pct}%</span>
|
|
211
343
|
</div>
|
|
212
|
-
|
|
344
|
+
{resetCountdown !== "-" && resetDisplay ? (
|
|
345
|
+
<div className="text-[10px] text-text-muted">
|
|
346
|
+
Reset in <span className="text-text-main">{resetCountdown}</span>
|
|
347
|
+
<span className="text-text-muted/70"> • {resetDisplay}</span>
|
|
348
|
+
</div>
|
|
349
|
+
) : (
|
|
350
|
+
<div className="text-[10px] text-text-muted">Reset time unavailable</div>
|
|
351
|
+
)}
|
|
213
352
|
</div>
|
|
214
353
|
);
|
|
215
354
|
};
|
|
216
355
|
|
|
356
|
+
const toggleAccountActive = async (accountId, nextActive) => {
|
|
357
|
+
if (!accountId || togglingAccountId || resettingAll || resettingAccountId) return;
|
|
358
|
+
setTogglingAccountId(accountId);
|
|
359
|
+
try {
|
|
360
|
+
const res = await fetch(`/api/providers/${accountId}`, {
|
|
361
|
+
method: "PUT",
|
|
362
|
+
headers: { "Content-Type": "application/json" },
|
|
363
|
+
body: JSON.stringify({ isActive: nextActive }),
|
|
364
|
+
});
|
|
365
|
+
if (res.ok) {
|
|
366
|
+
await onRefreshConnections?.();
|
|
367
|
+
}
|
|
368
|
+
} catch { /* ignore */ }
|
|
369
|
+
setTogglingAccountId(null);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const resetAccountStreak = async (accountId) => {
|
|
373
|
+
if (!accountId || resettingAccountId || resettingAll || togglingAccountId) return;
|
|
374
|
+
setResettingAccountId(accountId);
|
|
375
|
+
try {
|
|
376
|
+
const res = await fetch(`/api/providers/${accountId}`, {
|
|
377
|
+
method: "PUT",
|
|
378
|
+
headers: { "Content-Type": "application/json" },
|
|
379
|
+
body: JSON.stringify({
|
|
380
|
+
lastUsedAt: null,
|
|
381
|
+
consecutiveUseCount: 0,
|
|
382
|
+
}),
|
|
383
|
+
});
|
|
384
|
+
if (res.ok) {
|
|
385
|
+
await onRefreshConnections?.();
|
|
386
|
+
}
|
|
387
|
+
} catch { /* ignore */ }
|
|
388
|
+
setResettingAccountId(null);
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const resetAllStreaks = async () => {
|
|
392
|
+
if (resettingAll || resettingAccountId || togglingAccountId || providerAccounts.length === 0) return;
|
|
393
|
+
setResettingAll(true);
|
|
394
|
+
try {
|
|
395
|
+
await Promise.all(providerAccounts.map((acc) => (
|
|
396
|
+
fetch(`/api/providers/${acc.id}`, {
|
|
397
|
+
method: "PUT",
|
|
398
|
+
headers: { "Content-Type": "application/json" },
|
|
399
|
+
body: JSON.stringify({
|
|
400
|
+
lastUsedAt: null,
|
|
401
|
+
consecutiveUseCount: 0,
|
|
402
|
+
}),
|
|
403
|
+
})
|
|
404
|
+
)));
|
|
405
|
+
await onRefreshConnections?.();
|
|
406
|
+
} catch { /* ignore */ }
|
|
407
|
+
setResettingAll(false);
|
|
408
|
+
};
|
|
409
|
+
|
|
217
410
|
return (
|
|
218
411
|
<Card padding="xs" className="overflow-hidden">
|
|
219
412
|
{/* ── Header ────────────────────────────────── */}
|
|
@@ -334,7 +527,7 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
334
527
|
<div className="flex items-center gap-2">
|
|
335
528
|
<p className="text-[10px] uppercase tracking-wider text-text-muted font-semibold">Pool Accounts</p>
|
|
336
529
|
<span className="text-[10px] text-text-muted">
|
|
337
|
-
{
|
|
530
|
+
{providerAccounts.length > 0 ? `${activeCount}/${providerAccounts.length} active` : "none"}
|
|
338
531
|
</span>
|
|
339
532
|
{activeCount > 1 && (
|
|
340
533
|
<span className="text-[9px] text-text-muted bg-surface border border-border px-1 py-0.5 rounded">
|
|
@@ -342,26 +535,114 @@ export default function TokenSwapPoolCard({ tool, connections = [], serverRunnin
|
|
|
342
535
|
</span>
|
|
343
536
|
)}
|
|
344
537
|
</div>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
538
|
+
<div className="flex items-center gap-2">
|
|
539
|
+
{activeCount > 0 && (
|
|
540
|
+
<button
|
|
541
|
+
onClick={() => fetchQuotas(activeAccounts, true)}
|
|
542
|
+
className="text-[10px] text-text-muted hover:text-primary flex items-center gap-0.5 transition-colors"
|
|
543
|
+
title="Refresh quotas"
|
|
544
|
+
>
|
|
545
|
+
<span className="material-symbols-outlined text-[12px]">refresh</span>
|
|
546
|
+
</button>
|
|
547
|
+
)}
|
|
548
|
+
{providerAccounts.length > 0 && (
|
|
549
|
+
<button
|
|
550
|
+
onClick={resetAllStreaks}
|
|
551
|
+
disabled={resettingAll || !!resettingAccountId}
|
|
552
|
+
className="text-[10px] text-text-muted hover:text-primary disabled:opacity-50 flex items-center gap-0.5 transition-colors"
|
|
553
|
+
title="Reset all streak counts"
|
|
554
|
+
>
|
|
555
|
+
<span className="material-symbols-outlined text-[12px]">restart_alt</span>
|
|
556
|
+
Reset all
|
|
557
|
+
</button>
|
|
558
|
+
)}
|
|
559
|
+
</div>
|
|
560
|
+
</div>
|
|
561
|
+
<p className="text-[10px] text-text-muted px-0.5">
|
|
562
|
+
Sticky round robin keeps the current account until its streak reaches {stickyLimit}, then rotates to the least recently used account.
|
|
563
|
+
</p>
|
|
564
|
+
<div className="flex items-center justify-between gap-3 px-2 py-2 rounded-lg border border-border bg-surface-alt/40">
|
|
565
|
+
<div className="min-w-0">
|
|
566
|
+
<p className="text-[11px] font-medium text-text-main">Mask account emails</p>
|
|
567
|
+
<p className="text-[10px] text-text-muted">
|
|
568
|
+
Hide pool account emails in token swap logs and this panel. Example: {maskEmail("email@gmail.com")}
|
|
569
|
+
</p>
|
|
570
|
+
</div>
|
|
571
|
+
<button
|
|
572
|
+
onClick={toggleMaskEmails}
|
|
573
|
+
disabled={togglingMaskEmails}
|
|
574
|
+
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors shrink-0 ${
|
|
575
|
+
maskEmails ? "bg-violet-500" : "bg-surface border border-border"
|
|
576
|
+
} ${togglingMaskEmails ? "opacity-50" : "cursor-pointer"}`}
|
|
577
|
+
title={maskEmails ? "Disable email masking" : "Enable email masking"}
|
|
578
|
+
>
|
|
579
|
+
<span
|
|
580
|
+
className={`inline-block h-3.5 w-3.5 transform rounded-full bg-white transition-transform shadow-sm ${
|
|
581
|
+
maskEmails ? "translate-x-4" : "translate-x-0.5"
|
|
582
|
+
}`}
|
|
583
|
+
/>
|
|
584
|
+
</button>
|
|
354
585
|
</div>
|
|
355
586
|
|
|
356
|
-
{
|
|
587
|
+
{providerAccounts.length > 0 ? (
|
|
357
588
|
<>
|
|
358
|
-
{
|
|
359
|
-
<div
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
589
|
+
{providerAccounts.map((acc) => (
|
|
590
|
+
<div
|
|
591
|
+
key={acc.id}
|
|
592
|
+
className={`rounded-xl border border-border bg-surface-alt/30 px-3 py-2.5 transition-colors ${
|
|
593
|
+
acc.isActive === false ? "opacity-65" : "hover:bg-surface-alt/50"
|
|
594
|
+
}`}
|
|
595
|
+
>
|
|
596
|
+
<div className="flex items-start justify-between gap-3">
|
|
597
|
+
<div className="min-w-0 flex-1">
|
|
598
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
599
|
+
<span className={`material-symbols-outlined text-[14px] shrink-0 ${acc.isActive === false ? "text-text-muted" : "text-green-500"}`}>
|
|
600
|
+
{acc.isActive === false ? "pause_circle" : "check_circle"}
|
|
601
|
+
</span>
|
|
602
|
+
<span className="text-xs font-medium text-text-main truncate">
|
|
603
|
+
{getAccountDisplay(acc, maskEmails)}
|
|
604
|
+
</span>
|
|
605
|
+
{preferredAccountId === acc.id && acc.isActive !== false && (
|
|
606
|
+
<span className="text-[9px] text-violet-300 bg-violet-500/10 border border-violet-500/20 px-1 py-0.5 rounded shrink-0">
|
|
607
|
+
next
|
|
608
|
+
</span>
|
|
609
|
+
)}
|
|
610
|
+
<Badge variant={acc.isActive === false ? "default" : "success"} size="sm">
|
|
611
|
+
{acc.isActive === false ? "disabled" : "active"}
|
|
612
|
+
</Badge>
|
|
613
|
+
</div>
|
|
614
|
+
<div className="mt-1 flex items-center gap-2 flex-wrap text-[10px] text-text-muted">
|
|
615
|
+
<span>Priority #{acc.priority ?? "-"}</span>
|
|
616
|
+
<span>Streak {acc.consecutiveUseCount || 0}/{stickyLimit}</span>
|
|
617
|
+
{acc.lastUsedAt && <span>Last used {new Date(acc.lastUsedAt).toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })}</span>}
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
<div className="flex items-center gap-3 shrink-0">
|
|
621
|
+
<button
|
|
622
|
+
onClick={() => resetAccountStreak(acc.id)}
|
|
623
|
+
disabled={resettingAll || togglingAccountId === acc.id || resettingAccountId === acc.id}
|
|
624
|
+
className="text-[10px] text-text-muted hover:text-primary disabled:opacity-50 transition-colors"
|
|
625
|
+
title="Reset this account streak"
|
|
626
|
+
>
|
|
627
|
+
{resettingAccountId === acc.id ? "..." : "Reset Streak"}
|
|
628
|
+
</button>
|
|
629
|
+
<Toggle
|
|
630
|
+
size="sm"
|
|
631
|
+
checked={acc.isActive !== false}
|
|
632
|
+
disabled={resettingAll || resettingAccountId === acc.id || togglingAccountId === acc.id}
|
|
633
|
+
onChange={(nextChecked) => toggleAccountActive(acc.id, nextChecked)}
|
|
634
|
+
/>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
<div className="mt-2 pl-6">
|
|
638
|
+
{acc.isActive === false ? (
|
|
639
|
+
<div className="text-[10px] text-text-muted">
|
|
640
|
+
Enable this account to include it in token rotation and load quota reset info.
|
|
641
|
+
</div>
|
|
642
|
+
) : (
|
|
643
|
+
renderAccountQuota(acc.id)
|
|
644
|
+
)}
|
|
645
|
+
</div>
|
|
365
646
|
</div>
|
|
366
647
|
))}
|
|
367
648
|
<Link
|
|
@@ -57,10 +57,16 @@ export default function MitmPageClient() {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
useEffect(() => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
const loadInitialData = async () => {
|
|
61
|
+
await Promise.all([
|
|
62
|
+
fetchConnections(),
|
|
63
|
+
fetchApiKeys(),
|
|
64
|
+
fetchAliases(),
|
|
65
|
+
fetchCloudSettings(),
|
|
66
|
+
]);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
loadInitialData();
|
|
64
70
|
}, []);
|
|
65
71
|
|
|
66
72
|
const getActiveProviders = () => connections.filter(c => c.isActive !== false);
|
|
@@ -111,6 +117,7 @@ export default function MitmPageClient() {
|
|
|
111
117
|
serverRunning={mitmStatus.running}
|
|
112
118
|
dnsActive={mitmStatus.dnsStatus?.[toolId] || false}
|
|
113
119
|
onToggle={(val) => setTokenSwapActive(val)}
|
|
120
|
+
onRefreshConnections={fetchConnections}
|
|
114
121
|
/>
|
|
115
122
|
)}
|
|
116
123
|
</Fragment>
|
|
@@ -98,6 +98,8 @@ export async function PUT(request, { params }) {
|
|
|
98
98
|
testStatus,
|
|
99
99
|
lastError,
|
|
100
100
|
lastErrorAt,
|
|
101
|
+
lastUsedAt,
|
|
102
|
+
consecutiveUseCount,
|
|
101
103
|
providerSpecificData
|
|
102
104
|
} = body;
|
|
103
105
|
|
|
@@ -126,6 +128,8 @@ export async function PUT(request, { params }) {
|
|
|
126
128
|
if (testStatus !== undefined) updateData.testStatus = testStatus;
|
|
127
129
|
if (lastError !== undefined) updateData.lastError = lastError;
|
|
128
130
|
if (lastErrorAt !== undefined) updateData.lastErrorAt = lastErrorAt;
|
|
131
|
+
if (lastUsedAt !== undefined) updateData.lastUsedAt = lastUsedAt;
|
|
132
|
+
if (consecutiveUseCount !== undefined) updateData.consecutiveUseCount = consecutiveUseCount;
|
|
129
133
|
|
|
130
134
|
if (
|
|
131
135
|
shouldMergeProviderSpecificData(
|