agent-resource-management 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@ Agent Resource Management CLI 是一个用于管理 Skills、Knowledges 和 Agen
13
13
  - 上传/下载/删除
14
14
  - 本地验证
15
15
  - 多服务端支持
16
+ - **JSON 输出模式** - 便于 AI Agent 集成
16
17
 
17
18
  ## 安装
18
19
 
@@ -96,10 +97,10 @@ arm agent download workspace-assistant ./agents
96
97
  ### 6. 上传
97
98
 
98
99
  ```bash
99
- # 上传本地 Skill 目录
100
+ # 上传本地 Skill 目录或 ZIP 文件
100
101
  arm skill upload ./my-skills/pdf-tool
101
102
 
102
- # 上传本地 Knowledge 目录
103
+ # 上传本地 Knowledge 文件或目录
103
104
  arm knowledge upload ./my-knowledges/api-doc
104
105
  ```
105
106
 
@@ -113,6 +114,39 @@ arm skill validate ./my-skills/pdf-tool
113
114
  arm skill validate ./my-skills/pdf-tool.zip
114
115
  ```
115
116
 
117
+ ### 8. JSON 输出模式
118
+
119
+ CLI 默认输出 JSON 格式,便于 AI Agent 解析和集成:
120
+
121
+ ```bash
122
+ # 查看当前输出模式
123
+ arm output
124
+
125
+ # 切换为文本模式(人类可读)
126
+ arm output text
127
+
128
+ # 切换为 JSON 模式(默认)
129
+ arm output json
130
+
131
+ # 所有命令都支持 --json 参数强制输出 JSON
132
+ arm agent create my-agent --json
133
+ arm skill ls --json
134
+ ```
135
+
136
+ JSON 输出格式:
137
+
138
+ ```json
139
+ {
140
+ "success": true,
141
+ "data": { ... }
142
+ }
143
+ // 或
144
+ {
145
+ "success": false,
146
+ "error": { "code": "ERROR_CODE", "message": "错误信息" }
147
+ }
148
+ ```
149
+
116
150
  ## 命令参考
117
151
 
118
152
  ### 认证命令
@@ -131,7 +165,7 @@ arm skill validate ./my-skills/pdf-tool.zip
131
165
  | `arm skill search <keyword>` | 搜索 Skills |
132
166
  | `arm skill info <name>` | 查看 Skill 详情 |
133
167
  | `arm skill download <name> [dir]` | 下载 Skill 到指定目录 |
134
- | `arm skill upload <path>` | 上传本地 Skill 目录 |
168
+ | `arm skill upload <path>` | 上传本地 Skill 目录或 ZIP 文件 |
135
169
  | `arm skill my` | 查看我发布的 Skills |
136
170
  | `arm skill delete <name>` | 删除我发布的 Skill |
137
171
  | `arm skill validate <path>` | 验证 Skill 格式(支持目录和 ZIP) |
@@ -144,7 +178,7 @@ arm skill validate ./my-skills/pdf-tool.zip
144
178
  | `arm knowledge search <keyword>` | 搜索 Knowledges |
145
179
  | `arm knowledge info <name>` | 查看 Knowledge 详情 |
146
180
  | `arm knowledge download <name> [dir]` | 下载 Knowledge 到指定目录 |
147
- | `arm knowledge upload <path>` | 上传本地 Knowledge 目录 |
181
+ | `arm knowledge upload <path>` | 上传本地 Knowledge 文件或目录 |
148
182
  | `arm knowledge my` | 查看我发布的 Knowledges |
149
183
  | `arm knowledge delete <name>` | 删除我发布的 Knowledge |
150
184
 
@@ -164,6 +198,26 @@ arm skill validate ./my-skills/pdf-tool.zip
164
198
  | `arm server` | 显示当前服务端 |
165
199
  | `arm server set <url>` | 设置默认服务端 |
166
200
 
201
+ ### 输出模式命令
202
+
203
+ | 命令 | 说明 |
204
+ |------|------|
205
+ | `arm output` | 查看当前输出模式 |
206
+ | `arm output json` | 设置为 JSON 模式(默认) |
207
+ | `arm output text` | 设置为文本模式 |
208
+
209
+ ### 输出格式说明
210
+
211
+ 所有命令都支持 `--json` 或 `-j` 参数强制输出 JSON 格式。当默认模式为 `json` 时,所有命令直接返回 JSON。
212
+
213
+ **Agent 命令(创建/更新/删除/绑定/解绑)** 始终支持 `--json`:
214
+ ```bash
215
+ arm agent create my-agent --json
216
+ arm agent update <id> --name=new-name --json
217
+ arm agent delete <id> --json
218
+ arm agent bind <id> --skill=<id> --json
219
+ ```
220
+
167
221
  ## Skill 格式规范
168
222
 
169
223
  上传的 Skill 目录必须包含 `SKILL.md` 文件,且 frontmatter 必须符合以下格式:
@@ -202,10 +256,13 @@ allowed-tools: tool1 tool2 # 可选,空格分隔
202
256
  "id": "user-id",
203
257
  "name": "username",
204
258
  "email": "user@example.com"
205
- }
259
+ },
260
+ "outputMode": "json"
206
261
  }
207
262
  ```
208
263
 
264
+ 其中 `outputMode` 可选值为 `json`(默认)或 `text`。
265
+
209
266
  ## 开发
210
267
 
