@parall/daemon 1.34.0 → 1.35.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.
@@ -1,3 +1,4 @@
1
+ import { type ViewerTurnConfig } from './browser-viewer-streamer.js';
1
2
  type BrowserProfileStatus = 'running' | 'stopped' | 'error';
2
3
  interface BrowserProfileManagerOptions {
3
4
  homeDir: string;
@@ -22,6 +23,7 @@ export declare class BrowserProfileManager {
22
23
  private readonly ensuredAccounts;
23
24
  private readonly ensuringAccounts;
24
25
  private readonly reportedStatuses;
26
+ private readonly viewer;
25
27
  constructor(opts: BrowserProfileManagerOptions);
26
28
  invoke({ profileId, account, command, input }: BrowserInvokeRequest): Promise<unknown>;
27
29
  ensureRuntime(profileId: string): Promise<void>;
@@ -29,6 +31,14 @@ export declare class BrowserProfileManager {
29
31
  stopProfile(profileId: string): Promise<void>;
30
32
  resetProfile(profileId: string): Promise<void>;
31
33
  stop(): Promise<void>;
34
+ /**
35
+ * Viewer control command entrypoint — delegates to BrowserViewerStreamer
36
+ * (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
37
+ * subprocesses and viewer tab navigation.
38
+ */
39
+ handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig): Promise<Record<string, unknown>>;
40
+ /** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
41
+ private cdpEndpoint;
32
42
  private prepareOpenProfile;
33
43
  private openProfileTabOnce;
34
44
  private resetProfileOnce;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"AAQA,KAAK,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7F;AASD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AA0BD,qBAAa,qBAAqB;IAQpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;gBAEjC,IAAI,EAAE,4BAA4B;IAIzD,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CtF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBb,kBAAkB;YAYlB,kBAAkB;YAUlB,gBAAgB;YAOhB,oBAAoB;YAKpB,yBAAyB;YAezB,aAAa;YAWb,mBAAmB;IAqBjC;;;;;;;;;;;;OAYG;YACW,uBAAuB;IAiCrC,oFAAoF;YACtE,oBAAoB;YAuBpB,aAAa;YAiCb,qBAAqB;YAKrB,wBAAwB;YAQxB,kBAAkB;YAQlB,yBAAyB;IAqBvC,OAAO,CAAC,YAAY;YAON,WAAW;YAaX,YAAY;YAmBZ,kBAAkB;YAUlB,aAAa;YAUb,iBAAiB;YAsBjB,WAAW;YAmDX,IAAI;CA2BnB"}
1
+ {"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"AAMA,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAG5F,KAAK,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7F;AAeD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AA0BD,qBAAa,qBAAqB;IAYpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAI9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAElB,IAAI,EAAE,4BAA4B;IAWzD,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CtF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;;OAIG;IACG,mBAAmB,CACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAInC,qEAAqE;YACvD,WAAW;YAWX,kBAAkB;YAYlB,kBAAkB;YAUlB,gBAAgB;YAOhB,oBAAoB;YAKpB,yBAAyB;YAezB,aAAa;YAWb,mBAAmB;IAqBjC;;;;;;;;;;;;OAYG;YACW,uBAAuB;IAiCrC,oFAAoF;YACtE,oBAAoB;YAuBpB,aAAa;YAiCb,qBAAqB;YAKrB,wBAAwB;YAQxB,kBAAkB;YAQlB,yBAAyB;IAqBvC,OAAO,CAAC,YAAY;YAON,WAAW;YAaX,YAAY;YAmBZ,kBAAkB;YAUlB,aAAa;YAUb,iBAAiB;YAsBjB,WAAW;YAmDX,IAAI;CA2BnB"}
@@ -2,9 +2,10 @@ import { spawn } from 'node:child_process';
2
2
  import { randomBytes } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, readFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
- import * as net from 'node:net';
6
5
  import * as path from 'node:path';
7
6
  import { fileURLToPath } from 'node:url';
7
+ import { BrowserViewerStreamer } from './browser-viewer-streamer.js';
8
+ import { findFreePort, formatErrorForLog, sleep } from './subprocess.js';
8
9
  const BB_BROWSER_DAEMON_START_TIMEOUT_MS = 15_000;
9
10
  class BrowserCommandError extends Error {
10
11
  method;
@@ -24,9 +25,20 @@ export class BrowserProfileManager {
24
25
  ensuredAccounts = new Set();
25
26
  ensuringAccounts = new Map();
26
27
  reportedStatuses = new Map();
28
+ // Live viewer orchestration (bb-viewer streamer subprocesses + viewer nav)
29
+ // lives in BrowserViewerStreamer; the manager hands it the account-scoped
30
+ // bb-browser command path via the host interface.
31
+ viewer;
27
32
  constructor(opts) {
28
33
  this.opts = opts;
29
34
  mkdirSync(this.opts.homeDir, { recursive: true });
35
+ this.viewer = new BrowserViewerStreamer({
36
+ log: this.opts.log,
37
+ sendBrowserCommand: (request) => this.sendCommand(request),
38
+ ensureAccount: (account) => this.ensureAccount(account),
39
+ withDaemonRecovery: (operation, label) => this.withDaemonRecovery(operation, label),
40
+ cdpEndpoint: () => this.cdpEndpoint(),
41
+ });
30
42
  }
31
43
  async invoke({ profileId, account, command, input }) {
32
44
  if (!profileId)
@@ -105,6 +117,8 @@ export class BrowserProfileManager {
105
117
  if (!profileId)
106
118
  throw new Error('browser profile id is required');
107
119
  this.reportedStatuses.delete(profileId); // see openProfile — lifecycle outcomes always report
120
+ // A stopped profile can have no live viewer — tear down its streamer.
121
+ this.viewer.killProfileStreamer(profileId);
108
122
  // bb-browser-pro does not expose a single-account "dispose context" command.
109
123
  // Server-side stopped status is the authority that gates future invokes.
110
124
  this.reportStatus(profileId, 'stopped');
@@ -113,6 +127,8 @@ export class BrowserProfileManager {
113
127
  if (!profileId)
114
128
  throw new Error('browser profile id is required');
115
129
  this.reportedStatuses.delete(profileId); // see openProfile — lifecycle outcomes always report
130
+ // Reset destroys the account context the viewer is connected to — kill it.
131
+ this.viewer.killProfileStreamer(profileId);
116
132
  try {
117
133
  await this.withDaemonRecovery(() => this.resetProfileOnce(profileId), `reset profile ${profileId}`);
118
134
  this.reportStatus(profileId, 'stopped');
@@ -124,6 +140,8 @@ export class BrowserProfileManager {
124
140
  }
125
141
  }
126
142
  async stop() {
143
+ // Tear down all per-profile bb-viewer streamers before the bb-browser daemon.
144
+ this.viewer.shutdown();
127
145
  const daemon = this.daemon;
128
146
  this.daemon = null;
129
147
  this.starting = null;
@@ -142,6 +160,25 @@ export class BrowserProfileManager {
142
160
  daemon.child.kill('SIGTERM');
143
161
  }
144
162
  }
163
+ /**
164
+ * Viewer control command entrypoint — delegates to BrowserViewerStreamer
165
+ * (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
166
+ * subprocesses and viewer tab navigation.
167
+ */
168
+ async handleViewerCommand(profileId, sessionId, command, input, turn) {
169
+ return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
170
+ }
171
+ /** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
172
+ async cdpEndpoint() {
173
+ const daemon = await this.ensureDaemon();
174
+ const status = await this.post('/status', daemon, undefined, 5_000);
175
+ const host = status.cdpHost?.trim();
176
+ const port = status.cdpPort;
177
+ if (!host || typeof port !== 'number' || !Number.isFinite(port)) {
178
+ throw new Error('bb-browser /status did not report a CDP endpoint');
179
+ }
180
+ return { host, port };
181
+ }
145
182
  async prepareOpenProfile(profileId, url) {
146
183
  const accountCreated = await this.ensureAccount(profileId, url !== 'about:blank' ? url : undefined);
147
184
  await this.sendCommand({ method: 'tab_list', account: profileId });
@@ -558,10 +595,6 @@ function comparableUrl(url) {
558
595
  return null;
559
596
  }
560
597
  }
561
- function formatErrorForLog(err) {
562
- const message = err instanceof Error ? err.message : String(err);
563
- return message.replace(/\s+/g, ' ').slice(0, 300);
564
- }
565
598
  function resolveBbBrowserDaemonPath() {
566
599
  // Bundle channels (CDN self-update, desktop, npm bin — all run the flat
567
600
  // esbuild artifact with NO node_modules) ship bb-browser as a sibling flat
@@ -582,27 +615,6 @@ function resolveBbBrowserDaemonPath() {
582
615
  }
583
616
  return full;
584
617
  }
585
- async function findFreePort() {
586
- return new Promise((resolve, reject) => {
587
- const server = net.createServer();
588
- server.unref();
589
- server.on('error', reject);
590
- server.listen(0, '127.0.0.1', () => {
591
- const address = server.address();
592
- server.close(() => {
593
- if (address && typeof address === 'object') {
594
- resolve(address.port);
595
- }
596
- else {
597
- reject(new Error('failed to allocate free port'));
598
- }
599
- });
600
- });
601
- });
602
- }
603
618
  function randomToken() {
604
619
  return randomBytes(16).toString('hex');
605
620
  }
606
- function sleep(ms) {
607
- return new Promise((resolve) => setTimeout(resolve, ms));
608
- }
@@ -0,0 +1,188 @@
1
+ /** Optional managed TURN relay config carried on a `stream.start` command. */
2
+ export interface ViewerTurnConfig {
3
+ url: string;
4
+ username?: string;
5
+ credential?: string;
6
+ }
7
+ /**
8
+ * The slice of BrowserProfileManager the viewer streamer depends on: the
9
+ * account-scoped bb-browser /command path and the daemon lifecycle helpers.
10
+ * One-directional — the manager constructs the streamer; the streamer never
11
+ * imports the manager.
12
+ */
13
+ export interface ViewerStreamerHost {
14
+ log: {
15
+ info(msg: string): void;
16
+ warn(msg: string): void;
17
+ error(msg: string): void;
18
+ };
19
+ /** Account-scoped bb-browser `/command` (BrowserProfileManager.sendCommand). */
20
+ sendBrowserCommand(request: Record<string, unknown> & {
21
+ method: string;
22
+ account?: string;
23
+ }): Promise<Record<string, unknown>>;
24
+ ensureAccount(account: string): Promise<boolean>;
25
+ withDaemonRecovery<T>(operation: () => Promise<T>, label: string): Promise<T>;
26
+ /** cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
27
+ cdpEndpoint(): Promise<{
28
+ host: string;
29
+ port: number;
30
+ }>;
31
+ }
32
+ export declare class BrowserViewerStreamer {
33
+ private readonly host;
34
+ private readonly streamers;
35
+ private stopping;
36
+ constructor(host: ViewerStreamerHost);
37
+ /**
38
+ * Handle a viewer control command for a profile. Throws on any failure (the
39
+ * supervisor maps a throw to a `{error:{message}}` reply); never returns a
40
+ * partial result. `data.input` / `data.turn` / `data.session_id` come from
41
+ * MachineBrowserProfileViewerData.
42
+ */
43
+ handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig): Promise<Record<string, unknown>>;
44
+ /**
45
+ * stream.start — spawn a FRESH bb-viewer for this profile (killing any prior
46
+ * one), resolve the profile's account-scoped page-target CDP ws URL, and run
47
+ * the streamer's `connect` command. Records sessionId on the streamer entry.
48
+ */
49
+ private viewerStreamStart;
50
+ /**
51
+ * stream.answer — apply the client's WebRTC answer to the profile's streamer.
52
+ * Rejects if the streamer's sessionId no longer matches (a stale viewer
53
+ * session from a superseded stream.start).
54
+ */
55
+ private viewerStreamAnswer;
56
+ /**
57
+ * stream.close — best-effort stop the streamer, then kill + remove it. A
58
+ * stale session's close (independent HTTP legs can arrive out of order after
59
+ * a retry) must NOT tear down a successor session's live streamer, so a
60
+ * mismatched sessionId is ignored. An EMPTY sessionId gets the same stale
61
+ * treatment — a client closed mid-handshake never received a session id, and
62
+ * its unscoped close racing the next session's stream.start would kill that
63
+ * session's live streamer (React StrictMode's dev double-mount exercises
64
+ * exactly this ordering). The only unscoped close is an explicit
65
+ * `{force:true}` input, reserved for a server-initiated admin disconnect.
66
+ */
67
+ private viewerStreamClose;
68
+ /**
69
+ * stream.switch — point the existing streamer at a different account-scoped
70
+ * tab. input {tab} is the bb-browser short tab id / target id. Guarded by
71
+ * sessionId like stream.answer: a superseded session must not steer the
72
+ * current streamer, and an EMPTY sessionId is rejected the same way — every
73
+ * real caller has its session id by the time it can switch (it arrives with
74
+ * the stream.start response), so a session-less switch is by definition not
75
+ * the current viewer.
76
+ */
77
+ private viewerStreamSwitch;
78
+ /**
79
+ * close — close an account tab via bb-browser, then, if the closed tab was
80
+ * the one being streamed, re-point the streamer at the profile's next active
81
+ * tab. Without the re-switch the streamer stays bound to a destroyed CDP
82
+ * target and the viewer goes black. Returns `streamed_tab_id` when a
83
+ * re-switch happened so the client can adopt the new active tab.
84
+ */
85
+ private viewerCloseTab;
86
+ /**
87
+ * Bring a CDP page target to the foreground via Chrome's DevTools HTTP
88
+ * endpoint (`/json/activate/{targetId}`, same host:port as the CDP ws). Chrome
89
+ * does not composite backgrounded tabs, so `Page.startScreencast` on a
90
+ * background tab yields zero frames (black viewer). Best-effort: a failure
91
+ * must not block the stream. host:port + targetId are parsed from the cdpUrl
92
+ * (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
93
+ */
94
+ private bringTargetToFront;
95
+ /**
96
+ * Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
97
+ * close) to bb-browser scoped to the profile's account. This lets the human
98
+ * in the live viewer navigate and handle OAuth popups directly. Reuses the
99
+ * same account-scoped sendCommand path the agent invokes use.
100
+ *
101
+ * SECURITY — the `account` field alone does NOT scope tab addressing:
102
+ * bb-browser's ensurePageTarget resolves a client-supplied tab/tabId against
103
+ * EVERY page target in the shared Chrome (all accounts), and tab_list
104
+ * returns every tab with `account` as a mere annotation (verified in
105
+ * @pinixai/bb-browser-pro@0.15.0 dist/daemon.js ensurePageTarget/tab_list).
106
+ * On a multi-profile host that would let one profile's viewer list,
107
+ * navigate, and close other profiles' logged-in tabs. So before forwarding:
108
+ * (a) any tab ref must resolve through accountTabTargetId, which throws on
109
+ * tabs the profile's account does not own; (b) ref-less tab-addressed
110
+ * commands are pinned to the profile's own active tab instead of
111
+ * bb-browser's account-blind global current tab; (c) tab_list output is
112
+ * filtered to the profile's own rows; and (d) open/tab_new URLs must be
113
+ * http(s)/about:blank — file:// or chrome:// would read the host
114
+ * filesystem / browser internals straight into the video stream.
115
+ */
116
+ private viewerForwardNav;
117
+ /**
118
+ * Resolve `ws://<cdpHost>:<cdpPort>/devtools/page/<targetId>` for a PAGE
119
+ * target owned by THIS profile's account — NOT bb-browser's global current
120
+ * tab (which `getCurrentTabCdpUrl`/`getTabCdpUrl` in the bundled daemon use
121
+ * via `cdp.currentTargetId`, an account-blind global; see daemon.js
122
+ * line ~10389/10410). When `tabRef` is given (stream.switch) we resolve that
123
+ * specific account tab; otherwise the profile's active page tab.
124
+ *
125
+ * Evidence from the installed @pinixai/bb-browser-pro@0.15.0 dist/daemon.js:
126
+ * - GET /status (handleStatus, line ~1659-1668) returns `cdpHost` + `cdpPort`
127
+ * — the CDP host/port the daemon's Chrome listens on.
128
+ * - The CDP page ws URL format is `ws://<cdp.host>:<cdp.port>/devtools/page/<page.id>`
129
+ * (getCurrentTabCdpUrl, line ~10392), where `page.id` is the full CDP
130
+ * targetId (getTargets maps `t.targetId` → `.id`, line ~2461-2466).
131
+ * - tab_list (command handler, line ~1041-1059) returns per-tab
132
+ * `{ tabId: t.id, tab: <shortId>, account: <accountName>, url, ... }`, so
133
+ * `tabId` is exactly the full targetId for the devtools URL and `account`
134
+ * is the per-tab attribution we filter on (reused by findAccountTabOnHost).
135
+ */
136
+ private resolveAccountPageCdpUrl;
137
+ /**
138
+ * Full CDP targetId of the profile account's active page tab (the owned tab
139
+ * Chrome marks active, else the first owned tab). If the account owns no
140
+ * page tab yet, open `about:blank` for it first (mirrors the agent-invoke
141
+ * path's "always give a fresh account an owned tab" rule).
142
+ */
143
+ private accountActivePageTargetId;
144
+ /**
145
+ * Full CDP targetId (`tabId`) of the account's active page tab: the owned tab
146
+ * Chrome marks `active` when it belongs to this account, else the first owned
147
+ * tab (the global active flag is account-blind — another profile's tab may
148
+ * hold it, which must not leak here).
149
+ */
150
+ private firstAccountPageTargetId;
151
+ /**
152
+ * Full CDP targetId for a specific account tab, addressed by the bb-browser
153
+ * short tab id (`tab`) or full target id (`tabId`). Used by stream.switch.
154
+ */
155
+ private accountTabTargetId;
156
+ /** `tab_list {account}` rows, typed to the fields we read (tabId/tab/account/active). */
157
+ private accountTabs;
158
+ /**
159
+ * Spawn a bb-viewer streamer for the profile and wait until healthy. Binary
160
+ * is PRLL_BB_VIEWER_BIN (our image sets it to /usr/local/bin/bb-viewer),
161
+ * defaulting to `bb-viewer` on PATH — we NEVER download it. A free port is
162
+ * allocated per streamer. Stores the entry in `this.streamers` once healthy.
163
+ */
164
+ private spawnStreamer;
165
+ /**
166
+ * SIGTERM with a SIGKILL escalation: a bb-viewer that hangs or ignores TERM
167
+ * would otherwise outlive its teardown holding the port + CDP session (the
168
+ * shell-level pattern-kill only exists on the hosted pod, not BYOC). The
169
+ * escalation timer is unref'd so it never holds the daemon open; it is
170
+ * cleared on exit. Caveat: a daemon process that exits immediately after
171
+ * shutdown() abandons the timer — acceptable, the TERM was still sent.
172
+ */
173
+ private killChild;
174
+ /**
175
+ * POST a command to the profile's bb-viewer `/command` endpoint. Parses the
176
+ * `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
177
+ * the inner result, throwing the error message on failure.
178
+ */
179
+ private streamerCommand;
180
+ /** Kill + remove the profile's bb-viewer streamer, if any. Idempotent. */
181
+ killProfileStreamer(profileId: string): void;
182
+ /**
183
+ * Tear down every profile streamer and refuse new spawns (daemon stop).
184
+ * Idempotent.
185
+ */
186
+ shutdown(): void;
187
+ }
188
+ //# sourceMappingURL=browser-viewer-streamer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-viewer-streamer.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-viewer-streamer.ts"],"names":[],"mappings":"AAsCA,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAcD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,gFAAgF;IAChF,kBAAkB,CAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GACtE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9E,gEAAgE;IAChE,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,qBAAa,qBAAqB;IAUpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAI9D,OAAO,CAAC,QAAQ,CAAS;gBAEI,IAAI,EAAE,kBAAkB;IAErD;;;;;OAKG;IACG,mBAAmB,CACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAyBnC;;;;OAIG;YACW,iBAAiB;IA+C/B;;;;OAIG;YACW,kBAAkB;IAgBhC;;;;;;;;;;OAUG;YACW,iBAAiB;IAuB/B;;;;;;;;OAQG;YACW,kBAAkB;IAuBhC;;;;;;OAMG;YACW,cAAc;IAiC5B;;;;;;;OAOG;YACW,kBAAkB;IAqBhC;;;;;;;;;;;;;;;;;;;;OAoBG;YACW,gBAAgB;IA6C9B;;;;;;;;;;;;;;;;;;OAkBG;YACW,wBAAwB;IAetC;;;;;OAKG;YACW,yBAAyB;IAevC;;;;;OAKG;YACW,wBAAwB;IAatC;;;OAGG;YACW,kBAAkB;IAehC,yFAAyF;YAC3E,WAAW;IAgBzB;;;;;OAKG;YACW,aAAa;IA2F3B;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;IAqBjB;;;;OAIG;YACW,eAAe;IA+B7B,0EAA0E;IAC1E,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO5C;;;OAGG;IACH,QAAQ,IAAI,IAAI;CAMjB"}