@zshuangmu/agenthub 0.1.2 → 0.1.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/README.md CHANGED
@@ -86,6 +86,11 @@ Visit http://localhost:3000 to see your agents!
86
86
  | `search` | Search agents in registry |
87
87
  | `info` | View agent details |
88
88
  | `install` | Install agent to workspace |
89
+ | `list` | List installed agents in workspace |
90
+ | `versions` | List available versions for one agent |
91
+ | `update` | Update installed agent to latest version |
92
+ | `rollback` | Roll back an installed agent |
93
+ | `stats` | View detailed statistics for an agent |
89
94
  | `serve` | Start web + API service |
90
95
  | `api` | Start API server only |
91
96
  | `web` | Start web frontend only |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zshuangmu/agenthub",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "AI Agent 打包与分发平台 - 一句话打包上传,一键下载获得能力",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/cli.js CHANGED
@@ -16,9 +16,17 @@ import {
16
16
  searchCommand,
17
17
  apiCommand,
18
18
  webCommand,
19
+ updateCommand,
20
+ rollbackCommand,
21
+ listCommand,
22
+ formatListOutput,
23
+ statsCommand,
24
+ formatStatsOutput,
25
+ versionsCommand,
26
+ formatVersionsOutput,
19
27
  } from "./index.js";
20
28
 
21
- const VERSION = "0.1.0";
29
+ const VERSION = "0.1.3";
22
30
 
