qwenproxy-cli 1.5.2 → 1.6.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.
@@ -296,7 +296,26 @@ function runMigrations(db2) {
296
296
  instruction_hash TEXT NOT NULL,
297
297
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
298
298
  );
299
+
300
+ CREATE TABLE IF NOT EXISTS account_daily_usage (
301
+ account_id TEXT NOT NULL,
302
+ date_utc TEXT NOT NULL,
303
+ prompt_tokens INTEGER NOT NULL DEFAULT 0,
304
+ completion_tokens INTEGER NOT NULL DEFAULT 0,
305
+ weighted_tokens REAL NOT NULL DEFAULT 0,
306
+ total_tokens INTEGER NOT NULL DEFAULT 0,
307
+ turns_count INTEGER NOT NULL DEFAULT 0,
308
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
309
+ PRIMARY KEY (account_id, date_utc)
310
+ );
311
+
312
+ CREATE INDEX IF NOT EXISTS idx_daily_usage_date ON account_daily_usage(date_utc);
299
313
  `);
314
+ try {
315
+ db2.exec(`ALTER TABLE account_daily_usage ADD COLUMN weighted_tokens REAL DEFAULT 0;`);
316
+ } catch (err) {
317
+ if (!isDuplicateColumnError(err)) throw err;
318
+ }
300
319
  try {
301
320
  db2.exec(
302
321
  `ALTER TABLE accounts ADD COLUMN cooldown_until INTEGER DEFAULT 0;`
@@ -397,7 +416,11 @@ function saveAuthSession(accountId, session) {
397
416
  ) VALUES (
398
417
  ?, ?, ?, ?, ?, ?,
399
418
  ?, ?, ?, ?,
400
- ?, ?, ?, ?, datetime('now')
419
+ COALESCE(?, (SELECT user_id FROM qwen_auth_sessions WHERE account_id = ?)),
420
+ ?,
421
+ COALESCE(?, (SELECT refresh_token FROM qwen_auth_sessions WHERE account_id = ?)),
422
+ ?,
423
+ datetime('now')
401
424
  )
402
425
  `);
403
426
  stmt.run(
@@ -412,8 +435,10 @@ function saveAuthSession(accountId, session) {
412
435
  session.secChUaPlatform || null,
413
436
  session.version || null,
414
437
  session.userId || null,
438
+ accountId,
415
439
  session.tokenExpiresAt || null,
416
440
  session.refreshToken || null,
441
+ accountId,
417
442
  session.capturedAt ?? Date.now()
418
443
  );
419
444
  }
@@ -4306,6 +4331,137 @@ async function loginViaUi(page, email, password) {
4306
4331
  return { success: false, reason: err?.message || String(err) };
4307
4332
  }
4308
4333
  }
