qwenproxy-cli 1.4.7 → 1.4.8
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 +17 -5
- package/dist/delete-chats.js +213 -69
- package/dist/index.js +215 -71
- package/dist/login.js +161 -59
- package/dist/reset-cooldowns.js +17 -5
- package/package.json +1 -1
- package/src/core/database.ts +10 -5
- package/src/core/version.ts +1 -1
- package/src/routes/chat/retry-policy.ts +4 -3
- package/src/services/playwright.ts +180 -58
- package/src/services/qwen-chat-pool.ts +9 -0
- package/src/services/qwen.ts +43 -10
package/dist/login.js
CHANGED
|
@@ -359,6 +359,7 @@ function runMigrations(db2) {
|
|
|
359
359
|
"sec_ch_ua_mobile TEXT",
|
|
360
360
|
"sec_ch_ua_platform TEXT",
|
|
361
361
|
"version TEXT",
|
|
362
|
+
"refresh_token TEXT",
|
|
362
363
|
"captured_at INTEGER DEFAULT 0"
|
|
363
364
|
];
|
|
364
365
|
for (const col of authSessionCols) {
|
|
@@ -437,11 +438,11 @@ function saveAuthSession(accountId, session) {
|
|
|
437
438
|
INSERT OR REPLACE INTO qwen_auth_sessions (
|
|
438
439
|
account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
439
440
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
440
|
-
user_id, token_expires_at, captured_at, updated_at
|
|
441
|
+
user_id, token_expires_at, refresh_token, captured_at, updated_at
|
|
441
442
|
) VALUES (
|
|
442
443
|
?, ?, ?, ?, ?, ?,
|
|
443
444
|
?, ?, ?, ?,
|
|
444
|
-
?, ?, ?, datetime('now')
|
|
445
|
+
?, ?, ?, ?, datetime('now')
|
|
445
446
|
)
|
|
446
447
|
`);
|
|
447
448
|
stmt.run(
|
|
@@ -457,6 +458,7 @@ function saveAuthSession(accountId, session) {
|
|
|
457
458
|
session.version || null,
|
|
458
459
|
session.userId || null,
|
|
459
460
|
session.tokenExpiresAt || null,
|
|
461
|
+
session.refreshToken || null,
|
|
460
462
|
session.capturedAt ?? Date.now()
|
|
461
463
|
);
|
|
462
464
|
}
|
|
@@ -465,7 +467,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
465
467
|
const row = database.prepare(
|
|
466
468
|
`SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
467
469
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
468
|
-
user_id, token_expires_at, captured_at
|
|
470
|
+
user_id, token_expires_at, refresh_token, captured_at
|
|
469
471
|
FROM qwen_auth_sessions WHERE account_id = ?`
|
|
470
472
|
).get(accountId);
|
|
471
473
|
if (!row) return null;
|
|
@@ -473,7 +475,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
473
475
|
if (capturedAt <= 0 || Date.now() - capturedAt > maxAgeMs) {
|
|
474
476
|
return null;
|
|
475
477
|
}
|
|
476
|
-
if (row.token_expires_at) {
|
|
478
|
+
if (!row.refresh_token && row.token_expires_at) {
|
|
477
479
|
const tokenExpMs = Number(row.token_expires_at) * 1e3;
|
|
478
480
|
if (tokenExpMs <= Date.now() + 5 * 60 * 1e3) {
|
|
479
481
|
return null;
|
|
@@ -495,6 +497,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
495
497
|
version: row.version || void 0,
|
|
496
498
|
userId: row.user_id || void 0,
|
|
497
499
|
tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : void 0,
|
|
500
|
+
refreshToken: row.refresh_token || void 0,
|
|
498
501
|
capturedAt
|
|
499
502
|
};
|
|
500
503
|
}
|
|
@@ -3137,6 +3140,7 @@ __export(playwright_exports, {
|
|
|
3137
3140
|
parseCookiesForBrowser: () => parseCookiesForBrowser,
|
|
3138
3141
|
pruneAllPlaywrightProfiles: () => pruneAllPlaywrightProfiles,
|
|
3139
3142
|
prunePlaywrightProfileCaches: () => prunePlaywrightProfileCaches,
|
|
3143
|
+
refreshAccountToken: () => refreshAccountToken,
|
|
3140
3144
|
refreshHeaders: () => refreshHeaders,
|
|
3141
3145
|
refreshHeadersWithProfileReset: () => refreshHeadersWithProfileReset,
|
|
3142
3146
|
registerPlaywrightAccountForTests: () => registerPlaywrightAccountForTests,
|
|
@@ -4772,6 +4776,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4772
4776
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4773
4777
|
version: cache.headers["version"] || void 0,
|
|
4774
4778
|
tokenExpiresAt: exp || void 0,
|
|
4779
|
+
refreshToken: apiResult.refreshToken,
|
|
4775
4780
|
capturedAt: Date.now()
|
|
4776
4781
|
});
|
|
4777
4782
|
}
|
|
@@ -4805,6 +4810,13 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4805
4810
|
markAccountHeadersReady(accountId);
|
|
4806
4811
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4807
4812
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
4813
|
+
let rTok = uiResult.refreshToken;
|
|
4814
|
+
if (!rTok) {
|
|
4815
|
+
try {
|
|
4816
|
+
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
4817
|
+
} catch {
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4808
4820
|
saveAuthSession2(accountId, {
|
|
4809
4821
|
cookie: cookieStr,
|
|
4810
4822
|
userAgent: cache.headers["user-agent"] || "",
|
|
@@ -4816,6 +4828,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4816
4828
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4817
4829
|
version: cache.headers["version"] || void 0,
|
|
4818
4830
|
tokenExpiresAt: exp || void 0,
|
|
4831
|
+
refreshToken: rTok,
|
|
4819
4832
|
capturedAt: Date.now()
|
|
4820
4833
|
});
|
|
4821
4834
|
}
|
|
@@ -4994,7 +5007,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4994
5007
|
});
|
|
4995
5008
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
4996
5009
|
});
|
|
4997
|
-
return { success: true };
|
|
5010
|
+
return { success: true, token, refreshToken };
|
|
4998
5011
|
}
|
|
4999
5012
|
}
|
|
5000
5013
|
return {
|
|
@@ -5237,6 +5250,137 @@ async function loginViaUi(page, email, password) {
|
|
|
5237
5250
|
return { success: false, reason: err?.message || String(err) };
|
|
5238
5251
|
}
|
|
5239
5252
|
}
|
|
5253
|
+
async function refreshAccountToken(accountId) {
|
|
5254
|
+
const page = accountPages.get(accountId);
|
|
5255
|
+
if (!page || page.isClosed()) {
|
|
5256
|
+
return { success: false, error: "Account page not active" };
|
|
5257
|
+
}
|
|
5258
|
+
const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5259
|
+
const session = getValidAuthSession2(accountId);
|
|
5260
|
+
let refreshToken = session?.refreshToken;
|
|
5261
|
+
if (!refreshToken) {
|
|
5262
|
+
try {
|
|
5263
|
+
refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
5264
|
+
} catch {
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
if (refreshToken) {
|
|
5268
|
+
try {
|
|
5269
|
+
const refreshResult = await page.evaluate(
|
|
5270
|
+
async ({ rTok }) => {
|
|
5271
|
+
try {
|
|
5272
|
+
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5273
|
+
method: "GET",
|
|
5274
|
+
credentials: "include",
|
|
5275
|
+
headers: {
|
|
5276
|
+
accept: "application/json, text/plain, */*",
|
|
5277
|
+
source: "web",
|
|
5278
|
+
version: "0.3.11",
|
|
5279
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
5280
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
5281
|
+
authorization: `Bearer ${rTok}`
|
|
5282
|
+
}
|
|
5283
|
+
});
|
|
5284
|
+
const json2 = await response.json().catch(() => null);
|
|
5285
|
+
return { ok: response.ok, status: response.status, data: json2 };
|
|
5286
|
+
} catch (e) {
|
|
5287
|
+
return { ok: false, error: e.message };
|
|
5288
|
+
}
|
|
5289
|
+
},
|
|
5290
|
+
{ rTok: refreshToken }
|
|
5291
|
+
);
|
|
5292
|
+
const json = refreshResult?.data;
|
|
5293
|
+
if (json && json.success === true && json.data) {
|
|
5294
|
+
const newAccessToken = json.data.access_token || json.data.token;
|
|
5295
|
+
const newRefreshToken = json.data.refresh_token || refreshToken;
|
|
5296
|
+
if (newAccessToken) {
|
|
5297
|
+
await page.context().addCookies([
|
|
5298
|
+
{
|
|
5299
|
+
name: "token",
|
|
5300
|
+
value: newAccessToken,
|
|
5301
|
+
domain: ".qwen.ai",
|
|
5302
|
+
path: "/",
|
|
5303
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
5304
|
+
httpOnly: false,
|
|
5305
|
+
secure: true,
|
|
5306
|
+
sameSite: "Lax"
|
|
5307
|
+
}
|
|
5308
|
+
]);
|
|
5309
|
+
await page.evaluate(
|
|
5310
|
+
({ tok, rTok }) => {
|
|
5311
|
+
try {
|
|
5312
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
5313
|
+
localStorage.setItem("token", tok);
|
|
5314
|
+
localStorage.setItem("access_token", tok);
|
|
5315
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
5316
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
5317
|
+
} catch {
|
|
5318
|
+
}
|
|
5319
|
+
},
|
|
5320
|
+
{ tok: newAccessToken, rTok: newRefreshToken }
|
|
5321
|
+
).catch(() => {
|
|
5322
|
+
});
|
|
5323
|
+
const liveCookies = await page.context().cookies();
|
|
5324
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5325
|
+
const cache = getHeaderCache(accountId);
|
|
5326
|
+
cache.headers.cookie = cookieStr;
|
|
5327
|
+
cache.lastRefresh = Date.now();
|
|
5328
|
+
markAccountHeadersReady(accountId);
|
|
5329
|
+
saveAuthSession2(accountId, {
|
|
5330
|
+
cookie: cookieStr,
|
|
5331
|
+
userAgent: cache.headers["user-agent"] || session?.userAgent || "",
|
|
5332
|
+
bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
5333
|
+
bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
|
|
5334
|
+
bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
5335
|
+
secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
|
|
5336
|
+
secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
5337
|
+
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
5338
|
+
version: cache.headers["version"] || session?.version,
|
|
5339
|
+
userId: session?.userId,
|
|
5340
|
+
tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
|
|
5341
|
+
refreshToken: newRefreshToken,
|
|
5342
|
+
capturedAt: Date.now()
|
|
5343
|
+
});
|
|
5344
|
+
return { success: true, token: newAccessToken };
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
} catch {
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
5351
|
+
const creds = getAccountCredentials2(accountId);
|
|
5352
|
+
if (creds && creds.email && creds.password) {
|
|
5353
|
+
const apiResult = await loginViaApi(page, creds.email, creds.password);
|
|
5354
|
+
if (apiResult.success) {
|
|
5355
|
+
const liveCookies = await page.context().cookies();
|
|
5356
|
+
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
5357
|
+
if (tokenCookie) {
|
|
5358
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5359
|
+
const cache = getHeaderCache(accountId);
|
|
5360
|
+
cache.headers.cookie = cookieStr;
|
|
5361
|
+
cache.lastRefresh = Date.now();
|
|
5362
|
+
markAccountHeadersReady(accountId);
|
|
5363
|
+
saveAuthSession2(accountId, {
|
|
5364
|
+
cookie: cookieStr,
|
|
5365
|
+
userAgent: cache.headers["user-agent"] || session?.userAgent || "",
|
|
5366
|
+
bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
5367
|
+
bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
|
|
5368
|
+
bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
5369
|
+
secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
|
|
5370
|
+
secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
5371
|
+
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
5372
|
+
version: cache.headers["version"] || session?.version,
|
|
5373
|
+
userId: session?.userId,
|
|
5374
|
+
tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
|
|
5375
|
+
refreshToken: apiResult.refreshToken,
|
|
5376
|
+
capturedAt: Date.now()
|
|
5377
|
+
});
|
|
5378
|
+
return { success: true, token: tokenCookie.value };
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
return { success: false, error: "Could not refresh or re-authenticate token" };
|
|
5383
|
+
}
|
|
5240
5384
|
async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
|
|
5241
5385
|
const page = pageOverride ?? accountPages.get(accountId);
|
|
5242
5386
|
if (!page || page.isClosed()) {
|
|
@@ -5753,16 +5897,9 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
5753
5897
|
}
|
|
5754
5898
|
};
|
|
5755
5899
|
if (forceReauth) {
|
|
5756
|
-
const
|
|
5757
|
-
if (
|
|
5758
|
-
|
|
5759
|
-
if (liveCookies.some((c) => c.name === "token")) {
|
|
5760
|
-
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5761
|
-
cache.headers.cookie = cookieStr;
|
|
5762
|
-
cache.lastRefresh = Date.now();
|
|
5763
|
-
markAccountHeadersReady(accountId);
|
|
5764
|
-
return;
|
|
5765
|
-
}
|
|
5900
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
5901
|
+
if (refreshed.success) {
|
|
5902
|
+
return;
|
|
5766
5903
|
}
|
|
5767
5904
|
await executeReauth();
|
|
5768
5905
|
} else {
|
|
@@ -6198,50 +6335,15 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6198
6335
|
const now = Date.now();
|
|
6199
6336
|
const currentUrl = page.url();
|
|
6200
6337
|
const cookie = await getCookies(accountId);
|
|
6201
|
-
if (cookie && isTokenExpiringSoon(cookie,
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
method: "GET",
|
|
6211
|
-
credentials: "include"
|
|
6212
|
-
}).catch(() => {
|
|
6213
|
-
});
|
|
6214
|
-
}).catch(() => {
|
|
6215
|
-
});
|
|
6216
|
-
const liveCookies = await page.context().cookies();
|
|
6217
|
-
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
6218
|
-
if (tokenCookie) {
|
|
6219
|
-
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6220
|
-
const cache = getHeaderCache(accountId);
|
|
6221
|
-
cache.headers.cookie = cookieStr;
|
|
6222
|
-
cache.lastRefresh = Date.now();
|
|
6223
|
-
markAccountHeadersReady(accountId);
|
|
6224
|
-
lastKeepAliveNavigation.set(accountId, now);
|
|
6225
|
-
touchAccountActivity(accountId);
|
|
6226
|
-
return true;
|
|
6227
|
-
}
|
|
6228
|
-
} catch {
|
|
6229
|
-
}
|
|
6230
|
-
const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
|
|
6231
|
-
if (!loggedIn) {
|
|
6232
|
-
console.log(
|
|
6233
|
-
`\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
|
|
6234
|
-
);
|
|
6235
|
-
try {
|
|
6236
|
-
await refreshHeadersInternal(accountId, config.timeouts.headers, true);
|
|
6237
|
-
lastKeepAliveNavigation.set(accountId, now);
|
|
6238
|
-
touchAccountActivity(accountId);
|
|
6239
|
-
return true;
|
|
6240
|
-
} catch (err) {
|
|
6241
|
-
console.warn(
|
|
6242
|
-
`[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
|
|
6243
|
-
);
|
|
6244
|
-
}
|
|
6338
|
+
if (cookie && isTokenExpiringSoon(cookie, 5)) {
|
|
6339
|
+
console.log(
|
|
6340
|
+
`\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
6341
|
+
);
|
|
6342
|
+
const res = await refreshAccountToken(accountId);
|
|
6343
|
+
if (res.success) {
|
|
6344
|
+
lastKeepAliveNavigation.set(accountId, now);
|
|
6345
|
+
touchAccountActivity(accountId);
|
|
6346
|
+
return true;
|
|
6245
6347
|
}
|
|
6246
6348
|
}
|
|
6247
6349
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -314,6 +314,7 @@ function runMigrations(db2) {
|
|
|
314
314
|
"sec_ch_ua_mobile TEXT",
|
|
315
315
|
"sec_ch_ua_platform TEXT",
|
|
316
316
|
"version TEXT",
|
|
317
|
+
"refresh_token TEXT",
|
|
317
318
|
"captured_at INTEGER DEFAULT 0"
|
|
318
319
|
];
|
|
319
320
|
for (const col of authSessionCols) {
|
|
@@ -392,11 +393,11 @@ function saveAuthSession(accountId, session) {
|
|
|
392
393
|
INSERT OR REPLACE INTO qwen_auth_sessions (
|
|
393
394
|
account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
394
395
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
395
|
-
user_id, token_expires_at, captured_at, updated_at
|
|
396
|
+
user_id, token_expires_at, refresh_token, captured_at, updated_at
|
|
396
397
|
) VALUES (
|
|
397
398
|
?, ?, ?, ?, ?, ?,
|
|
398
399
|
?, ?, ?, ?,
|
|
399
|
-
?, ?, ?, datetime('now')
|
|
400
|
+
?, ?, ?, ?, datetime('now')
|
|
400
401
|
)
|
|
401
402
|
`);
|
|
402
403
|
stmt.run(
|
|
@@ -412,6 +413,7 @@ function saveAuthSession(accountId, session) {
|
|
|
412
413
|
session.version || null,
|
|
413
414
|
session.userId || null,
|
|
414
415
|
session.tokenExpiresAt || null,
|
|
416
|
+
session.refreshToken || null,
|
|
415
417
|
session.capturedAt ?? Date.now()
|
|
416
418
|
);
|
|
417
419
|
}
|
|
@@ -420,7 +422,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
420
422
|
const row = database.prepare(
|
|
421
423
|
`SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
422
424
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
423
|
-
user_id, token_expires_at, captured_at
|
|
425
|
+
user_id, token_expires_at, refresh_token, captured_at
|
|
424
426
|
FROM qwen_auth_sessions WHERE account_id = ?`
|
|
425
427
|
).get(accountId);
|
|
426
428
|
if (!row) return null;
|
|
@@ -428,7 +430,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
428
430
|
if (capturedAt <= 0 || Date.now() - capturedAt > maxAgeMs) {
|
|
429
431
|
return null;
|
|
430
432
|
}
|
|
431
|
-
if (row.token_expires_at) {
|
|
433
|
+
if (!row.refresh_token && row.token_expires_at) {
|
|
432
434
|
const tokenExpMs = Number(row.token_expires_at) * 1e3;
|
|
433
435
|
if (tokenExpMs <= Date.now() + 5 * 60 * 1e3) {
|
|
434
436
|
return null;
|
|
@@ -450,6 +452,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
450
452
|
version: row.version || void 0,
|
|
451
453
|
userId: row.user_id || void 0,
|
|
452
454
|
tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : void 0,
|
|
455
|
+
refreshToken: row.refresh_token || void 0,
|
|
453
456
|
capturedAt
|
|
454
457
|
};
|
|
455
458
|
}
|
|
@@ -4048,6 +4051,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4048
4051
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4049
4052
|
version: cache.headers["version"] || void 0,
|
|
4050
4053
|
tokenExpiresAt: exp || void 0,
|
|
4054
|
+
refreshToken: apiResult.refreshToken,
|
|
4051
4055
|
capturedAt: Date.now()
|
|
4052
4056
|
});
|
|
4053
4057
|
}
|
|
@@ -4081,6 +4085,13 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4081
4085
|
markAccountHeadersReady(accountId);
|
|
4082
4086
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4083
4087
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
4088
|
+
let rTok = uiResult.refreshToken;
|
|
4089
|
+
if (!rTok) {
|
|
4090
|
+
try {
|
|
4091
|
+
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
4092
|
+
} catch {
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4084
4095
|
saveAuthSession2(accountId, {
|
|
4085
4096
|
cookie: cookieStr,
|
|
4086
4097
|
userAgent: cache.headers["user-agent"] || "",
|
|
@@ -4092,6 +4103,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4092
4103
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4093
4104
|
version: cache.headers["version"] || void 0,
|
|
4094
4105
|
tokenExpiresAt: exp || void 0,
|
|
4106
|
+
refreshToken: rTok,
|
|
4095
4107
|
capturedAt: Date.now()
|
|
4096
4108
|
});
|
|
4097
4109
|
}
|
|
@@ -4270,7 +4282,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4270
4282
|
});
|
|
4271
4283
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
4272
4284
|
});
|
|
4273
|
-
return { success: true };
|
|
4285
|
+
return { success: true, token, refreshToken };
|
|
4274
4286
|
}
|
|
4275
4287
|
}
|
|
4276
4288
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.8",
|
|
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/database.ts
CHANGED
|
@@ -193,6 +193,7 @@ function runMigrations(db: Database.Database): void {
|
|
|
193
193
|
"sec_ch_ua_mobile TEXT",
|
|
194
194
|
"sec_ch_ua_platform TEXT",
|
|
195
195
|
"version TEXT",
|
|
196
|
+
"refresh_token TEXT",
|
|
196
197
|
"captured_at INTEGER DEFAULT 0",
|
|
197
198
|
];
|
|
198
199
|
for (const col of authSessionCols) {
|
|
@@ -309,6 +310,7 @@ export interface PersistedAuthSession {
|
|
|
309
310
|
version?: string;
|
|
310
311
|
userId?: string;
|
|
311
312
|
tokenExpiresAt?: number;
|
|
313
|
+
refreshToken?: string;
|
|
312
314
|
capturedAt: number;
|
|
313
315
|
}
|
|
314
316
|
|
|
@@ -326,6 +328,7 @@ export function saveAuthSession(
|
|
|
326
328
|
version?: string;
|
|
327
329
|
userId?: string;
|
|
328
330
|
tokenExpiresAt?: number;
|
|
331
|
+
refreshToken?: string;
|
|
329
332
|
capturedAt?: number;
|
|
330
333
|
},
|
|
331
334
|
): void {
|
|
@@ -334,11 +337,11 @@ export function saveAuthSession(
|
|
|
334
337
|
INSERT OR REPLACE INTO qwen_auth_sessions (
|
|
335
338
|
account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
336
339
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
337
|
-
user_id, token_expires_at, captured_at, updated_at
|
|
340
|
+
user_id, token_expires_at, refresh_token, captured_at, updated_at
|
|
338
341
|
) VALUES (
|
|
339
342
|
?, ?, ?, ?, ?, ?,
|
|
340
343
|
?, ?, ?, ?,
|
|
341
|
-
?, ?, ?, datetime('now')
|
|
344
|
+
?, ?, ?, ?, datetime('now')
|
|
342
345
|
)
|
|
343
346
|
`);
|
|
344
347
|
stmt.run(
|
|
@@ -354,6 +357,7 @@ export function saveAuthSession(
|
|
|
354
357
|
session.version || null,
|
|
355
358
|
session.userId || null,
|
|
356
359
|
session.tokenExpiresAt || null,
|
|
360
|
+
session.refreshToken || null,
|
|
357
361
|
session.capturedAt ?? Date.now(),
|
|
358
362
|
);
|
|
359
363
|
}
|
|
@@ -367,7 +371,7 @@ export function getValidAuthSession(
|
|
|
367
371
|
.prepare(
|
|
368
372
|
`SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
369
373
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
370
|
-
user_id, token_expires_at, captured_at
|
|
374
|
+
user_id, token_expires_at, refresh_token, captured_at
|
|
371
375
|
FROM qwen_auth_sessions WHERE account_id = ?`,
|
|
372
376
|
)
|
|
373
377
|
.get(accountId) as any;
|
|
@@ -379,9 +383,9 @@ export function getValidAuthSession(
|
|
|
379
383
|
return null;
|
|
380
384
|
}
|
|
381
385
|
|
|
382
|
-
if (row.token_expires_at) {
|
|
386
|
+
if (!row.refresh_token && row.token_expires_at) {
|
|
383
387
|
const tokenExpMs = Number(row.token_expires_at) * 1000;
|
|
384
|
-
// Safety margin of 5 minutes before token expires
|
|
388
|
+
// Safety margin of 5 minutes before token expires (only when no refresh_token exists)
|
|
385
389
|
if (tokenExpMs <= Date.now() + 5 * 60 * 1000) {
|
|
386
390
|
return null;
|
|
387
391
|
}
|
|
@@ -405,6 +409,7 @@ export function getValidAuthSession(
|
|
|
405
409
|
version: row.version || undefined,
|
|
406
410
|
userId: row.user_id || undefined,
|
|
407
411
|
tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : undefined,
|
|
412
|
+
refreshToken: row.refresh_token || undefined,
|
|
408
413
|
capturedAt,
|
|
409
414
|
};
|
|
410
415
|
}
|
package/src/core/version.ts
CHANGED
|
@@ -479,8 +479,8 @@ export function classifyRetryAction(
|
|
|
479
479
|
// Agent instructions ride ONLY the account-level personalization. An
|
|
480
480
|
// unconfirmed sync means this account cannot serve the request as-is —
|
|
481
481
|
// rotate to another account (each attempt re-syncs on its own account).
|
|
482
|
-
//
|
|
483
|
-
//
|
|
482
|
+
// Do NOT park the account with a 300s cooldown: rotation across accounts
|
|
483
|
+
// is already guarded by triedAccounts, preventing pool collapse.
|
|
484
484
|
if (err instanceof PersonalizationSyncError) {
|
|
485
485
|
return makeRetryAction("personalization_sync_failed", {
|
|
486
486
|
switchAccount: true,
|
|
@@ -502,7 +502,8 @@ export function classifyRetryAction(
|
|
|
502
502
|
message.includes("não tem permissão para acessar") ||
|
|
503
503
|
message.includes("401 unauthorized") ||
|
|
504
504
|
message.includes('"code":"unauthorized"') ||
|
|
505
|
-
message.includes('"code": "unauthorized"')
|
|
505
|
+
message.includes('"code": "unauthorized"') ||
|
|
506
|
+
message.includes("token has expired")
|
|
506
507
|
) {
|
|
507
508
|
return makeRetryAction("account_initialization_failed", {
|
|
508
509
|
switchAccount: true,
|