pi-landstrip 0.19.12 → 0.19.14

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.
Files changed (4) hide show
  1. package/README.md +51 -71
  2. package/api.ts +134 -11
  3. package/dist/index.ts +63 -23
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -9,52 +9,38 @@ Requires Pi ≥0.82.0 and Node.js ≥22.19.0.
9
9
  pi install npm:pi-landstrip
10
10
  ```
11
11
 
12
- Includes native binaries for Linux, macOS, and Windows on x64 and Arm64.
13
- Enabled sandboxing fails closed if the binary or platform is unusable;
14
- `--no-sandbox` or `enabled: false` explicitly permits unsandboxed execution.
12
+ Unusable sandboxing fails closed; `--no-sandbox` or `enabled: false` permits
13
+ unsandboxed execution.
15
14
 
16
15
  ## Permissions and configuration
17
16
 
18
- Agent permissions govern tool dispatch, not sandbox access: approval never
19
- bypasses a sandbox hard denial. With sandboxing enabled, AI Bash, `!`, `!!`, and
20
- subagents use OS isolation. Primary file tools run in trusted Pi; setting
21
- `toolFilesystemPolicy: "sandbox"` applies filesystem policy checks, not OS isolation.
17
+ With sandboxing enabled, Bash (`bash`, `!`, `!!`) and workers are OS-isolated.
18
+ Primary file tools remain in trusted Pi; `toolFilesystemPolicy: "sandbox"`
19
+ adds policy checks, not isolation. Tool approval never bypasses hard denials.
22
20
 
23
- Policy merges from bundled [`sandbox.json`](sandbox.json), then
24
- `~/.pi/agent/sandbox.json`, then trusted-project `.pi/sandbox.json`.
25
- Arrays combine; later scalar values win. Persistent sandbox approvals update
26
- the global or project file. Store only overrides:
21
+ Configuration: bundled [`sandbox.json`](sandbox.json) → `~/.pi/agent/sandbox.json`
22
+ → trusted-project `.pi/sandbox.json`. Arrays combine; later scalars win.
23
+ Persistent approvals update global/project overrides. For policy-only shell reads:
27
24
 
28
25
  ```json
29
- {
30
- "shell": { "readAccess": "policy" },
31
- "filesystem": { "allowWrite": ["."] },
32
- "network": { "allowNetwork": false }
33
- }
26
+ { "shell": { "readAccess": "policy" } }
34
27
  ```
35
28
 
36
- The bundled shell default is `readAccess: "host"`; workers always use policy
37
- reads. Windows requires an explicit read allowlist. Native policy and CLI
38
- semantics are in [landstrip(1)](../landstrip/man/man1/landstrip.1).
39
- Runtime seccomp traps and query approval are Linux-only. On macOS, `--trap`
40
- reports setup/launch errors only and closes on successful exec.
41
-
42
- Worker read boundaries:
43
-
44
- - Bootstrap grants can override bundled `denyRead` defaults, never persisted
45
- global or trusted-project denies. Denied startup resources prevent launch.
46
- Legacy copies of `/home` or `/Users` count as explicit denies: remove them only
47
- if unintended; bundled defaults remain. New global files start as `{}`.
48
- - `filesystem.denyReadAlways` cannot be overridden by equal/nested read grants
49
- or shell host reads. Private workers protect parent auth-file and lock roots
50
- with this policy and `denyWrite`. Non-empty `denyReadAlways` and private worker
51
- auth are unsupported on Windows.
52
- - `filesystem.denyWriteAlways` cannot be overridden by equal/nested write grants
53
- or interactive prompts; write attempts are silently rejected. Non-empty
54
- `denyWriteAlways` is unsupported on Windows.
55
- - External skill, prompt, and theme directories may need explicit discovery
56
- access. Prefer resource file paths: resolved-file grants do not cover enclosing
57
- directories.
29
+ Shell reads default to `"host"`; workers always use policy reads. See
30
+ [landstrip(1)](../landstrip/man/man1/landstrip.1) for native policy, traps,
31
+ and platform limits, including Windows read grants and hard-denial restrictions.
32
+
33
+ Worker boundaries:
34
+
35
+ - Bootstrap grants override bundled `denyRead`, never global/trusted-project
36
+ denies. Denied startup resources prevent launch. Legacy `/home` or `/Users`
37
+ denies are explicit overrides; remove them only if unintended.
38
+ - Hard read denials also apply to shell host reads. Private workers protect
39
+ parent auth-file/lock roots with `denyReadAlways` and `denyWrite`;
40
+ private worker authentication is unsupported on Windows.
41
+ - `filesystem.denyWriteAlways` rejects write attempts without prompting.
42
+ - External skills/prompts/themes may need directory discovery grants;
43
+ resolved-file grants do not include parent directories.
58
44
 
59
45
  ## Agents and tasks
60
46
 
@@ -69,49 +55,43 @@ Configure `~/.pi/agent/landstrip.json`, trusted-project `.pi/landstrip.json`, or
69
55
  }
70
56
  ```
