cartethyia 2.5.1 → 2.5.3
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 +5 -1
- package/dist/dashboard/assets/{AccountCooldown-ChpdzxRH.js → AccountCooldown-Bjo92Qat.js} +1 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/{BannedPage-ChqnTRpZ.js → BannedPage-hfCzi-Ux.js} +1 -1
- package/dist/dashboard/assets/BannedPage-hfCzi-Ux.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BXUaRCQQ.js → CliToolsPage-DEDyKBlC.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DEDyKBlC.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js +1 -0
- package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DDVIfZGW.js +1 -0
- package/dist/dashboard/assets/LoginPage-DDVIfZGW.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js +1 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js +1 -0
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js +7 -0
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js +1 -0
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-D6pmQ86L.js +1 -0
- package/dist/dashboard/assets/SetupPage-D6pmQ86L.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DWvKiuSl.js +1 -0
- package/dist/dashboard/assets/SortableList-DWvKiuSl.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-DqK5E26W.js → StudioPage-CmkUrgdj.js} +3 -3
- package/dist/dashboard/assets/StudioPage-CmkUrgdj.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B47uOydr.js +56 -0
- package/dist/dashboard/assets/UsagePage-B47uOydr.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BmDbsior.js → api-keys-uNywYlfv.js} +1 -1
- package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
- package/dist/dashboard/assets/{app-C-Y4oZT0.js → app-CafWtVXA.js} +1 -1
- package/dist/dashboard/assets/{app-C-Y4oZT0.js.gz → app-CafWtVXA.js.gz} +0 -0
- package/dist/dashboard/assets/app-DxivpfYG.css +2 -0
- package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
- package/dist/dashboard/assets/{assert-BHfaei-X.js → assert-NHwYq95t.js} +1 -1
- package/dist/dashboard/assets/{assert-BHfaei-X.js.gz → assert-NHwYq95t.js.gz} +0 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js +1 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js +1 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
- package/dist/dashboard/assets/card--AtFedAq.js +1 -0
- package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-B0wBq-RL.js → cli-tools-B3e7dx3N.js} +1 -1
- package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js +1 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js +1 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js +1 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
- package/dist/dashboard/assets/entry-CkryrIea.js +4 -0
- package/dist/dashboard/assets/entry-CkryrIea.js.gz +0 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js +1 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
- package/dist/dashboard/assets/{entry-D4O2229X.js → entry-jy9_4-Ke.js} +1 -1
- package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js +1 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
- package/dist/dashboard/assets/{index-CcD1eSJ6.js → index-BydyKaDz.js} +2 -2
- package/dist/dashboard/assets/index-BydyKaDz.js.gz +0 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js +1 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
- package/dist/dashboard/assets/input-BG41et9G.js +1 -0
- package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js +1 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js +1 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
- package/dist/dashboard/assets/{live-yfEJFF9T.js → live-BJGYRBnR.js} +1 -1
- package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CT-TxSNX.js → model-bans-DeVLDA7I.js} +1 -1
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js +2 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
- package/dist/dashboard/assets/{routing-Cs6jl49L.js → routing-CcusqXA7.js} +1 -1
- package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
- package/dist/dashboard/assets/select-BO6h8yNv.js +41 -0
- package/dist/dashboard/assets/select-BO6h8yNv.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CfcTcOyU.js → settings-mpmXTgUa.js} +1 -1
- package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js +1 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js +1 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js +1 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQKMcJ1H.js → useMutation-w4u4CVue.js} +1 -1
- package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -54
- package/package.json +1 -1
- package/scripts/cli/command/console-menu.ts +248 -246
- package/scripts/cli/command/tray.ts +208 -204
- package/src/boot/app.ts +3 -2
- package/src/config/spec.ts +26 -2
- package/src/console/backup/routes.ts +52 -1
- package/src/console/backup/service.ts +60 -4
- package/src/console/observability/contracts.ts +11 -1
- package/src/console/observability/store.ts +9 -6
- package/src/console/share/share-router.ts +6 -9
- package/src/observability/payload-capture.ts +366 -344
- package/src/observability/payload-store.ts +12 -2
- package/src/observability/telemetry-status.ts +28 -16
- package/src/persistence/stores/api-key-store.ts +6 -0
- package/src/persistence/stores/share-store.ts +583 -556
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/protocol/response/gemini.ts +18 -4
- package/src/providers/discovery/openrouter-free-roster.ts +3 -11
- package/src/providers/discovery/probe-phases.ts +42 -5
- package/src/providers/discovery/probing-service.ts +25 -2
- package/src/providers/integrations/antigravity/antigravity-quota.ts +11 -3
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +4 -4
- package/src/providers/integrations/buddy/workbuddy/shared.ts +201 -200
- package/src/providers/integrations/claude/claude.ts +3 -1
- package/src/providers/integrations/cline/cline-quota.ts +8 -11
- package/src/providers/integrations/cloudflareai/cloudflareai-models.ts +3 -1
- package/src/providers/integrations/inferhub.ts +3 -1
- package/src/providers/integrations/kilo/kilo.ts +0 -1
- package/src/providers/integrations/kiro/kiro-catalog.ts +8 -2
- package/src/providers/integrations/opencode/provider.ts +532 -532
- package/src/providers/integrations/openrouter/provider.ts +5 -15
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae-catalog.ts +5 -3
- package/src/providers/operations/account-health-service.ts +23 -0
- package/src/providers/operations/client-versions.ts +9 -2
- package/src/providers/provider-metadata.ts +7 -1
- package/src/security/api-key-auth.ts +23 -0
- package/src/transport/dispatch/attempt-finalize.ts +46 -5
- package/src/transport/dispatch/proxy-request.ts +9 -0
- package/src/transport/dispatch/streaming/stream.ts +16 -2
- package/src/transport/guards/auth-guard.ts +2 -3
- package/src/transport/routing/route-catalog.ts +806 -799
- package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BXUaRCQQ.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js +0 -1
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CytxSeGP.js +0 -1
- package/dist/dashboard/assets/LoginPage-CytxSeGP.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +0 -1
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js +0 -7
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-8-eVadhc.js +0 -1
- package/dist/dashboard/assets/SettingsPage-8-eVadhc.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-uEIXPyGO.js +0 -1
- package/dist/dashboard/assets/SetupPage-uEIXPyGO.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js +0 -1
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-DqK5E26W.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js +0 -56
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
- package/dist/dashboard/assets/app-BbpSarVV.css +0 -2
- package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
- package/dist/dashboard/assets/badge-DpXT4Ixt.js +0 -1
- package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
- package/dist/dashboard/assets/banner-CSAAWLJv.js +0 -1
- package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
- package/dist/dashboard/assets/card-DEhRXXw4.js +0 -1
- package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
- package/dist/dashboard/assets/check-D69FzdvC.js +0 -1
- package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +0 -1
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js +0 -1
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BvQeBMfP.js +0 -1
- package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CMwPXwQB.js +0 -1
- package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
- package/dist/dashboard/assets/entry--yj84zk3.js +0 -4
- package/dist/dashboard/assets/entry--yj84zk3.js.gz +0 -0
- package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js +0 -1
- package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js +0 -1
- package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
- package/dist/dashboard/assets/index-CcD1eSJ6.js.gz +0 -0
- package/dist/dashboard/assets/inline-D1IlZZ8H.js +0 -1
- package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
- package/dist/dashboard/assets/input-BpVZdrbc.js +0 -1
- package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
- package/dist/dashboard/assets/layout-BioLW7gN.js +0 -1
- package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CwVcGAIc.js +0 -1
- package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
- package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js +0 -1
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
- package/dist/dashboard/assets/select-XPJ-dIw3.js +0 -41
- package/dist/dashboard/assets/select-XPJ-dIw3.js.gz +0 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
- package/dist/dashboard/assets/stack-C9_uUjaA.js +0 -1
- package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
- package/dist/dashboard/assets/state-DA0B95UQ.js +0 -1
- package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
- package/dist/dashboard/assets/switch-Cittxy-x.js +0 -1
- package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js +0 -2
- package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
|
@@ -1,200 +1,201 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
|
|
4
|
-
import type { OpenAICompatibleAdapterConfig } from "../../../adapters/compatible-adapter";
|
|
5
|
-
import { withBearerAuthentication } from "../../../adapters/compatible-adapter";
|
|
6
|
-
import type { CanonicalRequest } from "../../../../protocol/canonical-model";
|
|
7
|
-
import type { ProviderDispatchContext, ProviderDispatchTarget, ProviderId } from "../../../provider-contracts";
|
|
8
|
-
import { resolveInboundSessionId } from "../../../operations/session-resolution";
|
|
9
|
-
import { buddyConversationHeaders } from "../buddy-conversation-headers";
|
|
10
|
-
import { buddyCliHeaders, buddyCliTraceHeaders } from "../buddy-cli-headers";
|
|
11
|
-
import { buddyUserId, resolveBuddyIdentity } from "../buddy-identity";
|
|
12
|
-
import {
|
|
13
|
-
buildWorkBuddyUserAgent,
|
|
14
|
-
getWorkBuddyClientVersion,
|
|
15
|
-
getWorkBuddyCliVersion,
|
|
16
|
-
} from "../../../operations/client-versions";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* WorkBuddy serves chat from a versioned path on a base URL that carries no
|
|
20
|
-
* version segment (billing uses the unversioned `/billing/…` path), so both the
|
|
21
|
-
* adapter config and the static catalog rows must state it. The generic
|
|
22
|
-
* `/chat/completions` default would join into
|
|
23
|
-
* `https://www.workbuddy.ai/chat/completions`, which that upstream answers with
|
|
24
|
-
* a 405 HTML error page.
|
|
25
|
-
*/
|
|
26
|
-
export const WORKBUDDY_CHAT_PATH = "/v2/chat/completions" as const;
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
buildWorkBuddyUserAgent,
|
|
30
|
-
getWorkBuddyClientVersion,
|
|
31
|
-
getWorkBuddyCliVersion,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/** WorkBuddy international gateway domain (Origin/Referer/X-Domain). */
|
|
35
|
-
export const WORKBUDDY_DOMAIN = "www.workbuddy.ai";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Stable per-account device identifiers.
|
|
39
|
-
*
|
|
40
|
-
* WorkBuddy's desktop client presents one fixed virtual device per account.
|
|
41
|
-
* Deriving `X-Machine-ID` / `X-Session-ID` from the account id (sha256, 36 hex)
|
|
42
|
-
* keeps them constant across restarts while staying distinct per account, so
|
|
43
|
-
* multiple pooled accounts are not correlated by a missing or drifting device
|
|
44
|
-
* fingerprint. Mirrors the reference gateway's `wb2a:<purpose>:<uid>` scheme.
|
|
45
|
-
*/
|
|
46
|
-
function deriveAccountStableId(accountId: string, purpose: string): string {
|
|
47
|
-
const sum = createHash("sha256").update(`wb2a:${purpose}:${accountId}`).digest("hex");
|
|
48
|
-
return sum.slice(0, 36);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Per-request WorkBuddy chat headers — the exact desktop-client contract.
|
|
53
|
-
*
|
|
54
|
-
* The international gateway gates chat on the desktop `User-Agent` (platform
|
|
55
|
-
* brand `WorkBuddy AI
|
|
56
|
-
* `X-CodeBuddy-Request` risk
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"x-
|
|
86
|
-
"x-
|
|
87
|
-
"x-ide-
|
|
88
|
-
"x-ide-
|
|
89
|
-
"x-
|
|
90
|
-
"x-
|
|
91
|
-
|
|
92
|
-
...
|
|
93
|
-
...
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
"x-
|
|
104
|
-
"x-
|
|
105
|
-
"x-
|
|
106
|
-
"x-
|
|
107
|
-
"x-ide-
|
|
108
|
-
"x-ide-
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
headers["x-
|
|
127
|
-
headers["x-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
import type { OpenAICompatibleAdapterConfig } from "../../../adapters/compatible-adapter";
|
|
5
|
+
import { withBearerAuthentication } from "../../../adapters/compatible-adapter";
|
|
6
|
+
import type { CanonicalRequest } from "../../../../protocol/canonical-model";
|
|
7
|
+
import type { ProviderDispatchContext, ProviderDispatchTarget, ProviderId } from "../../../provider-contracts";
|
|
8
|
+
import { resolveInboundSessionId } from "../../../operations/session-resolution";
|
|
9
|
+
import { buddyConversationHeaders } from "../buddy-conversation-headers";
|
|
10
|
+
import { buddyCliHeaders, buddyCliTraceHeaders } from "../buddy-cli-headers";
|
|
11
|
+
import { buddyUserId, resolveBuddyIdentity } from "../buddy-identity";
|
|
12
|
+
import {
|
|
13
|
+
buildWorkBuddyUserAgent,
|
|
14
|
+
getWorkBuddyClientVersion,
|
|
15
|
+
getWorkBuddyCliVersion,
|
|
16
|
+
} from "../../../operations/client-versions";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* WorkBuddy serves chat from a versioned path on a base URL that carries no
|
|
20
|
+
* version segment (billing uses the unversioned `/billing/…` path), so both the
|
|
21
|
+
* adapter config and the static catalog rows must state it. The generic
|
|
22
|
+
* `/chat/completions` default would join into
|
|
23
|
+
* `https://www.workbuddy.ai/chat/completions`, which that upstream answers with
|
|
24
|
+
* a 405 HTML error page.
|
|
25
|
+
*/
|
|
26
|
+
export const WORKBUDDY_CHAT_PATH = "/v2/chat/completions" as const;
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
buildWorkBuddyUserAgent,
|
|
30
|
+
getWorkBuddyClientVersion,
|
|
31
|
+
getWorkBuddyCliVersion,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** WorkBuddy international gateway domain (Origin/Referer/X-Domain). */
|
|
35
|
+
export const WORKBUDDY_DOMAIN = "www.workbuddy.ai";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Stable per-account device identifiers.
|
|
39
|
+
*
|
|
40
|
+
* WorkBuddy's desktop client presents one fixed virtual device per account.
|
|
41
|
+
* Deriving `X-Machine-ID` / `X-Session-ID` from the account id (sha256, 36 hex)
|
|
42
|
+
* keeps them constant across restarts while staying distinct per account, so
|
|
43
|
+
* multiple pooled accounts are not correlated by a missing or drifting device
|
|
44
|
+
* fingerprint. Mirrors the reference gateway's `wb2a:<purpose>:<uid>` scheme.
|
|
45
|
+
*/
|
|
46
|
+
function deriveAccountStableId(accountId: string, purpose: string): string {
|
|
47
|
+
const sum = createHash("sha256").update(`wb2a:${purpose}:${accountId}`).digest("hex");
|
|
48
|
+
return sum.slice(0, 36);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Per-request WorkBuddy chat headers — the exact desktop-client contract.
|
|
53
|
+
*
|
|
54
|
+
* The international gateway gates chat on the desktop `User-Agent` (platform
|
|
55
|
+
* brand `WorkBuddy AI`, carried on the wire as the token `workbuddy-ai`;
|
|
56
|
+
* the wrong token trips 403 code 11140), plus the `X-CodeBuddy-Request` risk
|
|
57
|
+
* gate and the account-scoped device headers.
|
|
58
|
+
* The background client-version monitor keeps this value fresh; dispatch only
|
|
59
|
+
* reads the cached value and never waits on a version endpoint.
|
|
60
|
+
*
|
|
61
|
+
* `cliIdentity` selects the reference CLI contract instead. The two are not
|
|
62
|
+
* variations of one set: the CLI drops every browser header and the device
|
|
63
|
+
* token, and adds the agent envelope. Left unset, nothing changes.
|
|
64
|
+
*/
|
|
65
|
+
export async function workbuddyHeaders(
|
|
66
|
+
context?: ProviderDispatchContext,
|
|
67
|
+
request?: CanonicalRequest,
|
|
68
|
+
domain = WORKBUDDY_DOMAIN,
|
|
69
|
+
clientVersion = getWorkBuddyClientVersion(),
|
|
70
|
+
cliIdentity?: boolean,
|
|
71
|
+
): Promise<Record<string, string>> {
|
|
72
|
+
const conversation = buddyConversationHeaders({
|
|
73
|
+
...(resolveInboundSessionId(context, request) === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: { conversationId: resolveInboundSessionId(context, request) }),
|
|
76
|
+
});
|
|
77
|
+
const accountId = context?.credential.account_id;
|
|
78
|
+
if (resolveBuddyIdentity(context, cliIdentity) === "cli") {
|
|
79
|
+
// The reference CLI: issued by the OpenAI Node SDK, so no browser header
|
|
80
|
+
// and no device token, and the machine/session device pair does not
|
|
81
|
+
// exist — only `X-User-Id`, which is `anonymous` without a uid.
|
|
82
|
+
return {
|
|
83
|
+
accept: "application/json",
|
|
84
|
+
"user-agent": `CLI/${getWorkBuddyCliVersion()} CodeBuddy/${getWorkBuddyCliVersion()}`,
|
|
85
|
+
"x-domain": domain,
|
|
86
|
+
"x-agent-purpose": "conversation",
|
|
87
|
+
"x-ide-name": "CLI",
|
|
88
|
+
"x-ide-type": "CLI",
|
|
89
|
+
"x-ide-version": getWorkBuddyCliVersion(),
|
|
90
|
+
"x-product": "SaaS",
|
|
91
|
+
"x-no-enterprise-id": "1",
|
|
92
|
+
...conversation,
|
|
93
|
+
...buddyCliTraceHeaders(conversation),
|
|
94
|
+
...buddyCliHeaders({ userId: buddyUserId(context) }),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const headers: Record<string, string> = {
|
|
98
|
+
accept: "application/json, text/event-stream",
|
|
99
|
+
"user-agent": buildWorkBuddyUserAgent(clientVersion, getWorkBuddyCliVersion()),
|
|
100
|
+
origin: `https://${domain}`,
|
|
101
|
+
referer: `https://${domain}/`,
|
|
102
|
+
"accept-language": "en-US",
|
|
103
|
+
"x-requested-with": "XMLHttpRequest",
|
|
104
|
+
"x-codebuddy-request": "1",
|
|
105
|
+
"x-domain": domain,
|
|
106
|
+
"x-agent-purpose": "conversation",
|
|
107
|
+
"x-ide-name": "WorkBuddy",
|
|
108
|
+
"x-ide-type": "WorkBuddy",
|
|
109
|
+
"x-ide-version": clientVersion,
|
|
110
|
+
// Both references send the literal `SaaS` here. `WorkBuddy` was a value
|
|
111
|
+
// this repo invented; the desktop product name belongs in `x-ide-name`,
|
|
112
|
+
// and the CN test below compares the two realms against each other rather
|
|
113
|
+
// than to a hard-coded string.
|
|
114
|
+
"x-product": "SaaS",
|
|
115
|
+
// Personal international accounts carry no enterprise id: declare it
|
|
116
|
+
// explicitly so upstream risk control does not treat the omission as
|
|
117
|
+
// suspicious.
|
|
118
|
+
"x-no-enterprise-id": "1",
|
|
119
|
+
};
|
|
120
|
+
// The conversation/tracing family the upstream's channel check looks for. A
|
|
121
|
+
// live request carrying only `x-session-id` was refused for a missing
|
|
122
|
+
// conversation id; the aggregation key below is the one the reference marks
|
|
123
|
+
// mandatory.
|
|
124
|
+
Object.assign(headers, conversation);
|
|
125
|
+
if (accountId) {
|
|
126
|
+
headers["x-user-id"] = accountId;
|
|
127
|
+
headers["x-machine-id"] = deriveAccountStableId(accountId, "machine");
|
|
128
|
+
headers["x-session-id"] = deriveAccountStableId(accountId, "session");
|
|
129
|
+
}
|
|
130
|
+
const deviceToken = readBuddyDeviceToken(context);
|
|
131
|
+
if (deviceToken !== undefined) headers["x-device-token"] = deviceToken;
|
|
132
|
+
return headers;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Desktop risk token, sent only when the account actually has one.
|
|
137
|
+
*
|
|
138
|
+
* The account's own `auth_state` wins. When it carries none, a file the
|
|
139
|
+
* desktop client writes (`CARTETHYIA_DEVICE_TOKEN_FILE`, default
|
|
140
|
+
* `/app/data/device_token`) is read instead — cached for five minutes, and
|
|
141
|
+
* ignored when it is larger than 1 KB, because a token file that size is not
|
|
142
|
+
* a token. A missing file is the normal case and reads as "no token".
|
|
143
|
+
*/
|
|
144
|
+
const DEVICE_TOKEN_FILE_MAX_BYTES = 1024;
|
|
145
|
+
const DEVICE_TOKEN_FILE_TTL_MS = 5 * 60_000;
|
|
146
|
+
let deviceTokenFileCache: { readonly value: string | undefined; readonly readAt: number } | undefined;
|
|
147
|
+
|
|
148
|
+
export function readBuddyDeviceToken(context?: ProviderDispatchContext): string | undefined {
|
|
149
|
+
const state = context?.credential.auth_state;
|
|
150
|
+
const value = state?.["deviceToken"] ?? state?.["device_token"];
|
|
151
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
152
|
+
return readDeviceTokenFile();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readDeviceTokenFile(): string | undefined {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
if (deviceTokenFileCache !== undefined && now - deviceTokenFileCache.readAt < DEVICE_TOKEN_FILE_TTL_MS) {
|
|
158
|
+
return deviceTokenFileCache.value;
|
|
159
|
+
}
|
|
160
|
+
const path = process.env["CARTETHYIA_DEVICE_TOKEN_FILE"] ?? "/app/data/device_token";
|
|
161
|
+
let value: string | undefined;
|
|
162
|
+
try {
|
|
163
|
+
const raw = readFileSync(path);
|
|
164
|
+
if (raw.byteLength > 0 && raw.byteLength <= DEVICE_TOKEN_FILE_MAX_BYTES) {
|
|
165
|
+
const text = raw.toString("utf8").trim();
|
|
166
|
+
if (text.length > 0) value = text;
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
value = undefined;
|
|
170
|
+
}
|
|
171
|
+
deviceTokenFileCache = { value, readAt: now };
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Shared OpenAI-compatible chat-adapter config for WorkBuddy: bearer auth
|
|
177
|
+
* (api_key and oauth both use `Authorization: Bearer`), `/v2/chat/completions`
|
|
178
|
+
* endpoint, per-request desktop-client headers, and the payload hook.
|
|
179
|
+
*/
|
|
180
|
+
export function workbuddyAdapterConfig(args: {
|
|
181
|
+
providerId: ProviderId;
|
|
182
|
+
baseUrl: string;
|
|
183
|
+
domain?: string;
|
|
184
|
+
prePayload: (
|
|
185
|
+
payload: Record<string, unknown>,
|
|
186
|
+
request: CanonicalRequest,
|
|
187
|
+
candidate: ProviderDispatchTarget,
|
|
188
|
+
) => void;
|
|
189
|
+
fetchImpl?: typeof fetch;
|
|
190
|
+
cliIdentity?: boolean;
|
|
191
|
+
}): OpenAICompatibleAdapterConfig {
|
|
192
|
+
return withBearerAuthentication({
|
|
193
|
+
provider_id: args.providerId,
|
|
194
|
+
base_url: args.baseUrl,
|
|
195
|
+
endpoint_paths_by_wire_family: { chat: WORKBUDDY_CHAT_PATH },
|
|
196
|
+
buildExtraHeaders: (context, request) =>
|
|
197
|
+
workbuddyHeaders(context, request, args.domain ?? WORKBUDDY_DOMAIN, undefined, args.cliIdentity),
|
|
198
|
+
prePayload: args.prePayload,
|
|
199
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
@@ -96,7 +96,9 @@ export const CLAUDE_MODELS: readonly ModelDefinition[] = [
|
|
|
96
96
|
claudeMessagesModel("claude-sonnet-5", 1_000_000, 128_000, true),
|
|
97
97
|
claudeMessagesModel("claude-sonnet-4-6", 1_000_000, 128_000, true),
|
|
98
98
|
claudeMessagesModel("claude-opus-4-5", 200_000, 64_000, true),
|
|
99
|
-
|
|
99
|
+
// 200K: the 4.5 trio predates the million-token window, which arrived with
|
|
100
|
+
// Opus 4.6. Only this row claimed 1M while its own siblings said 200K.
|
|
101
|
+
claudeMessagesModel("claude-sonnet-4-5", 200_000, 64_000, true),
|
|
100
102
|
claudeMessagesModel("claude-haiku-4-5", 200_000, 64_000, true),
|
|
101
103
|
];
|
|
102
104
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
|
|
2
|
-
import { authCredential, getJson, record, text } from "../../quota/quota-contracts";
|
|
2
|
+
import { authCredential, getJson, record, text, unsupportedQuota } from "../../quota/quota-contracts";
|
|
3
3
|
import { parseLimitWindows } from "../../quota/quota-limit-windows";
|
|
4
|
-
import { probeApiKeyConnectivity } from "../../quota/quota-support";
|
|
5
4
|
import { getClineCliVersion, getClineSdkVersion } from "../../operations/client-versions";
|
|
6
5
|
import { clinePlatform, clinePlatformVersion } from "../../operations/cli-platform";
|
|
7
6
|
|
|
@@ -87,16 +86,14 @@ export async function fetchClineQuota(
|
|
|
87
86
|
credential: string,
|
|
88
87
|
fetcher: FetchLike,
|
|
89
88
|
): Promise<ProviderQuotaResult> {
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
89
|
+
// An api_key account has no OAuth envelope, and Cline's quota surface
|
|
90
|
+
// answers 401 for one — the upstream's message asks the operator to
|
|
91
|
+
// re-authenticate, which is a false verdict: the key is fine and the gateway
|
|
92
|
+
// never had a quota to read. Report "not available" and make no upstream
|
|
93
|
+
// call. A probe here also cannot distinguish a dead key from Cline refusing
|
|
94
|
+
// the client, so the only honest answer is that quota is unavailable.
|
|
94
95
|
if (credential.startsWith(CLINE_API_KEY_PREFIX)) {
|
|
95
|
-
return
|
|
96
|
-
CLINE_PROVIDER_ID,
|
|
97
|
-
credential.slice(CLINE_API_KEY_PREFIX.length),
|
|
98
|
-
fetcher,
|
|
99
|
-
);
|
|
96
|
+
return unsupportedQuota(CLINE_PROVIDER_ID);
|
|
100
97
|
}
|
|
101
98
|
const fields = authCredential(credential);
|
|
102
99
|
const access = text(fields.accessToken);
|
|
@@ -59,7 +59,9 @@ export const CLOUDFLAREAI_MODELS: readonly ModelDefinition[] = [
|
|
|
59
59
|
["claude-opus-4.7", 1_000_000, 128_000] as const,
|
|
60
60
|
["claude-opus-4.8", 1_000_000, 128_000] as const,
|
|
61
61
|
["claude-opus-5", 1_000_000, 128_000] as const,
|
|
62
|
-
|
|
62
|
+
// 200K, matching the other two 4.5 rows: the million-token window starts at
|
|
63
|
+
// Opus 4.6, so the 4.5 trio is 200K across the board.
|
|
64
|
+
["claude-sonnet-4.5", 200_000, 64_000] as const,
|
|
63
65
|
["claude-sonnet-4.6", 1_000_000, 128_000] as const,
|
|
64
66
|
["claude-sonnet-5", 1_000_000, 128_000] as const,
|
|
65
67
|
].map(cloudflareaiModel);
|
|
@@ -124,7 +124,9 @@ const INFERHUB_MODELS_RAW: readonly {
|
|
|
124
124
|
{ id: "cc/claude-opus-4-8", contextLimit: 1000000, outputLimit: 128000 },
|
|
125
125
|
{ id: "cc/claude-opus-5", contextLimit: 1000000, outputLimit: 128000 },
|
|
126
126
|
{ id: "cc/claude-sonnet-4-5", contextLimit: 200000, outputLimit: 64000 },
|
|
127
|
-
|
|
127
|
+
// 128K: Sonnet 4.6 is in the 1M/128K generation, the same as the Opus 4.6
|
|
128
|
+
// row directly above it and every other 4.6+ row here.
|
|
129
|
+
{ id: "cc/claude-sonnet-4-6", contextLimit: 1000000, outputLimit: 128000 },
|
|
128
130
|
{ id: "cc/claude-sonnet-5", contextLimit: 1000000, outputLimit: 128000 },
|
|
129
131
|
// cb/ — CodeBuddy (12 models)
|
|
130
132
|
{ id: "cb/claude-opus-4.6", contextLimit: 1000000, outputLimit: 128000 },
|
|
@@ -87,7 +87,6 @@ export const KILO_MODELS: readonly ModelDefinition[] = [
|
|
|
87
87
|
// window the router may select into rather than one model's.
|
|
88
88
|
kiloModel("kilo-auto/free", 256_000, 32_768, { reasoning: true }),
|
|
89
89
|
kiloModel("kilo-auto/small", 262_144, 32_768, { vision: true, reasoning: true }),
|
|
90
|
-
kiloModel("openrouter/auto", 2_000_000, 128_000, { reasoning: true }),
|
|
91
90
|
];
|
|
92
91
|
|
|
93
92
|
/**
|
|
@@ -71,9 +71,15 @@ export const KIRO_MODELS: readonly ModelDefinition[] = [
|
|
|
71
71
|
kiroModel({ id: "claude-opus-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
72
72
|
kiroModel({ id: "claude-sonnet-5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
73
73
|
kiroModel({ id: "claude-sonnet-5.5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
74
|
-
|
|
74
|
+
// 200K, not the 1M the 1M-window models use: Sonnet 4.5 predates it. The
|
|
75
|
+
// million-token window arrived with Opus 4.6 and the 5.x generation; the 4.5
|
|
76
|
+
// trio is 200K, and Opus and Haiku here already say so.
|
|
77
|
+
kiroModel({ id: "claude-sonnet-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
75
78
|
kiroModel({ id: "claude-haiku-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
76
|
-
|
|
79
|
+
// 1M/128K, not the 200K/64K of the 4.5 row above it: Haiku moved to the
|
|
80
|
+
// million-token window and the doubled output ceiling with the rest of the
|
|
81
|
+
// 5.x generation, and this line had been copied from its predecessor.
|
|
82
|
+
kiroModel({ id: "claude-haiku-5.5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
77
83
|
kiroModel({ id: "gpt-5.5", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|
|
78
84
|
kiroModel({ id: "gpt-5.6-sol", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|
|
79
85
|
kiroModel({ id: "gpt-5.6-terra", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|