opencode-landstrip 0.2.2 → 0.3.1

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 (3) hide show
  1. package/index.ts +56 -8
  2. package/package.json +13 -5
  3. package/tui.ts +20 -0
package/index.ts CHANGED
@@ -54,8 +54,7 @@ interface LandstripErrorResponse {
54
54
  category: 'policy' | 'tool' | 'platform' | 'system';
55
55
  file?: string;
56
56
  program?: string;
57
- target?: 'filesystem' | 'network' | 'platform';
58
- kind?: 'launch' | 'encoding';
57
+ type?: 'filesystem' | 'network' | 'platform' | 'launch' | 'encoding';
59
58
  message: string;
60
59
  }
61
60
 
@@ -75,7 +74,7 @@ interface BashSandboxState {
75
74
 
76
75
  type ToastVariant = 'info' | 'success' | 'warning' | 'error';
77
76
 
78
- const LANDSTRIP_VERSION = [0, 9, 7] as const;
77
+ const LANDSTRIP_VERSION = [0, 10, 1] as const;
79
78
  const SUPPORTED_PLATFORMS = new Set<NodeJS.Platform>(['linux', 'darwin', 'win32']);
80
79
 
81
80
  const DEFAULT_CONFIG: SandboxConfig = {
@@ -384,6 +383,9 @@ function parseLandstripErrors(output: string): LandstripErrorResponse[] {
384
383
  parsed !== null &&
385
384
  typeof parsed.category === 'string' &&
386
385
  ['policy', 'tool', 'platform', 'system'].includes(parsed.category) &&
386
+ (parsed.type === undefined ||
387
+ (typeof parsed.type === 'string' &&
388
+ ['filesystem', 'network', 'platform', 'launch', 'encoding'].includes(parsed.type))) &&
387
389
  typeof parsed.message === 'string' &&
388
390
  parsed.message.length > 0
389
391
  ) {
@@ -402,14 +404,14 @@ function formatLandstripErrors(errors: LandstripErrorResponse[]): string {
402
404
  .map((err) => {
403
405
  const parts: string[] = [`landstrip: ${err.category}`];
404
406
 
405
- if (err.target) {
406
- parts.push(`(${err.target})`);
407
+ if (err.file) {
408
+ parts.push(` (${err.file})`);
407
409
  }
408
410
  if (err.program) {
409
411
  parts.push(` ${err.program}`);
410
412
  }
411
- if (err.kind) {
412
- parts.push(`:${err.kind}`);
413
+ if (err.type) {
414
+ parts.push(`:${err.type}`);
413
415
  }
414
416
  parts.push(`: ${err.message}`);
415
417
 
@@ -777,7 +779,7 @@ export default (async ({ client, directory }: PluginInput, options?: PluginOptio
777
779
  if (!hasMinimumVersion(version, LANDSTRIP_VERSION)) {
778
780
  landstripCheck = {
779
781
  ok: false,
780
- reason: `landstrip 0.9.7 or newer is required; found: ${version}`,
782
+ reason: `landstrip 0.10.1 or newer is required; found: ${version}`,
781
783
  };
782
784
  return landstripCheck;
783
785
  }
@@ -898,6 +900,38 @@ export default (async ({ client, directory }: PluginInput, options?: PluginOptio
898
900
  args.description = landstripDescription(args.description);
899
901
  }
900
902
 
903
+ function buildConfigSummary(config: SandboxConfig): string {
904
+ const { globalPath, projectPath } = getConfigPaths(directory);
905
+ const check = checkLandstrip();
906
+ const version = check?.ok === true ? check.version : 'unknown';
907
+ const status = config.enabled && check?.ok === true ? 'enabled' : 'disabled';
908
+
909
+ const lines = [
910
+ 'Sandbox Configuration',
911
+ ` Status: ${status}`,
912
+ ` Project config: ${projectPath}`,
913
+ ` Global config: ${globalPath}`,
914
+ ` landstrip: ${binaryPath()} (v${version})`,
915
+ '',
916
+ 'Network (bash commands go through HTTP proxy):',
917
+ ` Allow local binding: ${config.network.allowLocalBinding}`,
918
+ ` Allow all Unix sockets: ${config.network.allowAllUnixSockets}`,
919
+ ...(config.network.allowUnixSockets.length > 0
920
+ ? [` Allow Unix sockets: ${config.network.allowUnixSockets.join(', ')}`]
921
+ : []),
922
+ ` Allowed domains: ${config.network.allowedDomains.join(', ') || '(none)'}`,
923
+ ` Denied domains: ${config.network.deniedDomains.join(', ') || '(none)'}`,
924
+ '',
925
+ 'Filesystem (bash + read/write/edit tools):',
926
+ ` Deny Read: ${config.filesystem.denyRead.join(', ') || '(none)'}`,
927
+ ` Allow Read: ${config.filesystem.allowRead.join(', ') || '(none)'}`,
928
+ ` Allow Write: ${config.filesystem.allowWrite.join(', ') || '(none)'}`,
929
+ ` Deny Write: ${config.filesystem.denyWrite.join(', ') || '(none)'}`,
930
+ ];
931
+
932
+ return lines.join('\n');
933
+ }
934
+
901
935
  const hooks: Hooks = {
902
936
  config: async (config) => {
903
937
  configuredShell = configuredShellPath(config);
@@ -972,6 +1006,20 @@ export default (async ({ client, directory }: PluginInput, options?: PluginOptio
972
1006
  await cleanupBash(input.callID);
973
1007
  },
974
1008
 
1009
+ 'command.execute.before': async (input, output) => {
1010
+ if (input.command !== 'sandbox') return;
1011
+
1012
+ const config = loadConfig(directory, optionOverrides);
1013
+ const summary = buildConfigSummary(config);
1014
+ output.parts.unshift({
1015
+ id: `landstrip-sandbox-${Date.now()}`,
1016
+ sessionID: input.sessionID,
1017
+ messageID: input.sessionID,
1018
+ type: 'text',
1019
+ text: `\n${summary}\n`,
1020
+ });
1021
+ },
1022
+
975
1023
  dispose: async () => {
976
1024
  await Promise.all([...activeBash.keys()].map((callID) => cleanupBash(callID)));
977
1025
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-landstrip",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Landlock-based sandboxing for opencode with landstrip",
5
5
  "keywords": [
6
6
  "landlock",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "index.ts",
18
+ "tui.ts",
18
19
  "README.md",
19
20
  "sandbox.json"
20
21
  ],
@@ -28,24 +29,31 @@
28
29
  "./server": {
29
30
  "import": "./index.ts",
30
31
  "types": "./index.ts"
32
+ },
33
+ "./tui": {
34
+ "import": "./tui.ts",
35
+ "types": "./tui.ts"
31
36
  }
32
37
  },
33
38
  "scripts": {
34
- "fmt": "oxfmt index.ts package.json tsconfig.json sandbox.json .oxfmtrc.json README.md test/*.test.mjs",
35
- "lint": "oxlint index.ts",
39
+ "fmt": "oxfmt index.ts tui.ts package.json tsconfig.json sandbox.json .oxfmtrc.json README.md test/*.test.mjs",
40
+ "lint": "oxlint index.ts tui.ts",
36
41
  "check": "tsc --noEmit",
37
42
  "test": "node --test test/*.test.mjs",
38
43
  "all": "npm run fmt && npm run lint && npm run check && npm test",
39
- "ci:fmt": "oxfmt --check index.ts package.json tsconfig.json sandbox.json .oxfmtrc.json README.md test/*.test.mjs",
44
+ "ci:fmt": "oxfmt --check index.ts tui.ts package.json tsconfig.json sandbox.json .oxfmtrc.json README.md test/*.test.mjs",
40
45
  "ci:lint": "npm run lint",
41
46
  "ci:check": "npm run check",
42
47
  "ci:test": "npm test"
43
48
  },
44
49
  "dependencies": {
45
- "@jarkkojs/landstrip": "^0.9.7"
50
+ "@jarkkojs/landstrip": "^0.10.1"
46
51
  },
47
52
  "devDependencies": {
48
53
  "@opencode-ai/plugin": "^1.16.2",
54
+ "@opentui/core": ">=0.3.2",
55
+ "@opentui/keymap": ">=0.3.2",
56
+ "@opentui/solid": ">=0.3.2",
49
57
  "@types/node": "^24.0.0",
50
58
  "oxfmt": "^0.53.0",
51
59
  "oxlint": "^1.68.0",
package/tui.ts ADDED
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (C) Jarkko Sakkinen 2026
3
+
4
+ import type { TuiPlugin } from '@opencode-ai/plugin/tui';
5
+
6
+ const tui: TuiPlugin = async (api) => {
7
+ api.command?.register(() => [
8
+ {
9
+ title: 'Sandbox',
10
+ value: 'sandbox',
11
+ description: 'Show sandbox configuration',
12
+ category: 'plugin',
13
+ slash: {
14
+ name: 'sandbox',
15
+ },
16
+ },
17
+ ]);
18
+ };
19
+
20
+ export default tui;