4334
+ async function refreshAccountToken(accountId) {
4335
+ const page = accountPages.get(accountId);
4336
+ if (!page || page.isClosed()) {
4337
+ return { success: false, error: "Account page not active" };
4338
+ }
4339
+ const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4340
+ const session = getValidAuthSession2(accountId);
4341
+ let refreshToken = session?.refreshToken;
4342
+ if (!refreshToken) {
4343
+ try {
4344
+ refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
4345
+ } catch {
4346
+ }
4347
+ }
4348
+ if (refreshToken) {
4349
+ try {
4350
+ const refreshResult = await page.evaluate(
4351
+ async ({ rTok }) => {
4352
+ try {
4353
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4354
+ method: "GET",
4355
+ credentials: "include",
4356
+ headers: {
4357
+ accept: "application/json, text/plain, */*",
4358
+ source: "web",
4359
+ version: "0.3.11",
4360
+ "x-request-origin": "https://chat.qwen.ai",
4361
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4362
+ authorization: `Bearer ${rTok}`
4363
+ }
4364
+ });
4365
+ const json2 = await response.json().catch(() => null);
4366
+ return { ok: response.ok, status: response.status, data: json2 };
4367
+ } catch (e) {
4368
+ return { ok: false, error: e.message };
4369
+ }
4370
+ },
4371
+ { rTok: refreshToken }
4372
+ );
4373
+ const json = refreshResult?.data;
4374
+ if (json && json.success === true && json.data) {
4375
+ const newAccessToken = json.data.access_token || json.data.token;
4376
+ const newRefreshToken = json.data.refresh_token || refreshToken;
4377
+ if (newAccessToken) {
4378
+ await page.context().addCookies([
4379
+ {
4380
+ name: "token",
4381
+ value: newAccessToken,
4382
+ domain: ".qwen.ai",
4383
+ path: "/",
4384
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
4385
+ httpOnly: false,
4386
+ secure: true,
4387
+ sameSite: "Lax"
4388
+ }
4389
+ ]);
4390
+ await page.evaluate(
4391
+ ({ tok, rTok }) => {
4392
+ try {
4393
+ localStorage.removeItem("qwen_token_logged_out_marker");
4394
+ localStorage.setItem("token", tok);
4395
+ localStorage.setItem("access_token", tok);
4396
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4397
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4398
+ } catch {
4399
+ }
4400
+ },
4401
+ { tok: newAccessToken, rTok: newRefreshToken }
4402
+ ).catch(() => {
4403
+ });
4404
+ const liveCookies = await page.context().cookies();
4405
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
4406
+ const cache = getHeaderCache(accountId);
4407
+ cache.headers.cookie = cookieStr;
4408
+ cache.lastRefresh = Date.now();
4409
+ markAccountHeadersReady(accountId);
4410
+ saveAuthSession2(accountId, {
4411
+ cookie: cookieStr,
4412
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
4413
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
4414
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
4415
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
4416
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
4417
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
4418
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
4419
+ version: cache.headers["version"] || session?.version,
4420
+ userId: session?.userId,
4421
+ tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
4422
+ refreshToken: newRefreshToken,
4423
+ capturedAt: Date.now()
4424
+ });
4425
+ return { success: true, token: newAccessToken };
4426
+ }
4427
+ }
4428
+ } catch {
4429
+ }
4430
+ }
4431
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4432
+ const creds = getAccountCredentials2(accountId);
4433
+ if (creds && creds.email && creds.password) {
4434
+ const apiResult = await loginViaApi(page, creds.email, creds.password);
4435
+ if (apiResult.success) {
4436
+ const liveCookies = await page.context().cookies();
4437
+ const tokenCookie = liveCookies.find((c) => c.name === "token");
4438
+ if (tokenCookie) {
4439
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
4440
+ const cache = getHeaderCache(accountId);
4441
+ cache.headers.cookie = cookieStr;
4442
+ cache.lastRefresh = Date.now();
4443
+ markAccountHeadersReady(accountId);
4444
+ saveAuthSession2(accountId, {
4445
+ cookie: cookieStr,
4446
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
4447
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
4448
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
4449
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
4450
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
4451
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
4452
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
4453
+ version: cache.headers["version"] || session?.version,
4454
+ userId: session?.userId,
4455
+ tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
4456
+ refreshToken: apiResult.refreshToken,
4457
+ capturedAt: Date.now()
4458
+ });
4459
+ return { success: true, token: tokenCookie.value };
4460
+ }
4461
+ }
4462
+ }
4463
+ return { success: false, error: "Could not refresh or re-authenticate token" };
4464
+ }
4309
4465
  async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
4310
4466
  const page = pageOverride ?? accountPages.get(accountId);
