agent-resource-management 2.1.6 → 2.1.7
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/AGENTS.md +368 -0
- package/README.md +7 -0
- package/dist/main.js +7 -6
- package/package.json +1 -1
- package/src/lib/client.ts +0 -1
- package/src/lib/validate.ts +1 -1
- package/src/main.ts +7 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# ARM CLI 开发规范
|
|
2
|
+
|
|
3
|
+
> Agent Resource Management CLI 工具
|
|
4
|
+
|
|
5
|
+
## 1. 项目概述
|
|
6
|
+
|
|
7
|
+
ARM CLI 是一个基于 Bun + TypeScript 的命令行工具,用于管理 Agent、Skill、Knowledge 资源。
|
|
8
|
+
|
|
9
|
+
### 命令结构
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
arm <entity> <action> [options]
|
|
13
|
+
|
|
14
|
+
Entities:
|
|
15
|
+
- auth # 认证
|
|
16
|
+
- skill # Skill 管理
|
|
17
|
+
- knowledge # Knowledge 管理
|
|
18
|
+
- agent # Agent 管理
|
|
19
|
+
- server # 服务端配置
|
|
20
|
+
- output # 输出模式配置
|
|
21
|
+
- me # 当前用户信息
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. 技术栈
|
|
27
|
+
|
|
28
|
+
| 组件 | 技术 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| Runtime | Bun |
|
|
31
|
+
| Language | TypeScript |
|
|
32
|
+
| HTTP Client | Fetch API |
|
|
33
|
+
| 类型 | `@pkg/types/skill` |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. 目录结构
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
cli/
|
|
41
|
+
├── src/
|
|
42
|
+
│ ├── main.ts # CLI 入口,命令解析
|
|
43
|
+
│ ├── cmd/ # 命令实现
|
|
44
|
+
│ │ ├── auth.ts # login/logout/register
|
|
45
|
+
│ │ ├── skill.ts # Skill CRUD
|
|
46
|
+
│ │ ├── knowledge.ts # Knowledge CRUD
|
|
47
|
+
│ │ ├── agent.ts # Agent CRUD + bind/sync
|
|
48
|
+
│ │ └── server.ts # 服务端配置
|
|
49
|
+
│ └── lib/ # 工具库
|
|
50
|
+
│ ├── client.ts # ApiClient HTTP 客户端
|
|
51
|
+
│ ├── storage.ts # 本地配置存储 (~/.arm/)
|
|
52
|
+
│ ├── output.ts # JSON/Text 输出模式
|
|
53
|
+
│ ├── formatter.ts # 格式化输出
|
|
54
|
+
│ └── validate.ts # 本地验证
|
|
55
|
+
├── package.json
|
|
56
|
+
└── tsconfig.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 4. 核心库
|
|
62
|
+
|
|
63
|
+
### 4.1 ApiClient
|
|
64
|
+
|
|
65
|
+
HTTP 客户端封装,基于 Fetch API:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { ApiClient } from './lib/client';
|
|
69
|
+
|
|
70
|
+
const client = new ApiClient(serverUrl, token);
|
|
71
|
+
|
|
72
|
+
// 设置 Token
|
|
73
|
+
client.setToken(token);
|
|
74
|
+
|
|
75
|
+
// 认证
|
|
76
|
+
await client.login(apiKey);
|
|
77
|
+
await client.me();
|
|
78
|
+
|
|
79
|
+
// Skills
|
|
80
|
+
await client.listSkills(keyword?, page?, pageSize?);
|
|
81
|
+
await client.getSkill(name);
|
|
82
|
+
await client.uploadSkill(filePath);
|
|
83
|
+
await client.downloadSkill(name);
|
|
84
|
+
|
|
85
|
+
// Agents
|
|
86
|
+
await client.listAgents(keyword?, page?, pageSize?);
|
|
87
|
+
await client.getAgent(id);
|
|
88
|
+
await client.createAgent(data);
|
|
89
|
+
await client.updateAgent(id, data);
|
|
90
|
+
await client.deleteAgent(id);
|
|
91
|
+
await client.bindSkillToAgent(agentId, skillId, version, config?);
|
|
92
|
+
await client.unbindSkillFromAgent(agentId, skillId, version?);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4.2 Storage
|
|
96
|
+
|
|
97
|
+
本地配置存储在 `~/.arm/config.json`:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { loadConfig, saveConfig, clearConfig } from './lib/storage';
|
|
101
|
+
|
|
102
|
+
const config = loadConfig();
|
|
103
|
+
// { serverUrl, token, user, outputMode }
|
|
104
|
+
|
|
105
|
+
saveConfig({ serverUrl, token, user });
|
|
106
|
+
clearConfig(); // 只清除 token 和 user
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 4.3 Output
|
|
110
|
+
|
|
111
|
+
支持 JSON 和 Text 两种输出模式:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { shouldOutputJson, outputJson } from './lib/output';
|
|
115
|
+
|
|
116
|
+
// 检查是否应该输出 JSON
|
|
117
|
+
if (shouldOutputJson()) {
|
|
118
|
+
outputJson({ success: true, data: {...} });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Text 模式输出
|
|
123
|
+
console.log('Normal output');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**JSON 输出格式**:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
{ success: true, data: {...} }
|
|
130
|
+
{ success: false, error: { code: 'ERROR_CODE', message: '...' } }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. 命令开发规范
|
|
136
|
+
|
|
137
|
+
### 5.1 命令模板
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
export async function myCommand(id: string, options: { flag?: boolean } = {}): Promise<void> {
|
|
141
|
+
// 1. 检查登录状态
|
|
142
|
+
const config = loadConfig();
|
|
143
|
+
if (!config?.token) {
|
|
144
|
+
if (shouldOutputJson()) {
|
|
145
|
+
outputJson({ success: false, error: { code: 'NOT_LOGGED_IN', message: '未登录' } });
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
error('未登录,请先运行 arm login');
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 2. 创建客户端
|
|
153
|
+
const client = new ApiClient(config.serverUrl, config.token);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// 3. 业务逻辑
|
|
157
|
+
const result = await client.someMethod();
|
|
158
|
+
|
|
159
|
+
// 4. 输出结果
|
|
160
|
+
if (shouldOutputJson()) {
|
|
161
|
+
outputJson({ success: true, data: result });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
success('操作成功');
|
|
165
|
+
|
|
166
|
+
} catch (err) {
|
|
167
|
+
// 5. 错误处理
|
|
168
|
+
if (shouldOutputJson()) {
|
|
169
|
+
outputJson({ success: false, error: { code: 'METHOD_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
error(`操作失败: ${err instanceof Error ? err.message : '未知错误'}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 5.2 main.ts 命令注册
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
case 'entity':
|
|
182
|
+
switch (subCommand) {
|
|
183
|
+
case 'action':
|
|
184
|
+
if (!args[2]) {
|
|
185
|
+
console.error('用法: arm entity action <id> [--flag] [--json]');
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
await myCommand(args[2], { flag: args.includes('--flag') });
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
console.log(`可用命令: arm entity action <id>`);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 5.3 错误码规范
|
|
197
|
+
|
|
198
|
+
| 错误码 | 说明 |
|
|
199
|
+
|--------|------|
|
|
200
|
+
| `NOT_LOGGED_IN` | 未登录 |
|
|
201
|
+
| `NOT_FOUND` | 资源不存在 |
|
|
202
|
+
| `CREATE_FAILED` | 创建失败 |
|
|
203
|
+
| `UPDATE_FAILED` | 更新失败 |
|
|
204
|
+
| `DELETE_FAILED` | 删除失败 |
|
|
205
|
+
| `BIND_FAILED` | 绑定失败 |
|
|
206
|
+
| `UNBIND_FAILED` | 解绑失败 |
|
|
207
|
+
| `UPLOAD_FAILED` | 上传失败 |
|
|
208
|
+
| `DOWNLOAD_FAILED` | 下载失败 |
|
|
209
|
+
| `SYNC_FAILED` | 同步失败 |
|
|
210
|
+
| `VALIDATION_FAILED` | 验证失败 |
|
|
211
|
+
| `FILE_NOT_FOUND` | 文件不存在 |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 6. 常用命令速查
|
|
216
|
+
|
|
217
|
+
### 6.1 认证
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
arm login <server-url> <api-key>
|
|
221
|
+
arm logout
|
|
222
|
+
arm me
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 6.2 Skill
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
arm skill ls # 列出
|
|
229
|
+
arm skill search <keyword> # 搜索
|
|
230
|
+
arm skill info <name> # 详情
|
|
231
|
+
arm skill download <name> [dir] # 下载
|
|
232
|
+
arm skill upload <path> # 上传
|
|
233
|
+
arm skill my # 我的发布
|
|
234
|
+
arm skill delete <name> # 删除
|
|
235
|
+
arm skill validate <path> # 验证
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 6.3 Knowledge
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
arm knowledge ls
|
|
242
|
+
arm knowledge search <keyword>
|
|
243
|
+
arm knowledge info <name>
|
|
244
|
+
arm knowledge download <name> [dir]
|
|
245
|
+
arm knowledge upload <path>
|
|
246
|
+
arm knowledge my
|
|
247
|
+
arm knowledge delete <name>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 6.4 Agent
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# 基础 CRUD
|
|
254
|
+
arm agent ls
|
|
255
|
+
arm agent search <keyword>
|
|
256
|
+
arm agent info <name>
|
|
257
|
+
arm agent create <name> [--description=...] [--prompt=...]
|
|
258
|
+
arm agent create --from=<folder> # 从文件夹创建
|
|
259
|
+
arm agent update <id> [--name=...] [--description=...] [--prompt=...] [--status=active|draft]
|
|
260
|
+
arm agent delete <id>
|
|
261
|
+
|
|
262
|
+
# 绑定管理
|
|
263
|
+
arm agent bind <id> --skill=<skillId> [--skill-config='{...}']
|
|
264
|
+
arm agent unbind <id> --skill=<skillId> [--version=<ver>]
|
|
265
|
+
arm agent bind <id> --knowledge=<knowledgeId>
|
|
266
|
+
arm agent unbind <id> --knowledge=<knowledgeId> [--version=<ver>]
|
|
267
|
+
|
|
268
|
+
# 同步
|
|
269
|
+
arm agent sync <folder> [--dry-run] [--force]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 6.5 配置
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
arm output [json|text] # 输出模式
|
|
276
|
+
arm server # 显示服务端
|
|
277
|
+
arm server set <url> # 设置服务端
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 7. Agent 文件夹结构
|
|
283
|
+
|
|
284
|
+
`arm agent create --from=<folder>` 支持的目录结构:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
<folder>/
|
|
288
|
+
├── AGENT.md # Agent 元信息 (必填)
|
|
289
|
+
├── skills/ # 可选: Skill 子目录
|
|
290
|
+
│ └── <skill-name>/
|
|
291
|
+
│ └── SKILL.md
|
|
292
|
+
└── knowledges/ # 可选: Knowledge 目录
|
|
293
|
+
└── <name>.md
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### AGENT.md 格式
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
---
|
|
300
|
+
name: my-agent
|
|
301
|
+
description: 我的 Agent 描述
|
|
302
|
+
prompt: |
|
|
303
|
+
You are a helpful assistant.
|
|
304
|
+
---
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 8. 开发命令
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
cd cli
|
|
313
|
+
|
|
314
|
+
# 安装依赖
|
|
315
|
+
bun install
|
|
316
|
+
|
|
317
|
+
# 运行 CLI
|
|
318
|
+
bun run src/main.ts <command>
|
|
319
|
+
|
|
320
|
+
# 或全局链接
|
|
321
|
+
bun link
|
|
322
|
+
arm <command>
|
|
323
|
+
|
|
324
|
+
# 类型检查
|
|
325
|
+
bun run typecheck
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## 9. 关键实现细节
|
|
331
|
+
|
|
332
|
+
### 9.1 API 路径前缀
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// client.ts
|
|
336
|
+
`${this.serverUrl}/api/v1${path}`
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### 9.2 分页参数
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
?page=1&pageSize=20
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### 9.3 搜索参数
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
?keyword=<encoded-keyword>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 9.4 状态过滤
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
?status=active
|
|
355
|
+
?status=draft
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### 9.5 文件上传
|
|
359
|
+
|
|
360
|
+
使用 `FormData` 而非 JSON:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
const formData = new FormData();
|
|
364
|
+
const blob = new Blob([fileBuffer]);
|
|
365
|
+
formData.append('file', blob, fileName);
|
|
366
|
+
|
|
367
|
+
fetch(url, { method: 'POST', body: formData, headers });
|
|
368
|
+
```
|
package/README.md
CHANGED
package/dist/main.js
CHANGED
|
@@ -100,7 +100,6 @@ class ApiClient {
|
|
|
100
100
|
body: formData
|
|
101
101
|
});
|
|
102
102
|
const data = await res.json();
|
|
103
|
-
console.error("DEBUG server response:", JSON.stringify(data));
|
|
104
103
|
if (!data.ok) {
|
|
105
104
|
throw new Error(data.msg);
|
|
106
105
|
}
|
|
@@ -813,7 +812,7 @@ function validateAgentDir(dirPath) {
|
|
|
813
812
|
result.metadata.knowledges = knowledgesLines.map((l) => l.replace(/^\s*-\s*/, "").trim());
|
|
814
813
|
}
|
|
815
814
|
const contentAfterFrontmatter = agentMdContent.replace(/^---[\s\S]*?---\n/, "");
|
|
816
|
-
const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n([\s\S]*?)$/);
|
|
815
|
+
const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n+([\s\S]*?)$/);
|
|
817
816
|
if (promptMatch) {
|
|
818
817
|
result.metadata.prompt = promptMatch[1].trim();
|
|
819
818
|
} else if (contentAfterFrontmatter.trim()) {
|
|
@@ -2437,10 +2436,6 @@ async function main() {
|
|
|
2437
2436
|
await downloadAgent(args[2], args[3]);
|
|
2438
2437
|
break;
|
|
2439
2438
|
case "create":
|
|
2440
|
-
if (!args[2]) {
|
|
2441
|
-
console.error(`用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config='{...}'] [--knowledge-config='{...}'] [--from=<folder-path>] [--json]`);
|
|
2442
|
-
process.exit(1);
|
|
2443
|
-
}
|
|
2444
2439
|
{
|
|
2445
2440
|
const name = args[2];
|
|
2446
2441
|
const options = {};
|
|
@@ -2449,6 +2444,12 @@ async function main() {
|
|
|
2449
2444
|
const skillConfigs = [];
|
|
2450
2445
|
const knowledgeConfigs = [];
|
|
2451
2446
|
let fromFolder;
|
|
2447
|
+
if (name?.startsWith("--from=")) {
|
|
2448
|
+
fromFolder = name.split("=").slice(1).join("=");
|
|
2449
|
+
} else if (!name) {
|
|
2450
|
+
console.error(`用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config='{...}'] [--knowledge-config='{...}'] [--from=<folder-path>] [--json]`);
|
|
2451
|
+
process.exit(1);
|
|
2452
|
+
}
|
|
2452
2453
|
for (let i = 3;i < args.length; i++) {
|
|
2453
2454
|
const arg = args[i];
|
|
2454
2455
|
if (arg.startsWith("--description=")) {
|
package/package.json
CHANGED
package/src/lib/client.ts
CHANGED
package/src/lib/validate.ts
CHANGED
|
@@ -283,7 +283,7 @@ export function validateAgentDir(dirPath: string): AgentValidationResult {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
const contentAfterFrontmatter = agentMdContent.replace(/^---[\s\S]*?---\n/, '');
|
|
286
|
-
const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n([\s\S]*?)$/);
|
|
286
|
+
const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n+([\s\S]*?)$/);
|
|
287
287
|
if (promptMatch) {
|
|
288
288
|
result.metadata.prompt = promptMatch[1].trim();
|
|
289
289
|
} else if (contentAfterFrontmatter.trim()) {
|
package/src/main.ts
CHANGED
|
@@ -224,10 +224,6 @@ async function main() {
|
|
|
224
224
|
await downloadAgent(args[2], args[3]);
|
|
225
225
|
break;
|
|
226
226
|
case 'create':
|
|
227
|
-
if (!args[2]) {
|
|
228
|
-
console.error('用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config=\'{...}\'] [--knowledge-config=\'{...}\'] [--from=<folder-path>] [--json]');
|
|
229
|
-
process.exit(1);
|
|
230
|
-
}
|
|
231
227
|
{
|
|
232
228
|
const name = args[2];
|
|
233
229
|
const options: Record<string, string | undefined> = {};
|
|
@@ -237,6 +233,13 @@ async function main() {
|
|
|
237
233
|
const knowledgeConfigs: string[] = [];
|
|
238
234
|
let fromFolder: string | undefined;
|
|
239
235
|
|
|
236
|
+
if (name?.startsWith('--from=')) {
|
|
237
|
+
fromFolder = name.split('=').slice(1).join('=');
|
|
238
|
+
} else if (!name) {
|
|
239
|
+
console.error('用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config=\'{...}\'] [--knowledge-config=\'{...}\'] [--from=<folder-path>] [--json]');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
240
243
|
for (let i = 3; i < args.length; i++) {
|
|
241
244
|
const arg = args[i];
|
|
242
245
|
if (arg.startsWith('--description=')) {
|