opencode-landstrip 0.16.3 → 0.16.5
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/index.ts +19 -12
- package/package.json +1 -1
- package/shared.ts +4 -0
- package/tui.ts +33 -6
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
|
|
813
|
-
const denied = config.network.deniedDomains
|
|
814
|
-
const denyRead = config.filesystem.denyRead
|
|
815
|
-
const allowRead = config.filesystem.allowRead
|
|
816
|
-
const allowWrite = config.filesystem.allowWrite
|
|
817
|
-
const denyWrite = config.filesystem.denyWrite
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1091
|
-
args.description = landstripDescription(args.description);
|
|
1098
|
+
rewriteDescription();
|
|
1092
1099
|
}
|
|
1093
1100
|
|
|
1094
1101
|
const hooks: Hooks = {
|
package/package.json
CHANGED
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('
|
|
399
|
+
socket.setEncoding('utf-8');
|
|
403
400
|
const socketId = ++socketSeq;
|
|
404
401
|
const seen = new Set<number>();
|
|
405
402
|
let buffer = '';
|
|
@@ -492,7 +489,7 @@ const tui: TuiPlugin = async (api, options, meta) => {
|
|
|
492
489
|
};
|
|
493
490
|
|
|
494
491
|
const executeServerCommand = async (command: string): Promise<boolean> => {
|
|
495
|
-
await api.client.tui.executeCommand({ command });
|
|
492
|
+
await api.client.tui.executeCommand({ command: `/${command}` });
|
|
496
493
|
return true;
|
|
497
494
|
};
|
|
498
495
|
|
|
@@ -531,6 +528,36 @@ const tui: TuiPlugin = async (api, options, meta) => {
|
|
|
531
528
|
],
|
|
532
529
|
});
|
|
533
530
|
|
|
531
|
+
api.command?.register(() => [
|
|
532
|
+
{
|
|
533
|
+
title: 'Sandbox',
|
|
534
|
+
value: 'sandbox',
|
|
535
|
+
description: 'Show sandbox configuration',
|
|
536
|
+
category: 'Sandbox',
|
|
537
|
+
suggested: true,
|
|
538
|
+
slash: { name: 'sandbox' },
|
|
539
|
+
onSelect: showSandbox,
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
title: 'Disable sandbox',
|
|
543
|
+
value: 'sandbox-disable',
|
|
544
|
+
description: 'Disable sandbox for this session',
|
|
545
|
+
category: 'Sandbox',
|
|
546
|
+
suggested: true,
|
|
547
|
+
slash: { name: 'sandbox-disable' },
|
|
548
|
+
onSelect: () => executeServerCommand('sandbox-disable'),
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
title: 'Enable sandbox',
|
|
552
|
+
value: 'sandbox-enable',
|
|
553
|
+
description: 'Re-enable sandbox for this session',
|
|
554
|
+
category: 'Sandbox',
|
|
555
|
+
suggested: true,
|
|
556
|
+
slash: { name: 'sandbox-enable' },
|
|
557
|
+
onSelect: () => executeServerCommand('sandbox-enable'),
|
|
558
|
+
},
|
|
559
|
+
]);
|
|
560
|
+
|
|
534
561
|
// Persistent status badge in the prompt area. It needs the host's Solid
|
|
535
562
|
// runtime, imported defensively so a host that resolves plugin imports
|
|
536
563
|
// differently still loads the plugin — the badge just stays absent there.
|