@pedrofariasx/qwenproxy 1.6.0 → 1.6.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pedrofariasx/qwenproxy",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Local OpenAI-compatible proxy API that routes requests to Qwen (chat.qwen.ai) via Playwright browser automation.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -74,9 +74,8 @@ export function listAccounts(): QwenAccount[] {
74
74
  }
75
75
 
76
76
  export function getAccountCredentials(id: string): QwenAccount | undefined {
77
- const db = getDatabase()
78
- const row = db.prepare('SELECT id, email, password, cooldown_until, cooldown_reason FROM accounts WHERE id = ?').get(id)
79
- return row as QwenAccount | undefined
77
+ const cached = getCachedAccounts()
78
+ return cached.find(a => a.id === id)
80
79
  }
81
80
 
82
81
  export function updateAccountCooldown(id: string, cooldownUntil: number, reason: string | null): void {
@@ -566,12 +566,12 @@ export async function chatCompletions(c: Context) {
566
566
  const createdTimestamp = Math.floor(Date.now() / 1000);
567
567
 
568
568
  const fastWriteContent = (content: string) => {
569
- const escaped = content.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
569
+ const escaped = JSON.stringify(content).slice(1, -1);
570
570
  streamWriter.write(`data: {"id":"${completionId}","object":"chat.completion.chunk","created":${createdTimestamp},"model":"${body.model}","choices":[{"index":0,"delta":{"content":"${escaped}"},"logprobs":null,"finish_reason":null}]}\n\n`);
571
571
  };
572
572
 
573
573
  const fastWriteReasoning = (content: string) => {
574
- const escaped = content.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
574
+ const escaped = JSON.stringify(content).slice(1, -1);
575
575
  streamWriter.write(`data: {"id":"${completionId}","object":"chat.completion.chunk","created":${createdTimestamp},"model":"${body.model}","choices":[{"index":0,"delta":{"reasoning_content":"${escaped}"},"logprobs":null,"finish_reason":null}]}\n\n`);
576
576
  };
577
577
 
@@ -562,21 +562,23 @@ export async function getGuestHeaders(): Promise<Record<string, string>> {
562
562
  await guestPage!.focus(inputSelector);
563
563
  await guestPage!.fill(inputSelector, '');
564
564
  await guestPage!.type(inputSelector, 'a', { delay: 50 });
565
- await sleep(1000);
565
+ await sleep(1500);
566
566
 
567
+ // Try pressing Enter first as it is highly reliable
568
+ await guestPage!.focus(inputSelector);
569
+ await guestPage!.keyboard.press('Enter');
570
+
567
571
  const selectors = ['.message-input-right-button-send .send-button', '.chat-prompt-send-button', 'button.send-button'];
568
- let clicked = false;
569
572
  for (const selector of selectors) {
570
- const btn = await guestPage!.$(selector);
571
- if (btn && await btn.isVisible()) {
572
- await btn.click({ force: true, delay: 50 }).catch(() => {});
573
- clicked = true;
574
- break;
573
+ try {
574
+ const btn = await guestPage!.$(selector);
575
+ if (btn && await btn.isVisible()) {
576
+ await btn.click({ force: true, delay: 50 }).catch(() => {});
577
+ }
578
+ } catch (e) {
579
+ // ignore click errors
575
580
  }
576
581
  }
577
- if (!clicked) {
578
- await guestPage!.keyboard.press('Enter');
579
- }
580
582
  } catch (e) {
581
583
  clearTimeout(timeout);
582
584
  reject(e);
@@ -825,47 +827,45 @@ async function _getQwenHeadersInternal(forceNew = false, accountId?: string): Pr
825
827
  console.log(`[Playwright] Triggering request for ${cacheKey}...`);
826
828
  const inputSelector = 'textarea:visible, [contenteditable="true"]:visible';
827
829
 
828
- await page.focus(inputSelector);
829
- await page.fill(inputSelector, '');
830
- await page.type(inputSelector, 'a', { delay: 100 });
831
- console.log(`[Playwright] Typed char for ${cacheKey}, waiting for UI to update...`);
832
- await sleep(2000);
833
-
834
- const selectors = [
835
- '.message-input-right-button-send .send-button',
836
- '.chat-prompt-send-button',
837
- 'button.send-button'
838
- ];
830
+ try {
831
+ await page.focus(inputSelector);
832
+ await page.fill(inputSelector, '');
833
+ await page.type(inputSelector, 'a', { delay: 50 });
834
+ console.log(`[Playwright] Typed char for ${cacheKey}, waiting for UI to update...`);
835
+ await sleep(1500);
839
836
 
840
- let clicked = false;
841
- for (const selector of selectors) {
842
- try {
843
- const btn = await page.$(selector);
844
- if (btn && await btn.isVisible()) {
845
- console.log(`[Playwright] Attempting click on: ${selector}`);
846
-
847
- await page.evaluate((sel) => {
848
- const element = document.querySelector(sel) as HTMLElement;
849
- if (element) {
850
- element.focus();
851
- element.click();
852
- }
853
- }, selector);
837
+ // Try pressing Enter first on the input field as it is highly reliable
838
+ console.log(`[Playwright] Pressing Enter for ${cacheKey}...`);
839
+ await page.focus(inputSelector);
840
+ await page.keyboard.press('Enter');
854
841
 
855
- await btn.click({ force: true, delay: 50 }).catch(() => {});
842
+ // Also attempt to click the send button in case Enter didn't submit
843
+ const selectors = [
844
+ '.message-input-right-button-send .send-button',
845
+ '.chat-prompt-send-button',
846
+ 'button.send-button'
847
+ ];
856
848
 
857
- clicked = true;
858
- break;
849
+ for (const selector of selectors) {
850
+ try {
851
+ const btn = await page.$(selector);
852
+ if (btn && await btn.isVisible()) {
853
+ console.log(`[Playwright] Also attempting click on: ${selector}`);
854
+ await page.evaluate((sel) => {
855
+ const element = document.querySelector(sel) as HTMLElement;
856
+ if (element) {
857
+ element.focus();
858
+ element.click();
859
+ }
860
+ }, selector);
861
+ await btn.click({ force: true, delay: 50 }).catch(() => {});
862
+ }
863
+ } catch (e) {
864
+ // ignore click errors
859
865
  }
860
- } catch (e) {
861
- console.error(`[Playwright] Error clicking ${selector} for ${cacheKey}:`, e);
862
866
  }
863
- }
864
-
865
- if (!clicked) {
866
- console.log(`[Playwright] No send button found/clicked for ${cacheKey}, fallback to Enter...`);
867
- await page.focus(inputSelector);
868
- await page.keyboard.press('Enter');
867
+ } catch (triggerErr: any) {
868
+ console.error(`[Playwright] Failed to trigger request for ${cacheKey}:`, triggerErr.message);
869
869
  }
870
870
  });
871
871
  });
@@ -86,6 +86,7 @@ const refillPromises: Map<string, Promise<void>> = new Map();
86
86
 
87
87
  const WARM_POOL_SIZE = 10;
88
88
  const WARM_POOL_TTL_MS = 10 * 60 * 1000;
89
+ const WARM_POOL_LOW_WATER = 3;
89
90
 
90
91
  function cleanupStalePool(accountId: string) {
91
92
  const pool = warmPool.get(accountId);
@@ -243,6 +244,11 @@ export async function getWarmedChat(accountId?: string) {
243
244
  let pool = warmPool.get(key);
244
245
  if (!pool) { pool = []; warmPool.set(key, pool); }
245
246
  cleanupStalePool(key);
247
+
248
+ if (pool.length < WARM_POOL_LOW_WATER && !refillPromises.has(key)) {
249
+ refillPromises.set(key, refillPoolForAccount(key).finally(() => refillPromises.delete(key)));
250
+ }
251
+
246
252
  if (pool.length === 0) {
247
253
  if (!refillPromises.has(key)) {
248
254
  refillPromises.set(key, refillPoolForAccount(key).finally(() => refillPromises.delete(key)));