@abdwhb-png/pi-test-harness 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.
- package/CHANGELOG.md +161 -0
- package/LICENSE +21 -0
- package/README.md +673 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +61 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/events.d.ts +6 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +33 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/mock-pi-script.mjs +176 -0
- package/dist/mock-pi.d.ts +32 -0
- package/dist/mock-pi.d.ts.map +1 -0
- package/dist/mock-pi.js +150 -0
- package/dist/mock-pi.js.map +1 -0
- package/dist/mock-tools.d.ts +51 -0
- package/dist/mock-tools.d.ts.map +1 -0
- package/dist/mock-tools.js +192 -0
- package/dist/mock-tools.js.map +1 -0
- package/dist/mock-ui.d.ts +13 -0
- package/dist/mock-ui.d.ts.map +1 -0
- package/dist/mock-ui.js +159 -0
- package/dist/mock-ui.js.map +1 -0
- package/dist/pi-loader-parity.d.ts +36 -0
- package/dist/pi-loader-parity.d.ts.map +1 -0
- package/dist/pi-loader-parity.js +60 -0
- package/dist/pi-loader-parity.js.map +1 -0
- package/dist/playbook.d.ts +44 -0
- package/dist/playbook.d.ts.map +1 -0
- package/dist/playbook.js +143 -0
- package/dist/playbook.js.map +1 -0
- package/dist/sandbox.d.ts +27 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +269 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +187 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +46 -0
- package/dist/utils.js.map +1 -0
- package/package.json +84 -0
- package/skills/pi-test-harness/SKILL.md +451 -0
- package/skills/pi-test-harness/evals/evals.json +26 -0
- package/skills/pi-test-harness/references/api-reference.md +480 -0
- package/skills/pi-test-harness/references/mock-pi-cli.md +135 -0
- package/skills/pi-test-harness/references/mock-tools.md +176 -0
- package/skills/pi-test-harness/references/mock-ui.md +170 -0
- package/skills/pi-test-harness/references/playbook-dsl.md +209 -0
- package/skills/pi-test-harness/references/sandbox-install.md +113 -0
- package/src/diagnostics.ts +90 -0
- package/src/events.ts +43 -0
- package/src/index.ts +42 -0
- package/src/mock-pi-script.mjs +176 -0
- package/src/mock-pi.ts +169 -0
- package/src/mock-tools.ts +252 -0
- package/src/mock-ui.ts +196 -0
- package/src/pi-loader-parity.ts +61 -0
- package/src/playbook.ts +189 -0
- package/src/sandbox.ts +334 -0
- package/src/session.ts +249 -0
- package/src/types.ts +203 -0
- package/src/utils.ts +46 -0
package/dist/session.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestSession — orchestrates a test run with playbook, mock tools, and mock UI.
|
|
3
|
+
*
|
|
4
|
+
* 1. Creates a real pi environment (extensions, tools, hooks, session)
|
|
5
|
+
* 2. Replaces streamFn with playbook
|
|
6
|
+
* 3. Intercepts tool.execute() for mockTools
|
|
7
|
+
* 4. Injects mock UI context
|
|
8
|
+
* 5. Collects events
|
|
9
|
+
* 6. Runs conversation script
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import * as os from "node:os";
|
|
14
|
+
import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager, ModelRuntime, } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { createPlaybookStreamFn } from "./playbook.js";
|
|
16
|
+
import { interceptToolExecution } from "./mock-tools.js";
|
|
17
|
+
import { createMockUIContext } from "./mock-ui.js";
|
|
18
|
+
import { createEventCollector } from "./events.js";
|
|
19
|
+
import { formatPlaybookDiagnostic } from "./diagnostics.js";
|
|
20
|
+
import { withoutJitiNativeImport } from "./pi-loader-parity.js";
|
|
21
|
+
export async function createTestSession(options = {}) {
|
|
22
|
+
const propagateErrors = options.propagateErrors ?? true;
|
|
23
|
+
const ownsTmpDir = !options.cwd;
|
|
24
|
+
const cwd = options.cwd ?? fs.mkdtempSync(path.join(os.tmpdir(), "pi-test-harness-"));
|
|
25
|
+
if (!fs.existsSync(cwd)) {
|
|
26
|
+
fs.mkdirSync(cwd, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
const settingsManager = SettingsManager.inMemory();
|
|
29
|
+
const loader = new DefaultResourceLoader({
|
|
30
|
+
cwd,
|
|
31
|
+
agentDir: cwd, // Use cwd as agent dir to avoid touching real ~/.pi
|
|
32
|
+
settingsManager,
|
|
33
|
+
additionalExtensionPaths: options.extensions?.map((p) => path.resolve(cwd, p)) ?? [],
|
|
34
|
+
extensionFactories: options.extensionFactories,
|
|
35
|
+
systemPromptOverride: options.systemPrompt
|
|
36
|
+
? () => options.systemPrompt
|
|
37
|
+
: undefined,
|
|
38
|
+
});
|
|
39
|
+
// Extensions load here. Pinned to the loader configuration Pi's shipped
|
|
40
|
+
// runtimes use — see withoutJitiNativeImport for why that matters.
|
|
41
|
+
await withoutJitiNativeImport(() => loader.reload());
|
|
42
|
+
// Create isolated ModelRuntime with auth under cwd and no persisted model catalog
|
|
43
|
+
const modelRuntime = await ModelRuntime.create({
|
|
44
|
+
authPath: path.join(cwd, "auth.json"),
|
|
45
|
+
modelsPath: null,
|
|
46
|
+
});
|
|
47
|
+
// Use a builtin model as placeholder (never actually called — playbook replaces streamFn)
|
|
48
|
+
const playbookModel = modelRuntime.getModel("openai", "gpt-4o");
|
|
49
|
+
if (!playbookModel) {
|
|
50
|
+
throw new Error("Model openai/gpt-4o not found in isolated ModelRuntime. " +
|
|
51
|
+
"This should not happen — builtin providers are always registered. " +
|
|
52
|
+
"Check that @earendil-works/pi-ai is installed.");
|
|
53
|
+
}
|
|
54
|
+
// Provide a dummy API key so AgentSession.prompt does not reject before
|
|
55
|
+
// the playbook replaces streamFunction. The key is never sent to any LLM.
|
|
56
|
+
// Pi 0.84 synchronizes runtime credentials with an offline model refresh.
|
|
57
|
+
await modelRuntime.setRuntimeApiKey("openai", "sk-test-harness-dummy");
|
|
58
|
+
const { session, extensionsResult } = await withoutJitiNativeImport(() => createAgentSession({
|
|
59
|
+
cwd,
|
|
60
|
+
agentDir: cwd,
|
|
61
|
+
model: playbookModel,
|
|
62
|
+
modelRuntime,
|
|
63
|
+
sessionManager: SessionManager.inMemory(),
|
|
64
|
+
settingsManager,
|
|
65
|
+
resourceLoader: loader,
|
|
66
|
+
}));
|
|
67
|
+
if (extensionsResult.errors.length > 0) {
|
|
68
|
+
session.dispose();
|
|
69
|
+
if (ownsTmpDir && fs.existsSync(cwd)) {
|
|
70
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
const errors = extensionsResult.errors
|
|
73
|
+
.map((e) => ` ${e.path}: ${e.error}`)
|
|
74
|
+
.join("\n");
|
|
75
|
+
throw new Error(`Extension load errors:\n${errors}`);
|
|
76
|
+
}
|
|
77
|
+
const events = createEventCollector();
|
|
78
|
+
let currentStep = 0;
|
|
79
|
+
let mockedToolNames = new Set();
|
|
80
|
+
// toolCallIds whose mock returned a ToolResult with isError:true — Pi 0.84
|
|
81
|
+
// hardcodes successful execute() as non-error, so records must consult this.
|
|
82
|
+
let mockedErrorToolCallIds = new Set();
|
|
83
|
+
session.subscribe((event) => {
|
|
84
|
+
events.all.push(event);
|
|
85
|
+
if (event.type === "tool_execution_start") {
|
|
86
|
+
const record = {
|
|
87
|
+
step: currentStep,
|
|
88
|
+
toolName: event.toolName,
|
|
89
|
+
input: event.args ?? {},
|
|
90
|
+
blocked: false,
|
|
91
|
+
};
|
|
92
|
+
events.toolCalls.push(record);
|
|
93
|
+
}
|
|
94
|
+
if (event.type === "tool_execution_end") {
|
|
95
|
+
const resultText = event.result?.content
|
|
96
|
+
?.filter((c) => c.type === "text")
|
|
97
|
+
?.map((c) => c.text)
|
|
98
|
+
?.join("\n") ?? "";
|
|
99
|
+
if (event.isError) {
|
|
100
|
+
const lastCall = events.toolCalls[events.toolCalls.length - 1];
|
|
101
|
+
if (lastCall && lastCall.toolName === event.toolName) {
|
|
102
|
+
if (resultText.includes("blocked") || resultText.includes("Plan mode")) {
|
|
103
|
+
lastCall.blocked = true;
|
|
104
|
+
lastCall.blockReason = resultText;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Record the final result (after afterToolCall modifications).
|
|
109
|
+
// Always push — each tool_execution_end has a unique toolCallId
|
|
110
|
+
// within a run, and the subscriber is the only source of results.
|
|
111
|
+
const isMocked = mockedToolNames.has(event.toolName);
|
|
112
|
+
events.toolResults.push({
|
|
113
|
+
step: currentStep,
|
|
114
|
+
toolName: event.toolName,
|
|
115
|
+
toolCallId: event.toolCallId,
|
|
116
|
+
text: resultText,
|
|
117
|
+
content: event.result?.content ?? [],
|
|
118
|
+
isError: event.isError || mockedErrorToolCallIds.has(event.toolCallId),
|
|
119
|
+
details: event.result?.details,
|
|
120
|
+
mocked: isMocked,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (event.type === "message_end") {
|
|
124
|
+
events.messages.push(event.message);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
let playbookState = null;
|
|
128
|
+
const mockUI = createMockUIContext(options.mockUI, events.ui);
|
|
129
|
+
await session.bindExtensions({
|
|
130
|
+
uiContext: mockUI,
|
|
131
|
+
onError: (err) => {
|
|
132
|
+
console.error(`[pi-test-harness] Extension error: ${err.event} — ${err.error}`);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const originalTools = [...session.agent.state.tools];
|
|
136
|
+
const testSession = {
|
|
137
|
+
session,
|
|
138
|
+
cwd,
|
|
139
|
+
events,
|
|
140
|
+
get playbook() {
|
|
141
|
+
return {
|
|
142
|
+
consumed: playbookState?.consumed ?? 0,
|
|
143
|
+
remaining: playbookState?.remaining ?? 0,
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
async run(...turns) {
|
|
147
|
+
const { streamFn, state } = createPlaybookStreamFn(turns);
|
|
148
|
+
playbookState = state;
|
|
149
|
+
// Assign playbook streamFn to the public Agent.streamFunction
|
|
150
|
+
session.agent.streamFunction = streamFn;
|
|
151
|
+
const effectiveMockTools = options.mockTools ?? {};
|
|
152
|
+
const currentTools = originalTools;
|
|
153
|
+
const { tools: interceptedTools, mockedNames, mockedErrorToolCallIds: errorIds, } = interceptToolExecution(currentTools, effectiveMockTools, state, propagateErrors);
|
|
154
|
+
mockedToolNames = mockedNames;
|
|
155
|
+
mockedErrorToolCallIds = errorIds;
|
|
156
|
+
session.agent.state.tools = interceptedTools;
|
|
157
|
+
for (const turn of turns) {
|
|
158
|
+
currentStep = state.consumed;
|
|
159
|
+
await session.prompt(turn.prompt);
|
|
160
|
+
await session.agent.waitForIdle();
|
|
161
|
+
}
|
|
162
|
+
if (state.remaining > 0) {
|
|
163
|
+
const allActions = turns.flatMap((t) => t.actions);
|
|
164
|
+
const remaining = allActions.slice(state.consumed);
|
|
165
|
+
const diagnostic = formatPlaybookDiagnostic("remaining", state, remaining);
|
|
166
|
+
throw new Error(diagnostic);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
/**
|
|
170
|
+
* Dispose the test session and clean up the temp directory (if owned).
|
|
171
|
+
*
|
|
172
|
+
* Note: `session.dispose()` does NOT fire `session_shutdown`. That event is
|
|
173
|
+
* dispatched by pi at Node.js process exit. Extensions that open resources in
|
|
174
|
+
* `session_start` (e.g., SQLite databases) keep those resources open until the
|
|
175
|
+
* process exit. Use `safeRmSync` when cleaning up extension-owned files in
|
|
176
|
+
* afterEach hooks on Windows to avoid EPERM errors.
|
|
177
|
+
*/
|
|
178
|
+
dispose() {
|
|
179
|
+
session.dispose();
|
|
180
|
+
if (ownsTmpDir && fs.existsSync(cwd)) {
|
|
181
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
return testSession;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for pi-test-harness.
|
|
3
|
+
*/
|
|
4
|
+
import type { AgentSession, AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
6
|
+
export interface PlaybookAction {
|
|
7
|
+
type: "call" | "say";
|
|
8
|
+
/** For "call": tool name */
|
|
9
|
+
toolName?: string;
|
|
10
|
+
/** For "call": static or late-bound params */
|
|
11
|
+
params?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
12
|
+
/** For "say": text content */
|
|
13
|
+
text?: string;
|
|
14
|
+
/** Optional callback after tool execution */
|
|
15
|
+
thenCallback?: (result: ToolResultRecord) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface Turn {
|
|
18
|
+
prompt: string;
|
|
19
|
+
actions: PlaybookAction[];
|
|
20
|
+
}
|
|
21
|
+
export interface ToolResult {
|
|
22
|
+
content: Array<{
|
|
23
|
+
type: string;
|
|
24
|
+
text: string;
|
|
25
|
+
}>;
|
|
26
|
+
details?: unknown;
|
|
27
|
+
isError?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type MockToolHandler = string | ToolResult | ((params: Record<string, unknown>) => string | ToolResult);
|
|
30
|
+
export interface MockUIConfig {
|
|
31
|
+
confirm?: boolean | ((title: string, message: string) => boolean);
|
|
32
|
+
select?: number | string | ((title: string, items: string[]) => string | undefined);
|
|
33
|
+
input?: string | ((title: string, placeholder?: string) => string | undefined);
|
|
34
|
+
editor?: string | ((title: string, prefilled?: string) => string | undefined);
|
|
35
|
+
}
|
|
36
|
+
export interface ToolCallRecord {
|
|
37
|
+
step: number;
|
|
38
|
+
toolName: string;
|
|
39
|
+
input: Record<string, unknown>;
|
|
40
|
+
blocked: boolean;
|
|
41
|
+
blockReason?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ToolResultRecord {
|
|
44
|
+
step: number;
|
|
45
|
+
toolName: string;
|
|
46
|
+
toolCallId: string;
|
|
47
|
+
text: string;
|
|
48
|
+
content: Array<{
|
|
49
|
+
type: string;
|
|
50
|
+
text?: string;
|
|
51
|
+
}>;
|
|
52
|
+
isError: boolean;
|
|
53
|
+
details?: unknown;
|
|
54
|
+
mocked: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface UICallRecord {
|
|
57
|
+
method: string;
|
|
58
|
+
args: unknown[];
|
|
59
|
+
returnValue?: unknown;
|
|
60
|
+
}
|
|
61
|
+
export interface TestEvents {
|
|
62
|
+
all: AgentSessionEvent[];
|
|
63
|
+
toolCalls: ToolCallRecord[];
|
|
64
|
+
toolResults: ToolResultRecord[];
|
|
65
|
+
messages: AgentMessage[];
|
|
66
|
+
ui: UICallRecord[];
|
|
67
|
+
toolCallsFor(name: string): ToolCallRecord[];
|
|
68
|
+
toolResultsFor(name: string): ToolResultRecord[];
|
|
69
|
+
blockedCalls(): ToolCallRecord[];
|
|
70
|
+
uiCallsFor(method: string): UICallRecord[];
|
|
71
|
+
/** Ordered list of tool names as they were called */
|
|
72
|
+
toolSequence(): string[];
|
|
73
|
+
}
|
|
74
|
+
export interface TestSessionOptions {
|
|
75
|
+
/** Extension file paths to load */
|
|
76
|
+
extensions?: string[];
|
|
77
|
+
/** Extension factory functions (inline) */
|
|
78
|
+
extensionFactories?: Array<(pi: any) => void>;
|
|
79
|
+
/** Working directory (auto temp dir if omitted, cleaned on dispose) */
|
|
80
|
+
cwd?: string;
|
|
81
|
+
/** System prompt override */
|
|
82
|
+
systemPrompt?: string;
|
|
83
|
+
/** Mock tool execution (intercepts tool.execute()) */
|
|
84
|
+
mockTools?: Record<string, MockToolHandler>;
|
|
85
|
+
/** Mock UI responses */
|
|
86
|
+
mockUI?: MockUIConfig;
|
|
87
|
+
/** Abort on real tool throw (default: true) */
|
|
88
|
+
propagateErrors?: boolean;
|
|
89
|
+
}
|
|
90
|
+
export interface TestSession {
|
|
91
|
+
/** Run a conversation script */
|
|
92
|
+
run(...turns: Turn[]): Promise<void>;
|
|
93
|
+
/** Real session underneath */
|
|
94
|
+
session: AgentSession;
|
|
95
|
+
/** Working directory */
|
|
96
|
+
cwd: string;
|
|
97
|
+
/** Collected events */
|
|
98
|
+
events: TestEvents;
|
|
99
|
+
/** Playbook consumption state */
|
|
100
|
+
playbook: {
|
|
101
|
+
consumed: number;
|
|
102
|
+
remaining: number;
|
|
103
|
+
};
|
|
104
|
+
/** Cleanup */
|
|
105
|
+
dispose(): void;
|
|
106
|
+
}
|
|
107
|
+
/** Configuration for a single mock pi invocation response. */
|
|
108
|
+
export interface MockPiCall {
|
|
109
|
+
/** Text output from the mock agent */
|
|
110
|
+
output?: string;
|
|
111
|
+
/** Exit code (default: 0) */
|
|
112
|
+
exitCode?: number;
|
|
113
|
+
/** Stderr output */
|
|
114
|
+
stderr?: string;
|
|
115
|
+
/** Delay in ms before responding */
|
|
116
|
+
delay?: number;
|
|
117
|
+
/** Raw JSONL events to emit instead of default message_end */
|
|
118
|
+
jsonl?: object[];
|
|
119
|
+
/** Files to write before exiting (path → content) */
|
|
120
|
+
writeFiles?: Record<string, string>;
|
|
121
|
+
}
|
|
122
|
+
/** Mock pi CLI for testing extensions that spawn pi as a subprocess. */
|
|
123
|
+
export interface MockPi {
|
|
124
|
+
/** Create temp dir with pi shim, prepend to PATH */
|
|
125
|
+
install(): void;
|
|
126
|
+
/** Remove from PATH, delete temp dir */
|
|
127
|
+
uninstall(): void;
|
|
128
|
+
/** Queue a response for the next pi invocation */
|
|
129
|
+
onCall(response: MockPiCall): void;
|
|
130
|
+
/** Clear the response queue and reset the call counter */
|
|
131
|
+
reset(): void;
|
|
132
|
+
/** Number of times the mock pi has been invoked */
|
|
133
|
+
callCount(): number;
|
|
134
|
+
/** The temporary directory containing the queue and shim */
|
|
135
|
+
dir: string;
|
|
136
|
+
}
|
|
137
|
+
export interface SandboxOptions {
|
|
138
|
+
/** Package directory (runs npm pack) */
|
|
139
|
+
packageDir: string;
|
|
140
|
+
/** Expected resources after install */
|
|
141
|
+
expect?: {
|
|
142
|
+
extensions?: number;
|
|
143
|
+
tools?: string[];
|
|
144
|
+
skills?: number;
|
|
145
|
+
};
|
|
146
|
+
/** Optional smoke test in the sandbox */
|
|
147
|
+
smoke?: {
|
|
148
|
+
mockTools?: Record<string, MockToolHandler>;
|
|
149
|
+
script: Turn[];
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Custom npm command and arguments for pack/install.
|
|
153
|
+
* Default: [process.platform === "win32" ? "npm.cmd" : "npm"]
|
|
154
|
+
* Use ["sfw", "npm"] to run through Socket Firewall, or provide
|
|
155
|
+
* an array like ["/usr/local/bin/node", "/path/to/npm_cli.js"]
|
|
156
|
+
* for a non-standard npm executable.
|
|
157
|
+
*/
|
|
158
|
+
npmCommand?: string[];
|
|
159
|
+
}
|
|
160
|
+
export interface SandboxResult {
|
|
161
|
+
loaded: {
|
|
162
|
+
extensions: number;
|
|
163
|
+
extensionErrors: string[];
|
|
164
|
+
tools: string[];
|
|
165
|
+
skills: number;
|
|
166
|
+
};
|
|
167
|
+
smoke?: {
|
|
168
|
+
events: TestEvents;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility helpers for pi-test-harness consumers.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Remove a file, silently ignoring EPERM/EBUSY errors.
|
|
6
|
+
*
|
|
7
|
+
* **Why this exists**: On Windows, pi extensions that open SQLite databases
|
|
8
|
+
* do so in the `session_start` event handler. The corresponding close happens
|
|
9
|
+
* in `session_shutdown` — but that event fires at Node.js **process exit**,
|
|
10
|
+
* NOT when `session.dispose()` is called. This means DB files remain locked
|
|
11
|
+
* for the lifetime of the test runner process.
|
|
12
|
+
*
|
|
13
|
+
* Safe pattern in afterEach:
|
|
14
|
+
* ```ts
|
|
15
|
+
* afterEach(() => {
|
|
16
|
+
* safeRmSync(dbPath);
|
|
17
|
+
* safeRmSync(dbPath + "-wal");
|
|
18
|
+
* safeRmSync(dbPath + "-shm");
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Files are cleaned up by the OS when the process exits (or on next run).
|
|
23
|
+
* Using unique DB paths per test ensures isolation.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Returns true for errno codes that represent a Windows file-lock condition.
|
|
27
|
+
* Exported for unit testing — not part of the public API contract.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export declare function _isLockedFileError(err: unknown): boolean;
|
|
31
|
+
export declare function safeRmSync(filePath: string): void;
|
|
32
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGxD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQjD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility helpers for pi-test-harness consumers.
|
|
3
|
+
*/
|
|
4
|
+
import { rmSync } from "node:fs";
|
|
5
|
+
/**
|
|
6
|
+
* Remove a file, silently ignoring EPERM/EBUSY errors.
|
|
7
|
+
*
|
|
8
|
+
* **Why this exists**: On Windows, pi extensions that open SQLite databases
|
|
9
|
+
* do so in the `session_start` event handler. The corresponding close happens
|
|
10
|
+
* in `session_shutdown` — but that event fires at Node.js **process exit**,
|
|
11
|
+
* NOT when `session.dispose()` is called. This means DB files remain locked
|
|
12
|
+
* for the lifetime of the test runner process.
|
|
13
|
+
*
|
|
14
|
+
* Safe pattern in afterEach:
|
|
15
|
+
* ```ts
|
|
16
|
+
* afterEach(() => {
|
|
17
|
+
* safeRmSync(dbPath);
|
|
18
|
+
* safeRmSync(dbPath + "-wal");
|
|
19
|
+
* safeRmSync(dbPath + "-shm");
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Files are cleaned up by the OS when the process exits (or on next run).
|
|
24
|
+
* Using unique DB paths per test ensures isolation.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Returns true for errno codes that represent a Windows file-lock condition.
|
|
28
|
+
* Exported for unit testing — not part of the public API contract.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export function _isLockedFileError(err) {
|
|
32
|
+
const code = err.code;
|
|
33
|
+
return code === "EPERM" || code === "EBUSY";
|
|
34
|
+
}
|
|
35
|
+
export function safeRmSync(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
rmSync(filePath, { force: true });
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
// Only swallow Windows file-lock errors. Everything else (permissions,
|
|
41
|
+
// bad path type, disk full) should still propagate so failures are visible.
|
|
42
|
+
if (!_isLockedFileError(err))
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC9C,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;IACjD,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IAC1C,IAAI,CAAC;QACJ,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,uEAAuE;QACvE,4EAA4E;QAC5E,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,CAAC;IACzC,CAAC;AACF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abdwhb-png/pi-test-harness",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Test harness for pi extensions — in-process session testing, package install verification, and subprocess mocking",
|
|
5
|
+
"author": "Abdullah Alblushi (fork)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/abdwhb-png/pi-test-harness.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/abdwhb-png/pi-test-harness",
|
|
13
|
+
"bugs": "https://github.com/abdwhb-png/pi-test-harness/issues",
|
|
14
|
+
"contributors": [
|
|
15
|
+
"Marc Fargas <marc@marcfargas.com> (https://github.com/marcfargas)",
|
|
16
|
+
"Abdullah Alblushi <abdwhb@gmail.com> (https://github.com/abdwhb)"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"pi",
|
|
23
|
+
"pi-package",
|
|
24
|
+
"testing",
|
|
25
|
+
"test-harness",
|
|
26
|
+
"mock",
|
|
27
|
+
"playbook"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/**/*.js",
|
|
37
|
+
"dist/**/*.d.ts",
|
|
38
|
+
"dist/**/*.d.ts.map",
|
|
39
|
+
"dist/**/*.js.map",
|
|
40
|
+
"dist/mock-pi-script.mjs",
|
|
41
|
+
"!dist/package/**",
|
|
42
|
+
"skills/**",
|
|
43
|
+
"src/**/*.ts",
|
|
44
|
+
"src/**/*.mjs",
|
|
45
|
+
"README.md",
|
|
46
|
+
"CHANGELOG.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.build.json && node -e \"require('fs').copyFileSync('src/mock-pi-script.mjs','dist/mock-pi-script.mjs')\" && node scripts/prepare-package.mjs",
|
|
51
|
+
"prepublishOnly": "npm run build",
|
|
52
|
+
"release": "changeset publish",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"lint": "eslint src/ __tests__/",
|
|
57
|
+
"test:unit": "vitest run __tests__/playbook.test.ts __tests__/mock-pi.test.ts __tests__/resolve-executable.test.ts",
|
|
58
|
+
"test:integration": "vitest run __tests__/integration.test.ts __tests__/extension-loading.test.ts __tests__/sandbox.test.ts __tests__/fixes.test.ts __tests__/pi-085-compat.test.ts __tests__/prepare-package.test.ts",
|
|
59
|
+
"test:bun": "bun test --isolate scripts/extension-loading-smoke.test.ts",
|
|
60
|
+
"test:consumer": "node scripts/consumer-smoke.mjs"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@earendil-works/pi-agent-core": "^0.85.0",
|
|
64
|
+
"@earendil-works/pi-ai": "^0.85.0",
|
|
65
|
+
"@earendil-works/pi-coding-agent": "^0.85.0"
|
|
66
|
+
},
|
|
67
|
+
"overrides": {
|
|
68
|
+
"read-yaml-file": "2.1.0"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
72
|
+
"@changesets/cli": "2.31.1",
|
|
73
|
+
"@earendil-works/pi-agent-core": "0.85.1",
|
|
74
|
+
"@earendil-works/pi-ai": "0.85.1",
|
|
75
|
+
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
76
|
+
"@eslint/js": "10.0.1",
|
|
77
|
+
"eslint": "10.8.0",
|
|
78
|
+
"globals": "^16.1.0",
|
|
79
|
+
"typebox": "1.3.7",
|
|
80
|
+
"typescript": "^5.8.3",
|
|
81
|
+
"typescript-eslint": "^8.32.1",
|
|
82
|
+
"vitest": "5.0.1"
|
|
83
|
+
}
|
|
84
|
+
}
|