@amir83nasr/10router 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +6 -6
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +23 -23
- package/.next/react-loadable-manifest.json +9 -0
- package/.next/server/app/(dashboard)/dashboard/basic-chat/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/basic-chat/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js +2 -2
- package/.next/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/cli-tools/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/combos/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/combos/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/console-log/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/console-log/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/endpoint/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/endpoint/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/[id]/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/[id]/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/media-providers/systemone/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/profile/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/profile/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/[id]/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/[id]/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/new/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/new/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/proxy-pools/page.js +2 -2
- package/.next/server/app/(dashboard)/dashboard/proxy-pools/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/pxpipe/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/pxpipe/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/pxpipe/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/quota/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/quota/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/token-saver/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/token-saver/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/token-saver/page_client-reference-manifest.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/usage/page.js +1 -1
- package/.next/server/app/(dashboard)/dashboard/usage/page.js.nft.json +1 -1
- package/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +5 -5
- package/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/cli-tools/all-statuses/route.js +1 -1
- package/.next/server/app/api/cli-tools/claude-settings/route.js +1 -1
- package/.next/server/app/api/cli-tools/codex-settings/route.js +1 -1
- package/.next/server/app/api/cli-tools/cowork-mcp-registry/route.js +1 -1
- package/.next/server/app/api/cli-tools/cowork-mcp-tools/route.js +1 -1
- package/.next/server/app/api/cli-tools/cowork-settings/route.js +1 -1
- package/.next/server/app/api/cli-tools/dsh-settings/route.js +1 -1
- package/.next/server/app/api/cli-tools/opencode-settings/route.js +1 -1
- package/.next/server/app/api/combos/[id]/route.js +1 -1
- package/.next/server/app/api/combos/route.js +1 -1
- package/.next/server/app/api/oauth/[provider]/[action]/route.js +1 -1
- package/.next/server/app/api/oauth/codex/bulk-import/route.js +1 -1
- package/.next/server/app/api/oauth/codex/import-token/route.js +1 -1
- package/.next/server/app/api/providers/[id]/models/route.js +1 -1
- package/.next/server/app/api/providers/client/route.js +1 -1
- package/.next/server/app/api/providers/route.js +1 -1
- package/.next/server/app/api/translator/send/route.js +1 -1
- package/.next/server/app/api/translator/translate/route.js +1 -1
- package/.next/server/app/api/tunnel/tailscale-install/route.js +2 -2
- package/.next/server/app/api/usage/[connectionId]/codex-reset-credits/route.js +1 -1
- package/.next/server/app/api/usage/[connectionId]/route.js +1 -1
- package/.next/server/app/api/usage/stream/route.js +2 -6
- package/.next/server/app/api/v1/audio/speech/route.js +1 -1
- package/.next/server/app/api/v1/audio/transcriptions/route.js +1 -1
- package/.next/server/app/api/v1/embeddings/route.js +1 -1
- package/.next/server/app/api/v1/images/generations/route.js +1 -1
- package/.next/server/app/api/v1/systemone/route.js +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/callback/page_client-reference-manifest.js +1 -1
- package/.next/server/app/callback.html +1 -1
- package/.next/server/app/callback.rsc +6 -6
- package/.next/server/app/callback.segments/_full.segment.rsc +6 -6
- package/.next/server/app/callback.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/callback.segments/callback/__PAGE__.segment.rsc +6 -6
- package/.next/server/app/dashboard/basic-chat.html +1 -1
- package/.next/server/app/dashboard/basic-chat.rsc +7 -7
- package/.next/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/basic-chat.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/basic-chat.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/cli-tools.html +1 -1
- package/.next/server/app/dashboard/cli-tools.rsc +7 -7
- package/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/cli-tools.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/combos.html +1 -1
- package/.next/server/app/dashboard/combos.rsc +7 -7
- package/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/combos.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/combos.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/endpoint.html +1 -1
- package/.next/server/app/dashboard/endpoint.rsc +7 -7
- package/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/endpoint.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/endpoint.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/media-providers/systemone.html +1 -1
- package/.next/server/app/dashboard/media-providers/systemone.rsc +7 -7
- package/.next/server/app/dashboard/media-providers/systemone.segments/!KGRhc2hib2FyZCk/dashboard/media-providers/systemone/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/media-providers/systemone.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/media-providers/systemone.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/profile.html +1 -1
- package/.next/server/app/dashboard/profile.rsc +7 -7
- package/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/profile.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/profile.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/providers/new.html +1 -1
- package/.next/server/app/dashboard/providers/new.rsc +7 -7
- package/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/providers/new.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/providers/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/providers.html +1 -1
- package/.next/server/app/dashboard/providers.rsc +7 -7
- package/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/providers.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/providers.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/proxy-pools.html +1 -1
- package/.next/server/app/dashboard/proxy-pools.rsc +7 -7
- package/.next/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/proxy-pools.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/pxpipe.html +1 -1
- package/.next/server/app/dashboard/pxpipe.rsc +7 -7
- package/.next/server/app/dashboard/pxpipe.segments/!KGRhc2hib2FyZCk/dashboard/pxpipe/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/pxpipe.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/pxpipe.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/quota.html +2 -2
- package/.next/server/app/dashboard/quota.rsc +8 -8
- package/.next/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +8 -8
- package/.next/server/app/dashboard/quota.segments/_full.segment.rsc +8 -8
- package/.next/server/app/dashboard/quota.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
- package/.next/server/app/dashboard/settings/pricing.html +1 -1
- package/.next/server/app/dashboard/settings/pricing.rsc +6 -6
- package/.next/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +6 -6
- package/.next/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +6 -6
- package/.next/server/app/dashboard/token-saver.html +1 -1
- package/.next/server/app/dashboard/token-saver.rsc +6 -6
- package/.next/server/app/dashboard/token-saver.segments/!KGRhc2hib2FyZCk/dashboard/token-saver/__PAGE__.segment.rsc +6 -6
- package/.next/server/app/dashboard/token-saver.segments/_full.segment.rsc +6 -6
- package/.next/server/app/dashboard/token-saver.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard/usage.html +2 -2
- package/.next/server/app/dashboard/usage.rsc +7 -7
- package/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard/usage.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard/usage.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/dashboard.html +1 -1
- package/.next/server/app/dashboard.rsc +7 -7
- package/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +7 -7
- package/.next/server/app/dashboard.segments/_full.segment.rsc +7 -7
- package/.next/server/app/dashboard.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/landing/page_client-reference-manifest.js +1 -1
- package/.next/server/app/landing.html +1 -1
- package/.next/server/app/landing.rsc +6 -6
- package/.next/server/app/landing.segments/_full.segment.rsc +6 -6
- package/.next/server/app/landing.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/landing.segments/landing/__PAGE__.segment.rsc +6 -6
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +5 -5
- package/.next/server/app/login.segments/_full.segment.rsc +5 -5
- package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +6 -6
- package/.next/server/chunks/1003.js +1 -1
- package/.next/server/chunks/1011.js +1 -1
- package/.next/server/chunks/1194.js +1 -1
- package/.next/server/chunks/1289.js +1 -1
- package/.next/server/chunks/1412.js +1 -1
- package/.next/server/chunks/1633.js +1 -1
- package/.next/server/chunks/1985.js +1 -1
- package/.next/server/chunks/2282.js +8 -4
- package/.next/server/chunks/2339.js +1 -1
- package/.next/server/chunks/2801.js +1 -1
- package/.next/server/chunks/3425.js +1 -1
- package/.next/server/chunks/4364.js +1 -1
- package/.next/server/chunks/460.js +1 -1
- package/.next/server/chunks/5761.js +1 -1
- package/.next/server/chunks/6056.js +1 -1
- package/.next/server/chunks/6343.js +3 -3
- package/.next/server/chunks/6823.js +1 -1
- package/.next/server/chunks/7019.js +2 -2
- package/.next/server/chunks/7348.js +1 -1
- package/.next/server/chunks/7504.js +1 -1
- package/.next/server/chunks/7535.js +2 -2
- package/.next/server/chunks/8923.js +1 -1
- package/.next/server/chunks/9018.js +1 -1
- package/.next/server/chunks/9250.js +1 -1
- package/.next/server/chunks/9429.js +1 -1
- package/.next/server/chunks/9646.js +1 -1
- package/.next/server/chunks/9817.js +67 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/middleware.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{1278-605cc36f762c2ea7.js → 1278-efb46cc82262aa4e.js} +1 -1
- package/.next/static/chunks/{1471-69a7c30abf98c656.js → 1471-10f225f62f7469cc.js} +1 -1
- package/.next/static/chunks/{1699-49be310981e2759e.js → 1699-13ee6864a8a31a96.js} +2 -2
- package/.next/static/chunks/{1830-901fe48c3c62604f.js → 1830-d26d77c95932d7ce.js} +1 -1
- package/.next/static/chunks/1940.1f30124180f22498.js +1 -0
- package/.next/static/chunks/{2836-8e7f4c382297ac97.js → 2836-d97f0aa2718945d5.js} +1 -1
- package/.next/static/chunks/3445-99e12528f235cedb.js +1 -0
- package/.next/static/chunks/{3577-1f02993330d7fba7.js → 3577-cd98d40e3099e1b0.js} +1 -1
- package/.next/static/chunks/{5862-6f3a3a6d4c00100d.js → 5862-f98c2f3d480b80d2.js} +1 -1
- package/.next/static/chunks/6770-e2921b2ce695ed57.js +1 -0
- package/.next/static/chunks/7276-a15e4d56ec28aba8.js +1 -0
- package/.next/static/chunks/{9369-7ef21480b9132957.js → 9369-0d43b295968c2168.js} +1 -1
- package/.next/static/chunks/{9ce8c186-6cd204e9c27ecee1.js → 9ce8c186-3c7d7dbc5d428052.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/cli-tools/[toolId]/{page-9efe55fed2f7c81f.js → page-e8bceccb95695755.js} +2 -2
- package/.next/static/chunks/app/(dashboard)/dashboard/cli-tools/{page-9dcb29085167b384.js → page-adee408d9de02423.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/combos/{page-f5db9f681c6bface.js → page-d4c1e29e8d038f09.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/console-log/{page-d134cc313c9fa0b8.js → page-139200833f7a4ecc.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/endpoint/{page-f2d1a733e7f841cb.js → page-67516b120adbc4d7.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/media-providers/systemone/[id]/{page-0d6e623de6134598.js → page-b751d9603cb82e62.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/{page-e8f1d8be5e294fb0.js → page-315c9f52c472a700.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/providers/[id]/{page-c96cbcc4f5736e36.js → page-d5d480e209fdc55a.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/providers/new/{page-cc51346e464158b1.js → page-61b87a31ba21a055.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/providers/{page-1ee147fbe0693dc1.js → page-2396571281d02804.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/proxy-pools/{page-2287b1915fa887fc.js → page-cfd6bcb7e55d7d18.js} +2 -2
- package/.next/static/chunks/app/(dashboard)/dashboard/pxpipe/{page-696cb5c2703ca1ec.js → page-4d758f53c8cfc308.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/quota/{page-54a329d725734a1f.js → page-fef6edb7aafd8bd0.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/dashboard/usage/{page-a81f78bb43042c30.js → page-697f46461825d7fc.js} +1 -1
- package/.next/static/chunks/app/(dashboard)/{layout-c199eb2ad56ae96a.js → layout-5039502fc12707c9.js} +1 -1
- package/.next/static/chunks/app/{layout-9bd5877ae6198856.js → layout-8810d4df3081b5df.js} +1 -1
- package/.next/static/chunks/webpack-cb89101193742e2b.js +1 -0
- package/CHANGELOG.md +19 -0
- package/open-sse/executors/zed.js +98 -5
- package/open-sse/handlers/chatCore/sseToJsonHandler.js +11 -4
- package/open-sse/providers/pricing.js +24 -10
- package/open-sse/services/usage/antigravity-weekly.js +10 -0
- package/open-sse/services/usage/claude.js +15 -0
- package/open-sse/shared/zedAuth.js +43 -2
- package/open-sse/translator/concerns/prefetch.js +12 -4
- package/open-sse/translator/response/claude-to-openai.js +2 -2
- package/open-sse/utils/streamHandler.js +1 -5
- package/package.json +1 -1
- package/src/app/(dashboard)/dashboard/providers/[id]/BulkImportGrokCliModal.tsx +1 -1
- package/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +1 -1
- package/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.tsx +5 -2
- package/src/app/api/combos/[id]/route.js +38 -1
- package/src/app/api/combos/route.js +27 -2
- package/src/app/api/providers/[id]/models/route.js +12 -1
- package/src/app/api/providers/route.js +4 -1
- package/src/app/api/usage/stream/route.js +74 -68
- package/src/app/api/v1/models/route.js +20 -6
- package/src/lib/db/index.js +3 -0
- package/src/lib/db/repos/settingsRepo.js +22 -1
- package/src/lib/oauth/services/xai.js +4 -4
- package/src/lib/validateComboModels.ts +48 -0
- package/src/shared/components/UsageStats.tsx +7 -1
- package/src/shared/hooks/useShutdown.ts +2 -4
- package/src/sse/handlers/chat.js +5 -1
- package/src/sse/services/antigravityQuota.ts +7 -3
- package/src/sse/services/model.ts +9 -20
- package/.next/server/chunks/5351.js +0 -67
- package/.next/static/chunks/3973-424848b943e2ecab.js +0 -1
- package/.next/static/chunks/7276-16ef14f7b2cd478d.js +0 -1
- package/.next/static/chunks/webpack-452a2f4aec1a53ee.js +0 -1
- /package/.next/static/{iz7psrsEuHZGPvj7rbSWc → awjDWqWCZbU3VVSuN7-oA}/_buildManifest.js +0 -0
- /package/.next/static/{iz7psrsEuHZGPvj7rbSWc → awjDWqWCZbU3VVSuN7-oA}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7177],{1352:(e,t,n)=>{"use strict";n.d(t,{Bc:()=>i,ZI:()=>l,k$:()=>s,m_:()=>d});var o=n(29798);n(15814);var r=n(5471),a=n(44484);function i({delayDuration:e=0,...t}){return(0,o.jsx)(a.Kq,{"data-slot":"tooltip-provider",delayDuration:e,...t})}function d({...e}){return(0,o.jsx)(a.bL,{"data-slot":"tooltip",...e})}function s({...e}){return(0,o.jsx)(a.l9,{"data-slot":"tooltip-trigger",...e})}function l({className:e,sideOffset:t=0,children:n,...i}){return(0,o.jsx)(a.ZL,{children:(0,o.jsxs)(a.UC,{"data-slot":"tooltip-content",sideOffset:t,className:(0,r.cn)("z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...i,children:[n,(0,o.jsx)(a.i3,{className:"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground"})]})})}},8872:(e,t,n)=>{Promise.resolve().then(n.bind(n,48565)),Promise.resolve().then(n.bind(n,1693)),Promise.resolve().then(n.bind(n,4780)),Promise.resolve().then(n.t.bind(n,52760,23)),Promise.resolve().then(n.t.bind(n,10414,23)),Promise.resolve().then(n.t.bind(n,65262,23)),Promise.resolve().then(n.t.bind(n,63185,23)),Promise.resolve().then(n.bind(n,84885)),Promise.resolve().then(n.bind(n,33526))},33526:(e,t,n)=>{"use strict";n.d(t,{ThemeProvider:()=>d});var o=n(29798),r=n(15814),a=n(88739),i=n(1352);function d({children:e}){let{initTheme:t}=(0,a.A)();return(0,r.useEffect)(()=>{t()},[t]),(0,o.jsx)(i.Bc,{children:e})}},51660:(e,t,n)=>{"use strict";n.d(t,{Q2:()=>o.Q2,fg:()=>o.fg,vQ:()=>r,_$:()=>i,MA:()=>o.MA,UY:()=>s,Nu:()=>a,zN:()=>o.zN,B2:()=>d});var o=n(13794);n(80159);let r={name:"10Router Proxy",description:"AI Infrastructure Management",version:"0.1.
|
|
1
|
+
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7177],{1352:(e,t,n)=>{"use strict";n.d(t,{Bc:()=>i,ZI:()=>l,k$:()=>s,m_:()=>d});var o=n(29798);n(15814);var r=n(5471),a=n(44484);function i({delayDuration:e=0,...t}){return(0,o.jsx)(a.Kq,{"data-slot":"tooltip-provider",delayDuration:e,...t})}function d({...e}){return(0,o.jsx)(a.bL,{"data-slot":"tooltip",...e})}function s({...e}){return(0,o.jsx)(a.l9,{"data-slot":"tooltip-trigger",...e})}function l({className:e,sideOffset:t=0,children:n,...i}){return(0,o.jsx)(a.ZL,{children:(0,o.jsxs)(a.UC,{"data-slot":"tooltip-content",sideOffset:t,className:(0,r.cn)("z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...i,children:[n,(0,o.jsx)(a.i3,{className:"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground"})]})})}},8872:(e,t,n)=>{Promise.resolve().then(n.bind(n,48565)),Promise.resolve().then(n.bind(n,1693)),Promise.resolve().then(n.bind(n,4780)),Promise.resolve().then(n.t.bind(n,52760,23)),Promise.resolve().then(n.t.bind(n,10414,23)),Promise.resolve().then(n.t.bind(n,65262,23)),Promise.resolve().then(n.t.bind(n,63185,23)),Promise.resolve().then(n.bind(n,84885)),Promise.resolve().then(n.bind(n,33526))},33526:(e,t,n)=>{"use strict";n.d(t,{ThemeProvider:()=>d});var o=n(29798),r=n(15814),a=n(88739),i=n(1352);function d({children:e}){let{initTheme:t}=(0,a.A)();return(0,r.useEffect)(()=>{t()},[t]),(0,o.jsx)(i.Bc,{children:e})}},51660:(e,t,n)=>{"use strict";n.d(t,{Q2:()=>o.Q2,fg:()=>o.fg,vQ:()=>r,_$:()=>i,MA:()=>o.MA,UY:()=>s,Nu:()=>a,zN:()=>o.zN,B2:()=>d});var o=n(13794);n(80159);let r={name:"10Router Proxy",description:"AI Infrastructure Management",version:"0.1.9"},a={changelogUrl:"/api/changelog"},i=20128,d={storageKey:"theme",defaultTheme:"light"},s={maxLines:500,pollIntervalMs:1e3}},51780:(e,t,n)=>{"use strict";n.d(t,{FE:()=>p,Tl:()=>u,kk:()=>c,oQ:()=>m,wn:()=>g});var o=n(66454);let r={},a=o.Xn,i=[];function d(){if("u"<typeof document)return o.Xn;let e=document.cookie.split(";").map(e=>e.trim()).find(e=>e.startsWith(`${o.CL}=`)),t=e?decodeURIComponent(e.split("=").slice(1).join("=")):o.Xn;return(0,o.QC)(t)}function s(e){"u">typeof document&&(document.documentElement.lang=e,document.documentElement.dir="fa"===e?"rtl":"ltr")}async function l(e){if("en"===e){r={};return}try{let t=await fetch(`/i18n/literals/${e}.json`);r=await t.json()}catch(e){console.error("Failed to load translations:",e),r={}}}function u(e){if(!e||"string"!=typeof e)return e;let t=e.trim();return t&&"en"!==a&&r[t]||e}function c(){return a}function m(e){return i.push(e),()=>{i=i.filter(t=>t!==e)}}function f(e){if(!e.nodeValue||!e.nodeValue.trim())return;let t=e.parentElement;if(!t)return;let n=t;for(;n;){if(n.hasAttribute&&n.hasAttribute("data-i18n-skip"))return;n=n.parentElement}if(["script","style","code","pre","colgroup","table","thead","tbody","tfoot","tr","select","datalist","optgroup"].includes(t.tagName?.toLowerCase()))return;e._originalText||(e._originalText=e.nodeValue);let o=u(e._originalText);o!==e.nodeValue&&(e.nodeValue=o)}function h(e){let t;if(!e)return;let n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null),o=[];for(;t=n.nextNode();)o.push(t);o.forEach(f)}async function p(){s(a=d()),await l(a),h(document.body),new MutationObserver(e=>{e.forEach(e=>{e.addedNodes.forEach(e=>{e.nodeType===Node.ELEMENT_NODE?h(e):e.nodeType===Node.TEXT_NODE&&f(e)})})}).observe(document.body,{childList:!0,subtree:!0})}async function g(){s(a=d()),await l(a),i.forEach(e=>e()),h(document.body)}},63185:()=>{},66454:(e,t,n)=>{"use strict";n.d(t,{CL:()=>a,QC:()=>i,Xn:()=>r,YZ:()=>o});let o=["en","fa"],r="en",a="locale";function i(e){return"fa"===e?"fa":r}},84885:(e,t,n)=>{"use strict";n.d(t,{RuntimeI18nProvider:()=>d});var o=n(29798),r=n(15814),a=n(70782),i=n(51780);function d({children:e}){let t=(0,a.usePathname)();return(0,r.useEffect)(()=>{(0,i.FE)()},[]),(0,r.useEffect)(()=>{t&&requestAnimationFrame(()=>{requestAnimationFrame(()=>{(0,i.wn)()})})},[t]),(0,o.jsx)(o.Fragment,{children:e})}},88739:(e,t,n)=>{"use strict";n.d(t,{A:()=>d});var o=n(58013),r=n(47353),a=n(51660);function i(){document.documentElement.classList.remove("dark")}let d=(0,o.v)()((0,r.Zr)(e=>({theme:"light",setTheme:()=>{e({theme:"light"}),i()},toggleTheme:()=>{e({theme:"light"}),i()},initTheme:()=>{e({theme:"light"}),i()}}),{name:a.B2.storageKey}))}},e=>{e.O(0,[165,2262,5166,7552,9980,4484,7287,159,427,9966,7358],()=>e(e.s=8872)),_N_E=e.O()}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e,t,r,o,n,a,i,l,s,u={},c={};function d(e){var t=c[e];if(void 0!==t)return t.exports;var r=c[e]={exports:{}},o=!0;try{u[e].call(r.exports,r,r.exports,d),o=!1}finally{o&&delete c[e]}return r.exports}d.m=u,e=[],d.O=(t,r,o,n)=>{if(r){n=n||0;for(var a=e.length;a>0&&e[a-1][2]>n;a--)e[a]=e[a-1];e[a]=[r,o,n];return}for(var i=1/0,a=0;a<e.length;a++){for(var[r,o,n]=e[a],l=!0,s=0;s<r.length;s++)(!1&n||i>=n)&&Object.keys(d.O).every(e=>d.O[e](r[s]))?r.splice(s--,1):(l=!1,n<i&&(i=n));if(l){e.splice(a--,1);var u=o();void 0!==u&&(t=u)}}return t},d.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return d.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,d.t=function(e,o){if(1&o&&(e=this(e)),8&o||"object"==typeof e&&e&&(4&o&&e.__esModule||16&o&&"function"==typeof e.then))return e;var n=Object.create(null);d.r(n);var a={};t=t||[null,r({}),r([]),r(r)];for(var i=2&o&&e;"object"==typeof i&&!~t.indexOf(i);i=r(i))Object.getOwnPropertyNames(i).forEach(t=>a[t]=()=>e[t]);return a.default=()=>e,d.d(n,a),n},d.d=(e,t)=>{for(var r in t)d.o(t,r)&&!d.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},d.f={},d.e=e=>Promise.all(Object.keys(d.f).reduce((t,r)=>(d.f[r](e,t),t),[])),d.u=e=>3445===e?"static/chunks/3445-99e12528f235cedb.js":7276===e?"static/chunks/7276-a15e4d56ec28aba8.js":"static/chunks/"+(6381===e?"80d4b624":e)+"."+({1940:"1f30124180f22498",4899:"0d4260af2a463837",6381:"42a8767f1c8767a3",7822:"01a4d5bdd39fa0b7"})[e]+".js",d.miniCssF=e=>"static/css/e07f2bf4fa519943.css",d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o={},d.l=(e,t,r,n)=>{if(o[e])return void o[e].push(t);if(void 0!==r)for(var a,i,l=document.getElementsByTagName("script"),s=0;s<l.length;s++){var u=l[s];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")=="_N_E:"+r){a=u;break}}a||(i=!0,(a=document.createElement("script")).charset="utf-8",a.timeout=120,d.nc&&a.setAttribute("nonce",d.nc),a.setAttribute("data-webpack","_N_E:"+r),a.src=d.tu(e)),o[e]=[t];var c=(t,r)=>{a.onerror=a.onload=null,clearTimeout(f);var n=o[e];if(delete o[e],a.parentNode&&a.parentNode.removeChild(a),n&&n.forEach(e=>e(r)),t)return t(r)},f=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),i&&document.head.appendChild(a)},d.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.tt=()=>(void 0===n&&(n={createScriptURL:e=>e},"u">typeof trustedTypes&&trustedTypes.createPolicy&&(n=trustedTypes.createPolicy("nextjs#bundler",n))),n),d.tu=e=>d.tt().createScriptURL(e),d.p="/_next/",a={8068:0},d.f.miniCss=(e,t)=>{if(a[e])t.push(a[e]);else 0!==a[e]&&({670:1})[e]&&t.push(a[e]=new Promise((t,r)=>{var o=d.miniCssF(e),n=d.p+o;if(((e,t)=>{for(var r=document.getElementsByTagName("link"),o=0;o<r.length;o++){var n=r[o],a=n.getAttribute("data-href")||n.getAttribute("href");if("stylesheet"===n.rel&&(a===e||a===t))return n}for(var i=document.getElementsByTagName("style"),o=0;o<i.length;o++){var n=i[o],a=n.getAttribute("data-href");if(a===e||a===t)return n}})(o,n))return t();((e,t,r,o)=>{var n=document.createElement("link");n.rel="stylesheet",n.type="text/css",n.onerror=n.onload=a=>{if(n.onerror=n.onload=null,"load"===a.type)r();else{var i=a&&("load"===a.type?"missing":a.type),l=a&&a.target&&a.target.href||t,s=Error("Loading CSS chunk "+e+" failed.\n("+l+")");s.code="CSS_CHUNK_LOAD_FAILED",s.type=i,s.request=l,n.parentNode.removeChild(n),o(s)}},n.href=t;if("function"==typeof _N_E_STYLE_LOAD){var a=n.href,i=n.onload,l=n.onerror;_N_E_STYLE_LOAD(0===a.indexOf(window.location.origin)?new URL(a).pathname:a).then(function(){i&&i.call(n,{type:"load"})},function(){l&&l.call(n,{})})}else document.head.appendChild(n)})(e,n,t,r)}).then(()=>{a[e]=0},t=>{throw delete a[e],t}))},i={8068:0,165:0,2262:0},d.f.j=(e,t)=>{var r=d.o(i,e)?i[e]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(165|2262|670|8068)$/.test(e))i[e]=0;else{var o=new Promise((t,o)=>r=i[e]=[t,o]);t.push(r[2]=o);var n=d.p+d.u(e),a=Error();d.l(n,t=>{if(d.o(i,e)&&(0!==(r=i[e])&&(i[e]=void 0),r)){var o=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;a.message="Loading chunk "+e+" failed.\n("+o+": "+n+")",a.name="ChunkLoadError",a.type=o,a.request=n,r[1](a)}},"chunk-"+e,e)}},d.O.j=e=>0===i[e],l=(e,t)=>{var r,o,[n,a,l]=t,s=0;if(n.some(e=>0!==i[e])){for(r in a)d.o(a,r)&&(d.m[r]=a[r]);if(l)var u=l(d)}for(e&&e(t);s<n.length;s++)o=n[s],d.o(i,o)&&i[o]&&i[o][0](),i[o]=0;return d.O(u)},(s=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(l.bind(null,0)),s.push=l.bind(null,s.push.bind(s)),d.nc=void 0})();
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to 10Router will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.9] — 2026-10-17
|
|
6
|
+
|
|
7
|
+
- Zed: detect context-window overflow reported as an in-stream `status:failed` frame and return it as a request-scoped `400 context_length_exceeded` instead of assistant text — the account is no longer cooled down by the fallback layer. History is trimmed to the model's catalog limit before sending, and a pre-send guard fails fast when even the trimmed body does not fit.
|
|
8
|
+
- Zed: retry transient network failures (`ECONNRESET`/`ETIMEDOUT`) on auth and completion fetches using the shared 502 retry config; caller aborts are never retried.
|
|
9
|
+
- Combos: validate members on create/update (`POST`/`PUT /api/combos`) — bare words from a bad paste, duplicates, self-references, and unknown names are rejected at save time with a message naming the offending entries, instead of failing every run at request time.
|
|
10
|
+
- Zed: model-list and usage resolvers now honour the connection's proxy pool instead of falling back to a direct call.
|
|
11
|
+
- Claude → OpenAI: stop emitting literal `<think>`/`</think>` markers into `delta.content` — thinking text streams as `reasoning_content` only.
|
|
12
|
+
- Fix: opencode model-list URL for Anthropic-compatible providers built `.../v1/messag` (`slice(0, -9)` ate a character); now sliced by `"/messages".length`.
|
|
13
|
+
- Fix: xAI OAuth poll interval was never cleared on the 5-minute auth timeout, leaking a 100 ms timer.
|
|
14
|
+
- Fix: `useShutdown` called `setState` inside an effect (cascading render); the transition now happens where the poll completes.
|
|
15
|
+
- Perf: `/api/usage/stream` ran the full usage-stats recompute once per open tab per event; all subscribers now share one cache and one in-flight recompute, so N tabs cost the same as one.
|
|
16
|
+
- Perf: `matchPattern` recompiled a `RegExp` on every call inside ~100-entry pricing/capability loops; patterns are now memoized.
|
|
17
|
+
- Perf: settings were read from the DB and re-merged against ~40 defaults three times per chat request; added a short-TTL cache with write invalidation.
|
|
18
|
+
- Perf: remote-image prefetch for base64-only targets now fetches in parallel instead of serially in the pre-first-byte path.
|
|
19
|
+
- Perf: custom provider-node prefix resolution did three identical type-filtered table scans per request; now one scan.
|
|
20
|
+
- Fix: token-keyed Claude/Antigravity usage caches never evicted retired tokens; added unref'd sweepers.
|
|
21
|
+
- Fix: silently swallowed failures now log — background projectId persistence and provider-node name lookup.
|
|
22
|
+
- Types: resolved 11 pre-existing `tsc --noEmit` errors under `src/` (stale generated `.next/types` route-tag warnings remain, covered by `ignoreBuildErrors`).
|
|
23
|
+
|
|
5
24
|
## [0.1.8] — 2026-10-15
|
|
6
25
|
|
|
7
26
|
- Deps: drop `better-sqlite3` — fixes `npm i -g` install-script warning (`allowScripts`) and the deprecated `prebuild-install` warning. DB chain is now built-in `node:sqlite` (≥22.5) → `sql.js` (WASM fallback); the adapter loads `better-sqlite3` lazily as a manual opt-in only, so the Next.js build no longer resolves it.
|
|
@@ -24,6 +24,8 @@ import { claudeToOpenAIResponse } from "../translator/response/claude-to-openai.
|
|
|
24
24
|
import { geminiToOpenAIResponse } from "../translator/response/gemini-to-openai.js";
|
|
25
25
|
import { openaiResponsesToOpenAIResponse } from "../translator/response/openai-responses.js";
|
|
26
26
|
import { ZED_HEADERS, resolveZedModels, zedLlmFetch } from "../shared/zedAuth.js";
|
|
27
|
+
import { estimateInputTokens } from "../utils/usageTracking.js";
|
|
28
|
+
import { stripHistoryForContext } from "../services/capacityAdapter.js";
|
|
27
29
|
|
|
28
30
|
// Wire values for the `provider` field of POST /completions. These are NOT
|
|
29
31
|
// display names: cloud.zed.dev matches them exactly, and an unrecognized value
|
|
@@ -139,6 +141,29 @@ function normalizeStatus(status) {
|
|
|
139
141
|
return null;
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
// Zed reports context overflow inside a `status:failed` frame on an HTTP-200
|
|
145
|
+
// stream (and occasionally as a non-200 body) — never as a typed API error.
|
|
146
|
+
// Detect it by wording so both paths below can treat it as request-scoped.
|
|
147
|
+
const OVERFLOW_RE =
|
|
148
|
+
/context_length_exceeded|\bcontext\b.*\b(window|length|limit)\b|exceeds.*token|too many tokens|prompt (is )?too long|maximum.*token/i;
|
|
149
|
+
|
|
150
|
+
function isContextOverflowText(value) {
|
|
151
|
+
return OVERFLOW_RE.test(String(value || ""));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// SSE error frame in the shared buildErrorBody shape ({error:{message,type,
|
|
155
|
+
// code}}) so stream aggregation and clients treat it as a failure, not as
|
|
156
|
+
// assistant text. Never a fabricated finish_reason.
|
|
157
|
+
function createOverflowErrorFrame(message) {
|
|
158
|
+
return `data: ${JSON.stringify({
|
|
159
|
+
error: {
|
|
160
|
+
message,
|
|
161
|
+
type: "invalid_request_error",
|
|
162
|
+
code: "context_length_exceeded",
|
|
163
|
+
},
|
|
164
|
+
})}\n\n`;
|
|
165
|
+
}
|
|
166
|
+
|
|
142
167
|
function wrapZedCompletionStream(response, provider, model) {
|
|
143
168
|
if (!response.ok || !response.body) return response;
|
|
144
169
|
|
|
@@ -169,8 +194,24 @@ function wrapZedCompletionStream(response, provider, model) {
|
|
|
169
194
|
if (status?.type === "failed" || status?.failed) {
|
|
170
195
|
const failed = status.failed || status;
|
|
171
196
|
const message = String(failed.message || failed.error || failed.code || "request failed");
|
|
172
|
-
|
|
173
|
-
|
|
197
|
+
// Context overflow must be a clean error, not assistant text:
|
|
198
|
+
// clients aggregate delta.content into the answer, and the fallback
|
|
199
|
+
// layer keys off status — so emit the error frame + [DONE] with no
|
|
200
|
+
// fabricated finish_reason, and skip the trailing translate flush.
|
|
201
|
+
if (isContextOverflowText(message)) {
|
|
202
|
+
controller.enqueue(
|
|
203
|
+
encoder.encode(
|
|
204
|
+
createOverflowErrorFrame(
|
|
205
|
+
`Context window exceeded for ${model}: ${message}. ` +
|
|
206
|
+
`Shorten the conversation or use a larger model.`,
|
|
207
|
+
) + "data: [DONE]\n\n",
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
done = true;
|
|
211
|
+
} else {
|
|
212
|
+
enqueueSseObject(controller, encoder, createErrorChunk(model, message));
|
|
213
|
+
finish(controller);
|
|
214
|
+
}
|
|
174
215
|
} else if (status?.type === "stream_ended" || status === "stream_ended") {
|
|
175
216
|
finish(controller);
|
|
176
217
|
}
|
|
@@ -243,9 +284,52 @@ class ZedExecutor extends BaseExecutor {
|
|
|
243
284
|
}
|
|
244
285
|
|
|
245
286
|
async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
|
|
246
|
-
const { provider } = await this.resolveModel(
|
|
247
|
-
|
|
248
|
-
|
|
287
|
+
const { raw, provider } = await this.resolveModel(
|
|
288
|
+
model,
|
|
289
|
+
credentials,
|
|
290
|
+
signal,
|
|
291
|
+
log,
|
|
292
|
+
proxyOptions,
|
|
293
|
+
);
|
|
294
|
+
let requestBody = body || {};
|
|
295
|
+
// Pre-send context guard: Zed reports overflow as an in-stream
|
|
296
|
+
// `status:failed` frame (HTTP 200 already sent), which can only surface
|
|
297
|
+
// as assistant text — never as a clean error. Trim history to the
|
|
298
|
+
// catalog window first; fail fast with 400 (request-scoped, no account
|
|
299
|
+
// lock) when even the trimmed body doesn't fit.
|
|
300
|
+
const contextLimit = Number(raw?.max_token_count ?? raw?.maxTokenCount) || null;
|
|
301
|
+
if (contextLimit) {
|
|
302
|
+
if (estimateInputTokens(requestBody) > contextLimit) {
|
|
303
|
+
const trimmed = stripHistoryForContext(requestBody, contextLimit);
|
|
304
|
+
if (trimmed !== requestBody) {
|
|
305
|
+
log?.info?.("ZED", `trimmed history for ${model} to fit ${contextLimit} tokens`);
|
|
306
|
+
requestBody = trimmed;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const estimated = estimateInputTokens(requestBody);
|
|
310
|
+
if (estimated > contextLimit) {
|
|
311
|
+
const message =
|
|
312
|
+
`Context window exceeded: ~${estimated} tokens exceeds the limit of ` +
|
|
313
|
+
`${contextLimit} tokens for ${model}. Shorten the conversation or use a larger model.`;
|
|
314
|
+
return {
|
|
315
|
+
response: new Response(
|
|
316
|
+
JSON.stringify({
|
|
317
|
+
error: {
|
|
318
|
+
message,
|
|
319
|
+
type: "invalid_request_error",
|
|
320
|
+
code: "context_length_exceeded",
|
|
321
|
+
},
|
|
322
|
+
}),
|
|
323
|
+
{ status: 400, headers: { "Content-Type": "application/json" } },
|
|
324
|
+
),
|
|
325
|
+
url: `${this.config?.llmBaseUrl || "https://cloud.zed.dev"}/completions`,
|
|
326
|
+
headers: { "Content-Type": "application/json", Authorization: "Bearer <zed-llm-token>" },
|
|
327
|
+
transformedBody: { model, estimatedInputTokens: estimated, contextLimit },
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const providerRequest = buildProviderRequest(provider, model, requestBody, stream, credentials);
|
|
332
|
+
const bodyRecord = requestBody;
|
|
249
333
|
const payload = {
|
|
250
334
|
thread_id: bodyRecord.thread_id || credentials?._clientSessionId,
|
|
251
335
|
prompt_id: bodyRecord.prompt_id,
|
|
@@ -292,6 +376,15 @@ class ZedExecutor extends BaseExecutor {
|
|
|
292
376
|
const errorObj = parsed?.error || undefined;
|
|
293
377
|
const code = parsed?.code || errorObj?.code || "";
|
|
294
378
|
const rawMessage = parsed?.message || errorObj?.message || bodyText || response.statusText;
|
|
379
|
+
// Overflow is request-scoped: return it as-is (no proxy/lock guidance, no
|
|
380
|
+
// retry wording) so the fallback layer hands it back without cooling the
|
|
381
|
+
// account. checkFallbackError already treats non-quota 400s this way.
|
|
382
|
+
if (code === "context_length_exceeded" || isContextOverflowText(rawMessage)) {
|
|
383
|
+
return {
|
|
384
|
+
status: 400,
|
|
385
|
+
message: `Context window exceeded for this request: ${rawMessage}. Shorten the conversation or use a larger model.`,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
295
388
|
if (code === "trial_blocked") {
|
|
296
389
|
return {
|
|
297
390
|
status: response.status,
|
|
@@ -413,10 +413,17 @@ export async function handleForcedSSEToJson({
|
|
|
413
413
|
"Invalid SSE response for non-streaming request",
|
|
414
414
|
);
|
|
415
415
|
if (parsed.error) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
);
|
|
416
|
+
// Request-scoped errors (e.g. Zed context_length_exceeded, surfaced as an
|
|
417
|
+
// in-stream error frame) keep their own status: 400 must not cool the
|
|
418
|
+
// account via the fallback layer, and must reach the client as 400.
|
|
419
|
+
const upstreamCode = String(parsed.error.code || "");
|
|
420
|
+
const status =
|
|
421
|
+
parsed.error.type === "invalid_request_error" ||
|
|
422
|
+
upstreamCode === "context_length_exceeded" ||
|
|
423
|
+
upstreamCode === "bad_request"
|
|
424
|
+
? HTTP_STATUS.BAD_REQUEST
|
|
425
|
+
: HTTP_STATUS.BAD_GATEWAY;
|
|
426
|
+
return createErrorResult(status, parsed.error.message || "Upstream SSE stream failed");
|
|
420
427
|
}
|
|
421
428
|
|
|
422
429
|
if (onRequestSuccess) await onRequestSuccess();
|
|
@@ -1099,18 +1099,32 @@ export const PATTERN_PRICING = [
|
|
|
1099
1099
|
/**
|
|
1100
1100
|
* Match a model ID against a glob pattern (* = wildcard). Case-insensitive:
|
|
1101
1101
|
* registry ids mix casing (e.g. "MiniMax-M2.5" vs "minimax-m2.5").
|
|
1102
|
+
*
|
|
1103
|
+
* Patterns are a fixed config table, so the compiled RegExp is memoized:
|
|
1104
|
+
* callers loop over ~100 patterns, and this used to recompile every one of
|
|
1105
|
+
* them on each call (several times per request).
|
|
1102
1106
|
*/
|
|
1107
|
+
const PATTERN_REGEX_CACHE = new Map();
|
|
1108
|
+
|
|
1109
|
+
function patternRegex(pattern) {
|
|
1110
|
+
let regex = PATTERN_REGEX_CACHE.get(pattern);
|
|
1111
|
+
if (!regex) {
|
|
1112
|
+
regex = new RegExp(
|
|
1113
|
+
"^" +
|
|
1114
|
+
pattern
|
|
1115
|
+
.split("*")
|
|
1116
|
+
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
1117
|
+
.join(".*") +
|
|
1118
|
+
"$",
|
|
1119
|
+
"i",
|
|
1120
|
+
);
|
|
1121
|
+
PATTERN_REGEX_CACHE.set(pattern, regex);
|
|
1122
|
+
}
|
|
1123
|
+
return regex;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1103
1126
|
export function matchPattern(pattern, model) {
|
|
1104
|
-
|
|
1105
|
-
"^" +
|
|
1106
|
-
pattern
|
|
1107
|
-
.split("*")
|
|
1108
|
-
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
1109
|
-
.join(".*") +
|
|
1110
|
-
"$",
|
|
1111
|
-
"i",
|
|
1112
|
-
);
|
|
1113
|
-
return regex.test(model);
|
|
1127
|
+
return patternRegex(pattern).test(model);
|
|
1114
1128
|
}
|
|
1115
1129
|
|
|
1116
1130
|
/**
|
|
@@ -16,6 +16,16 @@ const WEEKLY_CONFIG = {
|
|
|
16
16
|
const WEEKLY_CACHE_TTL_MS = 180_000; // 3 minutes
|
|
17
17
|
const weeklyCache = new Map(); // cacheKey -> { result, expiresAt } | { promise }
|
|
18
18
|
|
|
19
|
+
// Keys embed the access token, which rotates. Expiry is only read-checked, so
|
|
20
|
+
// retired tokens would accumulate — sweep on a slow unref'd timer.
|
|
21
|
+
setInterval(() => {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
for (const [key, hit] of weeklyCache) {
|
|
24
|
+
if (hit?.promise) continue; // in-flight
|
|
25
|
+
if (!hit || hit.expiresAt <= now) weeklyCache.delete(key);
|
|
26
|
+
}
|
|
27
|
+
}, WEEKLY_CACHE_TTL_MS).unref?.();
|
|
28
|
+
|
|
19
29
|
function cacheKey(accessToken, projectId) {
|
|
20
30
|
return `${accessToken}::${projectId || ""}`;
|
|
21
31
|
}
|
|
@@ -24,6 +24,21 @@ const oauthCooldown = new Map();
|
|
|
24
24
|
const USAGE_CACHE_TTL_MS = 300000;
|
|
25
25
|
const usageCache = new Map(); // token -> { promise } | { result, expiresAt }
|
|
26
26
|
|
|
27
|
+
// Both maps are keyed by access token, which rotates. Expiry is only checked on
|
|
28
|
+
// read, so entries for retired tokens would live forever — sweep them on a slow
|
|
29
|
+
// timer (unref'd so it never holds the process open).
|
|
30
|
+
function sweepExpired() {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
for (const [token, hit] of usageCache) {
|
|
33
|
+
if (hit?.promise) continue; // in-flight
|
|
34
|
+
if (!hit || hit.expiresAt <= now) usageCache.delete(token);
|
|
35
|
+
}
|
|
36
|
+
for (const [token, until] of oauthCooldown) {
|
|
37
|
+
if (until <= now) oauthCooldown.delete(token);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
setInterval(sweepExpired, USAGE_CACHE_TTL_MS).unref?.();
|
|
41
|
+
|
|
27
42
|
export async function getClaudeUsage(accessToken, proxyOptions = null, options = {}) {
|
|
28
43
|
const force = options?.force === true;
|
|
29
44
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
13
|
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
|
14
|
+
import { DEFAULT_RETRY_CONFIG, resolveRetryEntry } from "../config/runtimeConfig.js";
|
|
14
15
|
|
|
15
16
|
export const ZED_WEB_BASE_URL = "https://zed.dev";
|
|
16
17
|
export const ZED_CLOUD_BASE_URL = "https://cloud.zed.dev";
|
|
@@ -179,8 +180,48 @@ function getSystemId(credentials) {
|
|
|
179
180
|
return String(credentials?.providerSpecificData?.systemId || credentials?.systemId || "");
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
function sleep(ms) {
|
|
184
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Transient socket failures (ECONNRESET/ETIMEDOUT/socket hang up via relay or
|
|
188
|
+
// direct) surface as thrown TypeErrors from fetch. Retry those with the same
|
|
189
|
+
// 502-config the BaseExecutor pipeline uses. Anything else — including caller
|
|
190
|
+
// aborts and permanent failures (ENOTFOUND, bad TLS, 5xx bodies that resolve
|
|
191
|
+
// normally) — is rethrown immediately: retrying it only adds delay to an
|
|
192
|
+
// outcome that cannot change.
|
|
193
|
+
const TRANSIENT_NET_CODES = new Set([
|
|
194
|
+
"ECONNRESET",
|
|
195
|
+
"ECONNREFUSED",
|
|
196
|
+
"ETIMEDOUT",
|
|
197
|
+
"EPIPE",
|
|
198
|
+
"EAI_AGAIN",
|
|
199
|
+
"UND_ERR_SOCKET",
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
function isTransientNetError(error) {
|
|
203
|
+
if (!error || error.name === "AbortError") return false;
|
|
204
|
+
const code = error.code || error.cause?.code;
|
|
205
|
+
if (code && TRANSIENT_NET_CODES.has(code)) return true;
|
|
206
|
+
// Node's undici raises a bare TypeError("fetch failed") / "socket hang up"
|
|
207
|
+
// for relay-side resets, so the message is the only signal available.
|
|
208
|
+
return /socket hang up|ECONNRESET|other side closed/i.test(String(error.message || ""));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function zedFetchWithRetry(url, options, proxyOptions = null, statusKey = 502) {
|
|
212
|
+
const { attempts, delayMs } = resolveRetryEntry(DEFAULT_RETRY_CONFIG[statusKey]);
|
|
213
|
+
for (let attempt = 0; ; attempt++) {
|
|
214
|
+
try {
|
|
215
|
+
return await proxyAwareFetch(url, options, proxyOptions);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (!isTransientNetError(error) || attempt >= attempts) throw error;
|
|
218
|
+
await sleep(delayMs);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
182
223
|
async function fetchJson(url, options, proxyOptions = null) {
|
|
183
|
-
const res = await
|
|
224
|
+
const res = await zedFetchWithRetry(url, options, proxyOptions);
|
|
184
225
|
const text = await res.text();
|
|
185
226
|
let data = null;
|
|
186
227
|
if (text) {
|
|
@@ -309,7 +350,7 @@ export async function zedLlmFetch(credentials, path, options = {}) {
|
|
|
309
350
|
const url = zedUrl(config, "llmBaseUrl", path, ZED_LLM_BASE_URL);
|
|
310
351
|
const buildRequest = async (forceRefresh) => {
|
|
311
352
|
const token = await fetchZedLlmToken(credentials, { ...options, forceRefresh });
|
|
312
|
-
return
|
|
353
|
+
return zedFetchWithRetry(
|
|
313
354
|
url,
|
|
314
355
|
{
|
|
315
356
|
...options.fetchOptions,
|
|
@@ -89,11 +89,19 @@ export async function prefetchRemoteImages(body, sourceFormat, targetFormat, opt
|
|
|
89
89
|
const refs = collectImageRefs(body, sourceFormat);
|
|
90
90
|
if (!refs.length) return 0;
|
|
91
91
|
|
|
92
|
+
// Fetch in parallel: this runs in the pre-first-byte path, and serial
|
|
93
|
+
// round trips made N images cost N × latency. Per-ref failure is already
|
|
94
|
+
// fail-open, so allSettled preserves existing behaviour.
|
|
95
|
+
const fetchedList = await Promise.all(
|
|
96
|
+
refs.map(async (ref) => {
|
|
97
|
+
const url = ref.get();
|
|
98
|
+
if (parseDataUri(url)) return { ref, fetched: null };
|
|
99
|
+
return { ref, fetched: await fetchImageAsBase64(url, options) };
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
|
|
92
103
|
let converted = 0;
|
|
93
|
-
for (const ref of
|
|
94
|
-
const url = ref.get();
|
|
95
|
-
if (parseDataUri(url)) continue; // already inline
|
|
96
|
-
const fetched = await fetchImageAsBase64(url, options);
|
|
104
|
+
for (const { ref, fetched } of fetchedList) {
|
|
97
105
|
if (!fetched) continue;
|
|
98
106
|
if (ref.set) ref.set(fetched.url);
|
|
99
107
|
else if (ref.part) {
|
|
@@ -71,9 +71,10 @@ export function claudeToOpenAIResponse(chunk, state) {
|
|
|
71
71
|
if (block?.type === CLAUDE_BLOCK.TEXT) {
|
|
72
72
|
state.textBlockStarted = true;
|
|
73
73
|
} else if (block?.type === CLAUDE_BLOCK.THINKING) {
|
|
74
|
+
// Thinking text streams as `reasoning_content` below; no literal tag is
|
|
75
|
+
// emitted into `content` — clients render that as part of the answer.
|
|
74
76
|
state.inThinkingBlock = true;
|
|
75
77
|
state.currentBlockIndex = chunk.index;
|
|
76
|
-
results.push(createChunk(state, { content: "<think>" }));
|
|
77
78
|
} else if (block?.type === CLAUDE_BLOCK.TOOL_USE) {
|
|
78
79
|
const toolCallIndex = state.toolCallIndex++;
|
|
79
80
|
// Restore original tool name from mapping (Claude OAuth)
|
|
@@ -128,7 +129,6 @@ export function claudeToOpenAIResponse(chunk, state) {
|
|
|
128
129
|
break;
|
|
129
130
|
}
|
|
130
131
|
if (state.inThinkingBlock && chunk.index === state.currentBlockIndex) {
|
|
131
|
-
results.push(createChunk(state, { content: "</think>" }));
|
|
132
132
|
state.inThinkingBlock = false;
|
|
133
133
|
}
|
|
134
134
|
state.textBlockStarted = false;
|
|
@@ -94,11 +94,7 @@ export function createStreamController({
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
logStream(
|
|
98
|
-
"ERROR",
|
|
99
|
-
`ERROR: ${error.message}${error.stack ? `\n ${error.stack}` : ""}`,
|
|
100
|
-
true,
|
|
101
|
-
);
|
|
97
|
+
logStream("ERROR", `${error.message}${error.stack ? `\n ${error.stack}` : ""}`, true);
|
|
102
98
|
onError?.(error);
|
|
103
99
|
},
|
|
104
100
|
|
package/package.json
CHANGED
|
@@ -76,7 +76,7 @@ export default function BulkImportGrokCliModal({ isOpen, onClose, onSuccess }: a
|
|
|
76
76
|
const processFiles = async (files: any) => {
|
|
77
77
|
if (!files || files.length === 0) return;
|
|
78
78
|
setParseError("");
|
|
79
|
-
const jsonFiles:
|
|
79
|
+
const jsonFiles: File[] = Array.from(files as FileList).filter(
|
|
80
80
|
(file) => file.name.endsWith(".json") || file.type === "application/json" || file.type === "",
|
|
81
81
|
);
|
|
82
82
|
|
|
@@ -423,12 +423,15 @@ export default function ProviderTopology({
|
|
|
423
423
|
const lastKey = lastProvider?.toLowerCase() || "";
|
|
424
424
|
const errorKey = errorProvider?.toLowerCase() || "";
|
|
425
425
|
|
|
426
|
-
const rawActiveSet = useMemo(
|
|
426
|
+
const rawActiveSet = useMemo(
|
|
427
|
+
() => new Set<string>(activeKey ? activeKey.split(",") : []),
|
|
428
|
+
[activeKey],
|
|
429
|
+
);
|
|
427
430
|
const lastSet = useMemo(() => new Set(lastKey ? [lastKey] : []), [lastKey]);
|
|
428
431
|
const errorSet = useMemo(() => new Set(errorKey ? [errorKey] : []), [errorKey]);
|
|
429
432
|
|
|
430
433
|
// Track firstSeen per active provider; drop provider if running too long (BE stuck)
|
|
431
|
-
const firstSeenRef = useRef({});
|
|
434
|
+
const firstSeenRef = useRef<Record<string, number>>({});
|
|
432
435
|
const [tick, setTick] = useState<number>(0);
|
|
433
436
|
|
|
434
437
|
useEffect(() => {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getComboById,
|
|
4
|
+
updateCombo,
|
|
5
|
+
deleteCombo,
|
|
6
|
+
getComboByName,
|
|
7
|
+
getCombos,
|
|
8
|
+
getModelAliases,
|
|
9
|
+
} from "@/lib/localDb";
|
|
3
10
|
import { resetComboRotation } from "open-sse/services/combo.js";
|
|
11
|
+
import { findComboMemberProblems } from "@/lib/validateComboModels";
|
|
4
12
|
|
|
5
13
|
// Validate combo name: only a-z, A-Z, 0-9, -, _
|
|
6
14
|
const VALID_NAME_REGEX = /^[a-zA-Z0-9_.\-]+$/;
|
|
@@ -46,6 +54,35 @@ export async function PUT(request, { params }) {
|
|
|
46
54
|
|
|
47
55
|
// Capture previous name to invalidate rotation state on rename
|
|
48
56
|
const prev = await getComboById(id);
|
|
57
|
+
if (!prev) {
|
|
58
|
+
return NextResponse.json({ error: "Combo not found" }, { status: 404 });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Same member validation as POST (see validateComboModels.ts).
|
|
62
|
+
if (body.models !== undefined) {
|
|
63
|
+
const [aliases, combos] = await Promise.all([getModelAliases(), getCombos()]);
|
|
64
|
+
const cleanedModels = Array.isArray(body.models)
|
|
65
|
+
? [
|
|
66
|
+
...new Set(
|
|
67
|
+
body.models.map((m) => (typeof m === "string" ? m.trim() : "")).filter(Boolean),
|
|
68
|
+
),
|
|
69
|
+
]
|
|
70
|
+
: [];
|
|
71
|
+
const problems = findComboMemberProblems(cleanedModels, {
|
|
72
|
+
aliases,
|
|
73
|
+
comboNames: combos.filter((c) => c.id !== id).map((c) => c.name),
|
|
74
|
+
selfName: body.name || prev.name,
|
|
75
|
+
});
|
|
76
|
+
if (!Array.isArray(body.models) || problems.length > 0) {
|
|
77
|
+
return NextResponse.json(
|
|
78
|
+
{
|
|
79
|
+
error: `Invalid combo models: ${!Array.isArray(body.models) ? "models must be an array" : problems.join("; ")}`,
|
|
80
|
+
},
|
|
81
|
+
{ status: 400 },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
body = { ...body, models: cleanedModels };
|
|
85
|
+
}
|
|
49
86
|
const combo = await updateCombo(id, body);
|
|
50
87
|
|
|
51
88
|
if (!combo) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import { getCombos, createCombo, getComboByName } from "@/lib/localDb";
|
|
2
|
+
import { getCombos, createCombo, getComboByName, getModelAliases } from "@/lib/localDb";
|
|
3
|
+
import { findComboMemberProblems } from "@/lib/validateComboModels";
|
|
3
4
|
|
|
4
5
|
export const dynamic = "force-dynamic";
|
|
5
6
|
|
|
@@ -41,7 +42,31 @@ export async function POST(request) {
|
|
|
41
42
|
return NextResponse.json({ error: "Combo name already exists" }, { status: 400 });
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
// Validate members: bare words ("Support", "Last") die at request time
|
|
46
|
+
// with "Invalid model format", so reject them at save time instead.
|
|
47
|
+
const [aliases, combos] = await Promise.all([getModelAliases(), getCombos()]);
|
|
48
|
+
if (models !== undefined && !Array.isArray(models)) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: "Invalid combo models: models must be an array" },
|
|
51
|
+
{ status: 400 },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const cleanedModels = Array.isArray(models)
|
|
55
|
+
? [...new Set(models.map((m) => (typeof m === "string" ? m.trim() : "")).filter(Boolean))]
|
|
56
|
+
: [];
|
|
57
|
+
const problems = findComboMemberProblems(cleanedModels, {
|
|
58
|
+
aliases,
|
|
59
|
+
comboNames: combos.map((c) => c.name),
|
|
60
|
+
selfName: name,
|
|
61
|
+
});
|
|
62
|
+
if (problems.length > 0) {
|
|
63
|
+
return NextResponse.json(
|
|
64
|
+
{ error: `Invalid combo models: ${problems.join("; ")}` },
|
|
65
|
+
{ status: 400 },
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const combo = await createCombo({ name, models: cleanedModels, kind: kind || null });
|
|
45
70
|
|
|
46
71
|
return NextResponse.json(combo, { status: 201 });
|
|
47
72
|
} catch (error) {
|
|
@@ -285,12 +285,23 @@ const PROVIDER_MODELS_CONFIG = {
|
|
|
285
285
|
zed: {
|
|
286
286
|
customResolver: async (connection) => {
|
|
287
287
|
try {
|
|
288
|
+
const proxy = await resolveConnectionProxyConfig(connection.providerSpecificData || {});
|
|
288
289
|
const result = await resolveZedModels(
|
|
289
290
|
{
|
|
290
291
|
accessToken: connection.accessToken,
|
|
291
292
|
providerSpecificData: connection.providerSpecificData || {},
|
|
292
293
|
},
|
|
293
|
-
{
|
|
294
|
+
{
|
|
295
|
+
config: ZED_HOSTED_CONFIG,
|
|
296
|
+
forceRefresh: true,
|
|
297
|
+
proxyOptions: {
|
|
298
|
+
connectionProxyEnabled: proxy.connectionProxyEnabled === true,
|
|
299
|
+
connectionProxyUrl: proxy.connectionProxyUrl || "",
|
|
300
|
+
connectionNoProxy: proxy.connectionNoProxy || "",
|
|
301
|
+
vercelRelayUrl: proxy.vercelRelayUrl || "",
|
|
302
|
+
strictProxy: proxy.strictProxy === true,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
294
305
|
);
|
|
295
306
|
const models = (result?.models || [])
|
|
296
307
|
.filter((m) => m && !m.isDisabled)
|
|
@@ -70,7 +70,10 @@ export async function GET() {
|
|
|
70
70
|
for (const node of nodes) {
|
|
71
71
|
if (node.id && node.name) nodeNameMap[node.id] = node.name;
|
|
72
72
|
}
|
|
73
|
-
} catch {
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// Non-fatal: names degrade to raw provider ids, but stay visible.
|
|
75
|
+
console.warn("Failed to load provider nodes for name map:", err);
|
|
76
|
+
}
|
|
74
77
|
|
|
75
78
|
// Hide sensitive fields, enrich name for compatible providers
|
|
76
79
|
const safeConnections = connections.map((c) => {
|