@commonlyai/cli 0.1.48 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonlyai/cli",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI — connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -12,10 +12,12 @@
12
12
  */
13
13
 
14
14
  import { Type } from 'typebox';
15
- import { connectMcp, readServers, toPiResult } from './pi-mcp-client.mjs';
15
+ import { connectMcp, takeServers, toPiResult } from './pi-mcp-client.mjs';
16
16
 
17
17
  export default async function commonlyMcpBridge(pi) {
18
- const servers = readServers(process.env.COMMONLY_PI_MCP);
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 []; }
@@ -145,6 +145,17 @@ export const sessionExists = async (sessionDir, sessionId) => {
145
145
  }
146
146
  };
147
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
+
148
159
  export const buildArgs = ({
149
160
  prompt, provider, model, thinking, sessionId, isResume, sessionDir, bridge,
150
161
  }) => [
@@ -214,6 +225,10 @@ export default {
214
225
  },
215
226
 
216
227
  async spawn(prompt, ctx = {}) {
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);
217
232
  const provider = resolveProvider(ctx.environment);
218
233
  const model = ctx.environment?.model || DEFAULT_MODEL;
219
234
  const thinking = thinkingFor(ctx.environment?.effort);