@eddyskywalker/dsh-chatgpt-subscription 0.12.4 → 0.13.0
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/CHANGELOG.md +637 -619
- package/lib/client.js +3975 -4521
- package/lib/client.js.map +1 -1
- package/lib/index.js +1920 -512
- package/lib/types/client/CodexComposerQuota.d.ts.map +1 -1
- package/lib/types/client/CodexSubscriptionSection.d.ts +1 -3
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/ProviderHubSection.d.ts +6 -5
- package/lib/types/client/ProviderHubSection.d.ts.map +1 -1
- package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts +22 -1
- package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -1
- package/lib/types/client/antigravity/AntigravitySection.d.ts +0 -2
- package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
- package/lib/types/client/antigravity/locales.d.ts +33 -26
- package/lib/types/client/antigravity/locales.d.ts.map +1 -1
- package/lib/types/client/antigravity/styles.d.ts.map +1 -1
- package/lib/types/client/claude/ClaudeComposerQuota.d.ts +3 -7
- package/lib/types/client/claude/ClaudeComposerQuota.d.ts.map +1 -1
- package/lib/types/client/claude/ClaudeSection.d.ts +10 -9
- package/lib/types/client/claude/ClaudeSection.d.ts.map +1 -1
- package/lib/types/client/claude/locales.d.ts +29 -29
- package/lib/types/client/claude/locales.d.ts.map +1 -1
- package/lib/types/client/claude/useClaudeSection.d.ts +93 -0
- package/lib/types/client/claude/useClaudeSection.d.ts.map +1 -0
- package/lib/types/client/command-code/CommandCodeComposerQuota.d.ts +3 -7
- package/lib/types/client/command-code/CommandCodeComposerQuota.d.ts.map +1 -1
- package/lib/types/client/command-code/CommandCodeSection.d.ts +0 -3
- package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -1
- package/lib/types/client/command-code/locales.d.ts +32 -29
- package/lib/types/client/command-code/locales.d.ts.map +1 -1
- package/lib/types/client/common/AccountPoolSection.d.ts +16 -4
- package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -1
- package/lib/types/client/common/ComposerQuotaBadge.d.ts +35 -0
- package/lib/types/client/common/ComposerQuotaBadge.d.ts.map +1 -0
- package/lib/types/client/common/ContextWindowEditor.d.ts +69 -0
- package/lib/types/client/common/ContextWindowEditor.d.ts.map +1 -0
- package/lib/types/client/common/ModelChecklist.d.ts +29 -0
- package/lib/types/client/common/ModelChecklist.d.ts.map +1 -0
- package/lib/types/client/common/account-pool-labels.d.ts +18 -2
- package/lib/types/client/common/account-pool-labels.d.ts.map +1 -1
- package/lib/types/client/common/account-quota.d.ts +60 -0
- package/lib/types/client/common/account-quota.d.ts.map +1 -0
- package/lib/types/client/common/format.d.ts +30 -0
- package/lib/types/client/common/format.d.ts.map +1 -0
- package/lib/types/client/common/line-api.d.ts +8 -0
- package/lib/types/client/common/line-api.d.ts.map +1 -0
- package/lib/types/client/common/model-checklist-styles.d.ts +2 -0
- package/lib/types/client/common/model-checklist-styles.d.ts.map +1 -0
- package/lib/types/client/common/styles.d.ts.map +1 -1
- package/lib/types/client/common/use-store.d.ts +3 -0
- package/lib/types/client/common/use-store.d.ts.map +1 -0
- package/lib/types/client/hub/HubOverview.d.ts +14 -0
- package/lib/types/client/hub/HubOverview.d.ts.map +1 -0
- package/lib/types/client/hub/brand-icons.d.ts +46 -0
- package/lib/types/client/hub/brand-icons.d.ts.map +1 -0
- package/lib/types/client/hub/brand-svg.d.ts +51 -0
- package/lib/types/client/hub/brand-svg.d.ts.map +1 -0
- package/lib/types/client/hub/hub-styles.d.ts +2 -0
- package/lib/types/client/hub/hub-styles.d.ts.map +1 -0
- package/lib/types/client/hub/providers.d.ts +38 -0
- package/lib/types/client/hub/providers.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +20 -6
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/kimi-code/KimiCodeComposerQuota.d.ts +3 -7
- package/lib/types/client/kimi-code/KimiCodeComposerQuota.d.ts.map +1 -1
- package/lib/types/client/kimi-code/KimiCodeSection.d.ts +0 -3
- package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/locales.d.ts +32 -32
- package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
- package/lib/types/client/kimi-code/styles.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +45 -35
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/minimax-code/MinimaxCodeComposerQuota.d.ts +4 -8
- package/lib/types/client/minimax-code/MinimaxCodeComposerQuota.d.ts.map +1 -1
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts +8 -37
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts.map +1 -1
- package/lib/types/client/minimax-code/api.d.ts +3 -3
- package/lib/types/client/minimax-code/api.d.ts.map +1 -1
- package/lib/types/client/minimax-code/locales.d.ts +29 -56
- package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
- package/lib/types/client/minimax-code/useMinimaxCodeSection.d.ts +115 -0
- package/lib/types/client/minimax-code/useMinimaxCodeSection.d.ts.map +1 -0
- package/lib/types/client/ollama/OllamaSection.d.ts +2 -2
- package/lib/types/client/ollama/OllamaSection.d.ts.map +1 -1
- package/lib/types/client/ollama/locales.d.ts +54 -24
- package/lib/types/client/ollama/locales.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts +3 -7
- package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts.map +1 -1
- package/lib/types/client/workbuddy/WorkBuddySection.d.ts +10 -37
- package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -1
- package/lib/types/client/workbuddy/locales.d.ts +0 -66
- package/lib/types/client/workbuddy/locales.d.ts.map +1 -1
- package/lib/types/client/workbuddy/useWorkBuddySection.d.ts +141 -0
- package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -0
- package/lib/types/host/antigravity/account-pool.d.ts +18 -1
- package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
- package/lib/types/host/antigravity/client.d.ts.map +1 -1
- package/lib/types/host/claude/account-pool.d.ts +22 -2
- package/lib/types/host/claude/account-pool.d.ts.map +1 -1
- package/lib/types/host/claude/adapter.d.ts +50 -0
- package/lib/types/host/claude/adapter.d.ts.map +1 -1
- package/lib/types/host/claude/client.d.ts +37 -5
- package/lib/types/host/claude/client.d.ts.map +1 -1
- package/lib/types/host/claude/mapper.d.ts +7 -0
- package/lib/types/host/claude/mapper.d.ts.map +1 -1
- package/lib/types/host/claude/routes.d.ts.map +1 -1
- package/lib/types/host/codex-account-pool.d.ts +26 -1
- package/lib/types/host/codex-account-pool.d.ts.map +1 -1
- package/lib/types/host/command-code/account-pool.d.ts +13 -1
- package/lib/types/host/command-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/command-code/adapter.d.ts +24 -0
- package/lib/types/host/command-code/adapter.d.ts.map +1 -1
- package/lib/types/host/command-code/client.d.ts +2 -1
- package/lib/types/host/command-code/client.d.ts.map +1 -1
- package/lib/types/host/command-code/mapper.d.ts +44 -0
- package/lib/types/host/command-code/mapper.d.ts.map +1 -1
- package/lib/types/host/common/account-quota.d.ts +44 -0
- package/lib/types/host/common/account-quota.d.ts.map +1 -0
- package/lib/types/host/common/catalog-snapshot.d.ts +9 -0
- package/lib/types/host/common/catalog-snapshot.d.ts.map +1 -1
- package/lib/types/host/common/provider-route.d.ts +56 -0
- package/lib/types/host/common/provider-route.d.ts.map +1 -0
- package/lib/types/host/common/stream-error.d.ts +93 -0
- package/lib/types/host/common/stream-error.d.ts.map +1 -0
- package/lib/types/host/hub-overview.d.ts +26 -0
- package/lib/types/host/hub-overview.d.ts.map +1 -0
- package/lib/types/host/kimi-code/account-pool.d.ts +24 -1
- package/lib/types/host/kimi-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +27 -0
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/client.d.ts +23 -2
- package/lib/types/host/kimi-code/client.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts +26 -0
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/account-pool.d.ts +34 -2
- package/lib/types/host/minimax-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/minimax-code/client.d.ts +23 -0
- package/lib/types/host/minimax-code/client.d.ts.map +1 -1
- package/lib/types/host/minimax-code/mapper.d.ts +39 -0
- package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/routes.d.ts.map +1 -1
- package/lib/types/host/oauth-service.d.ts +16 -0
- package/lib/types/host/oauth-service.d.ts.map +1 -1
- package/lib/types/host/ollama/adapter.d.ts.map +1 -1
- package/lib/types/host/ollama/types.d.ts +33 -0
- package/lib/types/host/ollama/types.d.ts.map +1 -1
- package/lib/types/host/usage-service.d.ts +27 -2
- package/lib/types/host/usage-service.d.ts.map +1 -1
- package/lib/types/host/workbuddy/account-pool.d.ts +13 -1
- package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -1
- package/lib/types/host/workbuddy/client.d.ts +15 -0
- package/lib/types/host/workbuddy/client.d.ts.map +1 -1
- package/lib/types/host/workbuddy/routes.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/account-pool-contracts.d.ts +44 -0
- package/lib/types/shared/account-pool-contracts.d.ts.map +1 -1
- package/lib/types/shared/antigravity-contracts.d.ts +9 -3
- package/lib/types/shared/antigravity-contracts.d.ts.map +1 -1
- package/lib/types/shared/hub-contracts.d.ts +37 -0
- package/lib/types/shared/hub-contracts.d.ts.map +1 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -516,6 +516,17 @@ function catalogSnapshotPath(name) {
|
|
|
516
516
|
return path.join(dshHomeDir(), "storages", `${name}-catalog.json`);
|
|
517
517
|
}
|
|
518
518
|
/**
|
|
519
|
+
* How many models one line's catalog holds, for the hub overview's "N/M models".
|
|
520
|
+
*
|
|
521
|
+
* An empty catalog is reported as `null` rather than `0`: "this line has synced
|
|
522
|
+
* nothing yet" and "this line offers no models" are different statements, and
|
|
523
|
+
* only the second one is a fact. The overview hides the model count for a null,
|
|
524
|
+
* which is what a line that has never synced should show.
|
|
525
|
+
*/
|
|
526
|
+
function catalogTotal(count) {
|
|
527
|
+
return count === 0 ? null : count;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
519
530
|
* Snapshot name for one catalog *and* one credential scope.
|
|
520
531
|
*
|
|
521
532
|
* A persisted snapshot answers before any credential is read — that is the whole
|
|
@@ -681,7 +692,7 @@ let snapshotRehydrated = null;
|
|
|
681
692
|
* directly; returning it here under a different entry type would force every
|
|
682
693
|
* one of them to convert it.
|
|
683
694
|
*/
|
|
684
|
-
function getCachedCatalog() {
|
|
695
|
+
function getCachedCatalog$2() {
|
|
685
696
|
return catalogCache$2?.models;
|
|
686
697
|
}
|
|
687
698
|
/**
|
|
@@ -740,7 +751,7 @@ async function performCatalogLoad$1(credentials, options) {
|
|
|
740
751
|
live = true;
|
|
741
752
|
writeCatalogSnapshot(catalogSnapshotName(SNAPSHOT_SCOPE, key), [...models], Date.now());
|
|
742
753
|
} catch {
|
|
743
|
-
models = getCachedCatalog() ?? [];
|
|
754
|
+
models = getCachedCatalog$2() ?? [];
|
|
744
755
|
live = false;
|
|
745
756
|
}
|
|
746
757
|
catalogCache$2 = {
|
|
@@ -791,7 +802,7 @@ function parseListing(payload) {
|
|
|
791
802
|
function parseListingEntry$1(value) {
|
|
792
803
|
const record = asRecord$10(value);
|
|
793
804
|
if (record === void 0) return [];
|
|
794
|
-
const id = asString$
|
|
805
|
+
const id = asString$18(record.slug) ?? asString$18(record.id);
|
|
795
806
|
if (id === void 0) return [];
|
|
796
807
|
const contextWindow = asNumber$4(record.context_window) ?? asNumber$4(record.contextWindow);
|
|
797
808
|
const modalities = parseModalities(record.input_modalities);
|
|
@@ -799,18 +810,18 @@ function parseListingEntry$1(value) {
|
|
|
799
810
|
const fallback = CODEX_MODEL_CATALOG.find((entry) => entry.id === id);
|
|
800
811
|
return [{
|
|
801
812
|
id,
|
|
802
|
-
name: asString$
|
|
813
|
+
name: asString$18(record.display_name) ?? asString$18(record.displayName) ?? fallback?.name ?? id,
|
|
803
814
|
contextWindow: contextWindow !== void 0 && contextWindow > 0 ? contextWindow : null,
|
|
804
815
|
inputModalities: modalities ?? (fallback ? [...fallback.inputModalities] : ["text"]),
|
|
805
816
|
...efforts !== null ? { reasoningEfforts: efforts } : {},
|
|
806
|
-
defaultReasoningEffort: asString$
|
|
817
|
+
defaultReasoningEffort: asString$18(record.default_reasoning_level) ?? fallback?.defaultReasoningEffort ?? null
|
|
807
818
|
}];
|
|
808
819
|
}
|
|
809
820
|
function parseModalities(value) {
|
|
810
821
|
if (!Array.isArray(value)) return null;
|
|
811
822
|
const out = [];
|
|
812
823
|
for (const item of value) {
|
|
813
|
-
const name = asString$
|
|
824
|
+
const name = asString$18(item);
|
|
814
825
|
if (name === "text" || name === "image") out.push(name);
|
|
815
826
|
}
|
|
816
827
|
return out.length > 0 ? out : null;
|
|
@@ -825,7 +836,7 @@ function parseEfforts(value) {
|
|
|
825
836
|
if (!Array.isArray(value)) return null;
|
|
826
837
|
const out = [];
|
|
827
838
|
for (const item of value) {
|
|
828
|
-
const effort = asString$
|
|
839
|
+
const effort = asString$18(item) ?? asString$18(asRecord$10(item)?.effort);
|
|
829
840
|
if (effort !== void 0 && !out.includes(effort)) out.push(effort);
|
|
830
841
|
}
|
|
831
842
|
return out.length > 0 ? out : null;
|
|
@@ -843,7 +854,7 @@ function accountKeyFor$1(credentials) {
|
|
|
843
854
|
function asRecord$10(value) {
|
|
844
855
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
845
856
|
}
|
|
846
|
-
function asString$
|
|
857
|
+
function asString$18(value) {
|
|
847
858
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
848
859
|
}
|
|
849
860
|
function asNumber$4(value) {
|
|
@@ -1085,6 +1096,22 @@ var WindowsDpapiTokenStore = class extends WindowsDpapiCredentialStore {
|
|
|
1085
1096
|
super(path, parseStoredCredentials);
|
|
1086
1097
|
}
|
|
1087
1098
|
};
|
|
1099
|
+
/**
|
|
1100
|
+
* How long a helper may run before it is killed and the read fails.
|
|
1101
|
+
*
|
|
1102
|
+
* A healthy spawn costs ~200 ms (see the cache note on this class), so the guard
|
|
1103
|
+
* exists only for a child that never exits, and it was set at 10 s - fifty times
|
|
1104
|
+
* the healthy cost. That turned out to be too tight for one real environment: a
|
|
1105
|
+
* cold Windows runner whose freshly-installed dependency tree is being scanned
|
|
1106
|
+
* on first spawn, where the helper legitimately took longer than 10 s and the
|
|
1107
|
+
* kill turned a working credential read into `DPAPI helper timed out`.
|
|
1108
|
+
*
|
|
1109
|
+
* The guard is kept, and only its headroom widened: 30 s is still bounded, still
|
|
1110
|
+
* far above anything a warm machine needs, and a hung helper now blocks one
|
|
1111
|
+
* credential read for 30 s rather than 10 - a worse wait in a case that is already
|
|
1112
|
+
* a failure, in exchange for not failing a machine that is merely cold.
|
|
1113
|
+
*/
|
|
1114
|
+
const POWERSHELL_HELPER_TIMEOUT_MS = 3e4;
|
|
1088
1115
|
function runPowerShell(script, path, stdin) {
|
|
1089
1116
|
return new Promise((resolve, reject) => {
|
|
1090
1117
|
const child = spawn("powershell.exe", [
|
|
@@ -1110,7 +1137,7 @@ function runPowerShell(script, path, stdin) {
|
|
|
1110
1137
|
const timer = setTimeout(() => {
|
|
1111
1138
|
child.kill();
|
|
1112
1139
|
reject(/* @__PURE__ */ new Error("DPAPI helper timed out"));
|
|
1113
|
-
},
|
|
1140
|
+
}, POWERSHELL_HELPER_TIMEOUT_MS);
|
|
1114
1141
|
child.stdout.setEncoding("utf8");
|
|
1115
1142
|
child.stdout.on("data", (chunk) => {
|
|
1116
1143
|
stdout += chunk;
|
|
@@ -2505,13 +2532,31 @@ var OAuthService = class {
|
|
|
2505
2532
|
* "credentials are required" on every web search.
|
|
2506
2533
|
*/
|
|
2507
2534
|
async credentials(forceRefresh = false, access = {}) {
|
|
2535
|
+
return (await this.credentialSelection(forceRefresh, access)).credentials;
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* The credential to use next, together with the pool row that owns it.
|
|
2539
|
+
*
|
|
2540
|
+
* The settings card keys each account's quota snapshot by the row the reading
|
|
2541
|
+
* was made for, and the credential cannot do that job: a refresh rotates its
|
|
2542
|
+
* tokens, and an account may state no id of its own. Callers that care about
|
|
2543
|
+
* identity ask here; every other caller keeps using {@link credentials} and
|
|
2544
|
+
* sees no difference.
|
|
2545
|
+
*
|
|
2546
|
+
* @param forceRefresh - refresh even a token that is not close to expiry.
|
|
2547
|
+
* @param access - who is asking; see {@link credentials}.
|
|
2548
|
+
*/
|
|
2549
|
+
async credentialSelection(forceRefresh = false, access = {}) {
|
|
2508
2550
|
if (this.pool !== null) {
|
|
2509
|
-
|
|
2510
|
-
return
|
|
2551
|
+
const picked = access.purpose === "tool" ? await this.pool.getCredentialAccount(this.fetchFn, forceRefresh) : await this.pool.getEffectiveAccount(void 0, this.fetchFn, forceRefresh);
|
|
2552
|
+
return {
|
|
2553
|
+
accountId: picked.account.id,
|
|
2554
|
+
credentials: picked.credentials
|
|
2555
|
+
};
|
|
2511
2556
|
}
|
|
2512
2557
|
const stored = await this.loadAuthenticated();
|
|
2513
|
-
if (forceRefresh || stored.expiresAt - this.now() <= 6e4) return this.refreshCredentials(stored);
|
|
2514
|
-
return stored;
|
|
2558
|
+
if (forceRefresh || stored.expiresAt - this.now() <= 6e4) return { credentials: await this.refreshCredentials(stored) };
|
|
2559
|
+
return { credentials: stored };
|
|
2515
2560
|
}
|
|
2516
2561
|
dispose() {
|
|
2517
2562
|
if (this.disposed) return;
|
|
@@ -2807,6 +2852,49 @@ async function oauthErrorIdentifier(response) {
|
|
|
2807
2852
|
return null;
|
|
2808
2853
|
}
|
|
2809
2854
|
//#endregion
|
|
2855
|
+
//#region src/host/common/account-quota.ts
|
|
2856
|
+
/**
|
|
2857
|
+
* A reset instant as Unix milliseconds.
|
|
2858
|
+
*
|
|
2859
|
+
* Providers state this in seconds, in milliseconds, or not at all, and the two
|
|
2860
|
+
* numeric forms are told apart by magnitude rather than by a flag: 10^10
|
|
2861
|
+
* seconds is the year 2286, so anything larger is already milliseconds.
|
|
2862
|
+
*/
|
|
2863
|
+
function quotaResetMs(value) {
|
|
2864
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
|
|
2865
|
+
return value > 1e10 ? value : value * 1e3;
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* One window, or null when its consumed share was never measured.
|
|
2869
|
+
*
|
|
2870
|
+
* `label` may be empty: the card names an unlabeled window from its length, and
|
|
2871
|
+
* only falls back to a generic name when the line stated neither.
|
|
2872
|
+
*/
|
|
2873
|
+
function quotaWindow$1(label, usedPercent, options = {}) {
|
|
2874
|
+
if (typeof usedPercent !== "number" || !Number.isFinite(usedPercent)) return null;
|
|
2875
|
+
const windowDurationMins = typeof options.windowDurationMins === "number" && Number.isFinite(options.windowDurationMins) ? options.windowDurationMins : null;
|
|
2876
|
+
return {
|
|
2877
|
+
label,
|
|
2878
|
+
usedPercent: Math.min(100, Math.max(0, usedPercent)),
|
|
2879
|
+
windowDurationMins,
|
|
2880
|
+
resetsAt: quotaResetMs(options.resetsAt)
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
/**
|
|
2884
|
+
* The snapshot to publish for one account, or undefined when there is none.
|
|
2885
|
+
*
|
|
2886
|
+
* Undefined is the honest answer for "never read" and for "read before this
|
|
2887
|
+
* process started": both mean the card has nothing to show, and neither means
|
|
2888
|
+
* the account is unspent.
|
|
2889
|
+
*/
|
|
2890
|
+
function poolQuota(fetchedAt, windows) {
|
|
2891
|
+
if (typeof fetchedAt !== "number" || !Number.isFinite(fetchedAt) || fetchedAt <= 0) return void 0;
|
|
2892
|
+
return {
|
|
2893
|
+
windows: windows.filter((window) => window !== null),
|
|
2894
|
+
fetchedAt
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
//#endregion
|
|
2810
2898
|
//#region src/host/codex-account-pool.ts
|
|
2811
2899
|
/** Encrypted pool file the ChatGPT line owns; the single-credential store stays the mirror. */
|
|
2812
2900
|
function codexPoolPath() {
|
|
@@ -2875,13 +2963,32 @@ const concurrency = new ConcurrencyGate();
|
|
|
2875
2963
|
function chatGPTConcurrency() {
|
|
2876
2964
|
return concurrency;
|
|
2877
2965
|
}
|
|
2966
|
+
/**
|
|
2967
|
+
* One ChatGPT usage snapshot as the account card's per-account quota.
|
|
2968
|
+
*
|
|
2969
|
+
* A bucket lists its windows either in `windows` or, on older payloads, only as
|
|
2970
|
+
* the `primary`/`secondary` pair — the same fallback the quota card itself
|
|
2971
|
+
* applies, so the account row cannot show fewer windows than the card does. The
|
|
2972
|
+
* label stays empty on purpose: a ChatGPT window is named by its length, and
|
|
2973
|
+
* the card localizes that length in the reader's own language.
|
|
2974
|
+
*/
|
|
2975
|
+
function codexAccountQuota(snapshot) {
|
|
2976
|
+
return poolQuota(snapshot.fetchedAt, snapshot.usage.buckets.flatMap((bucket) => {
|
|
2977
|
+
return (bucket.windows.length > 0 ? bucket.windows : [bucket.primary, bucket.secondary].filter((window) => window !== null)).map((window) => quotaWindow$1("", window.usedPercent, {
|
|
2978
|
+
windowDurationMins: window.windowDurationMins,
|
|
2979
|
+
resetsAt: window.resetsAt
|
|
2980
|
+
}));
|
|
2981
|
+
}));
|
|
2982
|
+
}
|
|
2878
2983
|
var CodexAccountPool = class extends AccountPoolCore {
|
|
2879
2984
|
refresherRef;
|
|
2880
2985
|
store;
|
|
2986
|
+
quotaSnapshotRef;
|
|
2881
2987
|
quotaBlockedUntil;
|
|
2882
2988
|
constructor(options = {}) {
|
|
2883
2989
|
const store = options.store ?? createPlatformTokenStore();
|
|
2884
2990
|
const ref = {};
|
|
2991
|
+
const quotaSnapshotRef = {};
|
|
2885
2992
|
const hooks = {
|
|
2886
2993
|
providerId: "codex-chatgpt",
|
|
2887
2994
|
displayName: "ChatGPT",
|
|
@@ -2932,7 +3039,11 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
2932
3039
|
extendSummary: (account, base) => ({
|
|
2933
3040
|
...base,
|
|
2934
3041
|
...account.email === void 0 ? {} : { email: account.email },
|
|
2935
|
-
...account.planType === void 0 ? {} : { planLabel: account.planType }
|
|
3042
|
+
...account.planType === void 0 ? {} : { planLabel: account.planType },
|
|
3043
|
+
...(() => {
|
|
3044
|
+
const quota = quotaSnapshotRef.current?.(account);
|
|
3045
|
+
return quota === void 0 ? {} : { quota };
|
|
3046
|
+
})()
|
|
2936
3047
|
}),
|
|
2937
3048
|
emptyMessage: "未登录 ChatGPT 账号,请在「设置 → 订阅服务 → ChatGPT」中添加并登录账号。",
|
|
2938
3049
|
...options.backend === void 0 ? {} : { backend: options.backend },
|
|
@@ -2940,6 +3051,7 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
2940
3051
|
};
|
|
2941
3052
|
super(hooks);
|
|
2942
3053
|
this.refresherRef = ref;
|
|
3054
|
+
this.quotaSnapshotRef = quotaSnapshotRef;
|
|
2943
3055
|
this.store = store;
|
|
2944
3056
|
}
|
|
2945
3057
|
/** Hand the pool the OAuth service's per-account refresh implementation. */
|
|
@@ -2957,6 +3069,18 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
2957
3069
|
setQuotaBlockedUntil(lookup) {
|
|
2958
3070
|
this.quotaBlockedUntil = lookup;
|
|
2959
3071
|
}
|
|
3072
|
+
/**
|
|
3073
|
+
* Wire in the account's own newest quota snapshot, for display only.
|
|
3074
|
+
*
|
|
3075
|
+
* The same map that already spares an exhausted account a wasted request also
|
|
3076
|
+
* answers "what did this account's window look like last time we read it".
|
|
3077
|
+
* The lookup must be a memory read: it runs while the settings card builds
|
|
3078
|
+
* its account list, and that page must never fan out an upstream request per
|
|
3079
|
+
* pooled account.
|
|
3080
|
+
*/
|
|
3081
|
+
setQuotaSnapshot(lookup) {
|
|
3082
|
+
this.quotaSnapshotRef.current = lookup;
|
|
3083
|
+
}
|
|
2960
3084
|
isEligible(account, now, triedAccountIds) {
|
|
2961
3085
|
if (!super.isEligible(account, now, triedAccountIds)) return false;
|
|
2962
3086
|
const blockedUntil = this.quotaBlockedUntil?.(account, now);
|
|
@@ -3309,13 +3433,13 @@ function createCodexFetchProvider(options = {}) {
|
|
|
3309
3433
|
//#region src/host/common/llm-compat.ts
|
|
3310
3434
|
/** This package's kind in the harness message-source map. */
|
|
3311
3435
|
const PLUGIN_MESSAGE_SOURCE_KIND = "dsh-chatgpt-subscription";
|
|
3312
|
-
function isRecord$
|
|
3436
|
+
function isRecord$30(value) {
|
|
3313
3437
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3314
3438
|
}
|
|
3315
3439
|
/** Read a message body's blocks, keeping every entry that carries a block tag. */
|
|
3316
3440
|
function readBlocks(raw) {
|
|
3317
3441
|
if (!Array.isArray(raw)) return [];
|
|
3318
|
-
return raw.filter((block) => isRecord$
|
|
3442
|
+
return raw.filter((block) => isRecord$30(block) && typeof block.type === "string");
|
|
3319
3443
|
}
|
|
3320
3444
|
/**
|
|
3321
3445
|
* Normalize one harness message list into {@link Message}.
|
|
@@ -3330,10 +3454,10 @@ function readBlocks(raw) {
|
|
|
3330
3454
|
function normalizeMessages(messages) {
|
|
3331
3455
|
const normalized = [];
|
|
3332
3456
|
for (const raw of messages ?? []) {
|
|
3333
|
-
if (!isRecord$
|
|
3457
|
+
if (!isRecord$30(raw)) continue;
|
|
3334
3458
|
const content = readBlocks(raw.content);
|
|
3335
3459
|
if (raw.role === "tool") {
|
|
3336
|
-
const source = isRecord$
|
|
3460
|
+
const source = isRecord$30(raw.source) ? raw.source : void 0;
|
|
3337
3461
|
const callId = String(raw.toolCallId ?? source?.callId ?? "");
|
|
3338
3462
|
normalized.push({
|
|
3339
3463
|
id: raw.id,
|
|
@@ -3355,7 +3479,7 @@ function normalizeMessages(messages) {
|
|
|
3355
3479
|
id: raw.id,
|
|
3356
3480
|
role: typeof raw.role === "string" ? raw.role : "user",
|
|
3357
3481
|
content,
|
|
3358
|
-
...isRecord$
|
|
3482
|
+
...isRecord$30(raw.source) ? { source: raw.source } : {}
|
|
3359
3483
|
});
|
|
3360
3484
|
}
|
|
3361
3485
|
return normalized;
|
|
@@ -4072,6 +4196,56 @@ function createControlledModelFetch(baseFetch, options) {
|
|
|
4072
4196
|
};
|
|
4073
4197
|
}
|
|
4074
4198
|
//#endregion
|
|
4199
|
+
//#region src/host/common/provider-route.ts
|
|
4200
|
+
/**
|
|
4201
|
+
* Claim one provider route for this plugin and keep claiming it.
|
|
4202
|
+
*
|
|
4203
|
+
* The route is taken immediately, and every `llm/adapters-updated` reports the
|
|
4204
|
+
* claim again — it is a no-op while the registration is held, and takes the
|
|
4205
|
+
* route back once another owner releases it. A rejected claim is remembered
|
|
4206
|
+
* rather than thrown, because the line whose route table asks through
|
|
4207
|
+
* {@link ProviderRouteClaim.conflict} still has to render.
|
|
4208
|
+
* @param ctx - The plugin context owning the `llm` service and the event seam.
|
|
4209
|
+
* @param options - The route id, the display label the two log lines name, and the adapter serving it.
|
|
4210
|
+
* @returns The claim's state and its disposer.
|
|
4211
|
+
*/
|
|
4212
|
+
function claimProviderRoute(ctx, options) {
|
|
4213
|
+
let registration;
|
|
4214
|
+
let conflict = null;
|
|
4215
|
+
const claim = () => {
|
|
4216
|
+
if (registration !== void 0) return;
|
|
4217
|
+
try {
|
|
4218
|
+
registration = ctx.llm.registerAdapter([options.providerId], options.adapter);
|
|
4219
|
+
if (conflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] ${options.label} route "${options.providerId}" is now served by this plugin`);
|
|
4220
|
+
conflict = null;
|
|
4221
|
+
} catch (error) {
|
|
4222
|
+
conflict = error instanceof Error ? error.message : String(error);
|
|
4223
|
+
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${options.providerId}" is already owned by another adapter; ${options.label} models keep being served by that one until its configuration is removed (${conflict})`);
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
claim();
|
|
4227
|
+
const watch = options.watch === false || typeof ctx.on !== "function" ? void 0 : ctx.on("llm/adapters-updated", () => {
|
|
4228
|
+
claim();
|
|
4229
|
+
});
|
|
4230
|
+
return {
|
|
4231
|
+
serving: () => registration !== void 0,
|
|
4232
|
+
conflict: () => conflict,
|
|
4233
|
+
dispose: () => {
|
|
4234
|
+
releaseHandle(watch);
|
|
4235
|
+
registration?.();
|
|
4236
|
+
registration = void 0;
|
|
4237
|
+
}
|
|
4238
|
+
};
|
|
4239
|
+
}
|
|
4240
|
+
/** Cordis event handles are either a disposer function or a disposable object. */
|
|
4241
|
+
function releaseHandle(handle) {
|
|
4242
|
+
if (typeof handle === "function") {
|
|
4243
|
+
handle();
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4246
|
+
handle?.dispose?.();
|
|
4247
|
+
}
|
|
4248
|
+
//#endregion
|
|
4075
4249
|
//#region src/host/common/request-diagnostics.ts
|
|
4076
4250
|
/**
|
|
4077
4251
|
* Reusable request diagnostics wrapper for model provider calls.
|
|
@@ -6223,6 +6397,72 @@ function acceptIdentity(current, incoming) {
|
|
|
6223
6397
|
return typeof incoming === "string" && incoming.length > 0 ? incoming : current;
|
|
6224
6398
|
}
|
|
6225
6399
|
//#endregion
|
|
6400
|
+
//#region src/shared/hub-contracts.ts
|
|
6401
|
+
/**
|
|
6402
|
+
* Wire contracts for the subscription hub's overview route.
|
|
6403
|
+
*
|
|
6404
|
+
* The overview is the settings page's opening screen: one card per provider
|
|
6405
|
+
* line, annotated with how many accounts it holds and whether it is enabled.
|
|
6406
|
+
* Everything here is a non-secret fact a settings card may render; no
|
|
6407
|
+
* credential, quota figure, or upstream call ever crosses this boundary.
|
|
6408
|
+
*/
|
|
6409
|
+
/** The aggregated, read-only overview the hub's opening screen renders. */
|
|
6410
|
+
const HUB_OVERVIEW_PATH = `${ROUTE_PREFIX$5}/hub/overview`;
|
|
6411
|
+
//#endregion
|
|
6412
|
+
//#region src/host/hub-overview.ts
|
|
6413
|
+
function registerHubOverviewRoutes(ctx, sources) {
|
|
6414
|
+
const handler = async (request, response) => {
|
|
6415
|
+
if (request.method !== "GET") {
|
|
6416
|
+
response.writeHead(405, {
|
|
6417
|
+
allow: "GET",
|
|
6418
|
+
"content-type": "application/json; charset=utf-8"
|
|
6419
|
+
});
|
|
6420
|
+
response.end(JSON.stringify({
|
|
6421
|
+
ok: false,
|
|
6422
|
+
error: {
|
|
6423
|
+
code: "bad-request",
|
|
6424
|
+
message: "Method not allowed."
|
|
6425
|
+
}
|
|
6426
|
+
}));
|
|
6427
|
+
return;
|
|
6428
|
+
}
|
|
6429
|
+
const envelope = {
|
|
6430
|
+
ok: true,
|
|
6431
|
+
value: { providers: (await Promise.allSettled(sources.map(async (source) => ({
|
|
6432
|
+
id: source.id,
|
|
6433
|
+
providerId: source.providerId,
|
|
6434
|
+
canToggle: source.canToggle,
|
|
6435
|
+
...await source.read()
|
|
6436
|
+
})))).map((result, index) => {
|
|
6437
|
+
if (result.status === "fulfilled") return result.value;
|
|
6438
|
+
const source = sources[index];
|
|
6439
|
+
return {
|
|
6440
|
+
id: source.id,
|
|
6441
|
+
providerId: source.providerId,
|
|
6442
|
+
canToggle: source.canToggle,
|
|
6443
|
+
enabled: !source.canToggle,
|
|
6444
|
+
accountCount: 0,
|
|
6445
|
+
authenticated: false,
|
|
6446
|
+
enabledModelCount: null,
|
|
6447
|
+
totalModelCount: null,
|
|
6448
|
+
error: true
|
|
6449
|
+
};
|
|
6450
|
+
}) }
|
|
6451
|
+
};
|
|
6452
|
+
response.writeHead(200, {
|
|
6453
|
+
"content-type": "application/json; charset=utf-8",
|
|
6454
|
+
"cache-control": "no-store",
|
|
6455
|
+
"x-content-type-options": "nosniff"
|
|
6456
|
+
});
|
|
6457
|
+
response.end(JSON.stringify(envelope));
|
|
6458
|
+
};
|
|
6459
|
+
return ctx.webServer.register({
|
|
6460
|
+
kind: "exact",
|
|
6461
|
+
path: HUB_OVERVIEW_PATH,
|
|
6462
|
+
handler
|
|
6463
|
+
});
|
|
6464
|
+
}
|
|
6465
|
+
//#endregion
|
|
6226
6466
|
//#region src/host/common/same-origin.ts
|
|
6227
6467
|
/**
|
|
6228
6468
|
* Whether one mutation request comes from this application's own document.
|
|
@@ -6330,9 +6570,9 @@ var UsageService = class {
|
|
|
6330
6570
|
};
|
|
6331
6571
|
const now = this.now();
|
|
6332
6572
|
if (!force && !this.invalidated && this.cache !== null && now - this.cache.fetchedAt < 6e4 && this.cacheIdentityRevision === this.oauth.currentIdentityRevision()) return this.fromCache(false);
|
|
6333
|
-
let
|
|
6573
|
+
let selection;
|
|
6334
6574
|
try {
|
|
6335
|
-
|
|
6575
|
+
selection = await this.oauth.credentialSelection(false, USAGE_CREDENTIAL_ACCESS);
|
|
6336
6576
|
} catch (error) {
|
|
6337
6577
|
const reason = error instanceof Error && error.message !== "" ? ` (${error.message})` : "";
|
|
6338
6578
|
return this.failure({
|
|
@@ -6340,7 +6580,8 @@ var UsageService = class {
|
|
|
6340
6580
|
message: `ChatGPT credentials could not be refreshed.${reason}`
|
|
6341
6581
|
});
|
|
6342
6582
|
}
|
|
6343
|
-
const
|
|
6583
|
+
const credentials = selection.credentials;
|
|
6584
|
+
const accountKey = this.snapshotKeyFor(selection.accountId, credentials);
|
|
6344
6585
|
if (this.cache !== null && this.cache.accountKey !== accountKey) this.clear();
|
|
6345
6586
|
if (!force && !this.invalidated && this.cache !== null && now - this.cache.fetchedAt < 6e4) return this.fromCache(false);
|
|
6346
6587
|
if (this.cache !== null && now - this.lastUpstreamAt < 15e3) return this.fromCache(this.invalidated || now - this.cache.fetchedAt >= 6e4);
|
|
@@ -6368,9 +6609,9 @@ var UsageService = class {
|
|
|
6368
6609
|
* The rejection is absorbed here: this refresh has no caller to receive it,
|
|
6369
6610
|
* and {@link fromCache} keeps serving the snapshot it failed to replace.
|
|
6370
6611
|
*/
|
|
6371
|
-
startBackgroundRefresh(credentials,
|
|
6612
|
+
startBackgroundRefresh(credentials, snapshotKey) {
|
|
6372
6613
|
if (this.inFlight !== null) return;
|
|
6373
|
-
const task = this.refreshUpstream(credentials,
|
|
6614
|
+
const task = this.refreshUpstream(credentials, snapshotKey).finally(() => {
|
|
6374
6615
|
if (this.inFlight === task) this.inFlight = null;
|
|
6375
6616
|
});
|
|
6376
6617
|
this.inFlight = task;
|
|
@@ -6387,8 +6628,8 @@ var UsageService = class {
|
|
|
6387
6628
|
* cooldown rather than forever, so a missing upstream field cannot strand an
|
|
6388
6629
|
* account permanently.
|
|
6389
6630
|
*/
|
|
6390
|
-
blockedUntilFor(credentials, now) {
|
|
6391
|
-
const snapshot = this.snapshots.get(
|
|
6631
|
+
blockedUntilFor(accountId, credentials, now) {
|
|
6632
|
+
const snapshot = this.snapshots.get(this.snapshotKeyFor(accountId, credentials));
|
|
6392
6633
|
if (snapshot === void 0) return void 0;
|
|
6393
6634
|
let reopen;
|
|
6394
6635
|
for (const bucket of snapshot.usage.buckets) {
|
|
@@ -6401,6 +6642,32 @@ var UsageService = class {
|
|
|
6401
6642
|
}
|
|
6402
6643
|
return reopen !== void 0 && reopen > now ? reopen : void 0;
|
|
6403
6644
|
}
|
|
6645
|
+
/**
|
|
6646
|
+
* The newest quota snapshot held for one account, if any.
|
|
6647
|
+
*
|
|
6648
|
+
* Quota follows the account, so the settings card shows each account its own
|
|
6649
|
+
* progress. The pool asks here with the row id it addresses the account by —
|
|
6650
|
+
* the identity that survives a token refresh — while its credentials answer
|
|
6651
|
+
* for a composition without a pool. This reads the remembered map only, never
|
|
6652
|
+
* the network, because opening the settings page must not cost one upstream
|
|
6653
|
+
* request per pooled account.
|
|
6654
|
+
*/
|
|
6655
|
+
snapshotFor(accountId, credentials) {
|
|
6656
|
+
return this.snapshots.get(this.snapshotKeyFor(accountId, credentials));
|
|
6657
|
+
}
|
|
6658
|
+
/**
|
|
6659
|
+
* Key one account's remembered snapshot is filed under.
|
|
6660
|
+
*
|
|
6661
|
+
* A pool row id when the caller names one, prefixed so it can never be
|
|
6662
|
+
* confused with the credential-derived key, and otherwise the credential
|
|
6663
|
+
* identity — which is all a composition without a pool has. Two pooled accounts
|
|
6664
|
+
* that state neither an account id nor an email would otherwise collapse onto
|
|
6665
|
+
* one key and show each other's numbers, so the row id is the answer wherever
|
|
6666
|
+
* a pool is in charge.
|
|
6667
|
+
*/
|
|
6668
|
+
snapshotKeyFor(accountId, credentials) {
|
|
6669
|
+
return accountId === void 0 || accountId === "" ? identityKey(credentials) : `row:${accountId}`;
|
|
6670
|
+
}
|
|
6404
6671
|
/** Remember the newest snapshot for one account, bounded to the accounts in use. */
|
|
6405
6672
|
rememberSnapshot(accountKey, usage, fetchedAt) {
|
|
6406
6673
|
this.snapshots.set(accountKey, {
|
|
@@ -6420,8 +6687,8 @@ var UsageService = class {
|
|
|
6420
6687
|
this.invalidated = false;
|
|
6421
6688
|
}
|
|
6422
6689
|
/** Drop the remembered snapshot of one account, after its window is known to have reset. */
|
|
6423
|
-
forgetSnapshot(credentials) {
|
|
6424
|
-
this.snapshots.delete(
|
|
6690
|
+
forgetSnapshot(accountId, credentials) {
|
|
6691
|
+
this.snapshots.delete(this.snapshotKeyFor(accountId, credentials));
|
|
6425
6692
|
}
|
|
6426
6693
|
async consumeResetCredit() {
|
|
6427
6694
|
if (this.resetConsumeInFlight !== null) return this.resetConsumeInFlight;
|
|
@@ -6432,12 +6699,17 @@ var UsageService = class {
|
|
|
6432
6699
|
}
|
|
6433
6700
|
async consumeResetCreditUpstream() {
|
|
6434
6701
|
let credentials;
|
|
6702
|
+
let accountId;
|
|
6435
6703
|
try {
|
|
6436
|
-
|
|
6704
|
+
const selection = await this.oauth.credentialSelection(false, USAGE_CREDENTIAL_ACCESS);
|
|
6705
|
+
credentials = selection.credentials;
|
|
6706
|
+
accountId = selection.accountId;
|
|
6437
6707
|
let creditsResponse = await this.fetchResetCredits(credentials);
|
|
6438
6708
|
if (creditsResponse.status === 401) {
|
|
6439
6709
|
await creditsResponse.body?.cancel().catch(() => void 0);
|
|
6440
|
-
|
|
6710
|
+
const renewed = await this.oauth.credentialSelection(true, USAGE_CREDENTIAL_ACCESS);
|
|
6711
|
+
credentials = renewed.credentials;
|
|
6712
|
+
accountId = renewed.accountId;
|
|
6441
6713
|
creditsResponse = await this.fetchResetCredits(credentials);
|
|
6442
6714
|
}
|
|
6443
6715
|
if (!creditsResponse.ok) {
|
|
@@ -6468,7 +6740,9 @@ var UsageService = class {
|
|
|
6468
6740
|
});
|
|
6469
6741
|
if (consumeResponse.status === 401) {
|
|
6470
6742
|
await consumeResponse.body?.cancel().catch(() => void 0);
|
|
6471
|
-
|
|
6743
|
+
const renewed = await this.oauth.credentialSelection(true, USAGE_CREDENTIAL_ACCESS);
|
|
6744
|
+
credentials = renewed.credentials;
|
|
6745
|
+
accountId = renewed.accountId;
|
|
6472
6746
|
consumeResponse = await this.fetchFn(CODEX_RESET_CREDITS_CONSUME_URL, {
|
|
6473
6747
|
method: "POST",
|
|
6474
6748
|
headers: {
|
|
@@ -6492,7 +6766,7 @@ var UsageService = class {
|
|
|
6492
6766
|
}
|
|
6493
6767
|
await consumeResponse.body?.cancel().catch(() => void 0);
|
|
6494
6768
|
this.clear();
|
|
6495
|
-
const refreshed = await this.refreshUpstream(credentials,
|
|
6769
|
+
const refreshed = await this.refreshUpstream(credentials, this.snapshotKeyFor(accountId, credentials));
|
|
6496
6770
|
if (refreshed.error !== void 0) throw new UsageServiceError({
|
|
6497
6771
|
code: "quota-failed",
|
|
6498
6772
|
message: "The reset credit was used, but usage could not be refreshed."
|
|
@@ -6519,16 +6793,17 @@ var UsageService = class {
|
|
|
6519
6793
|
checkedAt: Math.floor(this.now() / 1e3)
|
|
6520
6794
|
};
|
|
6521
6795
|
}
|
|
6522
|
-
async refreshUpstream(initialCredentials,
|
|
6796
|
+
async refreshUpstream(initialCredentials, initialSnapshotKey) {
|
|
6523
6797
|
this.lastUpstreamAt = this.now();
|
|
6524
6798
|
try {
|
|
6525
6799
|
let credentials = initialCredentials;
|
|
6526
|
-
let
|
|
6800
|
+
let snapshotKey = initialSnapshotKey;
|
|
6527
6801
|
let response = await this.fetch(credentials);
|
|
6528
6802
|
if (response.status === 401) {
|
|
6529
6803
|
await response.body?.cancel().catch(() => void 0);
|
|
6530
|
-
|
|
6531
|
-
|
|
6804
|
+
const renewed = await this.oauth.credentialSelection(true, USAGE_CREDENTIAL_ACCESS);
|
|
6805
|
+
credentials = renewed.credentials;
|
|
6806
|
+
snapshotKey = this.snapshotKeyFor(renewed.accountId, credentials);
|
|
6532
6807
|
response = await this.fetch(credentials);
|
|
6533
6808
|
}
|
|
6534
6809
|
if (response.status === 429) {
|
|
@@ -6561,10 +6836,10 @@ var UsageService = class {
|
|
|
6561
6836
|
this.cache = {
|
|
6562
6837
|
usage,
|
|
6563
6838
|
fetchedAt: this.now(),
|
|
6564
|
-
accountKey
|
|
6839
|
+
accountKey: snapshotKey
|
|
6565
6840
|
};
|
|
6566
6841
|
this.cacheIdentityRevision = this.oauth.currentIdentityRevision();
|
|
6567
|
-
this.rememberSnapshot(
|
|
6842
|
+
this.rememberSnapshot(snapshotKey, usage, this.cache.fetchedAt);
|
|
6568
6843
|
this.invalidated = false;
|
|
6569
6844
|
return this.fromCache(false);
|
|
6570
6845
|
} catch (error) {
|
|
@@ -7055,7 +7330,7 @@ function field(value, name) {
|
|
|
7055
7330
|
const candidate = value[name];
|
|
7056
7331
|
return typeof candidate === "string" && candidate !== "" ? candidate : null;
|
|
7057
7332
|
}
|
|
7058
|
-
function isRecord$
|
|
7333
|
+
function isRecord$29(value) {
|
|
7059
7334
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7060
7335
|
}
|
|
7061
7336
|
function readPreferencesUpdate(value, current) {
|
|
@@ -7089,7 +7364,7 @@ function readPreferencesUpdate(value, current) {
|
|
|
7089
7364
|
patch.searchProvider = value.searchProvider;
|
|
7090
7365
|
}
|
|
7091
7366
|
if ("contextWindowOverrides" in value) {
|
|
7092
|
-
if (!isRecord$
|
|
7367
|
+
if (!isRecord$29(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
7093
7368
|
const overrides = {};
|
|
7094
7369
|
for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
|
|
7095
7370
|
if (!isCodexModelId(model)) throw new PreferenceError("Unknown Codex model for a context window override.");
|
|
@@ -7903,7 +8178,7 @@ var FileModelSettingsStore$2 = class {
|
|
|
7903
8178
|
*/
|
|
7904
8179
|
/** Default loopback port the Antigravity CLI's own flow uses. */
|
|
7905
8180
|
const DEFAULT_CALLBACK_PORT$2 = 51121;
|
|
7906
|
-
let cachedQuota
|
|
8181
|
+
let cachedQuota;
|
|
7907
8182
|
let quotaFetchInFlight = null;
|
|
7908
8183
|
/** Bumped by every cache clear so an in-flight fetch cannot publish stale state. */
|
|
7909
8184
|
let quotaCacheEpoch$2 = 0;
|
|
@@ -8080,7 +8355,7 @@ function parseQuotaSummary(data) {
|
|
|
8080
8355
|
for (const bucket of rawBuckets) {
|
|
8081
8356
|
if (typeof bucket !== "object" || bucket === null) continue;
|
|
8082
8357
|
const bRec = bucket;
|
|
8083
|
-
const remaining = typeof bRec.remainingFraction === "number" ? Math.max(0, Math.min(1, bRec.remainingFraction)) :
|
|
8358
|
+
const remaining = typeof bRec.remainingFraction === "number" && Number.isFinite(bRec.remainingFraction) ? Math.max(0, Math.min(1, bRec.remainingFraction)) : null;
|
|
8084
8359
|
buckets.push({
|
|
8085
8360
|
bucketId: String(bRec.bucketId || bRec.displayName || "limit"),
|
|
8086
8361
|
displayName: String(bRec.displayName || bRec.bucketId || "Limit"),
|
|
@@ -8143,7 +8418,7 @@ function mergeCatalogSelection(stored, storedCatalog) {
|
|
|
8143
8418
|
return stored.filter((id) => known.has(id));
|
|
8144
8419
|
}
|
|
8145
8420
|
async function fetchAccountQuota(store = new FileCredentialStore$2(), modelSettings, fetchFn = fetch, force = false) {
|
|
8146
|
-
if (!force && cachedQuota
|
|
8421
|
+
if (!force && cachedQuota && Date.now() - (cachedQuota.fetchedAt || 0) < 12e4) return cachedQuota;
|
|
8147
8422
|
if (quotaFetchInFlight) return quotaFetchInFlight;
|
|
8148
8423
|
const epoch = quotaCacheEpoch$2;
|
|
8149
8424
|
const request = (async () => {
|
|
@@ -8179,7 +8454,7 @@ async function fetchAccountQuota(store = new FileCredentialStore$2(), modelSetti
|
|
|
8179
8454
|
fetchedAt: Date.now()
|
|
8180
8455
|
};
|
|
8181
8456
|
if (epoch !== quotaCacheEpoch$2) return snapshot;
|
|
8182
|
-
cachedQuota
|
|
8457
|
+
cachedQuota = snapshot;
|
|
8183
8458
|
if (modelSettings && catalogModels.length > 0) {
|
|
8184
8459
|
const current = await modelSettings.read();
|
|
8185
8460
|
const mergedEnabled = mergeCatalogSelection(current.enabledModelIds, current.catalogModels);
|
|
@@ -8195,11 +8470,11 @@ async function fetchAccountQuota(store = new FileCredentialStore$2(), modelSetti
|
|
|
8195
8470
|
}
|
|
8196
8471
|
}
|
|
8197
8472
|
function getCachedQuota() {
|
|
8198
|
-
return cachedQuota
|
|
8473
|
+
return cachedQuota;
|
|
8199
8474
|
}
|
|
8200
8475
|
function clearCachedQuota() {
|
|
8201
8476
|
quotaCacheEpoch$2 += 1;
|
|
8202
|
-
cachedQuota
|
|
8477
|
+
cachedQuota = void 0;
|
|
8203
8478
|
quotaFetchInFlight = null;
|
|
8204
8479
|
}
|
|
8205
8480
|
//#endregion
|
|
@@ -8589,7 +8864,7 @@ const schemaFields = /* @__PURE__ */ new Set([
|
|
|
8589
8864
|
"required",
|
|
8590
8865
|
"propertyOrdering"
|
|
8591
8866
|
]);
|
|
8592
|
-
function isRecord$
|
|
8867
|
+
function isRecord$28(value) {
|
|
8593
8868
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8594
8869
|
}
|
|
8595
8870
|
function mergeSchemas(left, right) {
|
|
@@ -8597,7 +8872,7 @@ function mergeSchemas(left, right) {
|
|
|
8597
8872
|
...left,
|
|
8598
8873
|
...right
|
|
8599
8874
|
};
|
|
8600
|
-
if (isRecord$
|
|
8875
|
+
if (isRecord$28(left.properties) && isRecord$28(right.properties)) merged.properties = {
|
|
8601
8876
|
...left.properties,
|
|
8602
8877
|
...right.properties
|
|
8603
8878
|
};
|
|
@@ -8633,9 +8908,9 @@ const mergedTypeKeys = /* @__PURE__ */ new Set([
|
|
|
8633
8908
|
* model with strictly less than the schema it started from.
|
|
8634
8909
|
*/
|
|
8635
8910
|
function describeBranch(branch) {
|
|
8636
|
-
const type = typeof branch.type === "string" ? branch.type : isRecord$
|
|
8911
|
+
const type = typeof branch.type === "string" ? branch.type : isRecord$28(branch.properties) ? "object" : isRecord$28(branch.items) ? "array" : "value";
|
|
8637
8912
|
const details = [];
|
|
8638
|
-
if (isRecord$
|
|
8913
|
+
if (isRecord$28(branch.properties)) {
|
|
8639
8914
|
const names = Object.keys(branch.properties);
|
|
8640
8915
|
if (names.length > 0) details.push(`{${names.join(",")}}`);
|
|
8641
8916
|
}
|
|
@@ -8649,7 +8924,7 @@ function describeBranch(branch) {
|
|
|
8649
8924
|
}
|
|
8650
8925
|
/** How well one branch can stand in for a whole union; a named object shape wins. */
|
|
8651
8926
|
function branchRank(branch) {
|
|
8652
|
-
if (isRecord$
|
|
8927
|
+
if (isRecord$28(branch.properties)) return 3;
|
|
8653
8928
|
if (branch.type === "object") return 2;
|
|
8654
8929
|
if (branch.type === "array") return 1;
|
|
8655
8930
|
return 0;
|
|
@@ -8678,7 +8953,7 @@ function unionShape(branches) {
|
|
|
8678
8953
|
shape: branches[0],
|
|
8679
8954
|
folded: false
|
|
8680
8955
|
};
|
|
8681
|
-
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$
|
|
8956
|
+
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$28(branch.properties) && !isRecord$28(branch.items))) {
|
|
8682
8957
|
const shape = { type: branches[0].type };
|
|
8683
8958
|
if (branches.every((branch) => Array.isArray(branch.enum))) {
|
|
8684
8959
|
const values = [...new Set(branches.flatMap((branch) => branch.enum))];
|
|
@@ -8715,7 +8990,7 @@ function foldOwnFields(node) {
|
|
|
8715
8990
|
const out = {};
|
|
8716
8991
|
for (const [key, value] of Object.entries(node)) {
|
|
8717
8992
|
if (key === "anyOf" || key === "oneOf" || key === "allOf") continue;
|
|
8718
|
-
if (key === "properties" && isRecord$
|
|
8993
|
+
if (key === "properties" && isRecord$28(value)) {
|
|
8719
8994
|
out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, foldSchemaUnions(child)]));
|
|
8720
8995
|
continue;
|
|
8721
8996
|
}
|
|
@@ -8744,15 +9019,15 @@ function foldOwnFields(node) {
|
|
|
8744
9019
|
* argument the model sends anyway fails there, naming the actual problem.
|
|
8745
9020
|
*/
|
|
8746
9021
|
function foldSchemaUnions(node) {
|
|
8747
|
-
if (!isRecord$
|
|
9022
|
+
if (!isRecord$28(node)) return node;
|
|
8748
9023
|
let own = foldOwnFields(node);
|
|
8749
9024
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) {
|
|
8750
9025
|
const folded = foldSchemaUnions(branch);
|
|
8751
|
-
if (isRecord$
|
|
9026
|
+
if (isRecord$28(folded)) own = mergeSchemas(own, folded);
|
|
8752
9027
|
}
|
|
8753
9028
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : Array.isArray(node.oneOf) ? node.oneOf : [];
|
|
8754
9029
|
if (alternatives.length === 0) return own;
|
|
8755
|
-
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$
|
|
9030
|
+
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$28);
|
|
8756
9031
|
if (branches.length === 0) return own;
|
|
8757
9032
|
const shapes = branches.filter((branch) => branch.type !== "null");
|
|
8758
9033
|
const acceptsNull = branches.some((branch) => branch.type === "null");
|
|
@@ -8768,7 +9043,7 @@ function foldSchemaUnions(node) {
|
|
|
8768
9043
|
return merged;
|
|
8769
9044
|
}
|
|
8770
9045
|
function toAntigravityToolSchema(schema, options = {}) {
|
|
8771
|
-
if (!isRecord$
|
|
9046
|
+
if (!isRecord$28(schema)) return schema;
|
|
8772
9047
|
const root = schema;
|
|
8773
9048
|
function resolveReference(reference) {
|
|
8774
9049
|
let target = root;
|
|
@@ -8776,13 +9051,13 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
8776
9051
|
const path = reference === "#" ? [] : reference.slice(2).split("/");
|
|
8777
9052
|
for (const segment of path) {
|
|
8778
9053
|
const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
8779
|
-
target = isRecord$
|
|
9054
|
+
target = isRecord$28(target) && Object.hasOwn(target, key) ? target[key] : void 0;
|
|
8780
9055
|
}
|
|
8781
|
-
if (!isRecord$
|
|
9056
|
+
if (!isRecord$28(target)) throw new LlmError(`Antigravity tool schema reference was not found: ${reference}`, "PROVIDER_ERROR");
|
|
8782
9057
|
return target;
|
|
8783
9058
|
}
|
|
8784
9059
|
function convert(node, references) {
|
|
8785
|
-
if (!isRecord$
|
|
9060
|
+
if (!isRecord$28(node)) return {};
|
|
8786
9061
|
let inherited = {};
|
|
8787
9062
|
if (typeof node.$ref === "string") {
|
|
8788
9063
|
if (references.has(node.$ref)) throw new LlmError(`Antigravity tool schema contains a recursive reference: ${node.$ref}`, "PROVIDER_ERROR");
|
|
@@ -8791,8 +9066,8 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
8791
9066
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) inherited = mergeSchemas(inherited, convert(branch, references));
|
|
8792
9067
|
const out = {};
|
|
8793
9068
|
for (const [key, value] of Object.entries(node)) if (schemaFields.has(key)) out[key] = value;
|
|
8794
|
-
if (isRecord$
|
|
8795
|
-
if (isRecord$
|
|
9069
|
+
if (isRecord$28(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
|
|
9070
|
+
if (isRecord$28(node.items)) out.items = convert(node.items, references);
|
|
8796
9071
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : node.oneOf;
|
|
8797
9072
|
if (Array.isArray(alternatives)) out.anyOf = alternatives.map((child) => convert(child, references));
|
|
8798
9073
|
if (Array.isArray(node.type)) {
|
|
@@ -8828,10 +9103,10 @@ let toolCallCounter = 0;
|
|
|
8828
9103
|
function sanitizeText$5(text) {
|
|
8829
9104
|
return text.replace(/\0/g, "");
|
|
8830
9105
|
}
|
|
8831
|
-
function isRecord$
|
|
9106
|
+
function isRecord$27(value) {
|
|
8832
9107
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8833
9108
|
}
|
|
8834
|
-
function asString$
|
|
9109
|
+
function asString$17(value) {
|
|
8835
9110
|
return typeof value === "string" ? value : void 0;
|
|
8836
9111
|
}
|
|
8837
9112
|
function safeJsonParse$5(text) {
|
|
@@ -8848,25 +9123,25 @@ function toolCallIdNeeded(modelId, runtimeModel) {
|
|
|
8848
9123
|
return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
|
|
8849
9124
|
}
|
|
8850
9125
|
function parseArguments$1(raw) {
|
|
8851
|
-
if (isRecord$
|
|
9126
|
+
if (isRecord$27(raw)) return raw;
|
|
8852
9127
|
if (raw === void 0 || raw === null || raw === "") return {};
|
|
8853
9128
|
const parsed = typeof raw === "string" ? safeJsonParse$5(raw) : raw;
|
|
8854
|
-
return isRecord$
|
|
9129
|
+
return isRecord$27(parsed) ? parsed : {};
|
|
8855
9130
|
}
|
|
8856
9131
|
const NO_RESOLVED_IMAGES$5 = /* @__PURE__ */ new Map();
|
|
8857
9132
|
function attachmentOf$5(block) {
|
|
8858
9133
|
const attachment = block.attachment;
|
|
8859
|
-
if (!isRecord$
|
|
9134
|
+
if (!isRecord$27(attachment)) return void 0;
|
|
8860
9135
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
8861
9136
|
}
|
|
8862
9137
|
function attachmentLabel$6(block) {
|
|
8863
|
-
const attachment = isRecord$
|
|
8864
|
-
return asString$
|
|
9138
|
+
const attachment = isRecord$27(block.attachment) ? block.attachment : void 0;
|
|
9139
|
+
return asString$17(attachment?.name) || asString$17(attachment?.attachmentId);
|
|
8865
9140
|
}
|
|
8866
9141
|
function collectImageRefs$4(content, refs) {
|
|
8867
9142
|
if (!Array.isArray(content)) return;
|
|
8868
9143
|
for (const block of content) {
|
|
8869
|
-
if (!isRecord$
|
|
9144
|
+
if (!isRecord$27(block)) continue;
|
|
8870
9145
|
if (block.type === "image") {
|
|
8871
9146
|
const attachment = attachmentOf$5(block);
|
|
8872
9147
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -8904,13 +9179,13 @@ function base64Length$4(bytes) {
|
|
|
8904
9179
|
function requestImageBytes$4(block) {
|
|
8905
9180
|
const attachment = attachmentOf$5(block);
|
|
8906
9181
|
if (attachment) return base64Length$4(attachment.bytes);
|
|
8907
|
-
const inline = asString$
|
|
9182
|
+
const inline = asString$17(block.data) || asString$17(block.base64);
|
|
8908
9183
|
return inline ? inline.length : void 0;
|
|
8909
9184
|
}
|
|
8910
9185
|
function collectRequestImageBytes$4(content, lengths) {
|
|
8911
9186
|
if (!Array.isArray(content)) return;
|
|
8912
9187
|
for (const block of content) {
|
|
8913
|
-
if (!isRecord$
|
|
9188
|
+
if (!isRecord$27(block) || block.type !== "image") continue;
|
|
8914
9189
|
const bytes = requestImageBytes$4(block);
|
|
8915
9190
|
if (bytes !== void 0) lengths.push(bytes);
|
|
8916
9191
|
}
|
|
@@ -8945,7 +9220,7 @@ function offloadOldestRequestImages$4(options) {
|
|
|
8945
9220
|
if (remaining.count === 0 || !Array.isArray(message.content)) return message;
|
|
8946
9221
|
let replaced = false;
|
|
8947
9222
|
const content = message.content.map((block) => {
|
|
8948
|
-
if (remaining.count === 0 || !isRecord$
|
|
9223
|
+
if (remaining.count === 0 || !isRecord$27(block) || block.type !== "image") return block;
|
|
8949
9224
|
if (requestImageBytes$4(block) === void 0) return block;
|
|
8950
9225
|
remaining.count -= 1;
|
|
8951
9226
|
replaced = true;
|
|
@@ -9008,10 +9283,10 @@ function unavailableImageText$6(block) {
|
|
|
9008
9283
|
return `[image unavailable: ${label ? `${label} could not be read` : "the image could not be read"}; ask the user to attach it again if the image is needed]`;
|
|
9009
9284
|
}
|
|
9010
9285
|
function imageBlockToPart(block, images) {
|
|
9011
|
-
let data = asString$
|
|
9012
|
-
const source = isRecord$
|
|
9013
|
-
if (!data && source) data = asString$
|
|
9014
|
-
let mimeType = asString$
|
|
9286
|
+
let data = asString$17(block.data) || asString$17(block.base64);
|
|
9287
|
+
const source = isRecord$27(block.source) ? block.source : void 0;
|
|
9288
|
+
if (!data && source) data = asString$17(source.data) || asString$17(source.base64);
|
|
9289
|
+
let mimeType = asString$17(block.mimeType) || asString$17(block.mediaType) || (source ? asString$17(source.mimeType) || asString$17(source.mediaType) : void 0) || "image/png";
|
|
9015
9290
|
if (data?.startsWith("data:")) {
|
|
9016
9291
|
const match = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
9017
9292
|
if (match) {
|
|
@@ -9034,8 +9309,8 @@ function contentToUserParts(content, images) {
|
|
|
9034
9309
|
if (typeof content === "string") return [{ text: sanitizeText$5(content) }];
|
|
9035
9310
|
if (!Array.isArray(content)) return [];
|
|
9036
9311
|
const parts = [];
|
|
9037
|
-
for (const block of content) if (isRecord$
|
|
9038
|
-
else if (isRecord$
|
|
9312
|
+
for (const block of content) if (isRecord$27(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$5(block.text) });
|
|
9313
|
+
else if (isRecord$27(block) && block.type === "image") {
|
|
9039
9314
|
const img = imageBlockToPart(block, images);
|
|
9040
9315
|
parts.push(img ?? { text: unavailableImageText$6(block) });
|
|
9041
9316
|
}
|
|
@@ -9050,7 +9325,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
9050
9325
|
if (!Array.isArray(blocks)) return [];
|
|
9051
9326
|
const parts = [];
|
|
9052
9327
|
for (const block of blocks) {
|
|
9053
|
-
if (!isRecord$
|
|
9328
|
+
if (!isRecord$27(block)) continue;
|
|
9054
9329
|
if (block.type === "image") {
|
|
9055
9330
|
parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$6(block) });
|
|
9056
9331
|
continue;
|
|
@@ -9062,7 +9337,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
9062
9337
|
function toolResultText$5(blocks) {
|
|
9063
9338
|
if (!Array.isArray(blocks)) return "";
|
|
9064
9339
|
return blocks.map((block) => {
|
|
9065
|
-
if (!isRecord$
|
|
9340
|
+
if (!isRecord$27(block)) return "";
|
|
9066
9341
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$5(block.text);
|
|
9067
9342
|
if (block.type === "tool-result") return toolResultText$5(block.content);
|
|
9068
9343
|
if (block.type === "image") {
|
|
@@ -9076,16 +9351,16 @@ function replayBlockFor$1(message, index) {
|
|
|
9076
9351
|
const source = message.source;
|
|
9077
9352
|
if (!source || source.kind !== "model" || source.provider !== "antigravity") return void 0;
|
|
9078
9353
|
const state = source.replayState;
|
|
9079
|
-
if (!isRecord$
|
|
9354
|
+
if (!isRecord$27(state)) return void 0;
|
|
9080
9355
|
if (Array.isArray(state.blocks)) return state.blocks[index];
|
|
9081
|
-
const resp = isRecord$
|
|
9356
|
+
const resp = isRecord$27(state.response) ? state.response : void 0;
|
|
9082
9357
|
if (resp) {
|
|
9083
9358
|
if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
|
|
9084
9359
|
if (Array.isArray(resp.blocks)) return resp.blocks[index];
|
|
9085
9360
|
}
|
|
9086
9361
|
}
|
|
9087
9362
|
function thoughtSignature(part) {
|
|
9088
|
-
return asString$
|
|
9363
|
+
return asString$17(part?.thoughtSignature) || asString$17(part?.thought_signature) || asString$17(part?.thinkingSignature) || asString$17(part?.textSignature);
|
|
9089
9364
|
}
|
|
9090
9365
|
function replayPart(part) {
|
|
9091
9366
|
const copy = { ...part };
|
|
@@ -9138,11 +9413,11 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
9138
9413
|
if (!Array.isArray(message.content)) return parts;
|
|
9139
9414
|
for (let index = 0; index < message.content.length; index++) {
|
|
9140
9415
|
const block = message.content[index];
|
|
9141
|
-
if (!isRecord$
|
|
9416
|
+
if (!isRecord$27(block)) continue;
|
|
9142
9417
|
if (block.type === "reasoning" && runtimeModel.startsWith("claude-") && (message.source?.kind !== "model" || message.source.provider !== "antigravity" || !("model" in message.source) || message.source.model !== model.id)) continue;
|
|
9143
9418
|
const replay = replayBlockFor$1(message, index);
|
|
9144
|
-
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$
|
|
9145
|
-
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$
|
|
9419
|
+
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$27) : [];
|
|
9420
|
+
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$17(part.text) || "").join("") === sanitizeText$5(String(block.text || ""))) {
|
|
9146
9421
|
parts.push(...originalParts.map(replayPart));
|
|
9147
9422
|
continue;
|
|
9148
9423
|
}
|
|
@@ -9162,8 +9437,8 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
9162
9437
|
} else if (block.type === "tool-call") {
|
|
9163
9438
|
const toolId = String(block.id || "");
|
|
9164
9439
|
const toolName = String(block.name || "");
|
|
9165
|
-
const originalCall = originalParts.find((part) => isRecord$
|
|
9166
|
-
const wireId = asString$
|
|
9440
|
+
const originalCall = originalParts.find((part) => isRecord$27(part.functionCall));
|
|
9441
|
+
const wireId = asString$17((isRecord$27(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
9167
9442
|
toolCalls.set(toolId, {
|
|
9168
9443
|
name: toolName,
|
|
9169
9444
|
id: wireId
|
|
@@ -9215,7 +9490,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
9215
9490
|
continue;
|
|
9216
9491
|
}
|
|
9217
9492
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
9218
|
-
const userParts = contentToUserParts(content.filter((b) => !isRecord$
|
|
9493
|
+
const userParts = contentToUserParts(content.filter((b) => !isRecord$27(b) || b.type !== "tool-result"), images);
|
|
9219
9494
|
if (role === "system") {
|
|
9220
9495
|
if (userParts.length) contents.push({
|
|
9221
9496
|
role: GEMINI_ROLE.user,
|
|
@@ -9227,7 +9502,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
9227
9502
|
role: GEMINI_ROLE.user,
|
|
9228
9503
|
parts: userParts
|
|
9229
9504
|
});
|
|
9230
|
-
for (const b of content) if (isRecord$
|
|
9505
|
+
for (const b of content) if (isRecord$27(b) && b.type === "tool-result") pushToolResult(contents, b, toolCalls, model, runtimeModel, images);
|
|
9231
9506
|
}
|
|
9232
9507
|
return contents;
|
|
9233
9508
|
}
|
|
@@ -9362,7 +9637,7 @@ const USAGE_FIELDS = [
|
|
|
9362
9637
|
"totalTokenCount"
|
|
9363
9638
|
];
|
|
9364
9639
|
function collectUsage(value, state) {
|
|
9365
|
-
if (!isRecord$
|
|
9640
|
+
if (!isRecord$27(value)) return;
|
|
9366
9641
|
for (const key of USAGE_FIELDS) {
|
|
9367
9642
|
const count = value[key];
|
|
9368
9643
|
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) continue;
|
|
@@ -9392,17 +9667,17 @@ function processStreamLine$2(line, state) {
|
|
|
9392
9667
|
}
|
|
9393
9668
|
if (!json) return [];
|
|
9394
9669
|
const chunk = safeJsonParse$5(json);
|
|
9395
|
-
if (!isRecord$
|
|
9396
|
-
const responseData = isRecord$
|
|
9397
|
-
const error = isRecord$
|
|
9398
|
-
if (error !== void 0) throw new LlmError$1(`Antigravity stream error: ${asString$
|
|
9670
|
+
if (!isRecord$27(chunk)) return [];
|
|
9671
|
+
const responseData = isRecord$27(chunk.response) ? chunk.response : chunk;
|
|
9672
|
+
const error = isRecord$27(chunk.error) ? chunk.error : isRecord$27(responseData.error) ? responseData.error : void 0;
|
|
9673
|
+
if (error !== void 0) throw new LlmError$1(`Antigravity stream error: ${asString$17(error.message) ?? "unknown error"}`, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
9399
9674
|
const candidates = Array.isArray(responseData.candidates) ? responseData.candidates : [];
|
|
9400
|
-
const candidate = isRecord$
|
|
9401
|
-
const content = isRecord$
|
|
9675
|
+
const candidate = isRecord$27(candidates[0]) ? candidates[0] : void 0;
|
|
9676
|
+
const content = isRecord$27(candidate?.content) ? candidate.content : void 0;
|
|
9402
9677
|
const parts = Array.isArray(content?.parts) ? content.parts : [];
|
|
9403
9678
|
const out = [];
|
|
9404
9679
|
for (const part of parts) {
|
|
9405
|
-
if (!isRecord$
|
|
9680
|
+
if (!isRecord$27(part)) continue;
|
|
9406
9681
|
if (typeof part.text === "string" && part.text !== "") {
|
|
9407
9682
|
const isThinking = Boolean(part.thought);
|
|
9408
9683
|
const blockType = isThinking ? "reasoning" : "text";
|
|
@@ -9434,7 +9709,7 @@ function processStreamLine$2(line, state) {
|
|
|
9434
9709
|
index: state.currentBlock.index,
|
|
9435
9710
|
text: delta
|
|
9436
9711
|
});
|
|
9437
|
-
} else if (!isRecord$
|
|
9712
|
+
} else if (!isRecord$27(part.functionCall) && thoughtSignature(part)) {
|
|
9438
9713
|
if (state.replayBlocks.length === 0) {
|
|
9439
9714
|
const type = part.thought ? "reasoning" : "text";
|
|
9440
9715
|
state.blocks.push({
|
|
@@ -9458,12 +9733,12 @@ function processStreamLine$2(line, state) {
|
|
|
9458
9733
|
}
|
|
9459
9734
|
state.replayBlocks[state.replayBlocks.length - 1].parts.push(replayPart(part));
|
|
9460
9735
|
}
|
|
9461
|
-
if (isRecord$
|
|
9736
|
+
if (isRecord$27(part.functionCall)) {
|
|
9462
9737
|
out.push(...closeCurrentBlock(state));
|
|
9463
9738
|
const fc = part.functionCall;
|
|
9464
|
-
const toolName = asString$
|
|
9465
|
-
const toolId = asString$
|
|
9466
|
-
const argsText = JSON.stringify(isRecord$
|
|
9739
|
+
const toolName = asString$17(fc.name) || "";
|
|
9740
|
+
const toolId = asString$17(fc.id) || sanitizeToolCallId("", toolName);
|
|
9741
|
+
const argsText = JSON.stringify(isRecord$27(fc.args) ? fc.args : {});
|
|
9467
9742
|
const index = state.blocks.length;
|
|
9468
9743
|
const block = {
|
|
9469
9744
|
type: "tool-call",
|
|
@@ -9500,7 +9775,7 @@ function processStreamLine$2(line, state) {
|
|
|
9500
9775
|
}
|
|
9501
9776
|
collectUsage(chunk.usageMetadata, state);
|
|
9502
9777
|
if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
|
|
9503
|
-
const finishReason = asString$
|
|
9778
|
+
const finishReason = asString$17(candidate?.finishReason) || asString$17(responseData.finishReason);
|
|
9504
9779
|
if (finishReason) {
|
|
9505
9780
|
state.finishReason = finishReason;
|
|
9506
9781
|
out.push(...closeCurrentBlock(state));
|
|
@@ -9579,6 +9854,54 @@ function createPoolCredentialBackend(filePath) {
|
|
|
9579
9854
|
throw new Error("Antigravity pool encrypted storage requires Windows, macOS, or Linux.");
|
|
9580
9855
|
}
|
|
9581
9856
|
/**
|
|
9857
|
+
* One bucket's reset moment as Unix milliseconds, when it parses as a date.
|
|
9858
|
+
*
|
|
9859
|
+
* The service states it as an ISO-ish string; anything `Date.parse` refuses is
|
|
9860
|
+
* dropped rather than guessed at, because a wrong instant on the card is worse
|
|
9861
|
+
* than none.
|
|
9862
|
+
*/
|
|
9863
|
+
function resetTimeMs(resetTime) {
|
|
9864
|
+
if (resetTime === void 0 || resetTime === "") return null;
|
|
9865
|
+
const parsed = Date.parse(resetTime);
|
|
9866
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
9867
|
+
}
|
|
9868
|
+
/**
|
|
9869
|
+
* One cached Antigravity snapshot as the shared account card renders it.
|
|
9870
|
+
*
|
|
9871
|
+
* One window per bucket. The service states what is LEFT, and the card draws what
|
|
9872
|
+
* is SPENT, so the used share is the complement. A bucket whose fraction is
|
|
9873
|
+
* `null` (never measured) or outside 0-1 is dropped rather than drawn as 0% or
|
|
9874
|
+
* 100%: "not stated" and "nothing used" are different claims and only one of
|
|
9875
|
+
* them is true. A stated 0 is a real measurement — the allowance is exhausted —
|
|
9876
|
+
* and is published as 100% used.
|
|
9877
|
+
*
|
|
9878
|
+
* `windowDurationMins` is deliberately absent. The bucket's `window` field is
|
|
9879
|
+
* carried through this line's cache verbatim and nothing in the line interprets
|
|
9880
|
+
* it as a duration, so naming a window from it would be a guess; the card names
|
|
9881
|
+
* each window from the label below instead.
|
|
9882
|
+
*/
|
|
9883
|
+
function antigravityPoolQuota(quota) {
|
|
9884
|
+
if (quota === void 0) return void 0;
|
|
9885
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
9886
|
+
for (const group of quota.groups) for (const bucket of group.buckets) {
|
|
9887
|
+
const name = bucket.displayName.trim();
|
|
9888
|
+
if (name === "") continue;
|
|
9889
|
+
nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
|
|
9890
|
+
}
|
|
9891
|
+
const windows = [];
|
|
9892
|
+
for (const group of quota.groups) {
|
|
9893
|
+
const groupName = group.displayName.trim();
|
|
9894
|
+
for (const bucket of group.buckets) {
|
|
9895
|
+
const remaining = bucket.remainingFraction;
|
|
9896
|
+
if (remaining === null || !Number.isFinite(remaining) || remaining < 0 || remaining > 1) continue;
|
|
9897
|
+
const name = bucket.displayName.trim();
|
|
9898
|
+
const label = (name === "" || (nameCounts.get(name) ?? 0) > 1) && groupName !== "" ? name === "" ? groupName : groupName + " · " + name : name;
|
|
9899
|
+
windows.push(quotaWindow$1(label, (1 - remaining) * 100, { resetsAt: resetTimeMs(bucket.resetTime) }));
|
|
9900
|
+
}
|
|
9901
|
+
}
|
|
9902
|
+
return poolQuota(quota.fetchedAt, windows);
|
|
9903
|
+
}
|
|
9904
|
+
/**
|
|
9582
9905
|
* Antigravity's account pool.
|
|
9583
9906
|
*
|
|
9584
9907
|
* The storage, eligibility, rotation and cooldown rules live in the shared
|
|
@@ -9642,7 +9965,11 @@ var AccountPoolStore = class {
|
|
|
9642
9965
|
...base,
|
|
9643
9966
|
...projectId === void 0 ? {} : { projectId },
|
|
9644
9967
|
...email === void 0 ? {} : { email },
|
|
9645
|
-
...account.planLabel === void 0 ? {} : { planLabel: account.planLabel }
|
|
9968
|
+
...account.planLabel === void 0 ? {} : { planLabel: account.planLabel },
|
|
9969
|
+
...account.isPrimary === true ? (() => {
|
|
9970
|
+
const quota = antigravityPoolQuota(getCachedQuota());
|
|
9971
|
+
return quota === void 0 ? {} : { quota };
|
|
9972
|
+
})() : {}
|
|
9646
9973
|
};
|
|
9647
9974
|
},
|
|
9648
9975
|
emptyMessage: "未登录 Antigravity 账号,请在「设置 → Antigravity」中添加并登录账号。",
|
|
@@ -11859,13 +12186,13 @@ function commandCodeHeaders(apiKey, extra = {}) {
|
|
|
11859
12186
|
function timeoutSignal$3(signal, ms) {
|
|
11860
12187
|
return signal ? AbortSignal.any([signal, AbortSignal.timeout(ms)]) : AbortSignal.timeout(ms);
|
|
11861
12188
|
}
|
|
11862
|
-
function isRecord$
|
|
12189
|
+
function isRecord$26(value) {
|
|
11863
12190
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11864
12191
|
}
|
|
11865
12192
|
function asRecord$9(value) {
|
|
11866
|
-
return isRecord$
|
|
12193
|
+
return isRecord$26(value) ? value : void 0;
|
|
11867
12194
|
}
|
|
11868
|
-
function asString$
|
|
12195
|
+
function asString$16(value) {
|
|
11869
12196
|
if (typeof value === "string" && value.trim() !== "") return value;
|
|
11870
12197
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
11871
12198
|
}
|
|
@@ -11878,7 +12205,7 @@ function asNumber$3(value) {
|
|
|
11878
12205
|
}
|
|
11879
12206
|
function firstString$1(record, keys) {
|
|
11880
12207
|
for (const key of keys) {
|
|
11881
|
-
const value = asString$
|
|
12208
|
+
const value = asString$16(record[key]);
|
|
11882
12209
|
if (value !== void 0) return value;
|
|
11883
12210
|
}
|
|
11884
12211
|
}
|
|
@@ -11907,7 +12234,7 @@ function deepValue(value, keys, depth = 0) {
|
|
|
11907
12234
|
}
|
|
11908
12235
|
return;
|
|
11909
12236
|
}
|
|
11910
|
-
if (!isRecord$
|
|
12237
|
+
if (!isRecord$26(value)) return void 0;
|
|
11911
12238
|
for (const key of keys) if (value[key] !== void 0 && value[key] !== null) return value[key];
|
|
11912
12239
|
for (const nested of Object.values(value)) {
|
|
11913
12240
|
const hit = deepValue(nested, keys, depth + 1);
|
|
@@ -11921,7 +12248,7 @@ function collectRecords(value, keys, depth = 0, out = []) {
|
|
|
11921
12248
|
for (const entry of value) collectRecords(entry, keys, depth + 1, out);
|
|
11922
12249
|
return out;
|
|
11923
12250
|
}
|
|
11924
|
-
if (!isRecord$
|
|
12251
|
+
if (!isRecord$26(value)) return out;
|
|
11925
12252
|
if (keys.some((key) => value[key] !== void 0)) out.push(value);
|
|
11926
12253
|
for (const nested of Object.values(value)) collectRecords(nested, keys, depth + 1, out);
|
|
11927
12254
|
return out;
|
|
@@ -12125,7 +12452,7 @@ function parseCreditBalances(payload, consumed = []) {
|
|
|
12125
12452
|
/** Resolve the plan id the subscription or credits payload reports, when either does. */
|
|
12126
12453
|
function parsePlanId(payloads) {
|
|
12127
12454
|
for (const payload of payloads) {
|
|
12128
|
-
const id = asString$
|
|
12455
|
+
const id = asString$16(deepValue(payload, [
|
|
12129
12456
|
"planId",
|
|
12130
12457
|
"plan_id",
|
|
12131
12458
|
"priceId",
|
|
@@ -12144,7 +12471,7 @@ function parsePlanId(payloads) {
|
|
|
12144
12471
|
*/
|
|
12145
12472
|
function parseSubscriptionStatus(payload) {
|
|
12146
12473
|
const root = asRecord$9(payload) ?? {};
|
|
12147
|
-
return asString$
|
|
12474
|
+
return asString$16(deepValue(asRecord$9(root.data) ?? root, ["status"])) ?? null;
|
|
12148
12475
|
}
|
|
12149
12476
|
/** End of the current billing period, in Unix milliseconds. */
|
|
12150
12477
|
function parseSubscriptionPeriodEnd(payload) {
|
|
@@ -12553,7 +12880,41 @@ function whoami(apiKey, options = {}) {
|
|
|
12553
12880
|
async function verifyApiKey(apiKey, options = {}) {
|
|
12554
12881
|
return parseWhoami(await whoami(apiKey, options), { authenticatedAt: Date.now() });
|
|
12555
12882
|
}
|
|
12556
|
-
|
|
12883
|
+
/**
|
|
12884
|
+
* One newest snapshot PER ACCOUNT.
|
|
12885
|
+
*
|
|
12886
|
+
* Quota follows the account: rotation decides which key spends the next
|
|
12887
|
+
* request, so a single slot only ever described whichever key was read last.
|
|
12888
|
+
* The pool's card draws every account its own reading, so each account keeps
|
|
12889
|
+
* its own entry; past the cap the oldest read is evicted.
|
|
12890
|
+
*
|
|
12891
|
+
* This map is a memory read only. Nothing here fetches — the settings card
|
|
12892
|
+
* builds its account list from it, and that must never cost one upstream
|
|
12893
|
+
* request per pooled key.
|
|
12894
|
+
*/
|
|
12895
|
+
const cachedQuotaEntries$1 = /* @__PURE__ */ new Map();
|
|
12896
|
+
/** How many accounts' snapshots are remembered; mirrors the Codex usage service. */
|
|
12897
|
+
const QUOTA_CACHE_LIMIT$1 = 20;
|
|
12898
|
+
/** Key of a snapshot whose caller named no account. */
|
|
12899
|
+
const UNNAMED_ACCOUNT_KEY$1 = "";
|
|
12900
|
+
function quotaCacheKey(accountId) {
|
|
12901
|
+
return accountId ?? UNNAMED_ACCOUNT_KEY$1;
|
|
12902
|
+
}
|
|
12903
|
+
/** The entry read most recently, whichever account it belongs to. */
|
|
12904
|
+
function newestQuotaEntry$2() {
|
|
12905
|
+
let newest;
|
|
12906
|
+
for (const entry of cachedQuotaEntries$1.values()) if (newest === void 0 || entry.fetchedAt > newest.fetchedAt) newest = entry;
|
|
12907
|
+
return newest;
|
|
12908
|
+
}
|
|
12909
|
+
/** Remember one account's newest snapshot, bounded to the accounts in use. */
|
|
12910
|
+
function rememberQuotaEntry(entry) {
|
|
12911
|
+
cachedQuotaEntries$1.set(quotaCacheKey(entry.accountId), entry);
|
|
12912
|
+
while (cachedQuotaEntries$1.size > QUOTA_CACHE_LIMIT$1) {
|
|
12913
|
+
const oldest = [...cachedQuotaEntries$1.entries()].sort((left, right) => left[1].fetchedAt - right[1].fetchedAt)[0];
|
|
12914
|
+
if (oldest === void 0) break;
|
|
12915
|
+
cachedQuotaEntries$1.delete(oldest[0]);
|
|
12916
|
+
}
|
|
12917
|
+
}
|
|
12557
12918
|
let quotaInFlight$2 = null;
|
|
12558
12919
|
let quotaInFlightAccountId$1;
|
|
12559
12920
|
let quotaCacheEpoch$1 = 0;
|
|
@@ -12562,21 +12923,22 @@ let quotaCacheEpoch$1 = 0;
|
|
|
12562
12923
|
* Kept with a no-argument signature for backward compatibility.
|
|
12563
12924
|
*/
|
|
12564
12925
|
function getCachedQuota$2() {
|
|
12565
|
-
return
|
|
12926
|
+
return newestQuotaEntry$2()?.quota;
|
|
12566
12927
|
}
|
|
12567
12928
|
/**
|
|
12568
12929
|
* The cached snapshot only when it belongs to this account.
|
|
12569
12930
|
*
|
|
12570
12931
|
* Passing no account keeps the pre-pool behavior of returning whatever was
|
|
12571
|
-
* fetched last.
|
|
12932
|
+
* fetched last. This is the lookup the account pool calls while it builds its
|
|
12933
|
+
* summaries: a pure memory read, never a fetch.
|
|
12572
12934
|
*/
|
|
12573
|
-
function getCachedQuotaFor$
|
|
12574
|
-
if (accountId === void 0 || accountId === null) return
|
|
12575
|
-
return
|
|
12935
|
+
function getCachedQuotaFor$3(accountId) {
|
|
12936
|
+
if (accountId === void 0 || accountId === null) return newestQuotaEntry$2()?.quota;
|
|
12937
|
+
return cachedQuotaEntries$1.get(accountId)?.quota;
|
|
12576
12938
|
}
|
|
12577
12939
|
function clearCachedQuota$2() {
|
|
12578
12940
|
quotaCacheEpoch$1 += 1;
|
|
12579
|
-
|
|
12941
|
+
cachedQuotaEntries$1.clear();
|
|
12580
12942
|
quotaInFlight$2 = null;
|
|
12581
12943
|
quotaInFlightAccountId$1 = void 0;
|
|
12582
12944
|
}
|
|
@@ -12591,8 +12953,8 @@ async function fetchAccountQuota$2(store = new FileCredentialStore$1(), fetchFnO
|
|
|
12591
12953
|
const fetchFn = typeof fetchFnOrOptions === "function" ? fetchFnOrOptions : fetchFnOrOptions?.fetchFn ?? fetch;
|
|
12592
12954
|
const effectiveForce = isOptionsObj ? fetchFnOrOptions.force ?? force : force;
|
|
12593
12955
|
const effectiveAccountId = isOptionsObj ? fetchFnOrOptions.accountId ?? accountId : accountId;
|
|
12594
|
-
const
|
|
12595
|
-
if (!effectiveForce &&
|
|
12956
|
+
const cached = getCachedQuotaFor$3(effectiveAccountId);
|
|
12957
|
+
if (!effectiveForce && cached !== void 0 && Date.now() - (cached.fetchedAt || 0) < 12e4) return cached;
|
|
12596
12958
|
if (quotaInFlight$2 !== null && (effectiveAccountId === void 0 || quotaInFlightAccountId$1 === effectiveAccountId)) return quotaInFlight$2;
|
|
12597
12959
|
const epoch = quotaCacheEpoch$1;
|
|
12598
12960
|
const request = (async () => {
|
|
@@ -12648,11 +13010,11 @@ async function fetchAccountQuota$2(store = new FileCredentialStore$1(), fetchFnO
|
|
|
12648
13010
|
sources
|
|
12649
13011
|
};
|
|
12650
13012
|
if (epoch !== quotaCacheEpoch$1) return snapshot;
|
|
12651
|
-
|
|
13013
|
+
rememberQuotaEntry({
|
|
12652
13014
|
accountId: effectiveAccountId,
|
|
12653
13015
|
quota: snapshot,
|
|
12654
13016
|
fetchedAt: snapshot.fetchedAt
|
|
12655
|
-
};
|
|
13017
|
+
});
|
|
12656
13018
|
return snapshot;
|
|
12657
13019
|
})();
|
|
12658
13020
|
quotaInFlight$2 = request;
|
|
@@ -12696,10 +13058,10 @@ function buildModelOptions$2(catalog, enabledModelIds, overrides) {
|
|
|
12696
13058
|
*
|
|
12697
13059
|
* https://commandcode.ai/blog/command-code-provider-api
|
|
12698
13060
|
*/
|
|
12699
|
-
function isRecord$
|
|
13061
|
+
function isRecord$25(value) {
|
|
12700
13062
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12701
13063
|
}
|
|
12702
|
-
function asString$
|
|
13064
|
+
function asString$15(value) {
|
|
12703
13065
|
return typeof value === "string" ? value : void 0;
|
|
12704
13066
|
}
|
|
12705
13067
|
function safeJsonParse$4(text) {
|
|
@@ -12736,17 +13098,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$4 = /* @__PURE__ */ new Set([
|
|
|
12736
13098
|
]);
|
|
12737
13099
|
function attachmentOf$4(block) {
|
|
12738
13100
|
const attachment = block.attachment;
|
|
12739
|
-
if (!isRecord$
|
|
13101
|
+
if (!isRecord$25(attachment)) return void 0;
|
|
12740
13102
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
12741
13103
|
}
|
|
12742
13104
|
function attachmentLabel$5(block) {
|
|
12743
|
-
const attachment = isRecord$
|
|
12744
|
-
return asString$
|
|
13105
|
+
const attachment = isRecord$25(block.attachment) ? block.attachment : void 0;
|
|
13106
|
+
return asString$15(attachment?.name) || asString$15(attachment?.attachmentId);
|
|
12745
13107
|
}
|
|
12746
13108
|
function collectImageRefs$3(content, refs) {
|
|
12747
13109
|
if (!Array.isArray(content)) return;
|
|
12748
13110
|
for (const block of content) {
|
|
12749
|
-
if (!isRecord$
|
|
13111
|
+
if (!isRecord$25(block)) continue;
|
|
12750
13112
|
if (block.type === "image") {
|
|
12751
13113
|
const attachment = attachmentOf$4(block);
|
|
12752
13114
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -12761,13 +13123,13 @@ function base64Length$3(bytes) {
|
|
|
12761
13123
|
function requestImageBytes$3(block) {
|
|
12762
13124
|
const attachment = attachmentOf$4(block);
|
|
12763
13125
|
if (attachment) return base64Length$3(attachment.bytes);
|
|
12764
|
-
const inline = asString$
|
|
13126
|
+
const inline = asString$15(block.data) || asString$15(block.base64);
|
|
12765
13127
|
return inline ? inline.length : void 0;
|
|
12766
13128
|
}
|
|
12767
13129
|
function collectRequestImageBytes$3(content, lengths) {
|
|
12768
13130
|
if (!Array.isArray(content)) return;
|
|
12769
13131
|
for (const block of content) {
|
|
12770
|
-
if (!isRecord$
|
|
13132
|
+
if (!isRecord$25(block) || block.type !== "image") continue;
|
|
12771
13133
|
const bytes = requestImageBytes$3(block);
|
|
12772
13134
|
if (bytes !== void 0) lengths.push(bytes);
|
|
12773
13135
|
}
|
|
@@ -12794,7 +13156,7 @@ function offloadOldestRequestImages$3(options) {
|
|
|
12794
13156
|
if (remaining.count === 0 || !Array.isArray(message.content)) return message;
|
|
12795
13157
|
let replaced = false;
|
|
12796
13158
|
const content = message.content.map((block) => {
|
|
12797
|
-
if (remaining.count === 0 || !isRecord$
|
|
13159
|
+
if (remaining.count === 0 || !isRecord$25(block) || block.type !== "image") return block;
|
|
12798
13160
|
if (requestImageBytes$3(block) === void 0) return block;
|
|
12799
13161
|
remaining.count -= 1;
|
|
12800
13162
|
replaced = true;
|
|
@@ -12847,10 +13209,10 @@ function unavailableImageText$5(block) {
|
|
|
12847
13209
|
return `[image unavailable: ${label ? `${label} could not be read` : "the image could not be read"}; ask the user to attach it again if the image is needed]`;
|
|
12848
13210
|
}
|
|
12849
13211
|
function imageBlockToInline$3(block, images) {
|
|
12850
|
-
let data = asString$
|
|
12851
|
-
const source = isRecord$
|
|
12852
|
-
if (!data && source) data = asString$
|
|
12853
|
-
let mediaType = asString$
|
|
13212
|
+
let data = asString$15(block.data) || asString$15(block.base64);
|
|
13213
|
+
const source = isRecord$25(block.source) ? block.source : void 0;
|
|
13214
|
+
if (!data && source) data = asString$15(source.data) || asString$15(source.base64);
|
|
13215
|
+
let mediaType = asString$15(block.mimeType) || asString$15(block.mediaType) || (source ? asString$15(source.mimeType) || asString$15(source.mediaType) : void 0) || "image/png";
|
|
12854
13216
|
if (data?.startsWith("data:")) {
|
|
12855
13217
|
const matched = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
12856
13218
|
if (matched) {
|
|
@@ -12874,7 +13236,7 @@ function textOf$5(content) {
|
|
|
12874
13236
|
if (!Array.isArray(content)) return "";
|
|
12875
13237
|
const parts = [];
|
|
12876
13238
|
for (const block of content) {
|
|
12877
|
-
if (!isRecord$
|
|
13239
|
+
if (!isRecord$25(block)) continue;
|
|
12878
13240
|
if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$4(block.text));
|
|
12879
13241
|
else if (block.type === "tool-result") parts.push(textOf$5(block.content));
|
|
12880
13242
|
}
|
|
@@ -12883,7 +13245,7 @@ function textOf$5(content) {
|
|
|
12883
13245
|
function toolResultText$4(blocks) {
|
|
12884
13246
|
if (!Array.isArray(blocks)) return "";
|
|
12885
13247
|
return blocks.map((block) => {
|
|
12886
|
-
if (!isRecord$
|
|
13248
|
+
if (!isRecord$25(block)) return "";
|
|
12887
13249
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$4(block.text);
|
|
12888
13250
|
if (block.type === "tool-result") return toolResultText$4(block.content);
|
|
12889
13251
|
if (block.type === "image") return `[image: ${attachmentLabel$5(block) ?? "attached image"}]`;
|
|
@@ -12901,7 +13263,7 @@ function toolResultBlocks$2(blocks, images) {
|
|
|
12901
13263
|
const out = [];
|
|
12902
13264
|
let hasImage = false;
|
|
12903
13265
|
for (const block of blocks) {
|
|
12904
|
-
if (!isRecord$
|
|
13266
|
+
if (!isRecord$25(block)) continue;
|
|
12905
13267
|
if (block.type === "text" && typeof block.text === "string") {
|
|
12906
13268
|
out.push({
|
|
12907
13269
|
type: "text",
|
|
@@ -12950,7 +13312,7 @@ function toolResultImageBlocks$2(blocks, images) {
|
|
|
12950
13312
|
if (!Array.isArray(blocks)) return [];
|
|
12951
13313
|
const out = [];
|
|
12952
13314
|
for (const block of blocks) {
|
|
12953
|
-
if (!isRecord$
|
|
13315
|
+
if (!isRecord$25(block)) continue;
|
|
12954
13316
|
if (block.type === "image") {
|
|
12955
13317
|
const inline = imageBlockToInline$3(block, images);
|
|
12956
13318
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
@@ -12994,7 +13356,7 @@ function nonSystemMessages$4(options) {
|
|
|
12994
13356
|
}
|
|
12995
13357
|
/** Drop the JSON-Schema keywords provider gateways reject or ignore. */
|
|
12996
13358
|
function stripMetaSchema$2(schema) {
|
|
12997
|
-
if (!isRecord$
|
|
13359
|
+
if (!isRecord$25(schema)) return {
|
|
12998
13360
|
type: "object",
|
|
12999
13361
|
properties: {}
|
|
13000
13362
|
};
|
|
@@ -13011,7 +13373,7 @@ function openAIUserContent$2(message, images) {
|
|
|
13011
13373
|
const parts = [];
|
|
13012
13374
|
let hasImage = false;
|
|
13013
13375
|
for (const block of message.content) {
|
|
13014
|
-
if (!isRecord$
|
|
13376
|
+
if (!isRecord$25(block)) continue;
|
|
13015
13377
|
if (block.type === "text" && typeof block.text === "string") {
|
|
13016
13378
|
const text = sanitizeText$4(block.text);
|
|
13017
13379
|
if (text !== "") parts.push({
|
|
@@ -13039,7 +13401,7 @@ function openAIAssistantContent$2(message) {
|
|
|
13039
13401
|
const textParts = [];
|
|
13040
13402
|
const toolCalls = [];
|
|
13041
13403
|
for (const block of message.content) {
|
|
13042
|
-
if (!isRecord$
|
|
13404
|
+
if (!isRecord$25(block)) continue;
|
|
13043
13405
|
if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$4(block.text));
|
|
13044
13406
|
else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
|
|
13045
13407
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`,
|
|
@@ -13071,7 +13433,7 @@ function buildOpenAIRequest$1(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
13071
13433
|
while (index < conversation.length && isToolResultMessage$4(conversation[index])) {
|
|
13072
13434
|
const current = conversation[index];
|
|
13073
13435
|
const block = current.content[0];
|
|
13074
|
-
const callId = isRecord$
|
|
13436
|
+
const callId = isRecord$25(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
|
|
13075
13437
|
messages.push({
|
|
13076
13438
|
role: "tool",
|
|
13077
13439
|
tool_call_id: callId,
|
|
@@ -13132,7 +13494,7 @@ function anthropicUserContent$2(message, images) {
|
|
|
13132
13494
|
if (!Array.isArray(message.content)) return [];
|
|
13133
13495
|
const blocks = [];
|
|
13134
13496
|
for (const block of message.content) {
|
|
13135
|
-
if (!isRecord$
|
|
13497
|
+
if (!isRecord$25(block)) continue;
|
|
13136
13498
|
if (block.type === "text" && typeof block.text === "string") {
|
|
13137
13499
|
const text = sanitizeText$4(block.text);
|
|
13138
13500
|
if (text !== "") blocks.push({
|
|
@@ -13169,7 +13531,7 @@ function anthropicUserContent$2(message, images) {
|
|
|
13169
13531
|
function anthropicAssistantContent$2(message) {
|
|
13170
13532
|
const blocks = [];
|
|
13171
13533
|
for (const block of message.content) {
|
|
13172
|
-
if (!isRecord$
|
|
13534
|
+
if (!isRecord$25(block)) continue;
|
|
13173
13535
|
if (block.type === "text" && typeof block.text === "string") {
|
|
13174
13536
|
const text = sanitizeText$4(block.text);
|
|
13175
13537
|
if (text !== "") blocks.push({
|
|
@@ -13182,7 +13544,7 @@ function anthropicAssistantContent$2(message) {
|
|
|
13182
13544
|
type: "tool_use",
|
|
13183
13545
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`,
|
|
13184
13546
|
name: block.name,
|
|
13185
|
-
input: isRecord$
|
|
13547
|
+
input: isRecord$25(parsed) ? parsed : {}
|
|
13186
13548
|
});
|
|
13187
13549
|
}
|
|
13188
13550
|
}
|
|
@@ -13276,7 +13638,7 @@ function responsesUserContent(message, images) {
|
|
|
13276
13638
|
if (!Array.isArray(message.content)) return [];
|
|
13277
13639
|
const parts = [];
|
|
13278
13640
|
for (const block of message.content) {
|
|
13279
|
-
if (!isRecord$
|
|
13641
|
+
if (!isRecord$25(block)) continue;
|
|
13280
13642
|
if (block.type === "text" && typeof block.text === "string") {
|
|
13281
13643
|
const text = sanitizeText$4(block.text);
|
|
13282
13644
|
if (text !== "") parts.push({
|
|
@@ -13301,7 +13663,7 @@ function responsesToolResultImageBlocks(blocks, images) {
|
|
|
13301
13663
|
if (!Array.isArray(blocks)) return [];
|
|
13302
13664
|
const out = [];
|
|
13303
13665
|
for (const block of blocks) {
|
|
13304
|
-
if (!isRecord$
|
|
13666
|
+
if (!isRecord$25(block)) continue;
|
|
13305
13667
|
if (block.type === "image") {
|
|
13306
13668
|
const inline = imageBlockToInline$3(block, images);
|
|
13307
13669
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
@@ -13328,8 +13690,8 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
13328
13690
|
const imageBlocks = [];
|
|
13329
13691
|
while (index < conversation.length && isToolResultMessage$4(conversation[index])) {
|
|
13330
13692
|
const current = conversation[index];
|
|
13331
|
-
const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$
|
|
13332
|
-
const callId = (isRecord$
|
|
13693
|
+
const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$25(b) && b.type === "tool-result") : void 0;
|
|
13694
|
+
const callId = (isRecord$25(toolResult) && typeof toolResult.toolCallId === "string" ? toolResult.toolCallId : "") || (typeof current.source?.callId === "string" ? current.source.callId : "");
|
|
13333
13695
|
input.push({
|
|
13334
13696
|
type: "function_call_output",
|
|
13335
13697
|
call_id: callId,
|
|
@@ -13352,7 +13714,7 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
13352
13714
|
content
|
|
13353
13715
|
});
|
|
13354
13716
|
for (const call of toolCalls) {
|
|
13355
|
-
const fn = isRecord$
|
|
13717
|
+
const fn = isRecord$25(call.function) ? call.function : void 0;
|
|
13356
13718
|
input.push({
|
|
13357
13719
|
type: "function_call",
|
|
13358
13720
|
call_id: call.id,
|
|
@@ -13475,25 +13837,33 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
13475
13837
|
}
|
|
13476
13838
|
if (payload === "") return [];
|
|
13477
13839
|
const chunk = safeJsonParse$4(payload);
|
|
13478
|
-
if (!isRecord$
|
|
13840
|
+
if (!isRecord$25(chunk)) return [];
|
|
13479
13841
|
const out = [];
|
|
13480
|
-
if (isRecord$
|
|
13481
|
-
|
|
13842
|
+
if (isRecord$25(chunk.error)) {
|
|
13843
|
+
const message = asString$15(chunk.error.message) ?? "unknown error";
|
|
13844
|
+
state.streamError = {
|
|
13845
|
+
vocabulary: "openai",
|
|
13846
|
+
error: chunk.error,
|
|
13847
|
+
message
|
|
13848
|
+
};
|
|
13849
|
+
throw new LlmError$1(`Command Code stream error: ${message}`, isContextOverflow(chunk.error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
13850
|
+
}
|
|
13851
|
+
const usage = isRecord$25(chunk.usage) ? chunk.usage : void 0;
|
|
13482
13852
|
if (usage) {
|
|
13483
13853
|
state.sawUsage = true;
|
|
13484
13854
|
const prompt = numberOr$5(usage.prompt_tokens, 0);
|
|
13485
|
-
const details = isRecord$
|
|
13855
|
+
const details = isRecord$25(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
|
|
13486
13856
|
const cached = details ? numberOr$5(details.cached_tokens, 0) : 0;
|
|
13487
13857
|
state.inputTokens = Math.max(0, prompt - cached);
|
|
13488
13858
|
state.cacheReadTokens = cached;
|
|
13489
13859
|
state.outputTokens = numberOr$5(usage.completion_tokens, state.outputTokens);
|
|
13490
|
-
if (isRecord$
|
|
13860
|
+
if (isRecord$25(usage.completion_tokens_details)) state.reasoningTokens = numberOr$5(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
|
|
13491
13861
|
}
|
|
13492
13862
|
const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
|
|
13493
|
-
const choice = isRecord$
|
|
13494
|
-
const delta = isRecord$
|
|
13863
|
+
const choice = isRecord$25(choices[0]) ? choices[0] : void 0;
|
|
13864
|
+
const delta = isRecord$25(choice?.delta) ? choice.delta : void 0;
|
|
13495
13865
|
if (delta) {
|
|
13496
|
-
const reasoning = asString$
|
|
13866
|
+
const reasoning = asString$15(delta.reasoning_content) ?? asString$15(delta.reasoning);
|
|
13497
13867
|
if (reasoning !== void 0 && reasoning !== "") {
|
|
13498
13868
|
out.push(...closeToolCalls$4(state));
|
|
13499
13869
|
if (state.current === null || state.current.type !== "reasoning") out.push(...openTextBlock$3(state, "reasoning"));
|
|
@@ -13505,7 +13875,7 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
13505
13875
|
text: sanitizeText$4(reasoning)
|
|
13506
13876
|
});
|
|
13507
13877
|
}
|
|
13508
|
-
const content = asString$
|
|
13878
|
+
const content = asString$15(delta.content);
|
|
13509
13879
|
if (content !== void 0 && content !== "") {
|
|
13510
13880
|
out.push(...closeToolCalls$4(state));
|
|
13511
13881
|
if (state.current === null || state.current.type !== "text") out.push(...openTextBlock$3(state, "text"));
|
|
@@ -13519,11 +13889,11 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
13519
13889
|
}
|
|
13520
13890
|
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
13521
13891
|
for (const entry of toolDeltas) {
|
|
13522
|
-
if (!isRecord$
|
|
13892
|
+
if (!isRecord$25(entry)) continue;
|
|
13523
13893
|
out.push(...applyOpenAIToolDelta$1(entry, state));
|
|
13524
13894
|
}
|
|
13525
13895
|
}
|
|
13526
|
-
const finish = asString$
|
|
13896
|
+
const finish = asString$15(choice?.finish_reason);
|
|
13527
13897
|
if (finish !== void 0 && finish !== "") {
|
|
13528
13898
|
state.finishReason = finish;
|
|
13529
13899
|
out.push(...closeCurrent$3(state));
|
|
@@ -13533,15 +13903,15 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
13533
13903
|
}
|
|
13534
13904
|
function applyOpenAIToolDelta$1(entry, state) {
|
|
13535
13905
|
const wireIndex = typeof entry.index === "number" ? entry.index : 0;
|
|
13536
|
-
const fn = isRecord$
|
|
13906
|
+
const fn = isRecord$25(entry.function) ? entry.function : {};
|
|
13537
13907
|
const out = [];
|
|
13538
13908
|
let call = state.toolCalls.get(wireIndex);
|
|
13539
13909
|
if (call === void 0) {
|
|
13540
13910
|
out.push(...closeCurrent$3(state));
|
|
13541
13911
|
call = {
|
|
13542
13912
|
blockIndex: state.blocks.length,
|
|
13543
|
-
id: asString$
|
|
13544
|
-
name: asString$
|
|
13913
|
+
id: asString$15(entry.id) ?? `call_${wireIndex}`,
|
|
13914
|
+
name: asString$15(fn.name) ?? "",
|
|
13545
13915
|
arguments: "",
|
|
13546
13916
|
started: false
|
|
13547
13917
|
};
|
|
@@ -13554,13 +13924,13 @@ function applyOpenAIToolDelta$1(entry, state) {
|
|
|
13554
13924
|
state.toolCalls.set(wireIndex, call);
|
|
13555
13925
|
} else {
|
|
13556
13926
|
if (call.id === `call_${wireIndex}`) {
|
|
13557
|
-
const id = asString$
|
|
13927
|
+
const id = asString$15(entry.id);
|
|
13558
13928
|
if (id !== void 0) call.id = id;
|
|
13559
13929
|
}
|
|
13560
|
-
const name = asString$
|
|
13930
|
+
const name = asString$15(fn.name);
|
|
13561
13931
|
if (name !== void 0 && name !== "") call.name = name;
|
|
13562
13932
|
}
|
|
13563
|
-
const argsDelta = asString$
|
|
13933
|
+
const argsDelta = asString$15(fn.arguments) ?? "";
|
|
13564
13934
|
if (argsDelta !== "") call.arguments += argsDelta;
|
|
13565
13935
|
if (!call.started) {
|
|
13566
13936
|
call.started = true;
|
|
@@ -13591,12 +13961,12 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13591
13961
|
const payload = trimmed.slice(5).trim();
|
|
13592
13962
|
if (payload === "" || payload === "[DONE]") return [];
|
|
13593
13963
|
const event = safeJsonParse$4(payload);
|
|
13594
|
-
if (!isRecord$
|
|
13595
|
-
const type = asString$
|
|
13964
|
+
if (!isRecord$25(event)) return [];
|
|
13965
|
+
const type = asString$15(event.type);
|
|
13596
13966
|
const out = [];
|
|
13597
13967
|
if (type === "message_start") {
|
|
13598
|
-
const message = isRecord$
|
|
13599
|
-
const usage = message && isRecord$
|
|
13968
|
+
const message = isRecord$25(event.message) ? event.message : void 0;
|
|
13969
|
+
const usage = message && isRecord$25(message.usage) ? message.usage : void 0;
|
|
13600
13970
|
if (usage) {
|
|
13601
13971
|
state.sawUsage = true;
|
|
13602
13972
|
state.inputTokens = numberOr$5(usage.input_tokens, 0);
|
|
@@ -13604,21 +13974,21 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13604
13974
|
state.cacheWriteTokens = numberOr$5(usage.cache_creation_input_tokens, 0);
|
|
13605
13975
|
state.outputTokens = numberOr$5(usage.output_tokens, 0);
|
|
13606
13976
|
}
|
|
13607
|
-
const stop = message ? asString$
|
|
13977
|
+
const stop = message ? asString$15(message.stop_reason) : void 0;
|
|
13608
13978
|
if (stop !== void 0 && stop !== null) state.finishReason = stop;
|
|
13609
13979
|
return out;
|
|
13610
13980
|
}
|
|
13611
13981
|
if (type === "content_block_start") {
|
|
13612
13982
|
const contentIndex = numberOr$5(event.index, 0);
|
|
13613
|
-
const block = isRecord$
|
|
13614
|
-
const blockType = asString$
|
|
13983
|
+
const block = isRecord$25(event.content_block) ? event.content_block : {};
|
|
13984
|
+
const blockType = asString$15(block.type);
|
|
13615
13985
|
out.push(...closeCurrent$3(state));
|
|
13616
13986
|
if (blockType === "tool_use") {
|
|
13617
13987
|
const index = state.blocks.length;
|
|
13618
13988
|
const pending = {
|
|
13619
13989
|
blockIndex: index,
|
|
13620
|
-
id: asString$
|
|
13621
|
-
name: asString$
|
|
13990
|
+
id: asString$15(block.id) ?? `toolu_${contentIndex}`,
|
|
13991
|
+
name: asString$15(block.name) ?? "",
|
|
13622
13992
|
arguments: "",
|
|
13623
13993
|
started: true
|
|
13624
13994
|
};
|
|
@@ -13660,11 +14030,11 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13660
14030
|
}
|
|
13661
14031
|
if (type === "content_block_delta") {
|
|
13662
14032
|
const contentIndex = numberOr$5(event.index, 0);
|
|
13663
|
-
const delta = isRecord$
|
|
13664
|
-
const deltaType = asString$
|
|
14033
|
+
const delta = isRecord$25(event.delta) ? event.delta : {};
|
|
14034
|
+
const deltaType = asString$15(delta.type);
|
|
13665
14035
|
if (deltaType === "input_json_delta") {
|
|
13666
14036
|
const pending = state.toolCalls.get(contentIndex);
|
|
13667
|
-
const partial = asString$
|
|
14037
|
+
const partial = asString$15(delta.partial_json) ?? "";
|
|
13668
14038
|
if (pending !== void 0) {
|
|
13669
14039
|
pending.arguments += partial;
|
|
13670
14040
|
out.push({
|
|
@@ -13677,7 +14047,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13677
14047
|
}
|
|
13678
14048
|
return out;
|
|
13679
14049
|
}
|
|
13680
|
-
const text = deltaType === "thinking_delta" ? asString$
|
|
14050
|
+
const text = deltaType === "thinking_delta" ? asString$15(delta.thinking) : asString$15(delta.text);
|
|
13681
14051
|
if (text !== void 0 && text !== "") {
|
|
13682
14052
|
const index = state.contentIndexes.get(contentIndex) ?? state.current?.index;
|
|
13683
14053
|
const kind = deltaType === "thinking_delta" ? "reasoning" : "text";
|
|
@@ -13733,10 +14103,10 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13733
14103
|
return out;
|
|
13734
14104
|
}
|
|
13735
14105
|
if (type === "message_delta") {
|
|
13736
|
-
const delta = isRecord$
|
|
13737
|
-
const stop = delta ? asString$
|
|
14106
|
+
const delta = isRecord$25(event.delta) ? event.delta : void 0;
|
|
14107
|
+
const stop = delta ? asString$15(delta.stop_reason) : void 0;
|
|
13738
14108
|
if (stop !== void 0 && stop !== "") state.finishReason = stop;
|
|
13739
|
-
const usage = isRecord$
|
|
14109
|
+
const usage = isRecord$25(event.usage) ? event.usage : void 0;
|
|
13740
14110
|
if (usage) {
|
|
13741
14111
|
state.sawUsage = true;
|
|
13742
14112
|
state.outputTokens = numberOr$5(usage.output_tokens, state.outputTokens);
|
|
@@ -13748,8 +14118,14 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
13748
14118
|
return closeStream$4(state);
|
|
13749
14119
|
}
|
|
13750
14120
|
if (type === "error") {
|
|
13751
|
-
const error = isRecord$
|
|
13752
|
-
|
|
14121
|
+
const error = isRecord$25(event.error) ? event.error : {};
|
|
14122
|
+
const message = asString$15(error.message) ?? "unknown error";
|
|
14123
|
+
state.streamError = {
|
|
14124
|
+
vocabulary: "anthropic",
|
|
14125
|
+
error,
|
|
14126
|
+
message
|
|
14127
|
+
};
|
|
14128
|
+
throw new LlmError$1(`Command Code stream error: ${message}`, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
13753
14129
|
}
|
|
13754
14130
|
return out;
|
|
13755
14131
|
}
|
|
@@ -13788,7 +14164,7 @@ function processResponsesStreamLine(line, state) {
|
|
|
13788
14164
|
} catch {
|
|
13789
14165
|
return [];
|
|
13790
14166
|
}
|
|
13791
|
-
if (!isRecord$
|
|
14167
|
+
if (!isRecord$25(parsed)) return [];
|
|
13792
14168
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
13793
14169
|
if (type === "response.output_text.delta") {
|
|
13794
14170
|
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
@@ -13815,7 +14191,7 @@ function processResponsesStreamLine(line, state) {
|
|
|
13815
14191
|
}];
|
|
13816
14192
|
}
|
|
13817
14193
|
if (type === "response.output_item.added") {
|
|
13818
|
-
const item = isRecord$
|
|
14194
|
+
const item = isRecord$25(parsed.item) ? parsed.item : null;
|
|
13819
14195
|
if (item === null || item.type !== "function_call") return [];
|
|
13820
14196
|
return startResponsesToolCall(state, parsed, item);
|
|
13821
14197
|
}
|
|
@@ -13839,19 +14215,24 @@ function processResponsesStreamLine(line, state) {
|
|
|
13839
14215
|
return [];
|
|
13840
14216
|
}
|
|
13841
14217
|
if (type === "response.completed" || type === "response.incomplete") {
|
|
13842
|
-
const response = isRecord$
|
|
14218
|
+
const response = isRecord$25(parsed.response) ? parsed.response : null;
|
|
13843
14219
|
if (response !== null) {
|
|
13844
14220
|
if (response.status === "failed") {
|
|
13845
14221
|
state.done = true;
|
|
13846
|
-
const errorObj = isRecord$
|
|
14222
|
+
const errorObj = isRecord$25(response.error) ? response.error : null;
|
|
13847
14223
|
const detail = errorObj && typeof errorObj.message === "string" ? errorObj.message : typeof response.error === "string" ? response.error : "";
|
|
14224
|
+
state.streamError = {
|
|
14225
|
+
vocabulary: "responses",
|
|
14226
|
+
error: errorObj,
|
|
14227
|
+
message: detail
|
|
14228
|
+
};
|
|
13848
14229
|
throw new LlmError$1(detail === "" ? "Command Code responses stream failed" : `Command Code responses stream failed: ${detail}`, "PROVIDER_ERROR");
|
|
13849
14230
|
}
|
|
13850
14231
|
readResponsesUsage(response, state);
|
|
13851
14232
|
}
|
|
13852
14233
|
state.done = true;
|
|
13853
14234
|
if (type === "response.incomplete") {
|
|
13854
|
-
const details = response && isRecord$
|
|
14235
|
+
const details = response && isRecord$25(response.incomplete_details) ? response.incomplete_details : null;
|
|
13855
14236
|
const reason = details && typeof details.reason === "string" ? details.reason : "";
|
|
13856
14237
|
state.finishReason = reason === "max_output_tokens" || reason === "length" || reason === "max_tokens" ? "length" : reason || "length";
|
|
13857
14238
|
} else state.finishReason = "stop";
|
|
@@ -13859,8 +14240,13 @@ function processResponsesStreamLine(line, state) {
|
|
|
13859
14240
|
}
|
|
13860
14241
|
if (type === "response.failed" || type === "error") {
|
|
13861
14242
|
state.done = true;
|
|
13862
|
-
const errorObj = isRecord$
|
|
13863
|
-
const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$
|
|
14243
|
+
const errorObj = isRecord$25(parsed.error) ? parsed.error : isRecord$25(parsed.response) && isRecord$25(parsed.response.error) ? parsed.response.error : null;
|
|
14244
|
+
const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$25(parsed.response) && typeof parsed.response.error === "string" ? parsed.response.error : typeof parsed.error === "string" ? parsed.error : "");
|
|
14245
|
+
state.streamError = {
|
|
14246
|
+
vocabulary: "responses",
|
|
14247
|
+
error: errorObj,
|
|
14248
|
+
message: detail
|
|
14249
|
+
};
|
|
13864
14250
|
throw new LlmError$1(detail === "" ? "Command Code responses stream failed" : `Command Code responses stream failed: ${detail}`, isContextOverflow(errorObj ?? detail) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
13865
14251
|
}
|
|
13866
14252
|
return [];
|
|
@@ -13943,15 +14329,15 @@ function responsesToolCall(state, key) {
|
|
|
13943
14329
|
for (const call of state.toolCalls.values()) if (call.id === toToolCallId(key)) return call;
|
|
13944
14330
|
}
|
|
13945
14331
|
function readResponsesUsage(response, state) {
|
|
13946
|
-
const usage = isRecord$
|
|
14332
|
+
const usage = isRecord$25(response.usage) ? response.usage : null;
|
|
13947
14333
|
if (usage === null) return;
|
|
13948
|
-
const cached = isRecord$
|
|
13949
|
-
const written = isRecord$
|
|
14334
|
+
const cached = isRecord$25(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
|
|
14335
|
+
const written = isRecord$25(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cache_write_tokens) : 0;
|
|
13950
14336
|
state.inputTokens = Math.max(0, numberOrZero(usage.input_tokens) - cached - written);
|
|
13951
14337
|
state.cacheReadTokens = cached;
|
|
13952
14338
|
state.cacheWriteTokens = written;
|
|
13953
14339
|
state.outputTokens = numberOrZero(usage.output_tokens);
|
|
13954
|
-
const outputDetails = isRecord$
|
|
14340
|
+
const outputDetails = isRecord$25(usage.output_tokens_details) ? usage.output_tokens_details : null;
|
|
13955
14341
|
if (outputDetails !== null) state.reasoningTokens = numberOrZero(outputDetails.reasoning_tokens);
|
|
13956
14342
|
state.sawUsage = true;
|
|
13957
14343
|
}
|
|
@@ -13978,6 +14364,136 @@ function assertStreamComplete$4(state) {
|
|
|
13978
14364
|
if (!state.done && state.finishReason === null) throw new LlmError$1("Command Code stream ended before its terminal event", "PROVIDER_ERROR");
|
|
13979
14365
|
}
|
|
13980
14366
|
//#endregion
|
|
14367
|
+
//#region src/host/common/stream-error.ts
|
|
14368
|
+
/**
|
|
14369
|
+
* A failure the provider reported INSIDE a 200 stream.
|
|
14370
|
+
*
|
|
14371
|
+
* A service can answer 200 and then report the failure in the stream, in the
|
|
14372
|
+
* same `{ error: { type, message } }` envelope a non-2xx body carries. A mapper
|
|
14373
|
+
* has to type that event with one code, and the code that is right in general is
|
|
14374
|
+
* PROVIDER_ERROR - it cannot know whether output has started - which is outside
|
|
14375
|
+
* every retry set. So a transient failure delivered this way ends the turn on
|
|
14376
|
+
* the first try while the identical failure delivered as a status is retried.
|
|
14377
|
+
*
|
|
14378
|
+
* The reclassification itself is per line, because only the adapter knows
|
|
14379
|
+
* whether anything has reached the caller yet, and because every line owns rules
|
|
14380
|
+
* its own HTTP path already owns. What is shared is the question each line asks
|
|
14381
|
+
* its own classifier: what status WOULD this failure have carried?
|
|
14382
|
+
*/
|
|
14383
|
+
/**
|
|
14384
|
+
* The status an in-band `error` event stands for, or null for a type this
|
|
14385
|
+
* vocabulary does not name.
|
|
14386
|
+
*
|
|
14387
|
+
* The Anthropic-compatible shims (MiniMax Code, Kimi Code, and Command Code's
|
|
14388
|
+
* messages stream) speak one vocabulary, and each of these types already has a
|
|
14389
|
+
* status: 529 is Anthropic's documented overload, 429 the rate limit, and a 5xx
|
|
14390
|
+
* the server-side failure. Naming the STATUS rather than the verdict is what lets
|
|
14391
|
+
* each line hand the answer to its own classifier, so a rule that line already
|
|
14392
|
+
* owns - a 429 that names an exhausted plan, a credential refusal reported as
|
|
14393
|
+
* 403 - keeps owning it here.
|
|
14394
|
+
*
|
|
14395
|
+
* Null for everything else: a request the provider rejected, a context
|
|
14396
|
+
* overflow, a credential refusal, a type from the future. Null is what keeps
|
|
14397
|
+
* the mapper's own verdict, and that is the safe direction to fail in - a
|
|
14398
|
+
* fallback to some made-up status would file an unknown type as a retryable
|
|
14399
|
+
* server error, which is worse than not retrying at all.
|
|
14400
|
+
*/
|
|
14401
|
+
function inBandAnthropicStatus(error) {
|
|
14402
|
+
const type = readErrorType(error);
|
|
14403
|
+
if (type === null) return null;
|
|
14404
|
+
return ANTHROPIC_IN_BAND_STATUS[type] ?? null;
|
|
14405
|
+
}
|
|
14406
|
+
/** The status each transient Anthropic-compatible in-band type stands for. */
|
|
14407
|
+
const ANTHROPIC_IN_BAND_STATUS = {
|
|
14408
|
+
overloaded_error: 529,
|
|
14409
|
+
rate_limit_error: 429,
|
|
14410
|
+
api_error: 500
|
|
14411
|
+
};
|
|
14412
|
+
/**
|
|
14413
|
+
* The DSH code an in-band Responses-vocabulary failure becomes, or null when it
|
|
14414
|
+
* names nothing transient.
|
|
14415
|
+
*
|
|
14416
|
+
* The Responses stream reports failure as `response.failed` or `error`, whose
|
|
14417
|
+
* message is free text and whose structured evidence is split across TWO fields:
|
|
14418
|
+
* this plugin's own routes document `{"error":{"message":"Upstream model
|
|
14419
|
+
* provider is temporarily unavailable. Please try again in a moment.","type":
|
|
14420
|
+
* "server_error"}}` - `type`, not `code` - so reading only `code` would miss
|
|
14421
|
+
* the exact body this exists for, and its message names nothing the heuristic
|
|
14422
|
+
* would catch. Both fields are read, and the message is read too, because a
|
|
14423
|
+
* deployment that says "overloaded" in prose is the whole signal there.
|
|
14424
|
+
*
|
|
14425
|
+
* Anything not recognized keeps the mapper's verdict.
|
|
14426
|
+
*/
|
|
14427
|
+
function inBandResponsesCode(error, message) {
|
|
14428
|
+
const fields = isRecord$24(error) ? error : {};
|
|
14429
|
+
const rawCode = (asString$14(fields.code) ?? asString$14(fields.type) ?? "").toLowerCase();
|
|
14430
|
+
const text = message.toLowerCase();
|
|
14431
|
+
if (text.includes("rate limit") || rawCode === "rate_limit" || rawCode === "rate_limit_exceeded") return "RATE_LIMIT";
|
|
14432
|
+
if (text.includes("overload") || text.includes("server error") || rawCode === "server_error" || rawCode === "service_unavailable" || rawCode === "internal_error") return "SERVER";
|
|
14433
|
+
return null;
|
|
14434
|
+
}
|
|
14435
|
+
/**
|
|
14436
|
+
* The error one in-band stream failure becomes, or `thrown` itself when the
|
|
14437
|
+
* line's own verdict stands.
|
|
14438
|
+
*
|
|
14439
|
+
* Call this only while nothing has reached the caller: the whole reason a fresh
|
|
14440
|
+
* request is still free is what makes the retry safe (see module note 1 in the
|
|
14441
|
+
* Claude adapter, which this generalizes). After the first chunk the mapper's
|
|
14442
|
+
* verdict stands and this must not be called at all.
|
|
14443
|
+
*
|
|
14444
|
+
* Only the CODE changes. The message stays the mapper's, which names the wire
|
|
14445
|
+
* type the user saw, and no `status` is attached: the response really was a
|
|
14446
|
+
* 200, and a synthetic one would misreport what the provider said.
|
|
14447
|
+
*
|
|
14448
|
+
* @param thrown - the mapper's own verdict, passed through when it stands.
|
|
14449
|
+
* @param rawError - the event's wire `error` object, from the stream state.
|
|
14450
|
+
* @param classify - this line's own failure classifier, so every rule its HTTP
|
|
14451
|
+
* path owns still owns the verdict reached here.
|
|
14452
|
+
* @param statusFor - the vocabulary's status table; Responses lines pass
|
|
14453
|
+
* {@link inBandResponsesCode} through {@link reclassifyInBandResponsesError}.
|
|
14454
|
+
*/
|
|
14455
|
+
function reclassifyInBandError(thrown, rawError, classify, statusFor = inBandAnthropicStatus) {
|
|
14456
|
+
const status = statusFor(rawError);
|
|
14457
|
+
if (status === null) return thrown;
|
|
14458
|
+
const failure = classify(status, JSON.stringify({ error: rawError }));
|
|
14459
|
+
if (!failure.retryable) return thrown;
|
|
14460
|
+
return new LlmError(thrown.message, failure.code, { cause: thrown });
|
|
14461
|
+
}
|
|
14462
|
+
/**
|
|
14463
|
+
* {@link reclassifyInBandError} for a line whose classifier reads a status, fed
|
|
14464
|
+
* by the Responses vocabulary instead of the Anthropic one.
|
|
14465
|
+
*
|
|
14466
|
+
* The transient verdict found here is a QUESTION, not the answer: a
|
|
14467
|
+
* `rate_limit` code says only that the failure is worth retrying, and whether
|
|
14468
|
+
* it actually is - a 429 naming a spent balance is this route's PROVIDER_ERROR,
|
|
14469
|
+
* not a RATE_LIMIT - is a rule the line's own classifier owns. So the answer is
|
|
14470
|
+
* asked of `classify` at the status the verdict stands for, exactly as the
|
|
14471
|
+
* Anthropic sibling does, and the CODE that comes back is the one the HTTP path
|
|
14472
|
+
* would have produced for the same body. That agreement is the whole point: the
|
|
14473
|
+
* in-band delivery must not be the one delivery with its own opinion.
|
|
14474
|
+
*/
|
|
14475
|
+
function reclassifyInBandResponsesError(thrown, rawError, message, classify) {
|
|
14476
|
+
const code = inBandResponsesCode(rawError, message);
|
|
14477
|
+
if (code === null) return thrown;
|
|
14478
|
+
const failure = classify(code === "RATE_LIMIT" ? 429 : 500, JSON.stringify({
|
|
14479
|
+
error: rawError,
|
|
14480
|
+
message
|
|
14481
|
+
}));
|
|
14482
|
+
if (!failure.retryable) return thrown;
|
|
14483
|
+
return new LlmError(thrown.message, failure.code, { cause: thrown });
|
|
14484
|
+
}
|
|
14485
|
+
/** The wire `error` object of an in-band event, read defensively. */
|
|
14486
|
+
function readErrorType(error) {
|
|
14487
|
+
if (!isRecord$24(error)) return null;
|
|
14488
|
+
return typeof error.type === "string" ? error.type : null;
|
|
14489
|
+
}
|
|
14490
|
+
function asString$14(value) {
|
|
14491
|
+
return typeof value === "string" ? value : void 0;
|
|
14492
|
+
}
|
|
14493
|
+
function isRecord$24(value) {
|
|
14494
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14495
|
+
}
|
|
14496
|
+
//#endregion
|
|
13981
14497
|
//#region src/host/common/capabilities.ts
|
|
13982
14498
|
/** Error thrown by requireCapability when a capability contract fails. */
|
|
13983
14499
|
var CapabilityError = class extends Error {
|
|
@@ -14253,6 +14769,35 @@ function isCommandCodeCredentialInvalid(status, detail) {
|
|
|
14253
14769
|
}
|
|
14254
14770
|
return false;
|
|
14255
14771
|
}
|
|
14772
|
+
/**
|
|
14773
|
+
* The verdict one failed response becomes on this route.
|
|
14774
|
+
*
|
|
14775
|
+
* An in-band `error` event is a status the provider chose not to send as a
|
|
14776
|
+
* status, so the chain that answers a non-2xx body has to answer it too rather
|
|
14777
|
+
* than a second, looser one: every rule this route already owns - a model the
|
|
14778
|
+
* plan does not include, a rejected key, a spent quota - keeps owning the
|
|
14779
|
+
* answer, and the codes below are the ONLY place the HTTP path reads them from.
|
|
14780
|
+
*
|
|
14781
|
+
* `retryable` is membership of this route's own policy rather than a second
|
|
14782
|
+
* judgement, because a code is worth repeating exactly when RETRY_POLICY repeats
|
|
14783
|
+
* it; a verdict the policy would not act on must not be filed as transient.
|
|
14784
|
+
*
|
|
14785
|
+
* @param status - the response status, or the status an in-band type stands for.
|
|
14786
|
+
* @param detail - the response body, or the serialized in-band error envelope.
|
|
14787
|
+
*/
|
|
14788
|
+
function classifyCommandCodeFailure(status, detail) {
|
|
14789
|
+
let code;
|
|
14790
|
+
if (status === 422) code = "PROVIDER_ERROR";
|
|
14791
|
+
else if (isCommandCodeModelAccessDenied(status, detail)) code = "PROVIDER_ERROR";
|
|
14792
|
+
else if (isCommandCodeCredentialInvalid(status, detail) || status === 401) code = "INVALID_CREDENTIAL";
|
|
14793
|
+
else if (status === 429) code = "RATE_LIMIT";
|
|
14794
|
+
else if (status >= 500) code = "SERVER";
|
|
14795
|
+
else code = isHttpContextOverflow(status, detail) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR";
|
|
14796
|
+
return {
|
|
14797
|
+
code,
|
|
14798
|
+
retryable: RETRY_POLICY$5.mode === "normal" && RETRY_POLICY$5.retryableCodes.includes(code)
|
|
14799
|
+
};
|
|
14800
|
+
}
|
|
14256
14801
|
/** Cooldown one rate-limited key takes when the provider states no delay. */
|
|
14257
14802
|
const POOL_COOLDOWN_MS$4 = 15 * 6e4;
|
|
14258
14803
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
@@ -14447,49 +14992,105 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
14447
14992
|
}
|
|
14448
14993
|
if (response === void 0 || !response.ok) {
|
|
14449
14994
|
const status = response?.status ?? 500;
|
|
14995
|
+
const { code } = classifyCommandCodeFailure(status, detail);
|
|
14450
14996
|
if (status === 422) {
|
|
14451
|
-
if (detail.includes("cmd_zdr_no_providers")) throw new LlmError(`${PROVIDER_NAME$5} rejected request under Zero Data Retention: no ZDR-capable upstream is available for this model (${detail || "cmd_zdr_no_providers"}).`,
|
|
14452
|
-
throw new LlmError(`${PROVIDER_NAME$5} validation error (422): ${detail || "Unprocessable Entity"}`,
|
|
14997
|
+
if (detail.includes("cmd_zdr_no_providers")) throw new LlmError(`${PROVIDER_NAME$5} rejected request under Zero Data Retention: no ZDR-capable upstream is available for this model (${detail || "cmd_zdr_no_providers"}).`, code, { status: 422 });
|
|
14998
|
+
throw new LlmError(`${PROVIDER_NAME$5} validation error (422): ${detail || "Unprocessable Entity"}`, code, { status: 422 });
|
|
14453
14999
|
}
|
|
14454
|
-
if (isCommandCodeModelAccessDenied(status, detail)) throw new LlmError(`${PROVIDER_NAME$5} access denied for model ${options.model}: this model is not included in the plan or requires higher entitlement (${status}).${detail ? ` ${detail}` : ""}`,
|
|
14455
|
-
if (isCommandCodeCredentialInvalid(status, detail) || status === 401) throw new LlmError(`${PROVIDER_NAME$5} rejected the stored API key (${status}). Sign in again from Settings > Command Code.${detail ? ` ${detail}` : ""}`,
|
|
15000
|
+
if (isCommandCodeModelAccessDenied(status, detail)) throw new LlmError(`${PROVIDER_NAME$5} access denied for model ${options.model}: this model is not included in the plan or requires higher entitlement (${status}).${detail ? ` ${detail}` : ""}`, code, { status });
|
|
15001
|
+
if (isCommandCodeCredentialInvalid(status, detail) || status === 401) throw new LlmError(`${PROVIDER_NAME$5} rejected the stored API key (${status}). Sign in again from Settings > Command Code.${detail ? ` ${detail}` : ""}`, code, { status });
|
|
14456
15002
|
if (status === 429) {
|
|
14457
15003
|
const after = response === void 0 ? void 0 : retryAfterMs$1(response.headers);
|
|
14458
|
-
throw new LlmError(`${PROVIDER_NAME$5} rate limit or plan quota reached (429). Check the quota card in Settings > Command Code.${detail ? ` ${detail}` : ""}`,
|
|
15004
|
+
throw new LlmError(`${PROVIDER_NAME$5} rate limit or plan quota reached (429). Check the quota card in Settings > Command Code.${detail ? ` ${detail}` : ""}`, code, {
|
|
14459
15005
|
status: 429,
|
|
14460
15006
|
...after === void 0 ? {} : { providerRetryAfterMs: after }
|
|
14461
15007
|
});
|
|
14462
15008
|
}
|
|
14463
|
-
if (status >= 500) throw new LlmError(`${PROVIDER_NAME$5} upstream server error (${status}): ${detail || "No response"}`,
|
|
14464
|
-
throw new LlmError(`${PROVIDER_NAME$5} API error (${status}): ${detail || "No response"}`,
|
|
15009
|
+
if (status >= 500) throw new LlmError(`${PROVIDER_NAME$5} upstream server error (${status}): ${detail || "No response"}`, code, { status });
|
|
15010
|
+
throw new LlmError(`${PROVIDER_NAME$5} API error (${status}): ${detail || "No response"}`, code, { status });
|
|
14465
15011
|
}
|
|
14466
15012
|
if (response.body === null) throw new LlmError("Command Code returned an empty response body", "PROVIDER_ERROR");
|
|
14467
15013
|
const reader = response.body.getReader();
|
|
14468
15014
|
const decoder = new TextDecoder();
|
|
14469
15015
|
const state = createStreamState$5(wire);
|
|
14470
15016
|
let buffer = "";
|
|
15017
|
+
/**
|
|
15018
|
+
* Set the moment ANY chunk reaches the caller, and never cleared.
|
|
15019
|
+
*
|
|
15020
|
+
* Deliberately broader than `a text delta or a tool call`: a retry would repeat
|
|
15021
|
+
* a block-start the caller has already seen just as surely, and could re-issue
|
|
15022
|
+
* a tool call the agent has already run, so the conservative reading is the
|
|
15023
|
+
* one that cannot be wrong. It is what keeps the in-band reclassification
|
|
15024
|
+
* below pre-output only, and before that point a fresh request is still free.
|
|
15025
|
+
*/
|
|
15026
|
+
let outputStarted = false;
|
|
14471
15027
|
try {
|
|
14472
|
-
|
|
14473
|
-
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
for (const
|
|
14480
|
-
|
|
15028
|
+
try {
|
|
15029
|
+
while (true) {
|
|
15030
|
+
const { done, value } = await reader.read();
|
|
15031
|
+
if (done) break;
|
|
15032
|
+
buffer += decoder.decode(value, { stream: true });
|
|
15033
|
+
const lines = buffer.split("\n");
|
|
15034
|
+
buffer = lines.pop() ?? "";
|
|
15035
|
+
for (const line of lines) {
|
|
15036
|
+
for (const chunk of processLine$1(line, state, wire)) {
|
|
15037
|
+
outputStarted = true;
|
|
15038
|
+
yield chunk;
|
|
15039
|
+
}
|
|
15040
|
+
if (state.finished) return;
|
|
15041
|
+
}
|
|
15042
|
+
}
|
|
15043
|
+
buffer += decoder.decode();
|
|
15044
|
+
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processLine$1(line, state, wire)) {
|
|
15045
|
+
outputStarted = true;
|
|
15046
|
+
yield chunk;
|
|
15047
|
+
}
|
|
15048
|
+
if (state.finished) return;
|
|
15049
|
+
assertStreamComplete$4(state);
|
|
15050
|
+
for (const chunk of closeStream$4(state)) {
|
|
15051
|
+
outputStarted = true;
|
|
15052
|
+
yield chunk;
|
|
14481
15053
|
}
|
|
15054
|
+
} catch (error) {
|
|
15055
|
+
const inBand = state.streamError;
|
|
15056
|
+
if (error instanceof LlmError && !outputStarted && inBand !== void 0 && error.code === "PROVIDER_ERROR") {
|
|
15057
|
+
const reclassified = reclassifyInBandStreamError(error, inBand);
|
|
15058
|
+
if (reclassified.code === "RATE_LIMIT" && pool !== null && accountId !== void 0) {
|
|
15059
|
+
const after = retryAfterMs$1(response.headers);
|
|
15060
|
+
await pool.markCooldown(accountId, after ?? POOL_COOLDOWN_MS$4, `${PROVIDER_NAME$5} in-stream 429`).catch(() => void 0);
|
|
15061
|
+
}
|
|
15062
|
+
throw reclassified;
|
|
15063
|
+
}
|
|
15064
|
+
throw error;
|
|
14482
15065
|
}
|
|
14483
|
-
buffer += decoder.decode();
|
|
14484
|
-
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processLine$1(line, state, wire)) yield chunk;
|
|
14485
|
-
if (state.finished) return;
|
|
14486
|
-
assertStreamComplete$4(state);
|
|
14487
|
-
for (const chunk of closeStream$4(state)) yield chunk;
|
|
14488
15066
|
} finally {
|
|
14489
15067
|
reader.cancel().catch(() => void 0);
|
|
14490
15068
|
}
|
|
14491
15069
|
}
|
|
14492
15070
|
};
|
|
15071
|
+
/**
|
|
15072
|
+
* The error one recorded in-band failure becomes, or the mapper's own verdict.
|
|
15073
|
+
*
|
|
15074
|
+
* Two rules, because this line speaks three in-band vocabularies and they do
|
|
15075
|
+
* not carry the same evidence. The messages wire names a transient failure with
|
|
15076
|
+
* a `type` that STANDS FOR A STATUS - an `overloaded_error` is the 529 it would
|
|
15077
|
+
* have sent - so its envelope is answered by the status table. The
|
|
15078
|
+
* chat-completions and Responses wires have no such type: their evidence is a
|
|
15079
|
+
* `code`, a `type` in the looser `server_error` sense, or plain prose, and the
|
|
15080
|
+
* status-reading helper is what turns that into the question this line's own
|
|
15081
|
+
* classifier answers.
|
|
15082
|
+
*
|
|
15083
|
+
* The switch is exhaustive on purpose. A fourth vocabulary must be a compile
|
|
15084
|
+
* error here rather than a silent fall into the rule above, which is the one
|
|
15085
|
+
* shape of this bug that cannot be caught by a test.
|
|
15086
|
+
*/
|
|
15087
|
+
function reclassifyInBandStreamError(thrown, inBand) {
|
|
15088
|
+
switch (inBand.vocabulary) {
|
|
15089
|
+
case "anthropic": return reclassifyInBandError(thrown, inBand.error, classifyCommandCodeFailure);
|
|
15090
|
+
case "openai":
|
|
15091
|
+
case "responses": return reclassifyInBandResponsesError(thrown, inBand.error, inBand.message, classifyCommandCodeFailure);
|
|
15092
|
+
}
|
|
15093
|
+
}
|
|
14493
15094
|
/** Path suffix the chosen route answers on. */
|
|
14494
15095
|
function endpointPathFor(wire) {
|
|
14495
15096
|
if (wire === "anthropic") return "/messages";
|
|
@@ -14503,6 +15104,34 @@ function processLine$1(line, state, wire) {
|
|
|
14503
15104
|
}
|
|
14504
15105
|
//#endregion
|
|
14505
15106
|
//#region src/host/command-code/account-pool.ts
|
|
15107
|
+
/**
|
|
15108
|
+
* One cached snapshot as the shared account card renders it.
|
|
15109
|
+
*
|
|
15110
|
+
* A usage window states its own consumed share, so it maps straight across; a
|
|
15111
|
+
* window whose share nobody measured is dropped rather than drawn at 0, which
|
|
15112
|
+
* would claim an unspent window no reading supports.
|
|
15113
|
+
*
|
|
15114
|
+
* Exported for the pool's regression tests; the card only reaches it through
|
|
15115
|
+
* the summary hook.
|
|
15116
|
+
*/
|
|
15117
|
+
function commandCodePoolQuota(cached) {
|
|
15118
|
+
return poolQuota(cached.fetchedAt, cached.windows.map((window) => quotaWindow$1(window.label, window.usedPercent, {
|
|
15119
|
+
windowDurationMins: window.windowDurationMins,
|
|
15120
|
+
resetsAt: window.resetsAt
|
|
15121
|
+
})));
|
|
15122
|
+
}
|
|
15123
|
+
/**
|
|
15124
|
+
* This key's own newest snapshot, ready for the account card.
|
|
15125
|
+
*
|
|
15126
|
+
* Quota follows the account, so the pool reports what the line already
|
|
15127
|
+
* remembered for THIS account id. The lookup is a pure memory read: the card
|
|
15128
|
+
* builds its account list here, and that must never cost one upstream request
|
|
15129
|
+
* per pooled key.
|
|
15130
|
+
*/
|
|
15131
|
+
function quotaFor$1(accountId) {
|
|
15132
|
+
const cached = getCachedQuotaFor$3(accountId);
|
|
15133
|
+
return cached === void 0 ? void 0 : commandCodePoolQuota(cached);
|
|
15134
|
+
}
|
|
14506
15135
|
/** Encrypted pool file this line owns; the pre-pool credential file stays the mirror. */
|
|
14507
15136
|
function commandCodePoolPath() {
|
|
14508
15137
|
return path.join(dshHomeDir(), "storages", "command-code-pool.json");
|
|
@@ -14640,7 +15269,11 @@ var CommandCodeAccountPool = class extends AccountPoolCore {
|
|
|
14640
15269
|
...account.userName === void 0 ? {} : { userName: account.userName },
|
|
14641
15270
|
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
14642
15271
|
...account.planId === void 0 ? {} : { planId: account.planId },
|
|
14643
|
-
...account.userId === void 0 ? {} : { userId: account.userId }
|
|
15272
|
+
...account.userId === void 0 ? {} : { userId: account.userId },
|
|
15273
|
+
...(() => {
|
|
15274
|
+
const quota = quotaFor$1(account.id);
|
|
15275
|
+
return quota === void 0 ? {} : { quota };
|
|
15276
|
+
})()
|
|
14644
15277
|
};
|
|
14645
15278
|
},
|
|
14646
15279
|
emptyMessage: "Not signed in to Command Code. 请在「设置 → 订阅服务 → Command Code」中添加账号或粘贴 API Key。",
|
|
@@ -15195,7 +15828,7 @@ async function getCommandCodeWebStatus(store, modelSettings, preferences, option
|
|
|
15195
15828
|
const catalog = live.length > 0 ? live : fallbackCatalog$1();
|
|
15196
15829
|
const enabled = settings.enabled !== false;
|
|
15197
15830
|
const models = buildModelOptions$2(catalog, resolveEnabledModelIds$4(settings.enabledModelIds, catalog, enabled), settings.contextWindowOverrides);
|
|
15198
|
-
const quota = getCachedQuotaFor$
|
|
15831
|
+
const quota = getCachedQuotaFor$3(activeAccount?.id ?? null);
|
|
15199
15832
|
const activeAccountView = activePoolAccount === void 0 ? null : {
|
|
15200
15833
|
userId: activePoolAccount.userId ?? activePoolAccount.credentials.userId ?? null,
|
|
15201
15834
|
userName: activePoolAccount.userName ?? activePoolAccount.credentials.userName ?? null,
|
|
@@ -15268,7 +15901,7 @@ function registerCommandCodeRoutes(ctx, store, modelSettings, preferences, optio
|
|
|
15268
15901
|
if (method !== "GET") return sendMethodNotAllowed$5(response);
|
|
15269
15902
|
const credentials = await store.read();
|
|
15270
15903
|
const targetId = await activeAccountId();
|
|
15271
|
-
const cached = getCachedQuotaFor$
|
|
15904
|
+
const cached = getCachedQuotaFor$3(targetId);
|
|
15272
15905
|
if ((credentials !== null || targetId !== void 0) && (cached === void 0 || Date.now() - (cached.fetchedAt || 0) > 12e4)) {
|
|
15273
15906
|
const refresh = () => fetchAccountQuota$2(quotaStore, fetchFn, false, targetId);
|
|
15274
15907
|
if (cached === void 0) await quotaRefresh.run(refresh);
|
|
@@ -15541,6 +16174,24 @@ const DEFAULT_CONTEXT_WINDOW$4 = 128e3;
|
|
|
15541
16174
|
* ceiling rather than a claim about what the model can do.
|
|
15542
16175
|
*/
|
|
15543
16176
|
const DEFAULT_MAX_OUTPUT_TOKENS = 8192;
|
|
16177
|
+
/**
|
|
16178
|
+
* Raise a cap that is too small for this model's forced thinking.
|
|
16179
|
+
*
|
|
16180
|
+
* Left alone: a model whose thinking CAN be disabled - its caller may have asked
|
|
16181
|
+
* for thinking off on purpose, and raising the cap would not enable it while the
|
|
16182
|
+
* number read back would be misleading - and an absent cap, which
|
|
16183
|
+
* `DEFAULT_MAX_OUTPUT_TOKENS` already owns at a size nothing is short of.
|
|
16184
|
+
*
|
|
16185
|
+
* @param modelId - the routed model id.
|
|
16186
|
+
* @param requested - the caller's cap, undefined when it stated none.
|
|
16187
|
+
* @returns the cap to request, raised only when that is the difference between an
|
|
16188
|
+
* empty truncated answer and a usable one.
|
|
16189
|
+
*/
|
|
16190
|
+
function floorForcedThinkingTokens$1(modelId, requested) {
|
|
16191
|
+
if (requested === void 0) return void 0;
|
|
16192
|
+
if (thinkingModeForModel(modelId) !== "levels") return requested;
|
|
16193
|
+
return Math.max(requested, 512);
|
|
16194
|
+
}
|
|
15544
16195
|
/** How long a catalog sync may take before the cached list is used instead. */
|
|
15545
16196
|
const CATALOG_TIMEOUT_MS = 15e3;
|
|
15546
16197
|
/**
|
|
@@ -16586,6 +17237,23 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
16586
17237
|
let lastStatus;
|
|
16587
17238
|
let lastDetail = "";
|
|
16588
17239
|
let accountId;
|
|
17240
|
+
/**
|
|
17241
|
+
* Set the moment ANY chunk reaches the caller, and never cleared.
|
|
17242
|
+
*
|
|
17243
|
+
* Deliberately broader than "some text arrived": a block start or a tool
|
|
17244
|
+
* call the caller has already seen is exactly as visible as the text, so the
|
|
17245
|
+
* conservative reading is the one that cannot be wrong. It also survives the
|
|
17246
|
+
* pool rotation below, because output an account produced before failing is
|
|
17247
|
+
* still output.
|
|
17248
|
+
*/
|
|
17249
|
+
let outputStarted = false;
|
|
17250
|
+
/**
|
|
17251
|
+
* The code a transient in-band failure becomes, or null for this attempt.
|
|
17252
|
+
*
|
|
17253
|
+
* Assigned per failure rather than accumulated, so a later attempt that
|
|
17254
|
+
* failed for a reason of its own is never judged by an earlier one's wording.
|
|
17255
|
+
*/
|
|
17256
|
+
let lastInBandCode = null;
|
|
16589
17257
|
for (;;) {
|
|
16590
17258
|
let credentials;
|
|
16591
17259
|
if (pool === null) {
|
|
@@ -16613,6 +17281,7 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
16613
17281
|
if (event.type === "error") {
|
|
16614
17282
|
lastStatus = openedStatus;
|
|
16615
17283
|
lastDetail = event.message;
|
|
17284
|
+
lastInBandCode = outputStarted || !(openedStatus !== void 0 && openedStatus >= 200 && openedStatus < 300) ? null : inBandTransientCode(event.message);
|
|
16616
17285
|
failed = true;
|
|
16617
17286
|
break;
|
|
16618
17287
|
}
|
|
@@ -16621,12 +17290,18 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
16621
17290
|
continue;
|
|
16622
17291
|
}
|
|
16623
17292
|
if (event.type === "text" || event.type === "thinking" || event.type === "tool_call") {
|
|
16624
|
-
for (const chunk of applyEvent(state, event))
|
|
17293
|
+
for (const chunk of applyEvent(state, event)) {
|
|
17294
|
+
outputStarted = true;
|
|
17295
|
+
yield chunk;
|
|
17296
|
+
}
|
|
16625
17297
|
continue;
|
|
16626
17298
|
}
|
|
16627
17299
|
}
|
|
16628
17300
|
if (!failed) {
|
|
16629
|
-
for (const chunk of closeStream$3(state))
|
|
17301
|
+
for (const chunk of closeStream$3(state)) {
|
|
17302
|
+
outputStarted = true;
|
|
17303
|
+
yield chunk;
|
|
17304
|
+
}
|
|
16630
17305
|
if (pool !== null && accountId !== void 0 && spent !== null) await pool.recordUsage(accountId, spent.inputTokens, spent.outputTokens);
|
|
16631
17306
|
return;
|
|
16632
17307
|
}
|
|
@@ -16646,9 +17321,76 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
16646
17321
|
const status = lastStatus;
|
|
16647
17322
|
if (status === 401 || status === 403) throw new LlmError(`${PROVIDER_NAME$4} rejected the API key (${status}). Replace it from Settings > ${PROVIDER_NAME$4}.${lastDetail ? ` ${lastDetail}` : ""}`, "INVALID_CREDENTIAL", { status });
|
|
16648
17323
|
if (status === 429) throw new LlmError(`${PROVIDER_NAME$4} rate limit or plan quota reached (429). Add another key, or wait for the cooldown shown in Settings > ${PROVIDER_NAME$4}.${lastDetail ? ` ${lastDetail}` : ""}`, "RATE_LIMIT", { status: 429 });
|
|
16649
|
-
throw new LlmError(`${PROVIDER_NAME$4} request failed${status === void 0 ? "" : ` (${status})`}.${lastDetail}`, status !== void 0 && status >= 500 ? "SERVER" : "INVALID_REQUEST", status === void 0 ? {} : { status });
|
|
17324
|
+
throw new LlmError(`${PROVIDER_NAME$4} request failed${status === void 0 ? "" : ` (${status})`}.${lastDetail}`, lastInBandCode ?? (status !== void 0 && status >= 500 ? "SERVER" : "INVALID_REQUEST"), status === void 0 ? {} : { status });
|
|
16650
17325
|
}
|
|
16651
17326
|
};
|
|
17327
|
+
/**
|
|
17328
|
+
* The DSH code an in-band Ollama failure becomes, or null when the message names
|
|
17329
|
+
* nothing transient.
|
|
17330
|
+
*
|
|
17331
|
+
* Ollama reports a failure inside a 200 as `{"error": "<message>"}` - one string,
|
|
17332
|
+
* no type - so the shared helper's Anthropic table cannot apply and there is no
|
|
17333
|
+
* status to name either. The wording is the whole signal, which is the same
|
|
17334
|
+
* message heuristic the Codex line already reads on `response.failed` in
|
|
17335
|
+
* `responses-client.ts` (generalized for the Responses vocabulary by
|
|
17336
|
+
* `inBandResponsesCode` in `common/stream-error.ts`); it is kept local here
|
|
17337
|
+
* because these are Ollama's own phrasings, not a vocabulary lines share.
|
|
17338
|
+
*
|
|
17339
|
+
* Every phrase below names a condition Ollama reports that clears on its own: a
|
|
17340
|
+
* model still being pulled into memory, a runner that would not start, a server
|
|
17341
|
+
* with no slot for this request, an overloaded or throttled front door. That is
|
|
17342
|
+
* what makes a retry worth taking - the request did not change, so a later one
|
|
17343
|
+
* is genuinely a different bet.
|
|
17344
|
+
*
|
|
17345
|
+
* Deliberately narrow, because the cost of guessing is paid by the user in
|
|
17346
|
+
* backoff. A refusal the wording does not name is usually the service objecting
|
|
17347
|
+
* to the caller's message, and retrying repeats it verbatim: three identical
|
|
17348
|
+
* requests ending in the same verdict, with the real fault unreported. Falling
|
|
17349
|
+
* back to some invented status instead would be worse on both counts, filing an
|
|
17350
|
+
* unrecognized refusal as a retryable server error and inviting the pool chain
|
|
17351
|
+
* to cool an account down for something nothing observed.
|
|
17352
|
+
*/
|
|
17353
|
+
function inBandTransientCode(message) {
|
|
17354
|
+
const text = message.toLowerCase();
|
|
17355
|
+
if (IN_BAND_RATE_LIMIT.some((phrase) => text.includes(phrase))) return "RATE_LIMIT";
|
|
17356
|
+
if (IN_BAND_SERVER.some((phrase) => text.includes(phrase))) return "SERVER";
|
|
17357
|
+
return null;
|
|
17358
|
+
}
|
|
17359
|
+
/**
|
|
17360
|
+
* Throttling wording, read first because it is the narrower of the two verdicts.
|
|
17361
|
+
*
|
|
17362
|
+
* The same conditions this line already types RATE_LIMIT for when they arrive as
|
|
17363
|
+
* a 429; a gateway that reports one inside the stream is describing the same
|
|
17364
|
+
* state, and the harness backoff that 429 earns is what the turn should get.
|
|
17365
|
+
*/
|
|
17366
|
+
const IN_BAND_RATE_LIMIT = [
|
|
17367
|
+
"rate limit",
|
|
17368
|
+
"rate-limit",
|
|
17369
|
+
"rate_limit",
|
|
17370
|
+
"too many requests",
|
|
17371
|
+
"quota"
|
|
17372
|
+
];
|
|
17373
|
+
/**
|
|
17374
|
+
* Server-side conditions, in no particular order.
|
|
17375
|
+
*
|
|
17376
|
+
* The model-loading and runner wording covers the two failures Ollama raises while
|
|
17377
|
+
* it is still getting ready to serve - the request was well formed and the
|
|
17378
|
+
* service was not ready for it - and the rest is what its front door says when it
|
|
17379
|
+
* is saturated.
|
|
17380
|
+
*/
|
|
17381
|
+
const IN_BAND_SERVER = [
|
|
17382
|
+
"overload",
|
|
17383
|
+
"server busy",
|
|
17384
|
+
"model is loading",
|
|
17385
|
+
"loading model",
|
|
17386
|
+
"couldn't load",
|
|
17387
|
+
"could not load",
|
|
17388
|
+
"unable to load",
|
|
17389
|
+
"failed to load",
|
|
17390
|
+
"runner",
|
|
17391
|
+
"service unavailable",
|
|
17392
|
+
"internal server error"
|
|
17393
|
+
];
|
|
16652
17394
|
function isAbort$3(error, signal) {
|
|
16653
17395
|
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
16654
17396
|
}
|
|
@@ -16812,7 +17554,8 @@ async function toOllamaRequest(options, attachments, signal) {
|
|
|
16812
17554
|
model: options.model,
|
|
16813
17555
|
messages
|
|
16814
17556
|
};
|
|
16815
|
-
|
|
17557
|
+
const flooredMax = floorForcedThinkingTokens$1(options.model, options.maxTokens);
|
|
17558
|
+
if (flooredMax !== void 0) request.maxOutputTokens = flooredMax;
|
|
16816
17559
|
if (options.temperature !== void 0) request.temperature = options.temperature;
|
|
16817
17560
|
const think = thinkForModel(options.model, options.reasoningEffort);
|
|
16818
17561
|
if (think !== void 0) request.think = think;
|
|
@@ -18971,19 +19714,89 @@ function buildModelOptions$1(catalog, enabledModelIds, contextWindowOverrides) {
|
|
|
18971
19714
|
};
|
|
18972
19715
|
});
|
|
18973
19716
|
}
|
|
18974
|
-
|
|
18975
|
-
|
|
19717
|
+
/**
|
|
19718
|
+
* How many accounts' snapshots are remembered.
|
|
19719
|
+
*
|
|
19720
|
+
* Mirrors the bound the ChatGPT line's usage service applies to its own
|
|
19721
|
+
* per-account map: room for every account a user is likely to pool, and no way
|
|
19722
|
+
* for a long-lived process to accumulate one snapshot per account it has seen.
|
|
19723
|
+
*/
|
|
19724
|
+
const QUOTA_ACCOUNT_LIMIT$1 = 20;
|
|
19725
|
+
/** Key of a snapshot whose caller named no account: the single-credential read. */
|
|
19726
|
+
const UNNAMED_ACCOUNT_KEY = "";
|
|
19727
|
+
/**
|
|
19728
|
+
* One newest snapshot PER ACCOUNT, keyed by the account id the caller stated.
|
|
19729
|
+
*
|
|
19730
|
+
* Quota follows the account: rotation decides which account spends the next
|
|
19731
|
+
* request, so a single slot could only ever describe whichever account was read
|
|
19732
|
+
* last. The pool's card draws every account its own reading, so each account
|
|
19733
|
+
* keeps its own entry; past the cap the oldest read is evicted.
|
|
19734
|
+
*
|
|
19735
|
+
* This map is a memory read only. Nothing here fetches — the settings card
|
|
19736
|
+
* builds its account list from it, and that must never cost one upstream
|
|
19737
|
+
* request per pooled account.
|
|
19738
|
+
*/
|
|
19739
|
+
const quotaCache$1 = /* @__PURE__ */ new Map();
|
|
19740
|
+
/**
|
|
19741
|
+
* Key of the entry written most recently.
|
|
19742
|
+
*
|
|
19743
|
+
* The pre-pool card asks "whatever was fetched last"; the last WRITE answers
|
|
19744
|
+
* that exactly, where a scan by `fetchedAt` would break a tie between two
|
|
19745
|
+
* accounts read inside the same millisecond by map order.
|
|
19746
|
+
*/
|
|
19747
|
+
let quotaLastKey$1 = UNNAMED_ACCOUNT_KEY;
|
|
19748
|
+
function quotaCacheKeyFor(accountId) {
|
|
19749
|
+
return accountId ?? UNNAMED_ACCOUNT_KEY;
|
|
19750
|
+
}
|
|
19751
|
+
/** The entry read most recently, whichever account it belongs to. */
|
|
19752
|
+
function newestQuotaEntry$1() {
|
|
19753
|
+
return quotaCache$1.get(quotaLastKey$1);
|
|
19754
|
+
}
|
|
19755
|
+
/** Remember one account's newest snapshot, bounded to the accounts in use. */
|
|
19756
|
+
function rememberQuota$1(accountId, snapshot) {
|
|
19757
|
+
const key = quotaCacheKeyFor(accountId);
|
|
19758
|
+
quotaCache$1.set(key, snapshot);
|
|
19759
|
+
quotaLastKey$1 = key;
|
|
19760
|
+
while (quotaCache$1.size > QUOTA_ACCOUNT_LIMIT$1) {
|
|
19761
|
+
const oldest = [...quotaCache$1.entries()].sort((left, right) => left[1].fetchedAt - right[1].fetchedAt)[0];
|
|
19762
|
+
if (oldest === void 0) break;
|
|
19763
|
+
quotaCache$1.delete(oldest[0]);
|
|
19764
|
+
if (quotaLastKey$1 === oldest[0]) quotaLastKey$1 = UNNAMED_ACCOUNT_KEY;
|
|
19765
|
+
}
|
|
19766
|
+
}
|
|
19767
|
+
/**
|
|
19768
|
+
* The snapshot read most recently, whichever account it belongs to.
|
|
19769
|
+
*
|
|
19770
|
+
* Kept for the pre-pool single-credential card, which has no account id to ask
|
|
19771
|
+
* with. A pooled caller asks {@link getCachedQuotaFor} instead.
|
|
19772
|
+
*/
|
|
18976
19773
|
function getCachedQuota$3() {
|
|
18977
|
-
return
|
|
19774
|
+
return newestQuotaEntry$1() ?? null;
|
|
18978
19775
|
}
|
|
18979
|
-
/**
|
|
18980
|
-
|
|
18981
|
-
|
|
18982
|
-
|
|
19776
|
+
/**
|
|
19777
|
+
* The cached snapshot only when it belongs to this account.
|
|
19778
|
+
*
|
|
19779
|
+
* Passing no account keeps the pre-pool behavior of returning whatever was
|
|
19780
|
+
* fetched last. Passing one answers that account's own newest snapshot, or null
|
|
19781
|
+
* when this line has never read it — null is "never read", which is a different
|
|
19782
|
+
* statement from a snapshot whose windows are all at zero.
|
|
19783
|
+
*/
|
|
19784
|
+
function getCachedQuotaFor$2(accountId) {
|
|
19785
|
+
if (accountId === void 0 || accountId === null) return newestQuotaEntry$1() ?? null;
|
|
19786
|
+
return cachedQuotaFor$1(accountId);
|
|
19787
|
+
}
|
|
19788
|
+
/**
|
|
19789
|
+
* The newest snapshot held for one account, for display only.
|
|
19790
|
+
*
|
|
19791
|
+
* The pool asks here while it builds the account list for the settings card, so
|
|
19792
|
+
* this is a pure memory read: it never fetches.
|
|
19793
|
+
*/
|
|
19794
|
+
function cachedQuotaFor$1(accountId) {
|
|
19795
|
+
return quotaCache$1.get(accountId) ?? null;
|
|
18983
19796
|
}
|
|
18984
19797
|
function clearCachedQuota$3() {
|
|
18985
|
-
quotaCache$1
|
|
18986
|
-
|
|
19798
|
+
quotaCache$1.clear();
|
|
19799
|
+
quotaLastKey$1 = UNNAMED_ACCOUNT_KEY;
|
|
18987
19800
|
}
|
|
18988
19801
|
/**
|
|
18989
19802
|
* Display name and nominal length for each window key the service reports.
|
|
@@ -19230,8 +20043,8 @@ function parsePlanLevel(payload) {
|
|
|
19230
20043
|
* account's real usage.
|
|
19231
20044
|
*/
|
|
19232
20045
|
async function fetchAccountQuota$3(store, options = {}) {
|
|
19233
|
-
const
|
|
19234
|
-
if (options.force !== true &&
|
|
20046
|
+
const cached = quotaCache$1.get(quotaCacheKeyFor(options.accountId));
|
|
20047
|
+
if (options.force !== true && cached !== void 0 && Date.now() - cached.fetchedAt < 12e4) return cached;
|
|
19235
20048
|
const fetchFn = options.fetchFn ?? fetch;
|
|
19236
20049
|
const pooled = options.credentials !== void 0;
|
|
19237
20050
|
let credentials;
|
|
@@ -19280,8 +20093,7 @@ async function fetchAccountQuota$3(store, options = {}) {
|
|
|
19280
20093
|
fetchedAt: Date.now(),
|
|
19281
20094
|
sources: [openAIUrl(USAGES_PATH, credentials.region)]
|
|
19282
20095
|
};
|
|
19283
|
-
|
|
19284
|
-
quotaAccountId = options.accountId;
|
|
20096
|
+
rememberQuota$1(options.accountId, snapshot);
|
|
19285
20097
|
return snapshot;
|
|
19286
20098
|
}
|
|
19287
20099
|
/**
|
|
@@ -21104,7 +21916,15 @@ function processOpenAIStreamLine(line, state) {
|
|
|
21104
21916
|
if (!isRecord$17(chunk)) return [];
|
|
21105
21917
|
const out = [];
|
|
21106
21918
|
const errorPayload = isRecord$17(chunk.error) ? chunk.error : void 0;
|
|
21107
|
-
if (errorPayload !== void 0)
|
|
21919
|
+
if (errorPayload !== void 0) {
|
|
21920
|
+
const message = asString$9(errorPayload.message) ?? "unknown error";
|
|
21921
|
+
state.streamError = {
|
|
21922
|
+
vocabulary: "openai",
|
|
21923
|
+
error: errorPayload,
|
|
21924
|
+
message
|
|
21925
|
+
};
|
|
21926
|
+
throw new LlmError$1(`Kimi Code stream error: ${message}`, isContextOverflow(errorPayload) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
21927
|
+
}
|
|
21108
21928
|
const usage = isRecord$17(chunk.usage) ? chunk.usage : void 0;
|
|
21109
21929
|
if (usage) {
|
|
21110
21930
|
state.sawUsage = true;
|
|
@@ -21373,7 +22193,13 @@ function processAnthropicStreamLine(line, state) {
|
|
|
21373
22193
|
}
|
|
21374
22194
|
if (type === "error") {
|
|
21375
22195
|
const error = isRecord$17(event.error) ? event.error : {};
|
|
21376
|
-
|
|
22196
|
+
const message = asString$9(error.message) ?? "unknown error";
|
|
22197
|
+
state.streamError = {
|
|
22198
|
+
vocabulary: "anthropic",
|
|
22199
|
+
error,
|
|
22200
|
+
message
|
|
22201
|
+
};
|
|
22202
|
+
throw new LlmError$1(`Kimi Code stream error: ${message}`, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
21377
22203
|
}
|
|
21378
22204
|
return out;
|
|
21379
22205
|
}
|
|
@@ -22014,27 +22840,98 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
22014
22840
|
const state = createStreamState$3(wire, requestOptions.sessionId);
|
|
22015
22841
|
markSessionActive(requestOptions.sessionId);
|
|
22016
22842
|
let buffer = "";
|
|
22843
|
+
/**
|
|
22844
|
+
* Set the moment ANY chunk reaches the caller, and never cleared.
|
|
22845
|
+
*
|
|
22846
|
+
* Deliberately broader than "text arrived": a block-start the caller has
|
|
22847
|
+
* already seen repeats just as badly as text it has already read, so the
|
|
22848
|
+
* conservative reading is the one that cannot be wrong. It is what keeps the
|
|
22849
|
+
* in-band reclassification below pre-output only.
|
|
22850
|
+
*/
|
|
22851
|
+
let outputStarted = false;
|
|
22017
22852
|
try {
|
|
22018
|
-
|
|
22019
|
-
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22023
|
-
|
|
22024
|
-
|
|
22025
|
-
for (const
|
|
22026
|
-
|
|
22853
|
+
try {
|
|
22854
|
+
while (true) {
|
|
22855
|
+
const { done, value } = await reader.read();
|
|
22856
|
+
if (done) break;
|
|
22857
|
+
buffer += decoder.decode(value, { stream: true });
|
|
22858
|
+
const lines = buffer.split("\n");
|
|
22859
|
+
buffer = lines.pop() ?? "";
|
|
22860
|
+
for (const line of lines) {
|
|
22861
|
+
for (const chunk of processLine(line, state, wire)) {
|
|
22862
|
+
outputStarted = true;
|
|
22863
|
+
yield chunk;
|
|
22864
|
+
}
|
|
22865
|
+
if (state.finished) return;
|
|
22866
|
+
}
|
|
22867
|
+
}
|
|
22868
|
+
buffer += decoder.decode();
|
|
22869
|
+
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processLine(line, state, wire)) {
|
|
22870
|
+
outputStarted = true;
|
|
22871
|
+
yield chunk;
|
|
22027
22872
|
}
|
|
22873
|
+
if (state.finished) return;
|
|
22874
|
+
assertStreamComplete$3(state);
|
|
22875
|
+
for (const chunk of closeStream$2(state, servedByAccountId)) {
|
|
22876
|
+
outputStarted = true;
|
|
22877
|
+
yield chunk;
|
|
22878
|
+
}
|
|
22879
|
+
} catch (error) {
|
|
22880
|
+
const inBand = state.streamError;
|
|
22881
|
+
if (error instanceof LlmError && !outputStarted && error.code === "PROVIDER_ERROR" && inBand !== void 0) throw await this.inBandStreamFailure(error, inBand, servedByAccountId);
|
|
22882
|
+
throw error;
|
|
22028
22883
|
}
|
|
22029
|
-
buffer += decoder.decode();
|
|
22030
|
-
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processLine(line, state, wire)) yield chunk;
|
|
22031
|
-
if (state.finished) return;
|
|
22032
|
-
assertStreamComplete$3(state);
|
|
22033
|
-
for (const chunk of closeStream$2(state, servedByAccountId)) yield chunk;
|
|
22034
22884
|
} finally {
|
|
22035
22885
|
reader.cancel().catch(() => void 0);
|
|
22036
22886
|
}
|
|
22037
22887
|
}
|
|
22888
|
+
/**
|
|
22889
|
+
* What one in-band failure becomes once the pool has been told what the
|
|
22890
|
+
* verdict means for it.
|
|
22891
|
+
*
|
|
22892
|
+
* Reclassification and the pool reaction are separate because they answer
|
|
22893
|
+
* different questions about the same classification: the code decides whether
|
|
22894
|
+
* the harness retries, `accountScoped` decides whether a retry could land
|
|
22895
|
+
* somewhere that can answer. The mapper can supply only the first, which is
|
|
22896
|
+
* why the wire object is recorded there and read here.
|
|
22897
|
+
*
|
|
22898
|
+
* The body is the one a non-2xx response would have carried, so every pattern
|
|
22899
|
+
* below reads exactly what the HTTP branch reads and a limit cannot mean one
|
|
22900
|
+
* thing as a status and another as an event. The request itself is NOT
|
|
22901
|
+
* re-issued: its body is already open and part-read, and the harness retry
|
|
22902
|
+
* policy owns the repeat.
|
|
22903
|
+
*/
|
|
22904
|
+
async inBandStreamFailure(thrown, inBand, accountId) {
|
|
22905
|
+
const bodyText = JSON.stringify({ error: inBand.error });
|
|
22906
|
+
const reclassified = inBand.vocabulary === "anthropic" ? reclassifyInBandError(thrown, inBand.error, classifyKimiFailure) : reclassifyInBandResponsesError(thrown, inBand.error, inBand.message, classifyKimiFailure);
|
|
22907
|
+
const pool = this.accountPool;
|
|
22908
|
+
if (pool === null || accountId === void 0) return reclassified;
|
|
22909
|
+
if (inBand.vocabulary === "anthropic") {
|
|
22910
|
+
const status = inBandAnthropicStatus(inBand.error);
|
|
22911
|
+
if (status === null) return reclassified;
|
|
22912
|
+
const failure = classifyKimiFailure(status, bodyText);
|
|
22913
|
+
const planScoped = status === 429 && matchesAny$1(bodyText, ENTITLEMENT_PATTERNS);
|
|
22914
|
+
if (status === 429 && !planScoped) await this.coolInBandRateLimit(pool, accountId, failure.accountScoped, bodyText);
|
|
22915
|
+
else if (failure.code === "INVALID_CREDENTIAL") await pool.markAuthFailed(accountId, failure.message).catch(() => void 0);
|
|
22916
|
+
else if (failure.accountScoped) await pool.markCooldown(accountId, accountLimitCooldownMs(bodyText), `${PROVIDER_NAME$3} in-stream 403`).catch(() => void 0);
|
|
22917
|
+
return reclassified;
|
|
22918
|
+
}
|
|
22919
|
+
if (reclassified.code === "RATE_LIMIT" && !matchesAny$1(bodyText, ENTITLEMENT_PATTERNS)) await this.coolInBandRateLimit(pool, accountId, classifyKimiFailure(429, bodyText).accountScoped, bodyText);
|
|
22920
|
+
return reclassified;
|
|
22921
|
+
}
|
|
22922
|
+
/**
|
|
22923
|
+
* Cool the account whose own rate limit the stream reported.
|
|
22924
|
+
*
|
|
22925
|
+
* The window this route already defaults to, never a Retry-After: there is no
|
|
22926
|
+
* 429 response to read one from, and a delay stated on the 200 belongs to a
|
|
22927
|
+
* different message. An account-scoped verdict takes its own window instead,
|
|
22928
|
+
* because a spent balance is not back-pressure and 15 minutes would put the
|
|
22929
|
+
* account back into rotation four times inside the window that refused it.
|
|
22930
|
+
*/
|
|
22931
|
+
async coolInBandRateLimit(pool, accountId, accountScoped, bodyText) {
|
|
22932
|
+
const cooldownMs = accountScoped ? accountLimitCooldownMs(bodyText) : POOL_COOLDOWN_MS$2;
|
|
22933
|
+
await pool.markCooldown(accountId, cooldownMs, `${PROVIDER_NAME$3} in-stream 429`).catch(() => void 0);
|
|
22934
|
+
}
|
|
22038
22935
|
};
|
|
22039
22936
|
function processLine(line, state, wire) {
|
|
22040
22937
|
return wire === "anthropic" ? processAnthropicStreamLine(line, state) : processOpenAIStreamLine(line, state);
|
|
@@ -22097,6 +22994,34 @@ function parseKimiCodePoolData(value) {
|
|
|
22097
22994
|
return result;
|
|
22098
22995
|
}
|
|
22099
22996
|
/**
|
|
22997
|
+
* One account's own newest quota snapshot, in the shape the account card draws.
|
|
22998
|
+
*
|
|
22999
|
+
* The snapshot comes from 'client.ts''s per-account cache keyed by the pool
|
|
23000
|
+
* account id, and this is where this line's own numbers are translated into the
|
|
23001
|
+
* shared shape. Every judgement below trades a possibly-nice-looking bar for an
|
|
23002
|
+
* honest one:
|
|
23003
|
+
*
|
|
23004
|
+
* - a window whose consumed share nobody measured is DROPPED (`quotaWindow`
|
|
23005
|
+
* returns null), because "0% used" and "not stated" are different claims. This
|
|
23006
|
+
* parser only mints a window once it has a ratio, so a window that states none
|
|
23007
|
+
* never reaches here at all — the drop is the second line of defence;
|
|
23008
|
+
* - `resetsAt` is already Unix MILLISECONDS on this line, so it is passed
|
|
23009
|
+
* through and normalized rather than multiplied;
|
|
23010
|
+
* - the read time is the snapshot's own `fetchedAt`, which this line always
|
|
23011
|
+
* sets. A snapshot with no read time is not a snapshot, and `poolQuota`
|
|
23012
|
+
* reports that as undefined.
|
|
23013
|
+
*
|
|
23014
|
+
* A null snapshot means "this account was never read", which the card renders as
|
|
23015
|
+
* exactly that instead of drawing a zero.
|
|
23016
|
+
*/
|
|
23017
|
+
function kimiCodePoolQuota(snapshot) {
|
|
23018
|
+
if (snapshot === null) return void 0;
|
|
23019
|
+
return poolQuota(snapshot.fetchedAt, snapshot.windows.map((window) => quotaWindow$1(window.label, window.usedPercent, {
|
|
23020
|
+
windowDurationMins: window.windowDurationMins,
|
|
23021
|
+
resetsAt: window.resetsAt
|
|
23022
|
+
})));
|
|
23023
|
+
}
|
|
23024
|
+
/**
|
|
22100
23025
|
* Kimi Code's account pool.
|
|
22101
23026
|
*
|
|
22102
23027
|
* The credential is an OAuth pair whose refresh token rotates, so the pool owns
|
|
@@ -22175,6 +23100,7 @@ var KimiCodeAccountPool = class extends AccountPoolCore {
|
|
|
22175
23100
|
const nickname = account.nickname ?? account.credentials.nickname;
|
|
22176
23101
|
const planName = account.planName ?? account.credentials.planName;
|
|
22177
23102
|
const region = account.region ?? account.credentials.region;
|
|
23103
|
+
const quota = kimiCodePoolQuota(cachedQuotaFor$1(account.id));
|
|
22178
23104
|
return {
|
|
22179
23105
|
...base,
|
|
22180
23106
|
...email === void 0 ? {} : { email },
|
|
@@ -22184,7 +23110,8 @@ var KimiCodeAccountPool = class extends AccountPoolCore {
|
|
|
22184
23110
|
planLabel: planName
|
|
22185
23111
|
},
|
|
22186
23112
|
...region === void 0 ? {} : { region },
|
|
22187
|
-
...account.userId === void 0 ? {} : { userId: account.userId }
|
|
23113
|
+
...account.userId === void 0 ? {} : { userId: account.userId },
|
|
23114
|
+
...quota === void 0 ? {} : { quota }
|
|
22188
23115
|
};
|
|
22189
23116
|
},
|
|
22190
23117
|
emptyMessage: "Not signed in to Kimi Code. Sign in from Settings > Kimi Code.",
|
|
@@ -22298,7 +23225,7 @@ async function getKimiCodeWebStatus(store, modelSettings, preferences, options =
|
|
|
22298
23225
|
const catalog = live.length > 0 ? live : fallbackCatalog();
|
|
22299
23226
|
const enabled = settings.enabled !== false;
|
|
22300
23227
|
const models = buildModelOptions$1(catalog, resolveEnabledModelIds$3(settings.enabledModelIds, catalog, enabled), settings.contextWindowOverrides);
|
|
22301
|
-
const quota = getCachedQuotaFor(activeAccount?.id ?? null);
|
|
23228
|
+
const quota = getCachedQuotaFor$2(activeAccount?.id ?? null);
|
|
22302
23229
|
const account = quota?.account ?? (active === null || active === void 0 ? null : accountFromCredentials$2(active));
|
|
22303
23230
|
return {
|
|
22304
23231
|
enabled,
|
|
@@ -22432,7 +23359,7 @@ function registerKimiCodeRoutes(ctx, store, modelSettings, preferences, options
|
|
|
22432
23359
|
if (path === "" || path === "status") {
|
|
22433
23360
|
if (method !== "GET") return sendMethodNotAllowed$3(response);
|
|
22434
23361
|
const poolActive = await activeAccount();
|
|
22435
|
-
const cached = getCachedQuotaFor(poolActive.id ?? null);
|
|
23362
|
+
const cached = getCachedQuotaFor$2(poolActive.id ?? null);
|
|
22436
23363
|
if (poolActive.credentials !== void 0 && (cached === null || Date.now() - (cached.fetchedAt || 0) > 12e4)) {
|
|
22437
23364
|
const refresh = async () => {
|
|
22438
23365
|
const credentials = await effectiveCredentials(poolActive);
|
|
@@ -23997,6 +24924,26 @@ function outputConfigFor(modelId, requestedEffort) {
|
|
|
23997
24924
|
const effort = effortForModel(modelId, requestedEffort ?? null);
|
|
23998
24925
|
return effort === "default" ? void 0 : { effort };
|
|
23999
24926
|
}
|
|
24927
|
+
/**
|
|
24928
|
+
* Raise a cap that is too small for this line's forced thinking.
|
|
24929
|
+
*
|
|
24930
|
+
* Left alone: a model that can disable thinking (its caller may have asked for
|
|
24931
|
+
* thinking off on purpose, and raising the cap would not enable it but a caller
|
|
24932
|
+
* reading the number back would be misled), and an absent cap (the caller stated
|
|
24933
|
+
* none, so \`maxOutputTokensFor\` owns the ceiling).
|
|
24934
|
+
*
|
|
24935
|
+
* @param modelId - the routed model id.
|
|
24936
|
+
* @param requested - the caller's cap, undefined when it stated none.
|
|
24937
|
+
* @returns the cap to request, raised only when that is the difference between an
|
|
24938
|
+
* empty truncated answer and a usable one.
|
|
24939
|
+
*/
|
|
24940
|
+
function floorForcedThinkingTokens(modelId, requested) {
|
|
24941
|
+
if (requested === void 0) return void 0;
|
|
24942
|
+
const model = catalogEntry(modelId);
|
|
24943
|
+
const thinking = model?.thinking;
|
|
24944
|
+
if (thinking !== "always-on" && thinking !== "forced-effort") return requested;
|
|
24945
|
+
return Math.min(Math.max(requested, 512), Math.max(requested, model?.maxTokens ?? requested));
|
|
24946
|
+
}
|
|
24000
24947
|
/** Output cap one request asks for, tracked against the model's declared ceiling. */
|
|
24001
24948
|
function maxOutputTokensFor$1(modelId, contextWindow) {
|
|
24002
24949
|
const model = catalogEntry(modelId);
|
|
@@ -24515,6 +25462,7 @@ function processMinimaxStreamLine(line, state) {
|
|
|
24515
25462
|
}
|
|
24516
25463
|
if (type === "error") {
|
|
24517
25464
|
const error = isRecord$16(event.error) ? event.error : {};
|
|
25465
|
+
state.streamError = error;
|
|
24518
25466
|
throw new LlmError$1("MiniMax Code(编程订阅) stream error: " + (asString$8(error.message) ?? "unknown error"), isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
24519
25467
|
}
|
|
24520
25468
|
return out;
|
|
@@ -26254,29 +27202,75 @@ const QUOTA_FAILURE_CACHE_MS = 10 * 6e4;
|
|
|
26254
27202
|
const QUOTA_TIMEOUT_MS = 8e3;
|
|
26255
27203
|
let quotaHostInForce = null;
|
|
26256
27204
|
/**
|
|
26257
|
-
*
|
|
27205
|
+
* How many accounts' snapshots are remembered.
|
|
26258
27206
|
*
|
|
26259
|
-
*
|
|
26260
|
-
*
|
|
26261
|
-
*
|
|
26262
|
-
*
|
|
26263
|
-
|
|
27207
|
+
* Bounded like the ChatGPT line's own per-account map: the pool accepts a
|
|
27208
|
+
* handful of accounts, and a long-lived process must not accumulate one entry
|
|
27209
|
+
* per id it has ever seen. The oldest WRITE is evicted, which is also the read
|
|
27210
|
+
* least likely to be looked at again.
|
|
27211
|
+
*/
|
|
27212
|
+
const QUOTA_SNAPSHOT_ACCOUNT_LIMIT = 20;
|
|
27213
|
+
/**
|
|
27214
|
+
* The newest read, whichever account it belonged to.
|
|
27215
|
+
*
|
|
27216
|
+
* Kept beside the per-account map because the page-level block and the
|
|
27217
|
+
* "is a read even worth starting" test are about the line's latest reading,
|
|
27218
|
+
* not about one account.
|
|
26264
27219
|
*/
|
|
26265
27220
|
let quotaSnapshot = null;
|
|
26266
|
-
/**
|
|
26267
|
-
|
|
27221
|
+
/** Per-account snapshots, so an account is never shown the numbers read for another. */
|
|
27222
|
+
const quotaByAccount = /* @__PURE__ */ new Map();
|
|
26268
27223
|
/** How long a refused credential is remembered before asking again. */
|
|
26269
27224
|
const QUOTA_REJECTED_CACHE_MS = 30 * 6e4;
|
|
26270
27225
|
/** Forget the cached usage snapshot (a sign-out, a sign-in, a test). */
|
|
26271
27226
|
function clearCachedQuota$4() {
|
|
26272
27227
|
quotaSnapshot = null;
|
|
26273
|
-
|
|
27228
|
+
quotaByAccount.clear();
|
|
26274
27229
|
quotaHostInForce = null;
|
|
26275
27230
|
}
|
|
26276
27231
|
/** The last usage snapshot without touching the network. */
|
|
26277
27232
|
function getCachedQuota$4() {
|
|
26278
27233
|
return quotaSnapshot?.value ?? null;
|
|
26279
27234
|
}
|
|
27235
|
+
/**
|
|
27236
|
+
* The cached snapshot only when it belongs to this account, without touching the
|
|
27237
|
+
* network.
|
|
27238
|
+
*
|
|
27239
|
+
* Quota follows the account: a rotation decides whose credential is spent next,
|
|
27240
|
+
* so the snapshot a read produced describes the row it was made for. The pool
|
|
27241
|
+
* asks here while it builds the settings card's account list, which must never
|
|
27242
|
+
* wait on — or trigger — an upstream read.
|
|
27243
|
+
*
|
|
27244
|
+
* An absent, empty or null id keeps the pre-pool behaviour and answers with the
|
|
27245
|
+
* newest read, which is what the page-level block wants.
|
|
27246
|
+
*/
|
|
27247
|
+
function getCachedQuotaFor$1(accountId) {
|
|
27248
|
+
if (accountId === void 0 || accountId === null || accountId === "") return getCachedQuota$4();
|
|
27249
|
+
return quotaByAccount.get(accountId)?.value ?? null;
|
|
27250
|
+
}
|
|
27251
|
+
/** The entry a read for this account should consult, when there is one. */
|
|
27252
|
+
function quotaEntryFor(accountId) {
|
|
27253
|
+
if (accountId === void 0 || accountId === "") return quotaSnapshot;
|
|
27254
|
+
return quotaByAccount.get(accountId) ?? null;
|
|
27255
|
+
}
|
|
27256
|
+
/**
|
|
27257
|
+
* Remember one read against the account it was made for.
|
|
27258
|
+
*
|
|
27259
|
+
* Re-inserting an id moves it to the newest position, so the map's first key is
|
|
27260
|
+
* always the least recently written entry and eviction is a single delete.
|
|
27261
|
+
*/
|
|
27262
|
+
function rememberQuotaSnapshot(entry) {
|
|
27263
|
+
quotaSnapshot = entry;
|
|
27264
|
+
const accountId = entry.accountId;
|
|
27265
|
+
if (accountId === void 0 || accountId === "") return;
|
|
27266
|
+
quotaByAccount.delete(accountId);
|
|
27267
|
+
quotaByAccount.set(accountId, entry);
|
|
27268
|
+
while (quotaByAccount.size > QUOTA_SNAPSHOT_ACCOUNT_LIMIT) {
|
|
27269
|
+
const oldest = quotaByAccount.keys().next().value;
|
|
27270
|
+
if (oldest === void 0) break;
|
|
27271
|
+
quotaByAccount.delete(oldest);
|
|
27272
|
+
}
|
|
27273
|
+
}
|
|
26280
27274
|
/** Why the last read produced no snapshot, or null when it produced one. */
|
|
26281
27275
|
function getQuotaUnavailable() {
|
|
26282
27276
|
return quotaSnapshot?.reason ?? null;
|
|
@@ -26398,9 +27392,10 @@ function parseTokenPlanQuota(payload, now = Date.now()) {
|
|
|
26398
27392
|
async function fetchTokenPlanQuota(credentials, options = {}) {
|
|
26399
27393
|
const fetchFn = options.fetchFn ?? fetch;
|
|
26400
27394
|
const now = Date.now();
|
|
26401
|
-
|
|
26402
|
-
|
|
26403
|
-
|
|
27395
|
+
const cached = quotaEntryFor(options.accountId);
|
|
27396
|
+
if (options.force !== true && cached !== null) {
|
|
27397
|
+
const ttl = cached.reason === "credential-not-accepted" ? QUOTA_REJECTED_CACHE_MS : cached.reason === "token-expired" && cached.value === null ? QUOTA_FAILURE_CACHE_MS : cached.value === null ? QUOTA_FAILURE_CACHE_MS : QUOTA_CACHE_MS;
|
|
27398
|
+
if (now - cached.at < ttl) return cached.value;
|
|
26404
27399
|
}
|
|
26405
27400
|
const candidates = quotaHostCandidates(credentials.region);
|
|
26406
27401
|
const ordered = quotaHostInForce === null ? candidates : [quotaHostInForce, ...candidates.filter((host) => host !== quotaHostInForce)];
|
|
@@ -26440,19 +27435,19 @@ async function fetchTokenPlanQuota(credentials, options = {}) {
|
|
|
26440
27435
|
});
|
|
26441
27436
|
}
|
|
26442
27437
|
if (value === null && reason === null) reason = authRefused ? "token-expired" : "unreachable";
|
|
26443
|
-
if (value
|
|
26444
|
-
|
|
26445
|
-
|
|
26446
|
-
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
reason = "stale";
|
|
27438
|
+
if (value === null) {
|
|
27439
|
+
const previous = quotaEntryFor(options.accountId);
|
|
27440
|
+
if (previous !== null && previous.value !== null) {
|
|
27441
|
+
value = previous.value;
|
|
27442
|
+
reason = "stale";
|
|
27443
|
+
}
|
|
26450
27444
|
}
|
|
26451
|
-
|
|
27445
|
+
rememberQuotaSnapshot({
|
|
27446
|
+
accountId: options.accountId,
|
|
26452
27447
|
at: Date.now(),
|
|
26453
27448
|
value,
|
|
26454
27449
|
reason
|
|
26455
|
-
};
|
|
27450
|
+
});
|
|
26456
27451
|
return value;
|
|
26457
27452
|
}
|
|
26458
27453
|
/**
|
|
@@ -27175,7 +28170,7 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
27175
28170
|
}
|
|
27176
28171
|
let authOwner = computeMinimaxAuthOwner(credentials, accountId);
|
|
27177
28172
|
const withUploads = await uploadOversizedMedia(requestOptions, images, videos, credentials, accountId, this.options.fetchFn ?? fetch, signal);
|
|
27178
|
-
const effectiveMax = clampOutputToContext(requestOptions.maxTokens ?? maxOutputTokensFor$1(options.model, contextWindow), contextWindow, estimatedInputTokens(requestOptions));
|
|
28173
|
+
const effectiveMax = clampOutputToContext(floorForcedThinkingTokens(options.model, requestOptions.maxTokens) ?? maxOutputTokensFor$1(options.model, contextWindow), contextWindow, estimatedInputTokens(requestOptions));
|
|
27179
28174
|
const buildBodyForOwner = (owner) => {
|
|
27180
28175
|
return assertRequestBodyFits(buildMinimaxRequest({
|
|
27181
28176
|
...withUploads,
|
|
@@ -27235,23 +28230,46 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
27235
28230
|
provider: PROVIDER_ID$2
|
|
27236
28231
|
});
|
|
27237
28232
|
let buffer = "";
|
|
28233
|
+
/**
|
|
28234
|
+
* Set the moment ANY chunk reaches the caller, and never cleared.
|
|
28235
|
+
*
|
|
28236
|
+
* Deliberately broader than "a content delta or a tool block": a retry would
|
|
28237
|
+
* duplicate a block-start the caller has already seen just as surely, so the
|
|
28238
|
+
* conservative reading is the one that cannot be wrong. It is what keeps the
|
|
28239
|
+
* in-band reclassification below pre-output only.
|
|
28240
|
+
*/
|
|
28241
|
+
let outputStarted = false;
|
|
27238
28242
|
try {
|
|
27239
|
-
|
|
27240
|
-
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
|
|
27246
|
-
for (const
|
|
27247
|
-
|
|
28243
|
+
try {
|
|
28244
|
+
while (true) {
|
|
28245
|
+
const { done, value } = await reader.read();
|
|
28246
|
+
if (done) break;
|
|
28247
|
+
buffer += decoder.decode(value, { stream: true });
|
|
28248
|
+
const lines = buffer.split("\n");
|
|
28249
|
+
buffer = lines.pop() ?? "";
|
|
28250
|
+
for (const line of lines) {
|
|
28251
|
+
for (const chunk of processMinimaxStreamLine(line, state)) {
|
|
28252
|
+
outputStarted = true;
|
|
28253
|
+
yield chunk;
|
|
28254
|
+
}
|
|
28255
|
+
if (state.finished) return;
|
|
28256
|
+
}
|
|
27248
28257
|
}
|
|
28258
|
+
buffer += decoder.decode();
|
|
28259
|
+
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processMinimaxStreamLine(line, state)) {
|
|
28260
|
+
outputStarted = true;
|
|
28261
|
+
yield chunk;
|
|
28262
|
+
}
|
|
28263
|
+
if (state.finished) return;
|
|
28264
|
+
assertStreamComplete(state);
|
|
28265
|
+
for (const chunk of closeMinimaxStream(state)) {
|
|
28266
|
+
outputStarted = true;
|
|
28267
|
+
yield chunk;
|
|
28268
|
+
}
|
|
28269
|
+
} catch (error) {
|
|
28270
|
+
if (error instanceof LlmError && !outputStarted && error.code === "PROVIDER_ERROR" && state.streamError !== void 0) throw reclassifyInBandError(error, state.streamError, classifyMinimaxFailure);
|
|
28271
|
+
throw error;
|
|
27249
28272
|
}
|
|
27250
|
-
buffer += decoder.decode();
|
|
27251
|
-
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processMinimaxStreamLine(line, state)) yield chunk;
|
|
27252
|
-
if (state.finished) return;
|
|
27253
|
-
assertStreamComplete(state);
|
|
27254
|
-
for (const chunk of closeMinimaxStream(state)) yield chunk;
|
|
27255
28273
|
} finally {
|
|
27256
28274
|
reader.cancel().catch(() => void 0);
|
|
27257
28275
|
}
|
|
@@ -27467,6 +28485,38 @@ function parseMinimaxCodePoolData(value) {
|
|
|
27467
28485
|
return result;
|
|
27468
28486
|
}
|
|
27469
28487
|
/**
|
|
28488
|
+
* Nominal length of the two windows the Token Plan reports, in minutes.
|
|
28489
|
+
*
|
|
28490
|
+
* The line's own card names these two keys (`quotaWindowInterval` = 5 小时窗口,
|
|
28491
|
+
* `quotaWindowWeekly` = 每周窗口), and 5 hours / 7 days is what those words mean,
|
|
28492
|
+
* so the shared account card can name an unlabeled window from its length
|
|
28493
|
+
* instead of showing a generic one. The same two numbers label the equivalent
|
|
28494
|
+
* windows on the Command Code and Kimi Code lines.
|
|
28495
|
+
*/
|
|
28496
|
+
const WINDOW_MINUTES = {
|
|
28497
|
+
interval: 300,
|
|
28498
|
+
weekly: 10080
|
|
28499
|
+
};
|
|
28500
|
+
/**
|
|
28501
|
+
* One cached read as the shared account card renders it.
|
|
28502
|
+
*
|
|
28503
|
+
* Every window is labeled through its LENGTH rather than with a string: the
|
|
28504
|
+
* canonical window names live in the client's dictionary, and the host has no
|
|
28505
|
+
* business shipping display text. A window whose consumed share nobody measured
|
|
28506
|
+
* is dropped by {@link quotaWindow} — a null percentage is not 0%, it is
|
|
28507
|
+
* "not stated", and a bar would lie about it. An `unlimited` window states no
|
|
28508
|
+
* consumed share at all and the per-account shape has no way to say
|
|
28509
|
+
* "unlimited", so it is dropped too rather than drawn as an empty bar.
|
|
28510
|
+
*/
|
|
28511
|
+
function minimaxCodePoolQuota(quota) {
|
|
28512
|
+
if (quota === null || quota === void 0) return void 0;
|
|
28513
|
+
const windows = quota.windows === void 0 || quota.windows.length === 0 ? [quotaWindow$1("", quota.usedPercent, { resetsAt: quota.resetsAtMs })] : quota.windows.map((window) => quotaWindow$1("", window.usedPercent, {
|
|
28514
|
+
windowDurationMins: WINDOW_MINUTES[window.key],
|
|
28515
|
+
resetsAt: window.resetsAtMs
|
|
28516
|
+
}));
|
|
28517
|
+
return poolQuota(quota.fetchedAtMs, windows);
|
|
28518
|
+
}
|
|
28519
|
+
/**
|
|
27470
28520
|
* MiniMax Code's account pool.
|
|
27471
28521
|
*
|
|
27472
28522
|
* It owns rotation and the write-back, exactly like the sibling lines, with one
|
|
@@ -27547,7 +28597,11 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
27547
28597
|
...planName === void 0 ? {} : {
|
|
27548
28598
|
planName,
|
|
27549
28599
|
planLabel: planName
|
|
27550
|
-
}
|
|
28600
|
+
},
|
|
28601
|
+
...(() => {
|
|
28602
|
+
const quota = minimaxCodePoolQuota(getCachedQuotaFor$1(account.id));
|
|
28603
|
+
return quota === void 0 ? {} : { quota };
|
|
28604
|
+
})()
|
|
27551
28605
|
};
|
|
27552
28606
|
},
|
|
27553
28607
|
liveCredentialsFor: (account) => this.nativeAuthority(account),
|
|
@@ -27634,6 +28688,34 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
27634
28688
|
});
|
|
27635
28689
|
}
|
|
27636
28690
|
/**
|
|
28691
|
+
* The account a usage read belongs to, together with the credential to read it
|
|
28692
|
+
* with.
|
|
28693
|
+
*
|
|
28694
|
+
* Authored here rather than in the routes because it is provider-shaped: the
|
|
28695
|
+
* routes need a credential and the ACCOUNT it was read for, so a snapshot can
|
|
28696
|
+
* be remembered against the row whose numbers it describes. The selection is
|
|
28697
|
+
* the core's own rule for a caller that names no account — the active one, then
|
|
28698
|
+
* the primary, then the first — repeated in these three lines because the core
|
|
28699
|
+
* exposes it only through `getFreshCredential`, which returns the credential
|
|
28700
|
+
* alone; the credential (and its single-flight refresh, write-back and
|
|
28701
|
+
* bookkeeping) still comes from the core, so no rotation rule lives here.
|
|
28702
|
+
*
|
|
28703
|
+
* No upstream request is made for the selection itself, and none of this moves
|
|
28704
|
+
* the rotation: reading quota is not serving a request.
|
|
28705
|
+
*/
|
|
28706
|
+
async getQuotaReadTarget(fetchFn = fetch) {
|
|
28707
|
+
const data = await this.read();
|
|
28708
|
+
const target = data.accounts.find((account) => account.id === data.activeAccountId) ?? data.accounts.find((account) => account.isPrimary) ?? data.accounts[0];
|
|
28709
|
+
if (target === void 0) {
|
|
28710
|
+
await this.getFreshCredential(void 0, fetchFn);
|
|
28711
|
+
throw new Error("MiniMax Code 号池中没有账号。");
|
|
28712
|
+
}
|
|
28713
|
+
return {
|
|
28714
|
+
accountId: target.id,
|
|
28715
|
+
credentials: await this.getFreshCredential(target.id, fetchFn)
|
|
28716
|
+
};
|
|
28717
|
+
}
|
|
28718
|
+
/**
|
|
27637
28719
|
* Pick the account for the next request.
|
|
27638
28720
|
*
|
|
27639
28721
|
* The returned credential carries its own region and record key, so a caller
|
|
@@ -27967,8 +29049,12 @@ function registerMinimaxCodeRoutes(ctx, store = new MinimaxCodeCredentialStore()
|
|
|
27967
29049
|
* network, and the status route reads the snapshot through the cache instead.
|
|
27968
29050
|
*/
|
|
27969
29051
|
const refreshQuota = async (force) => {
|
|
27970
|
-
const
|
|
27971
|
-
|
|
29052
|
+
const target = await (options.accountPool === void 0 ? ensureAccessToken$2(store, { fetchFn }).then((credentials) => ({
|
|
29053
|
+
accountId: void 0,
|
|
29054
|
+
credentials
|
|
29055
|
+
})).catch(() => null) : options.accountPool.getQuotaReadTarget(fetchFn).catch(() => null));
|
|
29056
|
+
if (target === null) return null;
|
|
29057
|
+
const credentials = target.credentials;
|
|
27972
29058
|
const readToken = credentials.refreshToken;
|
|
27973
29059
|
const isCredentialStale = async () => {
|
|
27974
29060
|
const live = await store.readFresh().catch(() => null);
|
|
@@ -27978,10 +29064,11 @@ function registerMinimaxCodeRoutes(ctx, store = new MinimaxCodeCredentialStore()
|
|
|
27978
29064
|
fetchFn,
|
|
27979
29065
|
force,
|
|
27980
29066
|
isCredentialStale,
|
|
29067
|
+
...target.accountId === void 0 ? {} : { accountId: target.accountId },
|
|
27981
29068
|
renewCredential: async () => await (options.accountPool === void 0 ? ensureAccessToken$2(store, {
|
|
27982
29069
|
fetchFn,
|
|
27983
29070
|
force: true
|
|
27984
|
-
}) : options.accountPool.renewCredential(
|
|
29071
|
+
}) : options.accountPool.renewCredential(target.accountId, fetchFn)).catch(() => null)
|
|
27985
29072
|
}).catch(() => null);
|
|
27986
29073
|
};
|
|
27987
29074
|
/**
|
|
@@ -30046,6 +31133,9 @@ const CATALOG_CACHE_TTL_MS$1 = 1800 * 1e3;
|
|
|
30046
31133
|
* every cache miss.
|
|
30047
31134
|
*/
|
|
30048
31135
|
const catalogSnapshotScopesLoaded = /* @__PURE__ */ new Set();
|
|
31136
|
+
function getCachedCatalog$1() {
|
|
31137
|
+
return cachedCatalog?.models ?? [];
|
|
31138
|
+
}
|
|
30049
31139
|
function clearCachedCatalog$1() {
|
|
30050
31140
|
cachedCatalog = void 0;
|
|
30051
31141
|
catalogCacheEpoch += 1;
|
|
@@ -30237,16 +31327,64 @@ function billingMeters(billing) {
|
|
|
30237
31327
|
}
|
|
30238
31328
|
return meters;
|
|
30239
31329
|
}
|
|
30240
|
-
|
|
31330
|
+
/**
|
|
31331
|
+
* One newest snapshot PER ACCOUNT, keyed by the snapshot's own `account.id` —
|
|
31332
|
+
* the same public key the pool addresses an account with.
|
|
31333
|
+
*
|
|
31334
|
+
* Quota follows the account: rotation decides which account spends the next
|
|
31335
|
+
* request, so a single slot only ever described whichever account was read
|
|
31336
|
+
* last. The pool's card draws every account its own reading, so each account
|
|
31337
|
+
* keeps its own entry; past the cap the oldest read is evicted.
|
|
31338
|
+
*
|
|
31339
|
+
* This map is a memory read only. Nothing here fetches — the settings card
|
|
31340
|
+
* builds its account list from it, and that must never cost one upstream
|
|
31341
|
+
* request per pooled account.
|
|
31342
|
+
*/
|
|
31343
|
+
const cachedQuotaEntries = /* @__PURE__ */ new Map();
|
|
31344
|
+
/** How many accounts' snapshots are remembered; mirrors the Codex usage service. */
|
|
31345
|
+
const QUOTA_CACHE_LIMIT = 20;
|
|
31346
|
+
/** The entry read most recently, whichever account it belongs to. */
|
|
31347
|
+
function newestCachedQuota() {
|
|
31348
|
+
let newest;
|
|
31349
|
+
for (const snapshot of cachedQuotaEntries.values()) if (newest === void 0 || (snapshot.fetchedAt || 0) > (newest.fetchedAt || 0)) newest = snapshot;
|
|
31350
|
+
return newest;
|
|
31351
|
+
}
|
|
31352
|
+
/** Remember one account's newest snapshot, bounded to the accounts in use. */
|
|
31353
|
+
function rememberCachedQuota(snapshot) {
|
|
31354
|
+
cachedQuotaEntries.set(snapshot.account.id, snapshot);
|
|
31355
|
+
while (cachedQuotaEntries.size > QUOTA_CACHE_LIMIT) {
|
|
31356
|
+
const oldest = [...cachedQuotaEntries.entries()].sort((left, right) => (left[1].fetchedAt || 0) - (right[1].fetchedAt || 0))[0];
|
|
31357
|
+
if (oldest === void 0) break;
|
|
31358
|
+
cachedQuotaEntries.delete(oldest[0]);
|
|
31359
|
+
}
|
|
31360
|
+
}
|
|
30241
31361
|
let quotaInFlight$1 = null;
|
|
30242
31362
|
let quotaInFlightAccountId = null;
|
|
30243
31363
|
let quotaCacheEpoch = 0;
|
|
31364
|
+
/**
|
|
31365
|
+
* The snapshot read most recently, whichever account it belongs to.
|
|
31366
|
+
*
|
|
31367
|
+
* Kept with a no-argument signature for backward compatibility; the page-level
|
|
31368
|
+
* facts use {@link getCachedQuotaFor} instead, because they describe one
|
|
31369
|
+
* account and must never be another account's numbers.
|
|
31370
|
+
*/
|
|
30244
31371
|
function getCachedQuota$5() {
|
|
30245
|
-
return
|
|
31372
|
+
return newestCachedQuota();
|
|
31373
|
+
}
|
|
31374
|
+
/**
|
|
31375
|
+
* The snapshot remembered for one account, if it was read before.
|
|
31376
|
+
*
|
|
31377
|
+
* This is the lookup the account pool calls while it builds its summaries: a
|
|
31378
|
+
* pure memory read, never a fetch. Passing no account keeps the pre-pool
|
|
31379
|
+
* behavior of returning whatever was fetched last.
|
|
31380
|
+
*/
|
|
31381
|
+
function getCachedQuotaFor(accountId) {
|
|
31382
|
+
if (accountId === void 0 || accountId === null) return newestCachedQuota();
|
|
31383
|
+
return cachedQuotaEntries.get(accountId);
|
|
30246
31384
|
}
|
|
30247
31385
|
function clearCachedQuota$5() {
|
|
30248
31386
|
quotaCacheEpoch += 1;
|
|
30249
|
-
|
|
31387
|
+
cachedQuotaEntries.clear();
|
|
30250
31388
|
quotaInFlight$1 = null;
|
|
30251
31389
|
quotaInFlightAccountId = null;
|
|
30252
31390
|
}
|
|
@@ -30263,7 +31401,8 @@ async function fetchAccountQuota$4(store = new FileCredentialStore$4(), fetchFn
|
|
|
30263
31401
|
});
|
|
30264
31402
|
if (credentials === null) throw new Error(`Not signed in to ${PROVIDER_NAME$1}.`);
|
|
30265
31403
|
const requestedAccountId = workBuddyAccountId(credentials);
|
|
30266
|
-
|
|
31404
|
+
const cached = cachedQuotaEntries.get(requestedAccountId);
|
|
31405
|
+
if (!force && cached !== void 0 && Date.now() - (cached.fetchedAt || 0) < 12e4) return cached;
|
|
30267
31406
|
if (quotaInFlight$1 && quotaInFlightAccountId === requestedAccountId) return quotaInFlight$1;
|
|
30268
31407
|
const epoch = quotaCacheEpoch;
|
|
30269
31408
|
const request = (async () => {
|
|
@@ -30299,7 +31438,7 @@ async function fetchAccountQuota$4(store = new FileCredentialStore$4(), fetchFn
|
|
|
30299
31438
|
sources: ["billing/meter/get-user-resource"]
|
|
30300
31439
|
};
|
|
30301
31440
|
if (epoch !== quotaCacheEpoch) return snapshot;
|
|
30302
|
-
|
|
31441
|
+
rememberCachedQuota(snapshot);
|
|
30303
31442
|
return snapshot;
|
|
30304
31443
|
})();
|
|
30305
31444
|
quotaInFlight$1 = request;
|
|
@@ -30614,7 +31753,7 @@ async function getWorkBuddyWebStatus(store, modelSettings, preferences, options
|
|
|
30614
31753
|
const catalog = live.length > 0 ? live : FALLBACK_MODELS$1;
|
|
30615
31754
|
const available = modelsForRegion(credentials?.region ?? "intl", catalog).map((model) => model.id);
|
|
30616
31755
|
const models = buildModelOptions(catalog, available, resolveEnabledModelIds$2(settings.enabledModelIds, available, enabled), settings.contextWindowOverrides);
|
|
30617
|
-
const quota =
|
|
31756
|
+
const quota = getCachedQuotaFor(credentials === null ? null : accountFromCredentials$1(credentials).id);
|
|
30618
31757
|
const poolData = options.accountPool === void 0 ? null : await options.accountPool.read().catch(() => null);
|
|
30619
31758
|
const poolAccounts = options.accountPool === void 0 ? [] : await options.accountPool.listAccounts().catch(() => []);
|
|
30620
31759
|
const hiddenIds = new Set(settings.hiddenAccountIds);
|
|
@@ -30673,12 +31812,10 @@ function registerWorkBuddyRoutes(ctx, store, modelSettings, preferences, options
|
|
|
30673
31812
|
accountId: settings.selectedAccountId,
|
|
30674
31813
|
hiddenAccountIds: settings.hiddenAccountIds
|
|
30675
31814
|
});
|
|
30676
|
-
const cached =
|
|
30677
|
-
|
|
30678
|
-
if (credentials !== null && (!quotaMatches || cached === void 0 || Date.now() - (cached.fetchedAt || 0) > 12e4)) {
|
|
30679
|
-
if (!quotaMatches) clearCachedQuota$5();
|
|
31815
|
+
const cached = getCachedQuotaFor(credentials === null ? null : accountFromCredentials$1(credentials).id);
|
|
31816
|
+
if (credentials !== null && (cached === void 0 || Date.now() - (cached.fetchedAt || 0) > 12e4)) {
|
|
30680
31817
|
const refresh = () => fetchAccountQuota$4(store, fetchFn, false, settings.selectedAccountId, settings.hiddenAccountIds);
|
|
30681
|
-
if (cached === void 0
|
|
31818
|
+
if (cached === void 0) await quotaRefresh.run(refresh);
|
|
30682
31819
|
else quotaRefresh.start(refresh);
|
|
30683
31820
|
}
|
|
30684
31821
|
return sendJson$1(response, 200, {
|
|
@@ -32164,6 +33301,47 @@ function defaultAliasFor(credentials, position) {
|
|
|
32164
33301
|
return credentials.nickname || credentials.uin || credentials.uid || `账号 ${position}`;
|
|
32165
33302
|
}
|
|
32166
33303
|
/**
|
|
33304
|
+
* A meter's consumed share, from whichever fraction this line measured.
|
|
33305
|
+
*
|
|
33306
|
+
* `usedFraction` is the DTO's own "share consumed"; `remainingFraction` is the
|
|
33307
|
+
* same statement read from the other end, and is the one the rest of the card
|
|
33308
|
+
* renders. A meter that states neither is dropped (null) rather than drawn at
|
|
33309
|
+
* 0, which would claim a full window no reading supports.
|
|
33310
|
+
*/
|
|
33311
|
+
function meterUsedPercent(meter) {
|
|
33312
|
+
if (typeof meter.usedFraction === "number" && Number.isFinite(meter.usedFraction)) return meter.usedFraction * 100;
|
|
33313
|
+
if (typeof meter.remainingFraction === "number" && Number.isFinite(meter.remainingFraction)) return (1 - meter.remainingFraction) * 100;
|
|
33314
|
+
return null;
|
|
33315
|
+
}
|
|
33316
|
+
/**
|
|
33317
|
+
* One cached snapshot as the shared account card renders it.
|
|
33318
|
+
*
|
|
33319
|
+
* Only what the DTO states is published: every meter that measured a share
|
|
33320
|
+
* becomes a window, and a meter that measured none is dropped rather than drawn
|
|
33321
|
+
* at 0 — which would claim an unspent allowance no reading supports.
|
|
33322
|
+
*
|
|
33323
|
+
* Exported for the pool's regression tests; the card only reaches it through
|
|
33324
|
+
* the summary hook.
|
|
33325
|
+
*/
|
|
33326
|
+
function workBuddyPoolQuota(snapshot) {
|
|
33327
|
+
const windows = snapshot.meters.map((meter) => quotaWindow$1(meter.label, meterUsedPercent(meter), { resetsAt: meter.resetsAt }));
|
|
33328
|
+
if (!snapshot.meters.some((meter) => meter.id === "cycle") && snapshot.cycleCredits !== null && snapshot.cycleCredits > 0 && snapshot.cycleUsedCredits !== null) windows.push(quotaWindow$1(snapshot.packageName ?? "Billing cycle", snapshot.cycleUsedCredits / snapshot.cycleCredits * 100, { resetsAt: snapshot.cycleEndsAt }));
|
|
33329
|
+
return poolQuota(snapshot.fetchedAt, windows);
|
|
33330
|
+
}
|
|
33331
|
+
/**
|
|
33332
|
+
* This account's own newest snapshot, ready for the account card.
|
|
33333
|
+
*
|
|
33334
|
+
* Quota follows the account, so the pool reports the snapshot the line already
|
|
33335
|
+
* remembered for THIS account id. The lookup is a pure memory read: the card
|
|
33336
|
+
* builds its account list here, and that must never cost one upstream request
|
|
33337
|
+
* per pooled account. Undefined means exactly that — never read — which is not
|
|
33338
|
+
* the same statement as "nothing used".
|
|
33339
|
+
*/
|
|
33340
|
+
function quotaFor(accountId) {
|
|
33341
|
+
const snapshot = getCachedQuotaFor(accountId);
|
|
33342
|
+
return snapshot === void 0 ? void 0 : workBuddyPoolQuota(snapshot);
|
|
33343
|
+
}
|
|
33344
|
+
/**
|
|
32167
33345
|
* WorkBuddy's account pool.
|
|
32168
33346
|
*
|
|
32169
33347
|
* Desktop accounts are adopted into the pool so they can be scheduled and
|
|
@@ -32224,7 +33402,11 @@ var WorkBuddyAccountPool = class extends AccountPoolCore {
|
|
|
32224
33402
|
...account.credentials.domain === "" ? {} : { domain: account.credentials.domain },
|
|
32225
33403
|
...account.credentials.backend === "" ? {} : { backend: account.credentials.backend },
|
|
32226
33404
|
...account.credentials.accountType === void 0 ? {} : { accountType: account.credentials.accountType },
|
|
32227
|
-
...account.credentials.expiresAt > 0 ? {} : { expiresAt: void 0 }
|
|
33405
|
+
...account.credentials.expiresAt > 0 ? {} : { expiresAt: void 0 },
|
|
33406
|
+
...(() => {
|
|
33407
|
+
const quota = quotaFor(account.id);
|
|
33408
|
+
return quota === void 0 ? {} : { quota };
|
|
33409
|
+
})()
|
|
32228
33410
|
}),
|
|
32229
33411
|
preferAccountId: () => this.resolveSelectionId(options.selection?.().selectedAccountId ?? null),
|
|
32230
33412
|
emptyMessage: `Not signed in to ${PROVIDER_NAME$1}. Sign in with the CodeBuddy desktop client, or add an account from Settings > WorkBuddy.`,
|
|
@@ -34233,6 +35415,10 @@ const catalogInFlight = /* @__PURE__ */ new Map();
|
|
|
34233
35415
|
function clearCachedCatalog() {
|
|
34234
35416
|
catalogCache = null;
|
|
34235
35417
|
}
|
|
35418
|
+
/** The listing currently held, or an empty array before the first load. */
|
|
35419
|
+
function getCachedCatalog() {
|
|
35420
|
+
return catalogCache?.models ?? [];
|
|
35421
|
+
}
|
|
34236
35422
|
/**
|
|
34237
35423
|
* Read one entry of the live listing.
|
|
34238
35424
|
*
|
|
@@ -34528,22 +35714,108 @@ function parseQuotaHeaders(headers) {
|
|
|
34528
35714
|
}
|
|
34529
35715
|
};
|
|
34530
35716
|
}
|
|
34531
|
-
|
|
35717
|
+
/**
|
|
35718
|
+
* Cache key of one account's snapshot.
|
|
35719
|
+
*
|
|
35720
|
+
* A pool row id when the caller names one — that is the identity that survives a
|
|
35721
|
+
* token refresh and the one the account card asks with — and otherwise the
|
|
35722
|
+
* credential's token tail, which is all a single-credential process has. The
|
|
35723
|
+
* `acc:` prefix keeps the two apart, so no row id can ever be answered with an
|
|
35724
|
+
* entry written under a token tail.
|
|
35725
|
+
*/
|
|
35726
|
+
function quotaEntryKey(accountId, credentials) {
|
|
35727
|
+
return accountId === void 0 || accountId === "" ? accountKeyFor(credentials) : `acc:${accountId}`;
|
|
35728
|
+
}
|
|
35729
|
+
/**
|
|
35730
|
+
* How many accounts' snapshots are remembered.
|
|
35731
|
+
*
|
|
35732
|
+
* Mirrors the bound the ChatGPT line's usage service applies to its own
|
|
35733
|
+
* per-account map: room for every account a user is likely to pool, and no way
|
|
35734
|
+
* for a long-lived process to accumulate one snapshot per token it has seen.
|
|
35735
|
+
*/
|
|
35736
|
+
const QUOTA_ACCOUNT_LIMIT = 20;
|
|
35737
|
+
/**
|
|
35738
|
+
* The newest snapshot of each account, keyed by {@link quotaEntryKey}.
|
|
35739
|
+
*
|
|
35740
|
+
* ONE ENTRY PER ACCOUNT rather than one slot for the line: quota follows the
|
|
35741
|
+
* account, and rotation decides which account spends the next request, so a
|
|
35742
|
+
* single slot could only ever describe whichever account was read last. This is
|
|
35743
|
+
* a memory read only — the settings card builds its account list from it, and
|
|
35744
|
+
* that must never cost one upstream request per pooled account.
|
|
35745
|
+
*/
|
|
35746
|
+
const quotaCache = /* @__PURE__ */ new Map();
|
|
35747
|
+
/**
|
|
35748
|
+
* Key of the entry written most recently.
|
|
35749
|
+
*
|
|
35750
|
+
* The pre-pool card asks "whatever was read last", and a header reading that
|
|
35751
|
+
* names no account may only merge into the snapshot it was read beside. Both
|
|
35752
|
+
* need the last WRITE rather than a scan: two accounts can be written inside one
|
|
35753
|
+
* millisecond, and a tie would otherwise be broken by map order.
|
|
35754
|
+
*/
|
|
35755
|
+
let quotaLastKey = null;
|
|
34532
35756
|
const quotaInFlight = /* @__PURE__ */ new Map();
|
|
34533
|
-
/** Drop
|
|
35757
|
+
/** Drop every cached snapshot. Called on sign-out and after a credential change. */
|
|
34534
35758
|
function clearCachedQuota$1() {
|
|
34535
|
-
quotaCache
|
|
35759
|
+
quotaCache.clear();
|
|
35760
|
+
quotaLastKey = null;
|
|
35761
|
+
}
|
|
35762
|
+
/** The entry written most recently, whichever account it belongs to. */
|
|
35763
|
+
function newestQuotaEntry() {
|
|
35764
|
+
return quotaLastKey === null ? null : quotaCache.get(quotaLastKey) ?? null;
|
|
35765
|
+
}
|
|
35766
|
+
/**
|
|
35767
|
+
* Remember one account's newest snapshot, bounded to the accounts in use.
|
|
35768
|
+
*
|
|
35769
|
+
* The oldest entry is measured by {@link QuotaCacheEntry.observedAt} rather than
|
|
35770
|
+
* by `fetchedAt`: a snapshot kept warm by response headers has no full read
|
|
35771
|
+
* behind it at all, and it is still the newest thing known about that account.
|
|
35772
|
+
*/
|
|
35773
|
+
function rememberQuota(entry) {
|
|
35774
|
+
quotaCache.set(entry.key, entry);
|
|
35775
|
+
quotaLastKey = entry.key;
|
|
35776
|
+
while (quotaCache.size > QUOTA_ACCOUNT_LIMIT) {
|
|
35777
|
+
const oldest = [...quotaCache.entries()].sort((left, right) => left[1].observedAt - right[1].observedAt)[0];
|
|
35778
|
+
if (oldest === void 0) break;
|
|
35779
|
+
quotaCache.delete(oldest[0]);
|
|
35780
|
+
if (quotaLastKey === oldest[0]) quotaLastKey = null;
|
|
35781
|
+
}
|
|
34536
35782
|
}
|
|
34537
35783
|
/**
|
|
34538
35784
|
* The cached snapshot, optionally only when it belongs to this credential.
|
|
34539
35785
|
*
|
|
34540
35786
|
* With one stored account the argument is unnecessary; it exists so a later
|
|
34541
35787
|
* pool chunk cannot render one account's usage under another account's name.
|
|
35788
|
+
* Given a credential the answer is THAT account's own newest snapshot, which is
|
|
35789
|
+
* what keeps two pooled accounts apart: a later read of a different account
|
|
35790
|
+
* cannot displace it. Without one the pre-pool answer is kept — whatever was
|
|
35791
|
+
* written last — because the single-account card has no identity to ask with.
|
|
34542
35792
|
*/
|
|
34543
35793
|
function getCachedQuota$1(credentials) {
|
|
34544
|
-
if (
|
|
34545
|
-
|
|
34546
|
-
|
|
35794
|
+
if (credentials === void 0) return newestQuotaEntry()?.quota ?? null;
|
|
35795
|
+
return cachedQuotaFor(credentials);
|
|
35796
|
+
}
|
|
35797
|
+
/**
|
|
35798
|
+
* The newest snapshot held for one credential's account, for display only.
|
|
35799
|
+
*
|
|
35800
|
+
* The token-tail form, kept for a composition with a single stored credential.
|
|
35801
|
+
* A pooled caller asks {@link cachedQuotaForPool} with the row id instead: a
|
|
35802
|
+
* token is rotated by every refresh, so an entry filed under a tail is lost the
|
|
35803
|
+
* moment the account renews.
|
|
35804
|
+
*/
|
|
35805
|
+
function cachedQuotaFor(credentials) {
|
|
35806
|
+
return quotaCache.get(accountKeyFor(credentials))?.quota ?? null;
|
|
35807
|
+
}
|
|
35808
|
+
/**
|
|
35809
|
+
* The newest snapshot held for one pool row, for display only.
|
|
35810
|
+
*
|
|
35811
|
+
* The pool asks here while it builds the account list for the settings card, so
|
|
35812
|
+
* this is a pure memory read: it never fetches. Null means nothing was ever read
|
|
35813
|
+
* for this account, which is a different statement from "nothing used". A row id
|
|
35814
|
+
* that is undefined falls back to the credential's own tail, which is what a
|
|
35815
|
+
* composition without a pool has.
|
|
35816
|
+
*/
|
|
35817
|
+
function cachedQuotaForPool(accountId, credentials) {
|
|
35818
|
+
return quotaCache.get(quotaEntryKey(accountId, credentials))?.quota ?? null;
|
|
34547
35819
|
}
|
|
34548
35820
|
/**
|
|
34549
35821
|
* Read the account's usage meters.
|
|
@@ -34561,12 +35833,13 @@ function getCachedQuota$1(credentials) {
|
|
|
34561
35833
|
* up staring at a card that claims they are fine.
|
|
34562
35834
|
*/
|
|
34563
35835
|
function fetchAccountQuota$1(credentials, options = {}) {
|
|
34564
|
-
const key =
|
|
35836
|
+
const key = quotaEntryKey(options.accountId, credentials);
|
|
34565
35837
|
const at = now();
|
|
34566
|
-
|
|
34567
|
-
|
|
34568
|
-
const
|
|
34569
|
-
|
|
35838
|
+
const cached = quotaCache.get(key);
|
|
35839
|
+
if (options.force !== true && cached !== void 0) {
|
|
35840
|
+
const warm = at - cached.observedAt < QUOTA_CACHE_TTL_MS;
|
|
35841
|
+
const freshFullRead = cached.fullReadAt !== null && at - cached.fullReadAt < 3e5;
|
|
35842
|
+
if (warm && freshFullRead) return Promise.resolve(cached.quota);
|
|
34570
35843
|
}
|
|
34571
35844
|
const pending = quotaInFlight.get(key);
|
|
34572
35845
|
if (pending !== void 0) return pending;
|
|
@@ -34581,9 +35854,9 @@ function fetchAccountQuota$1(credentials, options = {}) {
|
|
|
34581
35854
|
return run;
|
|
34582
35855
|
}
|
|
34583
35856
|
async function performQuotaFetch(credentials, options) {
|
|
34584
|
-
const key =
|
|
35857
|
+
const key = quotaEntryKey(options.accountId, credentials);
|
|
34585
35858
|
const fetchFn = options.fetchFn ?? fetch;
|
|
34586
|
-
const previous = quotaCache
|
|
35859
|
+
const previous = quotaCache.get(key)?.quota ?? null;
|
|
34587
35860
|
let response;
|
|
34588
35861
|
try {
|
|
34589
35862
|
response = await fetchFn("https://api.anthropic.com/api/oauth/usage", {
|
|
@@ -34621,12 +35894,12 @@ async function performQuotaFetch(credentials, options) {
|
|
|
34621
35894
|
status: null,
|
|
34622
35895
|
representativeClaim: null
|
|
34623
35896
|
};
|
|
34624
|
-
|
|
35897
|
+
rememberQuota({
|
|
34625
35898
|
quota,
|
|
34626
35899
|
key,
|
|
34627
35900
|
fullReadAt: at,
|
|
34628
35901
|
observedAt: at
|
|
34629
|
-
};
|
|
35902
|
+
});
|
|
34630
35903
|
return quota;
|
|
34631
35904
|
}
|
|
34632
35905
|
/**
|
|
@@ -36641,6 +37914,7 @@ function processStreamLine(line, state) {
|
|
|
36641
37914
|
const error = isRecord$5(event.error) ? event.error : {};
|
|
36642
37915
|
const message = asString$2(error.message) ?? "unknown error";
|
|
36643
37916
|
const kind = asString$2(error.type);
|
|
37917
|
+
state.streamError = error;
|
|
36644
37918
|
throw new LlmError$1("Claude stream error" + (kind === void 0 ? "" : " (" + kind + ")") + ": " + message, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
36645
37919
|
}
|
|
36646
37920
|
return out;
|
|
@@ -37814,6 +39088,13 @@ function cancelLogin() {
|
|
|
37814
39088
|
* - `request` -> PROVIDER_ERROR;
|
|
37815
39089
|
* - `network` -> TRANSPORT.
|
|
37816
39090
|
*
|
|
39091
|
+
* An in-band `error` event inside a 200 stream carries the same envelope and is
|
|
39092
|
+
* classified the same way while nothing has reached the caller; after the first
|
|
39093
|
+
* chunk it is surfaced as PROVIDER_ERROR, for the reason in note 1
|
|
39094
|
+
* ({@link inBandStreamVerdict}). A reclassified in-band failure is weighed by
|
|
39095
|
+
* {@link shouldRotateAccount} exactly as a non-2xx one is, so an account-scoped
|
|
39096
|
+
* limit reported inside the stream still takes that account out of rotation.
|
|
39097
|
+
*
|
|
37817
39098
|
* A reported-client-version rejection is a REQUEST problem even though it
|
|
37818
39099
|
* arrives as a 400 that mentions the client: it must NOT sign the user out, and
|
|
37819
39100
|
* it has a real remedy (raise the reported version), so it is handled
|
|
@@ -38213,8 +39494,19 @@ var ClaudeAdapter = class ClaudeAdapter extends LlmAdapter {
|
|
|
38213
39494
|
* a later reordering of this method cannot silently drop it.
|
|
38214
39495
|
*/
|
|
38215
39496
|
let outputStarted = false;
|
|
39497
|
+
/**
|
|
39498
|
+
* The account the response now being streamed was issued with.
|
|
39499
|
+
*
|
|
39500
|
+
* Hoisted out of the loop because the in-stream failure path needs it: a
|
|
39501
|
+
* rate limit reported inside a 200 stream belongs to the account that was
|
|
39502
|
+
* asked, and that account is what the failure takes out of rotation — the
|
|
39503
|
+
* harness is about to repeat this request, and repeating it against the same
|
|
39504
|
+
* exhausted account fails identically.
|
|
39505
|
+
*/
|
|
39506
|
+
let usedAccountId;
|
|
38216
39507
|
while (true) {
|
|
38217
39508
|
const { credentials, accountId } = await this.resolveCredential(pool, tried, fetchFn, signal);
|
|
39509
|
+
usedAccountId = accountId;
|
|
38218
39510
|
response = await this.attemptRequest(credentials, requestOptions, images, toolNames, thinking, settings, signal, fetchFn);
|
|
38219
39511
|
if (response.ok) break;
|
|
38220
39512
|
const detail = (await response.text().catch(() => "")).slice(0, 2e3);
|
|
@@ -38260,7 +39552,12 @@ var ClaudeAdapter = class ClaudeAdapter extends LlmAdapter {
|
|
|
38260
39552
|
yield chunk;
|
|
38261
39553
|
}
|
|
38262
39554
|
} catch (error) {
|
|
38263
|
-
if (error instanceof LlmError)
|
|
39555
|
+
if (error instanceof LlmError) {
|
|
39556
|
+
if (outputStarted || error.code !== "PROVIDER_ERROR" || state.streamError === void 0) throw error;
|
|
39557
|
+
const verdict = inBandStreamVerdict(error, state.streamError, response.headers);
|
|
39558
|
+
if (verdict.failure !== null && shouldRotateAccount(verdict.failure, outputStarted) && usedAccountId !== void 0) await pool?.markCooldown(usedAccountId, cooldownMsFor(verdict.failure), "Claude(订阅) in-stream 429").catch(() => void 0);
|
|
39559
|
+
throw verdict.error;
|
|
39560
|
+
}
|
|
38264
39561
|
if (signal.aborted) throw new LlmError("Claude request aborted", "ABORTED", { cause: error });
|
|
38265
39562
|
throw new LlmError("Claude stream failed: " + (error instanceof Error ? error.message : String(error)), outputStarted ? "PROVIDER_ERROR" : "TRANSPORT", { cause: error });
|
|
38266
39563
|
}
|
|
@@ -38350,6 +39647,49 @@ function codeForFailure(failure) {
|
|
|
38350
39647
|
return "PROVIDER_ERROR";
|
|
38351
39648
|
}
|
|
38352
39649
|
/**
|
|
39650
|
+
* The DSH error an in-band `error` event becomes when it arrived BEFORE any
|
|
39651
|
+
* chunk reached the caller.
|
|
39652
|
+
*
|
|
39653
|
+
* Anthropic can answer 200 and then report the failure inside the stream, in
|
|
39654
|
+
* the same `{ error: { type, message } }` envelope a non-2xx body carries. The
|
|
39655
|
+
* mapper types every such event PROVIDER_ERROR, because it cannot know whether
|
|
39656
|
+
* output has started — and PROVIDER_ERROR is outside the retry set, so an
|
|
39657
|
+
* `overloaded_error` delivered this way ended the turn on the first try while
|
|
39658
|
+
* the identical overload delivered as a 529 is retried. Before any output a
|
|
39659
|
+
* fresh request is still free (module note 1), so the envelope is classified
|
|
39660
|
+
* exactly as a response body would be, and a transient verdict (overloaded,
|
|
39661
|
+
* server, rate limit) takes its retryable code.
|
|
39662
|
+
*
|
|
39663
|
+
* Everything else keeps the mapper's verdict: a request problem, a context
|
|
39664
|
+
* overflow, a credential refusal, and a type this line does not recognize. The
|
|
39665
|
+
* last matters: classifyFailure falls back to the status line for an unknown
|
|
39666
|
+
* type, and no status here describes the failure — the response itself was a
|
|
39667
|
+
* 200 — so the status passed is 0 and never consulted for a recognized type.
|
|
39668
|
+
*
|
|
39669
|
+
* The message stays the mapper's, which names the wire type the user saw.
|
|
39670
|
+
*
|
|
39671
|
+
* The classified failure rides along beside the error so the caller can weigh it
|
|
39672
|
+
* with {@link shouldRotateAccount} instead of re-deriving what it says. It is
|
|
39673
|
+
* null whenever the mapper's verdict stands, so a caller never has to guess
|
|
39674
|
+
* whether `accountScoped` is meaningful.
|
|
39675
|
+
*
|
|
39676
|
+
* @param thrown - the mapper's PROVIDER_ERROR (or context-overflow) verdict.
|
|
39677
|
+
* @param streamError - the event's wire `error` object, from the stream state.
|
|
39678
|
+
* @param headers - the 200 response's headers, read for the rate-limit verdict.
|
|
39679
|
+
*/
|
|
39680
|
+
function inBandStreamVerdict(thrown, streamError, headers) {
|
|
39681
|
+
const failure = classifyFailure(0, JSON.stringify({ error: streamError }), headers);
|
|
39682
|
+
if (failure.type === null || !failure.retryable) return {
|
|
39683
|
+
error: thrown,
|
|
39684
|
+
failure: null
|
|
39685
|
+
};
|
|
39686
|
+
const retryAfter = failure.retryAfterMs !== null && failure.retryAfterMs > 0 ? failure.retryAfterMs : void 0;
|
|
39687
|
+
return {
|
|
39688
|
+
error: new LlmError(thrown.message, codeForFailure(failure), retryAfter === void 0 ? {} : { providerRetryAfterMs: retryAfter }),
|
|
39689
|
+
failure
|
|
39690
|
+
};
|
|
39691
|
+
}
|
|
39692
|
+
/**
|
|
38353
39693
|
* Turn one classified failure into the DSH error the caller sees.
|
|
38354
39694
|
*
|
|
38355
39695
|
* The facts the harness needs ride along: the HTTP status, and the delay the
|
|
@@ -39186,6 +40526,32 @@ async function refreshClaudeCredential(credentials, fetchFn) {
|
|
|
39186
40526
|
};
|
|
39187
40527
|
}
|
|
39188
40528
|
/**
|
|
40529
|
+
* One account's own newest quota snapshot, in the shape the account card draws.
|
|
40530
|
+
*
|
|
40531
|
+
* The snapshot comes from 'client.ts''s per-account cache, and this is where
|
|
40532
|
+
* this line's own numbers are translated into the shared shape. Every judgement
|
|
40533
|
+
* below trades a possibly-nice-looking bar for an honest one:
|
|
40534
|
+
*
|
|
40535
|
+
* - a window whose consumed share nobody measured is DROPPED (`quotaWindow`
|
|
40536
|
+
* returns null), because "0% used" and "not stated" are different claims;
|
|
40537
|
+
* - the share falls back to `100 - remainingPercent` only when that field was
|
|
40538
|
+
* stated; when the payload stated NEITHER share there is nothing to draw;
|
|
40539
|
+
* - the read time is the full read's own `fetchedAt`, or — for a snapshot that
|
|
40540
|
+
* only response headers ever kept warm, which performed no full read — the
|
|
40541
|
+
* `observedAt` instant those numbers arrived. Either way it is when this line
|
|
40542
|
+
* saw the numbers, never when upstream served them.
|
|
40543
|
+
*
|
|
40544
|
+
* A null snapshot means "this account was never read", which `poolQuota` reports
|
|
40545
|
+
* as undefined so the card can say exactly that instead of drawing a zero.
|
|
40546
|
+
*/
|
|
40547
|
+
function claudePoolQuota(snapshot) {
|
|
40548
|
+
if (snapshot === null) return void 0;
|
|
40549
|
+
return poolQuota(snapshot.fetchedAt ?? snapshot.observedAt, snapshot.windows.map((window) => quotaWindow$1(window.label, window.usedPercent ?? (window.remainingPercent === null ? null : 100 - window.remainingPercent), {
|
|
40550
|
+
windowDurationMins: window.windowMinutes,
|
|
40551
|
+
resetsAt: typeof window.resetsAt === "string" ? Date.parse(window.resetsAt) : null
|
|
40552
|
+
})));
|
|
40553
|
+
}
|
|
40554
|
+
/**
|
|
39189
40555
|
* The Claude subscription account pool.
|
|
39190
40556
|
*
|
|
39191
40557
|
* Read the module comment first: it carries the two rules this class exists to
|
|
@@ -39283,6 +40649,7 @@ var ClaudeAccountPool = class extends AccountPoolCore {
|
|
|
39283
40649
|
const subscriptionType = account.subscriptionType ?? credentials.subscriptionType;
|
|
39284
40650
|
const adopted = account.adopted === true;
|
|
39285
40651
|
const expired = adopted && adoptedCredentialExpired(credentials);
|
|
40652
|
+
const quota = claudePoolQuota(cachedQuotaForPool(account.id, credentials));
|
|
39286
40653
|
return {
|
|
39287
40654
|
...base,
|
|
39288
40655
|
...email === void 0 ? {} : { email },
|
|
@@ -39294,6 +40661,7 @@ var ClaudeAccountPool = class extends AccountPoolCore {
|
|
|
39294
40661
|
source: account.source,
|
|
39295
40662
|
removable: !adopted,
|
|
39296
40663
|
...account.sourcePath === void 0 ? {} : { sourcePath: account.sourcePath },
|
|
40664
|
+
...quota === void 0 ? {} : { quota },
|
|
39297
40665
|
...expired ? {
|
|
39298
40666
|
authStatus: "expired",
|
|
39299
40667
|
authFailedReason: ADOPTED_CREDENTIAL_EXPIRED_HINT
|
|
@@ -39860,7 +41228,7 @@ async function getClaudeWebStatus(store, modelSettings, preferences, options = {
|
|
|
39860
41228
|
const catalog = live.length > 0 ? live : FALLBACK_MODELS;
|
|
39861
41229
|
const available = catalog.map((model) => model.id);
|
|
39862
41230
|
const models = buildClaudeModelOptions(catalog, available, resolveEnabledModelIds(settings.enabledModelIds, available, enabled), settings.contextWindowOverrides);
|
|
39863
|
-
const quota = credential === void 0 ? getCachedQuota$1() :
|
|
41231
|
+
const quota = credential === void 0 ? getCachedQuota$1() : cachedQuotaForPool(active.id, credential);
|
|
39864
41232
|
const poolData = options.accountPool === void 0 ? null : await options.accountPool.read().catch(() => null);
|
|
39865
41233
|
const poolAccounts = options.accountPool === void 0 ? [] : await options.accountPool.listAccounts().catch(() => []);
|
|
39866
41234
|
const presence = await claudeCodeCredentialPresence(options.adoptPaths ?? claudeCodeCredentialPaths()).catch(() => ({
|
|
@@ -39994,13 +41362,14 @@ function registerClaudeRoutes(ctx, store, modelSettings, preferences, options =
|
|
|
39994
41362
|
if (path === "" || path === "status") {
|
|
39995
41363
|
if (method !== "GET") return sendMethodNotAllowed(response);
|
|
39996
41364
|
const active = await activeAccount();
|
|
39997
|
-
const cached = active.credentials === void 0 ? null :
|
|
41365
|
+
const cached = active.credentials === void 0 ? null : cachedQuotaForPool(active.id, active.credentials);
|
|
39998
41366
|
const stale = cached === null || Date.now() - (cached.fetchedAt || 0) > 12e4;
|
|
39999
41367
|
if (active.credentials !== void 0 && stale) {
|
|
40000
41368
|
const credentials = active.credentials;
|
|
40001
41369
|
const refresh = () => fetchAccountQuota$1(credentials, {
|
|
40002
41370
|
fetchFn,
|
|
40003
|
-
signal: quotaAbort.signal
|
|
41371
|
+
signal: quotaAbort.signal,
|
|
41372
|
+
accountId: active.id
|
|
40004
41373
|
});
|
|
40005
41374
|
if (cached === null) await quotaRefresh.run(refresh);
|
|
40006
41375
|
else quotaRefresh.start(refresh);
|
|
@@ -40122,7 +41491,8 @@ function registerClaudeRoutes(ctx, store, modelSettings, preferences, options =
|
|
|
40122
41491
|
await fetchAccountQuota$1(credentials, {
|
|
40123
41492
|
fetchFn,
|
|
40124
41493
|
force: method === "POST",
|
|
40125
|
-
signal: quotaAbort.signal
|
|
41494
|
+
signal: quotaAbort.signal,
|
|
41495
|
+
accountId: active.id
|
|
40126
41496
|
});
|
|
40127
41497
|
} catch (error) {
|
|
40128
41498
|
quotaError = error instanceof Error ? error.message : String(error);
|
|
@@ -43738,39 +45108,24 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
43738
45108
|
provider,
|
|
43739
45109
|
limits: providerLimits
|
|
43740
45110
|
})]));
|
|
43741
|
-
const
|
|
43742
|
-
|
|
43743
|
-
|
|
43744
|
-
|
|
43745
|
-
|
|
43746
|
-
|
|
43747
|
-
|
|
43748
|
-
|
|
43749
|
-
try {
|
|
43750
|
-
antigravityRegistration = ctx.llm.registerAdapter([PROVIDER_ID$6], antigravityAdapter);
|
|
43751
|
-
if (antigravityConflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] Antigravity route "${PROVIDER_ID$6}" is now served by this plugin`);
|
|
43752
|
-
antigravityConflict = null;
|
|
43753
|
-
} catch (error) {
|
|
43754
|
-
antigravityConflict = error instanceof Error ? error.message : String(error);
|
|
43755
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$6}" is already owned by another adapter; Antigravity models keep being served by that one until its configuration is removed (${antigravityConflict})`);
|
|
43756
|
-
}
|
|
43757
|
-
};
|
|
43758
|
-
claimAntigravityRoute();
|
|
43759
|
-
const antigravityRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43760
|
-
claimAntigravityRoute();
|
|
43761
|
-
}) : void 0;
|
|
45111
|
+
const antigravityClaim = claimProviderRoute(ctx, {
|
|
45112
|
+
providerId: PROVIDER_ID$6,
|
|
45113
|
+
label: PROVIDER_NAME$6,
|
|
45114
|
+
adapter: new AntigravityAdapter(antigravityStore, antigravityModelSettings, antigravityPreferences, {
|
|
45115
|
+
fetchFn: modelFetch.antigravity,
|
|
45116
|
+
attachments: ctx.attachments
|
|
45117
|
+
}, antigravityAccountPool)
|
|
45118
|
+
});
|
|
43762
45119
|
const disposeAntigravityRoutes = registerAntigravityRoutes(ctx, antigravityStore, antigravityModelSettings, antigravityPreferences, proxyFetch, antigravityAccountPool);
|
|
43763
45120
|
const disposeCommandCodeRoutes = registerCommandCodeRoutes(ctx, commandCodeStore, commandCodeModelSettings, commandCodePreferences, {
|
|
43764
45121
|
fetchFn: proxyFetch,
|
|
43765
|
-
serving: () =>
|
|
43766
|
-
conflict: () =>
|
|
45122
|
+
serving: () => commandCodeClaim.serving(),
|
|
45123
|
+
conflict: () => commandCodeClaim.conflict()
|
|
43767
45124
|
}, commandCodeAccountPool);
|
|
43768
45125
|
const commandCodeAdapter = new CommandCodeAdapter(commandCodeStore, commandCodeModelSettings, commandCodePreferences, {
|
|
43769
45126
|
fetchFn: modelFetch["command-code"],
|
|
43770
45127
|
attachments: ctx.attachments
|
|
43771
45128
|
}, commandCodeAccountPool);
|
|
43772
|
-
let commandCodeRegistration;
|
|
43773
|
-
let commandCodeConflict = null;
|
|
43774
45129
|
const ollamaStore = new FileCredentialStore$5();
|
|
43775
45130
|
const ollamaModelSettings = new FileModelSettingsStore$5();
|
|
43776
45131
|
const ollamaAccountPool = new OllamaAccountPool({ store: ollamaStore });
|
|
@@ -43778,92 +45133,48 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
43778
45133
|
fetchFn: modelFetch.ollama,
|
|
43779
45134
|
attachments: ctx.attachments
|
|
43780
45135
|
}, ollamaAccountPool);
|
|
43781
|
-
|
|
43782
|
-
|
|
43783
|
-
|
|
43784
|
-
|
|
43785
|
-
|
|
43786
|
-
|
|
43787
|
-
|
|
43788
|
-
|
|
43789
|
-
|
|
43790
|
-
|
|
43791
|
-
|
|
43792
|
-
|
|
43793
|
-
const
|
|
43794
|
-
|
|
43795
|
-
|
|
43796
|
-
|
|
43797
|
-
|
|
43798
|
-
|
|
43799
|
-
} catch (error) {
|
|
43800
|
-
kimiCodeConflict = error instanceof Error ? error.message : String(error);
|
|
43801
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$3}" is already owned by another adapter; ${PROVIDER_NAME$3} models keep being served by that one until its configuration is removed (${kimiCodeConflict})`);
|
|
43802
|
-
}
|
|
43803
|
-
};
|
|
43804
|
-
claimKimiCodeRoute();
|
|
43805
|
-
const kimiCodeRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43806
|
-
claimKimiCodeRoute();
|
|
43807
|
-
}) : void 0;
|
|
43808
|
-
const claimOllamaRoute = () => {
|
|
43809
|
-
if (ollamaRegistration !== void 0) return;
|
|
43810
|
-
try {
|
|
43811
|
-
ollamaRegistration = ctx.llm.registerAdapter([PROVIDER_ID$4], ollamaAdapter);
|
|
43812
|
-
if (ollamaConflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] ${PROVIDER_NAME$4} route "${PROVIDER_ID$4}" is now served by this plugin`);
|
|
43813
|
-
ollamaConflict = null;
|
|
43814
|
-
} catch (error) {
|
|
43815
|
-
ollamaConflict = error instanceof Error ? error.message : String(error);
|
|
43816
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$4}" is already owned by another adapter; ${PROVIDER_NAME$4} models keep being served by that one until its configuration is removed (${ollamaConflict})`);
|
|
43817
|
-
}
|
|
43818
|
-
};
|
|
43819
|
-
claimOllamaRoute();
|
|
45136
|
+
const kimiCodeClaim = claimProviderRoute(ctx, {
|
|
45137
|
+
providerId: PROVIDER_ID$3,
|
|
45138
|
+
label: PROVIDER_NAME$3,
|
|
45139
|
+
adapter: new KimiCodeAdapter(kimiCodeStore, kimiCodeModelSettings, kimiCodePreferences, {
|
|
45140
|
+
fetchFn: modelFetch["kimi-code"],
|
|
45141
|
+
attachments: ctx.attachments,
|
|
45142
|
+
videos: { readVideo: async (ref) => ({
|
|
45143
|
+
data: await readVideoBytes(ref),
|
|
45144
|
+
mediaType: ref.mediaType
|
|
45145
|
+
}) }
|
|
45146
|
+
}, kimiCodeAccountPool)
|
|
45147
|
+
});
|
|
45148
|
+
const ollamaClaim = claimProviderRoute(ctx, {
|
|
45149
|
+
providerId: PROVIDER_ID$4,
|
|
45150
|
+
label: PROVIDER_NAME$4,
|
|
45151
|
+
adapter: ollamaAdapter,
|
|
45152
|
+
watch: false
|
|
45153
|
+
});
|
|
43820
45154
|
const disposeOllamaRoutes = registerOllamaRoutes(ctx, {
|
|
43821
45155
|
accountPool: ollamaAccountPool,
|
|
43822
45156
|
modelSettings: ollamaModelSettings,
|
|
43823
45157
|
fetchFn: proxyFetch
|
|
43824
45158
|
});
|
|
43825
|
-
const
|
|
43826
|
-
|
|
43827
|
-
|
|
43828
|
-
|
|
43829
|
-
|
|
43830
|
-
commandCodeConflict = null;
|
|
43831
|
-
} catch (error) {
|
|
43832
|
-
commandCodeConflict = error instanceof Error ? error.message : String(error);
|
|
43833
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$5}" is already owned by another adapter; ${PROVIDER_NAME$5} models keep being served by that one until its configuration is removed (${commandCodeConflict})`);
|
|
43834
|
-
}
|
|
43835
|
-
};
|
|
43836
|
-
claimCommandCodeRoute();
|
|
43837
|
-
const commandCodeRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43838
|
-
claimCommandCodeRoute();
|
|
43839
|
-
}) : void 0;
|
|
45159
|
+
const commandCodeClaim = claimProviderRoute(ctx, {
|
|
45160
|
+
providerId: PROVIDER_ID$5,
|
|
45161
|
+
label: PROVIDER_NAME$5,
|
|
45162
|
+
adapter: commandCodeAdapter
|
|
45163
|
+
});
|
|
43840
45164
|
const disposeKimiCodeRoutes = registerKimiCodeRoutes(ctx, kimiCodeStore, kimiCodeModelSettings, kimiCodePreferences, {
|
|
43841
45165
|
fetchFn: proxyFetch,
|
|
43842
|
-
serving: () =>
|
|
43843
|
-
conflict: () =>
|
|
45166
|
+
serving: () => kimiCodeClaim.serving(),
|
|
45167
|
+
conflict: () => kimiCodeClaim.conflict()
|
|
43844
45168
|
}, kimiCodeAccountPool);
|
|
43845
|
-
const
|
|
43846
|
-
|
|
43847
|
-
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
43854
|
-
try {
|
|
43855
|
-
minimaxCodeRegistration = ctx.llm.registerAdapter([PROVIDER_ID$2], minimaxCodeAdapter);
|
|
43856
|
-
if (minimaxCodeConflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] ${PROVIDER_NAME$2} route "${PROVIDER_ID$2}" is now served by this plugin`);
|
|
43857
|
-
minimaxCodeConflict = null;
|
|
43858
|
-
} catch (error) {
|
|
43859
|
-
minimaxCodeConflict = error instanceof Error ? error.message : String(error);
|
|
43860
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$2}" is already owned by another adapter; ${PROVIDER_NAME$2} models keep being served by that one until its configuration is removed (${minimaxCodeConflict})`);
|
|
43861
|
-
}
|
|
43862
|
-
};
|
|
43863
|
-
claimMinimaxCodeRoute();
|
|
43864
|
-
const minimaxCodeRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43865
|
-
claimMinimaxCodeRoute();
|
|
43866
|
-
}) : void 0;
|
|
45169
|
+
const minimaxCodeClaim = claimProviderRoute(ctx, {
|
|
45170
|
+
providerId: PROVIDER_ID$2,
|
|
45171
|
+
label: PROVIDER_NAME$2,
|
|
45172
|
+
adapter: new MinimaxCodeAdapter(minimaxCodeStore, {
|
|
45173
|
+
fetchFn: modelFetch["minimax-code"],
|
|
45174
|
+
attachments: ctx.attachments,
|
|
45175
|
+
accountPool: minimaxCodeAccountPool
|
|
45176
|
+
}, minimaxCodeModelSettings, minimaxCodePreferences)
|
|
45177
|
+
});
|
|
43867
45178
|
let minimaxCodeAppVersion;
|
|
43868
45179
|
try {
|
|
43869
45180
|
const manifest = createRequire(import.meta.url)("../package.json");
|
|
@@ -43887,33 +45198,20 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
43887
45198
|
minimaxCodeCheckinTick();
|
|
43888
45199
|
const disposeMinimaxCodeRoutes = registerMinimaxCodeRoutes(ctx, minimaxCodeStore, {
|
|
43889
45200
|
fetchFn: proxyFetch,
|
|
43890
|
-
serving: () =>
|
|
43891
|
-
conflict: () =>
|
|
45201
|
+
serving: () => minimaxCodeClaim.serving(),
|
|
45202
|
+
conflict: () => minimaxCodeClaim.conflict(),
|
|
43892
45203
|
accountPool: minimaxCodeAccountPool,
|
|
43893
45204
|
checkin: minimaxCodeCheckin
|
|
43894
45205
|
}, minimaxCodeModelSettings, minimaxCodePreferences);
|
|
43895
|
-
const
|
|
43896
|
-
|
|
43897
|
-
|
|
43898
|
-
|
|
45206
|
+
const workBuddyClaim = claimProviderRoute(ctx, {
|
|
45207
|
+
providerId: PROVIDER_ID$1,
|
|
45208
|
+
label: PROVIDER_NAME$1,
|
|
45209
|
+
adapter: new WorkBuddyAdapter(workBuddyStore, workBuddyModelSettings, workBuddyPreferences, {
|
|
45210
|
+
fetchFn: modelFetch.workbuddy,
|
|
45211
|
+
attachments: ctx.attachments,
|
|
45212
|
+
accountPool: workBuddyAccountPool
|
|
45213
|
+
})
|
|
43899
45214
|
});
|
|
43900
|
-
let workBuddyRegistration;
|
|
43901
|
-
let workBuddyConflict = null;
|
|
43902
|
-
const claimWorkBuddyRoute = () => {
|
|
43903
|
-
if (workBuddyRegistration !== void 0) return;
|
|
43904
|
-
try {
|
|
43905
|
-
workBuddyRegistration = ctx.llm.registerAdapter([PROVIDER_ID$1], workBuddyAdapter);
|
|
43906
|
-
if (workBuddyConflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] ${PROVIDER_NAME$1} route "${PROVIDER_ID$1}" is now served by this plugin`);
|
|
43907
|
-
workBuddyConflict = null;
|
|
43908
|
-
} catch (error) {
|
|
43909
|
-
workBuddyConflict = error instanceof Error ? error.message : String(error);
|
|
43910
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID$1}" is already owned by another adapter; ${PROVIDER_NAME$1} models keep being served by that one until its configuration is removed (${workBuddyConflict})`);
|
|
43911
|
-
}
|
|
43912
|
-
};
|
|
43913
|
-
claimWorkBuddyRoute();
|
|
43914
|
-
const workBuddyRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43915
|
-
claimWorkBuddyRoute();
|
|
43916
|
-
}) : void 0;
|
|
43917
45215
|
const workBuddyCheckin = new WorkBuddyCheckinService(workBuddyStore, {
|
|
43918
45216
|
fetchFn: proxyFetch,
|
|
43919
45217
|
settings: () => workBuddyPreferences.status().checkin,
|
|
@@ -43929,37 +45227,24 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
43929
45227
|
checkinTick();
|
|
43930
45228
|
const disposeWorkBuddyRoutes = registerWorkBuddyRoutes(ctx, workBuddyStore, workBuddyModelSettings, workBuddyPreferences, {
|
|
43931
45229
|
fetchFn: proxyFetch,
|
|
43932
|
-
serving: () =>
|
|
43933
|
-
conflict: () =>
|
|
45230
|
+
serving: () => workBuddyClaim.serving(),
|
|
45231
|
+
conflict: () => workBuddyClaim.conflict(),
|
|
43934
45232
|
accountPool: workBuddyAccountPool,
|
|
43935
45233
|
checkin: workBuddyCheckin
|
|
43936
45234
|
});
|
|
43937
|
-
const
|
|
43938
|
-
|
|
43939
|
-
|
|
43940
|
-
|
|
45235
|
+
const claudeClaim = claimProviderRoute(ctx, {
|
|
45236
|
+
providerId: PROVIDER_ID,
|
|
45237
|
+
label: PROVIDER_NAME,
|
|
45238
|
+
adapter: new ClaudeAdapter(claudeStore, claudeModelSettings, claudePreferences, {
|
|
45239
|
+
fetchFn: modelFetch.claude,
|
|
45240
|
+
attachments: ctx.attachments,
|
|
45241
|
+
accountPool: claudeAccountPool
|
|
45242
|
+
})
|
|
43941
45243
|
});
|
|
43942
|
-
let claudeRegistration;
|
|
43943
|
-
let claudeConflict = null;
|
|
43944
|
-
const claimClaudeRoute = () => {
|
|
43945
|
-
if (claudeRegistration !== void 0) return;
|
|
43946
|
-
try {
|
|
43947
|
-
claudeRegistration = ctx.llm.registerAdapter([PROVIDER_ID], claudeAdapter);
|
|
43948
|
-
if (claudeConflict !== null) ctx.logger.info(`[dsh-chatgpt-subscription] ${PROVIDER_NAME} route "${PROVIDER_ID}" is now served by this plugin`);
|
|
43949
|
-
claudeConflict = null;
|
|
43950
|
-
} catch (error) {
|
|
43951
|
-
claudeConflict = error instanceof Error ? error.message : String(error);
|
|
43952
|
-
ctx.logger.warn(`[dsh-chatgpt-subscription] provider route "${PROVIDER_ID}" is already owned by another adapter; ${PROVIDER_NAME} models keep being served by that one until its configuration is removed (${claudeConflict})`);
|
|
43953
|
-
}
|
|
43954
|
-
};
|
|
43955
|
-
claimClaudeRoute();
|
|
43956
|
-
const claudeRouteWatch = typeof ctx.on === "function" ? ctx.on("llm/adapters-updated", () => {
|
|
43957
|
-
claimClaudeRoute();
|
|
43958
|
-
}) : void 0;
|
|
43959
45244
|
const disposeClaudeRoutes = registerClaudeRoutes(ctx, claudeStore, claudeModelSettings, claudePreferences, {
|
|
43960
45245
|
fetchFn: proxyFetch,
|
|
43961
|
-
serving: () =>
|
|
43962
|
-
conflict: () =>
|
|
45246
|
+
serving: () => claudeClaim.serving(),
|
|
45247
|
+
conflict: () => claudeClaim.conflict(),
|
|
43963
45248
|
accountPool: claudeAccountPool
|
|
43964
45249
|
});
|
|
43965
45250
|
workBuddyAccountPool.syncDesktopAccounts().catch(() => void 0);
|
|
@@ -43970,7 +45255,11 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
43970
45255
|
pool: codexAccountPool
|
|
43971
45256
|
});
|
|
43972
45257
|
const usage = new UsageService(oauth, { fetchFn: proxyFetch });
|
|
43973
|
-
codexAccountPool.setQuotaBlockedUntil((account, now) => usage.blockedUntilFor(account.credentials, now));
|
|
45258
|
+
codexAccountPool.setQuotaBlockedUntil((account, now) => usage.blockedUntilFor(account.id, account.credentials, now));
|
|
45259
|
+
codexAccountPool.setQuotaSnapshot((account) => {
|
|
45260
|
+
const snapshot = usage.snapshotFor(account.id, account.credentials);
|
|
45261
|
+
return snapshot === void 0 ? void 0 : codexAccountQuota(snapshot);
|
|
45262
|
+
});
|
|
43974
45263
|
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
43975
45264
|
fetchFn: modelFetch["codex-chatgpt"],
|
|
43976
45265
|
accountPool: codexAccountPool,
|
|
@@ -44026,6 +45315,145 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
44026
45315
|
});
|
|
44027
45316
|
};
|
|
44028
45317
|
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, readRouteAudit, codexAccountPool, fetchConfiguration);
|
|
45318
|
+
const routable = (accounts) => accounts.some((account) => account.authStatus === void 0 || account.authStatus === "ok");
|
|
45319
|
+
const disposeHubOverview = registerHubOverviewRoutes(ctx, [
|
|
45320
|
+
{
|
|
45321
|
+
id: "chatgpt",
|
|
45322
|
+
providerId: PROVIDER_ID$7,
|
|
45323
|
+
canToggle: true,
|
|
45324
|
+
read: async () => {
|
|
45325
|
+
const current = preferences.status();
|
|
45326
|
+
const accounts = await codexAccountPool.listAccounts().catch(() => []);
|
|
45327
|
+
return {
|
|
45328
|
+
enabled: current.enabled !== false,
|
|
45329
|
+
accountCount: accounts.length,
|
|
45330
|
+
authenticated: routable(accounts),
|
|
45331
|
+
enabledModelCount: current.visibleModelIds.length,
|
|
45332
|
+
totalModelCount: CODEX_MODEL_CATALOG.length
|
|
45333
|
+
};
|
|
45334
|
+
}
|
|
45335
|
+
},
|
|
45336
|
+
{
|
|
45337
|
+
id: "antigravity",
|
|
45338
|
+
providerId: PROVIDER_ID$6,
|
|
45339
|
+
canToggle: true,
|
|
45340
|
+
read: async () => {
|
|
45341
|
+
const current = antigravityPreferences.status();
|
|
45342
|
+
const accounts = await antigravityAccountPool.listAccounts().catch(() => []);
|
|
45343
|
+
const total = catalogTotal((await antigravityModelSettings.read()).catalogModels.length);
|
|
45344
|
+
return {
|
|
45345
|
+
enabled: current.enabled !== false,
|
|
45346
|
+
accountCount: accounts.length,
|
|
45347
|
+
authenticated: routable(accounts),
|
|
45348
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45349
|
+
totalModelCount: total
|
|
45350
|
+
};
|
|
45351
|
+
}
|
|
45352
|
+
},
|
|
45353
|
+
{
|
|
45354
|
+
id: "command-code",
|
|
45355
|
+
providerId: PROVIDER_ID$5,
|
|
45356
|
+
canToggle: true,
|
|
45357
|
+
read: async () => {
|
|
45358
|
+
const current = commandCodePreferences.status();
|
|
45359
|
+
const accounts = await commandCodeAccountPool.listAccounts().catch(() => []);
|
|
45360
|
+
const total = catalogTotal((await commandCodeModelSettings.read()).catalogModels.length);
|
|
45361
|
+
return {
|
|
45362
|
+
enabled: current.enabled !== false,
|
|
45363
|
+
accountCount: accounts.length,
|
|
45364
|
+
authenticated: routable(accounts),
|
|
45365
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45366
|
+
totalModelCount: total
|
|
45367
|
+
};
|
|
45368
|
+
}
|
|
45369
|
+
},
|
|
45370
|
+
{
|
|
45371
|
+
id: "kimi-code",
|
|
45372
|
+
providerId: PROVIDER_ID$3,
|
|
45373
|
+
canToggle: true,
|
|
45374
|
+
read: async () => {
|
|
45375
|
+
const current = kimiCodePreferences.status();
|
|
45376
|
+
const accounts = await kimiCodeAccountPool.listAccounts().catch(() => []);
|
|
45377
|
+
const total = catalogTotal((await kimiCodeModelSettings.read()).catalogModels.length);
|
|
45378
|
+
return {
|
|
45379
|
+
enabled: current.enabled !== false,
|
|
45380
|
+
accountCount: accounts.length,
|
|
45381
|
+
authenticated: routable(accounts),
|
|
45382
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45383
|
+
totalModelCount: total
|
|
45384
|
+
};
|
|
45385
|
+
}
|
|
45386
|
+
},
|
|
45387
|
+
{
|
|
45388
|
+
id: "workbuddy",
|
|
45389
|
+
providerId: PROVIDER_ID$1,
|
|
45390
|
+
canToggle: true,
|
|
45391
|
+
read: async () => {
|
|
45392
|
+
const current = workBuddyPreferences.status();
|
|
45393
|
+
const accounts = await workBuddyAccountPool.listAccounts().catch(() => []);
|
|
45394
|
+
const total = catalogTotal(getCachedCatalog$1().length);
|
|
45395
|
+
return {
|
|
45396
|
+
enabled: current.enabled !== false,
|
|
45397
|
+
accountCount: accounts.length,
|
|
45398
|
+
authenticated: routable(accounts),
|
|
45399
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45400
|
+
totalModelCount: total
|
|
45401
|
+
};
|
|
45402
|
+
}
|
|
45403
|
+
},
|
|
45404
|
+
{
|
|
45405
|
+
id: "minimax-code",
|
|
45406
|
+
providerId: PROVIDER_ID$2,
|
|
45407
|
+
canToggle: true,
|
|
45408
|
+
read: async () => {
|
|
45409
|
+
const current = minimaxCodePreferences.status();
|
|
45410
|
+
const accounts = await minimaxCodeAccountPool.listAccounts().catch(() => []);
|
|
45411
|
+
const total = catalogTotal(catalogSize());
|
|
45412
|
+
return {
|
|
45413
|
+
enabled: current.enabled !== false,
|
|
45414
|
+
accountCount: accounts.length,
|
|
45415
|
+
authenticated: routable(accounts),
|
|
45416
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45417
|
+
totalModelCount: total
|
|
45418
|
+
};
|
|
45419
|
+
}
|
|
45420
|
+
},
|
|
45421
|
+
{
|
|
45422
|
+
id: "claude",
|
|
45423
|
+
providerId: PROVIDER_ID,
|
|
45424
|
+
canToggle: true,
|
|
45425
|
+
read: async () => {
|
|
45426
|
+
const current = claudePreferences.status();
|
|
45427
|
+
const accounts = await claudeAccountPool.listAccounts().catch(() => []);
|
|
45428
|
+
const total = catalogTotal(getCachedCatalog().length);
|
|
45429
|
+
return {
|
|
45430
|
+
enabled: current.enabled !== false,
|
|
45431
|
+
accountCount: accounts.length,
|
|
45432
|
+
authenticated: routable(accounts),
|
|
45433
|
+
enabledModelCount: total === null ? null : Math.min(current.enabledModelIds?.length ?? 0, total),
|
|
45434
|
+
totalModelCount: total
|
|
45435
|
+
};
|
|
45436
|
+
}
|
|
45437
|
+
},
|
|
45438
|
+
{
|
|
45439
|
+
id: "ollama",
|
|
45440
|
+
providerId: PROVIDER_ID$4,
|
|
45441
|
+
canToggle: false,
|
|
45442
|
+
read: async () => {
|
|
45443
|
+
const accounts = await ollamaAccountPool.listAccounts().catch(() => []);
|
|
45444
|
+
const current = ollamaModelSettings.status();
|
|
45445
|
+
const total = catalogTotal(current.catalogModels.length);
|
|
45446
|
+
const enabled = current.enabledModelIds.length === 0 ? total : current.enabledModelIds.length;
|
|
45447
|
+
return {
|
|
45448
|
+
enabled: true,
|
|
45449
|
+
accountCount: accounts.length,
|
|
45450
|
+
authenticated: routable(accounts),
|
|
45451
|
+
enabledModelCount: total === null ? null : Math.min(enabled ?? 0, total),
|
|
45452
|
+
totalModelCount: total
|
|
45453
|
+
};
|
|
45454
|
+
}
|
|
45455
|
+
}
|
|
45456
|
+
]);
|
|
44029
45457
|
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID$7], adapter);
|
|
44030
45458
|
const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
|
|
44031
45459
|
const disposeVideoTool = ctx.tools.register(createKimiVideoTool(ctx, { fetchFn: proxyFetch }));
|
|
@@ -44052,48 +45480,28 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
44052
45480
|
disposeVideoTool();
|
|
44053
45481
|
disposeAdapter();
|
|
44054
45482
|
disposeRoutes();
|
|
45483
|
+
disposeHubOverview();
|
|
44055
45484
|
disposeAntigravityRoutes();
|
|
44056
|
-
|
|
44057
|
-
antigravityRegistration?.();
|
|
44058
|
-
antigravityRegistration = void 0;
|
|
45485
|
+
antigravityClaim.dispose();
|
|
44059
45486
|
disposeOllamaRoutes();
|
|
44060
|
-
|
|
44061
|
-
ollamaRegistration = void 0;
|
|
45487
|
+
ollamaClaim.dispose();
|
|
44062
45488
|
disposeCommandCodeRoutes();
|
|
44063
|
-
|
|
44064
|
-
commandCodeRegistration?.();
|
|
44065
|
-
commandCodeRegistration = void 0;
|
|
45489
|
+
commandCodeClaim.dispose();
|
|
44066
45490
|
disposeKimiCodeRoutes();
|
|
44067
|
-
|
|
44068
|
-
kimiCodeRegistration?.();
|
|
44069
|
-
kimiCodeRegistration = void 0;
|
|
45491
|
+
kimiCodeClaim.dispose();
|
|
44070
45492
|
disposeMinimaxCodeRoutes();
|
|
44071
|
-
|
|
44072
|
-
minimaxCodeRegistration?.();
|
|
44073
|
-
minimaxCodeRegistration = void 0;
|
|
45493
|
+
minimaxCodeClaim.dispose();
|
|
44074
45494
|
clearInterval(minimaxCodeCheckinTimer);
|
|
44075
45495
|
clearInterval(checkinTimer);
|
|
44076
45496
|
disposeWorkBuddyRoutes();
|
|
44077
|
-
|
|
44078
|
-
workBuddyRegistration?.();
|
|
44079
|
-
workBuddyRegistration = void 0;
|
|
45497
|
+
workBuddyClaim.dispose();
|
|
44080
45498
|
disposeClaudeRoutes();
|
|
44081
|
-
|
|
44082
|
-
claudeRegistration?.();
|
|
44083
|
-
claudeRegistration = void 0;
|
|
45499
|
+
claudeClaim.dispose();
|
|
44084
45500
|
oauth.dispose();
|
|
44085
45501
|
proxyManager.dispose();
|
|
44086
45502
|
};
|
|
44087
45503
|
}, "dsh-chatgpt-subscription: adapter, routes, and lifecycle");
|
|
44088
45504
|
}
|
|
44089
|
-
/** Cordis event handles are either a disposer function or a disposable object. */
|
|
44090
|
-
function releaseHandle(handle) {
|
|
44091
|
-
if (typeof handle === "function") {
|
|
44092
|
-
handle();
|
|
44093
|
-
return;
|
|
44094
|
-
}
|
|
44095
|
-
handle?.dispose?.();
|
|
44096
|
-
}
|
|
44097
45505
|
function localWebServerBaseUrl(host, port) {
|
|
44098
45506
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
44099
45507
|
}
|