pi-landstrip 0.11.7 → 0.11.8

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/README.md +12 -4
  2. package/index.ts +55 -39
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,12 +23,20 @@ Project config takes precedence.
23
23
 
24
24
  See [`sandbox.json`](./sandbox.json) for a starter config.
25
25
 
26
- ## Usage
27
-
28
- ```bash
29
- pi --no-sandbox
26
+ Use Pi settings to toggle sandboxing:
27
+
28
+ ```json
29
+ {
30
+ "landstrip": {
31
+ "enabled": false
32
+ }
33
+ }
30
34
  ```
31
35
 
36
+ Project Pi settings override global Pi settings.
37
+
38
+ ## Usage
39
+
32
40
  Use `/sandbox` inside Pi to show the active config.
33
41
 
34
42
  ## License
package/index.ts CHANGED
@@ -62,6 +62,12 @@ interface SandboxConfig {
62
62
  filesystem: SandboxFilesystemConfig;
63
63
  }
64
64
 
65
+ interface PiLandstripSettings {
66
+ landstrip?: {
67
+ enabled?: boolean;
68
+ };
69
+ }
70
+
65
71
  interface LandstripPolicy {
66
72
  network: {
67
73
  allowNetwork: boolean;
@@ -184,6 +190,14 @@ function loadConfig(cwd: string): SandboxConfig {
184
190
  return deepMerge(deepMerge(DEFAULT_CONFIG, globalConfig), projectConfig);
185
191
  }
186
192
 
193
+ function isSandboxEnabledInPiSettings(cwd: string): boolean {
194
+ const settings = SettingsManager.create(cwd);
195
+ const globalSettings = settings.getGlobalSettings() as PiLandstripSettings;
196
+ const projectSettings = settings.getProjectSettings() as PiLandstripSettings;
197
+
198
+ return projectSettings.landstrip?.enabled ?? globalSettings.landstrip?.enabled ?? true;
199
+ }
200
+
187
201
  function mergeArray(base: string[], override?: string[]): string[] {
188
202
  if (!override) return base;
189
203
  return [...new Set([...base, ...override])];
@@ -1430,6 +1444,34 @@ export function createLandstripIntegration(
1430
1444
  return true;
1431
1445
  }
1432
1446
 
1447
+ let noSandboxFlag = false;
1448
+ function disableSandbox(ctx: ExtensionContext): void {
1449
+ sandboxEnabled = false;
1450
+ sandboxReady = false;
1451
+ setTuiStatus(ctx, 'sandbox', undefined);
1452
+ }
1453
+
1454
+ function ensureSandboxState(ctx: ExtensionContext): boolean {
1455
+ if (noSandboxFlag) {
1456
+ disableSandbox(ctx);
1457
+ return false;
1458
+ }
1459
+
1460
+ if (!isSandboxEnabledInPiSettings(ctx.cwd)) {
1461
+ disableSandbox(ctx);
1462
+ return false;
1463
+ }
1464
+
1465
+ const config = loadConfig(ctx.cwd);
1466
+ if (!config.enabled) {
1467
+ disableSandbox(ctx);
1468
+ return false;
1469
+ }
1470
+
1471
+ if (!sandboxEnabled || !sandboxReady) return enableSandbox(ctx);
1472
+ return true;
1473
+ }
1474
+
1433
1475
  function createBashTool(cwd: string, ctx?: ExtensionContext): LandstripBashTool {
1434
1476
  const localBash = createPlainBashTool(cwd);
1435
1477
 
@@ -1438,7 +1480,7 @@ export function createLandstripIntegration(
1438
1480
  label: 'bash (landstrip)',
1439
1481
  async execute(id, params, signal, onUpdate, callCtx) {
1440
1482
  const effectiveCtx = callCtx ?? ctx;
1441
- if (!sandboxEnabled || !sandboxReady || !effectiveCtx)
1483
+ if (!effectiveCtx || !ensureSandboxState(effectiveCtx))
1442
1484
  return localBash.execute(id, params, signal, onUpdate, effectiveCtx);
1443
1485
 
1444
1486
  return runBashWithOptionalRetry(id, params, signal, onUpdate, effectiveCtx);
@@ -1462,9 +1504,8 @@ export function createLandstripIntegration(
1462
1504
  if (shouldRegisterBashTool) pi.registerTool(createBashTool(localCwd));
1463
1505
 
1464
1506
  pi.on('user_bash', async (event, ctx) => {
1465
- if (!sandboxEnabled || !sandboxReady) return;
1507
+ if (!ensureSandboxState(ctx)) return;
1466
1508
  const config = loadConfig(ctx.cwd);
1467
- if (!config.enabled) return;
1468
1509
 
1469
1510
  if (!config.network.allowNetwork) {
1470
1511
  const blockedDomain = await preflightCommandDomains(event.command, ctx);
@@ -1484,10 +1525,9 @@ export function createLandstripIntegration(
1484
1525
  });
1485
1526
 
1486
1527
  pi.on('tool_call', async (event, ctx) => {
1487
- if (!sandboxEnabled) return;
1528
+ if (!ensureSandboxState(ctx)) return;
1488
1529
 
1489
1530
  const config = loadConfig(ctx.cwd);
1490
- if (!config.enabled) return;
1491
1531
 
1492
1532
  const { globalPath, projectPath } = getConfigPaths(ctx.cwd);
1493
1533
 
@@ -1544,53 +1584,29 @@ export function createLandstripIntegration(
1544
1584
 
1545
1585
  pi.on('session_start', async (_event, ctx) => {
1546
1586
  resetSessionAllowances();
1547
- const noSandbox = maybePi.getFlag?.('no-sandbox') as boolean;
1587
+ noSandboxFlag = Boolean(maybePi.getFlag?.('no-sandbox'));
1548
1588
 
1549
- if (noSandbox) {
1550
- sandboxEnabled = false;
1551
- sandboxReady = false;
1589
+ if (noSandboxFlag) {
1590
+ disableSandbox(ctx);
1552
1591
  notify(ctx, 'Sandbox disabled via --no-sandbox', 'warning');
1553
1592
  return;
1554
1593
  }
1555
1594
 
1595
+ if (!isSandboxEnabledInPiSettings(ctx.cwd)) {
1596
+ disableSandbox(ctx);
1597
+ notify(ctx, 'Sandbox disabled via pi settings', 'info');
1598
+ return;
1599
+ }
1600
+
1556
1601
  const config = loadConfig(ctx.cwd);
1557
1602
  if (!config.enabled) {
1558
- sandboxEnabled = false;
1559
- sandboxReady = false;
1603
+ disableSandbox(ctx);
1560
1604
  notify(ctx, 'Sandbox disabled via config', 'info');
1561
1605
  return;
1562
1606
  }
1563
1607
 
1564
1608
  enableSandbox(ctx);
1565
1609
  });
1566
-
1567
- maybePi.registerCommand?.('sandbox-enable', {
1568
- description: 'Enable the landstrip sandbox for this session',
1569
- handler: async (_args, ctx) => {
1570
- if (sandboxEnabled) {
1571
- notify(ctx, 'Sandbox is already enabled', 'info');
1572
- return;
1573
- }
1574
-
1575
- if (enableSandbox(ctx)) notify(ctx, 'Sandbox enabled', 'info');
1576
- },
1577
- });
1578
-
1579
- maybePi.registerCommand?.('sandbox-disable', {
1580
- description: 'Disable the landstrip sandbox for this session',
1581
- handler: async (_args, ctx) => {
1582
- if (!sandboxEnabled) {
1583
- notify(ctx, 'Sandbox is already disabled', 'info');
1584
- return;
1585
- }
1586
-
1587
- sandboxEnabled = false;
1588
- sandboxReady = false;
1589
- setTuiStatus(ctx, 'sandbox', undefined);
1590
- notify(ctx, 'Sandbox disabled', 'info');
1591
- },
1592
- });
1593
-
1594
1610
  maybePi.registerCommand?.('sandbox', {
1595
1611
  description: 'Show sandbox configuration',
1596
1612
  handler: async (_args, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.11.7",
3
+ "version": "0.11.8",
4
4
  "description": "Landlock-based sandboxing for pi with interactive permission prompts",
5
5
  "keywords": [
6
6
  "landstrip",