cartethyia 2.7.5 → 2.7.7
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/.env.example +8 -3
- package/README.md +26 -2
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js +1 -0
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js +1 -0
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-DPgbklad.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-BKj_r0rp.js → CustomizationPage-pnfAqr_-.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-C-e2AMz6.js} +245 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-C-e2AMz6.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-J8wPbmfT.js +1 -0
- package/dist/dashboard/assets/LoginPage-J8wPbmfT.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-DnQPSIA8.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js +7 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-BBTRBAQ3.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BBTRBAQ3.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-D7xp5Ij_.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-LND6_Ss7.js +1 -0
- package/dist/dashboard/assets/SetupPage-LND6_Ss7.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-BzNnm6Bi.js} +4 -4
- package/dist/dashboard/assets/StudioPage-BzNnm6Bi.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-BNzeZ2Vk.js → UsagePage-C1TbL24N.js} +13 -13
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0K-c4eo.js → assert-CidQVoNo.js} +1 -1
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DfbhQU4k.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BllT4LcF.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/{card-CtkX66Wd.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CQXVMn4P.js → cli-tools-Bwv5-zGZ.js} +1 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-BQqBQd2n.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BrTEZ8zu.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/entry-vqGb8yVz.js +4 -0
- package/dist/dashboard/assets/entry-vqGb8yVz.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js +1 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-FQlj0HUT.js → index-D36vCZht.js} +2 -2
- package/dist/dashboard/assets/index-D36vCZht.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C4gU5iOL.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-BHHX_Tmm.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CajrYUnD.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Bx2I7XUe.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/{live-DNNqRbj2.js → live-7_QpnYGu.js} +1 -1
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/{network-C5UBACrJ.js → network-CuMDaTzT.js} +1 -1
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-Xsi9Qx04.js → pagination-B5aOoweZ.js} +1 -1
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/{routing-in0f_IfK.js → routing-fnCsxgNO.js} +1 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BLULNqTp.js → select-C9Ssle9b.js} +2 -2
- package/dist/dashboard/assets/select-C9Ssle9b.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CcGczj6I.js → settings-BxslsErn.js} +1 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/{stack-D2lIR0tg.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-DTARjfiW.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-c0oPSnqW.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-m8WGNPrz.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -57
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/full-postgres/0043_add_provider_custom_user_agent.sql +14 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/migrations/lite-sqlite3/0043_add_provider_custom_user_agent.sql +10 -0
- package/package.json +3 -1
- package/src/boot/dependencies.ts +10 -2
- package/src/config/providers.ts +13 -7
- package/src/config/security.ts +1 -1
- package/src/config/spec.ts +31 -17
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/routes.ts +19 -23
- package/src/console/cli-tools/cartethyia-websearch-plugin.ts +186 -0
- package/src/console/cli-tools/contracts.ts +39 -295
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +6 -6
- package/src/console/cli-tools/injectors/driver.ts +8 -105
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +68 -17
- package/src/console/cli-tools/routes.ts +6 -0
- package/src/console/cli-tools/service.ts +7 -6
- package/src/console/observability/contracts.ts +6 -0
- package/src/console/observability/store.ts +3 -0
- package/src/console/providers/catalog/contracts.ts +4 -0
- package/src/console/providers/catalog/routes.ts +1 -0
- package/src/console/providers/detail/routes.ts +8 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/quota/account-quota-tenant.ts +74 -0
- package/src/console/share/share-stats.ts +7 -9
- package/src/observability/event-log.ts +7 -0
- package/src/observability/event-query.ts +2 -0
- package/src/observability/telemetry-buffer.ts +9 -1
- package/src/persistence/boot.ts +13 -4
- package/src/persistence/schema.ts +15 -1
- package/src/persistence/stores/api-key-store.ts +19 -1
- package/src/protocol/canonical-model.ts +7 -0
- package/src/protocol/dsml.ts +5 -76
- package/src/protocol/request/gemini.ts +20 -1
- package/src/protocol/tool-identity.ts +15 -1
- package/src/providers/adapters/compatible-adapter.ts +29 -1
- package/src/providers/default-registry.ts +20 -4
- package/src/providers/discovery/discovery-types.ts +18 -0
- package/src/providers/discovery/probe-phases.ts +45 -0
- package/src/providers/discovery/probing-service.ts +41 -6
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +108 -16
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +44 -11
- package/src/providers/integrations/codex/codex.ts +28 -1
- package/src/providers/integrations/cursor/cursor.ts +2 -3
- package/src/providers/integrations/github/github-discovery.ts +15 -2
- package/src/providers/integrations/github/github-oauth.ts +178 -9
- package/src/providers/integrations/github/github.ts +95 -14
- package/src/providers/integrations/kiro/kiro.ts +1 -2
- package/src/providers/integrations/qoder/chat-proto.ts +328 -0
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +258 -0
- package/src/providers/integrations/qoder/qoder-adapter.ts +275 -0
- package/src/providers/integrations/qoder/qoder-auth.ts +201 -0
- package/src/providers/integrations/qoder/qoder-body.ts +78 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +14 -4
- package/src/providers/integrations/qoder/qoder-chat.ts +514 -0
- package/src/providers/integrations/qoder/qoder-cosy.ts +162 -0
- package/src/providers/integrations/qoder/qoder-grpc.ts +667 -0
- package/src/providers/integrations/qoder/qoder-ide.ts +584 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +4 -4
- package/src/providers/integrations/qoder/qoder-quota.ts +1 -1
- package/src/providers/integrations/qoder/qoder-realm.ts +40 -4
- package/src/providers/integrations/qoder/qoder-types.ts +51 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +123 -8
- package/src/providers/integrations/qoder/qoder.ts +147 -1830
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/operations/account-error-classification.ts +67 -17
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/provider-contracts.ts +7 -0
- package/src/providers/provider-usage.ts +24 -55
- package/src/security/abuse.ts +5 -5
- package/src/security/admission/service.ts +11 -4
- package/src/security/api-key-auth.ts +5 -3
- package/src/transport/dispatch/attempt-finalize.ts +69 -21
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/upstream.ts +4 -2
- package/src/transport/failure-policy.ts +15 -1
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/request/state.ts +6 -0
- package/src/transport/routing/route-catalog.ts +8 -0
- package/src/transport/routing/route-model.ts +4 -0
- package/src/transport/routing/router.ts +8 -110
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/vision-handoff.ts +83 -28
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js +0 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +0 -1
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +0 -1
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js +0 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +0 -1
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js +0 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +0 -1
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +0 -2
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/app-Bgd3zANQ.js +0 -9
- package/dist/dashboard/assets/app-Bgd3zANQ.js.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BEK7KP0H.js +0 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +0 -1
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/entry-EBHJ3DrE.js +0 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +0 -4
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BAACA727.js +0 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js +0 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +0 -2
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +0 -1
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- package/src/providers/integrations/qoder/qoder-text-tools.ts +0 -277
|
@@ -46,6 +46,12 @@ export interface ProxyRequestOutcome {
|
|
|
46
46
|
readonly firstContentDeltaAtMs?: number;
|
|
47
47
|
/** Last event timestamp (for generation duration calculation). */
|
|
48
48
|
readonly lastEventAtMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* True for a completed request whose output was far smaller than the model's
|
|
51
|
+
* ceiling and which ended with an ordinary `stop` (a "degenerate stop").
|
|
52
|
+
* Set by completion bookkeeping; carried onto the telemetry row.
|
|
53
|
+
*/
|
|
54
|
+
readonly degenerateStop?: boolean;
|
|
49
55
|
}
|
|
50
56
|
export interface ProxyRequestState {
|
|
51
57
|
readonly requestId: string;
|
|
@@ -354,6 +354,7 @@ const ROUTING_COLUMNS = {
|
|
|
354
354
|
enabled: providerRoutingSettings.enabled,
|
|
355
355
|
skipProxy: providerRoutingSettings.skipProxy,
|
|
356
356
|
userAgent: providerRoutingSettings.userAgent,
|
|
357
|
+
customUserAgent: providerRoutingSettings.customUserAgent,
|
|
357
358
|
} as const;
|
|
358
359
|
|
|
359
360
|
const POOL_COLUMNS = {
|
|
@@ -553,6 +554,7 @@ class RouteCatalogRepository {
|
|
|
553
554
|
enabled: row.enabled,
|
|
554
555
|
skipProxy: row.skipProxy,
|
|
555
556
|
userAgent: row.userAgent,
|
|
557
|
+
customUserAgent: row.customUserAgent,
|
|
556
558
|
};
|
|
557
559
|
}
|
|
558
560
|
/** Tenant-specific setting wins over global; an unconfigured provider
|
|
@@ -573,6 +575,12 @@ class RouteCatalogRepository {
|
|
|
573
575
|
function resolveUserAgent(providerId: string, rowTenantId: string | null): string | undefined {
|
|
574
576
|
const tenantSetting = rowTenantId ? providerRouting[rowTenantId]?.[providerId]?.userAgent : undefined;
|
|
575
577
|
const globalSetting = providerRouting.__global__?.[providerId]?.userAgent;
|
|
578
|
+
const tenantCustom = rowTenantId ? providerRouting[rowTenantId]?.[providerId]?.customUserAgent : undefined;
|
|
579
|
+
const globalCustom = providerRouting.__global__?.[providerId]?.customUserAgent;
|
|
580
|
+
// An operator-set custom User-Agent overrides the native identity when it
|
|
581
|
+
// is non-empty; absent custom falls back to the native route identity.
|
|
582
|
+
const custom = resolveTenantOverride(tenantCustom, globalCustom, undefined);
|
|
583
|
+
if (custom !== undefined && custom !== null && custom.trim() !== "") return custom.trim();
|
|
576
584
|
return resolveRouteUserAgent(providerId, tenantSetting, globalSetting);
|
|
577
585
|
}
|
|
578
586
|
|
|
@@ -170,6 +170,10 @@ export interface ProviderRoutingSetting {
|
|
|
170
170
|
readonly enabled: boolean;
|
|
171
171
|
/** Built-in API-key User-Agent; OAuth and custom providers retain their identities. */
|
|
172
172
|
readonly userAgent?: string;
|
|
173
|
+
/** Operator-set custom User-Agent override; non-empty overrides `userAgent`
|
|
174
|
+
* (and any provider-native identity) at the request edge. `null`/empty
|
|
175
|
+
* means no custom override. */
|
|
176
|
+
readonly customUserAgent?: string | null;
|
|
173
177
|
/** When true, this (tenant, provider) dispatches direct — candidates
|
|
174
178
|
* never carry `network_pool_ids` regardless of pool availability. */
|
|
175
179
|
readonly skipProxy: boolean;
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
parseModelReference,
|
|
34
34
|
routeCandidateIndex,
|
|
35
35
|
} from "./candidate-index";
|
|
36
|
-
import { totalRemainingCredit,
|
|
36
|
+
import { totalRemainingCredit, type ProviderQuotaWindow } from "../../providers/quota/quota-contracts";
|
|
37
37
|
import { getCachedQuotaEntry, quotaLens } from "../../providers/quota/cache";
|
|
38
38
|
import { readQuotaSnapshot } from "../../providers/quota/snapshot";
|
|
39
39
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
@@ -88,64 +88,6 @@ export function filterCandidateQuotaWindows(
|
|
|
88
88
|
return windows;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
92
|
-
* True when the account's remaining allowance is at or below its provider's
|
|
93
|
-
* floor. Credit mode sums the absolute remaining credit; percent mode uses the
|
|
94
|
-
* aggregate credit percentage and the lowest independent percentage window.
|
|
95
|
-
* No reading means unknown, which is not below the floor.
|
|
96
|
-
*/
|
|
97
|
-
function belowUsageFloor(state: RoutedCandidate): boolean {
|
|
98
|
-
const rawWindows = state.quota_windows;
|
|
99
|
-
// A successful Start Plan balance (`quota_entitlements_known`) authoritatively
|
|
100
|
-
// lists its model grants: when the provider is zcode-start and no window
|
|
101
|
-
// matches the requested model, the account is below the floor even with an
|
|
102
|
-
// empty/absent window list — there is no grant to fund the model.
|
|
103
|
-
if (state.provider_id === "zcode-start" && state.quota_entitlements_known === true) {
|
|
104
|
-
if (rawWindows === undefined || rawWindows.length === 0) return true;
|
|
105
|
-
const knownGrants = rawWindows.some((window) => window.modelIds !== undefined);
|
|
106
|
-
if (!knownGrants) return true;
|
|
107
|
-
}
|
|
108
|
-
if (rawWindows === undefined || rawWindows.length === 0) return false;
|
|
109
|
-
const relevant = filterCandidateQuotaWindows(rawWindows, state);
|
|
110
|
-
// Model-scoped grants (Start Plan) are checked separately: a grant that has
|
|
111
|
-
// not started, has expired, or is exhausted cannot fund the requested model,
|
|
112
|
-
// so it must not count as credit.
|
|
113
|
-
const now = Date.now();
|
|
114
|
-
const grants = relevant.filter((window) => window.modelIds !== undefined);
|
|
115
|
-
if (state.provider_id === "zcode-start" && grants.length === 0 && rawWindows.some((window) => window.modelIds !== undefined)) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
if (
|
|
119
|
-
grants.length > 0 &&
|
|
120
|
-
!grants.some((window) => {
|
|
121
|
-
if (window.startsAt && Date.parse(window.startsAt) > now) return false;
|
|
122
|
-
if (window.resetsAt && Date.parse(window.resetsAt) <= now) return false;
|
|
123
|
-
if (typeof window.remaining === "number" && window.remaining <= 0) return false;
|
|
124
|
-
if (typeof window.remainingPercent === "number" && window.remainingPercent <= 0) return false;
|
|
125
|
-
const remaining = state.usage_floor_unit === "credit" ? window.remaining : window.remainingPercent;
|
|
126
|
-
return state.usage_floor === undefined || remaining == null || remaining > state.usage_floor;
|
|
127
|
-
})
|
|
128
|
-
) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
const windows = relevant.filter((window) => window.modelIds === undefined);
|
|
132
|
-
const credit = totalRemainingCredit(windows);
|
|
133
|
-
const percent = totalRemainingPercent(windows);
|
|
134
|
-
const exhaustedIndependentWindow = windows.some(
|
|
135
|
-
(window) =>
|
|
136
|
-
window.accounting !== "pooled" &&
|
|
137
|
-
typeof window.remainingPercent === "number" &&
|
|
138
|
-
Number.isFinite(window.remainingPercent) &&
|
|
139
|
-
window.remainingPercent <= 0,
|
|
140
|
-
);
|
|
141
|
-
if ((credit !== null && credit <= 0) || exhaustedIndependentWindow) return true;
|
|
142
|
-
if (credit === null && percent !== null && percent <= 0) return true;
|
|
143
|
-
const floor = state.usage_floor;
|
|
144
|
-
if (floor === undefined) return false;
|
|
145
|
-
const remaining = state.usage_floor_unit === "credit" ? credit : percent;
|
|
146
|
-
return remaining !== null && remaining <= floor;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
91
|
/**
|
|
150
92
|
* Routing priority for an account's remaining allowance when two candidates sit
|
|
151
93
|
* in the same rank group: positive remaining credit first, then unknown, then
|
|
@@ -310,42 +252,6 @@ function spreadDeadline(cooldownUntil: number | undefined): { cooldown_until?: n
|
|
|
310
252
|
return cooldownUntil === undefined ? {} : { cooldown_until: cooldownUntil };
|
|
311
253
|
}
|
|
312
254
|
|
|
313
|
-
/**
|
|
314
|
-
* The soonest moment a quota window the caller is blocked on refills.
|
|
315
|
-
*
|
|
316
|
-
* A usage-floor exclusion is time-bounded by construction — the floor compares
|
|
317
|
-
* the *remaining* allowance, and the window states when that allowance resets.
|
|
318
|
-
* The exclusion carries a deadline so `plan()` can tell a client how long to
|
|
319
|
-
* wait and `earliestResetMs` reports a value. The minimum across windows, for
|
|
320
|
-
* the same reason `earliestResetMs` takes it: the account
|
|
321
|
-
* can serve again as soon as its first window refills, so quoting the slowest
|
|
322
|
-
* would hold the client off longer than necessary.
|
|
323
|
-
*
|
|
324
|
-
* `undefined` when no window states a reset, which is the honest answer and
|
|
325
|
-
* keeps the reason a plain 503 rather than a 429 with an unknown retry-after.
|
|
326
|
-
*/
|
|
327
|
-
function earliestWindowResetMs(
|
|
328
|
-
windows: readonly ProviderQuotaWindow[] | undefined,
|
|
329
|
-
nowMs: number,
|
|
330
|
-
candidate: RouteCandidate,
|
|
331
|
-
): number | undefined {
|
|
332
|
-
if (windows === undefined) return undefined;
|
|
333
|
-
windows = filterCandidateQuotaWindows(windows, candidate);
|
|
334
|
-
let earliest: number | undefined;
|
|
335
|
-
for (const window of windows) {
|
|
336
|
-
if (window.startsAt && Date.parse(window.startsAt) > nowMs) {
|
|
337
|
-
const at = Date.parse(window.startsAt);
|
|
338
|
-
if (earliest === undefined || at < earliest) earliest = at;
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
if (typeof window.resetsAt !== "string" || window.resetsAt.length === 0) continue;
|
|
342
|
-
const at = Date.parse(window.resetsAt);
|
|
343
|
-
if (!Number.isFinite(at) || at <= nowMs) continue;
|
|
344
|
-
if (earliest === undefined || at < earliest) earliest = at;
|
|
345
|
-
}
|
|
346
|
-
return earliest;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
255
|
/**
|
|
350
256
|
* The earliest instant any cooling candidate recovers, in epoch ms.
|
|
351
257
|
*
|
|
@@ -376,25 +282,17 @@ export class EligibilityEvaluator {
|
|
|
376
282
|
return { eligible: false, reason: "model_cooldown", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
377
283
|
if (candidate.health_status === "cooldown" && candidate.cooldown_kind === "hard")
|
|
378
284
|
return { eligible: false, reason: "cooldown_hard", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
candidate,
|
|
387
|
-
...spreadDeadline(earliestWindowResetMs(candidate.quota_windows, Date.now(), candidate)),
|
|
388
|
-
};
|
|
285
|
+
// Quota/usage-floor is deliberately NOT an exclusion: an account whose
|
|
286
|
+
// window is spent or below its configured floor is still routed, and the
|
|
287
|
+
// upstream is the one that answers (its refusal is classified into a
|
|
288
|
+
// cooldown then). Blocking here made free-tier and exhausted accounts
|
|
289
|
+
// unreachable even when the operator wanted them tried — "don't follow
|
|
290
|
+
// quota, route whether or not it has any."
|
|
291
|
+
//
|
|
389
292
|
// A slow lane is orthogonal to cooldown, never a member of it: the account
|
|
390
293
|
// still serves on spare capacity when the request carries the slow usage
|
|
391
294
|
// limit, so excluding it would park a serving credential. It stays eligible
|
|
392
295
|
// and only orders behind fresh siblings (see the ordering below).
|
|
393
|
-
//
|
|
394
|
-
// Deliberately AFTER the floor gate: orthogonal means it does not add an
|
|
395
|
-
// exclusion, not that it overrides one. An account with no usable allowance
|
|
396
|
-
// is not rescued by the lane — the lane is a latency budget against spare
|
|
397
|
-
// capacity, and an account below its floor has none.
|
|
398
296
|
if (candidate.lane === "slow")
|
|
399
297
|
return { eligible: true, reason: "slow", candidate };
|
|
400
298
|
if (candidate.health_status === "cooldown")
|
|
@@ -51,12 +51,10 @@ export function reasoningSummaryHasBoundary(
|
|
|
51
51
|
* ends one item is the first half of a word the next item finishes. Inserting
|
|
52
52
|
* a break there painted `Gil` in the thinking pane and `iran` in the answer.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* carry no terminal punctuation (`**Crafting a brief reply**`), which glued
|
|
59
|
-
* separate thoughts into one line.
|
|
54
|
+
* A capital by itself is not a boundary: a sentence can continue in the next
|
|
55
|
+
* item, and inserting a paragraph there makes a captured chat look split. A
|
|
56
|
+
* sentence terminator or an explicit Markdown block marker is stronger
|
|
57
|
+
* evidence; otherwise a capitalized continuation gets only a space.
|
|
60
58
|
*/
|
|
61
59
|
export function reasoningSummarySeparator(
|
|
62
60
|
previous: ReasoningSummaryIdentity | undefined,
|
|
@@ -66,13 +64,10 @@ export function reasoningSummarySeparator(
|
|
|
66
64
|
): string {
|
|
67
65
|
if (!reasoningSummaryHasBoundary(previous, current)) return "";
|
|
68
66
|
if (/\s$/.test(previousText ?? "") || /^\s/.test(currentText)) return "";
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (/^[A-Z
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// a sentence, the next one is a different artifact (the raw reasoning after a
|
|
75
|
-
// summary), and gluing them produced `file.raw private reasoning`.
|
|
76
|
-
if (/[.!?][)"'\]]*$/.test(previousText ?? "")) return " ";
|
|
67
|
+
if (/^[*#\-]/.test(currentText)) return "\n\n";
|
|
68
|
+
const previousEndedSentence = /[.!?][)"'\]]*$/.test(previousText ?? "");
|
|
69
|
+
if (previousEndedSentence && /^[A-ZÀ-ÖØ-Þ]/.test(currentText)) return "\n\n";
|
|
70
|
+
if (/^[A-ZÀ-ÖØ-Þ]/.test(currentText)) return " ";
|
|
71
|
+
if (previousEndedSentence) return " ";
|
|
77
72
|
return "";
|
|
78
73
|
}
|
|
@@ -27,7 +27,9 @@ import { buildUpstreamDispatchContext } from "./dispatch/upstream";
|
|
|
27
27
|
import type { GatewayHandlerDeps } from "./dispatch/handler-deps";
|
|
28
28
|
import type { ByokUpstreamHost } from "../providers/operations/provider-catalog-service";
|
|
29
29
|
import type { PreparedProxyRequest } from "./request/preparer";
|
|
30
|
-
import {
|
|
30
|
+
import { estimateInputTokens } from "./request/token-estimate";
|
|
31
|
+
import { getCachedVisionDescription, setCachedVisionDescription } from "./vision-handoff-cache";
|
|
32
|
+
import { modelsDevCatalog } from "../providers/discovery/models-dev-catalog";
|
|
31
33
|
|
|
32
34
|
/** Dependency surface needed to run a vision handoff (a gateway-handler deps). */
|
|
33
35
|
export interface VisionHandoffDeps extends GatewayHandlerDeps {
|
|
@@ -47,6 +49,16 @@ const VISION_HANDOFF_MIN_BUDGET_MS = 2_000;
|
|
|
47
49
|
/** Per-model budget cap so one slow vision model cannot eat the whole deadline. */
|
|
48
50
|
const VISION_HANDOFF_PER_MODEL_TIMEOUT_MS = 12_000;
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Fallback context window for a vision model whose catalog row is unknown.
|
|
54
|
+
* Used only to bound how much trailing history a handoff sub-request carries;
|
|
55
|
+
* absent that boundary a giant conversation could overflow the vision model.
|
|
56
|
+
*/
|
|
57
|
+
const VISION_HANDOFF_FALLBACK_CONTEXT_TOKENS = 200_000;
|
|
58
|
+
|
|
59
|
+
/** Headroom kept for the describe-instruction and the active image turn. */
|
|
60
|
+
const VISION_HANDOFF_CONTEXT_HEADROOM_TOKENS = 8_000;
|
|
61
|
+
|
|
50
62
|
/** The result of one successful handoff. */
|
|
51
63
|
export interface VisionHandoffResult {
|
|
52
64
|
readonly description: string;
|
|
@@ -99,26 +111,16 @@ function lastRealImageCarryingUserTurnIndex(request: CanonicalRequest): number {
|
|
|
99
111
|
return -1;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
|
-
/** Hash of the active image set (the last image-carrying user turn's images). */
|
|
103
|
-
function hashActiveImageSet(request: CanonicalRequest): string {
|
|
104
|
-
const index = lastRealImageCarryingUserTurnIndex(request);
|
|
105
|
-
if (index < 0) return "";
|
|
106
|
-
const images = request.messages[index]!.content.filter(
|
|
107
|
-
(part): part is { kind: "image"; payload: unknown } => part.kind === "image",
|
|
108
|
-
);
|
|
109
|
-
const hashes = images.map(hashImagePart).sort();
|
|
110
|
-
return createHash("sha256").update(hashes.join("|")).digest("hex");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
114
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
115
|
+
* Selects the messages sent to one vision model: the active image-carrying
|
|
116
|
+
* turn (with the describe-instruction) plus as much *trailing* history as fits
|
|
117
|
+
* the vision model's context window. Carrying only the last turn loses the
|
|
118
|
+
* surrounding conversation ("memory lost to handoff"), while carrying all of it
|
|
119
|
+
* overflows a small-context vision model. Walking backwards from the image turn
|
|
120
|
+
* and estimating tokens keeps the sub-request within the model's context without
|
|
121
|
+
* guessing.
|
|
120
122
|
*/
|
|
121
|
-
function
|
|
123
|
+
function selectVisionContextMessages(parent: CanonicalRequest): CanonicalMessage[] {
|
|
122
124
|
const index = lastRealImageCarryingUserTurnIndex(parent);
|
|
123
125
|
if (index < 0) {
|
|
124
126
|
throw new GatewayError(
|
|
@@ -127,14 +129,60 @@ function buildVisionSubRequest(parent: CanonicalRequest, model: string): Canonic
|
|
|
127
129
|
"vision handoff cannot build a sub-request from a request with no image-carrying user turns (request {requestId})",
|
|
128
130
|
);
|
|
129
131
|
}
|
|
130
|
-
const
|
|
131
|
-
const kept =
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
const active = parent.messages[index]!;
|
|
133
|
+
const kept = active.content.filter((part) => part.kind === "text" || part.kind === "image");
|
|
134
|
+
const activeTurn: CanonicalMessage = {
|
|
135
|
+
role: "user",
|
|
136
|
+
content: [{ kind: "text", text: VISION_HANDOFF_INSTRUCTION }, ...kept],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const contextLimit = visionModelContextLimit(parent);
|
|
140
|
+
const budget = Math.max(1, contextLimit - VISION_HANDOFF_CONTEXT_HEADROOM_TOKENS);
|
|
141
|
+
const messages: CanonicalMessage[] = [activeTurn];
|
|
142
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
143
|
+
const message = parent.messages[i]!;
|
|
144
|
+
const candidateMessages = [message, ...messages];
|
|
145
|
+
const estimated = estimateInputTokens({ ...parent, messages: candidateMessages });
|
|
146
|
+
if (estimated > budget) break;
|
|
147
|
+
messages.length = 0;
|
|
148
|
+
messages.push(...candidateMessages);
|
|
149
|
+
}
|
|
150
|
+
return messages;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolves the vision model's context window. Prefers:
|
|
155
|
+
* 1. an explicit operator-provided limit on the request metadata;
|
|
156
|
+
* 2. the packed catalog row for the first vision model (the one the handoff
|
|
157
|
+
* will actually dispatch to);
|
|
158
|
+
* 3. the fallback window.
|
|
159
|
+
*/
|
|
160
|
+
function visionModelContextLimit(parent: CanonicalRequest): number {
|
|
161
|
+
const explicit = parent.metadata?.["visionModelContextLimit"];
|
|
162
|
+
if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
|
|
163
|
+
return explicit;
|
|
164
|
+
}
|
|
165
|
+
const row = modelsDevCatalog.resolve("", parent.model);
|
|
166
|
+
if (row !== undefined && row.contextLimit !== null && row.contextLimit > 0) {
|
|
167
|
+
return row.contextLimit;
|
|
168
|
+
}
|
|
169
|
+
return VISION_HANDOFF_FALLBACK_CONTEXT_TOKENS;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Deterministic cache key: the exact context the vision model will see. */
|
|
173
|
+
function visionContextCacheKey(parent: CanonicalRequest): string {
|
|
174
|
+
const messages = selectVisionContextMessages(parent);
|
|
175
|
+
const serialized = JSON.stringify(messages);
|
|
176
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Builds the sub-request sent to one vision model.
|
|
181
|
+
*/
|
|
182
|
+
function buildVisionSubRequest(parent: CanonicalRequest, model: string): CanonicalRequest {
|
|
135
183
|
return {
|
|
136
184
|
model,
|
|
137
|
-
messages,
|
|
185
|
+
messages: selectVisionContextMessages(parent),
|
|
138
186
|
generation_controls: {},
|
|
139
187
|
stream: true,
|
|
140
188
|
source_surface: "chat",
|
|
@@ -154,9 +202,16 @@ export async function tryVisionHandoff(
|
|
|
154
202
|
input: VisionHandoffCall,
|
|
155
203
|
models: readonly string[],
|
|
156
204
|
): Promise<readonly VisionHandoffResult[]> {
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
205
|
+
// The cache key covers the full vision context (active image turn plus the
|
|
206
|
+
// trailing history we actually send), so a changed conversation re-describes
|
|
207
|
+
// the same attachment instead of serving a stale description. A malformed
|
|
208
|
+
// request that cannot build a vision context simply skips the cache.
|
|
209
|
+
let cacheHash = "";
|
|
210
|
+
try {
|
|
211
|
+
cacheHash = visionContextCacheKey(input.canonicalRequest);
|
|
212
|
+
} catch {
|
|
213
|
+
cacheHash = "";
|
|
214
|
+
}
|
|
160
215
|
const cached = cacheHash.length > 0 ? getCachedVisionDescription(cacheHash) : undefined;
|
|
161
216
|
if (cached !== undefined && cached.trim().length > 0) {
|
|
162
217
|
return [{ description: cached.trim(), model: models[0] ?? "cache" }];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./jsx-runtime-CXUMngI_.js";import{at as t}from"./entry-LCS85hiq.js";var n=e();function r(){return(0,n.jsx)(`main`,{className:`auth-viewport`,children:(0,n.jsxs)(`div`,{className:`card-solid auth-window border border-red-soft`,children:[(0,n.jsxs)(`div`,{className:`auth-header`,children:[(0,n.jsx)(`div`,{"aria-hidden":`true`,className:`auth-logo bg-red bg-none`,children:(0,n.jsx)(t,{size:24})}),(0,n.jsx)(`h1`,{className:`auth-title`,children:`Access Restricted`}),(0,n.jsx)(`p`,{className:`auth-desc`,children:`Security policy lockout or untrusted origin address`})]}),(0,n.jsxs)(`div`,{className:`rounded-panel border border-inner-border bg-surface-2 p-7 text-caption leading-relaxed text-text-secondary`,children:[(0,n.jsx)(`strong`,{className:`block text-text-primary mb-2`,children:`HTTP 403 Forbidden`}),`This client IP has been temporarily locked out due to repeated invalid credential attempts or untrusted proxy headers.`]})]})})}export{r as default};
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{i}from"./terminal-0_IjPWTk.js";import{S as a,l as o,s}from"./numbers-CUWAZK4J.js";import{n as c,r as l,t as u}from"./state-DTARjfiW.js";import{t as d}from"./dialog-BEK7KP0H.js";import{t as f}from"./rotate-ccw-CufcqITi.js";import{t as p}from"./trash-Cppn5c3R.js";import{C as m,T as h,v as g}from"./assert-C0K-c4eo.js";import{l as _,o as v,t as y}from"./query-keys-BloKtaXJ.js";import{t as b}from"./useMutation-m8WGNPrz.js";import{A as x,B as S,b as C,n as w,y as T}from"./entry-LCS85hiq.js";import{t as E}from"./badge-DfbhQU4k.js";import{n as D}from"./inline-C4gU5iOL.js";import{t as O}from"./layout-CajrYUnD.js";var k=e(`gift`,[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8`,key:`1sqzm4`}],[`path`,{d:`M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5`,key:`kc0143`}],[`rect`,{x:`3`,y:`7`,width:`18`,height:`4`,rx:`1`,key:`1hberx`}]]),A=t(n(),1),j=r();async function M(e){return v(`/quota/overview`,e?{signal:e}:void 0)}function N(){return m({queryKey:y.quota.all,queryFn:e=>M(g(e)),staleTime:1/0,refetchInterval:!1,refetchIntervalInBackground:!1,refetchOnMount:!1,refetchOnWindowFocus:!1,placeholderData:e=>e})}function P(e){let t=h(),n=y.quota.account(e);return b({mutationFn:async()=>v(`/accounts/${encodeURIComponent(e)}/quota/refresh`,{method:`POST`}),onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:n,exact:!0}),t.invalidateQueries({queryKey:y.quota.all}),t.invalidateQueries({queryKey:y.providers.all})])}})}function F(e){let t=h();return b({mutationFn:async()=>(await v(`/accounts/${encodeURIComponent(e)}/claim-gifts`,{method:`POST`})).data,onSuccess:async n=>{n.claimed.length!==0&&await Promise.all([t.invalidateQueries({queryKey:y.quota.account(e),exact:!0}),t.invalidateQueries({queryKey:y.quota.all}),t.invalidateQueries({queryKey:y.providers.all})])}})}function I(){let e=h();return b({mutationFn:async({accountIds:e,onProgress:t})=>{let n=e.length,r=0,i=await v(`/quota/refresh`,{method:`POST`,body:JSON.stringify({accountIds:e})});return r=i.queued,t?.(r,n),i},onSuccess:async(t,n)=>{let r=n.accountIds,i=new Set(t.failures.map(e=>e.accountId)),a=r.filter(e=>!i.has(e));await Promise.all([...a.map(t=>e.invalidateQueries({queryKey:y.quota.account(t),exact:!0})),e.invalidateQueries({queryKey:y.quota.all})])}})}function L(){let e=h();return b({mutationFn:({id:e,active:t})=>v(`/accounts/${encodeURIComponent(e)}`,{method:`PATCH`,body:JSON.stringify({active:t})}),onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:y.quota.all}),e.invalidateQueries({queryKey:y.providers.all})])}})}function R(){let e=h();return b({mutationFn:async({ids:e,active:t})=>{e.length!==0&&await v(`/accounts/batch`,{method:`PATCH`,body:JSON.stringify({ids:e,active:t})})},onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:y.quota.all}),e.invalidateQueries({queryKey:y.providers.all})])}})}function z(){let e=h();return b({mutationFn:async e=>{await v(`/accounts/${encodeURIComponent(e)}`,{method:`DELETE`})},onSuccess:async(t,n)=>{e.removeQueries({queryKey:y.quota.account(n),exact:!0}),await Promise.all([e.invalidateQueries({queryKey:y.quota.all}),e.invalidateQueries({queryKey:y.providers.all})])}})}var B=new Set([`workbuddy`,`workbuddycn`,`cb`,`cbcn`]);function V(e){return B.has(e.trim().toLowerCase())}function H(e){let t=h();return b({mutationFn:async()=>{if(e.length===0)throw Error(`Select a provider first`);return v(`/quota/daily-growth`,{method:`POST`,body:JSON.stringify({providerId:e})})},onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:y.quota.all}),t.invalidateQueries({queryKey:y.providers.all})])}})}function U(e){let t=h();return b({mutationFn:async()=>{let t=await v(`/accounts/${encodeURIComponent(e)}/activity-report`,{method:`POST`}),n=t.message??`Activity reported`,r=await v(`/accounts/${encodeURIComponent(e)}/checkin`,{method:`POST`}),i=r.message??`Daily check-in done`;return{ok:t.ok,message:t.ok?`Report success · Check-in ${i}`:`Report failed: ${n} · Check-in ${i}`,checkin:r,report:t}},onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:y.quota.all}),t.invalidateQueries({queryKey:y.providers.all})])}})}var W=new Set([`codex`,`claude`,`anthropic`]);function G(e){return W.has(e.trim().toLowerCase())}function K(e,t){return m({queryKey:y.quota.resets(e),queryFn:({signal:t})=>v(`/accounts/${encodeURIComponent(e)}/resets`,t?{signal:t}:void 0),enabled:t,staleTime:3e4})}function q(e){let t=h();return b({mutationFn:async t=>v(`/accounts/${encodeURIComponent(e)}/reset`,{method:`POST`,body:JSON.stringify(t??{})}),onSettled:async()=>{await Promise.all([t.invalidateQueries({queryKey:y.quota.all}),t.invalidateQueries({queryKey:y.providers.all})])}})}function J(e){return e===`cooldown`}function Y(e){return e===`active`?`ok`:e===`disabled`?`disabled`:`warn`}function X(e,t){return e?{title:`Enable static-token mode?`,message:`${t} will be used exactly as issued and will not be refreshed. Re-enable refresh before this token expires.`,confirmLabel:`Enable static mode`}:{title:`Re-enable credential refresh?`,message:`${t} will return to the OAuth refresh sweep. Use this only when a refresh token is available.`,confirmLabel:`Re-enable refresh`}}function Z({summary:e,onClose:t}){let{providerId:n,accountId:r,title:a,status:m,errorCategory:h,errorMessage:g,jwtIssuer:_}=e,v=w(n,r),y=C(),b=v.data??[],k=x(),M=e.credentialKind===`oauth`||e.credentialMode===`jwt`||e.staticToken===!0,[N,P]=(0,A.useState)(e.staticToken===!0),[F,I]=(0,A.useState)(null),[L,R]=(0,A.useState)(!1);(0,A.useEffect)(()=>P(e.staticToken===!0),[e.staticToken]);let z=T();return(0,j.jsx)(d,{open:!0,onClose:t,title:`Health & Error Log — ${a}`,size:`lg`,children:(0,j.jsxs)(`div`,{className:`flex flex-col gap-7 min-w-0`,children:[(0,j.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-5 rounded-control border border-inner-border bg-surface-2 py-5 px-7`,children:[(0,j.jsxs)(`div`,{className:`min-w-0`,children:[(0,j.jsxs)(D,{gap:`8px`,className:`flex-wrap`,children:[(0,j.jsx)(`span`,{className:`text-body font-semibold`,children:`Account status:`}),(0,j.jsx)(E,{tone:Y(m),dot:!0,children:m.toUpperCase()}),h?(0,j.jsx)(E,{tone:`warn`,title:g??void 0,children:h}):null]}),g?(0,j.jsx)(`p`,{className:`text-badge text-red mt-2 wrap-anywhere`,children:g}):null]}),J(m)?(0,j.jsx)(o,{size:`sm`,variant:`secondary`,icon:(0,j.jsx)(f,{size:12,className:y.isPending?`animate-spin`:``}),disabled:y.isPending,onClick:()=>y.mutate({providerId:n,accountId:r},{onSuccess:()=>void v.refetch()}),children:y.isPending?`Recovering...`:`Recover Now`}):null]}),M?(0,j.jsxs)(`div`,{className:`flex items-center justify-between gap-6 rounded-control border border-inner-border bg-surface-2 py-5 px-7`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`div`,{className:`text-caption font-semibold`,children:`Credential management`}),(0,j.jsx)(`div`,{className:`text-badge text-text-tertiary`,children:N?`Static mode: no refresh request will be sent.`:`OAuth refresh is enabled for this account.`}),_?(0,j.jsxs)(`div`,{className:`text-badge text-text-tertiary mt-1 wrap-anywhere`,children:[`Static · no refresh · issuer`,` `,(0,j.jsx)(`span`,{className:`font-mono text-text-secondary`,children:_})]}):null]}),(0,j.jsx)(o,{size:`sm`,variant:N?`primary`:`secondary`,disabled:k.isPending,onClick:()=>I(!N),children:N?`Static · no refresh`:`Enable static mode`})]}):null,(0,j.jsxs)(`div`,{className:`min-w-0`,children:[(0,j.jsxs)(`div`,{className:`flex items-center justify-between gap-5 mb-4`,children:[(0,j.jsxs)(`h4`,{className:`text-caption font-semibold text-text-secondary`,children:[`Transition Events & Audit (`,b.length,`)`]}),(0,j.jsx)(o,{size:`sm`,variant:`secondary`,icon:(0,j.jsx)(p,{size:12}),disabled:b.length===0||z.isPending,onClick:()=>R(!0),title:`Deletes this account's recorded transitions and audit entries.`,children:`Purge log`})]}),v.isPending?(0,j.jsx)(l,{compact:!0,label:`Loading health events...`}):v.isError?(0,j.jsx)(c,{compact:!0,message:`Failed to load health events`,onRetry:()=>void v.refetch()}):b.length===0?(0,j.jsx)(u,{compact:!0,title:`No health events recorded`,message:e.emptyMessage,icon:(0,j.jsx)(i,{size:20})}):(0,j.jsx)(`div`,{className:`rounded-inset border border-inner-border`,children:(0,j.jsx)(O,{maxHeight:480,scrollRegion:!0,headers:[`Timestamp`,`From → To`,`Model`,`Category`,`Reason / Details`],children:b.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`td`,{className:`text-badge text-text-tertiary whitespace-nowrap`,children:new Date(e.createdAt).toLocaleString()}),(0,j.jsxs)(`td`,{className:`whitespace-nowrap`,children:[e.fromStatus!==null&&e.fromStatus!==e.toStatus?(0,j.jsxs)(`span`,{className:`text-badge text-text-secondary`,children:[e.fromStatus,` →`,` `]}):(0,j.jsx)(`span`,{className:`text-badge text-text-tertiary`,children:`logged · `}),(0,j.jsx)(E,{tone:Y(e.toStatus),children:e.toStatus})]}),(0,j.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary wrap-anywhere`,children:e.modelId??`—`}),(0,j.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary`,children:e.errorCategory??`—`}),(0,j.jsx)(`td`,{className:`text-badge text-text-primary min-w-110 max-w-260 wrap-anywhere`,children:e.reason??`—`})]},e.id))})})]}),(0,j.jsx)(S,{open:F!==null,onClose:()=>I(null),onConfirm:()=>{let e=F===!0;k.mutate({providerId:n,accountId:r,request:{staticToken:e}},{onSuccess:()=>{P(e),I(null),s.success(e?`Static mode enabled`:`Credential refresh re-enabled`,a),v.refetch()},onError:e=>s.error(`Credential update failed`,e instanceof Error?e.message:`Unable to update credential mode`)})},title:X(F===!0,a).title,message:X(F===!0,a).message,confirmLabel:X(F===!0,a).confirmLabel,cancelLabel:`Cancel`}),(0,j.jsx)(S,{open:L,onClose:()=>R(!1),onConfirm:()=>{z.mutate({providerId:n,accountId:r,title:a},{onSuccess:()=>R(!1)})},title:`Purge the health & error log for ${a}?`,message:`Deletes every transition and audit entry recorded for this account. Its status, credentials, and counters are untouched. This cannot be undone.`,confirmLabel:`Purge log`,cancelLabel:`Cancel`}),(0,j.jsx)(D,{justify:`flex-end`,gap:`0px`,children:(0,j.jsx)(o,{variant:`secondary`,size:`sm`,onClick:t,children:`Close`})})]})})}function Q({accountId:e,accountName:t}){let n=F(e),r=P(e),i=n.isPending||r.isPending;return(0,j.jsx)(o,{size:`sm`,variant:`secondary`,title:`Manually claim available ZCode gift plans`,"aria-label":`Claim gifts for ${t}`,disabled:i,icon:i?(0,j.jsx)(a,{size:12,className:`animate-spin`}):(0,j.jsx)(k,{size:12}),onClick:()=>n.mutate(void 0,{onSuccess:e=>{let t=[e.claimed.length>0?`Claimed: ${e.claimed.join(`, `)}`:null,e.detail].filter(Boolean).join(` · `);switch(e.state){case`claimed`:s.success(`Gift plans claimed`,t);break;case`already_claimed`:s.success(`Gift plans already claimed`,t);break;case`nothing`:s.success(`No gift plans to claim`,t);break;case`captcha`:s.warning(`CAPTCHA required — open the ZCode app`,t);break;case`partial`:s.warning(`Gift claim only partially completed`,t);break;case`error`:s.error(`Gift claim failed`,t)}e.claimed.length>0&&r.mutate(void 0,{onSuccess:e=>{e.ok||s.warning(`Gifts claimed; quota refresh failed`,e.message)},onError:e=>s.warning(`Gifts claimed; quota refresh failed`,_(e,`Refresh quota manually`))})},onError:e=>s.error(`Gift claim failed`,_(e,`Unable to claim gift plans`))}),children:i?`Claiming…`:`Claim gifts`})}export{K as a,P as c,q as d,U as f,G as i,I as l,L as m,Z as n,z as o,H as p,V as r,N as s,Q as t,R as u};
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{t as i}from"./arrow-left-CpBxkoxI.js";import{E as a,l as o,s}from"./numbers-CUWAZK4J.js";import{t as ee}from"./circle-check-BU3v_mFW.js";import{n as c,r as l}from"./state-DTARjfiW.js";import{t as u}from"./route-CNSNj9Zm.js";import{t as d}from"./search-JOKus9rb.js";import{t as f}from"./settings-2-Ds9DQp0t.js";import{l as p}from"./query-keys-BloKtaXJ.js";import{et as m,nt as h}from"./entry-LCS85hiq.js";import{n as g,r as _,t as v}from"./card-CtkX66Wd.js";import{n as y}from"./download-dRFo5_Wu.js";import{t as b}from"./switch-c0oPSnqW.js";import{t as x}from"./badge-DfbhQU4k.js";import{n as te}from"./input-BHHX_Tmm.js";import{t as ne}from"./select-BLULNqTp.js";import{t as re}from"./ModelPicker-DnQPSIA8.js";import{t as S}from"./stack-D2lIR0tg.js";import{n as C}from"./inline-C4gU5iOL.js";import{h as ie,t as w}from"./api-keys-l29JROPv.js";import{a as T,i as ae,n as oe,o as E,r as D,t as se}from"./cli-tools-CQXVMn4P.js";import{t as ce}from"./ToolIcon-K1ckcDKg.js";var le=e(`book-open`,[[`path`,{d:`M12 5v16`,key:`1f6ucr`}],[`path`,{d:`M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z`,key:`1fyvmf`}]]),ue=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),de=e(`file-code-corner`,[[`path`,{d:`M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35`,key:`1wthlu`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`m5 16-3 3 3 3`,key:`331omg`}],[`path`,{d:`m9 22 3-3-3-3`,key:`lsp7cz`}]]),O=t(n(),1);function k(e,t){return e==null?!1:e instanceof Set?e.has(t):e.includes(t)}function A(e){return e==null?0:e instanceof Set?e.size:e.length}function j(e){let t=e.lastIndexOf(`/`);return t<0?e:e.slice(t+1)}function M(e,t,n,r){let i=n?`${n}/${j(t)}`:void 0,a=[t,j(t),...i?[i]:[]];r&&r!==t&&a.push(r);let o=e.model_list;return e.model_access_mode===`blacklist`?a.some(e=>k(o,e))?`model-denied`:null:o==null||A(o)===0||a.some(e=>k(o,e))?null:`model-not-allowed`}function N(e,t,n,r){return M(e,t,n,r)===null}var P=r();function F(){let{toolId:e}=h(),t=T(),n=E(),r=w(),i=(0,O.useMemo)(()=>t.data?.find(t=>t.id===e)??null,[t.data,e]),a=(0,O.useMemo)(()=>{let t=n.data;if(t)return Array.isArray(t)?t.find(t=>t.toolId===e):t[e]},[n.data,e]);return t.isLoading?(0,P.jsx)(l,{label:`Loading tool details…`}):t.isError?(0,P.jsx)(c,{title:`Unable to load tool`,message:t.error?.message??`Could not load the CLI tool registry.`,onRetry:()=>void t.refetch()}):i?(0,P.jsx)(I,{tool:i,status:a,apiKeys:r.data??[]}):(0,P.jsxs)(S,{gap:`12px`,children:[(0,P.jsx)(m,{to:`/cli-tools`,className:`text-caption text-accent`,children:`← Back to CLI Tools`}),(0,P.jsx)(c,{title:`Tool not found`,message:`Unknown CLI tool id.`})]})}function I({tool:e,status:t,apiKeys:n}){let[r,c]=(0,O.useState)(``),[l,h]=(0,O.useState)(()=>typeof window>`u`?`http://localhost:12800`:window.location.origin),[w,T]=(0,O.useState)({}),[E,k]=(0,O.useState)(!1),[A,j]=(0,O.useState)({}),[M,F]=(0,O.useState)(null),[I,L]=(0,O.useState)(`slot`),R=(0,O.useRef)(null),z=(0,O.useMemo)(()=>n.filter(e=>e.keyMode===`personal`||e.keyMode===`share`&&e.parentKeyId===void 0),[n]),B=ae(e.id,r),V=D(),H=oe(),U=ie(),W=se(),G=z.find(e=>e.id===r),K=G?.scopes?.includes(`routing:cli_mapping`)===!0,q=G?.keyMode===`personal`,J=G?.modelAccessMode??`whitelist`,Y=G?.modelList,X=e=>N({model_access_mode:J,model_list:Y},e),fe=(0,O.useMemo)(()=>{let t=new Set;if(!e.mappingSupported)return t;for(let n of e.defaultModels){let e=A[n.alias]??``;if(e.length===0)continue;let r=w[n.alias]??n.defaultValue??n.id;!X(e)&&!X(r)&&t.add(n.alias)}return t},[e.defaultModels,e.mappingSupported,A,w,G]),pe=e=>{if(!G)return;let t=Y??[],n=J===`blacklist`?t.filter(t=>t!==e):[...new Set([...t,e])];U.mutate({keyId:G.id,request:{modelAccessMode:J,modelList:n}},{onSuccess:()=>s.success(J===`blacklist`?`Model unblocked for this key`:`Model allowed for this key`),onError:e=>s.error(p(e,`Could not update the API key.`))})};(0,O.useEffect)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));T(t),j({}),R.current=null},[e,r]),(0,O.useEffect)(()=>{let t=B.data;if(t&&t.apiKeyId===r){if(k(t.enabled),t.mappings.length>0)T(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.sourceModel]))})),j(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.targetModel]))}));else{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));T(t),j({})}R.current=JSON.stringify({enabled:t.enabled,mappings:t.mappings})}},[r,B.data,e.defaultModels]);let me=t?.installed??!1,Z=t?.configured??!1,he=e.configType===`guide`,Q=(0,O.useMemo)(()=>e.defaultModels.map(e=>w[e.alias]??e.defaultValue??e.id),[w,e.defaultModels]),$=(0,O.useCallback)(()=>{if(e.mappingSupported)return{enabled:E,mappings:e.defaultModels.filter(e=>(A[e.alias]??``).length>0).map(e=>({slotKey:e.alias,sourceModel:w[e.alias]??e.defaultValue??e.id,targetModel:A[e.alias]??``,enabled:!0})).filter(e=>e.targetModel.length>0)}},[E,A,w,e.defaultModels,e.mappingSupported]);(0,O.useEffect)(()=>{if(!e.mappingSupported||r.length===0)return;let t=B.data;if(!t||t.apiKeyId!==r)return;let n=$();if(!n)return;let i=JSON.stringify({enabled:n.enabled,mappings:n.mappings});if(i===R.current)return;let a=setTimeout(()=>{V.mutate({toolId:e.id,keyId:r,input:n},{onSuccess:()=>{R.current=i},onError:e=>s.error(p(e,`Could not save mappings.`))})},600);return()=>clearTimeout(a)},[$,r,B.data,V,e.id,e.mappingSupported]);let ge=(0,O.useCallback)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,w[e.alias]??e.defaultValue??e.id])),n=e.defaultModels.find(e=>e.roleKind===`subagent`);return{endpoint:l,apiKey:``,modelIds:e.id===`claude`?[]:Q,modelSlots:t,...Q[0]?{activeModel:Q[0]}:{},...n?{subagentModel:w[n.alias]??n.defaultValue??n.id}:{},mappingOwnerId:r,...e.mappingSupported?{mapping:$()}:{}}},[Q,$,r,l,w,e]);return(0,P.jsxs)(S,{gap:`16px`,children:[(0,P.jsxs)(m,{to:`/cli-tools`,className:`inline-flex items-center gap-3 text-caption text-accent`,children:[(0,P.jsx)(i,{size:14}),`Back to CLI Tools`]}),(0,P.jsx)(v,{children:(0,P.jsxs)(g,{className:`flex flex-col gap-5`,children:[(0,P.jsxs)(C,{gap:`14px`,align:`flex-start`,className:`flex-wrap`,children:[(0,P.jsx)(ce,{toolId:e.id,name:e.name,color:e.color,size:46}),(0,P.jsxs)(C,{gap:`8px`,className:`flex-wrap`,children:[(0,P.jsx)(`h1`,{className:`text-title font-bold m-0`,children:e.name}),me?(0,P.jsx)(x,{tone:`ok`,children:`Installed`}):(0,P.jsx)(x,{tone:`disabled`,children:`Not installed`}),Z?(0,P.jsx)(x,{tone:`accent`,children:`Configured`}):null,e.configType===`guide`?(0,P.jsx)(x,{tone:`info`,children:`Guide`}):null]})]}),(0,P.jsx)(`p`,{className:`m-0 text-caption text-text-secondary`,children:e.description}),e.docsUrl?(0,P.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`inline-block text-caption text-accent`,children:`Official documentation →`}):null,t?.settingsPath?(0,P.jsxs)(`div`,{className:`font-mono tabular-nums px-6 py-4 rounded-inset bg-surface-2 border border-inner-border text-badge text-text-secondary break-all`,children:[`Config file: `,t.settingsPath]}):null,t?.message?(0,P.jsxs)(`div`,{className:`flex items-center gap-3 text-badge ${Z?`text-green`:`text-orange`}`,children:[Z?(0,P.jsx)(ee,{size:13}):(0,P.jsx)(ue,{size:13}),t.message]}):null]})}),(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Setup`,subtitle:`Pick the Apikey Name and endpoint, set the models, then download. The key's secret is read server-side — you never paste it.`,icon:(0,P.jsx)(f,{size:16})}),(0,P.jsx)(g,{children:(0,P.jsx)(S,{gap:`12px`,children:(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-3 min-w-0`,children:[(0,P.jsx)(ne,{label:`Apikey Name`,id:`api-key-select`,value:r,onValueChange:c,options:[{value:``,label:`Select…`},...z.map(e=>({value:e.id,label:`${e.label} — ${e.keyMode===`personal`?`Personal`:`Share template`}`}))]}),(0,P.jsx)(`div`,{className:`flex items-center gap-3 flex-wrap`,children:G===void 0?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Choose the Apikey Name whose routes this CLI session should use.`}):K?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(x,{tone:`ok`,children:`routing:cli_mapping`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes work with this key.`})]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(x,{tone:`warn`,title:`This key cannot resolve persisted CLI mappings on /v1/*. Turn on Remote Routing below to enable it.`,children:`no CLI scope`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes stay inert on this key — turning on Remote Routing below will activate it.`})]})}),G?.keyMode===`share`?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Share template: its child keys inherit these routes automatically.`}):null]}),(0,P.jsx)(`div`,{className:`min-w-0`,children:(0,P.jsx)(te,{label:`Endpoint`,value:l,onChange:e=>h(e.target.value)})})]})})})]}),(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Model routing`,icon:(0,P.jsx)(u,{size:16}),subtitle:e.mappingSupported?`Left is the model name the CLI asks for. Right is the route Cartethyia sends it to. Leave a target empty to send that slot straight to the provider.`:`The model names written into this tool's config.`,action:e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex items-center gap-5`,children:[(0,P.jsx)(b,{checked:E,disabled:!r,onChange:t=>{if(k(t),!r){s.error(`Select an API key first to configure its Remote Routing.`);return}if(t&&G&&!K){let e=[...new Set([...G.scopes??[],`routing:cli_mapping`])];U.mutate({keyId:G.id,request:{scopes:e}},{onSuccess:()=>{s.success(`Activated routing:cli_mapping on "${G.label}".`)},onError:e=>{s.error(p(e,`Could not update key scope.`))}})}let n=$();n&&V.mutate({toolId:e.id,keyId:r,input:{...n,enabled:t}},{onError:e=>s.error(p(e,`Could not save mappings.`))})},label:E?`Remote Routing on`:`Remote Routing off`}),(0,P.jsx)(o,{variant:`secondary`,size:`sm`,disabled:!r||H.isPending,onClick:()=>{if(!r){s.error(`Select an API key first.`);return}H.mutate({toolId:e.id,keyId:r},{onSuccess:()=>{k(!1),j({}),s.success(`Remote routes cleared and Remote Routing turned off.`)},onError:e=>s.error(p(e,`Could not reset remote routes.`))})},children:H.isPending?`Resetting…`:`Reset`})]}):void 0}),(0,P.jsxs)(g,{children:[r?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary mb-5`,children:`Select an API key above to load and save remote routes for that key. You can still set models below to download a config file.`}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`mb-5 flex items-center gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3 text-micro text-text-tertiary`,children:[(0,P.jsx)(`span`,{className:`flex-1`,children:`CLI asks for`}),(0,P.jsx)(`span`,{className:`w-9 text-center`,children:`→`}),(0,P.jsx)(`span`,{className:`flex-1`,children:`Cartethyia routes to`})]}):null,(0,P.jsx)(S,{gap:`6px`,children:e.defaultModels.map(t=>{let n=w[t.alias]??t.defaultValue??t.id,r=A[t.alias]??``,i=r===n;return(0,P.jsxs)(`div`,{className:`grid items-center gap-4 ${e.mappingSupported?`grid-cols-2`:`grid-cols-1`}`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`slot`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3.5 text-left text-badge text-text-primary`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:n}),(0,P.jsx)(d,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsx)(`span`,{className:`text-micro text-text-tertiary pl-1`,children:t.roleLabel??t.name})]}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`target`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border px-5 py-3.5 text-left text-badge ${i?`border-inner-border bg-surface-2 text-text-tertiary`:`border-accent bg-accent-soft text-text-primary`}`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:r.length>0?r:`Not routed`}),(0,P.jsx)(d,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsxs)(`span`,{className:`pl-1 text-micro ${r.length===0?`text-text-tertiary`:`text-accent`}`,children:[r.length===0?`not routed`:`rerouted`,r.length>0?(0,P.jsx)(`button`,{type:`button`,onClick:()=>j(e=>({...e,[t.alias]:``})),className:`ml-3 border-none bg-none p-0 text-micro text-text-tertiary cursor-pointer underline`,children:`clear`}):null]}),fe.has(t.alias)?(0,P.jsxs)(`span`,{className:`mt-1 flex items-center gap-3 text-micro text-orange`,children:[(0,P.jsx)(`span`,{children:J===`blacklist`?`This key blocks the target model.`:`This key does not allow the target model.`}),(0,P.jsx)(`button`,{type:`button`,disabled:U.isPending,onClick:()=>pe(r),className:`border-none bg-none p-0 text-micro font-semibold text-accent underline ${U.isPending?`cursor-not-allowed`:`cursor-pointer`}`,children:J===`blacklist`?`Unblock`:`Allow this model`})]}):null]}):null]},t.alias)})})]})]}),he&&e.guideSteps?(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Guide`,subtitle:`Manual setup steps for tools that do not support file injection.`,icon:(0,P.jsx)(le,{size:16})}),(0,P.jsx)(g,{children:(0,P.jsx)(`ol`,{className:`flex flex-col gap-6 pl-9`,children:e.guideSteps.map(e=>(0,P.jsxs)(`li`,{children:[(0,P.jsx)(`strong`,{className:`text-body`,children:e.title}),e.desc?(0,P.jsx)(`p`,{className:`text-caption text-text-secondary mt-2`,children:e.desc}):null,e.value?(0,P.jsx)(`pre`,{className:`mt-3 overflow-x-auto rounded-control border border-inner-border bg-surface-2 p-5 font-mono text-badge`,children:e.value.replace(/\{\{baseUrl\}\}/g,l)}):null]},e.step))})})]}):null,(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Config file`,subtitle:`Download the config file for this CLI tool. The selected API key secret is decrypted automatically into the download.`,icon:(0,P.jsx)(de,{size:16})}),(0,P.jsxs)(g,{className:`flex flex-col gap-6`,children:[(0,P.jsx)(`div`,{className:`flex gap-4 flex-wrap justify-end`,children:(0,P.jsx)(o,{variant:`primary`,icon:(0,P.jsx)(a,{size:14}),onClick:()=>{if(!r){s.error(`Select an API key first.`);return}W.mutate({toolId:e.id,keyId:r,input:ge()},{onSuccess:e=>{y(e.filename,e.content,e.mimeType),s.success(`Downloaded ${e.filename}.`)},onError:e=>s.error(p(e,`Could not download config.`))})},disabled:W.isPending||!q,children:W.isPending?`Downloading…`:`Download config`})}),q?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary text-right`,children:G?.keyMode===`share`?`A share template has no secret to write into a config. Select a personal key to download the config file.`:`Select an Apikey Name above to enable downloading the config file.`})]})]}),(0,P.jsx)(re,{open:M!==null,onClose:()=>F(null),selected:M?[I===`target`?A[M]??w[M]??``:w[M]??``]:[],onToggle:()=>{},onSelectOne:e=>{M&&(I===`target`?j(t=>({...t,[M]:e})):T(t=>({...t,[M]:e}))),F(null)},title:M?`Select ${M} model`:`Select model`,multi:!1})]})}export{F as default};
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e,r as t,t as n}from"./jsx-runtime-CXUMngI_.js";import{t as r}from"./terminal-0_IjPWTk.js";import{n as i,r as a,t as o}from"./state-DTARjfiW.js";import{et as s}from"./entry-LCS85hiq.js";import{n as c,r as l,t as u}from"./card-CtkX66Wd.js";import{t as d}from"./badge-DfbhQU4k.js";import{t as f}from"./stack-D2lIR0tg.js";import{n as p}from"./inline-C4gU5iOL.js";import{a as m,o as h}from"./cli-tools-CQXVMn4P.js";import{t as g}from"./ToolIcon-K1ckcDKg.js";import{n as _}from"./layout-CajrYUnD.js";var v=e(t(),1),y=n();function b(){let e=m(),t=h(),n=(0,v.useMemo)(()=>{let e=new Map,n=t.data;if(!n)return e;if(Array.isArray(n))for(let t of n)t?.toolId&&e.set(t.toolId,t);else if(typeof n==`object`)for(let[t,r]of Object.entries(n))r&&e.set(r.toolId||t,r);return e},[t.data]);if(e.isLoading)return(0,y.jsx)(a,{label:`Loading CLI tools…`});if(e.isError||!e.data)return(0,y.jsx)(i,{title:`Unable to load CLI tools`,message:e.error?.message??`Could not load the CLI tools registry.`,onRetry:()=>void e.refetch()});let b=e.data,x=b.filter(e=>e.configType!==`guide`),S=b.filter(e=>e.configType===`guide`),C=x.filter(e=>n.get(e.id)?.configured).length;return b.length===0?(0,y.jsx)(`div`,{className:`flex-1 flex items-center justify-center p-12 border border-dashed border-transparent rounded-card bg-transparent text-center`,children:(0,y.jsx)(o,{title:`No CLI tools registered`,message:`The registry is empty, so there is nothing to configure yet.`})}):(0,y.jsx)(f,{gap:`16px`,children:(0,y.jsxs)(u,{children:[(0,y.jsx)(l,{title:`CLI Tools`,subtitle:`${C}/${x.length} file-injected tools configured`,icon:(0,y.jsx)(r,{size:16}),action:(0,y.jsx)(d,{tone:`accent`,children:`${b.length} tools`})}),(0,y.jsxs)(c,{className:`flex flex-col gap-8`,children:[(0,y.jsxs)(`section`,{className:`flex flex-col gap-6`,children:[(0,y.jsx)(_,{title:`File-injected tools (${x.length})`,description:`Direct config file writing and preset model mappings`}),(0,y.jsx)(`div`,{className:`provider-grid grid gap-6 w-full min-w-0`,children:x.map(e=>{let t=n.get(e.id);return(0,y.jsx)(u,{interactive:!0,className:`relative overflow-hidden rounded-panel`,children:(0,y.jsx)(s,{to:`/cli-tools/${e.id}`,className:`no-underline text-inherit block p-6`,children:(0,y.jsxs)(p,{gap:`10px`,children:[(0,y.jsx)(g,{toolId:e.id,name:e.name,color:e.color,size:32}),(0,y.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,y.jsx)(`div`,{className:`text-body font-semibold overflow-hidden truncate whitespace-nowrap`,children:e.name}),(0,y.jsx)(`div`,{className:`mt-1.5`,children:t?.configured?(0,y.jsx)(d,{tone:`ok`,dot:!0,children:`Configured`}):t?.installed?(0,y.jsx)(d,{tone:`warn`,dot:!0,children:`Installed`}):(0,y.jsx)(d,{children:`Not installed`})})]}),(0,y.jsxs)(`div`,{className:`flex flex-col items-end gap-1.5 shrink-0`,children:[(0,y.jsx)(`span`,{className:`font-mono tabular-nums text-micro font-semibold bg-kbd-bg py-0.5 px-2.5 rounded-tiny text-text-secondary`,children:e.id}),e.surface?(0,y.jsx)(d,{tone:`default`,children:e.surface}):null]})]})})},e.id)})})]}),(0,y.jsxs)(`section`,{className:`flex flex-col gap-6`,children:[(0,y.jsx)(_,{title:`Guide-only tools (${S.length})`,description:`Manual setup steps and exportable config templates`}),(0,y.jsx)(`div`,{className:`provider-grid grid gap-6 w-full min-w-0`,children:S.map(e=>(0,y.jsx)(u,{interactive:!0,className:`relative overflow-hidden rounded-panel`,children:(0,y.jsx)(s,{to:`/cli-tools/${e.id}`,className:`no-underline text-inherit block p-6`,children:(0,y.jsxs)(p,{gap:`10px`,children:[(0,y.jsx)(g,{toolId:e.id,name:e.name,color:e.color,size:32}),(0,y.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,y.jsx)(`div`,{className:`text-body font-semibold overflow-hidden truncate whitespace-nowrap`,children:e.name}),(0,y.jsx)(`div`,{className:`mt-1.5`,children:(0,y.jsx)(d,{tone:`info`,children:`Guide`})})]}),(0,y.jsxs)(`div`,{className:`flex flex-col items-end gap-1.5 shrink-0`,children:[(0,y.jsx)(`span`,{className:`font-mono tabular-nums text-micro font-semibold bg-kbd-bg py-0.5 px-2.5 rounded-tiny text-text-secondary`,children:e.id}),e.surface?(0,y.jsx)(d,{tone:`default`,children:e.surface}):null]})]})})},e.id))})]})]})]})})}export{b as default};
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{t as i}from"./arrow-right-Dl5yKnp8.js";import{D as a,l as o,s}from"./numbers-CUWAZK4J.js";import{a as c,i as l,o as u,r as d,s as f,t as ee}from"./InjectSystemPromptPanel-B5nZvmf0.js";import{t as p}from"./image-BPaouViG.js";import{n as m,r as h,t as te}from"./state-DTARjfiW.js";import{t as g}from"./layers-2-glBbjl.js";import{r as _}from"./use-modal-focus-By7oJoGd.js";import{t as v}from"./dialog-BEK7KP0H.js";import{t as ne}from"./pencil-768ysRAr.js";import{t as y}from"./plus-DIdX_g-x.js";import{t as b}from"./route-CNSNj9Zm.js";import{t as re}from"./search-JOKus9rb.js";import{t as ie}from"./trash-Cppn5c3R.js";import{l as x}from"./query-keys-BloKtaXJ.js";import{t as ae}from"./dialog-actions-B1axpEg7.js";import{z as S}from"./entry-LCS85hiq.js";import{n as C,r as w,t as T}from"./card-CtkX66Wd.js";import{t as oe}from"./use-clipboard-B0Sy03fE.js";import{t as E}from"./badge-DfbhQU4k.js";import{n as D,r as O}from"./input-BHHX_Tmm.js";import{t as k}from"./select-BLULNqTp.js";import{t as A}from"./ModelPicker-DnQPSIA8.js";import{t as j}from"./stack-D2lIR0tg.js";import{n as M,t as se}from"./inline-C4gU5iOL.js";import{n as N,t as P}from"./settings-CcGczj6I.js";import{a as ce,d as F,f as I,i as L,n as R,o as z,p as B,r as V,s as le,t as H,u as ue}from"./routing-in0f_IfK.js";import{t as U}from"./list-row-Bx2I7XUe.js";import{t as de}from"./use-timeout-BwcBBRrT.js";var fe=e(`copy-plus`,[[`line`,{x1:`15`,x2:`15`,y1:`12`,y2:`18`,key:`1p7wdc`}],[`line`,{x1:`12`,x2:`18`,y1:`15`,y2:`15`,key:`1nscbv`}],[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),W=t(n(),1),G=r(),K=[{value:`append`,label:`Append`},{value:`override`,label:`Override`}];function q(e,t){if(e.kind===`off`)return{label:`Off`,active:!1};if(e.kind===`global`)return{label:`Following global`,active:!1};if(e.kind===`preset`){let n=t[e.presetId];return{label:`Preset · ${n?.name??e.presetId}`,active:n?.enabled===!0}}return{label:e.setting.enabled?`Custom`:`Custom off`,active:e.setting.enabled}}function pe({kind:e,name:t,value:n,onChange:r,presets:i}){let a=n,{label:o,active:s}=q(a,i),c=a.kind,l=Object.values(i);return(0,G.jsxs)(`div`,{className:`rounded-control border border-inner-border bg-surface-2 p-4`,children:[(0,G.jsxs)(`div`,{className:`flex items-center justify-between gap-3`,children:[(0,G.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,G.jsx)(f,{size:14,className:`shrink-0 text-text-tertiary`,"aria-hidden":`true`}),(0,G.jsx)(`p`,{className:`text-caption font-semibold text-text-primary`,children:`System prompt`})]}),(0,G.jsx)(E,{tone:s?`ok`:`default`,dot:!0,className:`shrink-0`,children:o})]}),(0,G.jsxs)(`div`,{className:`mt-3 grid grid-cols-4 gap-2 max-small:grid-cols-2`,role:`radiogroup`,"aria-label":`System prompt source`,children:[(0,G.jsxs)(`label`,{className:`flex cursor-pointer items-center justify-center gap-2 rounded-control border border-inner-border bg-surface-2 px-2 py-2 text-badge font-medium text-text-primary select-none has-checked:border-accent has-checked:bg-accent-soft`,children:[(0,G.jsx)(`input`,{type:`radio`,name:`scp-${e}-${t}`,checked:c===`off`,onChange:()=>r({kind:`off`}),className:`accent-accent`}),`Off`]}),(0,G.jsxs)(`label`,{className:`flex cursor-pointer items-center justify-center gap-2 rounded-control border border-inner-border bg-surface-2 px-2 py-2 text-badge font-medium text-text-primary select-none has-checked:border-accent has-checked:bg-accent-soft`,children:[(0,G.jsx)(`input`,{type:`radio`,name:`scp-${e}-${t}`,checked:c===`global`,onChange:()=>r({kind:`global`}),className:`accent-accent`}),`Global`]}),(0,G.jsxs)(`label`,{className:`flex cursor-pointer items-center justify-center gap-2 rounded-control border border-inner-border bg-surface-2 px-2 py-2 text-badge font-medium text-text-primary select-none has-checked:border-accent has-checked:bg-accent-soft`,children:[(0,G.jsx)(`input`,{type:`radio`,name:`scp-${e}-${t}`,checked:c===`preset`,onChange:()=>{let e=l.find(e=>e.enabled)??l[0];r(e?{kind:`preset`,presetId:e.id}:{kind:`global`})},className:`accent-accent`}),`Preset`]}),(0,G.jsxs)(`label`,{className:`flex cursor-pointer items-center justify-center gap-2 rounded-control border border-inner-border bg-surface-2 px-2 py-2 text-badge font-medium text-text-primary select-none has-checked:border-accent has-checked:bg-accent-soft`,children:[(0,G.jsx)(`input`,{type:`radio`,name:`scp-${e}-${t}`,checked:c===`custom`,onChange:()=>r({kind:`custom`,setting:{enabled:!0,mode:`append`,text:``}}),className:`accent-accent`}),`Custom`]})]}),c===`preset`?(0,G.jsxs)(`div`,{className:`mt-3`,children:[(0,G.jsx)(k,{"aria-label":`Preset`,value:a.kind===`preset`?a.presetId:``,onValueChange:e=>r({kind:`preset`,presetId:e}),options:l.map(e=>({value:e.id,label:`${e.name}${e.enabled?``:` (off)`}`})),size:`sm`,className:`w-full`}),l.length===0?(0,G.jsx)(`p`,{className:`mt-2 text-badge text-text-tertiary`,children:`No presets yet — create one in Inject System Prompt first.`}):null]}):null,c===`custom`?(0,G.jsxs)(`div`,{className:`mt-3`,children:[(0,G.jsxs)(`div`,{className:`mb-2 flex items-center gap-3`,children:[(0,G.jsx)(k,{"aria-label":`Injection mode`,value:a.kind===`custom`?a.setting.mode:`append`,onValueChange:e=>r({kind:`custom`,setting:{enabled:!0,mode:e,text:a.kind===`custom`?a.setting.text:``}}),options:K,size:`sm`,className:`w-40`}),a.kind===`custom`?(0,G.jsxs)(`span`,{className:`text-badge tabular-nums text-text-tertiary`,children:[a.setting.text.length.toLocaleString(),` chars`]}):null]}),(0,G.jsx)(O,{id:`scp-${e}-${t}-text`,rows:4,value:a.kind===`custom`?a.setting.text:``,onChange:e=>r({kind:`custom`,setting:{enabled:!0,mode:a.kind===`custom`?a.setting.mode:`append`,text:e.target.value}}),placeholder:`Injected system prompt…`})]}):null]})}var me=[{value:`fallback`,label:`Fallback (try in order)`},{value:`round_robin`,label:`Round Robin (rotate)`}],J=()=>`m${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`;function Y(){let e=z(),t=R(),n=I(),r=L(),f=ue(),[p,g]=(0,W.useState)(!1),[_,E]=(0,W.useState)(null),[O,k]=(0,W.useState)(``),[N,P]=(0,W.useState)(``),[ce,F]=(0,W.useState)(!1),[B,V]=(0,W.useState)(null),[le,H]=(0,W.useState)(null),{copy:fe}=oe(),K=de(),q=l(),me=c(),J=d(q.data??ee),[Y,X]=(0,W.useState)({kind:`global`}),Z=(0,W.useMemo)(()=>e.data??[],[e.data]),he=()=>{E(null),k(``),P(``),g(!0)},ge=e=>{E(e),k(e.alias),P(e.targetModel),X(J.aliases[e.alias]??{kind:`global`}),g(!0)},_e=e=>{if(e.preventDefault(),O.trim()&&N.trim()){if(_){let e=O.trim();n.mutate({id:_.id,request:{alias:e,targetModel:N.trim()}},{onSuccess:()=>{let t={...J,aliases:{...J.aliases,[e]:Y}};_.alias!==e&&delete t.aliases[_.alias],me.mutate(t,{onError:e=>s.error(x(e,`Could not update system prompt.`))}),g(!1),E(null),s.success(`Alias updated`)},onError:e=>s.error(x(e,`Could not save the alias.`))})}else t.mutate({alias:O.trim(),targetModel:N.trim()},{onSuccess:()=>{g(!1),k(``),P(``),s.success(`Alias created`)},onError:e=>s.error(x(e,`Could not create the alias.`))})}},ve=(e,t)=>{fe(e).then(e=>{if(!e){s.error(`Copy failed`);return}H(t),K(()=>H(null),1500)})};return(0,G.jsxs)(T,{children:[(0,G.jsx)(w,{title:`Model Aliases`,subtitle:`Map readable names to real models (e.g. claude-mythos-5 → claude-opus-5)`,icon:(0,G.jsx)(b,{size:16}),action:(0,G.jsx)(se,{children:(0,G.jsx)(o,{variant:`primary`,size:`sm`,icon:(0,G.jsx)(y,{size:13}),onClick:he,children:`New Alias`})})}),(0,G.jsx)(C,{children:e.isPending?(0,G.jsx)(h,{label:`Loading aliases...`}):e.isError?(0,G.jsx)(m,{message:`Failed to load aliases`,onRetry:()=>e.refetch()}):Z.length===0?(0,G.jsx)(te,{title:`No aliases defined`,message:`Create an alias to route short or friendly names to real provider model IDs.`}):(0,G.jsx)(u,{items:Z,label:`Model aliases`,gap:`8px`,disabled:f.isPending,onReorder:e=>f.mutate(e,{onError:e=>s.error(x(e,`Could not save the new order.`))}),renderItem:e=>(0,G.jsx)(U,{className:`items-start flex-col sm:flex-row`,actions:(0,G.jsxs)(M,{gap:`4px`,className:`w-full justify-start sm:w-auto sm:justify-end`,children:[(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(a,{size:13}),onClick:()=>ve(e.alias,e.id),title:le===e.id?`Copied!`:`Copy alias`}),(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(ne,{size:13}),onClick:()=>ge(e),title:`Edit target model`}),(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(ie,{size:13}),onClick:()=>V(e),title:`Delete alias`,className:`text-red`})]}),children:(0,G.jsxs)(`div`,{className:`flex items-center gap-4 flex-wrap min-w-0`,children:[(0,G.jsx)(S,{value:e.alias,tone:`accent`}),(0,G.jsx)(i,{size:13,className:`text-text-tertiary shrink-0`}),(0,G.jsx)(S,{value:e.targetModel,tone:`plain`})]})})})}),(0,G.jsx)(v,{open:p,onClose:()=>g(!1),title:_?`Edit Model Alias`:`New Model Alias`,footer:(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(o,{variant:`secondary`,type:`button`,onClick:()=>g(!1),children:`Cancel`}),(0,G.jsx)(o,{variant:`primary`,type:`submit`,form:`alias-form`,disabled:t.isPending||n.isPending||!O.trim()||!N.trim(),children:_?`Save Changes`:`Create Alias`})]}),children:(0,G.jsxs)(`form`,{id:`alias-form`,onSubmit:_e,className:`flex flex-col gap-6`,children:[(0,G.jsx)(D,{label:`Alias name`,id:`alias-name-input`,value:O,onChange:e=>k(e.target.value),placeholder:`e.g. fast, sonnet, smart`,required:!0}),(0,G.jsxs)(M,{gap:`8px`,align:`flex-end`,children:[(0,G.jsx)(`div`,{className:`flex-1`,children:(0,G.jsx)(D,{label:`Target model`,id:`target-model-input`,value:N,onChange:e=>P(e.target.value),placeholder:`e.g. codex/gpt-5.5, claude/claude-sonnet-5`,required:!0})}),(0,G.jsx)(o,{variant:`secondary`,size:`sm`,type:`button`,onClick:()=>F(!0),icon:(0,G.jsx)(re,{size:13}),children:`Browse`})]}),(0,G.jsx)(A,{open:ce,onClose:()=>F(!1),selected:N?[N]:[],onToggle:()=>{},onSelectOne:e=>P(e),title:`Select target model`,multi:!1}),_?(0,G.jsx)(pe,{kind:`alias`,name:_.alias,value:Y,onChange:X,presets:J.presets}):null]})}),(0,G.jsx)(v,{open:!!B,onClose:()=>V(null),title:`Delete Model Alias`,children:(0,G.jsxs)(j,{gap:`12px`,children:[(0,G.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete alias `,(0,G.jsx)(`strong`,{children:B?.alias}),`? Clients requesting this name will need their full model path.`]}),(0,G.jsxs)(ae,{children:[(0,G.jsx)(o,{variant:`secondary`,onClick:()=>V(null),children:`Cancel`}),(0,G.jsx)(o,{variant:`danger`,onClick:()=>{B&&r.mutate(B.id,{onSuccess:()=>V(null)})},disabled:r.isPending,children:r.isPending?`Deleting...`:`Delete Alias`})]})]})})]})}function X(e){return e.kind===`model`&&e.entry.vision===!0}function Z(){let e=P(),t=N(),[n,r]=(0,W.useState)(!1),i=e.data,a=i?.visionHandoffModels??[],[c,l]=(0,W.useState)([]);(0,W.useEffect)(()=>{l(a.map(e=>({id:`v${e}`,value:e})))},[i]);let d=(e,n)=>{t.mutate(e,{onSuccess:()=>s.success(n),onError:e=>s.error(x(e,`Could not update vision handoff.`))})},f=e=>{let t=c.some(t=>t.value===e)?c.filter(t=>t.value!==e):[...c,{id:`v${e}-${c.length}`,value:e}];l(t),d({visionHandoffModels:t.map(e=>e.value)},`Vision handoff models updated.`)};return e.isPending?(0,G.jsx)(h,{label:`Loading vision handoff settings…`}):e.isError||!i?(0,G.jsx)(m,{message:x(e.error,`Failed to load runtime settings`),onRetry:()=>void e.refetch()}):(0,G.jsxs)(T,{children:[(0,G.jsx)(w,{title:`Vision Handoff`,subtitle:`When the selected model cannot see images, describe them with a vision model first`,icon:(0,G.jsx)(p,{size:16}),action:(0,G.jsx)(se,{children:(0,G.jsx)(o,{variant:`secondary`,size:`sm`,icon:(0,G.jsx)(y,{size:13}),onClick:()=>r(!0),children:`Pick models`})})}),(0,G.jsx)(C,{children:c.length===0?(0,G.jsx)(te,{title:`No vision handoff models`,message:`Add an ordered list of image-capable models. When a request carries an image but the chosen model cannot see it, the gateway will ask the first healthy model on this list to describe it and pass that description to the chosen model as text.`}):(0,G.jsx)(u,{items:c,label:`Vision handoff models`,gap:`6px`,onReorder:e=>{let t=new Map(c.map(e=>[e.id,e])),n=e.map(e=>t.get(e)).filter(e=>!!e);l(n),d({visionHandoffModels:n.map(e=>e.value)},`Vision handoff order updated.`)},renderItem:e=>(0,G.jsx)(U,{density:`compact`,actions:(0,G.jsx)(`button`,{type:`button`,"aria-label":`Remove ${e.value}`,onClick:()=>f(e.value),className:`grid size-7 cursor-pointer place-items-center rounded-control text-text-tertiary hover:bg-hover`,children:(0,G.jsx)(_,{size:12})}),children:(0,G.jsxs)(`div`,{className:`flex items-center gap-3 min-w-0`,children:[(0,G.jsx)(p,{size:14,className:`shrink-0 text-text-tertiary`}),(0,G.jsx)(S,{value:e.value,tone:`accent`})]})})})}),(0,G.jsx)(A,{open:n,onClose:()=>r(!1),selected:c.map(e=>e.value),onToggle:f,title:`Select vision models`,multi:!0,filter:X})]})}function he(){let e=le(),t=V(),n=H(),r=B(),i=ce(),f=F(),[p,b]=(0,W.useState)(!1),[E,O]=(0,W.useState)(null),[N,P]=(0,W.useState)(``),[I,L]=(0,W.useState)([]),[R,z]=(0,W.useState)(`fallback`),[ue,K]=(0,W.useState)(!1),[q,Y]=(0,W.useState)(null),[X,Z]=(0,W.useState)(null),{copy:he}=oe(),ge=de(),_e=l(),ve=c(),Q=d(_e.data??ee),[ye,be]=(0,W.useState)({kind:`global`}),$=(0,W.useMemo)(()=>I.map(e=>e.value),[I]),xe=e=>{$.includes(e)?L(I.filter(t=>t.value!==e)):L([...I,{id:J(),value:e.trim()}])},Se=e.data??[],Ce=()=>{O(null),P(``),L([]),z(`fallback`),b(!0)},we=e=>{O(e),P(e.name),be(Q.combos[e.name]??{kind:`global`}),L(e.members.map(e=>({id:J(),value:e}))),b(!0)},Te=e=>{n.mutate(e.id,{onSuccess:e=>{let t=e.skippedMembers;t.length>0?s.success(`Combo cloned`,`${e.combo.name} — skipped unresolvable member${t.length>1?`s`:``}: ${t.join(`, `)}`):s.success(`Combo cloned`,e.combo.name)},onError:e=>s.error(x(e,`Could not clone the combo.`))})},Ee=e=>{if(e.preventDefault(),N.trim()&&$.length!==0){if(E){let e=N.trim();r.mutate({id:E.id,request:{name:e,members:$,strategy:R}},{onSuccess:()=>{let t={...Q,combos:{...Q.combos,[e]:ye}};E.name!==e&&delete t.combos[E.name],ve.mutate(t,{onError:e=>s.error(x(e,`Could not update system prompt.`))}),b(!1),O(null),s.success(`Combo saved`)},onError:e=>s.error(x(e,`Could not save the combo.`))})}else t.mutate({name:N.trim(),members:$,strategy:R},{onSuccess:()=>{b(!1),P(``),L([]),s.success(`Combo created`)},onError:e=>s.error(x(e,`Could not create the combo.`))})}},De=(e,t)=>{r.mutate({id:e.id,request:{strategy:t}},{onSuccess:()=>s.success(`Combo strategy updated`),onError:e=>s.error(x(e,`Could not update the combo strategy.`))})},Oe=(e,t)=>{he(e).then(e=>{if(!e){s.error(`Copy failed`);return}Z(t),ge(()=>Z(null),1500)})};return(0,G.jsxs)(T,{children:[(0,G.jsx)(w,{title:`Combos`,subtitle:`Combine multiple models with fallback or round-robin rotation`,icon:(0,G.jsx)(g,{size:16}),action:(0,G.jsx)(se,{children:(0,G.jsx)(o,{variant:`primary`,size:`sm`,icon:(0,G.jsx)(y,{size:13}),onClick:Ce,children:`New Combo`})})}),(0,G.jsx)(C,{children:e.isPending?(0,G.jsx)(h,{label:`Loading combos...`}):e.isError?(0,G.jsx)(m,{message:`Failed to load combos`,onRetry:()=>e.refetch()}):Se.length===0?(0,G.jsx)(te,{title:`No combos defined`,message:`Create a model combo to distribute requests across multiple models.`}):(0,G.jsx)(u,{items:Se,label:`Model combos`,disabled:f.isPending,onReorder:e=>f.mutate(e,{onError:e=>s.error(x(e,`Could not save the new order.`))}),renderItem:e=>(0,G.jsxs)(U,{className:`items-start flex-col sm:flex-row`,actions:(0,G.jsxs)(M,{gap:`8px`,className:`w-full justify-between sm:w-auto sm:justify-start`,children:[(0,G.jsx)(k,{value:e.strategy,onValueChange:t=>De(e,t),options:me}),(0,G.jsxs)(M,{gap:`4px`,className:`flex-wrap`,children:[(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(a,{size:13}),onClick:()=>Oe(e.name,e.id),title:X===e.id?`Copied!`:`Copy combo name`}),(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(fe,{size:13}),onClick:()=>Te(e),disabled:n.isPending,title:`Clone combo (same members and strategy)`}),(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(ne,{size:13}),onClick:()=>we(e),title:`Edit combo`}),(0,G.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,G.jsx)(ie,{size:13}),onClick:()=>Y(e),title:`Delete combo`,className:`text-red`})]})]}),children:[(0,G.jsx)(`div`,{className:`flex items-center justify-between gap-6 flex-wrap`,children:(0,G.jsx)(`strong`,{className:`text-body`,children:e.name})}),(0,G.jsx)(M,{gap:`6px`,className:`w-full flex-wrap`,children:e.members.map(e=>(0,G.jsx)(S,{value:e},e))})]})})}),(0,G.jsx)(v,{open:p,onClose:()=>b(!1),title:E?`Edit Model Combo`:`New Model Combo`,footer:(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(o,{variant:`secondary`,type:`button`,onClick:()=>b(!1),children:`Cancel`}),(0,G.jsx)(o,{variant:`primary`,type:`submit`,form:`combo-form`,disabled:t.isPending||r.isPending||!N.trim()||$.length===0,children:E?`Save Changes`:`Create Combo`})]}),children:(0,G.jsxs)(`form`,{id:`combo-form`,onSubmit:Ee,className:`flex flex-col gap-6`,children:[(0,G.jsx)(D,{label:`Combo name`,id:`combo-name-input`,value:N,onChange:e=>P(e.target.value),placeholder:`e.g. smart-combo, fast-pool`,required:!0}),(0,G.jsx)(k,{label:`Strategy`,id:`combo-strategy-select`,value:R,onValueChange:e=>z(e),options:me}),E?(0,G.jsx)(pe,{kind:`combo`,name:E.name,value:ye,onChange:be,presets:Q.presets}):null,(0,G.jsxs)(j,{gap:`6px`,children:[(0,G.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,G.jsx)(`label`,{className:`text-caption font-semibold text-text-secondary`,children:`Member models — use arrows or drag to reorder`}),(0,G.jsx)(o,{variant:`secondary`,size:`sm`,type:`button`,icon:(0,G.jsx)(re,{size:12}),onClick:()=>K(!0),children:`Browse models`})]}),(0,G.jsx)(`div`,{role:`list`,"aria-label":`Combo members`,"data-testid":`combo-members-list`,className:`flex min-h-22 flex-col gap-2 rounded-control border border-dashed border-inner-border ${I.length===0?`p-0`:`p-3`}`,children:I.length===0?(0,G.jsx)(`button`,{type:`button`,onClick:()=>K(!0),className:`flex-1 cursor-pointer font-inherit text-caption text-text-tertiary`,children:`Pick models to add`}):(0,G.jsx)(u,{items:I,label:`Combo members`,gap:`4px`,onReorder:e=>{let t=new Map(I.map(e=>[e.id,e]));L(e.map(e=>t.get(e)).filter(Boolean))},renderItem:e=>(0,G.jsx)(U,{density:`compact`,actions:(0,G.jsx)(`button`,{type:`button`,onClick:()=>xe(e.value),"aria-label":`Remove ${e.value}`,className:`flex shrink-0 cursor-pointer text-text-tertiary`,children:(0,G.jsx)(_,{size:12})}),children:(0,G.jsx)(S,{value:e.value,tone:`accent`})})})})]}),(0,G.jsx)(A,{open:ue,onClose:()=>K(!1),selected:$,onToggle:xe,title:`Select combo members`,multi:!0})]})}),(0,G.jsx)(v,{open:!!q,onClose:()=>Y(null),title:`Delete Model Combo`,children:(0,G.jsxs)(j,{gap:`12px`,children:[(0,G.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete combo `,(0,G.jsx)(`strong`,{children:q?.name}),`? Any aliases pointing to it will fail to resolve.`]}),(0,G.jsxs)(ae,{children:[(0,G.jsx)(o,{variant:`secondary`,onClick:()=>Y(null),children:`Cancel`}),(0,G.jsx)(o,{variant:`danger`,onClick:()=>{q&&i.mutate(q.id,{onSuccess:()=>Y(null)})},disabled:i.isPending,children:i.isPending?`Deleting...`:`Delete Combo`})]})]})})]})}function ge(){return(0,G.jsxs)(j,{gap:`16px`,children:[(0,G.jsx)(he,{}),(0,G.jsx)(Y,{}),(0,G.jsx)(Z,{})]})}export{Z as VisionHandoffSection,ge as default};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e,r as t,t as n}from"./jsx-runtime-CXUMngI_.js";import{l as r}from"./numbers-CUWAZK4J.js";import{t as i}from"./log-in-BmRjy60c.js";import{o as a,t as o}from"./query-keys-BloKtaXJ.js";import{R as s,rt as c,tt as l}from"./entry-LCS85hiq.js";import{n as u}from"./input-BHHX_Tmm.js";var d=e(t(),1),f=n();function p(e){return e?.startsWith(`/`)&&!e.startsWith(`//`)?e:`/`}function m(){let e=l(),[t]=c(),[n,m]=(0,d.useState)(``),[h,g]=(0,d.useState)(``),[_,v]=(0,d.useState)(null),[y,b]=(0,d.useState)(!1),[x,S]=(0,d.useState)(!0);return(0,d.useEffect)(()=>{let t=!0;return a(`/auth/first-boot`).then(n=>{if(t){if(n.requires_setup){e(`/setup`,{replace:!0});return}S(!1)}}).catch(()=>{t&&S(!1)}),()=>{t=!1}},[e]),x?(0,f.jsx)(`main`,{className:`auth-viewport`,children:(0,f.jsx)(`div`,{className:`card-solid auth-window`,children:(0,f.jsxs)(`div`,{className:`auth-header`,children:[(0,f.jsx)(`div`,{className:`auth-logo`,"aria-hidden":`true`}),(0,f.jsx)(`h1`,{className:`auth-title`,children:`Cartethyia Console`}),(0,f.jsx)(`p`,{className:`auth-desc`,children:`Checking console setup...`})]})})}):(0,f.jsx)(`main`,{className:`auth-viewport`,children:(0,f.jsxs)(`div`,{className:`card-solid auth-window`,children:[(0,f.jsxs)(`div`,{className:`auth-header`,children:[(0,f.jsx)(`div`,{className:`auth-logo`,"aria-hidden":`true`}),(0,f.jsx)(`h1`,{className:`auth-title`,children:`Cartethyia Console`}),(0,f.jsx)(`p`,{className:`auth-desc`,children:`Sign in to manage AI Gateway routing and providers`})]}),(0,f.jsxs)(`form`,{onSubmit:async r=>{r.preventDefault(),b(!0),v(null);try{let r=await a(`/auth/login`,{method:`POST`,body:JSON.stringify({username:n.trim(),password:h})});if(r.status!==`success`){if(r.requires_setup){e(`/setup`,{replace:!0});return}g(``),v(r.message??`Authentication failed. Please check your credentials.`);return}s.removeQueries({queryKey:o.session.current}),e(r.requires_setup?`/setup`:p(t.get(`returnTo`)),{replace:!0})}catch(e){if(e&&typeof e==`object`&&`message`in e){let t=e.message;if(typeof t==`string`){g(``),v(t);return}}v(`An unexpected network error occurred.`)}finally{b(!1)}},className:`flex flex-col gap-7`,children:[(0,f.jsx)(u,{label:`Username`,id:`login-username`,value:n,onChange:e=>m(e.target.value),required:!0,autoComplete:`username`,placeholder:`admin`}),(0,f.jsx)(u,{label:`Password`,id:`login-password`,type:`password`,value:h,onChange:e=>g(e.target.value),required:!0,autoComplete:`current-password`}),_?(0,f.jsx)(`div`,{className:`py-5 px-7 rounded-control bg-red-soft text-red text-caption font-medium`,role:`alert`,children:_}):null,(0,f.jsx)(r,{variant:`primary`,type:`submit`,disabled:y,className:`w-full mt-3 h-20`,icon:(0,f.jsx)(i,{size:15}),children:y?`Authenticating…`:`Sign In`})]})]})})}export{m as default};
|
|
Binary file
|
|
Binary file
|