cartethyia 2.7.5 → 2.7.6
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 +4 -1
- package/README.md +1 -1
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-KRYjHZsX.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-d5v3K_Yc.js} +245 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.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-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.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-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-BW4AKj2H.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.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-B7M7LvMM.js} +13 -13
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.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-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.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/{formatters-BAACA727.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.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-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.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-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.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/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +10 -2
- package/src/config/providers.ts +13 -7
- package/src/config/spec.ts +18 -11
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/routes.ts +19 -23
- 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 +28 -5
- 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/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 +7 -0
- package/src/persistence/schema.ts +8 -0
- package/src/persistence/stores/api-key-store.ts +19 -1
- package/src/protocol/dsml.ts +5 -76
- package/src/providers/adapters/compatible-adapter.ts +29 -1
- package/src/providers/default-registry.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -14
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +20 -11
- 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/qoder-catalog.ts +9 -3
- package/src/providers/integrations/qoder/qoder.ts +147 -88
- 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-usage.ts +15 -49
- 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/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/router.ts +7 -0
- 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 +0 -1
- 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.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
|
@@ -388,9 +388,11 @@ export async function resolveApiKeyAuthorization(
|
|
|
388
388
|
tenant_id: row.tenantId,
|
|
389
389
|
// Family quota / concurrency share one admission counter namespace so two
|
|
390
390
|
// recipients cannot each burn a full lifetime/daily budget. In `per_key`
|
|
391
|
-
// concurrency mode the child
|
|
392
|
-
//
|
|
393
|
-
|
|
391
|
+
// concurrency mode the child keeps its own cap and counter for
|
|
392
|
+
// *concurrency* only — token budgets (daily/monthly/lifetime) stay
|
|
393
|
+
// family-wide on the parent, so the admission identity is always the
|
|
394
|
+
// parent's.
|
|
395
|
+
...(parent ? { admission_identity: parent.id } : {}),
|
|
394
396
|
...(policy.modelList ? { model_list: policy.modelList as string[] } : {}),
|
|
395
397
|
model_access_mode: policy.modelAccessMode ?? "whitelist",
|
|
396
398
|
...(policy.clientRouterDenylist
|
|
@@ -16,6 +16,7 @@ import { finalizeRequestTelemetry } from "../guards/error-lifecycle";
|
|
|
16
16
|
import { debitCachedQuota, quotaLens } from "../../providers/quota/cache";
|
|
17
17
|
import { BUDDY_PROVIDER_IDS } from "../../providers/provider-metadata";
|
|
18
18
|
import { log } from "../../observability/logger";
|
|
19
|
+
import { modelsDevCatalog } from "../../providers/discovery/models-dev-catalog";
|
|
19
20
|
import {
|
|
20
21
|
disablePoolForProxyHttpStatus,
|
|
21
22
|
recordPoolDispatchOutcome,
|
|
@@ -711,6 +712,32 @@ async function debitBuddySpend(completion: AttemptCompletion): Promise<void> {
|
|
|
711
712
|
}
|
|
712
713
|
}
|
|
713
714
|
|
|
715
|
+
/**
|
|
716
|
+
* Whether a completed attempt is a "degenerate stop": output far smaller than
|
|
717
|
+
* the model's ceiling, ended by an ordinary `stop` rather than `length`.
|
|
718
|
+
*
|
|
719
|
+
* This is the signature of a model truncating itself on a cached prefix — the
|
|
720
|
+
* pattern that surfaced as short, cut-off chat replies that the upstream still
|
|
721
|
+
* reported as a clean stop. It is measured so the operator can see how often
|
|
722
|
+
* it happens instead of eyeballing request detail.
|
|
723
|
+
*/
|
|
724
|
+
export function isDegenerateStop(
|
|
725
|
+
usage: UsageRecord | undefined,
|
|
726
|
+
outputLimit: number | undefined,
|
|
727
|
+
): boolean {
|
|
728
|
+
if (usage === undefined) return false;
|
|
729
|
+
const output = usage.output_tokens;
|
|
730
|
+
const reasoning = typeof usage.reasoning_tokens === "number" ? usage.reasoning_tokens : 0;
|
|
731
|
+
const totalOutput = output + reasoning;
|
|
732
|
+
if (totalOutput === 0 || totalOutput > 512) return false;
|
|
733
|
+
// A model with no meaningful ceiling cannot be "far smaller than it can
|
|
734
|
+
// produce", so the flag is only meaningful when a ceiling exists.
|
|
735
|
+
if (!(typeof outputLimit === "number" && Number.isFinite(outputLimit) && outputLimit > 0)) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
return totalOutput <= Math.max(1, Math.floor(outputLimit / 10));
|
|
739
|
+
}
|
|
740
|
+
|
|
714
741
|
export async function completeAttempt(
|
|
715
742
|
state: ProxyRequestState,
|
|
716
743
|
completion: AttemptCompletion,
|
|
@@ -761,6 +788,15 @@ export async function completeAttempt(
|
|
|
761
788
|
...(completion.firstContentDeltaAtMs === undefined ? {} : { firstContentDeltaAtMs: completion.firstContentDeltaAtMs }),
|
|
762
789
|
...(completion.lastEventAtMs === undefined ? {} : { lastEventAtMs: completion.lastEventAtMs }),
|
|
763
790
|
...(completion.reportedModel === undefined ? {} : { reportedModel: completion.reportedModel }),
|
|
791
|
+
...(completion.status === "completed" &&
|
|
792
|
+
isDegenerateStop(
|
|
793
|
+
completion.usage,
|
|
794
|
+
completion.providerId !== undefined && completion.modelId !== undefined
|
|
795
|
+
? (modelsDevCatalog.resolve(completion.providerId, completion.modelId)?.outputLimit ?? undefined)
|
|
796
|
+
: undefined,
|
|
797
|
+
)
|
|
798
|
+
? { degenerateStop: true }
|
|
799
|
+
: {}),
|
|
764
800
|
};
|
|
765
801
|
if (completion.terminal) state.completed = true;
|
|
766
802
|
// Usage reconciliation is bookkeeping, and it is the last thing standing
|
|
@@ -785,27 +821,39 @@ export async function completeAttempt(
|
|
|
785
821
|
// advisory: a failed report must never re-enter the retry path (a retry
|
|
786
822
|
// would see `completed` and skip completion entirely), so it is swallowed.
|
|
787
823
|
if (completion.status !== "cancelled" && !disableProxy) {
|
|
788
|
-
//
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
824
|
+
// Account health records only failures that are evidence about the
|
|
825
|
+
// provider's own account: upstream verdicts (quota, auth, model
|
|
826
|
+
// capacity, rate limits), or credential-scoped refusals from the
|
|
827
|
+
// upstream. Local rejections — API-key admission (`max_concurrent`,
|
|
828
|
+
// rpm/token budgets), routing capacity, pool selection, request
|
|
829
|
+
// preparation — are decisions this gateway made about its own limits;
|
|
830
|
+
// they say nothing about the account and must not cool it or write a
|
|
831
|
+
// phantom `logged -> cooldown` transition. Success always reports so a
|
|
832
|
+
// cooling account recovers.
|
|
833
|
+
const succeeded = completion.status === "completed";
|
|
834
|
+
const gatewayError = completion.error instanceof GatewayError ? completion.error : undefined;
|
|
835
|
+
const upstreamVerdict = gatewayError?.origin === "upstream";
|
|
836
|
+
const isProxyDeploymentError =
|
|
837
|
+
completion.error instanceof Error ? completion.error.message.toLowerCase().includes("deployment_disabled") || (completion.error.message.toLowerCase().includes("sin1::") && completion.error.message.toLowerCase().includes("deployment")) : false;
|
|
838
|
+
if (succeeded || isProxyDeploymentError || upstreamVerdict) {
|
|
839
|
+
const evidence = isProxyDeploymentError
|
|
840
|
+
? { scope: "pool" as const, origin: "network" as const }
|
|
841
|
+
: { ...classifyUpstreamFailure(completion.error) };
|
|
842
|
+
try {
|
|
843
|
+
await reportAttemptOutcome(completion.db, {
|
|
844
|
+
accountId: completion.accountId,
|
|
845
|
+
...(completion.modelId === undefined ? {} : { modelId: completion.modelId }),
|
|
846
|
+
...(completion.status === "failed"
|
|
847
|
+
? {
|
|
848
|
+
error: completion.error,
|
|
849
|
+
evidence,
|
|
850
|
+
}
|
|
851
|
+
: {}),
|
|
852
|
+
...(completion.snapshotService ? { snapshotService: completion.snapshotService } : {}),
|
|
853
|
+
});
|
|
854
|
+
} catch {
|
|
855
|
+
// Swallowed: health reporting is non-critical bookkeeping.
|
|
856
|
+
}
|
|
809
857
|
}
|
|
810
858
|
}
|
|
811
859
|
if (completion.networkPoolId && completion.status !== "cancelled") {
|
|
@@ -61,6 +61,27 @@ function comboCandidateKey(candidate: RouteCandidate): string {
|
|
|
61
61
|
].join(":");
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* True when the rejection is about the API key or the gateway's own policy,
|
|
66
|
+
* not about the provider account that was dialled.
|
|
67
|
+
*
|
|
68
|
+
* These are decided before the upstream is ever called, so every candidate in
|
|
69
|
+
* the plan fails identically; walking the rest of the list would just burn
|
|
70
|
+
* attempts and write phantom account-health rows (a local `capacity_exhausted`
|
|
71
|
+
* used to read as a provider `rate_limit_transient` cooldown).
|
|
72
|
+
*/
|
|
73
|
+
function isClientScopedRejection(error: unknown): boolean {
|
|
74
|
+
if (!(error instanceof GatewayError)) return false;
|
|
75
|
+
return (
|
|
76
|
+
error.code === "capacity_exhausted" ||
|
|
77
|
+
error.code === "tenant_capacity_exhausted" ||
|
|
78
|
+
error.code === "admission_unavailable" ||
|
|
79
|
+
error.code === "model_not_allowed" ||
|
|
80
|
+
error.code === "ambiguous_model" ||
|
|
81
|
+
error.code === "route_unavailable"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
64
85
|
/**
|
|
65
86
|
* Which combo member a candidate belongs to.
|
|
66
87
|
*
|
|
@@ -402,6 +423,7 @@ export async function runAttemptLoop<TResult, TAdapter>(
|
|
|
402
423
|
}
|
|
403
424
|
const terminalAttempt =
|
|
404
425
|
cancelled ||
|
|
426
|
+
isClientScopedRejection(error) ||
|
|
405
427
|
!isRetryableFailure(error) ||
|
|
406
428
|
(index === candidates.length - 1 && !poolScopedFailure);
|
|
407
429
|
// Track only the most recent fired exchange; the array this replaced
|
|
@@ -920,7 +920,21 @@ export async function mapUpstreamHttpError(response: Response, providerId: strin
|
|
|
920
920
|
? { code: "context_length_exceeded" as const, status: 413, origin: "upstream" as const }
|
|
921
921
|
: classifyUpstreamError(response.status, identifier);
|
|
922
922
|
const extracted = extractUpstreamMessage(parsedBody ?? text);
|
|
923
|
-
|
|
923
|
+
let message =
|
|
924
|
+
extracted.length > 0
|
|
925
|
+
? extracted
|
|
926
|
+
: text.trim() || `Provider returned HTTP ${response.status}.`;
|
|
927
|
+
// A bare "Provider returned HTTP <5xx>" tells the operator nothing actionable;
|
|
928
|
+
// platform-unavailable fallbacks get the same human sign-off every other
|
|
929
|
+
// gateway error carries. The raw upstream body stays in `details.raw` for
|
|
930
|
+
// diagnostics either way.
|
|
931
|
+
if (
|
|
932
|
+
classification.code === "platform_unavailable" &&
|
|
933
|
+
message === `Provider returned HTTP ${response.status}.`
|
|
934
|
+
) {
|
|
935
|
+
message = `The provider's service is temporarily unavailable (HTTP ${response.status}) (request {requestId}). Your request did not go through — wait a moment and try again. Hold tight ☁️`;
|
|
936
|
+
}
|
|
937
|
+
message = message.slice(0, MAX_UPSTREAM_ERROR_BYTES).replace(/[\r\n]+/g, " ");
|
|
924
938
|
const headerWaitMs = parseUpstreamBackoff(response.headers);
|
|
925
939
|
// A queue carries its own interval, which is the one the caller must honour:
|
|
926
940
|
// it is what the upstream promised, while `Retry-After` may be absent or may
|
|
@@ -312,6 +312,7 @@ function requestLogMetadata(
|
|
|
312
312
|
cachedInputTokens: state.outcome.usage.cached_input_tokens,
|
|
313
313
|
reasoningTokens: state.outcome.usage.reasoning_tokens,
|
|
314
314
|
estimatedCost: state.outcome.usage.estimated_cost,
|
|
315
|
+
...(state.outcome.degenerateStop ? { degenerateStop: true } : {}),
|
|
315
316
|
},
|
|
316
317
|
}
|
|
317
318
|
: {}),
|
|
@@ -385,6 +386,9 @@ function requestTelemetryEvent(
|
|
|
385
386
|
...(state.payloadCaptureState === undefined
|
|
386
387
|
? {}
|
|
387
388
|
: { payloadCaptureState: state.payloadCaptureState }),
|
|
389
|
+
// Degenerate stop (short output ended by an ordinary `stop`) — measured
|
|
390
|
+
// so a remote operator can spot the pattern in the request list.
|
|
391
|
+
...(state.outcome?.degenerateStop ? { degenerateStop: true } : {}),
|
|
388
392
|
};
|
|
389
393
|
}
|
|
390
394
|
|
|
@@ -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;
|
|
@@ -142,6 +142,13 @@ function belowUsageFloor(state: RoutedCandidate): boolean {
|
|
|
142
142
|
if (credit === null && percent !== null && percent <= 0) return true;
|
|
143
143
|
const floor = state.usage_floor;
|
|
144
144
|
if (floor === undefined) return false;
|
|
145
|
+
// Free-tier models are not credit-funded, so the operator's configured floor
|
|
146
|
+
// threshold does not apply to them — a free model may use any remaining
|
|
147
|
+
// balance. This mirrors `creditPriority` (free tier skips credit preference).
|
|
148
|
+
// It deliberately comes AFTER the exhausted-window checks above: a free-tier
|
|
149
|
+
// account whose own quota window is spent (0 left) is still excluded, because
|
|
150
|
+
// that is the provider's own limit, not the credit floor.
|
|
151
|
+
if (state.free_tier === true) return false;
|
|
145
152
|
const remaining = state.usage_floor_unit === "credit" ? credit : percent;
|
|
146
153
|
return remaining !== null && remaining <= floor;
|
|
147
154
|
}
|
|
@@ -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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{l as i,v as a}from"./numbers-CUWAZK4J.js";import{t as o}from"./image-BPaouViG.js";import{t as s}from"./layers-2-glBbjl.js";import{t as c}from"./rotate-ccw-CufcqITi.js";import{$ as l,G as u,H as d,J as f,Q as p,U as m,V as h,W as g,X as _,Y as v,Z as y,q as b}from"./entry-LCS85hiq.js";import{n as x,r as S,t as C}from"./card-CtkX66Wd.js";import{t as w}from"./switch-c0oPSnqW.js";import{t as T}from"./select-BLULNqTp.js";import{t as E}from"./stack-D2lIR0tg.js";import{t as D}from"./inline-C4gU5iOL.js";var O=e(`frame`,[[`line`,{x1:`22`,x2:`2`,y1:`6`,y2:`6`,key:`15w7dq`}],[`line`,{x1:`22`,x2:`2`,y1:`18`,y2:`18`,key:`1ip48p`}],[`line`,{x1:`6`,x2:`6`,y1:`2`,y2:`22`,key:`a2lnyx`}],[`line`,{x1:`18`,x2:`18`,y1:`2`,y2:`22`,key:`8vb6jd`}]]),k=e(`panel-top`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M3 9h18`,key:`1pudct`}]]),A=e(`rectangle-horizontal`,[[`rect`,{width:`20`,height:`12`,x:`2`,y:`6`,rx:`2`,key:`9lu3g6`}]]),j=t(n(),1),M=r(),N={cards:{label:`Cards`,description:`The card body on every page`,icon:(0,M.jsx)(A,{size:15})},chrome:{label:`Sidebar & topbar`,description:`The shell around the page`,icon:(0,M.jsx)(k,{size:15})}};function P(e){return e<1048576?`${Math.max(1,Math.round(e/1024))} KB`:`${(e/1048576).toFixed(1)} MB`}function F(){let[e,t]=u(),[n,r]=(0,j.useState)(!1),[f,y]=(0,j.useState)(null),w=async n=>{let i=n.target.files?.[0];if(n.target.value=``,!i)return;let o=_(i);if(!o){a.error(`Choose an image or a video.`);return}if(i.size>4294967296){a.error(`File too big. 4 GB is the limit.`);return}let s=i.size>b;r(!0);try{let n=o===`image`?await p(i):i;await l(n,o),y(n.size),t({backgroundSet:!0,backgroundEnabled:!0,backgroundRevision:e.backgroundRevision+1}),a.success(o===`video`?`Video background saved in this browser.`:`Background saved in this browser.`),s&&a.warning(`File too big. Your web performance and experience may be degraded.`)}catch{a.error(`Could not store that file. The browser may be out of space.`)}finally{r(!1)}},k=async()=>{r(!0);try{await v(),y(null),t({backgroundSet:!1,backgroundEnabled:!1})}catch{a.error(`Could not remove the background.`)}finally{r(!1)}};return(0,M.jsxs)(E,{gap:`16px`,children:[(0,M.jsxs)(C,{children:[(0,M.jsx)(S,{title:`Background`,subtitle:`Stored in this browser only. It never leaves the machine.`,icon:(0,M.jsx)(o,{size:16}),action:(0,M.jsx)(D,{children:(0,M.jsx)(i,{variant:`ghost`,size:`sm`,icon:(0,M.jsx)(c,{size:13}),onClick:()=>{g({backgroundOpacity:d.backgroundOpacity,backgroundScale:d.backgroundScale,backgroundX:d.backgroundX,backgroundY:d.backgroundY,backgroundBlur:d.backgroundBlur,backgroundEnabled:e.backgroundSet}),a.success(`Background reset to defaults.`)},children:`Reset`})})}),(0,M.jsx)(x,{children:(0,M.jsxs)(E,{gap:`14px`,children:[(0,M.jsx)(I,{settings:e,onBytes:y,bytes:f}),(0,M.jsxs)(`div`,{className:`flex flex-wrap items-center gap-4`,children:[(0,M.jsxs)(`label`,{className:`btn btn-primary inline-flex items-center justify-center gap-3.5 rounded-control font-semibold text-caption px-7 py-2 cursor-pointer bg-accent text-accent-foreground`,children:[n?`Working…`:e.backgroundSet?`Replace`:`Upload image or video`,(0,M.jsx)(`input`,{type:`file`,accept:`image/*,video/*`,className:`sr-only`,onChange:e=>void w(e),disabled:n})]}),e.backgroundSet?(0,M.jsx)(i,{variant:`danger`,size:`sm`,onClick:()=>void k(),disabled:n,children:`Remove`}):null,(0,M.jsx)(`span`,{className:`text-badge text-text-tertiary`,children:`Images are resized to 2560px. Videos play on desktop and show their first frame on mobile.`})]}),(0,M.jsx)(L,{settings:e,setSettings:t})]})})]}),(0,M.jsxs)(C,{children:[(0,M.jsx)(S,{title:`Blur`,subtitle:`Each surface keeps its own style. Dialogs are never blurred, so they stay readable.`,icon:(0,M.jsx)(s,{size:16}),action:(0,M.jsx)(D,{children:(0,M.jsx)(i,{variant:`ghost`,size:`sm`,icon:(0,M.jsx)(c,{size:13}),onClick:()=>{g({blurCardsAmount:d.blurCardsAmount,blurChromeAmount:d.blurChromeAmount,blurCardsEnabled:d.blurCardsEnabled,blurChromeEnabled:d.blurChromeEnabled,blurCardsDepth:d.blurCardsDepth,blurChromeDepth:d.blurChromeDepth,renderMode:d.renderMode}),a.success(`Blur reset to defaults.`)},children:`Reset`})})}),(0,M.jsxs)(x,{children:[(0,M.jsxs)(`div`,{className:`mb-10 flex items-center justify-between gap-6 rounded-card border border-inner-border bg-surface-2 px-7 py-6 max-small:flex-col max-small:items-stretch`,children:[(0,M.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,M.jsx)(`span`,{className:`text-caption font-semibold text-text-primary`,children:`Render preset`}),(0,M.jsx)(`span`,{className:`text-badge text-text-tertiary`,children:`Pretty keeps surfaces near-solid with no card blur; Extreme removes the backdrop read everywhere. Either one overrides the toggles below while active — the background image stays.`})]}),(0,M.jsx)(T,{value:e.renderMode,onValueChange:e=>{t({renderMode:e}),a.success(e===`off`?`Render preset off`:`Render preset: ${e===`pretty`?`Pretty`:`Extreme`}`)},options:[{value:`off`,label:`Off (default)`},{value:`pretty`,label:`Pretty`},{value:`extreme`,label:`Extreme`}],size:`sm`,"aria-label":`Render preset`,className:`max-small:self-end`})]}),(0,M.jsx)(`div`,{className:`grid gap-10 lg:grid-cols-2`,children:h.map(n=>{let r=n===`chrome`,i=e.renderMode===`off`?void 0:m[e.renderMode][r?`chrome`:`cards`];return(0,M.jsx)(z,{target:n,enabled:r?e.blurChromeEnabled:e.blurCardsEnabled,held:e.renderMode===`off`?void 0:e.renderMode,onToggle:()=>t(r?{blurChromeEnabled:!e.blurChromeEnabled}:{blurCardsEnabled:!e.blurCardsEnabled}),clarity:i?i.amount:r?e.blurChromeAmount:e.blurCardsAmount,onClarity:e=>t(r?{blurChromeAmount:e}:{blurCardsAmount:e}),depth:i?i.depth:r?e.blurChromeDepth:e.blurCardsDepth,onDepth:e=>t(r?{blurChromeDepth:e}:{blurCardsDepth:e})},n)})})]})]}),(0,M.jsxs)(C,{children:[(0,M.jsx)(S,{title:`Empty panels`,subtitle:`A dashed frame around a page that has nothing to show yet.`,icon:(0,M.jsx)(O,{size:16})}),(0,M.jsx)(x,{children:(0,M.jsxs)(`button`,{type:`button`,role:`switch`,"aria-checked":e.emptyOutline,onClick:()=>t({emptyOutline:!e.emptyOutline}),className:`flex w-full items-center justify-between gap-6 rounded-card border border-inner-border bg-surface-2 px-7 py-6 text-left`,children:[(0,M.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,M.jsx)(`span`,{className:`text-caption font-semibold text-text-primary`,children:`Dashed border`}),(0,M.jsx)(`span`,{className:`text-badge text-text-tertiary`,children:`Off leaves the empty state sitting on the card.`})]}),(0,M.jsx)(`span`,{"aria-hidden":`true`,className:e.emptyOutline?`h-10 w-18 shrink-0 rounded-pill bg-accent p-1`:`h-10 w-18 shrink-0 rounded-pill bg-surface-active p-1`,children:(0,M.jsx)(`span`,{className:e.emptyOutline?`block size-8 translate-x-8 rounded-full bg-accent-foreground transition-transform`:`block size-8 rounded-full bg-text-secondary transition-transform`})})]})})]})]})}function I({settings:e,bytes:t,onBytes:n}){let[r,i]=(0,j.useState)(null),[a,o]=(0,j.useState)(null);if((0,j.useEffect)(()=>{if(!e.backgroundSet){i(f),o(`image`);return}let t=!0,r=null;return y().then(e=>{if(e){if(!t){URL.revokeObjectURL(e.url);return}r=e.url,i(e.url),o(e.kind),n(e.blob.size)}}).catch(()=>{}),()=>{t=!1,r&&URL.revokeObjectURL(r)}},[e.backgroundSet,n]),!r)return(0,M.jsx)(`div`,{className:`flex h-60 items-center justify-center rounded-card border border-dashed border-inner-border bg-surface-2 text-caption text-text-tertiary`,children:`No background yet. The console uses its plain surface.`});let s={objectFit:`cover`,objectPosition:`${50+e.backgroundX/2}% ${50+e.backgroundY/2}%`,transform:`scale(${e.backgroundScale/100})`,opacity:e.backgroundOpacity/100};return(0,M.jsxs)(`div`,{className:`relative h-75 overflow-hidden rounded-card border border-inner-border bg-surface-2`,children:[a===`video`?(0,M.jsx)(`video`,{src:r,muted:!0,playsInline:!0,preload:`metadata`,className:`size-full`,style:s}):(0,M.jsx)(`img`,{src:r,alt:``,className:`size-full`,style:s}),(0,M.jsxs)(`span`,{className:`absolute bottom-4 left-5 rounded-control bg-surface-1 px-4 py-1 text-micro text-text-secondary`,children:[a===`video`?`Video`:`Image`,t===null?``:` · ${P(t)}`,e.backgroundEnabled?``:` · hidden`]})]})}function L({settings:e,setSettings:t}){return(0,M.jsxs)(`div`,{className:`flex flex-col gap-7`,children:[(0,M.jsxs)(`label`,{className:`flex min-h-18 items-center justify-between gap-6 text-body font-semibold text-text-primary`,children:[(0,M.jsx)(`span`,{children:`Show background`}),(0,M.jsx)(w,{checked:e.backgroundEnabled,onChange:e=>t({backgroundEnabled:e}),"aria-label":`Show background`})]}),(0,M.jsxs)(`div`,{className:`grid gap-7 sm:grid-cols-2`,children:[(0,M.jsx)(R,{label:`Visibility`,value:e.backgroundOpacity,defaultValue:d.backgroundOpacity,min:0,max:100,unit:`%`,onChange:e=>t({backgroundOpacity:e})}),(0,M.jsx)(R,{label:`Blur`,value:e.backgroundBlur,defaultValue:d.backgroundBlur,min:0,max:100,unit:`%`,onChange:e=>t({backgroundBlur:e})}),(0,M.jsx)(R,{label:`Zoom`,value:e.backgroundScale,defaultValue:d.backgroundScale,min:100,max:180,unit:`%`,onChange:e=>t({backgroundScale:e})}),(0,M.jsx)(R,{label:`Shift horizontal`,value:e.backgroundX,defaultValue:d.backgroundX,min:-100,max:100,onChange:e=>t({backgroundX:e})}),(0,M.jsx)(R,{label:`Shift vertical`,value:e.backgroundY,defaultValue:d.backgroundY,min:-100,max:100,onChange:e=>t({backgroundY:e})})]})]})}function R({label:e,value:t,defaultValue:n,min:r,max:i,unit:a,onChange:o}){return(0,M.jsxs)(`label`,{className:`flex flex-col gap-2 text-caption text-text-secondary`,children:[(0,M.jsxs)(`span`,{className:`flex items-center justify-between`,children:[e,(0,M.jsxs)(`span`,{className:`flex items-center gap-3`,children:[(0,M.jsxs)(`span`,{className:`font-mono tabular-nums text-badge text-text-tertiary`,children:[t,a??``]}),(0,M.jsx)(`button`,{type:`button`,onClick:()=>o(n),disabled:t===n,"aria-label":`Reset ${e}`,title:`Reset ${e} to ${n}${a??``}`,className:`flex size-9 items-center justify-center rounded-control text-text-tertiary hover:bg-hover hover:text-text-primary disabled:pointer-events-none disabled:opacity-30`,children:(0,M.jsx)(c,{size:11})})]})]}),(0,M.jsx)(`input`,{type:`range`,min:r,max:i,value:t,onChange:e=>o(Number(e.target.value)),"aria-label":e,className:`blur-slider w-full h-11 m-0 border-0 rounded-pill cursor-pointer`,style:{"--fill":`${(t-r)/(i-r)*100}%`}})]})}function z({target:e,enabled:t,held:n,onToggle:r,clarity:i,onClarity:a,depth:o,onDepth:s}){let c=N[e]??N.cards,l=n===`pretty`?`Held by Pretty`:n===`extreme`?`Held by Extreme`:void 0,u=t&&n===void 0;return(0,M.jsxs)(`div`,{className:`flex flex-col gap-8 rounded-card border border-inner-border bg-surface-2 p-8`,children:[(0,M.jsxs)(`div`,{className:`flex items-center gap-5`,children:[(0,M.jsx)(`span`,{className:`flex size-16 shrink-0 items-center justify-center rounded-control bg-surface-1 text-text-secondary`,children:c.icon}),(0,M.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,M.jsx)(`span`,{className:`text-body font-semibold text-text-primary`,children:c.label}),(0,M.jsx)(`span`,{className:`text-micro text-text-tertiary`,children:l??c.description})]}),(0,M.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":n===void 0&&t,"aria-label":`Toggle ${c.label} blur`,disabled:n!==void 0,onClick:n===void 0?r:void 0,className:`${n===void 0?t?`h-10 w-18 shrink-0 rounded-pill bg-accent p-1`:`h-10 w-18 shrink-0 rounded-pill bg-surface-active p-1`:`h-10 w-18 shrink-0 rounded-pill bg-surface-active p-1 cursor-default`}`,children:(0,M.jsx)(`span`,{className:n!==void 0||!t?`block size-8 rounded-full bg-text-secondary`:`block size-8 translate-x-8 rounded-full bg-accent-foreground`})})]}),(0,M.jsxs)(`div`,{className:u?`flex flex-col gap-6`:`flex flex-col gap-6 opacity-40`,children:[(0,M.jsxs)(`label`,{className:`flex flex-col gap-3 text-caption text-text-secondary`,children:[(0,M.jsxs)(`span`,{className:`flex items-center justify-between`,children:[`Clarity`,(0,M.jsxs)(`span`,{className:`font-mono text-badge tabular-nums text-text-tertiary`,children:[i,`%`]})]}),(0,M.jsx)(`input`,{type:`range`,min:0,max:100,value:i,disabled:!t,onChange:e=>a(Number(e.target.value)),"aria-label":`${c.label} clarity. How see-through the surface is.`,className:`blur-slider w-full h-11 m-0 border-0 rounded-pill cursor-pointer`,style:{"--fill":`${i}%`}}),(0,M.jsxs)(`span`,{className:`flex justify-between text-badge text-text-tertiary`,children:[(0,M.jsx)(`span`,{children:`See-through`}),(0,M.jsx)(`span`,{children:`Solid`})]})]}),(0,M.jsxs)(`label`,{className:`flex flex-col gap-3 text-caption text-text-secondary`,children:[(0,M.jsxs)(`span`,{className:`flex items-center justify-between`,children:[`Blur`,(0,M.jsxs)(`span`,{className:`font-mono text-badge tabular-nums text-text-tertiary`,children:[o,`%`]})]}),(0,M.jsx)(`input`,{type:`range`,min:0,max:100,value:o,disabled:!t,onChange:e=>s(Number(e.target.value)),"aria-label":`${c.label} blur. How blurred whatever is behind it looks.`,className:`blur-slider w-full h-11 m-0 border-0 rounded-pill cursor-pointer`,style:{"--fill":`${o}%`}}),(0,M.jsxs)(`span`,{className:`flex justify-between text-badge text-text-tertiary`,children:[(0,M.jsx)(`span`,{children:`Sharp`}),(0,M.jsx)(`span`,{children:`Blurred`})]})]})]})]})}export{F as default};
|
|
Binary file
|