wb-browser-runtime 0.10.0 → 0.11.0

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.
@@ -56,7 +56,7 @@ if (recording.enabled) {
56
56
 
57
57
  const sessions = new SessionManager();
58
58
 
59
- async function ensureSession(name, { profile } = {}) {
59
+ async function ensureSession(name, { profile, restoreSession } = {}) {
60
60
  return sessions.ensure(name, async () => {
61
61
  // Vendors charge for the session the moment allocate() returns; if
62
62
  // anything after this point throws (getLiveUrl, CDP connect, newContext,
@@ -69,11 +69,22 @@ async function ensureSession(name, { profile } = {}) {
69
69
  // against a cold vendor region, but the live-URL fetch and
70
70
  // newContext/newPage can each stall independently.
71
71
  const t0 = Date.now();
72
- const allocated = await provider.allocate({ profile, sessionName: name });
72
+ const restored =
73
+ restoreSession &&
74
+ restoreSession.vendor === provider.name &&
75
+ restoreSession.cdpUrl;
76
+ const allocated = restored
77
+ ? {
78
+ sid: restoreSession.sid,
79
+ cdpUrl: restoreSession.cdpUrl,
80
+ _liveUrl: restoreSession.liveUrl ?? null,
81
+ _restored: true,
82
+ }
83
+ : await provider.allocate({ profile, sessionName: name });
73
84
  const tAllocated = Date.now();
74
85
  let browser = null;
75
86
  try {
76
- const liveUrl = await provider.getLiveUrl(allocated);
87
+ const liveUrl = allocated._liveUrl ?? (await provider.getLiveUrl(allocated));
77
88
  browser = await chromium.connectOverCDP(allocated.cdpUrl);
78
89
  const tConnected = Date.now();
79
90
  const context = browser.contexts()[0] ?? (await browser.newContext());
@@ -82,6 +93,8 @@ async function ensureSession(name, { profile } = {}) {
82
93
 
83
94
  const info = {
84
95
  sid: allocated.sid,
96
+ cdpUrl: allocated.cdpUrl,
97
+ vendor: provider.name,
85
98
  browser,
86
99
  context,
87
100
  page,
@@ -95,6 +108,7 @@ async function ensureSession(name, { profile } = {}) {
95
108
  session_id: allocated.sid,
96
109
  live_url: liveUrl,
97
110
  vendor: provider.name,
111
+ restored: Boolean(restored),
98
112
  started_at: new Date().toISOString(),
99
113
  timings: {
100
114
  allocate_ms: tAllocated - t0,
@@ -107,12 +121,12 @@ async function ensureSession(name, { profile } = {}) {
107
121
  await recording.start(info, name);
108
122
  return info;
109
123
  } catch (e) {
110
- if (browser) {
124
+ if (browser && !allocated._restored) {
111
125
  try {
112
126
  await browser.close();
113
127
  } catch {}
114
128
  }
115
- await provider.release(allocated.sid);
129
+ if (!allocated._restored) await provider.release(allocated.sid);
116
130
  throw e;
117
131
  }
118
132
  });
@@ -252,10 +266,14 @@ async function handleSlice(msg) {
252
266
  const verbs = Array.isArray(msg.verbs) ? msg.verbs : [];
253
267
  const sessionName = msg.session || "default";
254
268
  const restore = msg.restore || null;
269
+ const restoreSession = restore?.state?.session || null;
255
270
 
256
271
  let session;
257
272
  try {
258
- session = await ensureSession(sessionName, { profile: msg.profile });
273
+ session = await ensureSession(sessionName, {
274
+ profile: msg.profile,
275
+ restoreSession,
276
+ });
259
277
  } catch (e) {
260
278
  send({
261
279
  type: "slice.failed",
@@ -311,7 +329,17 @@ async function handleSlice(msg) {
311
329
  // descriptor and handed back on resume. The verb can stash
312
330
  // whatever it needs here; we always ensure verb_index is set
313
331
  // so the dispatcher can compute startAt on re-entry.
314
- sidecar_state: { ...(pauseMeta.sidecar_state || {}), verb_index: i },
332
+ sidecar_state: {
333
+ ...(pauseMeta.sidecar_state || {}),
334
+ verb_index: i,
335
+ session: {
336
+ vendor: session.vendor,
337
+ name: sessionName,
338
+ sid: session.sid,
339
+ cdpUrl: session.cdpUrl,
340
+ liveUrl: session.liveUrl,
341
+ },
342
+ },
315
343
  });
316
344
  return;
317
345
  }
@@ -391,6 +419,32 @@ async function shutdown() {
391
419
  process.exit(0);
392
420
  }
393
421
 
422
+ async function suspend() {
423
+ if (shuttingDown) return;
424
+ shuttingDown = true;
425
+ // Flush recordings while CDP is still connected, but intentionally leave
426
+ // browser contexts and vendor sessions open. The operator needs the live
427
+ // inspector after wb exits 42, and wb resume reconnects using the persisted
428
+ // cdpUrl/liveUrl in sidecar_state.
429
+ for (const [name, info] of sessions) {
430
+ try {
431
+ await recording.flush(info, name);
432
+ } catch (e) {
433
+ log(`[suspend] flush recording ${name}: ${e.message}`);
434
+ try {
435
+ send({
436
+ type: "slice.recording.failed",
437
+ session: name,
438
+ run_id: recording.runId,
439
+ reason: `suspend_finalize_error: ${e.message}`,
440
+ });
441
+ } catch {}
442
+ }
443
+ }
444
+ log("[suspend] leaving browser session alive for external resume");
445
+ process.exit(0);
446
+ }
447
+
394
448
  // --- Main loop --------------------------------------------------------------
395
449
 
396
450
  const rl = readline.createInterface({ input: process.stdin, terminal: false });
@@ -426,6 +480,15 @@ async function drainAndShutdown() {
426
480
  await shutdown();
427
481
  }
428
482
 
483
+ async function drainAndSuspend() {
484
+ try {
485
+ await sessions.drainAll();
486
+ } catch (e) {
487
+ log(`[suspend] drain failed: ${e.message}`);
488
+ }
489
+ await suspend();
490
+ }
491
+
429
492
  rl.on("line", (line) => {
430
493
  const trimmed = line.trim();
431
494
  if (!trimmed) return;
@@ -453,6 +516,9 @@ rl.on("line", (line) => {
453
516
  case "shutdown":
454
517
  drainAndShutdown();
455
518
  break;
519
+ case "suspend":
520
+ drainAndSuspend();
521
+ break;
456
522
  default:
457
523
  log(`[warn] unknown message type: ${msg.type}`);
458
524
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wb-browser-runtime",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Browser sidecar runtime for wb — Playwright over CDP (Browserbase, browser-use) via the wb-sidecar/1 line-framed JSON protocol.",
5
5
  "bin": {
6
6
  "wb-browser-runtime": "bin/wb-browser-runtime.js"