@pi-harness/plugin-api 0.1.52 → 0.1.54
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/README.md +1 -1
- package/dist/bounded-file.d.ts +2 -2
- package/dist/bounded-file.d.ts.map +1 -1
- package/dist/bounded-file.js +15 -3
- package/dist/bounded-file.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ The argument is the list of scripted model responses; pass `[]` for a plugin tha
|
|
|
62
62
|
- **Services** — the service interfaces the harness puts on the Cordis `Context` (`PiToolsSnapshot`, `PiSessionService`, `PiModelsService`, `PiRuntimeService`, `PiResourcesService`, `PiMcpService`, `PiTelemetryService`, `PiHarnessLaunch`), the `declare module` augmentation that attaches them, and the `PiToolRegistry` / `PiPluginUiRegistry` a test can construct directly.
|
|
63
63
|
- **Config** — `EmptyConfig` for a plugin that takes no options, and `assertKnownConfigKeys` for rejecting unknown keys with a message that lists the supported ones. Both are built on [Schemastery](https://github.com/shigma/schemastery).
|
|
64
64
|
- **Workspace paths** — `resolveExistingWorkspacePath`, `resolveWorkspaceFilePath`, `prepareWorkspaceFile` and `isPathInside`, which keep a plugin from escaping the workspace root through symlinks or `..`.
|
|
65
|
-
- **Bounded file access** — `readBoundedFile` and `readBoundedTextFile` with their `BoundedFileSizeError` / `BoundedFileTypeError`, which refuse oversized files, directories, FIFOs and symlinks rather than stranding the process on them.
|
|
65
|
+
- **Bounded file access** — `readBoundedFile` and `readBoundedTextFile` with their `BoundedFileSizeError` / `BoundedFileTypeError`, which refuse oversized files, directories, FIFOs and symlinks rather than stranding the process on them. Both readers accept an optional `AbortSignal` and stop between filesystem reads when the caller cancels.
|
|
66
66
|
- **Atomic writes** — `atomicWriteFile`, which serialises concurrent writes to the same path, preserves an existing target's permission bits and fsyncs the containing directory.
|
|
67
67
|
- **Bounded commands** — `runBoundedCommand(argv, cwd, timeoutMs, maxOutputBytes, signal?, options?)`, a no-shell, stdin-EOF runner with per-stream byte limits. Optional `env` applies only to the child; `encoding: "buffer"` preserves raw stdout/stderr on success and failure, while the default remains UTF-8 strings. Timeout must be an integer from 1 to 2147483647 ms; output limits must be non-negative safe integers. Failures retain bounded stdout/stderr and exec-style code/killed metadata. POSIX commands own a process group; failure signals the group and escalates after one second even if its leader exits. Escalation closes pipes and settles failure independently of escaped descendants. Windows uses taskkill tree termination with direct-process fallback; native Windows acceptance is outstanding. This is local lifecycle cleanup, not a sandbox or proof that deliberately escaped descendants or remote jobs stopped.
|
|
68
68
|
|
package/dist/bounded-file.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export declare class BoundedFileSizeError extends Error {
|
|
|
2
2
|
}
|
|
3
3
|
export declare class BoundedFileTypeError extends Error {
|
|
4
4
|
}
|
|
5
|
-
export declare function readBoundedFile(path: string, maxBytes: number, label: string): Promise<Buffer>;
|
|
6
|
-
export declare function readBoundedTextFile(path: string, maxBytes: number, label: string): Promise<string>;
|
|
5
|
+
export declare function readBoundedFile(path: string, maxBytes: number, label: string, signal?: AbortSignal): Promise<Buffer>;
|
|
6
|
+
export declare function readBoundedTextFile(path: string, maxBytes: number, label: string, signal?: AbortSignal): Promise<string>;
|
|
7
7
|
//# sourceMappingURL=bounded-file.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-file.d.ts","sourceRoot":"","sources":["../src/bounded-file.ts"],"names":[],"mappings":"AAGA,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAClD,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;
|
|
1
|
+
{"version":3,"file":"bounded-file.d.ts","sourceRoot":"","sources":["../src/bounded-file.ts"],"names":[],"mappings":"AAGA,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAClD,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAQlD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA+B1H;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAO9H"}
|
package/dist/bounded-file.js
CHANGED
|
@@ -4,14 +4,24 @@ export class BoundedFileSizeError extends Error {
|
|
|
4
4
|
}
|
|
5
5
|
export class BoundedFileTypeError extends Error {
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
function throwIfAborted(signal) {
|
|
8
|
+
if (!signal?.aborted)
|
|
9
|
+
return;
|
|
10
|
+
if (signal.reason instanceof Error)
|
|
11
|
+
throw signal.reason;
|
|
12
|
+
throw new Error("Bounded file read cancelled", { cause: signal.reason });
|
|
13
|
+
}
|
|
14
|
+
export async function readBoundedFile(path, maxBytes, label, signal) {
|
|
8
15
|
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
|
|
9
16
|
throw new Error("Bounded file size must be a non-negative safe integer");
|
|
17
|
+
throwIfAborted(signal);
|
|
10
18
|
let handle;
|
|
11
19
|
try {
|
|
12
20
|
// O_NOFOLLOW only rejects symbolic links. Without O_NONBLOCK a FIFO or a blocking device node would suspend the open until a writer appears, so the regular-file check below would never run and the libuv thread serving the call would be lost for the life of the process.
|
|
13
21
|
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
22
|
+
throwIfAborted(signal);
|
|
14
23
|
const metadata = await handle.stat();
|
|
24
|
+
throwIfAborted(signal);
|
|
15
25
|
if (!metadata.isFile())
|
|
16
26
|
throw new BoundedFileTypeError(`${label} must be a regular file`);
|
|
17
27
|
if (metadata.size > maxBytes)
|
|
@@ -19,8 +29,10 @@ export async function readBoundedFile(path, maxBytes, label) {
|
|
|
19
29
|
const chunks = [];
|
|
20
30
|
let total = 0;
|
|
21
31
|
while (total <= maxBytes) {
|
|
32
|
+
throwIfAborted(signal);
|
|
22
33
|
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - total));
|
|
23
34
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
35
|
+
throwIfAborted(signal);
|
|
24
36
|
if (bytesRead === 0)
|
|
25
37
|
break;
|
|
26
38
|
chunks.push(buffer.subarray(0, bytesRead));
|
|
@@ -39,8 +51,8 @@ export async function readBoundedFile(path, maxBytes, label) {
|
|
|
39
51
|
await handle?.close();
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
|
-
export async function readBoundedTextFile(path, maxBytes, label) {
|
|
43
|
-
const bytes = await readBoundedFile(path, maxBytes, label);
|
|
54
|
+
export async function readBoundedTextFile(path, maxBytes, label, signal) {
|
|
55
|
+
const bytes = await readBoundedFile(path, maxBytes, label, signal);
|
|
44
56
|
try {
|
|
45
57
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
46
58
|
}
|
package/dist/bounded-file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-file.js","sourceRoot":"","sources":["../src/bounded-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAClD,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAElD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAa;
|
|
1
|
+
{"version":3,"file":"bounded-file.js","sourceRoot":"","sources":["../src/bounded-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAClD,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAElD,SAAS,cAAc,CAAC,MAA+B;IACrD,IAAI,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO;IAC7B,IAAI,MAAM,CAAC,MAAM,YAAY,KAAK;QAAE,MAAM,MAAM,CAAC,MAAM,CAAC;IACxD,MAAM,IAAI,KAAK,CAAC,6BAA6B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAa,EAAE,MAAoB;IACvG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC9H,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,MAAoD,CAAC;IACzD,IAAI,CAAC;QACH,8QAA8Q;QAC9Q,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5F,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACrC,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,yBAAyB,CAAC,CAAC;QAC1F,IAAI,QAAQ,CAAC,IAAI,GAAG,QAAQ;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,gBAAgB,QAAQ,aAAa,CAAC,CAAC;QAC5G,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,OAAO,KAAK,IAAI,QAAQ,EAAE,CAAC;YACzB,cAAc,CAAC,MAAM,CAAC,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;YAC7E,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxE,cAAc,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,SAAS,KAAK,CAAC;gBAAE,MAAM;YAC3B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;YAC3C,KAAK,IAAI,SAAS,CAAC;QACrB,CAAC;QACD,IAAI,KAAK,GAAG,QAAQ;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,gBAAgB,QAAQ,aAAa,CAAC,CAAC;QACpG,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,4BAA4B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9I,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAa,EAAE,MAAoB;IAC3G,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC;QACH,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,2BAA2B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/plugin-api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.54",
|
|
4
4
|
"description": "Public contract for authoring Pi Harness plugins: Cordis context services, plugin configuration helpers, and bounded workspace file access",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|