71
57
 
72
- `maxSubagents` accepts 0–16. Agent definitions can override model, prompt, mode,
73
- options, and permissions.
58
+ `maxSubagents`: 0–16. Agent definitions can override model, prompt, mode, options,
59
+ and permissions.
74
60
 
75
- `/landstrip` opens the management pane; `/landstrip help` lists commands and
76
- shortcuts. Common commands:
61
+ Use `/landstrip` for the management pane and `/landstrip help` for commands.
62
+ Task IDs accept unique prefixes; bare IDs open logs. `/landstrip tasks kill <id>`
63
+ terminates a task. `Ctrl+Shift+A` cycles primary agents while idle.
64
+ The `task` tool accepts `background: true` for immediate return and `task_id`
65
+ to continue a saved task. Press `Enter` in running/queued logs to steer workers.
77
66
 
78
- | Command | Action |
79
- | --------------------------------- | ------------------------------- |
80
- | `/landstrip status` | Inspect sandbox, agents, tasks. |
81
- | `/landstrip settings` | Set concurrency/file policy. |
82
- | `/landstrip sandbox [on\|off]` | Inspect/toggle OS isolation. |
83
- | `/landstrip agents [@name]` | List/select primary agents. |
84
- | `/landstrip subagents` | List process subagents. |
85
- | `/landstrip tasks [list]` | List task sessions. |
86
- | `/landstrip tasks kill <task-id>` | Terminate a task. |
87
- | `/landstrip logs [task-id]` | Open task logs. |
67
+ ### Worker environment and authentication
88
68
 
89
- Task IDs accept unique prefixes; bare IDs open logs in the pane.
90
- `Ctrl+Shift+A` cycles primary agents while Pi is idle. The `task` tool accepts
91
- `background: true` for immediate return and `task_id` to continue a saved task.
92
- Press `Enter` in running/queued task logs to steer the worker.
69
+ Workers start `--offline`; install resources and refresh model catalogs in the
70
+ parent first. Provider endpoints still need network permission.
93
71
 
94
- ### Worker environment and authentication
72
+ Only runtime paths, locale, certificate paths, and supported Pi config are
73
+ inherited—not parent API keys, tool tokens, proxy URLs, or arbitrary variables.
74
+ The parent refreshes selected-provider auth and sends it, along with selected
75
+ Bedrock/Vertex/Azure settings, over a private Unix pipe, not arguments or
76
+ environment. Configure tool logins separately; authenticated workers cannot
77
+ launch on Windows.
95
78
 
96
- Workers start `--offline`: install resources and refresh model catalogs in the
97
- parent first. Model/auth requests still need permitted endpoints.
79
+ Ambient AWS profiles need an explicit region for non-ARN Bedrock models.
80
+ Custom credential-chain environment paths are unsupported; use scoped keys or
81
+ a Bedrock bearer token. Provider credential files need filesystem grants.
98
82
 
99
- Workers inherit only runtime paths, locale, certificate paths, and supported Pi
100
- configuration—not parent API keys, tool tokens, proxy URLs, or arbitrary variables.
101
- The parent refreshes selected-provider auth and sends it through a private Unix
102
- pipe, never arguments or worker environment. Environment-based tool logins need
103
- separate configuration; authenticated workers fail before spawning on Windows.
83
+ ## Extension API
104
84
 
105
- Selected Bedrock, Vertex, and Azure settings also travel privately. Ambient AWS
106
- profiles need an explicit region for non-ARN models; custom credential-chain
107
- environment paths are unsupported (use scoped keys or a Bedrock bearer token).
108
- Provider credential files still need filesystem permission.
85
+ [`pi-landstrip/api`](api.ts) exports `createLandstrip`. `prepareProcess()` returns
86
+ `undefined` without a runtime. Dispose prepared processes separately from the
87
+ discovery handle; generic `spawn()` returns `ChildProcess`, RPC workers use
88
+ piped streams.
109
89
 
110
90
  ## Development
