@eddyskywalker/dsh-chatgpt-subscription 0.10.7 → 0.10.9
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 +27 -0
- package/README.md +2 -1
- package/lib/client.js +36 -24
- package/lib/client.js.map +1 -1
- package/lib/index.js +730 -62
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts.map +1 -1
- package/lib/types/client/minimax-code/locales.d.ts +8 -2
- package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
- package/lib/types/host/common/account-pool.d.ts +20 -0
- package/lib/types/host/common/account-pool.d.ts.map +1 -1
- package/lib/types/host/minimax-code/account-pool.d.ts +59 -0
- package/lib/types/host/minimax-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/minimax-code/checkin.d.ts.map +1 -1
- package/lib/types/host/minimax-code/client.d.ts +27 -1
- package/lib/types/host/minimax-code/client.d.ts.map +1 -1
- package/lib/types/host/minimax-code/oauth.d.ts +23 -7
- package/lib/types/host/minimax-code/oauth.d.ts.map +1 -1
- package/lib/types/host/minimax-code/routes.d.ts.map +1 -1
- package/lib/types/host/minimax-code/token-store.d.ts +75 -0
- package/lib/types/host/minimax-code/token-store.d.ts.map +1 -1
- package/lib/types/host/minimax-code/types.d.ts +37 -6
- package/lib/types/host/minimax-code/types.d.ts.map +1 -1
- package/lib/types/host/reasoning-collapse-guard/index.d.ts +130 -0
- package/lib/types/host/reasoning-collapse-guard/index.d.ts.map +1 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/minimax-code-contracts.d.ts +6 -1
- package/lib/types/shared/minimax-code-contracts.d.ts.map +1 -1
- package/package.json +31 -31
package/lib/index.js
CHANGED
|
@@ -1251,6 +1251,12 @@ var AccountPoolCore = class {
|
|
|
1251
1251
|
if (!this.isCredentialUsable(target)) throw new LlmError(`${this.hooks.displayName} 账号的凭据已被拒绝,需要重新登录。`, "AUTH", { status: 401 });
|
|
1252
1252
|
if (!force && (!this.shouldRefreshCredential(target, Date.now()) || !this.hooks.refresh)) return target.credentials;
|
|
1253
1253
|
if (!this.hooks.refresh) return target.credentials;
|
|
1254
|
+
const live = await this.hooks.liveCredentialsFor?.(target).catch(() => null);
|
|
1255
|
+
if (live !== null && live !== void 0 && live.advanced) {
|
|
1256
|
+
target.credentials = live.credentials;
|
|
1257
|
+
await this.write(data).catch(() => void 0);
|
|
1258
|
+
return live.credentials;
|
|
1259
|
+
}
|
|
1254
1260
|
let refreshed;
|
|
1255
1261
|
try {
|
|
1256
1262
|
refreshed = await this.hooks.refresh(target.credentials, fetchFn);
|
|
@@ -13612,7 +13618,7 @@ async function refreshAccessToken$1(refreshToken, options = {}) {
|
|
|
13612
13618
|
throw new KimiCodeRetryableError("Token refresh failed after retries.", lastError);
|
|
13613
13619
|
}
|
|
13614
13620
|
/** One in-flight refresh, so concurrent callers share a single request. */
|
|
13615
|
-
let inFlightRefresh$
|
|
13621
|
+
let inFlightRefresh$1 = null;
|
|
13616
13622
|
/**
|
|
13617
13623
|
* Return a usable access token, refreshing and persisting when needed.
|
|
13618
13624
|
*
|
|
@@ -13626,7 +13632,7 @@ async function ensureAccessToken$1(store, options = {}) {
|
|
|
13626
13632
|
const threshold = refreshThresholdMs(credentials.expiresIn);
|
|
13627
13633
|
if (options.force !== true && credentials.expiresAt - Date.now() > threshold) return credentials;
|
|
13628
13634
|
if (isRecentlyRejected(credentials.refreshToken)) throw new KimiCodeUnauthorizedError("Kimi Code rejected the stored refresh token. Sign in again from Settings > Kimi Code.");
|
|
13629
|
-
if (inFlightRefresh$
|
|
13635
|
+
if (inFlightRefresh$1 !== null) return inFlightRefresh$1;
|
|
13630
13636
|
const pending = (async () => {
|
|
13631
13637
|
try {
|
|
13632
13638
|
const token = await refreshAccessToken$1(credentials.refreshToken, {
|
|
@@ -13653,10 +13659,10 @@ async function ensureAccessToken$1(store, options = {}) {
|
|
|
13653
13659
|
if (error instanceof KimiCodeUnauthorizedError) rememberRejected$1(credentials.refreshToken);
|
|
13654
13660
|
throw error;
|
|
13655
13661
|
} finally {
|
|
13656
|
-
inFlightRefresh$
|
|
13662
|
+
inFlightRefresh$1 = null;
|
|
13657
13663
|
}
|
|
13658
13664
|
})();
|
|
13659
|
-
inFlightRefresh$
|
|
13665
|
+
inFlightRefresh$1 = pending;
|
|
13660
13666
|
return pending;
|
|
13661
13667
|
}
|
|
13662
13668
|
let webLoginFlow = { status: "idle" };
|
|
@@ -17922,14 +17928,43 @@ const USER_AGENT = "dsh-chatgpt-subscription (+https://github.com/Aa728848/dsh-c
|
|
|
17922
17928
|
/**
|
|
17923
17929
|
* How long before expiry a token is replaced.
|
|
17924
17930
|
*
|
|
17925
|
-
* The access token lives
|
|
17926
|
-
*
|
|
17927
|
-
*
|
|
17928
|
-
*
|
|
17929
|
-
*
|
|
17930
|
-
* desktop app for a rotation it does
|
|
17931
|
+
* The measured MiniMax Code access token lives ONE HOUR, not the day an earlier
|
|
17932
|
+
* note assumed, and the desktop app refreshes the same file this plugin reads, so
|
|
17933
|
+
* the policy is "read-only first": a token that is still comfortably valid is used
|
|
17934
|
+
* exactly as stored. The margin exists only so a request cannot start with a token
|
|
17935
|
+
* that expires while it is in flight — it is deliberately far below the token's
|
|
17936
|
+
* own lifetime so the plugin does not race the desktop app for a rotation it does
|
|
17937
|
+
* not need.
|
|
17931
17938
|
*/
|
|
17932
17939
|
const REFRESH_MARGIN_MS$1 = 6e4;
|
|
17940
|
+
/**
|
|
17941
|
+
* How long before expiry this plugin renews the credential ON ITS OWN schedule.
|
|
17942
|
+
*
|
|
17943
|
+
* This is the constant that removes the every-hour sign-out. The access token
|
|
17944
|
+
* lives one hour, so a reader that only acts once the token is already inside
|
|
17945
|
+
* {@link REFRESH_MARGIN_MS} of expiry presents a bearer the service has already
|
|
17946
|
+
* stopped accepting: every hour the line dips through 401 → refresh → retry, and
|
|
17947
|
+
* a burst of tool calls at that boundary has two callers spend the same rotating
|
|
17948
|
+
* refresh token. Renewing a few minutes early moves the rotation off the boundary
|
|
17949
|
+
* and onto a schedule this plugin controls.
|
|
17950
|
+
*
|
|
17951
|
+
* It must stay far away from the token's lifetime for the opposite reason: a
|
|
17952
|
+
* margin near the lifetime would rotate on every single read and race the desktop
|
|
17953
|
+
* app for a rotation neither side needs. Five minutes of a sixty-minute token is
|
|
17954
|
+
* the balance — the rotation happens once, well before the token can be refused.
|
|
17955
|
+
*/
|
|
17956
|
+
const PRE_EXPIRY_REFRESH_MS = 5 * 6e4;
|
|
17957
|
+
/**
|
|
17958
|
+
* How long a rotation stays "recent" for other readers of the same credential.
|
|
17959
|
+
*
|
|
17960
|
+
* A refresh token rotates: the moment one holder spends it, every other holder is
|
|
17961
|
+
* left with a token the service has already invalidated. This window is the answer
|
|
17962
|
+
* — a caller that observes the credential rotating right now waits for the winner
|
|
17963
|
+
* to write its result back and adopts that result instead of spending a token that
|
|
17964
|
+
* is already gone. It is a local patience bound, not a lock: no file is created,
|
|
17965
|
+
* and the desktop app's own `auth.lock` is never touched.
|
|
17966
|
+
*/
|
|
17967
|
+
const CREDENTIAL_ROTATION_WAIT_MS = 15e3;
|
|
17933
17968
|
/** Exponential backoff base for a retryable refresh failure. */
|
|
17934
17969
|
const REFRESH_BACKOFF_BASE_MS$1 = 1e3;
|
|
17935
17970
|
/**
|
|
@@ -19048,6 +19083,24 @@ function credentialIsFresh(credentials, now = Date.now()) {
|
|
|
19048
19083
|
return credentials.expiresAtMs - now > REFRESH_MARGIN_MS$1;
|
|
19049
19084
|
}
|
|
19050
19085
|
/**
|
|
19086
|
+
* Whether a credential should be renewed BEFORE it can be refused.
|
|
19087
|
+
*
|
|
19088
|
+
* {@link credentialIsFresh} answers "can this token be presented right now"; this
|
|
19089
|
+
* answers "is it worth rotating on this plugin's own schedule instead of waiting
|
|
19090
|
+
* for the service to say no". The two questions need different answers: the whole
|
|
19091
|
+
* one-hour sign-out was the second question being answered with the first, so the
|
|
19092
|
+
* line only ever rotated inside the last sixty seconds of a sixty-minute token —
|
|
19093
|
+
* exactly the window in which concurrent callers spend the same rotating refresh
|
|
19094
|
+
* token, and exactly the window in which a request can already be refused.
|
|
19095
|
+
*
|
|
19096
|
+
* The desktop app reads the same file and remains the first authority for it: this
|
|
19097
|
+
* plugin renews a few minutes early, writes the rotation back atomically, and
|
|
19098
|
+
* never touches the app's own `auth.lock`.
|
|
19099
|
+
*/
|
|
19100
|
+
function credentialNeedsRefresh(credentials, now = Date.now()) {
|
|
19101
|
+
return credentials.expiresAtMs - now <= PRE_EXPIRY_REFRESH_MS;
|
|
19102
|
+
}
|
|
19103
|
+
/**
|
|
19051
19104
|
* Write one file through a temporary sibling plus `rename`.
|
|
19052
19105
|
*
|
|
19053
19106
|
* The temporary file is created in the same directory so the rename stays on one
|
|
@@ -19079,6 +19132,188 @@ function serialize(key, operation) {
|
|
|
19079
19132
|
});
|
|
19080
19133
|
return result;
|
|
19081
19134
|
}
|
|
19135
|
+
const rotations = /* @__PURE__ */ new Map();
|
|
19136
|
+
/**
|
|
19137
|
+
* The credential the most recent storage read produced, and when.
|
|
19138
|
+
*
|
|
19139
|
+
* The store is read on every request, and on Windows each uncached read spawns
|
|
19140
|
+
* DPAPI - so the reads a single turn performs are served from here. Correctness
|
|
19141
|
+
* rests on one property the write path enforces: a rotation is written back
|
|
19142
|
+
* BEFORE its caller is handed the credential, and every write lands in the same
|
|
19143
|
+
* `fileOperations` chain this snapshot is filled from. A cached value therefore
|
|
19144
|
+
* can never outlive a write - it is either the current credential or one the very
|
|
19145
|
+
* next write replaces.
|
|
19146
|
+
*
|
|
19147
|
+
* The window is short for the opposite kind of risk: another process (the desktop
|
|
19148
|
+
* app) rotates the same file on its own schedule, and this snapshot must not hide
|
|
19149
|
+
* that for long.
|
|
19150
|
+
*/
|
|
19151
|
+
const RECENT_READ_WINDOW_MS = 1500;
|
|
19152
|
+
let recentRead = null;
|
|
19153
|
+
function rememberCredentialRead(filePath, credentials) {
|
|
19154
|
+
recentRead = {
|
|
19155
|
+
at: Date.now(),
|
|
19156
|
+
path: path.resolve(filePath),
|
|
19157
|
+
credentials
|
|
19158
|
+
};
|
|
19159
|
+
}
|
|
19160
|
+
/**
|
|
19161
|
+
* The credential a read of THIS file inside the window produced, if any.
|
|
19162
|
+
*
|
|
19163
|
+
* The file is part of the key because a store whose path no longer matches what the
|
|
19164
|
+
* snapshot was taken from has nothing to do with it: one process can hold several
|
|
19165
|
+
* stores, and the tests point each case at a fresh temporary home.
|
|
19166
|
+
*/
|
|
19167
|
+
function recentCredentialRead(filePath) {
|
|
19168
|
+
if (recentRead === null) return null;
|
|
19169
|
+
if (recentRead.path !== path.resolve(filePath) || Date.now() - recentRead.at > RECENT_READ_WINDOW_MS) {
|
|
19170
|
+
recentRead = null;
|
|
19171
|
+
return null;
|
|
19172
|
+
}
|
|
19173
|
+
return recentRead.credentials;
|
|
19174
|
+
}
|
|
19175
|
+
/**
|
|
19176
|
+
* Identity one credential rotation is registered under.
|
|
19177
|
+
*
|
|
19178
|
+
* Built from the same facts {@link minimaxCodePoolIdentity} uses - a native
|
|
19179
|
+
* record slot, or the sign-in that produced the credential - so every holder of
|
|
19180
|
+
* one session agrees on the key. The region is part of it because the same record
|
|
19181
|
+
* key in two regions is two different sessions.
|
|
19182
|
+
*/
|
|
19183
|
+
function rotationKey(credentials) {
|
|
19184
|
+
if (credentials.recordKey !== null && credentials.recordKey !== "") return "native:" + credentials.region + ":" + credentials.recordKey;
|
|
19185
|
+
if (credentials.loginEpoch !== "") return "signin:" + credentials.region + ":" + credentials.loginEpoch;
|
|
19186
|
+
return "token:" + credentials.refreshToken;
|
|
19187
|
+
}
|
|
19188
|
+
/**
|
|
19189
|
+
* Whether two credentials are two generations of ONE session.
|
|
19190
|
+
*
|
|
19191
|
+
* The file this line reads holds a single record, but the plugin's own file is a
|
|
19192
|
+
* mirror that the pool writes for whichever account is primary — so a credential
|
|
19193
|
+
* read from storage may belong to a completely different account than the one a
|
|
19194
|
+
* caller is working on. Adopting a stranger's token would present one account's
|
|
19195
|
+
* session as another's, which is the one mistake this whole line must never make.
|
|
19196
|
+
*
|
|
19197
|
+
* The identity is therefore the same one {@link rotationKey} uses: a native record
|
|
19198
|
+
* slot, or the sign-in that produced a plugin-owned credential.
|
|
19199
|
+
*/
|
|
19200
|
+
function minimaxCodeSameCredentialSession(a, b) {
|
|
19201
|
+
if (a === null || b === null) return false;
|
|
19202
|
+
if (a.source !== b.source || a.region !== b.region) return false;
|
|
19203
|
+
if (a.recordKey !== b.recordKey) return false;
|
|
19204
|
+
if (a.recordKey === null || a.recordKey === "") return a.loginEpoch === b.loginEpoch;
|
|
19205
|
+
return true;
|
|
19206
|
+
}
|
|
19207
|
+
/**
|
|
19208
|
+
* Whether one credential is a LATER state of another.
|
|
19209
|
+
*
|
|
19210
|
+
* The generation is the desktop app's own rotation counter and the expiry moves
|
|
19211
|
+
* forward with every rotation, so either being greater means the candidate is the
|
|
19212
|
+
* result of a rotation the baseline predates. This is also what stops a stale copy
|
|
19213
|
+
* from rolling a newer state back: a pool row the mirror could not write must never
|
|
19214
|
+
* overwrite a fresh file.
|
|
19215
|
+
*/
|
|
19216
|
+
function minimaxCodeCredentialAdvancedPast(candidate, baseline) {
|
|
19217
|
+
if (candidate === null) return false;
|
|
19218
|
+
if (candidate.refreshToken !== baseline.refreshToken) return candidate.generation > baseline.generation || candidate.expiresAtMs > baseline.expiresAtMs;
|
|
19219
|
+
return candidate.expiresAtMs > baseline.expiresAtMs + SAME_TOKEN_ADVANCE_TOLERANCE_MS;
|
|
19220
|
+
}
|
|
19221
|
+
/**
|
|
19222
|
+
* How much later an expiry must be, with an unchanged refresh token, to count as a
|
|
19223
|
+
* renewal rather than the same credential read twice.
|
|
19224
|
+
*/
|
|
19225
|
+
const SAME_TOKEN_ADVANCE_TOLERANCE_MS = 6e4;
|
|
19226
|
+
/** Sleep, honouring an abort signal. */
|
|
19227
|
+
function rotationSleep(ms, signal) {
|
|
19228
|
+
return new Promise((resolve) => {
|
|
19229
|
+
if (signal?.aborted === true) {
|
|
19230
|
+
resolve();
|
|
19231
|
+
return;
|
|
19232
|
+
}
|
|
19233
|
+
const onAbort = () => {
|
|
19234
|
+
clearTimeout(timer);
|
|
19235
|
+
resolve();
|
|
19236
|
+
};
|
|
19237
|
+
const timer = setTimeout(() => {
|
|
19238
|
+
signal?.removeEventListener("abort", onAbort);
|
|
19239
|
+
resolve();
|
|
19240
|
+
}, ms);
|
|
19241
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19242
|
+
});
|
|
19243
|
+
}
|
|
19244
|
+
/**
|
|
19245
|
+
* Rotate one credential, or adopt the rotation somebody else is doing.
|
|
19246
|
+
*
|
|
19247
|
+
* The distinction matters: a caller that ADOPTED a result must use the credential
|
|
19248
|
+
* that comes back, because the winner replaced the whole record.
|
|
19249
|
+
*/
|
|
19250
|
+
async function rotateMinimaxCodeCredential(store, credentials, perform, options = {}) {
|
|
19251
|
+
const key = rotationKey(credentials);
|
|
19252
|
+
const seen = credentials.refreshToken;
|
|
19253
|
+
const deadline = Date.now() + CREDENTIAL_ROTATION_WAIT_MS;
|
|
19254
|
+
while (true) {
|
|
19255
|
+
const active = rotations.get(key);
|
|
19256
|
+
if (active === void 0) break;
|
|
19257
|
+
if (active.promise !== null) {
|
|
19258
|
+
if (active.token === seen) return {
|
|
19259
|
+
credentials: await active.promise,
|
|
19260
|
+
adopted: true
|
|
19261
|
+
};
|
|
19262
|
+
await active.promise.catch(() => void 0);
|
|
19263
|
+
break;
|
|
19264
|
+
}
|
|
19265
|
+
const current = await store.readFresh().catch(() => null);
|
|
19266
|
+
if (minimaxCodeSameCredentialSession(current, credentials) && minimaxCodeCredentialAdvancedPast(current, credentials)) return {
|
|
19267
|
+
credentials: current,
|
|
19268
|
+
adopted: true
|
|
19269
|
+
};
|
|
19270
|
+
if (Date.now() >= deadline) break;
|
|
19271
|
+
await rotationSleep(200, options.signal);
|
|
19272
|
+
}
|
|
19273
|
+
const latest = await store.readFresh().catch(() => null);
|
|
19274
|
+
if (minimaxCodeSameCredentialSession(latest, credentials) && minimaxCodeCredentialAdvancedPast(latest, credentials)) return {
|
|
19275
|
+
credentials: latest,
|
|
19276
|
+
adopted: true
|
|
19277
|
+
};
|
|
19278
|
+
const record = {
|
|
19279
|
+
token: seen,
|
|
19280
|
+
expiresAtMs: credentials.expiresAtMs,
|
|
19281
|
+
promise: null
|
|
19282
|
+
};
|
|
19283
|
+
const pending = (async () => {
|
|
19284
|
+
rotations.set(key, record);
|
|
19285
|
+
try {
|
|
19286
|
+
const next = await perform();
|
|
19287
|
+
const settled = await store.readFresh().catch(() => null);
|
|
19288
|
+
if (minimaxCodeSameCredentialSession(settled, credentials) && minimaxCodeCredentialAdvancedPast(settled, credentials)) return settled;
|
|
19289
|
+
const rotated = next.source === credentials.source && next.recordKey === credentials.recordKey ? next : {
|
|
19290
|
+
...next,
|
|
19291
|
+
source: credentials.source,
|
|
19292
|
+
recordKey: credentials.recordKey,
|
|
19293
|
+
buildEnv: credentials.buildEnv
|
|
19294
|
+
};
|
|
19295
|
+
await store.write(rotated);
|
|
19296
|
+
const verified = await store.readFresh().catch(() => null);
|
|
19297
|
+
if (minimaxCodeSameCredentialSession(verified, rotated) && minimaxCodeCredentialAdvancedPast(verified, rotated)) return verified;
|
|
19298
|
+
return rotated;
|
|
19299
|
+
} catch (error) {
|
|
19300
|
+
if (error instanceof Error && error.name === "MinimaxCodeUnauthorizedError") {
|
|
19301
|
+
const settled = await store.readFresh().catch(() => null);
|
|
19302
|
+
if (minimaxCodeSameCredentialSession(settled, credentials) && minimaxCodeCredentialAdvancedPast(settled, credentials)) return settled;
|
|
19303
|
+
}
|
|
19304
|
+
throw error;
|
|
19305
|
+
} finally {
|
|
19306
|
+
if (rotations.get(key) === record) rotations.delete(key);
|
|
19307
|
+
}
|
|
19308
|
+
})();
|
|
19309
|
+
pending.catch(() => void 0);
|
|
19310
|
+
record.promise = pending;
|
|
19311
|
+
rotations.set(key, record);
|
|
19312
|
+
return {
|
|
19313
|
+
credentials: await pending,
|
|
19314
|
+
adopted: false
|
|
19315
|
+
};
|
|
19316
|
+
}
|
|
19082
19317
|
/**
|
|
19083
19318
|
* Read the desktop app's credential document for one region.
|
|
19084
19319
|
*
|
|
@@ -19190,9 +19425,32 @@ var MinimaxCodeCredentialStore = class {
|
|
|
19190
19425
|
return null;
|
|
19191
19426
|
}
|
|
19192
19427
|
}
|
|
19193
|
-
read() {
|
|
19428
|
+
async read() {
|
|
19429
|
+
await this.drain();
|
|
19430
|
+
const recent = recentCredentialRead(this.path());
|
|
19431
|
+
if (recent !== null) return recent;
|
|
19194
19432
|
return serialize(this.path(), async () => await this.readNative() ?? await this.readPluginFile());
|
|
19195
19433
|
}
|
|
19434
|
+
/** Resolve once every operation already queued for this store's file settled. */
|
|
19435
|
+
drain() {
|
|
19436
|
+
return fileOperations.get(path.resolve(this.path())) ?? Promise.resolve();
|
|
19437
|
+
}
|
|
19438
|
+
/**
|
|
19439
|
+
* Read storage, ignoring the snapshot.
|
|
19440
|
+
*
|
|
19441
|
+
* Used by the rotation path, where the whole question is whether somebody else
|
|
19442
|
+
* has already replaced the credential: a cached answer would report the very
|
|
19443
|
+
* state the caller is trying to move past. This is also what makes a rotation
|
|
19444
|
+
* observable at all - the desktop app's write is only visible by reading the
|
|
19445
|
+
* file again.
|
|
19446
|
+
*/
|
|
19447
|
+
readFresh() {
|
|
19448
|
+
return serialize(this.path(), async () => {
|
|
19449
|
+
const stored = await this.readNative() ?? await this.readPluginFile();
|
|
19450
|
+
rememberCredentialRead(this.path(), stored);
|
|
19451
|
+
return stored;
|
|
19452
|
+
});
|
|
19453
|
+
}
|
|
19196
19454
|
/**
|
|
19197
19455
|
* The credential in force plus where it came from, in one pass.
|
|
19198
19456
|
*
|
|
@@ -19228,9 +19486,23 @@ var MinimaxCodeCredentialStore = class {
|
|
|
19228
19486
|
* written to the plugin's own file. Either way the write is atomic.
|
|
19229
19487
|
*/
|
|
19230
19488
|
write(credentials) {
|
|
19489
|
+
const target = credentials.source === "minimax-native" && credentials.recordKey !== null ? authJsonPath(credentials.region, credentials.buildEnv) : pluginCredentialPath();
|
|
19490
|
+
return serialize(target, async () => {
|
|
19491
|
+
await this.writeOnce(credentials);
|
|
19492
|
+
rememberCredentialRead(target, credentials);
|
|
19493
|
+
});
|
|
19494
|
+
}
|
|
19495
|
+
/**
|
|
19496
|
+
* One write, to whichever file the credential came from.
|
|
19497
|
+
*
|
|
19498
|
+
* Called with the destination already held by {@link write}'s `serialize`; it
|
|
19499
|
+
* must NOT claim the same chain again, or the outer frame would be waiting on the
|
|
19500
|
+
* inner one while the inner one waits on the outer.
|
|
19501
|
+
*/
|
|
19502
|
+
async writeOnce(credentials) {
|
|
19231
19503
|
const native = credentials.source === "minimax-native" && credentials.recordKey !== null;
|
|
19232
19504
|
const target = native ? authJsonPath(credentials.region, credentials.buildEnv) : pluginCredentialPath();
|
|
19233
|
-
|
|
19505
|
+
{
|
|
19234
19506
|
if (!native) {
|
|
19235
19507
|
await writeFileAtomic(pluginCredentialPath(), JSON.stringify(credentials, null, 2) + "\n");
|
|
19236
19508
|
return;
|
|
@@ -19269,7 +19541,7 @@ var MinimaxCodeCredentialStore = class {
|
|
|
19269
19541
|
generation: credentials.generation,
|
|
19270
19542
|
expiresAtMs: credentials.expiresAtMs
|
|
19271
19543
|
});
|
|
19272
|
-
}
|
|
19544
|
+
}
|
|
19273
19545
|
}
|
|
19274
19546
|
/**
|
|
19275
19547
|
* Forget the credential this plugin owns.
|
|
@@ -19283,6 +19555,7 @@ var MinimaxCodeCredentialStore = class {
|
|
|
19283
19555
|
delete() {
|
|
19284
19556
|
return serialize(pluginCredentialPath(), async () => {
|
|
19285
19557
|
await fsPromises.rm(pluginCredentialPath(), { force: true });
|
|
19558
|
+
recentRead = null;
|
|
19286
19559
|
});
|
|
19287
19560
|
}
|
|
19288
19561
|
};
|
|
@@ -19608,8 +19881,7 @@ async function requestDeviceAuthorization$1(options = {}) {
|
|
|
19608
19881
|
function parseTokenResponse$1(record) {
|
|
19609
19882
|
const accessToken = asString$6(record.access_token);
|
|
19610
19883
|
if (accessToken === void 0) throw new Error("MiniMax Code token response is missing its access token");
|
|
19611
|
-
const refreshToken = asString$6(record.refresh_token);
|
|
19612
|
-
if (refreshToken === void 0) throw new Error("MiniMax Code token response is missing its refresh token");
|
|
19884
|
+
const refreshToken = asString$6(record.refresh_token) ?? "";
|
|
19613
19885
|
const expiresIn = Number(record.expires_in);
|
|
19614
19886
|
if (!Number.isFinite(expiresIn) || expiresIn <= 0) throw new Error("MiniMax Code token response carried an invalid lifetime");
|
|
19615
19887
|
return {
|
|
@@ -19820,8 +20092,35 @@ function isRefreshTokenRejected(refreshToken) {
|
|
|
19820
20092
|
}
|
|
19821
20093
|
return true;
|
|
19822
20094
|
}
|
|
19823
|
-
/**
|
|
19824
|
-
|
|
20095
|
+
/**
|
|
20096
|
+
* One in-flight refresh per credential, so concurrent callers share a rotation.
|
|
20097
|
+
*
|
|
20098
|
+
* Keyed on the credential the rotation started from. The module-wide promise this
|
|
20099
|
+
* replaced was shared by EVERY caller, which cannot be right for a pool: a second
|
|
20100
|
+
* account would have been handed the first account's freshly rotated token, and
|
|
20101
|
+
* the desktop app's own file would have been written with a stranger's session.
|
|
20102
|
+
*/
|
|
20103
|
+
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
20104
|
+
/** Identity one credential's in-flight rotation is registered under. */
|
|
20105
|
+
function refreshFlightKey(credentials) {
|
|
20106
|
+
if (credentials.recordKey !== null && credentials.recordKey !== "") return "native:" + credentials.region + ":" + credentials.recordKey;
|
|
20107
|
+
if (credentials.loginEpoch !== "") return "signin:" + credentials.region + ":" + credentials.loginEpoch;
|
|
20108
|
+
return "token:" + credentials.refreshToken;
|
|
20109
|
+
}
|
|
20110
|
+
/**
|
|
20111
|
+
* The in-flight rotation this call may join.
|
|
20112
|
+
*
|
|
20113
|
+
* ONLY one that started from the very token this caller holds. A rotation of a
|
|
20114
|
+
* LATER credential of the same session is not a substitute — its result belongs to
|
|
20115
|
+
* the caller that started it, and adopting it here would hand back a credential
|
|
20116
|
+
* this caller never asked for. That case is handled by re-reading the store, which
|
|
20117
|
+
* is where the newer credential already is.
|
|
20118
|
+
*/
|
|
20119
|
+
function joinInFlightRefresh(credentials) {
|
|
20120
|
+
const active = inFlightRefreshes.get(refreshFlightKey(credentials));
|
|
20121
|
+
if (active === void 0 || active.token !== credentials.refreshToken) return null;
|
|
20122
|
+
return active.promise;
|
|
20123
|
+
}
|
|
19825
20124
|
/** The message a user sees when the credential can no longer be renewed. */
|
|
19826
20125
|
function signInAgainMessage(reason) {
|
|
19827
20126
|
return reason + " Sign in again in the MiniMax Code app, then reopen this card.";
|
|
@@ -19831,47 +20130,70 @@ function signInAgainMessage(reason) {
|
|
|
19831
20130
|
*
|
|
19832
20131
|
* This is the whole of the contention discipline in one function:
|
|
19833
20132
|
*
|
|
19834
|
-
* 1. read the stored credential and use it as-is while it is valid —
|
|
19835
|
-
* common case, and the one that never touches the file or the network;
|
|
19836
|
-
* 2.
|
|
19837
|
-
* already
|
|
19838
|
-
*
|
|
19839
|
-
*
|
|
20133
|
+
* 1. read the stored credential and use it as-is while it is comfortably valid —
|
|
20134
|
+
* the common case, and the one that never touches the file or the network;
|
|
20135
|
+
* 2. renew EARLY (PRE_EXPIRY_REFRESH_MS before expiry) rather than at the boundary
|
|
20136
|
+
* the service refuses at, or when `force` reports a 401 the service has already
|
|
20137
|
+
* issued;
|
|
20138
|
+
* 3. share one rotation between concurrent callers of the SAME credential, and go
|
|
20139
|
+
* through the store's rotation registry so a rotation the desktop app is doing
|
|
20140
|
+
* to the same file is waited for rather than raced;
|
|
19840
20141
|
* 4. write the result back through the store's atomic replacement, and remember a
|
|
19841
|
-
*
|
|
20142
|
+
* rejection only when the credential on disk is still the rejected one.
|
|
20143
|
+
*
|
|
20144
|
+
* Step 4 is the one the every-hour sign-out hung on. `invalid_grant` used to be
|
|
20145
|
+
* recorded unconditionally, but that verdict has two possible meanings and only
|
|
20146
|
+
* one of them is "this sign-in is dead":
|
|
20147
|
+
*
|
|
20148
|
+
* - the token really is revoked (`invalid_grant` / `invalid_client`), which is a
|
|
20149
|
+
* sign-in problem;
|
|
20150
|
+
* - another holder of the very same session rotated first, so the token this call
|
|
20151
|
+
* presented was already spent. The service cannot tell the two apart, and the
|
|
20152
|
+
* plugin must not either — so it looks at the file. A credential newer than the
|
|
20153
|
+
* one it presented means somebody else rotated successfully, and that credential
|
|
20154
|
+
* is adopted instead of being buried under a tombstone.
|
|
19842
20155
|
*/
|
|
19843
20156
|
async function ensureAccessToken$2(store, options = {}) {
|
|
19844
20157
|
const credentials = await store.read();
|
|
19845
20158
|
if (credentials === null) throw new MinimaxCodeUnauthorizedError(signInAgainMessage("Not signed in to MiniMax Code."));
|
|
19846
|
-
if (options.force !== true &&
|
|
19847
|
-
if (isRefreshTokenRejected(credentials.refreshToken))
|
|
19848
|
-
|
|
20159
|
+
if (options.force !== true && !credentialNeedsRefresh(credentials)) return credentials;
|
|
20160
|
+
if (isRefreshTokenRejected(credentials.refreshToken)) {
|
|
20161
|
+
const current = await store.readFresh().catch(() => null);
|
|
20162
|
+
if (current !== null && current.refreshToken !== credentials.refreshToken) return current;
|
|
20163
|
+
throw new MinimaxCodeUnauthorizedError(signInAgainMessage("MiniMax Code rejected the stored refresh token " + redactToken(credentials.refreshToken) + "."));
|
|
20164
|
+
}
|
|
20165
|
+
const joined = joinInFlightRefresh(credentials);
|
|
20166
|
+
if (joined !== null) return joined;
|
|
19849
20167
|
const pending = (async () => {
|
|
19850
|
-
|
|
20168
|
+
return (await rotateMinimaxCodeCredential(store, credentials, async () => {
|
|
19851
20169
|
const token = await refreshAccessToken$2(credentials.refreshToken, {
|
|
19852
20170
|
fetchFn: options.fetchFn,
|
|
19853
20171
|
signal: options.signal,
|
|
19854
20172
|
region: credentials.region
|
|
19855
20173
|
});
|
|
19856
|
-
|
|
20174
|
+
return {
|
|
19857
20175
|
...credentials,
|
|
19858
20176
|
accessToken: token.accessToken,
|
|
19859
|
-
refreshToken: token.refreshToken,
|
|
20177
|
+
refreshToken: token.refreshToken === "" ? credentials.refreshToken : token.refreshToken,
|
|
19860
20178
|
tokenType: token.tokenType,
|
|
19861
20179
|
expiresAtMs: token.expiresAtMs,
|
|
19862
20180
|
generation: credentials.generation + 1,
|
|
19863
20181
|
seenAt: Date.now()
|
|
19864
20182
|
};
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
} finally {
|
|
19871
|
-
inFlightRefresh$1 = null;
|
|
20183
|
+
}, { signal: options.signal })).credentials;
|
|
20184
|
+
})().catch(async (error) => {
|
|
20185
|
+
if (error instanceof MinimaxCodeUnauthorizedError) {
|
|
20186
|
+
const current = await store.readFresh().catch(() => null);
|
|
20187
|
+
if (current === null || current.refreshToken === credentials.refreshToken) rememberRejected(credentials.refreshToken);
|
|
19872
20188
|
}
|
|
19873
|
-
|
|
19874
|
-
|
|
20189
|
+
throw error;
|
|
20190
|
+
}).finally(() => {
|
|
20191
|
+
if (inFlightRefreshes.get(refreshFlightKey(credentials))?.promise === pending) inFlightRefreshes.delete(refreshFlightKey(credentials));
|
|
20192
|
+
});
|
|
20193
|
+
inFlightRefreshes.set(refreshFlightKey(credentials), {
|
|
20194
|
+
token: credentials.refreshToken,
|
|
20195
|
+
promise: pending
|
|
20196
|
+
});
|
|
19875
20197
|
return pending;
|
|
19876
20198
|
}
|
|
19877
20199
|
/**
|
|
@@ -20196,12 +20518,24 @@ const QUOTA_FAILURE_CACHE_MS = 10 * 6e4;
|
|
|
20196
20518
|
/** Local timeout for one usage read. */
|
|
20197
20519
|
const QUOTA_TIMEOUT_MS = 8e3;
|
|
20198
20520
|
let quotaHostInForce = null;
|
|
20521
|
+
/**
|
|
20522
|
+
* The last usage snapshot, plus the last failure to have one.
|
|
20523
|
+
*
|
|
20524
|
+
* `value` and `reason` are tracked SEPARATELY from the last GOOD value on
|
|
20525
|
+
* purpose. A read that fails carries no information about the previous one, so
|
|
20526
|
+
* replacing a good snapshot with `null` turned every transient refusal into a
|
|
20527
|
+
* visible disappearance of the quota box; `quotaLastGood` lets the card keep
|
|
20528
|
+
* showing the last known numbers while saying they are old.
|
|
20529
|
+
*/
|
|
20199
20530
|
let quotaSnapshot = null;
|
|
20531
|
+
/** The last snapshot that actually parsed, kept across failures. */
|
|
20532
|
+
let quotaLastGood = null;
|
|
20200
20533
|
/** How long a refused credential is remembered before asking again. */
|
|
20201
20534
|
const QUOTA_REJECTED_CACHE_MS = 30 * 6e4;
|
|
20202
20535
|
/** Forget the cached usage snapshot (a sign-out, a sign-in, a test). */
|
|
20203
20536
|
function clearCachedQuota$4() {
|
|
20204
20537
|
quotaSnapshot = null;
|
|
20538
|
+
quotaLastGood = null;
|
|
20205
20539
|
quotaHostInForce = null;
|
|
20206
20540
|
}
|
|
20207
20541
|
/** The last usage snapshot without touching the network. */
|
|
@@ -20330,13 +20664,14 @@ async function fetchTokenPlanQuota(credentials, options = {}) {
|
|
|
20330
20664
|
const fetchFn = options.fetchFn ?? fetch;
|
|
20331
20665
|
const now = Date.now();
|
|
20332
20666
|
if (options.force !== true && quotaSnapshot !== null) {
|
|
20333
|
-
const ttl = quotaSnapshot.reason === "credential-not-accepted" ? QUOTA_REJECTED_CACHE_MS : quotaSnapshot.value === null ? QUOTA_FAILURE_CACHE_MS : QUOTA_CACHE_MS;
|
|
20667
|
+
const ttl = quotaSnapshot.reason === "credential-not-accepted" ? QUOTA_REJECTED_CACHE_MS : quotaSnapshot.reason === "token-expired" && quotaSnapshot.value === null ? QUOTA_FAILURE_CACHE_MS : quotaSnapshot.value === null ? QUOTA_FAILURE_CACHE_MS : QUOTA_CACHE_MS;
|
|
20334
20668
|
if (now - quotaSnapshot.at < ttl) return quotaSnapshot.value;
|
|
20335
20669
|
}
|
|
20336
20670
|
const candidates = quotaHostCandidates(credentials.region);
|
|
20337
20671
|
const ordered = quotaHostInForce === null ? candidates : [quotaHostInForce, ...candidates.filter((host) => host !== quotaHostInForce)];
|
|
20338
20672
|
let value = null;
|
|
20339
20673
|
let reason = null;
|
|
20674
|
+
let authRefused = false;
|
|
20340
20675
|
for (const host of ordered) try {
|
|
20341
20676
|
const remainsPath = TOKEN_PLAN_REMAINS_PATH;
|
|
20342
20677
|
const response = await fetchFn(tokenPlanRemainsUrl(host), {
|
|
@@ -20344,6 +20679,11 @@ async function fetchTokenPlanQuota(credentials, options = {}) {
|
|
|
20344
20679
|
headers: quotaRequestHeaders(credentials.accessToken, remainsPath),
|
|
20345
20680
|
signal: withTimeout$1(options.signal, QUOTA_TIMEOUT_MS)
|
|
20346
20681
|
});
|
|
20682
|
+
if (response.status === 401 || response.status === 403) {
|
|
20683
|
+
if (await options.isCredentialStale?.() === true) return null;
|
|
20684
|
+
authRefused = true;
|
|
20685
|
+
break;
|
|
20686
|
+
}
|
|
20347
20687
|
if (!response.ok) continue;
|
|
20348
20688
|
const payload = await response.json().catch(() => void 0);
|
|
20349
20689
|
if (numberOr$2(asRecord$5(asRecord$5(payload)?.base_resp)?.status_code, 0) !== 0) {
|
|
@@ -20357,7 +20697,22 @@ async function fetchTokenPlanQuota(credentials, options = {}) {
|
|
|
20357
20697
|
reason = null;
|
|
20358
20698
|
break;
|
|
20359
20699
|
} catch {}
|
|
20360
|
-
if (value === null &&
|
|
20700
|
+
if (value === null && authRefused && options.renewCredential !== void 0) {
|
|
20701
|
+
const renewed = await options.renewCredential().catch(() => null);
|
|
20702
|
+
if (renewed !== null && renewed.accessToken !== credentials.accessToken) return fetchTokenPlanQuota(renewed, {
|
|
20703
|
+
...options,
|
|
20704
|
+
force: true
|
|
20705
|
+
});
|
|
20706
|
+
}
|
|
20707
|
+
if (value === null && reason === null) reason = authRefused ? "token-expired" : "unreachable";
|
|
20708
|
+
if (value !== null) quotaLastGood = {
|
|
20709
|
+
value,
|
|
20710
|
+
at: Date.now()
|
|
20711
|
+
};
|
|
20712
|
+
else if (quotaLastGood !== null) {
|
|
20713
|
+
value = quotaLastGood.value;
|
|
20714
|
+
reason = "stale";
|
|
20715
|
+
}
|
|
20361
20716
|
quotaSnapshot = {
|
|
20362
20717
|
at: Date.now(),
|
|
20363
20718
|
value,
|
|
@@ -20884,6 +21239,15 @@ function projectAccount(input) {
|
|
|
20884
21239
|
...facts.planName === void 0 ? {} : { planName: facts.planName }
|
|
20885
21240
|
};
|
|
20886
21241
|
}
|
|
21242
|
+
/**
|
|
21243
|
+
* Whether an account row carries an auth failure the card would render.
|
|
21244
|
+
*
|
|
21245
|
+
* Takes the summary shape as well as the pool row, because both the card's list
|
|
21246
|
+
* and the routing read ask the same question about it.
|
|
21247
|
+
*/
|
|
21248
|
+
function isMarkedFailed(account) {
|
|
21249
|
+
return account.authStatus !== void 0 && account.authStatus !== "ok";
|
|
21250
|
+
}
|
|
20887
21251
|
/** The alias an account is labelled with when the user has not named it. */
|
|
20888
21252
|
function defaultAliasFor$1(credentials, position) {
|
|
20889
21253
|
const email = displayFacts(credentials).email;
|
|
@@ -21004,23 +21368,25 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
21004
21368
|
isPrimary
|
|
21005
21369
|
}),
|
|
21006
21370
|
expiresAt: (credentials) => credentials.expiresAtMs,
|
|
21007
|
-
needsRefresh: (credentials, now) =>
|
|
21371
|
+
needsRefresh: (credentials, now) => credentialNeedsRefresh(credentials, now),
|
|
21008
21372
|
refresh: async (credentials, fetchFn) => {
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21373
|
+
return (await rotateMinimaxCodeCredential(store, credentials, async () => {
|
|
21374
|
+
const token = await refreshAccessToken$2(credentials.refreshToken, {
|
|
21375
|
+
fetchFn,
|
|
21376
|
+
region: credentials.region
|
|
21377
|
+
});
|
|
21378
|
+
const next = {
|
|
21379
|
+
...credentials,
|
|
21380
|
+
accessToken: token.accessToken,
|
|
21381
|
+
refreshToken: token.refreshToken === "" ? credentials.refreshToken : token.refreshToken,
|
|
21382
|
+
tokenType: token.tokenType,
|
|
21383
|
+
expiresAtMs: token.expiresAtMs,
|
|
21384
|
+
generation: credentials.generation + 1,
|
|
21385
|
+
seenAt: Date.now()
|
|
21386
|
+
};
|
|
21387
|
+
if (next.source === "minimax-native" && next.recordKey !== null) await store.write(next).catch(() => void 0);
|
|
21388
|
+
return next;
|
|
21389
|
+
})).credentials;
|
|
21024
21390
|
},
|
|
21025
21391
|
refreshFailureStatus: (error) => error instanceof MinimaxCodeUnauthorizedError ? "expired" : void 0,
|
|
21026
21392
|
authRejectedReason: (credentials) => isRefreshTokenRejected(credentials.refreshToken) ? "MiniMax Code 已拒绝该账号的刷新令牌,需要重新登录。" : void 0,
|
|
@@ -21059,6 +21425,7 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
21059
21425
|
}
|
|
21060
21426
|
};
|
|
21061
21427
|
},
|
|
21428
|
+
liveCredentialsFor: (account) => this.nativeAuthority(account),
|
|
21062
21429
|
emptyMessage: "Not signed in to MiniMax Code. Sign in with the MiniMax Code app, or add an account from Settings > MiniMax Code.",
|
|
21063
21430
|
...options.backend === void 0 ? {} : { backend: options.backend },
|
|
21064
21431
|
...options.maxAccounts === void 0 ? {} : { maxAccounts: options.maxAccounts }
|
|
@@ -21067,6 +21434,74 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
21067
21434
|
this.store = store;
|
|
21068
21435
|
}
|
|
21069
21436
|
/**
|
|
21437
|
+
* Account summaries, with a false auth failure cleared.
|
|
21438
|
+
*
|
|
21439
|
+
* A row can carry an `authStatus` of expired for a rotation that actually
|
|
21440
|
+
* succeeded — the second of two concurrent rotations of one session gets a final
|
|
21441
|
+
* verdict for a token the first already spent. Nobody comes back to clear that
|
|
21442
|
+
* marker, so the account stays out of rotation until the user presses 重新登录.
|
|
21443
|
+
*
|
|
21444
|
+
* The verdict is re-checked against the token in force instead: a row whose
|
|
21445
|
+
* credential has moved on since the failure is routable again, and the marker is
|
|
21446
|
+
* removed. A row still holding the refused token keeps its marker, because that
|
|
21447
|
+
* one really does need a sign-in.
|
|
21448
|
+
*/
|
|
21449
|
+
async listAccounts() {
|
|
21450
|
+
const summaries = await super.listAccounts();
|
|
21451
|
+
if (!summaries.some(isMarkedFailed)) return summaries;
|
|
21452
|
+
await this.healStaleAuthFailures().catch(() => void 0);
|
|
21453
|
+
return await super.listAccounts();
|
|
21454
|
+
}
|
|
21455
|
+
/**
|
|
21456
|
+
* Pick an account, healing a stale auth failure first.
|
|
21457
|
+
*
|
|
21458
|
+
* The healing matters here, not only on the card: an account marked expired is
|
|
21459
|
+
* excluded by the core's eligibility rule, so until the marker is gone the line
|
|
21460
|
+
* answers a single-account pool with "all accounts need a new sign-in" — for a
|
|
21461
|
+
* rotation that succeeded. Doing it on the request path means the very next
|
|
21462
|
+
* request after a false refusal routes normally instead of waiting for the card's
|
|
21463
|
+
* next poll.
|
|
21464
|
+
*/
|
|
21465
|
+
async getEffectiveAccount(excludeIds, fetchFn = fetch) {
|
|
21466
|
+
const data = await this.read();
|
|
21467
|
+
if (data.accounts.some(isMarkedFailed)) await this.healStaleAuthFailures(data).catch(() => void 0);
|
|
21468
|
+
return await super.getEffectiveAccount(excludeIds, fetchFn);
|
|
21469
|
+
}
|
|
21470
|
+
/**
|
|
21471
|
+
* Drop an `authStatus` that describes a credential this machine no longer holds.
|
|
21472
|
+
*
|
|
21473
|
+
* A row can be marked expired by a rotation that actually SUCCEEDED: the refresh
|
|
21474
|
+
* token is single-use, so when two callers of one session rotate at the same time
|
|
21475
|
+
* the loser is told `invalid_grant` for a token the winner already spent. That is
|
|
21476
|
+
* a verdict about a spent generation, not about the sign-in — and nothing else in
|
|
21477
|
+
* the system ever revisits it, which is why the account stayed parked at
|
|
21478
|
+
* "需要重新登录" until the user pressed a button.
|
|
21479
|
+
*
|
|
21480
|
+
* The verdict is re-checked against the credential in force: if the stored
|
|
21481
|
+
* credential for the same session has moved on, the marker was about the old
|
|
21482
|
+
* token and is removed. A row still holding the refused token keeps its marker,
|
|
21483
|
+
* because that one genuinely needs a sign-in.
|
|
21484
|
+
*/
|
|
21485
|
+
async healStaleAuthFailures(current) {
|
|
21486
|
+
const data = current ?? await this.read();
|
|
21487
|
+
if (!data.accounts.some(isMarkedFailed)) return false;
|
|
21488
|
+
const live = await this.store.read().catch(() => null);
|
|
21489
|
+
if (live === null) return false;
|
|
21490
|
+
let changed = false;
|
|
21491
|
+
for (const account of data.accounts) {
|
|
21492
|
+
if (account.authStatus === void 0 || account.authStatus === "ok") continue;
|
|
21493
|
+
if (!minimaxCodeSameCredentialSession(live, account.credentials)) continue;
|
|
21494
|
+
if (!minimaxCodeCredentialAdvancedPast(live, account.credentials)) continue;
|
|
21495
|
+
account.authStatus = void 0;
|
|
21496
|
+
account.authFailedReason = void 0;
|
|
21497
|
+
account.credentials = live;
|
|
21498
|
+
changed = true;
|
|
21499
|
+
}
|
|
21500
|
+
if (!changed) return false;
|
|
21501
|
+
await this.write(data).catch(() => void 0);
|
|
21502
|
+
return true;
|
|
21503
|
+
}
|
|
21504
|
+
/**
|
|
21070
21505
|
* Pick the account for the next request.
|
|
21071
21506
|
*
|
|
21072
21507
|
* The returned credential carries its own region and record key, so a caller
|
|
@@ -21095,6 +21530,24 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
21095
21530
|
* pair back, and reading the pre-refresh file would undo it.
|
|
21096
21531
|
*/
|
|
21097
21532
|
async liveNativeCredential(account) {
|
|
21533
|
+
const found = await this.nativeAuthority(account);
|
|
21534
|
+
return found === null || !found.advanced ? null : found.credentials;
|
|
21535
|
+
}
|
|
21536
|
+
/**
|
|
21537
|
+
* The desktop app's own copy of one native account's session, and whether it has
|
|
21538
|
+
* moved on from the row's copy.
|
|
21539
|
+
*
|
|
21540
|
+
* The app rotates its token on its own schedule, so the copy the pool adopted can
|
|
21541
|
+
* be older than what is on disk. Only the same record slot counts: a different
|
|
21542
|
+
* record key or region is a different account, and substituting it would present
|
|
21543
|
+
* one account's token as another's.
|
|
21544
|
+
*
|
|
21545
|
+
* `advanced` is the whole reason this does not simply return the file: an
|
|
21546
|
+
* EXPIRED file is not newer authority (that is the app's session having lapsed),
|
|
21547
|
+
* while one that has moved on from the row is. The row's refresh token was spent
|
|
21548
|
+
* by whichever side rotated last, and only the file can say what replaced it.
|
|
21549
|
+
*/
|
|
21550
|
+
async nativeAuthority(account) {
|
|
21098
21551
|
if (account.source !== "minimax-native") return null;
|
|
21099
21552
|
const recordKey = account.credentials.recordKey;
|
|
21100
21553
|
if (recordKey === null || recordKey === "") return null;
|
|
@@ -21102,7 +21555,10 @@ var MinimaxCodeAccountPool = class extends AccountPoolCore {
|
|
|
21102
21555
|
if (stored === null || stored.source !== "minimax-native") return null;
|
|
21103
21556
|
if (stored.recordKey !== recordKey || stored.region !== account.credentials.region) return null;
|
|
21104
21557
|
if (!credentialIsFresh(stored)) return null;
|
|
21105
|
-
return
|
|
21558
|
+
return {
|
|
21559
|
+
credentials: stored,
|
|
21560
|
+
advanced: stored.refreshToken !== account.credentials.refreshToken
|
|
21561
|
+
};
|
|
21106
21562
|
}
|
|
21107
21563
|
/**
|
|
21108
21564
|
* Adopt the sign-in the desktop app currently holds into the pool.
|
|
@@ -21373,11 +21829,21 @@ function registerMinimaxCodeRoutes(ctx, store = new MinimaxCodeCredentialStore()
|
|
|
21373
21829
|
* network, and the status route reads the snapshot through the cache instead.
|
|
21374
21830
|
*/
|
|
21375
21831
|
const refreshQuota = async (force) => {
|
|
21376
|
-
const credentials = await store.
|
|
21832
|
+
const credentials = await (options.accountPool === void 0 ? ensureAccessToken$2(store, { fetchFn }).catch(() => null) : options.accountPool.getFreshCredential(void 0, fetchFn).catch(() => null));
|
|
21377
21833
|
if (credentials === null) return null;
|
|
21834
|
+
const readToken = credentials.refreshToken;
|
|
21835
|
+
const isCredentialStale = async () => {
|
|
21836
|
+
const live = await store.readFresh().catch(() => null);
|
|
21837
|
+
return live !== null && live.refreshToken !== readToken;
|
|
21838
|
+
};
|
|
21378
21839
|
return await fetchTokenPlanQuota(credentials, {
|
|
21379
21840
|
fetchFn,
|
|
21380
|
-
force
|
|
21841
|
+
force,
|
|
21842
|
+
isCredentialStale,
|
|
21843
|
+
renewCredential: async () => await (options.accountPool === void 0 ? ensureAccessToken$2(store, {
|
|
21844
|
+
fetchFn,
|
|
21845
|
+
force: true
|
|
21846
|
+
}) : options.accountPool.renewCredential(void 0, fetchFn)).catch(() => null)
|
|
21381
21847
|
}).catch(() => null);
|
|
21382
21848
|
};
|
|
21383
21849
|
/**
|
|
@@ -36317,13 +36783,204 @@ function installRelayProbe(ctx, options) {
|
|
|
36317
36783
|
};
|
|
36318
36784
|
}
|
|
36319
36785
|
//#endregion
|
|
36786
|
+
//#region src/host/reasoning-collapse-guard/index.ts
|
|
36787
|
+
const DEFAULT_GUARD_OPTIONS = {
|
|
36788
|
+
windowChars: 4096,
|
|
36789
|
+
minWindowChars: 1500,
|
|
36790
|
+
threshold: .85,
|
|
36791
|
+
ngramSize: 8,
|
|
36792
|
+
maxBreaksPerTurn: 3,
|
|
36793
|
+
cooldownMs: 3e4,
|
|
36794
|
+
includeModels: [],
|
|
36795
|
+
now: () => Date.now()
|
|
36796
|
+
};
|
|
36797
|
+
function pruneUndefined(options) {
|
|
36798
|
+
const result = {};
|
|
36799
|
+
for (const [key, value] of Object.entries(options)) if (value !== void 0) result[key] = value;
|
|
36800
|
+
return result;
|
|
36801
|
+
}
|
|
36802
|
+
/**
|
|
36803
|
+
* Validate caller options fail-loud and fill in defaults. A threshold outside
|
|
36804
|
+
* (0,1), a minimum above the window, or a non-positive budget throws rather
|
|
36805
|
+
* than falling back to a value nobody chose.
|
|
36806
|
+
*/
|
|
36807
|
+
function resolveGuardOptions(options = {}) {
|
|
36808
|
+
const resolved = {
|
|
36809
|
+
...DEFAULT_GUARD_OPTIONS,
|
|
36810
|
+
...pruneUndefined(options)
|
|
36811
|
+
};
|
|
36812
|
+
if (!Number.isInteger(resolved.windowChars) || resolved.windowChars < 1) throw new Error(`reasoning-collapse-guard: invalid windowChars ${String(resolved.windowChars)} — must be an integer >= 1`);
|
|
36813
|
+
if (!Number.isInteger(resolved.ngramSize) || resolved.ngramSize < 2) throw new Error(`reasoning-collapse-guard: invalid ngramSize ${String(resolved.ngramSize)} — must be an integer >= 2`);
|
|
36814
|
+
if (!Number.isInteger(resolved.minWindowChars) || resolved.minWindowChars < 1) throw new Error(`reasoning-collapse-guard: invalid minWindowChars ${String(resolved.minWindowChars)} — must be an integer >= 1`);
|
|
36815
|
+
if (resolved.minWindowChars > resolved.windowChars) throw new Error(`reasoning-collapse-guard: invalid minWindowChars ${resolved.minWindowChars} — must not exceed windowChars ${resolved.windowChars}`);
|
|
36816
|
+
if (resolved.windowChars < resolved.ngramSize) throw new Error(`reasoning-collapse-guard: windowChars ${resolved.windowChars} is smaller than ngramSize ${resolved.ngramSize}`);
|
|
36817
|
+
if (!Number.isFinite(resolved.threshold) || resolved.threshold <= 0 || resolved.threshold >= 1) throw new Error(`reasoning-collapse-guard: invalid threshold ${String(resolved.threshold)} — must be strictly between 0 and 1`);
|
|
36818
|
+
if (!Number.isInteger(resolved.maxBreaksPerTurn) || resolved.maxBreaksPerTurn < 1) throw new Error(`reasoning-collapse-guard: invalid maxBreaksPerTurn ${String(resolved.maxBreaksPerTurn)} — must be an integer >= 1`);
|
|
36819
|
+
if (!Number.isInteger(resolved.cooldownMs) || resolved.cooldownMs < 0) throw new Error(`reasoning-collapse-guard: invalid cooldownMs ${String(resolved.cooldownMs)} — must be an integer >= 0`);
|
|
36820
|
+
if (!Array.isArray(resolved.includeModels) || resolved.includeModels.some((entry) => typeof entry !== "string")) throw new Error("reasoning-collapse-guard: includeModels must be an array of strings");
|
|
36821
|
+
if (typeof resolved.now !== "function") throw new Error("reasoning-collapse-guard: now must be a function");
|
|
36822
|
+
return resolved;
|
|
36823
|
+
}
|
|
36824
|
+
/**
|
|
36825
|
+
* `1 - uniqueNgrams / totalNgrams` over the window.
|
|
36826
|
+
*
|
|
36827
|
+
* Scored over the whole window: a degenerate stream repeats within its own
|
|
36828
|
+
* trailing characters, so scoring part of the window would dilute the signal.
|
|
36829
|
+
* @param window - trailing reasoning characters.
|
|
36830
|
+
* @param n - n-gram size.
|
|
36831
|
+
* @returns collapse score in [0,1]; 0 means every n-gram is unique.
|
|
36832
|
+
*/
|
|
36833
|
+
function collapseScore(window, n) {
|
|
36834
|
+
if (window.length < n) return 0;
|
|
36835
|
+
const grams = /* @__PURE__ */ new Set();
|
|
36836
|
+
let total = 0;
|
|
36837
|
+
for (let i = 0; i + n <= window.length; i++) {
|
|
36838
|
+
grams.add(window.slice(i, i + n));
|
|
36839
|
+
total++;
|
|
36840
|
+
}
|
|
36841
|
+
if (total === 0) return 0;
|
|
36842
|
+
return 1 - grams.size / total;
|
|
36843
|
+
}
|
|
36844
|
+
const RESUME_HINT = "Your previous reasoning became repetitive and was stopped before it finished. Resume from what you already established instead of restarting the analysis, and keep the rest of this response short.";
|
|
36845
|
+
/** The second resume message, used once a resume has also collapsed. */
|
|
36846
|
+
const RESUME_HINT_STRICT = "Your reasoning has now repeated across two attempts. Do not re-derive the analysis a third time. Either state the conclusion you already reached, or make the single next tool call that moves the task forward. Keep this response to a few sentences.";
|
|
36847
|
+
function createBreakerState() {
|
|
36848
|
+
return {
|
|
36849
|
+
breaksThisTurn: 0,
|
|
36850
|
+
lastBreakAt: -Infinity,
|
|
36851
|
+
exhausted: false,
|
|
36852
|
+
resuming: false
|
|
36853
|
+
};
|
|
36854
|
+
}
|
|
36855
|
+
/**
|
|
36856
|
+
* The resume message is a literal user message so the guard loads on every
|
|
36857
|
+
* generation without importing a generation-bound message factory.
|
|
36858
|
+
*/
|
|
36859
|
+
function createResumeMessage(text) {
|
|
36860
|
+
return {
|
|
36861
|
+
role: "user",
|
|
36862
|
+
content: [{
|
|
36863
|
+
type: "text",
|
|
36864
|
+
text
|
|
36865
|
+
}]
|
|
36866
|
+
};
|
|
36867
|
+
}
|
|
36868
|
+
function appendWindow(current, addition, limit) {
|
|
36869
|
+
const combined = current + addition;
|
|
36870
|
+
return combined.length > limit ? combined.slice(-limit) : combined;
|
|
36871
|
+
}
|
|
36872
|
+
/**
|
|
36873
|
+
* Install the reasoning-collapse guard on a Context.
|
|
36874
|
+
*
|
|
36875
|
+
* The guard is inert unless the host exposes the `llm/stream` seam, so a
|
|
36876
|
+
* harness without it keeps the built-in behaviour instead of failing this
|
|
36877
|
+
* plugin's load.
|
|
36878
|
+
*
|
|
36879
|
+
* Resuming needs the live Agent for `cancel` and `steer`, and no supported
|
|
36880
|
+
* generation hands the agent to `llm/stream`. `agent/created` reports it on
|
|
36881
|
+
* every one of them, so the guard tracks the newest agent and keys the per-turn
|
|
36882
|
+
* break budget on the request's own abort signal — which exists for a whole
|
|
36883
|
+
* turn — rather than on the agent object.
|
|
36884
|
+
*
|
|
36885
|
+
* @param ctx - plugin context.
|
|
36886
|
+
* @param options - see {@link GuardOptions}; validated fail-loud.
|
|
36887
|
+
* @returns a disposer, or undefined when the seam is unavailable.
|
|
36888
|
+
*/
|
|
36889
|
+
function installReasoningCollapseGuard(ctx, options = {}) {
|
|
36890
|
+
const resolved = resolveGuardOptions(options);
|
|
36891
|
+
const runtime = ctx.llm;
|
|
36892
|
+
if (runtime === void 0 || typeof runtime.stream !== "function") return void 0;
|
|
36893
|
+
const original = runtime.stream;
|
|
36894
|
+
const watched = new Set(resolved.includeModels);
|
|
36895
|
+
const breakers = /* @__PURE__ */ new WeakMap();
|
|
36896
|
+
let currentAgent;
|
|
36897
|
+
const releaseAgent = typeof ctx.on === "function" ? ctx.on("agent/created", ({ agent }) => {
|
|
36898
|
+
currentAgent = agent;
|
|
36899
|
+
}) : void 0;
|
|
36900
|
+
function breakerFor(key) {
|
|
36901
|
+
let state = breakers.get(key);
|
|
36902
|
+
if (state === void 0) {
|
|
36903
|
+
state = createBreakerState();
|
|
36904
|
+
breakers.set(key, state);
|
|
36905
|
+
}
|
|
36906
|
+
return state;
|
|
36907
|
+
}
|
|
36908
|
+
function watchedModel(model) {
|
|
36909
|
+
if (watched.size === 0) return true;
|
|
36910
|
+
return model !== void 0 && watched.has(model);
|
|
36911
|
+
}
|
|
36912
|
+
/**
|
|
36913
|
+
* Stop the attempt and queue at most one resume for the turn.
|
|
36914
|
+
*
|
|
36915
|
+
* The resume is queued on a microtask because cancellation may discard
|
|
36916
|
+
* steering submitted while a turn is still unwinding; by the time the
|
|
36917
|
+
* microtask runs the abort has settled, so the wake lands on a fresh turn that
|
|
36918
|
+
* inherits the kept inbox.
|
|
36919
|
+
*/
|
|
36920
|
+
function breakOff(state, score) {
|
|
36921
|
+
state.breaksThisTurn++;
|
|
36922
|
+
state.lastBreakAt = resolved.now();
|
|
36923
|
+
ctx.logger?.warn(`reasoning-collapse-guard: stopped degenerate reasoning (score ${score.toFixed(3)}, break ${state.breaksThisTurn}/${resolved.maxBreaksPerTurn})`);
|
|
36924
|
+
const agent = currentAgent;
|
|
36925
|
+
agent?.cancel?.({
|
|
36926
|
+
kind: "hook",
|
|
36927
|
+
reason: "reasoning-collapse-guard"
|
|
36928
|
+
}, { keepInbox: true });
|
|
36929
|
+
if (agent === void 0 || state.resuming) return;
|
|
36930
|
+
state.resuming = true;
|
|
36931
|
+
const text = state.breaksThisTurn >= 2 ? RESUME_HINT_STRICT : RESUME_HINT;
|
|
36932
|
+
queueMicrotask(() => {
|
|
36933
|
+
state.resuming = false;
|
|
36934
|
+
agent.steer?.(createResumeMessage(text));
|
|
36935
|
+
});
|
|
36936
|
+
}
|
|
36937
|
+
runtime.stream = function reasoningCollapseGuardStream(request, next) {
|
|
36938
|
+
const state = breakerFor(request.signal ?? STATIC_KEY);
|
|
36939
|
+
if (state.exhausted || !watchedModel(request.model)) return next();
|
|
36940
|
+
async function* guarded() {
|
|
36941
|
+
let window = "";
|
|
36942
|
+
for await (const chunk of next()) {
|
|
36943
|
+
if (chunk.type === "block-start") window = "";
|
|
36944
|
+
if (chunk.type === "reasoning-delta" && typeof chunk.text === "string") {
|
|
36945
|
+
window = appendWindow(window, chunk.text, resolved.windowChars);
|
|
36946
|
+
if (window.length >= resolved.minWindowChars && resolved.now() - state.lastBreakAt >= resolved.cooldownMs) {
|
|
36947
|
+
const score = collapseScore(window, resolved.ngramSize);
|
|
36948
|
+
if (score >= resolved.threshold) if (state.breaksThisTurn >= resolved.maxBreaksPerTurn) state.exhausted = true;
|
|
36949
|
+
else {
|
|
36950
|
+
breakOff(state, score);
|
|
36951
|
+
return;
|
|
36952
|
+
}
|
|
36953
|
+
}
|
|
36954
|
+
}
|
|
36955
|
+
yield chunk;
|
|
36956
|
+
}
|
|
36957
|
+
}
|
|
36958
|
+
return guarded();
|
|
36959
|
+
};
|
|
36960
|
+
return () => {
|
|
36961
|
+
runtime.stream = original;
|
|
36962
|
+
releaseListener(releaseAgent);
|
|
36963
|
+
};
|
|
36964
|
+
}
|
|
36965
|
+
/** Shared key for the rare request that carries no abort signal. */
|
|
36966
|
+
const STATIC_KEY = {};
|
|
36967
|
+
/** Cordis event handles are either a disposer function or a disposable object. */
|
|
36968
|
+
function releaseListener(handle) {
|
|
36969
|
+
if (typeof handle === "function") {
|
|
36970
|
+
handle();
|
|
36971
|
+
return;
|
|
36972
|
+
}
|
|
36973
|
+
handle?.dispose?.();
|
|
36974
|
+
}
|
|
36975
|
+
//#endregion
|
|
36320
36976
|
//#region src/index.ts
|
|
36321
36977
|
const Config = z.object({
|
|
36322
36978
|
syncAgentPresets: z.boolean().default(true),
|
|
36323
36979
|
subagentModelAuthorization: z.boolean().default(true),
|
|
36324
36980
|
subagentModelTools: z.array(z.string()).default([]),
|
|
36325
36981
|
subagentModelInheritTools: z.array(z.string()).default(DEFAULT_SUBAGENT_INHERIT_TOOLS),
|
|
36326
|
-
subagentModelScope: z.union([z.const("session"), z.const("preference")]).default("session")
|
|
36982
|
+
subagentModelScope: z.union([z.const("session"), z.const("preference")]).default("session"),
|
|
36983
|
+
reasoningCollapseGuard: z.union([z.object({}), z.const(false)]).default({})
|
|
36327
36984
|
});
|
|
36328
36985
|
const inject = [
|
|
36329
36986
|
"webServer",
|
|
@@ -36388,6 +37045,17 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
36388
37045
|
});
|
|
36389
37046
|
}, "dsh-chatgpt-subscription: subagent model authorization");
|
|
36390
37047
|
});
|
|
37048
|
+
if (pluginConfig.reasoningCollapseGuard !== false) {
|
|
37049
|
+
const guardOptions = pluginConfig.reasoningCollapseGuard ?? {};
|
|
37050
|
+
ctx.effect(() => {
|
|
37051
|
+
const dispose = installReasoningCollapseGuard(ctx, guardOptions);
|
|
37052
|
+
if (dispose === void 0) {
|
|
37053
|
+
ctx.logger.warn("[dsh-chatgpt-subscription] reasoning-collapse guard skipped: this harness exposes no llm/stream seam");
|
|
37054
|
+
return () => void 0;
|
|
37055
|
+
}
|
|
37056
|
+
return dispose;
|
|
37057
|
+
}, "dsh-chatgpt-subscription: reasoning collapse guard");
|
|
37058
|
+
}
|
|
36391
37059
|
const probeEnvFile = relayProbeEnvFile();
|
|
36392
37060
|
if (relayProbeEnabled(process.env, { envFile: probeEnvFile })) {
|
|
36393
37061
|
const probePath = relayProbeLogPath(process.env, { envFile: probeEnvFile });
|
|
@@ -36716,4 +37384,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
36716
37384
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
36717
37385
|
}
|
|
36718
37386
|
//#endregion
|
|
36719
|
-
export { AccountPoolCore, AccountPoolStore, AntigravityAdapter, ADOPTED_CREDENTIAL_EXPIRED_HINT as CLAUDE_ADOPTED_CREDENTIAL_EXPIRED_HINT, API_BASE as CLAUDE_API_BASE, DEFAULT_VISIBLE_MODEL_IDS as CLAUDE_DEFAULT_VISIBLE_MODELS, FALLBACK_MODELS as CLAUDE_FALLBACK_MODELS, MESSAGES_PATH as CLAUDE_MESSAGES_PATH, CLAUDE_MODELS, CLAUDE_MODEL_IDS, PROVIDER_ID as CLAUDE_PROVIDER_ID, PROVIDER_NAME as CLAUDE_PROVIDER_NAME, QUOTA_CACHE_TTL_MS as CLAUDE_QUOTA_CACHE_TTL_MS, CLAUDE_REASONING_EFFORTS, ROUTE_PREFIX as CLAUDE_ROUTE_PREFIX, ClaudeAccountPool, ClaudeAdapter, FileCredentialStore as ClaudeCredentialStore, FileModelSettingsStore as ClaudeModelSettingsStore, CodexAccountPool, CodexChatGptAdapter, CommandCodeAccountPool, CommandCodeAdapter, FileCredentialStore$1 as CommandCodeCredentialStore, FileModelSettingsStore$1 as CommandCodeModelSettingsStore, Config, DEFAULT_SUBAGENT_INHERIT_TOOLS, FileCredentialStore$2 as FileCredentialStore, FileModelSettingsStore$2 as FileModelSettingsStore, KIMI_CODE_MODELS, KIMI_CODE_RETRY_POLICY_CONFIG, KimiCodeAccountPool, KimiCodeAdapter, FileCredentialStore$3 as KimiCodeCredentialStore, FileModelSettingsStore$3 as KimiCodeModelSettingsStore, LinuxFileTokenStore, AGENT_LLM_PREFIX as MINIMAX_CODE_AGENT_LLM_PREFIX, MESSAGES_PATH$1 as MINIMAX_CODE_MESSAGES_PATH, MINIMAX_CODE_MODELS, PROBE_MODEL as MINIMAX_CODE_PROBE_MODEL, MINIMAX_CODE_PROVIDER_ID, MINIMAX_CODE_PROVIDER_NAME, MINIMAX_CODE_REASONING_EFFORTS, REGION_HOSTS as MINIMAX_CODE_REGION_HOSTS, MINIMAX_CODE_RETRY_POLICY_CONFIG, MINIMAX_CODE_ROUTE_PREFIX, MacKeychainTokenStore, MinimaxCodeAccountPool, MinimaxCodeAdapter, MinimaxCodeCheckinService, MinimaxCodeCredentialStore, MinimaxCodeModelSettingsStore, OAuthService, ProxyManager, RELAY_PROBE_ENV, RELAY_PROBE_FILE_ENV, RELAY_PROBE_FILE_NAME, RELAY_PROBE_MAX_BYTES, RELAY_SOURCE_KINDS, RelayProbe, ResponsesClient, SUBAGENT_MODEL_SELECTION_NAMESPACE, SUBAGENT_POLICY_EVENT, SearchProviderSwitcher, UsageService, FALLBACK_MODELS$1 as WORKBUDDY_MODELS, WORKBUDDY_MODEL_IDS, WindowsDpapiTokenStore, WorkBuddyAdapter, WorkBuddyCheckinService, FileCredentialStore$4 as WorkBuddyCredentialStore, FileModelSettingsStore$4 as WorkBuddyModelSettingsStore, adoptedPoolAccountKey, apply, assertRequestBodyFits as assertMinimaxCodeBodyFits, assertStreamComplete as assertMinimaxCodeStreamComplete, auditChildRoutes, auditedRoutesOf, authorizedRoutesFor, beginLogin as beginClaudeLogin, beginWebLogin as beginKimiCodeLogin, beginWebLogin$1 as beginMinimaxCodeLogin, beginWebLogin$2 as beginWebLogin, buildClaudeModelOptions, buildMinimaxCodeModelOptions, buildMinimaxRequest, cancelLogin as cancelClaudeLogin, cancelWebLogin as cancelMinimaxCodeLogin, childRoutesOf, clampOutputToContext as clampMinimaxCodeOutputToContext, classifyFailure as classifyClaudeFailure, classifyKimiFailure, classifyMinimaxFailure, classifyFailure$1 as classifyWorkBuddyFailure, claudeCliVersion, claudeCodeCredentialPaths, claudeCodeCredentialPresence, codeForFailure as claudeCodeForFailure, credentialPath as claudeCredentialPath, defaultContextWindowFor as claudeDefaultContextWindow, maxOutputTokensFor as claudeMaxOutputTokens, claudeModelCanDisableThinking, modelSettingsPath as claudeModelSettingsPath, claudeModelSupportsImage, claudeModelSupportsTemperature, claudePoolPath, claudeReasoningEfforts, claudeThinkingMode, toLlmError as claudeToLlmError, clearCachedQuota, clearCachedCatalog as clearClaudeCatalog, clearCachedQuota$1 as clearClaudeQuota, clearCachedQuota$2 as clearCommandCodeQuota, clearCachedQuota$3 as clearKimiCodeQuota, clearCachedQuota$4 as clearMinimaxCodeQuota, clearCachedCatalog$1 as clearWorkBuddyCatalog, clearCachedQuota$5 as clearWorkBuddyQuota, closeMinimaxStream, codeBuddyAuthDir, codexPoolPath, commandCodeAccountKey, credentialPath$1 as commandCodeCredentialPath, modelSettingsPath$1 as commandCodeModelSettingsPath, commandCodePoolPath, contextWindowForModel, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createFileRelayProbeSink, createMinimaxCodeAccountsHandler, createMessage as createMinimaxCodeMessage, createPkcePair as createMinimaxCodePkcePair, createStreamState as createMinimaxCodeStreamState, createPlatformTokenStore, createSubagentAuthorization, credentialPath$2 as credentialPath, delegationDenialReason, delegationModeOf, describeCredentials as describeMinimaxCodeCredentials, detectSystemProxy, dshHomeDir, effectiveRouteOf, ensureAccessToken as ensureClaudeAccessToken, ensureAccessToken$1 as ensureKimiCodeAccessToken, ensureAccessToken$2 as ensureMinimaxCodeAccessToken, fetchAccountQuota, fetchAccountQuota$1 as fetchClaudeQuota, fetchAccountQuota$2 as fetchCommandCodeQuota, fetchAccountQuota$3 as fetchKimiCodeQuota, fetchUserInfo as fetchKimiCodeUserInfo, fetchTokenPlanQuota as fetchMinimaxCodeQuota, fetchAccountQuota$4 as fetchWorkBuddyQuota, getCachedQuota, getLoginStatus as getClaudeLoginStatus, getCachedQuota$1 as getClaudeQuota, getClaudeWebStatus, getWebLoginStatus as getCommandCodeLoginStatus, getCachedQuota$2 as getCommandCodeQuota, getCommandCodeWebStatus, getWebLoginStatus$1 as getKimiCodeLoginStatus, getCachedQuota$3 as getKimiCodeQuota, getKimiCodeWebStatus, getWebLogin as getMinimaxCodeLogin, getCachedQuota$4 as getMinimaxCodeQuota, getMinimaxCodeWebStatus, getCachedQuota$5 as getWorkBuddyQuota, getWorkBuddyWebStatus, inheritOverrideReason, inheritRouteDenialReason, inheritedRouteOf, inject, installRelayProbe, installSubagentModelAuthorization, isAdoptedClaudeCredential, isAdoptedCredentialExpired, isAdoptedPoolCredential, isClaudeReasoningEffort, isMinimaxCodeReasoningEffort, isRefreshTokenRejected as isMinimaxCodeRefreshTokenRejected, isThinkingDisabledEffort, credentialPath$3 as kimiCodeCredentialPath, kimiCodeModelDef, modelSettingsPath$2 as kimiCodeModelSettingsPath, kimiCodePoolPath, listModelIds as listMinimaxCodeModelIds, loadCatalog as loadClaudeCatalog, loadProviderModels as loadCommandCodeModels, loadProviderModels$1 as loadKimiCodeModels, loadConfigCatalog as loadWorkBuddyModels, loginAndSave, mapCodexUsage, accountFromCredentials as minimaxCodeAccountFromCredentials, accountHost as minimaxCodeAccountHost, agentBaseUrl as minimaxCodeAgentBaseUrl, authJsonPath as minimaxCodeAuthJsonPath, authStateJsonPath as minimaxCodeAuthStateJsonPath, catalogSize as minimaxCodeCatalogSize, credentialIsFresh as minimaxCodeCredentialIsFresh, effortForModel as minimaxCodeEffortForModel, minimaxHomeDir as minimaxCodeHomeDir, maxOutputTokensFor$1 as minimaxCodeMaxOutputTokens, messagesUrl as minimaxCodeMessagesUrl, minimaxCodeModelDef, minimaxCodeModelIds, minimaxCodeModelName, modelRequestHeaders as minimaxCodeModelRequestHeaders, modelSettingsPath$3 as minimaxCodeModelSettingsPath, pluginCredentialPath as minimaxCodePluginCredentialPath, minimaxCodePoolIdentity, minimaxCodePoolPath, minimaxCodePoolStatus, quotaHostCandidates as minimaxCodeQuotaHostCandidates, quotaRequestHeaders as minimaxCodeQuotaRequestHeaders, subPathOf as minimaxCodeSubPathOf, thinkingFieldFor as minimaxCodeThinkingField, tokenPlanRemainsUrl as minimaxCodeTokenPlanRemainsUrl, modelSettingsPath$4 as modelSettingsPath, normalizeDelegationToolNames, normalizeInheritToolNames, normalizeRotationStrategy, parseAllowedRoutes, parseClaudeCredentialDocument, parseClaudeCredentials, parseClaudePoolData, parseUsagePayload as parseClaudeUsagePayload, parseCodexPoolData, parseCodexUsage, parseCommandCodePoolData, parseKimiCodePoolData, parseMinimaxCodeCredentials, parseMinimaxCodePoolData, parseTokenPlanQuota as parseMinimaxCodeQuota, parseAnthropicUsage as parseMinimaxCodeUsage, parseResponsesStream, parseBilling as parseWorkBuddyBilling, parseConfigModels as parseWorkBuddyConfigModels, parseCredentialFile as parseWorkBuddyCredential, policyRoutesOf, pollWebLogin as pollMinimaxCodeLogin, probeConnection as probeClaudeConnection, processMinimaxStreamLine, readChildDescriptor, readClaudeCodeCredentials, redactToken as redactMinimaxCodeToken, refreshAntigravityToken, refreshAccessToken as refreshClaudeToken, refreshAccessToken$1 as refreshKimiCodeToken, refreshAccessToken$2 as refreshMinimaxCodeToken, refreshCredentials as refreshWorkBuddyCredentials, registerClaudePreferenceStore, registerClaudeRoutes, registerCommandCodePreferenceStore, registerCommandCodeRoutes, registerKimiCodePreferenceStore, registerKimiCodeRoutes, registerMinimaxCodePreferenceStore, registerMinimaxCodeRoutes, registerWorkBuddyPreferenceStore, registerWorkBuddyRoutes, relayProbeEnabled, relayProbeEnvFile, relayProbeEnvValue, relayProbeLogPath, requestDeviceAuthorization as requestKimiCodeDeviceAuthorization, requestDeviceAuthorization$1 as requestMinimaxCodeDeviceAuthorization, resetWebLogins as resetMinimaxCodeLogins, resolveEnabledModelIds as resolveClaudeEnabledModelIds, resolveLoginInput as resolveClaudeLoginInput, resolveClaudeModel, resolveRegion as resolveKimiCodeRegion, resolveMinimaxCodeEnabledModelIds, resolveQuotaCounts as resolveMinimaxCodeQuotaCounts, resolveWorkBuddyModel, revokeToken as revokeMinimaxCodeToken, saveApiKey as saveCommandCodeApiKey, scanCredentials as scanWorkBuddyCredentials, setClaudeCliVersion, beginWebLogin$3 as startCommandCodeLogin, subagentModelSelectionPreference, submitLoginInput as submitClaudeLoginInput, summarizeFailureBody as summarizeMinimaxCodeFailureBody, testConnection as testMinimaxCodeConnection, unauthorizedRouteReason, validateAuthorizationScope, validateDelegationToolNames, validateInheritToolNames, violationText, modelSettingsPath$5 as workBuddyModelSettingsPath, modelsForRegion as workBuddyModelsForRegion };
|
|
37387
|
+
export { AccountPoolCore, AccountPoolStore, AntigravityAdapter, ADOPTED_CREDENTIAL_EXPIRED_HINT as CLAUDE_ADOPTED_CREDENTIAL_EXPIRED_HINT, API_BASE as CLAUDE_API_BASE, DEFAULT_VISIBLE_MODEL_IDS as CLAUDE_DEFAULT_VISIBLE_MODELS, FALLBACK_MODELS as CLAUDE_FALLBACK_MODELS, MESSAGES_PATH as CLAUDE_MESSAGES_PATH, CLAUDE_MODELS, CLAUDE_MODEL_IDS, PROVIDER_ID as CLAUDE_PROVIDER_ID, PROVIDER_NAME as CLAUDE_PROVIDER_NAME, QUOTA_CACHE_TTL_MS as CLAUDE_QUOTA_CACHE_TTL_MS, CLAUDE_REASONING_EFFORTS, ROUTE_PREFIX as CLAUDE_ROUTE_PREFIX, ClaudeAccountPool, ClaudeAdapter, FileCredentialStore as ClaudeCredentialStore, FileModelSettingsStore as ClaudeModelSettingsStore, CodexAccountPool, CodexChatGptAdapter, CommandCodeAccountPool, CommandCodeAdapter, FileCredentialStore$1 as CommandCodeCredentialStore, FileModelSettingsStore$1 as CommandCodeModelSettingsStore, Config, DEFAULT_GUARD_OPTIONS, DEFAULT_SUBAGENT_INHERIT_TOOLS, FileCredentialStore$2 as FileCredentialStore, FileModelSettingsStore$2 as FileModelSettingsStore, KIMI_CODE_MODELS, KIMI_CODE_RETRY_POLICY_CONFIG, KimiCodeAccountPool, KimiCodeAdapter, FileCredentialStore$3 as KimiCodeCredentialStore, FileModelSettingsStore$3 as KimiCodeModelSettingsStore, LinuxFileTokenStore, AGENT_LLM_PREFIX as MINIMAX_CODE_AGENT_LLM_PREFIX, MESSAGES_PATH$1 as MINIMAX_CODE_MESSAGES_PATH, MINIMAX_CODE_MODELS, PROBE_MODEL as MINIMAX_CODE_PROBE_MODEL, MINIMAX_CODE_PROVIDER_ID, MINIMAX_CODE_PROVIDER_NAME, MINIMAX_CODE_REASONING_EFFORTS, REGION_HOSTS as MINIMAX_CODE_REGION_HOSTS, MINIMAX_CODE_RETRY_POLICY_CONFIG, MINIMAX_CODE_ROUTE_PREFIX, MacKeychainTokenStore, MinimaxCodeAccountPool, MinimaxCodeAdapter, MinimaxCodeCheckinService, MinimaxCodeCredentialStore, MinimaxCodeModelSettingsStore, OAuthService, ProxyManager, RELAY_PROBE_ENV, RELAY_PROBE_FILE_ENV, RELAY_PROBE_FILE_NAME, RELAY_PROBE_MAX_BYTES, RELAY_SOURCE_KINDS, RESUME_HINT, RESUME_HINT_STRICT, RelayProbe, ResponsesClient, SUBAGENT_MODEL_SELECTION_NAMESPACE, SUBAGENT_POLICY_EVENT, SearchProviderSwitcher, UsageService, FALLBACK_MODELS$1 as WORKBUDDY_MODELS, WORKBUDDY_MODEL_IDS, WindowsDpapiTokenStore, WorkBuddyAdapter, WorkBuddyCheckinService, FileCredentialStore$4 as WorkBuddyCredentialStore, FileModelSettingsStore$4 as WorkBuddyModelSettingsStore, adoptedPoolAccountKey, apply, assertRequestBodyFits as assertMinimaxCodeBodyFits, assertStreamComplete as assertMinimaxCodeStreamComplete, auditChildRoutes, auditedRoutesOf, authorizedRoutesFor, beginLogin as beginClaudeLogin, beginWebLogin as beginKimiCodeLogin, beginWebLogin$1 as beginMinimaxCodeLogin, beginWebLogin$2 as beginWebLogin, buildClaudeModelOptions, buildMinimaxCodeModelOptions, buildMinimaxRequest, cancelLogin as cancelClaudeLogin, cancelWebLogin as cancelMinimaxCodeLogin, childRoutesOf, clampOutputToContext as clampMinimaxCodeOutputToContext, classifyFailure as classifyClaudeFailure, classifyKimiFailure, classifyMinimaxFailure, classifyFailure$1 as classifyWorkBuddyFailure, claudeCliVersion, claudeCodeCredentialPaths, claudeCodeCredentialPresence, codeForFailure as claudeCodeForFailure, credentialPath as claudeCredentialPath, defaultContextWindowFor as claudeDefaultContextWindow, maxOutputTokensFor as claudeMaxOutputTokens, claudeModelCanDisableThinking, modelSettingsPath as claudeModelSettingsPath, claudeModelSupportsImage, claudeModelSupportsTemperature, claudePoolPath, claudeReasoningEfforts, claudeThinkingMode, toLlmError as claudeToLlmError, clearCachedQuota, clearCachedCatalog as clearClaudeCatalog, clearCachedQuota$1 as clearClaudeQuota, clearCachedQuota$2 as clearCommandCodeQuota, clearCachedQuota$3 as clearKimiCodeQuota, clearCachedQuota$4 as clearMinimaxCodeQuota, clearCachedCatalog$1 as clearWorkBuddyCatalog, clearCachedQuota$5 as clearWorkBuddyQuota, closeMinimaxStream, codeBuddyAuthDir, codexPoolPath, collapseScore, commandCodeAccountKey, credentialPath$1 as commandCodeCredentialPath, modelSettingsPath$1 as commandCodeModelSettingsPath, commandCodePoolPath, contextWindowForModel, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createFileRelayProbeSink, createMinimaxCodeAccountsHandler, createMessage as createMinimaxCodeMessage, createPkcePair as createMinimaxCodePkcePair, createStreamState as createMinimaxCodeStreamState, createPlatformTokenStore, createSubagentAuthorization, credentialPath$2 as credentialPath, delegationDenialReason, delegationModeOf, describeCredentials as describeMinimaxCodeCredentials, detectSystemProxy, dshHomeDir, effectiveRouteOf, ensureAccessToken as ensureClaudeAccessToken, ensureAccessToken$1 as ensureKimiCodeAccessToken, ensureAccessToken$2 as ensureMinimaxCodeAccessToken, fetchAccountQuota, fetchAccountQuota$1 as fetchClaudeQuota, fetchAccountQuota$2 as fetchCommandCodeQuota, fetchAccountQuota$3 as fetchKimiCodeQuota, fetchUserInfo as fetchKimiCodeUserInfo, fetchTokenPlanQuota as fetchMinimaxCodeQuota, fetchAccountQuota$4 as fetchWorkBuddyQuota, getCachedQuota, getLoginStatus as getClaudeLoginStatus, getCachedQuota$1 as getClaudeQuota, getClaudeWebStatus, getWebLoginStatus as getCommandCodeLoginStatus, getCachedQuota$2 as getCommandCodeQuota, getCommandCodeWebStatus, getWebLoginStatus$1 as getKimiCodeLoginStatus, getCachedQuota$3 as getKimiCodeQuota, getKimiCodeWebStatus, getWebLogin as getMinimaxCodeLogin, getCachedQuota$4 as getMinimaxCodeQuota, getMinimaxCodeWebStatus, getCachedQuota$5 as getWorkBuddyQuota, getWorkBuddyWebStatus, inheritOverrideReason, inheritRouteDenialReason, inheritedRouteOf, inject, installReasoningCollapseGuard, installRelayProbe, installSubagentModelAuthorization, isAdoptedClaudeCredential, isAdoptedCredentialExpired, isAdoptedPoolCredential, isClaudeReasoningEffort, isMinimaxCodeReasoningEffort, isRefreshTokenRejected as isMinimaxCodeRefreshTokenRejected, isThinkingDisabledEffort, credentialPath$3 as kimiCodeCredentialPath, kimiCodeModelDef, modelSettingsPath$2 as kimiCodeModelSettingsPath, kimiCodePoolPath, listModelIds as listMinimaxCodeModelIds, loadCatalog as loadClaudeCatalog, loadProviderModels as loadCommandCodeModels, loadProviderModels$1 as loadKimiCodeModels, loadConfigCatalog as loadWorkBuddyModels, loginAndSave, mapCodexUsage, accountFromCredentials as minimaxCodeAccountFromCredentials, accountHost as minimaxCodeAccountHost, agentBaseUrl as minimaxCodeAgentBaseUrl, authJsonPath as minimaxCodeAuthJsonPath, authStateJsonPath as minimaxCodeAuthStateJsonPath, catalogSize as minimaxCodeCatalogSize, credentialIsFresh as minimaxCodeCredentialIsFresh, effortForModel as minimaxCodeEffortForModel, minimaxHomeDir as minimaxCodeHomeDir, maxOutputTokensFor$1 as minimaxCodeMaxOutputTokens, messagesUrl as minimaxCodeMessagesUrl, minimaxCodeModelDef, minimaxCodeModelIds, minimaxCodeModelName, modelRequestHeaders as minimaxCodeModelRequestHeaders, modelSettingsPath$3 as minimaxCodeModelSettingsPath, pluginCredentialPath as minimaxCodePluginCredentialPath, minimaxCodePoolIdentity, minimaxCodePoolPath, minimaxCodePoolStatus, quotaHostCandidates as minimaxCodeQuotaHostCandidates, quotaRequestHeaders as minimaxCodeQuotaRequestHeaders, subPathOf as minimaxCodeSubPathOf, thinkingFieldFor as minimaxCodeThinkingField, tokenPlanRemainsUrl as minimaxCodeTokenPlanRemainsUrl, modelSettingsPath$4 as modelSettingsPath, normalizeDelegationToolNames, normalizeInheritToolNames, normalizeRotationStrategy, parseAllowedRoutes, parseClaudeCredentialDocument, parseClaudeCredentials, parseClaudePoolData, parseUsagePayload as parseClaudeUsagePayload, parseCodexPoolData, parseCodexUsage, parseCommandCodePoolData, parseKimiCodePoolData, parseMinimaxCodeCredentials, parseMinimaxCodePoolData, parseTokenPlanQuota as parseMinimaxCodeQuota, parseAnthropicUsage as parseMinimaxCodeUsage, parseResponsesStream, parseBilling as parseWorkBuddyBilling, parseConfigModels as parseWorkBuddyConfigModels, parseCredentialFile as parseWorkBuddyCredential, policyRoutesOf, pollWebLogin as pollMinimaxCodeLogin, probeConnection as probeClaudeConnection, processMinimaxStreamLine, readChildDescriptor, readClaudeCodeCredentials, redactToken as redactMinimaxCodeToken, refreshAntigravityToken, refreshAccessToken as refreshClaudeToken, refreshAccessToken$1 as refreshKimiCodeToken, refreshAccessToken$2 as refreshMinimaxCodeToken, refreshCredentials as refreshWorkBuddyCredentials, registerClaudePreferenceStore, registerClaudeRoutes, registerCommandCodePreferenceStore, registerCommandCodeRoutes, registerKimiCodePreferenceStore, registerKimiCodeRoutes, registerMinimaxCodePreferenceStore, registerMinimaxCodeRoutes, registerWorkBuddyPreferenceStore, registerWorkBuddyRoutes, relayProbeEnabled, relayProbeEnvFile, relayProbeEnvValue, relayProbeLogPath, requestDeviceAuthorization as requestKimiCodeDeviceAuthorization, requestDeviceAuthorization$1 as requestMinimaxCodeDeviceAuthorization, resetWebLogins as resetMinimaxCodeLogins, resolveEnabledModelIds as resolveClaudeEnabledModelIds, resolveLoginInput as resolveClaudeLoginInput, resolveClaudeModel, resolveGuardOptions, resolveRegion as resolveKimiCodeRegion, resolveMinimaxCodeEnabledModelIds, resolveQuotaCounts as resolveMinimaxCodeQuotaCounts, resolveWorkBuddyModel, revokeToken as revokeMinimaxCodeToken, saveApiKey as saveCommandCodeApiKey, scanCredentials as scanWorkBuddyCredentials, setClaudeCliVersion, beginWebLogin$3 as startCommandCodeLogin, subagentModelSelectionPreference, submitLoginInput as submitClaudeLoginInput, summarizeFailureBody as summarizeMinimaxCodeFailureBody, testConnection as testMinimaxCodeConnection, unauthorizedRouteReason, validateAuthorizationScope, validateDelegationToolNames, validateInheritToolNames, violationText, modelSettingsPath$5 as workBuddyModelSettingsPath, modelsForRegion as workBuddyModelsForRegion };
|