coding-tool-x 3.3.4 → 3.3.6
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/dist/web/assets/{Analytics-B6CWdkhx.js → Analytics-TtaduRqL.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BW6LEgd8.js → ConfigTemplates-BP2lLBMN.js} +1 -1
- package/dist/web/assets/{Home-B2B2gS2-.js → Home-CbbyopS-.js} +1 -1
- package/dist/web/assets/{PluginManager-Bqc7ldY-.js → PluginManager-HmISlyMK.js} +1 -1
- package/dist/web/assets/{ProjectList-BFdZZm_8.js → ProjectList-DoN8Hjbu.js} +1 -1
- package/dist/web/assets/{SessionList-B_Tp37kM.js → SessionList-Da8BYzNi.js} +1 -1
- package/dist/web/assets/{SkillManager-ul2rcS3o.js → SkillManager-DqLAXh9o.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Dp5Jvdtu.js → WorkspaceManager-B_TxOgPW.js} +1 -1
- package/dist/web/assets/{index-CSBDZxYn.js → index-By3mDEvx.js} +2 -2
- package/dist/web/assets/{index-DxRneGyu.css → index-CsWInMQV.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +2 -1
- package/src/commands/doctor.js +3 -3
- package/src/commands/export-config.js +2 -2
- package/src/commands/logs.js +42 -9
- package/src/commands/port-config.js +2 -2
- package/src/commands/switch.js +2 -2
- package/src/commands/update.js +21 -6
- package/src/config/default.js +4 -1
- package/src/config/loader.js +5 -2
- package/src/config/paths.js +25 -21
- package/src/reset-config.js +3 -5
- package/src/server/api/agents.js +2 -3
- package/src/server/api/claude-hooks.js +67 -10
- package/src/server/api/config-export.js +21 -2
- package/src/server/api/opencode-sessions.js +2 -2
- package/src/server/api/pm2-autostart.js +20 -8
- package/src/server/api/proxy.js +2 -3
- package/src/server/api/sessions.js +6 -6
- package/src/server/index.js +5 -9
- package/src/server/services/agents-service.js +6 -3
- package/src/server/services/channels.js +6 -7
- package/src/server/services/codex-channels.js +4 -1
- package/src/server/services/codex-config.js +4 -1
- package/src/server/services/codex-settings-manager.js +18 -9
- package/src/server/services/commands-service.js +2 -2
- package/src/server/services/config-export-service.js +96 -28
- package/src/server/services/config-registry-service.js +7 -6
- package/src/server/services/config-sync-manager.js +2 -2
- package/src/server/services/config-sync-service.js +2 -2
- package/src/server/services/env-checker.js +2 -2
- package/src/server/services/favorites.js +3 -4
- package/src/server/services/gemini-channels.js +4 -4
- package/src/server/services/gemini-config.js +2 -2
- package/src/server/services/gemini-sessions.js +3 -3
- package/src/server/services/gemini-settings-manager.js +5 -5
- package/src/server/services/mcp-client.js +101 -14
- package/src/server/services/mcp-service.js +98 -8
- package/src/server/services/model-detector.js +2 -2
- package/src/server/services/opencode-channels.js +5 -5
- package/src/server/services/plugins-service.js +3 -4
- package/src/server/services/prompts-service.js +7 -4
- package/src/server/services/proxy-runtime.js +2 -2
- package/src/server/services/repo-scanner-base.js +2 -2
- package/src/server/services/request-logger.js +2 -2
- package/src/server/services/security-config.js +2 -2
- package/src/server/services/session-cache.js +2 -2
- package/src/server/services/session-converter.js +9 -4
- package/src/server/services/sessions.js +8 -5
- package/src/server/services/settings-manager.js +3 -4
- package/src/server/services/skill-service.js +5 -5
- package/src/server/services/statistics-service.js +2 -2
- package/src/server/services/ui-config.js +3 -4
- package/src/server/websocket-server.js +2 -2
- package/src/utils/home-dir.js +82 -0
- package/src/utils/port-helper.js +34 -12
package/dist/web/index.html
CHANGED
|
@@ -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-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-By3mDEvx.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-C9MYpaSi.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-DET08QYg.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendors-DMjSfzlv.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-CxpuzdjU.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/icons-B29onFfZ.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CsWInMQV.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.3.
|
|
3
|
+
"version": "3.3.6",
|
|
4
4
|
"description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"test:basic": "node scripts/test-basic.js",
|
|
13
13
|
"test:api": "node scripts/test-api-consistency.js",
|
|
14
14
|
"test:codex-agents": "node scripts/test-codex-agents.js",
|
|
15
|
+
"test:windows": "node scripts/test-windows-regression.js",
|
|
15
16
|
"benchmark:codex": "node scripts/benchmark-codex-loading.js",
|
|
16
17
|
"build:web": "cd src/web && npm run build",
|
|
17
18
|
"dev:web": "cd src/web && npm run dev",
|
package/src/commands/doctor.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const os = require('os');
|
|
5
4
|
const { exec } = require('child_process');
|
|
6
5
|
const { promisify } = require('util');
|
|
7
6
|
const { loadConfig, getConfigFilePath } = require('../config/loader');
|
|
7
|
+
const { PATHS, NATIVE_PATHS } = require('../config/paths');
|
|
8
8
|
const { isPortInUse } = require('../utils/port-helper');
|
|
9
9
|
|
|
10
10
|
const execAsync = promisify(exec);
|
|
@@ -185,7 +185,7 @@ async function checkPorts() {
|
|
|
185
185
|
* 检查 Claude Code 配置
|
|
186
186
|
*/
|
|
187
187
|
async function checkClaudeConfig() {
|
|
188
|
-
const settingsPath =
|
|
188
|
+
const settingsPath = NATIVE_PATHS.claude.settings;
|
|
189
189
|
const exists = fs.existsSync(settingsPath);
|
|
190
190
|
|
|
191
191
|
if (exists) {
|
|
@@ -208,7 +208,7 @@ async function checkClaudeConfig() {
|
|
|
208
208
|
* 检查日志目录
|
|
209
209
|
*/
|
|
210
210
|
async function checkLogsDirectory() {
|
|
211
|
-
const logsDir =
|
|
211
|
+
const logsDir = PATHS.logs;
|
|
212
212
|
const exists = fs.existsSync(logsDir);
|
|
213
213
|
|
|
214
214
|
if (exists) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const archiver = require('archiver');
|
|
5
4
|
const chalk = require('chalk');
|
|
5
|
+
const { HOME_DIR } = require('../config/paths');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 导出 Claude Code 配置为 ZIP 压缩包
|
|
@@ -14,7 +14,7 @@ async function exportConfig(options = {}) {
|
|
|
14
14
|
try {
|
|
15
15
|
console.log(chalk.blue('🚀 开始导出 Claude Code 配置...'));
|
|
16
16
|
|
|
17
|
-
const homeDir =
|
|
17
|
+
const homeDir = HOME_DIR;
|
|
18
18
|
const claudeDir = path.join(homeDir, '.claude');
|
|
19
19
|
const currentDir = process.cwd();
|
|
20
20
|
|
package/src/commands/logs.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const os = require('os');
|
|
5
4
|
const { spawn } = require('child_process');
|
|
5
|
+
const { PATHS } = require('../config/paths');
|
|
6
6
|
|
|
7
|
-
const LOGS_DIR =
|
|
7
|
+
const LOGS_DIR = PATHS.logs;
|
|
8
8
|
|
|
9
9
|
const LOG_FILES = {
|
|
10
10
|
ui: 'cc-tool-out.log',
|
|
@@ -145,29 +145,59 @@ function showLastLines(filePath, lines) {
|
|
|
145
145
|
/**
|
|
146
146
|
* 实时跟踪日志文件
|
|
147
147
|
*/
|
|
148
|
+
function buildFollowProcessSpec(filePath, runtimePlatform = process.platform) {
|
|
149
|
+
if (runtimePlatform === 'win32') {
|
|
150
|
+
return {
|
|
151
|
+
command: 'powershell',
|
|
152
|
+
args: [
|
|
153
|
+
'-NoProfile',
|
|
154
|
+
'-Command',
|
|
155
|
+
`Get-Content -Path '${String(filePath).replace(/'/g, "''")}' -Tail 50 -Wait`
|
|
156
|
+
],
|
|
157
|
+
options: { windowsHide: true }
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
command: 'tail',
|
|
163
|
+
args: ['-n', '50', '-f', filePath],
|
|
164
|
+
options: {}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
148
168
|
function tailFile(filePath) {
|
|
149
169
|
console.log(chalk.gray('按 Ctrl+C 停止跟踪\n'));
|
|
150
170
|
|
|
151
|
-
const
|
|
171
|
+
const followSpec = buildFollowProcessSpec(filePath);
|
|
172
|
+
const isWindows = followSpec.command.toLowerCase() === 'powershell';
|
|
173
|
+
const followProcess = spawn(followSpec.command, followSpec.args, followSpec.options);
|
|
152
174
|
|
|
153
|
-
|
|
175
|
+
followProcess.stdout.on('data', (data) => {
|
|
154
176
|
process.stdout.write(data.toString());
|
|
155
177
|
});
|
|
156
178
|
|
|
157
|
-
|
|
179
|
+
followProcess.stderr.on('data', (data) => {
|
|
158
180
|
process.stderr.write(chalk.red(data.toString()));
|
|
159
181
|
});
|
|
160
182
|
|
|
161
|
-
|
|
183
|
+
followProcess.on('error', (err) => {
|
|
162
184
|
console.error(chalk.red(`\n❌ 跟踪日志失败: ${err.message}\n`));
|
|
185
|
+
if (isWindows) {
|
|
186
|
+
console.log(chalk.gray('提示: 请确认系统可用 powershell 命令。\n'));
|
|
187
|
+
}
|
|
163
188
|
process.exit(1);
|
|
164
189
|
});
|
|
165
190
|
|
|
166
191
|
// 处理退出信号
|
|
167
|
-
|
|
168
|
-
|
|
192
|
+
const handleSigint = () => {
|
|
193
|
+
followProcess.kill();
|
|
169
194
|
console.log(chalk.gray('\n\n已停止跟踪日志\n'));
|
|
170
195
|
process.exit(0);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
process.once('SIGINT', handleSigint);
|
|
199
|
+
followProcess.once('close', () => {
|
|
200
|
+
process.removeListener('SIGINT', handleSigint);
|
|
171
201
|
});
|
|
172
202
|
}
|
|
173
203
|
|
|
@@ -257,5 +287,8 @@ function getTypeColor(type) {
|
|
|
257
287
|
}
|
|
258
288
|
|
|
259
289
|
module.exports = {
|
|
260
|
-
handleLogs
|
|
290
|
+
handleLogs,
|
|
291
|
+
_test: {
|
|
292
|
+
buildFollowProcessSpec
|
|
293
|
+
}
|
|
261
294
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// 端口配置命令
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
|
-
const os = require('os');
|
|
5
4
|
const { loadConfig, saveConfig } = require('../config/loader');
|
|
5
|
+
const { HOME_DIR } = require('../config/paths');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 配置端口
|
|
@@ -112,7 +112,7 @@ async function handlePortConfig() {
|
|
|
112
112
|
// 保存配置(保留其余字段)
|
|
113
113
|
saveConfig({
|
|
114
114
|
...config,
|
|
115
|
-
projectsDir: config.projectsDir.replace(
|
|
115
|
+
projectsDir: config.projectsDir.replace(HOME_DIR, '~'),
|
|
116
116
|
ports: config.ports,
|
|
117
117
|
});
|
|
118
118
|
|
package/src/commands/switch.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// 切换项目命令
|
|
2
2
|
const chalk = require('chalk');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const { getAvailableProjects } = require('../utils/session');
|
|
5
4
|
const { promptSelectProject } = require('../ui/prompts');
|
|
6
5
|
const { saveConfig } = require('../config/loader');
|
|
6
|
+
const { HOME_DIR } = require('../config/paths');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* 切换项目
|
|
@@ -31,7 +31,7 @@ async function switchProject(config) {
|
|
|
31
31
|
// 保存到配置文件(保留其余字段)
|
|
32
32
|
saveConfig({
|
|
33
33
|
...config,
|
|
34
|
-
projectsDir: config.projectsDir.replace(
|
|
34
|
+
projectsDir: config.projectsDir.replace(HOME_DIR, '~')
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
// 使用解析后的名称显示
|
package/src/commands/update.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
const { spawn } = require('child_process');
|
|
1
|
+
const { spawn, execFile } = require('child_process');
|
|
2
2
|
const { promisify } = require('util');
|
|
3
|
-
const { exec } = require('child_process');
|
|
4
3
|
const semver = require('semver');
|
|
5
4
|
const chalk = require('chalk');
|
|
6
5
|
const packageInfo = require('../../package.json');
|
|
7
6
|
|
|
8
|
-
const
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
|
|
9
|
+
function resolveNpmCommand() {
|
|
10
|
+
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
11
|
+
}
|
|
9
12
|
|
|
10
13
|
async function getLatestVersion(packageName) {
|
|
11
|
-
const
|
|
14
|
+
const npmCommand = resolveNpmCommand();
|
|
15
|
+
const { stdout } = await execFileAsync(
|
|
16
|
+
npmCommand,
|
|
17
|
+
['view', packageName, 'version', '--json'],
|
|
18
|
+
{ timeout: 15000 }
|
|
19
|
+
);
|
|
12
20
|
const parsed = JSON.parse(stdout.trim());
|
|
13
21
|
if (typeof parsed === 'string') return parsed;
|
|
14
22
|
throw new Error('无法解析 npm 返回的版本号');
|
|
@@ -16,11 +24,18 @@ async function getLatestVersion(packageName) {
|
|
|
16
24
|
|
|
17
25
|
function runNpmInstall(packageName, version) {
|
|
18
26
|
return new Promise((resolve, reject) => {
|
|
19
|
-
const
|
|
27
|
+
const npmCommand = resolveNpmCommand();
|
|
28
|
+
const child = spawn(npmCommand, ['install', '-g', `${packageName}@${version}`], {
|
|
20
29
|
stdio: 'inherit'
|
|
21
30
|
});
|
|
22
31
|
|
|
23
|
-
child.on('error',
|
|
32
|
+
child.on('error', (err) => {
|
|
33
|
+
if (err && err.code === 'ENOENT') {
|
|
34
|
+
reject(new Error(`命令 "${npmCommand}" 未找到,请确认 Node.js/npm 已安装并在 PATH 中`));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
reject(err);
|
|
38
|
+
});
|
|
24
39
|
child.on('exit', (code) => {
|
|
25
40
|
if (code === 0) {
|
|
26
41
|
resolve();
|
package/src/config/default.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// 默认配置
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
4
5
|
const modelMetadataConfig = require('./model-metadata.json');
|
|
5
6
|
|
|
7
|
+
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
8
|
+
|
|
6
9
|
const DEFAULT_CONFIG = {
|
|
7
|
-
projectsDir: path.join(
|
|
10
|
+
projectsDir: path.join(HOME_DIR, '.claude', 'projects'),
|
|
8
11
|
defaultProject: null,
|
|
9
12
|
maxDisplaySessions: 100,
|
|
10
13
|
pageSize: 15,
|
package/src/config/loader.js
CHANGED
|
@@ -4,11 +4,14 @@ const path = require('path');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const DEFAULT_CONFIG = require('./default');
|
|
6
6
|
const { PATHS, ensureStorageDirMigrated } = require('./paths');
|
|
7
|
+
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
7
8
|
const eventBus = require('../plugins/event-bus');
|
|
8
9
|
|
|
10
|
+
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
11
|
+
|
|
9
12
|
const LEGACY_CONFIG_FILES = [
|
|
10
13
|
path.join(__dirname, '../../config.json'),
|
|
11
|
-
path.join(
|
|
14
|
+
path.join(HOME_DIR, '.claude', 'config.json')
|
|
12
15
|
];
|
|
13
16
|
|
|
14
17
|
function getConfigFilePath() {
|
|
@@ -21,7 +24,7 @@ function getConfigFilePath() {
|
|
|
21
24
|
*/
|
|
22
25
|
function expandHome(filepath) {
|
|
23
26
|
if (filepath.startsWith('~')) {
|
|
24
|
-
return path.join(
|
|
27
|
+
return path.join(HOME_DIR, filepath.slice(1));
|
|
25
28
|
}
|
|
26
29
|
return filepath;
|
|
27
30
|
}
|
package/src/config/paths.js
CHANGED
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
+
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
7
|
+
|
|
8
|
+
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
6
9
|
|
|
7
10
|
// 基础目录
|
|
8
|
-
const CC_TOOL_BASE_DIR = path.join(
|
|
11
|
+
const CC_TOOL_BASE_DIR = path.join(HOME_DIR, '.cc-tool');
|
|
9
12
|
// 兼容旧变量名,避免外部调用方断裂
|
|
10
13
|
const CTX_BASE_DIR = CC_TOOL_BASE_DIR;
|
|
11
14
|
|
|
12
15
|
// 旧目录(升级时自动合并到 ~/.cc-tool)
|
|
13
16
|
const LEGACY_BASE_DIRS = [
|
|
14
|
-
path.join(
|
|
15
|
-
path.join(
|
|
17
|
+
path.join(HOME_DIR, '.claude', 'ctx'),
|
|
18
|
+
path.join(HOME_DIR, '.claude', 'cc-tool')
|
|
16
19
|
];
|
|
17
20
|
|
|
18
21
|
let migrationChecked = false;
|
|
@@ -127,7 +130,7 @@ const PATHS = {
|
|
|
127
130
|
notifyHook: path.join(CC_TOOL_BASE_DIR, 'notify-hook.js'),
|
|
128
131
|
|
|
129
132
|
// Skills 安装目录(注意:这个仍使用 Claude 原生路径)
|
|
130
|
-
skills: path.join(
|
|
133
|
+
skills: path.join(HOME_DIR, '.claude', 'skills'),
|
|
131
134
|
|
|
132
135
|
// MCP 配置(注意:这个仍使用 Claude 原生路径)
|
|
133
136
|
mcpConfig: path.join(CC_TOOL_BASE_DIR, 'mcp-config.json'),
|
|
@@ -148,41 +151,42 @@ const PATHS = {
|
|
|
148
151
|
const NATIVE_PATHS = {
|
|
149
152
|
// Claude Code 原生配置
|
|
150
153
|
claude: {
|
|
151
|
-
settings: path.join(
|
|
152
|
-
settingsBackup: path.join(
|
|
153
|
-
projects: path.join(
|
|
154
|
+
settings: path.join(HOME_DIR, '.claude', 'settings.json'),
|
|
155
|
+
settingsBackup: path.join(HOME_DIR, '.claude', 'settings.json.cc-tool-backup'),
|
|
156
|
+
projects: path.join(HOME_DIR, '.claude', 'projects')
|
|
154
157
|
},
|
|
155
158
|
|
|
156
159
|
// Codex 原生配置
|
|
157
160
|
codex: {
|
|
158
|
-
config: path.join(
|
|
159
|
-
configBackup: path.join(
|
|
160
|
-
auth: path.join(
|
|
161
|
-
authBackup: path.join(
|
|
162
|
-
sessions: path.join(
|
|
161
|
+
config: path.join(HOME_DIR, '.codex', 'config.toml'),
|
|
162
|
+
configBackup: path.join(HOME_DIR, '.codex', 'config.toml.cc-tool-backup'),
|
|
163
|
+
auth: path.join(HOME_DIR, '.codex', 'auth.json'),
|
|
164
|
+
authBackup: path.join(HOME_DIR, '.codex', 'auth.json.cc-tool-backup'),
|
|
165
|
+
sessions: path.join(HOME_DIR, '.codex', 'sessions')
|
|
163
166
|
},
|
|
164
167
|
|
|
165
168
|
// Gemini 原生配置
|
|
166
169
|
gemini: {
|
|
167
|
-
env: path.join(
|
|
168
|
-
envBackup: path.join(
|
|
169
|
-
tmp: path.join(
|
|
170
|
+
env: path.join(HOME_DIR, '.gemini', '.env'),
|
|
171
|
+
envBackup: path.join(HOME_DIR, '.gemini', '.env.cc-tool-backup'),
|
|
172
|
+
tmp: path.join(HOME_DIR, '.gemini', 'tmp')
|
|
170
173
|
},
|
|
171
174
|
|
|
172
175
|
// OpenCode 原生配置
|
|
173
176
|
opencode: {
|
|
174
|
-
data: path.join(
|
|
175
|
-
config: path.join(
|
|
176
|
-
sessions: path.join(
|
|
177
|
-
projects: path.join(
|
|
178
|
-
messages: path.join(
|
|
179
|
-
log: path.join(
|
|
177
|
+
data: path.join(HOME_DIR, '.local', 'share', 'opencode'),
|
|
178
|
+
config: path.join(HOME_DIR, '.config', 'opencode'),
|
|
179
|
+
sessions: path.join(HOME_DIR, '.local', 'share', 'opencode', 'storage', 'session'),
|
|
180
|
+
projects: path.join(HOME_DIR, '.local', 'share', 'opencode', 'storage', 'project'),
|
|
181
|
+
messages: path.join(HOME_DIR, '.local', 'share', 'opencode', 'storage', 'message'),
|
|
182
|
+
log: path.join(HOME_DIR, '.local', 'share', 'opencode', 'log')
|
|
180
183
|
}
|
|
181
184
|
};
|
|
182
185
|
|
|
183
186
|
module.exports = {
|
|
184
187
|
PATHS,
|
|
185
188
|
NATIVE_PATHS,
|
|
189
|
+
HOME_DIR,
|
|
186
190
|
CTX_BASE_DIR,
|
|
187
191
|
CC_TOOL_BASE_DIR,
|
|
188
192
|
LEGACY_BASE_DIRS,
|
package/src/reset-config.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const { PATHS, ensureStorageDirMigrated } = require('./config/paths');
|
|
2
|
+
const { PATHS, NATIVE_PATHS, ensureStorageDirMigrated } = require('./config/paths');
|
|
5
3
|
|
|
6
4
|
// 恢复配置到默认状态
|
|
7
5
|
async function resetConfig() {
|
|
@@ -25,8 +23,8 @@ async function resetConfig() {
|
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
// 2. 检查并恢复 settings.json
|
|
28
|
-
const settingsPath =
|
|
29
|
-
const backupPath =
|
|
26
|
+
const settingsPath = NATIVE_PATHS.claude.settings;
|
|
27
|
+
const backupPath = NATIVE_PATHS.claude.settingsBackup;
|
|
30
28
|
|
|
31
29
|
if (fs.existsSync(backupPath)) {
|
|
32
30
|
console.log('发现备份文件,正在恢复...');
|
package/src/server/api/agents.js
CHANGED
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
const express = require('express');
|
|
8
8
|
const fs = require('fs');
|
|
9
|
-
const os = require('os');
|
|
10
9
|
const path = require('path');
|
|
11
10
|
const { AgentsService } = require('../services/agents-service');
|
|
12
|
-
const { PATHS } = require('../../config/paths');
|
|
11
|
+
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
13
12
|
|
|
14
13
|
const router = express.Router();
|
|
15
14
|
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'opencode'];
|
|
16
15
|
const agentServices = new Map();
|
|
17
|
-
const DEFAULT_PROJECT_ALLOWED_ROOTS = [
|
|
16
|
+
const DEFAULT_PROJECT_ALLOWED_ROOTS = [HOME_DIR, process.cwd()];
|
|
18
17
|
|
|
19
18
|
function isSupportedPlatform(platform) {
|
|
20
19
|
return SUPPORTED_PLATFORMS.includes(platform);
|
|
@@ -5,18 +5,21 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const https = require('https');
|
|
7
7
|
const http = require('http');
|
|
8
|
+
const { resolvePreferredHomeDir, normalizeWindowsHomePath } = require('../../utils/home-dir');
|
|
9
|
+
|
|
10
|
+
// 检测操作系统
|
|
11
|
+
const platform = os.platform(); // 'darwin' | 'win32' | 'linux'
|
|
12
|
+
|
|
13
|
+
const HOME_DIR = resolvePreferredHomeDir(platform, process.env, os.homedir());
|
|
8
14
|
|
|
9
15
|
// Claude settings.json 路径
|
|
10
|
-
const CLAUDE_SETTINGS_PATH = path.join(
|
|
16
|
+
const CLAUDE_SETTINGS_PATH = path.join(HOME_DIR, '.claude', 'settings.json');
|
|
11
17
|
|
|
12
18
|
// UI 配置路径(记录用户是否主动关闭过、飞书配置等)
|
|
13
|
-
const UI_CONFIG_PATH = path.join(
|
|
19
|
+
const UI_CONFIG_PATH = path.join(HOME_DIR, '.cc-tool', 'ui-config.json');
|
|
14
20
|
|
|
15
21
|
// 通知脚本路径(用于飞书通知)
|
|
16
|
-
const NOTIFY_SCRIPT_PATH = path.join(
|
|
17
|
-
|
|
18
|
-
// 检测操作系统
|
|
19
|
-
const platform = os.platform(); // 'darwin' | 'win32' | 'linux'
|
|
22
|
+
const NOTIFY_SCRIPT_PATH = path.join(HOME_DIR, '.cc-tool', 'notify-hook.js');
|
|
20
23
|
|
|
21
24
|
// 读取 Claude settings.json
|
|
22
25
|
function readClaudeSettings() {
|
|
@@ -197,6 +200,42 @@ function parseNotifyTypeMarker(command) {
|
|
|
197
200
|
return marker ? marker[2].toLowerCase() : null;
|
|
198
201
|
}
|
|
199
202
|
|
|
203
|
+
function getStopHookCommand(settings) {
|
|
204
|
+
const hooks = settings?.hooks?.Stop;
|
|
205
|
+
if (!Array.isArray(hooks) || hooks.length === 0) {
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
const firstHook = hooks[0]?.hooks;
|
|
209
|
+
if (!Array.isArray(firstHook) || firstHook.length === 0) {
|
|
210
|
+
return '';
|
|
211
|
+
}
|
|
212
|
+
return firstHook[0]?.command || '';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function normalizePathForCompare(rawPath) {
|
|
216
|
+
return String(rawPath || '').replace(/\\/g, '/');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function shouldRepairStopHook(settings, expectedScriptPath = NOTIFY_SCRIPT_PATH, fileExists = fs.existsSync) {
|
|
220
|
+
const command = getStopHookCommand(settings);
|
|
221
|
+
if (!command || !command.includes('notify-hook.js')) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const markerType = parseNotifyTypeMarker(command);
|
|
226
|
+
if (!markerType) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const normalizedCommand = normalizePathForCompare(command);
|
|
231
|
+
const normalizedExpected = normalizePathForCompare(expectedScriptPath);
|
|
232
|
+
if (!normalizedCommand.includes(normalizedExpected)) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return !fileExists(expectedScriptPath);
|
|
237
|
+
}
|
|
238
|
+
|
|
200
239
|
function buildStopHookCommand(type) {
|
|
201
240
|
const notifyType = type === 'dialog' ? 'dialog' : 'notification';
|
|
202
241
|
return `node "${NOTIFY_SCRIPT_PATH}" --cc-notify-type=${notifyType}`;
|
|
@@ -302,7 +341,9 @@ function updateStopHook(systemNotification, feishu) {
|
|
|
302
341
|
}
|
|
303
342
|
} else {
|
|
304
343
|
// 生成并写入通知脚本
|
|
305
|
-
writeNotifyScript({ systemNotification, feishu })
|
|
344
|
+
if (!writeNotifyScript({ systemNotification, feishu })) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
306
347
|
|
|
307
348
|
// 更新 Stop hook 指向通知脚本
|
|
308
349
|
settings.hooks = settings.hooks || {};
|
|
@@ -335,9 +376,22 @@ function initDefaultHooks() {
|
|
|
335
376
|
const settings = readClaudeSettings();
|
|
336
377
|
const currentStatus = parseStopHookStatus(settings);
|
|
337
378
|
|
|
338
|
-
// 如果已经有 Stop hook
|
|
379
|
+
// 如果已经有 Stop hook 配置,优先尝试自愈旧路径,再决定是否跳过
|
|
339
380
|
if (currentStatus.enabled) {
|
|
340
|
-
|
|
381
|
+
if (shouldRepairStopHook(settings)) {
|
|
382
|
+
const systemNotification = {
|
|
383
|
+
enabled: true,
|
|
384
|
+
type: currentStatus.type || 'notification'
|
|
385
|
+
};
|
|
386
|
+
const feishu = getFeishuConfig();
|
|
387
|
+
if (updateStopHook(systemNotification, feishu)) {
|
|
388
|
+
console.log('[Claude Hooks] 检测到旧版 Stop hook 路径,已自动修复');
|
|
389
|
+
} else {
|
|
390
|
+
console.warn('[Claude Hooks] Stop hook 路径修复失败,保留原配置');
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
console.log('[Claude Hooks] 已存在 Stop hook 配置,跳过初始化');
|
|
394
|
+
}
|
|
341
395
|
return;
|
|
342
396
|
}
|
|
343
397
|
|
|
@@ -499,5 +553,8 @@ module.exports._test = {
|
|
|
499
553
|
generateSystemNotificationCommand,
|
|
500
554
|
parseStopHookStatus,
|
|
501
555
|
parseNotifyTypeMarker,
|
|
502
|
-
buildStopHookCommand
|
|
556
|
+
buildStopHookCommand,
|
|
557
|
+
normalizeWindowsHomePath,
|
|
558
|
+
resolvePreferredHomeDir,
|
|
559
|
+
shouldRepairStopHook
|
|
503
560
|
};
|
|
@@ -18,13 +18,32 @@ function parseConfigZip(buffer) {
|
|
|
18
18
|
return JSON.parse(content);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function resolveChannelsByType(exportData) {
|
|
22
|
+
const raw = exportData?.data || {};
|
|
23
|
+
const typed = raw.channelsByType && typeof raw.channelsByType === 'object' ? raw.channelsByType : {};
|
|
24
|
+
return {
|
|
25
|
+
claude: Array.isArray(typed.claude) ? typed.claude : (Array.isArray(raw.channels) ? raw.channels : []),
|
|
26
|
+
codex: Array.isArray(typed.codex) ? typed.codex : [],
|
|
27
|
+
gemini: Array.isArray(typed.gemini) ? typed.gemini : [],
|
|
28
|
+
opencode: Array.isArray(typed.opencode) ? typed.opencode : []
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
21
32
|
function buildPreviewSummary(data) {
|
|
33
|
+
const channelsByType = resolveChannelsByType(data);
|
|
34
|
+
const allChannels = [
|
|
35
|
+
...channelsByType.claude.map(c => ({ ...c, type: c.type || 'claude' })),
|
|
36
|
+
...channelsByType.codex.map(c => ({ ...c, type: c.type || 'codex' })),
|
|
37
|
+
...channelsByType.gemini.map(c => ({ ...c, type: c.type || 'gemini' })),
|
|
38
|
+
...channelsByType.opencode.map(c => ({ ...c, type: c.type || 'opencode' }))
|
|
39
|
+
];
|
|
40
|
+
|
|
22
41
|
return {
|
|
23
42
|
version: data.version,
|
|
24
43
|
exportedAt: data.exportedAt,
|
|
25
44
|
counts: {
|
|
26
45
|
configTemplates: (data.data.configTemplates || []).length,
|
|
27
|
-
channels:
|
|
46
|
+
channels: allChannels.length,
|
|
28
47
|
plugins: (data.data.plugins || []).length
|
|
29
48
|
},
|
|
30
49
|
items: {
|
|
@@ -33,7 +52,7 @@ function buildPreviewSummary(data) {
|
|
|
33
52
|
name: t.name,
|
|
34
53
|
description: t.description
|
|
35
54
|
})),
|
|
36
|
-
channels:
|
|
55
|
+
channels: allChannels.map(c => ({
|
|
37
56
|
id: c.id,
|
|
38
57
|
name: c.name,
|
|
39
58
|
type: c.type
|
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
} = require('../services/opencode-sessions');
|
|
15
15
|
const { loadAliases } = require('../services/alias');
|
|
16
16
|
const { broadcastLog } = require('../websocket-server');
|
|
17
|
-
const
|
|
17
|
+
const { HOME_DIR } = require('../../config/paths');
|
|
18
18
|
|
|
19
19
|
function isNotFoundError(error) {
|
|
20
20
|
if (!error || !error.message) {
|
|
@@ -295,7 +295,7 @@ module.exports = (config) => {
|
|
|
295
295
|
|
|
296
296
|
const projects = getProjects();
|
|
297
297
|
const project = projects.find(p => p.name === projectName);
|
|
298
|
-
const cwd = session.directory || project?.fullPath ||
|
|
298
|
+
const cwd = session.directory || project?.fullPath || HOME_DIR;
|
|
299
299
|
const command = `opencode -r ${sessionId}`;
|
|
300
300
|
const quotedCwd = `"${String(cwd).replace(/"/g, '\\"')}"`;
|
|
301
301
|
const copyCommand = `cd ${quotedCwd} && ${command}`;
|