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,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 项目管理命令
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const config = require("../lib/config-manager");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 解析项目路径
|
|
12
|
+
* @param {string} projectPath - 用户输入的路径
|
|
13
|
+
* @returns {string} 解析后的绝对路径
|
|
14
|
+
*/
|
|
15
|
+
function resolveProjectPath(projectPath) {
|
|
16
|
+
// 如果是绝对路径,直接返回
|
|
17
|
+
if (path.isAbsolute(projectPath)) {
|
|
18
|
+
return projectPath;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 如果是相对路径且存在,相对于当前工作目录
|
|
22
|
+
const relativeToCwd = path.resolve(process.cwd(), projectPath);
|
|
23
|
+
if (fs.existsSync(relativeToCwd)) {
|
|
24
|
+
return relativeToCwd;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 否则相对于 defaultWorkDir 解析
|
|
28
|
+
const workDir = config.get("projects.defaultWorkDir");
|
|
29
|
+
return path.resolve(workDir, projectPath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 克隆 Git 仓库
|
|
34
|
+
* @param {string} repoUrl - 仓库 URL
|
|
35
|
+
* @param {string} targetPath - 目标路径
|
|
36
|
+
* @returns {Promise<void>}
|
|
37
|
+
*/
|
|
38
|
+
async function cloneRepo(repoUrl, targetPath) {
|
|
39
|
+
console.log("📦 开始克隆仓库...");
|
|
40
|
+
console.log(` URL: ${repoUrl}`);
|
|
41
|
+
console.log(` 目标: ${targetPath}`);
|
|
42
|
+
console.log("");
|
|
43
|
+
|
|
44
|
+
// 确保工作空间目录存在
|
|
45
|
+
const workDir = config.get("projects.defaultWorkDir");
|
|
46
|
+
if (!fs.existsSync(workDir)) {
|
|
47
|
+
fs.mkdirSync(workDir, { recursive: true });
|
|
48
|
+
console.log(`✅ 已创建工作空间: ${workDir}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// 使用 git clone 命令克隆仓库
|
|
53
|
+
execSync(`git clone "${repoUrl}" "${targetPath}"`, {
|
|
54
|
+
stdio: "inherit",
|
|
55
|
+
encoding: "utf-8",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log("✅ 仓库克隆成功!");
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new Error(`克隆失败: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 添加项目
|
|
67
|
+
*/
|
|
68
|
+
async function add(projectPath, options) {
|
|
69
|
+
console.log("📁 添加项目");
|
|
70
|
+
console.log("");
|
|
71
|
+
console.log("======================================");
|
|
72
|
+
|
|
73
|
+
// 检查是否是 Git URL(支持 http/https/ssh 协议)
|
|
74
|
+
const isGitUrl = /^(https?|git|ssh):\/\/|^git@[\w.-]+:/i.test(projectPath);
|
|
75
|
+
|
|
76
|
+
let resolvedPath;
|
|
77
|
+
let repoUrl = options.repoUrl;
|
|
78
|
+
|
|
79
|
+
if (isGitUrl) {
|
|
80
|
+
// 传入的是 Git URL
|
|
81
|
+
repoUrl = projectPath;
|
|
82
|
+
|
|
83
|
+
// 从 Git URL 提取项目名称作为默认本地路径
|
|
84
|
+
const urlParts = projectPath.split("/");
|
|
85
|
+
const repoName = urlParts[urlParts.length - 1].replace(/\.git$/, "");
|
|
86
|
+
resolvedPath = path.resolve(
|
|
87
|
+
config.get("projects.defaultWorkDir"),
|
|
88
|
+
repoName
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
console.log(`Git 仓库 URL: ${repoUrl}`);
|
|
92
|
+
console.log(`目标路径: ${resolvedPath}`);
|
|
93
|
+
console.log("======================================");
|
|
94
|
+
console.log("");
|
|
95
|
+
|
|
96
|
+
// 检查本地路径是否已存在
|
|
97
|
+
if (fs.existsSync(resolvedPath)) {
|
|
98
|
+
console.log(`✅ 本地路径已存在: ${resolvedPath}`);
|
|
99
|
+
} else {
|
|
100
|
+
// 自动克隆仓库
|
|
101
|
+
console.log(`⚠️ 本地路径不存在,将自动克隆仓库`);
|
|
102
|
+
console.log("");
|
|
103
|
+
|
|
104
|
+
// 询问是否确认克隆 (如果没有 --yes 参数)
|
|
105
|
+
if (!options.yes && !options.autoConfirm) {
|
|
106
|
+
// 使用简单的 readline 代替 inquirer (ES 模块兼容问题)
|
|
107
|
+
const readline = require("readline");
|
|
108
|
+
const rl = readline.createInterface({
|
|
109
|
+
input: process.stdin,
|
|
110
|
+
output: process.stdout,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const confirmed = await new Promise((resolve) => {
|
|
114
|
+
rl.question("确认克隆仓库到工作空间? (Y/n): ", (answer) => {
|
|
115
|
+
rl.close();
|
|
116
|
+
resolve(
|
|
117
|
+
!answer ||
|
|
118
|
+
answer.toLowerCase() === "y" ||
|
|
119
|
+
answer.toLowerCase() === "yes"
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!confirmed) {
|
|
125
|
+
console.log("❌ 已取消");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await cloneRepo(repoUrl, resolvedPath);
|
|
131
|
+
console.log("");
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
// 传入的是本地路径 - 使用智能解析
|
|
135
|
+
resolvedPath = resolveProjectPath(projectPath);
|
|
136
|
+
console.log(`路径: ${resolvedPath}`);
|
|
137
|
+
console.log("======================================");
|
|
138
|
+
console.log("");
|
|
139
|
+
|
|
140
|
+
// 检查项目路径是否存在
|
|
141
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
142
|
+
throw new Error(`项目路径不存在: ${resolvedPath}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// 检查是否为 Git 仓库
|
|
148
|
+
const gitDir = path.join(resolvedPath, ".git");
|
|
149
|
+
if (!fs.existsSync(gitDir)) {
|
|
150
|
+
console.log("⚠️ 警告: 不是 Git 仓库");
|
|
151
|
+
console.log("");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 检测项目信息
|
|
155
|
+
const name = options.name || path.basename(resolvedPath);
|
|
156
|
+
const alias = options.alias;
|
|
157
|
+
|
|
158
|
+
// 如果没有指定 repoUrl,尝试检测
|
|
159
|
+
if (!repoUrl) {
|
|
160
|
+
repoUrl = detectRepoUrl(resolvedPath);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 检测默认分支
|
|
164
|
+
let defaultBranch = "main";
|
|
165
|
+
if (
|
|
166
|
+
fs.existsSync(path.join(resolvedPath, ".git", "refs", "heads", "main"))
|
|
167
|
+
) {
|
|
168
|
+
defaultBranch = "main";
|
|
169
|
+
} else if (
|
|
170
|
+
fs.existsSync(path.join(resolvedPath, ".git", "refs", "heads", "master"))
|
|
171
|
+
) {
|
|
172
|
+
defaultBranch = "master";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 创建项目对象
|
|
176
|
+
const project = {
|
|
177
|
+
path: resolvedPath,
|
|
178
|
+
name,
|
|
179
|
+
alias,
|
|
180
|
+
repoUrl,
|
|
181
|
+
defaultBranch,
|
|
182
|
+
tags: [],
|
|
183
|
+
createdAt: new Date().toISOString(),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// 添加到配置
|
|
187
|
+
config.addProject(project);
|
|
188
|
+
|
|
189
|
+
console.log("✅ 项目已添加");
|
|
190
|
+
console.log("");
|
|
191
|
+
console.log("项目信息:");
|
|
192
|
+
console.log(` 名称: ${name}`);
|
|
193
|
+
console.log(` 路径: ${resolvedPath}`);
|
|
194
|
+
if (alias) console.log(` 别名: ${alias}`);
|
|
195
|
+
if (repoUrl) console.log(` 仓库: ${repoUrl}`);
|
|
196
|
+
console.log(` 分支: ${defaultBranch}`);
|
|
197
|
+
console.log("");
|
|
198
|
+
console.log("提示:");
|
|
199
|
+
console.log(` cfix "${name}" <需求> # 使用项目名称`);
|
|
200
|
+
if (alias) console.log(` cfix "${alias}" <需求> # 使用项目别名`);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`❌ 添加项目失败: ${error.message}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 移除项目
|
|
209
|
+
*/
|
|
210
|
+
async function remove(projectPath) {
|
|
211
|
+
console.log("🗑️ 移除项目");
|
|
212
|
+
console.log("");
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
config.removeProject(projectPath);
|
|
216
|
+
console.log(`✅ 项目已移除: ${projectPath}`);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error(`❌ 移除项目失败: ${error.message}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 列出所有项目
|
|
225
|
+
*/
|
|
226
|
+
async function list() {
|
|
227
|
+
const projects = config.listProjects();
|
|
228
|
+
|
|
229
|
+
console.log("📋 项目列表");
|
|
230
|
+
console.log("");
|
|
231
|
+
console.log("======================================");
|
|
232
|
+
|
|
233
|
+
if (projects.length === 0) {
|
|
234
|
+
console.log("暂无项目");
|
|
235
|
+
console.log("");
|
|
236
|
+
console.log("使用 `cfix project add <path>` 添加项目");
|
|
237
|
+
} else {
|
|
238
|
+
console.log(`总计: ${projects.length} 个项目`);
|
|
239
|
+
console.log("======================================");
|
|
240
|
+
console.log("");
|
|
241
|
+
|
|
242
|
+
projects.forEach((project, index) => {
|
|
243
|
+
console.log(`${index + 1}. ${project.name}`);
|
|
244
|
+
console.log(` 路径: ${project.path}`);
|
|
245
|
+
if (project.alias) console.log(` 别名: ${project.alias}`);
|
|
246
|
+
if (project.repoUrl) console.log(` 仓库: ${project.repoUrl}`);
|
|
247
|
+
console.log(` 分支: ${project.defaultBranch}`);
|
|
248
|
+
console.log("");
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
console.log("======================================");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 项目详情
|
|
257
|
+
*/
|
|
258
|
+
async function info(projectPath) {
|
|
259
|
+
console.log("📊 项目详情");
|
|
260
|
+
console.log("");
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const project = config.findProject(projectPath);
|
|
264
|
+
|
|
265
|
+
if (!project) {
|
|
266
|
+
console.log(`❌ 项目不存在: ${projectPath}`);
|
|
267
|
+
console.log("");
|
|
268
|
+
console.log("提示:");
|
|
269
|
+
console.log(" cfix project list # 查看所有项目");
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log("======================================");
|
|
274
|
+
console.log(`名称: ${project.name}`);
|
|
275
|
+
console.log("======================================");
|
|
276
|
+
console.log("");
|
|
277
|
+
console.log("基本信息:");
|
|
278
|
+
console.log(` 路径: ${project.path}`);
|
|
279
|
+
if (project.alias) console.log(` 别名: ${project.alias}`);
|
|
280
|
+
if (project.repoUrl) console.log(` 仓库: ${project.repoUrl}`);
|
|
281
|
+
console.log(` 分支: ${project.defaultBranch}`);
|
|
282
|
+
console.log(
|
|
283
|
+
` 添加时间: ${new Date(project.createdAt).toLocaleString("zh-CN")}`
|
|
284
|
+
);
|
|
285
|
+
if (project.tags && project.tags.length > 0) {
|
|
286
|
+
console.log(` 标签: ${project.tags.join(", ")}`);
|
|
287
|
+
}
|
|
288
|
+
console.log("");
|
|
289
|
+
|
|
290
|
+
// 检查项目配置文件
|
|
291
|
+
const projectConfig = config.getProjectConfig(project.path);
|
|
292
|
+
if (projectConfig) {
|
|
293
|
+
console.log("项目配置 (.cfix.json):");
|
|
294
|
+
console.log(` AI 引擎: ${projectConfig.ai?.engine || "默认"}`);
|
|
295
|
+
console.log(` 最大轮次: ${projectConfig.ai?.maxTurns || "默认"}`);
|
|
296
|
+
if (projectConfig.tests?.enabled) {
|
|
297
|
+
console.log(` 测试命令: ${projectConfig.tests.command || "npm test"}`);
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
console.log("⚠️ 未找到 .cfix.json 配置文件");
|
|
301
|
+
console.log("使用 `cfix init` 初始化项目配置");
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error(`❌ 获取项目信息失败: ${error.message}`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* 检测 Git 仓库 URL
|
|
311
|
+
*/
|
|
312
|
+
function detectRepoUrl(projectPath) {
|
|
313
|
+
try {
|
|
314
|
+
const gitConfig = path.join(projectPath, ".git", "config");
|
|
315
|
+
if (!fs.existsSync(gitConfig)) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const content = fs.readFileSync(gitConfig, "utf-8");
|
|
320
|
+
const match = content.match(/url = (.+)/);
|
|
321
|
+
if (match) {
|
|
322
|
+
return match[1].trim();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return null;
|
|
326
|
+
} catch {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
module.exports = { add, remove, list, info };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 服务管理命令
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const ProcessManager = require("../lib/process-manager");
|
|
6
|
+
const ServiceClient = require("../lib/service-client");
|
|
7
|
+
const config = require("../lib/config-manager");
|
|
8
|
+
|
|
9
|
+
const pm = new ProcessManager(config);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 启动服务
|
|
13
|
+
*/
|
|
14
|
+
async function start(options) {
|
|
15
|
+
console.log("🚀 启动服务...");
|
|
16
|
+
console.log("");
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// 检查是否已运行
|
|
20
|
+
if (await pm.isRunning()) {
|
|
21
|
+
const pid = pm.getPid();
|
|
22
|
+
console.log(`⚠️ 服务已在运行 (PID: ${pid})`);
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log("使用 `cfix stop` 停止服务");
|
|
25
|
+
console.log("使用 `cfix status` 查看服务状态");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 显示服务配置
|
|
30
|
+
const serviceConfig = config.getServiceConfig();
|
|
31
|
+
const workDir = config.get("projects.defaultWorkDir");
|
|
32
|
+
console.log(`📋 服务配置:`);
|
|
33
|
+
console.log(` 端口: ${serviceConfig.port}`);
|
|
34
|
+
console.log(` 主机: ${serviceConfig.host}`);
|
|
35
|
+
console.log(` 日志级别: ${serviceConfig.logLevel}`);
|
|
36
|
+
console.log(` 最大并发: ${serviceConfig.maxConcurrent}`);
|
|
37
|
+
console.log("");
|
|
38
|
+
|
|
39
|
+
// 显示工作空间配置
|
|
40
|
+
console.log(`📂 工作空间: ${workDir}`);
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log("配置工作空间:");
|
|
43
|
+
console.log(
|
|
44
|
+
` cfix config set projects.defaultWorkDir /path/to/workspace or "E:\\demo\\projects" 双引号也需要复制`
|
|
45
|
+
);
|
|
46
|
+
console.log("");
|
|
47
|
+
|
|
48
|
+
// 启动服务
|
|
49
|
+
const pid = await pm.start({
|
|
50
|
+
daemon: options.daemon !== false,
|
|
51
|
+
wait: options.wait,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log(`✅ 服务已启动 (PID: ${pid})`);
|
|
55
|
+
console.log("");
|
|
56
|
+
|
|
57
|
+
// 显示服务地址
|
|
58
|
+
console.log(
|
|
59
|
+
`🌐 服务地址: http://${serviceConfig.host}:${serviceConfig.port}`
|
|
60
|
+
);
|
|
61
|
+
console.log("");
|
|
62
|
+
|
|
63
|
+
// 显示常用命令
|
|
64
|
+
console.log("常用命令:");
|
|
65
|
+
console.log(" cfix status - 查看服务状态");
|
|
66
|
+
console.log(" cfix logs - 查看日志");
|
|
67
|
+
console.log(" cfix stop - 停止服务");
|
|
68
|
+
console.log(" cfix monitor - 打开监控面板");
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(`❌ 启动失败: ${error.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 停止服务
|
|
77
|
+
*/
|
|
78
|
+
async function stop(options) {
|
|
79
|
+
console.log("🛑 停止服务...");
|
|
80
|
+
console.log("");
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await pm.stop(options);
|
|
84
|
+
if (!options.quiet) {
|
|
85
|
+
console.log("✅ 服务已停止");
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`❌ 停止失败: ${error.message}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 重启服务
|
|
95
|
+
*/
|
|
96
|
+
async function restart() {
|
|
97
|
+
console.log("🔄 重启服务...");
|
|
98
|
+
console.log("");
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// 先停止
|
|
102
|
+
if (await pm.isRunning()) {
|
|
103
|
+
console.log("⏹️ 停止服务...");
|
|
104
|
+
await pm.stop({ quiet: true });
|
|
105
|
+
console.log("✅ 服务已停止");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log("");
|
|
109
|
+
console.log("⏯️ 启动服务...");
|
|
110
|
+
|
|
111
|
+
// 再启动
|
|
112
|
+
const pid = await pm.start({ daemon: true, wait: false });
|
|
113
|
+
|
|
114
|
+
// 等待服务就绪
|
|
115
|
+
console.log("⏳ 等待服务就绪...");
|
|
116
|
+
await pm.waitForReady();
|
|
117
|
+
|
|
118
|
+
console.log("");
|
|
119
|
+
console.log(`✅ 服务已重启 (PID: ${pid})`);
|
|
120
|
+
console.log("");
|
|
121
|
+
|
|
122
|
+
// 验证服务状态
|
|
123
|
+
const client = new ServiceClient();
|
|
124
|
+
const health = await client.healthCheck();
|
|
125
|
+
console.log(`📊 服务状态: ${health.status}`);
|
|
126
|
+
console.log(`⏱️ 运行时间: ${Math.floor(health.uptime / 60)} 分钟`);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`❌ 重启失败: ${error.message}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = { start, stop, restart };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 状态命令
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const ProcessManager = require('../lib/process-manager');
|
|
6
|
+
const ServiceClient = require('../lib/service-client');
|
|
7
|
+
const config = require('../lib/config-manager');
|
|
8
|
+
|
|
9
|
+
const pm = new ProcessManager(config);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 显示服务状态
|
|
13
|
+
*/
|
|
14
|
+
async function handle() {
|
|
15
|
+
console.log('📊 CodeFix 服务状态');
|
|
16
|
+
console.log('');
|
|
17
|
+
console.log('======================================');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// 检查进程状态
|
|
21
|
+
const isRunning = await pm.isRunning();
|
|
22
|
+
|
|
23
|
+
if (isRunning) {
|
|
24
|
+
const pid = pm.getPid();
|
|
25
|
+
console.log('状态: ✅ 运行中');
|
|
26
|
+
console.log(`PID: ${pid}`);
|
|
27
|
+
console.log('');
|
|
28
|
+
|
|
29
|
+
// 获取服务健康状态
|
|
30
|
+
try {
|
|
31
|
+
const client = new ServiceClient();
|
|
32
|
+
const health = await client.healthCheck();
|
|
33
|
+
|
|
34
|
+
console.log('服务信息:');
|
|
35
|
+
console.log(` 状态: ${health.status}`);
|
|
36
|
+
console.log(` 运行时间: ${formatUptime(health.uptime)}`);
|
|
37
|
+
console.log(` 时间戳: ${new Date(health.timestamp).toLocaleString('zh-CN')}`);
|
|
38
|
+
|
|
39
|
+
// 显示性能指标
|
|
40
|
+
if (health.metrics) {
|
|
41
|
+
const tasks = health.metrics.tasks || {};
|
|
42
|
+
const queue = health.metrics.queue || {};
|
|
43
|
+
|
|
44
|
+
// 只有当有任务数据时才显示
|
|
45
|
+
if (tasks.total !== undefined || tasks.pending !== undefined) {
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('任务统计:');
|
|
48
|
+
console.log(` 总计: ${tasks.total || 0}`);
|
|
49
|
+
console.log(` 等待中: ${tasks.pending || 0}`);
|
|
50
|
+
console.log(` 运行中: ${tasks.running || 0}`);
|
|
51
|
+
console.log(` 已完成: ${tasks.completed || 0}`);
|
|
52
|
+
console.log(` 失败: ${tasks.failed || 0}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 显示队列统计
|
|
56
|
+
if (queue.waiting !== undefined || queue.active !== undefined) {
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log('队列统计:');
|
|
59
|
+
console.log(` 等待: ${queue.waiting || 0}`);
|
|
60
|
+
console.log(` 活跃: ${queue.active || 0}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log('⚠️ 无法获取服务详细信息');
|
|
66
|
+
console.log(`错误: ${error.message}`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
console.log('状态: ❌ 未运行');
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log('使用 `cfix start` 启动服务');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 显示配置
|
|
75
|
+
const serviceConfig = config.getServiceConfig();
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('======================================');
|
|
78
|
+
console.log('配置信息:');
|
|
79
|
+
console.log(` 端口: ${serviceConfig.port}`);
|
|
80
|
+
console.log(` 主机: ${serviceConfig.host}`);
|
|
81
|
+
console.log(` 日志级别: ${serviceConfig.logLevel}`);
|
|
82
|
+
console.log(` 最大并发: ${serviceConfig.maxConcurrent}`);
|
|
83
|
+
|
|
84
|
+
// 显示服务地址
|
|
85
|
+
if (isRunning) {
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(`🌐 服务地址: http://${serviceConfig.host}:${serviceConfig.port}`);
|
|
88
|
+
console.log(`📱 监控面板: http://${serviceConfig.host}:${serviceConfig.port}/monitor.html`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(`❌ 获取状态失败: ${error.message}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('======================================');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 格式化运行时间
|
|
101
|
+
*/
|
|
102
|
+
function formatUptime(seconds) {
|
|
103
|
+
if (seconds < 60) {
|
|
104
|
+
return `${seconds} 秒`;
|
|
105
|
+
} else if (seconds < 3600) {
|
|
106
|
+
const minutes = Math.floor(seconds / 60);
|
|
107
|
+
return `${minutes} 分钟`;
|
|
108
|
+
} else {
|
|
109
|
+
const hours = Math.floor(seconds / 3600);
|
|
110
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
111
|
+
return `${hours} 小时 ${minutes} 分钟`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { handle };
|