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
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
inspectClientSyncStatus,
|
|
13
13
|
} from "../../sync/index.ts";
|
|
14
14
|
import type { SyncClientName } from "../../sync/types.ts";
|
|
15
|
-
import { fetchLiveModels } from "../proxy-client.ts";
|
|
15
|
+
import { fetchLiveModels, DEFAULT_FALLBACK_MODELS } from "../proxy-client.ts";
|
|
16
16
|
|
|
17
17
|
interface ClientOption {
|
|
18
18
|
id: SyncClientName;
|
|
@@ -30,18 +30,17 @@ export class SyncView implements TuiView {
|
|
|
30
30
|
private clients: ClientOption[] = [];
|
|
31
31
|
private selectedRowIndex = 0; // 0..9 for clients, 10 for model, 11 for scope, 12 for sync, 13 for restore
|
|
32
32
|
private hoveredActionRow: number | null = null;
|
|
33
|
-
private availableModels = [
|
|
34
|
-
"qwen3.8-max",
|
|
35
|
-
"qwen3.7-plus",
|
|
36
|
-
"qwen3.7-max",
|
|
37
|
-
"z-image-turbo",
|
|
38
|
-
"qwen-image-3.0-pro",
|
|
39
|
-
"wan3.0-video",
|
|
40
|
-
];
|
|
33
|
+
private availableModels = [...DEFAULT_FALLBACK_MODELS];
|
|
41
34
|
private modelIndex = 0;
|
|
42
35
|
private syncAllModels = true;
|
|
43
36
|
private actionLog: string[] = [];
|
|
44
37
|
private lastLeftW = 46;
|
|
38
|
+
private lastClientStartRow = 8;
|
|
39
|
+
private lastClientEndRow = 17;
|
|
40
|
+
private lastModelRow = 20;
|
|
41
|
+
private lastScopeRow = 21;
|
|
42
|
+
private lastSyncRow = 24;
|
|
43
|
+
private lastRestoreRow = 25;
|
|
45
44
|
constructor() {
|
|
46
45
|
this.detectClients();
|
|
47
46
|
}
|
|
@@ -50,11 +49,14 @@ export class SyncView implements TuiView {
|
|
|
50
49
|
this.detectClients();
|
|
51
50
|
void this.refreshModels();
|
|
52
51
|
}
|
|
53
|
-
|
|
52
|
+
public async refreshModels(): Promise<void> {
|
|
54
53
|
try {
|
|
55
54
|
const live = await fetchLiveModels();
|
|
56
55
|
if (live && live.length > 0) {
|
|
56
|
+
const current = this.availableModels[this.modelIndex];
|
|
57
57
|
this.availableModels = live;
|
|
58
|
+
const foundIdx = this.availableModels.indexOf(current);
|
|
59
|
+
this.modelIndex = foundIdx !== -1 ? foundIdx : 0;
|
|
58
60
|
}
|
|
59
61
|
} catch {}
|
|
60
62
|
}
|
|
@@ -103,27 +105,62 @@ export class SyncView implements TuiView {
|
|
|
103
105
|
const { row, col } = key.mouse;
|
|
104
106
|
const leftW = this.lastLeftW || 46;
|
|
105
107
|
if (col >= 2 && col <= leftW - 1) {
|
|
106
|
-
if (row >=
|
|
107
|
-
const targetRow = row -
|
|
108
|
+
if (row >= this.lastClientStartRow && row <= this.lastClientEndRow) {
|
|
109
|
+
const targetRow = row - this.lastClientStartRow;
|
|
110
|
+
let changed = false;
|
|
108
111
|
if (this.selectedRowIndex !== targetRow) {
|
|
109
112
|
this.selectedRowIndex = targetRow;
|
|
110
|
-
|
|
113
|
+
changed = true;
|
|
114
|
+
}
|
|
115
|
+
if (this.hoveredActionRow !== null) {
|
|
116
|
+
this.hoveredActionRow = null;
|
|
117
|
+
changed = true;
|
|
111
118
|
}
|
|
112
|
-
|
|
119
|
+
if (changed) return true;
|
|
120
|
+
} else if (row === this.lastModelRow) {
|
|
121
|
+
let changed = false;
|
|
113
122
|
if (this.selectedRowIndex !== 10) {
|
|
114
123
|
this.selectedRowIndex = 10;
|
|
115
|
-
|
|
124
|
+
changed = true;
|
|
116
125
|
}
|
|
117
|
-
|
|
126
|
+
if (this.hoveredActionRow !== null) {
|
|
127
|
+
this.hoveredActionRow = null;
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
if (changed) return true;
|
|
131
|
+
} else if (row === this.lastScopeRow) {
|
|
132
|
+
let changed = false;
|
|
118
133
|
if (this.selectedRowIndex !== 11) {
|
|
119
134
|
this.selectedRowIndex = 11;
|
|
120
|
-
|
|
135
|
+
changed = true;
|
|
121
136
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
if (this.hoveredActionRow !== null) {
|
|
138
|
+
this.hoveredActionRow = null;
|
|
139
|
+
changed = true;
|
|
140
|
+
}
|
|
141
|
+
if (changed) return true;
|
|
142
|
+
} else if (row === this.lastSyncRow) {
|
|
143
|
+
let changed = false;
|
|
144
|
+
if (this.selectedRowIndex !== 12) {
|
|
145
|
+
this.selectedRowIndex = 12;
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
if (this.hoveredActionRow !== this.lastSyncRow) {
|
|
149
|
+
this.hoveredActionRow = this.lastSyncRow;
|
|
150
|
+
changed = true;
|
|
151
|
+
}
|
|
152
|
+
if (changed) return true;
|
|
153
|
+
} else if (row === this.lastRestoreRow) {
|
|
154
|
+
let changed = false;
|
|
155
|
+
if (this.selectedRowIndex !== 13) {
|
|
156
|
+
this.selectedRowIndex = 13;
|
|
157
|
+
changed = true;
|
|
126
158
|
}
|
|
159
|
+
if (this.hoveredActionRow !== this.lastRestoreRow) {
|
|
160
|
+
this.hoveredActionRow = this.lastRestoreRow;
|
|
161
|
+
changed = true;
|
|
162
|
+
}
|
|
163
|
+
if (changed) return true;
|
|
127
164
|
} else if (this.hoveredActionRow !== null) {
|
|
128
165
|
this.hoveredActionRow = null;
|
|
129
166
|
return true;
|
|
@@ -140,35 +177,40 @@ export class SyncView implements TuiView {
|
|
|
140
177
|
const leftW = this.lastLeftW || 46;
|
|
141
178
|
if (col >= 2 && col <= leftW - 1) {
|
|
142
179
|
// Rows 8..17: Toggle client
|
|
143
|
-
if (row >=
|
|
144
|
-
const client = this.clients[row -
|
|
180
|
+
if (row >= this.lastClientStartRow && row <= this.lastClientEndRow) {
|
|
181
|
+
const client = this.clients[row - this.lastClientStartRow];
|
|
145
182
|
if (client) {
|
|
146
183
|
client.selected = !client.selected;
|
|
147
|
-
this.selectedRowIndex = row -
|
|
184
|
+
this.selectedRowIndex = row - this.lastClientStartRow;
|
|
185
|
+
this.hoveredActionRow = null;
|
|
148
186
|
return true;
|
|
149
187
|
}
|
|
150
188
|
}
|
|
151
189
|
// Model selector
|
|
152
|
-
if (row ===
|
|
190
|
+
if (row === this.lastModelRow) {
|
|
153
191
|
this.modelIndex = (this.modelIndex + 1) % this.availableModels.length;
|
|
154
192
|
this.selectedRowIndex = 10;
|
|
193
|
+
this.hoveredActionRow = null;
|
|
155
194
|
return true;
|
|
156
195
|
}
|
|
157
196
|
// Scope selector
|
|
158
|
-
if (row ===
|
|
197
|
+
if (row === this.lastScopeRow) {
|
|
159
198
|
this.syncAllModels = !this.syncAllModels;
|
|
160
199
|
this.selectedRowIndex = 11;
|
|
200
|
+
this.hoveredActionRow = null;
|
|
161
201
|
return true;
|
|
162
202
|
}
|
|
163
203
|
// Sincronizar button
|
|
164
|
-
if (row ===
|
|
204
|
+
if (row === this.lastSyncRow) {
|
|
165
205
|
this.selectedRowIndex = 12;
|
|
206
|
+
this.hoveredActionRow = this.lastSyncRow;
|
|
166
207
|
this.executeSync();
|
|
167
208
|
return true;
|
|
168
209
|
}
|
|
169
210
|
// Restaurar button
|
|
170
|
-
if (row ===
|
|
211
|
+
if (row === this.lastRestoreRow) {
|
|
171
212
|
this.selectedRowIndex = 13;
|
|
213
|
+
this.hoveredActionRow = this.lastRestoreRow;
|
|
172
214
|
this.executeRollback();
|
|
173
215
|
return true;
|
|
174
216
|
}
|
|
@@ -282,9 +324,16 @@ export class SyncView implements TuiView {
|
|
|
282
324
|
}
|
|
283
325
|
|
|
284
326
|
private executeRollback(): void {
|
|
285
|
-
|
|
327
|
+
const selectedTargets = this.clients
|
|
328
|
+
.filter((c) => c.selected)
|
|
329
|
+
.map((c) => c.id);
|
|
330
|
+
|
|
331
|
+
const targetDesc = selectedTargets.length > 0 ? `[${selectedTargets.join(", ")}]` : "todos os clientes";
|
|
332
|
+
this.actionLog.unshift(theme.yellow(`⏳ Restaurando backups anteriores de ${targetDesc}...`));
|
|
286
333
|
try {
|
|
287
|
-
const res = restoreAllClients(
|
|
334
|
+
const res = restoreAllClients({
|
|
335
|
+
targets: selectedTargets.length > 0 ? selectedTargets : undefined,
|
|
336
|
+
});
|
|
288
337
|
this.actionLog.unshift(
|
|
289
338
|
theme.green(`✓ Rollback concluído: ${res.restoredCount} arquivo(s) restaurados com sucesso.`),
|
|
290
339
|
);
|
|
@@ -298,6 +347,12 @@ export class SyncView implements TuiView {
|
|
|
298
347
|
const contentH = Math.max(22, height);
|
|
299
348
|
const leftW = Math.max(48, Math.floor(width * 0.52));
|
|
300
349
|
this.lastLeftW = leftW;
|
|
350
|
+
this.lastClientStartRow = 8;
|
|
351
|
+
this.lastClientEndRow = 8 + this.clients.length - 1;
|
|
352
|
+
this.lastModelRow = this.lastClientEndRow + 3;
|
|
353
|
+
this.lastScopeRow = this.lastModelRow + 1;
|
|
354
|
+
this.lastSyncRow = this.lastScopeRow + 3;
|
|
355
|
+
this.lastRestoreRow = this.lastSyncRow + 1;
|
|
301
356
|
const rightW = Math.max(30, width - leftW - 1);
|
|
302
357
|
|
|
303
358
|
// Left Panel: Options and Selectors
|
|
@@ -351,13 +406,13 @@ export class SyncView implements TuiView {
|
|
|
351
406
|
leftContent.push(` ${theme.bold("Ações:")}`);
|
|
352
407
|
// Row index 12: Sincronizar
|
|
353
408
|
const isSyncFocused = this.selectedRowIndex === 12;
|
|
354
|
-
const isSyncHovered = this.hoveredActionRow ===
|
|
409
|
+
const isSyncHovered = this.hoveredActionRow === this.lastSyncRow;
|
|
355
410
|
const syncLine = ` ${isSyncHovered || isSyncFocused ? theme.bgHover(` ${theme.cyan("[ Enter ] Sincronizar")} `) : `${theme.cyan("[ Enter ]")} Sincronizar`}`;
|
|
356
411
|
leftContent.push(syncLine);
|
|
357
412
|
|
|
358
413
|
// Row index 13: Restaurar
|
|
359
414
|
const isRestoreFocused = this.selectedRowIndex === 13;
|
|
360
|
-
const isRestoreHovered = this.hoveredActionRow ===
|
|
415
|
+
const isRestoreHovered = this.hoveredActionRow === this.lastRestoreRow;
|
|
361
416
|
const restoreLine = ` ${isRestoreHovered || isRestoreFocused ? theme.bgHover(` ${theme.yellow("[ R ] Restaurar")} `) : `${theme.yellow("[ R ]")} Restaurar`}`;
|
|
362
417
|
leftContent.push(restoreLine);
|
|
363
418
|
|
package/src/update-cli.ts
CHANGED
|
@@ -1,161 +1,17 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const packageRoot = path.resolve(__dirname, "..");
|
|
8
|
-
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
9
|
-
|
|
10
1
|
export type PackageManager = "bun" | "pnpm" | "yarn" | "npm";
|
|
11
2
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const execPath = process.execPath.toLowerCase();
|
|
20
|
-
if (execPath.includes("bun")) return "bun";
|
|
21
|
-
|
|
22
|
-
// Check if installation path contains pnpm / bun / yarn markers
|
|
23
|
-
const currentPath = packageRoot.toLowerCase();
|
|
24
|
-
if (currentPath.includes(".pnpm") || currentPath.includes("pnpm")) return "pnpm";
|
|
25
|
-
if (currentPath.includes(".bun") || currentPath.includes("bun")) return "bun";
|
|
26
|
-
if (currentPath.includes("yarn")) return "yarn";
|
|
27
|
-
|
|
28
|
-
return "npm";
|
|
29
|
-
}
|
|
30
|
-
export function getUpdateArgs(
|
|
31
|
-
pm: PackageManager,
|
|
32
|
-
packageName: string,
|
|
33
|
-
targetVersion?: string,
|
|
34
|
-
): { cmd: string; args: string[] } {
|
|
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: string, latest: string): boolean {
|
|
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: string): Promise<string> {
|
|
72
|
-
// 1. Direct fast HTTP query to registry.npmjs.org (bypasses child process & Windows npm.cmd cold start)
|
|
73
|
-
try {
|
|
74
|
-
const controller = new AbortController();
|
|
75
|
-
const timer = setTimeout(() => controller.abort(), 6000);
|
|
76
|
-
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
77
|
-
headers: { Accept: "application/json" },
|
|
78
|
-
signal: controller.signal,
|
|
79
|
-
});
|
|
80
|
-
clearTimeout(timer);
|
|
81
|
-
if (res.ok) {
|
|
82
|
-
const data = (await res.json()) as { version?: string };
|
|
83
|
-
if (data.version && typeof data.version === "string") {
|
|
84
|
-
return data.version.trim();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
} catch {}
|
|
88
|
-
|
|
89
|
-
// 2. Fallback: npm view via CLI with a generous 30s timeout and clean semver extraction
|
|
90
|
-
try {
|
|
91
|
-
const fullCmd = `npm view ${packageName} version`;
|
|
92
|
-
const res = spawnSync(fullCmd, {
|
|
93
|
-
encoding: "utf-8",
|
|
94
|
-
shell: true,
|
|
95
|
-
timeout: 30000,
|
|
96
|
-
});
|
|
97
|
-
if (res.stdout) {
|
|
98
|
-
const lines = res.stdout.trim().split(/\r?\n/);
|
|
99
|
-
for (const line of lines.reverse()) {
|
|
100
|
-
const cleaned = line.trim().replace(/^v/i, "");
|
|
101
|
-
if (/^\d+\.\d+\.\d+/.test(cleaned)) {
|
|
102
|
-
return cleaned;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
} catch {}
|
|
3
|
+
export {
|
|
4
|
+
detectPackageManager,
|
|
5
|
+
getUpdateArgs,
|
|
6
|
+
isNewerVersion,
|
|
7
|
+
fetchLatestNpmVersion,
|
|
8
|
+
runUpdateCommand,
|
|
9
|
+
} from "../bin/update.js";
|
|
107
10
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
export async function runUpdateCommand(): Promise<void> {
|
|
112
|
-
let pkg: any = { name: "qwenproxy-cli", version: "1.0.0" };
|
|
113
|
-
try {
|
|
114
|
-
pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
115
|
-
} catch {}
|
|
116
|
-
|
|
117
|
-
const currentVersion = pkg.version || "1.0.0";
|
|
118
|
-
const packageName = pkg.name || "qwenproxy-cli";
|
|
119
|
-
const pm = detectPackageManager();
|
|
120
|
-
|
|
121
|
-
console.log(`\n📦 [QwenProxy] Versão local instalada: v${currentVersion}`);
|
|
122
|
-
console.log(`⚙️ [QwenProxy] Gerenciador de pacotes detectado: ${pm}`);
|
|
123
|
-
console.log(`🔍 [QwenProxy] Verificando se há novas versões de ${packageName} no npm registry...`);
|
|
124
|
-
|
|
125
|
-
const latestVersion = await fetchLatestNpmVersion(packageName);
|
|
126
|
-
|
|
127
|
-
if (!latestVersion) {
|
|
128
|
-
console.warn("⚠️ [QwenProxy] Não foi possível consultar o registro online.");
|
|
129
|
-
const manual = getUpdateArgs(pm, packageName);
|
|
130
|
-
console.log(`👉 Você pode forçar a atualização manualmente com:\n ${manual.cmd} ${manual.args.join(" ")}\n`);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
console.log(`🌐 [QwenProxy] Versão mais recente disponível: v${latestVersion}`);
|
|
135
|
-
|
|
136
|
-
if (!isNewerVersion(currentVersion, latestVersion)) {
|
|
137
|
-
console.log(`\n✨ Você já está utilizando a versão mais recente (v${currentVersion})!\n`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
console.log(`\n🚀 Nova versão disponível: v${currentVersion} ➔ v${latestVersion}`);
|
|
142
|
-
const { cmd, args } = getUpdateArgs(pm, packageName, latestVersion);
|
|
143
|
-
console.log(`⏳ Atualizando globalmente via ${pm} (${cmd} ${args.join(" ")})...`);
|
|
144
|
-
const fullUpdateCmd = `${cmd} ${args.join(" ")}`;
|
|
145
|
-
const updateProc = spawnSync(fullUpdateCmd, {
|
|
146
|
-
stdio: "inherit",
|
|
147
|
-
shell: true,
|
|
148
|
-
});
|
|
149
|
-
if (updateProc.status === 0) {
|
|
150
|
-
console.log(`\n✅ [QwenProxy] Atualizado com sucesso para a versão v${latestVersion}!`);
|
|
151
|
-
console.log("👉 Digite 'qpx' para iniciar a nova versão.\n");
|
|
152
|
-
} else {
|
|
153
|
-
console.error(`\n❌ [QwenProxy] Falha ao atualizar automaticamente com ${cmd}.`);
|
|
154
|
-
console.log(`👉 Tente executar manualmente:\n ${cmd} ${args.join(" ")}\n`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
11
|
+
import { runUpdateCommand } from "../bin/update.js";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
157
14
|
|
|
158
|
-
// Execute if run directly via CLI runner, not when imported in unit tests
|
|
159
15
|
const isDirectRun =
|
|
160
16
|
Boolean(process.argv[1]) &&
|
|
161
17
|
(fileURLToPath(import.meta.url) === path.resolve(process.argv[1]) ||
|