@poncho-ai/browser 0.6.24 → 0.6.25

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/browser@0.6.24 build /home/runner/work/poncho-ai/poncho-ai/packages/browser
2
+ > @poncho-ai/browser@0.6.25 build /home/runner/work/poncho-ai/poncho-ai/packages/browser
3
3
  > tsup src/index.ts --format esm --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -7,8 +7,8 @@
7
7
  CLI tsup v8.5.1
8
8
  CLI Target: es2022
9
9
  ESM Build start
10
- ESM dist/index.js 47.13 KB
11
- ESM ⚡️ Build success in 67ms
10
+ ESM dist/index.js 47.44 KB
11
+ ESM ⚡️ Build success in 63ms
12
12
  DTS Build start
13
- DTS ⚡️ Build success in 5077ms
13
+ DTS ⚡️ Build success in 5080ms
14
14
  DTS dist/index.d.ts 13.69 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @poncho-ai/browser
2
2
 
3
+ ## 0.6.25
4
+
5
+ ### Patch Changes
6
+
7
+ - [#180](https://github.com/cesr/poncho-ai/pull/180) [`4e27887`](https://github.com/cesr/poncho-ai/commit/4e27887655eda1d420b8f69097cfc79e42f9596c) Thanks [@cesr](https://github.com/cesr)! - Fix a deadlock that wedged the browser session after the first lock-acquire
8
+ timeout. `lock()` pushed a wrapper closure onto `_lockQueue` but, on the 30s
9
+ timeout, tried to remove the entry with `indexOf(resolve)` — searching for a
10
+ different function — so the timed-out waiter was never spliced out. When the
11
+ current owner later called `unlock()`, it `shift()`ed that zombie waiter and
12
+ invoked it; `resolve()` on the already-rejected promise was a no-op, so the
13
+ unlock was consumed by a dead waiter, `_locked` stayed `true`, and no live
14
+ operation could ever acquire the lock again. Every subsequent browser call
15
+ then returned "Browser operation timed out waiting for lock (30s)" until the
16
+ session was torn down. Waiters are now tracked as objects with a `settled`
17
+ flag: a timed-out waiter removes itself from the queue, and `unlock()` skips
18
+ any already-settled waiters when handing off ownership.
19
+
3
20
  ## 0.6.24
4
21
 
5
22
  ### Patch Changes
package/dist/index.js CHANGED
@@ -275,21 +275,33 @@ var BrowserSession = class {
275
275
  return;
276
276
  }
277
277
  return new Promise((resolve2, reject) => {
278
+ const waiter = { settled: false, grant: () => {
279
+ } };
278
280
  const timer = setTimeout(() => {
279
- const idx = this._lockQueue.indexOf(resolve2);
281
+ if (waiter.settled) return;
282
+ waiter.settled = true;
283
+ const idx = this._lockQueue.indexOf(waiter);
280
284
  if (idx !== -1) this._lockQueue.splice(idx, 1);
281
285
  reject(new Error("Browser operation timed out waiting for lock (30s)"));
282
286
  }, 3e4);
283
- this._lockQueue.push(() => {
287
+ waiter.grant = () => {
288
+ if (waiter.settled) return;
289
+ waiter.settled = true;
284
290
  clearTimeout(timer);
285
291
  resolve2();
286
- });
292
+ };
293
+ this._lockQueue.push(waiter);
287
294
  });
288
295
  }
289
296
  unlock() {
290
- const next = this._lockQueue.shift();
291
- if (next) next();
292
- else this._locked = false;
297
+ while (this._lockQueue.length > 0) {
298
+ const next = this._lockQueue.shift();
299
+ if (!next.settled) {
300
+ next.grant();
301
+ return;
302
+ }
303
+ }
304
+ this._locked = false;
293
305
  }
294
306
  // -----------------------------------------------------------------------
295
307
  // Core browser + tab management
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/browser",
3
- "version": "0.6.24",
3
+ "version": "0.6.25",
4
4
  "description": "Browser automation for Poncho agents, powered by agent-browser",
5
5
  "repository": {
6
6
  "type": "git",
package/src/session.ts CHANGED
@@ -163,7 +163,7 @@ export class BrowserSession {
163
163
  private readonly _uaOverrideApplied = new Set<string>();
164
164
 
165
165
  // Serialization lock for tab-switching operations
166
- private _lockQueue: Array<() => void> = [];
166
+ private _lockQueue: Array<{ grant: () => void; settled: boolean }> = [];
167
167
  private _locked = false;
168
168
 
169
169
  // Currently screencast conversation (only one at a time due to CDP)
@@ -189,19 +189,38 @@ export class BrowserSession {
189
189
  return;
190
190
  }
191
191
  return new Promise<void>((resolve, reject) => {
192
+ const waiter = { settled: false, grant: () => {} };
192
193
  const timer = setTimeout(() => {
193
- const idx = this._lockQueue.indexOf(resolve);
194
+ if (waiter.settled) return;
195
+ waiter.settled = true;
196
+ const idx = this._lockQueue.indexOf(waiter);
194
197
  if (idx !== -1) this._lockQueue.splice(idx, 1);
195
198
  reject(new Error("Browser operation timed out waiting for lock (30s)"));
196
199
  }, 30_000);
197
- this._lockQueue.push(() => { clearTimeout(timer); resolve(); });
200
+ waiter.grant = () => {
201
+ if (waiter.settled) return;
202
+ waiter.settled = true;
203
+ clearTimeout(timer);
204
+ resolve();
205
+ };
206
+ this._lockQueue.push(waiter);
198
207
  });
199
208
  }
200
209
 
201
210
  private unlock(): void {
202
- const next = this._lockQueue.shift();
203
- if (next) next();
204
- else this._locked = false;
211
+ // Hand ownership to the next waiter that is still live. A waiter whose
212
+ // 30s timeout already fired has rejected its promise and marked itself
213
+ // settled; granting it would silently drop the lock (resolve is a no-op)
214
+ // and leave `_locked` true forever, wedging every later caller. Skip the
215
+ // dead ones; if none remain, release the lock.
216
+ while (this._lockQueue.length > 0) {
217
+ const next = this._lockQueue.shift()!;
218
+ if (!next.settled) {
219
+ next.grant();
220
+ return;
221
+ }
222
+ }
223
+ this._locked = false;
205
224
  }
206
225
 
207
226
  // -----------------------------------------------------------------------