qwenproxy-cli 1.5.2 → 1.5.3

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.
@@ -9567,9 +9567,13 @@ function parseQwenJsonError(raw, status, accountId) {
9567
9567
  }
9568
9568
  if (errorJson?.success === false) {
9569
9569
  const code = errorJson.data?.code || errorJson.code || "UpstreamError";
9570
- if (status === 401 || code === "Unauthorized" || typeof details === "string" && (details.includes("login") || details.includes("session"))) {
9570
+ const detailsStr = typeof details === "string" ? details : "";
9571
+ const codeStr = String(code).toLowerCase();
9572
+ const detailsLower = detailsStr.toLowerCase();
9573
+ const isAuthExpired = status === 401 || codeStr.includes("unauthorized") || detailsLower.includes("token has expired") || detailsLower.includes("log in") || detailsLower.includes("login") || detailsLower.includes("session") || detailsLower.includes("n\xE3o autorizado") || detailsLower.includes("unauthorized");
9574
+ if (isAuthExpired) {
9571
9575
  return new QwenSessionExpiredError(
9572
- `Session expired: ${details}`,
9576
+ `Session expired: ${detailsStr || codeStr}`,
9573
9577
  accountId || "global"
9574
9578
  );
9575
9579
  }
@@ -10108,8 +10112,24 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10108
10112
  )
10109
10113
  );
10110
10114
  }
