@zshuangmu/agenthub 0.3.2 → 0.3.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zshuangmu/agenthub",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "AI Agent 打包与分发平台 - 一句话打包上传,一键下载获得能力",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/cli.js CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  formatStatsOutput,
27
27
  versionsCommand,
28
28
  formatVersionsOutput,
29
+ doctorCommand,
29
30
  } from "./index.js";
30
31
 
31
32
  import { success, error, warning, info as infoColor, highlight, muted, symbols } from "./lib/colors.js";
@@ -69,6 +70,7 @@ AgentHub v${VERSION} - AI Agent 打包与分发平台
69
70
  update 更新已安装 Agent 到最新版
70
71
  rollback 回滚已安装 Agent 到指定版本
71
72
  stats 查看 Agent 统计信息
73
+ doctor 诊断 AgentHub 安装和环境问题
72
74
  serve 启动 Web + API 服务
73
75
 
74
76
  选项:
@@ -241,6 +243,22 @@ agenthub stats - 查看 Agent 统计信息
241
243
 
242
244
  示例:
243
245
  agenthub stats workspace --registry ./.registry
246
+ `,
247
+ doctor: `
248
+ agenthub doctor - 诊断 AgentHub 安装和环境问题
249
+
250
+ 用法:
251
+ agenthub doctor [options]
252
+
253
+ 选项:
254
+ --full 运行完整诊断(包括测试套件)
255
+ --no-network 跳过网络连接检查
256
+ --server <url> 指定服务器地址检查(默认: https://agenthub.cyou)
257
+
258
+ 示例:
259
+ agenthub doctor
260
+ agenthub doctor --full
261
+ agenthub doctor --no-network
244
262
  `,
245
263
  serve: `
246
264
  agenthub serve - 启动完整服务(前端+后端)
@@ -418,6 +436,14 @@ async function main() {
418
436
  return;
419
437
  }
420
438
 
439
+ case "doctor": {
440
+ const result = await doctorCommand(options);
441
+ if (!result.healthy) {
442
+ process.exitCode = 1;
443
+ }
444
+ return;
445
+ }
446
+
421
447
  case "serve": {
422
448
  const result = await serveCommand(options);
423
449
  console.log(success(`Server listening at ${result.baseUrl}`));
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Doctor Command
3
+ * 诊断 AgentHub 安装和环境问题
4
+ */
5
+
6
+ import { execSync } from "node:child_process";
7
+ import { access, constants, readFile } from "node:fs/promises";
8
+ import path from "node:path";
9
+ import { success, error, warning, info as infoColor, highlight, muted } from "../lib/colors.js";
10
+
11
+ const PROJECT_ROOT = path.resolve(import.meta.dirname, "../..");
12
+
13
+ /**
14
+ * 检查 Node.js 版本
15
+ */
16
+ function checkNodeVersion() {
17
+ const version = process.version;
18
+ const major = parseInt(version.slice(1).split(".")[0], 10);
19
+ const required = 18;
20
+ const passed = major >= required;
21
+ return {
22
+ name: "Node.js 版本",
23
+ passed,
24
+ message: passed
25
+ ? `${version} (需要 >= ${required})`
26
+ : `${version} (需要 >= ${required})`,
27
+ severity: passed ? "ok" : "error",
28
+ fix: passed ? null : `升级 Node.js 到 v${required} 或更高版本`,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 检查 npm 包安装
34
+ */
35
+ async function checkNpmPackage() {
36
+ try {
37
+ const packageJson = await readFile(
38
+ path.join(PROJECT_ROOT, "package.json"),
39
+ "utf8"
40
+ );
41
+ const pkg = JSON.parse(packageJson);
42
+ return {
43
+ name: "AgentHub 包",
44
+ passed: true,
45
+ message: `v${pkg.version} 已安装`,
46
+ severity: "ok",
47
+ fix: null,
48
+ };
49
+ } catch {
50
+ return {
51
+ name: "AgentHub 包",
52
+ passed: false,
53
+ message: "无法读取 package.json",
54
+ severity: "error",
55
+ fix: "重新安装 AgentHub: npm install -g @zshuangmu/agenthub",
56
+ };
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 检查 CLI 可执行文件
62
+ */
63
+ async function checkCliExecutable() {
64
+ try {
65
+ const result = execSync("node src/cli.js --version", {
66
+ cwd: PROJECT_ROOT,
67
+ encoding: "utf8",
68
+ stdio: ["pipe", "pipe", "pipe"],
69
+ });
70
+ const version = result.trim();
71
+ return {
72
+ name: "CLI 可执行文件",
73
+ passed: true,
74
+ message: version,
75
+ severity: "ok",
76
+ fix: null,
77
+ };
78
+ } catch (err) {
79
+ return {
80
+ name: "CLI 可执行文件",
81
+ passed: false,
82
+ message: "CLI 无法执行",
83
+ severity: "error",
84
+ fix: "检查 Node.js 安装和文件权限",
85
+ };
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 检查测试套件
91
+ */
92
+ async function checkTests() {
93
+ try {
94
+ const result = execSync("npm test", {
95
+ cwd: PROJECT_ROOT,
96
+ encoding: "utf8",
97
+ stdio: ["pipe", "pipe", "pipe"],
98
+ timeout: 30000,
99
+ });
100
+ const match = result.match(/ℹ tests (\d+).*ℹ pass (\d+).*ℹ fail (\d+)/s);
101
+ if (match) {
102
+ const [, total, passed, failed] = match;
103
+ const passRate = (parseInt(passed, 10) / parseInt(total, 10)) * 100;
104
+ return {
105
+ name: "测试套件",
106
+ passed: parseInt(failed, 10) === 0,
107
+ message: `${passed}/${total} 通过 (${passRate.toFixed(0)}%)`,
108
+ severity: parseInt(failed, 10) > 0 ? "warning" : "ok",
109
+ fix: parseInt(failed, 10) > 0 ? "运行 npm test 查看失败详情" : null,
110
+ };
111
+ }
112
+ return {
113
+ name: "测试套件",
114
+ passed: true,
115
+ message: "测试通过",
116
+ severity: "ok",
117
+ fix: null,
118
+ };
119
+ } catch (err) {
120
+ return {
121
+ name: "测试套件",
122
+ passed: false,
123
+ message: "测试执行失败",
124
+ severity: "warning",
125
+ fix: "运行 npm test 查看失败详情",
126
+ };
127
+ }
128
+ }
129
+
130
+ /**
131
+ * 检查样本 Agent
132
+ */
133
+ async function checkSampleAgents() {
134
+ const samplesDir = path.join(PROJECT_ROOT, "samples");
135
+ const expectedSamples = [
136
+ "code-review-assistant",
137
+ "team-knowledge-assistant",
138
+ "product-doc-writer",
139
+ ];
140
+ const results = [];
141
+
142
+ for (const sample of expectedSamples) {
143
+ const samplePath = path.join(samplesDir, sample);
144
+ try {
145
+ await access(samplePath, constants.R_OK);
146
+ results.push({ name: sample, exists: true });
147
+ } catch {
148
+ results.push({ name: sample, exists: false });
149
+ }
150
+ }
151
+
152
+ const existing = results.filter((r) => r.exists).length;
153
+ const passed = existing === expectedSamples.length;
154
+
155
+ return {
156
+ name: "样本 Agent",
157
+ passed,
158
+ message: `${existing}/${expectedSamples.length} 可用`,
159
+ severity: passed ? "ok" : "warning",
160
+ fix: passed ? null : `缺失样本: ${results
161
+ .filter((r) => !r.exists)
162
+ .map((r) => r.name)
163
+ .join(", ")}`,
164
+ };
165
+ }
166
+
167
+ /**
168
+ * 检查文档
169
+ */
170
+ async function checkDocumentation() {
171
+ const docsDir = path.join(PROJECT_ROOT, "docs");
172
+ const expectedDocs = [
173
+ "faq.md",
174
+ "growth-plan.md",
175
+ "team-distribution-guide.md",
176
+ "quick-start-3-steps.md",
177
+ ];
178
+ const results = [];
179
+
180
+ for (const doc of expectedDocs) {
181
+ const docPath = path.join(docsDir, doc);
182
+ try {
183
+ await access(docPath, constants.R_OK);
184
+ results.push({ name: doc, exists: true });
185
+ } catch {
186
+ results.push({ name: doc, exists: false });
187
+ }
188
+ }
189
+
190
+ const existing = results.filter((r) => r.exists).length;
191
+ const passed = existing >= expectedDocs.length - 1; // 允许缺少1个
192
+
193
+ return {
194
+ name: "文档完整性",
195
+ passed,
196
+ message: `${existing}/${expectedDocs.length} 文档可用`,
197
+ severity: passed ? "ok" : "warning",
198
+ fix: passed ? null : `检查 docs/ 目录`,
199
+ };
200
+ }
201
+
202
+ /**
203
+ * 检查网络连接 (可选)
204
+ */
205
+ async function checkNetworkConnection(serverUrl) {
206
+ const url = serverUrl || "https://agenthub.cyou";
207
+ try {
208
+ const controller = new AbortController();
209
+ const timeout = setTimeout(() => controller.abort(), 5000);
210
+
211
+ const response = await fetch(url, {
212
+ method: "HEAD",
213
+ signal: controller.signal,
214
+ });
215
+ clearTimeout(timeout);
216
+
217
+ return {
218
+ name: "网络连接",
219
+ passed: response.ok,
220
+ message: `${url} - ${response.status}`,
221
+ severity: response.ok ? "ok" : "warning",
222
+ fix: response.ok ? null : "检查网络连接或服务器状态",
223
+ };
224
+ } catch (err) {
225
+ return {
226
+ name: "网络连接",
227
+ passed: false,
228
+ message: `${url} - 连接失败`,
229
+ severity: "warning",
230
+ fix: "检查网络连接或稍后重试",
231
+ };
232
+ }
233
+ }
234
+
235
+ /**
236
+ * 运行所有诊断检查
237
+ */
238
+ export async function doctorCommand(options) {
239
+ const checks = [];
240
+
241
+ console.log(`\n${highlight("🔍 AgentHub 诊断检查")}\n`);
242
+ console.log(`${muted("检查 AgentHub 安装和环境配置...")}\n`);
243
+
244
+ // 基础检查
245
+ checks.push(checkNodeVersion());
246
+ checks.push(await checkNpmPackage());
247
+ checks.push(await checkCliExecutable());
248
+
249
+ // 测试检查
250
+ if (options.full) {
251
+ checks.push(await checkTests());
252
+ }
253
+
254
+ // 内容检查
255
+ checks.push(await checkSampleAgents());
256
+ checks.push(await checkDocumentation());
257
+
258
+ // 网络检查
259
+ if (options.network !== false) {
260
+ checks.push(await checkNetworkConnection(options.server));
261
+ }
262
+
263
+ // 输出结果
264
+ let passed = 0;
265
+ let warnings = 0;
266
+ let errors = 0;
267
+
268
+ for (const check of checks) {
269
+ let symbol;
270
+ let colorFn;
271
+ switch (check.severity) {
272
+ case "ok":
273
+ symbol = "✅";
274
+ colorFn = success;
275
+ passed++;
276
+ break;
277
+ case "warning":
278
+ symbol = "⚠️";
279
+ colorFn = warning;
280
+ warnings++;
281
+ break;
282
+ case "error":
283
+ symbol = "❌";
284
+ colorFn = error;
285
+ errors++;
286
+ break;
287
+ }
288
+
289
+ console.log(` ${symbol} ${check.name}: ${colorFn(check.message)}`);
290
+ if (check.fix) {
291
+ console.log(` ${muted("建议:")} ${check.fix}`);
292
+ }
293
+ }
294
+
295
+ // 总结
296
+ console.log(`\n${muted("─".repeat(40))}\n`);
297
+
298
+ const total = checks.length;
299
+ if (errors === 0 && warnings === 0) {
300
+ console.log(success(`🎉 所有检查通过!(${passed}/${total})`));
301
+ console.log(`${infoColor("AgentHub 已就绪,可以正常使用。")}\n`);
302
+ } else if (errors === 0) {
303
+ console.log(warning(`⚠️ 检查完成,有 ${warnings} 个警告 (${passed}/${total} 通过)`));
304
+ console.log(`${infoColor("AgentHub 可以使用,但建议解决上述警告。")}\n`);
305
+ } else {
306
+ console.log(error(`❌ 检查失败,有 ${errors} 个错误 (${passed}/${total} 通过)`));
307
+ console.log(`${infoColor("请先解决上述错误再使用 AgentHub。")}\n`);
308
+ }
309
+
310
+ return {
311
+ passed,
312
+ warnings,
313
+ errors,
314
+ total,
315
+ healthy: errors === 0,
316
+ checks,
317
+ };
318
+ }
319
+
320
+ /**
321
+ * 格式化输出
322
+ */
323
+ export function formatDoctorOutput(result) {
324
+ const lines = [];
325
+ lines.push(`\n${highlight("🔍 AgentHub 诊断检查")}\n`);
326
+ lines.push(`${muted("检查结果:")} ${result.passed}/${result.total} 通过\n`);
327
+
328
+ if (result.healthy) {
329
+ lines.push(success("✅ AgentHub 健康状态良好"));
330
+ } else {
331
+ lines.push(error("❌ AgentHub 需要修复"));
332
+ }
333
+
334
+ return lines.join("\n");
335
+ }
package/src/index.js CHANGED
@@ -13,4 +13,5 @@ export { statsCommand, formatStatsOutput } from "./commands/stats.js";
13
13
  export { listCommand, formatListOutput } from "./commands/list.js";
14
14
  export { apiCommand } from "./commands/api.js";
15
15
  export { webCommand } from "./commands/web.js";
16
+ export { doctorCommand } from "./commands/doctor.js";
16
17
  export { parseSpec, compareVersionsDesc, getLatestVersion, sortByDownloadsAndTime } from "./lib/registry.js";