@eddyskywalker/dsh-chatgpt-subscription 0.6.5 → 0.7.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 +8 -0
- package/lib/client.js +99 -0
- package/lib/client.js.map +1 -1
- package/lib/index.js +245 -40
- package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/locales.d.ts +23 -2
- package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
- package/lib/types/host/codex-search.d.ts.map +1 -1
- package/lib/types/host/common/account-pool.d.ts +42 -0
- package/lib/types/host/common/account-pool.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts +40 -3
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
- package/lib/types/host/oauth-service.d.ts +29 -1
- package/lib/types/host/oauth-service.d.ts.map +1 -1
- package/lib/types/host/preset-sync.d.ts +5 -0
- package/lib/types/host/preset-sync.d.ts.map +1 -1
- package/lib/types/host/usage-service.d.ts.map +1 -1
- package/lib/types/shared/kimi-code-contracts.d.ts +8 -0
- package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2,12 +2,12 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import * as LlmModule from "@deepseek-ai/dsh-llm";
|
|
4
4
|
import { HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
5
|
-
import path, { basename, dirname, join, relative } from "node:path";
|
|
5
|
+
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
6
6
|
import os, { homedir } from "node:os";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
import { isDeepStrictEqual } from "node:util";
|
|
9
9
|
import { execSync, spawn } from "node:child_process";
|
|
10
|
-
import fs, { appendFileSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
10
|
+
import fs, { appendFileSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
11
11
|
import fsPromises, { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
12
12
|
import http, { createServer } from "node:http";
|
|
13
13
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
@@ -887,6 +887,84 @@ var AccountPoolCore = class {
|
|
|
887
887
|
return data.accounts.some((account) => this.isEligible(account, now, triedAccountIds));
|
|
888
888
|
}
|
|
889
889
|
/**
|
|
890
|
+
* Read one account's credential for work that is *not* a metered model
|
|
891
|
+
* request, without letting rotation state stand in the way.
|
|
892
|
+
*
|
|
893
|
+
* There are two different questions, and conflating them broke both:
|
|
894
|
+
*
|
|
895
|
+
* - "which account should serve this request?" — a routing decision, answered
|
|
896
|
+
* by {@link getEffectiveAccount} through cooldowns, auth status and the
|
|
897
|
+
* rotation strategy.
|
|
898
|
+
* - "may I have a usable ChatGPT credential?" — a credential question, asked
|
|
899
|
+
* by the web search, web fetch and image tools.
|
|
900
|
+
*
|
|
901
|
+
* A spent Codex rate-limit window says nothing about the other ChatGPT
|
|
902
|
+
* endpoints: search, fetch and image generation are not metered against it and
|
|
903
|
+
* keep working with the very same token. Routing those tools through
|
|
904
|
+
* {@link getEffectiveAccount} made one exhausted window fail all of them —
|
|
905
|
+
* and reported it as "credentials are required", which sent people looking for
|
|
906
|
+
* a sign-in problem they did not have.
|
|
907
|
+
*
|
|
908
|
+
* A credential that is *known* to be unusable is still refused (a rejected
|
|
909
|
+
* refresh token, an account the upstream already rejected at sign-in); only
|
|
910
|
+
* cooldowns — which are rotation bookkeeping — are ignored.
|
|
911
|
+
*
|
|
912
|
+
* @param fetchFn - fetch used if the credential is close to expiry.
|
|
913
|
+
*/
|
|
914
|
+
async getCredentialAccount(fetchFn = fetch) {
|
|
915
|
+
const data = await this.read();
|
|
916
|
+
if (data.accounts.length === 0) throw new Error(this.hooks.emptyMessage ?? `未登录 ${this.hooks.displayName} 账号,请先在设置页添加账号。`);
|
|
917
|
+
const now = Date.now();
|
|
918
|
+
const usable = data.accounts.filter((account) => this.isCredentialUsable(account));
|
|
919
|
+
if (usable.length === 0) throw new LlmError(`全部 ${data.accounts.length} 个 ${this.hooks.displayName} 账号均需要重新登录。`, "AUTH", { status: 401 });
|
|
920
|
+
const selected = this.selectAccount(usable, data);
|
|
921
|
+
if (this.shouldRefreshCredential(selected, now) && this.hooks.refresh) {
|
|
922
|
+
const refreshed = await this.hooks.refresh(selected.credentials, fetchFn);
|
|
923
|
+
await this.updateAccountCredentials(selected.id, refreshed);
|
|
924
|
+
return {
|
|
925
|
+
account: selected,
|
|
926
|
+
credentials: refreshed
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return {
|
|
930
|
+
account: selected,
|
|
931
|
+
credentials: selected.credentials
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
/** Whether one account's credential must be refreshed before it is handed out. */
|
|
935
|
+
shouldRefreshCredential(account, now) {
|
|
936
|
+
if (this.hooks.needsRefresh) return this.hooks.needsRefresh(account.credentials, now);
|
|
937
|
+
const expires = this.hooks.expiresAt?.(account.credentials);
|
|
938
|
+
return expires !== void 0 && expires <= now + 6e4;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Whether an account's stored credential is known to be unusable.
|
|
942
|
+
*
|
|
943
|
+
* Rotation bookkeeping — cooldowns, the tried set — is deliberately absent:
|
|
944
|
+
* those describe routing, not the credential.
|
|
945
|
+
*/
|
|
946
|
+
isCredentialUsable(account) {
|
|
947
|
+
if (account.authStatus !== void 0 && account.authStatus !== "ok") return false;
|
|
948
|
+
if (this.hooks.authRejectedReason?.(account.credentials)) return false;
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
/** The account a rotation strategy picks out of an already-eligible set. */
|
|
952
|
+
selectAccount(eligible, data) {
|
|
953
|
+
const pinned = this.hooks.preferAccountId?.() ?? null;
|
|
954
|
+
const pinnedAccount = pinned === null ? void 0 : eligible.find((account) => account.id === pinned);
|
|
955
|
+
if (pinnedAccount !== void 0) return pinnedAccount;
|
|
956
|
+
if (data.rotationStrategy === "round-robin") return [...eligible].sort((a, b) => (a.lastUsedAt || 0) - (b.lastUsedAt || 0))[0];
|
|
957
|
+
if (data.rotationStrategy === "sticky") return eligible.find((account) => account.id === data.activeAccountId) || eligible.find((account) => account.isPrimary) || eligible[0];
|
|
958
|
+
return eligible.find((account) => account.isPrimary) || eligible[0];
|
|
959
|
+
}
|
|
960
|
+
/** The error every-account-unavailable raises, including the shortest wait. */
|
|
961
|
+
allUnavailableError(data, now) {
|
|
962
|
+
let shortest = Infinity;
|
|
963
|
+
for (const account of data.accounts) if (account.cooldownUntil !== void 0 && account.cooldownUntil > now) shortest = Math.min(shortest, account.cooldownUntil - now);
|
|
964
|
+
const waitMinutes = Number.isFinite(shortest) ? Math.ceil(shortest / 6e4) : 15;
|
|
965
|
+
return new LlmError(this.hooks.allUnavailableMessage?.(data.accounts.length, waitMinutes) ?? `全部 ${data.accounts.length} 个 ${this.hooks.displayName} 账号均处于配额限制或冷却中 (429)。最短预计在 ${waitMinutes} 分钟后解除冷却。`, "RATE_LIMIT", { status: 429 });
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
890
968
|
* Pick the account for the next request, refreshing its credential when it is
|
|
891
969
|
* about to expire.
|
|
892
970
|
*
|
|
@@ -900,23 +978,9 @@ var AccountPoolCore = class {
|
|
|
900
978
|
if (data.accounts.length === 0) throw new Error(this.hooks.emptyMessage ?? `未登录 ${this.hooks.displayName} 账号,请先在设置页添加账号。`);
|
|
901
979
|
const now = Date.now();
|
|
902
980
|
const eligible = data.accounts.filter((account) => this.isEligible(account, now, excludeIds));
|
|
903
|
-
if (eligible.length === 0)
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
const waitMinutes = Number.isFinite(shortest) ? Math.ceil(shortest / 6e4) : 15;
|
|
907
|
-
throw new LlmError(this.hooks.allUnavailableMessage?.(data.accounts.length, waitMinutes) ?? `全部 ${data.accounts.length} 个 ${this.hooks.displayName} 账号均处于配额限制或冷却中 (429)。最短预计在 ${waitMinutes} 分钟后解除冷却。`, "RATE_LIMIT", { status: 429 });
|
|
908
|
-
}
|
|
909
|
-
const pinned = this.hooks.preferAccountId?.() ?? null;
|
|
910
|
-
const pinnedAccount = pinned === null ? void 0 : eligible.find((account) => account.id === pinned);
|
|
911
|
-
let selected;
|
|
912
|
-
if (pinnedAccount !== void 0) selected = pinnedAccount;
|
|
913
|
-
else if (data.rotationStrategy === "round-robin") selected = [...eligible].sort((a, b) => (a.lastUsedAt || 0) - (b.lastUsedAt || 0))[0];
|
|
914
|
-
else if (data.rotationStrategy === "sticky") selected = eligible.find((account) => account.id === data.activeAccountId) || eligible.find((account) => account.isPrimary) || eligible[0];
|
|
915
|
-
else selected = eligible.find((account) => account.isPrimary) || eligible[0];
|
|
916
|
-
if ((this.hooks.needsRefresh ? this.hooks.needsRefresh(selected.credentials, now) : (() => {
|
|
917
|
-
const expires = this.hooks.expiresAt?.(selected.credentials);
|
|
918
|
-
return expires !== void 0 && expires <= now + 6e4;
|
|
919
|
-
})()) && this.hooks.refresh) {
|
|
981
|
+
if (eligible.length === 0) throw this.allUnavailableError(data, now);
|
|
982
|
+
const selected = this.selectAccount(eligible, data);
|
|
983
|
+
if (this.shouldRefreshCredential(selected, now) && this.hooks.refresh) {
|
|
920
984
|
let refreshed;
|
|
921
985
|
try {
|
|
922
986
|
refreshed = await this.hooks.refresh(selected.credentials, fetchFn);
|
|
@@ -1335,8 +1399,26 @@ var OAuthService = class {
|
|
|
1335
1399
|
this.lastLoginError = void 0;
|
|
1336
1400
|
this.logger.info("[dsh-chatgpt-subscription] OAuth credentials cleared");
|
|
1337
1401
|
}
|
|
1338
|
-
|
|
1402
|
+
/**
|
|
1403
|
+
* The credential to use next.
|
|
1404
|
+
*
|
|
1405
|
+
* @param forceRefresh - refresh even a token that is not close to expiry,
|
|
1406
|
+
* which is how a 401 discovered mid-request recovers.
|
|
1407
|
+
* @param access - who is asking. A `'tool'` caller (web search, fetch, image
|
|
1408
|
+
* generation) bypasses the pool's rotation state: an account cooling down
|
|
1409
|
+
* after a Codex 429 still holds a working token for those endpoints, and
|
|
1410
|
+
* refusing it there turned "your Codex window is spent" into a bogus
|
|
1411
|
+
* "credentials are required" on every web search.
|
|
1412
|
+
*/
|
|
1413
|
+
async credentials(forceRefresh = false, access = {}) {
|
|
1339
1414
|
if (this.pool !== null) {
|
|
1415
|
+
if (access.purpose === "tool") {
|
|
1416
|
+
const { account, credentials } = await this.pool.getCredentialAccount(this.fetchFn);
|
|
1417
|
+
if (!forceRefresh) return credentials;
|
|
1418
|
+
const refreshed = await this.refreshAccount(credentials, this.fetchFn);
|
|
1419
|
+
await this.pool.updateAccountCredentials(account.id, refreshed);
|
|
1420
|
+
return refreshed;
|
|
1421
|
+
}
|
|
1340
1422
|
const { account, credentials } = await this.pool.getEffectiveAccount(void 0, this.fetchFn);
|
|
1341
1423
|
if (!forceRefresh) return credentials;
|
|
1342
1424
|
const refreshed = await this.refreshAccount(credentials, this.fetchFn);
|
|
@@ -2266,9 +2348,9 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
2266
2348
|
}
|
|
2267
2349
|
async function imageCredentials(oauth, force = false) {
|
|
2268
2350
|
try {
|
|
2269
|
-
return await oauth.credentials(force);
|
|
2351
|
+
return await oauth.credentials(force, { purpose: "tool" });
|
|
2270
2352
|
} catch (error) {
|
|
2271
|
-
throw new HarnessError(
|
|
2353
|
+
throw new HarnessError(`ChatGPT subscription credentials are required for Codex image generation.${error instanceof Error && error.message !== "" ? ` (${error.message})` : ""}`, "CODEX_IMAGE_CREDENTIAL_MISSING", { cause: error });
|
|
2272
2354
|
}
|
|
2273
2355
|
}
|
|
2274
2356
|
function requestImage(fetchFn, credentials, prompt, turnId, signal) {
|
|
@@ -2351,11 +2433,26 @@ function createCodexSearchProvider(oauth, options = {}) {
|
|
|
2351
2433
|
}
|
|
2352
2434
|
async function searchCredentials(oauth, force = false) {
|
|
2353
2435
|
try {
|
|
2354
|
-
return await oauth.credentials(force);
|
|
2436
|
+
return await oauth.credentials(force, { purpose: "tool" });
|
|
2355
2437
|
} catch (error) {
|
|
2356
|
-
throw
|
|
2438
|
+
throw credentialError(error);
|
|
2357
2439
|
}
|
|
2358
2440
|
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Map a credential failure onto the right web error.
|
|
2443
|
+
*
|
|
2444
|
+
* The old single mapping called every failure "credentials are required", so
|
|
2445
|
+
* an unavailable account sent the user hunting for a sign-in problem they did
|
|
2446
|
+
* not have. The provider's own code is preserved and the reason is carried
|
|
2447
|
+
* through, because the two failures need opposite actions: refill the quota
|
|
2448
|
+
* versus sign in again.
|
|
2449
|
+
*/
|
|
2450
|
+
function credentialError(error) {
|
|
2451
|
+
const reason = error instanceof Error && error.message !== "" ? error.message : "";
|
|
2452
|
+
if (error instanceof LlmError && error.code === "RATE_LIMIT") return new WebError(reason === "" ? "Codex search is rate limited." : `Codex search is rate limited. ${reason}`, "WEB_PROVIDER_RATE_LIMITED", { cause: error });
|
|
2453
|
+
if (error instanceof LlmError && (error.code === "AUTH" || error.code === "INVALID_CREDENTIAL")) return new WebError(reason === "" ? "Sign in with ChatGPT to use Codex search." : `Sign in with ChatGPT to use Codex search. ${reason}`, "WEB_PROVIDER_CREDENTIAL_MISSING", { cause: error });
|
|
2454
|
+
return new WebError(reason === "" ? "ChatGPT subscription credentials are required for Codex search." : `ChatGPT subscription credentials are required for Codex search. (${reason})`, "WEB_PROVIDER_CREDENTIAL_MISSING", { cause: error });
|
|
2455
|
+
}
|
|
2359
2456
|
function sendSearch(fetchFn, credentials, query, model, id, signal) {
|
|
2360
2457
|
return fetchFn(CODEX_SEARCH_URL, {
|
|
2361
2458
|
method: "POST",
|
|
@@ -3852,6 +3949,15 @@ function acceptIdentity(current, incoming) {
|
|
|
3852
3949
|
}
|
|
3853
3950
|
//#endregion
|
|
3854
3951
|
//#region src/host/usage-service.ts
|
|
3952
|
+
/**
|
|
3953
|
+
* The quota card reads the same account the conversation uses, but it must keep
|
|
3954
|
+
* reading it while that account is cooling down — otherwise the card that exists
|
|
3955
|
+
* to explain a spent window is the first thing the spent window breaks.
|
|
3956
|
+
*
|
|
3957
|
+
* Every credential this service asks for is therefore a `'tool'` read: not a
|
|
3958
|
+
* metered model request, so rotation cooldowns do not apply.
|
|
3959
|
+
*/
|
|
3960
|
+
const USAGE_CREDENTIAL_ACCESS = { purpose: "tool" };
|
|
3855
3961
|
const EMPTY_USAGE = {
|
|
3856
3962
|
buckets: [],
|
|
3857
3963
|
credits: null,
|
|
@@ -3885,7 +3991,7 @@ var UsageService = class {
|
|
|
3885
3991
|
const now = this.now();
|
|
3886
3992
|
let credentials;
|
|
3887
3993
|
try {
|
|
3888
|
-
credentials = await this.oauth.credentials();
|
|
3994
|
+
credentials = await this.oauth.credentials(false, USAGE_CREDENTIAL_ACCESS);
|
|
3889
3995
|
} catch (error) {
|
|
3890
3996
|
const reason = error instanceof Error && error.message !== "" ? ` (${error.message})` : "";
|
|
3891
3997
|
return this.failure({
|
|
@@ -3963,11 +4069,11 @@ var UsageService = class {
|
|
|
3963
4069
|
async consumeResetCreditUpstream() {
|
|
3964
4070
|
let credentials;
|
|
3965
4071
|
try {
|
|
3966
|
-
credentials = await this.oauth.credentials();
|
|
4072
|
+
credentials = await this.oauth.credentials(false, USAGE_CREDENTIAL_ACCESS);
|
|
3967
4073
|
let creditsResponse = await this.fetchResetCredits(credentials);
|
|
3968
4074
|
if (creditsResponse.status === 401) {
|
|
3969
4075
|
await creditsResponse.body?.cancel().catch(() => void 0);
|
|
3970
|
-
credentials = await this.oauth.credentials(true);
|
|
4076
|
+
credentials = await this.oauth.credentials(true, USAGE_CREDENTIAL_ACCESS);
|
|
3971
4077
|
creditsResponse = await this.fetchResetCredits(credentials);
|
|
3972
4078
|
}
|
|
3973
4079
|
if (!creditsResponse.ok) {
|
|
@@ -3998,7 +4104,7 @@ var UsageService = class {
|
|
|
3998
4104
|
});
|
|
3999
4105
|
if (consumeResponse.status === 401) {
|
|
4000
4106
|
await consumeResponse.body?.cancel().catch(() => void 0);
|
|
4001
|
-
credentials = await this.oauth.credentials(true);
|
|
4107
|
+
credentials = await this.oauth.credentials(true, USAGE_CREDENTIAL_ACCESS);
|
|
4002
4108
|
consumeResponse = await this.fetchFn(CODEX_RESET_CREDITS_CONSUME_URL, {
|
|
4003
4109
|
method: "POST",
|
|
4004
4110
|
headers: {
|
|
@@ -4057,7 +4163,7 @@ var UsageService = class {
|
|
|
4057
4163
|
let response = await this.fetch(credentials);
|
|
4058
4164
|
if (response.status === 401) {
|
|
4059
4165
|
await response.body?.cancel().catch(() => void 0);
|
|
4060
|
-
credentials = await this.oauth.credentials(true);
|
|
4166
|
+
credentials = await this.oauth.credentials(true, USAGE_CREDENTIAL_ACCESS);
|
|
4061
4167
|
accountKey = identityKey(credentials);
|
|
4062
4168
|
response = await this.fetch(credentials);
|
|
4063
4169
|
}
|
|
@@ -13972,6 +14078,16 @@ function isToolResultMessage$1(message) {
|
|
|
13972
14078
|
* so a `system` message used as a tool-declaration carrier must stay
|
|
13973
14079
|
* content-less: adding text to it would both move that text to the front of the
|
|
13974
14080
|
* request and give the declaration a `content` field the service forbids.
|
|
14081
|
+
*
|
|
14082
|
+
* Cache-stability invariant: the system prompt is the head of the prefix the
|
|
14083
|
+
* service caches, so this function must be a pure, order-stable fold of its
|
|
14084
|
+
* inputs. `options.system` always leads and message text follows in history
|
|
14085
|
+
* order — never re-sorted, never re-dated, never decorated with per-turn
|
|
14086
|
+
* metadata. Any value that changes turn-over-turn belongs in a trailing user
|
|
14087
|
+
* message (where the harness's time/snapshot injectors already put it), not in
|
|
14088
|
+
* this head, because one changed byte here invalidates the whole cached prefix
|
|
14089
|
+
* and collapses the hit rate. `trackPrefixStability` fingerprints this exact
|
|
14090
|
+
* string so a drift is attributed rather than silent.
|
|
13975
14091
|
*/
|
|
13976
14092
|
function leadingSystemText$1(options) {
|
|
13977
14093
|
const parts = [];
|
|
@@ -14409,18 +14525,17 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$1, preserveThin
|
|
|
14409
14525
|
/**
|
|
14410
14526
|
* Stable identifier for the conversation this request belongs to.
|
|
14411
14527
|
*
|
|
14412
|
-
*
|
|
14413
|
-
*
|
|
14414
|
-
*
|
|
14528
|
+
* The key is derived from the session identifier alone — never from message
|
|
14529
|
+
* content. The official agent keys its cache on the conversation (its
|
|
14530
|
+
* sessionId) so the key survives context compaction and multimodal churn
|
|
14531
|
+
* byte-for-byte. Deriving it from the first user message (as a fallback once
|
|
14532
|
+
* did) produces a key that CHANGES the moment compaction rewrites that first
|
|
14533
|
+
* message, silently re-routing the whole session to a cold cache entry and
|
|
14534
|
+
* collapsing the hit rate. A session that has no identity yet sends no key at
|
|
14535
|
+
* all rather than a key that will drift.
|
|
14415
14536
|
*/
|
|
14416
14537
|
function promptCacheKey(options) {
|
|
14417
14538
|
if (typeof options.sessionId === "string" && options.sessionId.trim() !== "") return `dsh-${options.sessionId.trim()}`;
|
|
14418
|
-
for (const message of options.messages) {
|
|
14419
|
-
if (message.role !== "user") continue;
|
|
14420
|
-
const text = textOf$1(message.content);
|
|
14421
|
-
if (text === "") continue;
|
|
14422
|
-
return `dsh-${createHash("sha256").update(text).digest("hex").slice(0, 32)}`;
|
|
14423
|
-
}
|
|
14424
14539
|
}
|
|
14425
14540
|
function anthropicUserContent(message, images) {
|
|
14426
14541
|
if (!Array.isArray(message.content)) return [];
|
|
@@ -14561,6 +14676,7 @@ function buildAnthropicRequest(options, images = NO_RESOLVED_IMAGES$1, _media =
|
|
|
14561
14676
|
}
|
|
14562
14677
|
/** Build the body for whichever endpoint serves `wire`. */
|
|
14563
14678
|
function buildRequest(options, wire, images = NO_RESOLVED_IMAGES$1, preserveThinking = preserveThinkingEnabled(), media = {}) {
|
|
14679
|
+
recordDriftCause(trackPrefixStability(options));
|
|
14564
14680
|
return wire === "anthropic" ? buildAnthropicRequest(options, images, media) : buildOpenAIRequest(options, images, preserveThinking, media);
|
|
14565
14681
|
}
|
|
14566
14682
|
/**
|
|
@@ -14943,6 +15059,65 @@ function processAnthropicStreamLine(line, state) {
|
|
|
14943
15059
|
if (type === "error") throw new LlmError(`Kimi Code stream error: ${asString$4((isRecord$4(event.error) ? event.error : {}).message) ?? "unknown error"}`, "PROVIDER_ERROR");
|
|
14944
15060
|
return out;
|
|
14945
15061
|
}
|
|
15062
|
+
/**
|
|
15063
|
+
* A short, stable fingerprint of one prefix-breaking input.
|
|
15064
|
+
*
|
|
15065
|
+
* The prefix cache is invalidated as a whole whenever the system prompt or the
|
|
15066
|
+
* tool list changes, so these are hashed (not stored verbatim) and compared
|
|
15067
|
+
* request-over-request. A 32-bit FNV-1a keeps this dependency-free; collisions
|
|
15068
|
+
* only ever produce a missed *diagnostic*, never a wrong request.
|
|
15069
|
+
*/
|
|
15070
|
+
function fingerprint(text) {
|
|
15071
|
+
let hash = 2166136261;
|
|
15072
|
+
for (let index = 0; index < text.length; index++) {
|
|
15073
|
+
hash ^= text.charCodeAt(index);
|
|
15074
|
+
hash = Math.imul(hash, 16777619);
|
|
15075
|
+
}
|
|
15076
|
+
return (hash >>> 0).toString(16);
|
|
15077
|
+
}
|
|
15078
|
+
let lastPrefixSnapshot = {
|
|
15079
|
+
systemPromptHash: null,
|
|
15080
|
+
toolsHash: null,
|
|
15081
|
+
cacheKey: null
|
|
15082
|
+
};
|
|
15083
|
+
/**
|
|
15084
|
+
* Fingerprint the current request's stability inputs and diff them against the
|
|
15085
|
+
* last request. Called once per request build; the result is read back when the
|
|
15086
|
+
* stream reports usage so a low-or-zero cache read can be explained.
|
|
15087
|
+
*/
|
|
15088
|
+
function trackPrefixStability(options) {
|
|
15089
|
+
const systemText = leadingSystemText$1(options) ?? "";
|
|
15090
|
+
const systemPromptHash = systemText === "" ? null : fingerprint(systemText);
|
|
15091
|
+
const tools = options.tools ?? [];
|
|
15092
|
+
const toolsHash = tools.length === 0 ? null : fingerprint(JSON.stringify(tools.map((tool) => [
|
|
15093
|
+
tool.name,
|
|
15094
|
+
tool.description ?? "",
|
|
15095
|
+
tool.parameters ?? {}
|
|
15096
|
+
])));
|
|
15097
|
+
const cacheKey = promptCacheKey(options) ?? null;
|
|
15098
|
+
const previous = lastPrefixSnapshot;
|
|
15099
|
+
lastPrefixSnapshot = {
|
|
15100
|
+
systemPromptHash,
|
|
15101
|
+
toolsHash,
|
|
15102
|
+
cacheKey
|
|
15103
|
+
};
|
|
15104
|
+
if (previous.systemPromptHash === null && previous.toolsHash === null && previous.cacheKey === null) return "first-request";
|
|
15105
|
+
if (cacheKey === null) return "cold-key";
|
|
15106
|
+
if (previous.cacheKey !== cacheKey) return "cache-key";
|
|
15107
|
+
if (previous.systemPromptHash !== systemPromptHash) return "system-prompt";
|
|
15108
|
+
if (previous.toolsHash !== toolsHash) return "tools";
|
|
15109
|
+
return "stable";
|
|
15110
|
+
}
|
|
15111
|
+
/** Last recorded drift cause, for the status surface. */
|
|
15112
|
+
let lastDriftCause = "first-request";
|
|
15113
|
+
/** Record the cause computed for the in-flight request. */
|
|
15114
|
+
function recordDriftCause(cause) {
|
|
15115
|
+
lastDriftCause = cause;
|
|
15116
|
+
}
|
|
15117
|
+
/** The cause attributed to the most recent request. */
|
|
15118
|
+
function getLastDriftCause() {
|
|
15119
|
+
return lastDriftCause;
|
|
15120
|
+
}
|
|
14946
15121
|
let cacheStats = {
|
|
14947
15122
|
requests: 0,
|
|
14948
15123
|
cachedTokens: 0,
|
|
@@ -15687,7 +15862,11 @@ async function getKimiCodeWebStatus(store, modelSettings, preferences, options =
|
|
|
15687
15862
|
/** Rolling cache totals, or null while no request has reported usage yet. */
|
|
15688
15863
|
function cacheStatsOrNull() {
|
|
15689
15864
|
const stats = getCacheStats();
|
|
15690
|
-
|
|
15865
|
+
if (stats.requests === 0) return null;
|
|
15866
|
+
return {
|
|
15867
|
+
...stats,
|
|
15868
|
+
lastDrift: getLastDriftCause()
|
|
15869
|
+
};
|
|
15691
15870
|
}
|
|
15692
15871
|
/** Register the Kimi Code settings routes under `/kimi-code/api`. */
|
|
15693
15872
|
function registerKimiCodeRoutes(ctx, store, modelSettings, preferences, options = {}, accountPool = options.accountPool) {
|
|
@@ -20521,10 +20700,36 @@ function dshHomeDir$1() {
|
|
|
20521
20700
|
return process.env.DSH_HOME?.trim() || join(homedir(), ".dsh");
|
|
20522
20701
|
}
|
|
20523
20702
|
/**
|
|
20703
|
+
* The harness entry script's directory, when this process is the harness.
|
|
20704
|
+
*
|
|
20705
|
+
* The harness keeps its own packages in the `node_modules` tree of its
|
|
20706
|
+
* installation — a global npm prefix, an npx cache, or a checkout — which no
|
|
20707
|
+
* profile directory, cwd, or plugin package root necessarily reaches: the
|
|
20708
|
+
* plugin lives in the profile's own dependency tree, and Node resolution from
|
|
20709
|
+
* here never crosses into the harness's. The CLI entry script always sits
|
|
20710
|
+
* inside that tree, so its directory is the one candidate guaranteed to reach
|
|
20711
|
+
* the packages the harness ships. Symlinks are resolved because an npm bin
|
|
20712
|
+
* shim may point into the tree from outside it.
|
|
20713
|
+
* @param entryScript - `process.argv[1]` of the running harness.
|
|
20714
|
+
* @returns the entry script's real directory, or `undefined` when there is none.
|
|
20715
|
+
*/
|
|
20716
|
+
function harnessEntryDir(entryScript) {
|
|
20717
|
+
if (!entryScript) return void 0;
|
|
20718
|
+
const absolute = resolve(entryScript);
|
|
20719
|
+
try {
|
|
20720
|
+
return dirname(realpathSync(absolute));
|
|
20721
|
+
} catch {
|
|
20722
|
+
return dirname(absolute);
|
|
20723
|
+
}
|
|
20724
|
+
}
|
|
20725
|
+
/**
|
|
20524
20726
|
* Candidate directories where the running harness or profile packages reside.
|
|
20727
|
+
* @param entryScript - harness CLI entry script, defaults to this process's.
|
|
20525
20728
|
*/
|
|
20526
|
-
function candidatePackageRoots() {
|
|
20729
|
+
function candidatePackageRoots(entryScript = process.argv[1]) {
|
|
20527
20730
|
const roots = /* @__PURE__ */ new Set();
|
|
20731
|
+
const entryDir = harnessEntryDir(entryScript);
|
|
20732
|
+
if (entryDir !== void 0) roots.add(entryDir);
|
|
20528
20733
|
const profilesDir = join(dshHomeDir$1(), "profiles");
|
|
20529
20734
|
if (existsSync(profilesDir)) {
|
|
20530
20735
|
roots.add(profilesDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KimiCodeSection.d.ts","sourceRoot":"","sources":["../../../../src/client/kimi-code/KimiCodeSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAe/D,UAAU,KAAK;IACb,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC;AAiBD,wEAAwE;AACxE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;
|
|
1
|
+
{"version":3,"file":"KimiCodeSection.d.ts","sourceRoot":"","sources":["../../../../src/client/kimi-code/KimiCodeSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAe/D,UAAU,KAAK;IACb,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC;AAiBD,wEAAwE;AACxE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAyDD,wBAAgB,eAAe,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAspBhG"}
|
|
@@ -90,6 +90,13 @@ export declare const zh: {
|
|
|
90
90
|
readonly cacheOutput: "输出 tokens";
|
|
91
91
|
readonly cacheRequests: "请求数";
|
|
92
92
|
readonly cacheHint: "同一会话内:系统提示与工具列表一旦变化会使缓存整体失效,因此保持工具集合稳定、把新增内容追加在末尾,命中率最高。";
|
|
93
|
+
readonly cacheDrift: "最近前缀状态";
|
|
94
|
+
readonly cacheDriftFirst: "首个请求(缓存为空)";
|
|
95
|
+
readonly cacheDriftStable: "稳定(前缀未变)";
|
|
96
|
+
readonly cacheDriftSystemPrompt: "系统提示已变化(缓存失效)";
|
|
97
|
+
readonly cacheDriftTools: "工具列表已变化(缓存失效)";
|
|
98
|
+
readonly cacheDriftCacheKey: "缓存键已变化(会话切换)";
|
|
99
|
+
readonly cacheDriftColdKey: "未发送缓存键(无法命中)";
|
|
93
100
|
readonly preserveThinking: "保留思考 (Preserved Thinking)";
|
|
94
101
|
readonly preserveOn: "已开启(官方默认):跨轮保留 reasoning_content,多轮工具调用更连贯,但思考 token 也计入输出。";
|
|
95
102
|
readonly preserveOff: "已关闭:不要求服务回传推理内容。可设置 DSH_KIMI_CODE_PRESERVE_THINKING=0 关闭。";
|
|
@@ -224,6 +231,13 @@ export declare const dictionaries: {
|
|
|
224
231
|
readonly cacheOutput: "输出 tokens";
|
|
225
232
|
readonly cacheRequests: "请求数";
|
|
226
233
|
readonly cacheHint: "同一会话内:系统提示与工具列表一旦变化会使缓存整体失效,因此保持工具集合稳定、把新增内容追加在末尾,命中率最高。";
|
|
234
|
+
readonly cacheDrift: "最近前缀状态";
|
|
235
|
+
readonly cacheDriftFirst: "首个请求(缓存为空)";
|
|
236
|
+
readonly cacheDriftStable: "稳定(前缀未变)";
|
|
237
|
+
readonly cacheDriftSystemPrompt: "系统提示已变化(缓存失效)";
|
|
238
|
+
readonly cacheDriftTools: "工具列表已变化(缓存失效)";
|
|
239
|
+
readonly cacheDriftCacheKey: "缓存键已变化(会话切换)";
|
|
240
|
+
readonly cacheDriftColdKey: "未发送缓存键(无法命中)";
|
|
227
241
|
readonly preserveThinking: "保留思考 (Preserved Thinking)";
|
|
228
242
|
readonly preserveOn: "已开启(官方默认):跨轮保留 reasoning_content,多轮工具调用更连贯,但思考 token 也计入输出。";
|
|
229
243
|
readonly preserveOff: "已关闭:不要求服务回传推理内容。可设置 DSH_KIMI_CODE_PRESERVE_THINKING=0 关闭。";
|
|
@@ -356,6 +370,13 @@ export declare const dictionaries: {
|
|
|
356
370
|
readonly cacheOutput: "输出 tokens";
|
|
357
371
|
readonly cacheRequests: "请求数";
|
|
358
372
|
readonly cacheHint: "同一会话内:系统提示与工具列表一旦变化会使缓存整体失效,因此保持工具集合稳定、把新增内容追加在末尾,命中率最高。";
|
|
373
|
+
readonly cacheDrift: "最近前缀状态";
|
|
374
|
+
readonly cacheDriftFirst: "首个请求(缓存为空)";
|
|
375
|
+
readonly cacheDriftStable: "稳定(前缀未变)";
|
|
376
|
+
readonly cacheDriftSystemPrompt: "系统提示已变化(缓存失效)";
|
|
377
|
+
readonly cacheDriftTools: "工具列表已变化(缓存失效)";
|
|
378
|
+
readonly cacheDriftCacheKey: "缓存键已变化(会话切换)";
|
|
379
|
+
readonly cacheDriftColdKey: "未发送缓存键(无法命中)";
|
|
359
380
|
readonly preserveThinking: "保留思考 (Preserved Thinking)";
|
|
360
381
|
readonly preserveOn: "已开启(官方默认):跨轮保留 reasoning_content,多轮工具调用更连贯,但思考 token 也计入输出。";
|
|
361
382
|
readonly preserveOff: "已关闭:不要求服务回传推理内容。可设置 DSH_KIMI_CODE_PRESERVE_THINKING=0 关闭。";
|
|
@@ -397,8 +418,8 @@ export declare const dictionaries: {
|
|
|
397
418
|
readonly lastUsed: string;
|
|
398
419
|
readonly accountId: string;
|
|
399
420
|
};
|
|
400
|
-
en: Record<"tokens" | "title" | "pageDesc" | "account" | "signedIn" | "signedOut" | "
|
|
401
|
-
'en-US': Record<"tokens" | "title" | "pageDesc" | "account" | "signedIn" | "signedOut" | "
|
|
421
|
+
en: Record<"tokens" | "title" | "pageDesc" | "account" | "signedIn" | "signedOut" | "nickname" | "email" | "plan" | "planUnknown" | "region" | "regionMainland" | "regionGlobal" | "authenticatedAt" | "storage" | "storageNotice" | "refreshRejected" | "signIn" | "signInAgain" | "signingIn" | "signOut" | "cancelSignIn" | "deviceCodeSection" | "deviceCodeHint" | "openSignInPage" | "copyCode" | "copied" | "userCode" | "codeExpires" | "waitingAuthorization" | "requestingCode" | "connection" | "provider" | "providerValue" | "oauthHost" | "codingEndpoint" | "connectionState" | "connected" | "untested" | "testConnection" | "testingConnection" | "testSuccess" | "testFailed" | "routeOwned" | "routeConflict" | "modelsSection" | "enableProvider" | "modelsHint" | "refreshCatalog" | "refreshingCatalog" | "selectAll" | "unselectAll" | "wireAnthropic" | "wireOpenai" | "capVideo" | "capDynamicTools" | "capImageOnly" | "capNone" | "capVideoFootnote" | "capabilitiesTitle" | "capColMedia" | "capColTools" | "capColNotes" | "capPlan" | "speedRegular" | "speedHighspeed" | "quotaCost" | "minimumPlan" | "enhanced" | "defaultReasoningEffort" | "defaultReasoningEffortHint" | "defaultEffortAuto" | "effortLow" | "effortHigh" | "effortMax" | "effortNone" | "contextWindowSection" | "contextWindowHint" | "contextWindowPlan" | "save" | "saved" | "saving" | "cacheSection" | "cacheDesc" | "cacheEmpty" | "cacheHitRatio" | "cacheCached" | "cacheFresh" | "cacheOutput" | "cacheRequests" | "cacheHint" | "cacheDrift" | "cacheDriftFirst" | "cacheDriftStable" | "cacheDriftSystemPrompt" | "cacheDriftTools" | "cacheDriftCacheKey" | "cacheDriftColdKey" | "preserveThinking" | "preserveOn" | "preserveOff" | "outputCap" | "quotaSection" | "quotaDesc" | "refreshQuota" | "refreshingQuota" | "quotaEmpty" | "quotaPlan" | "quotaWallet" | "quotaWalletLimit" | "quotaWalletUsed" | "quotaUnlimited" | "updatedAt" | "resetAt" | "loading" | "loginFailed" | "requests" | "accountPool" | "addAccount" | "accountCount" | "primaryAccount" | "activeAccount" | "setPrimary" | "deleteAccount" | "cooling" | "cooldownLeft" | "clearCooldown" | "needsRelogin" | "relogin" | "rotationStrategy" | "strategySequential" | "strategyRoundRobin" | "strategySticky" | "noAccounts" | "expires" | "lastUsed" | "accountId", string>;
|
|
422
|
+
'en-US': Record<"tokens" | "title" | "pageDesc" | "account" | "signedIn" | "signedOut" | "nickname" | "email" | "plan" | "planUnknown" | "region" | "regionMainland" | "regionGlobal" | "authenticatedAt" | "storage" | "storageNotice" | "refreshRejected" | "signIn" | "signInAgain" | "signingIn" | "signOut" | "cancelSignIn" | "deviceCodeSection" | "deviceCodeHint" | "openSignInPage" | "copyCode" | "copied" | "userCode" | "codeExpires" | "waitingAuthorization" | "requestingCode" | "connection" | "provider" | "providerValue" | "oauthHost" | "codingEndpoint" | "connectionState" | "connected" | "untested" | "testConnection" | "testingConnection" | "testSuccess" | "testFailed" | "routeOwned" | "routeConflict" | "modelsSection" | "enableProvider" | "modelsHint" | "refreshCatalog" | "refreshingCatalog" | "selectAll" | "unselectAll" | "wireAnthropic" | "wireOpenai" | "capVideo" | "capDynamicTools" | "capImageOnly" | "capNone" | "capVideoFootnote" | "capabilitiesTitle" | "capColMedia" | "capColTools" | "capColNotes" | "capPlan" | "speedRegular" | "speedHighspeed" | "quotaCost" | "minimumPlan" | "enhanced" | "defaultReasoningEffort" | "defaultReasoningEffortHint" | "defaultEffortAuto" | "effortLow" | "effortHigh" | "effortMax" | "effortNone" | "contextWindowSection" | "contextWindowHint" | "contextWindowPlan" | "save" | "saved" | "saving" | "cacheSection" | "cacheDesc" | "cacheEmpty" | "cacheHitRatio" | "cacheCached" | "cacheFresh" | "cacheOutput" | "cacheRequests" | "cacheHint" | "cacheDrift" | "cacheDriftFirst" | "cacheDriftStable" | "cacheDriftSystemPrompt" | "cacheDriftTools" | "cacheDriftCacheKey" | "cacheDriftColdKey" | "preserveThinking" | "preserveOn" | "preserveOff" | "outputCap" | "quotaSection" | "quotaDesc" | "refreshQuota" | "refreshingQuota" | "quotaEmpty" | "quotaPlan" | "quotaWallet" | "quotaWalletLimit" | "quotaWalletUsed" | "quotaUnlimited" | "updatedAt" | "resetAt" | "loading" | "loginFailed" | "requests" | "accountPool" | "addAccount" | "accountCount" | "primaryAccount" | "activeAccount" | "setPrimary" | "deleteAccount" | "cooling" | "cooldownLeft" | "clearCooldown" | "needsRelogin" | "relogin" | "rotationStrategy" | "strategySequential" | "strategyRoundRobin" | "strategySticky" | "noAccounts" | "expires" | "lastUsed" | "accountId", string>;
|
|
402
423
|
};
|
|
403
424
|
export type KimiCodeLocaleKey = keyof typeof zh;
|
|
404
425
|
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../../src/client/kimi-code/locales.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kBAAkB,CAAA;AAE3C,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../../src/client/kimi-code/locales.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,kBAAkB,CAAA;AAE3C,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwHL,CAAA;AAEV,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,OAAO,EAAE,EAAE,MAAM,CAuHrC,CAAA;AAEV,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKxB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-search.d.ts","sourceRoot":"","sources":["../../../src/host/codex-search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex-search.d.ts","sourceRoot":"","sources":["../../../src/host/codex-search.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,iBAAiB,EAAqE,MAAM,sBAAsB,CAAA;AAM1I,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAIjD,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,MAAM,CAAA;CACzB;AAID,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,0BAA+B,GACvC,iBAAiB,CA8BnB"}
|
|
@@ -180,6 +180,48 @@ export declare class AccountPoolCore<TCredentials, TAccount extends PoolAccountS
|
|
|
180
180
|
/** Whether an account may serve a request right now. */
|
|
181
181
|
protected isEligible(account: TAccount, now: number, triedAccountIds?: ReadonlySet<string>): boolean;
|
|
182
182
|
hasAnotherAvailableAccount(triedAccountIds: ReadonlySet<string>): Promise<boolean>;
|
|
183
|
+
/**
|
|
184
|
+
* Read one account's credential for work that is *not* a metered model
|
|
185
|
+
* request, without letting rotation state stand in the way.
|
|
186
|
+
*
|
|
187
|
+
* There are two different questions, and conflating them broke both:
|
|
188
|
+
*
|
|
189
|
+
* - "which account should serve this request?" — a routing decision, answered
|
|
190
|
+
* by {@link getEffectiveAccount} through cooldowns, auth status and the
|
|
191
|
+
* rotation strategy.
|
|
192
|
+
* - "may I have a usable ChatGPT credential?" — a credential question, asked
|
|
193
|
+
* by the web search, web fetch and image tools.
|
|
194
|
+
*
|
|
195
|
+
* A spent Codex rate-limit window says nothing about the other ChatGPT
|
|
196
|
+
* endpoints: search, fetch and image generation are not metered against it and
|
|
197
|
+
* keep working with the very same token. Routing those tools through
|
|
198
|
+
* {@link getEffectiveAccount} made one exhausted window fail all of them —
|
|
199
|
+
* and reported it as "credentials are required", which sent people looking for
|
|
200
|
+
* a sign-in problem they did not have.
|
|
201
|
+
*
|
|
202
|
+
* A credential that is *known* to be unusable is still refused (a rejected
|
|
203
|
+
* refresh token, an account the upstream already rejected at sign-in); only
|
|
204
|
+
* cooldowns — which are rotation bookkeeping — are ignored.
|
|
205
|
+
*
|
|
206
|
+
* @param fetchFn - fetch used if the credential is close to expiry.
|
|
207
|
+
*/
|
|
208
|
+
getCredentialAccount(fetchFn?: typeof fetch): Promise<{
|
|
209
|
+
account: TAccount;
|
|
210
|
+
credentials: TCredentials;
|
|
211
|
+
}>;
|
|
212
|
+
/** Whether one account's credential must be refreshed before it is handed out. */
|
|
213
|
+
private shouldRefreshCredential;
|
|
214
|
+
/**
|
|
215
|
+
* Whether an account's stored credential is known to be unusable.
|
|
216
|
+
*
|
|
217
|
+
* Rotation bookkeeping — cooldowns, the tried set — is deliberately absent:
|
|
218
|
+
* those describe routing, not the credential.
|
|
219
|
+
*/
|
|
220
|
+
protected isCredentialUsable(account: TAccount): boolean;
|
|
221
|
+
/** The account a rotation strategy picks out of an already-eligible set. */
|
|
222
|
+
private selectAccount;
|
|
223
|
+
/** The error every-account-unavailable raises, including the shortest wait. */
|
|
224
|
+
private allUnavailableError;
|
|
183
225
|
/**
|
|
184
226
|
* Pick the account for the next request, refreshing its credential when it is
|
|
185
227
|
* about to expire.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-pool.d.ts","sourceRoot":"","sources":["../../../../src/host/common/account-pool.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"account-pool.d.ts","sourceRoot":"","sources":["../../../../src/host/common/account-pool.ts"],"names":[],"mappings":"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"}
|