4311
4467
  if (!page || page.isClosed()) {
@@ -4459,6 +4615,11 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
4459
4615
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4460
4616
  const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
4461
4617
  const tokenExpiry = parseJwtExpiry2(capturedHeaders.cookie);
4618
+ let rTok;
4619
+ try {
4620
+ rTok = (await page.evaluate(() => localStorage.getItem("refresh_token") || "")).trim() || void 0;
4621
+ } catch {
4622
+ }
4462
4623
  saveAuthSession2(accountId, {
4463
4624
  cookie: capturedHeaders.cookie,
4464
4625
  userAgent: capturedHeaders["user-agent"],
@@ -4470,6 +4631,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
4470
4631
  secChUaPlatform: capturedHeaders["sec-ch-ua-platform"],
4471
4632
  version: capturedHeaders["version"],
4472
4633
  tokenExpiresAt: tokenExpiry || void 0,
4634
+ refreshToken: rTok,
4473
4635
  capturedAt: Date.now()
4474
4636
  });
4475
4637
  } catch {
@@ -4501,38 +4663,57 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
4501
4663
  const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
4502
4664
  const isSuspectedGuest = isAuthUrl || attempt >= 2 && !await isPageLoggedIn(page, 5e3);
4503
4665
  if (isSuspectedGuest) {
4504
- const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4505
- const creds = getAccountCredentials2(accountId);
4506
- if (creds && creds.email && creds.password) {
4507
- console.warn(
4508
- `\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
4666
+ const silentRefresh = await refreshAccountToken(accountId);
4667
+ if (silentRefresh.success) {
4668
+ console.log(
4669
+ `\u26A1 [Playwright] Token refreshed silently during header capture for ${accountId}`
4509
4670
  );
4510
- armOverallDeadline();
4511
- const ok = await loginToQwen(accountId, creds.email, creds.password);
4512
- if (ok) {
4671
+ if (isAuthUrl) {
4513
4672
  await page.goto(qwenUrl("/"), {
4514
4673
  waitUntil: "domcontentloaded",
4515
4674
  timeout: Math.min(config.timeouts.navigation, timeoutMs)
4516
4675
  });
4517
- await sleep2(2e3);
4676
+ await sleep2(1500);
4677
+ }
4678
+ if (await isPageLoggedIn(page, 5e3)) {
4679
+ armOverallDeadline();
4518
4680
  }
4519
- if (!ok || !await isPageLoggedIn(page, 6e3)) {
4681
+ }
4682
+ const stillSuspected = !silentRefresh.success || !await isPageLoggedIn(page, 2e3);
4683
+ if (stillSuspected) {
4684
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4685
+ const creds = getAccountCredentials2(accountId);
4686
+ if (creds && creds.email && creds.password) {
4687
+ console.warn(
4688
+ `\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
4689
+ );
4690
+ armOverallDeadline();
4691
+ const ok = await loginToQwen(accountId, creds.email, creds.password);
4692
+ if (ok) {
4693
+ await page.goto(qwenUrl("/"), {
4694
+ waitUntil: "domcontentloaded",
4695
+ timeout: Math.min(config.timeouts.navigation, timeoutMs)
4696
+ });
4697
+ await sleep2(2e3);
4698
+ }
4699
+ if (!ok || !await isPageLoggedIn(page, 6e3)) {
4700
+ settle(
4701
+ new Error(
4702
+ `Header capture failed for ${accountId}: re-login after session expiry did not succeed`
4703
+ )
4704
+ );
4705
+ return;
4706
+ }
4707
+ armOverallDeadline();
4708
+ if (settled || page.isClosed()) return;
4709
+ } else {
4520
4710
  settle(
4521
4711
  new Error(
4522
- `Header capture failed for ${accountId}: re-login after session expiry did not succeed`
4712
+ `Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
4523
4713
  )
4524
4714
  );
4525
4715
  return;
4526
4716
  }
4527
- armOverallDeadline();
4528
- if (settled || page.isClosed()) return;
4529
- } else {
4530
- settle(
4531
- new Error(
4532
- `Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
4533
- )
4534
- );
4535
- return;
4536
4717
  }
4537
4718
  }
4538
4719
  if (settled || page.isClosed()) return;
@@ -5022,6 +5203,7 @@ function cleanupPlaywrightAccountState(accountId) {
5022
5203
  cookieCaches.delete(accountId);
5023
5204
  lastAccountActivity.delete(accountId);
5024
5205
  lastKeepAliveNavigation.delete(accountId);
5206
+ lastProactiveTokenRefresh.delete(accountId);
5025
5207
  clearFingerprintCache(accountId);
5026
5208
  unmarkAccountHeadersReady(accountId);
5027
5209
  }
@@ -5096,7 +5278,7 @@ async function closePlaywrightForAccount(accountId) {
5096
5278
  release();
5097
5279
  }
5098
5280
  }
5099
- var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, inFlightAccountInits, headerCaches, HEADER_CACHE_TTL, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
5281
+ var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, inFlightAccountInits, headerCaches, HEADER_CACHE_TTL, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
5100
5282
  var init_playwright = __esm({
5101
5283
  "src/services/playwright.ts"() {
5102
5284
  "use strict";
@@ -5128,6 +5310,7 @@ var init_playwright = __esm({
5128
5310
  cookieCaches = /* @__PURE__ */ new Map();
5129
5311
  lastAccountActivity = /* @__PURE__ */ new Map();
5130
5312
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
5313
+ lastProactiveTokenRefresh = /* @__PURE__ */ new Map();
5131
5314
  profileResetQueue = /* @__PURE__ */ new Map();
5132
5315
  profileResetChain = Promise.resolve();
5133
5316
  closingAllPlaywright = false;