Spaces:
Paused
Paused
| import fs from "node:fs/promises"; | |
| import os from "node:os"; | |
| import path from "node:path"; | |
| import { afterEach, describe, expect, it } from "vitest"; | |
| import { | |
| listCodexSkills, | |
| syncCodexSkills, | |
| } from "@paperclipai/adapter-codex-local/server"; | |
| async function makeTempDir(prefix: string): Promise<string> { | |
| return fs.mkdtemp(path.join(os.tmpdir(), prefix)); | |
| } | |
| describe("codex local skill sync", () => { | |
| const paperclipKey = "paperclipai/paperclip/paperclip"; | |
| const cleanupDirs = new Set<string>(); | |
| afterEach(async () => { | |
| await Promise.all(Array.from(cleanupDirs).map((dir) => fs.rm(dir, { recursive: true, force: true }))); | |
| cleanupDirs.clear(); | |
| }); | |
| it("reports configured Paperclip skills for workspace injection on the next run", async () => { | |
| const codexHome = await makeTempDir("paperclip-codex-skill-sync-"); | |
| cleanupDirs.add(codexHome); | |
| const ctx = { | |
| agentId: "agent-1", | |
| companyId: "company-1", | |
| adapterType: "codex_local", | |
| config: { | |
| env: { | |
| CODEX_HOME: codexHome, | |
| }, | |
| paperclipSkillSync: { | |
| desiredSkills: [paperclipKey], | |
| }, | |
| }, | |
| } as const; | |
| const before = await listCodexSkills(ctx); | |
| expect(before.mode).toBe("ephemeral"); | |
| expect(before.desiredSkills).toContain(paperclipKey); | |
| expect(before.entries.find((entry) => entry.key === paperclipKey)?.required).toBe(true); | |
| expect(before.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("configured"); | |
| expect(before.entries.find((entry) => entry.key === paperclipKey)?.detail).toContain("CODEX_HOME/skills/"); | |
| }); | |
| it("does not persist Paperclip skills into CODEX_HOME during sync", async () => { | |
| const codexHome = await makeTempDir("paperclip-codex-skill-prune-"); | |
| cleanupDirs.add(codexHome); | |
| const configuredCtx = { | |
| agentId: "agent-2", | |
| companyId: "company-1", | |
| adapterType: "codex_local", | |
| config: { | |
| env: { | |
| CODEX_HOME: codexHome, | |
| }, | |
| paperclipSkillSync: { | |
| desiredSkills: [paperclipKey], | |
| }, | |
| }, | |
| } as const; | |
| const after = await syncCodexSkills(configuredCtx, [paperclipKey]); | |
| expect(after.mode).toBe("ephemeral"); | |
| expect(after.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("configured"); | |
| await expect(fs.lstat(path.join(codexHome, "skills", "paperclip"))).rejects.toMatchObject({ | |
| code: "ENOENT", | |
| }); | |
| }); | |
| it("keeps required bundled Paperclip skills configured even when the desired set is emptied", async () => { | |
| const codexHome = await makeTempDir("paperclip-codex-skill-required-"); | |
| cleanupDirs.add(codexHome); | |
| const configuredCtx = { | |
| agentId: "agent-2", | |
| companyId: "company-1", | |
| adapterType: "codex_local", | |
| config: { | |
| env: { | |
| CODEX_HOME: codexHome, | |
| }, | |
| paperclipSkillSync: { | |
| desiredSkills: [], | |
| }, | |
| }, | |
| } as const; | |
| const after = await syncCodexSkills(configuredCtx, []); | |
| expect(after.desiredSkills).toContain(paperclipKey); | |
| expect(after.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("configured"); | |
| }); | |
| it("normalizes legacy flat Paperclip skill refs before reporting configured state", async () => { | |
| const codexHome = await makeTempDir("paperclip-codex-legacy-skill-sync-"); | |
| cleanupDirs.add(codexHome); | |
| const snapshot = await listCodexSkills({ | |
| agentId: "agent-3", | |
| companyId: "company-1", | |
| adapterType: "codex_local", | |
| config: { | |
| env: { | |
| CODEX_HOME: codexHome, | |
| }, | |
| paperclipSkillSync: { | |
| desiredSkills: ["paperclip"], | |
| }, | |
| }, | |
| }); | |
| expect(snapshot.warnings).toEqual([]); | |
| expect(snapshot.desiredSkills).toContain(paperclipKey); | |
| expect(snapshot.desiredSkills).not.toContain("paperclip"); | |
| expect(snapshot.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("configured"); | |
| expect(snapshot.entries.find((entry) => entry.key === "paperclip")).toBeUndefined(); | |
| }); | |
| }); | |