Initialize repository with harness code and assets
Initial import of all source code, config, and README assets: the packages workspace (cli, core, server, web, docs, landing, skills), build scripts, tooling config, and CI workflows. Includes the data-layout revision made on this branch: the local data root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the installer keeps its binaries in ~/.penguin), and every Agent lives under <project>/agents/<agent>/ — path helpers, the three agent-enumeration scans, the system prompt, built-in Skills, tests and docs all follow the new layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
This commit is contained in:
1 parent
056bed7aeb
commit
45bfae6e94
543 files changed
+92949
No files matched your search
@@ -0,0 +1,84 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { assertValidId, isValidId, loadOrInitAgentState } from "../src/state/index.js";
|
||||
|
||||
describe("isValidId / assertValidId", () => {
|
||||
const valid = ["default_project", "default_agent", "my-agent", "agent-1", "ABC_123"];
|
||||
// Only alphanumerics, `_`, and `-` are allowed; dots, spaces/tabs, path separators, and any
|
||||
// other character are all rejected.
|
||||
const invalid = [
|
||||
"",
|
||||
" ",
|
||||
"a b",
|
||||
"a\tb",
|
||||
" lead",
|
||||
"trail ",
|
||||
"a/b",
|
||||
"a\\b",
|
||||
".",
|
||||
"..",
|
||||
"...",
|
||||
"a.b",
|
||||
"v1.0",
|
||||
".hidden",
|
||||
"中文ok",
|
||||
"emoji😀",
|
||||
];
|
||||
|
||||
it.each(valid)("接受合法单段目录名:%j", (id) => {
|
||||
expect(isValidId(id)).toBe(true);
|
||||
expect(() => assertValidId("project_id", id)).not.toThrow();
|
||||
expect(() => assertValidId("agent_id", id)).not.toThrow();
|
||||
});
|
||||
|
||||
it.each(invalid)("拒绝非法 id:%j", (id) => {
|
||||
expect(isValidId(id)).toBe(false);
|
||||
expect(() => assertValidId("agent_id", id)).toThrow();
|
||||
});
|
||||
|
||||
it("错误信息点明 kind 与非法 id", () => {
|
||||
expect(() => assertValidId("agent_id", "a/b")).toThrow(/agent_id/);
|
||||
expect(() => assertValidId("agent_id", "a/b")).toThrow(/a\/b/);
|
||||
expect(() => assertValidId("project_id", "..")).toThrow(/project_id/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadOrInitAgentState id 校验", () => {
|
||||
let tmpRoot: string;
|
||||
let prevHome: string | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
prevHome = process.env.PENGUIN_HOME;
|
||||
tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "penguin-validate-id-"));
|
||||
process.env.PENGUIN_HOME = tmpRoot;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (prevHome === undefined) {
|
||||
delete process.env.PENGUIN_HOME;
|
||||
} else {
|
||||
process.env.PENGUIN_HOME = prevHome;
|
||||
}
|
||||
await fs.rm(tmpRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("agentId 含路径分隔符时抛错", async () => {
|
||||
await expect(loadOrInitAgentState({ agentId: "a/b" })).rejects.toThrow(/agent_id/);
|
||||
});
|
||||
|
||||
it("projectId 为 .. 时抛错", async () => {
|
||||
await expect(loadOrInitAgentState({ projectId: ".." })).rejects.toThrow(/project_id/);
|
||||
});
|
||||
|
||||
it("agentId 为 Windows 结尾空格变体 '.. ' 时抛错(防路径穿越绕过)", async () => {
|
||||
await expect(loadOrInitAgentState({ agentId: ".. " })).rejects.toThrow(/agent_id/);
|
||||
});
|
||||
|
||||
it("默认 id 合法,可正常初始化", async () => {
|
||||
const state = await loadOrInitAgentState();
|
||||
expect(state.projectId).toBe("default_project");
|
||||
expect(state.agentId).toBe("default_agent");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user