opencode-landstrip 0.16.3 → 0.16.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.
Files changed (4) hide show
  1. package/index.ts +19 -12
  2. package/package.json +1 -1
  3. package/shared.ts +4 -0
  4. package/tui.ts +2 -5
package/index.ts CHANGED
@@ -11,6 +11,7 @@ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
11
11
  import { URL } from 'node:url';
12
12
 
13
13
  import {
14
+ list,
14
15
  type LandstripTrap,
15
16
  type SandboxConfig,
16
17
  type SandboxFilesystemConfig,
@@ -809,12 +810,12 @@ const plugin: Plugin = async ({ client, directory }: PluginInput, options?: Plug
809
810
  function sandboxSummary(config: SandboxConfig): string {
810
811
  const { globalPath, projectPath } = getConfigPaths(directory);
811
812
  const networkMode = config.network.allowNetwork ? 'unrestricted' : 'proxied';
812
- const allowed = config.network.allowedDomains.join(', ') || '(none)';
813
- const denied = config.network.deniedDomains.join(', ') || '(none)';
814
- const denyRead = config.filesystem.denyRead.join(', ') || '(none)';
815
- const allowRead = config.filesystem.allowRead.join(', ') || '(none)';
816
- const allowWrite = config.filesystem.allowWrite.join(', ') || '(none)';
817
- const denyWrite = config.filesystem.denyWrite.join(', ') || '(none)';
813
+ const allowed = list(config.network.allowedDomains);
814
+ const denied = list(config.network.deniedDomains);
815
+ const denyRead = list(config.filesystem.denyRead);
816
+ const allowRead = list(config.filesystem.allowRead);
817
+ const allowWrite = list(config.filesystem.allowWrite);
818
+ const denyWrite = list(config.filesystem.denyWrite);
818
819
 
819
820
  return [
820
821
  '# Sandbox Configuration',
@@ -995,6 +996,10 @@ const plugin: Plugin = async ({ client, directory }: PluginInput, options?: Plug
995
996
  const state = activeBash.get(callID);
996
997
  if (!state) return;
997
998
 
999
+ for (const key of callAllowances) {
1000
+ if (key.startsWith(`${callID}:`)) callAllowances.delete(key);
1001
+ }
1002
+
998
1003
  activeBash.delete(callID);
999
1004
  if (state.stop) await state.stop().catch(() => undefined);
1000
1005
  rmSync(state.policyDir, { recursive: true, force: true });
@@ -1007,12 +1012,16 @@ const plugin: Plugin = async ({ client, directory }: PluginInput, options?: Plug
1007
1012
  ): Promise<void> {
1008
1013
  if (typeof args.command !== 'string') return;
1009
1014
 
1015
+ const rewriteDescription = (): void => {
1016
+ if (typeof args.description === 'string')
1017
+ args.description = landstripDescription(args.description);
1018
+ };
1019
+
1010
1020
  const existing = activeBash.get(callID);
1011
1021
  if (existing) {
1012
1022
  if (args.command === existing.originalCommand || args.command === existing.wrappedCommand) {
1013
1023
  args.command = existing.wrappedCommand;
1014
- if (typeof args.description === 'string')
1015
- args.description = landstripDescription(args.description);
1024
+ rewriteDescription();
1016
1025
  return;
1017
1026
  }
1018
1027
 
@@ -1022,8 +1031,7 @@ const plugin: Plugin = async ({ client, directory }: PluginInput, options?: Plug
1022
1031
  if (isGeneratedWrappedCommand(args.command as string)) {
1023
1032
  const policyMatch = (args.command as string).match(/\s'-p'\s+'([^']+)'/);
1024
1033
  if (policyMatch?.[1] && existsSync(policyMatch[1])) {
1025
- if (typeof args.description === 'string')
1026
- args.description = landstripDescription(args.description);
1034
+ rewriteDescription();
1027
1035
  return;
1028
1036
  }
1029
1037
  if (activeBash.has(callID)) await cleanupBash(callID);
@@ -1087,8 +1095,7 @@ const plugin: Plugin = async ({ client, directory }: PluginInput, options?: Plug
1087
1095
  });
1088
1096
 
1089
1097
  args.command = wrappedCommand;
1090
- if (typeof args.description === 'string')
1091
- args.description = landstripDescription(args.description);
1098
+ rewriteDescription();
1092
1099
  }
1093
1100
 
1094
1101
  const hooks: Hooks = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-landstrip",
3
- "version": "0.16.3",
3
+ "version": "0.16.4",
4
4
  "description": "Landlock-based sandboxing for opencode with landstrip",
5
5
  "keywords": [
6
6
  "landlock",
package/shared.ts CHANGED
@@ -66,6 +66,10 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
66
66
  return typeof value === 'object' && value !== null && !Array.isArray(value);
67
67
  }
68
68
 
69
+ export function list(values: string[]): string {
70
+ return values.join(', ') || '(none)';
71
+ }
72
+
69
73
  function stringArray(value: unknown): string[] | undefined {
70
74
  if (!Array.isArray(value)) return undefined;
71
75
  return value.every((item) => typeof item === 'string') ? [...value] : undefined;
package/tui.ts CHANGED
@@ -7,6 +7,7 @@ import { existsSync } from 'node:fs';
7
7
  import { type AddressInfo, createServer, type Socket as NetSocket } from 'node:net';
8
8
 
9
9
  import {
10
+ list,
10
11
  type SandboxConfigOverrides,
11
12
  getConfigPaths,
12
13
  landstripBinaryPath,
@@ -57,10 +58,6 @@ interface PermissionEntry {
57
58
 
58
59
  type QueueEntry = PermissionEntry | FsQueryEntry;
59
60
 
60
- function list(values: string[]): string {
61
- return values.join(', ') || '(none)';
62
- }
63
-
64
61
  function configPathLine(label: string, filePath: string): string {
65
62
  return `${label}: ${filePath} ${existsSync(filePath) ? '(found)' : '(missing)'}`;
66
63
  }
@@ -399,7 +396,7 @@ const tui: TuiPlugin = async (api, options, meta) => {
399
396
 
400
397
  socketServer = createServer((socket) => {
401
398
  sockets.add(socket);
402
- socket.setEncoding('utf8');
399
+ socket.setEncoding('utf-8');
403
400
  const socketId = ++socketSeq;
404
401
  const seen = new Set<number>();
405
402
  let buffer = '';