mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
180 lines
5.8 KiB
TypeScript
180 lines
5.8 KiB
TypeScript
import { chmod, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { buildAgentDecl, StateManager } from "@openagentpack/sdk";
|
|
import { expect, test } from "vite-plus/test";
|
|
import {
|
|
buildAgentSkillRefs,
|
|
normalizeAgentKey,
|
|
replaceConfigAtomically,
|
|
selectAgentKey,
|
|
selectLocalSkillKey,
|
|
} from "../src/commands/managed-agent/agent/create.ts";
|
|
import {
|
|
normalizeResourceKey,
|
|
parseMetadata,
|
|
} from "../src/commands/managed-agent/_engine/scoped-create.ts";
|
|
|
|
function candidate(instructions = "help") {
|
|
return buildAgentDecl(undefined, {
|
|
name: "Assistant",
|
|
model: "qwen3.8-max",
|
|
instructions,
|
|
provider: "bailian",
|
|
}).agent;
|
|
}
|
|
|
|
test("Agent key 从显示名生成并保留 Unicode", () => {
|
|
expect(normalizeAgentKey(" Data Assistant ")).toBe("data-assistant");
|
|
expect(normalizeAgentKey("数据分析 助手")).toBe("数据分析-助手");
|
|
expect(normalizeAgentKey("***")).toBe("agent");
|
|
});
|
|
|
|
test("通用资源 key 与 metadata 输入保持稳定", () => {
|
|
expect(normalizeResourceKey(" Production Vault ", "vault")).toBe("production-vault");
|
|
expect(normalizeResourceKey("生产 环境", "environment")).toBe("生产-环境");
|
|
expect(normalizeResourceKey("***", "vault")).toBe("vault");
|
|
expect(parseMetadata(["owner=platform", "empty="])).toEqual({
|
|
owner: "platform",
|
|
empty: "",
|
|
});
|
|
expect(() => parseMetadata(["missing-separator"])).toThrow(/Invalid metadata/);
|
|
});
|
|
|
|
test("同名已跟踪 Agent 分配递增 key", () => {
|
|
const state = StateManager.initialize("/tmp/bailian-cli-agent-create-state.json");
|
|
state.setResource({
|
|
address: { type: "agent", name: "assistant", provider: "bailian" },
|
|
remote_id: "agent_existing",
|
|
content_hash: "hash",
|
|
});
|
|
|
|
expect(
|
|
selectAgentKey({
|
|
displayName: "Assistant",
|
|
provider: "bailian",
|
|
agents: {
|
|
assistant: candidate(),
|
|
"assistant-2": { ...candidate(), instructions: "other" },
|
|
},
|
|
candidate: candidate(),
|
|
state,
|
|
}),
|
|
).toEqual({ key: "assistant-3", reusedPending: false });
|
|
});
|
|
|
|
test("远端失败后复用完全相同的待创建声明", () => {
|
|
const state = StateManager.initialize("/tmp/bailian-cli-agent-create-pending-state.json");
|
|
|
|
expect(
|
|
selectAgentKey({
|
|
displayName: "Assistant",
|
|
provider: "bailian",
|
|
agents: { assistant: candidate() },
|
|
candidate: candidate(),
|
|
state,
|
|
}),
|
|
).toEqual({ key: "assistant", reusedPending: true });
|
|
});
|
|
|
|
test("同名待创建声明配置不同则创建新的逻辑 key", () => {
|
|
const state = StateManager.initialize("/tmp/bailian-cli-agent-create-different-state.json");
|
|
|
|
expect(
|
|
selectAgentKey({
|
|
displayName: "Assistant",
|
|
provider: "bailian",
|
|
agents: { assistant: candidate("old") },
|
|
candidate: candidate("new"),
|
|
state,
|
|
}),
|
|
).toEqual({ key: "assistant-2", reusedPending: false });
|
|
});
|
|
|
|
test("Agent Skill ID 默认生成 custom 外部引用并去重", () => {
|
|
expect(buildAgentSkillRefs(["skill_report", "skill_report", "skill_chart"])).toEqual([
|
|
{ type: "custom", skill_id: "skill_report" },
|
|
{ type: "custom", skill_id: "skill_chart" },
|
|
]);
|
|
});
|
|
|
|
test("Agent Skill ID 可生成 official 外部引用", () => {
|
|
expect(buildAgentSkillRefs(["skill_pptx"], "official")).toEqual([
|
|
{ type: "official", skill_id: "skill_pptx" },
|
|
]);
|
|
expect(() => buildAgentSkillRefs([" "])).toThrow(/must not be empty/);
|
|
});
|
|
|
|
test("本地 Skill 来源复用同声明的待创建或已跟踪 key", () => {
|
|
const declaration = {
|
|
name: "local-report",
|
|
source: "skills/local-report.zip",
|
|
origin: "custom",
|
|
provider: "bailian",
|
|
};
|
|
const pendingState = StateManager.initialize("/tmp/bailian-cli-local-skill-pending.json");
|
|
expect(
|
|
selectLocalSkillKey({
|
|
displayName: "local-report",
|
|
provider: "bailian",
|
|
declarations: { "local-report": declaration },
|
|
candidate: declaration,
|
|
state: pendingState,
|
|
}),
|
|
).toEqual({
|
|
key: "local-report",
|
|
needsCreate: true,
|
|
reusedPending: true,
|
|
reusedTracked: false,
|
|
});
|
|
|
|
const trackedState = StateManager.initialize("/tmp/bailian-cli-local-skill-tracked.json");
|
|
trackedState.setResource({
|
|
address: { type: "skill", name: "local-report", provider: "bailian" },
|
|
remote_id: "skill_local_report",
|
|
content_hash: "hash",
|
|
});
|
|
expect(
|
|
selectLocalSkillKey({
|
|
displayName: "local-report",
|
|
provider: "bailian",
|
|
declarations: { "local-report": declaration },
|
|
candidate: declaration,
|
|
state: trackedState,
|
|
}),
|
|
).toEqual({
|
|
key: "local-report",
|
|
needsCreate: false,
|
|
reusedPending: false,
|
|
reusedTracked: true,
|
|
});
|
|
expect(() =>
|
|
selectLocalSkillKey({
|
|
displayName: "local-report",
|
|
provider: "bailian",
|
|
declarations: { "local-report": declaration },
|
|
candidate: { ...declaration, source: "skills/new-local-report.zip" },
|
|
state: trackedState,
|
|
}),
|
|
).toThrow(/already tracked with a different declaration/);
|
|
});
|
|
|
|
test("YAML 替换校验原内容并保留文件权限", async () => {
|
|
const directory = await mkdtemp(join(tmpdir(), "bailian-agent-create-"));
|
|
const configPath = join(directory, "agents.yaml");
|
|
try {
|
|
await writeFile(configPath, "version: old\n", "utf8");
|
|
await chmod(configPath, 0o640);
|
|
await replaceConfigAtomically(configPath, "version: old\n", "version: new\n");
|
|
expect(await readFile(configPath, "utf8")).toBe("version: new\n");
|
|
expect((await stat(configPath)).mode & 0o777).toBe(0o640);
|
|
|
|
await expect(
|
|
replaceConfigAtomically(configPath, "version: stale\n", "version: overwritten\n"),
|
|
).rejects.toThrow(/changed while resource create was being prepared/);
|
|
expect(await readFile(configPath, "utf8")).toBe("version: new\n");
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
});
|