23
31
  function printHelp() {
24
32
  console.log(`
@@ -31,9 +39,14 @@ AgentHub v${VERSION} - AI Agent 打包与分发平台
31
39
  pack 打包 OpenClaw 工作区为 Agent Bundle
32
40
  publish 发布 Bundle 到本地 Registry
33
41
  publish-remote 发布 Bundle 到远程服务器
34
- install 安装 Agent 到目标工作区
42
+ install 安装 Agent 到目标工作区(默认当前目录)
35
43
  search 搜索 Registry 中的 Agent
36
44
  info 查看 Agent 详情
45
+ list 列出当前目录/指定目录的已安装 Agent
46
+ versions 查看 Agent 版本历史
47
+ update 更新已安装 Agent 到最新版
48
+ rollback 回滚已安装 Agent 到指定版本
49
+ stats 查看 Agent 统计信息
37
50
  serve 启动 Web + API 服务
38
51
 
39
52
  选项:
@@ -121,6 +134,70 @@ agenthub search - 搜索 Agent
121
134
  示例:
122
135
  agenthub search "code review" --registry ./.registry
123
136
  agenthub search "" --registry ./.registry # 列出所有
137
+ `,
138
+ list: `
139
+ agenthub list - 列出已安装 Agent
140
+
141
+ 用法:
142
+ agenthub list [--target-workspace <dir>]
143
+
144
+ 选项:
145
+ --target-workspace <dir> 指定工作区目录(可选)
146
+
147
+ 示例:
148
+ agenthub list
149
+ agenthub list --target-workspace ./my-workspace
150
+ `,
151
+ versions: `
152
+ agenthub versions - 查看版本历史
153
+
154
+ 用法:
155
+ agenthub versions <agent-slug> --registry <dir>
156
+
157
+ 选项:
158
+ --registry <dir> Registry 目录 (必需)
159
+
160
+ 示例:
161
+ agenthub versions workspace --registry ./.registry
162
+ `,
163
+ update: `
164
+ agenthub update - 更新 Agent 到最新版
165
+
166
+ 用法:
167
+ agenthub update <agent-slug> --registry <dir> --target-workspace <dir>
168
+
169
+ 选项:
170
+ --registry <dir> Registry 目录 (必需)
171
+ --target-workspace <dir> 目标工作区目录 (必需)
172
+
173
+ 示例:
174
+ agenthub update workspace --registry ./.registry --target-workspace ./my-workspace
175
+ `,
176
+ rollback: `
177
+ agenthub rollback - 回滚 Agent 到指定版本
178
+
179
+ 用法:
180
+ agenthub rollback <agent-slug> --to <version> --registry <dir> --target-workspace <dir>
181
+
182
+ 选项:
183
+ --to <version> 目标版本 (必需)
184
+ --registry <dir> Registry 目录 (必需)
185
+ --target-workspace <dir> 目标工作区目录 (必需)
186
+
187
+ 示例:
188
+ agenthub rollback workspace --to 1.0.0 --registry ./.registry --target-workspace ./my-workspace
189
+ `,
190
+ stats: `
191
+ agenthub stats - 查看 Agent 统计信息
192
+
193
+ 用法:
194
+ agenthub stats <agent-slug> --registry <dir>
195
+
196
+ 选项:
197
+ --registry <dir> Registry 目录 (必需)
198
+
199
+ 示例:
200
+ agenthub stats workspace --registry ./.registry
124
201
  `,
125
202
  serve: `
126
203
  agenthub serve - 启动完整服务(前端+后端)
@@ -265,12 +342,62 @@ async function main() {
265
342
  return;
266
343
  }
267
344
  console.log(`\n📥 正在安装 ${rest[0]}...\n`);
268
- const result = await installCommand(rest[0], options);
269
- console.log(`✓ 已安装 ${result.manifest.slug}@${result.manifest.version}`);
345
+ const installResult = await installCommand(rest[0], options);
346
+ console.log(`✓ 已安装 ${installResult.manifest.slug}@${installResult.manifest.version}`);
270
347
  console.log(` 位置: ${options.targetWorkspace || "当前目录"}`);
271
348
  return;
272
349
  }
273
350
 
351
+ case "list": {
352
+ const list = await listCommand(options);
353
+ console.log(formatListOutput(list));
354
+ return;
355
+ }
356
+
357
+ case "versions": {
358
+ if (!rest[0]) {
359
+ console.error("错误: 需要指定 agent slug");
360
+ process.exitCode = 1;
361
+ return;
362
+ }
363
+ const versions = await versionsCommand(rest[0], options);
364
+ console.log(formatVersionsOutput(rest[0], versions));
365
+ return;
366
+ }
367
+
368
+ case "update": {
369
+ if (!rest[0]) {
370
+ console.error("错误: 需要指定 agent slug");
371
+ process.exitCode = 1;
372
+ return;
373
+ }
374
+ const updateResult = await updateCommand(rest[0], options);
375
+ console.log(updateResult.message);
376
+ return;
377
+ }
378
+
379
+ case "rollback": {
380
+ if (!rest[0]) {
381
+ console.error("错误: 需要指定 agent slug");
382
+ process.exitCode = 1;
383
+ return;
384
+ }
385
+ const rollbackResult = await rollbackCommand(rest[0], options);
386
+ console.log(rollbackResult.message);
387
+ return;
388
+ }
389
+
390
+ case "stats": {
391
+ if (!rest[0]) {
392
+ console.error("错误: 需要指定 agent slug");
393
+ process.exitCode = 1;
394
+ return;
395
+ }
396
+ const stats = await statsCommand(rest[0], options);
397
+ console.log(formatStatsOutput(stats));
398
+ return;
399
+ }
400
+
274
401
  case "serve": {
275
402
  const result = await serveCommand(options);
276
403
  console.log(`Server listening at ${result.baseUrl}`);
@@ -338,7 +465,10 @@ async function main() {
338
465
  process.exitCode = 1;
339
466
  }
340
467
  } catch (error) {
341
- console.error(`\n❌ 错误: ${error.message}`);
468
+ // 提取更详细的错误信息
469
+ const causeMsg = error.cause?.errors?.[0]?.message || error.cause?.message || "";
470
+ const detailMsg = causeMsg ? `${error.message}\n 原因: ${causeMsg}` : error.message;
471
+ console.error(`\n❌ 错误: ${detailMsg}`);
342
472
  process.exitCode = 1;
343
473
  }
344
474
  }
@@ -1,28 +1,56 @@
1
1
  import path from "node:path";
2
2
  import { installBundle } from "../lib/install.js";
3
+ import { writeJson } from "../lib/fs-utils.js";
3
4
 
4
5
  export async function installCommand(agentSpec, options) {
5
- const targetWorkspace = path.resolve(options.targetWorkspace);
6
- if (!targetWorkspace) {
7
- throw new Error("--target-workspace is required");
8
- }
6
+ // 未显式提供时,默认在当前目录执行安装
7
+ const targetWorkspace = path.resolve(options.targetWorkspace || process.cwd());
9
8
 
10
9
  // 默认走远程服务器,提升网站复制命令可用性
11
10
  if (!options.registry && !options.server) {
12
11
  options.server = "https://agenthub.cyou";
13
12
  }
14
13
 
14
+ const debugEnabled = Boolean(process.env.AGENTHUB_DEBUG_INSTALL);
15
+ const debug = (message, details) => {
16
+ if (!debugEnabled) return;
17
+ console.error(`[agenthub:install] ${message} | ${JSON.stringify(details)}`);
18
+ };
19
+
20
+ debug("start install", {
21
+ agentSpec,
22
+ hasRegistry: Boolean(options.registry),
23
+ hasServer: Boolean(options.server),
24
+ targetWorkspace,
25
+ });
26
+
27
+ let result;
15
28
  if (options.registry) {
16
- return installBundle({
29
+ result = await installBundle({
17
30
  registryDir: path.resolve(options.registry),
18
31
  agentSpec,
19
32
  targetWorkspace,
20
33
  });
34
+ } else {
35
+ result = await installBundle({
36
+ serverUrl: options.server,
37
+ agentSpec,
38
+ targetWorkspace,
39
+ });
21
40
  }
22
41
 
23
- return installBundle({
24
- serverUrl: options.server,
25
- agentSpec,
26
- targetWorkspace,
42
+ debug("install finished", {
43
+ slug: result.manifest.slug,
44
+ version: result.manifest.version,
45
+ installedAt: new Date().toISOString(),
27
46
  });
47
+
48
+ const installRecordPath = path.join(targetWorkspace, ".agenthub", "install.json");
49
+ await writeJson(installRecordPath, {
50
+ slug: result.manifest.slug,
51
+ version: result.manifest.version,
52
+ installedAt: new Date().toISOString(),
53
+ });
54
+
55
+ return result;
28
56
  }
package/src/lib/html.js CHANGED
@@ -1643,7 +1643,7 @@ export function renderAgentDetailPage(manifest) {
1643
1643
  </div>
1644
1644
  </div>
1645
1645
  <div class="detail-install" title="Click to copy">
1646
- <span class="code-text">npx @zshuangmu/agenthub install ${manifest.slug}</span>
1646
+ <span class="code-text">npx @zshuangmu/agenthub install ${manifest.slug} --target-workspace ./my-workspace</span>
1647
1647
  <button class="copy-btn" title="Copy">📋</button>
1648
1648
  </div>
1649
1649
  </div>
@@ -2,6 +2,79 @@ import path from "node:path";
2
2
  import { copyDir, ensureDir, readJson, writeJson } from "./fs-utils.js";
3
3
  import { readAgentInfo } from "./registry.js";
4
4
  import { materializeBundlePayload } from "./bundle-transfer.js";
5
+ import { request as httpsRequest } from "node:https";
6
+ import { request as httpRequest } from "node:http";
7
+
8
+ function debugLog(message, details) {
9
+ if (!process.env.AGENTHUB_DEBUG_INSTALL) return;
10
+ const suffix = details ? ` | ${JSON.stringify(details)}` : "";
11
+ console.error(`[agenthub:install] ${message}${suffix}`);
12
+ }
13
+
14
+ async function requestPayloadText(rawUrl) {
15
+ const parsed = new URL(rawUrl);
16
+ const transport = parsed.protocol === "http:" ? httpRequest : httpsRequest;
17
+ const requestOptions = {
18
+ method: "GET",
19
+ hostname: parsed.hostname,
20
+ port: parsed.port,
21
+ path: `${parsed.pathname}${parsed.search || ""}`,
22
+ protocol: parsed.protocol,
23
+ headers: {
24
+ "User-Agent": "agenthub-cli/0.1.3",
25
+ Accept: "application/json",
26
+ },
27
+ };
28
+
29
+ debugLog("fallback request begin", { url: rawUrl, protocol: parsed.protocol, hostname: parsed.hostname, path: requestOptions.path });
30
+
31
+ return await new Promise((resolve, reject) => {
32
+ const req = transport(requestOptions, (res) => {
33
+ let data = "";
34
+ res.on("data", (chunk) => {
35
+ data += chunk;
36
+ });
37
+ res.on("end", () => {
38
+ if (res.statusCode < 200 || res.statusCode >= 300) {
39
+ const reason = `${res.statusCode} ${res.statusMessage || ""}`;
40
+ debugLog("fallback request failed", { reason, rawUrl });
41
+ reject(new Error(`Remote install failed: ${reason}`));
42
+ return;
43
+ }
44
+ debugLog("fallback request success", { rawUrl, bytes: data.length });
45
+ resolve(data);
46
+ });
47
+ });
48
+
49
+ req.on("error", (error) => {
50
+ debugLog("fallback request error", { rawUrl, message: error.message });
51
+ reject(error);
52
+ });
53
+ req.end();
54
+ });
55
+ }
56
+
57
+ async function fetchPayload(url) {
58
+ const baseUrl = url.toString();
59
+ debugLog("requesting payload", { url: baseUrl });
60
+
61
+ try {
62
+ const response = await fetch(baseUrl);
63
+ debugLog("primary fetch done", { url: baseUrl, ok: response.ok, status: response.status, statusText: response.statusText });
64
+ if (!response.ok) {
65
+ throw new Error(`${response.status} ${response.statusText}`);
66
+ }
67
+ return response;
68
+ } catch (err) {
69
+ debugLog("primary fetch failed, fallback to http/https", { url: baseUrl, error: err.message });
70
+ const payloadText = await requestPayloadText(baseUrl);
71
+ return {
72
+ ok: true,
73
+ json: async () => JSON.parse(payloadText),
74
+ statusText: "OK",
75
+ };
76
+ }
77
+ }
5
78
 
6
79
  async function applyBundleDir({ bundleDir, targetWorkspace }) {
7
80
  const manifest = await readJson(path.join(bundleDir, "MANIFEST.json"));
@@ -39,11 +112,26 @@ async function installFromRemote({ serverUrl, agentSpec, targetWorkspace }) {
39
112
  if (version) {
40
113
  url.searchParams.set("version", version);
41
114
  }
42
- const response = await fetch(url);
115
+
116
+ debugLog("installing from remote", { serverUrl, slug, version: version || "latest", targetWorkspace, url: url.toString() });
117
+
118
+ let response;
119
+ try {
120
+ response = await fetchPayload(url);
121
+ } catch (error) {
122
+ debugLog("remote install failed", { slug, error: error.message, cause: error.cause });
123
+ // 提取更详细的错误信息
124
+ const causeMsg = error.cause?.errors?.[0]?.message || error.cause?.message || "";
125
+ const detailMsg = causeMsg ? `${error.message}: ${causeMsg}` : error.message;
126
+ throw new Error(`Remote install failed: ${detailMsg}`);
127
+ }
128
+
43
129
  if (!response.ok) {
44
- throw new Error(`Remote install failed: ${response.status} ${await response.text()}`);
130
+ throw new Error(`Remote install failed: ${response.status} ${response.statusText}`);
45
131
  }
132
+
46
133
  const payload = await response.json();
134
+ debugLog("payload received", { slug, size: JSON.stringify(payload).length });
47
135
  const bundleDir = await materializeBundlePayload(payload);
48
136
  return applyBundleDir({ bundleDir, targetWorkspace });
49
137
  }