pi-landstrip 0.18.11 → 0.18.12

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 +69 -28
  2. package/package.json +2 -2
package/dist/index.ts CHANGED
@@ -275,38 +275,69 @@ function colorizeAgentText(color, text, themeFg, fallback = "accent") {
275
275
 
276
276
  class AsyncQueue {
277
277
  tail = Promise.resolve();
278
+ generation = 0;
279
+ resetWaiters = [];
278
280
  async acquire(signal, cancellationMessage = "Request cancelled") {
279
281
  if (signal?.aborted)
280
282
  throw new Error(cancellationMessage);
283
+ const generation = this.generation;
281
284
  let release;
282
285
  const previous = this.tail;
283
286
  this.tail = new Promise((resolve2) => {
284
287
  release = resolve2;
285
288
  });
286
- if (!signal) {
287
- await previous;
288
- return release;
289
- }
290
- let abort;
289
+ const finish = () => {
290
+ release?.();
291
+ };
292
+ let wakeReset;
293
+ const resetSignal = new Promise((resolve2) => {
294
+ wakeReset = resolve2;
295
+ this.resetWaiters.push(resolve2);
296
+ });
297
+ const dropResetWaiter = () => {
298
+ if (!wakeReset)
299
+ return;
300
+ const index = this.resetWaiters.indexOf(wakeReset);
301
+ if (index >= 0)
302
+ this.resetWaiters.splice(index, 1);
303
+ wakeReset = undefined;
304
+ };
291
305
  try {
292
- await Promise.race([
293
- previous,
294
- new Promise((_resolve, reject) => {
295
- abort = () => reject(new Error(cancellationMessage));
296
- signal.addEventListener("abort", abort, { once: true });
297
- })
298
- ]);
306
+ if (!signal) {
307
+ await Promise.race([previous, resetSignal]);
308
+ } else {
309
+ let abort;
310
+ try {
311
+ await Promise.race([
312
+ previous,
313
+ resetSignal,
314
+ new Promise((_resolve, reject) => {
315
+ abort = () => reject(new Error(cancellationMessage));
316
+ signal.addEventListener("abort", abort, { once: true });
317
+ })
318
+ ]);
319
+ } finally {
320
+ if (abort)
321
+ signal.removeEventListener("abort", abort);
322
+ }
323
+ }
324
+ if (this.generation !== generation) {
325
+ throw new Error(cancellationMessage);
326
+ }
327
+ return finish;
299
328
  } catch (error) {
300
- previous.finally(() => release?.());
329
+ previous.finally(finish);
301
330
  throw error;
302
331
  } finally {
303
- if (abort)
304
- signal.removeEventListener("abort", abort);
332
+ dropResetWaiter();
305
333
  }
306
- return release;
307
334
  }
308
335
  reset() {
336
+ this.generation += 1;
309
337
  this.tail = Promise.resolve();
338
+ const waiters = this.resetWaiters.splice(0);
339
+ for (const wake of waiters)
340
+ wake();
310
341
  }
311
342
  }
312
343
 
@@ -2085,14 +2116,14 @@ class Semaphore {
2085
2116
  if (this.active >= this.limit)
2086
2117
  return;
2087
2118
  this.active += 1;
2088
- return () => this.release();
2119
+ return this.releaseOnce();
2089
2120
  }
2090
2121
  async acquire(signal) {
2091
2122
  if (signal?.aborted)
2092
2123
  throw new Error("Task cancelled");
2093
2124
  if (this.active < this.limit) {
2094
2125
  this.active += 1;
2095
- return () => this.release();
2126
+ return this.releaseOnce();
2096
2127
  }
2097
2128
  await new Promise((resolve3, reject) => {
2098
2129
  const abort = () => {
@@ -2109,11 +2140,17 @@ class Semaphore {
2109
2140
  this.waiters.push(start);
2110
2141
  signal?.addEventListener("abort", abort, { once: true });
2111
2142
  });
2112
- return () => this.release();
2143
+ return this.releaseOnce();
2113
2144
  }
2114
- release() {
2115
- this.active -= 1;
2116
- this.waiters.shift()?.();
2145
+ releaseOnce() {
2146
+ let released = false;
2147
+ return () => {
2148
+ if (released)
2149
+ return;
2150
+ released = true;
2151
+ this.active -= 1;
2152
+ this.waiters.shift()?.();
2153
+ };
2117
2154
  }
2118
2155
  }
2119
2156
 
@@ -2708,7 +2745,9 @@ ${this.primaryAgent.prompt}` };
2708
2745
  const dirty = dirtySettings.has(index) ? theme.fg("warning", " *") : "";
2709
2746
  lines2.push(`${cursor} ${value} ${label}${dirty}`);
2710
2747
  }
2711
- lines2.push("", theme.fg("dim", selectedSetting === 0 ? rows[0]?.inherited ? "Sets concurrent subagents. Zero disables the task tool. Using the Global value." : "Sets concurrent subagents. Zero disables the task tool." : rows[1]?.inherited ? "Controls OS sandboxing. Use /sandbox to inspect its policy. Using the Global value." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
2748
+ if (selectedSetting === 1) {
2749
+ lines2.push("", theme.fg("dim", rows[1]?.inherited ? "Controls OS sandboxing. Use /sandbox to inspect its policy. Using the Global value." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
2750
+ }
2712
2751
  if (saving)
2713
2752
  lines2.push("", theme.fg("dim", "Saving…"));
2714
2753
  return box(lines2);
@@ -3718,7 +3757,9 @@ ${catalog.diagnostics.join(`
3718
3757
  });
3719
3758
  } finally {
3720
3759
  if (handedOff)
3721
- await this.restoreLease(task.id, signal);
3760
+ await this.restoreLease(task.id).catch(() => {
3761
+ return;
3762
+ });
3722
3763
  }
3723
3764
  response = {
3724
3765
  ok: true,
@@ -4015,11 +4056,11 @@ ${renderTaskResult(task.id, failed ? "error" : "completed", value)}`;
4015
4056
  lease.release = undefined;
4016
4057
  return true;
4017
4058
  }
4018
- async restoreLease(taskId, signal) {
4059
+ async restoreLease(taskId) {
4019
4060
  const lease = this.leases.get(taskId);
4020
4061
  if (!lease || lease.release)
4021
4062
  return;
4022
- const release = await this.semaphore.acquire(signal);
4063
+ const release = await this.semaphore.acquire();
4023
4064
  const current = this.leases.get(taskId);
4024
4065
  if (!current || current.release) {
4025
4066
  release();
@@ -4194,8 +4235,8 @@ function readOrEmptyConfig(configPath) {
4194
4235
  return {};
4195
4236
  try {
4196
4237
  return JSON.parse(readFileSync6(configPath, "utf-8"));
4197
- } catch {
4198
- return {};
4238
+ } catch (error) {
4239
+ throw new Error(`${configPath}: ${formatError(error)}`);
4199
4240
  }
4200
4241
  }
4201
4242
  function writeConfigFile(configPath, config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.18.11",
3
+ "version": "0.18.12",
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.11",
42
+ "@landstrip/landstrip": "^0.18.12",
43
43
  "jsonc-parser": "^3.3.1",
44
44
  "minimatch": "^10.2.5",
45
45
  "yaml": "^2.9.0"