@cleocode/cleo-os 2026.4.99 → 2026.4.101
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/dist/harnesses/index.d.ts +62 -0
- package/dist/harnesses/index.d.ts.map +1 -0
- package/dist/harnesses/index.js +72 -0
- package/dist/harnesses/index.js.map +1 -0
- package/dist/harnesses/pi-coding-agent/adapter.d.ts +132 -0
- package/dist/harnesses/pi-coding-agent/adapter.d.ts.map +1 -0
- package/dist/harnesses/pi-coding-agent/adapter.js +265 -0
- package/dist/harnesses/pi-coding-agent/adapter.js.map +1 -0
- package/dist/harnesses/pi-coding-agent/docker-mode.d.ts +179 -0
- package/dist/harnesses/pi-coding-agent/docker-mode.d.ts.map +1 -0
- package/dist/harnesses/pi-coding-agent/docker-mode.js +241 -0
- package/dist/harnesses/pi-coding-agent/docker-mode.js.map +1 -0
- package/dist/harnesses/pi-coding-agent/index.d.ts +13 -0
- package/dist/harnesses/pi-coding-agent/index.d.ts.map +1 -0
- package/dist/harnesses/pi-coding-agent/index.js +12 -0
- package/dist/harnesses/pi-coding-agent/index.js.map +1 -0
- package/dist/harnesses/pi-coding-agent/pi-wrapper.d.ts +178 -0
- package/dist/harnesses/pi-coding-agent/pi-wrapper.d.ts.map +1 -0
- package/dist/harnesses/pi-coding-agent/pi-wrapper.js +359 -0
- package/dist/harnesses/pi-coding-agent/pi-wrapper.js.map +1 -0
- package/dist/harnesses/pi-coding-agent/types.d.ts +185 -0
- package/dist/harnesses/pi-coding-agent/types.d.ts.map +1 -0
- package/dist/harnesses/pi-coding-agent/types.js +20 -0
- package/dist/harnesses/pi-coding-agent/types.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleo-os Harness Registry.
|
|
3
|
+
*
|
|
4
|
+
* Centralises construction and lookup of {@link HarnessAdapter} implementations
|
|
5
|
+
* available in cleo-os. Adding a new harness means:
|
|
6
|
+
*
|
|
7
|
+
* 1. Implement {@link HarnessAdapter} in a subdirectory under `harnesses/`.
|
|
8
|
+
* 2. Add an entry to {@link HARNESS_REGISTRY} below.
|
|
9
|
+
* 3. Export the adapter class from the subdirectory's `index.ts`.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
import type { HarnessAdapter } from './pi-coding-agent/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Metadata entry for a registered harness adapter.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export interface HarnessRegistryEntry {
|
|
20
|
+
/** Short stable identifier matching {@link HarnessAdapter.id}. */
|
|
21
|
+
id: string;
|
|
22
|
+
/** Human-readable display name. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Factory function that creates a fresh adapter instance. */
|
|
25
|
+
create: () => HarnessAdapter;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Look up a harness adapter by ID.
|
|
29
|
+
*
|
|
30
|
+
* @param id - Harness adapter ID (e.g. `"pi-coding-agent"`).
|
|
31
|
+
* @returns The registry entry, or `undefined` when not found.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const entry = getHarnessEntry('pi-coding-agent');
|
|
36
|
+
* const adapter = entry?.create();
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export declare function getHarnessEntry(id: string): HarnessRegistryEntry | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Create a fresh instance of the harness adapter with the given ID.
|
|
44
|
+
*
|
|
45
|
+
* @param id - Harness adapter ID.
|
|
46
|
+
* @returns A new {@link HarnessAdapter} instance, or `null` when the ID is
|
|
47
|
+
* not registered.
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export declare function createHarness(id: string): HarnessAdapter | null;
|
|
52
|
+
/**
|
|
53
|
+
* Return all registered harness adapter entries.
|
|
54
|
+
*
|
|
55
|
+
* @returns Array of all registry entries, in insertion order.
|
|
56
|
+
*
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
export declare function listHarnesses(): HarnessRegistryEntry[];
|
|
60
|
+
export { PiCodingAgentAdapter } from './pi-coding-agent/adapter.js';
|
|
61
|
+
export type { HarnessAdapter } from './pi-coding-agent/types.js';
|
|
62
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAMjE;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,MAAM,EAAE,MAAM,cAAc,CAAC;CAC9B;AAsBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAE5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,oBAAoB,EAAE,CAEtD;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleo-os Harness Registry.
|
|
3
|
+
*
|
|
4
|
+
* Centralises construction and lookup of {@link HarnessAdapter} implementations
|
|
5
|
+
* available in cleo-os. Adding a new harness means:
|
|
6
|
+
*
|
|
7
|
+
* 1. Implement {@link HarnessAdapter} in a subdirectory under `harnesses/`.
|
|
8
|
+
* 2. Add an entry to {@link HARNESS_REGISTRY} below.
|
|
9
|
+
* 3. Export the adapter class from the subdirectory's `index.ts`.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
import { PiCodingAgentAdapter } from './pi-coding-agent/adapter.js';
|
|
14
|
+
/**
|
|
15
|
+
* All harness adapters registered with cleo-os.
|
|
16
|
+
*
|
|
17
|
+
* Keyed by the adapter's {@link HarnessAdapter.id}.
|
|
18
|
+
*/
|
|
19
|
+
const HARNESS_REGISTRY = new Map([
|
|
20
|
+
[
|
|
21
|
+
'pi-coding-agent',
|
|
22
|
+
{
|
|
23
|
+
id: 'pi-coding-agent',
|
|
24
|
+
name: 'Pi Coding Agent',
|
|
25
|
+
create: () => new PiCodingAgentAdapter(),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
]);
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Public API
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Look up a harness adapter by ID.
|
|
34
|
+
*
|
|
35
|
+
* @param id - Harness adapter ID (e.g. `"pi-coding-agent"`).
|
|
36
|
+
* @returns The registry entry, or `undefined` when not found.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const entry = getHarnessEntry('pi-coding-agent');
|
|
41
|
+
* const adapter = entry?.create();
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export function getHarnessEntry(id) {
|
|
47
|
+
return HARNESS_REGISTRY.get(id);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a fresh instance of the harness adapter with the given ID.
|
|
51
|
+
*
|
|
52
|
+
* @param id - Harness adapter ID.
|
|
53
|
+
* @returns A new {@link HarnessAdapter} instance, or `null` when the ID is
|
|
54
|
+
* not registered.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export function createHarness(id) {
|
|
59
|
+
return HARNESS_REGISTRY.get(id)?.create() ?? null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Return all registered harness adapter entries.
|
|
63
|
+
*
|
|
64
|
+
* @returns Array of all registry entries, in insertion order.
|
|
65
|
+
*
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
export function listHarnesses() {
|
|
69
|
+
return Array.from(HARNESS_REGISTRY.values());
|
|
70
|
+
}
|
|
71
|
+
export { PiCodingAgentAdapter } from './pi-coding-agent/adapter.js';
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAqBpE;;;;GAIG;AACH,MAAM,gBAAgB,GAA8C,IAAI,GAAG,CAAC;IAC1E;QACE,iBAAiB;QACjB;YACE,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,iBAAiB;YACvB,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,oBAAoB,EAAE;SACzC;KACF;CACF,CAAC,CAAC;AAEH,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC;AACpD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Coding Agent Harness Adapter — cleo-os implementation.
|
|
3
|
+
*
|
|
4
|
+
* Implements the {@link HarnessAdapter} interface for the Pi coding agent CLI
|
|
5
|
+
* (`@mariozechner/pi-coding-agent`). Provides process spawn, status, kill,
|
|
6
|
+
* and output streaming for Pi processes launched by cleo-os.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* This adapter is the cleo-os counterpart to the CAAMP
|
|
10
|
+
* `PiSpawnProvider` in `packages/adapters/src/providers/pi/spawn.ts`.
|
|
11
|
+
* Where the CAAMP spawn provider is a detached fire-and-forget launcher,
|
|
12
|
+
* this adapter owns the full process lifecycle — attached stdin/stdout/stderr,
|
|
13
|
+
* bounded output buffering, and structured exit promises — so it can be
|
|
14
|
+
* used in orchestrated sandbox runs.
|
|
15
|
+
*
|
|
16
|
+
* Two launch modes are supported:
|
|
17
|
+
* - **Host-native** (default): Pi is invoked directly from PATH (or via
|
|
18
|
+
* `CLEO_PI_BINARY`). This is the standard mode for local development.
|
|
19
|
+
* - **Docker sandbox** (opt-in): Pi is launched inside the
|
|
20
|
+
* `cleo-sandbox/pi:local` Docker container when `CLEO_PI_SANDBOXED=1` is
|
|
21
|
+
* set or `sandboxed: true` is passed to {@link PiCodingAgentAdapter.spawn}.
|
|
22
|
+
* Falls back to host-native with a warning when Docker is unavailable.
|
|
23
|
+
*
|
|
24
|
+
* Instance IDs have the format `pi-<taskId>-<shortRandom>` to make
|
|
25
|
+
* correlation with task records straightforward.
|
|
26
|
+
*
|
|
27
|
+
* @see packages/adapters/src/providers/pi/spawn.ts — CAAMP spawn provider
|
|
28
|
+
* @see packages/cleo-os/src/harnesses/pi-coding-agent/pi-wrapper.ts — process management
|
|
29
|
+
* @see packages/cleo-os/src/harnesses/pi-coding-agent/docker-mode.ts — sandbox mode
|
|
30
|
+
* @task T922
|
|
31
|
+
* @epic T911
|
|
32
|
+
* @packageDocumentation
|
|
33
|
+
*/
|
|
34
|
+
import type { HarnessAdapter, HarnessOutputLine, HarnessProcessStatus, HarnessSpawnOptions, HarnessSpawnResult } from './types.js';
|
|
35
|
+
/**
|
|
36
|
+
* Harness adapter for the Pi coding agent CLI in the cleo-os sandbox.
|
|
37
|
+
*
|
|
38
|
+
* Manages a pool of Pi processes, each identified by a stable instance ID.
|
|
39
|
+
* Supports host-native and Docker sandbox launch modes.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const adapter = new PiCodingAgentAdapter();
|
|
44
|
+
* const { instanceId, exitPromise } = await adapter.spawn('T123', 'Write a hello-world script');
|
|
45
|
+
* const status = await exitPromise;
|
|
46
|
+
* console.log('exit code:', status.exitCode);
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export declare class PiCodingAgentAdapter implements HarnessAdapter {
|
|
52
|
+
/** Short adapter identifier. */
|
|
53
|
+
readonly id = "pi-coding-agent";
|
|
54
|
+
/** Active process tracking entries keyed by instance ID. */
|
|
55
|
+
private readonly processes;
|
|
56
|
+
/** Low-level Pi process launcher. */
|
|
57
|
+
private readonly wrapper;
|
|
58
|
+
/** Docker sandbox mode adapter. */
|
|
59
|
+
private readonly docker;
|
|
60
|
+
/**
|
|
61
|
+
* Generate a stable instance ID for a spawned process.
|
|
62
|
+
*
|
|
63
|
+
* @param taskId - CLEO task ID.
|
|
64
|
+
* @returns Instance ID string in the format `pi-<taskId>-<shortRandom>`.
|
|
65
|
+
*/
|
|
66
|
+
private makeInstanceId;
|
|
67
|
+
/**
|
|
68
|
+
* Spawn a Pi coding agent process with the given task prompt.
|
|
69
|
+
*
|
|
70
|
+
* When `opts.sandboxed` is `true` (or `CLEO_PI_SANDBOXED=1` is set), the
|
|
71
|
+
* adapter first verifies Docker readiness and delegates to
|
|
72
|
+
* {@link DockerModeAdapter.spawnInDocker}. On Docker failure it falls back
|
|
73
|
+
* to host-native mode with a warning to stderr.
|
|
74
|
+
*
|
|
75
|
+
* The returned {@link HarnessSpawnResult.exitPromise} resolves once the
|
|
76
|
+
* process exits or is killed. It NEVER rejects — failures are encoded in
|
|
77
|
+
* the resolved {@link HarnessProcessStatus}.
|
|
78
|
+
*
|
|
79
|
+
* @param taskId - Stable CLEO task identifier associated with this run.
|
|
80
|
+
* @param prompt - Prompt text to pass to the Pi agent.
|
|
81
|
+
* @param opts - Optional spawn configuration.
|
|
82
|
+
* @returns Spawn result containing the instance ID, PID, and exit promise.
|
|
83
|
+
*
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
spawn(taskId: string, prompt: string, opts?: HarnessSpawnOptions): Promise<HarnessSpawnResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Query the current status of a spawned process.
|
|
89
|
+
*
|
|
90
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
91
|
+
* @returns Current status snapshot, or `null` when the ID is not tracked.
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
status(instanceId: string): HarnessProcessStatus | null;
|
|
96
|
+
/**
|
|
97
|
+
* Terminate a running Pi process via SIGTERM-then-SIGKILL.
|
|
98
|
+
*
|
|
99
|
+
* Idempotent — subsequent calls after the first are no-ops.
|
|
100
|
+
*
|
|
101
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
kill(instanceId: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Return recently captured output lines for a process.
|
|
108
|
+
*
|
|
109
|
+
* Returns a snapshot of the bounded ring buffer maintained by the wrapper.
|
|
110
|
+
* The buffer is limited to `CLEO_HARNESS_OUTPUT_BUFFER` lines (default 500).
|
|
111
|
+
*
|
|
112
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
113
|
+
* @returns Array of recent output lines, oldest first; empty when not found.
|
|
114
|
+
*
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
output(instanceId: string): HarnessOutputLine[];
|
|
118
|
+
/**
|
|
119
|
+
* Spawn Pi inside a Docker sandbox container.
|
|
120
|
+
*
|
|
121
|
+
* Checks Docker readiness first. On failure, falls back to host-native
|
|
122
|
+
* mode with a stderr warning. Prompt delivery and output buffering follow
|
|
123
|
+
* the same pattern as the host-native path in {@link PiWrapper.start}.
|
|
124
|
+
*
|
|
125
|
+
* @param entry - Process tracking entry (mutated in place).
|
|
126
|
+
* @param prompt - Prompt text to deliver to Pi.
|
|
127
|
+
* @param cwd - Host working directory (bind-mounted into container).
|
|
128
|
+
* @param env - Extra environment variable overrides.
|
|
129
|
+
*/
|
|
130
|
+
private spawnInSandbox;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/harnesses/pi-coding-agent/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAKH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,gCAAgC;IAChC,QAAQ,CAAC,EAAE,qBAAqB;IAEhC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAE/D,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAElD;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAKtB;;;;;;;;;;;;;;;;;;OAkBG;IACG,KAAK,CACT,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,kBAAkB,CAAC;IAmC9B;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI;IAMvD;;;;;;;;OAQG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAU/C;;;;;;;;;;;OAWG;YACW,cAAc;CAuF7B"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Coding Agent Harness Adapter — cleo-os implementation.
|
|
3
|
+
*
|
|
4
|
+
* Implements the {@link HarnessAdapter} interface for the Pi coding agent CLI
|
|
5
|
+
* (`@mariozechner/pi-coding-agent`). Provides process spawn, status, kill,
|
|
6
|
+
* and output streaming for Pi processes launched by cleo-os.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* This adapter is the cleo-os counterpart to the CAAMP
|
|
10
|
+
* `PiSpawnProvider` in `packages/adapters/src/providers/pi/spawn.ts`.
|
|
11
|
+
* Where the CAAMP spawn provider is a detached fire-and-forget launcher,
|
|
12
|
+
* this adapter owns the full process lifecycle — attached stdin/stdout/stderr,
|
|
13
|
+
* bounded output buffering, and structured exit promises — so it can be
|
|
14
|
+
* used in orchestrated sandbox runs.
|
|
15
|
+
*
|
|
16
|
+
* Two launch modes are supported:
|
|
17
|
+
* - **Host-native** (default): Pi is invoked directly from PATH (or via
|
|
18
|
+
* `CLEO_PI_BINARY`). This is the standard mode for local development.
|
|
19
|
+
* - **Docker sandbox** (opt-in): Pi is launched inside the
|
|
20
|
+
* `cleo-sandbox/pi:local` Docker container when `CLEO_PI_SANDBOXED=1` is
|
|
21
|
+
* set or `sandboxed: true` is passed to {@link PiCodingAgentAdapter.spawn}.
|
|
22
|
+
* Falls back to host-native with a warning when Docker is unavailable.
|
|
23
|
+
*
|
|
24
|
+
* Instance IDs have the format `pi-<taskId>-<shortRandom>` to make
|
|
25
|
+
* correlation with task records straightforward.
|
|
26
|
+
*
|
|
27
|
+
* @see packages/adapters/src/providers/pi/spawn.ts — CAAMP spawn provider
|
|
28
|
+
* @see packages/cleo-os/src/harnesses/pi-coding-agent/pi-wrapper.ts — process management
|
|
29
|
+
* @see packages/cleo-os/src/harnesses/pi-coding-agent/docker-mode.ts — sandbox mode
|
|
30
|
+
* @task T922
|
|
31
|
+
* @epic T911
|
|
32
|
+
* @packageDocumentation
|
|
33
|
+
*/
|
|
34
|
+
import { writeFile } from 'node:fs/promises';
|
|
35
|
+
import { DockerModeAdapter, isSandboxedGlobally } from './docker-mode.js';
|
|
36
|
+
import { buildStatus, createProcessEntry, PiWrapper } from './pi-wrapper.js';
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// PiCodingAgentAdapter
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Harness adapter for the Pi coding agent CLI in the cleo-os sandbox.
|
|
42
|
+
*
|
|
43
|
+
* Manages a pool of Pi processes, each identified by a stable instance ID.
|
|
44
|
+
* Supports host-native and Docker sandbox launch modes.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const adapter = new PiCodingAgentAdapter();
|
|
49
|
+
* const { instanceId, exitPromise } = await adapter.spawn('T123', 'Write a hello-world script');
|
|
50
|
+
* const status = await exitPromise;
|
|
51
|
+
* console.log('exit code:', status.exitCode);
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export class PiCodingAgentAdapter {
|
|
57
|
+
/** Short adapter identifier. */
|
|
58
|
+
id = 'pi-coding-agent';
|
|
59
|
+
/** Active process tracking entries keyed by instance ID. */
|
|
60
|
+
processes = new Map();
|
|
61
|
+
/** Low-level Pi process launcher. */
|
|
62
|
+
wrapper = new PiWrapper();
|
|
63
|
+
/** Docker sandbox mode adapter. */
|
|
64
|
+
docker = new DockerModeAdapter();
|
|
65
|
+
/**
|
|
66
|
+
* Generate a stable instance ID for a spawned process.
|
|
67
|
+
*
|
|
68
|
+
* @param taskId - CLEO task ID.
|
|
69
|
+
* @returns Instance ID string in the format `pi-<taskId>-<shortRandom>`.
|
|
70
|
+
*/
|
|
71
|
+
makeInstanceId(taskId) {
|
|
72
|
+
const rnd = Math.random().toString(36).slice(2, 9);
|
|
73
|
+
return `pi-${taskId}-${rnd}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Spawn a Pi coding agent process with the given task prompt.
|
|
77
|
+
*
|
|
78
|
+
* When `opts.sandboxed` is `true` (or `CLEO_PI_SANDBOXED=1` is set), the
|
|
79
|
+
* adapter first verifies Docker readiness and delegates to
|
|
80
|
+
* {@link DockerModeAdapter.spawnInDocker}. On Docker failure it falls back
|
|
81
|
+
* to host-native mode with a warning to stderr.
|
|
82
|
+
*
|
|
83
|
+
* The returned {@link HarnessSpawnResult.exitPromise} resolves once the
|
|
84
|
+
* process exits or is killed. It NEVER rejects — failures are encoded in
|
|
85
|
+
* the resolved {@link HarnessProcessStatus}.
|
|
86
|
+
*
|
|
87
|
+
* @param taskId - Stable CLEO task identifier associated with this run.
|
|
88
|
+
* @param prompt - Prompt text to pass to the Pi agent.
|
|
89
|
+
* @param opts - Optional spawn configuration.
|
|
90
|
+
* @returns Spawn result containing the instance ID, PID, and exit promise.
|
|
91
|
+
*
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
async spawn(taskId, prompt, opts) {
|
|
95
|
+
const instanceId = this.makeInstanceId(taskId);
|
|
96
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
97
|
+
const env = opts?.env ?? {};
|
|
98
|
+
const useSandbox = opts?.sandboxed === true || isSandboxedGlobally();
|
|
99
|
+
// Build exit promise — resolved by the process entry when the child exits.
|
|
100
|
+
let resolveExit;
|
|
101
|
+
const exitPromise = new Promise((resolve) => {
|
|
102
|
+
resolveExit = resolve;
|
|
103
|
+
});
|
|
104
|
+
const entry = createProcessEntry(instanceId, taskId, resolveExit);
|
|
105
|
+
this.processes.set(instanceId, entry);
|
|
106
|
+
// Wire abort signal to kill.
|
|
107
|
+
if (opts?.signal !== undefined) {
|
|
108
|
+
opts.signal.addEventListener('abort', () => {
|
|
109
|
+
void this.kill(instanceId);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (useSandbox) {
|
|
113
|
+
await this.spawnInSandbox(entry, prompt, cwd, env);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
await this.wrapper.start(entry, prompt, cwd, env);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
instanceId,
|
|
120
|
+
pid: entry.pid,
|
|
121
|
+
exitPromise,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Query the current status of a spawned process.
|
|
126
|
+
*
|
|
127
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
128
|
+
* @returns Current status snapshot, or `null` when the ID is not tracked.
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
status(instanceId) {
|
|
133
|
+
const entry = this.processes.get(instanceId);
|
|
134
|
+
if (entry === undefined)
|
|
135
|
+
return null;
|
|
136
|
+
return buildStatus(entry);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Terminate a running Pi process via SIGTERM-then-SIGKILL.
|
|
140
|
+
*
|
|
141
|
+
* Idempotent — subsequent calls after the first are no-ops.
|
|
142
|
+
*
|
|
143
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
144
|
+
*
|
|
145
|
+
* @public
|
|
146
|
+
*/
|
|
147
|
+
async kill(instanceId) {
|
|
148
|
+
const entry = this.processes.get(instanceId);
|
|
149
|
+
if (entry === undefined)
|
|
150
|
+
return;
|
|
151
|
+
this.wrapper.terminate(entry);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Return recently captured output lines for a process.
|
|
155
|
+
*
|
|
156
|
+
* Returns a snapshot of the bounded ring buffer maintained by the wrapper.
|
|
157
|
+
* The buffer is limited to `CLEO_HARNESS_OUTPUT_BUFFER` lines (default 500).
|
|
158
|
+
*
|
|
159
|
+
* @param instanceId - Instance ID returned from {@link spawn}.
|
|
160
|
+
* @returns Array of recent output lines, oldest first; empty when not found.
|
|
161
|
+
*
|
|
162
|
+
* @public
|
|
163
|
+
*/
|
|
164
|
+
output(instanceId) {
|
|
165
|
+
const entry = this.processes.get(instanceId);
|
|
166
|
+
if (entry === undefined)
|
|
167
|
+
return [];
|
|
168
|
+
return [...entry.outputBuffer];
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Internal — Docker sandbox path
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* Spawn Pi inside a Docker sandbox container.
|
|
175
|
+
*
|
|
176
|
+
* Checks Docker readiness first. On failure, falls back to host-native
|
|
177
|
+
* mode with a stderr warning. Prompt delivery and output buffering follow
|
|
178
|
+
* the same pattern as the host-native path in {@link PiWrapper.start}.
|
|
179
|
+
*
|
|
180
|
+
* @param entry - Process tracking entry (mutated in place).
|
|
181
|
+
* @param prompt - Prompt text to deliver to Pi.
|
|
182
|
+
* @param cwd - Host working directory (bind-mounted into container).
|
|
183
|
+
* @param env - Extra environment variable overrides.
|
|
184
|
+
*/
|
|
185
|
+
async spawnInSandbox(entry, prompt, cwd, env) {
|
|
186
|
+
const readiness = await this.docker.checkReadiness();
|
|
187
|
+
if (!readiness.ready) {
|
|
188
|
+
process.stderr.write(`[cleo-os/pi-coding-agent] Docker sandbox not ready: ${readiness.reason ?? 'unknown reason'}. Falling back to host-native mode.\n`);
|
|
189
|
+
await this.wrapper.start(entry, prompt, cwd, env);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Write prompt to a host-side temp file (bind-mounted read-only into container).
|
|
193
|
+
const promptFilePath = `/tmp/cleo-pi-${entry.instanceId}.txt`;
|
|
194
|
+
entry.tmpFile = promptFilePath;
|
|
195
|
+
try {
|
|
196
|
+
await writeFile(promptFilePath, prompt, 'utf-8');
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
entry.state = 'failed';
|
|
200
|
+
entry.error = err instanceof Error ? err.message : String(err);
|
|
201
|
+
entry.endedAt = new Date().toISOString();
|
|
202
|
+
entry.resolveExit(buildStatus(entry));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Delegate to DockerModeAdapter which builds `docker run` args and spawns.
|
|
206
|
+
const child = this.docker.spawnInDocker({
|
|
207
|
+
prompt,
|
|
208
|
+
cwd,
|
|
209
|
+
env,
|
|
210
|
+
promptFilePath,
|
|
211
|
+
});
|
|
212
|
+
entry.child = child;
|
|
213
|
+
entry.pid = child.pid ?? null;
|
|
214
|
+
entry.state = 'running';
|
|
215
|
+
const bufferSize = 500; // use default; getOutputBufferSize() is in pi-wrapper.ts
|
|
216
|
+
child.stdout?.setEncoding('utf-8');
|
|
217
|
+
child.stdout?.on('data', (chunk) => {
|
|
218
|
+
for (const rawLine of chunk.split('\n')) {
|
|
219
|
+
const line = rawLine.trimEnd();
|
|
220
|
+
if (line.length === 0)
|
|
221
|
+
continue;
|
|
222
|
+
entry.outputBuffer.push({ source: 'stdout', line, timestamp: new Date().toISOString() });
|
|
223
|
+
if (entry.outputBuffer.length > bufferSize)
|
|
224
|
+
entry.outputBuffer.shift();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
child.stderr?.setEncoding('utf-8');
|
|
228
|
+
child.stderr?.on('data', (chunk) => {
|
|
229
|
+
for (const rawLine of chunk.split('\n')) {
|
|
230
|
+
const line = rawLine.trimEnd();
|
|
231
|
+
if (line.length === 0)
|
|
232
|
+
continue;
|
|
233
|
+
entry.outputBuffer.push({ source: 'stderr', line, timestamp: new Date().toISOString() });
|
|
234
|
+
if (entry.outputBuffer.length > bufferSize)
|
|
235
|
+
entry.outputBuffer.shift();
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
child.on('close', async (code, signal) => {
|
|
239
|
+
if (entry.killTimer !== null) {
|
|
240
|
+
clearTimeout(entry.killTimer);
|
|
241
|
+
entry.killTimer = null;
|
|
242
|
+
}
|
|
243
|
+
entry.child = null;
|
|
244
|
+
entry.endedAt = new Date().toISOString();
|
|
245
|
+
if (entry.state === 'running') {
|
|
246
|
+
entry.state = signal !== null ? 'killed' : 'exited';
|
|
247
|
+
entry.exitCode = code;
|
|
248
|
+
entry.terminatingSignal = signal;
|
|
249
|
+
}
|
|
250
|
+
// Clean up the host-side prompt temp file.
|
|
251
|
+
if (entry.tmpFile !== null) {
|
|
252
|
+
try {
|
|
253
|
+
const { unlink } = await import('node:fs/promises');
|
|
254
|
+
await unlink(entry.tmpFile);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Best-effort.
|
|
258
|
+
}
|
|
259
|
+
entry.tmpFile = null;
|
|
260
|
+
}
|
|
261
|
+
entry.resolveExit(buildStatus(entry));
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/harnesses/pi-coding-agent/adapter.ts"],"names":[],"mappings":"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"}
|