Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Luke The Dev
2026-03-23 11:44:25 -05:00
committed by GitHub
parent c2cbdeec44
commit 5e7812c352
30 changed files with 2213 additions and 128 deletions
+44
View File
@@ -0,0 +1,44 @@
import { readFileSync, readdirSync } from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { buildPackagedSkillStatusEntry, listPackagedSkills } from "@/lib/skills/catalog";
import { resolveSkillMarketplaceMetadata } from "@/lib/skills/marketplace";
import { readPackagedSkillFiles } from "@/lib/skills/packaged";
const resolveAssetDir = (packageId: string) => path.join(process.cwd(), "assets/skills", packageId);
describe("packaged skills", () => {
it("keeps packaged skill files synchronized with the asset source files", () => {
for (const packagedSkill of listPackagedSkills()) {
const assetDir = resolveAssetDir(packagedSkill.packageId);
const expectedFiles = readdirSync(assetDir, { withFileTypes: true })
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
.sort();
const packagedFiles = readPackagedSkillFiles(packagedSkill.packageId);
const packagedRelativePaths = packagedFiles.map((file) => file.relativePath).sort();
expect(packagedRelativePaths).toEqual(expectedFiles);
for (const file of packagedFiles) {
const assetContent = readFileSync(path.join(assetDir, file.relativePath), "utf8");
expect(file.content).toBe(assetContent);
}
}
});
it("exposes creator attribution and hides fake popularity stats for packaged skills", () => {
for (const packagedSkill of listPackagedSkills()) {
expect(packagedSkill.creatorName).toBeTruthy();
expect(packagedSkill.creatorUrl).toBeTruthy();
const metadata = resolveSkillMarketplaceMetadata(buildPackagedSkillStatusEntry(packagedSkill));
expect(metadata.poweredByName).toBe(packagedSkill.creatorName);
expect(metadata.poweredByUrl).toBe(packagedSkill.creatorUrl);
expect(metadata.hideStats).toBe(true);
}
});
});
+76
View File
@@ -0,0 +1,76 @@
import { describe, expect, it } from "vitest";
import {
buildOfficeSkillTriggerHoldMaps,
DEFAULT_SKILL_TRIGGER_FALLBACKS_BY_SKILL_KEY,
OFFICE_SKILL_TRIGGER_PLACE_REGISTRY,
} from "@/lib/office/places";
import {
listPackagedSkillTriggerDefinitions,
resolveTriggeredSkillDefinition,
} from "@/lib/skills/triggers";
describe("skill triggers", () => {
it("parses packaged skill trigger definitions from SKILL.md", () => {
const todoTrigger = listPackagedSkillTriggerDefinitions().find(
(entry) => entry.skillKey === "todo-board",
);
expect(todoTrigger).not.toBeUndefined();
expect(todoTrigger?.movementTarget).toBe("desk");
expect(todoTrigger?.activationPhrases).toContain("todo");
expect(todoTrigger?.activationPhrases).toContain("blocked tasks");
});
it("matches the running agent's latest request against enabled skill triggers", () => {
const todoTrigger = listPackagedSkillTriggerDefinitions().find(
(entry) => entry.skillKey === "todo-board",
);
const matched = resolveTriggeredSkillDefinition({
isAgentRunning: true,
lastUserMessage: "On telegram, add this to my todo list.",
transcriptEntries: [],
triggers: todoTrigger ? [todoTrigger] : [],
});
expect(matched?.skillKey).toBe("todo-board");
expect(matched?.movementTarget).toBe("desk");
});
it("does not match triggers when the agent is not running", () => {
const todoTrigger = listPackagedSkillTriggerDefinitions().find(
(entry) => entry.skillKey === "todo-board",
);
const matched = resolveTriggeredSkillDefinition({
isAgentRunning: false,
lastUserMessage: "Add this to my todo list.",
transcriptEntries: [],
triggers: todoTrigger ? [todoTrigger] : [],
});
expect(matched).toBeNull();
});
it("keeps trigger places and fallback definitions in one central registry", () => {
expect(OFFICE_SKILL_TRIGGER_PLACE_REGISTRY.desk.interactionTarget).toBe("desk");
expect(OFFICE_SKILL_TRIGGER_PLACE_REGISTRY.github.interactionTarget).toBe("server_room");
expect(DEFAULT_SKILL_TRIGGER_FALLBACKS_BY_SKILL_KEY["todo-board"]?.movementTarget).toBe("desk");
});
it("builds animation hold maps from the central place registry", () => {
const holdMaps = buildOfficeSkillTriggerHoldMaps({
"agent-a": "desk",
"agent-b": "github",
"agent-c": "gym",
"agent-d": "qa_lab",
});
expect(holdMaps.deskHoldByAgentId).toEqual({ "agent-a": true });
expect(holdMaps.githubHoldByAgentId).toEqual({ "agent-b": true });
expect(holdMaps.gymHoldByAgentId).toEqual({ "agent-c": true });
expect(holdMaps.qaHoldByAgentId).toEqual({ "agent-d": true });
expect(holdMaps.skillGymHoldByAgentId).toEqual({ "agent-c": true });
});
});
+127
View File
@@ -0,0 +1,127 @@
import { describe, expect, it, vi } from "vitest";
import type { GatewayClient } from "@/lib/gateway/GatewayClient";
import { installPackagedSkillViaGatewayAgent } from "@/lib/skills/install-gateway";
describe("skills install gateway", () => {
it("creates a temporary installer agent and installs a workspace skill", async () => {
const call = vi.fn(async (method: string) => {
if (method === "agents.create") {
return { agentId: "installer-1" };
}
if (method === "config.get") {
return {
exists: true,
hash: "hash-1",
config: {
agents: {
list: [{ id: "installer-1", tools: {} }],
},
},
};
}
if (method === "config.set") {
return { ok: true };
}
if (method === "config.patch") {
return { ok: true };
}
if (method === "agents.list") {
return { mainKey: "main" };
}
if (method === "chat.send") {
return { runId: "run-1", status: "started" };
}
if (method === "agent.wait") {
return { ok: true };
}
throw new Error(`Unexpected method: ${method}`);
});
const result = await installPackagedSkillViaGatewayAgent({
client: { call } as unknown as GatewayClient,
request: {
packageId: "todo-board",
source: "openclaw-workspace",
workspaceDir: "/home/openclaw/workspace-demo",
managedSkillsDir: "/home/openclaw/.openclaw/skills",
},
});
expect(result).toEqual({
installed: true,
installedPath: "/home/openclaw/workspace-demo/skills/todo-board",
source: "openclaw-workspace",
skillKey: "todo-board",
});
expect(call).toHaveBeenCalledWith("agents.create", {
name: expect.stringContaining("Skill Installer"),
workspace: "/home/openclaw/workspace-demo",
});
expect(call).toHaveBeenCalledWith(
"chat.send",
expect.objectContaining({
sessionKey: "agent:installer-1:main",
deliver: false,
})
);
expect(call).toHaveBeenCalledWith("agent.wait", { runId: "run-1", timeoutMs: 60_000 });
expect(call).toHaveBeenCalledWith(
"config.patch",
expect.objectContaining({
baseHash: "hash-1",
})
);
});
it("cleans up the temporary installer agent when install fails", async () => {
const call = vi.fn(async (method: string) => {
if (method === "agents.create") {
return { agentId: "installer-2" };
}
if (method === "config.get") {
return {
exists: true,
hash: "hash-2",
config: {
agents: {
list: [{ id: "installer-2", tools: {} }],
},
},
};
}
if (method === "config.set") {
return { ok: true };
}
if (method === "agents.list") {
return { mainKey: "main" };
}
if (method === "chat.send") {
throw new Error("chat failed");
}
if (method === "config.patch") {
return { ok: true };
}
throw new Error(`Unexpected method: ${method}`);
});
await expect(
installPackagedSkillViaGatewayAgent({
client: { call } as unknown as GatewayClient,
request: {
packageId: "todo-board",
source: "openclaw-workspace",
workspaceDir: "/home/openclaw/workspace-demo",
managedSkillsDir: "/home/openclaw/.openclaw/skills",
},
})
).rejects.toThrow("chat failed");
expect(call).toHaveBeenCalledWith(
"config.patch",
expect.objectContaining({
baseHash: "hash-2",
})
);
});
});
+18 -19
View File
@@ -1,28 +1,26 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { removeSkillFromGateway } from "@/lib/skills/remove";
import { removeSkillViaGatewayAgent } from "@/lib/skills/remove-gateway";
vi.mock("@/lib/skills/remove-gateway", () => ({
removeSkillViaGatewayAgent: vi.fn(),
}));
describe("skills remove client", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("posts skill removal payload to the Studio API route", async () => {
const fetchMock = vi.fn(async () => ({
ok: true,
text: async () =>
JSON.stringify({
result: {
removed: true,
removedPath: "/tmp/workspace/skills/github",
source: "openclaw-workspace",
},
}),
}));
vi.stubGlobal("fetch", fetchMock);
it("delegates sanitized skill removal payloads to the gateway-native remover", async () => {
vi.mocked(removeSkillViaGatewayAgent).mockResolvedValueOnce({
removed: true,
removedPath: "/tmp/workspace/skills/github",
source: "openclaw-workspace",
});
const result = await removeSkillFromGateway({
client: { call: vi.fn() } as never,
skillKey: " github ",
source: "openclaw-workspace",
baseDir: " /tmp/workspace/skills/github ",
@@ -30,16 +28,15 @@ describe("skills remove client", () => {
managedSkillsDir: " /tmp/managed ",
});
expect(fetchMock).toHaveBeenCalledWith("/api/gateway/skills/remove", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
expect(removeSkillViaGatewayAgent).toHaveBeenCalledWith({
client: expect.any(Object),
request: {
skillKey: "github",
source: "openclaw-workspace",
baseDir: "/tmp/workspace/skills/github",
workspaceDir: "/tmp/workspace",
managedSkillsDir: "/tmp/managed",
}),
},
});
expect(result).toEqual({
removed: true,
@@ -51,6 +48,7 @@ describe("skills remove client", () => {
it("fails fast when required payload fields are missing", async () => {
await expect(
removeSkillFromGateway({
client: { call: vi.fn() } as never,
skillKey: " ",
source: "openclaw-workspace",
baseDir: "/tmp/workspace/skills/github",
@@ -61,6 +59,7 @@ describe("skills remove client", () => {
await expect(
removeSkillFromGateway({
client: { call: vi.fn() } as never,
skillKey: "github",
source: "openclaw-workspace",
baseDir: " ",
+127
View File
@@ -0,0 +1,127 @@
import { describe, expect, it, vi } from "vitest";
import type { GatewayClient } from "@/lib/gateway/GatewayClient";
import { removeSkillViaGatewayAgent } from "@/lib/skills/remove-gateway";
describe("skills remove gateway", () => {
it("creates a temporary remover agent and removes a workspace skill", async () => {
const call = vi.fn(async (method: string) => {
if (method === "agents.create") {
return { agentId: "remover-1" };
}
if (method === "config.get") {
return {
exists: true,
hash: "hash-1",
config: {
agents: {
list: [{ id: "remover-1", tools: {} }],
},
},
};
}
if (method === "config.set") {
return { ok: true };
}
if (method === "config.patch") {
return { ok: true };
}
if (method === "agents.list") {
return { mainKey: "main" };
}
if (method === "chat.send") {
return { runId: "run-1", status: "started" };
}
if (method === "agent.wait") {
return { ok: true };
}
throw new Error(`Unexpected method: ${method}`);
});
const result = await removeSkillViaGatewayAgent({
client: { call } as unknown as GatewayClient,
request: {
skillKey: "todo-board",
source: "openclaw-workspace",
baseDir: "/home/openclaw/workspace-demo/skills/todo-board",
workspaceDir: "/home/openclaw/workspace-demo",
managedSkillsDir: "/home/openclaw/.openclaw/skills",
},
});
expect(result).toEqual({
removed: true,
removedPath: "/home/openclaw/workspace-demo/skills/todo-board",
source: "openclaw-workspace",
});
expect(call).toHaveBeenCalledWith("agents.create", {
name: expect.stringContaining("Skill Remover"),
workspace: "/home/openclaw/workspace-demo",
});
expect(call).toHaveBeenCalledWith(
"chat.send",
expect.objectContaining({
sessionKey: "agent:remover-1:main",
deliver: false,
}),
);
expect(call).toHaveBeenCalledWith("agent.wait", { runId: "run-1", timeoutMs: 60_000 });
expect(call).toHaveBeenCalledWith(
"config.patch",
expect.objectContaining({
baseHash: "hash-1",
}),
);
});
it("uses the managed skills directory as workspace for managed skill removal", async () => {
const call = vi.fn(async (method: string) => {
if (method === "agents.create") {
return { agentId: "remover-2" };
}
if (method === "config.get") {
return {
exists: true,
hash: "hash-2",
config: {
agents: {
list: [{ id: "remover-2", tools: {} }],
},
},
};
}
if (method === "config.set") {
return { ok: true };
}
if (method === "config.patch") {
return { ok: true };
}
if (method === "agents.list") {
return { mainKey: "main" };
}
if (method === "chat.send") {
return { runId: "run-2", status: "started" };
}
if (method === "agent.wait") {
return { ok: true };
}
throw new Error(`Unexpected method: ${method}`);
});
await removeSkillViaGatewayAgent({
client: { call } as unknown as GatewayClient,
request: {
skillKey: "github",
source: "openclaw-managed",
baseDir: "/home/openclaw/.openclaw/skills/github",
workspaceDir: "/home/openclaw/workspace-demo",
managedSkillsDir: "/home/openclaw/.openclaw/skills",
},
});
expect(call).toHaveBeenCalledWith("agents.create", {
name: expect.stringContaining("Skill Remover"),
workspace: "/home/openclaw/.openclaw/skills",
});
});
});