cartethyia 2.7.6 → 2.7.8
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 -2
- package/README.md +26 -2
- package/dist/dashboard/assets/{BannedPage-B1N603cM.js → BannedPage-DeIbIiBU.js} +1 -1
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-9q4_4NoK.js → CliToolDetailPage-BRWKPQPf.js} +2 -2
- package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DwshGtK-.js → CliToolsPage-BP-Y5iHN.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-D762MIkp.js → CombosPage-B7JHozVa.js} +1 -1
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-KRYjHZsX.js → ConsoleLogPage-GX7iGGcu.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-d5v3K_Yc.js → InjectSystemPromptPanel-Cv-2Ol_K.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-Ccv4tjNS.js → LoginPage-bKfz_b1K.js} +1 -1
- package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C6WlpU5E.js → ModelPicker-B-WQxx_H.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-DN9fYNWA.js → OverviewPage-Z9pJZFo8.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +7 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-DxjvUzSD.js → ProxyPage-emlTuxz6.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BW4AKj2H.js → SettingsPage-BSy7EwGo.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-44BZrxFC.js → SetupPage-CUZrKs9r.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-DRPavCdU.js → StudioPage-jWmUdDpy.js} +2 -2
- package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-B7M7LvMM.js → UsagePage-5F9tGVGG.js} +2 -2
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
- package/dist/dashboard/assets/app-BEsU8hKs.css +2 -0
- package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
- package/dist/dashboard/assets/{badge-BaX2GTrn.js → badge-DyI_mJfi.js} +1 -1
- package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DFH4Bkte.js → banner-B-yXtCqb.js} +1 -1
- package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
- package/dist/dashboard/assets/{card-CxDw0lK6.js → card-_HwAEqFF.js} +1 -1
- package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DfS1ntiq.js → clipboard-button-ByjCk0o9.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BK-NRr5u.js → dialog-BZhOlfz-.js} +1 -1
- package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-QqPul6sC.js → drawer-BADqIRYP.js} +1 -1
- package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BMlYJVFj.js → entry-COaC-irN.js} +1 -1
- package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
- package/dist/dashboard/assets/{entry-D6WvojeD.js → entry-Ca7LSCet.js} +3 -3
- package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
- package/dist/dashboard/assets/{entry-Dmx7gBiN.js → entry-PDd27uCd.js} +1 -1
- package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js +1 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
- package/dist/dashboard/assets/{index-Ko4dLRnV.js → index-CgKmFngv.js} +2 -2
- package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
- package/dist/dashboard/assets/{inline-CHJQwxLa.js → inline-B2BirKry.js} +1 -1
- package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
- package/dist/dashboard/assets/{input-BlmlizVV.js → input-D-qV9dKY.js} +1 -1
- package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
- package/dist/dashboard/assets/{layout-Bz93I-yE.js → layout-B7tVWhtV.js} +1 -1
- package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Ml16KCfu.js → list-row-CtkGE3bF.js} +1 -1
- package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
- package/dist/dashboard/assets/{live-7_QpnYGu.js → live-DGizF06-.js} +1 -1
- package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
- package/dist/dashboard/assets/{network-CuMDaTzT.js → network-C9UE-bOr.js} +1 -1
- package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CoqpaDMx.js → numbers-BnAd22a_.js} +1 -1
- package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-B5aOoweZ.js → pagination-BAywyMzS.js} +1 -1
- package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
- package/dist/dashboard/assets/{select-BHqpd-bg.js → select-BCOcv8WK.js} +1 -1
- package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
- package/dist/dashboard/assets/{stack-j7cXjgYX.js → stack-ByKt3Q1g.js} +1 -1
- package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
- package/dist/dashboard/assets/{state-DHmEnLa2.js → state-DXbGTx7N.js} +1 -1
- package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DmZcyTIC.js → switch-ZeTsW623.js} +1 -1
- package/dist/dashboard/assets/switch-ZeTsW623.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 +44 -44
- package/migrations/full-postgres/0043_add_provider_custom_user_agent.sql +14 -0
- package/migrations/lite-sqlite3/0043_add_provider_custom_user_agent.sql +10 -0
- package/package.json +3 -1
- package/src/config/security.ts +1 -1
- package/src/config/spec.ts +13 -6
- package/src/console/cli-tools/cartethyia-websearch-plugin.ts +186 -0
- package/src/console/cli-tools/injectors/opencode.ts +42 -14
- package/src/console/providers/catalog/contracts.ts +4 -0
- package/src/console/providers/catalog/routes.ts +1 -0
- package/src/console/providers/detail/routes.ts +8 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/quota/account-quota-tenant.ts +74 -0
- package/src/observability/telemetry-buffer.ts +2 -1
- package/src/persistence/boot.ts +13 -4
- package/src/persistence/schema.ts +7 -1
- package/src/protocol/canonical-model.ts +7 -0
- package/src/protocol/request/gemini.ts +20 -1
- package/src/protocol/tool-identity.ts +15 -1
- package/src/providers/default-registry.ts +8 -2
- package/src/providers/discovery/discovery-types.ts +18 -0
- package/src/providers/discovery/probe-phases.ts +45 -0
- package/src/providers/discovery/probing-service.ts +41 -6
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +20 -2
- package/src/providers/integrations/antigravity/antigravity.ts +24 -0
- package/src/providers/integrations/codex/codex.ts +28 -1
- package/src/providers/integrations/qoder/chat-proto.ts +328 -0
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +258 -0
- package/src/providers/integrations/qoder/qoder-adapter.ts +275 -0
- package/src/providers/integrations/qoder/qoder-auth.ts +201 -0
- package/src/providers/integrations/qoder/qoder-body.ts +78 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +5 -1
- package/src/providers/integrations/qoder/qoder-chat.ts +514 -0
- package/src/providers/integrations/qoder/qoder-cosy.ts +162 -0
- package/src/providers/integrations/qoder/qoder-grpc.ts +667 -0
- package/src/providers/integrations/qoder/qoder-ide.ts +584 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +4 -4
- package/src/providers/integrations/qoder/qoder-quota.ts +1 -1
- package/src/providers/integrations/qoder/qoder-realm.ts +40 -4
- package/src/providers/integrations/qoder/qoder-types.ts +51 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +123 -8
- package/src/providers/integrations/qoder/qoder.ts +147 -1889
- package/src/providers/operations/byok-wire-profile.ts +9 -12
- package/src/providers/provider-contracts.ts +7 -0
- package/src/providers/provider-usage.ts +9 -6
- package/src/security/abuse.ts +5 -5
- package/src/transport/dispatch/upstream.ts +4 -2
- package/src/transport/routing/route-catalog.ts +8 -0
- package/src/transport/routing/route-model.ts +4 -0
- package/src/transport/routing/router.ts +8 -117
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +0 -1
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +0 -2
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-u7JPB5BX.js +0 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/src/providers/integrations/qoder/qoder-text-tools.ts +0 -277
- /package/dist/dashboard/assets/{app-DRnQLhsc.js → app-CTxoqyYB.js} +0 -0
- /package/dist/dashboard/assets/{app-DRnQLhsc.js.gz → app-CTxoqyYB.js.gz} +0 -0
|
@@ -17,9 +17,6 @@ import type { WireFamily } from "../../protocol/canonical-model";
|
|
|
17
17
|
import type { CompatibilityProfile } from "../provider-metadata";
|
|
18
18
|
import { BARE_ROOT_ENDPOINTS, type AuthHeaderShape } from "../../protocol/codecs";
|
|
19
19
|
|
|
20
|
-
/** The two wires an OpenAI-compatible root conventionally serves. */
|
|
21
|
-
const OPENAI_WIRE_FAMILIES: readonly WireFamily[] = ["chat", "responses"];
|
|
22
|
-
|
|
23
20
|
export interface ByokWireProfile {
|
|
24
21
|
/** Header shape the upstream expects for the operator's credential. */
|
|
25
22
|
readonly authHeaderShape: AuthHeaderShape;
|
|
@@ -47,10 +44,14 @@ export function byokAuthHeaderShape(families: readonly WireFamily[]): AuthHeader
|
|
|
47
44
|
*
|
|
48
45
|
* Operator truth wins: an explicit `endpoint_paths_by_wire_family` names
|
|
49
46
|
* exactly the families the provider serves. Without one, the provider serves
|
|
50
|
-
* the family it was created with
|
|
51
|
-
* `responses`
|
|
52
|
-
*
|
|
53
|
-
*
|
|
47
|
+
* exactly the family it was created with — a `chat` default serves only chat,
|
|
48
|
+
* a `responses` default only responses, a `messages` default only messages.
|
|
49
|
+
*
|
|
50
|
+
* A `chat` default used to additionally serve `responses`, and discovery then
|
|
51
|
+
* reclassified gpt-5/gpt-6 ids as responses even when the operator picked
|
|
52
|
+
* "Chat completions" — the model ended up on `/v1/responses` (which the
|
|
53
|
+
* upstream may not serve) and the probe timed out. The operator's selection
|
|
54
|
+
* is the contract; nothing is inferred beyond it.
|
|
54
55
|
*/
|
|
55
56
|
export function resolveByokWireProfile(
|
|
56
57
|
wireFamilyDefault: WireFamily | null | undefined,
|
|
@@ -62,11 +63,7 @@ export function resolveByokWireProfile(
|
|
|
62
63
|
: [];
|
|
63
64
|
const defaultFamily: WireFamily = wireFamilyDefault ?? "chat";
|
|
64
65
|
const supportedWireFamilies: readonly WireFamily[] =
|
|
65
|
-
declaredFamilies.length > 0
|
|
66
|
-
? declaredFamilies
|
|
67
|
-
: defaultFamily === "chat"
|
|
68
|
-
? OPENAI_WIRE_FAMILIES
|
|
69
|
-
: [defaultFamily];
|
|
66
|
+
declaredFamilies.length > 0 ? declaredFamilies : [defaultFamily];
|
|
70
67
|
const endpointPathsByWireFamily: Partial<Record<WireFamily, string>> = {};
|
|
71
68
|
for (const family of supportedWireFamilies) {
|
|
72
69
|
const path = declaredPaths?.[family] ?? BARE_ROOT_ENDPOINTS[family];
|
|
@@ -270,6 +270,13 @@ export interface ProviderDispatchTarget {
|
|
|
270
270
|
readonly capabilities: Readonly<Record<string, boolean>>;
|
|
271
271
|
/** Explicit wire dialect (from `models.dialect`), when set. Absent = resolver fallback. */
|
|
272
272
|
readonly dialect?: string;
|
|
273
|
+
/**
|
|
274
|
+
* Transport override for a one-shot probe, when the operator explicitly
|
|
275
|
+
* asked for one. Live dispatch never sets this — it is a probe-only knob
|
|
276
|
+
* that lets a provider expose a surface the production transports do not
|
|
277
|
+
* use (Qoder's IDE surface is the only one that echoes strategy metadata).
|
|
278
|
+
*/
|
|
279
|
+
readonly probe_surface?: "cosy" | "grpc" | "ide";
|
|
273
280
|
}
|
|
274
281
|
|
|
275
282
|
/** Provider adapter boundary: canonical request/events in, no public HTTP routes. */
|
|
@@ -299,6 +299,14 @@ export function normalizeUsage(input: Record<string, unknown>): UsageRecord {
|
|
|
299
299
|
},
|
|
300
300
|
rawCost,
|
|
301
301
|
) ?? null;
|
|
302
|
+
// Provider-billed credits: an explicit `credit_used` wins, then Qoder's
|
|
303
|
+
// `credits` (COSY terminal frame), then Tencent buddy-meter `credit`.
|
|
304
|
+
// Negative/NaN values are dropped, never clamped.
|
|
305
|
+
const creditRaw = input.credit_used ?? input.credits ?? input.credit;
|
|
306
|
+
const creditUsed =
|
|
307
|
+
typeof creditRaw === "number" && Number.isFinite(creditRaw) && creditRaw >= 0
|
|
308
|
+
? { credit_used: creditRaw }
|
|
309
|
+
: {};
|
|
302
310
|
return {
|
|
303
311
|
input_tokens,
|
|
304
312
|
cached_input_tokens,
|
|
@@ -307,12 +315,7 @@ export function normalizeUsage(input: Record<string, unknown>): UsageRecord {
|
|
|
307
315
|
output_tokens,
|
|
308
316
|
reasoning_tokens,
|
|
309
317
|
estimated_cost,
|
|
310
|
-
|
|
311
|
-
// (probed live on cb/deepseek-v4.1-flash-sg: 0 on a 29-token turn, 1.01
|
|
312
|
-
// on a 72k-token turn). Negative/NaN values are dropped, never clamped.
|
|
313
|
-
...(typeof input.credit === "number" && Number.isFinite(input.credit) && input.credit >= 0
|
|
314
|
-
? { credit_used: input.credit }
|
|
315
|
-
: {}),
|
|
318
|
+
...creditUsed,
|
|
316
319
|
...(Object.keys(details).length > 0 ? { details } : {}),
|
|
317
320
|
};
|
|
318
321
|
}
|
package/src/security/abuse.ts
CHANGED
|
@@ -201,7 +201,7 @@ export class InMemoryIpAbuseStore implements IpAbuseStore {
|
|
|
201
201
|
typeof windowOrOptions === "number" ? { windowMs: windowOrOptions } : windowOrOptions;
|
|
202
202
|
this.windowMs = options.windowMs ?? 60_000;
|
|
203
203
|
this.clock = options.clock ?? (() => Date.now());
|
|
204
|
-
// 10k keys is a few MB even at the default
|
|
204
|
+
// 10k keys is a few MB even at the default 540 RPM ring size; the previous
|
|
205
205
|
// 100k bound could reach ~190 MB, and a fan-out over unique source IPs is
|
|
206
206
|
// the one input that can actually reach it.
|
|
207
207
|
this.maxKeys = options.maxKeys ?? 10_000;
|
|
@@ -382,14 +382,14 @@ export class IpAbuseProtectionService {
|
|
|
382
382
|
) {
|
|
383
383
|
if (!store)
|
|
384
384
|
throw new Error("IpAbuseProtectionService requires a store — fail-closed, not no-op");
|
|
385
|
-
//
|
|
386
|
-
// with the documented default: unset env →
|
|
385
|
+
// 540 requests per 60 s window = 9 RPS/IP steady-state cap. Aligns
|
|
386
|
+
// with the documented default: unset env → 540 RPM per IP, not
|
|
387
387
|
// unlimited. Operators raise or lower via IP_RATE_MAX_PER_WINDOW.
|
|
388
|
-
this.maxRequestsPerWindow = opts.maxRequestsPerWindow ??
|
|
388
|
+
this.maxRequestsPerWindow = opts.maxRequestsPerWindow ?? 540;
|
|
389
389
|
// A ban only makes sense once the limit was breached, so the threshold is
|
|
390
390
|
// clamped above the cap: below it, escalation would be unreachable (the
|
|
391
391
|
// count can never exceed `maxRequestsPerWindow` without being rejected).
|
|
392
|
-
this.banThreshold = Math.max(opts.banThreshold ??
|
|
392
|
+
this.banThreshold = Math.max(opts.banThreshold ?? 1080, this.maxRequestsPerWindow + 1);
|
|
393
393
|
this.banDurationMs = opts.banDurationMs ?? 60 * 60 * 1000;
|
|
394
394
|
}
|
|
395
395
|
|
|
@@ -85,8 +85,10 @@ export function buildUpstreamDispatchContext(input: {
|
|
|
85
85
|
const requestHeaders =
|
|
86
86
|
init?.headers ?? (request instanceof Request ? request.headers : undefined);
|
|
87
87
|
const headers = new Headers(requestHeaders);
|
|
88
|
-
//
|
|
89
|
-
|
|
88
|
+
// A route-level user agent is an explicit operator override: it
|
|
89
|
+
// replaces whatever identity the provider would otherwise send.
|
|
90
|
+
// Absent route identity leaves the provider's own header untouched.
|
|
91
|
+
if (userAgent) headers.set("user-agent", userAgent);
|
|
90
92
|
return sourceFetch(request, { ...init, headers });
|
|
91
93
|
}
|
|
92
94
|
: sourceFetch;
|
|
@@ -354,6 +354,7 @@ const ROUTING_COLUMNS = {
|
|
|
354
354
|
enabled: providerRoutingSettings.enabled,
|
|
355
355
|
skipProxy: providerRoutingSettings.skipProxy,
|
|
356
356
|
userAgent: providerRoutingSettings.userAgent,
|
|
357
|
+
customUserAgent: providerRoutingSettings.customUserAgent,
|
|
357
358
|
} as const;
|
|
358
359
|
|
|
359
360
|
const POOL_COLUMNS = {
|
|
@@ -553,6 +554,7 @@ class RouteCatalogRepository {
|
|
|
553
554
|
enabled: row.enabled,
|
|
554
555
|
skipProxy: row.skipProxy,
|
|
555
556
|
userAgent: row.userAgent,
|
|
557
|
+
customUserAgent: row.customUserAgent,
|
|
556
558
|
};
|
|
557
559
|
}
|
|
558
560
|
/** Tenant-specific setting wins over global; an unconfigured provider
|
|
@@ -573,6 +575,12 @@ class RouteCatalogRepository {
|
|
|
573
575
|
function resolveUserAgent(providerId: string, rowTenantId: string | null): string | undefined {
|
|
574
576
|
const tenantSetting = rowTenantId ? providerRouting[rowTenantId]?.[providerId]?.userAgent : undefined;
|
|
575
577
|
const globalSetting = providerRouting.__global__?.[providerId]?.userAgent;
|
|
578
|
+
const tenantCustom = rowTenantId ? providerRouting[rowTenantId]?.[providerId]?.customUserAgent : undefined;
|
|
579
|
+
const globalCustom = providerRouting.__global__?.[providerId]?.customUserAgent;
|
|
580
|
+
// An operator-set custom User-Agent overrides the native identity when it
|
|
581
|
+
// is non-empty; absent custom falls back to the native route identity.
|
|
582
|
+
const custom = resolveTenantOverride(tenantCustom, globalCustom, undefined);
|
|
583
|
+
if (custom !== undefined && custom !== null && custom.trim() !== "") return custom.trim();
|
|
576
584
|
return resolveRouteUserAgent(providerId, tenantSetting, globalSetting);
|
|
577
585
|
}
|
|
578
586
|
|
|
@@ -170,6 +170,10 @@ export interface ProviderRoutingSetting {
|
|
|
170
170
|
readonly enabled: boolean;
|
|
171
171
|
/** Built-in API-key User-Agent; OAuth and custom providers retain their identities. */
|
|
172
172
|
readonly userAgent?: string;
|
|
173
|
+
/** Operator-set custom User-Agent override; non-empty overrides `userAgent`
|
|
174
|
+
* (and any provider-native identity) at the request edge. `null`/empty
|
|
175
|
+
* means no custom override. */
|
|
176
|
+
readonly customUserAgent?: string | null;
|
|
173
177
|
/** When true, this (tenant, provider) dispatches direct — candidates
|
|
174
178
|
* never carry `network_pool_ids` regardless of pool availability. */
|
|
175
179
|
readonly skipProxy: boolean;
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
parseModelReference,
|
|
34
34
|
routeCandidateIndex,
|
|
35
35
|
} from "./candidate-index";
|
|
36
|
-
import { totalRemainingCredit,
|
|
36
|
+
import { totalRemainingCredit, type ProviderQuotaWindow } from "../../providers/quota/quota-contracts";
|
|
37
37
|
import { getCachedQuotaEntry, quotaLens } from "../../providers/quota/cache";
|
|
38
38
|
import { readQuotaSnapshot } from "../../providers/quota/snapshot";
|
|
39
39
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
@@ -88,71 +88,6 @@ export function filterCandidateQuotaWindows(
|
|
|
88
88
|
return windows;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
92
|
-
* True when the account's remaining allowance is at or below its provider's
|
|
93
|
-
* floor. Credit mode sums the absolute remaining credit; percent mode uses the
|
|
94
|
-
* aggregate credit percentage and the lowest independent percentage window.
|
|
95
|
-
* No reading means unknown, which is not below the floor.
|
|
96
|
-
*/
|
|
97
|
-
function belowUsageFloor(state: RoutedCandidate): boolean {
|
|
98
|
-
const rawWindows = state.quota_windows;
|
|
99
|
-
// A successful Start Plan balance (`quota_entitlements_known`) authoritatively
|
|
100
|
-
// lists its model grants: when the provider is zcode-start and no window
|
|
101
|
-
// matches the requested model, the account is below the floor even with an
|
|
102
|
-
// empty/absent window list — there is no grant to fund the model.
|
|
103
|
-
if (state.provider_id === "zcode-start" && state.quota_entitlements_known === true) {
|
|
104
|
-
if (rawWindows === undefined || rawWindows.length === 0) return true;
|
|
105
|
-
const knownGrants = rawWindows.some((window) => window.modelIds !== undefined);
|
|
106
|
-
if (!knownGrants) return true;
|
|
107
|
-
}
|
|
108
|
-
if (rawWindows === undefined || rawWindows.length === 0) return false;
|
|
109
|
-
const relevant = filterCandidateQuotaWindows(rawWindows, state);
|
|
110
|
-
// Model-scoped grants (Start Plan) are checked separately: a grant that has
|
|
111
|
-
// not started, has expired, or is exhausted cannot fund the requested model,
|
|
112
|
-
// so it must not count as credit.
|
|
113
|
-
const now = Date.now();
|
|
114
|
-
const grants = relevant.filter((window) => window.modelIds !== undefined);
|
|
115
|
-
if (state.provider_id === "zcode-start" && grants.length === 0 && rawWindows.some((window) => window.modelIds !== undefined)) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
if (
|
|
119
|
-
grants.length > 0 &&
|
|
120
|
-
!grants.some((window) => {
|
|
121
|
-
if (window.startsAt && Date.parse(window.startsAt) > now) return false;
|
|
122
|
-
if (window.resetsAt && Date.parse(window.resetsAt) <= now) return false;
|
|
123
|
-
if (typeof window.remaining === "number" && window.remaining <= 0) return false;
|
|
124
|
-
if (typeof window.remainingPercent === "number" && window.remainingPercent <= 0) return false;
|
|
125
|
-
const remaining = state.usage_floor_unit === "credit" ? window.remaining : window.remainingPercent;
|
|
126
|
-
return state.usage_floor === undefined || remaining == null || remaining > state.usage_floor;
|
|
127
|
-
})
|
|
128
|
-
) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
const windows = relevant.filter((window) => window.modelIds === undefined);
|
|
132
|
-
const credit = totalRemainingCredit(windows);
|
|
133
|
-
const percent = totalRemainingPercent(windows);
|
|
134
|
-
const exhaustedIndependentWindow = windows.some(
|
|
135
|
-
(window) =>
|
|
136
|
-
window.accounting !== "pooled" &&
|
|
137
|
-
typeof window.remainingPercent === "number" &&
|
|
138
|
-
Number.isFinite(window.remainingPercent) &&
|
|
139
|
-
window.remainingPercent <= 0,
|
|
140
|
-
);
|
|
141
|
-
if ((credit !== null && credit <= 0) || exhaustedIndependentWindow) return true;
|
|
142
|
-
if (credit === null && percent !== null && percent <= 0) return true;
|
|
143
|
-
const floor = state.usage_floor;
|
|
144
|
-
if (floor === undefined) return false;
|
|
145
|
-
// 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;
|
|
152
|
-
const remaining = state.usage_floor_unit === "credit" ? credit : percent;
|
|
153
|
-
return remaining !== null && remaining <= floor;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
91
|
/**
|
|
157
92
|
* Routing priority for an account's remaining allowance when two candidates sit
|
|
158
93
|
* in the same rank group: positive remaining credit first, then unknown, then
|
|
@@ -317,42 +252,6 @@ function spreadDeadline(cooldownUntil: number | undefined): { cooldown_until?: n
|
|
|
317
252
|
return cooldownUntil === undefined ? {} : { cooldown_until: cooldownUntil };
|
|
318
253
|
}
|
|
319
254
|
|
|
320
|
-
/**
|
|
321
|
-
* The soonest moment a quota window the caller is blocked on refills.
|
|
322
|
-
*
|
|
323
|
-
* A usage-floor exclusion is time-bounded by construction — the floor compares
|
|
324
|
-
* the *remaining* allowance, and the window states when that allowance resets.
|
|
325
|
-
* The exclusion carries a deadline so `plan()` can tell a client how long to
|
|
326
|
-
* wait and `earliestResetMs` reports a value. The minimum across windows, for
|
|
327
|
-
* the same reason `earliestResetMs` takes it: the account
|
|
328
|
-
* can serve again as soon as its first window refills, so quoting the slowest
|
|
329
|
-
* would hold the client off longer than necessary.
|
|
330
|
-
*
|
|
331
|
-
* `undefined` when no window states a reset, which is the honest answer and
|
|
332
|
-
* keeps the reason a plain 503 rather than a 429 with an unknown retry-after.
|
|
333
|
-
*/
|
|
334
|
-
function earliestWindowResetMs(
|
|
335
|
-
windows: readonly ProviderQuotaWindow[] | undefined,
|
|
336
|
-
nowMs: number,
|
|
337
|
-
candidate: RouteCandidate,
|
|
338
|
-
): number | undefined {
|
|
339
|
-
if (windows === undefined) return undefined;
|
|
340
|
-
windows = filterCandidateQuotaWindows(windows, candidate);
|
|
341
|
-
let earliest: number | undefined;
|
|
342
|
-
for (const window of windows) {
|
|
343
|
-
if (window.startsAt && Date.parse(window.startsAt) > nowMs) {
|
|
344
|
-
const at = Date.parse(window.startsAt);
|
|
345
|
-
if (earliest === undefined || at < earliest) earliest = at;
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
if (typeof window.resetsAt !== "string" || window.resetsAt.length === 0) continue;
|
|
349
|
-
const at = Date.parse(window.resetsAt);
|
|
350
|
-
if (!Number.isFinite(at) || at <= nowMs) continue;
|
|
351
|
-
if (earliest === undefined || at < earliest) earliest = at;
|
|
352
|
-
}
|
|
353
|
-
return earliest;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
255
|
/**
|
|
357
256
|
* The earliest instant any cooling candidate recovers, in epoch ms.
|
|
358
257
|
*
|
|
@@ -383,25 +282,17 @@ export class EligibilityEvaluator {
|
|
|
383
282
|
return { eligible: false, reason: "model_cooldown", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
384
283
|
if (candidate.health_status === "cooldown" && candidate.cooldown_kind === "hard")
|
|
385
284
|
return { eligible: false, reason: "cooldown_hard", candidate, ...spreadDeadline(candidate.cooldown_until) };
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
candidate,
|
|
394
|
-
...spreadDeadline(earliestWindowResetMs(candidate.quota_windows, Date.now(), candidate)),
|
|
395
|
-
};
|
|
285
|
+
// Quota/usage-floor is deliberately NOT an exclusion: an account whose
|
|
286
|
+
// window is spent or below its configured floor is still routed, and the
|
|
287
|
+
// upstream is the one that answers (its refusal is classified into a
|
|
288
|
+
// cooldown then). Blocking here made free-tier and exhausted accounts
|
|
289
|
+
// unreachable even when the operator wanted them tried — "don't follow
|
|
290
|
+
// quota, route whether or not it has any."
|
|
291
|
+
//
|
|
396
292
|
// A slow lane is orthogonal to cooldown, never a member of it: the account
|
|
397
293
|
// still serves on spare capacity when the request carries the slow usage
|
|
398
294
|
// limit, so excluding it would park a serving credential. It stays eligible
|
|
399
295
|
// and only orders behind fresh siblings (see the ordering below).
|
|
400
|
-
//
|
|
401
|
-
// Deliberately AFTER the floor gate: orthogonal means it does not add an
|
|
402
|
-
// exclusion, not that it overrides one. An account with no usable allowance
|
|
403
|
-
// is not rescued by the lane — the lane is a latency budget against spare
|
|
404
|
-
// capacity, and an account below its floor has none.
|
|
405
296
|
if (candidate.lane === "slow")
|
|
406
297
|
return { eligible: true, reason: "slow", candidate };
|
|
407
298
|
if (candidate.health_status === "cooldown")
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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{c as i,v as a}from"./numbers-CoqpaDMx.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,K as p,Q as m,U as h,W as g,X as _,Y as v,Z as y,et as b}from"./entry-D6WvojeD.js";import{n as x,r as S,t as C}from"./card-CxDw0lK6.js";import{t as w}from"./switch-DmZcyTIC.js";import{t as T}from"./select-BHqpd-bg.js";import{t as E}from"./stack-j7cXjgYX.js";import{t as D}from"./inline-CHJQwxLa.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]=p(),[n,r]=(0,j.useState)(!1),[m,v]=(0,j.useState)(null),w=async n=>{let i=n.target.files?.[0];if(n.target.value=``,!i)return;let o=y(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>f;r(!0);try{let n=o===`image`?await l(i):i;await b(n,o),v(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(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:()=>{u({backgroundOpacity:h.backgroundOpacity,backgroundScale:h.backgroundScale,backgroundX:h.backgroundX,backgroundY:h.backgroundY,backgroundBlur:h.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:v,bytes:m}),(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:()=>{u({blurCardsAmount:h.blurCardsAmount,blurChromeAmount:h.blurChromeAmount,blurCardsEnabled:h.blurCardsEnabled,blurChromeEnabled:h.blurChromeEnabled,blurCardsDepth:h.blurCardsDepth,blurChromeDepth:h.blurChromeDepth,renderMode:h.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:d.map(n=>{let r=n===`chrome`,i=e.renderMode===`off`?void 0:g[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(v),o(`image`);return}let t=!0,r=null;return m().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:h.backgroundOpacity,min:0,max:100,unit:`%`,onChange:e=>t({backgroundOpacity:e})}),(0,M.jsx)(R,{label:`Blur`,value:e.backgroundBlur,defaultValue:h.backgroundBlur,min:0,max:100,unit:`%`,onChange:e=>t({backgroundBlur:e})}),(0,M.jsx)(R,{label:`Zoom`,value:e.backgroundScale,defaultValue:h.backgroundScale,min:100,max:180,unit:`%`,onChange:e=>t({backgroundScale:e})}),(0,M.jsx)(R,{label:`Shift horizontal`,value:e.backgroundX,defaultValue:h.backgroundX,min:-100,max:100,onChange:e=>t({backgroundX:e})}),(0,M.jsx)(R,{label:`Shift vertical`,value:e.backgroundY,defaultValue:h.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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e,r as t,t as n}from"./jsx-runtime-CXUMngI_.js";import{i as r}from"./terminal-0_IjPWTk.js";import{c as i,g as a}from"./numbers-CoqpaDMx.js";import{n as o,r as s,t as c}from"./state-DHmEnLa2.js";import{t as l}from"./dialog-BK-NRr5u.js";import{t as u}from"./rotate-ccw-CufcqITi.js";import{t as d}from"./trash-Cppn5c3R.js";import{C as f,T as p,v as m}from"./assert-CidQVoNo.js";import{o as h,t as g}from"./query-keys-YSoQOUh6.js";import{t as _}from"./useMutation-CbCP4w1o.js";import{V as v,b as y,j as b,r as x,x as S}from"./entry-D6WvojeD.js";import{t as C}from"./badge-BaX2GTrn.js";import{n as w}from"./inline-CHJQwxLa.js";import{t as T}from"./layout-Bz93I-yE.js";async function E(e){return h(`/quota/overview`,e?{signal:e}:void 0)}function D(){return f({queryKey:g.quota.all,queryFn:e=>E(m(e)),staleTime:1/0,refetchInterval:!1,refetchIntervalInBackground:!1,refetchOnMount:!1,refetchOnWindowFocus:!1,placeholderData:e=>e})}function O(e){let t=p(),n=g.quota.account(e);return _({mutationFn:async()=>h(`/accounts/${encodeURIComponent(e)}/quota/refresh`,{method:`POST`}),onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:n,exact:!0}),t.invalidateQueries({queryKey:g.quota.all}),t.invalidateQueries({queryKey:g.providers.all})])}})}function k(e){let t=p();return _({mutationFn:async()=>(await h(`/accounts/${encodeURIComponent(e)}/claim-gifts`,{method:`POST`})).data,onSuccess:async n=>{n.claimed.length!==0&&await Promise.all([t.invalidateQueries({queryKey:g.quota.account(e),exact:!0}),t.invalidateQueries({queryKey:g.quota.all}),t.invalidateQueries({queryKey:g.providers.all})])}})}function A(){let e=p();return _({mutationFn:async({accountIds:e,onProgress:t})=>{let n=e.length,r=0,i=await h(`/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:g.quota.account(t),exact:!0})),e.invalidateQueries({queryKey:g.quota.all})])}})}function j(){let e=p();return _({mutationFn:({id:e,active:t})=>h(`/accounts/${encodeURIComponent(e)}`,{method:`PATCH`,body:JSON.stringify({active:t})}),onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:g.quota.all}),e.invalidateQueries({queryKey:g.providers.all})])}})}function M(){let e=p();return _({mutationFn:async({ids:e,active:t})=>{e.length!==0&&await h(`/accounts/batch`,{method:`PATCH`,body:JSON.stringify({ids:e,active:t})})},onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:g.quota.all}),e.invalidateQueries({queryKey:g.providers.all})])}})}function N(){let e=p();return _({mutationFn:async e=>{await h(`/accounts/${encodeURIComponent(e)}`,{method:`DELETE`})},onSuccess:async(t,n)=>{e.removeQueries({queryKey:g.quota.account(n),exact:!0}),await Promise.all([e.invalidateQueries({queryKey:g.quota.all}),e.invalidateQueries({queryKey:g.providers.all})])}})}var P=new Set([`workbuddy`,`workbuddycn`,`cb`,`cbcn`]);function F(e){return P.has(e.trim().toLowerCase())}function I(e){let t=p();return _({mutationFn:async()=>{if(e.length===0)throw Error(`Select a provider first`);return h(`/quota/daily-growth`,{method:`POST`,body:JSON.stringify({providerId:e})})},onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:g.quota.all}),t.invalidateQueries({queryKey:g.providers.all})])}})}function L(e){let t=p();return _({mutationFn:async()=>{let t=await h(`/accounts/${encodeURIComponent(e)}/activity-report`,{method:`POST`}),n=t.message??`Activity reported`,r=await h(`/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:g.quota.all}),t.invalidateQueries({queryKey:g.providers.all})])}})}var R=new Set([`codex`,`claude`,`anthropic`]);function z(e){return R.has(e.trim().toLowerCase())}function B(e,t){return f({queryKey:g.quota.resets(e),queryFn:({signal:t})=>h(`/accounts/${encodeURIComponent(e)}/resets`,t?{signal:t}:void 0),enabled:t,staleTime:3e4})}function V(e){let t=p();return _({mutationFn:async t=>h(`/accounts/${encodeURIComponent(e)}/reset`,{method:`POST`,body:JSON.stringify(t??{})}),onSettled:async()=>{await Promise.all([t.invalidateQueries({queryKey:g.quota.all}),t.invalidateQueries({queryKey:g.providers.all})])}})}var H=e(t(),1),U=n();function W(e){return e===`cooldown`}function G(e){return e===`active`?`ok`:e===`disabled`?`disabled`:`warn`}function K(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 q({summary:e,onClose:t}){let{providerId:n,accountId:f,title:p,status:m,errorCategory:h,errorMessage:g,jwtIssuer:_}=e,E=x(n,f),D=S(),O=E.data??[],k=b(),A=e.credentialKind===`oauth`||e.credentialMode===`jwt`||e.staticToken===!0,[j,M]=(0,H.useState)(e.staticToken===!0),[N,P]=(0,H.useState)(null),[F,I]=(0,H.useState)(!1);(0,H.useEffect)(()=>M(e.staticToken===!0),[e.staticToken]);let L=y();return(0,U.jsx)(l,{open:!0,onClose:t,title:`Health & Error Log — ${p}`,size:`lg`,children:(0,U.jsxs)(`div`,{className:`flex flex-col gap-7 min-w-0`,children:[(0,U.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,U.jsxs)(`div`,{className:`min-w-0`,children:[(0,U.jsxs)(w,{gap:`8px`,className:`flex-wrap`,children:[(0,U.jsx)(`span`,{className:`text-body font-semibold`,children:`Account status:`}),(0,U.jsx)(C,{tone:G(m),dot:!0,children:m.toUpperCase()}),h?(0,U.jsx)(C,{tone:`warn`,title:g??void 0,children:h}):null]}),g?(0,U.jsx)(`p`,{className:`text-badge text-red mt-2 wrap-anywhere`,children:g}):null]}),W(m)?(0,U.jsx)(i,{size:`sm`,variant:`secondary`,icon:(0,U.jsx)(u,{size:12,className:D.isPending?`animate-spin`:``}),disabled:D.isPending,onClick:()=>D.mutate({providerId:n,accountId:f},{onSuccess:()=>void E.refetch()}),children:D.isPending?`Recovering...`:`Recover Now`}):null]}),A?(0,U.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,U.jsxs)(`div`,{children:[(0,U.jsx)(`div`,{className:`text-caption font-semibold`,children:`Credential management`}),(0,U.jsx)(`div`,{className:`text-badge text-text-tertiary`,children:j?`Static mode: no refresh request will be sent.`:`OAuth refresh is enabled for this account.`}),_?(0,U.jsxs)(`div`,{className:`text-badge text-text-tertiary mt-1 wrap-anywhere`,children:[`Static · no refresh · issuer`,` `,(0,U.jsx)(`span`,{className:`font-mono text-text-secondary`,children:_})]}):null]}),(0,U.jsx)(i,{size:`sm`,variant:j?`primary`:`secondary`,disabled:k.isPending,onClick:()=>P(!j),children:j?`Static · no refresh`:`Enable static mode`})]}):null,(0,U.jsxs)(`div`,{className:`min-w-0`,children:[(0,U.jsxs)(`div`,{className:`flex items-center justify-between gap-5 mb-4`,children:[(0,U.jsxs)(`h4`,{className:`text-caption font-semibold text-text-secondary`,children:[`Transition Events & Audit (`,O.length,`)`]}),(0,U.jsx)(i,{size:`sm`,variant:`secondary`,icon:(0,U.jsx)(d,{size:12}),disabled:O.length===0||L.isPending,onClick:()=>I(!0),title:`Deletes this account's recorded transitions and audit entries.`,children:`Purge log`})]}),E.isPending?(0,U.jsx)(s,{compact:!0,label:`Loading health events...`}):E.isError?(0,U.jsx)(o,{compact:!0,message:`Failed to load health events`,onRetry:()=>void E.refetch()}):O.length===0?(0,U.jsx)(c,{compact:!0,title:`No health events recorded`,message:e.emptyMessage,icon:(0,U.jsx)(r,{size:20})}):(0,U.jsx)(`div`,{className:`rounded-inset border border-inner-border`,children:(0,U.jsx)(T,{maxHeight:480,scrollRegion:!0,headers:[`Timestamp`,`From → To`,`Model`,`Category`,`Reason / Details`],children:O.map(e=>(0,U.jsxs)(`tr`,{children:[(0,U.jsx)(`td`,{className:`text-badge text-text-tertiary whitespace-nowrap`,children:new Date(e.createdAt).toLocaleString()}),(0,U.jsxs)(`td`,{className:`whitespace-nowrap`,children:[e.fromStatus!==null&&e.fromStatus!==e.toStatus?(0,U.jsxs)(`span`,{className:`text-badge text-text-secondary`,children:[e.fromStatus,` →`,` `]}):(0,U.jsx)(`span`,{className:`text-badge text-text-tertiary`,children:`logged · `}),(0,U.jsx)(C,{tone:G(e.toStatus),children:e.toStatus})]}),(0,U.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary wrap-anywhere`,children:e.modelId??`—`}),(0,U.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary`,children:e.errorCategory??`—`}),(0,U.jsx)(`td`,{className:`text-badge text-text-primary min-w-110 max-w-260 wrap-anywhere`,children:e.reason??`—`})]},e.id))})})]}),(0,U.jsx)(v,{open:N!==null,onClose:()=>P(null),onConfirm:()=>{let e=N===!0;k.mutate({providerId:n,accountId:f,request:{staticToken:e}},{onSuccess:()=>{M(e),P(null),a.success(e?`Static mode enabled`:`Credential refresh re-enabled`,p),E.refetch()},onError:e=>a.error(`Credential update failed`,e instanceof Error?e.message:`Unable to update credential mode`)})},title:K(N===!0,p).title,message:K(N===!0,p).message,confirmLabel:K(N===!0,p).confirmLabel,cancelLabel:`Cancel`}),(0,U.jsx)(v,{open:F,onClose:()=>I(!1),onConfirm:()=>{L.mutate({providerId:n,accountId:f,title:p},{onSuccess:()=>I(!1)})},title:`Purge the health & error log for ${p}?`,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,U.jsx)(w,{justify:`flex-end`,gap:`0px`,children:(0,U.jsx)(i,{variant:`secondary`,size:`sm`,onClick:t,children:`Close`})})]})})}export{k as a,O as c,V as d,L as f,B as i,A as l,j as m,F as n,N as o,I as p,z as r,D as s,q as t,M as u};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|