agent-resource-management 2.1.3 → 2.1.5
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/dist/main.js +346 -47
- package/package.json +1 -1
- package/src/cmd/agent.ts +320 -19
- package/src/lib/client.ts +43 -8
- package/src/main.ts +58 -10
- package/dist/test.md +0 -3
- package/dist/test2.md +0 -3
package/dist/main.js
CHANGED
|
@@ -268,34 +268,42 @@ class ApiClient {
|
|
|
268
268
|
throw new Error(res.msg);
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
|
-
async bindSkillToAgent(agentId, skillId, config) {
|
|
271
|
+
async bindSkillToAgent(agentId, skillId, version, config) {
|
|
272
272
|
const res = await this.request(`/agents/${agentId}/skills`, {
|
|
273
273
|
method: "POST",
|
|
274
|
-
body: JSON.stringify({ skillId, config })
|
|
274
|
+
body: JSON.stringify({ skillId, version, config })
|
|
275
275
|
});
|
|
276
276
|
if (!res.ok) {
|
|
277
277
|
throw new Error(res.msg);
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
|
-
async unbindSkillFromAgent(agentId, skillId) {
|
|
281
|
-
|
|
280
|
+
async unbindSkillFromAgent(agentId, skillId, version) {
|
|
281
|
+
let path = `/agents/${agentId}/skills?skillId=${skillId}`;
|
|
282
|
+
if (version) {
|
|
283
|
+
path += `&version=${version}`;
|
|
284
|
+
}
|
|
285
|
+
const res = await this.request(path, {
|
|
282
286
|
method: "DELETE"
|
|
283
287
|
});
|
|
284
288
|
if (!res.ok) {
|
|
285
289
|
throw new Error(res.msg);
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
|
-
async bindKnowledgeToAgent(agentId, knowledgeId, retrievalConfig) {
|
|
292
|
+
async bindKnowledgeToAgent(agentId, knowledgeId, version, retrievalConfig) {
|
|
289
293
|
const res = await this.request(`/agents/${agentId}/knowledges`, {
|
|
290
294
|
method: "POST",
|
|
291
|
-
body: JSON.stringify({ knowledgeId, retrievalConfig })
|
|
295
|
+
body: JSON.stringify({ knowledgeId, version, retrievalConfig })
|
|
292
296
|
});
|
|
293
297
|
if (!res.ok) {
|
|
294
298
|
throw new Error(res.msg);
|
|
295
299
|
}
|
|
296
300
|
}
|
|
297
|
-
async unbindKnowledgeFromAgent(agentId, knowledgeId) {
|
|
298
|
-
|
|
301
|
+
async unbindKnowledgeFromAgent(agentId, knowledgeId, version) {
|
|
302
|
+
let path = `/agents/${agentId}/knowledges?knowledgeId=${knowledgeId}`;
|
|
303
|
+
if (version) {
|
|
304
|
+
path += `&version=${version}`;
|
|
305
|
+
}
|
|
306
|
+
const res = await this.request(path, {
|
|
299
307
|
method: "DELETE"
|
|
300
308
|
});
|
|
301
309
|
if (!res.ok) {
|
|
@@ -306,6 +314,13 @@ class ApiClient {
|
|
|
306
314
|
const result = await this.listAgents(name, 1, 1);
|
|
307
315
|
return result.agents.find((a) => a.name === name) || null;
|
|
308
316
|
}
|
|
317
|
+
async getAgentBindingsHistory(agentId) {
|
|
318
|
+
const res = await this.request(`/agents/${agentId}/bindings/history`);
|
|
319
|
+
if (!res.ok) {
|
|
320
|
+
throw new Error(res.msg);
|
|
321
|
+
}
|
|
322
|
+
return res.data;
|
|
323
|
+
}
|
|
309
324
|
}
|
|
310
325
|
|
|
311
326
|
// src/lib/storage.ts
|
|
@@ -577,7 +592,7 @@ function setOutputMode(mode) {
|
|
|
577
592
|
}
|
|
578
593
|
|
|
579
594
|
// src/lib/validate.ts
|
|
580
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2, statSync
|
|
595
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, statSync } from "fs";
|
|
581
596
|
import { execSync } from "child_process";
|
|
582
597
|
import { join as join2, extname } from "path";
|
|
583
598
|
import { mkdtempSync, rmSync } from "fs";
|
|
@@ -597,7 +612,7 @@ function validateZip(filePath) {
|
|
|
597
612
|
result.errors.push("文件必须是 ZIP 格式");
|
|
598
613
|
return result;
|
|
599
614
|
}
|
|
600
|
-
const stats =
|
|
615
|
+
const stats = statSync(filePath);
|
|
601
616
|
if (stats.size === 0) {
|
|
602
617
|
result.valid = false;
|
|
603
618
|
result.errors.push("ZIP 文件为空");
|
|
@@ -809,9 +824,9 @@ function validateAgentDir(dirPath) {
|
|
|
809
824
|
result.errors.push("AGENT.md 缺少 frontmatter (--- 包裹的 YAML)");
|
|
810
825
|
}
|
|
811
826
|
const skillsDir = join2(dirPath, "skills");
|
|
812
|
-
if (existsSync2(skillsDir) &&
|
|
827
|
+
if (existsSync2(skillsDir) && statSync(skillsDir).isDirectory()) {
|
|
813
828
|
const skillDirs = execSync(`ls -1 "${skillsDir}"`, { encoding: "utf-8" }).split(`
|
|
814
|
-
`).filter((l) => l.trim() && existsSync2(join2(skillsDir, l)) &&
|
|
829
|
+
`).filter((l) => l.trim() && existsSync2(join2(skillsDir, l)) && statSync(join2(skillsDir, l)).isDirectory());
|
|
815
830
|
for (const skillDir of skillDirs) {
|
|
816
831
|
const skillMdPath = join2(skillsDir, skillDir, "SKILL.md");
|
|
817
832
|
if (!existsSync2(skillMdPath)) {
|
|
@@ -820,7 +835,7 @@ function validateAgentDir(dirPath) {
|
|
|
820
835
|
}
|
|
821
836
|
}
|
|
822
837
|
const knowledgesDir = join2(dirPath, "knowledges");
|
|
823
|
-
if (existsSync2(knowledgesDir) &&
|
|
838
|
+
if (existsSync2(knowledgesDir) && statSync(knowledgesDir).isDirectory()) {
|
|
824
839
|
const mdFiles = execSync(`ls -1 "${knowledgesDir}"`, { encoding: "utf-8" }).split(`
|
|
825
840
|
`).filter((l) => l.trim().endsWith(".md"));
|
|
826
841
|
if (mdFiles.length === 0) {
|
|
@@ -835,7 +850,7 @@ function validateAgentDir(dirPath) {
|
|
|
835
850
|
}
|
|
836
851
|
|
|
837
852
|
// src/cmd/skill.ts
|
|
838
|
-
import { writeFileSync as writeFileSync2, existsSync as existsSync3, statSync as
|
|
853
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
839
854
|
import { join as join3, basename, dirname } from "path";
|
|
840
855
|
import { execSync as execSync2 } from "child_process";
|
|
841
856
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
@@ -967,7 +982,7 @@ async function downloadSkill(name, outputDir) {
|
|
|
967
982
|
const nonZipEntries = entries.filter((e) => e !== `${name}.zip`);
|
|
968
983
|
if (nonZipEntries.length === 1) {
|
|
969
984
|
const onlyEntry = join3(tempDir, nonZipEntries[0]);
|
|
970
|
-
if (
|
|
985
|
+
if (statSync2(onlyEntry).isDirectory()) {
|
|
971
986
|
execSync2(`cp -r "${onlyEntry}"/* "${targetDir}/"`, { stdio: "pipe" });
|
|
972
987
|
} else {
|
|
973
988
|
execSync2(`cp -r "${onlyEntry}" "${targetDir}/"`, { stdio: "pipe" });
|
|
@@ -1116,7 +1131,7 @@ async function deleteSkill(name) {
|
|
|
1116
1131
|
}
|
|
1117
1132
|
}
|
|
1118
1133
|
async function validateSkill(filePath) {
|
|
1119
|
-
const isDir = existsSync3(filePath) &&
|
|
1134
|
+
const isDir = existsSync3(filePath) && statSync2(filePath).isDirectory();
|
|
1120
1135
|
const result = isDir ? validateSkillDir(filePath) : validateZip(filePath);
|
|
1121
1136
|
if (shouldOutputJson()) {
|
|
1122
1137
|
outputJson({
|
|
@@ -1172,7 +1187,7 @@ async function validateSkill(filePath) {
|
|
|
1172
1187
|
}
|
|
1173
1188
|
|
|
1174
1189
|
// src/cmd/agent.ts
|
|
1175
|
-
import { writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
1190
|
+
import { writeFileSync as writeFileSync3, existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1176
1191
|
import { join as join4 } from "path";
|
|
1177
1192
|
import { execSync as execSync3 } from "child_process";
|
|
1178
1193
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3 } from "fs";
|
|
@@ -1283,7 +1298,7 @@ async function deleteAgent(id) {
|
|
|
1283
1298
|
process.exit(1);
|
|
1284
1299
|
}
|
|
1285
1300
|
}
|
|
1286
|
-
async function bindSkill(id, skillId, config) {
|
|
1301
|
+
async function bindSkill(id, skillId, version = "1.0.0", config) {
|
|
1287
1302
|
const configStore = loadConfig();
|
|
1288
1303
|
if (!configStore?.token) {
|
|
1289
1304
|
if (shouldOutputJson()) {
|
|
@@ -1296,12 +1311,12 @@ async function bindSkill(id, skillId, config) {
|
|
|
1296
1311
|
const client = new ApiClient(configStore.serverUrl, configStore.token);
|
|
1297
1312
|
try {
|
|
1298
1313
|
const parsedConfig = config ? JSON.parse(config) : undefined;
|
|
1299
|
-
await client.bindSkillToAgent(id, skillId, parsedConfig);
|
|
1314
|
+
await client.bindSkillToAgent(id, skillId, version, parsedConfig);
|
|
1300
1315
|
if (shouldOutputJson()) {
|
|
1301
|
-
outputJson({ success: true, data: { agentId: id, skillId, config: parsedConfig } });
|
|
1316
|
+
outputJson({ success: true, data: { agentId: id, skillId, version, config: parsedConfig } });
|
|
1302
1317
|
return;
|
|
1303
1318
|
}
|
|
1304
|
-
success(`Skill "${skillId}" 已绑定到 Agent "${id}"`);
|
|
1319
|
+
success(`Skill "${skillId}@${version}" 已绑定到 Agent "${id}"`);
|
|
1305
1320
|
} catch (err) {
|
|
1306
1321
|
if (shouldOutputJson()) {
|
|
1307
1322
|
outputJson({ success: false, error: { code: "BIND_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
|
|
@@ -1311,7 +1326,7 @@ async function bindSkill(id, skillId, config) {
|
|
|
1311
1326
|
process.exit(1);
|
|
1312
1327
|
}
|
|
1313
1328
|
}
|
|
1314
|
-
async function unbindSkill(id, skillId) {
|
|
1329
|
+
async function unbindSkill(id, skillId, version) {
|
|
1315
1330
|
const config = loadConfig();
|
|
1316
1331
|
if (!config?.token) {
|
|
1317
1332
|
if (shouldOutputJson()) {
|
|
@@ -1323,12 +1338,12 @@ async function unbindSkill(id, skillId) {
|
|
|
1323
1338
|
}
|
|
1324
1339
|
const client = new ApiClient(config.serverUrl, config.token);
|
|
1325
1340
|
try {
|
|
1326
|
-
await client.unbindSkillFromAgent(id, skillId);
|
|
1341
|
+
await client.unbindSkillFromAgent(id, skillId, version);
|
|
1327
1342
|
if (shouldOutputJson()) {
|
|
1328
|
-
outputJson({ success: true, data: { agentId: id, skillId } });
|
|
1343
|
+
outputJson({ success: true, data: { agentId: id, skillId, version } });
|
|
1329
1344
|
return;
|
|
1330
1345
|
}
|
|
1331
|
-
success(`Skill "${skillId}" 已从 Agent "${id}" 解绑`);
|
|
1346
|
+
success(`Skill "${skillId}${version ? "@" + version : ""}" 已从 Agent "${id}" 解绑`);
|
|
1332
1347
|
} catch (err) {
|
|
1333
1348
|
if (shouldOutputJson()) {
|
|
1334
1349
|
outputJson({ success: false, error: { code: "UNBIND_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
|
|
@@ -1338,7 +1353,7 @@ async function unbindSkill(id, skillId) {
|
|
|
1338
1353
|
process.exit(1);
|
|
1339
1354
|
}
|
|
1340
1355
|
}
|
|
1341
|
-
async function bindKnowledge(id, knowledgeId, retrievalConfig) {
|
|
1356
|
+
async function bindKnowledge(id, knowledgeId, version = "1.0.0", retrievalConfig) {
|
|
1342
1357
|
const configStore = loadConfig();
|
|
1343
1358
|
if (!configStore?.token) {
|
|
1344
1359
|
if (shouldOutputJson()) {
|
|
@@ -1351,12 +1366,12 @@ async function bindKnowledge(id, knowledgeId, retrievalConfig) {
|
|
|
1351
1366
|
const client = new ApiClient(configStore.serverUrl, configStore.token);
|
|
1352
1367
|
try {
|
|
1353
1368
|
const parsedConfig = retrievalConfig ? JSON.parse(retrievalConfig) : undefined;
|
|
1354
|
-
await client.bindKnowledgeToAgent(id, knowledgeId, parsedConfig);
|
|
1369
|
+
await client.bindKnowledgeToAgent(id, knowledgeId, version, parsedConfig);
|
|
1355
1370
|
if (shouldOutputJson()) {
|
|
1356
|
-
outputJson({ success: true, data: { agentId: id, knowledgeId, retrievalConfig: parsedConfig } });
|
|
1371
|
+
outputJson({ success: true, data: { agentId: id, knowledgeId, version, retrievalConfig: parsedConfig } });
|
|
1357
1372
|
return;
|
|
1358
1373
|
}
|
|
1359
|
-
success(`Knowledge "${knowledgeId}" 已绑定到 Agent "${id}"`);
|
|
1374
|
+
success(`Knowledge "${knowledgeId}@${version}" 已绑定到 Agent "${id}"`);
|
|
1360
1375
|
} catch (err) {
|
|
1361
1376
|
if (shouldOutputJson()) {
|
|
1362
1377
|
outputJson({ success: false, error: { code: "BIND_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
|
|
@@ -1366,7 +1381,7 @@ async function bindKnowledge(id, knowledgeId, retrievalConfig) {
|
|
|
1366
1381
|
process.exit(1);
|
|
1367
1382
|
}
|
|
1368
1383
|
}
|
|
1369
|
-
async function unbindKnowledge(id, knowledgeId) {
|
|
1384
|
+
async function unbindKnowledge(id, knowledgeId, version) {
|
|
1370
1385
|
const config = loadConfig();
|
|
1371
1386
|
if (!config?.token) {
|
|
1372
1387
|
if (shouldOutputJson()) {
|
|
@@ -1378,12 +1393,12 @@ async function unbindKnowledge(id, knowledgeId) {
|
|
|
1378
1393
|
}
|
|
1379
1394
|
const client = new ApiClient(config.serverUrl, config.token);
|
|
1380
1395
|
try {
|
|
1381
|
-
await client.unbindKnowledgeFromAgent(id, knowledgeId);
|
|
1396
|
+
await client.unbindKnowledgeFromAgent(id, knowledgeId, version);
|
|
1382
1397
|
if (shouldOutputJson()) {
|
|
1383
|
-
outputJson({ success: true, data: { agentId: id, knowledgeId } });
|
|
1398
|
+
outputJson({ success: true, data: { agentId: id, knowledgeId, version } });
|
|
1384
1399
|
return;
|
|
1385
1400
|
}
|
|
1386
|
-
success(`Knowledge "${knowledgeId}" 已从 Agent "${id}" 解绑`);
|
|
1401
|
+
success(`Knowledge "${knowledgeId}${version ? "@" + version : ""}" 已从 Agent "${id}" 解绑`);
|
|
1387
1402
|
} catch (err) {
|
|
1388
1403
|
if (shouldOutputJson()) {
|
|
1389
1404
|
outputJson({ success: false, error: { code: "UNBIND_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
|
|
@@ -1603,9 +1618,9 @@ async function createAgentFromFolder(folderPath) {
|
|
|
1603
1618
|
const uploadedSkills = [];
|
|
1604
1619
|
const uploadedKnowledges = [];
|
|
1605
1620
|
const skillsDir = join4(folderPath, "skills");
|
|
1606
|
-
if (existsSync4(skillsDir) &&
|
|
1621
|
+
if (existsSync4(skillsDir) && statSync3(skillsDir).isDirectory()) {
|
|
1607
1622
|
const skillDirs = execSync3(`ls -1 "${skillsDir}"`, { encoding: "utf-8" }).split(`
|
|
1608
|
-
`).filter((l) => l.trim() && existsSync4(join4(skillsDir, l)) &&
|
|
1623
|
+
`).filter((l) => l.trim() && existsSync4(join4(skillsDir, l)) && statSync3(join4(skillsDir, l)).isDirectory());
|
|
1609
1624
|
for (const skillDir of skillDirs) {
|
|
1610
1625
|
const skillPath = join4(skillsDir, skillDir);
|
|
1611
1626
|
try {
|
|
@@ -1637,7 +1652,7 @@ async function createAgentFromFolder(folderPath) {
|
|
|
1637
1652
|
}
|
|
1638
1653
|
}
|
|
1639
1654
|
const knowledgesDir = join4(folderPath, "knowledges");
|
|
1640
|
-
if (existsSync4(knowledgesDir) &&
|
|
1655
|
+
if (existsSync4(knowledgesDir) && statSync3(knowledgesDir).isDirectory()) {
|
|
1641
1656
|
const mdFiles = execSync3(`ls -1 "${knowledgesDir}"`, { encoding: "utf-8" }).split(`
|
|
1642
1657
|
`).filter((l) => l.trim().endsWith(".md"));
|
|
1643
1658
|
for (const mdFile of mdFiles) {
|
|
@@ -1680,13 +1695,13 @@ async function createAgentFromFolder(folderPath) {
|
|
|
1680
1695
|
if (shouldOutputJson()) {
|
|
1681
1696
|
info(`绑定技能: ${skill.name}`);
|
|
1682
1697
|
}
|
|
1683
|
-
await client.bindSkillToAgent(agent.id, skill.id);
|
|
1698
|
+
await client.bindSkillToAgent(agent.id, skill.id, undefined);
|
|
1684
1699
|
}
|
|
1685
1700
|
for (const knowledge of uploadedKnowledges) {
|
|
1686
1701
|
if (shouldOutputJson()) {
|
|
1687
1702
|
info(`绑定知识: ${knowledge.title}`);
|
|
1688
1703
|
}
|
|
1689
|
-
await client.bindKnowledgeToAgent(agent.id, knowledge.id);
|
|
1704
|
+
await client.bindKnowledgeToAgent(agent.id, knowledge.id, undefined);
|
|
1690
1705
|
}
|
|
1691
1706
|
if (shouldOutputJson()) {
|
|
1692
1707
|
outputJson({
|
|
@@ -1711,6 +1726,244 @@ async function createAgentFromFolder(folderPath) {
|
|
|
1711
1726
|
process.exit(1);
|
|
1712
1727
|
}
|
|
1713
1728
|
}
|
|
1729
|
+
async function syncAgent(folderPath, options = {}) {
|
|
1730
|
+
const config = loadConfig();
|
|
1731
|
+
if (!config?.token) {
|
|
1732
|
+
if (shouldOutputJson()) {
|
|
1733
|
+
outputJson({ success: false, error: { code: "NOT_LOGGED_IN", message: "未登录,请先运行 arm login" } });
|
|
1734
|
+
process.exit(1);
|
|
1735
|
+
}
|
|
1736
|
+
error("未登录,请先运行 arm login");
|
|
1737
|
+
process.exit(1);
|
|
1738
|
+
}
|
|
1739
|
+
if (!existsSync4(folderPath)) {
|
|
1740
|
+
if (shouldOutputJson()) {
|
|
1741
|
+
outputJson({ success: false, error: { code: "FILE_NOT_FOUND", message: `目录不存在: ${folderPath}` } });
|
|
1742
|
+
process.exit(1);
|
|
1743
|
+
}
|
|
1744
|
+
error(`目录不存在: ${folderPath}`);
|
|
1745
|
+
process.exit(1);
|
|
1746
|
+
}
|
|
1747
|
+
const validation = validateAgentDir(folderPath);
|
|
1748
|
+
if (!validation.valid) {
|
|
1749
|
+
if (shouldOutputJson()) {
|
|
1750
|
+
outputJson({ success: false, error: { code: "VALIDATION_FAILED", message: validation.errors.join(", ") } });
|
|
1751
|
+
process.exit(1);
|
|
1752
|
+
}
|
|
1753
|
+
error(`验证失败: ${validation.errors.join(", ")}`);
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
const metadata = validation.metadata;
|
|
1757
|
+
const agentName = metadata.name;
|
|
1758
|
+
const client = new ApiClient(config.serverUrl, config.token);
|
|
1759
|
+
if (shouldOutputJson()) {
|
|
1760
|
+
info(`正在同步 Agent: ${agentName}...`);
|
|
1761
|
+
}
|
|
1762
|
+
try {
|
|
1763
|
+
const result = await client.listAgents(agentName, 1, 1);
|
|
1764
|
+
const existingAgent = result.agents.find((a) => a.name === agentName);
|
|
1765
|
+
if (!existingAgent) {
|
|
1766
|
+
if (shouldOutputJson()) {
|
|
1767
|
+
outputJson({ success: false, error: { code: "NOT_FOUND", message: `Agent "${agentName}" 不存在,请先使用 arm agent create --from 创建` } });
|
|
1768
|
+
process.exit(1);
|
|
1769
|
+
}
|
|
1770
|
+
error(`Agent "${agentName}" 不存在,请先使用 arm agent create --from 创建`);
|
|
1771
|
+
process.exit(1);
|
|
1772
|
+
}
|
|
1773
|
+
const cloudAgent = await client.getAgent(existingAgent.id);
|
|
1774
|
+
const localSkills = await parseLocalSkills(folderPath);
|
|
1775
|
+
const localKnowledges = await parseLocalKnowledges(folderPath);
|
|
1776
|
+
const cloudSkillBindings = cloudAgent.skills || [];
|
|
1777
|
+
const cloudKnowledgeBindings = cloudAgent.knowledges || [];
|
|
1778
|
+
const changes = {
|
|
1779
|
+
metadataChanged: false,
|
|
1780
|
+
newSkills: [],
|
|
1781
|
+
removedSkills: [],
|
|
1782
|
+
newKnowledges: [],
|
|
1783
|
+
removedKnowledges: []
|
|
1784
|
+
};
|
|
1785
|
+
if (cloudAgent.prompt !== metadata.prompt || cloudAgent.description !== metadata.description) {
|
|
1786
|
+
changes.metadataChanged = true;
|
|
1787
|
+
}
|
|
1788
|
+
const cloudSkillNames = new Set(cloudSkillBindings.map((s) => s.skill?.name).filter(Boolean));
|
|
1789
|
+
for (const localSkill of localSkills) {
|
|
1790
|
+
const existingBinding = cloudSkillBindings.find((sb) => sb.skill?.name === localSkill.name && sb.version === localSkill.version);
|
|
1791
|
+
if (!existingBinding) {
|
|
1792
|
+
changes.newSkills.push(localSkill);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
const localSkillNames = new Set(localSkills.map((s) => s.name));
|
|
1796
|
+
for (const cloudBinding of cloudSkillBindings) {
|
|
1797
|
+
if (cloudBinding.skill?.name && !localSkillNames.has(cloudBinding.skill.name)) {
|
|
1798
|
+
changes.removedSkills.push(cloudBinding.skill.name);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
const cloudKnowledgeNames = new Set(cloudKnowledgeBindings.map((k) => k.knowledge?.name).filter(Boolean));
|
|
1802
|
+
for (const localKnowledge of localKnowledges) {
|
|
1803
|
+
const existingBinding = cloudKnowledgeBindings.find((kb) => kb.knowledge?.name === localKnowledge.name && kb.version === localKnowledge.version);
|
|
1804
|
+
if (!existingBinding) {
|
|
1805
|
+
changes.newKnowledges.push(localKnowledge);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
const localKnowledgeNames = new Set(localKnowledges.map((k) => k.name));
|
|
1809
|
+
for (const cloudBinding of cloudKnowledgeBindings) {
|
|
1810
|
+
if (cloudBinding.knowledge?.name && !localKnowledgeNames.has(cloudBinding.knowledge.name)) {
|
|
1811
|
+
changes.removedKnowledges.push(cloudBinding.knowledge.name);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
if (options.dryRun) {
|
|
1815
|
+
if (shouldOutputJson()) {
|
|
1816
|
+
outputJson({
|
|
1817
|
+
success: true,
|
|
1818
|
+
data: {
|
|
1819
|
+
agentName,
|
|
1820
|
+
agentId: existingAgent.id,
|
|
1821
|
+
changes,
|
|
1822
|
+
dryRun: true
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
console.log(`
|
|
1828
|
+
[DRY-RUN] 预览 ${agentName} 的变更:
|
|
1829
|
+
`);
|
|
1830
|
+
if (changes.metadataChanged) {
|
|
1831
|
+
console.log(" 元信息: 将更新 (prompt/description 变更)");
|
|
1832
|
+
}
|
|
1833
|
+
if (changes.newSkills.length > 0) {
|
|
1834
|
+
console.log(` 新增 Skills: ${changes.newSkills.map((s) => `${s.name}@${s.version}`).join(", ")}`);
|
|
1835
|
+
}
|
|
1836
|
+
if (changes.removedSkills.length > 0) {
|
|
1837
|
+
console.log(` 移除 Skills: ${changes.removedSkills.join(", ")}`);
|
|
1838
|
+
}
|
|
1839
|
+
if (changes.newKnowledges.length > 0) {
|
|
1840
|
+
console.log(` 新增 Knowledges: ${changes.newKnowledges.map((k) => `${k.name}@${k.version}`).join(", ")}`);
|
|
1841
|
+
}
|
|
1842
|
+
if (changes.removedKnowledges.length > 0) {
|
|
1843
|
+
console.log(` 移除 Knowledges: ${changes.removedKnowledges.join(", ")}`);
|
|
1844
|
+
}
|
|
1845
|
+
if (!changes.metadataChanged && changes.newSkills.length === 0 && changes.removedSkills.length === 0 && changes.newKnowledges.length === 0 && changes.removedKnowledges.length === 0) {
|
|
1846
|
+
console.log(" 无变更");
|
|
1847
|
+
}
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (changes.metadataChanged) {
|
|
1851
|
+
if (shouldOutputJson()) {
|
|
1852
|
+
info("更新 Agent 元信息...");
|
|
1853
|
+
}
|
|
1854
|
+
await client.updateAgent(existingAgent.id, {
|
|
1855
|
+
prompt: metadata.prompt,
|
|
1856
|
+
description: metadata.description
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
for (const skill of changes.newSkills) {
|
|
1860
|
+
if (shouldOutputJson()) {
|
|
1861
|
+
info(`上传并绑定新技能: ${skill.name} (版本自动分配)`);
|
|
1862
|
+
}
|
|
1863
|
+
const skillTempDir = mkdtempSync3("/tmp/skill-sync-");
|
|
1864
|
+
const zipPath = join4(skillTempDir, `${skill.name}.zip`);
|
|
1865
|
+
execSync3(`cd "${skill.path}" && zip -r "${zipPath}" . -x ".*"`, { stdio: "pipe" });
|
|
1866
|
+
try {
|
|
1867
|
+
const uploadedSkill = await client.uploadSkill(zipPath);
|
|
1868
|
+
await client.bindSkillToAgent(existingAgent.id, uploadedSkill.id, undefined);
|
|
1869
|
+
} finally {
|
|
1870
|
+
rmSync3(skillTempDir, { recursive: true, force: true });
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
for (const skillName of changes.removedSkills) {
|
|
1874
|
+
const binding = cloudSkillBindings.find((sb) => sb.skill?.name === skillName);
|
|
1875
|
+
if (binding) {
|
|
1876
|
+
if (shouldOutputJson()) {
|
|
1877
|
+
info(`解绑技能: ${skillName}`);
|
|
1878
|
+
}
|
|
1879
|
+
await client.unbindSkillFromAgent(existingAgent.id, binding.skillId, binding.version);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
for (const knowledge of changes.newKnowledges) {
|
|
1883
|
+
if (shouldOutputJson()) {
|
|
1884
|
+
info(`上传并绑定新知识: ${knowledge.name} (版本自动分配)`);
|
|
1885
|
+
}
|
|
1886
|
+
const uploadedKnowledge = await client.uploadKnowledge(knowledge.path);
|
|
1887
|
+
await client.bindKnowledgeToAgent(existingAgent.id, uploadedKnowledge.id, undefined);
|
|
1888
|
+
}
|
|
1889
|
+
for (const knowledgeName of changes.removedKnowledges) {
|
|
1890
|
+
const binding = cloudKnowledgeBindings.find((kb) => kb.knowledge?.name === knowledgeName);
|
|
1891
|
+
if (binding) {
|
|
1892
|
+
if (shouldOutputJson()) {
|
|
1893
|
+
info(`解绑知识: ${knowledgeName}`);
|
|
1894
|
+
}
|
|
1895
|
+
await client.unbindKnowledgeFromAgent(existingAgent.id, binding.knowledgeId, binding.version);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
if (shouldOutputJson()) {
|
|
1899
|
+
outputJson({
|
|
1900
|
+
success: true,
|
|
1901
|
+
data: {
|
|
1902
|
+
agentName,
|
|
1903
|
+
agentId: existingAgent.id,
|
|
1904
|
+
changes
|
|
1905
|
+
}
|
|
1906
|
+
});
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
success(`Agent "${agentName}" 同步完成`);
|
|
1910
|
+
const changeCount = (changes.metadataChanged ? 1 : 0) + changes.newSkills.length + changes.removedSkills.length + changes.newKnowledges.length + changes.removedKnowledges.length;
|
|
1911
|
+
if (changeCount === 0) {
|
|
1912
|
+
info("无变更");
|
|
1913
|
+
} else {
|
|
1914
|
+
console.log(` 更新了 ${changeCount} 项`);
|
|
1915
|
+
}
|
|
1916
|
+
} catch (err) {
|
|
1917
|
+
if (shouldOutputJson()) {
|
|
1918
|
+
outputJson({ success: false, error: { code: "SYNC_FAILED", message: err instanceof Error ? err.message : "未知错误" } });
|
|
1919
|
+
process.exit(1);
|
|
1920
|
+
}
|
|
1921
|
+
error(`同步失败: ${err instanceof Error ? err.message : "未知错误"}`);
|
|
1922
|
+
process.exit(1);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
async function parseLocalSkills(folderPath) {
|
|
1926
|
+
const skills = [];
|
|
1927
|
+
const skillsDir = join4(folderPath, "skills");
|
|
1928
|
+
if (!existsSync4(skillsDir) || !statSync3(skillsDir).isDirectory()) {
|
|
1929
|
+
return skills;
|
|
1930
|
+
}
|
|
1931
|
+
try {
|
|
1932
|
+
const skillDirs = execSync3(`ls -1 "${skillsDir}"`, { encoding: "utf-8" }).split(`
|
|
1933
|
+
`).filter((l) => l.trim() && existsSync4(join4(skillsDir, l)) && statSync3(join4(skillsDir, l)).isDirectory());
|
|
1934
|
+
for (const skillDir of skillDirs) {
|
|
1935
|
+
const skillPath = join4(skillsDir, skillDir);
|
|
1936
|
+
const skillMdPath = join4(skillPath, "SKILL.md");
|
|
1937
|
+
if (existsSync4(skillMdPath)) {
|
|
1938
|
+
const content = readFileSync3(skillMdPath, "utf-8");
|
|
1939
|
+
const versionMatch = content.match(/^---\n[\s\S]*?version:\s*(.+)\n/);
|
|
1940
|
+
const version = versionMatch ? versionMatch[1].trim() : "1.0.0";
|
|
1941
|
+
skills.push({ name: skillDir, path: skillPath, version });
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
} catch {}
|
|
1945
|
+
return skills;
|
|
1946
|
+
}
|
|
1947
|
+
async function parseLocalKnowledges(folderPath) {
|
|
1948
|
+
const knowledges = [];
|
|
1949
|
+
const knowledgesDir = join4(folderPath, "knowledges");
|
|
1950
|
+
if (!existsSync4(knowledgesDir) || !statSync3(knowledgesDir).isDirectory()) {
|
|
1951
|
+
return knowledges;
|
|
1952
|
+
}
|
|
1953
|
+
try {
|
|
1954
|
+
const mdFiles = execSync3(`ls -1 "${knowledgesDir}"`, { encoding: "utf-8" }).split(`
|
|
1955
|
+
`).filter((l) => l.trim().endsWith(".md"));
|
|
1956
|
+
for (const mdFile of mdFiles) {
|
|
1957
|
+
const mdPath = join4(knowledgesDir, mdFile);
|
|
1958
|
+
const name = mdFile.replace(".md", "");
|
|
1959
|
+
const content = readFileSync3(mdPath, "utf-8");
|
|
1960
|
+
const versionMatch = content.match(/^---\n[\s\S]*?version:\s*(.+)\n/);
|
|
1961
|
+
const version = versionMatch ? versionMatch[1].trim() : "1.0.0";
|
|
1962
|
+
knowledges.push({ name, path: mdPath, version });
|
|
1963
|
+
}
|
|
1964
|
+
} catch {}
|
|
1965
|
+
return knowledges;
|
|
1966
|
+
}
|
|
1714
1967
|
|
|
1715
1968
|
// src/cmd/knowledge.ts
|
|
1716
1969
|
import { writeFileSync as writeFileSync4, existsSync as existsSync5, statSync as statSync4 } from "fs";
|
|
@@ -1976,10 +2229,18 @@ function setServer(url) {
|
|
|
1976
2229
|
}
|
|
1977
2230
|
|
|
1978
2231
|
// src/main.ts
|
|
2232
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2233
|
+
import { join as join6 } from "path";
|
|
2234
|
+
var __dirname = "/Users/lk/Documents/Dev/aims/xuanwu/xuanwu-agents/agent-resource-management/cli/src";
|
|
1979
2235
|
var args = process.argv.slice(2);
|
|
1980
2236
|
var command = args[0];
|
|
1981
2237
|
var subCommand = args[1];
|
|
2238
|
+
var VERSION = JSON.parse(readFileSync4(join6(__dirname, "../package.json"), "utf-8")).version;
|
|
1982
2239
|
async function main() {
|
|
2240
|
+
if (command === "--version" || command === "-v") {
|
|
2241
|
+
console.log(`arm v${VERSION}`);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
1983
2244
|
switch (command) {
|
|
1984
2245
|
case "register":
|
|
1985
2246
|
if (args[1] && args[1].startsWith("--")) {
|
|
@@ -2264,6 +2525,7 @@ async function main() {
|
|
|
2264
2525
|
case "bind":
|
|
2265
2526
|
if (!args[2]) {
|
|
2266
2527
|
console.error("用法: arm agent bind <id> --skill=<skillId> [--skill-config='{...}'] 或 arm agent bind <id> --knowledge=<knowledgeId> [--knowledge-config='{...}'] [--json]");
|
|
2528
|
+
console.error("注意: version 缺省时自动绑定新版本");
|
|
2267
2529
|
process.exit(1);
|
|
2268
2530
|
}
|
|
2269
2531
|
{
|
|
@@ -2285,9 +2547,9 @@ async function main() {
|
|
|
2285
2547
|
}
|
|
2286
2548
|
}
|
|
2287
2549
|
if (skillId) {
|
|
2288
|
-
await bindSkill(id, skillId, skillConfig);
|
|
2550
|
+
await bindSkill(id, skillId, undefined, skillConfig);
|
|
2289
2551
|
} else if (knowledgeId) {
|
|
2290
|
-
await bindKnowledge(id, knowledgeId, knowledgeConfig);
|
|
2552
|
+
await bindKnowledge(id, knowledgeId, undefined, knowledgeConfig);
|
|
2291
2553
|
} else {
|
|
2292
2554
|
console.error("用法: arm agent bind <id> --skill=<skillId> 或 --knowledge=<knowledgeId>");
|
|
2293
2555
|
process.exit(1);
|
|
@@ -2296,31 +2558,67 @@ async function main() {
|
|
|
2296
2558
|
break;
|
|
2297
2559
|
case "unbind":
|
|
2298
2560
|
if (!args[2]) {
|
|
2299
|
-
console.error("用法: arm agent unbind <id> --skill=<skillId> 或 --knowledge=<knowledgeId> [--json]");
|
|
2561
|
+
console.error("用法: arm agent unbind <id> --skill=<skillId> [--version=<ver>] 或 --knowledge=<knowledgeId> [--version=<ver>] [--json]");
|
|
2300
2562
|
process.exit(1);
|
|
2301
2563
|
}
|
|
2302
2564
|
{
|
|
2303
2565
|
const id = args[2];
|
|
2304
2566
|
let skillId;
|
|
2305
2567
|
let knowledgeId;
|
|
2568
|
+
let skillVersion;
|
|
2569
|
+
let knowledgeVersion;
|
|
2306
2570
|
for (let i = 3;i < args.length; i++) {
|
|
2307
2571
|
const arg = args[i];
|
|
2308
2572
|
if (arg.startsWith("--skill=")) {
|
|
2309
2573
|
skillId = arg.split("=").slice(1).join("=");
|
|
2310
2574
|
} else if (arg.startsWith("--knowledge=")) {
|
|
2311
2575
|
knowledgeId = arg.split("=").slice(1).join("=");
|
|
2576
|
+
} else if (arg.startsWith("--version=")) {
|
|
2577
|
+
const ver = arg.split("=").slice(1).join("=");
|
|
2578
|
+
if (skillId)
|
|
2579
|
+
skillVersion = ver;
|
|
2580
|
+
if (knowledgeId)
|
|
2581
|
+
knowledgeVersion = ver;
|
|
2312
2582
|
}
|
|
2313
2583
|
}
|
|
2314
2584
|
if (skillId) {
|
|
2315
|
-
await unbindSkill(id, skillId);
|
|
2585
|
+
await unbindSkill(id, skillId, skillVersion);
|
|
2316
2586
|
} else if (knowledgeId) {
|
|
2317
|
-
await unbindKnowledge(id, knowledgeId);
|
|
2587
|
+
await unbindKnowledge(id, knowledgeId, knowledgeVersion);
|
|
2318
2588
|
} else {
|
|
2319
2589
|
console.error("用法: arm agent unbind <id> --skill=<skillId> 或 --knowledge=<knowledgeId>");
|
|
2320
2590
|
process.exit(1);
|
|
2321
2591
|
}
|
|
2322
2592
|
}
|
|
2323
2593
|
break;
|
|
2594
|
+
case "sync":
|
|
2595
|
+
if (!args[2]) {
|
|
2596
|
+
console.error("用法: arm agent sync <folder> [--dry-run] [--force] [--json]");
|
|
2597
|
+
process.exit(1);
|
|
2598
|
+
}
|
|
2599
|
+
{
|
|
2600
|
+
const folder = args[2];
|
|
2601
|
+
const syncOptions = {};
|
|
2602
|
+
for (let i = 3;i < args.length; i++) {
|
|
2603
|
+
const arg = args[i];
|
|
2604
|
+
if (arg === "--dry-run") {
|
|
2605
|
+
syncOptions.dryRun = true;
|
|
2606
|
+
} else if (arg === "--force") {
|
|
2607
|
+
syncOptions.force = true;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
await syncAgent(folder, syncOptions);
|
|
2611
|
+
}
|
|
2612
|
+
break;
|
|
2613
|
+
case "bindings":
|
|
2614
|
+
if (!args[2]) {
|
|
2615
|
+
console.error("用法: arm agent bindings <name> [--history] [--json]");
|
|
2616
|
+
process.exit(1);
|
|
2617
|
+
}
|
|
2618
|
+
if (subCommand === "history") {
|
|
2619
|
+
console.log("bindings history 需要通过 info 命令查看");
|
|
2620
|
+
}
|
|
2621
|
+
break;
|
|
2324
2622
|
default:
|
|
2325
2623
|
console.log(`
|
|
2326
2624
|
可用命令:
|
|
@@ -2332,10 +2630,11 @@ async function main() {
|
|
|
2332
2630
|
arm agent create --from=<folder> 从本地文件夹创建 Agent
|
|
2333
2631
|
arm agent update <id> 更新 Agent (--name, --description, --prompt, --avatar, --status)
|
|
2334
2632
|
arm agent delete <id> 删除 Agent
|
|
2335
|
-
arm agent bind <id> --skill=<id>
|
|
2336
|
-
arm agent unbind <id> --skill=<id> 解绑 Skill
|
|
2337
|
-
arm agent bind <id> --knowledge=<id> 绑定 Knowledge 到 Agent
|
|
2338
|
-
arm agent unbind <id> --knowledge=<id> 解绑 Knowledge
|
|
2633
|
+
arm agent bind <id> --skill=<id> [--version=<ver>] 绑定 Skill 到 Agent
|
|
2634
|
+
arm agent unbind <id> --skill=<id> [--version=<ver>] 解绑 Skill
|
|
2635
|
+
arm agent bind <id> --knowledge=<id> [--version=<ver>] 绑定 Knowledge 到 Agent
|
|
2636
|
+
arm agent unbind <id> --knowledge=<id> [--version=<ver>] 解绑 Knowledge
|
|
2637
|
+
arm agent sync <folder> [--dry-run] 同步本地文件夹到云端 Agent
|
|
2339
2638
|
所有命令支持 --json 参数获取机器可读输出
|
|
2340
2639
|
`);
|
|
2341
2640
|
}
|
package/package.json
CHANGED
package/src/cmd/agent.ts
CHANGED
|
@@ -3,7 +3,7 @@ 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
5
|
import { validateAgentDir } from '../lib/validate';
|
|
6
|
-
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
6
|
+
import { writeFileSync, existsSync, readFileSync, statSync } from 'fs';
|
|
7
7
|
import { join, basename } from 'path';
|
|
8
8
|
import { execSync } from 'child_process';
|
|
9
9
|
import { mkdtempSync, rmSync } from 'fs';
|
|
@@ -156,7 +156,7 @@ export async function deleteAgent(id: string): Promise<void> {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
export async function bindSkill(id: string, skillId: string, config?: string): Promise<void> {
|
|
159
|
+
export async function bindSkill(id: string, skillId: string, version: string = '1.0.0', config?: string): Promise<void> {
|
|
160
160
|
const configStore = loadConfig();
|
|
161
161
|
if (!configStore?.token) {
|
|
162
162
|
if (shouldOutputJson()) {
|
|
@@ -170,13 +170,13 @@ export async function bindSkill(id: string, skillId: string, config?: string): P
|
|
|
170
170
|
const client = new ApiClient(configStore.serverUrl, configStore.token);
|
|
171
171
|
try {
|
|
172
172
|
const parsedConfig = config ? JSON.parse(config) : undefined;
|
|
173
|
-
await client.bindSkillToAgent(id, skillId, parsedConfig);
|
|
173
|
+
await client.bindSkillToAgent(id, skillId, version, parsedConfig);
|
|
174
174
|
|
|
175
175
|
if (shouldOutputJson()) {
|
|
176
|
-
outputJson({ success: true, data: { agentId: id, skillId, config: parsedConfig } });
|
|
176
|
+
outputJson({ success: true, data: { agentId: id, skillId, version, config: parsedConfig } });
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
-
success(`Skill "${skillId}" 已绑定到 Agent "${id}"`);
|
|
179
|
+
success(`Skill "${skillId}@${version}" 已绑定到 Agent "${id}"`);
|
|
180
180
|
} catch (err) {
|
|
181
181
|
if (shouldOutputJson()) {
|
|
182
182
|
outputJson({ success: false, error: { code: 'BIND_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
@@ -187,7 +187,7 @@ export async function bindSkill(id: string, skillId: string, config?: string): P
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
export async function unbindSkill(id: string, skillId: string): Promise<void> {
|
|
190
|
+
export async function unbindSkill(id: string, skillId: string, version?: string): Promise<void> {
|
|
191
191
|
const config = loadConfig();
|
|
192
192
|
if (!config?.token) {
|
|
193
193
|
if (shouldOutputJson()) {
|
|
@@ -200,13 +200,13 @@ export async function unbindSkill(id: string, skillId: string): Promise<void> {
|
|
|
200
200
|
|
|
201
201
|
const client = new ApiClient(config.serverUrl, config.token);
|
|
202
202
|
try {
|
|
203
|
-
await client.unbindSkillFromAgent(id, skillId);
|
|
203
|
+
await client.unbindSkillFromAgent(id, skillId, version);
|
|
204
204
|
|
|
205
205
|
if (shouldOutputJson()) {
|
|
206
|
-
outputJson({ success: true, data: { agentId: id, skillId } });
|
|
206
|
+
outputJson({ success: true, data: { agentId: id, skillId, version } });
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
|
-
success(`Skill "${skillId}" 已从 Agent "${id}" 解绑`);
|
|
209
|
+
success(`Skill "${skillId}${version ? '@' + version : ''}" 已从 Agent "${id}" 解绑`);
|
|
210
210
|
} catch (err) {
|
|
211
211
|
if (shouldOutputJson()) {
|
|
212
212
|
outputJson({ success: false, error: { code: 'UNBIND_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
@@ -217,7 +217,7 @@ export async function unbindSkill(id: string, skillId: string): Promise<void> {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
export async function bindKnowledge(id: string, knowledgeId: string, retrievalConfig?: string): Promise<void> {
|
|
220
|
+
export async function bindKnowledge(id: string, knowledgeId: string, version: string = '1.0.0', retrievalConfig?: string): Promise<void> {
|
|
221
221
|
const configStore = loadConfig();
|
|
222
222
|
if (!configStore?.token) {
|
|
223
223
|
if (shouldOutputJson()) {
|
|
@@ -231,13 +231,13 @@ export async function bindKnowledge(id: string, knowledgeId: string, retrievalCo
|
|
|
231
231
|
const client = new ApiClient(configStore.serverUrl, configStore.token);
|
|
232
232
|
try {
|
|
233
233
|
const parsedConfig = retrievalConfig ? JSON.parse(retrievalConfig) : undefined;
|
|
234
|
-
await client.bindKnowledgeToAgent(id, knowledgeId, parsedConfig);
|
|
234
|
+
await client.bindKnowledgeToAgent(id, knowledgeId, version, parsedConfig);
|
|
235
235
|
|
|
236
236
|
if (shouldOutputJson()) {
|
|
237
|
-
outputJson({ success: true, data: { agentId: id, knowledgeId, retrievalConfig: parsedConfig } });
|
|
237
|
+
outputJson({ success: true, data: { agentId: id, knowledgeId, version, retrievalConfig: parsedConfig } });
|
|
238
238
|
return;
|
|
239
239
|
}
|
|
240
|
-
success(`Knowledge "${knowledgeId}" 已绑定到 Agent "${id}"`);
|
|
240
|
+
success(`Knowledge "${knowledgeId}@${version}" 已绑定到 Agent "${id}"`);
|
|
241
241
|
} catch (err) {
|
|
242
242
|
if (shouldOutputJson()) {
|
|
243
243
|
outputJson({ success: false, error: { code: 'BIND_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
@@ -248,7 +248,7 @@ export async function bindKnowledge(id: string, knowledgeId: string, retrievalCo
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
export async function unbindKnowledge(id: string, knowledgeId: string): Promise<void> {
|
|
251
|
+
export async function unbindKnowledge(id: string, knowledgeId: string, version?: string): Promise<void> {
|
|
252
252
|
const config = loadConfig();
|
|
253
253
|
if (!config?.token) {
|
|
254
254
|
if (shouldOutputJson()) {
|
|
@@ -261,13 +261,13 @@ export async function unbindKnowledge(id: string, knowledgeId: string): Promise<
|
|
|
261
261
|
|
|
262
262
|
const client = new ApiClient(config.serverUrl, config.token);
|
|
263
263
|
try {
|
|
264
|
-
await client.unbindKnowledgeFromAgent(id, knowledgeId);
|
|
264
|
+
await client.unbindKnowledgeFromAgent(id, knowledgeId, version);
|
|
265
265
|
|
|
266
266
|
if (shouldOutputJson()) {
|
|
267
|
-
outputJson({ success: true, data: { agentId: id, knowledgeId } });
|
|
267
|
+
outputJson({ success: true, data: { agentId: id, knowledgeId, version } });
|
|
268
268
|
return;
|
|
269
269
|
}
|
|
270
|
-
success(`Knowledge "${knowledgeId}" 已从 Agent "${id}" 解绑`);
|
|
270
|
+
success(`Knowledge "${knowledgeId}${version ? '@' + version : ''}" 已从 Agent "${id}" 解绑`);
|
|
271
271
|
} catch (err) {
|
|
272
272
|
if (shouldOutputJson()) {
|
|
273
273
|
outputJson({ success: false, error: { code: 'UNBIND_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
@@ -592,14 +592,14 @@ export async function createAgentFromFolder(folderPath: string): Promise<void> {
|
|
|
592
592
|
if (shouldOutputJson()) {
|
|
593
593
|
info(`绑定技能: ${skill.name}`);
|
|
594
594
|
}
|
|
595
|
-
await client.bindSkillToAgent(agent.id, skill.id);
|
|
595
|
+
await client.bindSkillToAgent(agent.id, skill.id, undefined);
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
for (const knowledge of uploadedKnowledges) {
|
|
599
599
|
if (shouldOutputJson()) {
|
|
600
600
|
info(`绑定知识: ${knowledge.title}`);
|
|
601
601
|
}
|
|
602
|
-
await client.bindKnowledgeToAgent(agent.id, knowledge.id);
|
|
602
|
+
await client.bindKnowledgeToAgent(agent.id, knowledge.id, undefined);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
if (shouldOutputJson()) {
|
|
@@ -624,4 +624,305 @@ export async function createAgentFromFolder(folderPath: string): Promise<void> {
|
|
|
624
624
|
error(`创建失败: ${err instanceof Error ? err.message : '未知错误'}`);
|
|
625
625
|
process.exit(1);
|
|
626
626
|
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export interface SyncOptions {
|
|
630
|
+
dryRun?: boolean;
|
|
631
|
+
force?: boolean;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
interface LocalSkill {
|
|
635
|
+
name: string;
|
|
636
|
+
path: string;
|
|
637
|
+
version: string;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
interface LocalKnowledge {
|
|
641
|
+
name: string;
|
|
642
|
+
path: string;
|
|
643
|
+
version: string;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export async function syncAgent(folderPath: string, options: SyncOptions = {}): Promise<void> {
|
|
647
|
+
const config = loadConfig();
|
|
648
|
+
if (!config?.token) {
|
|
649
|
+
if (shouldOutputJson()) {
|
|
650
|
+
outputJson({ success: false, error: { code: 'NOT_LOGGED_IN', message: '未登录,请先运行 arm login' } });
|
|
651
|
+
process.exit(1);
|
|
652
|
+
}
|
|
653
|
+
error('未登录,请先运行 arm login');
|
|
654
|
+
process.exit(1);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (!existsSync(folderPath)) {
|
|
658
|
+
if (shouldOutputJson()) {
|
|
659
|
+
outputJson({ success: false, error: { code: 'FILE_NOT_FOUND', message: `目录不存在: ${folderPath}` } });
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
error(`目录不存在: ${folderPath}`);
|
|
663
|
+
process.exit(1);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const validation = validateAgentDir(folderPath);
|
|
667
|
+
if (!validation.valid) {
|
|
668
|
+
if (shouldOutputJson()) {
|
|
669
|
+
outputJson({ success: false, error: { code: 'VALIDATION_FAILED', message: validation.errors.join(', ') } });
|
|
670
|
+
process.exit(1);
|
|
671
|
+
}
|
|
672
|
+
error(`验证失败: ${validation.errors.join(', ')}`);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const metadata = validation.metadata!;
|
|
677
|
+
const agentName = metadata.name!;
|
|
678
|
+
const client = new ApiClient(config.serverUrl, config.token);
|
|
679
|
+
|
|
680
|
+
if (shouldOutputJson()) {
|
|
681
|
+
info(`正在同步 Agent: ${agentName}...`);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
const result = await client.listAgents(agentName, 1, 1);
|
|
686
|
+
const existingAgent = result.agents.find(a => a.name === agentName);
|
|
687
|
+
|
|
688
|
+
if (!existingAgent) {
|
|
689
|
+
if (shouldOutputJson()) {
|
|
690
|
+
outputJson({ success: false, error: { code: 'NOT_FOUND', message: `Agent "${agentName}" 不存在,请先使用 arm agent create --from 创建` } });
|
|
691
|
+
process.exit(1);
|
|
692
|
+
}
|
|
693
|
+
error(`Agent "${agentName}" 不存在,请先使用 arm agent create --from 创建`);
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const cloudAgent = await client.getAgent(existingAgent.id);
|
|
698
|
+
|
|
699
|
+
const localSkills = await parseLocalSkills(folderPath);
|
|
700
|
+
const localKnowledges = await parseLocalKnowledges(folderPath);
|
|
701
|
+
|
|
702
|
+
const cloudSkillBindings = cloudAgent.skills || [];
|
|
703
|
+
const cloudKnowledgeBindings = cloudAgent.knowledges || [];
|
|
704
|
+
|
|
705
|
+
const changes: {
|
|
706
|
+
metadataChanged: boolean;
|
|
707
|
+
newSkills: LocalSkill[];
|
|
708
|
+
removedSkills: string[];
|
|
709
|
+
newKnowledges: LocalKnowledge[];
|
|
710
|
+
removedKnowledges: string[];
|
|
711
|
+
} = {
|
|
712
|
+
metadataChanged: false,
|
|
713
|
+
newSkills: [],
|
|
714
|
+
removedSkills: [],
|
|
715
|
+
newKnowledges: [],
|
|
716
|
+
removedKnowledges: [],
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
if (cloudAgent.prompt !== metadata.prompt || cloudAgent.description !== metadata.description) {
|
|
720
|
+
changes.metadataChanged = true;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const cloudSkillNames = new Set(cloudSkillBindings.map(s => s.skill?.name).filter(Boolean));
|
|
724
|
+
for (const localSkill of localSkills) {
|
|
725
|
+
const existingBinding = cloudSkillBindings.find(
|
|
726
|
+
sb => sb.skill?.name === localSkill.name && sb.version === localSkill.version
|
|
727
|
+
);
|
|
728
|
+
if (!existingBinding) {
|
|
729
|
+
changes.newSkills.push(localSkill);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const localSkillNames = new Set(localSkills.map(s => s.name));
|
|
733
|
+
for (const cloudBinding of cloudSkillBindings) {
|
|
734
|
+
if (cloudBinding.skill?.name && !localSkillNames.has(cloudBinding.skill.name)) {
|
|
735
|
+
changes.removedSkills.push(cloudBinding.skill.name);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const cloudKnowledgeNames = new Set(cloudKnowledgeBindings.map(k => k.knowledge?.name).filter(Boolean));
|
|
740
|
+
for (const localKnowledge of localKnowledges) {
|
|
741
|
+
const existingBinding = cloudKnowledgeBindings.find(
|
|
742
|
+
kb => kb.knowledge?.name === localKnowledge.name && kb.version === localKnowledge.version
|
|
743
|
+
);
|
|
744
|
+
if (!existingBinding) {
|
|
745
|
+
changes.newKnowledges.push(localKnowledge);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const localKnowledgeNames = new Set(localKnowledges.map(k => k.name));
|
|
749
|
+
for (const cloudBinding of cloudKnowledgeBindings) {
|
|
750
|
+
if (cloudBinding.knowledge?.name && !localKnowledgeNames.has(cloudBinding.knowledge.name)) {
|
|
751
|
+
changes.removedKnowledges.push(cloudBinding.knowledge.name);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (options.dryRun) {
|
|
756
|
+
if (shouldOutputJson()) {
|
|
757
|
+
outputJson({
|
|
758
|
+
success: true,
|
|
759
|
+
data: {
|
|
760
|
+
agentName,
|
|
761
|
+
agentId: existingAgent.id,
|
|
762
|
+
changes,
|
|
763
|
+
dryRun: true,
|
|
764
|
+
},
|
|
765
|
+
});
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
console.log(`\n[DRY-RUN] 预览 ${agentName} 的变更:\n`);
|
|
769
|
+
if (changes.metadataChanged) {
|
|
770
|
+
console.log(' 元信息: 将更新 (prompt/description 变更)');
|
|
771
|
+
}
|
|
772
|
+
if (changes.newSkills.length > 0) {
|
|
773
|
+
console.log(` 新增 Skills: ${changes.newSkills.map(s => `${s.name}@${s.version}`).join(', ')}`);
|
|
774
|
+
}
|
|
775
|
+
if (changes.removedSkills.length > 0) {
|
|
776
|
+
console.log(` 移除 Skills: ${changes.removedSkills.join(', ')}`);
|
|
777
|
+
}
|
|
778
|
+
if (changes.newKnowledges.length > 0) {
|
|
779
|
+
console.log(` 新增 Knowledges: ${changes.newKnowledges.map(k => `${k.name}@${k.version}`).join(', ')}`);
|
|
780
|
+
}
|
|
781
|
+
if (changes.removedKnowledges.length > 0) {
|
|
782
|
+
console.log(` 移除 Knowledges: ${changes.removedKnowledges.join(', ')}`);
|
|
783
|
+
}
|
|
784
|
+
if (!changes.metadataChanged && changes.newSkills.length === 0 && changes.removedSkills.length === 0 &&
|
|
785
|
+
changes.newKnowledges.length === 0 && changes.removedKnowledges.length === 0) {
|
|
786
|
+
console.log(' 无变更');
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (changes.metadataChanged) {
|
|
792
|
+
if (shouldOutputJson()) {
|
|
793
|
+
info('更新 Agent 元信息...');
|
|
794
|
+
}
|
|
795
|
+
await client.updateAgent(existingAgent.id, {
|
|
796
|
+
prompt: metadata.prompt,
|
|
797
|
+
description: metadata.description,
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
for (const skill of changes.newSkills) {
|
|
802
|
+
if (shouldOutputJson()) {
|
|
803
|
+
info(`上传并绑定新技能: ${skill.name} (版本自动分配)`);
|
|
804
|
+
}
|
|
805
|
+
const skillTempDir = mkdtempSync('/tmp/skill-sync-');
|
|
806
|
+
const zipPath = join(skillTempDir, `${skill.name}.zip`);
|
|
807
|
+
execSync(`cd "${skill.path}" && zip -r "${zipPath}" . -x ".*"`, { stdio: 'pipe' });
|
|
808
|
+
try {
|
|
809
|
+
const uploadedSkill = await client.uploadSkill(zipPath);
|
|
810
|
+
await client.bindSkillToAgent(existingAgent.id, uploadedSkill.id, undefined);
|
|
811
|
+
} finally {
|
|
812
|
+
rmSync(skillTempDir, { recursive: true, force: true });
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
for (const skillName of changes.removedSkills) {
|
|
817
|
+
const binding = cloudSkillBindings.find(sb => sb.skill?.name === skillName);
|
|
818
|
+
if (binding) {
|
|
819
|
+
if (shouldOutputJson()) {
|
|
820
|
+
info(`解绑技能: ${skillName}`);
|
|
821
|
+
}
|
|
822
|
+
await client.unbindSkillFromAgent(existingAgent.id, binding.skillId, binding.version);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
for (const knowledge of changes.newKnowledges) {
|
|
827
|
+
if (shouldOutputJson()) {
|
|
828
|
+
info(`上传并绑定新知识: ${knowledge.name} (版本自动分配)`);
|
|
829
|
+
}
|
|
830
|
+
const uploadedKnowledge = await client.uploadKnowledge(knowledge.path);
|
|
831
|
+
await client.bindKnowledgeToAgent(existingAgent.id, uploadedKnowledge.id, undefined);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
for (const knowledgeName of changes.removedKnowledges) {
|
|
835
|
+
const binding = cloudKnowledgeBindings.find(kb => kb.knowledge?.name === knowledgeName);
|
|
836
|
+
if (binding) {
|
|
837
|
+
if (shouldOutputJson()) {
|
|
838
|
+
info(`解绑知识: ${knowledgeName}`);
|
|
839
|
+
}
|
|
840
|
+
await client.unbindKnowledgeFromAgent(existingAgent.id, binding.knowledgeId, binding.version);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (shouldOutputJson()) {
|
|
845
|
+
outputJson({
|
|
846
|
+
success: true,
|
|
847
|
+
data: {
|
|
848
|
+
agentName,
|
|
849
|
+
agentId: existingAgent.id,
|
|
850
|
+
changes,
|
|
851
|
+
},
|
|
852
|
+
});
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
success(`Agent "${agentName}" 同步完成`);
|
|
856
|
+
const changeCount = (changes.metadataChanged ? 1 : 0) + changes.newSkills.length +
|
|
857
|
+
changes.removedSkills.length + changes.newKnowledges.length + changes.removedKnowledges.length;
|
|
858
|
+
if (changeCount === 0) {
|
|
859
|
+
info('无变更');
|
|
860
|
+
} else {
|
|
861
|
+
console.log(` 更新了 ${changeCount} 项`);
|
|
862
|
+
}
|
|
863
|
+
} catch (err) {
|
|
864
|
+
if (shouldOutputJson()) {
|
|
865
|
+
outputJson({ success: false, error: { code: 'SYNC_FAILED', message: err instanceof Error ? err.message : '未知错误' } });
|
|
866
|
+
process.exit(1);
|
|
867
|
+
}
|
|
868
|
+
error(`同步失败: ${err instanceof Error ? err.message : '未知错误'}`);
|
|
869
|
+
process.exit(1);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
async function parseLocalSkills(folderPath: string): Promise<LocalSkill[]> {
|
|
874
|
+
const skills: LocalSkill[] = [];
|
|
875
|
+
const skillsDir = join(folderPath, 'skills');
|
|
876
|
+
|
|
877
|
+
if (!existsSync(skillsDir) || !statSync(skillsDir).isDirectory()) {
|
|
878
|
+
return skills;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
try {
|
|
882
|
+
const skillDirs = execSync(`ls -1 "${skillsDir}"`, { encoding: 'utf-8' })
|
|
883
|
+
.split('\n')
|
|
884
|
+
.filter(l => l.trim() && existsSync(join(skillsDir, l)) && statSync(join(skillsDir, l)).isDirectory());
|
|
885
|
+
|
|
886
|
+
for (const skillDir of skillDirs) {
|
|
887
|
+
const skillPath = join(skillsDir, skillDir);
|
|
888
|
+
const skillMdPath = join(skillPath, 'SKILL.md');
|
|
889
|
+
|
|
890
|
+
if (existsSync(skillMdPath)) {
|
|
891
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
892
|
+
const versionMatch = content.match(/^---\n[\s\S]*?version:\s*(.+)\n/);
|
|
893
|
+
const version = versionMatch ? versionMatch[1].trim() : '1.0.0';
|
|
894
|
+
skills.push({ name: skillDir, path: skillPath, version });
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return skills;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async function parseLocalKnowledges(folderPath: string): Promise<LocalKnowledge[]> {
|
|
904
|
+
const knowledges: LocalKnowledge[] = [];
|
|
905
|
+
const knowledgesDir = join(folderPath, 'knowledges');
|
|
906
|
+
|
|
907
|
+
if (!existsSync(knowledgesDir) || !statSync(knowledgesDir).isDirectory()) {
|
|
908
|
+
return knowledges;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
try {
|
|
912
|
+
const mdFiles = execSync(`ls -1 "${knowledgesDir}"`, { encoding: 'utf-8' })
|
|
913
|
+
.split('\n')
|
|
914
|
+
.filter(l => l.trim().endsWith('.md'));
|
|
915
|
+
|
|
916
|
+
for (const mdFile of mdFiles) {
|
|
917
|
+
const mdPath = join(knowledgesDir, mdFile);
|
|
918
|
+
const name = mdFile.replace('.md', '');
|
|
919
|
+
const content = readFileSync(mdPath, 'utf-8');
|
|
920
|
+
const versionMatch = content.match(/^---\n[\s\S]*?version:\s*(.+)\n/);
|
|
921
|
+
const version = versionMatch ? versionMatch[1].trim() : '1.0.0';
|
|
922
|
+
knowledges.push({ name, path: mdPath, version });
|
|
923
|
+
}
|
|
924
|
+
} catch {
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return knowledges;
|
|
627
928
|
}
|
package/src/lib/client.ts
CHANGED
|
@@ -327,18 +327,22 @@ export class ApiClient {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
async bindSkillToAgent(agentId: string, skillId: string, config?: Record<string, unknown>): Promise<void> {
|
|
330
|
+
async bindSkillToAgent(agentId: string, skillId: string, version: string, config?: Record<string, unknown>): Promise<void> {
|
|
331
331
|
const res = await this.request<null>(`/agents/${agentId}/skills`, {
|
|
332
332
|
method: 'POST',
|
|
333
|
-
body: JSON.stringify({ skillId, config }),
|
|
333
|
+
body: JSON.stringify({ skillId, version, config }),
|
|
334
334
|
});
|
|
335
335
|
if (!res.ok) {
|
|
336
336
|
throw new Error(res.msg);
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
async unbindSkillFromAgent(agentId: string, skillId: string): Promise<void> {
|
|
341
|
-
|
|
340
|
+
async unbindSkillFromAgent(agentId: string, skillId: string, version?: string): Promise<void> {
|
|
341
|
+
let path = `/agents/${agentId}/skills?skillId=${skillId}`;
|
|
342
|
+
if (version) {
|
|
343
|
+
path += `&version=${version}`;
|
|
344
|
+
}
|
|
345
|
+
const res = await this.request<null>(path, {
|
|
342
346
|
method: 'DELETE',
|
|
343
347
|
});
|
|
344
348
|
if (!res.ok) {
|
|
@@ -346,18 +350,22 @@ export class ApiClient {
|
|
|
346
350
|
}
|
|
347
351
|
}
|
|
348
352
|
|
|
349
|
-
async bindKnowledgeToAgent(agentId: string, knowledgeId: string, retrievalConfig?: { topK?: number; similarityThreshold?: number }): Promise<void> {
|
|
353
|
+
async bindKnowledgeToAgent(agentId: string, knowledgeId: string, version: string, retrievalConfig?: { topK?: number; similarityThreshold?: number }): Promise<void> {
|
|
350
354
|
const res = await this.request<null>(`/agents/${agentId}/knowledges`, {
|
|
351
355
|
method: 'POST',
|
|
352
|
-
body: JSON.stringify({ knowledgeId, retrievalConfig }),
|
|
356
|
+
body: JSON.stringify({ knowledgeId, version, retrievalConfig }),
|
|
353
357
|
});
|
|
354
358
|
if (!res.ok) {
|
|
355
359
|
throw new Error(res.msg);
|
|
356
360
|
}
|
|
357
361
|
}
|
|
358
362
|
|
|
359
|
-
async unbindKnowledgeFromAgent(agentId: string, knowledgeId: string): Promise<void> {
|
|
360
|
-
|
|
363
|
+
async unbindKnowledgeFromAgent(agentId: string, knowledgeId: string, version?: string): Promise<void> {
|
|
364
|
+
let path = `/agents/${agentId}/knowledges?knowledgeId=${knowledgeId}`;
|
|
365
|
+
if (version) {
|
|
366
|
+
path += `&version=${version}`;
|
|
367
|
+
}
|
|
368
|
+
const res = await this.request<null>(path, {
|
|
361
369
|
method: 'DELETE',
|
|
362
370
|
});
|
|
363
371
|
if (!res.ok) {
|
|
@@ -369,4 +377,31 @@ export class ApiClient {
|
|
|
369
377
|
const result = await this.listAgents(name, 1, 1);
|
|
370
378
|
return result.agents.find(a => a.name === name) || null;
|
|
371
379
|
}
|
|
380
|
+
|
|
381
|
+
async getAgentBindingsHistory(agentId: string): Promise<{
|
|
382
|
+
skillBindings: Array<{
|
|
383
|
+
id: string;
|
|
384
|
+
skillId: string;
|
|
385
|
+
skillName: string;
|
|
386
|
+
version: string;
|
|
387
|
+
config: Record<string, unknown> | null;
|
|
388
|
+
createdAt: string;
|
|
389
|
+
deletedAt: string | null;
|
|
390
|
+
}>;
|
|
391
|
+
knowledgeBindings: Array<{
|
|
392
|
+
id: string;
|
|
393
|
+
knowledgeId: string;
|
|
394
|
+
knowledgeName: string;
|
|
395
|
+
version: string;
|
|
396
|
+
retrievalConfig: Record<string, unknown> | null;
|
|
397
|
+
createdAt: string;
|
|
398
|
+
deletedAt: string | null;
|
|
399
|
+
}>;
|
|
400
|
+
}> {
|
|
401
|
+
const res = await this.request<any>(`/agents/${agentId}/bindings/history`);
|
|
402
|
+
if (!res.ok) {
|
|
403
|
+
throw new Error(res.msg);
|
|
404
|
+
}
|
|
405
|
+
return res.data;
|
|
406
|
+
}
|
|
372
407
|
}
|
package/src/main.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
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, createAgentFromFolder } from './cmd/agent';
|
|
3
|
+
import { listAgents, searchAgents, infoAgent, downloadAgent, createAgent, updateAgent, deleteAgent, bindSkill, unbindSkill, bindKnowledge, unbindKnowledge, createAgentFromFolder, syncAgent } 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';
|
|
7
7
|
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
|
|
8
11
|
const args = process.argv.slice(2);
|
|
9
12
|
const command = args[0];
|
|
10
13
|
const subCommand = args[1];
|
|
11
14
|
|
|
15
|
+
const VERSION = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8')).version;
|
|
16
|
+
|
|
12
17
|
async function main() {
|
|
18
|
+
if (command === '--version' || command === '-v') {
|
|
19
|
+
console.log(`arm v${VERSION}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
switch (command) {
|
|
14
24
|
case 'register':
|
|
15
25
|
if (args[1] && args[1].startsWith('--')) {
|
|
@@ -306,6 +316,7 @@ async function main() {
|
|
|
306
316
|
case 'bind':
|
|
307
317
|
if (!args[2]) {
|
|
308
318
|
console.error('用法: arm agent bind <id> --skill=<skillId> [--skill-config=\'{...}\'] 或 arm agent bind <id> --knowledge=<knowledgeId> [--knowledge-config=\'{...}\'] [--json]');
|
|
319
|
+
console.error('注意: version 缺省时自动绑定新版本');
|
|
309
320
|
process.exit(1);
|
|
310
321
|
}
|
|
311
322
|
{
|
|
@@ -329,9 +340,9 @@ async function main() {
|
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
if (skillId) {
|
|
332
|
-
await bindSkill(id, skillId, skillConfig);
|
|
343
|
+
await bindSkill(id, skillId, undefined, skillConfig);
|
|
333
344
|
} else if (knowledgeId) {
|
|
334
|
-
await bindKnowledge(id, knowledgeId, knowledgeConfig);
|
|
345
|
+
await bindKnowledge(id, knowledgeId, undefined, knowledgeConfig);
|
|
335
346
|
} else {
|
|
336
347
|
console.error('用法: arm agent bind <id> --skill=<skillId> 或 --knowledge=<knowledgeId>');
|
|
337
348
|
process.exit(1);
|
|
@@ -340,13 +351,15 @@ async function main() {
|
|
|
340
351
|
break;
|
|
341
352
|
case 'unbind':
|
|
342
353
|
if (!args[2]) {
|
|
343
|
-
console.error('用法: arm agent unbind <id> --skill=<skillId> 或 --knowledge=<knowledgeId> [--json]');
|
|
354
|
+
console.error('用法: arm agent unbind <id> --skill=<skillId> [--version=<ver>] 或 --knowledge=<knowledgeId> [--version=<ver>] [--json]');
|
|
344
355
|
process.exit(1);
|
|
345
356
|
}
|
|
346
357
|
{
|
|
347
358
|
const id = args[2];
|
|
348
359
|
let skillId: string | undefined;
|
|
349
360
|
let knowledgeId: string | undefined;
|
|
361
|
+
let skillVersion: string | undefined;
|
|
362
|
+
let knowledgeVersion: string | undefined;
|
|
350
363
|
|
|
351
364
|
for (let i = 3; i < args.length; i++) {
|
|
352
365
|
const arg = args[i];
|
|
@@ -354,19 +367,53 @@ async function main() {
|
|
|
354
367
|
skillId = arg.split('=').slice(1).join('=');
|
|
355
368
|
} else if (arg.startsWith('--knowledge=')) {
|
|
356
369
|
knowledgeId = arg.split('=').slice(1).join('=');
|
|
370
|
+
} else if (arg.startsWith('--version=')) {
|
|
371
|
+
const ver = arg.split('=').slice(1).join('=');
|
|
372
|
+
if (skillId) skillVersion = ver;
|
|
373
|
+
if (knowledgeId) knowledgeVersion = ver;
|
|
357
374
|
}
|
|
358
375
|
}
|
|
359
376
|
|
|
360
377
|
if (skillId) {
|
|
361
|
-
await unbindSkill(id, skillId);
|
|
378
|
+
await unbindSkill(id, skillId, skillVersion);
|
|
362
379
|
} else if (knowledgeId) {
|
|
363
|
-
await unbindKnowledge(id, knowledgeId);
|
|
380
|
+
await unbindKnowledge(id, knowledgeId, knowledgeVersion);
|
|
364
381
|
} else {
|
|
365
382
|
console.error('用法: arm agent unbind <id> --skill=<skillId> 或 --knowledge=<knowledgeId>');
|
|
366
383
|
process.exit(1);
|
|
367
384
|
}
|
|
368
385
|
}
|
|
369
386
|
break;
|
|
387
|
+
case 'sync':
|
|
388
|
+
if (!args[2]) {
|
|
389
|
+
console.error('用法: arm agent sync <folder> [--dry-run] [--force] [--json]');
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
{
|
|
393
|
+
const folder = args[2];
|
|
394
|
+
const syncOptions: { dryRun?: boolean; force?: boolean } = {};
|
|
395
|
+
|
|
396
|
+
for (let i = 3; i < args.length; i++) {
|
|
397
|
+
const arg = args[i];
|
|
398
|
+
if (arg === '--dry-run') {
|
|
399
|
+
syncOptions.dryRun = true;
|
|
400
|
+
} else if (arg === '--force') {
|
|
401
|
+
syncOptions.force = true;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
await syncAgent(folder, syncOptions);
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
case 'bindings':
|
|
409
|
+
if (!args[2]) {
|
|
410
|
+
console.error('用法: arm agent bindings <name> [--history] [--json]');
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
if (subCommand === 'history') {
|
|
414
|
+
console.log('bindings history 需要通过 info 命令查看');
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
370
417
|
default:
|
|
371
418
|
console.log(`
|
|
372
419
|
可用命令:
|
|
@@ -378,10 +425,11 @@ async function main() {
|
|
|
378
425
|
arm agent create --from=<folder> 从本地文件夹创建 Agent
|
|
379
426
|
arm agent update <id> 更新 Agent (--name, --description, --prompt, --avatar, --status)
|
|
380
427
|
arm agent delete <id> 删除 Agent
|
|
381
|
-
arm agent bind <id> --skill=<id>
|
|
382
|
-
arm agent unbind <id> --skill=<id> 解绑 Skill
|
|
383
|
-
arm agent bind <id> --knowledge=<id> 绑定 Knowledge 到 Agent
|
|
384
|
-
arm agent unbind <id> --knowledge=<id> 解绑 Knowledge
|
|
428
|
+
arm agent bind <id> --skill=<id> [--version=<ver>] 绑定 Skill 到 Agent
|
|
429
|
+
arm agent unbind <id> --skill=<id> [--version=<ver>] 解绑 Skill
|
|
430
|
+
arm agent bind <id> --knowledge=<id> [--version=<ver>] 绑定 Knowledge 到 Agent
|
|
431
|
+
arm agent unbind <id> --knowledge=<id> [--version=<ver>] 解绑 Knowledge
|
|
432
|
+
arm agent sync <folder> [--dry-run] 同步本地文件夹到云端 Agent
|
|
385
433
|
所有命令支持 --json 参数获取机器可读输出
|
|
386
434
|
`);
|
|
387
435
|
}
|
package/dist/test.md
DELETED
package/dist/test2.md
DELETED