cfix 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +69 -0
- package/README.md +1590 -0
- package/bin/cfix +14 -0
- package/bin/cfix.cmd +6 -0
- package/cli/commands/config.js +58 -0
- package/cli/commands/doctor.js +240 -0
- package/cli/commands/fix.js +211 -0
- package/cli/commands/help.js +62 -0
- package/cli/commands/init.js +226 -0
- package/cli/commands/logs.js +161 -0
- package/cli/commands/monitor.js +151 -0
- package/cli/commands/project.js +331 -0
- package/cli/commands/service.js +133 -0
- package/cli/commands/status.js +115 -0
- package/cli/commands/task.js +412 -0
- package/cli/commands/version.js +19 -0
- package/cli/index.js +269 -0
- package/cli/lib/config-manager.js +612 -0
- package/cli/lib/formatter.js +224 -0
- package/cli/lib/process-manager.js +233 -0
- package/cli/lib/service-client.js +271 -0
- package/cli/scripts/install-completion.js +133 -0
- package/package.json +85 -0
- package/public/monitor.html +1096 -0
- package/scripts/completion.bash +87 -0
- package/scripts/completion.zsh +102 -0
- package/src/assets/README.md +32 -0
- package/src/assets/error.png +0 -0
- package/src/assets/icon.png +0 -0
- package/src/assets/success.png +0 -0
- package/src/claude-cli-service.js +216 -0
- package/src/config/index.js +69 -0
- package/src/database/manager.js +391 -0
- package/src/database/migration.js +252 -0
- package/src/git-service.js +1278 -0
- package/src/index.js +1658 -0
- package/src/logger.js +139 -0
- package/src/metrics/collector.js +184 -0
- package/src/middleware/auth.js +86 -0
- package/src/middleware/rate-limit.js +85 -0
- package/src/queue/integration-example.js +283 -0
- package/src/queue/task-queue.js +333 -0
- package/src/services/notification-limiter.js +48 -0
- package/src/services/notification-service.js +115 -0
- package/src/services/system-notifier.js +130 -0
- package/src/task-manager.js +289 -0
- package/src/utils/exec.js +87 -0
- package/src/utils/project-lock.js +246 -0
- package/src/utils/retry.js +110 -0
- package/src/utils/sanitizer.js +174 -0
- package/src/websocket/notifier.js +363 -0
- package/src/wechat-notifier.js +97 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cfix bash completion
|
|
3
|
+
|
|
4
|
+
_cfix_completion() {
|
|
5
|
+
local cur prev words cword
|
|
6
|
+
_init_completion || return
|
|
7
|
+
|
|
8
|
+
# 主命令
|
|
9
|
+
if [[ ${cword} -eq 1 ]]; then
|
|
10
|
+
COMPREPLY=($(compgen -W "
|
|
11
|
+
fix
|
|
12
|
+
start stop restart
|
|
13
|
+
status logs
|
|
14
|
+
project
|
|
15
|
+
config
|
|
16
|
+
tasks task approve reject cancel wait
|
|
17
|
+
init monitor doctor
|
|
18
|
+
version help
|
|
19
|
+
--help --version
|
|
20
|
+
" -- "${cur}"))
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# 子命令补全
|
|
25
|
+
case "${words[1]}" in
|
|
26
|
+
project)
|
|
27
|
+
if [[ ${cword} -eq 2 ]]; then
|
|
28
|
+
COMPREPLY=($(compgen -W "add remove list info" -- "${cur}"))
|
|
29
|
+
fi
|
|
30
|
+
;;
|
|
31
|
+
|
|
32
|
+
config)
|
|
33
|
+
if [[ ${cword} -eq 2 ]]; then
|
|
34
|
+
COMPREPLY=($(compgen -W "get set list edit reset" -- "${cur}"))
|
|
35
|
+
fi
|
|
36
|
+
;;
|
|
37
|
+
|
|
38
|
+
init)
|
|
39
|
+
# 目录补全
|
|
40
|
+
COMPREPLY=($(compgen -d -- "${cur}"))
|
|
41
|
+
;;
|
|
42
|
+
|
|
43
|
+
monitor)
|
|
44
|
+
if [[ ${cword} -eq 2 ]]; then
|
|
45
|
+
COMPREPLY=($(compgen -W "--follow --no-auto-start" -- "${cur}"))
|
|
46
|
+
fi
|
|
47
|
+
;;
|
|
48
|
+
|
|
49
|
+
fix)
|
|
50
|
+
if [[ "${cur}" == -* ]]; then
|
|
51
|
+
COMPREPLY=($(compgen -W "--project --output --wait --yes --help" -- "${cur}"))
|
|
52
|
+
fi
|
|
53
|
+
;;
|
|
54
|
+
|
|
55
|
+
start|stop|restart)
|
|
56
|
+
if [[ "${cur}" == -* ]]; then
|
|
57
|
+
COMPREPLY=($(compgen -W "--daemon --wait --quiet --help" -- "${cur}"))
|
|
58
|
+
fi
|
|
59
|
+
;;
|
|
60
|
+
|
|
61
|
+
logs)
|
|
62
|
+
if [[ "${cur}" == -* ]]; then
|
|
63
|
+
COMPREPLY=($(compgen -W "--follow --errors --help" -- "${cur}"))
|
|
64
|
+
fi
|
|
65
|
+
;;
|
|
66
|
+
|
|
67
|
+
tasks)
|
|
68
|
+
if [[ "${cur}" == -* ]]; then
|
|
69
|
+
COMPREPLY=($(compgen -W "--project --status --all --help" -- "${cur}"))
|
|
70
|
+
fi
|
|
71
|
+
;;
|
|
72
|
+
|
|
73
|
+
wait)
|
|
74
|
+
if [[ "${cur}" == -* ]]; then
|
|
75
|
+
COMPREPLY=($(compgen -W "--timeout --interval --yes --help" -- "${cur}"))
|
|
76
|
+
fi
|
|
77
|
+
;;
|
|
78
|
+
|
|
79
|
+
approve)
|
|
80
|
+
if [[ "${cur}" == -* ]]; then
|
|
81
|
+
COMPREPLY=($(compgen -W "--yes --help" -- "${cur}"))
|
|
82
|
+
fi
|
|
83
|
+
;;
|
|
84
|
+
esac
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
complete -F _cfix_completion cfix
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#compdef cfix
|
|
2
|
+
# cfix zsh completion
|
|
3
|
+
|
|
4
|
+
_cfix() {
|
|
5
|
+
local -a commands
|
|
6
|
+
commands=(
|
|
7
|
+
'fix:创建修复任务'
|
|
8
|
+
'start:启动服务'
|
|
9
|
+
'stop:停止服务'
|
|
10
|
+
'restart:重启服务'
|
|
11
|
+
'status:查看服务状态'
|
|
12
|
+
'logs:查看日志'
|
|
13
|
+
'project:项目管理命令'
|
|
14
|
+
'config:配置管理命令'
|
|
15
|
+
'tasks:查看任务列表'
|
|
16
|
+
'task:查看任务详情'
|
|
17
|
+
'approve:批准任务'
|
|
18
|
+
'reject:拒绝任务'
|
|
19
|
+
'cancel:取消任务'
|
|
20
|
+
'wait:等待任务完成'
|
|
21
|
+
'init:初始化项目配置'
|
|
22
|
+
'monitor:打开监控面板'
|
|
23
|
+
'doctor:环境检查'
|
|
24
|
+
'version:版本信息'
|
|
25
|
+
'help:帮助信息'
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if [[ CURRENT -eq 1 ]]; then
|
|
29
|
+
_describe 'command' commands
|
|
30
|
+
return
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
case "${words[2]}" in
|
|
34
|
+
project)
|
|
35
|
+
_cfix_project() {
|
|
36
|
+
local -a subcommands
|
|
37
|
+
subcommands=(
|
|
38
|
+
'add:添加项目'
|
|
39
|
+
'remove:移除项目'
|
|
40
|
+
'list:列出所有项目'
|
|
41
|
+
'info:项目详情'
|
|
42
|
+
)
|
|
43
|
+
if [[ CURRENT -eq 2 ]]; then
|
|
44
|
+
_describe 'subcommand' subcommands
|
|
45
|
+
elif [[ "${words[3]}" == add ]]; then
|
|
46
|
+
_files -/
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
_cfix_project
|
|
50
|
+
;;
|
|
51
|
+
|
|
52
|
+
config)
|
|
53
|
+
_cfix_config() {
|
|
54
|
+
local -a subcommands
|
|
55
|
+
subcommands=(
|
|
56
|
+
'get:获取配置'
|
|
57
|
+
'set:设置配置'
|
|
58
|
+
'list:列出配置'
|
|
59
|
+
'edit:编辑配置文件'
|
|
60
|
+
'reset:重置配置'
|
|
61
|
+
)
|
|
62
|
+
if [[ CURRENT -eq 2 ]]; then
|
|
63
|
+
_describe 'subcommand' subcommands
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
_cfix_config
|
|
67
|
+
;;
|
|
68
|
+
|
|
69
|
+
init)
|
|
70
|
+
_directories
|
|
71
|
+
;;
|
|
72
|
+
|
|
73
|
+
fix)
|
|
74
|
+
_arguments \
|
|
75
|
+
'--project[项目路径]' \
|
|
76
|
+
'--output[输出格式]:format:(interactive json quiet)' \
|
|
77
|
+
'--wait[等待任务完成]' \
|
|
78
|
+
'--yes[自动确认]' \
|
|
79
|
+
'--help[显示帮助]'
|
|
80
|
+
;;
|
|
81
|
+
|
|
82
|
+
monitor)
|
|
83
|
+
_arguments \
|
|
84
|
+
'--follow[持续监控状态]' \
|
|
85
|
+
'--no-auto-start[不自动启动服务]'
|
|
86
|
+
;;
|
|
87
|
+
|
|
88
|
+
wait)
|
|
89
|
+
_arguments \
|
|
90
|
+
'--timeout[超时时间(毫秒)]' \
|
|
91
|
+
'--interval[检查间隔(毫秒)]' \
|
|
92
|
+
'--yes[自动批准]'
|
|
93
|
+
;;
|
|
94
|
+
|
|
95
|
+
approve)
|
|
96
|
+
_arguments \
|
|
97
|
+
'--yes[跳过确认]'
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_cfix "$@"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 通知图标资源
|
|
2
|
+
|
|
3
|
+
此目录用于存放系统通知所需的图标资源。
|
|
4
|
+
|
|
5
|
+
## 所需图标
|
|
6
|
+
|
|
7
|
+
请准备以下图标文件(PNG 格式,建议尺寸 64x64 或 128x128):
|
|
8
|
+
|
|
9
|
+
1. **icon.png** - 主图标(用于通知)
|
|
10
|
+
2. **success.png** - 成功图标(可选)
|
|
11
|
+
3. **error.png** - 错误图标(可选)
|
|
12
|
+
|
|
13
|
+
## 如何添加图标
|
|
14
|
+
|
|
15
|
+
### 方法 1: 使用现有图标
|
|
16
|
+
将您的项目 logo 或品牌图标复制到此目录,重命名为 `icon.png`
|
|
17
|
+
|
|
18
|
+
### 方法 2: 使用在线工具生成
|
|
19
|
+
1. 访问 https://www.favicon-generator.org/
|
|
20
|
+
2. 上传您的图片或使用文字生成
|
|
21
|
+
3. 下载 PNG 格式图标
|
|
22
|
+
4. 保存到此目录
|
|
23
|
+
|
|
24
|
+
### 方法 3: 使用默认图标
|
|
25
|
+
如果不提供图标,node-notifier 将使用系统默认图标
|
|
26
|
+
|
|
27
|
+
## 注意事项
|
|
28
|
+
|
|
29
|
+
- 图标文件应为透明背景的 PNG 格式
|
|
30
|
+
- 建议尺寸:64x64 或 128x128 像素
|
|
31
|
+
- 文件大小建议小于 100KB
|
|
32
|
+
- 如果图标未找到,通知功能仍能正常工作,只是不会显示自定义图标
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const ExecUtil = require("./utils/exec");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { logger } = require("./logger");
|
|
4
|
+
const GitService = require("./git-service");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Claude CLI 服务
|
|
8
|
+
* 使用 claude -p "prompt" 命令执行一次性任务
|
|
9
|
+
* 参考命令:claude -p "prompt" --permission-mode acceptEdits --allowedTools "..." --debug
|
|
10
|
+
*/
|
|
11
|
+
class ClaudeCLIService {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.claudePath = config.claudePath || "claude"; // Claude CLI 路径
|
|
14
|
+
this.permissionMode = config.permissionMode || "acceptEdits"; // 权限模式
|
|
15
|
+
this.allowedTools =
|
|
16
|
+
config.allowedTools || "Bash(*) Read(*) Edit(*) Write(*)"; // 允许的工具
|
|
17
|
+
this.debug = config.debug || true; // 是否开启调试模式
|
|
18
|
+
this.maxTurns = config.maxTurns || 20;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 使用 Claude CLI 修复代码
|
|
23
|
+
* @param {string} projectPath - 项目路径
|
|
24
|
+
* @param {string} requirement - 修复要求
|
|
25
|
+
* @param {Object} options - 可选参数
|
|
26
|
+
* @param {Function} options.onProcessCreated - 子进程创建回调
|
|
27
|
+
* @returns {Promise<object>} 修复结果
|
|
28
|
+
*/
|
|
29
|
+
async fixCode(projectPath, requirement, options = {}) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
let isCancelled = false;
|
|
33
|
+
|
|
34
|
+
// 构建提示词
|
|
35
|
+
const prompt = `你是一个专业的代码修复助手。我需要你帮我完成以下任务:
|
|
36
|
+
|
|
37
|
+
【任务描述】
|
|
38
|
+
${requirement}
|
|
39
|
+
|
|
40
|
+
【项目路径】
|
|
41
|
+
${projectPath}
|
|
42
|
+
|
|
43
|
+
【工作流程】
|
|
44
|
+
1. 了解项目结构
|
|
45
|
+
2. 查找相关代码
|
|
46
|
+
3. 读取需要修改的文件
|
|
47
|
+
4. 完成代码修改
|
|
48
|
+
5. 完成后请总结你做了哪些更改
|
|
49
|
+
|
|
50
|
+
【重要提示】
|
|
51
|
+
- 请仔细阅读现有代码,理解项目结构后再修改
|
|
52
|
+
- 修改时保持代码风格一致
|
|
53
|
+
- 确保修改后的代码语法正确
|
|
54
|
+
- 完成后请明确说明修改了哪些文件
|
|
55
|
+
|
|
56
|
+
现在开始工作吧!`;
|
|
57
|
+
|
|
58
|
+
logger.info("🤖 启动 Claude CLI 进行代码修复...");
|
|
59
|
+
logger.info(`📂 工作目录: ${projectPath}`);
|
|
60
|
+
logger.info(`📋 需求: ${requirement}`);
|
|
61
|
+
logger.info(`🔧 权限模式: ${this.permissionMode}`);
|
|
62
|
+
logger.info(`🛠️ 允许工具: ${this.allowedTools}`);
|
|
63
|
+
logger.info(`📝 Prompt 长度: ${prompt.length} 字符`);
|
|
64
|
+
|
|
65
|
+
// 转义 prompt 用于 shell 命令(关键:处理换行符)
|
|
66
|
+
const escapedPrompt = prompt
|
|
67
|
+
.replace(/\\/g, '\\\\') // 转义反斜杠
|
|
68
|
+
.replace(/\r\n/g, '\\n') // Windows 换行符转为 \n
|
|
69
|
+
.replace(/\n/g, '\\n') // Unix 换行符转为 \n
|
|
70
|
+
.replace(/\r/g, '\\n') // Mac 换行符转为 \n
|
|
71
|
+
.replace(/"/g, '\\"') // 转义双引号
|
|
72
|
+
.replace(/`/g, '\\`') // 转义反引号
|
|
73
|
+
.replace(/\$/g, '\\$'); // 转义美元符号
|
|
74
|
+
|
|
75
|
+
// 构建完整的 shell 命令字符串
|
|
76
|
+
let command = `${this.claudePath} -p "${escapedPrompt}" --permission-mode ${this.permissionMode} --allowedTools "${this.allowedTools}"`;
|
|
77
|
+
|
|
78
|
+
// 添加调试模式
|
|
79
|
+
if (this.debug) {
|
|
80
|
+
command += " --debug";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
logger.info(
|
|
84
|
+
`🔧 执行命令: ${this.claudePath} -p "<prompt>" --permission-mode ${
|
|
85
|
+
this.permissionMode
|
|
86
|
+
}${this.debug ? " --debug" : ""}`
|
|
87
|
+
);
|
|
88
|
+
logger.debug("command:", command);
|
|
89
|
+
|
|
90
|
+
// 启动 Claude CLI 子进程(使用 shell 模式,传入完整命令字符串)
|
|
91
|
+
// 使用 ExecUtil.spawnShell 确保在 Windows 上隐藏窗口
|
|
92
|
+
const claude = ExecUtil.spawnShell(command, {
|
|
93
|
+
cwd: projectPath,
|
|
94
|
+
stdio: ["ignore", "pipe", "pipe"], // stdin 设为 ignore,避免等待输入
|
|
95
|
+
env: {
|
|
96
|
+
...process.env,
|
|
97
|
+
FORCE_COLOR: "0", // 禁用颜色输出
|
|
98
|
+
CI: "true", // 非交互式环境标识
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 通知父进程:子进程已创建,传递终止函数
|
|
103
|
+
if (options.onProcessCreated) {
|
|
104
|
+
options.onProcessCreated({
|
|
105
|
+
childProcess: claude,
|
|
106
|
+
cancel: () => {
|
|
107
|
+
if (!isCancelled) {
|
|
108
|
+
isCancelled = true;
|
|
109
|
+
logger.warn('🛑 收到取消请求,正在终止 Claude CLI 进程...');
|
|
110
|
+
|
|
111
|
+
// 先尝试优雅终止
|
|
112
|
+
claude.kill('SIGTERM');
|
|
113
|
+
|
|
114
|
+
// 5秒后强制终止
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
if (claude.exitCode === null) {
|
|
117
|
+
logger.warn('⚠️ 进程未响应 SIGTERM,使用 SIGKILL 强制终止');
|
|
118
|
+
claude.kill('SIGKILL');
|
|
119
|
+
}
|
|
120
|
+
}, 5000);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let stdout = "";
|
|
127
|
+
let stderr = "";
|
|
128
|
+
// 不再从输出提取文件列表,改为通过 git diff 获取
|
|
129
|
+
|
|
130
|
+
// 添加超时保护(30分钟)
|
|
131
|
+
const timeout = setTimeout(() => {
|
|
132
|
+
logger.warn("⚠️ 执行超时,强制终止 Claude CLI 进程");
|
|
133
|
+
claude.kill("SIGTERM");
|
|
134
|
+
setTimeout(() => claude.kill("SIGKILL"), 5000);
|
|
135
|
+
}, 30 * 60 * 1000);
|
|
136
|
+
|
|
137
|
+
// 收集标准输出
|
|
138
|
+
claude.stdout.on("data", (data) => {
|
|
139
|
+
const output = data.toString();
|
|
140
|
+
stdout += output;
|
|
141
|
+
|
|
142
|
+
// 实时显示输出
|
|
143
|
+
process.stdout.write(output);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// 收集标准错误
|
|
147
|
+
claude.stderr.on("data", (data) => {
|
|
148
|
+
const error = data.toString();
|
|
149
|
+
stderr += error;
|
|
150
|
+
// 调试模式下显示 stderr
|
|
151
|
+
if (this.debug) {
|
|
152
|
+
process.stderr.write(error);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// 处理进程退出
|
|
157
|
+
claude.on("close", (code) => {
|
|
158
|
+
clearTimeout(timeout); // 清理超时定时器
|
|
159
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
160
|
+
|
|
161
|
+
logger.info("=".repeat(60));
|
|
162
|
+
logger.info(`✅ Claude CLI 执行完成`);
|
|
163
|
+
logger.info(`⏱️ 耗时: ${duration}s`);
|
|
164
|
+
logger.info(`📝 退出代码: ${code}`);
|
|
165
|
+
logger.info("=".repeat(60));
|
|
166
|
+
|
|
167
|
+
// 如果是取消导致的退出
|
|
168
|
+
if (isCancelled) {
|
|
169
|
+
reject(new Error('任务已被用户取消'));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (code === 0) {
|
|
174
|
+
// 使用 GitService 获取变更信息
|
|
175
|
+
const gitDiffResult = GitService.getWorkspaceDiff(projectPath);
|
|
176
|
+
|
|
177
|
+
resolve({
|
|
178
|
+
success: true,
|
|
179
|
+
changedFiles: gitDiffResult.actualChangedFiles, // 实际变更的文件列表
|
|
180
|
+
summary: gitDiffResult.fullDiff
|
|
181
|
+
? `变更了 ${gitDiffResult.actualChangedFiles.length} 个文件`
|
|
182
|
+
: "", // 简单摘要
|
|
183
|
+
fullOutput: stdout,
|
|
184
|
+
conversationTurns: this.countConversationTurns(stdout),
|
|
185
|
+
duration: `${duration}s`,
|
|
186
|
+
exitCode: code,
|
|
187
|
+
// 新增: Git diff 详细信息
|
|
188
|
+
gitDiff: gitDiffResult,
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
reject(
|
|
192
|
+
new Error(`Claude CLI 执行失败,退出代码: ${code}\n${stderr}`)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// 处理错误
|
|
198
|
+
claude.on("error", (error) => {
|
|
199
|
+
clearTimeout(timeout); // 清理超时定时器
|
|
200
|
+
logger.error("❌ Claude CLI 启动失败:", error.message);
|
|
201
|
+
reject(new Error(`无法启动 Claude CLI: ${error.message}`));
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 统计对话轮次
|
|
208
|
+
*/
|
|
209
|
+
countConversationTurns(output) {
|
|
210
|
+
// 简单统计 "Assistant:" 或类似标记的出现次数
|
|
211
|
+
const matches = output.match(/(?:Assistant|Claude|AI):/gi);
|
|
212
|
+
return matches ? matches.length : 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = ClaudeCLIService;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const UnifiedConfigManager = require('../../cli/lib/config-manager');
|
|
2
|
+
const { logger } = require('../logger');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 服务端配置包装器
|
|
6
|
+
* 使用统一配置管理器,添加服务端特定验证
|
|
7
|
+
*/
|
|
8
|
+
class ServerConfigWrapper {
|
|
9
|
+
constructor() {
|
|
10
|
+
// 使用统一配置管理器(单例)
|
|
11
|
+
this.configManager = UnifiedConfigManager;
|
|
12
|
+
this.validate();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 获取配置
|
|
17
|
+
*/
|
|
18
|
+
get(path) {
|
|
19
|
+
return this.configManager.get(path);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 设置配置
|
|
24
|
+
*/
|
|
25
|
+
set(path, value) {
|
|
26
|
+
return this.configManager.set(path, value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取所有配置
|
|
31
|
+
*/
|
|
32
|
+
getAll() {
|
|
33
|
+
return this.configManager.get();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 验证配置(服务端特定)
|
|
38
|
+
*/
|
|
39
|
+
validate() {
|
|
40
|
+
const errors = [];
|
|
41
|
+
|
|
42
|
+
// 必需配置检查
|
|
43
|
+
const workDir = this.get('server.workDir');
|
|
44
|
+
if (!workDir) {
|
|
45
|
+
errors.push('server.workDir 未配置(请设置 WORK_DIR 环境变量或在配置文件中设置)');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// AI 引擎配置检查
|
|
49
|
+
const engine = this.get('ai.defaultEngine');
|
|
50
|
+
if (engine !== 'claude-cli') {
|
|
51
|
+
errors.push(`未知的 AI 引擎: ${engine},当前仅支持 claude-cli`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 端口检查
|
|
55
|
+
const port = this.get('server.port') || this.get('service.port');
|
|
56
|
+
if (port && (port < 1 || port > 65535)) {
|
|
57
|
+
errors.push(`无效的端口号: ${port}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (errors.length > 0) {
|
|
61
|
+
logger.error('配置验证失败:', errors);
|
|
62
|
+
throw new Error(`配置验证失败: ${errors.join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logger.info('✅ 配置验证通过');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = new ServerConfigWrapper();
|