@commonlyai/cli 0.1.47 → 0.1.50
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/package.json
CHANGED
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { Type } from 'typebox';
|
|
15
|
-
import { connectMcp,
|
|
15
|
+
import { connectMcp, takeServers, toPiResult } from './pi-mcp-client.mjs';
|
|
16
16
|
|
|
17
17
|
export default async function commonlyMcpBridge(pi) {
|
|
18
|
-
|
|
18
|
+
// Read once and remove: the list carries the seat token, and pi's bash tool
|
|
19
|
+
// inherits this process's env (see takeServers).
|
|
20
|
+
const servers = takeServers(process.env);
|
|
19
21
|
const clients = [];
|
|
20
22
|
for (const server of servers) {
|
|
21
23
|
const client = connectMcp(server);
|
|
@@ -78,6 +78,19 @@ export const toPiResult = (result) => {
|
|
|
78
78
|
return { content: [{ type: 'text', text: result?.isError ? `error: ${text}` : text }], details: { isError: !!result?.isError } };
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Read the server list and REMOVE it from the environment. The list carries each
|
|
83
|
+
* server's substituted env — the seat's bearer token among it — and pi's `bash`
|
|
84
|
+
* tool spawns with `{ ...process.env }` (pi's getShellEnv), so leaving it in place
|
|
85
|
+
* lets one `env` from the model print the token. The MCP children already hold
|
|
86
|
+
* their own env from spawn time; nothing else reads this variable.
|
|
87
|
+
*/
|
|
88
|
+
export const takeServers = (env = process.env) => {
|
|
89
|
+
const servers = readServers(env.COMMONLY_PI_MCP);
|
|
90
|
+
delete env.COMMONLY_PI_MCP;
|
|
91
|
+
return servers;
|
|
92
|
+
};
|
|
93
|
+
|
|
81
94
|
export const readServers = (raw) => {
|
|
82
95
|
if (!raw) return [];
|
|
83
96
|
try { return JSON.parse(raw).filter((s) => s?.name && Array.isArray(s.command) && s.command.length); } catch { return []; }
|
package/src/lib/adapters/pi.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { spawn as childSpawn, spawnSync } from 'child_process';
|
|
36
36
|
import { createHash, randomUUID } from 'crypto';
|
|
37
|
-
import { mkdir, writeFile } from 'fs/promises';
|
|
37
|
+
import { mkdir, readdir, writeFile } from 'fs/promises';
|
|
38
38
|
import { homedir } from 'os';
|
|
39
39
|
import { dirname, join } from 'path';
|
|
40
40
|
import { fileURLToPath } from 'url';
|
|
@@ -127,6 +127,35 @@ export const seatHome = (ctx) => {
|
|
|
127
127
|
return ctx._piHome || join(homedir(), '.commonly', 'pi-homes', hash);
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Only a session pi wrote can be resumed. The wrapper persists one id per
|
|
132
|
+
* (agent, pod) across adapters, so a seat switched from codex hands pi the
|
|
133
|
+
* codex thread id on its first turn — pi answers `No session found` and the
|
|
134
|
+
* turn fails (sprint-impl's first spawn, 2026-09-18 05:06Z). pi's session
|
|
135
|
+
* files are `<timestamp>_<id>.jsonl` under the seat's session dir; an id with
|
|
136
|
+
* no file starts a fresh session under a new id, which the wrapper persists.
|
|
137
|
+
*/
|
|
138
|
+
export const sessionExists = async (sessionDir, sessionId) => {
|
|
139
|
+
if (!sessionId) return false;
|
|
140
|
+
try {
|
|
141
|
+
const files = await readdir(sessionDir);
|
|
142
|
+
return files.some((name) => name.endsWith(`_${sessionId}.jsonl`) || name === `${sessionId}.jsonl`);
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** Fail closed: pi cannot enforce `environment.sandbox`, so a seat that declares one does not start. */
|
|
149
|
+
export const assertNoSandboxDeclared = (environment = {}) => {
|
|
150
|
+
const sandbox = environment?.sandbox || {};
|
|
151
|
+
if (sandbox.trust === 'public') {
|
|
152
|
+
throw new Error('pi adapter: public-trust seats are not supported — pi has no enforced sandbox; do not attach a pi seat to a stranger-readable pod');
|
|
153
|
+
}
|
|
154
|
+
if (sandbox.mode && sandbox.mode !== 'none') {
|
|
155
|
+
throw new Error(`pi adapter: sandbox.mode=${sandbox.mode} cannot be enforced by pi; remove it or use the claude/codex adapter`);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
130
159
|
export const buildArgs = ({
|
|
131
160
|
prompt, provider, model, thinking, sessionId, isResume, sessionDir, bridge,
|
|
132
161
|
}) => [
|
|
@@ -196,9 +225,10 @@ export default {
|
|
|
196
225
|
},
|
|
197
226
|
|
|
198
227
|
async spawn(prompt, ctx = {}) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
228
|
+
// pi has no enforced sandbox. claude.js and codex.js refuse a public-trust
|
|
229
|
+
// seat they cannot confine; so does this adapter, and it refuses any declared
|
|
230
|
+
// sandbox mode rather than run unconfined under a spec that promised one.
|
|
231
|
+
assertNoSandboxDeclared(ctx.environment);
|
|
202
232
|
const provider = resolveProvider(ctx.environment);
|
|
203
233
|
const model = ctx.environment?.model || DEFAULT_MODEL;
|
|
204
234
|
const thinking = thinkingFor(ctx.environment?.effort);
|
|
@@ -213,6 +243,12 @@ export default {
|
|
|
213
243
|
const sessionDir = join(home, 'sessions');
|
|
214
244
|
await mkdir(agentDir, { recursive: true, mode: 0o700 });
|
|
215
245
|
await mkdir(sessionDir, { recursive: true, mode: 0o700 });
|
|
246
|
+
|
|
247
|
+
// Resume only what pi wrote; anything else (a codex thread id from before
|
|
248
|
+
// the switch, a wiped home) starts fresh under a new id.
|
|
249
|
+
const isResume = await sessionExists(sessionDir, ctx.sessionId);
|
|
250
|
+
const sessionId = isResume ? ctx.sessionId : randomUUID();
|
|
251
|
+
const fullPrompt = buildMemoryPreamble(prompt, ctx.memoryLongTerm, { freshSession: !isResume });
|
|
216
252
|
await writeFile(join(agentDir, 'models.json'), `${JSON.stringify(buildModelsJson(provider, model), null, 2)}\n`, { mode: 0o600 });
|
|
217
253
|
|
|
218
254
|
const servers = resolveMcpServers(ctx.environment?.mcp, ctx);
|