@adversity/coding-tool-x 3.1.0 → 3.1.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/CHANGELOG.md +39 -18
- package/README.md +8 -8
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-DvcbKKdS.js +1 -0
- package/dist/web/assets/Home-BJKPCBuk.css +1 -0
- package/dist/web/assets/Home-Cw-F_Wnu.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/PluginManager-jy_4GVxI.js +1 -0
- package/dist/web/assets/ProjectList-C1fQb9OW.css +1 -0
- package/dist/web/assets/ProjectList-Df1-NcNr.js +1 -0
- package/dist/web/assets/SessionList-BGJWyneI.css +1 -0
- package/dist/web/assets/SessionList-UWcZtC2r.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/SkillManager-IRdseMKB.js +1 -0
- package/dist/web/assets/Terminal-BasTyDut.js +1 -0
- package/dist/web/assets/Terminal-DGNJeVtc.css +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/WorkspaceManager-D-D2kK1V.js +1 -0
- package/dist/web/assets/icons-kcfLIMBB.js +1 -0
- package/dist/web/assets/index-CoB3zF0K.css +1 -0
- package/dist/web/assets/index-CryrSLv8.js +2 -0
- package/dist/web/assets/markdown-BfC0goYb.css +10 -0
- package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
- package/dist/web/assets/naive-ui-CSrLusZZ.js +1 -0
- package/dist/web/assets/{vendors-D2HHw_aW.js → vendors-CO3Upi1d.js} +2 -2
- package/dist/web/assets/vue-vendor-DqyWIXEb.js +45 -0
- package/dist/web/assets/xterm-6GBZ9nXN.css +32 -0
- package/dist/web/assets/xterm-BJzAjXCH.js +13 -0
- package/dist/web/index.html +8 -6
- package/package.json +4 -2
- package/src/commands/channels.js +48 -1
- package/src/commands/cli-type.js +4 -2
- package/src/commands/daemon.js +81 -12
- package/src/commands/doctor.js +10 -9
- package/src/commands/list.js +1 -1
- package/src/commands/logs.js +6 -4
- package/src/commands/port-config.js +24 -4
- package/src/commands/proxy-control.js +12 -6
- package/src/commands/search.js +1 -1
- package/src/commands/security.js +3 -2
- package/src/commands/stats.js +226 -52
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +31 -6
- package/src/commands/update.js +97 -0
- package/src/commands/workspace.js +1 -1
- package/src/config/default.js +41 -2
- package/src/config/loader.js +74 -8
- package/src/config/model-metadata.js +415 -0
- package/src/config/model-pricing.js +23 -93
- package/src/config/paths.js +105 -33
- package/src/index.js +64 -3
- package/src/plugins/constants.js +3 -2
- package/src/plugins/plugin-api.js +1 -1
- package/src/reset-config.js +4 -2
- package/src/server/api/agents.js +57 -14
- package/src/server/api/channels.js +112 -33
- package/src/server/api/codex-channels.js +111 -18
- package/src/server/api/codex-proxy.js +14 -8
- package/src/server/api/commands.js +71 -18
- package/src/server/api/config-export.js +0 -6
- package/src/server/api/config-registry.js +11 -3
- package/src/server/api/config.js +376 -5
- package/src/server/api/convert.js +133 -0
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/gemini-channels.js +107 -18
- package/src/server/api/gemini-proxy.js +14 -8
- package/src/server/api/gemini-sessions.js +1 -1
- package/src/server/api/health-check.js +4 -3
- package/src/server/api/mcp.js +3 -3
- package/src/server/api/opencode-channels.js +497 -0
- package/src/server/api/opencode-projects.js +99 -0
- package/src/server/api/opencode-proxy.js +207 -0
- package/src/server/api/opencode-sessions.js +345 -0
- package/src/server/api/opencode-statistics.js +57 -0
- package/src/server/api/plugins.js +66 -19
- package/src/server/api/prompts.js +2 -2
- package/src/server/api/proxy.js +7 -4
- package/src/server/api/sessions.js +3 -0
- package/src/server/api/settings.js +111 -0
- package/src/server/api/skills.js +69 -18
- package/src/server/api/workspaces.js +78 -6
- package/src/server/codex-proxy-server.js +36 -22
- package/src/server/dev-server.js +1 -1
- package/src/server/gemini-proxy-server.js +21 -7
- package/src/server/index.js +174 -58
- package/src/server/opencode-proxy-server.js +5486 -0
- package/src/server/proxy-server.js +33 -22
- package/src/server/services/agents-service.js +61 -24
- package/src/server/services/channel-scheduler.js +9 -5
- package/src/server/services/channels.js +64 -37
- package/src/server/services/codex-channels.js +56 -43
- package/src/server/services/codex-sessions.js +105 -6
- package/src/server/services/codex-settings-manager.js +271 -49
- package/src/server/services/codex-statistics-service.js +2 -2
- package/src/server/services/commands-service.js +84 -25
- package/src/server/services/config-export-service.js +7 -45
- package/src/server/services/config-registry-service.js +63 -17
- package/src/server/services/config-sync-manager.js +160 -7
- package/src/server/services/config-templates-service.js +204 -51
- package/src/server/services/env-checker.js +50 -13
- package/src/server/services/env-manager.js +155 -19
- package/src/server/services/favorites.js +5 -3
- package/src/server/services/gemini-channels.js +33 -44
- package/src/server/services/gemini-statistics-service.js +2 -2
- package/src/server/services/mcp-service.js +350 -9
- package/src/server/services/model-detector.js +707 -221
- package/src/server/services/network-access.js +80 -0
- package/src/server/services/opencode-channels.js +208 -0
- package/src/server/services/opencode-gateway-converter.js +639 -0
- package/src/server/services/opencode-sessions.js +931 -0
- package/src/server/services/opencode-settings-manager.js +478 -0
- package/src/server/services/opencode-statistics-service.js +255 -0
- package/src/server/services/plugins-service.js +479 -22
- package/src/server/services/prompts-service.js +53 -11
- package/src/server/services/proxy-runtime.js +1 -1
- package/src/server/services/repo-scanner-base.js +1 -1
- package/src/server/services/response-decoder.js +21 -0
- package/src/server/services/security-config.js +1 -1
- package/src/server/services/session-cache.js +1 -1
- package/src/server/services/skill-service.js +300 -46
- package/src/server/services/speed-test.js +464 -186
- package/src/server/services/statistics-service.js +2 -2
- package/src/server/services/terminal-commands.js +10 -3
- package/src/server/services/terminal-config.js +1 -1
- package/src/server/services/ui-config.js +1 -1
- package/src/server/services/workspace-service.js +57 -100
- package/src/server/websocket-server.js +156 -8
- package/src/ui/menu.js +49 -40
- package/src/utils/port-helper.js +22 -8
- package/src/utils/session.js +5 -4
- package/dist/web/assets/icons-CO_2OFES.js +0 -1
- package/dist/web/assets/index-DI8QOi-E.js +0 -14
- package/dist/web/assets/index-uLHGdeZh.css +0 -41
- package/dist/web/assets/naive-ui-B1re3c-e.js +0 -1
- package/dist/web/assets/vue-vendor-6JaYHOiI.js +0 -44
- package/src/server/api/oauth.js +0 -294
- package/src/server/api/permissions.js +0 -385
- package/src/server/config/oauth-providers.js +0 -68
- package/src/server/services/oauth-callback-server.js +0 -284
- package/src/server/services/oauth-service.js +0 -378
- package/src/server/services/oauth-token-storage.js +0 -135
- package/src/server/services/permission-templates-service.js +0 -308
package/src/config/paths.js
CHANGED
|
@@ -1,90 +1,149 @@
|
|
|
1
1
|
// CTX 工具路径配置
|
|
2
|
-
// 所有路径统一使用 ~/.
|
|
2
|
+
// 所有路径统一使用 ~/.cc-tool 目录
|
|
3
|
+
const fs = require('fs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const os = require('os');
|
|
5
6
|
|
|
6
7
|
// 基础目录
|
|
7
|
-
const
|
|
8
|
+
const CC_TOOL_BASE_DIR = path.join(os.homedir(), '.cc-tool');
|
|
9
|
+
// 兼容旧变量名,避免外部调用方断裂
|
|
10
|
+
const CTX_BASE_DIR = CC_TOOL_BASE_DIR;
|
|
11
|
+
|
|
12
|
+
// 旧目录(升级时自动合并到 ~/.cc-tool)
|
|
13
|
+
const LEGACY_BASE_DIRS = [
|
|
14
|
+
path.join(os.homedir(), '.claude', 'ctx'),
|
|
15
|
+
path.join(os.homedir(), '.claude', 'cc-tool')
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
let migrationChecked = false;
|
|
19
|
+
|
|
20
|
+
function mergeDirectory(sourceDir, targetDir) {
|
|
21
|
+
if (!fs.existsSync(sourceDir)) return;
|
|
22
|
+
|
|
23
|
+
const sourceStat = fs.statSync(sourceDir);
|
|
24
|
+
if (sourceStat.isDirectory()) {
|
|
25
|
+
if (!fs.existsSync(targetDir)) {
|
|
26
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const entries = fs.readdirSync(sourceDir);
|
|
30
|
+
entries.forEach((entry) => {
|
|
31
|
+
mergeDirectory(path.join(sourceDir, entry), path.join(targetDir, entry));
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(targetDir)) {
|
|
37
|
+
fs.copyFileSync(sourceDir, targetDir);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ensureStorageDirMigrated() {
|
|
42
|
+
if (migrationChecked) {
|
|
43
|
+
return CC_TOOL_BASE_DIR;
|
|
44
|
+
}
|
|
45
|
+
migrationChecked = true;
|
|
46
|
+
|
|
47
|
+
if (!fs.existsSync(CC_TOOL_BASE_DIR)) {
|
|
48
|
+
fs.mkdirSync(CC_TOOL_BASE_DIR, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
LEGACY_BASE_DIRS.forEach((legacyDir) => {
|
|
52
|
+
if (!fs.existsSync(legacyDir)) return;
|
|
53
|
+
try {
|
|
54
|
+
mergeDirectory(legacyDir, CC_TOOL_BASE_DIR);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn(`[paths] 迁移目录失败: ${legacyDir} -> ${CC_TOOL_BASE_DIR}`, error.message);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return CC_TOOL_BASE_DIR;
|
|
61
|
+
}
|
|
8
62
|
|
|
9
63
|
// 路径配置
|
|
10
64
|
const PATHS = {
|
|
11
65
|
// 基础目录
|
|
12
|
-
base:
|
|
66
|
+
base: CC_TOOL_BASE_DIR,
|
|
13
67
|
|
|
14
68
|
// 项目目录(存储项目配置和会话)
|
|
15
|
-
projects: path.join(
|
|
69
|
+
projects: path.join(CC_TOOL_BASE_DIR, 'projects'),
|
|
16
70
|
|
|
17
71
|
// 配置文件目录
|
|
18
|
-
config: path.join(
|
|
19
|
-
configFile: path.join(
|
|
72
|
+
config: path.join(CC_TOOL_BASE_DIR, 'config'),
|
|
73
|
+
configFile: path.join(CC_TOOL_BASE_DIR, 'config.json'),
|
|
20
74
|
|
|
21
75
|
// 日志目录
|
|
22
|
-
logs: path.join(
|
|
76
|
+
logs: path.join(CC_TOOL_BASE_DIR, 'logs'),
|
|
23
77
|
|
|
24
78
|
// 别名存储
|
|
25
|
-
aliases: path.join(
|
|
79
|
+
aliases: path.join(CC_TOOL_BASE_DIR, 'aliases.json'),
|
|
26
80
|
|
|
27
81
|
// 收藏夹存储
|
|
28
|
-
favorites: path.join(
|
|
82
|
+
favorites: path.join(CC_TOOL_BASE_DIR, 'favorites.json'),
|
|
29
83
|
|
|
30
84
|
// 渠道配置
|
|
31
85
|
channels: {
|
|
32
|
-
claude: path.join(
|
|
33
|
-
codex: path.join(
|
|
34
|
-
gemini: path.join(
|
|
86
|
+
claude: path.join(CC_TOOL_BASE_DIR, 'channels.json'),
|
|
87
|
+
codex: path.join(CC_TOOL_BASE_DIR, 'codex-channels.json'),
|
|
88
|
+
gemini: path.join(CC_TOOL_BASE_DIR, 'gemini-channels.json'),
|
|
89
|
+
opencode: path.join(CC_TOOL_BASE_DIR, 'opencode-channels.json')
|
|
35
90
|
},
|
|
36
91
|
|
|
37
92
|
// 激活渠道标记
|
|
38
93
|
activeChannel: {
|
|
39
|
-
claude: path.join(
|
|
40
|
-
codex: path.join(
|
|
41
|
-
gemini: path.join(
|
|
94
|
+
claude: path.join(CC_TOOL_BASE_DIR, 'active-channel.json'),
|
|
95
|
+
codex: path.join(CC_TOOL_BASE_DIR, 'codex-active-channel.json'),
|
|
96
|
+
gemini: path.join(CC_TOOL_BASE_DIR, 'gemini-active-channel.json'),
|
|
97
|
+
opencode: path.join(CC_TOOL_BASE_DIR, 'opencode-active-channel.json')
|
|
42
98
|
},
|
|
43
99
|
|
|
44
100
|
// 统计数据
|
|
45
101
|
statistics: {
|
|
46
|
-
claude: path.join(
|
|
47
|
-
codex: path.join(
|
|
48
|
-
gemini: path.join(
|
|
102
|
+
claude: path.join(CC_TOOL_BASE_DIR, 'statistics.json'),
|
|
103
|
+
codex: path.join(CC_TOOL_BASE_DIR, 'codex-statistics.json'),
|
|
104
|
+
gemini: path.join(CC_TOOL_BASE_DIR, 'gemini-statistics.json'),
|
|
105
|
+
opencode: path.join(CC_TOOL_BASE_DIR, 'opencode-statistics.json'),
|
|
49
106
|
dailyStats: {
|
|
50
|
-
claude: path.join(
|
|
51
|
-
codex: path.join(
|
|
52
|
-
gemini: path.join(
|
|
107
|
+
claude: path.join(CC_TOOL_BASE_DIR, 'daily-stats'),
|
|
108
|
+
codex: path.join(CC_TOOL_BASE_DIR, 'codex-daily-stats'),
|
|
109
|
+
gemini: path.join(CC_TOOL_BASE_DIR, 'gemini-daily-stats'),
|
|
110
|
+
opencode: path.join(CC_TOOL_BASE_DIR, 'opencode-daily-stats')
|
|
53
111
|
}
|
|
54
112
|
},
|
|
55
113
|
|
|
56
114
|
// 会话缓存
|
|
57
|
-
sessionCache: path.join(
|
|
115
|
+
sessionCache: path.join(CC_TOOL_BASE_DIR, 'session-cache.json'),
|
|
58
116
|
|
|
59
117
|
// 项目顺序
|
|
60
|
-
projectOrder: path.join(
|
|
118
|
+
projectOrder: path.join(CC_TOOL_BASE_DIR, 'project-order.json'),
|
|
61
119
|
|
|
62
120
|
// 环境备份
|
|
63
|
-
envBackups: path.join(
|
|
121
|
+
envBackups: path.join(CC_TOOL_BASE_DIR, 'env-backups'),
|
|
64
122
|
|
|
65
123
|
// UI 配置
|
|
66
|
-
uiConfig: path.join(
|
|
124
|
+
uiConfig: path.join(CC_TOOL_BASE_DIR, 'ui-config.json'),
|
|
67
125
|
|
|
68
126
|
// 飞书通知脚本
|
|
69
|
-
notifyHook: path.join(
|
|
127
|
+
notifyHook: path.join(CC_TOOL_BASE_DIR, 'notify-hook.js'),
|
|
70
128
|
|
|
71
129
|
// Skills 安装目录(注意:这个仍使用 Claude 原生路径)
|
|
72
130
|
skills: path.join(os.homedir(), '.claude', 'skills'),
|
|
73
131
|
|
|
74
132
|
// MCP 配置(注意:这个仍使用 Claude 原生路径)
|
|
75
|
-
mcpConfig: path.join(
|
|
133
|
+
mcpConfig: path.join(CC_TOOL_BASE_DIR, 'mcp-config.json'),
|
|
76
134
|
|
|
77
135
|
// Terminal 配置
|
|
78
|
-
terminalConfig: path.join(
|
|
136
|
+
terminalConfig: path.join(CC_TOOL_BASE_DIR, 'terminal-config.json'),
|
|
79
137
|
|
|
80
138
|
// Prompts
|
|
81
|
-
prompts: path.join(
|
|
139
|
+
prompts: path.join(CC_TOOL_BASE_DIR, 'prompts.json'),
|
|
82
140
|
|
|
83
141
|
// 代理运行时状态
|
|
84
142
|
proxyRuntime: {
|
|
85
|
-
claude: path.join(
|
|
86
|
-
codex: path.join(
|
|
87
|
-
gemini: path.join(
|
|
143
|
+
claude: path.join(CC_TOOL_BASE_DIR, 'proxy-runtime.json'),
|
|
144
|
+
codex: path.join(CC_TOOL_BASE_DIR, 'codex-proxy-runtime.json'),
|
|
145
|
+
gemini: path.join(CC_TOOL_BASE_DIR, 'gemini-proxy-runtime.json'),
|
|
146
|
+
opencode: path.join(CC_TOOL_BASE_DIR, 'opencode-proxy-runtime.json')
|
|
88
147
|
}
|
|
89
148
|
};
|
|
90
149
|
|
|
@@ -111,11 +170,24 @@ const NATIVE_PATHS = {
|
|
|
111
170
|
env: path.join(os.homedir(), '.gemini', '.env'),
|
|
112
171
|
envBackup: path.join(os.homedir(), '.gemini', '.env.cc-tool-backup'),
|
|
113
172
|
tmp: path.join(os.homedir(), '.gemini', 'tmp')
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// OpenCode 原生配置
|
|
176
|
+
opencode: {
|
|
177
|
+
data: path.join(os.homedir(), '.local', 'share', 'opencode'),
|
|
178
|
+
config: path.join(os.homedir(), '.config', 'opencode'),
|
|
179
|
+
sessions: path.join(os.homedir(), '.local', 'share', 'opencode', 'storage', 'session'),
|
|
180
|
+
projects: path.join(os.homedir(), '.local', 'share', 'opencode', 'storage', 'project'),
|
|
181
|
+
messages: path.join(os.homedir(), '.local', 'share', 'opencode', 'storage', 'message'),
|
|
182
|
+
log: path.join(os.homedir(), '.local', 'share', 'opencode', 'log')
|
|
114
183
|
}
|
|
115
184
|
};
|
|
116
185
|
|
|
117
186
|
module.exports = {
|
|
118
187
|
PATHS,
|
|
119
188
|
NATIVE_PATHS,
|
|
120
|
-
CTX_BASE_DIR
|
|
189
|
+
CTX_BASE_DIR,
|
|
190
|
+
CC_TOOL_BASE_DIR,
|
|
191
|
+
LEGACY_BASE_DIRS,
|
|
192
|
+
ensureStorageDirMigrated
|
|
121
193
|
};
|
package/src/index.js
CHANGED
|
@@ -20,7 +20,9 @@ const { handleProxyStart: proxyStart, handleProxyStop: proxyStop, handleProxyRes
|
|
|
20
20
|
const { handleLogs } = require('./commands/logs');
|
|
21
21
|
const { handleStats, handleStatsExport } = require('./commands/stats');
|
|
22
22
|
const { handleDoctor } = require('./commands/doctor');
|
|
23
|
+
const { handleUpdate } = require('./commands/update');
|
|
23
24
|
const { workspaceMenu } = require('./commands/workspace');
|
|
25
|
+
const { ensureStorageDirMigrated } = require('./config/paths');
|
|
24
26
|
const PluginManager = require('./plugins/plugin-manager');
|
|
25
27
|
const eventBus = require('./plugins/event-bus');
|
|
26
28
|
const chalk = require('chalk');
|
|
@@ -61,7 +63,8 @@ function showHelp() {
|
|
|
61
63
|
console.log(' ctx claude status 查看 Claude 代理状态');
|
|
62
64
|
console.log(' ctx codex start 启动 Codex 代理');
|
|
63
65
|
console.log(' ctx gemini start 启动 Gemini 代理');
|
|
64
|
-
console.log(
|
|
66
|
+
console.log(' ctx opencode start 启动 OpenCode 代理');
|
|
67
|
+
console.log(chalk.gray(' (codex/gemini/opencode 命令与 claude 类似)\n'));
|
|
65
68
|
|
|
66
69
|
console.log(chalk.yellow('📋 日志管理:'));
|
|
67
70
|
console.log(' ctx logs 查看所有日志');
|
|
@@ -78,7 +81,9 @@ function showHelp() {
|
|
|
78
81
|
console.log(' ctx stats export 导出统计数据\n');
|
|
79
82
|
|
|
80
83
|
console.log(chalk.yellow('🛠️ 其他命令:'));
|
|
84
|
+
console.log(' ctx update 检查并更新到最新版本');
|
|
81
85
|
console.log(' ctx doctor 系统诊断');
|
|
86
|
+
console.log(' ctx port 配置端口');
|
|
82
87
|
console.log(' ctx reset 重置配置');
|
|
83
88
|
console.log(' ctx security reset 关闭访问密码');
|
|
84
89
|
console.log(' ctx --version, -v 显示版本');
|
|
@@ -132,6 +137,8 @@ process.on('SIGINT', async () => {
|
|
|
132
137
|
* 主函数
|
|
133
138
|
*/
|
|
134
139
|
async function main() {
|
|
140
|
+
ensureStorageDirMigrated();
|
|
141
|
+
|
|
135
142
|
// 处理命令行参数
|
|
136
143
|
const args = process.argv.slice(2);
|
|
137
144
|
|
|
@@ -147,6 +154,47 @@ async function main() {
|
|
|
147
154
|
return;
|
|
148
155
|
}
|
|
149
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
|
+
|
|
150
198
|
// reset 命令 - 恢复默认配置
|
|
151
199
|
if (args[0] === 'reset') {
|
|
152
200
|
await resetConfig();
|
|
@@ -207,8 +255,8 @@ async function main() {
|
|
|
207
255
|
return;
|
|
208
256
|
}
|
|
209
257
|
|
|
210
|
-
// claude/codex/gemini 代理管理命令
|
|
211
|
-
const channels = ['claude', 'codex', 'gemini'];
|
|
258
|
+
// claude/codex/gemini/opencode 代理管理命令
|
|
259
|
+
const channels = ['claude', 'codex', 'gemini', 'opencode'];
|
|
212
260
|
if (channels.includes(args[0])) {
|
|
213
261
|
const channel = args[0];
|
|
214
262
|
const action = args[1] || 'status';
|
|
@@ -281,6 +329,19 @@ async function main() {
|
|
|
281
329
|
return;
|
|
282
330
|
}
|
|
283
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
|
+
|
|
284
345
|
// 代理命令
|
|
285
346
|
if (args[0] === 'proxy') {
|
|
286
347
|
const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
|
package/src/plugins/constants.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const
|
|
2
|
+
const { PATHS, ensureStorageDirMigrated } = require('../config/paths');
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
ensureStorageDirMigrated();
|
|
5
|
+
const PLUGINS_DIR = path.join(PATHS.base, 'plugins');
|
|
5
6
|
const REGISTRY_FILE = path.join(PLUGINS_DIR, 'registry.json');
|
|
6
7
|
const CONFIG_DIR = path.join(PLUGINS_DIR, 'config');
|
|
7
8
|
const INSTALLED_DIR = path.join(PLUGINS_DIR, 'installed');
|
|
@@ -15,7 +15,7 @@ const packageJson = require('../../package.json');
|
|
|
15
15
|
function createPluginContext(pluginName, pluginConfig, pluginDir) {
|
|
16
16
|
const commandRegistry = new Map();
|
|
17
17
|
|
|
18
|
-
// Storage API - persists plugin data to ~/.
|
|
18
|
+
// Storage API - persists plugin data to ~/.cc-tool/plugins/config/<plugin-name>.json
|
|
19
19
|
const storage = createStorageAPI(pluginName);
|
|
20
20
|
|
|
21
21
|
// Logger API - prefixed logging
|
package/src/reset-config.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const { PATHS, ensureStorageDirMigrated } = require('./config/paths');
|
|
4
5
|
|
|
5
6
|
// 恢复配置到默认状态
|
|
6
7
|
async function resetConfig() {
|
|
7
8
|
console.log('\n开始恢复默认配置...\n');
|
|
9
|
+
ensureStorageDirMigrated();
|
|
8
10
|
|
|
9
11
|
try {
|
|
10
12
|
// 1. 尝试停止代理服务器(如果正在运行)
|
|
@@ -42,10 +44,10 @@ async function resetConfig() {
|
|
|
42
44
|
console.log('检测到代理配置,尝试恢复到正常渠道...');
|
|
43
45
|
|
|
44
46
|
// 读取激活的渠道
|
|
45
|
-
const activeChannelPath =
|
|
47
|
+
const activeChannelPath = PATHS.activeChannel.claude;
|
|
46
48
|
if (fs.existsSync(activeChannelPath)) {
|
|
47
49
|
const activeChannelData = JSON.parse(fs.readFileSync(activeChannelPath, 'utf8'));
|
|
48
|
-
const channelsPath =
|
|
50
|
+
const channelsPath = PATHS.channels.claude;
|
|
49
51
|
|
|
50
52
|
if (fs.existsSync(channelsPath)) {
|
|
51
53
|
const channelsData = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
|
package/src/server/api/agents.js
CHANGED
|
@@ -8,7 +8,24 @@ const express = require('express');
|
|
|
8
8
|
const { AgentsService } = require('../services/agents-service');
|
|
9
9
|
|
|
10
10
|
const router = express.Router();
|
|
11
|
-
const
|
|
11
|
+
const SUPPORTED_PLATFORMS = ['claude', 'opencode'];
|
|
12
|
+
const agentServices = new Map();
|
|
13
|
+
|
|
14
|
+
function resolvePlatform(rawPlatform) {
|
|
15
|
+
return SUPPORTED_PLATFORMS.includes(rawPlatform) ? rawPlatform : 'claude';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPlatform(req) {
|
|
19
|
+
return resolvePlatform(req.query?.platform || req.body?.platform);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getAgentsService(req) {
|
|
23
|
+
const platform = getPlatform(req);
|
|
24
|
+
if (!agentServices.has(platform)) {
|
|
25
|
+
agentServices.set(platform, new AgentsService(platform));
|
|
26
|
+
}
|
|
27
|
+
return { platform, service: agentServices.get(platform) };
|
|
28
|
+
}
|
|
12
29
|
|
|
13
30
|
/**
|
|
14
31
|
* 获取代理列表
|
|
@@ -17,11 +34,13 @@ const agentsService = new AgentsService();
|
|
|
17
34
|
*/
|
|
18
35
|
router.get('/', (req, res) => {
|
|
19
36
|
try {
|
|
37
|
+
const { platform, service } = getAgentsService(req);
|
|
20
38
|
const { projectPath } = req.query;
|
|
21
|
-
const result =
|
|
39
|
+
const result = service.listAgents(projectPath || null);
|
|
22
40
|
|
|
23
41
|
res.json({
|
|
24
42
|
success: true,
|
|
43
|
+
platform,
|
|
25
44
|
...result
|
|
26
45
|
});
|
|
27
46
|
} catch (err) {
|
|
@@ -39,11 +58,13 @@ router.get('/', (req, res) => {
|
|
|
39
58
|
*/
|
|
40
59
|
router.get('/stats', (req, res) => {
|
|
41
60
|
try {
|
|
61
|
+
const { platform, service } = getAgentsService(req);
|
|
42
62
|
const { projectPath } = req.query;
|
|
43
|
-
const stats =
|
|
63
|
+
const stats = service.getStats(projectPath || null);
|
|
44
64
|
|
|
45
65
|
res.json({
|
|
46
66
|
success: true,
|
|
67
|
+
platform,
|
|
47
68
|
...stats
|
|
48
69
|
});
|
|
49
70
|
} catch (err) {
|
|
@@ -61,6 +82,7 @@ router.get('/stats', (req, res) => {
|
|
|
61
82
|
*/
|
|
62
83
|
router.get('/:scope/:fileName', (req, res) => {
|
|
63
84
|
try {
|
|
85
|
+
const { platform, service } = getAgentsService(req);
|
|
64
86
|
const { scope, fileName } = req.params;
|
|
65
87
|
const { projectPath } = req.query;
|
|
66
88
|
|
|
@@ -78,7 +100,7 @@ router.get('/:scope/:fileName', (req, res) => {
|
|
|
78
100
|
});
|
|
79
101
|
}
|
|
80
102
|
|
|
81
|
-
const agent =
|
|
103
|
+
const agent = service.getAgent(fileName, scope, projectPath || null);
|
|
82
104
|
|
|
83
105
|
if (!agent) {
|
|
84
106
|
return res.status(404).json({
|
|
@@ -89,6 +111,7 @@ router.get('/:scope/:fileName', (req, res) => {
|
|
|
89
111
|
|
|
90
112
|
res.json({
|
|
91
113
|
success: true,
|
|
114
|
+
platform,
|
|
92
115
|
agent
|
|
93
116
|
});
|
|
94
117
|
} catch (err) {
|
|
@@ -107,6 +130,7 @@ router.get('/:scope/:fileName', (req, res) => {
|
|
|
107
130
|
*/
|
|
108
131
|
router.post('/', (req, res) => {
|
|
109
132
|
try {
|
|
133
|
+
const { platform, service } = getAgentsService(req);
|
|
110
134
|
const { fileName, scope, projectPath, name, description, tools, model, permissionMode, skills, systemPrompt } = req.body;
|
|
111
135
|
|
|
112
136
|
if (!fileName) {
|
|
@@ -130,7 +154,7 @@ router.post('/', (req, res) => {
|
|
|
130
154
|
});
|
|
131
155
|
}
|
|
132
156
|
|
|
133
|
-
const agent =
|
|
157
|
+
const agent = service.createAgent({
|
|
134
158
|
fileName,
|
|
135
159
|
scope,
|
|
136
160
|
projectPath: projectPath || null,
|
|
@@ -145,6 +169,7 @@ router.post('/', (req, res) => {
|
|
|
145
169
|
|
|
146
170
|
res.json({
|
|
147
171
|
success: true,
|
|
172
|
+
platform,
|
|
148
173
|
agent,
|
|
149
174
|
message: '代理创建成功'
|
|
150
175
|
});
|
|
@@ -163,6 +188,7 @@ router.post('/', (req, res) => {
|
|
|
163
188
|
*/
|
|
164
189
|
router.put('/:scope/:fileName', (req, res) => {
|
|
165
190
|
try {
|
|
191
|
+
const { platform, service } = getAgentsService(req);
|
|
166
192
|
const { scope, fileName } = req.params;
|
|
167
193
|
const { projectPath, name, description, tools, model, permissionMode, skills, systemPrompt } = req.body;
|
|
168
194
|
|
|
@@ -180,7 +206,7 @@ router.put('/:scope/:fileName', (req, res) => {
|
|
|
180
206
|
});
|
|
181
207
|
}
|
|
182
208
|
|
|
183
|
-
const agent =
|
|
209
|
+
const agent = service.updateAgent({
|
|
184
210
|
fileName,
|
|
185
211
|
scope,
|
|
186
212
|
projectPath: projectPath || null,
|
|
@@ -195,6 +221,7 @@ router.put('/:scope/:fileName', (req, res) => {
|
|
|
195
221
|
|
|
196
222
|
res.json({
|
|
197
223
|
success: true,
|
|
224
|
+
platform,
|
|
198
225
|
agent,
|
|
199
226
|
message: '代理更新成功'
|
|
200
227
|
});
|
|
@@ -213,6 +240,7 @@ router.put('/:scope/:fileName', (req, res) => {
|
|
|
213
240
|
*/
|
|
214
241
|
router.delete('/:scope/:fileName', (req, res) => {
|
|
215
242
|
try {
|
|
243
|
+
const { platform, service } = getAgentsService(req);
|
|
216
244
|
const { scope, fileName } = req.params;
|
|
217
245
|
const { projectPath } = req.query;
|
|
218
246
|
|
|
@@ -230,9 +258,10 @@ router.delete('/:scope/:fileName', (req, res) => {
|
|
|
230
258
|
});
|
|
231
259
|
}
|
|
232
260
|
|
|
233
|
-
const result =
|
|
261
|
+
const result = service.deleteAgent(fileName, scope, projectPath || null);
|
|
234
262
|
|
|
235
263
|
res.json({
|
|
264
|
+
platform,
|
|
236
265
|
success: result.success,
|
|
237
266
|
message: result.message
|
|
238
267
|
});
|
|
@@ -254,12 +283,14 @@ router.delete('/:scope/:fileName', (req, res) => {
|
|
|
254
283
|
*/
|
|
255
284
|
router.get('/all', async (req, res) => {
|
|
256
285
|
try {
|
|
286
|
+
const { platform, service } = getAgentsService(req);
|
|
257
287
|
const { projectPath, refresh } = req.query;
|
|
258
288
|
const forceRefresh = refresh === '1';
|
|
259
|
-
const result = await
|
|
289
|
+
const result = await service.listAllAgents(projectPath || null, forceRefresh);
|
|
260
290
|
|
|
261
291
|
res.json({
|
|
262
292
|
success: true,
|
|
293
|
+
platform,
|
|
263
294
|
...result
|
|
264
295
|
});
|
|
265
296
|
} catch (err) {
|
|
@@ -277,9 +308,11 @@ router.get('/all', async (req, res) => {
|
|
|
277
308
|
*/
|
|
278
309
|
router.get('/repos', (req, res) => {
|
|
279
310
|
try {
|
|
280
|
-
const
|
|
311
|
+
const { platform, service } = getAgentsService(req);
|
|
312
|
+
const repos = service.getRepos();
|
|
281
313
|
res.json({
|
|
282
314
|
success: true,
|
|
315
|
+
platform,
|
|
283
316
|
repos
|
|
284
317
|
});
|
|
285
318
|
} catch (err) {
|
|
@@ -298,6 +331,7 @@ router.get('/repos', (req, res) => {
|
|
|
298
331
|
*/
|
|
299
332
|
router.post('/repos', (req, res) => {
|
|
300
333
|
try {
|
|
334
|
+
const { platform, service } = getAgentsService(req);
|
|
301
335
|
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
302
336
|
|
|
303
337
|
if (!owner || !name) {
|
|
@@ -307,10 +341,11 @@ router.post('/repos', (req, res) => {
|
|
|
307
341
|
});
|
|
308
342
|
}
|
|
309
343
|
|
|
310
|
-
const repos =
|
|
344
|
+
const repos = service.addRepo({ owner, name, branch, directory, enabled });
|
|
311
345
|
|
|
312
346
|
res.json({
|
|
313
347
|
success: true,
|
|
348
|
+
platform,
|
|
314
349
|
repos
|
|
315
350
|
});
|
|
316
351
|
} catch (err) {
|
|
@@ -329,12 +364,14 @@ router.post('/repos', (req, res) => {
|
|
|
329
364
|
*/
|
|
330
365
|
router.delete('/repos/:owner/:name', (req, res) => {
|
|
331
366
|
try {
|
|
367
|
+
const { platform, service } = getAgentsService(req);
|
|
332
368
|
const { owner, name } = req.params;
|
|
333
369
|
const { directory = '' } = req.query;
|
|
334
|
-
const repos =
|
|
370
|
+
const repos = service.removeRepo(owner, name, directory);
|
|
335
371
|
|
|
336
372
|
res.json({
|
|
337
373
|
success: true,
|
|
374
|
+
platform,
|
|
338
375
|
repos
|
|
339
376
|
});
|
|
340
377
|
} catch (err) {
|
|
@@ -353,13 +390,15 @@ router.delete('/repos/:owner/:name', (req, res) => {
|
|
|
353
390
|
*/
|
|
354
391
|
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
355
392
|
try {
|
|
393
|
+
const { platform, service } = getAgentsService(req);
|
|
356
394
|
const { owner, name } = req.params;
|
|
357
395
|
const { enabled, directory = '' } = req.body;
|
|
358
396
|
|
|
359
|
-
const repos =
|
|
397
|
+
const repos = service.toggleRepo(owner, name, directory, enabled);
|
|
360
398
|
|
|
361
399
|
res.json({
|
|
362
400
|
success: true,
|
|
401
|
+
platform,
|
|
363
402
|
repos
|
|
364
403
|
});
|
|
365
404
|
} catch (err) {
|
|
@@ -378,6 +417,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
378
417
|
*/
|
|
379
418
|
router.post('/install', async (req, res) => {
|
|
380
419
|
try {
|
|
420
|
+
const { platform, service } = getAgentsService(req);
|
|
381
421
|
const agent = req.body;
|
|
382
422
|
|
|
383
423
|
if (!agent || !agent.repoOwner || !agent.repoName) {
|
|
@@ -387,10 +427,11 @@ router.post('/install', async (req, res) => {
|
|
|
387
427
|
});
|
|
388
428
|
}
|
|
389
429
|
|
|
390
|
-
const result = await
|
|
430
|
+
const result = await service.installFromRemote(agent);
|
|
391
431
|
|
|
392
432
|
res.json({
|
|
393
433
|
success: true,
|
|
434
|
+
platform,
|
|
394
435
|
...result
|
|
395
436
|
});
|
|
396
437
|
} catch (err) {
|
|
@@ -409,6 +450,7 @@ router.post('/install', async (req, res) => {
|
|
|
409
450
|
*/
|
|
410
451
|
router.post('/uninstall', (req, res) => {
|
|
411
452
|
try {
|
|
453
|
+
const { platform, service } = getAgentsService(req);
|
|
412
454
|
const { fileName } = req.body;
|
|
413
455
|
|
|
414
456
|
if (!fileName) {
|
|
@@ -418,10 +460,11 @@ router.post('/uninstall', (req, res) => {
|
|
|
418
460
|
});
|
|
419
461
|
}
|
|
420
462
|
|
|
421
|
-
const result =
|
|
463
|
+
const result = service.uninstallAgent(fileName);
|
|
422
464
|
|
|
423
465
|
res.json({
|
|
424
466
|
success: true,
|
|
467
|
+
platform,
|
|
425
468
|
...result
|
|
426
469
|
});
|
|
427
470
|
} catch (err) {
|