@gotgenes/pi-permission-system 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,139 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ mkdtempSync,
5
+ readFileSync,
6
+ rmSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
13
+ import {
14
+ CONFIG_PATH,
15
+ DEFAULT_EXTENSION_CONFIG,
16
+ } from "../src/extension-config.js";
17
+ import piPermissionSystemExtension from "../src/index.js";
18
+ import type { GlobalPermissionConfig } from "../src/types.js";
19
+
20
+ type MockHandler = (
21
+ event: Record<string, unknown>,
22
+ ctx: Record<string, unknown>,
23
+ ) =>
24
+ | Promise<Record<string, unknown> | undefined>
25
+ | Record<string, unknown>
26
+ | undefined;
27
+
28
+ describe("session_start handler consolidation", () => {
29
+ let baseDir: string;
30
+ let originalAgentDir: string | undefined;
31
+ let originalExtensionConfig: string | null;
32
+
33
+ beforeEach(() => {
34
+ baseDir = mkdtempSync(join(tmpdir(), "pi-permission-session-start-"));
35
+ originalAgentDir = process.env.PI_CODING_AGENT_DIR;
36
+ originalExtensionConfig = existsSync(CONFIG_PATH)
37
+ ? readFileSync(CONFIG_PATH, "utf8")
38
+ : null;
39
+
40
+ mkdirSync(join(baseDir, "agents"), { recursive: true });
41
+
42
+ const config: GlobalPermissionConfig = {
43
+ defaultPolicy: {
44
+ tools: "ask",
45
+ bash: "ask",
46
+ mcp: "ask",
47
+ skills: "ask",
48
+ special: "ask",
49
+ },
50
+ };
51
+ writeFileSync(
52
+ join(baseDir, "pi-permissions.jsonc"),
53
+ `${JSON.stringify(config, null, 2)}\n`,
54
+ "utf8",
55
+ );
56
+ writeFileSync(
57
+ CONFIG_PATH,
58
+ `${JSON.stringify(DEFAULT_EXTENSION_CONFIG, null, 2)}\n`,
59
+ "utf8",
60
+ );
61
+
62
+ process.env.PI_CODING_AGENT_DIR = baseDir;
63
+ });
64
+
65
+ afterEach(() => {
66
+ if (originalAgentDir === undefined) {
67
+ delete process.env.PI_CODING_AGENT_DIR;
68
+ } else {
69
+ process.env.PI_CODING_AGENT_DIR = originalAgentDir;
70
+ }
71
+ if (originalExtensionConfig === null) {
72
+ if (existsSync(CONFIG_PATH)) {
73
+ unlinkSync(CONFIG_PATH);
74
+ }
75
+ } else {
76
+ writeFileSync(CONFIG_PATH, originalExtensionConfig, "utf8");
77
+ }
78
+ rmSync(baseDir, { recursive: true, force: true });
79
+ });
80
+
81
+ test("registers exactly one session_start handler", () => {
82
+ const registrations: Array<{ name: string; handler: MockHandler }> = [];
83
+
84
+ piPermissionSystemExtension({
85
+ on: (name: string, handler: MockHandler): void => {
86
+ registrations.push({ name, handler });
87
+ },
88
+ registerCommand: (): void => {},
89
+ getAllTools: (): Array<{ name: string }> => [],
90
+ setActiveTools: (): void => {},
91
+ registerProvider: (): void => {},
92
+ events: {
93
+ emit: (): void => {},
94
+ },
95
+ } as never);
96
+
97
+ const sessionStartHandlers = registrations.filter(
98
+ (r) => r.name === "session_start",
99
+ );
100
+ expect(sessionStartHandlers).toHaveLength(1);
101
+ });
102
+
103
+ test("session_start handler preserves lifecycle.reload debug log", async () => {
104
+ const registrations: Array<{ name: string; handler: MockHandler }> = [];
105
+
106
+ piPermissionSystemExtension({
107
+ on: (name: string, handler: MockHandler): void => {
108
+ registrations.push({ name, handler });
109
+ },
110
+ registerCommand: (): void => {},
111
+ getAllTools: (): Array<{ name: string }> => [],
112
+ setActiveTools: (): void => {},
113
+ registerProvider: (): void => {},
114
+ events: {
115
+ emit: (): void => {},
116
+ },
117
+ } as never);
118
+
119
+ const sessionStartHandlers = registrations.filter(
120
+ (r) => r.name === "session_start",
121
+ );
122
+
123
+ // The single handler should accept event with reason="reload" without throwing
124
+ const mockCtx = {
125
+ cwd: baseDir,
126
+ ui: { select: async () => "", input: async () => "" },
127
+ agent: { name: "test-agent" },
128
+ sessionManager: {
129
+ getEntries: () => [],
130
+ addEntry: () => {},
131
+ },
132
+ };
133
+
134
+ // Should not throw when called with a reload event
135
+ await expect(
136
+ sessionStartHandlers[0].handler({ reason: "reload" }, mockCtx),
137
+ ).resolves.not.toThrow();
138
+ });
139
+ });