qwenproxy-cli 1.6.4 → 1.6.5
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 +73 -10
- package/dist/delete-chats.js +172 -44
- package/dist/index.js +234 -58
- package/dist/login.js +81 -15
- package/dist/reset-cooldowns.js +76 -11
- package/package.json +1 -1
- package/src/core/mutex.ts +4 -0
- package/src/core/version.ts +1 -1
- package/src/routes/anthropic/translate.ts +15 -7
- package/src/routes/chat/helpers.ts +4 -2
- package/src/routes/chat/streaming.ts +32 -10
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +41 -15
- package/src/services/qwen-headers.ts +3 -2
- package/src/services/qwen.ts +87 -30
- package/src/tools/tool-call-heuristics.ts +88 -0
- package/src/utils/timezone.ts +56 -0
package/dist/login.js
CHANGED
|
@@ -1892,6 +1892,7 @@ var init_mutex = __esm({
|
|
|
1892
1892
|
locked = false;
|
|
1893
1893
|
lockedAt = 0;
|
|
1894
1894
|
lockedByKey = "";
|
|
1895
|
+
epoch = 0;
|
|
1895
1896
|
async acquire(timeoutMs = 3e5, key = "") {
|
|
1896
1897
|
const holdLimitMs = this.maxHoldMs;
|
|
1897
1898
|
if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
|
|
@@ -1902,6 +1903,7 @@ var init_mutex = __esm({
|
|
|
1902
1903
|
this.locked = false;
|
|
1903
1904
|
this.lockedAt = 0;
|
|
1904
1905
|
this.lockedByKey = "";
|
|
1906
|
+
this.epoch++;
|
|
1905
1907
|
}
|
|
1906
1908
|
if (!this.locked) {
|
|
1907
1909
|
this.locked = true;
|
|
@@ -1940,10 +1942,12 @@ var init_mutex = __esm({
|
|
|
1940
1942
|
}
|
|
1941
1943
|
}
|
|
1942
1944
|
createRelease() {
|
|
1945
|
+
const epoch = this.epoch;
|
|
1943
1946
|
let released = false;
|
|
1944
1947
|
return () => {
|
|
1945
1948
|
if (released) return;
|
|
1946
1949
|
released = true;
|
|
1950
|
+
if (epoch !== this.epoch) return;
|
|
1947
1951
|
this.release();
|
|
1948
1952
|
};
|
|
1949
1953
|
}
|
|
@@ -2934,6 +2938,45 @@ var init_waf_isolation = __esm({
|
|
|
2934
2938
|
}
|
|
2935
2939
|
});
|
|
2936
2940
|
|
|
2941
|
+
// src/utils/timezone.ts
|
|
2942
|
+
function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
|
|
2943
|
+
const dayName = DAYS[date.getDay()];
|
|
2944
|
+
const monthName = MONTHS[date.getMonth()];
|
|
2945
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2946
|
+
const year = date.getFullYear();
|
|
2947
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2948
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2949
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2950
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
2951
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
2952
|
+
const absOffset = Math.abs(offsetMinutes);
|
|
2953
|
+
const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
|
|
2954
|
+
const offsetMins = String(absOffset % 60).padStart(2, "0");
|
|
2955
|
+
const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
|
|
2956
|
+
return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
|
|
2957
|
+
}
|
|
2958
|
+
var DAYS, MONTHS;
|
|
2959
|
+
var init_timezone = __esm({
|
|
2960
|
+
"src/utils/timezone.ts"() {
|
|
2961
|
+
"use strict";
|
|
2962
|
+
DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2963
|
+
MONTHS = [
|
|
2964
|
+
"Jan",
|
|
2965
|
+
"Feb",
|
|
2966
|
+
"Mar",
|
|
2967
|
+
"Apr",
|
|
2968
|
+
"May",
|
|
2969
|
+
"Jun",
|
|
2970
|
+
"Jul",
|
|
2971
|
+
"Aug",
|
|
2972
|
+
"Sep",
|
|
2973
|
+
"Oct",
|
|
2974
|
+
"Nov",
|
|
2975
|
+
"Dec"
|
|
2976
|
+
];
|
|
2977
|
+
}
|
|
2978
|
+
});
|
|
2979
|
+
|
|
2937
2980
|
// src/services/qwen-headers.ts
|
|
2938
2981
|
import { v4 as uuidv4 } from "uuid";
|
|
2939
2982
|
function getDefaultQwenUserAgent(major = getChromeMajor()) {
|
|
@@ -2954,6 +2997,7 @@ var init_qwen_headers = __esm({
|
|
|
2954
2997
|
init_qwen_url();
|
|
2955
2998
|
init_config();
|
|
2956
2999
|
init_fingerprint();
|
|
3000
|
+
init_timezone();
|
|
2957
3001
|
dynamicWebVersion = null;
|
|
2958
3002
|
QWEN_WEB_VERSION = config.qwen.webVersion;
|
|
2959
3003
|
DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
|
|
@@ -4590,6 +4634,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4590
4634
|
}
|
|
4591
4635
|
}
|
|
4592
4636
|
}
|
|
4637
|
+
const currentCache = getHeaderCache(account.id);
|
|
4638
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
4639
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
4640
|
+
try {
|
|
4641
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
4642
|
+
if (refreshRes.success) {
|
|
4643
|
+
console.log(
|
|
4644
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
4645
|
+
);
|
|
4646
|
+
}
|
|
4647
|
+
} catch (err) {
|
|
4648
|
+
console.warn(
|
|
4649
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
4650
|
+
);
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4593
4653
|
return;
|
|
4594
4654
|
}
|
|
4595
4655
|
const cookies = await acctContext.cookies();
|
|
@@ -4877,7 +4937,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
4877
4937
|
email: normalizedEmail,
|
|
4878
4938
|
requestId,
|
|
4879
4939
|
version: getQwenWebVersion() || "0.3.12",
|
|
4880
|
-
timezone: (
|
|
4940
|
+
timezone: formatAsciiTimezone()
|
|
4881
4941
|
}
|
|
4882
4942
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4883
4943
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -4972,7 +5032,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
4972
5032
|
code: cleanCode,
|
|
4973
5033
|
requestId,
|
|
4974
5034
|
version: getQwenWebVersion() || "0.3.12",
|
|
4975
|
-
timezone: (
|
|
5035
|
+
timezone: formatAsciiTimezone()
|
|
4976
5036
|
}
|
|
4977
5037
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4978
5038
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -5253,7 +5313,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5253
5313
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
5254
5314
|
const requestId = crypto3.randomUUID();
|
|
5255
5315
|
const evalRes = await page.evaluate(
|
|
5256
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
5316
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
5257
5317
|
try {
|
|
5258
5318
|
const response = await fetch(signinUrl2, {
|
|
5259
5319
|
method: "POST",
|
|
@@ -5265,7 +5325,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5265
5325
|
source: "web",
|
|
5266
5326
|
version: "0.3.11",
|
|
5267
5327
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5268
|
-
timezone:
|
|
5328
|
+
timezone: tz,
|
|
5269
5329
|
"x-request-id": requestId2
|
|
5270
5330
|
},
|
|
5271
5331
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5276,7 +5336,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5276
5336
|
return { ok: false, error: e.message };
|
|
5277
5337
|
}
|
|
5278
5338
|
},
|
|
5279
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
5339
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
5280
5340
|
).catch(() => null);
|
|
5281
5341
|
let data = evalRes?.data;
|
|
5282
5342
|
let signinSuccess = Boolean(
|
|
@@ -5285,7 +5345,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5285
5345
|
if (!signinSuccess && !data?.data?.code) {
|
|
5286
5346
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
5287
5347
|
const fallbackRes = await page.evaluate(
|
|
5288
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
5348
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
5289
5349
|
try {
|
|
5290
5350
|
const response = await fetch(fallbackUrl2, {
|
|
5291
5351
|
method: "POST",
|
|
@@ -5296,7 +5356,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5296
5356
|
"content-type": "application/json",
|
|
5297
5357
|
source: "web",
|
|
5298
5358
|
version: "0.3.11",
|
|
5299
|
-
timezone:
|
|
5359
|
+
timezone: tz,
|
|
5300
5360
|
"x-request-id": requestId2
|
|
5301
5361
|
},
|
|
5302
5362
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5307,7 +5367,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5307
5367
|
return { ok: false, error: e.message };
|
|
5308
5368
|
}
|
|
5309
5369
|
},
|
|
5310
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
5370
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
5311
5371
|
).catch(() => null);
|
|
5312
5372
|
if (fallbackRes?.data) {
|
|
5313
5373
|
data = fallbackRes.data;
|
|
@@ -5639,7 +5699,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5639
5699
|
if (refreshToken) {
|
|
5640
5700
|
try {
|
|
5641
5701
|
const refreshResult = await page.evaluate(
|
|
5642
|
-
async ({ rTok }) => {
|
|
5702
|
+
async ({ rTok, tz }) => {
|
|
5643
5703
|
try {
|
|
5644
5704
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5645
5705
|
method: "GET",
|
|
@@ -5649,7 +5709,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5649
5709
|
source: "web",
|
|
5650
5710
|
version: "0.3.11",
|
|
5651
5711
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5652
|
-
timezone:
|
|
5712
|
+
timezone: tz,
|
|
5653
5713
|
authorization: `Bearer ${rTok}`
|
|
5654
5714
|
}
|
|
5655
5715
|
});
|
|
@@ -5659,7 +5719,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5659
5719
|
return { ok: false, error: e.message };
|
|
5660
5720
|
}
|
|
5661
5721
|
},
|
|
5662
|
-
{ rTok: refreshToken }
|
|
5722
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
5663
5723
|
);
|
|
5664
5724
|
const json = refreshResult?.data;
|
|
5665
5725
|
if (json && json.success === true && json.data) {
|
|
@@ -5697,6 +5757,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5697
5757
|
const cache = getHeaderCache(accountId);
|
|
5698
5758
|
cache.headers.cookie = cookieStr;
|
|
5699
5759
|
cache.lastRefresh = Date.now();
|
|
5760
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5700
5761
|
markAccountHeadersReady(accountId);
|
|
5701
5762
|
saveAuthSession2(accountId, {
|
|
5702
5763
|
cookie: cookieStr,
|
|
@@ -5731,6 +5792,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5731
5792
|
const cache = getHeaderCache(accountId);
|
|
5732
5793
|
cache.headers.cookie = cookieStr;
|
|
5733
5794
|
cache.lastRefresh = Date.now();
|
|
5795
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5734
5796
|
markAccountHeadersReady(accountId);
|
|
5735
5797
|
saveAuthSession2(accountId, {
|
|
5736
5798
|
cookie: cookieStr,
|
|
@@ -6785,11 +6847,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6785
6847
|
const now = Date.now();
|
|
6786
6848
|
const currentUrl = page.url();
|
|
6787
6849
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
6788
|
-
const cookie = await getCookies(accountId);
|
|
6850
|
+
const cookie = await getCookies(accountId, true);
|
|
6789
6851
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
6790
6852
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
6791
|
-
|
|
6792
|
-
|
|
6853
|
+
logger.debug(
|
|
6854
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
6793
6855
|
);
|
|
6794
6856
|
const res = await refreshAccountToken(accountId);
|
|
6795
6857
|
if (res.success) {
|
|
@@ -6797,6 +6859,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6797
6859
|
touchAccountActivity(accountId);
|
|
6798
6860
|
return true;
|
|
6799
6861
|
}
|
|
6862
|
+
logger.warn(
|
|
6863
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
6864
|
+
);
|
|
6800
6865
|
}
|
|
6801
6866
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
6802
6867
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -7034,6 +7099,7 @@ var init_playwright = __esm({
|
|
|
7034
7099
|
init_qwen_headers();
|
|
7035
7100
|
init_paths();
|
|
7036
7101
|
init_jwt();
|
|
7102
|
+
init_timezone();
|
|
7037
7103
|
requireLocal = createRequire(import.meta.url);
|
|
7038
7104
|
contextInitHooks = [];
|
|
7039
7105
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -7047,7 +7113,7 @@ var init_playwright = __esm({
|
|
|
7047
7113
|
headerCaches = /* @__PURE__ */ new Map();
|
|
7048
7114
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
7049
7115
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
7050
|
-
COOKIE_CACHE_TTL =
|
|
7116
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
7051
7117
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
7052
7118
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
7053
7119
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -2001,6 +2001,7 @@ var init_mutex = __esm({
|
|
|
2001
2001
|
locked = false;
|
|
2002
2002
|
lockedAt = 0;
|
|
2003
2003
|
lockedByKey = "";
|
|
2004
|
+
epoch = 0;
|
|
2004
2005
|
async acquire(timeoutMs = 3e5, key = "") {
|
|
2005
2006
|
const holdLimitMs = this.maxHoldMs;
|
|
2006
2007
|
if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
|
|
@@ -2011,6 +2012,7 @@ var init_mutex = __esm({
|
|
|
2011
2012
|
this.locked = false;
|
|
2012
2013
|
this.lockedAt = 0;
|
|
2013
2014
|
this.lockedByKey = "";
|
|
2015
|
+
this.epoch++;
|
|
2014
2016
|
}
|
|
2015
2017
|
if (!this.locked) {
|
|
2016
2018
|
this.locked = true;
|
|
@@ -2049,10 +2051,12 @@ var init_mutex = __esm({
|
|
|
2049
2051
|
}
|
|
2050
2052
|
}
|
|
2051
2053
|
createRelease() {
|
|
2054
|
+
const epoch = this.epoch;
|
|
2052
2055
|
let released = false;
|
|
2053
2056
|
return () => {
|
|
2054
2057
|
if (released) return;
|
|
2055
2058
|
released = true;
|
|
2059
|
+
if (epoch !== this.epoch) return;
|
|
2056
2060
|
this.release();
|
|
2057
2061
|
};
|
|
2058
2062
|
}
|
|
@@ -2715,6 +2719,45 @@ var init_waf_isolation = __esm({
|
|
|
2715
2719
|
}
|
|
2716
2720
|
});
|
|
2717
2721
|
|
|
2722
|
+
// src/utils/timezone.ts
|
|
2723
|
+
function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
|
|
2724
|
+
const dayName = DAYS[date.getDay()];
|
|
2725
|
+
const monthName = MONTHS[date.getMonth()];
|
|
2726
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2727
|
+
const year = date.getFullYear();
|
|
2728
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2729
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2730
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2731
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
2732
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
2733
|
+
const absOffset = Math.abs(offsetMinutes);
|
|
2734
|
+
const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
|
|
2735
|
+
const offsetMins = String(absOffset % 60).padStart(2, "0");
|
|
2736
|
+
const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
|
|
2737
|
+
return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
|
|
2738
|
+
}
|
|
2739
|
+
var DAYS, MONTHS;
|
|
2740
|
+
var init_timezone = __esm({
|
|
2741
|
+
"src/utils/timezone.ts"() {
|
|
2742
|
+
"use strict";
|
|
2743
|
+
DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2744
|
+
MONTHS = [
|
|
2745
|
+
"Jan",
|
|
2746
|
+
"Feb",
|
|
2747
|
+
"Mar",
|
|
2748
|
+
"Apr",
|
|
2749
|
+
"May",
|
|
2750
|
+
"Jun",
|
|
2751
|
+
"Jul",
|
|
2752
|
+
"Aug",
|
|
2753
|
+
"Sep",
|
|
2754
|
+
"Oct",
|
|
2755
|
+
"Nov",
|
|
2756
|
+
"Dec"
|
|
2757
|
+
];
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
|
|
2718
2761
|
// src/services/qwen-headers.ts
|
|
2719
2762
|
import { v4 as uuidv4 } from "uuid";
|
|
2720
2763
|
function getDefaultQwenUserAgent(major = getChromeMajor()) {
|
|
@@ -2732,6 +2775,7 @@ var init_qwen_headers = __esm({
|
|
|
2732
2775
|
init_qwen_url();
|
|
2733
2776
|
init_config();
|
|
2734
2777
|
init_fingerprint();
|
|
2778
|
+
init_timezone();
|
|
2735
2779
|
dynamicWebVersion = null;
|
|
2736
2780
|
QWEN_WEB_VERSION = config.qwen.webVersion;
|
|
2737
2781
|
DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
|
|
@@ -4021,6 +4065,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4021
4065
|
}
|
|
4022
4066
|
}
|
|
4023
4067
|
}
|
|
4068
|
+
const currentCache = getHeaderCache(account.id);
|
|
4069
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
4070
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
4071
|
+
try {
|
|
4072
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
4073
|
+
if (refreshRes.success) {
|
|
4074
|
+
console.log(
|
|
4075
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
4076
|
+
);
|
|
4077
|
+
}
|
|
4078
|
+
} catch (err) {
|
|
4079
|
+
console.warn(
|
|
4080
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4024
4084
|
return;
|
|
4025
4085
|
}
|
|
4026
4086
|
const cookies = await acctContext.cookies();
|
|
@@ -4278,7 +4338,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4278
4338
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4279
4339
|
const requestId = crypto3.randomUUID();
|
|
4280
4340
|
const evalRes = await page.evaluate(
|
|
4281
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
4341
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
4282
4342
|
try {
|
|
4283
4343
|
const response = await fetch(signinUrl2, {
|
|
4284
4344
|
method: "POST",
|
|
@@ -4290,7 +4350,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4290
4350
|
source: "web",
|
|
4291
4351
|
version: "0.3.11",
|
|
4292
4352
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4293
|
-
timezone:
|
|
4353
|
+
timezone: tz,
|
|
4294
4354
|
"x-request-id": requestId2
|
|
4295
4355
|
},
|
|
4296
4356
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -4301,7 +4361,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4301
4361
|
return { ok: false, error: e.message };
|
|
4302
4362
|
}
|
|
4303
4363
|
},
|
|
4304
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
4364
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
4305
4365
|
).catch(() => null);
|
|
4306
4366
|
let data = evalRes?.data;
|
|
4307
4367
|
let signinSuccess = Boolean(
|
|
@@ -4310,7 +4370,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4310
4370
|
if (!signinSuccess && !data?.data?.code) {
|
|
4311
4371
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4312
4372
|
const fallbackRes = await page.evaluate(
|
|
4313
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
4373
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
4314
4374
|
try {
|
|
4315
4375
|
const response = await fetch(fallbackUrl2, {
|
|
4316
4376
|
method: "POST",
|
|
@@ -4321,7 +4381,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4321
4381
|
"content-type": "application/json",
|
|
4322
4382
|
source: "web",
|
|
4323
4383
|
version: "0.3.11",
|
|
4324
|
-
timezone:
|
|
4384
|
+
timezone: tz,
|
|
4325
4385
|
"x-request-id": requestId2
|
|
4326
4386
|
},
|
|
4327
4387
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -4332,7 +4392,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4332
4392
|
return { ok: false, error: e.message };
|
|
4333
4393
|
}
|
|
4334
4394
|
},
|
|
4335
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
4395
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
4336
4396
|
).catch(() => null);
|
|
4337
4397
|
if (fallbackRes?.data) {
|
|
4338
4398
|
data = fallbackRes.data;
|
|
@@ -4664,7 +4724,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4664
4724
|
if (refreshToken) {
|
|
4665
4725
|
try {
|
|
4666
4726
|
const refreshResult = await page.evaluate(
|
|
4667
|
-
async ({ rTok }) => {
|
|
4727
|
+
async ({ rTok, tz }) => {
|
|
4668
4728
|
try {
|
|
4669
4729
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
4670
4730
|
method: "GET",
|
|
@@ -4674,7 +4734,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4674
4734
|
source: "web",
|
|
4675
4735
|
version: "0.3.11",
|
|
4676
4736
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4677
|
-
timezone:
|
|
4737
|
+
timezone: tz,
|
|
4678
4738
|
authorization: `Bearer ${rTok}`
|
|
4679
4739
|
}
|
|
4680
4740
|
});
|
|
@@ -4684,7 +4744,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4684
4744
|
return { ok: false, error: e.message };
|
|
4685
4745
|
}
|
|
4686
4746
|
},
|
|
4687
|
-
{ rTok: refreshToken }
|
|
4747
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
4688
4748
|
);
|
|
4689
4749
|
const json = refreshResult?.data;
|
|
4690
4750
|
if (json && json.success === true && json.data) {
|
|
@@ -4722,6 +4782,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4722
4782
|
const cache = getHeaderCache(accountId);
|
|
4723
4783
|
cache.headers.cookie = cookieStr;
|
|
4724
4784
|
cache.lastRefresh = Date.now();
|
|
4785
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
4725
4786
|
markAccountHeadersReady(accountId);
|
|
4726
4787
|
saveAuthSession2(accountId, {
|
|
4727
4788
|
cookie: cookieStr,
|
|
@@ -4756,6 +4817,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4756
4817
|
const cache = getHeaderCache(accountId);
|
|
4757
4818
|
cache.headers.cookie = cookieStr;
|
|
4758
4819
|
cache.lastRefresh = Date.now();
|
|
4820
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
4759
4821
|
markAccountHeadersReady(accountId);
|
|
4760
4822
|
saveAuthSession2(accountId, {
|
|
4761
4823
|
cookie: cookieStr,
|
|
@@ -5636,6 +5698,7 @@ var init_playwright = __esm({
|
|
|
5636
5698
|
init_qwen_headers();
|
|
5637
5699
|
init_paths();
|
|
5638
5700
|
init_jwt();
|
|
5701
|
+
init_timezone();
|
|
5639
5702
|
requireLocal = createRequire(import.meta.url);
|
|
5640
5703
|
contextInitHooks = [];
|
|
5641
5704
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -5645,7 +5708,7 @@ var init_playwright = __esm({
|
|
|
5645
5708
|
inFlightAccountInits = /* @__PURE__ */ new Map();
|
|
5646
5709
|
headerCaches = /* @__PURE__ */ new Map();
|
|
5647
5710
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
5648
|
-
COOKIE_CACHE_TTL =
|
|
5711
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
5649
5712
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
5650
5713
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
5651
5714
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -5927,7 +5990,7 @@ function clearPersonalizationDbCache(accountId) {
|
|
|
5927
5990
|
return 0;
|
|
5928
5991
|
}
|
|
5929
5992
|
}
|
|
5930
|
-
var BROWSER_STREAM_BINDING, browserStreamStates, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, lastSyncedPersonalizationHashes, activePersonalizationByAccount, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
5993
|
+
var BROWSER_STREAM_BINDING, browserStreamStates, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, lastSyncedPersonalizationHashes, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
5931
5994
|
var init_qwen = __esm({
|
|
5932
5995
|
"src/services/qwen.ts"() {
|
|
5933
5996
|
"use strict";
|
|
@@ -5945,6 +6008,7 @@ var init_qwen = __esm({
|
|
|
5945
6008
|
init_captcha_coordinator();
|
|
5946
6009
|
init_captcha_solver();
|
|
5947
6010
|
init_account_concurrency();
|
|
6011
|
+
init_timezone();
|
|
5948
6012
|
init_qwen_errors();
|
|
5949
6013
|
init_qwen_thread_state();
|
|
5950
6014
|
init_qwen_chat_pool();
|
|
@@ -5960,6 +6024,7 @@ var init_qwen = __esm({
|
|
|
5960
6024
|
MODEL_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
5961
6025
|
lastSyncedPersonalizationHashes = /* @__PURE__ */ new Map();
|
|
5962
6026
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
6027
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
5963
6028
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
5964
6029
|
ui: {
|
|
5965
6030
|
autoTags: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
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": {
|
package/src/core/mutex.ts
CHANGED
|
@@ -14,6 +14,7 @@ export class Mutex {
|
|
|
14
14
|
private locked = false;
|
|
15
15
|
private lockedAt = 0;
|
|
16
16
|
private lockedByKey = "";
|
|
17
|
+
private epoch = 0;
|
|
17
18
|
|
|
18
19
|
constructor(
|
|
19
20
|
public readonly name: string = "unnamed",
|
|
@@ -35,6 +36,7 @@ export class Mutex {
|
|
|
35
36
|
this.locked = false;
|
|
36
37
|
this.lockedAt = 0;
|
|
37
38
|
this.lockedByKey = "";
|
|
39
|
+
this.epoch++;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
if (!this.locked) {
|
|
@@ -78,10 +80,12 @@ export class Mutex {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
private createRelease(): () => void {
|
|
83
|
+
const epoch = this.epoch;
|
|
81
84
|
let released = false;
|
|
82
85
|
return () => {
|
|
83
86
|
if (released) return;
|
|
84
87
|
released = true;
|
|
88
|
+
if (epoch !== this.epoch) return;
|
|
85
89
|
this.release();
|
|
86
90
|
};
|
|
87
91
|
}
|
package/src/core/version.ts
CHANGED
|
@@ -316,9 +316,14 @@ export function translateOpenAIToAnthropic(
|
|
|
316
316
|
};
|
|
317
317
|
|
|
318
318
|
const hasToolUse = content.some((b) => b.type === "tool_use");
|
|
319
|
-
const stopReason =
|
|
320
|
-
|
|
321
|
-
|
|
319
|
+
const stopReason =
|
|
320
|
+
choice.finish_reason === "length"
|
|
321
|
+
? "max_tokens"
|
|
322
|
+
: choice.finish_reason === "content_filter"
|
|
323
|
+
? "end_turn"
|
|
324
|
+
: hasToolUse
|
|
325
|
+
? "tool_use"
|
|
326
|
+
: stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
|
|
322
327
|
|
|
323
328
|
return {
|
|
324
329
|
id: generateMessageId(),
|
|
@@ -498,11 +503,14 @@ export function translateStreamChunk(
|
|
|
498
503
|
state.currentBlockType = null;
|
|
499
504
|
}
|
|
500
505
|
|
|
501
|
-
const stopReason =
|
|
502
|
-
|
|
503
|
-
: choice.finish_reason === "length"
|
|
506
|
+
const stopReason =
|
|
507
|
+
choice.finish_reason === "length"
|
|
504
508
|
? "max_tokens"
|
|
505
|
-
: "
|
|
509
|
+
: choice.finish_reason === "content_filter"
|
|
510
|
+
? "end_turn"
|
|
511
|
+
: state.hasEmittedToolUse
|
|
512
|
+
? "tool_use"
|
|
513
|
+
: "end_turn";
|
|
506
514
|
|
|
507
515
|
events.push(
|
|
508
516
|
JSON.stringify({
|
|
@@ -231,8 +231,10 @@ export function applyUpstreamUsage(
|
|
|
231
231
|
if (!candidate || typeof candidate !== "object") return;
|
|
232
232
|
|
|
233
233
|
const usage = candidate as Record<string, unknown>;
|
|
234
|
-
const promptTokens =
|
|
235
|
-
|
|
234
|
+
const promptTokens =
|
|
235
|
+
asFiniteNumber(usage.input_tokens) ?? asFiniteNumber(usage.prompt_tokens);
|
|
236
|
+
const completionTokens =
|
|
237
|
+
asFiniteNumber(usage.output_tokens) ?? asFiniteNumber(usage.completion_tokens);
|
|
236
238
|
const totalTokens = asFiniteNumber(usage.total_tokens);
|
|
237
239
|
|
|
238
240
|
if (promptTokens !== null) {
|