qwenproxy-cli 1.0.32 → 1.2.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.
- package/README.es.md +267 -0
- package/README.md +174 -783
- package/README.pt-BR.md +1031 -0
- package/bin/qwenproxy.js +7 -3
- package/bin/update.d.ts +11 -0
- package/bin/update.js +155 -0
- package/package.json +4 -4
- package/src/api/models.ts +18 -2
- package/src/api/server.ts +18 -2
- package/src/core/accounts.ts +130 -0
- package/src/core/config.ts +96 -5
- package/src/core/model-alias.ts +17 -0
- package/src/index.ts +3 -0
- package/src/reset-cooldowns.ts +5 -1
- package/src/routes/chat/context.ts +9 -9
- package/src/routes/chat/index.ts +15 -6
- package/src/services/qwen-chat-pool.ts +4 -5
- package/src/services/qwen.ts +60 -27
- 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/tui/app.ts +27 -10
- package/src/tui/index.ts +3 -0
- package/src/tui/proxy-client.ts +20 -16
- package/src/tui/screen.ts +10 -3
- package/src/tui/settings.ts +2 -0
- package/src/tui/theme.ts +2 -0
- package/src/tui/types.ts +1 -0
- package/src/tui/views/accounts-view.ts +343 -23
- package/src/tui/views/chat-view.ts +302 -71
- package/src/tui/views/status-view.ts +99 -15
- package/src/tui/views/sync-view.ts +87 -32
- package/src/update-cli.ts +10 -154
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.0
|
|
3
|
+
"version": "1.2.0",
|
|
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": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"src",
|
|
12
12
|
"!src/tests",
|
|
13
13
|
"!src/benchmarks",
|
|
14
|
-
"README
|
|
14
|
+
"README*.md",
|
|
15
15
|
"LICENSE"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
@@ -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",
|
|
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",
|
|
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/api/server.ts
CHANGED
|
@@ -3,7 +3,8 @@ import net from "node:net";
|
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
import { Hono, type Context } from "hono";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
|
-
import { config } from "../core/config.js";
|
|
6
|
+
import { config, getRuntimeChatMode, setRuntimeChatMode } from "../core/config.js";
|
|
7
|
+
import { saveTuiSettings } from "../tui/settings.ts";
|
|
7
8
|
import { metrics } from "../core/metrics.js";
|
|
8
9
|
import { logger, maskEmail } from "../core/logger.js";
|
|
9
10
|
import { MemoryCache } from "../cache/memory-cache.js";
|
|
@@ -248,7 +249,22 @@ app.post("/v1/upload", uploadFile);
|
|
|
248
249
|
app.post("/v1/images/generations", imagesGenerations);
|
|
249
250
|
app.post("/v1/videos/generations", videosGenerations);
|
|
250
251
|
app.get("/v1/tasks/status/:taskId", videoTaskStatus);
|
|
251
|
-
|
|
252
|
+
app.get("/v1/chat/mode", (c) => {
|
|
253
|
+
const error = verifyApiKey(c);
|
|
254
|
+
if (error) return error;
|
|
255
|
+
return c.json({ mode: getRuntimeChatMode() });
|
|
256
|
+
});
|
|
257
|
+
app.post("/v1/chat/mode", async (c) => {
|
|
258
|
+
const error = verifyApiKey(c);
|
|
259
|
+
if (error) return error;
|
|
260
|
+
const body = (await c.req.json().catch(() => ({}))) as { mode?: string };
|
|
261
|
+
if (!body.mode) {
|
|
262
|
+
return c.json({ error: { message: "Field 'mode' is required" } }, 400);
|
|
263
|
+
}
|
|
264
|
+
const updated = setRuntimeChatMode(body.mode);
|
|
265
|
+
saveTuiSettings({ chat: { mode: updated } });
|
|
266
|
+
return c.json({ success: true, mode: updated });
|
|
267
|
+
});
|
|
252
268
|
// OpenAI Responses API compatible routes
|
|
253
269
|
app.route("", responsesApp);
|
|
254
270
|
app.route("", anthropicApp);
|
package/src/core/accounts.ts
CHANGED
|
@@ -122,7 +122,137 @@ export function invalidateAccountsCache(): void {
|
|
|
122
122
|
accountsCache = null;
|
|
123
123
|
accountsCacheTime = 0;
|
|
124
124
|
}
|
|
125
|
+
export interface BatchAccountEntry {
|
|
126
|
+
email: string;
|
|
127
|
+
password: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function parseBatchAccounts(rawInput: string): {
|
|
131
|
+
entries: BatchAccountEntry[];
|
|
132
|
+
invalid: string[];
|
|
133
|
+
} {
|
|
134
|
+
if (!rawInput || typeof rawInput !== "string") {
|
|
135
|
+
return { entries: [], invalid: [] };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let text = rawInput.trim();
|
|
139
|
+
// Strip optional QWEN_ACCOUNTS= prefix and surrounding quotes
|
|
140
|
+
text = text.replace(/^QWEN_ACCOUNTS\s*=\s*["']?/i, "").replace(/["']?\s*$/i, "");
|
|
141
|
+
|
|
142
|
+
const lines = text.split(/\r?\n/);
|
|
143
|
+
const entries: BatchAccountEntry[] = [];
|
|
144
|
+
const invalid: string[] = [];
|
|
145
|
+
const seenEmails = new Set<string>();
|
|
146
|
+
|
|
147
|
+
for (const rawLine of lines) {
|
|
148
|
+
const line = rawLine.trim();
|
|
149
|
+
if (!line || line.startsWith("#") || line.startsWith("//")) continue;
|
|
150
|
+
|
|
151
|
+
// Check if line contains multiple accounts separated by comma or semicolon
|
|
152
|
+
const segments =
|
|
153
|
+
(line.includes(",") || line.includes(";")) && (line.match(/@/g) || []).length > 1
|
|
154
|
+
? line.split(/[,;]/).map((s) => s.trim()).filter(Boolean)
|
|
155
|
+
: [line];
|
|
156
|
+
|
|
157
|
+
for (const seg of segments) {
|
|
158
|
+
let email = "";
|
|
159
|
+
let password = "";
|
|
160
|
+
|
|
161
|
+
if (seg.includes("---")) {
|
|
162
|
+
const parts = seg.split("---");
|
|
163
|
+
email = parts[0].trim();
|
|
164
|
+
password = parts.slice(1).join("---").trim();
|
|
165
|
+
} else if (seg.includes("\t")) {
|
|
166
|
+
const parts = seg.split("\t");
|
|
167
|
+
email = parts[0].trim();
|
|
168
|
+
password = parts.slice(1).join("\t").trim();
|
|
169
|
+
} else if (seg.includes(" | ")) {
|
|
170
|
+
const parts = seg.split(" | ");
|
|
171
|
+
email = parts[0].trim();
|
|
172
|
+
password = parts.slice(1).join(" | ").trim();
|
|
173
|
+
} else if (seg.includes(":")) {
|
|
174
|
+
const colonIdx = seg.indexOf(":");
|
|
175
|
+
email = seg.slice(0, colonIdx).trim();
|
|
176
|
+
password = seg.slice(colonIdx + 1).trim();
|
|
177
|
+
} else if (seg.includes(",")) {
|
|
178
|
+
const commaIdx = seg.indexOf(",");
|
|
179
|
+
email = seg.slice(0, commaIdx).trim();
|
|
180
|
+
password = seg.slice(commaIdx + 1).trim();
|
|
181
|
+
} else {
|
|
182
|
+
invalid.push(seg);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Validate email format
|
|
187
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
188
|
+
if (!email || !password || !emailRegex.test(email)) {
|
|
189
|
+
invalid.push(seg);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const normalizedEmail = email.toLowerCase();
|
|
194
|
+
if (!seenEmails.has(normalizedEmail)) {
|
|
195
|
+
seenEmails.add(normalizedEmail);
|
|
196
|
+
entries.push({ email, password });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { entries, invalid };
|
|
202
|
+
}
|
|
125
203
|
|
|
204
|
+
export function addAccountsBatch(
|
|
205
|
+
entries: BatchAccountEntry[],
|
|
206
|
+
): { added: QwenAccount[]; skipped: string[]; invalid: string[] } {
|
|
207
|
+
const db = getDatabase();
|
|
208
|
+
const added: QwenAccount[] = [];
|
|
209
|
+
const skipped: string[] = [];
|
|
210
|
+
const invalid: string[] = [];
|
|
211
|
+
|
|
212
|
+
const existingEmails = new Set(
|
|
213
|
+
(db.prepare("SELECT email FROM accounts").all() as Array<{ email: string }>).map((r) =>
|
|
214
|
+
r.email.toLowerCase(),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const insertStmt = db.prepare(
|
|
219
|
+
"INSERT INTO accounts (id, email, password) VALUES (?, ?, ?)",
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const insertBatch = db.transaction((items: BatchAccountEntry[]) => {
|
|
223
|
+
for (const item of items) {
|
|
224
|
+
const email = item.email.trim();
|
|
225
|
+
const password = item.password;
|
|
226
|
+
if (!email || !password) {
|
|
227
|
+
invalid.push(email || "(empty)");
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (existingEmails.has(email.toLowerCase())) {
|
|
232
|
+
skipped.push(email);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const newAccount: QwenAccount = {
|
|
237
|
+
id: crypto.randomUUID(),
|
|
238
|
+
email,
|
|
239
|
+
password,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
insertStmt.run(newAccount.id, newAccount.email, encrypt(newAccount.password));
|
|
243
|
+
existingEmails.add(email.toLowerCase());
|
|
244
|
+
added.push(newAccount);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
insertBatch(entries);
|
|
249
|
+
|
|
250
|
+
if (added.length > 0) {
|
|
251
|
+
invalidateAccountsCache();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { added, skipped, invalid };
|
|
255
|
+
}
|
|
126
256
|
export function addAccount(
|
|
127
257
|
email: string,
|
|
128
258
|
password: string,
|
package/src/core/config.ts
CHANGED
|
@@ -34,7 +34,18 @@ const envSchema = z
|
|
|
34
34
|
// temp chat (chat_mode:"local") for every request and sends the full
|
|
35
35
|
// history inline (OpenAI standard). Temp chats are ephemeral and never
|
|
36
36
|
// appear in the account's chat list (live-probed).
|
|
37
|
-
QWEN_CHAT_MODE: z
|
|
37
|
+
QWEN_CHAT_MODE: z
|
|
38
|
+
.enum([
|
|
39
|
+
"thread",
|
|
40
|
+
"temp",
|
|
41
|
+
"temp-thread",
|
|
42
|
+
"thread-temp",
|
|
43
|
+
"stateless",
|
|
44
|
+
"stateles",
|
|
45
|
+
"stateless-temp",
|
|
46
|
+
"stateles-temp",
|
|
47
|
+
])
|
|
48
|
+
.default("thread"),
|
|
38
49
|
PLAYWRIGHT_HEADLESS: z.string().default("true"),
|
|
39
50
|
PLAYWRIGHT_BROWSER: z
|
|
40
51
|
.enum(["chromium", "chrome", "edge"])
|
|
@@ -156,7 +167,7 @@ const envSchema = z
|
|
|
156
167
|
QWEN_BROWSER_ONLY_FETCH: z.string().default("true"),
|
|
157
168
|
QWEN_MAP_OPENAI_MODELS: z.string().default("true"),
|
|
158
169
|
QWEN_MAX_PROMPT_BYTES: z.string().default("0"),
|
|
159
|
-
QWEN_MAX_PERSONALIZATION_BYTES: z.string().default("
|
|
170
|
+
QWEN_MAX_PERSONALIZATION_BYTES: z.string().default("500000"),
|
|
160
171
|
CONTEXT_METER_ENABLED: z.string().default("true"),
|
|
161
172
|
CONTEXT_METER_WINDOW_TOKENS: z.string().default("0"),
|
|
162
173
|
CONTEXT_METER_REPORT_USAGE: z.string().default("true"),
|
|
@@ -362,7 +373,7 @@ export const config = {
|
|
|
362
373
|
/** When true, all requests (personalization, models, media, chat) route exclusively through the browser page (no direct Node fetch). */
|
|
363
374
|
browserOnlyFetch: env.QWEN_BROWSER_ONLY_FETCH !== "false",
|
|
364
375
|
mapOpenAiModels: env.QWEN_MAP_OPENAI_MODELS !== "false",
|
|
365
|
-
chatMode: env.QWEN_CHAT_MODE,
|
|
376
|
+
chatMode: normalizeChatMode(env.QWEN_CHAT_MODE),
|
|
366
377
|
maxPromptBytes: Math.max(0, parseInt(env.QWEN_MAX_PROMPT_BYTES)),
|
|
367
378
|
maxPersonalizationBytes: Math.max(
|
|
368
379
|
0,
|
|
@@ -388,5 +399,85 @@ export const config = {
|
|
|
388
399
|
|
|
389
400
|
export type Config = typeof config;
|
|
390
401
|
|
|
391
|
-
/**
|
|
392
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Conversation modes:
|
|
404
|
+
* - "thread": thread-native delta (~1KB), saved in Qwen account sidebar (default)
|
|
405
|
+
* - "thread-temp" (alias "temp-thread"): thread-native delta (~1KB), ephemeral local chat (not saved)
|
|
406
|
+
* - "stateless": full history re-sent every turn (OpenAI standard), saved in Qwen account sidebar
|
|
407
|
+
* - "stateless-temp" (alias "temp"): full history re-sent every turn (OpenAI standard), ephemeral local chat (not saved)
|
|
408
|
+
*/
|
|
409
|
+
export type ChatMode =
|
|
410
|
+
| "thread"
|
|
411
|
+
| "thread-temp"
|
|
412
|
+
| "temp-thread"
|
|
413
|
+
| "stateless"
|
|
414
|
+
| "stateles"
|
|
415
|
+
| "stateless-temp"
|
|
416
|
+
| "stateles-temp"
|
|
417
|
+
| "temp";
|
|
418
|
+
|
|
419
|
+
export function isStatelessChatMode(mode?: string | null): boolean {
|
|
420
|
+
if (!mode) return false;
|
|
421
|
+
const m = mode.trim().toLowerCase().replace(/_/g, "-");
|
|
422
|
+
return (
|
|
423
|
+
m === "stateless" ||
|
|
424
|
+
m === "stateles" ||
|
|
425
|
+
m === "stateless-temp" ||
|
|
426
|
+
m === "stateles-temp" ||
|
|
427
|
+
m === "temp"
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function isLocalChatMode(mode?: string | null): boolean {
|
|
432
|
+
if (!mode) return false;
|
|
433
|
+
const m = mode.trim().toLowerCase().replace(/_/g, "-");
|
|
434
|
+
return (
|
|
435
|
+
m === "stateless-temp" ||
|
|
436
|
+
m === "stateles-temp" ||
|
|
437
|
+
m === "temp" ||
|
|
438
|
+
m === "thread-temp" ||
|
|
439
|
+
m === "temp-thread"
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export function normalizeChatMode(
|
|
444
|
+
mode?: string | null,
|
|
445
|
+
): "thread" | "thread-temp" | "stateless" | "stateless-temp" {
|
|
446
|
+
if (!mode) return "thread";
|
|
447
|
+
const m = mode.trim().toLowerCase().replace(/_/g, "-");
|
|
448
|
+
if (m === "stateless-temp" || m === "stateles-temp" || m === "temp") {
|
|
449
|
+
return "stateless-temp";
|
|
450
|
+
}
|
|
451
|
+
if (m === "stateless" || m === "stateles") {
|
|
452
|
+
return "stateless";
|
|
453
|
+
}
|
|
454
|
+
if (m === "thread-temp" || m === "temp-thread") {
|
|
455
|
+
return "thread-temp";
|
|
456
|
+
}
|
|
457
|
+
return "thread";
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export function setRuntimeChatMode(
|
|
461
|
+
mode?: string | null,
|
|
462
|
+
): "thread" | "thread-temp" | "stateless" | "stateless-temp" {
|
|
463
|
+
const normalized = normalizeChatMode(mode);
|
|
464
|
+
(config.qwen as { chatMode: "thread" | "thread-temp" | "stateless" | "stateless-temp" }).chatMode = normalized;
|
|
465
|
+
return normalized;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function getRuntimeChatMode(): "thread" | "thread-temp" | "stateless" | "stateless-temp" {
|
|
469
|
+
return config.qwen.chatMode as "thread" | "thread-temp" | "stateless" | "stateless-temp";
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export function cycleNextChatMode(): "thread" | "thread-temp" | "stateless" | "stateless-temp" {
|
|
473
|
+
const current = getRuntimeChatMode();
|
|
474
|
+
const order: Array<"thread" | "thread-temp" | "stateless-temp" | "stateless"> = [
|
|
475
|
+
"thread",
|
|
476
|
+
"thread-temp",
|
|
477
|
+
"stateless-temp",
|
|
478
|
+
"stateless",
|
|
479
|
+
];
|
|
480
|
+
const idx = order.indexOf(current);
|
|
481
|
+
const next = order[(idx + 1) % order.length];
|
|
482
|
+
return setRuntimeChatMode(next);
|
|
483
|
+
}
|
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/index.ts
CHANGED
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();
|