111
91
 
112
- From this package: `npm ci`, then `npm run all` (format, lint, typecheck, build,
113
- and tests; building requires Bun). For a read-only formatting check: `npm run ci:fmt`.
92
+ From this package: `npm ci && npm run all` (Bun required).
93
+ `npm run ci:fmt` checks formatting without edits.
114
94
 
115
95
  ## License
116
96
 
117
- Apache-2.0; see [LICENSE](LICENSE).
97
+ [Apache-2.0](LICENSE).
package/api.ts CHANGED
@@ -105,6 +105,11 @@ export interface LandstripPreparedProcess {
105
105
  dispose(): Promise<void>;
106
106
  }
107
107
 
108
+ export interface Landstrip {
109
+ prepareProcess(options: LandstripProcessOptions): Promise<LandstripPreparedProcess | undefined>;
110
+ dispose(): void;
111
+ }
112
+
108
113
  export interface LandstripRpcChildProcess {
109
114
  readonly stdin: Writable;
110
115
  readonly stdout: Readable;
@@ -126,7 +131,7 @@ export type LandstripSpawn = (
126
131
  command: string,
127
132
  args: readonly string[],
128
133
  options: SpawnOptions,
129
- ) => ChildProcess | LandstripRpcChildProcess;
134
+ ) => ChildProcess;
130
135
 
131
136
  export type LandstripEvent =
132
137
  | { readonly type: 'sandbox.changed'; readonly context: LandstripContextV2 }
@@ -184,35 +189,152 @@ function isRuntime(value: unknown): value is PiLandstripRuntimeV2 {
184
189
  );
185
190
  }
186
191
 
