pi-landstrip 0.3.0 → 0.3.2

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 +226 -164
  2. package/landstrip.d.ts +3 -0
  3. package/package.json +3 -2
package/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // Copyright (C) Jarkko Sakkinen 2026
3
3
 
4
+ /// <reference path="./landstrip.d.ts" />
5
+
4
6
  import type {
5
7
  AgentToolResult,
6
8
  AgentToolUpdateCallback,
@@ -72,12 +74,11 @@ interface LandstripErrorResponse {
72
74
  category: 'policy' | 'tool' | 'platform' | 'system';
73
75
  file?: string;
74
76
  program?: string;
75
- target?: 'filesystem' | 'network' | 'platform';
76
- kind?: 'launch' | 'encoding';
77
+ type?: 'filesystem' | 'network' | 'platform' | 'launch' | 'encoding';
77
78
  message: string;
78
79
  }
79
80
 
80
- const LANDSTRIP_VERSION = [0, 9, 2] as const;
81
+ const LANDSTRIP_VERSION = [0, 9, 5] as const;
81
82
  const SUPPORTED_PLATFORMS = new Set<NodeJS.Platform>(['linux', 'darwin', 'win32']);
82
83
 
83
84
  const DEFAULT_CONFIG: SandboxConfig = {
@@ -353,6 +354,9 @@ function parseLandstripErrors(output: string): LandstripErrorResponse[] {
353
354
  parsed !== null &&
354
355
  typeof parsed.category === 'string' &&
355
356
  ['policy', 'tool', 'platform', 'system'].includes(parsed.category) &&
357
+ (parsed.type === undefined ||
358
+ (typeof parsed.type === 'string' &&
359
+ ['filesystem', 'network', 'platform', 'launch', 'encoding'].includes(parsed.type))) &&
356
360
  typeof parsed.message === 'string' &&
357
361
  parsed.message.length > 0
358
362
  ) {
@@ -371,14 +375,14 @@ function formatLandstripErrors(errors: LandstripErrorResponse[]): string {
371
375
  .map((err) => {
372
376
  const parts: string[] = [`landstrip: ${err.category}`];
373
377
 
374
- if (err.target) {
375
- parts.push(`(${err.target})`);
378
+ if (err.file) {
379
+ parts.push(` (${err.file})`);
376
380
  }
377
381
  if (err.program) {
378
382
  parts.push(` ${err.program}`);
379
383
  }
380
- if (err.kind) {
381
- parts.push(`:${err.kind}`);
384
+ if (err.type) {
385
+ parts.push(`:${err.type}`);
382
386
  }
383
387
  parts.push(`: ${err.message}`);
384
388
 
@@ -591,16 +595,33 @@ function pipeSockets(client: Socket, upstream: Socket, initialData?: Buffer): vo
591
595
  upstream.pipe(client);
592
596
  }
593
597
 
598
+ type LandstripBashTool = ReturnType<typeof createBashToolDefinition>;
599
+
600
+ export interface LandstripIntegrationOptions {
601
+ registerBashTool?: boolean;
602
+ cwd?: string;
603
+ }
604
+
605
+ export interface LandstripIntegration {
606
+ createBashTool(cwd: string, ctx?: ExtensionContext): LandstripBashTool;
607
+ register(pi: ExtensionAPI): void;
608
+ }
609
+
594
610
  export default function (pi: ExtensionAPI) {
595
- pi.registerFlag('no-sandbox', {
596
- description: 'Disable landstrip sandboxing for bash commands',
597
- type: 'boolean',
598
- default: false,
599
- });
611
+ createLandstripIntegration().register(pi);
612
+ }
613
+
614
+ export function createLandstripIntegration(
615
+ options: LandstripIntegrationOptions = {},
616
+ ): LandstripIntegration {
617
+ const shouldRegisterBashTool = options.registerBashTool ?? true;
618
+ const localCwd = options.cwd ?? process.cwd();
600
619
 
601
- const localCwd = process.cwd();
602
- const userShellPath = SettingsManager.create(localCwd).getShellPath();
603
- const localBash = createBashToolDefinition(localCwd, { shellPath: userShellPath });
620
+ function createPlainBashTool(cwd: string): LandstripBashTool {
621
+ return createBashToolDefinition(cwd, {
622
+ shellPath: SettingsManager.create(cwd).getShellPath(),
623
+ });
624
+ }
604
625
 
605
626
  let sandboxEnabled = false;
606
627
  let sandboxReady = false;
@@ -828,12 +849,15 @@ export default function (pi: ExtensionAPI) {
828
849
  });
829
850
  }
830
851
 
831
- function createLandstripBashOps(ctx: ExtensionContext): BashOperations {
852
+ function createLandstripBashOps(
853
+ ctx: ExtensionContext,
854
+ onStderr: (data: Buffer) => void = () => {},
855
+ ): BashOperations {
832
856
  return {
833
857
  async exec(command, cwd, { onData, signal, timeout, env }) {
834
858
  if (!existsSync(cwd)) throw new Error(`Working directory does not exist: ${cwd}`);
835
859
 
836
- const { shell, args } = getShellConfig(userShellPath);
860
+ const { shell, args } = getShellConfig(SettingsManager.create(cwd).getShellPath());
837
861
  const proxy = await startProxy(ctx, cwd);
838
862
  const policy = writePolicyFile(cwd, proxy.port);
839
863
  const landstripArgs = ['-p', policy.path, shell, ...args, command];
@@ -881,7 +905,10 @@ export default function (pi: ExtensionAPI) {
881
905
 
882
906
  signal?.addEventListener('abort', onAbort, { once: true });
883
907
  child.stdout?.on('data', onData);
884
- child.stderr?.on('data', onData);
908
+ child.stderr?.on('data', (data: Buffer) => {
909
+ onStderr(data);
910
+ onData(data);
911
+ });
885
912
 
886
913
  child.on('error', (error) => {
887
914
  cleanup();
@@ -910,18 +937,30 @@ export default function (pi: ExtensionAPI) {
910
937
  onUpdate: AgentToolUpdateCallback<BashToolDetails | undefined> | undefined,
911
938
  ctx: ExtensionContext,
912
939
  ): Promise<AgentToolResult<BashToolDetails | undefined>> {
913
- const sandboxedBash = createBashToolDefinition(localCwd, {
914
- operations: createLandstripBashOps(ctx),
915
- shellPath: userShellPath,
940
+ let landstripStderr = '';
941
+ const sandboxedBash = createBashToolDefinition(ctx.cwd, {
942
+ operations: createLandstripBashOps(ctx, (data) => {
943
+ landstripStderr += data.toString('utf8');
944
+ }),
945
+ shellPath: SettingsManager.create(ctx.cwd).getShellPath(),
916
946
  });
917
947
 
918
948
  const run = () => sandboxedBash.execute(id, params, signal, onUpdate, ctx);
919
- const result = await run();
949
+ let result: AgentToolResult<BashToolDetails | undefined>;
950
+ try {
951
+ result = await run();
952
+ } catch (error) {
953
+ const landstripErrors = parseLandstripErrors(landstripStderr);
954
+ if (landstripErrors.length > 0) {
955
+ throw new Error(formatLandstripErrors(landstripErrors));
956
+ }
957
+ throw error;
958
+ }
920
959
  const outputText = result.content
921
960
  .filter((content) => content.type === 'text')
922
961
  .map((content) => content.text)
923
962
  .join('\n');
924
- const landstripErrors = parseLandstripErrors(outputText);
963
+ const landstripErrors = parseLandstripErrors(landstripStderr);
925
964
  if (landstripErrors.length > 0) {
926
965
  const message = formatLandstripErrors(landstripErrors);
927
966
  result.content.unshift({ type: 'text', text: `\n${message}\n` });
@@ -1010,7 +1049,7 @@ export default function (pi: ExtensionAPI) {
1010
1049
  if (!hasMinimumVersion(version, LANDSTRIP_VERSION)) {
1011
1050
  sandboxEnabled = false;
1012
1051
  sandboxReady = false;
1013
- ctx.ui.notify(`landstrip 0.9.2 or newer is required; found: ${version}`, 'error');
1052
+ ctx.ui.notify(`landstrip 0.9.5 or newer is required; found: ${version}`, 'error');
1014
1053
  return false;
1015
1054
  }
1016
1055
 
@@ -1021,178 +1060,201 @@ export default function (pi: ExtensionAPI) {
1021
1060
  return true;
1022
1061
  }
1023
1062
 
1024
- pi.registerTool({
1025
- ...localBash,
1026
- label: 'bash (landstrip)',
1027
- async execute(id, params, signal, onUpdate, ctx) {
1028
- if (!sandboxEnabled || !sandboxReady)
1029
- return localBash.execute(id, params, signal, onUpdate, ctx);
1030
-
1031
- return runBashWithOptionalRetry(id, params, signal, onUpdate, ctx);
1032
- },
1033
- });
1063
+ function createBashTool(cwd: string, ctx?: ExtensionContext): LandstripBashTool {
1064
+ const localBash = createPlainBashTool(cwd);
1034
1065
 
1035
- pi.on('user_bash', async (event, ctx) => {
1036
- if (!sandboxEnabled || !sandboxReady) return;
1037
- if (!loadConfig(ctx.cwd).enabled) return;
1038
-
1039
- const blockedDomain = await preflightCommandDomains(event.command, ctx);
1040
- if (blockedDomain) {
1041
- return {
1042
- result: {
1043
- output: `Blocked: "${blockedDomain}" is not allowed by the sandbox. Use /sandbox to review your config.`,
1044
- exitCode: 1,
1045
- cancelled: false,
1046
- truncated: false,
1047
- },
1048
- };
1049
- }
1066
+ return {
1067
+ ...localBash,
1068
+ label: 'bash (landstrip)',
1069
+ async execute(id, params, signal, onUpdate, callCtx) {
1070
+ const effectiveCtx = callCtx ?? ctx;
1071
+ if (!sandboxEnabled || !sandboxReady || !effectiveCtx)
1072
+ return localBash.execute(id, params, signal, onUpdate, effectiveCtx);
1073
+
1074
+ return runBashWithOptionalRetry(id, params, signal, onUpdate, effectiveCtx);
1075
+ },
1076
+ };
1077
+ }
1050
1078
 
1051
- return { operations: createLandstripBashOps(ctx) };
1052
- });
1079
+ function register(pi: ExtensionAPI): void {
1080
+ const maybePi = pi as ExtensionAPI & {
1081
+ getFlag?: (name: string) => unknown;
1082
+ registerCommand?: ExtensionAPI['registerCommand'];
1083
+ registerFlag?: ExtensionAPI['registerFlag'];
1084
+ };
1053
1085
 
1054
- pi.on('tool_call', async (event, ctx) => {
1055
- if (!sandboxEnabled) return;
1086
+ maybePi.registerFlag?.('no-sandbox', {
1087
+ description: 'Disable landstrip sandboxing for bash commands',
1088
+ type: 'boolean',
1089
+ default: false,
1090
+ });
1056
1091
 
1057
- const config = loadConfig(ctx.cwd);
1058
- if (!config.enabled) return;
1092
+ if (shouldRegisterBashTool) pi.registerTool(createBashTool(localCwd));
1059
1093
 
1060
- const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1094
+ pi.on('user_bash', async (event, ctx) => {
1095
+ if (!sandboxEnabled || !sandboxReady) return;
1096
+ if (!loadConfig(ctx.cwd).enabled) return;
1061
1097
 
1062
- if (sandboxReady && isToolCallEventType('bash', event)) {
1063
- const blockedDomain = await preflightCommandDomains(event.input.command, ctx);
1098
+ const blockedDomain = await preflightCommandDomains(event.command, ctx);
1064
1099
  if (blockedDomain) {
1065
1100
  return {
1066
- block: true,
1067
- reason: `Network access to "${blockedDomain}" is blocked by the sandbox.`,
1101
+ result: {
1102
+ output: `Blocked: "${blockedDomain}" is not allowed by the sandbox. Use /sandbox to review your config.`,
1103
+ exitCode: 1,
1104
+ cancelled: false,
1105
+ truncated: false,
1106
+ },
1068
1107
  };
1069
1108
  }
1070
- }
1071
1109
 
1072
- if (isToolCallEventType('read', event)) {
1073
- const filePath = canonicalizePath(event.input.path);
1074
- if (!matchesPattern(filePath, getEffectiveAllowRead(ctx.cwd))) {
1075
- const choice = await promptReadBlock(ctx, filePath);
1076
- if (choice === 'abort') {
1110
+ return { operations: createLandstripBashOps(ctx) };
1111
+ });
1112
+
1113
+ pi.on('tool_call', async (event, ctx) => {
1114
+ if (!sandboxEnabled) return;
1115
+
1116
+ const config = loadConfig(ctx.cwd);
1117
+ if (!config.enabled) return;
1118
+
1119
+ const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1120
+
1121
+ if (sandboxReady && isToolCallEventType('bash', event)) {
1122
+ const blockedDomain = await preflightCommandDomains(event.input.command, ctx);
1123
+ if (blockedDomain) {
1077
1124
  return {
1078
1125
  block: true,
1079
- reason: `Sandbox: read access denied for "${filePath}"`,
1126
+ reason: `Network access to "${blockedDomain}" is blocked by the sandbox.`,
1080
1127
  };
1081
1128
  }
1082
- await applyReadChoice(choice, filePath, ctx.cwd);
1083
1129
  }
1084
- }
1085
1130
 
1086
- if (isToolCallEventType('write', event) || isToolCallEventType('edit', event)) {
1087
- const filePath = canonicalizePath((event.input as { path: string }).path);
1088
-
1089
- if (matchesPattern(filePath, config.filesystem.denyWrite)) {
1090
- return {
1091
- block: true,
1092
- reason:
1093
- `Sandbox: write access denied for "${filePath}" (in denyWrite). ` +
1094
- `To change this, edit denyWrite in:\n ${projectPath}\n ${globalPath}`,
1095
- };
1131
+ if (isToolCallEventType('read', event)) {
1132
+ const filePath = canonicalizePath(event.input.path);
1133
+ if (!matchesPattern(filePath, getEffectiveAllowRead(ctx.cwd))) {
1134
+ const choice = await promptReadBlock(ctx, filePath);
1135
+ if (choice === 'abort') {
1136
+ return {
1137
+ block: true,
1138
+ reason: `Sandbox: read access denied for "${filePath}"`,
1139
+ };
1140
+ }
1141
+ await applyReadChoice(choice, filePath, ctx.cwd);
1142
+ }
1096
1143
  }
1097
1144
 
1098
- if (shouldPromptForWrite(filePath, getEffectiveAllowWrite(ctx.cwd), matchesPattern)) {
1099
- const choice = await promptWriteBlock(ctx, filePath);
1100
- if (choice === 'abort') {
1145
+ if (isToolCallEventType('write', event) || isToolCallEventType('edit', event)) {
1146
+ const filePath = canonicalizePath((event.input as { path: string }).path);
1147
+
1148
+ if (matchesPattern(filePath, config.filesystem.denyWrite)) {
1101
1149
  return {
1102
1150
  block: true,
1103
- reason: `Sandbox: write access denied for "${filePath}" (not in allowWrite)`,
1151
+ reason:
1152
+ `Sandbox: write access denied for "${filePath}" (in denyWrite). ` +
1153
+ `To change this, edit denyWrite in:\n ${projectPath}\n ${globalPath}`,
1104
1154
  };
1105
1155
  }
1106
- await applyWriteChoice(choice, filePath, ctx.cwd);
1107
- }
1108
- }
1109
- });
1110
1156
 
1111
- pi.on('session_start', async (_event, ctx) => {
1112
- const noSandbox = pi.getFlag('no-sandbox') as boolean;
1113
-
1114
- if (noSandbox) {
1115
- sandboxEnabled = false;
1116
- sandboxReady = false;
1117
- ctx.ui.notify('Sandbox disabled via --no-sandbox', 'warning');
1118
- return;
1119
- }
1120
-
1121
- const config = loadConfig(ctx.cwd);
1122
- if (!config.enabled) {
1123
- sandboxEnabled = false;
1124
- sandboxReady = false;
1125
- ctx.ui.notify('Sandbox disabled via config', 'info');
1126
- return;
1127
- }
1157
+ if (shouldPromptForWrite(filePath, getEffectiveAllowWrite(ctx.cwd), matchesPattern)) {
1158
+ const choice = await promptWriteBlock(ctx, filePath);
1159
+ if (choice === 'abort') {
1160
+ return {
1161
+ block: true,
1162
+ reason: `Sandbox: write access denied for "${filePath}" (not in allowWrite)`,
1163
+ };
1164
+ }
1165
+ await applyWriteChoice(choice, filePath, ctx.cwd);
1166
+ }
1167
+ }
1168
+ });
1128
1169
 
1129
- enableSandbox(ctx);
1130
- });
1170
+ pi.on('session_start', async (_event, ctx) => {
1171
+ const noSandbox = maybePi.getFlag?.('no-sandbox') as boolean;
1131
1172
 
1132
- pi.registerCommand('sandbox-enable', {
1133
- description: 'Enable the landstrip sandbox for this session',
1134
- handler: async (_args, ctx) => {
1135
- if (sandboxEnabled) {
1136
- ctx.ui.notify('Sandbox is already enabled', 'info');
1173
+ if (noSandbox) {
1174
+ sandboxEnabled = false;
1175
+ sandboxReady = false;
1176
+ ctx.ui.notify('Sandbox disabled via --no-sandbox', 'warning');
1137
1177
  return;
1138
1178
  }
1139
1179
 
1140
- if (enableSandbox(ctx)) ctx.ui.notify('Sandbox enabled', 'info');
1141
- },
1142
- });
1143
-
1144
- pi.registerCommand('sandbox-disable', {
1145
- description: 'Disable the landstrip sandbox for this session',
1146
- handler: async (_args, ctx) => {
1147
- if (!sandboxEnabled) {
1148
- ctx.ui.notify('Sandbox is already disabled', 'info');
1180
+ const config = loadConfig(ctx.cwd);
1181
+ if (!config.enabled) {
1182
+ sandboxEnabled = false;
1183
+ sandboxReady = false;
1184
+ ctx.ui.notify('Sandbox disabled via config', 'info');
1149
1185
  return;
1150
1186
  }
1151
1187
 
1152
- sandboxEnabled = false;
1153
- sandboxReady = false;
1154
- ctx.ui.setStatus('sandbox', '');
1155
- ctx.ui.notify('Sandbox disabled', 'info');
1156
- },
1157
- });
1188
+ enableSandbox(ctx);
1189
+ });
1158
1190
 
1159
- pi.registerCommand('sandbox', {
1160
- description: 'Show sandbox configuration',
1161
- handler: async (_args, ctx) => {
1162
- if (!sandboxEnabled) {
1163
- ctx.ui.notify('Sandbox is disabled', 'info');
1164
- return;
1165
- }
1191
+ maybePi.registerCommand?.('sandbox-enable', {
1192
+ description: 'Enable the landstrip sandbox for this session',
1193
+ handler: async (_args, ctx) => {
1194
+ if (sandboxEnabled) {
1195
+ ctx.ui.notify('Sandbox is already enabled', 'info');
1196
+ return;
1197
+ }
1166
1198
 
1167
- const config = loadConfig(ctx.cwd);
1168
- const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1169
- const lines = [
1170
- 'Sandbox Configuration',
1171
- ` Project config: ${projectPath}`,
1172
- ` Global config: ${globalPath}`,
1173
- ` landstrip: ${binaryPath()}`,
1174
- '',
1175
- 'Network (bash through HTTP proxy):',
1176
- ` Allowed domains: ${config.network.allowedDomains.join(', ') || '(none)'}`,
1177
- ` Denied domains: ${config.network.deniedDomains.join(', ') || '(none)'}`,
1178
- ...(sessionAllowedDomains.length > 0
1179
- ? [` Session allowed: ${sessionAllowedDomains.join(', ')}`]
1180
- : []),
1181
- '',
1182
- 'Filesystem (bash + read/write/edit tools):',
1183
- ` Deny Read: ${config.filesystem.denyRead.join(', ') || '(none)'}`,
1184
- ` Allow Read: ${config.filesystem.allowRead.join(', ') || '(none)'}`,
1185
- ` Allow Write: ${config.filesystem.allowWrite.join(', ') || '(none)'}`,
1186
- ` Deny Write: ${config.filesystem.denyWrite.join(', ') || '(none)'}`,
1187
- ...(sessionAllowedReadPaths.length > 0
1188
- ? [` Session read: ${sessionAllowedReadPaths.join(', ')}`]
1189
- : []),
1190
- ...(sessionAllowedWritePaths.length > 0
1191
- ? [` Session write: ${sessionAllowedWritePaths.join(', ')}`]
1192
- : []),
1193
- ];
1194
-
1195
- ctx.ui.notify(lines.join('\n'), 'info');
1196
- },
1197
- });
1199
+ if (enableSandbox(ctx)) ctx.ui.notify('Sandbox enabled', 'info');
1200
+ },
1201
+ });
1202
+
1203
+ maybePi.registerCommand?.('sandbox-disable', {
1204
+ description: 'Disable the landstrip sandbox for this session',
1205
+ handler: async (_args, ctx) => {
1206
+ if (!sandboxEnabled) {
1207
+ ctx.ui.notify('Sandbox is already disabled', 'info');
1208
+ return;
1209
+ }
1210
+
1211
+ sandboxEnabled = false;
1212
+ sandboxReady = false;
1213
+ ctx.ui.setStatus('sandbox', '');
1214
+ ctx.ui.notify('Sandbox disabled', 'info');
1215
+ },
1216
+ });
1217
+
1218
+ maybePi.registerCommand?.('sandbox', {
1219
+ description: 'Show sandbox configuration',
1220
+ handler: async (_args, ctx) => {
1221
+ if (!sandboxEnabled) {
1222
+ ctx.ui.notify('Sandbox is disabled', 'info');
1223
+ return;
1224
+ }
1225
+
1226
+ const config = loadConfig(ctx.cwd);
1227
+ const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1228
+ const lines = [
1229
+ 'Sandbox Configuration',
1230
+ ` Project config: ${projectPath}`,
1231
+ ` Global config: ${globalPath}`,
1232
+ ` landstrip: ${binaryPath()}`,
1233
+ '',
1234
+ 'Network (bash through HTTP proxy):',
1235
+ ` Allowed domains: ${config.network.allowedDomains.join(', ') || '(none)'}`,
1236
+ ` Denied domains: ${config.network.deniedDomains.join(', ') || '(none)'}`,
1237
+ ...(sessionAllowedDomains.length > 0
1238
+ ? [` Session allowed: ${sessionAllowedDomains.join(', ')}`]
1239
+ : []),
1240
+ '',
1241
+ 'Filesystem (bash + read/write/edit tools):',
1242
+ ` Deny Read: ${config.filesystem.denyRead.join(', ') || '(none)'}`,
1243
+ ` Allow Read: ${config.filesystem.allowRead.join(', ') || '(none)'}`,
1244
+ ` Allow Write: ${config.filesystem.allowWrite.join(', ') || '(none)'}`,
1245
+ ` Deny Write: ${config.filesystem.denyWrite.join(', ') || '(none)'}`,
1246
+ ...(sessionAllowedReadPaths.length > 0
1247
+ ? [` Session read: ${sessionAllowedReadPaths.join(', ')}`]
1248
+ : []),
1249
+ ...(sessionAllowedWritePaths.length > 0
1250
+ ? [` Session write: ${sessionAllowedWritePaths.join(', ')}`]
1251
+ : []),
1252
+ ];
1253
+
1254
+ ctx.ui.notify(lines.join('\n'), 'info');
1255
+ },
1256
+ });
1257
+ }
1258
+
1259
+ return { createBashTool, register };
1198
1260
  }
package/landstrip.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ declare module '@jarkkojs/landstrip' {
2
+ function binaryPath(): string;
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Landlock-based sandboxing for pi with interactive permission prompts",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "index.ts",
18
+ "landstrip.d.ts",
18
19
  "README.md",
19
20
  "sandbox.json"
20
21
  ],
@@ -30,7 +31,7 @@
30
31
  },
31
32
  "dependencies": {
32
33
  "@earendil-works/pi-tui": "^0.78.0",
33
- "@jarkkojs/landstrip": "^0.9.2"
34
+ "@jarkkojs/landstrip": "^0.9.5"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@earendil-works/pi-coding-agent": "^0.78.0",