qwenproxy-cli 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +14 -14
- package/README.md +906 -906
- package/bin/qwenproxy.js +5 -1
- package/package.json +77 -78
- package/src/api/error-classifier.ts +159 -159
- package/src/api/error-helpers.ts +118 -118
- package/src/api/models.ts +261 -261
- package/src/api/server.ts +860 -859
- package/src/cache/memory-cache.ts +385 -385
- package/src/clean-cache.ts +204 -204
- package/src/core/account-concurrency.ts +671 -671
- package/src/core/account-manager.ts +301 -297
- package/src/core/account-priority.ts +163 -163
- package/src/core/accounts.ts +186 -186
- package/src/core/config.ts +383 -383
- package/src/core/crypto-utils.ts +79 -79
- package/src/core/database.ts +276 -276
- package/src/core/errors.ts +118 -118
- package/src/core/logger.ts +269 -269
- package/src/core/memory-usage.ts +84 -84
- package/src/core/metrics.ts +291 -291
- package/src/core/model-alias.ts +77 -77
- package/src/core/model-registry.ts +544 -544
- package/src/core/mutex.ts +119 -119
- package/src/core/paths.ts +199 -199
- package/src/core/prompt-limits.ts +214 -214
- package/src/core/reasoning-effort.ts +102 -102
- package/src/core/stream-registry.ts +96 -96
- package/src/core/waf-isolation.ts +117 -117
- package/src/core/watchdog.ts +195 -195
- package/src/delete-chats.ts +23 -23
- package/src/index.ts +65 -64
- package/src/login.ts +147 -147
- package/src/reset-cooldowns.ts +11 -11
- package/src/routes/anthropic/index.ts +355 -355
- package/src/routes/anthropic/translate.ts +522 -522
- package/src/routes/anthropic/types.ts +154 -154
- package/src/routes/anthropic/validation.ts +144 -144
- package/src/routes/chat/account.ts +1817 -1817
- package/src/routes/chat/context.ts +241 -241
- package/src/routes/chat/errors.ts +85 -85
- package/src/routes/chat/helpers.ts +268 -268
- package/src/routes/chat/index.ts +618 -618
- package/src/routes/chat/media.ts +285 -285
- package/src/routes/chat/retry-policy.ts +754 -754
- package/src/routes/chat/stop.ts +98 -98
- package/src/routes/chat/streaming.ts +2710 -2710
- package/src/routes/chat/validation.ts +526 -526
- package/src/routes/chat.ts +2 -2
- package/src/routes/completions.ts +290 -290
- package/src/routes/images.ts +139 -139
- package/src/routes/responses/adapter.ts +503 -503
- package/src/routes/responses/index.ts +405 -405
- package/src/routes/responses/state.ts +230 -230
- package/src/routes/responses/streaming.ts +528 -528
- package/src/routes/responses/types.ts +285 -285
- package/src/routes/responses/validation.ts +202 -202
- package/src/routes/upload.ts +731 -731
- package/src/routes/videos.ts +214 -214
- package/src/services/auth-playwright.ts +173 -173
- package/src/services/captcha-coordinator.ts +161 -161
- package/src/services/captcha-solver.ts +553 -553
- package/src/services/chat-cleanup.ts +80 -80
- package/src/services/context-meter.ts +317 -317
- package/src/services/fingerprint.ts +242 -242
- package/src/services/human-behavior.ts +173 -173
- package/src/services/media-generation.ts +1748 -1748
- package/src/services/playwright.ts +2878 -2800
- package/src/services/qwen-chat-pool.ts +345 -345
- package/src/services/qwen-errors.ts +133 -133
- package/src/services/qwen-headers.ts +79 -79
- package/src/services/qwen-thread-state.ts +393 -393
- package/src/services/qwen-url.ts +19 -19
- package/src/services/qwen.ts +3126 -3126
- package/src/services/session-keeper.ts +88 -88
- package/src/services/token-estimation-metrics.ts +118 -118
- package/src/sync/claude-code.ts +75 -75
- package/src/sync/codex.ts +123 -123
- package/src/sync/index.ts +362 -362
- package/src/sync/omp.ts +105 -105
- package/src/sync/opencode.ts +214 -214
- package/src/sync/types.ts +53 -53
- package/src/sync/utils.ts +27 -27
- package/src/sync-clients.ts +189 -189
- package/src/tools/instructions.ts +137 -137
- package/src/tools/manifest.ts +81 -81
- package/src/tools/parser.ts +2989 -2989
- package/src/tools/toolcall-tags.ts +142 -142
- package/src/tui/app.ts +259 -264
- package/src/tui/index.ts +61 -61
- package/src/tui/markdown.ts +258 -258
- package/src/tui/proxy-client.ts +331 -326
- package/src/tui/screen.ts +294 -278
- package/src/tui/server-manager.ts +270 -270
- package/src/tui/theme.ts +432 -432
- package/src/tui/types.ts +33 -33
- package/src/tui/views/accounts-view.ts +656 -656
- package/src/tui/views/chat-view.ts +1018 -823
- package/src/tui/views/logs-view.ts +479 -413
- package/src/tui/views/status-view.ts +204 -204
- package/src/tui/views/storage-view.ts +304 -291
- package/src/tui/views/sync-view.ts +409 -409
- package/src/types/ali-oss.d.ts +32 -32
- package/src/update-cli.ts +121 -0
- package/src/utils/context-truncation.ts +84 -84
- package/src/utils/json.ts +380 -380
- package/src/utils/session-id.ts +37 -37
- package/src/utils/tool-call-guard.ts +84 -84
- package/src/utils/types.ts +109 -109
package/src/sync/codex.ts
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import type { ClientSyncResult, SyncOptions } from "./types.ts";
|
|
4
|
-
import { createTimestampBackup, restoreFromBackup } from "./utils.ts";
|
|
5
|
-
|
|
6
|
-
function updateTopLevelKey(content: string, key: string, value: string | number): string {
|
|
7
|
-
// Find first section header [section]
|
|
8
|
-
const firstSectionIdx = content.search(/^\[/m);
|
|
9
|
-
const topPart = firstSectionIdx === -1 ? content : content.slice(0, firstSectionIdx);
|
|
10
|
-
const restPart = firstSectionIdx === -1 ? "" : content.slice(firstSectionIdx);
|
|
11
|
-
|
|
12
|
-
const formattedValue = typeof value === "string" ? `"${value}"` : String(value);
|
|
13
|
-
const keyRegex = new RegExp(`^${key}\\s*=.*$`, "m");
|
|
14
|
-
|
|
15
|
-
let newTopPart: string;
|
|
16
|
-
if (keyRegex.test(topPart)) {
|
|
17
|
-
newTopPart = topPart.replace(keyRegex, `${key} = ${formattedValue}`);
|
|
18
|
-
} else {
|
|
19
|
-
newTopPart = `${key} = ${formattedValue}\n` + topPart.trimStart();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return newTopPart + restPart;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function syncCodex(options: SyncOptions): ClientSyncResult {
|
|
26
|
-
const { filePath, apiKey, baseUrl, model = "qwen3.8-max", setActive = true } = options;
|
|
27
|
-
try {
|
|
28
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
29
|
-
|
|
30
|
-
let backupPath: string | undefined;
|
|
31
|
-
let content = "";
|
|
32
|
-
|
|
33
|
-
if (fs.existsSync(filePath)) {
|
|
34
|
-
backupPath = createTimestampBackup(filePath);
|
|
35
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Build the qwenproxy model_provider block
|
|
39
|
-
const providerBlock = `[model_providers.qwenproxy]
|
|
40
|
-
name = "QwenProxy"
|
|
41
|
-
base_url = "${baseUrl}"
|
|
42
|
-
wire_api = "responses"
|
|
43
|
-
experimental_bearer_token = "${apiKey}"
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
// Replace or append [model_providers.qwenproxy]
|
|
47
|
-
const providerRegex = /\[model_providers\.qwenproxy\][\s\S]*?(?=(?:^\[|\Z))/m;
|
|
48
|
-
if (providerRegex.test(content)) {
|
|
49
|
-
content = content.replace(providerRegex, providerBlock);
|
|
50
|
-
} else {
|
|
51
|
-
content = content.trimEnd() + (content.length > 0 ? "\n\n" : "") + providerBlock;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Set active model if requested
|
|
55
|
-
if (setActive) {
|
|
56
|
-
content = updateTopLevelKey(content, "model", model);
|
|
57
|
-
content = updateTopLevelKey(content, "model_provider", "qwenproxy");
|
|
58
|
-
content = updateTopLevelKey(content, "model_context_window", 1000000);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// If a custom model_catalog_json is configured, ensure the target model is listed in it
|
|
62
|
-
const catalogMatch = content.match(/^model_catalog_json\s*=\s*["']([^"']+)["']/m);
|
|
63
|
-
if (catalogMatch && catalogMatch[1]) {
|
|
64
|
-
try {
|
|
65
|
-
const rawCatalogPath = catalogMatch[1];
|
|
66
|
-
const catalogPath = path.isAbsolute(rawCatalogPath)
|
|
67
|
-
? rawCatalogPath
|
|
68
|
-
: path.resolve(path.dirname(filePath), rawCatalogPath);
|
|
69
|
-
if (fs.existsSync(catalogPath)) {
|
|
70
|
-
const catalog = JSON.parse(fs.readFileSync(catalogPath, "utf-8"));
|
|
71
|
-
if (Array.isArray(catalog.models)) {
|
|
72
|
-
const hasModel = catalog.models.some((m: any) => m?.slug === model);
|
|
73
|
-
if (!hasModel) {
|
|
74
|
-
const template = catalog.models[0] || {};
|
|
75
|
-
catalog.models.unshift({
|
|
76
|
-
...template,
|
|
77
|
-
slug: model,
|
|
78
|
-
display_name: `Qwen (${model})`,
|
|
79
|
-
description: `QwenProxy ${model} with 1,000,000 token context window`,
|
|
80
|
-
context_window: 1000000,
|
|
81
|
-
max_context_window: 1000000,
|
|
82
|
-
});
|
|
83
|
-
fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2), "utf-8");
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
// Non-fatal catalog enhancement
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
fs.writeFileSync(filePath, content.trimEnd() + "\n", "utf-8");
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
client: "codex",
|
|
96
|
-
filePath,
|
|
97
|
-
backupPath,
|
|
98
|
-
success: true,
|
|
99
|
-
action: backupPath ? "updated" : "created",
|
|
100
|
-
message: `Configured Codex with provider qwenproxy (${baseUrl})`,
|
|
101
|
-
};
|
|
102
|
-
} catch (err: any) {
|
|
103
|
-
return {
|
|
104
|
-
client: "codex",
|
|
105
|
-
filePath,
|
|
106
|
-
success: false,
|
|
107
|
-
action: "failed",
|
|
108
|
-
error: err?.message || String(err),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function restoreCodex(filePath: string, backupPath?: string): ClientSyncResult {
|
|
114
|
-
const restored = restoreFromBackup(filePath, backupPath);
|
|
115
|
-
return {
|
|
116
|
-
client: "codex",
|
|
117
|
-
filePath,
|
|
118
|
-
backupPath,
|
|
119
|
-
success: restored,
|
|
120
|
-
action: restored ? "restored" : "failed",
|
|
121
|
-
message: restored ? "Restored Codex config from backup" : "Backup file not found",
|
|
122
|
-
};
|
|
123
|
-
}
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { ClientSyncResult, SyncOptions } from "./types.ts";
|
|
4
|
+
import { createTimestampBackup, restoreFromBackup } from "./utils.ts";
|
|
5
|
+
|
|
6
|
+
function updateTopLevelKey(content: string, key: string, value: string | number): string {
|
|
7
|
+
// Find first section header [section]
|
|
8
|
+
const firstSectionIdx = content.search(/^\[/m);
|
|
9
|
+
const topPart = firstSectionIdx === -1 ? content : content.slice(0, firstSectionIdx);
|
|
10
|
+
const restPart = firstSectionIdx === -1 ? "" : content.slice(firstSectionIdx);
|
|
11
|
+
|
|
12
|
+
const formattedValue = typeof value === "string" ? `"${value}"` : String(value);
|
|
13
|
+
const keyRegex = new RegExp(`^${key}\\s*=.*$`, "m");
|
|
14
|
+
|
|
15
|
+
let newTopPart: string;
|
|
16
|
+
if (keyRegex.test(topPart)) {
|
|
17
|
+
newTopPart = topPart.replace(keyRegex, `${key} = ${formattedValue}`);
|
|
18
|
+
} else {
|
|
19
|
+
newTopPart = `${key} = ${formattedValue}\n` + topPart.trimStart();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return newTopPart + restPart;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function syncCodex(options: SyncOptions): ClientSyncResult {
|
|
26
|
+
const { filePath, apiKey, baseUrl, model = "qwen3.8-max", setActive = true } = options;
|
|
27
|
+
try {
|
|
28
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
29
|
+
|
|
30
|
+
let backupPath: string | undefined;
|
|
31
|
+
let content = "";
|
|
32
|
+
|
|
33
|
+
if (fs.existsSync(filePath)) {
|
|
34
|
+
backupPath = createTimestampBackup(filePath);
|
|
35
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Build the qwenproxy model_provider block
|
|
39
|
+
const providerBlock = `[model_providers.qwenproxy]
|
|
40
|
+
name = "QwenProxy"
|
|
41
|
+
base_url = "${baseUrl}"
|
|
42
|
+
wire_api = "responses"
|
|
43
|
+
experimental_bearer_token = "${apiKey}"
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
// Replace or append [model_providers.qwenproxy]
|
|
47
|
+
const providerRegex = /\[model_providers\.qwenproxy\][\s\S]*?(?=(?:^\[|\Z))/m;
|
|
48
|
+
if (providerRegex.test(content)) {
|
|
49
|
+
content = content.replace(providerRegex, providerBlock);
|
|
50
|
+
} else {
|
|
51
|
+
content = content.trimEnd() + (content.length > 0 ? "\n\n" : "") + providerBlock;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Set active model if requested
|
|
55
|
+
if (setActive) {
|
|
56
|
+
content = updateTopLevelKey(content, "model", model);
|
|
57
|
+
content = updateTopLevelKey(content, "model_provider", "qwenproxy");
|
|
58
|
+
content = updateTopLevelKey(content, "model_context_window", 1000000);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If a custom model_catalog_json is configured, ensure the target model is listed in it
|
|
62
|
+
const catalogMatch = content.match(/^model_catalog_json\s*=\s*["']([^"']+)["']/m);
|
|
63
|
+
if (catalogMatch && catalogMatch[1]) {
|
|
64
|
+
try {
|
|
65
|
+
const rawCatalogPath = catalogMatch[1];
|
|
66
|
+
const catalogPath = path.isAbsolute(rawCatalogPath)
|
|
67
|
+
? rawCatalogPath
|
|
68
|
+
: path.resolve(path.dirname(filePath), rawCatalogPath);
|
|
69
|
+
if (fs.existsSync(catalogPath)) {
|
|
70
|
+
const catalog = JSON.parse(fs.readFileSync(catalogPath, "utf-8"));
|
|
71
|
+
if (Array.isArray(catalog.models)) {
|
|
72
|
+
const hasModel = catalog.models.some((m: any) => m?.slug === model);
|
|
73
|
+
if (!hasModel) {
|
|
74
|
+
const template = catalog.models[0] || {};
|
|
75
|
+
catalog.models.unshift({
|
|
76
|
+
...template,
|
|
77
|
+
slug: model,
|
|
78
|
+
display_name: `Qwen (${model})`,
|
|
79
|
+
description: `QwenProxy ${model} with 1,000,000 token context window`,
|
|
80
|
+
context_window: 1000000,
|
|
81
|
+
max_context_window: 1000000,
|
|
82
|
+
});
|
|
83
|
+
fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2), "utf-8");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Non-fatal catalog enhancement
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fs.writeFileSync(filePath, content.trimEnd() + "\n", "utf-8");
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
client: "codex",
|
|
96
|
+
filePath,
|
|
97
|
+
backupPath,
|
|
98
|
+
success: true,
|
|
99
|
+
action: backupPath ? "updated" : "created",
|
|
100
|
+
message: `Configured Codex with provider qwenproxy (${baseUrl})`,
|
|
101
|
+
};
|
|
102
|
+
} catch (err: any) {
|
|
103
|
+
return {
|
|
104
|
+
client: "codex",
|
|
105
|
+
filePath,
|
|
106
|
+
success: false,
|
|
107
|
+
action: "failed",
|
|
108
|
+
error: err?.message || String(err),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function restoreCodex(filePath: string, backupPath?: string): ClientSyncResult {
|
|
114
|
+
const restored = restoreFromBackup(filePath, backupPath);
|
|
115
|
+
return {
|
|
116
|
+
client: "codex",
|
|
117
|
+
filePath,
|
|
118
|
+
backupPath,
|
|
119
|
+
success: restored,
|
|
120
|
+
action: restored ? "restored" : "failed",
|
|
121
|
+
message: restored ? "Restored Codex config from backup" : "Backup file not found",
|
|
122
|
+
};
|
|
123
|
+
}
|