187
- export function useLandstrip(
192
+ function isDisposableProcess(value: unknown): value is Pick<LandstripPreparedProcess, 'dispose'> {
193
+ return isRecord(value) && typeof value.dispose === 'function';
194
+ }
195
+
196
+ function isPreparedProcess(value: unknown): value is LandstripPreparedProcess {
197
+ return (
198
+ isRecord(value) &&
199
+ typeof value.command === 'string' &&
200
+ value.command.trim().length > 0 &&
201
+ Array.isArray(value.args) &&
202
+ [...value.args].every((arg: unknown) => typeof arg === 'string') &&
203
+ typeof value.cwd === 'string' &&
204
+ value.cwd.length > 0 &&
205
+ isRecord(value.env) &&
206
+ Object.values(value.env).every((entry) => entry === undefined || typeof entry === 'string') &&
207
+ typeof value.spawn === 'function' &&
208
+ isDisposableProcess(value)
209
+ );
210
+ }
211
+
212
+ function discoverLandstrip(
188
213
  pi: ExtensionAPI,
189
214
  callback: (runtime: PiLandstripRuntimeV2) => void,
215
+ onInvalid?: (error: Error) => void,
190
216
  ): () => void {
191
217
  const events = eventBus(pi);
192
218
  if (!events) return () => undefined;
193
219
 
194
220
  let current: PiLandstripRuntimeV2 | undefined;
195
- const register = (runtime: PiLandstripRuntimeV2): void => {
196
- if (runtime === current) return;
197
- current = runtime;
198
- callback(runtime);
221
+ let disposed = false;
222
+ const register = (value: unknown): void => {
223
+ if (disposed) return;
224
+ if (!isRuntime(value)) {
225
+ onInvalid?.(new Error('Invalid Landstrip runtime v2 declaration'));
226
+ return;
227
+ }
228
+ if (value === current) return;
229
+ current = value;
230
+ callback(value);
199
231
  };
200
232
  const unsubscribe = events.on(RUNTIME_REGISTER_EVENT, (value) => {
201
- if (!isRecord(value) || value.version !== LANDSTRIP_RUNTIME_VERSION) return;
202
- if (isRuntime(value.runtime)) register(value.runtime);
233
+ if (disposed) return;
234
+ if (!isRecord(value) || value.version !== LANDSTRIP_RUNTIME_VERSION) {
235
+ onInvalid?.(new Error('Invalid Landstrip runtime v2 registration'));
236
+ return;
237
+ }
238
+ register(value.runtime);
203
239
  });
240
+ const dispose = (): void => {
241
+ if (disposed) return;
242
+ disposed = true;
243
+ unsubscribe();
244
+ };
204
245
  try {
205
246
  events.emit(RUNTIME_DISCOVER_EVENT, {
206
247
  version: LANDSTRIP_RUNTIME_VERSION,
207
248
  register,
208
249
  } satisfies RuntimeDiscovery);
209
- return unsubscribe;
250
+ return dispose;
210
251
  } catch (error) {
211
- unsubscribe();
252
+ dispose();
212
253
  throw error;
213
254
  }
214
255
  }
215
256
 
257
+ export function useLandstrip(
258
+ pi: ExtensionAPI,
259
+ callback: (runtime: PiLandstripRuntimeV2) => void,
260
+ ): () => void {
261
+ return discoverLandstrip(pi, callback);
262
+ }
263
+
264
+ export function createLandstrip(pi: ExtensionAPI): Landstrip {
265
+ let runtime: PiLandstripRuntimeV2 | undefined;
266
+ let runtimeError: Error | undefined;
267
+ let disposed = false;
268
+ const stopDiscovery = discoverLandstrip(
269
+ pi,
270
+ (value) => {
271
+ runtime = value;
272
+ },
273
+ (error) => {
274
+ runtimeError ??= error;
275
+ },
276
+ );
277
+ if (runtimeError) {
278
+ stopDiscovery();
279
+ throw runtimeError;
280
+ }
281
+
282
+ return {
283
+ async prepareProcess(options) {
284
+ if (disposed) throw new Error('Landstrip handle has been disposed');
285
+ if (runtimeError) throw runtimeError;
286
+ options.signal?.throwIfAborted();
287
+ if (!runtime) return undefined;
288
+
289
+ const prepared: unknown = await runtime.prepareProcess(options);
290
+ if (!isDisposableProcess(prepared)) {
291
+ throw new Error('Landstrip returned a process without a disposal method');
292
+ }
293
+ let disposePromise: Promise<void> | undefined;
294
+ const dispose = (): Promise<void> => {
295
+ disposePromise ??= Promise.resolve().then(() => prepared.dispose());
296
+ return disposePromise;
297
+ };
298
+ try {
299
+ options.signal?.throwIfAborted();
300
+ if (disposed) throw new Error('Landstrip handle has been disposed');
301
+ if (runtimeError) throw runtimeError;
302
+ if (!isPreparedProcess(prepared)) {
303
+ throw new Error('Landstrip returned an invalid prepared process');
304
+ }
305
+ } catch (error) {
306
+ try {
307
+ await dispose();
308
+ } catch (cleanupError) {
309
+ throw new AggregateError(
310
+ [error, cleanupError],
311
+ 'Landstrip process preparation cleanup failed',
312
+ );
313
+ }
314
+ throw error;
315
+ }
316
+
317
+ return {
318
+ command: prepared.command,
319
+ args: prepared.args,
320
+ cwd: prepared.cwd,
321
+ env: prepared.env,
322
+ spawn(command, args, spawnOptions) {
323
+ if (disposePromise) throw new Error('Prepared Landstrip process has been disposed');
324
+ return prepared.spawn(command, args, spawnOptions);
325
+ },
326
+ dispose,
327
+ };
328
+ },
329
+ dispose() {
330
+ if (disposed) return;
331
+ disposed = true;
332
+ stopDiscovery();
333
+ runtime = undefined;
334
+ },
335
+ };
336
+ }
337
+
216
338
  function provideExtensionProvider(
217
339
  pi: ExtensionAPI,
218
340
  register: (runtime: PiLandstripRuntimeV2) => () => void,
@@ -283,7 +405,8 @@ export function contextFromEnvironment(
283
405
  value.version !== LANDSTRIP_RUNTIME_VERSION ||
284
406
  value.host !== 'pi' ||
285
407
  value.role !== 'subagent' ||
286
- !['enabled', 'disabled', 'unavailable'].includes(String(value.sandbox)) ||
408
+ typeof value.sandbox !== 'string' ||
409
+ !['enabled', 'disabled', 'unavailable'].includes(value.sandbox) ||
287
410
  typeof value.cwd !== 'string' ||
288
411
  !Number.isInteger(value.depth) ||
289
412
  (value.depth as number) < 0 ||
package/dist/index.ts CHANGED
@@ -5895,10 +5895,24 @@ function shellEnvironment(env, cwd, proxyPort, proxyToken) {
5895
5895
  const result = { ...process.env, ...env, PWD: resolve4(cwd) };
5896
5896
  return createProxyEnvironment(proxyPort, proxyToken, result);
5897
5897
  }
5898
+ var WINDOWS_LAUNCHER_ENVIRONMENT = [
5899
+ "ProgramData",
5900
+ "LOCALAPPDATA",
5901
+ "SystemRoot",
5902
+ "windir",
5903
+ "ComSpec",
5904
+ "PATHEXT"
5905
+ ];
5898
5906
  function createLandstripLauncherEnvironment(providerEnv, hostEnv = process.env, platform = process.platform) {
5899
- if (platform !== "win32" || hostEnv.ProgramData === undefined)
5907
+ if (platform !== "win32")
5900
5908
  return providerEnv;
5901
- return { ...providerEnv, ProgramData: hostEnv.ProgramData };
5909
+ const result = { ...providerEnv };
5910
+ for (const name of WINDOWS_LAUNCHER_ENVIRONMENT) {
5911
+ const value = hostEnv[name];
5912
+ if (value !== undefined)
5913
+ result[name] = value;
5914
+ }
5915
+ return result;
5902
5916
  }
5903
5917
  function createWindowsDenyRead(denyRead, volumeRoot) {
5904
5918
  return [...new Set([...denyRead.filter((path) => !path.startsWith("/")), volumeRoot])];
@@ -6015,7 +6029,7 @@ function createLandstripIntegrationWithPrompts(options, permissionPrompts) {
6015
6029
  if (!provider)
6016
6030
  return { decision: "abstain" };
6017
6031
  const result = await provider.decide(request);
6018
- if (typeof result !== "object" || result === null || !("decision" in result) || !["allow", "deny", "abstain"].includes(String(result.decision)) || "reason" in result && result.reason !== undefined && typeof result.reason !== "string") {
6032
+ if (typeof result !== "object" || result === null || !("decision" in result) || typeof result.decision !== "string" || !["allow", "deny", "abstain"].includes(result.decision) || "reason" in result && result.reason !== undefined && typeof result.reason !== "string") {
6019
6033
  throw new Error(`Permission ask provider "${provider.id}" returned an invalid decision`);
6020
6034
  }
6021
6035
  return result;
@@ -6376,7 +6390,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6376
6390
  }
6377
6391
  });
6378
6392
  }
6379
- function prepareUnsandboxedRpcWorker(options) {
6393
+ function prepareUnsandboxedProcess(options) {
6380
6394
  let spawned = false;
6381
6395
  let disposed = false;
6382
6396
  let disposePromise;
@@ -6412,7 +6426,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6412
6426
  }
6413
6427
  };
6414
6428
  }
6415
- async function prepareRpcWorker(options) {
6429
+ async function prepareManagedProcess(options) {
6416
6430
  if (options.signal?.aborted)
6417
6431
  throw new Error("Task cancelled");
6418
6432
  if (!ensureSandboxState(options.ctx)) {
@@ -6423,7 +6437,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6423
6437
  warnUnsandboxed(options.ctx, "Subagent processes are running without Landstrip sandboxing", "warning");
6424
6438
  unsandboxedWorkerWarningShown = true;
6425
6439
  }
6426
- return prepareUnsandboxedRpcWorker(options);
6440
+ return prepareUnsandboxedProcess(options);
6427
6441
  }
6428
6442
  const allowances = {
6429
6443
  permissionPromptLabel: options.permissionPromptLabel,
@@ -6461,7 +6475,28 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6461
6475
  let policy;
6462
6476
  let trapSocket;
6463
6477
  let childEnd;
6478
+ let preparedChild;
6464
6479
  let disposed = false;
6480
+ const cleanup = async () => {
6481
+ const errors = [];
6482
+ for (const release of [
6483
+ () => stopPreparedChild(preparedChild),
6484
+ () => trapSocket?.destroy(),
6485
+ () => childEnd?.destroy(),
6486
+ () => {
6487
+ if (policy)
6488
+ rmSync2(policy.dir, { recursive: true, force: true });
6489
+ },
6490
+ () => proxy?.stop()
6491
+ ]) {
6492
+ try {
6493
+ await release();
6494
+ } catch (error) {
6495
+ errors.push(error);
6496
+ }
6497
+ }
6498
+ return errors;
6499
+ };
6465
6500
  try {
6466
6501
  if (options.signal?.aborted)
6467
6502
  throw new Error("Task cancelled");
@@ -6473,20 +6508,17 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6473
6508
  attachWorkerTrap(trapSocket, options.ctx, options.cwd, processAllowances, options.signal);
6474
6509
  }
6475
6510
  } catch (error) {
6476
- trapSocket?.destroy();
6477
- childEnd?.destroy();
6478
- if (policy)
6479
- rmSync2(policy.dir, { recursive: true, force: true });
6480
- await proxy?.stop();
6511
+ const cleanupErrors = await cleanup();
6512
+ if (cleanupErrors.length > 0) {
6513
+ throw new AggregateError([error, ...cleanupErrors], "Landstrip process preparation cleanup failed");
6514
+ }
6481
6515
  throw error;
6482
6516
  }
6483
6517
  const workerPolicy = policy;
6484
- const workerTrapSocket = trapSocket;
6485
6518
  const workerChildEnd = childEnd;
6486
6519
  const workerEnv = createProxyEnvironment(proxy?.port ?? null, proxyToken, options.env ?? {});
6487
6520
  let spawned = false;
6488
6521
  let disposePromise;
6489
- let preparedChild;
6490
6522
  const spawnWorker = (_command, _args, spawnOptions) => {
6491
6523
  if (disposed)
6492
6524
  throw new Error("Prepared process has been disposed");
@@ -6521,9 +6553,9 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6521
6553
  detached: true,
6522
6554
  stdio
6523
6555
  });
6524
- workerChildEnd?.destroy();
6525
- patchProcessGroupKill(child);
6526
6556
  preparedChild = child;
6557
+ patchProcessGroupKill(child);
6558
+ workerChildEnd?.destroy();
6527
6559
  return child;
6528
6560
  };
6529
6561
  return {
@@ -6535,21 +6567,29 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6535
6567
  dispose() {
6536
6568
  disposePromise ??= (async () => {
6537
6569
  disposed = true;
6538
- await stopPreparedChild(preparedChild);
6539
- workerTrapSocket?.destroy();
6540
- workerChildEnd?.destroy();
6541
- rmSync2(workerPolicy.dir, { recursive: true, force: true });
6542
- await proxy?.stop();
6570
+ const cleanupErrors = await cleanup();
6571
+ if (cleanupErrors.length > 0) {
6572
+ throw new AggregateError(cleanupErrors, "Landstrip process disposal cleanup failed");
6573
+ }
6543
6574
  })();
6544
6575
  return disposePromise;
6545
6576
  }
6546
6577
  };
6547
6578
  }
