qwenproxy-cli 1.6.5 → 1.6.6

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.
@@ -3588,6 +3588,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
3588
3588
  await existingInit;
3589
3589
  return;
3590
3590
  }
3591
+ let resolveInit;
3592
+ let rejectInit;
3593
+ const inFlightDeferred = new Promise((res, rej) => {
3594
+ resolveInit = res;
3595
+ rejectInit = rej;
3596
+ });
3597
+ inFlightAccountInits.set(account.id, inFlightDeferred);
3591
3598
  const initPromise = (async () => {
3592
3599
  const release = await acquireAccountMutex(
3593
3600
  account.id,
@@ -3872,9 +3879,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
3872
3879
  release();
3873
3880
  }
3874
3881
  })();
3875
- inFlightAccountInits.set(account.id, initPromise);
3876
3882
  try {
3877
3883
  await initPromise;
3884
+ resolveInit();
3885
+ } catch (error) {
3886
+ rejectInit(error);
3887
+ throw error;
3878
3888
  } finally {
3879
3889
  inFlightAccountInits.delete(account.id);
3880
3890
  }
@@ -4447,6 +4447,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4447
4447
  await existingInit;
4448
4448
  return;
4449
4449
  }
4450
+ let resolveInit;
4451
+ let rejectInit;
4452
+ const inFlightDeferred = new Promise((res, rej) => {
4453
+ resolveInit = res;
4454
+ rejectInit = rej;
4455
+ });
4456
+ inFlightAccountInits.set(account.id, inFlightDeferred);
4450
4457
  const initPromise = (async () => {
4451
4458
  const release = await acquireAccountMutex(
4452
4459
  account.id,
@@ -4731,9 +4738,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4731
4738
  release();
4732
4739
  }
4733
4740
  })();
4734
- inFlightAccountInits.set(account.id, initPromise);
4735
4741
  try {
4736
4742
  await initPromise;
4743
+ resolveInit();
4744
+ } catch (error) {
4745
+ rejectInit(error);
4746
+ throw error;
4737
4747
  } finally {
4738
4748
  inFlightAccountInits.delete(account.id);
4739
4749
  }
package/dist/index.js CHANGED
@@ -5557,6 +5557,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5557
5557
  await existingInit;
5558
5558
  return;
5559
5559
  }
5560
+ let resolveInit;
5561
+ let rejectInit;
5562
+ const inFlightDeferred = new Promise((res, rej) => {
5563
+ resolveInit = res;
5564
+ rejectInit = rej;
5565
+ });
5566
+ inFlightAccountInits.set(account.id, inFlightDeferred);
5560
5567
  const initPromise = (async () => {
5561
5568
  const release = await acquireAccountMutex(
5562
5569
  account.id,
@@ -5841,9 +5848,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5841
5848
  release();
5842
5849
  }
5843
5850
  })();
5844
- inFlightAccountInits.set(account.id, initPromise);
5845
5851
  try {
5846
5852
  await initPromise;
5853
+ resolveInit();
5854
+ } catch (error) {
5855
+ rejectInit(error);
5856
+ throw error;
5847
5857
  } finally {
5848
5858
  inFlightAccountInits.delete(account.id);
5849
5859
  }
