pi-landstrip 0.5.2 → 0.5.4
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/README.md +2 -0
- package/index.ts +40 -6
- package/package.json +2 -2
package/README.md
CHANGED
package/index.ts
CHANGED
|
@@ -80,7 +80,7 @@ interface LandstripErrorResponse {
|
|
|
80
80
|
message: string;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const LANDSTRIP_VERSION = [0, 10,
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.5.4",
|
|
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.
|
|
34
|
+
"@jarkkojs/landstrip": "^0.10.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@earendil-works/pi-coding-agent": "^0.78.0",
|