qwenproxy-cli 1.4.8 → 1.5.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/dist/clean-cache.js +21 -0
- package/dist/delete-chats.js +50 -6
- package/dist/index.js +114 -42
- package/dist/login.js +8 -0
- package/dist/reset-cooldowns.js +8 -0
- package/package.json +1 -1
- package/src/core/account-manager.ts +9 -0
- package/src/core/server-log-buffer.ts +10 -3
- package/src/core/version.ts +1 -1
- package/src/routes/chat/account.ts +22 -10
- package/src/routes/chat/helpers.ts +29 -1
- package/src/routes/chat/retry-policy.ts +5 -5
- package/src/routes/chat/streaming.ts +12 -5
- package/src/routes/upload.ts +19 -14
- package/src/services/qwen-chat-pool.ts +10 -6
- package/src/services/qwen-headers.ts +2 -1
- package/src/services/qwen.ts +22 -5
package/dist/clean-cache.js
CHANGED
|
@@ -1915,7 +1915,15 @@ function defaultCooldownDurationMs() {
|
|
|
1915
1915
|
return computeQuotaCooldownMs(Date.now());
|
|
1916
1916
|
}
|
|
1917
1917
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
1918
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
1919
|
+
clearAccountCooldown(accountId);
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1918
1922
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
1923
|
+
if (duration <= 0) {
|
|
1924
|
+
clearAccountCooldown(accountId);
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1919
1927
|
const until = Date.now() + duration;
|
|
1920
1928
|
const cooldownReason = reason ?? "RateLimited";
|
|
1921
1929
|
cooldowns.set(accountId, {
|
|
@@ -1938,6 +1946,19 @@ function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
|
1938
1946
|
);
|
|
1939
1947
|
}
|
|
1940
1948
|
}
|
|
1949
|
+
function clearAccountCooldown(accountId) {
|
|
1950
|
+
cooldowns.delete(accountId);
|
|
1951
|
+
if (accountId !== "global") {
|
|
1952
|
+
try {
|
|
1953
|
+
updateAccountCooldown(accountId, 0, null);
|
|
1954
|
+
} catch (err) {
|
|
1955
|
+
console.error(
|
|
1956
|
+
`\u274C [AccountManager] Failed to clear cooldown in DB for ${accountId}:`,
|
|
1957
|
+
err.message
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1941
1962
|
function markAccountHeadersReady(accountId) {
|
|
1942
1963
|
if (!accountId || accountId === "global") return;
|
|
1943
1964
|
headersReadyAccounts.add(accountId);
|
package/dist/delete-chats.js
CHANGED
|
@@ -1978,7 +1978,15 @@ function defaultCooldownDurationMs() {
|
|
|
1978
1978
|
return computeQuotaCooldownMs(Date.now());
|
|
1979
1979
|
}
|
|
1980
1980
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
1981
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
1982
|
+
clearAccountCooldown(accountId);
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1981
1985
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
1986
|
+
if (duration <= 0) {
|
|
1987
|
+
clearAccountCooldown(accountId);
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1982
1990
|
const until = Date.now() + duration;
|
|
1983
1991
|
const cooldownReason = reason ?? "RateLimited";
|
|
1984
1992
|
cooldowns.set(accountId, {
|
|
@@ -2001,6 +2009,19 @@ function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
|
2001
2009
|
);
|
|
2002
2010
|
}
|
|
2003
2011
|
}
|
|
2012
|
+
function clearAccountCooldown(accountId) {
|
|
2013
|
+
cooldowns.delete(accountId);
|
|
2014
|
+
if (accountId !== "global") {
|
|
2015
|
+
try {
|
|
2016
|
+
updateAccountCooldown(accountId, 0, null);
|
|
2017
|
+
} catch (err) {
|
|
2018
|
+
console.error(
|
|
2019
|
+
`\u274C [AccountManager] Failed to clear cooldown in DB for ${accountId}:`,
|
|
2020
|
+
err.message
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2004
2025
|
function markAccountHeadersReady(accountId) {
|
|
2005
2026
|
if (!accountId || accountId === "global") return;
|
|
2006
2027
|
headersReadyAccounts.add(accountId);
|
|
@@ -2769,7 +2790,8 @@ function buildQwenRequestHeaders(opts) {
|
|
|
2769
2790
|
"bx-v": opts.bxV || "2.5.37",
|
|
2770
2791
|
source: "web",
|
|
2771
2792
|
version: opts.version || getQwenWebVersion(),
|
|
2772
|
-
timezone: opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
2793
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
2794
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
2773
2795
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
2774
2796
|
// back to the static fingerprint otherwise.
|
|
2775
2797
|
"sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
|
|
@@ -7623,13 +7645,15 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
|
|
|
7623
7645
|
} catch {
|
|
7624
7646
|
}
|
|
7625
7647
|
}
|
|
7626
|
-
|
|
7648
|
+
const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
|
|
7649
|
+
if (isUnauthorized) {
|
|
7627
7650
|
if (accountId) {
|
|
7628
7651
|
try {
|
|
7629
7652
|
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
7630
7653
|
const refreshed = await refreshAccountToken2(accountId);
|
|
7631
7654
|
if (refreshed.success) {
|
|
7632
|
-
|
|
7655
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
7656
|
+
return createQwenChatSession(freshHeaders, model, accountId, chatMode);
|
|
7633
7657
|
}
|
|
7634
7658
|
} catch {
|
|
7635
7659
|
}
|
|
@@ -9623,8 +9647,18 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
9623
9647
|
);
|
|
9624
9648
|
}
|
|
9625
9649
|
};
|
|
9650
|
+
let forceRefresh = options?.forceNewChat === true;
|
|
9651
|
+
if (!forceRefresh && accountId) {
|
|
9652
|
+
try {
|
|
9653
|
+
const basic = await getBasicHeaders2(accountId);
|
|
9654
|
+
if (isTokenExpiringSoon(basic.cookie, 3)) {
|
|
9655
|
+
forceRefresh = true;
|
|
9656
|
+
}
|
|
9657
|
+
} catch {
|
|
9658
|
+
}
|
|
9659
|
+
}
|
|
9626
9660
|
const captured = await getQwenHeaders(
|
|
9627
|
-
|
|
9661
|
+
forceRefresh,
|
|
9628
9662
|
accountId
|
|
9629
9663
|
);
|
|
9630
9664
|
ensureNotAborted();
|
|
@@ -9638,7 +9672,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
9638
9672
|
if (options && "chatSessionId" in options) {
|
|
9639
9673
|
if (options.chatSessionId === null || options.chatSessionId === "") {
|
|
9640
9674
|
const acquired = await acquireNewQwenChatSession(
|
|
9641
|
-
|
|
9675
|
+
activeHeaders,
|
|
9642
9676
|
model,
|
|
9643
9677
|
accountId,
|
|
9644
9678
|
options?.chatMode ?? "thread"
|
|
@@ -9646,6 +9680,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
9646
9680
|
chatSessionId = acquired.chatId;
|
|
9647
9681
|
leasedWarmChat = acquired.leasedFromPool;
|
|
9648
9682
|
createdNewChat = true;
|
|
9683
|
+
try {
|
|
9684
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
9685
|
+
activeHeaders = fresh.headers;
|
|
9686
|
+
} catch {
|
|
9687
|
+
}
|
|
9649
9688
|
} else {
|
|
9650
9689
|
chatSessionId = options.chatSessionId;
|
|
9651
9690
|
}
|
|
@@ -9653,7 +9692,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
9653
9692
|
chatSessionId = captured.chatSessionId;
|
|
9654
9693
|
if (!chatSessionId) {
|
|
9655
9694
|
const acquired = await acquireNewQwenChatSession(
|
|
9656
|
-
|
|
9695
|
+
activeHeaders,
|
|
9657
9696
|
model,
|
|
9658
9697
|
accountId,
|
|
9659
9698
|
options?.chatMode ?? "thread"
|
|
@@ -9661,6 +9700,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
9661
9700
|
chatSessionId = acquired.chatId;
|
|
9662
9701
|
leasedWarmChat = acquired.leasedFromPool;
|
|
9663
9702
|
createdNewChat = true;
|
|
9703
|
+
try {
|
|
9704
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
9705
|
+
activeHeaders = fresh.headers;
|
|
9706
|
+
} catch {
|
|
9707
|
+
}
|
|
9664
9708
|
}
|
|
9665
9709
|
}
|
|
9666
9710
|
ensureNotAborted();
|
package/dist/index.js
CHANGED
|
@@ -2986,7 +2986,15 @@ function defaultCooldownDurationMs() {
|
|
|
2986
2986
|
return computeQuotaCooldownMs(Date.now());
|
|
2987
2987
|
}
|
|
2988
2988
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
2989
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
2990
|
+
clearAccountCooldown(accountId);
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2989
2993
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
2994
|
+
if (duration <= 0) {
|
|
2995
|
+
clearAccountCooldown(accountId);
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2990
2998
|
const until = Date.now() + duration;
|
|
2991
2999
|
const cooldownReason = reason ?? "RateLimited";
|
|
2992
3000
|
cooldowns.set(accountId, {
|
|
@@ -3986,7 +3994,8 @@ function buildQwenRequestHeaders(opts) {
|
|
|
3986
3994
|
"bx-v": opts.bxV || "2.5.37",
|
|
3987
3995
|
source: "web",
|
|
3988
3996
|
version: opts.version || getQwenWebVersion(),
|
|
3989
|
-
timezone: opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3997
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3998
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3990
3999
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
3991
4000
|
// back to the static fingerprint otherwise.
|
|
3992
4001
|
"sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
|
|
@@ -9087,8 +9096,11 @@ ${thought}`);
|
|
|
9087
9096
|
}
|
|
9088
9097
|
return sections.join("\n\n");
|
|
9089
9098
|
}
|
|
9099
|
+
function isAnswerPhase(phase) {
|
|
9100
|
+
return typeof phase === "string" && ANSWER_PHASES.has(phase);
|
|
9101
|
+
}
|
|
9090
9102
|
function isThinkingPhase(phase) {
|
|
9091
|
-
return phase === "
|
|
9103
|
+
return typeof phase === "string" && THINKING_PHASES.has(phase);
|
|
9092
9104
|
}
|
|
9093
9105
|
function extractThinkingContent(delta) {
|
|
9094
9106
|
if (Array.isArray(delta?.extra?.summary_title?.content) || Array.isArray(delta?.extra?.summary_thought?.content)) {
|
|
@@ -9179,9 +9191,25 @@ function buildUsage(accumulator) {
|
|
|
9179
9191
|
}
|
|
9180
9192
|
return usage;
|
|
9181
9193
|
}
|
|
9194
|
+
var ANSWER_PHASES, THINKING_PHASES;
|
|
9182
9195
|
var init_helpers = __esm({
|
|
9183
9196
|
"src/routes/chat/helpers.ts"() {
|
|
9184
9197
|
"use strict";
|
|
9198
|
+
ANSWER_PHASES = /* @__PURE__ */ new Set([
|
|
9199
|
+
"answer",
|
|
9200
|
+
"deep_research_answer",
|
|
9201
|
+
"ReportGeneration",
|
|
9202
|
+
"PdfMdGen",
|
|
9203
|
+
"image",
|
|
9204
|
+
"image_generation",
|
|
9205
|
+
"image_gen",
|
|
9206
|
+
"t2i"
|
|
9207
|
+
]);
|
|
9208
|
+
THINKING_PHASES = /* @__PURE__ */ new Set([
|
|
9209
|
+
"think",
|
|
9210
|
+
"thinking_summary",
|
|
9211
|
+
"DeepThinking"
|
|
9212
|
+
]);
|
|
9185
9213
|
}
|
|
9186
9214
|
});
|
|
9187
9215
|
|
|
@@ -9355,7 +9383,7 @@ function classifyRetryAction(err, options) {
|
|
|
9355
9383
|
switchAccount: true,
|
|
9356
9384
|
forceNewChat: true,
|
|
9357
9385
|
retryAfterMs: baseDelayMs,
|
|
9358
|
-
accountCooldownMs:
|
|
9386
|
+
accountCooldownMs: 0,
|
|
9359
9387
|
accountCooldownReason: "PersonalizationFailed"
|
|
9360
9388
|
});
|
|
9361
9389
|
}
|
|
@@ -9365,8 +9393,8 @@ function classifyRetryAction(err, options) {
|
|
|
9365
9393
|
forceNewChat: true,
|
|
9366
9394
|
retryWithFullPrompt: true,
|
|
9367
9395
|
retryAfterMs: Math.min(baseDelayMs, 1e3),
|
|
9368
|
-
accountCooldownMs:
|
|
9369
|
-
accountCooldownReason: "
|
|
9396
|
+
accountCooldownMs: 0,
|
|
9397
|
+
accountCooldownReason: "AuthExpired"
|
|
9370
9398
|
});
|
|
9371
9399
|
}
|
|
9372
9400
|
if (isCorruptedChatHistoryError(err)) {
|
|
@@ -9783,8 +9811,8 @@ async function requestCompletionsInBrowser(params) {
|
|
|
9783
9811
|
const choice = chunk?.choices?.[0];
|
|
9784
9812
|
const delta = choice?.delta;
|
|
9785
9813
|
const phase = delta?.phase;
|
|
9786
|
-
const
|
|
9787
|
-
if (choice?.finish_reason || delta?.status === "finished" &&
|
|
9814
|
+
const isAnswerPhase2 = !phase || phase === "answer";
|
|
9815
|
+
if (choice?.finish_reason || delta?.status === "finished" && isAnswerPhase2) {
|
|
9788
9816
|
finished = true;
|
|
9789
9817
|
break;
|
|
9790
9818
|
}
|
|
@@ -10835,13 +10863,15 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
|
|
|
10835
10863
|
} catch {
|
|
10836
10864
|
}
|
|
10837
10865
|
}
|
|
10838
|
-
|
|
10866
|
+
const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
|
|
10867
|
+
if (isUnauthorized) {
|
|
10839
10868
|
if (accountId) {
|
|
10840
10869
|
try {
|
|
10841
10870
|
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
10842
10871
|
const refreshed = await refreshAccountToken2(accountId);
|
|
10843
10872
|
if (refreshed.success) {
|
|
10844
|
-
|
|
10873
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
10874
|
+
return createQwenChatSession(freshHeaders, model, accountId, chatMode);
|
|
10845
10875
|
}
|
|
10846
10876
|
} catch {
|
|
10847
10877
|
}
|
|
@@ -12835,8 +12865,18 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12835
12865
|
);
|
|
12836
12866
|
}
|
|
12837
12867
|
};
|
|
12868
|
+
let forceRefresh = options?.forceNewChat === true;
|
|
12869
|
+
if (!forceRefresh && accountId) {
|
|
12870
|
+
try {
|
|
12871
|
+
const basic = await getBasicHeaders2(accountId);
|
|
12872
|
+
if (isTokenExpiringSoon(basic.cookie, 3)) {
|
|
12873
|
+
forceRefresh = true;
|
|
12874
|
+
}
|
|
12875
|
+
} catch {
|
|
12876
|
+
}
|
|
12877
|
+
}
|
|
12838
12878
|
const captured = await getQwenHeaders(
|
|
12839
|
-
|
|
12879
|
+
forceRefresh,
|
|
12840
12880
|
accountId
|
|
12841
12881
|
);
|
|
12842
12882
|
ensureNotAborted();
|
|
@@ -12850,7 +12890,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12850
12890
|
if (options && "chatSessionId" in options) {
|
|
12851
12891
|
if (options.chatSessionId === null || options.chatSessionId === "") {
|
|
12852
12892
|
const acquired = await acquireNewQwenChatSession(
|
|
12853
|
-
|
|
12893
|
+
activeHeaders,
|
|
12854
12894
|
model,
|
|
12855
12895
|
accountId,
|
|
12856
12896
|
options?.chatMode ?? "thread"
|
|
@@ -12858,6 +12898,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12858
12898
|
chatSessionId = acquired.chatId;
|
|
12859
12899
|
leasedWarmChat = acquired.leasedFromPool;
|
|
12860
12900
|
createdNewChat = true;
|
|
12901
|
+
try {
|
|
12902
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
12903
|
+
activeHeaders = fresh.headers;
|
|
12904
|
+
} catch {
|
|
12905
|
+
}
|
|
12861
12906
|
} else {
|
|
12862
12907
|
chatSessionId = options.chatSessionId;
|
|
12863
12908
|
}
|
|
@@ -12865,7 +12910,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12865
12910
|
chatSessionId = captured.chatSessionId;
|
|
12866
12911
|
if (!chatSessionId) {
|
|
12867
12912
|
const acquired = await acquireNewQwenChatSession(
|
|
12868
|
-
|
|
12913
|
+
activeHeaders,
|
|
12869
12914
|
model,
|
|
12870
12915
|
accountId,
|
|
12871
12916
|
options?.chatMode ?? "thread"
|
|
@@ -12873,6 +12918,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12873
12918
|
chatSessionId = acquired.chatId;
|
|
12874
12919
|
leasedWarmChat = acquired.leasedFromPool;
|
|
12875
12920
|
createdNewChat = true;
|
|
12921
|
+
try {
|
|
12922
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
12923
|
+
activeHeaders = fresh.headers;
|
|
12924
|
+
} catch {
|
|
12925
|
+
}
|
|
12876
12926
|
}
|
|
12877
12927
|
}
|
|
12878
12928
|
ensureNotAborted();
|
|
@@ -14031,10 +14081,12 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
|
|
|
14031
14081
|
}
|
|
14032
14082
|
data = await response.json().catch(() => null);
|
|
14033
14083
|
}
|
|
14034
|
-
const
|
|
14084
|
+
const codeLower = (data?.data?.code || data?.code || "").toLowerCase();
|
|
14085
|
+
const detailsLower = (typeof data?.data?.details === "string" ? data.data.details : typeof data?.message === "string" ? data.message : "").toLowerCase();
|
|
14086
|
+
const is401 = data?.success === false && (codeLower === "unauthorized" || detailsLower.includes("401") || detailsLower.includes("token has expired") || detailsLower.includes("unauthorized"));
|
|
14035
14087
|
if (is401) {
|
|
14036
14088
|
try {
|
|
14037
|
-
const {
|
|
14089
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
14038
14090
|
const { getBasicHeaders: getBasicHeaders3 } = await Promise.resolve().then(() => (init_auth_playwright(), auth_playwright_exports));
|
|
14039
14091
|
const { loadAccounts: loadAccounts3 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
14040
14092
|
const resolvedId2 = accountId ?? loadAccounts3()[0]?.id;
|
|
@@ -14042,16 +14094,18 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
|
|
|
14042
14094
|
console.warn(
|
|
14043
14095
|
`[Upload] STS token 401 \u2014 refreshing session with re-auth and retrying...`
|
|
14044
14096
|
);
|
|
14045
|
-
await
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14097
|
+
const refreshResult = await refreshAccountToken2(resolvedId2);
|
|
14098
|
+
if (refreshResult.success) {
|
|
14099
|
+
const fresh = await getBasicHeaders3(resolvedId2);
|
|
14100
|
+
headers.cookie = fresh.cookie;
|
|
14101
|
+
headers["user-agent"] = fresh.userAgent;
|
|
14102
|
+
headers["bx-v"] = fresh.bxV;
|
|
14103
|
+
if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
|
|
14104
|
+
if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
|
|
14105
|
+
const retryRes = await doFetch(headers);
|
|
14106
|
+
if (retryRes.ok) {
|
|
14107
|
+
data = await retryRes.json().catch(() => null);
|
|
14108
|
+
}
|
|
14055
14109
|
}
|
|
14056
14110
|
}
|
|
14057
14111
|
} catch (refreshErr) {
|
|
@@ -17166,15 +17220,26 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17166
17220
|
}
|
|
17167
17221
|
}
|
|
17168
17222
|
if (policy.reason === "account_initialization_failed") {
|
|
17169
|
-
|
|
17170
|
-
|
|
17171
|
-
|
|
17172
|
-
|
|
17173
|
-
|
|
17174
|
-
currentAccountId
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17223
|
+
const cooldownMs = policy.accountCooldownMs ?? 0;
|
|
17224
|
+
if (cooldownMs > 0) {
|
|
17225
|
+
console.warn(
|
|
17226
|
+
`\u26A0\uFE0F [Chat] Account initialization failed | ${currentAccountEmail} | cooldown=${Math.round(cooldownMs / 1e3)}s`
|
|
17227
|
+
);
|
|
17228
|
+
markAccountFailed(currentAccountId);
|
|
17229
|
+
markAccountRateLimited(
|
|
17230
|
+
currentAccountId,
|
|
17231
|
+
cooldownMs,
|
|
17232
|
+
policy.accountCooldownReason
|
|
17233
|
+
);
|
|
17234
|
+
} else {
|
|
17235
|
+
console.warn(
|
|
17236
|
+
`\u26A0\uFE0F [Chat] Account auth expired/retryable | ${currentAccountEmail} | rotating without cooldown`
|
|
17237
|
+
);
|
|
17238
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
17239
|
+
Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
|
|
17240
|
+
});
|
|
17241
|
+
}
|
|
17242
|
+
}
|
|
17178
17243
|
return { success: false, error: err };
|
|
17179
17244
|
}
|
|
17180
17245
|
if (policy.retryable && shouldSwitchAccount && !isSingleAccount && accountSwitches < maxAccountSwitches) {
|
|
@@ -17183,7 +17248,7 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17183
17248
|
console.warn(
|
|
17184
17249
|
`\u{1F504} [Chat] Switching account after ${policy.reason} | ${currentAccountEmail} -> ${maskEmail(nextAccount.email)}`
|
|
17185
17250
|
);
|
|
17186
|
-
if (policy.accountCooldownMs
|
|
17251
|
+
if ((policy.accountCooldownMs ?? 0) > 0) {
|
|
17187
17252
|
markAccountRateLimited(
|
|
17188
17253
|
currentAccountId,
|
|
17189
17254
|
policy.accountCooldownMs,
|
|
@@ -20066,7 +20131,7 @@ async function processNonStreamingResponse(params) {
|
|
|
20066
20131
|
foundStr = true;
|
|
20067
20132
|
}
|
|
20068
20133
|
}
|
|
20069
|
-
} else if (delta.phase
|
|
20134
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
20070
20135
|
isThinkingChunk = false;
|
|
20071
20136
|
if (delta.content !== void 0) {
|
|
20072
20137
|
const newContent = delta.content || "";
|
|
@@ -20805,13 +20870,17 @@ async function processStreamingResponse(params) {
|
|
|
20805
20870
|
return false;
|
|
20806
20871
|
}
|
|
20807
20872
|
const switchAccount = policy.switchAccount && !retryInvalidInputOnSameAccount;
|
|
20808
|
-
if (switchAccount && (policy.accountCooldownMs
|
|
20873
|
+
if (switchAccount && (policy.accountCooldownMs ?? 0) > 0) {
|
|
20809
20874
|
markAccountRateLimited(
|
|
20810
20875
|
currentAccountId,
|
|
20811
20876
|
policy.accountCooldownMs,
|
|
20812
20877
|
policy.accountCooldownReason || "StreamRetry"
|
|
20813
20878
|
);
|
|
20814
20879
|
}
|
|
20880
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
20881
|
+
Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
|
|
20882
|
+
});
|
|
20883
|
+
}
|
|
20815
20884
|
retryContext.releaseAccountLease?.();
|
|
20816
20885
|
retryContext.releaseAccountLease = null;
|
|
20817
20886
|
await reader.cancel().catch(() => void 0);
|
|
@@ -21055,7 +21124,7 @@ async function processStreamingResponse(params) {
|
|
|
21055
21124
|
}
|
|
21056
21125
|
);
|
|
21057
21126
|
}
|
|
21058
|
-
if (delta.phase
|
|
21127
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
21059
21128
|
upstreamDone = true;
|
|
21060
21129
|
if (!clientDisconnected) flushWrites();
|
|
21061
21130
|
break;
|
|
@@ -21078,7 +21147,7 @@ async function processStreamingResponse(params) {
|
|
|
21078
21147
|
foundStr = true;
|
|
21079
21148
|
}
|
|
21080
21149
|
}
|
|
21081
|
-
} else if (delta.phase
|
|
21150
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
21082
21151
|
isThinkingChunk = false;
|
|
21083
21152
|
if (delta.content !== void 0) {
|
|
21084
21153
|
const newContent = delta.content || "";
|
|
@@ -21456,7 +21525,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21456
21525
|
if (!delta) {
|
|
21457
21526
|
continue;
|
|
21458
21527
|
}
|
|
21459
|
-
if (delta.phase
|
|
21528
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
21460
21529
|
upstreamDone = true;
|
|
21461
21530
|
break retryReadLoop;
|
|
21462
21531
|
}
|
|
@@ -25650,11 +25719,14 @@ function recordServerLog(level, text) {
|
|
|
25650
25719
|
}
|
|
25651
25720
|
line = line.replace(/^(?:\[?(?:INFO|WARN|WARNING|ERROR|ERR|DEBUG)\]?\s+)+/i, "").replace(/([\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}]\uFE0F?)\s{2,}/gu, "$1 ");
|
|
25652
25721
|
if (!line) continue;
|
|
25653
|
-
const
|
|
25722
|
+
const now = Date.now();
|
|
25723
|
+
const isDuplicate = logHistory.slice(-10).some(
|
|
25724
|
+
(entry2) => (entry2.time === time || entry2._ts && now - entry2._ts < 1500) && entry2.level === level && entry2.message === line
|
|
25725
|
+
);
|
|
25654
25726
|
if (isDuplicate) {
|
|
25655
25727
|
continue;
|
|
25656
25728
|
}
|
|
25657
|
-
const entry = { time, level, message: line };
|
|
25729
|
+
const entry = { time, level, message: line, _ts: now };
|
|
25658
25730
|
logHistory.push(entry);
|
|
25659
25731
|
if (logHistory.length > MAX_LOG_HISTORY) {
|
|
25660
25732
|
logHistory.shift();
|
|
@@ -25817,7 +25889,7 @@ function getAppVersion() {
|
|
|
25817
25889
|
}
|
|
25818
25890
|
} catch {
|
|
25819
25891
|
}
|
|
25820
|
-
cachedVersion = "v1.
|
|
25892
|
+
cachedVersion = "v1.5.0";
|
|
25821
25893
|
return cachedVersion;
|
|
25822
25894
|
}
|
|
25823
25895
|
var cachedVersion;
|
package/dist/login.js
CHANGED
|
@@ -2016,7 +2016,15 @@ function defaultCooldownDurationMs() {
|
|
|
2016
2016
|
return computeQuotaCooldownMs(Date.now());
|
|
2017
2017
|
}
|
|
2018
2018
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
2019
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
2020
|
+
clearAccountCooldown(accountId);
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2019
2023
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
2024
|
+
if (duration <= 0) {
|
|
2025
|
+
clearAccountCooldown(accountId);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2020
2028
|
const until = Date.now() + duration;
|
|
2021
2029
|
const cooldownReason = reason ?? "RateLimited";
|
|
2022
2030
|
cooldowns.set(accountId, {
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -1110,7 +1110,15 @@ function defaultCooldownDurationMs() {
|
|
|
1110
1110
|
return computeQuotaCooldownMs(Date.now());
|
|
1111
1111
|
}
|
|
1112
1112
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
1113
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
1114
|
+
clearAccountCooldown(accountId);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1113
1117
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
1118
|
+
if (duration <= 0) {
|
|
1119
|
+
clearAccountCooldown(accountId);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1114
1122
|
const until = Date.now() + duration;
|
|
1115
1123
|
const cooldownReason = reason ?? "RateLimited";
|
|
1116
1124
|
cooldowns.set(accountId, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -50,7 +50,16 @@ export function markAccountRateLimited(
|
|
|
50
50
|
reason?: string,
|
|
51
51
|
options: { silent?: boolean } = {},
|
|
52
52
|
): void {
|
|
53
|
+
// If cooldown is explicitly 0 or negative, do not park the account.
|
|
54
|
+
if (cooldownMs !== undefined && cooldownMs <= 0) {
|
|
55
|
+
clearAccountCooldown(accountId);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
53
58
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
59
|
+
if (duration <= 0) {
|
|
60
|
+
clearAccountCooldown(accountId);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
54
63
|
const until = Date.now() + duration;
|
|
55
64
|
const cooldownReason = reason ?? "RateLimited";
|
|
56
65
|
|
|
@@ -11,6 +11,7 @@ export interface ServerLogMessage {
|
|
|
11
11
|
time: string;
|
|
12
12
|
level: "INFO" | "WARN" | "ERROR";
|
|
13
13
|
message: string;
|
|
14
|
+
_ts?: number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const MAX_LOG_HISTORY = 2000;
|
|
@@ -80,15 +81,21 @@ export function recordServerLog(level: "INFO" | "WARN" | "ERROR", text: string):
|
|
|
80
81
|
.replace(/([\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}]\uFE0F?)\s{2,}/gu, "$1 ");
|
|
81
82
|
if (!line) continue;
|
|
82
83
|
|
|
83
|
-
// Prevent identical duplicate logs within the same second window
|
|
84
|
+
// Prevent identical duplicate logs within the same second or 1.5s window
|
|
85
|
+
const now = Date.now();
|
|
84
86
|
const isDuplicate = logHistory
|
|
85
87
|
.slice(-10)
|
|
86
|
-
.some(
|
|
88
|
+
.some(
|
|
89
|
+
(entry) =>
|
|
90
|
+
(entry.time === time || (entry._ts && now - entry._ts < 1500)) &&
|
|
91
|
+
entry.level === level &&
|
|
92
|
+
entry.message === line,
|
|
93
|
+
);
|
|
87
94
|
if (isDuplicate) {
|
|
88
95
|
continue;
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
const entry: ServerLogMessage = { time, level, message: line };
|
|
98
|
+
const entry: ServerLogMessage = { time, level, message: line, _ts: now };
|
|
92
99
|
logHistory.push(entry);
|
|
93
100
|
if (logHistory.length > MAX_LOG_HISTORY) {
|
|
94
101
|
logHistory.shift();
|
package/src/core/version.ts
CHANGED
|
@@ -1681,15 +1681,27 @@ async function tryCreateStreamWithRetry(
|
|
|
1681
1681
|
}
|
|
1682
1682
|
|
|
1683
1683
|
if (policy.reason === "account_initialization_failed") {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
currentAccountId
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1684
|
+
const cooldownMs = policy.accountCooldownMs ?? 0;
|
|
1685
|
+
if (cooldownMs > 0) {
|
|
1686
|
+
console.warn(
|
|
1687
|
+
`⚠️ [Chat] Account initialization failed | ${currentAccountEmail} | cooldown=${Math.round(cooldownMs / 1000)}s`,
|
|
1688
|
+
);
|
|
1689
|
+
markAccountFailed(currentAccountId);
|
|
1690
|
+
markAccountRateLimited(
|
|
1691
|
+
currentAccountId,
|
|
1692
|
+
cooldownMs,
|
|
1693
|
+
policy.accountCooldownReason,
|
|
1694
|
+
);
|
|
1695
|
+
} else {
|
|
1696
|
+
console.warn(
|
|
1697
|
+
`⚠️ [Chat] Account auth expired/retryable | ${currentAccountEmail} | rotating without cooldown`,
|
|
1698
|
+
);
|
|
1699
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
1700
|
+
import("../../services/playwright.ts")
|
|
1701
|
+
.then(({ refreshAccountToken }) => refreshAccountToken(currentAccountId))
|
|
1702
|
+
.catch(() => {});
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1693
1705
|
return { success: false, error: err };
|
|
1694
1706
|
}
|
|
1695
1707
|
|
|
@@ -1712,7 +1724,7 @@ async function tryCreateStreamWithRetry(
|
|
|
1712
1724
|
console.warn(
|
|
1713
1725
|
`🔄 [Chat] Switching account after ${policy.reason} | ${currentAccountEmail} -> ${maskEmail(nextAccount.email)}`,
|
|
1714
1726
|
);
|
|
1715
|
-
if (policy.accountCooldownMs
|
|
1727
|
+
if ((policy.accountCooldownMs ?? 0) > 0) {
|
|
1716
1728
|
markAccountRateLimited(
|
|
1717
1729
|
currentAccountId,
|
|
1718
1730
|
policy.accountCooldownMs,
|
|
@@ -113,13 +113,41 @@ export function formatThinkingSummaryContent(delta: any): string {
|
|
|
113
113
|
return sections.join("\n\n");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Verified answer/content phases from Qwen Web:
|
|
118
|
+
* - "answer": standard text completions
|
|
119
|
+
* - "deep_research_answer", "ReportGeneration", "PdfMdGen": deep research & search modes
|
|
120
|
+
* - "image", "image_generation", "image_gen", "t2i": multimodal/image phases
|
|
121
|
+
*/
|
|
122
|
+
export const ANSWER_PHASES = new Set([
|
|
123
|
+
"answer",
|
|
124
|
+
"deep_research_answer",
|
|
125
|
+
"ReportGeneration",
|
|
126
|
+
"PdfMdGen",
|
|
127
|
+
"image",
|
|
128
|
+
"image_generation",
|
|
129
|
+
"image_gen",
|
|
130
|
+
"t2i",
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
export function isAnswerPhase(phase: unknown): boolean {
|
|
134
|
+
return typeof phase === "string" && ANSWER_PHASES.has(phase);
|
|
135
|
+
}
|
|
136
|
+
|
|
116
137
|
/**
|
|
117
138
|
* Matches thinking phases verified in upstream Qwen Web HAR:
|
|
118
139
|
* - "thinking_summary": structured thinking in Qwen Max / Plus
|
|
119
140
|
* - "think": incremental thinking deltas in Qwen Omni
|
|
141
|
+
* - "DeepThinking": deep thinking models
|
|
120
142
|
*/
|
|
143
|
+
export const THINKING_PHASES = new Set([
|
|
144
|
+
"think",
|
|
145
|
+
"thinking_summary",
|
|
146
|
+
"DeepThinking",
|
|
147
|
+
]);
|
|
148
|
+
|
|
121
149
|
export function isThinkingPhase(phase: unknown): boolean {
|
|
122
|
-
return phase === "
|
|
150
|
+
return typeof phase === "string" && THINKING_PHASES.has(phase);
|
|
123
151
|
}
|
|
124
152
|
|
|
125
153
|
/**
|
|
@@ -486,14 +486,14 @@ export function classifyRetryAction(
|
|
|
486
486
|
switchAccount: true,
|
|
487
487
|
forceNewChat: true,
|
|
488
488
|
retryAfterMs: baseDelayMs,
|
|
489
|
-
accountCooldownMs:
|
|
489
|
+
accountCooldownMs: 0,
|
|
490
490
|
accountCooldownReason: "PersonalizationFailed",
|
|
491
491
|
});
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
// Upstream 401 / Unauthorized on chat creation or API requests:
|
|
495
|
-
//
|
|
496
|
-
//
|
|
495
|
+
// Access token expired (15m window). Rotate without locking into a 300s cooldown
|
|
496
|
+
// so the account can immediately be renewed via refresh_token.
|
|
497
497
|
if (
|
|
498
498
|
code === "createchatinvalidresponse" ||
|
|
499
499
|
code === "createchatfailed" ||
|
|
@@ -510,8 +510,8 @@ export function classifyRetryAction(
|
|
|
510
510
|
forceNewChat: true,
|
|
511
511
|
retryWithFullPrompt: true,
|
|
512
512
|
retryAfterMs: Math.min(baseDelayMs, 1_000),
|
|
513
|
-
accountCooldownMs:
|
|
514
|
-
accountCooldownReason: "
|
|
513
|
+
accountCooldownMs: 0,
|
|
514
|
+
accountCooldownReason: "AuthExpired",
|
|
515
515
|
});
|
|
516
516
|
}
|
|
517
517
|
// Specialized recoveries first (even if wrapped as RetryableQwenStreamError)
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
import {
|
|
69
69
|
getIncrementalDelta,
|
|
70
70
|
isThinkingPhase,
|
|
71
|
+
isAnswerPhase,
|
|
71
72
|
extractThinkingContent,
|
|
72
73
|
formatThinkingSummaryContent,
|
|
73
74
|
shouldSuppressStreamAbort,
|
|
@@ -470,7 +471,7 @@ export async function processNonStreamingResponse(
|
|
|
470
471
|
foundStr = true;
|
|
471
472
|
}
|
|
472
473
|
}
|
|
473
|
-
} else if (delta.phase
|
|
474
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
474
475
|
isThinkingChunk = false;
|
|
475
476
|
if (delta.content !== undefined) {
|
|
476
477
|
const newContent = delta.content || "";
|
|
@@ -1436,7 +1437,7 @@ export async function processStreamingResponse(
|
|
|
1436
1437
|
|
|
1437
1438
|
if (
|
|
1438
1439
|
switchAccount &&
|
|
1439
|
-
(policy.accountCooldownMs
|
|
1440
|
+
(policy.accountCooldownMs ?? 0) > 0
|
|
1440
1441
|
) {
|
|
1441
1442
|
markAccountRateLimited(
|
|
1442
1443
|
currentAccountId,
|
|
@@ -1445,6 +1446,12 @@ export async function processStreamingResponse(
|
|
|
1445
1446
|
);
|
|
1446
1447
|
}
|
|
1447
1448
|
|
|
1449
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
1450
|
+
import("../../services/playwright.ts")
|
|
1451
|
+
.then(({ refreshAccountToken }) => refreshAccountToken(currentAccountId))
|
|
1452
|
+
.catch(() => {});
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1448
1455
|
retryContext.releaseAccountLease?.();
|
|
1449
1456
|
retryContext.releaseAccountLease = null;
|
|
1450
1457
|
await reader.cancel().catch(() => undefined);
|
|
@@ -1776,7 +1783,7 @@ export async function processStreamingResponse(
|
|
|
1776
1783
|
// "phase":"answer"} delta and NO trailing [DONE]. Treat it as
|
|
1777
1784
|
// the terminal event so we don't wait on the keep-alive
|
|
1778
1785
|
// connection to close (up to the 60s/10min idle timeout).
|
|
1779
|
-
if (delta.phase
|
|
1786
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
1780
1787
|
upstreamDone = true;
|
|
1781
1788
|
if (!clientDisconnected) flushWrites();
|
|
1782
1789
|
break; // Exit the for loop; the while check leaves the read loop
|
|
@@ -1800,7 +1807,7 @@ export async function processStreamingResponse(
|
|
|
1800
1807
|
foundStr = true;
|
|
1801
1808
|
}
|
|
1802
1809
|
}
|
|
1803
|
-
} else if (delta.phase
|
|
1810
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
1804
1811
|
isThinkingChunk = false;
|
|
1805
1812
|
if (delta.content !== undefined) {
|
|
1806
1813
|
const newContent = delta.content || "";
|
|
@@ -2290,7 +2297,7 @@ export async function processStreamingResponse(
|
|
|
2290
2297
|
// The retry stream may also terminate with an answer-finished
|
|
2291
2298
|
// delta when upstream sends no [DONE]. Leave the read loop
|
|
2292
2299
|
// immediately so we don't stall on the keep-alive connection.
|
|
2293
|
-
if (delta.phase
|
|
2300
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
2294
2301
|
upstreamDone = true;
|
|
2295
2302
|
break retryReadLoop;
|
|
2296
2303
|
}
|
package/src/routes/upload.ts
CHANGED
|
@@ -387,15 +387,18 @@ async function getSTSToken(
|
|
|
387
387
|
}
|
|
388
388
|
data = await response.json().catch(() => null);
|
|
389
389
|
}
|
|
390
|
+
const codeLower = (data?.data?.code || data?.code || "").toLowerCase();
|
|
391
|
+
const detailsLower = (typeof data?.data?.details === "string" ? data.data.details : typeof data?.message === "string" ? data.message : "").toLowerCase();
|
|
390
392
|
const is401 =
|
|
391
393
|
data?.success === false &&
|
|
392
|
-
(
|
|
393
|
-
(
|
|
394
|
-
|
|
394
|
+
(codeLower === "unauthorized" ||
|
|
395
|
+
detailsLower.includes("401") ||
|
|
396
|
+
detailsLower.includes("token has expired") ||
|
|
397
|
+
detailsLower.includes("unauthorized"));
|
|
395
398
|
|
|
396
399
|
if (is401) {
|
|
397
400
|
try {
|
|
398
|
-
const {
|
|
401
|
+
const { refreshAccountToken } = await import("../services/playwright.ts");
|
|
399
402
|
const { getBasicHeaders } = await import("../services/auth-playwright.ts");
|
|
400
403
|
const { loadAccounts } = await import("../core/accounts.ts");
|
|
401
404
|
const resolvedId = accountId ?? loadAccounts()[0]?.id;
|
|
@@ -403,16 +406,18 @@ async function getSTSToken(
|
|
|
403
406
|
console.warn(
|
|
404
407
|
`[Upload] STS token 401 — refreshing session with re-auth and retrying...`,
|
|
405
408
|
);
|
|
406
|
-
await
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
409
|
+
const refreshResult = await refreshAccountToken(resolvedId);
|
|
410
|
+
if (refreshResult.success) {
|
|
411
|
+
const fresh = await getBasicHeaders(resolvedId);
|
|
412
|
+
headers.cookie = fresh.cookie;
|
|
413
|
+
headers["user-agent"] = fresh.userAgent;
|
|
414
|
+
headers["bx-v"] = fresh.bxV;
|
|
415
|
+
if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
|
|
416
|
+
if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
|
|
417
|
+
const retryRes = await doFetch(headers);
|
|
418
|
+
if (retryRes.ok) {
|
|
419
|
+
data = await retryRes.json().catch(() => null);
|
|
420
|
+
}
|
|
416
421
|
}
|
|
417
422
|
}
|
|
418
423
|
} catch (refreshErr) {
|
|
@@ -71,18 +71,22 @@ async function createQwenChatSession(
|
|
|
71
71
|
} catch {}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
json?.data?.code === "
|
|
76
|
-
json?.code === "
|
|
74
|
+
const isUnauthorized =
|
|
75
|
+
json?.data?.code?.toLowerCase() === "unauthorized" ||
|
|
76
|
+
json?.code?.toLowerCase() === "unauthorized" ||
|
|
77
77
|
rawLower.includes("unauthorized") ||
|
|
78
|
-
rawLower.includes("permission to access")
|
|
79
|
-
|
|
78
|
+
rawLower.includes("permission to access") ||
|
|
79
|
+
rawLower.includes("token has expired") ||
|
|
80
|
+
rawLower.includes("401");
|
|
81
|
+
|
|
82
|
+
if (isUnauthorized) {
|
|
80
83
|
if (accountId) {
|
|
81
84
|
try {
|
|
82
85
|
const { refreshAccountToken } = await import("./playwright.ts");
|
|
83
86
|
const refreshed = await refreshAccountToken(accountId);
|
|
84
87
|
if (refreshed.success) {
|
|
85
|
-
|
|
88
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
89
|
+
return createQwenChatSession(freshHeaders, model, accountId, chatMode);
|
|
86
90
|
}
|
|
87
91
|
} catch {}
|
|
88
92
|
}
|
|
@@ -79,7 +79,8 @@ export function buildQwenRequestHeaders(
|
|
|
79
79
|
"bx-v": opts.bxV || "2.5.37",
|
|
80
80
|
source: "web",
|
|
81
81
|
version: opts.version || getQwenWebVersion(),
|
|
82
|
-
timezone: opts.extra?.timezone || new Date().toString().split(" (")[0],
|
|
82
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || new Date().toString().split(" (")[0],
|
|
83
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || new Date().toString().split(" (")[0],
|
|
83
84
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
84
85
|
// back to the static fingerprint otherwise.
|
|
85
86
|
"sec-ch-ua":
|
package/src/services/qwen.ts
CHANGED
|
@@ -2763,10 +2763,19 @@ async function createQwenStreamInternal(
|
|
|
2763
2763
|
};
|
|
2764
2764
|
|
|
2765
2765
|
// A new logical chat session should reuse the warmed header cache when available.
|
|
2766
|
-
//
|
|
2767
|
-
|
|
2766
|
+
// Proactively check if the access token is expiring soon (<3m) and refresh quietly before starting.
|
|
2767
|
+
let forceRefresh = options?.forceNewChat === true;
|
|
2768
|
+
if (!forceRefresh && accountId) {
|
|
2769
|
+
try {
|
|
2770
|
+
const basic = await getBasicHeaders(accountId);
|
|
2771
|
+
if (isTokenExpiringSoon(basic.cookie, 3)) {
|
|
2772
|
+
forceRefresh = true;
|
|
2773
|
+
}
|
|
2774
|
+
} catch {}
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2768
2777
|
const captured = await getQwenHeaders(
|
|
2769
|
-
|
|
2778
|
+
forceRefresh,
|
|
2770
2779
|
accountId,
|
|
2771
2780
|
);
|
|
2772
2781
|
ensureNotAborted();
|
|
@@ -2782,7 +2791,7 @@ async function createQwenStreamInternal(
|
|
|
2782
2791
|
if (options && "chatSessionId" in options) {
|
|
2783
2792
|
if (options.chatSessionId === null || options.chatSessionId === "") {
|
|
2784
2793
|
const acquired = await acquireNewQwenChatSession(
|
|
2785
|
-
|
|
2794
|
+
activeHeaders,
|
|
2786
2795
|
model,
|
|
2787
2796
|
accountId,
|
|
2788
2797
|
options?.chatMode ?? "thread",
|
|
@@ -2790,6 +2799,10 @@ async function createQwenStreamInternal(
|
|
|
2790
2799
|
chatSessionId = acquired.chatId;
|
|
2791
2800
|
leasedWarmChat = acquired.leasedFromPool;
|
|
2792
2801
|
createdNewChat = true;
|
|
2802
|
+
try {
|
|
2803
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
2804
|
+
activeHeaders = fresh.headers;
|
|
2805
|
+
} catch {}
|
|
2793
2806
|
} else {
|
|
2794
2807
|
chatSessionId = options.chatSessionId;
|
|
2795
2808
|
}
|
|
@@ -2797,7 +2810,7 @@ async function createQwenStreamInternal(
|
|
|
2797
2810
|
chatSessionId = captured.chatSessionId;
|
|
2798
2811
|
if (!chatSessionId) {
|
|
2799
2812
|
const acquired = await acquireNewQwenChatSession(
|
|
2800
|
-
|
|
2813
|
+
activeHeaders,
|
|
2801
2814
|
model,
|
|
2802
2815
|
accountId,
|
|
2803
2816
|
options?.chatMode ?? "thread",
|
|
@@ -2805,6 +2818,10 @@ async function createQwenStreamInternal(
|
|
|
2805
2818
|
chatSessionId = acquired.chatId;
|
|
2806
2819
|
leasedWarmChat = acquired.leasedFromPool;
|
|
2807
2820
|
createdNewChat = true;
|
|
2821
|
+
try {
|
|
2822
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
2823
|
+
activeHeaders = fresh.headers;
|
|
2824
|
+
} catch {}
|
|
2808
2825
|
}
|
|
2809
2826
|
}
|
|
2810
2827
|
|