agent-resource-management 1.3.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.
Files changed (30) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/README.md +196 -0
  3. package/bun.lock +32 -0
  4. package/data/skills/bad-name-skill/SKILL.md +8 -0
  5. package/data/skills/github-tool/SKILL.md +11 -0
  6. package/data/skills/invalid-skill/SKILL.md +3 -0
  7. package/data/skills/pdf-tool/SKILL.md +11 -0
  8. package/data/skills/pdf-tool.zip +0 -0
  9. package/dist/main.js +1330 -0
  10. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/AGENT.md +18 -0
  11. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260Loki/346/227/245/345/277/227/346/216/222/346/237/245/346/226/271/346/263/225.md +27 -0
  12. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/344/273/273/345/212/241/350/247/246/345/217/221/345/220/270/346/224/266/345/231/250/344/270/216/345/215/240/344/275/215/350/247/246/345/217/221/345/220/270/346/224/266/345/231/250/344/275/277/347/224/250/345/234/272/346/231/257.md +18 -0
  13. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/345/220/216/347/253/257Swagger/346/216/245/345/217/243/346/226/207/346/241/243.md +22 -0
  14. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/345/275/223/345/211/215/350/277/220/350/241/214/345/256/236/344/276/213/345/217/212/345/256/236/344/276/213/350/277/220/350/241/214/346/227/245/345/277/227/346/216/222/346/237/245.md +39 -0
  15. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/346/216/245/345/217/243/346/216/222/346/237/245/350/203/214/346/231/257/347/237/245/350/257/206.md +49 -0
  16. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/350/264/247/350/275/275/347/224/237/345/221/275/345/221/250/346/234/237/346/237/245/350/257/242/346/265/201/347/250/213.md +165 -0
  17. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//345/244/251/350/275/246/350/260/203/345/272/246/347/263/273/347/273/237/351/207/215/347/273/204/346/216/245/345/217/243/350/260/203/347/224/250/344/270/216/346/227/245/345/277/227/345/210/206/346/236/220/346/226/271/346/263/225.md +137 -0
  18. package/package.json +20 -0
  19. package/src/cmd/agent.ts +137 -0
  20. package/src/cmd/auth.ts +50 -0
  21. package/src/cmd/knowledge.ts +171 -0
  22. package/src/cmd/server.ts +11 -0
  23. package/src/cmd/skill.ts +220 -0
  24. package/src/lib/client.ts +274 -0
  25. package/src/lib/formatter.ts +196 -0
  26. package/src/lib/storage.ts +63 -0
  27. package/src/lib/validate.ts +210 -0
  28. package/src/main.ts +235 -0
  29. package/test-regression.sh +273 -0
  30. package/tsconfig.json +17 -0
