qwenproxy-cli 1.1.0 → 1.2.1
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/README.es.md +1 -3
- package/README.md +1 -3
- package/README.pt-BR.md +2 -4
- package/bin/qwenproxy.js +7 -3
- package/bin/update.d.ts +11 -0
- package/bin/update.js +155 -0
- package/package.json +3 -3
- package/src/api/models.ts +18 -2
- package/src/core/config.ts +1 -1
- package/src/core/model-alias.ts +17 -0
- package/src/reset-cooldowns.ts +5 -1
- package/src/services/captcha-coordinator.ts +1 -1
- package/src/services/playwright.ts +22 -11
- package/src/services/qwen.ts +58 -22
- package/src/sync/claude-code.ts +41 -4
- package/src/sync/cline.ts +34 -4
- package/src/sync/codex.ts +29 -4
- package/src/sync/index.ts +98 -70
- package/src/sync/omp.ts +23 -4
- package/src/sync/opencode.ts +28 -4
- package/src/sync/utils.ts +32 -3
- package/src/sync/zed.ts +28 -4
- package/src/sync-clients.ts +3 -4
- package/src/tools/parser.ts +16 -11
- package/src/tui/app.ts +19 -1
- package/src/tui/proxy-client.ts +17 -14
- package/src/tui/theme.ts +1 -0
- package/src/tui/views/chat-view.ts +97 -61
- package/src/tui/views/status-view.ts +68 -15
- package/src/tui/views/sync-view.ts +87 -32
- package/src/update-cli.ts +10 -154
package/README.es.md
CHANGED
|
@@ -164,9 +164,7 @@ El sincronizador configura de forma transparente:
|
|
|
164
164
|
- **OpenAI Codex CLI** (`~/.codex/config.toml`) — Protocolo nativo Responses (`/v1/responses`).
|
|
165
165
|
- **OpenCode** (`~/.config/opencode/opencode.jsonc`) — Proveedor compatible con OpenAI.
|
|
166
166
|
- **Cline, OMP, Zed, Kilo Code y Hermes Agent**.
|
|
167
|
-
|
|
168
|
-
> **Consejo de Restauración:** Puedes restaurar las configuraciones originales en cualquier momento ejecutando `qpx sync -- --restore`.
|
|
169
|
-
|
|
167
|
+
> **Consejo de Restauración:** Puedes restaurar las configuraciones originales en cualquier momento ejecutando `qpx sync --restore` (o `npm run sync:restore`).
|
|
170
168
|
### 5. ¡Listo para Programar!
|
|
171
169
|
|
|
172
170
|
Ejecuta tu agente favorito con total normalidad:
|
package/README.md
CHANGED
|
@@ -164,9 +164,7 @@ The synchronizer configures:
|
|
|
164
164
|
- **OpenAI Codex CLI** (`~/.codex/config.toml`) — Native Responses protocol (`/v1/responses`).
|
|
165
165
|
- **OpenCode** (`~/.config/opencode/opencode.jsonc`) — OpenAI-compatible provider.
|
|
166
166
|
- **Cline, OMP, Zed, Kilo Code, and Hermes Agent**.
|
|
167
|
-
|
|
168
|
-
> **Rollback tip:** Restore previous client configuration backups anytime with `qpx sync -- --restore`.
|
|
169
|
-
|
|
167
|
+
> **Rollback tip:** Restore previous client configuration backups anytime with `qpx sync --restore` (or `npm run sync:restore`).
|
|
170
168
|
### 5. Start Coding!
|
|
171
169
|
|
|
172
170
|
Launch your favorite coding tool as you normally would:
|
package/README.pt-BR.md
CHANGED
|
@@ -321,9 +321,7 @@ O sincronizador detecta e configura automaticamente:
|
|
|
321
321
|
- **OpenAI Codex CLI** (`~/.codex/config.toml`) — Usa o protocolo nativo Responses (`/v1/responses`).
|
|
322
322
|
- **OpenCode** (`~/.config/opencode/opencode.jsonc`) — Configura provider OpenAI-compatible.
|
|
323
323
|
- **Cline, OMP, Zed, Kilo Code e Hermes Agent**.
|
|
324
|
-
|
|
325
|
-
> **Dica de Rollback:** Se quiser desfazer a configuração e restaurar os arquivos originais a qualquer momento, execute `qpx sync -- --restore`.
|
|
326
|
-
|
|
324
|
+
> **Dica de Rollback:** Se quiser desfazer a configuração e restaurar os arquivos originais a qualquer momento, execute `qpx sync --restore` (ou `npm run sync:restore`).
|
|
327
325
|
### 5. Pronto para Trabalhar!
|
|
328
326
|
|
|
329
327
|
Agora basta abrir o seu agente favorito normalmente:
|
|
@@ -501,7 +499,7 @@ npm run typecheck # tipos
|
|
|
501
499
|
| `DELETE_ALL_CHATS_ON_SHUTDOWN` | `false` | Limpa chats no shutdown |
|
|
502
500
|
| `QWEN_PERSONALIZATION_FROM_REQUEST` | `true` | Envia system + tools via `/settings/personalization` |
|
|
503
501
|
| `QWEN_PERSONALIZATION_VERIFY_GET` | `true` | Confirma personalization com GET |
|
|
504
|
-
| `QWEN_MAX_PERSONALIZATION_BYTES` | `
|
|
502
|
+
| `QWEN_MAX_PERSONALIZATION_BYTES` | `500000` | Teto UTF-8 para personalization por request (500KB); acomoda centenas de MCP tools com folga |
|
|
505
503
|
| `QWEN_CHAT_POOL_SIZE` | `1` | Warm pool de chats por modelo; fica desativado quando personalization por request está ativa |
|
|
506
504
|
| `QWEN_CHAT_POOL_MODELS` | `qwen3.7-plus` | Modelos aquecidos no warm pool |
|
|
507
505
|
| `QWEN_CHAT_MODE` | `thread` | Modo de conversa padrão: `thread`, `thread-temp`, `stateless` ou `stateless-temp`. Override dinâmico via TUI (`M`/`F4`), HTTP (`/v1/chat/mode`) ou header `X-QwenProxy-Chat-Mode`. |
|
package/bin/qwenproxy.js
CHANGED
|
@@ -67,6 +67,11 @@ Options:
|
|
|
67
67
|
`);
|
|
68
68
|
process.exit(0);
|
|
69
69
|
}
|
|
70
|
+
// 2.5 Pure Node update execution (bypasses tsx and esbuild.exe locks)
|
|
71
|
+
if (firstArg === "update") {
|
|
72
|
+
const { runUpdateCommand } = await import("./update.js");
|
|
73
|
+
await runUpdateCommand();
|
|
74
|
+
} else {
|
|
70
75
|
|
|
71
76
|
// 3. Command dispatcher
|
|
72
77
|
let scriptFile = "src/index.ts";
|
|
@@ -93,12 +98,10 @@ if (firstArg === "start" || rawArgs.includes("--server")) {
|
|
|
93
98
|
} else if (firstArg === "purge") {
|
|
94
99
|
scriptFile = "src/delete-chats.ts";
|
|
95
100
|
scriptArgs = rawArgs.slice(1);
|
|
96
|
-
} else if (firstArg === "update") {
|
|
97
|
-
scriptFile = "src/update-cli.ts";
|
|
98
|
-
scriptArgs = rawArgs.slice(1);
|
|
99
101
|
} else if (firstArg === "login") {
|
|
100
102
|
scriptFile = "src/login.ts";
|
|
101
103
|
scriptArgs = rawArgs.slice(1);
|
|
104
|
+
} else {
|
|
102
105
|
// Pass through remaining options to index.ts with default TUI flag
|
|
103
106
|
scriptFile = "src/index.ts";
|
|
104
107
|
scriptArgs = ["--tui", ...rawArgs];
|
|
@@ -228,3 +231,4 @@ process.on("SIGTERM", () => {
|
|
|
228
231
|
});
|
|
229
232
|
|
|
230
233
|
process.on("exit", restoreTerminal);
|
|
234
|
+
}
|
package/bin/update.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PackageManager = "bun" | "pnpm" | "yarn" | "npm";
|
|
2
|
+
|
|
3
|
+
export function detectPackageManager(): PackageManager;
|
|
4
|
+
export function getUpdateArgs(
|
|
5
|
+
pm: PackageManager,
|
|
6
|
+
packageName: string,
|
|
7
|
+
targetVersion?: string,
|
|
8
|
+
): { cmd: string; args: string[] };
|
|
9
|
+
export function isNewerVersion(current: string, latest: string): boolean;
|
|
10
|
+
export function fetchLatestNpmVersion(packageName: string): Promise<string>;
|
|
11
|
+
export function runUpdateCommand(): Promise<void>;
|
package/bin/update.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QwenProxy - Fast, Pure-Node CLI Self-Updater
|
|
3
|
+
*
|
|
4
|
+
* Runs natively in Node.js without compiling via tsx or spawning esbuild.exe,
|
|
5
|
+
* ensuring zero file locks on native binaries during global npm/pnpm/bun updates on Windows.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
15
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
16
|
+
|
|
17
|
+
export function detectPackageManager() {
|
|
18
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
19
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
20
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
21
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
22
|
+
|
|
23
|
+
const execPath = process.execPath.toLowerCase();
|
|
24
|
+
if (execPath.includes("bun")) return "bun";
|
|
25
|
+
|
|
26
|
+
const currentPath = packageRoot.toLowerCase();
|
|
27
|
+
if (currentPath.includes(".pnpm") || currentPath.includes("pnpm")) return "pnpm";
|
|
28
|
+
if (currentPath.includes(".bun") || currentPath.includes("bun")) return "bun";
|
|
29
|
+
if (currentPath.includes("yarn")) return "yarn";
|
|
30
|
+
|
|
31
|
+
return "npm";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getUpdateArgs(pm, packageName, targetVersion) {
|
|
35
|
+
const versionTag = targetVersion ? `@${targetVersion}` : "@latest";
|
|
36
|
+
switch (pm) {
|
|
37
|
+
case "bun":
|
|
38
|
+
return { cmd: "bun", args: ["add", "-g", `${packageName}${versionTag}`] };
|
|
39
|
+
case "pnpm":
|
|
40
|
+
return {
|
|
41
|
+
cmd: "pnpm",
|
|
42
|
+
args: targetVersion
|
|
43
|
+
? ["add", "-g", `${packageName}@${targetVersion}`]
|
|
44
|
+
: ["update", "-g", packageName],
|
|
45
|
+
};
|
|
46
|
+
case "yarn":
|
|
47
|
+
return {
|
|
48
|
+
cmd: "yarn",
|
|
49
|
+
args: targetVersion
|
|
50
|
+
? ["global", "add", `${packageName}@${targetVersion}`]
|
|
51
|
+
: ["global", "upgrade", packageName],
|
|
52
|
+
};
|
|
53
|
+
case "npm":
|
|
54
|
+
default:
|
|
55
|
+
return { cmd: "npm", args: ["install", "-g", `${packageName}${versionTag}`] };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function isNewerVersion(current, latest) {
|
|
60
|
+
const c = current.replace(/^v/i, "").split(".").map((n) => parseInt(n, 10));
|
|
61
|
+
const l = latest.replace(/^v/i, "").split(".").map((n) => parseInt(n, 10));
|
|
62
|
+
for (let i = 0; i < 3; i++) {
|
|
63
|
+
const cv = c[i] || 0;
|
|
64
|
+
const lv = l[i] || 0;
|
|
65
|
+
if (lv > cv) return true;
|
|
66
|
+
if (lv < cv) return false;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function fetchLatestNpmVersion(packageName) {
|
|
72
|
+
try {
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
const timer = setTimeout(() => controller.abort(), 6000);
|
|
75
|
+
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
76
|
+
headers: { Accept: "application/json" },
|
|
77
|
+
signal: controller.signal,
|
|
78
|
+
});
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
if (res.ok) {
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
if (data.version && typeof data.version === "string") {
|
|
83
|
+
return data.version.trim();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch {}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const fullCmd = `npm view ${packageName} version`;
|
|
90
|
+
const res = spawnSync(fullCmd, {
|
|
91
|
+
encoding: "utf-8",
|
|
92
|
+
shell: true,
|
|
93
|
+
timeout: 30000,
|
|
94
|
+
});
|
|
95
|
+
if (res.stdout) {
|
|
96
|
+
const lines = res.stdout.trim().split(/\r?\n/);
|
|
97
|
+
for (const line of lines.reverse()) {
|
|
98
|
+
const cleaned = line.trim().replace(/^v/i, "");
|
|
99
|
+
if (/^\d+\.\d+\.\d+/.test(cleaned)) {
|
|
100
|
+
return cleaned;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {}
|
|
105
|
+
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function runUpdateCommand() {
|
|
110
|
+
let pkg = { name: "qwenproxy-cli", version: "1.0.0" };
|
|
111
|
+
try {
|
|
112
|
+
pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
113
|
+
} catch {}
|
|
114
|
+
|
|
115
|
+
const currentVersion = pkg.version || "1.0.0";
|
|
116
|
+
const packageName = pkg.name || "qwenproxy-cli";
|
|
117
|
+
const pm = detectPackageManager();
|
|
118
|
+
|
|
119
|
+
console.log(`\n📦 [QwenProxy] Versão local instalada: v${currentVersion}`);
|
|
120
|
+
console.log(`⚙️ [QwenProxy] Gerenciador de pacotes detectado: ${pm}`);
|
|
121
|
+
console.log(`🔍 [QwenProxy] Verificando se há novas versões de ${packageName} no npm registry...`);
|
|
122
|
+
|
|
123
|
+
const latestVersion = await fetchLatestNpmVersion(packageName);
|
|
124
|
+
|
|
125
|
+
if (!latestVersion) {
|
|
126
|
+
console.warn("⚠️ [QwenProxy] Não foi possível consultar o registro online.");
|
|
127
|
+
const manual = getUpdateArgs(pm, packageName);
|
|
128
|
+
console.log(`👉 Você pode forçar a atualização manualmente com:\n ${manual.cmd} ${manual.args.join(" ")}\n`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`🌐 [QwenProxy] Versão mais recente disponível: v${latestVersion}`);
|
|
133
|
+
|
|
134
|
+
if (!isNewerVersion(currentVersion, latestVersion)) {
|
|
135
|
+
console.log(`\n✨ Você já está utilizando a versão mais recente (v${currentVersion})!\n`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log(`\n🚀 Nova versão disponível: v${currentVersion} ➔ v${latestVersion}`);
|
|
140
|
+
const { cmd, args } = getUpdateArgs(pm, packageName, latestVersion);
|
|
141
|
+
console.log(`⏳ Atualizando globalmente via ${pm} (${cmd} ${args.join(" ")})...`);
|
|
142
|
+
const fullUpdateCmd = `${cmd} ${args.join(" ")}`;
|
|
143
|
+
const updateProc = spawnSync(fullUpdateCmd, {
|
|
144
|
+
stdio: "inherit",
|
|
145
|
+
shell: true,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (updateProc.status === 0) {
|
|
149
|
+
console.log(`\n✅ [QwenProxy] Atualizado com sucesso para a versão v${latestVersion}!`);
|
|
150
|
+
console.log("👉 Digite 'qpx' para iniciar a nova versão.\n");
|
|
151
|
+
} else {
|
|
152
|
+
console.error(`\n❌ [QwenProxy] Falha ao atualizar automaticamente com ${cmd}.`);
|
|
153
|
+
console.log(`👉 Tente executar manualmente:\n ${cmd} ${args.join(" ")}\n`);
|
|
154
|
+
}
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
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": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"reset": "npx tsx src/reset-cooldowns.ts",
|
|
27
27
|
"purge": "npx tsx src/delete-chats.ts",
|
|
28
28
|
"test": "npm run test:mock && npm run test:live",
|
|
29
|
-
"test:mock": "tsx --test --test-concurrency=1 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts",
|
|
30
|
-
"test:mock:fast": "tsx --test --test-concurrency=2 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts",
|
|
29
|
+
"test:mock": "tsx --test --test-concurrency=1 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts",
|
|
30
|
+
"test:mock:fast": "tsx --test --test-concurrency=2 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts",
|
|
31
31
|
"test:live": "tsx --test src/tests/concurrentChat.test.ts src/tests/agenticStress.test.ts",
|
|
32
32
|
"benchmark:proxy": "npx tsx src/benchmarks/proxy-baseline.ts",
|
|
33
33
|
"typecheck": "tsc --noEmit"
|
package/src/api/models.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
|
-
import { fetchQwenModels } from "../services/qwen.js";
|
|
3
|
+
import { fetchQwenModels, getAnyCachedQwenModels } from "../services/qwen.js";
|
|
4
|
+
import { DEFAULT_FALLBACK_MODELS } from "../core/model-alias.ts";
|
|
4
5
|
import { loadAccounts } from "../core/accounts.ts";
|
|
5
6
|
import { getAccountCooldownInfo } from "../core/account-manager.ts";
|
|
6
7
|
import { getAccountsByPriority } from "../core/account-priority.ts";
|
|
@@ -141,7 +142,22 @@ async function loadModelsWithVariants(): Promise<{
|
|
|
141
142
|
accountId?: string;
|
|
142
143
|
}> {
|
|
143
144
|
const accountId = getPreferredModelsAccountId();
|
|
144
|
-
|
|
145
|
+
let models: PublicModel[] = [];
|
|
146
|
+
try {
|
|
147
|
+
models = (await fetchQwenModels(accountId)) as unknown as PublicModel[];
|
|
148
|
+
} catch {
|
|
149
|
+
const cached = getAnyCachedQwenModels();
|
|
150
|
+
if (cached && cached.length > 0) {
|
|
151
|
+
models = cached as unknown as PublicModel[];
|
|
152
|
+
} else {
|
|
153
|
+
models = DEFAULT_FALLBACK_MODELS.map((id) => ({
|
|
154
|
+
id,
|
|
155
|
+
object: "model",
|
|
156
|
+
created: MEDIA_MODELS_CREATED_AT,
|
|
157
|
+
owned_by: "qwen",
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
145
161
|
const expanded = expandModelVariants(models, accountId);
|
|
146
162
|
|
|
147
163
|
// Advertise media generation models so clients can discover them via
|
package/src/core/config.ts
CHANGED
|
@@ -167,7 +167,7 @@ const envSchema = z
|
|
|
167
167
|
QWEN_BROWSER_ONLY_FETCH: z.string().default("true"),
|
|
168
168
|
QWEN_MAP_OPENAI_MODELS: z.string().default("true"),
|
|
169
169
|
QWEN_MAX_PROMPT_BYTES: z.string().default("0"),
|
|
170
|
-
QWEN_MAX_PERSONALIZATION_BYTES: z.string().default("
|
|
170
|
+
QWEN_MAX_PERSONALIZATION_BYTES: z.string().default("500000"),
|
|
171
171
|
CONTEXT_METER_ENABLED: z.string().default("true"),
|
|
172
172
|
CONTEXT_METER_WINDOW_TOKENS: z.string().default("0"),
|
|
173
173
|
CONTEXT_METER_REPORT_USAGE: z.string().default("true"),
|
package/src/core/model-alias.ts
CHANGED
|
@@ -6,6 +6,23 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export type ReasoningMode = "auto" | "thinking" | "fast";
|
|
8
8
|
|
|
9
|
+
export const DEFAULT_FALLBACK_MODELS = [
|
|
10
|
+
"qwen3.8-max",
|
|
11
|
+
"qwen3.7-plus",
|
|
12
|
+
"qwen3.8-omni-flash",
|
|
13
|
+
"qwen3.7-max",
|
|
14
|
+
"qwen3.6-plus",
|
|
15
|
+
"qwen3.5-plus",
|
|
16
|
+
"qwen3.5-omni-plus",
|
|
17
|
+
"z-image-turbo",
|
|
18
|
+
"qwen-image-3.0-pro",
|
|
19
|
+
"qwen-image-3.0",
|
|
20
|
+
"wan2.7-image-pro",
|
|
21
|
+
"wan2.7-image",
|
|
22
|
+
"wan3.0-video",
|
|
23
|
+
"wan2.7-t2v",
|
|
24
|
+
"wan2.7-i2v",
|
|
25
|
+
];
|
|
9
26
|
/**
|
|
10
27
|
* Resolução de variantes de raciocínio dos modelos Qwen públicos.
|
|
11
28
|
*
|
package/src/reset-cooldowns.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { clearAllAccountCooldowns } from "./core/account-manager.ts";
|
|
2
2
|
import { loadAccounts } from "./core/accounts.ts";
|
|
3
|
+
import { clearPersonalizationDbCache } from "./services/qwen.ts";
|
|
3
4
|
|
|
4
5
|
function main() {
|
|
5
6
|
const accounts = loadAccounts();
|
|
6
7
|
console.log(`🔍 Checking cooldowns for ${accounts.length} configured account(s)...`);
|
|
7
8
|
const cleared = clearAllAccountCooldowns();
|
|
8
|
-
|
|
9
|
+
const clearedPersonalization = clearPersonalizationDbCache();
|
|
10
|
+
console.log(
|
|
11
|
+
`✅ Cooldowns reset successfully: ${cleared} account(s) cleared. Personalization cache cleared: ${clearedPersonalization} account(s).`,
|
|
12
|
+
);
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
main();
|
|
@@ -2335,19 +2335,30 @@ export async function captureQwenHeaders(
|
|
|
2335
2335
|
1,
|
|
2336
2336
|
Math.min(CHAT_INPUT_ACTION_TIMEOUT_MS, remainingBudgetMs()),
|
|
2337
2337
|
);
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
if (settled || page.isClosed()) return;
|
|
2341
|
-
await page.fill(inputSelector, "", { timeout: inputActionTimeoutMs });
|
|
2338
|
+
let interactionSucceeded = false;
|
|
2339
|
+
for (let interactionTry = 1; interactionTry <= 2; interactionTry++) {
|
|
2342
2340
|
if (settled || page.isClosed()) return;
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2341
|
+
try {
|
|
2342
|
+
await page.focus(inputSelector, { timeout: inputActionTimeoutMs });
|
|
2343
|
+
if (settled || page.isClosed()) return;
|
|
2344
|
+
await page.fill(inputSelector, "", { timeout: inputActionTimeoutMs });
|
|
2345
|
+
if (settled || page.isClosed()) return;
|
|
2346
|
+
await page.type(inputSelector, "a", {
|
|
2347
|
+
delay: 100,
|
|
2348
|
+
timeout: inputActionTimeoutMs,
|
|
2349
|
+
});
|
|
2350
|
+
interactionSucceeded = true;
|
|
2351
|
+
break;
|
|
2352
|
+
} catch {
|
|
2353
|
+
if (settled || page.isClosed()) return;
|
|
2354
|
+
if (interactionTry === 1) {
|
|
2355
|
+
await sleep(300);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
if (!interactionSucceeded) {
|
|
2348
2360
|
// The input detached mid-interaction (challenge overlay, SPA
|
|
2349
|
-
// re-render):
|
|
2350
|
-
// recovers it.
|
|
2361
|
+
// re-render): only a fresh load recovers it.
|
|
2351
2362
|
if (settled || page.isClosed()) return;
|
|
2352
2363
|
console.warn(
|
|
2353
2364
|
`⏱️ [Playwright] Chat input interaction failed for ${accountId} (attempt ${attempt}); reloading`,
|
package/src/services/qwen.ts
CHANGED
|
@@ -440,6 +440,14 @@ const modelsCache = new Map<
|
|
|
440
440
|
{ models: PublicQwenModel[]; fetchedAt: number }
|
|
441
441
|
>();
|
|
442
442
|
|
|
443
|
+
export function getAnyCachedQwenModels(): PublicQwenModel[] | undefined {
|
|
444
|
+
for (const entry of modelsCache.values()) {
|
|
445
|
+
if (entry.models && entry.models.length > 0) {
|
|
446
|
+
return entry.models;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
443
451
|
const nativeToolsDisabled = new Set<string>();
|
|
444
452
|
const disablingNativeToolsInProgress = new Set<string>();
|
|
445
453
|
const lastSyncedPersonalizationHashes = new Map<string, string>();
|
|
@@ -492,6 +500,25 @@ function setPersonalizationHashInDb(accountId: string, hash: string): void {
|
|
|
492
500
|
);
|
|
493
501
|
}
|
|
494
502
|
}
|
|
503
|
+
export function clearPersonalizationDbCache(accountId?: string): number {
|
|
504
|
+
try {
|
|
505
|
+
const db = getDatabase();
|
|
506
|
+
if (accountId) {
|
|
507
|
+
const info = db
|
|
508
|
+
.prepare("DELETE FROM personalization_cache WHERE account_id = ?")
|
|
509
|
+
.run(accountId);
|
|
510
|
+
lastSyncedPersonalizationHashes.delete(accountId);
|
|
511
|
+
activePersonalizationByAccount.delete(accountId);
|
|
512
|
+
return info.changes;
|
|
513
|
+
}
|
|
514
|
+
const info = db.prepare("DELETE FROM personalization_cache").run();
|
|
515
|
+
lastSyncedPersonalizationHashes.clear();
|
|
516
|
+
activePersonalizationByAccount.clear();
|
|
517
|
+
return info.changes;
|
|
518
|
+
} catch {
|
|
519
|
+
return 0;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
495
522
|
|
|
496
523
|
function shortContentHash(value: string): string {
|
|
497
524
|
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
@@ -666,23 +693,14 @@ export function buildQwenSettingsUpdatePayload(
|
|
|
666
693
|
currentSettings: any,
|
|
667
694
|
instruction: string,
|
|
668
695
|
): Record<string, unknown> {
|
|
669
|
-
// The real client (HAR networkv2) POSTs ONLY `{personalization: {...}}` to
|
|
670
|
-
// /api/v2/users/user/settings/update. Live probes confirmed the personalization
|
|
671
|
-
// object accepts the GET-personalization spread + enable_for_new_chat, but the
|
|
672
|
-
// FULL-settings spread this used to send (ui/memory/tools_enabled + every GET
|
|
673
|
-
// field like tts_speaker_v2, code_settings, manage_cookies) is rejected with
|
|
674
|
-
// RequestValidationError. Safe-settings are applied by disableNativeTools as
|
|
675
|
-
// their own combined partial POST (probe-accepted). NOTE: the persistent
|
|
676
|
-
// RequestValidationError that haunted the sync was NOT the payload — it was a
|
|
677
|
-
// missing Content-Type header (attemptPost received the raw getQwenHeaders
|
|
678
|
-
// map); the body was not parsed as a JSON object ("Field '': Input should be
|
|
679
|
-
// a valid dictionary...").
|
|
680
696
|
const currentPersonalization =
|
|
681
697
|
currentSettings?.personalization &&
|
|
682
698
|
typeof currentSettings.personalization === "object"
|
|
683
699
|
? currentSettings.personalization
|
|
684
700
|
: {};
|
|
685
701
|
|
|
702
|
+
const hasInstruction = instruction.trim().length > 0;
|
|
703
|
+
|
|
686
704
|
return {
|
|
687
705
|
personalization: {
|
|
688
706
|
...currentPersonalization,
|
|
@@ -693,7 +711,7 @@ export function buildQwenSettingsUpdatePayload(
|
|
|
693
711
|
: currentPersonalization.description,
|
|
694
712
|
style: null,
|
|
695
713
|
instruction,
|
|
696
|
-
enable_for_new_chat:
|
|
714
|
+
enable_for_new_chat: hasInstruction,
|
|
697
715
|
},
|
|
698
716
|
};
|
|
699
717
|
}
|
|
@@ -1519,7 +1537,12 @@ export async function syncQwenRequestPersonalization(
|
|
|
1519
1537
|
}
|
|
1520
1538
|
|
|
1521
1539
|
// 2. Check DB cache (survives restarts) (skipped on forceSync)
|
|
1522
|
-
|
|
1540
|
+
const isEmptyInstruction = instruction.trim().length === 0;
|
|
1541
|
+
|
|
1542
|
+
// 2. Check DB cache (survives restarts) (skipped on forceSync)
|
|
1543
|
+
// For empty instructions, do not blindly trust DB cache without verifying
|
|
1544
|
+
// because external agents or web sessions might have altered personalization.
|
|
1545
|
+
if (!bypassCache && syncHash && !cachedHash && !isEmptyInstruction) {
|
|
1523
1546
|
const dbHash = getPersonalizationHashFromDb(cacheKey);
|
|
1524
1547
|
if (dbHash === syncHash) {
|
|
1525
1548
|
lastSyncedPersonalizationHashes.set(cacheKey, syncHash);
|
|
@@ -1528,7 +1551,6 @@ export async function syncQwenRequestPersonalization(
|
|
|
1528
1551
|
return true;
|
|
1529
1552
|
}
|
|
1530
1553
|
}
|
|
1531
|
-
|
|
1532
1554
|
let existing = { chars: null, bytes: null, hash: null } as ReturnType<
|
|
1533
1555
|
typeof textSize
|
|
1534
1556
|
>;
|
|
@@ -1544,7 +1566,9 @@ export async function syncQwenRequestPersonalization(
|
|
|
1544
1566
|
);
|
|
1545
1567
|
currentSettings = existingJson?.data ?? null;
|
|
1546
1568
|
payload = buildQwenSettingsUpdatePayload(currentSettings, instruction);
|
|
1547
|
-
|
|
1569
|
+
const existingInstruction = existingJson?.data?.personalization?.instruction;
|
|
1570
|
+
const existingEnabled = existingJson?.data?.personalization?.enable_for_new_chat === true;
|
|
1571
|
+
existing = textSize(existingInstruction);
|
|
1548
1572
|
const existingSafeSettingsApplied =
|
|
1549
1573
|
existingJson?.data?.ui?.largeTextAsFile === false &&
|
|
1550
1574
|
existingJson?.data?.ui?.splitLargeChunks === false &&
|
|
@@ -1554,8 +1578,12 @@ export async function syncQwenRequestPersonalization(
|
|
|
1554
1578
|
existingJson?.data?.memory?.enable_history_memory === false &&
|
|
1555
1579
|
existingJson?.data?.tools_enabled?.web_search === false &&
|
|
1556
1580
|
existingJson?.data?.tools_enabled?.code_interpreter === false;
|
|
1557
|
-
|
|
1558
|
-
|
|
1581
|
+
const isEmptyAndCleared =
|
|
1582
|
+
isEmptyInstruction &&
|
|
1583
|
+
(!existingInstruction || existingInstruction.trim().length === 0) &&
|
|
1584
|
+
!existingEnabled;
|
|
1585
|
+
const isContentMatched = existing.hash !== null && existing.hash === sent.hash;
|
|
1586
|
+
if ((isContentMatched || isEmptyAndCleared) && existingSafeSettingsApplied) {
|
|
1559
1587
|
setPersonalizationHashInDb(cacheKey, syncHash);
|
|
1560
1588
|
rememberActivePersonalization(
|
|
1561
1589
|
cacheKey,
|
|
@@ -1669,6 +1697,7 @@ export async function syncQwenRequestPersonalization(
|
|
|
1669
1697
|
typeof textSize
|
|
1670
1698
|
>;
|
|
1671
1699
|
|
|
1700
|
+
let verifyData: any = null;
|
|
1672
1701
|
if (config.qwen.personalizationVerifyGet) {
|
|
1673
1702
|
const { json: verifyJson } =
|
|
1674
1703
|
await requestQwenPersonalizationInBrowser(
|
|
@@ -1677,11 +1706,18 @@ export async function syncQwenRequestPersonalization(
|
|
|
1677
1706
|
"/api/v2/users/user/settings",
|
|
1678
1707
|
requestHeaders,
|
|
1679
1708
|
);
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const
|
|
1709
|
+
verifyData = verifyJson?.data?.personalization;
|
|
1710
|
+
stored = textSize(verifyData?.instruction);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
const matchReturned =
|
|
1714
|
+
(isEmptyInstruction && (!returnedInstruction || returned.chars === 0)) ||
|
|
1715
|
+
(returned.hash !== null && returned.hash === sent.hash);
|
|
1716
|
+
const matchStored =
|
|
1717
|
+
stored.hash === null
|
|
1718
|
+
? null
|
|
1719
|
+
: (isEmptyInstruction && (!verifyData?.instruction || stored.chars === 0)) ||
|
|
1720
|
+
stored.hash === sent.hash;
|
|
1685
1721
|
const applied = matchReturned || matchStored === true;
|
|
1686
1722
|
if (syncHash && applied) {
|
|
1687
1723
|
lastSyncedPersonalizationHashes.set(cacheKey, syncHash);
|