6579
+ async function prepareRpcWorker(options) {
6580
+ const prepared = await prepareManagedProcess(options);
6581
+ return {
6582
+ ...prepared,
6583
+ spawn(command, args, spawnOptions) {
6584
+ return prepared.spawn(command, args, spawnOptions);
6585
+ }
6586
+ };
6587
+ }
6548
6588
  async function prepareProcess(options) {
6549
6589
  if (process.platform === "win32" && (getNoSandboxFlag() || !loadConfig(options.cwd).enabled)) {
6550
6590
  throw new Error("Generic process preparation requires sandboxing on Windows");
6551
6591
  }
6552
- return prepareRpcWorker({
6592
+ return prepareManagedProcess({
6553
6593
  ...options,
6554
6594
  env: options.env ?? process.env,
6555
6595
  readPaths: options.readPaths ?? [],
@@ -7003,7 +7043,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
7003
7043
  const errorText = formatError(error);
7004
7044
  const fallbackOutput = `${stderrOutput}
7005
7045
  ${errorText}`;
7006
- const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(fallbackOutput, ctx.cwd);
7046
+ const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(fallbackOutput, ctx.cwd, params.command);
7007
7047
  if (blockedWritePath) {
7008
7048
  const retryResult = await retryWithAccess("write", blockedWritePath);
7009
7049
  if (retryResult)
@@ -7028,7 +7068,7 @@ ${errorText}`;
7028
7068
  ${message}
7029
7069
  ` });
7030
7070
  }
7031
- const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(stderrOutput, ctx.cwd);
7071
+ const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(stderrOutput, ctx.cwd, params.command);
7032
7072
  if (blockedWritePath) {
7033
7073
  const retryResult = await retryWithAccess("write", blockedWritePath);
7034
7074
  if (retryResult)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.19.12",
3
+ "version": "0.19.14",
4
4
  "description": "Sandboxed Bash and process-backed agents for Pi",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -39,7 +39,7 @@
39
39
  "ci:test": "npm run test"
40
40
  },
41
41
  "dependencies": {
42
- "@landstrip/landstrip-api": "^0.19.12",
42
+ "@landstrip/landstrip-api": "^0.19.14",
43
43
  "jsonc-parser": "^3.3.1",
44
44
  "minimatch": "^10.2.5",
45
45
  "undici": "^8.9.0",