@@ -21513,7 +21523,7 @@ async function processStreamingResponse(params) {
21513
21523
  let chatInProgressSameAccountRetries = 0;
21514
21524
  let antiBotSameAccountRetries = 0;
21515
21525
  const totalConfiguredAccounts = loadAccounts().length;
21516
- let quotaRotationsLeft = Math.min(Math.max(1, totalConfiguredAccounts - 1), 6);
21526
+ let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
21517
21527
  let streamCompletedOk = false;
21518
21528
  let stoppedByToolCap = false;
21519
21529
  const runDisconnectTeardown = () => {
@@ -22950,28 +22960,32 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
22950
22960
  logger.error("[Chat] Stream callback error", errorDetails);
22951
22961
  }
22952
22962
  try {
22953
- const userFriendlyNotice = `
22963
+ const isNetwork = isNetworkLikeError(err);
22964
+ const isModeration = normalizedErrorCode === "data_inspection_failed" || normalizedErrorCode === "fail_sys_user_validate" || err.message?.toLowerCase().includes("content moderation") || err.message?.toLowerCase().includes("security verification");
22965
+ if (!isNetwork && isModeration) {
22966
+ const userFriendlyNotice = `
22954
22967
 
22955
- \u26A0\uFE0F **[Qwen Security / Erro]** ${err.message || "A resposta foi interrompida pelo Qwen."}
22968
+ \u26A0\uFE0F **[Qwen Modera\xE7\xE3o / Seguran\xE7a]** ${err.message || "A resposta foi interrompida pelas pol\xEDticas de seguran\xE7a do Qwen."}
22956
22969
 
22957
22970
  `;
22958
- const errorDelta = {
22959
- id: completionId,
22960
- object: "chat.completion.chunk",
22961
- created: Math.floor(Date.now() / 1e3),
22962
- model: body.model,
22963
- choices: [
22964
- {
22965
- index: 0,
22966
- delta: { content: userFriendlyNotice },
22967
- logprobs: null,
22968
- finish_reason: "stop"
22969
- }
22970
- ]
22971
- };
22972
- await errorStream.write(`data: ${JSON.stringify(errorDelta)}
22971
+ const errorDelta = {
22972
+ id: completionId,
22973
+ object: "chat.completion.chunk",
22974
+ created: Math.floor(Date.now() / 1e3),
22975
+ model: body.model,
22976
+ choices: [
22977
+ {
22978
+ index: 0,
22979
+ delta: { content: userFriendlyNotice },
22980
+ logprobs: null,
22981
+ finish_reason: "content_filter"
22982
+ }
22983
+ ]
22984
+ };
22985
+ await errorStream.write(`data: ${JSON.stringify(errorDelta)}
22973
22986
 
22974
22987
  `);
22988
+ }
22975
22989
  await errorStream.write(
22976
22990
  `data: ${JSON.stringify({
22977
22991
  error: {
@@ -27004,7 +27018,7 @@ function getAppVersion() {
27004
27018
  }
27005
27019
  } catch {
27006
27020
  }
27007
- cachedVersion = "v1.6.5";
27021
+ cachedVersion = "v1.6.6";
27008
27022
  return cachedVersion;
27009
27023
  }
27010
27024
  var cachedVersion;
package/dist/login.js CHANGED
@@ -4420,6 +4420,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4420
4420
  await existingInit;
4421
4421
  return;
4422
4422
  }
4423
+ let resolveInit;
4424
+ let rejectInit;
4425
+ const inFlightDeferred = new Promise((res, rej) => {
4426
+ resolveInit = res;
4427
+ rejectInit = rej;
4428
+ });
4429
+ inFlightAccountInits.set(account.id, inFlightDeferred);
4423
4430
  const initPromise = (async () => {
4424
4431
  const release = await acquireAccountMutex(
4425
4432
  account.id,
@@ -4704,9 +4711,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4704
4711
  release();
4705
4712
  }
4706
4713
  })();
4707
- inFlightAccountInits.set(account.id, initPromise);
4708
4714
  try {
4709
4715
  await initPromise;
4716
+ resolveInit();
4717
+ } catch (error) {
4718
+ rejectInit(error);
4719
+ throw error;
4710
4720
  } finally {
4711
4721
  inFlightAccountInits.delete(account.id);
4712
4722
  }
@@ -3851,6 +3851,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
3851
3851
  await existingInit;
3852
3852
  return;
3853
3853
  }
3854
+ let resolveInit;
3855
+ let rejectInit;
3856
+ const inFlightDeferred = new Promise((res, rej) => {
3857
+ resolveInit = res;
3858
+ rejectInit = rej;
3859
+ });
3860
+ inFlightAccountInits.set(account.id, inFlightDeferred);
3854
3861
  const initPromise = (async () => {
3855
3862
  const release = await acquireAccountMutex(
3856
3863
  account.id,
@@ -4135,9 +4142,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4135
4142
  release();
4136
4143
  }
4137
4144
  })();
4138
- inFlightAccountInits.set(account.id, initPromise);
4139
4145
  try {
4140
4146
  await initPromise;
4147
+ resolveInit();
4148
+ } catch (error) {
4149
+ rejectInit(error);
4150
+ throw error;
4141
4151
  } finally {
4142
4152
  inFlightAccountInits.delete(account.id);
4143
4153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.6.5",
3
+ "version": "1.6.6",
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.6.5";
20
+ cachedVersion = "v1.6.6";
21
21
  return cachedVersion;
22
22
  }
@@ -937,7 +937,7 @@ export async function processStreamingResponse(
937
937
  let chatInProgressSameAccountRetries = 0;
938
938
  let antiBotSameAccountRetries = 0;
939
939
  const totalConfiguredAccounts = loadAccounts().length;
940
- let quotaRotationsLeft = Math.min(Math.max(1, totalConfiguredAccounts - 1), 6);
940
+ let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
941
941
  // Set when the terminal [DONE] reached the client. The `| recovered`
942
942
  // suffix on Stream done must only appear for attempts that COMPLETED after
943
943
  // a mid-stream retry, not for failed attempts that consumed retries.
@@ -2831,28 +2831,41 @@ export async function processStreamingResponse(
2831
2831
  logger.error("[Chat] Stream callback error", errorDetails);
2832
2832
  }
2833
2833
  // The HTTP response is already committed at this point.
2834
- // 1. Emit an explicit assistant message delta so CLI/TUI clients that only
2835
- // listen for choices[].delta.content (e.g. OpenCode, Claude Code, etc.)
2836
- // render the error visibly in the chat instead of appearing blank/frozen.
2837
- // 2. Emit a terminal OpenAI-compatible SSE error event + [DONE].
2834
+ // 1. Emit an explicit assistant message delta ONLY if this was an upstream content moderation /
2835
+ // policy violation (with finish_reason: "content_filter"), so the user understands why the
2836
+ // prompt was blocked.
2837
+ // 2. For network/transient/quota errors, NEVER emit fake assistant content with finish_reason: "stop".
2838
+ // Doing so masks network failures as successful AI answers, preventing client agent retries and
2839
+ // poisoning chat history with error banners.
2840
+ // 3. Emit a terminal OpenAI-compatible SSE error event + [DONE].
2838
2841
  try {
2839
- const userFriendlyNotice = `\n\n⚠️ **[Qwen Security / Erro]** ${err.message || "A resposta foi interrompida pelo Qwen."}\n\n`;
2840
- const errorDelta = {
2841
- id: completionId,
2842
- object: "chat.completion.chunk",
2843
- created: Math.floor(Date.now() / 1000),
2844
- model: body.model,
2845
- choices: [
2846
- {
2847
- index: 0,
2848
- delta: { content: userFriendlyNotice },
2849
- logprobs: null,
2850
- finish_reason: "stop",
2851
- },
2852
- ],
2853
- };
2842
+ const isNetwork = isNetworkLikeError(err);
2843
+ const isModeration =
2844
+ normalizedErrorCode === "data_inspection_failed" ||
2845
+ normalizedErrorCode === "fail_sys_user_validate" ||
2846
+ err.message?.toLowerCase().includes("content moderation") ||
2847
+ err.message?.toLowerCase().includes("security verification");
2848
+
2849
+ if (!isNetwork && isModeration) {
2850
+ const userFriendlyNotice = `\n\n⚠️ **[Qwen Moderação / Segurança]** ${err.message || "A resposta foi interrompida pelas políticas de segurança do Qwen."}\n\n`;
2851
+ const errorDelta = {
2852
+ id: completionId,
2853
+ object: "chat.completion.chunk",
2854
+ created: Math.floor(Date.now() / 1000),
2855
+ model: body.model,
2856
+ choices: [
2857
+ {
2858
+ index: 0,
2859
+ delta: { content: userFriendlyNotice },
2860
+ logprobs: null,
2861
+ finish_reason: "content_filter",
2862
+ },
2863
+ ],
2864
+ };
2865
+
2866
+ await errorStream.write(`data: ${JSON.stringify(errorDelta)}\n\n`);
2867
+ }
2854
2868
 
2855
- await errorStream.write(`data: ${JSON.stringify(errorDelta)}\n\n`);
2856
2869
  await errorStream.write(
2857
2870
  `data: ${JSON.stringify({
2858
2871
  error: {
@@ -1529,6 +1529,14 @@ export async function initPlaywrightForAccount(
1529
1529
  return;
1530
1530
  }
1531
1531
 
1532
+ let resolveInit!: () => void;
1533
+ let rejectInit!: (err: any) => void;
1534
+ const inFlightDeferred = new Promise<void>((res, rej) => {
1535
+ resolveInit = res;
1536
+ rejectInit = rej;
1537
+ });
1538
+ inFlightAccountInits.set(account.id, inFlightDeferred);
1539
+
1532
1540
  const initPromise = (async () => {
1533
1541
  const release = await acquireAccountMutex(
1534
1542
  account.id,
@@ -1536,10 +1544,10 @@ export async function initPlaywrightForAccount(
1536
1544
  Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, 120_000),
1537
1545
  );
1538
1546
  try {
1539
- // Double-check after acquiring lock
1540
- if (accountPages.has(account.id)) {
1541
- return;
1542
- }
1547
+ // Double-check after acquiring lock
1548
+ if (accountPages.has(account.id)) {
1549
+ return;
1550
+ }
1543
1551
 
1544
1552
  // If a context limit is configured, make room by closing idle contexts.
1545
1553
  await evictIdlePlaywrightContextsToLimit().catch(() => {});
@@ -1866,9 +1874,12 @@ export async function initPlaywrightForAccount(
1866
1874
  }
1867
1875
  })();
1868
1876
 
1869
- inFlightAccountInits.set(account.id, initPromise);
1870
1877
  try {
1871
1878
  await initPromise;
1879
+ resolveInit();
1880
+ } catch (error) {
1881
+ rejectInit(error);
1882
+ throw error;
1872
1883
  } finally {
1873
1884
  inFlightAccountInits.delete(account.id);
1874
1885
  }