@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.
- package/bundle/manifest.json +9 -9
- package/bundle/parall-claude-agent.js +168 -92
- package/bundle/parall-codex-agent.js +163 -91
- package/bundle/parall-daemon.js +702 -38
- package/dist/clip-runtime/browser-profile-manager.d.ts +10 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +38 -26
- package/dist/clip-runtime/browser-viewer-streamer.d.ts +188 -0
- package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -0
- package/dist/clip-runtime/browser-viewer-streamer.js +595 -0
- package/dist/clip-runtime/subprocess.d.ts +11 -0
- package/dist/clip-runtime/subprocess.d.ts.map +1 -0
- package/dist/clip-runtime/subprocess.js +33 -0
- package/dist/supervisor.d.ts +10 -1
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +48 -6
- package/package.json +6 -6
|
@@ -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":"
|
|
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"}
|