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/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 alreadyIn = await isPageLoggedIn(page, 2e3).catch(() => false);
5757
- if (alreadyIn) {
5758
- const liveCookies = await page.context().cookies();
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, 2)) {
6202
- try {
6203
- await page.evaluate(async () => {
6204
- await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
6205
- method: "GET",
6206
- credentials: "include"
6207
- }).catch(() => {
6208
- });
6209
- await fetch("/api/v1/auths/", {
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;
@@ -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.7",
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": {
@@ -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
  }
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.4.7";
20
+ cachedVersion = "v1.4.8";
21
21
  return cachedVersion;
22
22
  }
@@ -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
- // Park the failing account with PersonalizationFailed cooldown so it does
483
- // not enter an infinite ping-pong loop when multiple accounts fail.
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,