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.
- package/.claude/settings.local.json +8 -0
- package/README.md +196 -0
- package/bun.lock +32 -0
- package/data/skills/bad-name-skill/SKILL.md +8 -0
- package/data/skills/github-tool/SKILL.md +11 -0
- package/data/skills/invalid-skill/SKILL.md +3 -0
- package/data/skills/pdf-tool/SKILL.md +11 -0
- package/data/skills/pdf-tool.zip +0 -0
- package/dist/main.js +1330 -0
- package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/AGENT.md +18 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/package.json +20 -0
- package/src/cmd/agent.ts +137 -0
- package/src/cmd/auth.ts +50 -0
- package/src/cmd/knowledge.ts +171 -0
- package/src/cmd/server.ts +11 -0
- package/src/cmd/skill.ts +220 -0
- package/src/lib/client.ts +274 -0
- package/src/lib/formatter.ts +196 -0
- package/src/lib/storage.ts +63 -0
- package/src/lib/validate.ts +210 -0
- package/src/main.ts +235 -0
- package/test-regression.sh +273 -0
- 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
|
+
});
|