10115
+ const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
10116
+ if (parsedJsonErr instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
10117
+ retriedNonSseResponse = true;
10118
+ logger.warn(
10119
+ "[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
10120
+ { accountId, chatId: chatSessionId ?? "new" }
10121
+ );
10122
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10123
+ const refreshed = await refreshAccountToken2(accountId);
10124
+ if (refreshed.success) {
10125
+ const fresh = await getQwenHeaders(false, accountId);
10126
+ activeHeaders = fresh.headers;
10127
+ response = await fetchCompletion(activeHeaders);
10128
+ continue;
10129
+ }
10130
+ }
10111
10131
  throw withCreatedChatMetadata(
10112
- parseQwenJsonError(errText, response.status, accountId) ?? new QwenUpstreamError(
10132
+ parsedJsonErr ?? new QwenUpstreamError(
10113
10133
  `Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
10114
10134
  "NonStreamJsonResponse",
10115
10135
  502
@@ -10160,6 +10180,31 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10160
10180
  response.status,
10161
10181
  accountId
10162
10182
  );
10183
+ if (parsedError instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
10184
+ retriedNonSseResponse = true;
10185
+ logger.warn(
10186
+ "[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
10187
+ { accountId, chatId: chatSessionId ?? "new", status: response.status }
10188
+ );
10189
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10190
+ const refreshed = await refreshAccountToken2(accountId);
10191
+ if (refreshed.success) {
10192
+ const fresh = await getQwenHeaders(false, accountId);
10193
+ activeHeaders = fresh.headers;
10194
+ response = await fetchCompletion(activeHeaders);
10195
+ if (response.ok && response.body) {
10196
+ return {
10197
+ stream: wrapUpstreamStream(response.body, controller),
10198
+ headers: activeHeaders,
10199
+ uiSessionId: chatSessionId || "",
10200
+ controller,
10201
+ accountId: accountId ?? "global",
10202
+ createdNewChat,
10203
+ tokenEstimationContext
10204
+ };
10205
+ }
10206
+ }
10207
+ }
10163
10208
  if (parsedError) {
10164
10209
  throw withCreatedChatMetadata(parsedError);
10165
10210
  }
package/dist/index.js CHANGED
@@ -12736,9 +12736,13 @@ function parseQwenJsonError(raw, status, accountId) {
12736
12736
  }
12737
12737
  if (errorJson?.success === false) {
12738
12738
  const code = errorJson.data?.code || errorJson.code || "UpstreamError";
12739
- if (status === 401 || code === "Unauthorized" || typeof details === "string" && (details.includes("login") || details.includes("session"))) {
12739
+ const detailsStr = typeof details === "string" ? details : "";
12740
+ const codeStr = String(code).toLowerCase();
12741
+ const detailsLower = detailsStr.toLowerCase();
12742
+ const isAuthExpired = status === 401 || codeStr.includes("unauthorized") || detailsLower.includes("token has expired") || detailsLower.includes("log in") || detailsLower.includes("login") || detailsLower.includes("session") || detailsLower.includes("n\xE3o autorizado") || detailsLower.includes("unauthorized");
12743
+ if (isAuthExpired) {
12740
12744
  return new QwenSessionExpiredError(
12741
- `Session expired: ${details}`,
12745
+ `Session expired: ${detailsStr || codeStr}`,
12742
12746
  accountId || "global"
12743
12747
  );
12744
12748
  }
@@ -13277,8 +13281,24 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
13277
13281
  )
13278
13282
  );
13279
13283
  }
13284
+ const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
13285
+ if (parsedJsonErr instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
13286
+ retriedNonSseResponse = true;
13287
+ logger.warn(
13288
+ "[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
13289
+ { accountId, chatId: chatSessionId ?? "new" }
13290
+ );
13291
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
13292
+ const refreshed = await refreshAccountToken2(accountId);
13293
+ if (refreshed.success) {
13294
+ const fresh = await getQwenHeaders(false, accountId);
13295
+ activeHeaders = fresh.headers;
13296
+ response = await fetchCompletion(activeHeaders);
13297
+ continue;
13298
+ }
13299
+ }
13280
13300
  throw withCreatedChatMetadata(
13281
- parseQwenJsonError(errText, response.status, accountId) ?? new QwenUpstreamError(
13301
+ parsedJsonErr ?? new QwenUpstreamError(
13282
13302
  `Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
13283
13303
  "NonStreamJsonResponse",
13284
13304
  502
@@ -13329,6 +13349,31 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
13329
13349
  response.status,
13330
13350
  accountId
13331
13351
  );
13352
+ if (parsedError instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
13353
+ retriedNonSseResponse = true;
13354
+ logger.warn(
13355
+ "[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
13356
+ { accountId, chatId: chatSessionId ?? "new", status: response.status }
13357
+ );
13358
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
13359
+ const refreshed = await refreshAccountToken2(accountId);
13360
+ if (refreshed.success) {
13361
+ const fresh = await getQwenHeaders(false, accountId);
13362
+ activeHeaders = fresh.headers;
13363
+ response = await fetchCompletion(activeHeaders);
13364
+ if (response.ok && response.body) {
13365
+ return {
13366
+ stream: wrapUpstreamStream(response.body, controller),
13367
+ headers: activeHeaders,
13368
+ uiSessionId: chatSessionId || "",
13369
+ controller,
13370
+ accountId: accountId ?? "global",
13371
+ createdNewChat,
13372
+ tokenEstimationContext
13373
+ };
13374
+ }
13375
+ }
13376
+ }
13332
13377
  if (parsedError) {
13333
13378
  throw withCreatedChatMetadata(parsedError);
13334
13379
  }
@@ -17106,16 +17151,16 @@ ${loopReminder}` : truncation.prompt;
17106
17151
  error: new Error("Failed to create Qwen stream")
17107
17152
  };
17108
17153
  }
17109
- if (err instanceof QwenSessionExpiredError || err.name === "QwenSessionExpiredError") {
17154
+ const isSessionExpired = err instanceof QwenSessionExpiredError || err?.name === "QwenSessionExpiredError" || err instanceof Error && (err.message.toLowerCase().includes("token has expired") || err.message.toLowerCase().includes("session expired") || err.message.toLowerCase().includes("401 n\xE3o autorizado") || err.message.toLowerCase().includes("unauthorized"));
17155
+ if (isSessionExpired) {
17110
17156
  console.warn(
17111
- `\u{1F504} [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting re-login...`
17157
+ `\u{1F504} [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting silent token refresh/re-login...`
17112
17158
  );
17113
17159
  const reLoginOk = await attemptRelogin(
17114
17160
  currentAccountId,
17115
17161
  currentAccountEmail
17116
17162
  );
17117
17163
  if (reLoginOk) continue;
17118
- return { success: false, error: err };
17119
17164
  }
17120
17165
  if (isAccountUnavailableError(err)) {
17121
17166
  const quotaMsg = err.message || "Unknown quota error";
@@ -25891,7 +25936,7 @@ function getAppVersion() {
25891
25936
  }
25892
25937
  } catch {
25893
25938
  }
25894
- cachedVersion = "v1.5.2";
25939
+ cachedVersion = "v1.5.3";
25895
25940
  return cachedVersion;
25896
25941
  }
25897
25942
  var cachedVersion;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
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": {
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.5.2";
20
+ cachedVersion = "v1.5.3";
21
21
  return cachedVersion;
22
22
  }
@@ -1487,19 +1487,24 @@ async function tryCreateStreamWithRetry(
1487
1487
  };
1488
1488
  }
1489
1489
 
1490
- if (
1490
+ const isSessionExpired =
1491
1491
  err instanceof QwenSessionExpiredError ||
1492
- err.name === "QwenSessionExpiredError"
1493
- ) {
1492
+ err?.name === "QwenSessionExpiredError" ||
1493
+ (err instanceof Error &&
1494
+ (err.message.toLowerCase().includes("token has expired") ||
1495
+ err.message.toLowerCase().includes("session expired") ||
1496
+ err.message.toLowerCase().includes("401 não autorizado") ||
1497
+ err.message.toLowerCase().includes("unauthorized")));
1498
+
1499
+ if (isSessionExpired) {
1494
1500
  console.warn(
1495
- `🔄 [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting re-login...`,
1501
+ `🔄 [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting silent token refresh/re-login...`,
1496
1502
  );
1497
1503
  const reLoginOk = await attemptRelogin(
1498
1504
  currentAccountId,
1499
1505
  currentAccountEmail,
1500
1506
  );
1501
1507
  if (reLoginOk) continue;
1502
- return { success: false, error: err };
1503
1508
  }
1504
1509
 
1505
1510
 
@@ -2502,15 +2502,23 @@ function parseQwenJsonError(
2502
2502
 
2503
2503
  if (errorJson?.success === false) {
2504
2504
  const code = errorJson.data?.code || errorJson.code || "UpstreamError";
2505
+ const detailsStr = typeof details === "string" ? details : "";
2506
+ const codeStr = String(code).toLowerCase();
2507
+ const detailsLower = detailsStr.toLowerCase();
2505
2508
 
2506
- if (
2509
+ const isAuthExpired =
2507
2510
  status === 401 ||
2508
- code === "Unauthorized" ||
2509
- (typeof details === "string" &&
2510
- (details.includes("login") || details.includes("session")))
2511
- ) {
2511
+ codeStr.includes("unauthorized") ||
2512
+ detailsLower.includes("token has expired") ||
2513
+ detailsLower.includes("log in") ||
2514
+ detailsLower.includes("login") ||
2515
+ detailsLower.includes("session") ||
2516
+ detailsLower.includes("não autorizado") ||
2517
+ detailsLower.includes("unauthorized");
2518
+
2519
+ if (isAuthExpired) {
2512
2520
  return new QwenSessionExpiredError(
2513
- `Session expired: ${details}`,
2521
+ `Session expired: ${detailsStr || codeStr}`,
2514
2522
  accountId || "global",
2515
2523
  );
2516
2524
  }
@@ -3301,8 +3309,29 @@ async function createQwenStreamInternal(
3301
3309
  );
3302
3310
  }
3303
3311
 
3312
+ const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
3313
+ if (
3314
+ parsedJsonErr instanceof QwenSessionExpiredError &&
3315
+ accountId &&
3316
+ !retriedNonSseResponse
3317
+ ) {
3318
+ retriedNonSseResponse = true;
3319
+ logger.warn(
3320
+ "[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
3321
+ { accountId, chatId: chatSessionId ?? "new" },
3322
+ );
3323
+ const { refreshAccountToken } = await import("./playwright.ts");
3324
+ const refreshed = await refreshAccountToken(accountId);
3325
+ if (refreshed.success) {
3326
+ const fresh = await getQwenHeaders(false, accountId);
3327
+ activeHeaders = fresh.headers;
3328
+ response = await fetchCompletion(activeHeaders);
3329
+ continue;
3330
+ }
3331
+ }
3332
+
3304
3333
  throw withCreatedChatMetadata(
3305
- parseQwenJsonError(errText, response.status, accountId) ??
3334
+ parsedJsonErr ??
3306
3335
  new QwenUpstreamError(
3307
3336
  `Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
3308
3337
  "NonStreamJsonResponse",
@@ -3378,6 +3407,35 @@ async function createQwenStreamInternal(
3378
3407
  response.status,
3379
3408
  accountId,
3380
3409
  );
3410
+ if (
3411
+ parsedError instanceof QwenSessionExpiredError &&
3412
+ accountId &&
3413
+ !retriedNonSseResponse
3414
+ ) {
3415
+ retriedNonSseResponse = true;
3416
+ logger.warn(
3417
+ "[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
3418
+ { accountId, chatId: chatSessionId ?? "new", status: response.status },
3419
+ );
3420
+ const { refreshAccountToken } = await import("./playwright.ts");
3421
+ const refreshed = await refreshAccountToken(accountId);
3422
+ if (refreshed.success) {
3423
+ const fresh = await getQwenHeaders(false, accountId);
3424
+ activeHeaders = fresh.headers;
3425
+ response = await fetchCompletion(activeHeaders);
3426
+ if (response.ok && response.body) {
3427
+ return {
3428
+ stream: wrapUpstreamStream(response.body, controller),
3429
+ headers: activeHeaders,
3430
+ uiSessionId: chatSessionId || "",
3431
+ controller,
3432
+ accountId: accountId ?? "global",
3433
+ createdNewChat,
3434
+ tokenEstimationContext,
3435
+ };
3436
+ }
3437
+ }
3438
+ }
3381
3439
  if (parsedError) {
3382
3440
  throw withCreatedChatMetadata(parsedError);
3383
3441
  }