211
268
  ```bash
package/dist/main.js CHANGED
@@ -40,6 +40,16 @@ class ApiClient {
40
40
  }
41
41
  return res.data;
42
42
  }
43
+ async register(email, password, name) {
44
+ const res = await this.request("/auth/register", {
45
+ method: "POST",
46
+ body: JSON.stringify({ email, password, name })
47
+ });
48
+ if (!res.ok) {
49
+ throw new Error(res.msg);
50
+ }
51
+ return res.data;
52
+ }
43
53
  async me() {
44
54
  const res = await this.request("/auth/me");
45
55
  if (!res.ok) {
@@ -462,6 +472,47 @@ function formatKnowledgeDetail(knowledge) {
462
472
  }
463
473
 
464
474
  // src/cmd/auth.ts
475
+ async function register(name, email, password) {
476
+ const { readline } = await import("readline");
477
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
478
+ const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
479
+ let finalEmail = email;
480
+ let finalPassword = password;
481
+ let finalName = name;
482
+ if (!finalEmail) {
483
+ finalEmail = await ask("请输入邮箱: ");
484
+ }
485
+ if (!finalPassword) {
486
+ finalPassword = await ask("请输入密码: ");
487
+ }
488
+ if (!finalName) {
489
+ finalName = await ask("请输入用户名: ");
490
+ }
491
+ rl.close();
492
+ if (!finalEmail || !finalPassword || !finalName) {
493
+ error("邮箱、密码和用户名都不能为空");
494
+ process.exit(1);
495
+ }
496
+ if (finalPassword.length < 8) {
497
+ error("密码至少需要 8 个字符");
498
+ process.exit(1);
499
+ }
500
+ const config = loadConfig();
501
+ const serverUrl = config?.serverUrl || "http://localhost:3000";
502
+ const client = new ApiClient(serverUrl);
503
+ try {
504
+ const result = await client.register(finalEmail, finalPassword, finalName);
505
+ saveConfig({
506
+ serverUrl,
507
+ token: result.token,
508
+ user: result.user
509
+ });
510
+ success(`注册成功! 欢迎, ${result.user.name}`);
511
+ } catch (err) {
512
+ error(`注册失败: ${err instanceof Error ? err.message : "未知错误"}`);
513
+ process.exit(1);
514
+ }
515
+ }
465
516
  async function login(serverUrl, apiKey) {
466
517
  try {
467
518
  const client = new ApiClient(serverUrl);
@@ -526,7 +577,7 @@ function setOutputMode(mode) {
526
577
  }
527
578
 
528
579
  // src/lib/validate.ts
529
- import { readFileSync as readFileSync2, existsSync as existsSync2, statSync } from "fs";
580
+ import { readFileSync as readFileSync2, existsSync as existsSync2, statSync as statSync2 } from "fs";
530
581
  import { execSync } from "child_process";
531
582
  import { join as join2, extname } from "path";
532
583
  import { mkdtempSync, rmSync } from "fs";
@@ -546,7 +597,7 @@ function validateZip(filePath) {
546
597
  result.errors.push("文件必须是 ZIP 格式");
547
598
  return result;
548
599
  }
549
- const stats = statSync(filePath);
600
+ const stats = statSync2(filePath);
550
601
  if (stats.size === 0) {
551
602
  result.valid = false;
552
603
  result.errors.push("ZIP 文件为空");
@@ -696,9 +747,95 @@ function validateSkillDir(dirPath) {
696
747
  }
697
748
  return result;
698
749
  }
750
+ function validateAgentDir(dirPath) {
751
+ const result = {
752
+ valid: true,
753
+ errors: [],
754
+ warnings: []
755
+ };
756
+ if (!existsSync2(dirPath)) {
757
+ result.valid = false;
758
+ result.errors.push(`目录不存在: ${dirPath}`);
759
+ return result;
760
+ }
761
+ const agentMdPath = join2(dirPath, "AGENT.md");
762
+ if (!existsSync2(agentMdPath)) {
763
+ result.valid = false;
764
+ result.errors.push("目录内缺少 AGENT.md 文件");
765
+ return result;
766
+ }
767
+ try {
768
+ const agentMdContent = readFileSync2(agentMdPath, "utf-8");
769
+ const frontmatterMatch = agentMdContent.match(/^---\n([\s\S]*?)\n---/);
770
+ if (frontmatterMatch) {
771
+ const frontmatter = frontmatterMatch[1];
772
+ result.metadata = {};
773
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
774
+ const versionMatch = frontmatter.match(/version:\s*(.+)/);
775
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
776
+ const skillsMatch = frontmatter.match(/skills:\s*([\s\S]*?)(?=\n\w|---)/);
777
+ const knowledgesMatch = frontmatter.match(/knowledges:\s*([\s\S]*?)(?=\n\w|---)/);
778
+ if (nameMatch) {
779
+ result.metadata.name = nameMatch[1].trim();
780
+ } else {
781
+ result.valid = false;
782
+ result.errors.push("缺少 name 字段");
783
+ }
784
+ if (versionMatch) {
785
+ result.metadata.version = versionMatch[1].trim();
786
+ }
787
+ if (descMatch) {
788
+ result.metadata.description = descMatch[1].trim();
789
+ }
790
+ if (skillsMatch) {
791
+ const skillsLines = skillsMatch[1].split(`
792
+ `).filter((l) => l.trim().startsWith("-"));
793
+ result.metadata.skills = skillsLines.map((l) => l.replace(/^\s*-\s*/, "").trim());
794
+ }
795
+ if (knowledgesMatch) {
796
+ const knowledgesLines = knowledgesMatch[1].split(`
797
+ `).filter((l) => l.trim().startsWith("-"));
798
+ result.metadata.knowledges = knowledgesLines.map((l) => l.replace(/^\s*-\s*/, "").trim());
799
+ }
800
+ const contentAfterFrontmatter = agentMdContent.replace(/^---[\s\S]*?---\n/, "");
801
+ const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n([\s\S]*?)$/);
802
+ if (promptMatch) {
803
+ result.metadata.prompt = promptMatch[1].trim();
804
+ } else if (contentAfterFrontmatter.trim()) {
805
+ result.metadata.prompt = contentAfterFrontmatter.trim();
806
+ }
807
+ } else {
808
+ result.valid = false;
809
+ result.errors.push("AGENT.md 缺少 frontmatter (--- 包裹的 YAML)");
810
+ }
811
+ const skillsDir = join2(dirPath, "skills");
812
+ if (existsSync2(skillsDir) && statSync2(skillsDir).isDirectory()) {
813
+ const skillDirs = execSync(`ls -1 "${skillsDir}"`, { encoding: "utf-8" }).split(`
814
+ `).filter((l) => l.trim() && existsSync2(join2(skillsDir, l)) && statSync2(join2(skillsDir, l)).isDirectory());
815
+ for (const skillDir of skillDirs) {
816
+ const skillMdPath = join2(skillsDir, skillDir, "SKILL.md");
817
+ if (!existsSync2(skillMdPath)) {
818
+ result.warnings.push(`skills/${skillDir} 目录内缺少 SKILL.md 文件`);
819
+ }
820
+ }
821
+ }
822
+ const knowledgesDir = join2(dirPath, "knowledges");
823
+ if (existsSync2(knowledgesDir) && statSync2(knowledgesDir).isDirectory()) {
824
+ const mdFiles = execSync(`ls -1 "${knowledgesDir}"`, { encoding: "utf-8" }).split(`
825
+ `).filter((l) => l.trim().endsWith(".md"));
826
+ if (mdFiles.length === 0) {
827
+ result.warnings.push("knowledges/ 目录内没有 .md 文件");
828
+ }
829
+ }
830
+ } catch (err) {
831
+ result.valid = false;
832
+ result.errors.push(`AGENT.md 读取失败: ${err instanceof Error ? err.message : "未知错误"}`);
833
+ }
834
+ return result;
835
+ }
699
836
 
700
837
  // src/cmd/skill.ts
701
- import { writeFileSync as writeFileSync2, existsSync as existsSync3, statSync as statSync2 } from "fs";
838
+ import { writeFileSync as writeFileSync2, existsSync as existsSync3, statSync as statSync3 } from "fs";
702
839
  import { join as join3, basename, dirname } from "path";
703
840
  import { execSync as execSync2 } from "child_process";
704
841
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
@@ -959,7 +1096,7 @@ async function deleteSkill(name) {
959
1096
  }
960
1097
  }
961
1098
  async function validateSkill(filePath) {
962
- const isDir = existsSync3(filePath) && statSync2(filePath).isDirectory();
1099
+ const isDir = existsSync3(filePath) && statSync3(filePath).isDirectory();
963
1100
  const result = isDir ? validateSkillDir(filePath) : validateZip(filePath);
964
1101
  if (shouldOutputJson()) {
965
1102
  outputJson({
@@ -1411,10 +1548,153 @@ async function downloadAgent(name, outputDir) {
1411
1548
  process.exit(1);
1412
1549
  }
1413
1550
  }
1551
+ async function createAgentFromFolder(folderPath) {
1552
+ const config = loadConfig();
1553
+ if (!config?.token) {
1554
+ if (shouldOutputJson()) {
1555
+ outputJson({ success: false, error: { code: "NOT_LOGGED_IN", message: "未登录,请先运行 arm login" } });
1556
+ process.exit(1);
1557
+ }
1558
+ error("未登录,请先运行 arm login");
1559
+ process.exit(1);
1560
+ }
1561
+ if (!existsSync4(folderPath)) {
1562
+ if (shouldOutputJson()) {
1563
+ outputJson({ success: false, error: { code: "FILE_NOT_FOUND", message: `目录不存在: ${folderPath}` } });
1564
+ process.exit(1);
1565
+ }
1566
+ error(`目录不存在: ${folderPath}`);
1567
+ process.exit(1);
1568
+ }
1569
+ const validation = validateAgentDir(folderPath);
1570
+ if (!validation.valid) {
1571
+ if (shouldOutputJson()) {
1572
+ outputJson({ success: false, error: { code: "VALIDATION_FAILED", message: validation.errors.join(", ") } });
1573
+ process.exit(1);
1574
+ }
1575
+ error(`验证失败: ${validation.errors.join(", ")}`);
1576
+ process.exit(1);
1577
+ }
1578
+ if (shouldOutputJson()) {
1579
+ info("正在解析 Agent 文件夹...");
1580
+ }
1581
+ const metadata = validation.metadata;
1582
+ const client = new ApiClient(config.serverUrl, config.token);
1583
+ const uploadedSkills = [];
1584
+ const uploadedKnowledges = [];
1585
+ const skillsDir = join4(folderPath, "skills");
1586
+ if (existsSync4(skillsDir) && statSync(skillsDir).isDirectory()) {
1587
+ const skillDirs = execSync3(`ls -1 "${skillsDir}"`, { encoding: "utf-8" }).split(`
1588
+ `).filter((l) => l.trim() && existsSync4(join4(skillsDir, l)) && statSync(join4(skillsDir, l)).isDirectory());
1589
+ for (const skillDir of skillDirs) {
1590
+ const skillPath = join4(skillsDir, skillDir);
1591
+ try {
1592
+ const existingSkill = await client.getSkill(skillDir).catch(() => null);
1593
+ if (existingSkill) {
1594
+ if (shouldOutputJson()) {
1595
+ info(`技能 ${skillDir} 已存在,将仅绑定`);
1596
+ }
1597
+ uploadedSkills.push({ name: skillDir, id: existingSkill.id });
1598
+ } else {
1599
+ if (shouldOutputJson()) {
1600
+ info(`上传新技能: ${skillDir}`);
1601
+ }
1602
+ const skillTempDir = mkdtempSync3("/tmp/skill-upload-");
1603
+ const zipPath = join4(skillTempDir, `${skillDir}.zip`);
1604
+ execSync3(`cd "${skillPath}" && zip -r "${zipPath}" . -x ".*"`, { stdio: "pipe" });
1605
+ const uploadedSkill = await client.uploadSkill(zipPath);
1606
+ uploadedSkills.push({ name: skillDir, id: uploadedSkill.id });
1607
+ rmSync3(skillTempDir, { recursive: true, force: true });
1608
+ }
1609
+ } catch (err) {
1610
+ if (shouldOutputJson()) {
1611
+ outputJson({ success: false, error: { code: "SKILL_UPLOAD_FAILED", message: `处理技能 ${skillDir} 失败: ${err instanceof Error ? err.message : "未知错误"}` } });
1612
+ process.exit(1);
1613
+ }
1614
+ error(`处理技能 ${skillDir} 失败: ${err instanceof Error ? err.message : "未知错误"}`);
1615
+ process.exit(1);
1616
+ }
1617
+ }
1618
+ }
1619
+ const knowledgesDir = join4(folderPath, "knowledges");
1620
+ if (existsSync4(knowledgesDir) && statSync(knowledgesDir).isDirectory()) {
1621
+ const mdFiles = execSync3(`ls -1 "${knowledgesDir}"`, { encoding: "utf-8" }).split(`
1622
+ `).filter((l) => l.trim().endsWith(".md"));
1623
+ for (const mdFile of mdFiles) {
1624
+ const mdPath = join4(knowledgesDir, mdFile);
1625
+ const knowledgeName = mdFile.replace(".md", "");
1626
+ try {
1627
+ const existingKnowledge = await client.getKnowledge(knowledgeName).catch(() => null);
1628
+ if (existingKnowledge) {
1629
+ if (shouldOutputJson()) {
1630
+ info(`知识 ${knowledgeName} 已存在,将仅绑定`);
1631
+ }
1632
+ uploadedKnowledges.push({ title: knowledgeName, id: existingKnowledge.id });
1633
+ } else {
1634
+ if (shouldOutputJson()) {
1635
+ info(`上传新知识: ${knowledgeName}`);
1636
+ }
1637
+ const uploadedKnowledge = await client.uploadKnowledge(mdPath);
1638
+ uploadedKnowledges.push({ title: knowledgeName, id: uploadedKnowledge.id });
1639
+ }
1640
+ } catch (err) {
1641
+ if (shouldOutputJson()) {
1642
+ outputJson({ success: false, error: { code: "KNOWLEDGE_UPLOAD_FAILED", message: `处理知识 ${knowledgeName} 失败: ${err instanceof Error ? err.message : "未知错误"}` } });
1643
+ process.exit(1);
1644
+ }
1645
+ error(`处理知识 ${knowledgeName} 失败: ${err instanceof Error ? err.message : "未知错误"}`);
1646
+ process.exit(1);
1647
+ }
1648
+ }
1649
+ }
1650
+ if (shouldOutputJson()) {
1651
+ info(`创建 Agent: ${metadata.name}`);
1652
+ }
1653
+ try {
1654
+ const agent = await client.createAgent({
1655
+ name: metadata.name,
1656
+ description: metadata.description,
1657
+ prompt: metadata.prompt
1658
+ });
1659
+ for (const skill of uploadedSkills) {
1660
+ if (shouldOutputJson()) {
1661
+ info(`绑定技能: ${skill.name}`);
1662
+ }
1663
+ await client.bindSkillToAgent(agent.id, skill.id);
1664
+ }
1665
+ for (const knowledge of uploadedKnowledges) {
1666
+ if (shouldOutputJson()) {
1667
+ info(`绑定知识: ${knowledge.title}`);
1668
+ }
1669
+ await client.bindKnowledgeToAgent(agent.id, knowledge.id);
1670
+ }
1671
+ if (shouldOutputJson()) {
1672
+ outputJson({
1673
+ success: true,
1674
+ data: {
1675
+ id: agent.id,
1676
+ name: agent.name,
1677
+ skillsCount: uploadedSkills.length,
1678
+ knowledgesCount: uploadedKnowledges.length
1679
+ }
1680
+ });
1681
+ return;
1682
+ }
1683
+ success(`Agent "${metadata.name}" 创建成功 (ID: ${agent.id})`);
1684
+ success(`已绑定 ${uploadedSkills.length} 个技能和 ${uploadedKnowledges.length} 个知识`);
1685
+ } catch (err) {
1686
+ if (shouldOutputJson()) {
1687
+ outputJson({ success: false, error: { code: "CREATE_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
1688
+ process.exit(1);
1689
+ }
1690
+ error(`创建失败: ${err instanceof Error ? err.message : "未知错误"}`);
1691
+ process.exit(1);
1692
+ }
1693
+ }
1414
1694
 
1415
1695
  // src/cmd/knowledge.ts
1416
- import { writeFileSync as writeFileSync4, existsSync as existsSync5, statSync as statSync3 } from "fs";
1417
- import { join as join5, basename as basename2, dirname as dirname2 } from "path";
1696
+ import { writeFileSync as writeFileSync4, existsSync as existsSync5, statSync as statSync4 } from "fs";
1697
+ import { join as join5, basename as basename3, dirname as dirname2 } from "path";
1418
1698
  import { execSync as execSync4 } from "child_process";
1419
1699
  import { mkdtempSync as mkdtempSync4, rmSync as rmSync4 } from "fs";
1420
1700
  async function listKnowledge() {
@@ -1568,12 +1848,12 @@ async function uploadKnowledge(filePath) {
1568
1848
  error(`上传失败: 目录不存在: ${filePath}`);
1569
1849
  process.exit(1);
1570
1850
  }
1571
- const knowledgeName = basename2(filePath);
1851
+ const knowledgeName = basename3(filePath);
1572
1852
  const tempDir = mkdtempSync4("/tmp/knowledge-upload-");
1573
1853
  const zipPath = join5(tempDir, `${knowledgeName}.zip`);
1574
1854
  try {
1575
- if (statSync3(filePath).isDirectory()) {
1576
- execSync4(`cd "${dirname2(filePath)}" && zip -r "${zipPath}" "${basename2(filePath)}" -x ".*"`, { stdio: "pipe" });
1855
+ if (statSync4(filePath).isDirectory()) {
1856
+ execSync4(`cd "${dirname2(filePath)}" && zip -r "${zipPath}" "${basename3(filePath)}" -x ".*"`, { stdio: "pipe" });
1577
1857
  } else {
1578
1858
  execSync4(`cp "${filePath}" "${zipPath}"`, { stdio: "pipe" });
1579
1859
  }
@@ -1587,6 +1867,9 @@ async function uploadKnowledge(filePath) {
1587
1867
  return;
1588
1868
  }
1589
1869
  success(`上传成功! Knowledge: ${knowledge.name}`);
1870
+ if (knowledge.shareUrl) {
1871
+ info(`分享链接: ${knowledge.shareUrl}`);
1872
+ }
1590
1873
  } catch (err) {
1591
1874
  if (shouldOutputJson()) {
1592
1875
  outputJson({ success: false, error: { code: "UPLOAD_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
@@ -1678,6 +1961,21 @@ var command = args[0];
1678
1961
  var subCommand = args[1];
1679
1962
  async function main() {
1680
1963
  switch (command) {
1964
+ case "register":
1965
+ if (args[1] && args[1].startsWith("--")) {
1966
+ const options = {};
1967
+ for (let i = 1;i < args.length; i++) {
1968
+ const arg = args[i];
1969
+ if (arg.startsWith("--")) {
1970
+ const [key, value] = arg.slice(2).split("=");
1971
+ options[key] = value;
1972
+ }
1973
+ }
1974
+ await register(options.name, options.email, options.password);
1975
+ } else {
1976
+ await register(args[1], args[2], args[3]);
1977
+ }
1978
+ break;
1681
1979
  case "login":
1682
1980
  if (!args[1] || !args[2]) {
1683
1981
  console.error("用法: arm login <server-url> <api-key>");
@@ -1859,7 +2157,7 @@ async function main() {
1859
2157
  break;
1860
2158
  case "create":
1861
2159
  if (!args[2]) {
1862
- console.error(`用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config='{...}'] [--knowledge-config='{...}'] [--json]`);
2160
+ console.error(`用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config='{...}'] [--knowledge-config='{...}'] [--from=<folder-path>] [--json]`);
1863
2161
  process.exit(1);
1864
2162
  }
1865
2163
  {
@@ -1869,6 +2167,7 @@ async function main() {
1869
2167
  const knowledges = [];
1870
2168
  const skillConfigs = [];
1871
2169
  const knowledgeConfigs = [];
2170
+ let fromFolder;
1872
2171
  for (let i = 3;i < args.length; i++) {
1873
2172
  const arg = args[i];
1874
2173
  if (arg.startsWith("--description=")) {
@@ -1885,17 +2184,23 @@ async function main() {
1885
2184
  skillConfigs.push(arg.split("=").slice(1).join("="));
1886
2185
  } else if (arg.startsWith("--knowledge-config=")) {
1887
2186
  knowledgeConfigs.push(arg.split("=").slice(1).join("="));
2187
+ } else if (arg.startsWith("--from=")) {
2188
+ fromFolder = arg.split("=").slice(1).join("=");
1888
2189
  }
1889
2190
  }
1890
- await createAgent(name, {
1891
- description: options.description,
1892
- prompt: options.prompt,
1893
- avatar: options.avatar,
1894
- skills,
1895
- knowledges,
1896
- skillConfigs,
1897
- knowledgeConfigs
1898
- });
2191
+ if (fromFolder) {
2192
+ await createAgentFromFolder(fromFolder);
2193
+ } else {
2194
+ await createAgent(name, {
2195
+ description: options.description,
2196
+ prompt: options.prompt,
2197
+ avatar: options.avatar,
2198
+ skills,
2199
+ knowledges,
2200
+ skillConfigs,
2201
+ knowledgeConfigs
2202
+ });
2203
+ }
1899
2204
  }
1900
2205
  break;
1901
2206
  case "update":
@@ -2004,6 +2309,7 @@ async function main() {
2004
2309
  arm agent info <name> 查看 Agent 详情
2005
2310
  arm agent download <name> [dir] 下载 Agent
2006
2311
  arm agent create <name> 创建 Agent (--description, --prompt, --avatar, --skill, --knowledge)
2312
+ arm agent create --from=<folder> 从本地文件夹创建 Agent
2007
2313
  arm agent update <id> 更新 Agent (--name, --description, --prompt, --avatar, --status)
2008
2314
  arm agent delete <id> 删除 Agent
2009
2315
  arm agent bind <id> --skill=<id> 绑定 Skill 到 Agent
@@ -2019,6 +2325,7 @@ async function main() {
2019
2325
  Agent Resource Management (arm)
2020
2326
 
2021
2327
  用法:
2328
+ arm register [--name=<name>] [--email=<email>] [--password=<password>] 注册 (交互式或参数)
2022
2329
  arm login <server-url> <api-key> 登录
2023
2330
  arm logout 登出
2024
2331
  arm output [json|text] 设置/查看输出模式 (默认json)
package/dist/test.md ADDED
@@ -0,0 +1,3 @@
1
+ # test
2
+ - hi
3
+ - huw
package/dist/test2.md ADDED
@@ -0,0 +1,3 @@
1
+ # test
2
+ - hi
3
+ - huw
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-resource-management",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "arm": "./dist/main.js"
package/src/cmd/agent.ts CHANGED
@@ -2,8 +2,9 @@ import { ApiClient } from '../lib/client';
2
2
  import { loadConfig } from '../lib/storage';
3
3
  import { formatAgent, formatAgentDetail, success, error, info } from '../lib/formatter';
4
4
  import { shouldOutputJson, outputJson } from '../lib/output';
5
- import { writeFileSync, existsSync } from 'fs';
6
- import { join } from 'path';
5
+ import { validateAgentDir } from '../lib/validate';
6
+ import { writeFileSync, existsSync, readFileSync } from 'fs';
7
+ import { join, basename } from 'path';
7
8
  import { execSync } from 'child_process';
8
9
  import { mkdtempSync, rmSync } from 'fs';
9
10
 
@@ -463,4 +464,164 @@ export async function downloadAgent(name: string, outputDir?: string): Promise<v
463
464
  error(`下载失败: ${err instanceof Error ? err.message : '未知错误'}`);
464
465
  process.exit(1);
465
466
  }
467
+ }
468
+
469
+ export async function createAgentFromFolder(folderPath: string): Promise<void> {
470
+ const config = loadConfig();
471
+ if (!config?.token) {
472
+ if (shouldOutputJson()) {
473
+ outputJson({ success: false, error: { code: 'NOT_LOGGED_IN', message: '未登录,请先运行 arm login' } });
474
+ process.exit(1);
475
+ }
476
+ error('未登录,请先运行 arm login');
477
+ process.exit(1);
478
+ }
479
+
480
+ if (!existsSync(folderPath)) {
481
+ if (shouldOutputJson()) {
482
+ outputJson({ success: false, error: { code: 'FILE_NOT_FOUND', message: `目录不存在: ${folderPath}` } });
483
+ process.exit(1);
484
+ }
485
+ error(`目录不存在: ${folderPath}`);
486
+ process.exit(1);
487
+ }
488
+
489
+ const validation = validateAgentDir(folderPath);
490
+ if (!validation.valid) {
491
+ if (shouldOutputJson()) {
492
+ outputJson({ success: false, error: { code: 'VALIDATION_FAILED', message: validation.errors.join(', ') } });
493
+ process.exit(1);
494
+ }
495
+ error(`验证失败: ${validation.errors.join(', ')}`);
496
+ process.exit(1);
497
+ }
498
+
499
+ if (shouldOutputJson()) {
500
+ info('正在解析 Agent 文件夹...');
501
+ }
502
+
503
+ const metadata = validation.metadata!;
504
+ const client = new ApiClient(config.serverUrl, config.token);
505
+
506
+ const uploadedSkills: { name: string; id: string }[] = [];
507
+ const uploadedKnowledges: { title: string; id: string }[] = [];
508
+
509
+ const skillsDir = join(folderPath, 'skills');
510
+ if (existsSync(skillsDir) && statSync(skillsDir).isDirectory()) {
511
+ const skillDirs = execSync(`ls -1 "${skillsDir}"`, { encoding: 'utf-8' })
512
+ .split('\n')
513
+ .filter(l => l.trim() && existsSync(join(skillsDir, l)) && statSync(join(skillsDir, l)).isDirectory());
514
+
515
+ for (const skillDir of skillDirs) {
516
+ const skillPath = join(skillsDir, skillDir);
517
+ try {
518
+ const existingSkill = await client.getSkill(skillDir).catch(() => null);
519
+ if (existingSkill) {
520
+ if (shouldOutputJson()) {
521
+ info(`技能 ${skillDir} 已存在,将仅绑定`);
522
+ }
523
+ uploadedSkills.push({ name: skillDir, id: existingSkill.id });
524
+ } else {
525
+ if (shouldOutputJson()) {
526
+ info(`上传新技能: ${skillDir}`);
527
+ }
528
+ const skillTempDir = mkdtempSync('/tmp/skill-upload-');
529
+ const zipPath = join(skillTempDir, `${skillDir}.zip`);
530
+ execSync(`cd "${skillPath}" && zip -r "${zipPath}" . -x ".*"`, { stdio: 'pipe' });
531
+ const uploadedSkill = await client.uploadSkill(zipPath);
532
+ uploadedSkills.push({ name: skillDir, id: uploadedSkill.id });
533
+ rmSync(skillTempDir, { recursive: true, force: true });
534
+ }
535
+ } catch (err) {
536
+ if (shouldOutputJson()) {
537
+ outputJson({ success: false, error: { code: 'SKILL_UPLOAD_FAILED', message: `处理技能 ${skillDir} 失败: ${err instanceof Error ? err.message : '未知错误'}` } });
538
+ process.exit(1);
539
+ }
540
+ error(`处理技能 ${skillDir} 失败: ${err instanceof Error ? err.message : '未知错误'}`);
541
+ process.exit(1);
542
+ }
543
+ }
544
+ }
545
+
546
+ const knowledgesDir = join(folderPath, 'knowledges');
547
+ if (existsSync(knowledgesDir) && statSync(knowledgesDir).isDirectory()) {
548
+ const mdFiles = execSync(`ls -1 "${knowledgesDir}"`, { encoding: 'utf-8' })
549
+ .split('\n')
550
+ .filter(l => l.trim().endsWith('.md'));
551
+
552
+ for (const mdFile of mdFiles) {
553
+ const mdPath = join(knowledgesDir, mdFile);
554
+ const knowledgeName = mdFile.replace('.md', '');
555
+ try {
556
+ const existingKnowledge = await client.getKnowledge(knowledgeName).catch(() => null);
557
+ if (existingKnowledge) {
558
+ if (shouldOutputJson()) {
559
+ info(`知识 ${knowledgeName} 已存在,将仅绑定`);
560
+ }
561
+ uploadedKnowledges.push({ title: knowledgeName, id: existingKnowledge.id });
562
+ } else {
563
+ if (shouldOutputJson()) {
564
+ info(`上传新知识: ${knowledgeName}`);
565
+ }
566
+ const uploadedKnowledge = await client.uploadKnowledge(mdPath);
567
+ uploadedKnowledges.push({ title: knowledgeName, id: uploadedKnowledge.id });
568
+ }
569
+ } catch (err) {
570
+ if (shouldOutputJson()) {
571
+ outputJson({ success: false, error: { code: 'KNOWLEDGE_UPLOAD_FAILED', message: `处理知识 ${knowledgeName} 失败: ${err instanceof Error ? err.message : '未知错误'}` } });
572
+ process.exit(1);
573
+ }
574
+ error(`处理知识 ${knowledgeName} 失败: ${err instanceof Error ? err.message : '未知错误'}`);
575
+ process.exit(1);
576
+ }
577
+ }
578
+ }
579
+
580
+ if (shouldOutputJson()) {
581
+ info(`创建 Agent: ${metadata.name}`);
582
+ }
583
+
584
+ try {
585
+ const agent = await client.createAgent({
586
+ name: metadata.name!,
587
+ description: metadata.description,
588
+ prompt: metadata.prompt,
589
+ });
590
+
591
+ for (const skill of uploadedSkills) {
592
+ if (shouldOutputJson()) {
593
+ info(`绑定技能: ${skill.name}`);
594
+ }
595
+ await client.bindSkillToAgent(agent.id, skill.id);
596
+ }
597
+
598
+ for (const knowledge of uploadedKnowledges) {
599
+ if (shouldOutputJson()) {
600
+ info(`绑定知识: ${knowledge.title}`);
601
+ }
602
+ await client.bindKnowledgeToAgent(agent.id, knowledge.id);
603
+ }
604
+
605
+ if (shouldOutputJson()) {
606
+ outputJson({
607
+ success: true,
608
+ data: {
609
+ id: agent.id,
610
+ name: agent.name,
611
+ skillsCount: uploadedSkills.length,
612
+ knowledgesCount: uploadedKnowledges.length,
613
+ },
614
+ });
615
+ return;
616
+ }
617
+ success(`Agent "${metadata.name}" 创建成功 (ID: ${agent.id})`);
618
+ success(`已绑定 ${uploadedSkills.length} 个技能和 ${uploadedKnowledges.length} 个知识`);
619
+ } catch (err) {
620
+ if (shouldOutputJson()) {
621
+ outputJson({ success: false, error: { code: 'CREATE_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
622
+ process.exit(1);
623
+ }
624
+ error(`创建失败: ${err instanceof Error ? err.message : '未知错误'}`);
625
+ process.exit(1);
626
+ }
466
627
  }
package/src/cmd/auth.ts CHANGED
@@ -2,6 +2,56 @@ import { ApiClient } from '../lib/client';
2
2
  import { loadConfig, saveConfig } from '../lib/storage';
3
3
  import { success, error, info } from '../lib/formatter';
4
4
 
5
+ export async function register(name?: string, email?: string, password?: string): Promise<void> {
6
+ const { readline } = await import('readline');
7
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
8
+
9
+ const ask = (question: string): Promise<string> =>
10
+ new Promise((resolve) => rl.question(question, resolve));
11
+
12
+ let finalEmail = email;
13
+ let finalPassword = password;
14
+ let finalName = name;
15
+
16
+ if (!finalEmail) {
17
+ finalEmail = await ask('请输入邮箱: ');
18
+ }
19
+ if (!finalPassword) {
20
+ finalPassword = await ask('请输入密码: ');
21
+ }
22
+ if (!finalName) {
23
+ finalName = await ask('请输入用户名: ');
24
+ }
25
+ rl.close();
26
+
27
+ if (!finalEmail || !finalPassword || !finalName) {
28
+ error('邮箱、密码和用户名都不能为空');
29
+ process.exit(1);
30
+ }
31
+
32
+ if (finalPassword.length < 8) {
33
+ error('密码至少需要 8 个字符');
34
+ process.exit(1);
35
+ }
36
+
37
+ const config = loadConfig();
38
+ const serverUrl = config?.serverUrl || 'http://localhost:3000';
39
+ const client = new ApiClient(serverUrl);
40
+
41
+ try {
42
+ const result = await client.register(finalEmail, finalPassword, finalName);
43
+ saveConfig({
44
+ serverUrl,
45
+ token: result.token,
46
+ user: result.user,
47
+ });
48
+ success(`注册成功! 欢迎, ${result.user.name}`);
49
+ } catch (err) {
50
+ error(`注册失败: ${err instanceof Error ? err.message : '未知错误'}`);
51
+ process.exit(1);
52
+ }
53
+ }
54
+
5
55
  export async function login(serverUrl: string, apiKey: string): Promise<void> {
6
56
  try {
7
57
  const client = new ApiClient(serverUrl);
@@ -185,6 +185,9 @@ export async function uploadKnowledge(filePath: string): Promise<void> {
185
185
  return;
186
186
  }
187
187
  success(`上传成功! Knowledge: ${knowledge.name}`);
188
+ if (knowledge.shareUrl) {
189
+ info(`分享链接: ${knowledge.shareUrl}`);
190
+ }
188
191
  } catch (err) {
189
192
  if (shouldOutputJson()) {
190
193
  outputJson({ success: false, error: { code: 'UPLOAD_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
package/src/lib/client.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { User, Skill, SkillListResponse, ApiResponse, LoginResponse, Agent, AgentListResponse } from '@pkg/types/skill';
1
+ import type { User, Skill, SkillListResponse, ApiResponse, LoginResponse, RegisterResponse, Agent, AgentListResponse } from '@pkg/types/skill';
2
2
 
3
3
  export class ApiClient {
4
4
  private token: string | null = null;
@@ -49,6 +49,17 @@ export class ApiClient {
49
49
  return res.data;
50
50
  }
51
51
 
52
+ async register(email: string, password: string, name: string): Promise<RegisterResponse> {
53
+ const res = await this.request<RegisterResponse>('/auth/register', {
54
+ method: 'POST',
55
+ body: JSON.stringify({ email, password, name }),
56
+ });
57
+ if (!res.ok) {
58
+ throw new Error(res.msg);
59
+ }
60
+ return res.data;
61
+ }
62
+
52
63
  async me(): Promise<User> {
53
64
  const res = await this.request<User>('/auth/me');
54
65
  if (!res.ok) {
@@ -203,7 +203,124 @@ export function validateSkillDir(dirPath: string): ValidationResult {
203
203
  result.errors.push('SKILL.md 缺少 frontmatter (--- 包裹的 YAML)');
204
204
  }
205
205
  } catch (err) {
206
- result.warnings.push(`SKILL.md 读取失败: ${err instanceof Error ? err.message : '未知错误'}`);
206
+ result.warnings.push(`SKILL.md 读取失败: ${err instanceof Error ? err.message : '未知错误'}`);
207
+ }
208
+
209
+ return result;
210
+ }
211
+
212
+ export interface AgentValidationResult {
213
+ valid: boolean;
214
+ errors: string[];
215
+ warnings: string[];
216
+ metadata?: {
217
+ name?: string;
218
+ version?: string;
219
+ description?: string;
220
+ prompt?: string;
221
+ skills?: string[];
222
+ knowledges?: string[];
223
+ };
224
+ }
225
+
226
+ export function validateAgentDir(dirPath: string): AgentValidationResult {
227
+ const result: AgentValidationResult = {
228
+ valid: true,
229
+ errors: [],
230
+ warnings: [],
231
+ };
232
+
233
+ if (!existsSync(dirPath)) {
234
+ result.valid = false;
235
+ result.errors.push(`目录不存在: ${dirPath}`);
236
+ return result;
237
+ }
238
+
239
+ const agentMdPath = join(dirPath, 'AGENT.md');
240
+ if (!existsSync(agentMdPath)) {
241
+ result.valid = false;
242
+ result.errors.push('目录内缺少 AGENT.md 文件');
243
+ return result;
244
+ }
245
+
246
+ try {
247
+ const agentMdContent = readFileSync(agentMdPath, 'utf-8');
248
+ const frontmatterMatch = agentMdContent.match(/^---\n([\s\S]*?)\n---/);
249
+
250
+ if (frontmatterMatch) {
251
+ const frontmatter = frontmatterMatch[1];
252
+ result.metadata = {};
253
+
254
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
255
+ const versionMatch = frontmatter.match(/version:\s*(.+)/);
256
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
257
+ const skillsMatch = frontmatter.match(/skills:\s*([\s\S]*?)(?=\n\w|---)/);
258
+ const knowledgesMatch = frontmatter.match(/knowledges:\s*([\s\S]*?)(?=\n\w|---)/);
259
+
260
+ if (nameMatch) {
261
+ result.metadata.name = nameMatch[1].trim();
262
+ } else {
263
+ result.valid = false;
264
+ result.errors.push('缺少 name 字段');
265
+ }
266
+
267
+ if (versionMatch) {
268
+ result.metadata.version = versionMatch[1].trim();
269
+ }
270
+
271
+ if (descMatch) {
272
+ result.metadata.description = descMatch[1].trim();
273
+ }
274
+
275
+ if (skillsMatch) {
276
+ const skillsLines = skillsMatch[1].split('\n').filter(l => l.trim().startsWith('-'));
277
+ result.metadata.skills = skillsLines.map(l => l.replace(/^\s*-\s*/, '').trim());
278
+ }
279
+
280
+ if (knowledgesMatch) {
281
+ const knowledgesLines = knowledgesMatch[1].split('\n').filter(l => l.trim().startsWith('-'));
282
+ result.metadata.knowledges = knowledgesLines.map(l => l.replace(/^\s*-\s*/, '').trim());
283
+ }
284
+
285
+ const contentAfterFrontmatter = agentMdContent.replace(/^---[\s\S]*?---\n/, '');
286
+ const promptMatch = contentAfterFrontmatter.match(/#\s*System\s*Prompt\n([\s\S]*?)$/);
287
+ if (promptMatch) {
288
+ result.metadata.prompt = promptMatch[1].trim();
289
+ } else if (contentAfterFrontmatter.trim()) {
290
+ result.metadata.prompt = contentAfterFrontmatter.trim();
291
+ }
292
+ } else {
293
+ result.valid = false;
294
+ result.errors.push('AGENT.md 缺少 frontmatter (--- 包裹的 YAML)');
295
+ }
296
+
297
+ const skillsDir = join(dirPath, 'skills');
298
+ if (existsSync(skillsDir) && statSync(skillsDir).isDirectory()) {
299
+ const skillDirs = execSync(`ls -1 "${skillsDir}"`, { encoding: 'utf-8' })
300
+ .split('\n')
301
+ .filter(l => l.trim() && existsSync(join(skillsDir, l)) && statSync(join(skillsDir, l)).isDirectory());
302
+
303
+ for (const skillDir of skillDirs) {
304
+ const skillMdPath = join(skillsDir, skillDir, 'SKILL.md');
305
+ if (!existsSync(skillMdPath)) {
306
+ result.warnings.push(`skills/${skillDir} 目录内缺少 SKILL.md 文件`);
307
+ }
308
+ }
309
+ }
310
+
311
+ const knowledgesDir = join(dirPath, 'knowledges');
312
+ if (existsSync(knowledgesDir) && statSync(knowledgesDir).isDirectory()) {
313
+ const mdFiles = execSync(`ls -1 "${knowledgesDir}"`, { encoding: 'utf-8' })
314
+ .split('\n')
315
+ .filter(l => l.trim().endsWith('.md'));
316
+
317
+ if (mdFiles.length === 0) {
318
+ result.warnings.push('knowledges/ 目录内没有 .md 文件');
319
+ }
320
+ }
321
+ } catch (err) {
322
+ result.valid = false;
323
+ result.errors.push(`AGENT.md 读取失败: ${err instanceof Error ? err.message : '未知错误'}`);
207
324
  }
208
325
 
209
326
  return result;
package/src/main.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { login, logout, getCurrentUser } from './cmd/auth';
1
+ import { login, logout, getCurrentUser, register } from './cmd/auth';
2
2
  import { listSkills, searchSkills, infoSkill, downloadSkill, uploadSkill, mySkills, deleteSkill, validateSkill } from './cmd/skill';
3
- import { listAgents, searchAgents, infoAgent, downloadAgent, createAgent, updateAgent, deleteAgent, bindSkill, unbindSkill, bindKnowledge, unbindKnowledge } from './cmd/agent';
3
+ import { listAgents, searchAgents, infoAgent, downloadAgent, createAgent, updateAgent, deleteAgent, bindSkill, unbindSkill, bindKnowledge, unbindKnowledge, createAgentFromFolder } from './cmd/agent';
4
4
  import { listKnowledge, searchKnowledge, infoKnowledge, downloadKnowledge, uploadKnowledge, myKnowledge, deleteKnowledge } from './cmd/knowledge';
5
5
  import { showServer, setServer } from './cmd/server';
6
6
  import { getOutputMode, setOutputMode } from './lib/output';
@@ -11,6 +11,22 @@ const subCommand = args[1];
11
11
 
12
12
  async function main() {
13
13
  switch (command) {
14
+ case 'register':
15
+ if (args[1] && args[1].startsWith('--')) {
16
+ const options: Record<string, string | undefined> = {};
17
+ for (let i = 1; i < args.length; i++) {
18
+ const arg = args[i];
19
+ if (arg.startsWith('--')) {
20
+ const [key, value] = arg.slice(2).split('=');
21
+ options[key] = value;
22
+ }
23
+ }
24
+ await register(options.name, options.email, options.password);
25
+ } else {
26
+ await register(args[1], args[2], args[3]);
27
+ }
28
+ break;
29
+
14
30
  case 'login':
15
31
  if (!args[1] || !args[2]) {
16
32
  console.error('用法: arm login <server-url> <api-key>');
@@ -199,7 +215,7 @@ async function main() {
199
215
  break;
200
216
  case 'create':
201
217
  if (!args[2]) {
202
- console.error('用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config=\'{...}\'] [--knowledge-config=\'{...}\'] [--json]');
218
+ console.error('用法: arm agent create <name> [--description="..."] [--prompt="..."] [--avatar="..."] [--skill=id] [--knowledge=id] [--skill-config=\'{...}\'] [--knowledge-config=\'{...}\'] [--from=<folder-path>] [--json]');
203
219
  process.exit(1);
204
220
  }
205
221
  {
@@ -209,6 +225,7 @@ async function main() {
209
225
  const knowledges: string[] = [];
210
226
  const skillConfigs: string[] = [];
211
227
  const knowledgeConfigs: string[] = [];
228
+ let fromFolder: string | undefined;
212
229
 
213
230
  for (let i = 3; i < args.length; i++) {
214
231
  const arg = args[i];
@@ -226,18 +243,24 @@ async function main() {
226
243
  skillConfigs.push(arg.split('=').slice(1).join('='));
227
244
  } else if (arg.startsWith('--knowledge-config=')) {
228
245
  knowledgeConfigs.push(arg.split('=').slice(1).join('='));
246
+ } else if (arg.startsWith('--from=')) {
247
+ fromFolder = arg.split('=').slice(1).join('=');
229
248
  }
230
249
  }
231
250
 
232
- await createAgent(name, {
233
- description: options.description,
234
- prompt: options.prompt,
235
- avatar: options.avatar,
236
- skills,
237
- knowledges,
238
- skillConfigs,
239
- knowledgeConfigs,
240
- });
251
+ if (fromFolder) {
252
+ await createAgentFromFolder(fromFolder);
253
+ } else {
254
+ await createAgent(name, {
255
+ description: options.description,
256
+ prompt: options.prompt,
257
+ avatar: options.avatar,
258
+ skills,
259
+ knowledges,
260
+ skillConfigs,
261
+ knowledgeConfigs,
262
+ });
263
+ }
241
264
  }
242
265
  break;
243
266
  case 'update':
@@ -352,6 +375,7 @@ async function main() {
352
375
  arm agent info <name> 查看 Agent 详情
353
376
  arm agent download <name> [dir] 下载 Agent
354
377
  arm agent create <name> 创建 Agent (--description, --prompt, --avatar, --skill, --knowledge)
378
+ arm agent create --from=<folder> 从本地文件夹创建 Agent
355
379
  arm agent update <id> 更新 Agent (--name, --description, --prompt, --avatar, --status)
356
380
  arm agent delete <id> 删除 Agent
357
381
  arm agent bind <id> --skill=<id> 绑定 Skill 到 Agent
@@ -368,6 +392,7 @@ async function main() {
368
392
  Agent Resource Management (arm)
369
393
 
370
394
  用法:
395
+ arm register [--name=<name>] [--email=<email>] [--password=<password>] 注册 (交互式或参数)
371
396
  arm login <server-url> <api-key> 登录
372
397
  arm logout 登出
373
398
  arm output [json|text] 设置/查看输出模式 (默认json)