coding-tool-x 3.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.
Files changed (185) hide show
  1. package/CHANGELOG.md +599 -0
  2. package/LICENSE +21 -0
  3. package/README.md +439 -0
  4. package/bin/ctx.js +8 -0
  5. package/dist/web/assets/Analytics-DN_YsnkW.js +39 -0
  6. package/dist/web/assets/Analytics-DuYvId7u.css +1 -0
  7. package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
  8. package/dist/web/assets/ConfigTemplates-DpXIMy0p.js +1 -0
  9. package/dist/web/assets/Home-38JTUlYt.js +1 -0
  10. package/dist/web/assets/Home-CjupSEWE.css +1 -0
  11. package/dist/web/assets/PluginManager-CX2tgq2H.js +1 -0
  12. package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
  13. package/dist/web/assets/ProjectList-C1lDcsn6.js +1 -0
  14. package/dist/web/assets/ProjectList-oJIyIRkP.css +1 -0
  15. package/dist/web/assets/SessionList-C55tjV7i.css +1 -0
  16. package/dist/web/assets/SessionList-CZ7T6rVx.js +1 -0
  17. package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
  18. package/dist/web/assets/SkillManager-DLN9f79y.js +1 -0
  19. package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
  20. package/dist/web/assets/WorkspaceManager-DxlHZkpZ.js +1 -0
  21. package/dist/web/assets/icons-DRrXwWZi.js +1 -0
  22. package/dist/web/assets/index-CetESrXw.css +1 -0
  23. package/dist/web/assets/index-Cfvn-2Gb.js +2 -0
  24. package/dist/web/assets/markdown-BfC0goYb.css +10 -0
  25. package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
  26. package/dist/web/assets/naive-ui-DlpKk-8M.js +1 -0
  27. package/dist/web/assets/vendors-DMjSfzlv.js +7 -0
  28. package/dist/web/assets/vue-vendor-DET08QYg.js +45 -0
  29. package/dist/web/favicon.ico +0 -0
  30. package/dist/web/index.html +20 -0
  31. package/dist/web/logo.png +0 -0
  32. package/docs/bannel.png +0 -0
  33. package/docs/home.png +0 -0
  34. package/docs/logo.png +0 -0
  35. package/docs/model-redirection.md +251 -0
  36. package/docs/multi-channel-load-balancing.md +249 -0
  37. package/package.json +80 -0
  38. package/src/commands/channels.js +551 -0
  39. package/src/commands/cli-type.js +101 -0
  40. package/src/commands/daemon.js +365 -0
  41. package/src/commands/doctor.js +333 -0
  42. package/src/commands/export-config.js +205 -0
  43. package/src/commands/list.js +222 -0
  44. package/src/commands/logs.js +261 -0
  45. package/src/commands/plugin.js +585 -0
  46. package/src/commands/port-config.js +135 -0
  47. package/src/commands/proxy-control.js +264 -0
  48. package/src/commands/proxy.js +152 -0
  49. package/src/commands/resume.js +137 -0
  50. package/src/commands/search.js +190 -0
  51. package/src/commands/security.js +37 -0
  52. package/src/commands/stats.js +398 -0
  53. package/src/commands/switch.js +48 -0
  54. package/src/commands/toggle-proxy.js +247 -0
  55. package/src/commands/ui.js +99 -0
  56. package/src/commands/update.js +97 -0
  57. package/src/commands/workspace.js +454 -0
  58. package/src/config/default.js +69 -0
  59. package/src/config/loader.js +149 -0
  60. package/src/config/model-metadata.js +167 -0
  61. package/src/config/model-metadata.json +125 -0
  62. package/src/config/model-pricing.js +35 -0
  63. package/src/config/paths.js +190 -0
  64. package/src/index.js +680 -0
  65. package/src/plugins/constants.js +15 -0
  66. package/src/plugins/event-bus.js +54 -0
  67. package/src/plugins/manifest-validator.js +129 -0
  68. package/src/plugins/plugin-api.js +128 -0
  69. package/src/plugins/plugin-installer.js +601 -0
  70. package/src/plugins/plugin-loader.js +229 -0
  71. package/src/plugins/plugin-manager.js +170 -0
  72. package/src/plugins/registry.js +152 -0
  73. package/src/plugins/schema/plugin-manifest.json +115 -0
  74. package/src/reset-config.js +94 -0
  75. package/src/server/api/agents.js +826 -0
  76. package/src/server/api/aliases.js +36 -0
  77. package/src/server/api/channels.js +368 -0
  78. package/src/server/api/claude-hooks.js +480 -0
  79. package/src/server/api/codex-channels.js +417 -0
  80. package/src/server/api/codex-projects.js +104 -0
  81. package/src/server/api/codex-proxy.js +195 -0
  82. package/src/server/api/codex-sessions.js +483 -0
  83. package/src/server/api/codex-statistics.js +57 -0
  84. package/src/server/api/commands.js +482 -0
  85. package/src/server/api/config-export.js +212 -0
  86. package/src/server/api/config-registry.js +357 -0
  87. package/src/server/api/config-sync.js +155 -0
  88. package/src/server/api/config-templates.js +248 -0
  89. package/src/server/api/config.js +521 -0
  90. package/src/server/api/convert.js +260 -0
  91. package/src/server/api/dashboard.js +142 -0
  92. package/src/server/api/env.js +144 -0
  93. package/src/server/api/favorites.js +77 -0
  94. package/src/server/api/gemini-channels.js +366 -0
  95. package/src/server/api/gemini-projects.js +91 -0
  96. package/src/server/api/gemini-proxy.js +173 -0
  97. package/src/server/api/gemini-sessions.js +376 -0
  98. package/src/server/api/gemini-statistics.js +57 -0
  99. package/src/server/api/health-check.js +31 -0
  100. package/src/server/api/mcp.js +399 -0
  101. package/src/server/api/opencode-channels.js +419 -0
  102. package/src/server/api/opencode-projects.js +99 -0
  103. package/src/server/api/opencode-proxy.js +207 -0
  104. package/src/server/api/opencode-sessions.js +327 -0
  105. package/src/server/api/opencode-statistics.js +57 -0
  106. package/src/server/api/plugins.js +463 -0
  107. package/src/server/api/pm2-autostart.js +269 -0
  108. package/src/server/api/projects.js +124 -0
  109. package/src/server/api/prompts.js +279 -0
  110. package/src/server/api/proxy.js +306 -0
  111. package/src/server/api/security.js +53 -0
  112. package/src/server/api/sessions.js +514 -0
  113. package/src/server/api/settings.js +142 -0
  114. package/src/server/api/skills.js +570 -0
  115. package/src/server/api/statistics.js +238 -0
  116. package/src/server/api/ui-config.js +64 -0
  117. package/src/server/api/workspaces.js +456 -0
  118. package/src/server/codex-proxy-server.js +681 -0
  119. package/src/server/dev-server.js +26 -0
  120. package/src/server/gemini-proxy-server.js +610 -0
  121. package/src/server/index.js +422 -0
  122. package/src/server/opencode-proxy-server.js +4771 -0
  123. package/src/server/proxy-server.js +669 -0
  124. package/src/server/services/agents-service.js +1137 -0
  125. package/src/server/services/alias.js +71 -0
  126. package/src/server/services/channel-health.js +234 -0
  127. package/src/server/services/channel-scheduler.js +240 -0
  128. package/src/server/services/channels.js +447 -0
  129. package/src/server/services/codex-channels.js +705 -0
  130. package/src/server/services/codex-config.js +90 -0
  131. package/src/server/services/codex-parser.js +322 -0
  132. package/src/server/services/codex-sessions.js +936 -0
  133. package/src/server/services/codex-settings-manager.js +619 -0
  134. package/src/server/services/codex-speed-test-template.json +24 -0
  135. package/src/server/services/codex-statistics-service.js +161 -0
  136. package/src/server/services/commands-service.js +574 -0
  137. package/src/server/services/config-export-service.js +1165 -0
  138. package/src/server/services/config-registry-service.js +828 -0
  139. package/src/server/services/config-sync-manager.js +941 -0
  140. package/src/server/services/config-sync-service.js +504 -0
  141. package/src/server/services/config-templates-service.js +913 -0
  142. package/src/server/services/enhanced-cache.js +196 -0
  143. package/src/server/services/env-checker.js +409 -0
  144. package/src/server/services/env-manager.js +436 -0
  145. package/src/server/services/favorites.js +165 -0
  146. package/src/server/services/format-converter.js +620 -0
  147. package/src/server/services/gemini-channels.js +459 -0
  148. package/src/server/services/gemini-config.js +73 -0
  149. package/src/server/services/gemini-sessions.js +689 -0
  150. package/src/server/services/gemini-settings-manager.js +263 -0
  151. package/src/server/services/gemini-statistics-service.js +157 -0
  152. package/src/server/services/health-check.js +85 -0
  153. package/src/server/services/mcp-client.js +790 -0
  154. package/src/server/services/mcp-service.js +1732 -0
  155. package/src/server/services/model-detector.js +1245 -0
  156. package/src/server/services/network-access.js +80 -0
  157. package/src/server/services/opencode-channels.js +366 -0
  158. package/src/server/services/opencode-gateway-adapters.js +1168 -0
  159. package/src/server/services/opencode-gateway-converter.js +639 -0
  160. package/src/server/services/opencode-sessions.js +931 -0
  161. package/src/server/services/opencode-settings-manager.js +478 -0
  162. package/src/server/services/opencode-statistics-service.js +161 -0
  163. package/src/server/services/plugins-service.js +1268 -0
  164. package/src/server/services/prompts-service.js +534 -0
  165. package/src/server/services/proxy-runtime.js +79 -0
  166. package/src/server/services/repo-scanner-base.js +708 -0
  167. package/src/server/services/request-logger.js +130 -0
  168. package/src/server/services/response-decoder.js +21 -0
  169. package/src/server/services/security-config.js +131 -0
  170. package/src/server/services/session-cache.js +127 -0
  171. package/src/server/services/session-converter.js +577 -0
  172. package/src/server/services/sessions.js +900 -0
  173. package/src/server/services/settings-manager.js +163 -0
  174. package/src/server/services/skill-service.js +1482 -0
  175. package/src/server/services/speed-test.js +1146 -0
  176. package/src/server/services/statistics-service.js +1043 -0
  177. package/src/server/services/ui-config.js +132 -0
  178. package/src/server/services/workspace-service.js +830 -0
  179. package/src/server/utils/pricing.js +73 -0
  180. package/src/server/websocket-server.js +513 -0
  181. package/src/ui/menu.js +139 -0
  182. package/src/ui/prompts.js +100 -0
  183. package/src/utils/format.js +43 -0
  184. package/src/utils/port-helper.js +108 -0
  185. package/src/utils/session.js +240 -0
