opencode-landstrip 0.14.2 → 0.15.0

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 -41
  2. package/package.json +2 -2
package/index.ts CHANGED
@@ -33,11 +33,11 @@ interface LandstripPolicy {
33
33
  }
34
34
 
35
35
  type LandstripTrap =
36
- | { kind: 'Filesystem'; operation: 'read' | 'write'; path: string; mechanism: string }
37
- | { kind: 'Network'; operation: string; target: string; mechanism: string }
38
- | { kind: 'Launch'; program: string; message: string }
39
- | { kind: 'Usage'; message: string }
40
- | { kind: 'Internal'; fields: Record<string, string> };
36
+ | { kind: 'filesystem'; operation: 'read' | 'write'; path: string; mechanism: string }
37
+ | { kind: 'network'; operation: string; target: string; mechanism: string }
38
+ | { kind: 'launch'; program: string; message: string }
39
+ | { kind: 'usage'; message: string }
40
+ | { kind: 'internal'; detail: Record<string, string> };
41
41
 
42
42
  interface BashSandboxState {
43
43
  originalCommand: string;
@@ -58,7 +58,7 @@ interface SandboxPermissionDecision {
58
58
 
59
59
  type ToastVariant = 'info' | 'success' | 'warning' | 'error';
60
60
 
61
- const LANDSTRIP_VERSION = [0, 14, 5] as const;
61
+ const LANDSTRIP_VERSION = [0, 15, 1] as const;
62
62
  const REQUIRED_LANDSTRIP_VERSION = LANDSTRIP_VERSION.join('.');
63
63
  const LANDSTRIP_OPERATIONS = new Set<'read' | 'write'>(['read', 'write']);
64
64
  const SUPPORTED_PLATFORMS = new Set<NodeJS.Platform>(['linux', 'darwin', 'win32']);
@@ -247,9 +247,9 @@ function extractBlockedPath(
247
247
  // Landstrip structured trap format carrying a denied path
248
248
  const landstripErrors = parseLandstripErrors(output);
249
249
  for (const trap of landstripErrors) {
250
- if (trap.kind === 'Filesystem') return normalizeBlockedPath(trap.path, baseDirectory);
251
- if (trap.kind === 'Internal' && trap.fields.file) {
252
- return normalizeBlockedPath(trap.fields.file, baseDirectory);
250
+ if (trap.kind === 'filesystem') return normalizeBlockedPath(trap.path, baseDirectory);
251
+ if (trap.kind === 'internal' && trap.detail.file) {
252
+ return normalizeBlockedPath(trap.detail.file, baseDirectory);
253
253
  }
254
254
  }
255
255
 
@@ -407,40 +407,39 @@ function hasMinimumVersion(version: string, minimum: readonly [number, number, n
407
407
 
408
408
  function decodeLandstripTrap(value: unknown): LandstripTrap | null {
409
409
  if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;
410
- const entries = Object.entries(value as Record<string, unknown>);
411
- if (entries.length !== 1) return null;
412
- const [kind, payload] = entries[0];
413
-
414
- switch (kind) {
415
- case 'Filesystem': {
416
- if (!Array.isArray(payload)) return null;
417
- const [operation, path, mechanism] = payload;
410
+ const record = value as Record<string, unknown>;
411
+ const mechanism = typeof record.mechanism === 'string' ? record.mechanism : '';
412
+
413
+ switch (record.kind) {
414
+ case 'filesystem': {
415
+ const { operation, path } = record;
418
416
  if (!isLandstripOperation(operation) || typeof path !== 'string') return null;
419
- return { kind, operation, path, mechanism: typeof mechanism === 'string' ? mechanism : '' };
417
+ return { kind: 'filesystem', operation, path, mechanism };
420
418
  }
421
- case 'Network': {
422
- if (!Array.isArray(payload)) return null;
423
- const [operation, target, mechanism] = payload;
419
+ case 'network': {
420
+ const { operation, target } = record;
424
421
  if (typeof operation !== 'string' || typeof target !== 'string') return null;
425
- return { kind, operation, target, mechanism: typeof mechanism === 'string' ? mechanism : '' };
422
+ return { kind: 'network', operation, target, mechanism };
426
423
  }
427
- case 'Launch': {
428
- if (!Array.isArray(payload)) return null;
429
- const [program, message] = payload;
424
+ case 'launch': {
425
+ const { program, message } = record;
430
426
  if (typeof program !== 'string') return null;
431
- return { kind, program, message: typeof message === 'string' ? message : '' };
427
+ return { kind: 'launch', program, message: typeof message === 'string' ? message : '' };
432
428
  }
433
- case 'Usage': {
434
- if (typeof payload !== 'string') return null;
435
- return { kind, message: payload };
429
+ case 'usage': {
430
+ const { message } = record;
431
+ if (typeof message !== 'string') return null;
432
+ return { kind: 'usage', message };
436
433
  }
437
- case 'Internal': {
438
- if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) return null;
439
- const fields: Record<string, string> = {};
440
- for (const [key, val] of Object.entries(payload as Record<string, unknown>)) {
441
- fields[key] = typeof val === 'string' ? val : JSON.stringify(val);
434
+ case 'internal': {
435
+ const detail: Record<string, string> = {};
436
+ const payload = record.detail;
437
+ if (typeof payload === 'object' && payload !== null && !Array.isArray(payload)) {
438
+ for (const [key, val] of Object.entries(payload as Record<string, unknown>)) {
439
+ detail[key] = typeof val === 'string' ? val : JSON.stringify(val);
440
+ }
442
441
  }
443
- return { kind, fields };
442
+ return { kind: 'internal', detail };
444
443
  }
445
444
  default:
446
445
  return null;
@@ -470,20 +469,20 @@ function parseLandstripErrors(output: string): LandstripTrap[] {
470
469
 
471
470
  function formatLandstripTrap(trap: LandstripTrap): string {
472
471
  switch (trap.kind) {
473
- case 'Filesystem':
472
+ case 'filesystem':
474
473
  return `landstrip: filesystem ${trap.operation} denied (${trap.path})${
475
474
  trap.mechanism ? ` [${trap.mechanism}]` : ''
476
475
  }`;
477
- case 'Network':
476
+ case 'network':
478
477
  return `landstrip: network ${trap.operation} denied (${trap.target})${
479
478
  trap.mechanism ? ` [${trap.mechanism}]` : ''
480
479
  }`;
481
- case 'Launch':
480
+ case 'launch':
482
481
  return `landstrip: launch failed (${trap.program})${trap.message ? `: ${trap.message}` : ''}`;
483
- case 'Usage':
482
+ case 'usage':
484
483
  return `landstrip: usage error: ${trap.message}`;
485
- case 'Internal': {
486
- const detail = Object.entries(trap.fields)
484
+ case 'internal': {
485
+ const detail = Object.entries(trap.detail)
487
486
  .map(([key, val]) => `${key}: ${val}`)
488
487
  .join(', ');
489
488
  return `landstrip: internal error${detail ? ` (${detail})` : ''}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-landstrip",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "description": "Landlock-based sandboxing for opencode with landstrip",
5
5
  "keywords": [
6
6
  "landlock",
@@ -49,7 +49,7 @@
49
49
  "ci:test": "npm test"
50
50
  },
51
51
  "dependencies": {
52
- "@landstrip/landstrip": "^0.14.5"
52
+ "@landstrip/landstrip": "^0.15.1"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@opencode-ai/plugin": "^1.17.7",