coding-tool-x 3.5.14 → 3.6.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/CHANGELOG.md +15 -0
- package/dist/web/assets/{Analytics-D24Q2IkD.js → Analytics-DvWZOZ13.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-xNm_dyGS.js → ConfigTemplates-DiuKu3LA.js} +1 -1
- package/dist/web/assets/{Home-YQrz9dNs.js → Home-DeGTc9uf.js} +1 -1
- package/dist/web/assets/{PluginManager-7ULrS-17.js → PluginManager-CljkN0Rb.js} +1 -1
- package/dist/web/assets/{ProjectList-Fy4xyQpg.js → ProjectList-CFQJaSQ8.js} +1 -1
- package/dist/web/assets/{SessionList-DcLW9x3Z.js → SessionList-Fq4uQSQp.js} +1 -1
- package/dist/web/assets/{SkillManager-RnoRbzHA.js → SkillManager-DLhg11LF.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-B_BWS-xG.js → WorkspaceManager-DXB4kE9p.js} +1 -1
- package/dist/web/assets/index-CLra7uzu.js +2 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/commands/daemon.js +55 -23
- package/src/index.js +18 -11
- package/src/server/api/codex-channels.js +5 -14
- package/src/server/services/codex-channels.js +3 -3
- package/src/server/services/proxy-log-helper.js +9 -4
- package/dist/web/assets/index-Bvdezm5-.js +0 -2
package/dist/web/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>coding-tool-x · AI 编程工作台</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CLra7uzu.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-DM897mIP.js">
|
package/package.json
CHANGED
package/src/commands/daemon.js
CHANGED
|
@@ -85,6 +85,18 @@ function deletePM2Process(name) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
function restartPM2Process(name) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
pm2.restart(name, (err) => {
|
|
91
|
+
if (err) {
|
|
92
|
+
reject(err);
|
|
93
|
+
} else {
|
|
94
|
+
resolve();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
/**
|
|
89
101
|
* 获取 Coding-Tool 进程
|
|
90
102
|
*/
|
|
@@ -169,6 +181,41 @@ function updatePM2Daemon() {
|
|
|
169
181
|
});
|
|
170
182
|
}
|
|
171
183
|
|
|
184
|
+
function dumpPM2State(timeoutMs = 2000) {
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
let settled = false;
|
|
187
|
+
const timer = setTimeout(() => {
|
|
188
|
+
if (settled) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
settled = true;
|
|
192
|
+
resolve({ timedOut: true, err: null });
|
|
193
|
+
}, timeoutMs);
|
|
194
|
+
|
|
195
|
+
pm2.dump((err) => {
|
|
196
|
+
if (settled) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
settled = true;
|
|
200
|
+
clearTimeout(timer);
|
|
201
|
+
resolve({ timedOut: false, err: err || null });
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function finalizePM2Session(options = {}) {
|
|
207
|
+
const shouldPersist = options.persist === true;
|
|
208
|
+
if (shouldPersist) {
|
|
209
|
+
const result = await dumpPM2State(options.timeoutMs);
|
|
210
|
+
if (result.err) {
|
|
211
|
+
console.log(chalk.yellow(`[WARN] 保存 PM2 状态失败: ${result.err.message}`));
|
|
212
|
+
} else if (result.timedOut) {
|
|
213
|
+
console.log(chalk.yellow('[WARN] 保存 PM2 状态超时,继续关闭 CLI 连接'));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
disconnectPM2();
|
|
217
|
+
}
|
|
218
|
+
|
|
172
219
|
function isPortOwnedByPid(port, pid) {
|
|
173
220
|
if (!pid || pid <= 0) {
|
|
174
221
|
return false;
|
|
@@ -432,10 +479,9 @@ async function attemptStartService(startOptions, port, options = {}) {
|
|
|
432
479
|
|
|
433
480
|
function cleanupFailedStart() {
|
|
434
481
|
return new Promise((resolve) => {
|
|
435
|
-
pm2.delete(PM2_APP_NAME, () => {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
});
|
|
482
|
+
pm2.delete(PM2_APP_NAME, async () => {
|
|
483
|
+
await finalizePM2Session({ persist: true });
|
|
484
|
+
resolve();
|
|
439
485
|
});
|
|
440
486
|
});
|
|
441
487
|
}
|
|
@@ -527,9 +573,7 @@ async function handleStart() {
|
|
|
527
573
|
console.log(chalk.gray(' ') + chalk.cyan('ctx logs') + chalk.gray(' - 查看实时日志'));
|
|
528
574
|
console.log(chalk.gray(' ') + chalk.cyan('ctx stop') + chalk.gray(' - 停止服务\n'));
|
|
529
575
|
|
|
530
|
-
|
|
531
|
-
disconnectPM2();
|
|
532
|
-
});
|
|
576
|
+
await finalizePM2Session({ persist: true });
|
|
533
577
|
} catch (error) {
|
|
534
578
|
console.error(chalk.red('启动失败:'), error.message);
|
|
535
579
|
disconnectPM2();
|
|
@@ -549,9 +593,7 @@ async function handleStop() {
|
|
|
549
593
|
const stopResult = await stopAllManagedInstances(existing, config);
|
|
550
594
|
printStopResult(stopResult);
|
|
551
595
|
|
|
552
|
-
|
|
553
|
-
disconnectPM2();
|
|
554
|
-
});
|
|
596
|
+
await finalizePM2Session({ persist: true });
|
|
555
597
|
} catch (error) {
|
|
556
598
|
console.error(chalk.red('停止失败:'), error.message);
|
|
557
599
|
disconnectPM2();
|
|
@@ -573,19 +615,9 @@ async function handleRestart() {
|
|
|
573
615
|
return;
|
|
574
616
|
}
|
|
575
617
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
disconnectPM2();
|
|
580
|
-
process.exit(1);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
console.log(chalk.green('\n[OK] Coding-Tool 服务已重启\n'));
|
|
584
|
-
|
|
585
|
-
pm2.dump((err) => {
|
|
586
|
-
disconnectPM2();
|
|
587
|
-
});
|
|
588
|
-
});
|
|
618
|
+
await restartPM2Process(PM2_APP_NAME);
|
|
619
|
+
console.log(chalk.green('\n[OK] Coding-Tool 服务已重启\n'));
|
|
620
|
+
await finalizePM2Session({ persist: true });
|
|
589
621
|
} catch (error) {
|
|
590
622
|
console.error(chalk.red('重启失败:'), error.message);
|
|
591
623
|
disconnectPM2();
|
package/src/index.js
CHANGED
|
@@ -31,6 +31,12 @@ function getVersion() {
|
|
|
31
31
|
return packageJson.version;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function finishCli(code = 0) {
|
|
35
|
+
eventBus.emitSync('cli:shutdown', {});
|
|
36
|
+
PluginManager.shutdownPlugins();
|
|
37
|
+
process.exit(code);
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
// 显示帮助信息
|
|
35
41
|
function showHelp() {
|
|
36
42
|
const version = getVersion();
|
|
@@ -156,19 +162,19 @@ async function main() {
|
|
|
156
162
|
|
|
157
163
|
if (subCommand === 'start') {
|
|
158
164
|
await handleStart();
|
|
159
|
-
return;
|
|
165
|
+
return finishCli(0);
|
|
160
166
|
}
|
|
161
167
|
if (subCommand === 'stop') {
|
|
162
168
|
await handleStop();
|
|
163
|
-
return;
|
|
169
|
+
return finishCli(0);
|
|
164
170
|
}
|
|
165
171
|
if (subCommand === 'restart') {
|
|
166
172
|
await handleRestart();
|
|
167
|
-
return;
|
|
173
|
+
return finishCli(0);
|
|
168
174
|
}
|
|
169
175
|
if (subCommand === 'status') {
|
|
170
176
|
await handleStatus();
|
|
171
|
-
return;
|
|
177
|
+
return finishCli(0);
|
|
172
178
|
}
|
|
173
179
|
if (subCommand === 'logs') {
|
|
174
180
|
const options = {};
|
|
@@ -213,25 +219,25 @@ async function main() {
|
|
|
213
219
|
// start 命令 - 启动服务(后台)
|
|
214
220
|
if (args[0] === 'start') {
|
|
215
221
|
await handleStart();
|
|
216
|
-
return;
|
|
222
|
+
return finishCli(0);
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
// stop 命令 - 停止服务
|
|
220
226
|
if (args[0] === 'stop') {
|
|
221
227
|
await handleStop();
|
|
222
|
-
return;
|
|
228
|
+
return finishCli(0);
|
|
223
229
|
}
|
|
224
230
|
|
|
225
231
|
// restart 命令 - 重启服务
|
|
226
232
|
if (args[0] === 'restart') {
|
|
227
233
|
await handleRestart();
|
|
228
|
-
return;
|
|
234
|
+
return finishCli(0);
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
// status 命令 - 查看服务状态
|
|
232
238
|
if (args[0] === 'status') {
|
|
233
239
|
await handleStatus();
|
|
234
|
-
return;
|
|
240
|
+
return finishCli(0);
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
// ui 命令 - Web UI 管理
|
|
@@ -239,10 +245,13 @@ async function main() {
|
|
|
239
245
|
const subCommand = args[1];
|
|
240
246
|
if (subCommand === 'start') {
|
|
241
247
|
await handleStart(); // UI start 实际上就是启动整个服务
|
|
248
|
+
return finishCli(0);
|
|
242
249
|
} else if (subCommand === 'stop') {
|
|
243
250
|
await handleStop();
|
|
251
|
+
return finishCli(0);
|
|
244
252
|
} else if (subCommand === 'restart') {
|
|
245
253
|
await handleRestart();
|
|
254
|
+
return finishCli(0);
|
|
246
255
|
} else {
|
|
247
256
|
// 默认前台运行
|
|
248
257
|
const { handleUI } = require('./commands/ui');
|
|
@@ -670,9 +679,7 @@ async function main() {
|
|
|
670
679
|
|
|
671
680
|
case 'exit':
|
|
672
681
|
console.log('\n[BYE] 再见!\n');
|
|
673
|
-
|
|
674
|
-
PluginManager.shutdownPlugins();
|
|
675
|
-
process.exit(0);
|
|
682
|
+
finishCli(0);
|
|
676
683
|
break;
|
|
677
684
|
|
|
678
685
|
default:
|
|
@@ -134,7 +134,6 @@ module.exports = (config) => {
|
|
|
134
134
|
providerKey,
|
|
135
135
|
baseUrl,
|
|
136
136
|
apiKey,
|
|
137
|
-
requiresOpenaiAuth,
|
|
138
137
|
websiteUrl,
|
|
139
138
|
enabled,
|
|
140
139
|
weight,
|
|
@@ -149,7 +148,7 @@ module.exports = (config) => {
|
|
|
149
148
|
return res.status(400).json({ error: 'Missing required fields: name, providerKey, baseUrl' });
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
if (!apiKey
|
|
151
|
+
if (!apiKey) {
|
|
153
152
|
return res.status(400).json({ error: 'Missing required fields: apiKey' });
|
|
154
153
|
}
|
|
155
154
|
|
|
@@ -159,7 +158,7 @@ module.exports = (config) => {
|
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
// wireApi 固定为 'responses' (OpenAI Responses API 格式)
|
|
162
|
-
const channel = createChannel(name, providerKey, baseUrl,
|
|
161
|
+
const channel = createChannel(name, providerKey, baseUrl, apiKey, 'responses', {
|
|
163
162
|
websiteUrl,
|
|
164
163
|
enabled,
|
|
165
164
|
weight,
|
|
@@ -168,7 +167,7 @@ module.exports = (config) => {
|
|
|
168
167
|
speedTestModel: speedTestModel || null,
|
|
169
168
|
presetId: presetId || null,
|
|
170
169
|
gatewaySourceType,
|
|
171
|
-
requiresOpenaiAuth:
|
|
170
|
+
requiresOpenaiAuth: false
|
|
172
171
|
});
|
|
173
172
|
res.json(channel);
|
|
174
173
|
broadcastSchedulerState('codex', getSchedulerState('codex'));
|
|
@@ -191,9 +190,6 @@ module.exports = (config) => {
|
|
|
191
190
|
const { channelId } = req.params;
|
|
192
191
|
const updates = req.body;
|
|
193
192
|
const currentChannel = getChannels().channels.find(channel => channel.id === channelId) || null;
|
|
194
|
-
const finalRequiresOpenaiAuth = Object.prototype.hasOwnProperty.call(updates, 'requiresOpenaiAuth')
|
|
195
|
-
? updates.requiresOpenaiAuth === true
|
|
196
|
-
: currentChannel?.requiresOpenaiAuth === true;
|
|
197
193
|
const finalApiKey = Object.prototype.hasOwnProperty.call(updates, 'apiKey')
|
|
198
194
|
? updates.apiKey
|
|
199
195
|
: currentChannel?.apiKey;
|
|
@@ -204,15 +200,10 @@ module.exports = (config) => {
|
|
|
204
200
|
}
|
|
205
201
|
}
|
|
206
202
|
if (currentChannel) {
|
|
207
|
-
if (!
|
|
203
|
+
if (!finalApiKey) {
|
|
208
204
|
return res.status(400).json({ error: 'Missing required fields: apiKey' });
|
|
209
205
|
}
|
|
210
|
-
|
|
211
|
-
updates.requiresOpenaiAuth = finalRequiresOpenaiAuth;
|
|
212
|
-
}
|
|
213
|
-
if (finalRequiresOpenaiAuth) {
|
|
214
|
-
updates.apiKey = '';
|
|
215
|
-
}
|
|
206
|
+
updates.requiresOpenaiAuth = false;
|
|
216
207
|
}
|
|
217
208
|
|
|
218
209
|
const channel = updateChannel(channelId, updates);
|
|
@@ -34,8 +34,8 @@ function resolveCurrentManagedChannel(channels = []) {
|
|
|
34
34
|
return allChannels.find(ch => ch.enabled !== false) || null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function channelRequiresOpenaiAuth(
|
|
38
|
-
return
|
|
37
|
+
function channelRequiresOpenaiAuth() {
|
|
38
|
+
return false;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function buildManagedCodexEnvMap(channels = [], { includeProxyKey = false, activeChannel = null } = {}) {
|
|
@@ -150,7 +150,7 @@ class CodexChannelService extends BaseChannelService {
|
|
|
150
150
|
ch.speedTestModel = ch.speedTestModel || null;
|
|
151
151
|
ch.modelRedirects = Array.isArray(ch.modelRedirects) ? ch.modelRedirects : [];
|
|
152
152
|
ch.gatewaySourceType = ch.gatewaySourceType || 'codex';
|
|
153
|
-
ch.requiresOpenaiAuth =
|
|
153
|
+
ch.requiresOpenaiAuth = false;
|
|
154
154
|
ch.queryParams = ch.queryParams || {};
|
|
155
155
|
return ch;
|
|
156
156
|
}
|
|
@@ -135,10 +135,14 @@ function buildFailureLogPayload({
|
|
|
135
135
|
stage,
|
|
136
136
|
timestamp = Date.now()
|
|
137
137
|
}) {
|
|
138
|
-
const
|
|
138
|
+
const rawErrorMessage = String(
|
|
139
139
|
error?.message
|
|
140
|
-
|| message
|
|
141
140
|
|| error
|
|
141
|
+
|| ''
|
|
142
|
+
).trim();
|
|
143
|
+
const displayMessage = String(
|
|
144
|
+
message
|
|
145
|
+
|| rawErrorMessage
|
|
142
146
|
|| 'Request failed'
|
|
143
147
|
);
|
|
144
148
|
|
|
@@ -149,8 +153,9 @@ function buildFailureLogPayload({
|
|
|
149
153
|
time: formatRealtimeTime(timestamp),
|
|
150
154
|
channel: channel || 'Unknown',
|
|
151
155
|
model: model || '',
|
|
152
|
-
message:
|
|
153
|
-
error:
|
|
156
|
+
message: displayMessage,
|
|
157
|
+
error: displayMessage,
|
|
158
|
+
rawError: rawErrorMessage || undefined,
|
|
154
159
|
statusCode: Number.isFinite(Number(statusCode)) ? Number(statusCode) : null,
|
|
155
160
|
stage: stage || 'proxy',
|
|
156
161
|
inputTokens: 0,
|