coding-tool-x 3.4.10 → 3.4.12

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.
@@ -5,14 +5,14 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>CC-TOOL - ClaudeCode增强工作助手</title>
8
- <script type="module" crossorigin src="/assets/index-B4Wl3JfR.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-EMrm1wk-.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-DyTJGI4N.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-3bf-fPGP.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendors-CKPV1OAU.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/naive-ui-Bdxp09n2.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/icons-B5Pl4lrD.js">
14
14
  <link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
15
- <link rel="stylesheet" crossorigin href="/assets/index-Bgt_oqoE.css">
15
+ <link rel="stylesheet" crossorigin href="/assets/index-B02wDWNC.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.4.10",
3
+ "version": "3.4.12",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -125,7 +125,7 @@ function restoreSingleChannelMode(cliType) {
125
125
  if (cliType === 'codex') {
126
126
  const { getChannels, applyChannelToSettings } = require('../server/services/codex-channels');
127
127
  const target = pickRestoredChannel(cliType, getChannels().channels || []);
128
- return target ? applyChannelToSettings(target.id, { pruneProviders: true }) : null;
128
+ return target ? applyChannelToSettings(target.id) : null;
129
129
  }
130
130
 
131
131
  if (cliType === 'gemini') {
@@ -1,569 +1,56 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const https = require('https');
7
- const http = require('http');
8
- const { PATHS, NATIVE_PATHS } = require('../../config/paths');
9
- const { resolvePreferredHomeDir, normalizeWindowsHomePath } = require('../../utils/home-dir');
3
+ const notificationHooks = require('../services/notification-hooks');
10
4
  const { createSameOriginGuard } = require('../services/network-access');
5
+ const { resolvePreferredHomeDir, normalizeWindowsHomePath } = require('../../utils/home-dir');
11
6
 
12
- // 检测操作系统
13
- const platform = os.platform(); // 'darwin' | 'win32' | 'linux'
14
7
  router.use(createSameOriginGuard({
15
8
  message: '禁止跨站访问 Claude Hooks 配置接口'
16
9
  }));
17
10
 
18
- const HOME_DIR = resolvePreferredHomeDir(platform, process.env, os.homedir());
19
-
20
- // Claude settings.json 路径
21
- const CLAUDE_SETTINGS_PATH = NATIVE_PATHS.claude.settings;
22
-
23
- // UI 配置路径(记录用户是否主动关闭过、飞书配置等)
24
- const UI_CONFIG_PATH = PATHS.uiConfig;
25
-
26
- // 通知脚本路径(用于飞书通知)
27
- const NOTIFY_SCRIPT_PATH = PATHS.notifyHook;
28
-
29
- function buildWindowsPopupCommand() {
30
- return `powershell -NoProfile -Command "$wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('任务已完成 | 等待交互', 5, 'Coding Tool', 0x40)"`;
31
- }
32
-
33
- // 读取 Claude settings.json
34
- function readClaudeSettings() {
35
- try {
36
- if (fs.existsSync(CLAUDE_SETTINGS_PATH)) {
37
- const content = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8');
38
- return JSON.parse(content);
39
- }
40
- return {};
41
- } catch (error) {
42
- console.error('Failed to read Claude settings:', error);
43
- return {};
44
- }
45
- }
46
-
47
- // 写入 Claude settings.json
48
- function writeClaudeSettings(settings) {
49
- try {
50
- const dir = path.dirname(CLAUDE_SETTINGS_PATH);
51
- if (!fs.existsSync(dir)) {
52
- fs.mkdirSync(dir, { recursive: true });
53
- }
54
- fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8');
55
- return true;
56
- } catch (error) {
57
- console.error('Failed to write Claude settings:', error);
58
- return false;
59
- }
60
- }
61
-
62
- // 读取 UI 配置
63
- function readUIConfig() {
64
- try {
65
- if (fs.existsSync(UI_CONFIG_PATH)) {
66
- const content = fs.readFileSync(UI_CONFIG_PATH, 'utf8');
67
- return JSON.parse(content);
68
- }
69
- return {};
70
- } catch (error) {
71
- return {};
72
- }
73
- }
74
-
75
- // 写入 UI 配置
76
- function writeUIConfig(config) {
77
- try {
78
- const dir = path.dirname(UI_CONFIG_PATH);
79
- if (!fs.existsSync(dir)) {
80
- fs.mkdirSync(dir, { recursive: true });
81
- }
82
- fs.writeFileSync(UI_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
83
- return true;
84
- } catch (error) {
85
- console.error('Failed to write UI config:', error);
86
- return false;
87
- }
88
- }
89
-
90
- // 生成系统通知命令(跨平台)
91
- function generateSystemNotificationCommand(type, platformOverride = platform) {
92
- if (platformOverride === 'darwin') {
93
- // macOS
94
- if (type === 'dialog') {
95
- return `osascript -e 'display dialog "Claude Code 任务已完成 | 等待交互" with title "Coding Tool" buttons {"好的"} default button 1 with icon note'`;
96
- } else {
97
- // 优先使用 terminal-notifier(点击可打开终端),否则使用 osascript
98
- // terminal-notifier 需要 brew install terminal-notifier
99
- return `if command -v terminal-notifier &>/dev/null; then terminal-notifier -title "Coding Tool" -message "任务已完成 | 等待交互" -sound Glass -activate com.apple.Terminal; else osascript -e 'display notification "任务已完成 | 等待交互" with title "Coding Tool" sound name "Glass"'; fi`;
100
- }
101
- } else if (platformOverride === 'win32') {
102
- // Windows
103
- if (type === 'dialog') {
104
- return `powershell -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('Claude Code 任务已完成 | 等待交互', 'Coding Tool', 'OK', 'Information')" || ${buildWindowsPopupCommand()}`;
105
- } else {
106
- return buildWindowsPopupCommand();
107
- }
108
- } else {
109
- // Linux
110
- if (type === 'dialog') {
111
- return `zenity --info --title="Coding Tool" --text="Claude Code 任务已完成 | 等待交互" 2>/dev/null || notify-send "Coding Tool" "任务已完成 | 等待交互"`;
112
- } else {
113
- return `notify-send "Coding Tool" "任务已完成 | 等待交互"`;
114
- }
115
- }
116
- }
117
-
118
- // 生成通知脚本内容(支持系统通知 + 飞书通知)
119
- function generateNotifyScript(config) {
120
- const { systemNotification, feishu } = config;
121
-
122
- let script = `#!/usr/bin/env node
123
- // CC-Tool 通知脚本 - 自动生成,请勿手动修改
124
- const https = require('https');
125
- const http = require('http');
126
- const { execSync } = require('child_process');
127
- const os = require('os');
128
-
129
- const platform = os.platform();
130
- const timestamp = new Date().toLocaleString('zh-CN');
131
-
132
- `;
133
-
134
- // 系统通知部分
135
- if (systemNotification && systemNotification.enabled) {
136
- const cmd = generateSystemNotificationCommand(systemNotification.type);
137
- script += `// 系统通知
138
- try {
139
- execSync(${JSON.stringify(cmd)}, { stdio: 'ignore', windowsHide: true });
140
- } catch (e) {
141
- console.error('系统通知失败:', e.message);
142
- }
143
-
144
- `;
145
- }
146
-
147
- // 飞书通知部分
148
- if (feishu && feishu.enabled && feishu.webhookUrl) {
149
- script += `// 飞书通知
150
- const feishuUrl = ${JSON.stringify(feishu.webhookUrl)};
151
- const feishuData = JSON.stringify({
152
- msg_type: 'interactive',
153
- card: {
154
- header: {
155
- title: { tag: 'plain_text', content: '[DONE] Coding Tool - 任务完成' },
156
- template: 'green'
157
- },
158
- elements: [
159
- {
160
- tag: 'div',
161
- text: { tag: 'lark_md', content: '**状态**: Claude Code 任务已完成 | 等待交互' }
162
- },
163
- {
164
- tag: 'div',
165
- text: { tag: 'lark_md', content: '**时间**: ' + timestamp }
166
- },
167
- {
168
- tag: 'div',
169
- text: { tag: 'lark_md', content: '**设备**: ' + os.hostname() }
170
- }
171
- ]
172
- }
173
- });
174
-
175
- try {
176
- const urlObj = new URL(feishuUrl);
177
- const options = {
178
- hostname: urlObj.hostname,
179
- port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
180
- path: urlObj.pathname + urlObj.search,
181
- method: 'POST',
182
- headers: {
183
- 'Content-Type': 'application/json',
184
- 'Content-Length': Buffer.byteLength(feishuData)
185
- },
186
- timeout: 10000
187
- };
188
-
189
- const reqModule = urlObj.protocol === 'https:' ? https : http;
190
- const req = reqModule.request(options, (res) => {
191
- // 忽略响应
192
- });
193
- req.on('error', (e) => {
194
- console.error('飞书通知失败:', e.message);
195
- });
196
- req.write(feishuData);
197
- req.end();
198
- } catch (e) {
199
- console.error('飞书通知失败:', e.message);
200
- }
201
- `;
202
- }
203
-
204
- return script;
205
- }
206
-
207
- function parseNotifyTypeMarker(command) {
208
- const marker = command.match(/--cc-notify-type=(['"])?(dialog|notification)\1/i);
209
- return marker ? marker[2].toLowerCase() : null;
210
- }
211
-
212
- function getStopHookCommand(settings) {
213
- const hooks = settings?.hooks?.Stop;
214
- if (!Array.isArray(hooks) || hooks.length === 0) {
215
- return '';
216
- }
217
- const firstHook = hooks[0]?.hooks;
218
- if (!Array.isArray(firstHook) || firstHook.length === 0) {
219
- return '';
220
- }
221
- return firstHook[0]?.command || '';
222
- }
223
-
224
- function normalizePathForCompare(rawPath) {
225
- return String(rawPath || '').replace(/\\/g, '/');
226
- }
227
-
228
- function shouldRepairStopHook(settings, expectedScriptPath = NOTIFY_SCRIPT_PATH, fileExists = fs.existsSync) {
229
- const command = getStopHookCommand(settings);
230
- if (!command || !command.includes('notify-hook.js')) {
231
- return false;
232
- }
233
-
234
- const normalizedCommand = normalizePathForCompare(command);
235
- const normalizedExpected = normalizePathForCompare(expectedScriptPath);
236
- if (!normalizedCommand.includes(normalizedExpected)) {
237
- return true;
238
- }
239
-
240
- const markerType = parseNotifyTypeMarker(command);
241
- if (!markerType) {
242
- return true;
243
- }
244
-
245
- return !fileExists(expectedScriptPath);
246
- }
247
-
248
- function buildStopHookCommand(type) {
249
- const notifyType = type === 'dialog' ? 'dialog' : 'notification';
250
- return `node "${NOTIFY_SCRIPT_PATH}" --cc-notify-type=${notifyType}`;
251
- }
252
-
253
- // 写入通知脚本
254
- function writeNotifyScript(config) {
255
- try {
256
- const dir = path.dirname(NOTIFY_SCRIPT_PATH);
257
- if (!fs.existsSync(dir)) {
258
- fs.mkdirSync(dir, { recursive: true });
259
- }
260
-
261
- const script = generateNotifyScript(config);
262
- fs.writeFileSync(NOTIFY_SCRIPT_PATH, script, { mode: 0o755 });
263
- return true;
264
- } catch (error) {
265
- console.error('Failed to write notify script:', error);
266
- return false;
267
- }
268
- }
269
-
270
- // 从现有 hooks 配置中解析 Stop hook 状态
271
- function parseStopHookStatus(settings) {
272
- const hooks = settings.hooks;
273
- if (!hooks || !hooks.Stop || !Array.isArray(hooks.Stop) || hooks.Stop.length === 0) {
274
- return { enabled: false, type: 'notification' };
275
- }
276
-
277
- const stopHook = hooks.Stop[0];
278
- if (!stopHook.hooks || !Array.isArray(stopHook.hooks) || stopHook.hooks.length === 0) {
279
- return { enabled: false, type: 'notification' };
280
- }
281
-
282
- const command = stopHook.hooks[0].command || '';
283
- const markerType = parseNotifyTypeMarker(command);
284
-
285
- if (markerType) {
286
- return { enabled: true, type: markerType };
287
- }
288
-
289
- // 判断通知类型(跨平台检测)
290
- const isDialog = command.includes('display dialog') ||
291
- command.includes('MessageBox') ||
292
- command.includes('zenity --info');
293
- const isNotification = command.includes('display notification') ||
294
- command.includes('Popup') ||
295
- command.includes('notify-send') ||
296
- command.includes('ToastNotificationManager') ||
297
- command.includes('CreateToastNotifier');
298
-
299
- // 检查是否是我们的通知脚本
300
- const isOurScript = command.includes('notify-hook.js');
301
-
302
- if (isDialog || isNotification || isOurScript) {
303
- return {
304
- enabled: true,
305
- type: isDialog ? 'dialog' : 'notification'
306
- };
307
- }
308
-
309
- return { enabled: false, type: 'notification' };
310
- }
311
-
312
- // 获取飞书配置
313
- function getFeishuConfig() {
314
- const uiConfig = readUIConfig();
315
- return {
316
- enabled: uiConfig.feishuNotification?.enabled || false,
317
- webhookUrl: uiConfig.feishuNotification?.webhookUrl || ''
318
- };
319
- }
320
-
321
- // 保存飞书配置
322
- function saveFeishuConfig(feishu) {
323
- const uiConfig = readUIConfig();
324
- uiConfig.feishuNotification = {
325
- enabled: feishu.enabled || false,
326
- webhookUrl: feishu.webhookUrl || ''
327
- };
328
- return writeUIConfig(uiConfig);
329
- }
330
-
331
- // 更新 Stop hook 配置
332
- function updateStopHook(systemNotification, feishu) {
333
- const settings = readClaudeSettings();
334
-
335
- // 检查是否有任何通知需要启用
336
- const hasSystemNotification = systemNotification && systemNotification.enabled;
337
- const hasFeishu = feishu && feishu.enabled && feishu.webhookUrl;
338
-
339
- if (!hasSystemNotification && !hasFeishu) {
340
- // 都关闭了,移除 Stop hook
341
- if (settings.hooks && settings.hooks.Stop) {
342
- delete settings.hooks.Stop;
343
- if (Object.keys(settings.hooks).length === 0) {
344
- delete settings.hooks;
345
- }
346
- }
347
- // 删除通知脚本
348
- if (fs.existsSync(NOTIFY_SCRIPT_PATH)) {
349
- fs.unlinkSync(NOTIFY_SCRIPT_PATH);
350
- }
351
- } else {
352
- // 生成并写入通知脚本
353
- if (!writeNotifyScript({ systemNotification, feishu })) {
354
- return false;
355
- }
356
-
357
- // 更新 Stop hook 指向通知脚本
358
- settings.hooks = settings.hooks || {};
359
- settings.hooks.Stop = [
360
- {
361
- hooks: [
362
- {
363
- type: 'command',
364
- command: buildStopHookCommand(systemNotification?.type)
365
- }
366
- ]
367
- }
368
- ];
369
- }
370
-
371
- return writeClaudeSettings(settings);
372
- }
373
-
374
- // 初始化默认 hooks 配置(服务启动时调用)
375
- function initDefaultHooks() {
376
- try {
377
- const uiConfig = readUIConfig();
378
-
379
- // 如果用户主动关闭过通知,不自动开启
380
- if (uiConfig.claudeNotificationDisabledByUser === true) {
381
- console.log('[Claude Hooks] 用户已主动关闭通知,跳过自动初始化');
382
- return;
383
- }
384
-
385
- const settings = readClaudeSettings();
386
- const currentStatus = parseStopHookStatus(settings);
387
-
388
- // 如果已经有 Stop hook 配置,优先尝试自愈旧路径,再决定是否跳过
389
- if (currentStatus.enabled) {
390
- if (shouldRepairStopHook(settings)) {
391
- const systemNotification = {
392
- enabled: true,
393
- type: currentStatus.type || 'notification'
394
- };
395
- const feishu = getFeishuConfig();
396
- if (updateStopHook(systemNotification, feishu)) {
397
- console.log('[Claude Hooks] 检测到旧版 Stop hook 路径,已自动修复');
398
- } else {
399
- console.warn('[Claude Hooks] Stop hook 路径修复失败,保留原配置');
400
- }
401
- } else {
402
- console.log('[Claude Hooks] 已存在 Stop hook 配置,跳过初始化');
403
- }
404
- return;
405
- }
406
-
407
- // 写入默认配置(右上角卡片通知)
408
- const systemNotification = { enabled: true, type: 'notification' };
409
- const feishu = getFeishuConfig();
410
-
411
- if (updateStopHook(systemNotification, feishu)) {
412
- console.log('[Claude Hooks] 已自动开启任务完成通知(右上角卡片)');
413
- }
414
- } catch (error) {
415
- console.error('[Claude Hooks] 初始化默认配置失败:', error);
416
- }
417
- }
418
-
419
- // GET /api/claude/hooks - 获取 hooks 配置状态
420
11
  router.get('/', (req, res) => {
421
12
  try {
422
- const settings = readClaudeSettings();
423
- const stopHook = parseStopHookStatus(settings);
424
- const feishu = getFeishuConfig();
425
-
426
- res.json({
427
- success: true,
428
- stopHook,
429
- feishu,
430
- platform
431
- });
13
+ res.json(notificationHooks.getLegacyClaudeHookSettings());
432
14
  } catch (error) {
433
15
  console.error('Error getting Claude hooks:', error);
434
- res.status(500).json({ error: error.message });
16
+ res.status(error.statusCode || 500).json({ error: error.message });
435
17
  }
436
18
  });
437
19
 
438
- // POST /api/claude/hooks - 保存 hooks 配置
439
20
  router.post('/', (req, res) => {
440
21
  try {
441
- const { stopHook, feishu } = req.body;
442
-
443
- // 保存飞书配置到 UI 配置文件
444
- if (feishu !== undefined) {
445
- saveFeishuConfig(feishu);
446
- }
447
-
448
- // 更新 Stop hook
449
- const systemNotification = stopHook ? {
450
- enabled: stopHook.enabled,
451
- type: stopHook.type || 'notification'
452
- } : { enabled: false, type: 'notification' };
453
-
454
- const feishuConfig = feishu || getFeishuConfig();
455
-
456
- // 更新用户关闭标记
457
- const uiConfig = readUIConfig();
458
- if (systemNotification.enabled || feishuConfig.enabled) {
459
- // 用户开启了通知,清除关闭标记
460
- if (uiConfig.claudeNotificationDisabledByUser) {
461
- delete uiConfig.claudeNotificationDisabledByUser;
462
- writeUIConfig(uiConfig);
463
- }
464
- } else {
465
- // 用户关闭了所有通知
466
- uiConfig.claudeNotificationDisabledByUser = true;
467
- writeUIConfig(uiConfig);
468
- }
469
-
470
- if (updateStopHook(systemNotification, feishuConfig)) {
471
- res.json({
472
- success: true,
473
- message: '配置已保存',
474
- stopHook: systemNotification,
475
- feishu: feishuConfig
476
- });
477
- } else {
478
- res.status(500).json({ error: '保存配置失败' });
479
- }
22
+ const result = notificationHooks.saveLegacyClaudeHookSettings(req.body || {});
23
+ res.json({
24
+ ...result,
25
+ message: '配置已保存'
26
+ });
480
27
  } catch (error) {
481
28
  console.error('Error saving Claude hooks:', error);
482
- res.status(500).json({ error: error.message });
29
+ res.status(error.statusCode || 500).json({ error: error.message });
483
30
  }
484
31
  });
485
32
 
486
- // POST /api/claude/hooks/test - 测试通知
487
- router.post('/test', (req, res) => {
33
+ router.post('/test', async (req, res) => {
488
34
  try {
489
- const { type, testFeishu, webhookUrl } = req.body;
490
-
491
- if (testFeishu && webhookUrl) {
492
- // 测试飞书通知
493
- const urlObj = new URL(webhookUrl);
494
- const data = JSON.stringify({
495
- msg_type: 'interactive',
496
- card: {
497
- header: {
498
- title: { tag: 'plain_text', content: '[TEST] Coding Tool - 测试通知' },
499
- template: 'blue'
500
- },
501
- elements: [
502
- {
503
- tag: 'div',
504
- text: { tag: 'lark_md', content: '**状态**: 这是一条测试通知' }
505
- },
506
- {
507
- tag: 'div',
508
- text: { tag: 'lark_md', content: '**时间**: ' + new Date().toLocaleString('zh-CN') }
509
- },
510
- {
511
- tag: 'div',
512
- text: { tag: 'lark_md', content: '**设备**: ' + os.hostname() }
513
- }
514
- ]
515
- }
516
- });
517
-
518
- const options = {
519
- hostname: urlObj.hostname,
520
- port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
521
- path: urlObj.pathname + urlObj.search,
522
- method: 'POST',
523
- headers: {
524
- 'Content-Type': 'application/json',
525
- 'Content-Length': Buffer.byteLength(data)
526
- },
527
- timeout: 10000
528
- };
529
-
530
- const reqModule = urlObj.protocol === 'https:' ? https : http;
531
- const request = reqModule.request(options, (response) => {
532
- let body = '';
533
- response.on('data', chunk => body += chunk);
534
- response.on('end', () => {
535
- res.json({ success: true, message: '飞书测试通知已发送' });
536
- });
537
- });
538
-
539
- request.on('error', (e) => {
540
- res.status(500).json({ error: '飞书通知发送失败: ' + e.message });
541
- });
542
-
543
- request.write(data);
544
- request.end();
545
- } else {
546
- // 测试系统通知
547
- const command = generateSystemNotificationCommand(type || 'notification');
548
- const { execSync } = require('child_process');
549
- execSync(command, { stdio: 'ignore', windowsHide: true });
550
- res.json({ success: true, message: '系统测试通知已发送' });
551
- }
35
+ await notificationHooks.testNotification(req.body || {});
36
+ res.json({
37
+ success: true,
38
+ message: req.body?.testFeishu ? '飞书测试通知已发送' : '系统测试通知已发送'
39
+ });
552
40
  } catch (error) {
553
41
  console.error('Error testing notification:', error);
554
- res.status(500).json({ error: error.message });
42
+ res.status(error.statusCode || 500).json({ error: error.message });
555
43
  }
556
44
  });
557
45
 
558
- // 导出初始化函数供服务启动时调用
559
46
  module.exports = router;
560
- module.exports.initDefaultHooks = initDefaultHooks;
47
+ module.exports.initDefaultHooks = notificationHooks.initDefaultHooks;
561
48
  module.exports._test = {
562
- generateSystemNotificationCommand,
563
- parseStopHookStatus,
564
- parseNotifyTypeMarker,
565
- buildStopHookCommand,
49
+ generateSystemNotificationCommand: notificationHooks._test.generateSystemNotificationCommand,
50
+ parseStopHookStatus: notificationHooks._test.parseStopHookStatus,
51
+ parseNotifyTypeMarker: notificationHooks._test.parseNotifyTypeMarker,
52
+ buildStopHookCommand: notificationHooks._test.buildStopHookCommand,
566
53
  normalizeWindowsHomePath,
567
54
  resolvePreferredHomeDir,
568
- shouldRepairStopHook
55
+ shouldRepairStopHook: notificationHooks._test.shouldRepairStopHook
569
56
  };
@@ -223,7 +223,7 @@ router.post('/stop', async (req, res) => {
223
223
  // 停止动态切换后回到单渠道模式:保留激活渠道,禁用其他渠道
224
224
  if (activeChannel) {
225
225
  const { applyChannelToSettings } = require('../services/codex-channels');
226
- applyChannelToSettings(activeChannel.id, { pruneProviders: true });
226
+ applyChannelToSettings(activeChannel.id);
227
227
  console.log(`[Codex Proxy] Single-channel mode restored: ${activeChannel.name}`);
228
228
  }
229
229