pi-landstrip 0.5.2 → 0.5.3

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 (2) hide show
  1. package/index.ts +40 -6
  2. package/package.json +2 -2
package/index.ts CHANGED
@@ -80,7 +80,7 @@ interface LandstripErrorResponse {
80
80
  message: string;
81
81
  }
82
82
 
83
- const LANDSTRIP_VERSION = [0, 10, 2] as const;
83
+ const LANDSTRIP_VERSION = [0, 10, 3] as const;
84
84
  const SUPPORTED_PLATFORMS = new Set<NodeJS.Platform>(['linux', 'darwin', 'win32']);
85
85
 
86
86
  const DEFAULT_CONFIG: SandboxConfig = {
@@ -337,7 +337,26 @@ function normalizeBlockedPath(path: string, cwd: string): string {
337
337
  return canonicalizePath(isAbsolute(path) ? path : join(cwd, path));
338
338
  }
339
339
 
340
- function extractBlockedPath(output: string, cwd: string): string | null {
340
+ function extractCandidatePaths(command: string): string[] {
341
+ const paths: string[] = [];
342
+ // Split on whitespace, preserving quoted strings minimally
343
+ const tokens = command.match(/[^\s"']+|"[^"]*"|'[^']*'/g) ?? [];
344
+ for (const token of tokens) {
345
+ const clean = token.replace(/^["']|["']$/g, '').replace(/[,;]$/, '');
346
+ if (
347
+ clean.startsWith('/') ||
348
+ clean.startsWith('~/') ||
349
+ clean === '~' ||
350
+ clean.startsWith('./') ||
351
+ clean.startsWith('../')
352
+ ) {
353
+ paths.push(clean);
354
+ }
355
+ }
356
+ return paths;
357
+ }
358
+
359
+ function extractBlockedPath(output: string, cwd: string, command?: string): string | null {
341
360
  // bash/sh: line X: /path: Permission denied
342
361
  let match = output.match(
343
362
  /(?:\/bin\/bash|bash|sh): (?:line \d+: )?([^:\n]+): (?:Operation not permitted|Permission denied)/,
@@ -362,11 +381,26 @@ function extractBlockedPath(output: string, cwd: string): string | null {
362
381
  if (error.file) return normalizeBlockedPath(error.file, cwd);
363
382
  }
364
383
 
384
+ // If landstrip reported an error but without a file field, try to
385
+ // extract the blocked path from the command itself
386
+ if (landstripErrors.length > 0 && command) {
387
+ const config = loadConfig(cwd);
388
+ for (const candidate of extractCandidatePaths(command)) {
389
+ const resolved = canonicalizePath(candidate);
390
+ if (
391
+ matchesPattern(resolved, config.filesystem.denyRead) ||
392
+ !matchesPattern(resolved, config.filesystem.allowRead)
393
+ ) {
394
+ return resolved;
395
+ }
396
+ }
397
+ }
398
+
365
399
  return null;
366
400
  }
367
401
 
368
- function extractBlockedWritePath(output: string, cwd: string): string | null {
369
- return extractBlockedPath(output, cwd);
402
+ function extractBlockedWritePath(output: string, cwd: string, command?: string): string | null {
403
+ return extractBlockedPath(output, cwd, command);
370
404
  }
371
405
 
372
406
  function parseLandstripErrors(output: string): LandstripErrorResponse[] {
@@ -1045,7 +1079,7 @@ export function createLandstripIntegration(
1045
1079
  return;
1046
1080
  }
1047
1081
 
1048
- const blockedPath = extractBlockedPath(stderrAcc, cwd);
1082
+ const blockedPath = extractBlockedPath(stderrAcc, cwd, command);
1049
1083
  if (blockedPath && ctx.hasUI) {
1050
1084
  const config = loadConfig(cwd);
1051
1085
  const isDeniedByDenyRead = matchesPattern(blockedPath, config.filesystem.denyRead);
@@ -1117,7 +1151,7 @@ export function createLandstripIntegration(
1117
1151
  const message = formatLandstripErrors(landstripErrors);
1118
1152
  result.content.unshift({ type: 'text', text: `\n${message}\n` });
1119
1153
  }
1120
- const blockedPath = extractBlockedWritePath(outputText, ctx.cwd);
1154
+ const blockedPath = extractBlockedWritePath(outputText, ctx.cwd, params.command);
1121
1155
 
1122
1156
  if (!blockedPath || !ctx.hasUI) return result;
1123
1157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Landlock-based sandboxing for pi with interactive permission prompts",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@earendil-works/pi-tui": "^0.78.0",
34
- "@jarkkojs/landstrip": "^0.10.2"
34
+ "@jarkkojs/landstrip": "^0.10.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@earendil-works/pi-coding-agent": "^0.78.0",