package/dist/main.js ADDED
@@ -0,0 +1,1330 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
+
5
+ // src/lib/client.ts
6
+ class ApiClient {
7
+ token = null;
8
+ serverUrl;
9
+ constructor(serverUrl, token) {
10
+ this.serverUrl = serverUrl.replace(/\/$/, "");
11
+ this.token = token || null;
12
+ }
13
+ setToken(token) {
14
+ this.token = token;
15
+ }
16
+ clearToken() {
17
+ this.token = null;
18
+ }
19
+ async request(path, options = {}) {
20
+ const headers = {
21
+ "Content-Type": "application/json",
22
+ ...options.headers
23
+ };
24
+ if (this.token) {
25
+ headers["Authorization"] = `Bearer ${this.token}`;
26
+ }
27
+ const res = await fetch(`${this.serverUrl}/api/v1${path}`, {
28
+ ...options,
29
+ headers
30
+ });
31
+ return res.json();
32
+ }
33
+ async login(apiKey) {
34
+ const res = await this.request("/auth/login", {
35
+ method: "POST",
36
+ body: JSON.stringify({ apiKey })
37
+ });
38
+ if (!res.ok) {
39
+ throw new Error(res.msg);
40
+ }
41
+ return res.data;
42
+ }
43
+ async me() {
44
+ const res = await this.request("/auth/me");
45
+ if (!res.ok) {
46
+ throw new Error(res.msg);
47
+ }
48
+ return res.data;
49
+ }
50
+ async listSkills(keyword, page = 1, pageSize = 20) {
51
+ let path = `/skills?page=${page}&pageSize=${pageSize}`;
52
+ if (keyword) {
53
+ path += `&keyword=${encodeURIComponent(keyword)}`;
54
+ }
55
+ const res = await this.request(path);
56
+ if (!res.ok) {
57
+ throw new Error(res.msg);
58
+ }
59
+ return res.data;
60
+ }
61
+ async getSkill(name) {
62
+ const res = await this.request(`/skills/${name}`);
63
+ if (!res.ok) {
64
+ throw new Error(res.msg);
65
+ }
66
+ return res.data;
67
+ }
68
+ async getMySkills() {
69
+ const res = await this.request("/users/me/skills");
70
+ if (!res.ok) {
71
+ throw new Error(res.msg);
72
+ }
73
+ return res.data;
74
+ }
75
+ async uploadSkill(filePath) {
76
+ const { readFileSync } = await import("fs");
77
+ const { basename } = await import("path");
78
+ const fileName = basename(filePath);
79
+ const fileBuffer = readFileSync(filePath);
80
+ const formData = new FormData;
81
+ const blob = new Blob([fileBuffer]);
82
+ formData.append("file", blob, fileName);
83
+ const headers = {};
84
+ if (this.token) {
85
+ headers["Authorization"] = `Bearer ${this.token}`;
86
+ }
87
+ const res = await fetch(`${this.serverUrl}/api/v1/skills`, {
88
+ method: "POST",
89
+ headers,
90
+ body: formData
91
+ });
92
+ const data = await res.json();
93
+ console.error("DEBUG server response:", JSON.stringify(data));
94
+ if (!data.ok) {
95
+ throw new Error(data.msg);
96
+ }
97
+ return data.data;
98
+ }
99
+ async deleteSkill(name) {
100
+ const res = await this.request(`/skills/${name}`, {
101
+ method: "DELETE"
102
+ });
103
+ if (!res.ok) {
104
+ throw new Error(res.msg);
105
+ }
106
+ }
107
+ async downloadSkill(name) {
108
+ const headers = {};
109
+ if (this.token) {
110
+ headers["Authorization"] = `Bearer ${this.token}`;
111
+ }
112
+ const res = await fetch(`${this.serverUrl}/api/v1/skills/${name}/download`, {
113
+ headers
114
+ });
115
+ if (!res.ok) {
116
+ if (res.status === 404) {
117
+ throw new Error("Skill 不存在");
118
+ }
119
+ throw new Error("下载失败");
120
+ }
121
+ return res.arrayBuffer();
122
+ }
123
+ async listAgents(keyword, page = 1, pageSize = 20) {
124
+ let path = `/agents?page=${page}&pageSize=${pageSize}`;
125
+ if (keyword) {
126
+ path += `&keyword=${encodeURIComponent(keyword)}`;
127
+ }
128
+ const res = await this.request(path);
129
+ if (!res.ok) {
130
+ throw new Error(res.msg);
131
+ }
132
+ return res.data;
133
+ }
134
+ async getAgent(id) {
135
+ const res = await this.request(`/agents/${id}`);
136
+ if (!res.ok) {
137
+ throw new Error(res.msg);
138
+ }
139
+ return res.data;
140
+ }
141
+ async downloadAgent(id) {
142
+ const headers = {};
143
+ if (this.token) {
144
+ headers["Authorization"] = `Bearer ${this.token}`;
145
+ }
146
+ const res = await fetch(`${this.serverUrl}/api/v1/agents/${id}/download`, {
147
+ headers
148
+ });
149
+ if (!res.ok) {
150
+ if (res.status === 404) {
151
+ throw new Error("Agent 不存在");
152
+ }
153
+ throw new Error("下载失败");
154
+ }
155
+ const version = res.headers.get("X-Version") || "unknown";
156
+ return {
157
+ buffer: await res.arrayBuffer(),
158
+ version
159
+ };
160
+ }
161
+ async listKnowledge(keyword, page = 1, pageSize = 20) {
162
+ let path = `/knowledges?page=${page}&pageSize=${pageSize}`;
163
+ if (keyword) {
164
+ path += `&keyword=${encodeURIComponent(keyword)}`;
165
+ }
166
+ const res = await this.request(path);
167
+ if (!res.ok) {
168
+ throw new Error(res.msg);
169
+ }
170
+ return res.data;
171
+ }
172
+ async getKnowledge(name) {
173
+ const res = await this.request(`/knowledges/${name}`);
174
+ if (!res.ok) {
175
+ throw new Error(res.msg);
176
+ }
177
+ return res.data;
178
+ }
179
+ async getMyKnowledge() {
180
+ const res = await this.request("/users/me/knowledges");
181
+ if (!res.ok) {
182
+ throw new Error(res.msg);
183
+ }
184
+ return res.data;
185
+ }
186
+ async uploadKnowledge(filePath) {
187
+ const { readFileSync } = await import("fs");
188
+ const { basename } = await import("path");
189
+ const fileName = basename(filePath);
190
+ const fileBuffer = readFileSync(filePath);
191
+ const formData = new FormData;
192
+ const blob = new Blob([fileBuffer]);
193
+ formData.append("file", blob, fileName);
194
+ const headers = {};
195
+ if (this.token) {
196
+ headers["Authorization"] = `Bearer ${this.token}`;
197
+ }
198
+ const res = await fetch(`${this.serverUrl}/api/v1/knowledges`, {
199
+ method: "POST",
200
+ headers,
201
+ body: formData
202
+ });
203
+ const data = await res.json();
204
+ if (!data.ok) {
205
+ throw new Error(data.msg);
206
+ }
207
+ return data.data;
208
+ }
209
+ async deleteKnowledge(name) {
210
+ const res = await this.request(`/knowledges/${name}`, {
211
+ method: "DELETE"
212
+ });
213
+ if (!res.ok) {
214
+ throw new Error(res.msg);
215
+ }
216
+ }
217
+ async downloadKnowledge(name) {
218
+ const headers = {};
219
+ if (this.token) {
220
+ headers["Authorization"] = `Bearer ${this.token}`;
221
+ }
222
+ const res = await fetch(`${this.serverUrl}/api/v1/knowledges/${name}/download`, {
223
+ headers
224
+ });
225
+ if (!res.ok) {
226
+ if (res.status === 404) {
227
+ throw new Error("Knowledge 不存在");
228
+ }
229
+ throw new Error("下载失败");
230
+ }
231
+ return res.arrayBuffer();
232
+ }
233
+ }
234
+
235
+ // src/lib/storage.ts
236
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
237
+ import { join } from "path";
238
+ var CONFIG_DIR = join(process.env.HOME || "/root", ".adk");
239
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
240
+ function ensureConfigDir() {
241
+ if (!existsSync(CONFIG_DIR)) {
242
+ mkdirSync(CONFIG_DIR, { recursive: true });
243
+ }
244
+ }
245
+ function saveConfig(config) {
246
+ ensureConfigDir();
247
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
248
+ }
249
+ function loadConfig() {
250
+ try {
251
+ ensureConfigDir();
252
+ if (!existsSync(CONFIG_FILE)) {
253
+ return null;
254
+ }
255
+ const content = readFileSync(CONFIG_FILE, "utf-8");
256
+ return JSON.parse(content);
257
+ } catch {
258
+ return null;
259
+ }
260
+ }
261
+ function getServerUrl() {
262
+ const config = loadConfig();
263
+ return config?.serverUrl || "http://localhost:3000";
264
+ }
265
+ function setServerUrl(url) {
266
+ const config = loadConfig() || {};
267
+ config.serverUrl = url;
268
+ saveConfig(config);
269
+ }
270
+
271
+ // src/lib/formatter.ts
272
+ function formatSkill(skill) {
273
+ const lines = [
274
+ `\x1B[1m${skill.name}\x1B[0m`,
275
+ ` ${skill.description}`,
276
+ ` 下载: ${skill.downloadCount}${skill.license ? ` | 许可: ${skill.license}` : ""}`
277
+ ];
278
+ return lines.join(`
279
+ `);
280
+ }
281
+ function formatSkillDetail(skill) {
282
+ const lines = [
283
+ `\x1B[1m${skill.name}\x1B[0m`,
284
+ "",
285
+ `描述: ${skill.description}`,
286
+ `文件大小: ${skill.fileSize} bytes`,
287
+ `下载次数: ${skill.downloadCount}`,
288
+ `发布时间: ${skill.publishedAt}`
289
+ ];
290
+ if (skill.license) {
291
+ lines.push(`许可: ${skill.license}`);
292
+ }
293
+ if (skill.compatibility) {
294
+ lines.push(`兼容性: ${skill.compatibility}`);
295
+ }
296
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
297
+ lines.push(`允许工具: ${skill.allowedTools.join(", ")}`);
298
+ }
299
+ if (skill.metadata) {
300
+ lines.push(`元数据: ${JSON.stringify(skill.metadata)}`);
301
+ }
302
+ return lines.join(`
303
+ `);
304
+ }
305
+ function success(msg) {
306
+ console.log(`\x1B[32m✓ ${msg}\x1B[0m`);
307
+ }
308
+ function error(msg) {
309
+ console.error(`\x1B[31m✗ ${msg}\x1B[0m`);
310
+ }
311
+ function info(msg) {
312
+ console.log(`\x1B[34mℹ ${msg}\x1B[0m`);
313
+ }
314
+ function formatAgent(agent) {
315
+ const lines = [
316
+ `\x1B[1m${agent.name}\x1B[0m`,
317
+ ` ${agent.description}`,
318
+ ` 版本: ${agent.version} | 状态: ${agent.status}${agent.skillsCount !== undefined ? ` | Skills: ${agent.skillsCount}` : ""}${agent.knowledgesCount !== undefined ? ` | Knowledges: ${agent.knowledgesCount}` : ""}`
319
+ ];
320
+ return lines.join(`
321
+ `);
322
+ }
323
+ function formatAgentDetail(agent) {
324
+ const lines = [
325
+ `\x1B[1m${agent.name}\x1B[0m`,
326
+ "",
327
+ `版本: ${agent.version}`,
328
+ `描述: ${agent.description || "暂无描述"}`,
329
+ `状态: ${agent.status}`,
330
+ `创建时间: ${formatTime(agent.createdAt)}`,
331
+ `更新时间: ${formatTime(agent.updatedAt)}`
332
+ ];
333
+ if (agent.skills && agent.skills.length > 0) {
334
+ lines.push("");
335
+ lines.push("Skills:");
336
+ for (const s of agent.skills) {
337
+ const hasConfig = s.config && Object.keys(s.config).length > 0;
338
+ lines.push(` - ${s.skill.name}${hasConfig ? ` (config: ${JSON.stringify(s.config)})` : ""}`);
339
+ }
340
+ }
341
+ if (agent.knowledges && agent.knowledges.length > 0) {
342
+ lines.push("");
343
+ lines.push("Knowledges:");
344
+ for (const k of agent.knowledges) {
345
+ const name = k.knowledgeName || k.knowledgeId;
346
+ const topK = k.retrievalConfig?.topK;
347
+ lines.push(` - ${name}${topK ? ` (topK: ${topK})` : ""}`);
348
+ }
349
+ }
350
+ if (agent.prompt) {
351
+ lines.push("");
352
+ lines.push("System Prompt:");
353
+ lines.push("---");
354
+ lines.push(agent.prompt);
355
+ lines.push("---");
356
+ }
357
+ return lines.join(`
358
+ `);
359
+ }
360
+ function formatTime(isoString) {
361
+ const date = new Date(isoString);
362
+ const year = date.getFullYear();
363
+ const month = String(date.getMonth() + 1).padStart(2, "0");
364
+ const day = String(date.getDate()).padStart(2, "0");
365
+ const hours = String(date.getHours()).padStart(2, "0");
366
+ const minutes = String(date.getMinutes()).padStart(2, "0");
367
+ const seconds = String(date.getSeconds()).padStart(2, "0");
368
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
369
+ }
370
+ function formatKnowledge(knowledge) {
371
+ const sizeStr = knowledge.size > 1024 * 1024 ? `${(knowledge.size / (1024 * 1024)).toFixed(2)} MB` : knowledge.size > 1024 ? `${(knowledge.size / 1024).toFixed(2)} KB` : `${knowledge.size} B`;
372
+ const lines = [
373
+ `\x1B[1m${knowledge.name}\x1B[0m`,
374
+ ` ${knowledge.description}`,
375
+ ` 类型: ${knowledge.type} | 大小: ${sizeStr} | 下载: ${knowledge.downloadCount}`
376
+ ];
377
+ return lines.join(`
378
+ `);
379
+ }
380
+ function formatKnowledgeDetail(knowledge) {
381
+ const sizeStr = knowledge.fileSize > 1024 * 1024 ? `${(knowledge.fileSize / (1024 * 1024)).toFixed(2)} MB` : knowledge.fileSize > 1024 ? `${(knowledge.fileSize / 1024).toFixed(2)} KB` : `${knowledge.fileSize} B`;
382
+ const lines = [
383
+ `\x1B[1m${knowledge.name}\x1B[0m`,
384
+ "",
385
+ `描述: ${knowledge.description}`,
386
+ `类型: ${knowledge.type}`,
387
+ `文件大小: ${sizeStr}`,
388
+ `下载次数: ${knowledge.downloadCount}`,
389
+ `发布时间: ${knowledge.publishedAt}`
390
+ ];
391
+ if (knowledge.tags && knowledge.tags.length > 0) {
392
+ lines.push(`标签: ${knowledge.tags.join(", ")}`);
393
+ }
394
+ return lines.join(`
395
+ `);
396
+ }
397
+
398
+ // src/cmd/auth.ts
399
+ async function login(serverUrl, apiKey) {
400
+ try {
401
+ const client = new ApiClient(serverUrl);
402
+ const loginRes = await client.login(apiKey);
403
+ saveConfig({
404
+ serverUrl,
405
+ token: loginRes.token,
406
+ user: loginRes.user
407
+ });
408
+ success(`登录成功! 欢迎, ${loginRes.user.name}`);
409
+ } catch (err) {
410
+ error(`登录失败: ${err instanceof Error ? err.message : "未知错误"}`);
411
+ process.exit(1);
412
+ }
413
+ }
414
+ async function logout() {
415
+ const config = loadConfig();
416
+ if (config?.token) {
417
+ config.token = undefined;
418
+ config.user = undefined;
419
+ saveConfig(config);
420
+ success("已退出登录");
421
+ } else {
422
+ info("未登录");
423
+ }
424
+ }
425
+ async function getCurrentUser() {
426
+ const config = loadConfig();
427
+ if (!config?.token) {
428
+ error("未登录,请先运行 arm login");
429
+ process.exit(1);
430
+ }
431
+ const client = new ApiClient(config.serverUrl, config.token);
432
+ try {
433
+ const user = await client.me();
434
+ console.log(`当前用户: ${user.name} (${user.email})`);
435
+ } catch (err) {
436
+ error(`获取用户信息失败: ${err instanceof Error ? err.message : "未知错误"}`);
437
+ process.exit(1);
438
+ }
439
+ }
440
+
441
+ // src/lib/validate.ts
442
+ import { readFileSync as readFileSync2, existsSync as existsSync2, statSync } from "fs";
443
+ import { execSync } from "child_process";
444
+ import { join as join2, extname } from "path";
445
+ import { mkdtempSync, rmSync } from "fs";
446
+ function validateZip(filePath) {
447
+ const result = {
448
+ valid: true,
449
+ errors: [],
450
+ warnings: []
451
+ };
452
+ if (!existsSync2(filePath)) {
453
+ result.valid = false;
454
+ result.errors.push(`文件不存在: ${filePath}`);
455
+ return result;
456
+ }
457
+ if (extname(filePath).toLowerCase() !== ".zip") {
458
+ result.valid = false;
459
+ result.errors.push("文件必须是 ZIP 格式");
460
+ return result;
461
+ }
462
+ const stats = statSync(filePath);
463
+ if (stats.size === 0) {
464
+ result.valid = false;
465
+ result.errors.push("ZIP 文件为空");
466
+ return result;
467
+ }
468
+ try {
469
+ const tempDir = mkdtempSync("/tmp/skill-validate-");
470
+ execSync(`unzip -o "${filePath}" -d "${tempDir}"`, { stdio: "pipe" });
471
+ const entries = execSync(`unzip -l "${filePath}"`, { encoding: "utf-8" });
472
+ const hasSkillMd = entries.includes("SKILL.md");
473
+ if (!hasSkillMd) {
474
+ result.valid = false;
475
+ result.errors.push("ZIP 包内缺少 SKILL.md 文件");
476
+ }
477
+ const skillMdPath = join2(tempDir, "SKILL.md");
478
+ if (existsSync2(skillMdPath)) {
479
+ const skillMdContent = readFileSync2(skillMdPath, "utf-8");
480
+ const frontmatterMatch = skillMdContent.match(/^---\n([\s\S]*?)\n---/);
481
+ if (frontmatterMatch) {
482
+ const frontmatter = frontmatterMatch[1];
483
+ result.metadata = {};
484
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
485
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
486
+ const licenseMatch = frontmatter.match(/license:\s*(.+)/);
487
+ const compatMatch = frontmatter.match(/compatibility:\s*(.+)/);
488
+ const toolsMatch = frontmatter.match(/allowed-tools:\s*(.+)/);
489
+ const metaMatch = frontmatter.match(/metadata:\s*([\s\S]*?)(?=\n\w|$)/);
490
+ if (nameMatch) {
491
+ const name = nameMatch[1].trim();
492
+ if (name.length < 1 || name.length > 64) {
493
+ result.valid = false;
494
+ result.errors.push("name 长度必须在 1-64 字符之间");
495
+ }
496
+ if (!/^[a-z0-9-]+$/.test(name)) {
497
+ result.valid = false;
498
+ result.errors.push("name 只能包含小写字母、数字和连字符");
499
+ }
500
+ result.metadata.name = name;
501
+ } else {
502
+ result.valid = false;
503
+ result.errors.push("缺少 name 字段");
504
+ }
505
+ if (descMatch) {
506
+ const desc = descMatch[1].trim();
507
+ if (desc.length < 1 || desc.length > 1024) {
508
+ result.valid = false;
509
+ result.errors.push("description 长度必须在 1-1024 字符之间");
510
+ }
511
+ result.metadata.description = desc;
512
+ } else {
513
+ result.valid = false;
514
+ result.errors.push("缺少 description 字段");
515
+ }
516
+ if (licenseMatch) {
517
+ result.metadata.license = licenseMatch[1].trim();
518
+ }
519
+ if (compatMatch) {
520
+ result.metadata.compatibility = compatMatch[1].trim();
521
+ }
522
+ if (toolsMatch) {
523
+ result.metadata.allowedTools = toolsMatch[1].split(",").map((t) => t.trim());
524
+ }
525
+ if (metaMatch) {
526
+ try {
527
+ result.metadata.metadata = {};
528
+ const metaLines = metaMatch[1].split(`
529
+ `).filter((l) => l.trim());
530
+ for (const line of metaLines) {
531
+ const [key, ...valueParts] = line.split(":");
532
+ if (key && valueParts.length) {
533
+ result.metadata.metadata[key.trim()] = valueParts.join(":").trim();
534
+ }
535
+ }
536
+ } catch {
537
+ result.warnings.push("metadata 格式解析失败");
538
+ }
539
+ }
540
+ } else {
541
+ result.valid = false;
542
+ result.errors.push("SKILL.md 缺少 frontmatter (--- 包裹的 YAML)");
543
+ }
544
+ }
545
+ rmSync(tempDir, { recursive: true, force: true });
546
+ } catch (err) {
547
+ result.valid = false;
548
+ result.errors.push(`解压失败: ${err instanceof Error ? err.message : "未知错误"}`);
549
+ }
550
+ return result;
551
+ }
552
+ function validateSkillDir(dirPath) {
553
+ const result = {
554
+ valid: true,
555
+ errors: [],
556
+ warnings: []
557
+ };
558
+ if (!existsSync2(dirPath)) {
559
+ result.valid = false;
560
+ result.errors.push(`目录不存在: ${dirPath}`);
561
+ return result;
562
+ }
563
+ const skillMdPath = join2(dirPath, "SKILL.md");
564
+ if (!existsSync2(skillMdPath)) {
565
+ result.valid = false;
566
+ result.errors.push("目录内缺少 SKILL.md 文件");
567
+ return result;
568
+ }
569
+ try {
570
+ const skillMdContent = readFileSync2(skillMdPath, "utf-8");
571
+ const frontmatterMatch = skillMdContent.match(/^---\n([\s\S]*?)\n---/);
572
+ if (frontmatterMatch) {
573
+ const frontmatter = frontmatterMatch[1];
574
+ result.metadata = {};
575
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
576
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
577
+ if (nameMatch) {
578
+ const name = nameMatch[1].trim();
579
+ if (name.length < 1 || name.length > 64) {
580
+ result.valid = false;
581
+ result.errors.push("name 长度必须在 1-64 字符之间");
582
+ }
583
+ if (!/^[a-z0-9-]+$/.test(name)) {
584
+ result.valid = false;
585
+ result.errors.push("name 只能包含小写字母、数字和连字符");
586
+ }
587
+ result.metadata.name = name;
588
+ } else {
589
+ result.valid = false;
590
+ result.errors.push("缺少 name 字段");
591
+ }
592
+ if (descMatch) {
593
+ const desc = descMatch[1].trim();
594
+ if (desc.length < 1 || desc.length > 1024) {
595
+ result.valid = false;
596
+ result.errors.push("description 长度必须在 1-1024 字符之间");
597
+ }
598
+ result.metadata.description = desc;
599
+ } else {
600
+ result.valid = false;
601
+ result.errors.push("缺少 description 字段");
602
+ }
603
+ } else {
604
+ result.valid = false;
605
+ result.errors.push("SKILL.md 缺少 frontmatter (--- 包裹的 YAML)");
606
+ }
607
+ } catch (err) {
608
+ result.warnings.push(`SKILL.md 读取失败: ${err instanceof Error ? err.message : "未知错误"}`);
609
+ }
610
+ return result;
611
+ }
612
+
613
+ // src/cmd/skill.ts
614
+ import { writeFileSync as writeFileSync2, existsSync as existsSync3, statSync as statSync2 } from "fs";
615
+ import { join as join3, basename, dirname } from "path";
616
+ import { execSync as execSync2 } from "child_process";
617
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
618
+ async function listSkills() {
619
+ const config = loadConfig();
620
+ if (!config?.token) {
621
+ error("未登录,请先运行 arm login");
622
+ process.exit(1);
623
+ }
624
+ const client = new ApiClient(config.serverUrl, config.token);
625
+ try {
626
+ const result = await client.listSkills();
627
+ if (result.skills.length === 0) {
628
+ info("暂无 Skill");
629
+ return;
630
+ }
631
+ console.log(`
632
+ 共 ${result.total} 个 Skill:
633
+ `);
634
+ for (const skill of result.skills) {
635
+ console.log(formatSkill(skill));
636
+ console.log("");
637
+ }
638
+ } catch (err) {
639
+ error(`获取列表失败: ${err instanceof Error ? err.message : "未知错误"}`);
640
+ process.exit(1);
641
+ }
642
+ }
643
+ async function searchSkills(keyword) {
644
+ const config = loadConfig();
645
+ if (!config?.token) {
646
+ error("未登录,请先运行 arm login");
647
+ process.exit(1);
648
+ }
649
+ const client = new ApiClient(config.serverUrl, config.token);
650
+ try {
651
+ const result = await client.listSkills(keyword);
652
+ if (result.skills.length === 0) {
653
+ info(`没有找到包含 "${keyword}" 的 Skill`);
654
+ return;
655
+ }
656
+ console.log(`
657
+ 找到 ${result.total} 个结果:
658
+ `);
659
+ for (const skill of result.skills) {
660
+ console.log(formatSkill(skill));
661
+ console.log("");
662
+ }
663
+ } catch (err) {
664
+ error(`搜索失败: ${err instanceof Error ? err.message : "未知错误"}`);
665
+ process.exit(1);
666
+ }
667
+ }
668
+ async function infoSkill(name) {
669
+ const config = loadConfig();
670
+ if (!config?.token) {
671
+ error("未登录,请先运行 arm login");
672
+ process.exit(1);
673
+ }
674
+ const client = new ApiClient(config.serverUrl, config.token);
675
+ try {
676
+ const skill = await client.getSkill(name);
677
+ console.log(formatSkillDetail(skill));
678
+ } catch (err) {
679
+ error(`获取详情失败: ${err instanceof Error ? err.message : "未知错误"}`);
680
+ process.exit(1);
681
+ }
682
+ }
683
+ async function downloadSkill(name, outputDir) {
684
+ const config = loadConfig();
685
+ if (!config?.token) {
686
+ error("未登录,请先运行 arm login");
687
+ process.exit(1);
688
+ }
689
+ const client = new ApiClient(config.serverUrl, config.token);
690
+ try {
691
+ info(`正在下载 ${name}...`);
692
+ const buffer = await client.downloadSkill(name);
693
+ const outputPath = join3(outputDir || ".", `${name}.zip`);
694
+ writeFileSync2(outputPath, Buffer.from(buffer));
695
+ success(`已下载到 ${outputPath}`);
696
+ } catch (err) {
697
+ error(`下载失败: ${err instanceof Error ? err.message : "未知错误"}`);
698
+ process.exit(1);
699
+ }
700
+ }
701
+ async function uploadSkill(filePath) {
702
+ const config = loadConfig();
703
+ if (!config?.token) {
704
+ error("未登录,请先运行 arm login");
705
+ process.exit(1);
706
+ }
707
+ if (!existsSync3(filePath)) {
708
+ error(`上传失败: 目录不存在: ${filePath}`);
709
+ process.exit(1);
710
+ }
711
+ const isZip = filePath.toLowerCase().endsWith(".zip");
712
+ const validation = isZip ? validateZip(filePath) : validateSkillDir(filePath);
713
+ if (!validation.valid) {
714
+ error(`上传失败: ${validation.errors.join(", ")}`);
715
+ process.exit(1);
716
+ }
717
+ const skillName = validation.metadata?.name || basename(filePath);
718
+ const tempDir = mkdtempSync2("/tmp/skill-upload-");
719
+ const zipPath = join3(tempDir, `${skillName}.zip`);
720
+ try {
721
+ if (isZip) {
722
+ execSync2(`cp "${filePath}" "${zipPath}"`, { stdio: "pipe" });
723
+ } else {
724
+ execSync2(`cd "${dirname(filePath)}" && zip -r "${zipPath}" "${basename(filePath)}" -x ".*"`, { stdio: "pipe" });
725
+ }
726
+ const client = new ApiClient(config.serverUrl, config.token);
727
+ info(`正在上传 ${filePath}...`);
728
+ const skill = await client.uploadSkill(zipPath);
729
+ success(`上传成功! Skill: ${skill.name}`);
730
+ } catch (err) {
731
+ console.error("DEBUG upload error:", err);
732
+ error(`上传失败: ${err instanceof Error ? err.message : "未知错误"}`);
733
+ process.exit(1);
734
+ } finally {
735
+ rmSync2(tempDir, { recursive: true, force: true });
736
+ }
737
+ }
738
+ async function mySkills() {
739
+ const config = loadConfig();
740
+ if (!config?.token) {
741
+ error("未登录,请先运行 arm login");
742
+ process.exit(1);
743
+ }
744
+ const client = new ApiClient(config.serverUrl, config.token);
745
+ try {
746
+ const skills = await client.getMySkills();
747
+ if (skills.length === 0) {
748
+ info("您还没有发布任何 Skill");
749
+ return;
750
+ }
751
+ console.log(`
752
+ 我发布的 Skill (共 ${skills.length}):
753
+ `);
754
+ for (const skill of skills) {
755
+ console.log(formatSkill(skill));
756
+ console.log("");
757
+ }
758
+ } catch (err) {
759
+ error(`获取列表失败: ${err instanceof Error ? err.message : "未知错误"}`);
760
+ process.exit(1);
761
+ }
762
+ }
763
+ async function deleteSkill(name) {
764
+ const config = loadConfig();
765
+ if (!config?.token) {
766
+ error("未登录,请先运行 arm login");
767
+ process.exit(1);
768
+ }
769
+ const client = new ApiClient(config.serverUrl, config.token);
770
+ try {
771
+ await client.deleteSkill(name);
772
+ success(`已删除 ${name}`);
773
+ } catch (err) {
774
+ error(`删除失败: ${err instanceof Error ? err.message : "未知错误"}`);
775
+ process.exit(1);
776
+ }
777
+ }
778
+ async function validateSkill(filePath) {
779
+ const isDir = existsSync3(filePath) && statSync2(filePath).isDirectory();
780
+ const result = isDir ? validateSkillDir(filePath) : validateZip(filePath);
781
+ if (result.valid) {
782
+ success("验证通过!");
783
+ } else {
784
+ error("验证失败:");
785
+ }
786
+ if (result.errors.length > 0) {
787
+ console.log(`
788
+ 错误:`);
789
+ for (const err of result.errors) {
790
+ console.log(` - ${err}`);
791
+ }
792
+ }
793
+ if (result.warnings.length > 0) {
794
+ console.log(`
795
+ 警告:`);
796
+ for (const warn of result.warnings) {
797
+ console.log(` - ${warn}`);
798
+ }
799
+ }
800
+ if (result.metadata) {
801
+ console.log(`
802
+ 元数据:`);
803
+ if (result.metadata.name)
804
+ console.log(` name: ${result.metadata.name}`);
805
+ if (result.metadata.description)
806
+ console.log(` description: ${result.metadata.description}`);
807
+ if (result.metadata.license)
808
+ console.log(` license: ${result.metadata.license}`);
809
+ if (result.metadata.compatibility)
810
+ console.log(` compatibility: ${result.metadata.compatibility}`);
811
+ if (result.metadata.allowedTools)
812
+ console.log(` allowed-tools: ${result.metadata.allowedTools.join(", ")}`);
813
+ }
814
+ if (!result.valid) {
815
+ process.exit(1);
816
+ }
817
+ }
818
+
819
+ // src/cmd/agent.ts
820
+ import { writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
821
+ import { join as join4 } from "path";
822
+ import { execSync as execSync3 } from "child_process";
823
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync3 } from "fs";
824
+ async function listAgents() {
825
+ const config = loadConfig();
826
+ if (!config?.token) {
827
+ error("未登录,请先运行 arm login");
828
+ process.exit(1);
829
+ }
830
+ const client = new ApiClient(config.serverUrl, config.token);
831
+ try {
832
+ const result = await client.listAgents();
833
+ if (result.agents.length === 0) {
834
+ info("暂无 Agent");
835
+ return;
836
+ }
837
+ console.log(`
838
+ 共 ${result.total} 个 Agent:
839
+ `);
840
+ for (const agent of result.agents) {
841
+ console.log(formatAgent(agent));
842
+ console.log("");
843
+ }
844
+ } catch (err) {
845
+ error(`获取列表失败: ${err instanceof Error ? err.message : "未知错误"}`);
846
+ process.exit(1);
847
+ }
848
+ }
849
+ async function searchAgents(keyword) {
850
+ const config = loadConfig();
851
+ if (!config?.token) {
852
+ error("未登录,请先运行 arm login");
853
+ process.exit(1);
854
+ }
855
+ const client = new ApiClient(config.serverUrl, config.token);
856
+ try {
857
+ const result = await client.listAgents(keyword);
858
+ if (result.agents.length === 0) {
859
+ info(`没有找到包含 "${keyword}" 的 Agent`);
860
+ return;
861
+ }
862
+ console.log(`
863
+ 找到 ${result.total} 个结果:
864
+ `);
865
+ for (const agent of result.agents) {
866
+ console.log(formatAgent(agent));
867
+ console.log("");
868
+ }
869
+ } catch (err) {
870
+ error(`搜索失败: ${err instanceof Error ? err.message : "未知错误"}`);
871
+ process.exit(1);
872
+ }
873
+ }
874
+ async function infoAgent(name) {
875
+ const config = loadConfig();
876
+ if (!config?.token) {
877
+ error("未登录,请先运行 arm login");
878
+ process.exit(1);
879
+ }
880
+ const client = new ApiClient(config.serverUrl, config.token);
881
+ try {
882
+ const result = await client.listAgents(name);
883
+ const agent = result.agents.find((a) => a.name === name);
884
+ if (!agent) {
885
+ error(`Agent "${name}" 不存在`);
886
+ process.exit(1);
887
+ }
888
+ const fullAgent = await client.getAgent(agent.id);
889
+ if (fullAgent.knowledges && fullAgent.knowledges.length > 0) {
890
+ const knowledgeNamePromises = fullAgent.knowledges.map(async (k) => {
891
+ try {
892
+ const knowledge = await client.getKnowledge(k.knowledgeId);
893
+ return { knowledgeId: k.knowledgeId, knowledgeName: knowledge.name, retrievalConfig: k.retrievalConfig };
894
+ } catch {
895
+ return k;
896
+ }
897
+ });
898
+ fullAgent.knowledges = await Promise.all(knowledgeNamePromises);
899
+ }
900
+ console.log(formatAgentDetail(fullAgent));
901
+ } catch (err) {
902
+ error(`获取详情失败: ${err instanceof Error ? err.message : "未知错误"}`);
903
+ process.exit(1);
904
+ }
905
+ }
906
+ async function downloadAgent(name, outputDir) {
907
+ const config = loadConfig();
908
+ if (!config?.token) {
909
+ error("未登录,请先运行 arm login");
910
+ process.exit(1);
911
+ }
912
+ const client = new ApiClient(config.serverUrl, config.token);
913
+ try {
914
+ const result = await client.listAgents(name);
915
+ const agent = result.agents.find((a) => a.name === name);
916
+ if (!agent) {
917
+ error(`Agent "${name}" 不存在`);
918
+ process.exit(1);
919
+ }
920
+ info(`正在下载 ${name}...`);
921
+ const { buffer, version } = await client.downloadAgent(agent.id);
922
+ const tempDir = mkdtempSync3("/tmp/agent-download-");
923
+ const zipPath = join4(tempDir, `${name}.zip`);
924
+ writeFileSync3(zipPath, Buffer.from(buffer));
925
+ const targetDir = join4(outputDir || ".", name);
926
+ execSync3(`mkdir -p "${targetDir}"`, { stdio: "pipe" });
927
+ execSync3(`unzip -q "${zipPath}" -d "${tempDir}"`, { stdio: "pipe" });
928
+ const contentDir = join4(tempDir, "agent-content");
929
+ if (existsSync4(contentDir)) {
930
+ execSync3(`cp -r "${contentDir}"/* "${targetDir}/"`, { stdio: "pipe" });
931
+ } else {
932
+ execSync3(`cp -r "${tempDir}"/* "${targetDir}/"`, { stdio: "pipe" });
933
+ }
934
+ rmSync3(tempDir, { recursive: true, force: true });
935
+ success(`已下载到 ${targetDir} (版本: ${version})`);
936
+ } catch (err) {
937
+ error(`下载失败: ${err instanceof Error ? err.message : "未知错误"}`);
938
+ process.exit(1);
939
+ }
940
+ }
941
+
942
+ // src/cmd/knowledge.ts
943
+ import { writeFileSync as writeFileSync4, existsSync as existsSync5, statSync as statSync3 } from "fs";
944
+ import { join as join5, basename as basename2, dirname as dirname2 } from "path";
945
+ import { execSync as execSync4 } from "child_process";
946
+ import { mkdtempSync as mkdtempSync4, rmSync as rmSync4 } from "fs";
947
+ async function listKnowledge() {
948
+ const config = loadConfig();
949
+ if (!config?.token) {
950
+ error("未登录,请先运行 arm login");
951
+ process.exit(1);
952
+ }
953
+ const client = new ApiClient(config.serverUrl, config.token);
954
+ try {
955
+ const result = await client.listKnowledge();
956
+ if (result.knowledges.length === 0) {
957
+ info("暂无 Knowledge");
958
+ return;
959
+ }
960
+ console.log(`
961
+ 共 ${result.total} 个 Knowledge:
962
+ `);
963
+ for (const knowledge of result.knowledges) {
964
+ console.log(formatKnowledge(knowledge));
965
+ console.log("");
966
+ }
967
+ } catch (err) {
968
+ error(`获取列表失败: ${err instanceof Error ? err.message : "未知错误"}`);
969
+ process.exit(1);
970
+ }
971
+ }
972
+ async function searchKnowledge(keyword) {
973
+ const config = loadConfig();
974
+ if (!config?.token) {
975
+ error("未登录,请先运行 arm login");
976
+ process.exit(1);
977
+ }
978
+ const client = new ApiClient(config.serverUrl, config.token);
979
+ try {
980
+ const result = await client.listKnowledge(keyword);
981
+ if (result.knowledges.length === 0) {
982
+ info(`没有找到包含 "${keyword}" 的 Knowledge`);
983
+ return;
984
+ }
985
+ console.log(`
986
+ 找到 ${result.total} 个结果:
987
+ `);
988
+ for (const knowledge of result.knowledges) {
989
+ console.log(formatKnowledge(knowledge));
990
+ console.log("");
991
+ }
992
+ } catch (err) {
993
+ error(`搜索失败: ${err instanceof Error ? err.message : "未知错误"}`);
994
+ process.exit(1);
995
+ }
996
+ }
997
+ async function infoKnowledge(name) {
998
+ const config = loadConfig();
999
+ if (!config?.token) {
1000
+ error("未登录,请先运行 arm login");
1001
+ process.exit(1);
1002
+ }
1003
+ const client = new ApiClient(config.serverUrl, config.token);
1004
+ try {
1005
+ const knowledge = await client.getKnowledge(name);
1006
+ console.log(formatKnowledgeDetail(knowledge));
1007
+ } catch (err) {
1008
+ error(`获取详情失败: ${err instanceof Error ? err.message : "未知错误"}`);
1009
+ process.exit(1);
1010
+ }
1011
+ }
1012
+ async function downloadKnowledge(name, outputDir) {
1013
+ const config = loadConfig();
1014
+ if (!config?.token) {
1015
+ error("未登录,请先运行 arm login");
1016
+ process.exit(1);
1017
+ }
1018
+ const client = new ApiClient(config.serverUrl, config.token);
1019
+ try {
1020
+ info(`正在下载 ${name}...`);
1021
+ const buffer = await client.downloadKnowledge(name);
1022
+ const outputPath = join5(outputDir || ".", `${name}.zip`);
1023
+ writeFileSync4(outputPath, Buffer.from(buffer));
1024
+ success(`已下载到 ${outputPath}`);
1025
+ } catch (err) {
1026
+ error(`下载失败: ${err instanceof Error ? err.message : "未知错误"}`);
1027
+ process.exit(1);
1028
+ }
1029
+ }
1030
+ async function uploadKnowledge(filePath) {
1031
+ const config = loadConfig();
1032
+ if (!config?.token) {
1033
+ error("未登录,请先运行 arm login");
1034
+ process.exit(1);
1035
+ }
1036
+ if (!existsSync5(filePath)) {
1037
+ error(`上传失败: 目录不存在: ${filePath}`);
1038
+ process.exit(1);
1039
+ }
1040
+ const knowledgeName = basename2(filePath);
1041
+ const tempDir = mkdtempSync4("/tmp/knowledge-upload-");
1042
+ const zipPath = join5(tempDir, `${knowledgeName}.zip`);
1043
+ try {
1044
+ if (statSync3(filePath).isDirectory()) {
1045
+ execSync4(`cd "${dirname2(filePath)}" && zip -r "${zipPath}" "${basename2(filePath)}" -x ".*"`, { stdio: "pipe" });
1046
+ } else {
1047
+ execSync4(`cp "${filePath}" "${zipPath}"`, { stdio: "pipe" });
1048
+ }
1049
+ const client = new ApiClient(config.serverUrl, config.token);
1050
+ info(`正在上传 ${filePath}...`);
1051
+ const knowledge = await client.uploadKnowledge(zipPath);
1052
+ success(`上传成功! Knowledge: ${knowledge.name}`);
1053
+ } catch (err) {
1054
+ error(`上传失败: ${err instanceof Error ? err.message : "未知错误"}`);
1055
+ process.exit(1);
1056
+ } finally {
1057
+ rmSync4(tempDir, { recursive: true, force: true });
1058
+ }
1059
+ }
1060
+ async function myKnowledge() {
1061
+ const config = loadConfig();
1062
+ if (!config?.token) {
1063
+ error("未登录,请先运行 arm login");
1064
+ process.exit(1);
1065
+ }
1066
+ const client = new ApiClient(config.serverUrl, config.token);
1067
+ try {
1068
+ const knowledges = await client.getMyKnowledge();
1069
+ if (knowledges.length === 0) {
1070
+ info("您还没有发布任何 Knowledge");
1071
+ return;
1072
+ }
1073
+ console.log(`
1074
+ 我发布的 Knowledge (共 ${knowledges.length}):
1075
+ `);
1076
+ for (const knowledge of knowledges) {
1077
+ console.log(formatKnowledge(knowledge));
1078
+ console.log("");
1079
+ }
1080
+ } catch (err) {
1081
+ error(`获取列表失败: ${err instanceof Error ? err.message : "未知错误"}`);
1082
+ process.exit(1);
1083
+ }
1084
+ }
1085
+ async function deleteKnowledge(name) {
1086
+ const config = loadConfig();
1087
+ if (!config?.token) {
1088
+ error("未登录,请先运行 arm login");
1089
+ process.exit(1);
1090
+ }
1091
+ const client = new ApiClient(config.serverUrl, config.token);
1092
+ try {
1093
+ await client.deleteKnowledge(name);
1094
+ success(`已删除 ${name}`);
1095
+ } catch (err) {
1096
+ error(`删除失败: ${err instanceof Error ? err.message : "未知错误"}`);
1097
+ process.exit(1);
1098
+ }
1099
+ }
1100
+
1101
+ // src/cmd/server.ts
1102
+ function showServer() {
1103
+ info(`当前服务端: ${getServerUrl()}`);
1104
+ }
1105
+ function setServer(url) {
1106
+ setServerUrl(url);
1107
+ info(`已设置服务端: ${url}`);
1108
+ }
1109
+
1110
+ // src/main.ts
1111
+ var args = process.argv.slice(2);
1112
+ var command = args[0];
1113
+ var subCommand = args[1];
1114
+ async function main() {
1115
+ switch (command) {
1116
+ case "login":
1117
+ if (!args[1] || !args[2]) {
1118
+ console.error("用法: arm login <server-url> <api-key>");
1119
+ process.exit(1);
1120
+ }
1121
+ await login(args[1], args[2]);
1122
+ break;
1123
+ case "logout":
1124
+ await logout();
1125
+ break;
1126
+ case "server":
1127
+ if (subCommand === "set" && args[2]) {
1128
+ setServer(args[2]);
1129
+ } else {
1130
+ showServer();
1131
+ }
1132
+ break;
1133
+ case "skill":
1134
+ switch (subCommand) {
1135
+ case "ls":
1136
+ await listSkills();
1137
+ break;
1138
+ case "search":
1139
+ if (!args[2]) {
1140
+ console.error("用法: arm skill search <keyword>");
1141
+ process.exit(1);
1142
+ }
1143
+ await searchSkills(args[2]);
1144
+ break;
1145
+ case "info":
1146
+ if (!args[2]) {
1147
+ console.error("用法: arm skill info <name>");
1148
+ process.exit(1);
1149
+ }
1150
+ await infoSkill(args[2]);
1151
+ break;
1152
+ case "download":
1153
+ if (!args[2]) {
1154
+ console.error("用法: arm skill download <name> [output-dir]");
1155
+ process.exit(1);
1156
+ }
1157
+ await downloadSkill(args[2], args[3]);
1158
+ break;
1159
+ case "upload":
1160
+ if (!args[2]) {
1161
+ console.error("用法: arm skill upload <path>");
1162
+ process.exit(1);
1163
+ }
1164
+ await uploadSkill(args[2]);
1165
+ break;
1166
+ case "my":
1167
+ await mySkills();
1168
+ break;
1169
+ case "delete":
1170
+ if (!args[2]) {
1171
+ console.error("用法: arm skill delete <name>");
1172
+ process.exit(1);
1173
+ }
1174
+ await deleteSkill(args[2]);
1175
+ break;
1176
+ case "validate":
1177
+ if (!args[2]) {
1178
+ console.error("用法: arm skill validate <path>");
1179
+ process.exit(1);
1180
+ }
1181
+ await validateSkill(args[2]);
1182
+ break;
1183
+ default:
1184
+ console.log(`
1185
+ 可用命令:
1186
+ arm login <server-url> <api-key> 登录
1187
+ arm logout 登出
1188
+ arm skill ls 列出所有 Skill
1189
+ arm skill search <keyword> 搜索 Skill
1190
+ arm skill info <name> 查看 Skill 详情
1191
+ arm skill download <name> [dir] 下载 Skill
1192
+ arm skill upload <path> 上传 Skill
1193
+ arm skill my 我的发布
1194
+ arm skill delete <name> 删除 Skill
1195
+ arm skill validate <path> 验证 Skill 格式
1196
+ arm server 显示当前服务端
1197
+ arm server set <url> 设置服务端
1198
+ `);
1199
+ }
1200
+ break;
1201
+ case "knowledge":
1202
+ switch (subCommand) {
1203
+ case "ls":
1204
+ await listKnowledge();
1205
+ break;
1206
+ case "search":
1207
+ if (!args[2]) {
1208
+ console.error("用法: arm knowledge search <keyword>");
1209
+ process.exit(1);
1210
+ }
1211
+ await searchKnowledge(args[2]);
1212
+ break;
1213
+ case "info":
1214
+ if (!args[2]) {
1215
+ console.error("用法: arm knowledge info <name>");
1216
+ process.exit(1);
1217
+ }
1218
+ await infoKnowledge(args[2]);
1219
+ break;
1220
+ case "download":
1221
+ if (!args[2]) {
1222
+ console.error("用法: arm knowledge download <name> [output-dir]");
1223
+ process.exit(1);
1224
+ }
1225
+ await downloadKnowledge(args[2], args[3]);
1226
+ break;
1227
+ case "upload":
1228
+ if (!args[2]) {
1229
+ console.error("用法: arm knowledge upload <path>");
1230
+ process.exit(1);
1231
+ }
1232
+ await uploadKnowledge(args[2]);
1233
+ break;
1234
+ case "my":
1235
+ await myKnowledge();
1236
+ break;
1237
+ case "delete":
1238
+ if (!args[2]) {
1239
+ console.error("用法: arm knowledge delete <name>");
1240
+ process.exit(1);
1241
+ }
1242
+ await deleteKnowledge(args[2]);
1243
+ break;
1244
+ default:
1245
+ console.log(`
1246
+ 可用命令:
1247
+ arm knowledge ls 列出所有 Knowledge
1248
+ arm knowledge search <keyword> 搜索 Knowledge
1249
+ arm knowledge info <name> 查看 Knowledge 详情
1250
+ arm knowledge download <name> [dir] 下载 Knowledge
1251
+ arm knowledge upload <path> 上传 Knowledge
1252
+ arm knowledge my 我的发布
1253
+ arm knowledge delete <name> 删除 Knowledge
1254
+ `);
1255
+ }
1256
+ break;
1257
+ case "me":
1258
+ await getCurrentUser();
1259
+ break;
1260
+ case "agent":
1261
+ switch (subCommand) {
1262
+ case "ls":
1263
+ await listAgents();
1264
+ break;
1265
+ case "search":
1266
+ if (!args[2]) {
1267
+ console.error("用法: arm agent search <keyword>");
1268
+ process.exit(1);
1269
+ }
1270
+ await searchAgents(args[2]);
1271
+ break;
1272
+ case "info":
1273
+ if (!args[2]) {
1274
+ console.error("用法: arm agent info <name>");
1275
+ process.exit(1);
1276
+ }
1277
+ await infoAgent(args[2]);
1278
+ break;
1279
+ case "download":
1280
+ if (!args[2]) {
1281
+ console.error("用法: arm agent download <name> [output-dir]");
1282
+ process.exit(1);
1283
+ }
1284
+ await downloadAgent(args[2], args[3]);
1285
+ break;
1286
+ default:
1287
+ console.log(`
1288
+ 可用命令:
1289
+ arm agent ls 列出所有 Agent
1290
+ arm agent search <keyword> 搜索 Agent
1291
+ arm agent info <name> 查看 Agent 详情
1292
+ arm agent download <name> [dir] 下载 Agent
1293
+ `);
1294
+ }
1295
+ break;
1296
+ default:
1297
+ console.log(`
1298
+ Agent Resource Management (arm)
1299
+
1300
+ 用法:
1301
+ arm login <server-url> <api-key> 登录
1302
+ arm logout 登出
1303
+ arm skill ls 列出所有 Skill
1304
+ arm skill search <keyword> 搜索 Skill
1305
+ arm skill info <name> 查看 Skill 详情
1306
+ arm skill download <name> [dir] 下载 Skill
1307
+ arm skill upload <path> 上传 Skill
1308
+ arm skill my 我的发布
1309
+ arm skill delete <name> 删除 Skill
1310
+ arm skill validate <path> 验证 Skill 格式
1311
+ arm knowledge ls 列出所有 Knowledge
1312
+ arm knowledge search <keyword> 搜索 Knowledge
1313
+ arm knowledge info <name> 查看 Knowledge 详情
1314
+ arm knowledge download <name> [dir] 下载 Knowledge
1315
+ arm knowledge upload <path> 上传 Knowledge
1316
+ arm knowledge my 我的发布
1317
+ arm knowledge delete <name> 删除 Knowledge
1318
+ arm agent ls 列出所有 Agent
1319
+ arm agent search <keyword> 搜索 Agent
1320
+ arm agent info <name> 查看 Agent 详情
1321
+ arm agent download <name> [dir] 下载 Agent
1322
+ arm server 显示当前服务端
1323
+ arm server set <url> 设置服务端
1324
+ `);
1325
+ }
1326
+ }
1327
+ main().catch((err) => {
1328
+ console.error("Error:", err);
1329
+ process.exit(1);
1330
+ });