package/src/index.js ADDED
@@ -0,0 +1,680 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CC-CLI - Claude Code 会话管理工具
5
+ * 主入口文件
6
+ */
7
+
8
+ const { loadConfig } = require('./config/loader');
9
+ const { showMainMenu } = require('./ui/menu');
10
+ const { handleList } = require('./commands/list');
11
+ const { handleSearch } = require('./commands/search');
12
+ const { switchProject } = require('./commands/switch');
13
+ const { resetConfig } = require('./reset-config');
14
+ const { handleChannelManagement, handleAddChannel, handleChannelStatus } = require('./commands/channels');
15
+ const { handleToggleProxy } = require('./commands/toggle-proxy');
16
+ const { handlePortConfig } = require('./commands/port-config');
17
+ const { handleSwitchCliType } = require('./commands/cli-type');
18
+ const { handleStart, handleStop, handleRestart, handleStatus } = require('./commands/daemon');
19
+ const { handleProxyStart: proxyStart, handleProxyStop: proxyStop, handleProxyRestart, handleProxyStatus: proxyStatus } = require('./commands/proxy-control');
20
+ const { handleLogs } = require('./commands/logs');
21
+ const { handleStats, handleStatsExport } = require('./commands/stats');
22
+ const { handleDoctor } = require('./commands/doctor');
23
+ const { handleUpdate } = require('./commands/update');
24
+ const { workspaceMenu } = require('./commands/workspace');
25
+ const { ensureStorageDirMigrated } = require('./config/paths');
26
+ const PluginManager = require('./plugins/plugin-manager');
27
+ const eventBus = require('./plugins/event-bus');
28
+ const chalk = require('chalk');
29
+ const inquirer = require('inquirer');
30
+ const path = require('path');
31
+ const fs = require('fs');
32
+
33
+ // 读取版本号
34
+ function getVersion() {
35
+ const packagePath = path.join(__dirname, '../package.json');
36
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
37
+ return packageJson.version;
38
+ }
39
+
40
+ // 显示帮助信息
41
+ function showHelp() {
42
+ const version = getVersion();
43
+ console.log(chalk.cyan.bold(`\nCODING-TOOL v${version}`));
44
+ console.log(chalk.gray('Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控\n'));
45
+
46
+ console.log(chalk.yellow('🚀 服务管理:'));
47
+ console.log(' ctx start 启动所有服务(后台运行)');
48
+ console.log(' ctx stop 停止所有服务');
49
+ console.log(' ctx restart 重启所有服务');
50
+ console.log(' ctx status 查看服务状态\n');
51
+
52
+ console.log(chalk.yellow('📱 UI 管理:'));
53
+ console.log(' ctx ui 前台启动 Web UI(仅本地访问)');
54
+ console.log(' ctx ui --host 前台启动 Web UI(允许 LAN 访问)');
55
+ console.log(' ctx ui start 后台启动 Web UI');
56
+ console.log(' ctx ui start --host 后台启动 Web UI(允许 LAN 访问)');
57
+ console.log(' ctx ui stop 停止 Web UI');
58
+ console.log(' ctx ui restart 重启 Web UI\n');
59
+
60
+ console.log(chalk.yellow('🔌 代理管理:'));
61
+ console.log(' ctx claude start 启动 Claude 代理');
62
+ console.log(' ctx claude stop 停止 Claude 代理');
63
+ console.log(' ctx claude status 查看 Claude 代理状态');
64
+ console.log(' ctx codex start 启动 Codex 代理');
65
+ console.log(' ctx gemini start 启动 Gemini 代理');
66
+ console.log(' ctx opencode start 启动 OpenCode 代理');
67
+ console.log(chalk.gray(' (codex/gemini/opencode 命令与 claude 类似)\n'));
68
+
69
+ console.log(chalk.yellow('📋 日志管理:'));
70
+ console.log(' ctx logs 查看所有日志');
71
+ console.log(' ctx logs ui 查看 UI 日志');
72
+ console.log(' ctx logs claude 查看 Claude 日志');
73
+ console.log(' ctx logs --lines 100 查看最近 100 行');
74
+ console.log(' ctx logs --follow 实时跟踪日志');
75
+ console.log(' ctx logs --clear 清空日志\n');
76
+
77
+ console.log(chalk.yellow('📊 统计信息:'));
78
+ console.log(' ctx stats 查看总体统计');
79
+ console.log(' ctx stats claude 查看 Claude 统计');
80
+ console.log(' ctx stats --today 查看今日统计');
81
+ console.log(' ctx stats export 导出统计数据\n');
82
+
83
+ console.log(chalk.yellow('🛠️ 其他命令:'));
84
+ console.log(' ctx update 检查并更新到最新版本');
85
+ console.log(' ctx doctor 系统诊断');
86
+ console.log(' ctx port 配置端口');
87
+ console.log(' ctx reset 重置配置');
88
+ console.log(' ctx security reset 关闭访问密码');
89
+ console.log(' ctx --version, -v 显示版本');
90
+ console.log(' ctx --help, -h 显示帮助\n');
91
+
92
+ console.log(chalk.yellow('🔌 插件管理:'));
93
+ console.log(' ctx plugin list 列出已安装插件');
94
+ console.log(' ctx plugin install <url> 从 Git 安装插件');
95
+ console.log(' ctx plugin remove <name> 卸载插件');
96
+ console.log(' ctx plugin enable <name> 启用插件');
97
+ console.log(' ctx plugin disable <name> 禁用插件');
98
+ console.log(' ctx plugin info <name> 查看插件详情');
99
+ console.log(' ctx plugin config <name> 配置插件');
100
+ console.log(' ctx plugin update <name> 更新插件');
101
+ console.log(' ctx plugin update --all 更新所有插件\n');
102
+
103
+ console.log(chalk.yellow('💡 快速开始:'));
104
+ console.log(chalk.gray(' $ ctx start # 后台启动服务(推荐)'));
105
+ console.log(chalk.gray(' $ ctx status # 查看服务状态'));
106
+ console.log(chalk.gray(' $ ctx logs # 查看实时日志'));
107
+ console.log(chalk.gray(' $ ctx stop # 停止服务\n'));
108
+
109
+ console.log(chalk.yellow('⭐ 开机自启(可选):'));
110
+ console.log(chalk.gray(' $ pm2 startup # 启用开机自启'));
111
+ console.log(chalk.gray(' $ pm2 save # 保存配置'));
112
+ console.log(chalk.gray(' $ pm2 unstartup # 禁用开机自启\n'));
113
+
114
+ console.log(chalk.yellow('更多信息:'));
115
+ console.log(chalk.gray(' 官网: https://github.com/ZeaoZhang/coding-tool'));
116
+ console.log(chalk.gray(' 文档: 运行 ctx start 后在 Web UI 右上角点击帮助'));
117
+ console.log(chalk.gray(' 问题: https://github.com/ZeaoZhang/coding-tool/issues\n'));
118
+ }
119
+
120
+ // 全局错误处理
121
+ process.on('uncaughtException', (err) => {
122
+ // 忽略终端相关的错误(通常在 Ctrl+C 时发生)
123
+ if (err.code === 'EIO' || err.code === 'ENOTTY' || err.code === 'EPIPE') {
124
+ process.exit(0);
125
+ }
126
+ throw err;
127
+ });
128
+
129
+ // 处理 SIGINT 信号(Ctrl+C)
130
+ process.on('SIGINT', async () => {
131
+ eventBus.emitSync('cli:shutdown', {});
132
+ PluginManager.shutdownPlugins();
133
+ process.exit(0);
134
+ });
135
+
136
+ /**
137
+ * 主函数
138
+ */
139
+ async function main() {
140
+ ensureStorageDirMigrated();
141
+
142
+ // 处理命令行参数
143
+ const args = process.argv.slice(2);
144
+
145
+ // --version 或 -v - 显示版本号
146
+ if (args[0] === '--version' || args[0] === '-v') {
147
+ console.log(getVersion());
148
+ return;
149
+ }
150
+
151
+ // --help 或 -h - 显示帮助信息
152
+ if (args[0] === '--help' || args[0] === '-h') {
153
+ showHelp();
154
+ return;
155
+ }
156
+
157
+ // daemon 命令兼容(保持与 README 中旧命令一致)
158
+ if (args[0] === 'daemon') {
159
+ const subCommand = args[1] || 'status';
160
+
161
+ if (subCommand === 'start') {
162
+ await handleStart();
163
+ return;
164
+ }
165
+ if (subCommand === 'stop') {
166
+ await handleStop();
167
+ return;
168
+ }
169
+ if (subCommand === 'restart') {
170
+ await handleRestart();
171
+ return;
172
+ }
173
+ if (subCommand === 'status') {
174
+ await handleStatus();
175
+ return;
176
+ }
177
+ if (subCommand === 'logs') {
178
+ const options = {};
179
+ for (let i = 2; i < args.length; i++) {
180
+ if (args[i] === '--lines' && args[i + 1]) {
181
+ options.lines = parseInt(args[i + 1], 10);
182
+ i++;
183
+ } else if (args[i] === '--follow' || args[i] === '-f') {
184
+ options.follow = true;
185
+ } else if (args[i] === '--clear') {
186
+ options.clear = true;
187
+ }
188
+ }
189
+ await handleLogs('ui', options);
190
+ return;
191
+ }
192
+
193
+ console.log(chalk.red(`\n❌ 未知 daemon 子命令: ${subCommand}\n`));
194
+ console.log(chalk.gray('支持的命令: start, stop, restart, status, logs\n'));
195
+ return;
196
+ }
197
+
198
+ // reset 命令 - 恢复默认配置
199
+ if (args[0] === 'reset') {
200
+ await resetConfig();
201
+ return;
202
+ }
203
+
204
+ // security 命令 - 安全设置
205
+ if (args[0] === 'security') {
206
+ const { showSecurityHelp, handleSecurityReset } = require('./commands/security');
207
+ const subCommand = args[1] || 'help';
208
+
209
+ if (subCommand === 'reset' || subCommand === 'disable' || subCommand === 'off') {
210
+ await handleSecurityReset();
211
+ } else {
212
+ showSecurityHelp();
213
+ }
214
+ return;
215
+ }
216
+
217
+ // start 命令 - 启动服务(后台)
218
+ if (args[0] === 'start') {
219
+ await handleStart();
220
+ return;
221
+ }
222
+
223
+ // stop 命令 - 停止服务
224
+ if (args[0] === 'stop') {
225
+ await handleStop();
226
+ return;
227
+ }
228
+
229
+ // restart 命令 - 重启服务
230
+ if (args[0] === 'restart') {
231
+ await handleRestart();
232
+ return;
233
+ }
234
+
235
+ // status 命令 - 查看服务状态
236
+ if (args[0] === 'status') {
237
+ await handleStatus();
238
+ return;
239
+ }
240
+
241
+ // ui 命令 - Web UI 管理
242
+ if (args[0] === 'ui') {
243
+ const subCommand = args[1];
244
+ if (subCommand === 'start') {
245
+ await handleStart(); // UI start 实际上就是启动整个服务
246
+ } else if (subCommand === 'stop') {
247
+ await handleStop();
248
+ } else if (subCommand === 'restart') {
249
+ await handleRestart();
250
+ } else {
251
+ // 默认前台运行
252
+ const { handleUI } = require('./commands/ui');
253
+ await handleUI();
254
+ }
255
+ return;
256
+ }
257
+
258
+ // claude/codex/gemini/opencode 代理管理命令
259
+ const channels = ['claude', 'codex', 'gemini', 'opencode'];
260
+ if (channels.includes(args[0])) {
261
+ const channel = args[0];
262
+ const action = args[1] || 'status';
263
+
264
+ switch (action) {
265
+ case 'start':
266
+ await proxyStart(channel);
267
+ break;
268
+ case 'stop':
269
+ await proxyStop(channel);
270
+ break;
271
+ case 'restart':
272
+ await handleProxyRestart(channel);
273
+ break;
274
+ case 'status':
275
+ await proxyStatus(channel);
276
+ break;
277
+ default:
278
+ console.log(chalk.red(`\n❌ 未知操作: ${action}\n`));
279
+ console.log(chalk.gray('支持的操作: start, stop, restart, status\n'));
280
+ }
281
+ return;
282
+ }
283
+
284
+ // logs 命令 - 日志管理
285
+ if (args[0] === 'logs') {
286
+ const type = args[1] && !args[1].startsWith('--') ? args[1] : null;
287
+ const options = {};
288
+
289
+ // 解析选项
290
+ for (let i = type ? 2 : 1; i < args.length; i++) {
291
+ if (args[i] === '--lines' && args[i + 1]) {
292
+ options.lines = parseInt(args[i + 1]);
293
+ i++;
294
+ } else if (args[i] === '--follow' || args[i] === '-f') {
295
+ options.follow = true;
296
+ } else if (args[i] === '--clear') {
297
+ options.clear = true;
298
+ }
299
+ }
300
+
301
+ await handleLogs(type, options);
302
+ return;
303
+ }
304
+
305
+ // stats 命令 - 统计信息
306
+ if (args[0] === 'stats') {
307
+ if (args[1] === 'export') {
308
+ const type = args[2] || null;
309
+ await handleStatsExport(type);
310
+ } else {
311
+ const type = args[1] && !args[1].startsWith('--') ? args[1] : null;
312
+ const options = {};
313
+
314
+ // 解析选项
315
+ for (let i = type ? 2 : 1; i < args.length; i++) {
316
+ if (args[i] === '--today') options.today = true;
317
+ else if (args[i] === '--week') options.week = true;
318
+ else if (args[i] === '--month') options.month = true;
319
+ }
320
+
321
+ await handleStats(type, options);
322
+ }
323
+ return;
324
+ }
325
+
326
+ // doctor 命令 - 系统诊断
327
+ if (args[0] === 'doctor') {
328
+ await handleDoctor();
329
+ return;
330
+ }
331
+
332
+ // update 命令 - 检查并更新到最新版本
333
+ if (args[0] === 'update') {
334
+ const checkOnly = args.includes('--check');
335
+ await handleUpdate({ checkOnly });
336
+ return;
337
+ }
338
+
339
+ // port 命令 - 配置端口
340
+ if (args[0] === 'port') {
341
+ await handlePortConfig();
342
+ return;
343
+ }
344
+
345
+ // 代理命令
346
+ if (args[0] === 'proxy') {
347
+ const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
348
+ const subCommand = args[1] || 'start';
349
+
350
+ switch (subCommand) {
351
+ case 'start':
352
+ await handleProxyStart();
353
+ return;
354
+
355
+ case 'stop':
356
+ await handleProxyStop();
357
+ return;
358
+
359
+ case 'status':
360
+ handleProxyStatus();
361
+ return;
362
+
363
+ default:
364
+ // 默认执行 start
365
+ await handleProxyStart();
366
+ return;
367
+ }
368
+ }
369
+
370
+ // plugin 命令 - 插件管理
371
+ if (args[0] === 'plugin') {
372
+ const { handlePluginCommand } = require('./commands/plugin');
373
+ await handlePluginCommand(args.slice(1));
374
+ return;
375
+ }
376
+
377
+ // 加载配置
378
+ let config = loadConfig();
379
+
380
+ // 初始化插件系统
381
+ const pluginResult = PluginManager.initializePlugins({ config, args });
382
+ if (pluginResult.loaded > 0) {
383
+ console.log(chalk.gray(`[Plugin] 已加载 ${pluginResult.loaded} 个插件`));
384
+ }
385
+ if (pluginResult.failed.length > 0) {
386
+ console.log(chalk.yellow(`[Plugin] ${pluginResult.failed.length} 个插件加载失败`));
387
+ }
388
+
389
+ // 检查是否为插件注册的命令
390
+ if (args[0] && PluginManager.isPluginCommand(args[0])) {
391
+ eventBus.emitSync('cli:command:before', { command: args[0], args: args.slice(1), config });
392
+ const result = await PluginManager.executePluginCommand(args[0], args.slice(1));
393
+ eventBus.emitSync('cli:command:after', { command: args[0], args: args.slice(1), result });
394
+ if (!result.success) {
395
+ console.error(chalk.red(result.error));
396
+ process.exit(1);
397
+ }
398
+ return;
399
+ }
400
+
401
+ while (true) {
402
+ // 显示主菜单
403
+ const action = await showMainMenu(config);
404
+
405
+ // 发送命令开始事件
406
+ eventBus.emitSync('cli:command:before', { command: action, args: [], config });
407
+
408
+ let result = { success: true };
409
+
410
+ switch (action) {
411
+ case 'list':
412
+ await handleList(config, async () => {
413
+ const switched = await switchProject(config);
414
+ if (switched) {
415
+ // 重新加载配置以获取最新的项目设置
416
+ config = loadConfig();
417
+ }
418
+ return switched;
419
+ }, true); // crossProject = true,跨项目显示最近会话
420
+ break;
421
+
422
+ case 'search':
423
+ await handleSearch(config, async () => {
424
+ const switched = await switchProject(config);
425
+ if (switched) {
426
+ config = loadConfig();
427
+ }
428
+ return switched;
429
+ });
430
+ break;
431
+
432
+ case 'switch':
433
+ const switched = await switchProject(config);
434
+ if (switched) {
435
+ config = loadConfig();
436
+ // 切换成功后自动进入会话列表
437
+ await handleList(config, async () => {
438
+ const switched = await switchProject(config);
439
+ if (switched) {
440
+ config = loadConfig();
441
+ }
442
+ return switched;
443
+ });
444
+ }
445
+ break;
446
+
447
+ case 'workspace':
448
+ await workspaceMenu();
449
+ break;
450
+
451
+ case 'switch-cli-type':
452
+ await handleSwitchCliType();
453
+ config = loadConfig(); // 重新加载配置以获取新的类型
454
+ break;
455
+
456
+ case 'switch-channel':
457
+ await handleChannelManagement();
458
+ break;
459
+ case 'channel-status':
460
+ await handleChannelStatus();
461
+ break;
462
+
463
+ case 'toggle-proxy':
464
+ await handleToggleProxy();
465
+ break;
466
+
467
+ case 'add-channel':
468
+ await handleAddChannel();
469
+ break;
470
+
471
+ case 'ui': {
472
+ const { handleUI } = require('./commands/ui');
473
+ await handleUI();
474
+ break;
475
+ }
476
+
477
+ case 'port-config':
478
+ await handlePortConfig();
479
+ break;
480
+
481
+ case 'reset':
482
+ await resetConfig();
483
+ break;
484
+
485
+ case 'plugin-menu': {
486
+ const { handlePluginCommand } = require('./commands/plugin');
487
+
488
+ // Show plugin management submenu
489
+ const pluginAction = await inquirer.prompt([{
490
+ type: 'list',
491
+ name: 'action',
492
+ message: chalk.cyan('选择插件操作:'),
493
+ choices: [
494
+ { name: '📋 列出已安装插件', value: 'list' },
495
+ { name: '📦 安装插件', value: 'install' },
496
+ { name: '🗑️ 卸载插件', value: 'remove' },
497
+ { name: '🔄 启用/禁用插件', value: 'toggle' },
498
+ { name: 'ℹ️ 查看插件信息', value: 'info' },
499
+ { name: '⬆️ 更新插件', value: 'update' },
500
+ { name: '⚙️ 配置插件', value: 'config' },
501
+ { name: '◀️ 返回主菜单', value: 'back' }
502
+ ]
503
+ }]);
504
+
505
+ if (pluginAction.action === 'back') {
506
+ break;
507
+ }
508
+
509
+ // Build args array based on action
510
+ let args = [pluginAction.action];
511
+
512
+ switch (pluginAction.action) {
513
+ case 'list':
514
+ // No additional args needed
515
+ await handlePluginCommand(args);
516
+ break;
517
+
518
+ case 'install': {
519
+ const installPrompt = await inquirer.prompt([{
520
+ type: 'input',
521
+ name: 'url',
522
+ message: '请输入插件 Git URL:',
523
+ validate: (input) => {
524
+ if (!input || input.trim() === '') {
525
+ return '请输入有效的 Git URL';
526
+ }
527
+ if (!input.match(/^https?:\/\//)) {
528
+ return '请输入完整的 URL (http:// 或 https://)';
529
+ }
530
+ return true;
531
+ }
532
+ }]);
533
+ args.push(installPrompt.url);
534
+ await handlePluginCommand(args);
535
+ break;
536
+ }
537
+
538
+ case 'remove': {
539
+ const removePrompt = await inquirer.prompt([{
540
+ type: 'input',
541
+ name: 'name',
542
+ message: '请输入要卸载的插件名称:',
543
+ validate: (input) => {
544
+ if (!input || input.trim() === '') {
545
+ return '请输入插件名称';
546
+ }
547
+ return true;
548
+ }
549
+ }]);
550
+ args.push(removePrompt.name);
551
+ await handlePluginCommand(args);
552
+ break;
553
+ }
554
+
555
+ case 'toggle': {
556
+ const toggleChoice = await inquirer.prompt([{
557
+ type: 'list',
558
+ name: 'operation',
559
+ message: '选择操作:',
560
+ choices: [
561
+ { name: '✅ 启用插件', value: 'enable' },
562
+ { name: '❌ 禁用插件', value: 'disable' }
563
+ ]
564
+ }]);
565
+
566
+ const togglePrompt = await inquirer.prompt([{
567
+ type: 'input',
568
+ name: 'name',
569
+ message: '请输入插件名称:',
570
+ validate: (input) => {
571
+ if (!input || input.trim() === '') {
572
+ return '请输入插件名称';
573
+ }
574
+ return true;
575
+ }
576
+ }]);
577
+
578
+ args = [toggleChoice.operation, togglePrompt.name];
579
+ await handlePluginCommand(args);
580
+ break;
581
+ }
582
+
583
+ case 'info': {
584
+ const infoPrompt = await inquirer.prompt([{
585
+ type: 'input',
586
+ name: 'name',
587
+ message: '请输入插件名称:',
588
+ validate: (input) => {
589
+ if (!input || input.trim() === '') {
590
+ return '请输入插件名称';
591
+ }
592
+ return true;
593
+ }
594
+ }]);
595
+ args.push(infoPrompt.name);
596
+ await handlePluginCommand(args);
597
+ break;
598
+ }
599
+
600
+ case 'update': {
601
+ const updateChoice = await inquirer.prompt([{
602
+ type: 'list',
603
+ name: 'option',
604
+ message: '选择更新选项:',
605
+ choices: [
606
+ { name: '🔄 更新指定插件', value: 'single' },
607
+ { name: '🔄 更新所有插件', value: 'all' }
608
+ ]
609
+ }]);
610
+
611
+ if (updateChoice.option === 'all') {
612
+ args.push('--all');
613
+ } else {
614
+ const updatePrompt = await inquirer.prompt([{
615
+ type: 'input',
616
+ name: 'name',
617
+ message: '请输入插件名称:',
618
+ validate: (input) => {
619
+ if (!input || input.trim() === '') {
620
+ return '请输入插件名称';
621
+ }
622
+ return true;
623
+ }
624
+ }]);
625
+ args.push(updatePrompt.name);
626
+ }
627
+ await handlePluginCommand(args);
628
+ break;
629
+ }
630
+
631
+ case 'config': {
632
+ const configPrompt = await inquirer.prompt([{
633
+ type: 'input',
634
+ name: 'name',
635
+ message: '请输入插件名称:',
636
+ validate: (input) => {
637
+ if (!input || input.trim() === '') {
638
+ return '请输入插件名称';
639
+ }
640
+ return true;
641
+ }
642
+ }]);
643
+ args.push(configPrompt.name);
644
+ await handlePluginCommand(args);
645
+ break;
646
+ }
647
+ }
648
+
649
+ // Wait for user to continue
650
+ await inquirer.prompt([{
651
+ type: 'input',
652
+ name: 'continue',
653
+ message: chalk.gray('按 Enter 继续...')
654
+ }]);
655
+ break;
656
+ }
657
+
658
+ case 'exit':
659
+ console.log('\n👋 再见!\n');
660
+ eventBus.emitSync('cli:shutdown', {});
661
+ PluginManager.shutdownPlugins();
662
+ process.exit(0);
663
+ break;
664
+
665
+ default:
666
+ console.log('未知操作');
667
+ result = { success: false, error: '未知操作' };
668
+ break;
669
+ }
670
+
671
+ // 发送命令完成事件
672
+ eventBus.emitSync('cli:command:after', { command: action, args: [], result });
673
+ }
674
+ }
675
+
676
+ // 启动应用
677
+ main().catch((error) => {
678
+ console.error('程序出错:', error);
679
+ process.exit(1);
680
+ });
@@ -0,0 +1,15 @@
1
+ const path = require('path');
2
+ const { PATHS, ensureStorageDirMigrated } = require('../config/paths');
3
+
4
+ ensureStorageDirMigrated();
5
+ const PLUGINS_DIR = path.join(PATHS.base, 'plugins');
6
+ const REGISTRY_FILE = path.join(PLUGINS_DIR, 'registry.json');
7
+ const CONFIG_DIR = path.join(PLUGINS_DIR, 'config');
8
+ const INSTALLED_DIR = path.join(PLUGINS_DIR, 'installed');
9
+
10
+ module.exports = {
11
+ PLUGINS_DIR,
12
+ REGISTRY_FILE,
13
+ CONFIG_DIR,
14
+ INSTALLED_DIR,
15
+ };