pi-landstrip 0.18.22 → 0.18.23

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 (2) hide show
  1. package/dist/index.ts +124 -2
  2. package/package.json +2 -2
package/dist/index.ts CHANGED
@@ -5425,6 +5425,118 @@ function createLandstripIntegration(options = {}) {
5425
5425
  writePaths: options2.writePaths ?? []
5426
5426
  });
5427
5427
  }
5428
+ function createProviderBashOps() {
5429
+ return {
5430
+ async exec(command, cwd, { onData, signal, timeout, env }) {
5431
+ if (!existsSync5(cwd))
5432
+ throw new Error(`Working directory does not exist: ${cwd}`);
5433
+ const provider = activeShellProvider();
5434
+ let invocation;
5435
+ try {
5436
+ invocation = await provider.prepare({
5437
+ command,
5438
+ cwd,
5439
+ env: shellEnvironment(env, cwd, null),
5440
+ signal
5441
+ });
5442
+ if (!invocation.executable.trim()) {
5443
+ throw new Error(`Shell provider "${provider.id}" returned an empty executable`);
5444
+ }
5445
+ if (signal?.aborted)
5446
+ throw new Error("aborted");
5447
+ const prepared = invocation;
5448
+ return await new Promise((resolvePromise, reject) => {
5449
+ let timeoutHandle;
5450
+ let timedOut = false;
5451
+ let settled = false;
5452
+ let childExited = false;
5453
+ let childExitCode = null;
5454
+ let postExitTimer;
5455
+ let stdoutEnded = false;
5456
+ let stderrEnded = false;
5457
+ const child = spawn2(prepared.executable, prepared.args, {
5458
+ cwd,
5459
+ env: createLandstripLauncherEnvironment(prepared.launcherEnv),
5460
+ detached: true,
5461
+ stdio: ["ignore", "pipe", "pipe"]
5462
+ });
5463
+ patchProcessGroupKill(child);
5464
+ const cleanup = async () => {
5465
+ if (timeoutHandle)
5466
+ clearTimeout(timeoutHandle);
5467
+ if (postExitTimer)
5468
+ clearTimeout(postExitTimer);
5469
+ signal?.removeEventListener("abort", onAbort);
5470
+ const toDispose = invocation;
5471
+ invocation = undefined;
5472
+ await toDispose?.dispose?.();
5473
+ };
5474
+ const finish = (code) => {
5475
+ if (settled)
5476
+ return;
5477
+ settled = true;
5478
+ child.stdout?.destroy();
5479
+ child.stderr?.destroy();
5480
+ cleanup().then(() => {
5481
+ if (signal?.aborted)
5482
+ reject(new Error("aborted"));
5483
+ else if (timedOut)
5484
+ reject(new Error(`timeout:${timeout}`));
5485
+ else
5486
+ resolvePromise({ exitCode: code });
5487
+ }, reject);
5488
+ };
5489
+ const maybeFinishAfterExit = () => {
5490
+ if (childExited && stdoutEnded && stderrEnded)
5491
+ finish(childExitCode);
5492
+ };
5493
+ const killChild = () => {
5494
+ child.kill("SIGKILL");
5495
+ };
5496
+ function onAbort() {
5497
+ killChild();
5498
+ }
5499
+ child.stdout?.on("data", onData);
5500
+ child.stderr?.on("data", onData);
5501
+ child.stdout?.once("end", () => {
5502
+ stdoutEnded = true;
5503
+ maybeFinishAfterExit();
5504
+ });
5505
+ child.stderr?.once("end", () => {
5506
+ stderrEnded = true;
5507
+ maybeFinishAfterExit();
5508
+ });
5509
+ child.once("error", (error) => {
5510
+ if (settled)
5511
+ return;
5512
+ settled = true;
5513
+ cleanup().then(() => reject(error), reject);
5514
+ });
5515
+ child.once("exit", (code) => {
5516
+ childExited = true;
5517
+ childExitCode = code;
5518
+ maybeFinishAfterExit();
5519
+ if (!settled)
5520
+ postExitTimer = setTimeout(() => finish(code), EXIT_STDIO_GRACE_MS);
5521
+ });
5522
+ child.once("close", (code) => finish(code));
5523
+ if (timeout !== undefined && timeout > 0) {
5524
+ timeoutHandle = setTimeout(() => {
5525
+ timedOut = true;
5526
+ killChild();
5527
+ }, timeout * 1000);
5528
+ }
5529
+ signal?.addEventListener("abort", onAbort, { once: true });
5530
+ if (signal?.aborted)
5531
+ onAbort();
5532
+ });
5533
+ } catch (error) {
5534
+ await invocation?.dispose?.();
5535
+ throw error;
5536
+ }
5537
+ }
5538
+ };
5539
+ }
5428
5540
  function createLandstripBashOps(ctx, callbacks = {}, allowances = { domains: [], readPaths: [], writePaths: [], targets: [] }) {
5429
5541
  return {
5430
5542
  async exec(command, cwd, { onData, signal, timeout, env }) {
@@ -5920,9 +6032,16 @@ ${message}
5920
6032
  if (!ctx)
5921
6033
  throw new Error("Sandbox context is unavailable");
5922
6034
  if (!ensureSandboxState(ctx)) {
5923
- if (sandboxState === "unavailable") {
6035
+ if (sandboxState === "unavailable" && externalShellProvider === undefined) {
5924
6036
  throw new Error("Sandbox is unavailable; refusing command");
5925
6037
  }
6038
+ if (externalShellProvider !== undefined) {
6039
+ const providerBash = createBashToolDefinition(ctx.cwd, {
6040
+ operations: createProviderBashOps(),
6041
+ shellPath: SettingsManager.create(ctx.cwd).getShellPath()
6042
+ });
6043
+ return providerBash.execute(id, params, signal, onUpdate, ctx);
6044
+ }
5926
6045
  return localBash.execute(id, params, signal, onUpdate, ctx);
5927
6046
  }
5928
6047
  return runBashWithOptionalRetry(id, params, signal, onUpdate, ctx);
@@ -5942,9 +6061,12 @@ ${message}
5942
6061
  pi.registerTool(createBashTool(localCwd));
5943
6062
  pi.on("user_bash", async (_event, ctx) => {
5944
6063
  if (!ensureSandboxState(ctx)) {
5945
- if (sandboxState === "unavailable") {
6064
+ if (sandboxState === "unavailable" && externalShellProvider === undefined) {
5946
6065
  throw new Error("Sandbox is unavailable; refusing command");
5947
6066
  }
6067
+ if (externalShellProvider !== undefined) {
6068
+ return { operations: createProviderBashOps() };
6069
+ }
5948
6070
  return;
5949
6071
  }
5950
6072
  return { operations: createLandstripBashOps(ctx, { promptOnBlock: true }) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.18.22",
3
+ "version": "0.18.23",
4
4
  "description": "Sandboxed Bash and process-backed agents for Pi",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -39,7 +39,7 @@
39
39
  "ci:test": "npm run test"
40
40
  },
41
41
  "dependencies": {
42
- "@landstrip/landstrip": "^0.18.22",
42
+ "@landstrip/landstrip": "^0.18.23",
43
43
  "jsonc-parser": "^3.3.1",
44
44
  "minimatch": "^10.2.5",
45
45
  "yaml": "^2.9.0"