|
|
@@ -1,6 +1,6 @@
|
|
|
import { mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, writeFile } from 'node:fs/promises';
|
|
|
import { tmpdir } from 'node:os';
|
|
|
-import { join } from 'node:path';
|
|
|
+import { join, resolve } from 'node:path';
|
|
|
import { crc32 } from 'node:zlib';
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi, type TestContext } from 'vitest';
|
|
|
import {
|
|
|
@@ -317,6 +317,13 @@ describe('remove', () => {
|
|
|
expect(await stat(join(skillsDir, 'demo-skill')).catch(() => null)).toBeNull();
|
|
|
});
|
|
|
|
|
|
+ it('按 UI 回传的 SKILL.md 路径删除整个 skill 目录', async () => {
|
|
|
+ const source = await writeSkillSource(join(root, 'src'));
|
|
|
+ await service.installFromFolder(source);
|
|
|
+ await service.remove({ path: join(skillsDir, 'demo-skill', 'SKILL.md') });
|
|
|
+ expect(await stat(join(skillsDir, 'demo-skill')).catch(() => null)).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
it('拒绝删除内置目录', async () => {
|
|
|
const systemDir = join(skillsDir, '.system', 'imagegen');
|
|
|
await mkdir(systemDir, { recursive: true });
|
|
|
@@ -400,6 +407,27 @@ describe('read', () => {
|
|
|
expect(result.content).toContain('demo-skill');
|
|
|
expect(result.files.map((file) => file.path).sort()).toEqual(['SKILL.md', 'notes.md']);
|
|
|
});
|
|
|
+
|
|
|
+ it('内置 skill 可查(path 给目录或 SKILL.md 都认),但仍不可删改', async () => {
|
|
|
+ const systemDir = join(skillsDir, '.system', 'imagegen');
|
|
|
+ await mkdir(systemDir, { recursive: true });
|
|
|
+ await writeFile(join(systemDir, 'SKILL.md'), SKILL_MD, 'utf8');
|
|
|
+
|
|
|
+ // Codex 的 skills/list 回的是 SKILL.md 文件路径,UI 原样回传
|
|
|
+ for (const path of [join(systemDir, 'SKILL.md'), systemDir]) {
|
|
|
+ const result = await service.read({ path });
|
|
|
+ expect(result.dir).toBe(resolve(systemDir));
|
|
|
+ expect(result.content).toContain('demo-skill');
|
|
|
+ }
|
|
|
+
|
|
|
+ await expect(service.remove({ path: join(systemDir, 'SKILL.md') })).rejects.toThrow(/内置或暂存目录/);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('越界路径与缺 SKILL.md 仍被拒绝', async () => {
|
|
|
+ await expect(service.read({ path: root })).rejects.toThrow(/skills 目录/);
|
|
|
+ await expect(service.read({ path: join(skillsDir, 'nope') })).rejects.toThrow(/SKILL\.md/);
|
|
|
+ await expect(service.read({})).rejects.toThrow(/path 或 name/);
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
describe('assertSafeRelativePath(第二道防线)', () => {
|