@parall/daemon 1.49.0 → 1.50.1
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 +11 -11
- package/bundle/parall-browser-pod.js +318 -104
- package/bundle/parall-claude-agent.js +124 -29
- package/bundle/parall-codex-agent.js +124 -29
- package/bundle/parall-daemon.js +368 -184
- package/dist/browser-pod.d.ts.map +1 -1
- package/dist/browser-pod.js +9 -14
- package/dist/browser-profile-viewer-control.d.ts +15 -0
- package/dist/browser-profile-viewer-control.d.ts.map +1 -0
- package/dist/browser-profile-viewer-control.js +27 -0
- package/dist/clip-runtime/bb-viewer-command-client.d.ts +19 -0
- package/dist/clip-runtime/bb-viewer-command-client.d.ts.map +1 -0
- package/dist/clip-runtime/bb-viewer-command-client.js +89 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +3 -1
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +2 -2
- package/dist/clip-runtime/browser-viewer-streamer.d.ts +7 -7
- package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -1
- package/dist/clip-runtime/browser-viewer-streamer.js +39 -47
- package/dist/clip-runtime/clip-provider.d.ts +3 -2
- package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
- package/dist/clip-runtime/clip-provider.js +13 -6
- package/dist/clip-runtime/hosted-viewer-command-queue.d.ts +43 -0
- package/dist/clip-runtime/hosted-viewer-command-queue.d.ts.map +1 -0
- package/dist/clip-runtime/hosted-viewer-command-queue.js +72 -0
- package/dist/clip-runtime/index.d.ts +1 -0
- package/dist/clip-runtime/index.d.ts.map +1 -1
- package/dist/clip-runtime/index.js +1 -0
- package/dist/clip-runtime/viewer-command-deadline.d.ts +14 -0
- package/dist/clip-runtime/viewer-command-deadline.d.ts.map +1 -0
- package/dist/clip-runtime/viewer-command-deadline.js +29 -0
- package/dist/clip-runtime/viewer-command-error.d.ts +18 -0
- package/dist/clip-runtime/viewer-command-error.d.ts.map +1 -0
- package/dist/clip-runtime/viewer-command-error.js +23 -0
- package/dist/supervisor.d.ts +0 -8
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +3 -32
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-pod.d.ts","sourceRoot":"","sources":["../src/browser-pod.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAIlC,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,2CAA2C,CAAC;AAInD,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"browser-pod.d.ts","sourceRoot":"","sources":["../src/browser-pod.ts"],"names":[],"mappings":"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"}
|
package/dist/browser-pod.js
CHANGED
|
@@ -57,6 +57,7 @@ import { awaitBrowserQuiescence } from './clip-runtime/browser-quiescence.js';
|
|
|
57
57
|
import { PUBLIC_PROXY_PROBE_FALLBACK_URL } from './clip-runtime/browser-readiness.js';
|
|
58
58
|
import { BrowserStateStore } from './clip-runtime/browser-state-store.js';
|
|
59
59
|
import { ClipProvider } from './clip-runtime/clip-provider.js';
|
|
60
|
+
import { HostedViewerCommandQueue } from './clip-runtime/hosted-viewer-command-queue.js';
|
|
60
61
|
import { ClipProcessManager } from './clip-runtime/process-manager.js';
|
|
61
62
|
/**
|
|
62
63
|
* Platform assignment token prefix (SSOT: server/pkg/platformauth/token.go). A
|
|
@@ -701,20 +702,14 @@ export async function preparePodRuntime(config, env, log) {
|
|
|
701
702
|
dataDir: path.join(config.stateDir, 'clip-data'),
|
|
702
703
|
browserProfileManager: browser,
|
|
703
704
|
});
|
|
704
|
-
// Hosted live-viewer handler (design §3.4):
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
//
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const onViewerCommand = (
|
|
712
|
-
const run = viewerChain
|
|
713
|
-
.catch(() => { }) // keep the chain moving past a prior failure
|
|
714
|
-
.then(() => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn));
|
|
715
|
-
viewerChain = run.catch(() => { });
|
|
716
|
-
return run;
|
|
717
|
-
};
|
|
705
|
+
// Hosted live-viewer handler (design §3.4): serialize mutations, but start the
|
|
706
|
+
// readiness budget at ProviderStream receipt/queue entry. A plain promise chain
|
|
707
|
+
// used to start bb-viewer's 30s watchdog only AFTER older work drained, so the
|
|
708
|
+
// API's absolute 45s deadline could win first with a generic timeout.
|
|
709
|
+
const viewerCommands = new HostedViewerCommandQueue((cmd, executionDeadlineUnixMs) => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn, {
|
|
710
|
+
deadlineUnixMs: executionDeadlineUnixMs,
|
|
711
|
+
}));
|
|
712
|
+
const onViewerCommand = viewerCommands.enqueue.bind(viewerCommands);
|
|
718
713
|
// ClipProvider passes authKey straight through as the ProviderStream bearer —
|
|
719
714
|
// it does not assume mck_ — so the pba_ assignment token authenticates the pod.
|
|
720
715
|
// providerName is cosmetic for routing (the hub keys the session by the token's
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GatewayLogger } from '@parall/agent-core';
|
|
2
|
+
import type { MachineBrowserProfileViewerData, ParallClient } from '@parall/sdk';
|
|
3
|
+
import type { BrowserProfilePool } from './clip-runtime/browser-profile-pool.js';
|
|
4
|
+
export interface BrowserProfileViewerControlDeps {
|
|
5
|
+
pool: BrowserProfilePool | null;
|
|
6
|
+
client: Pick<ParallClient, 'postBrowserProfileViewerResponse'>;
|
|
7
|
+
log: Pick<GatewayLogger, 'warn'>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Handle one viewer control command and reply via REST. Always answers the
|
|
11
|
+
* request/reply bridge exactly once ({result} on success,
|
|
12
|
+
* {error:{code?,message}} on failure) and never throws into the WS event loop.
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleBrowserProfileViewerCommand(deps: BrowserProfileViewerControlDeps, data: MachineBrowserProfileViewerData): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=browser-profile-viewer-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-profile-viewer-control.d.ts","sourceRoot":"","sources":["../src/browser-profile-viewer-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,+BAA+B,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAGjF,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,kCAAkC,CAAC,CAAC;IAC/D,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAsB,iCAAiC,CACrD,IAAI,EAAE,+BAA+B,EACrC,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { viewerCommandFailure } from './clip-runtime/viewer-command-error.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handle one viewer control command and reply via REST. Always answers the
|
|
4
|
+
* request/reply bridge exactly once ({result} on success,
|
|
5
|
+
* {error:{code?,message}} on failure) and never throws into the WS event loop.
|
|
6
|
+
*/
|
|
7
|
+
export async function handleBrowserProfileViewerCommand(deps, data) {
|
|
8
|
+
if (!deps.pool) {
|
|
9
|
+
await deps.client
|
|
10
|
+
.postBrowserProfileViewerResponse(data.request_id, {
|
|
11
|
+
error: { message: 'Browser profile runtime is disabled on this daemon' },
|
|
12
|
+
})
|
|
13
|
+
.catch((err) => deps.log.warn(`browser profile viewer negative ACK failed: ${String(err)}`));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const result = await deps.pool.handleViewerCommand(data.profile_id, data.session_id, data.command, data.input, data.turn);
|
|
18
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, { result });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const failure = viewerCommandFailure(err);
|
|
22
|
+
deps.log.warn(`browser profile viewer command failed (profile=${data.profile_id}, command=${data.command}): ${failure.message}`);
|
|
23
|
+
await deps.client
|
|
24
|
+
.postBrowserProfileViewerResponse(data.request_id, { error: failure })
|
|
25
|
+
.catch((replyErr) => deps.log.warn(`browser profile viewer error ACK failed: ${String(replyErr)}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Protocol 2 answer/switch wait for a bound first frame. Keep their caller
|
|
2
|
+
* budget above bb-viewer's 25s total readiness watchdog so its stable typed
|
|
3
|
+
* failure wins before the daemon aborts the HTTP request. */
|
|
4
|
+
export declare function bbViewerCommandTimeoutMs(method: string): number;
|
|
5
|
+
/** Shrink bb-viewer's command watchdog to the remaining hosted-pod budget. */
|
|
6
|
+
export declare function bbViewerCommandEffectiveTimeoutMs(method: string, deadlineUnixMs?: number, now?: number): number;
|
|
7
|
+
/**
|
|
8
|
+
* POST one command to bb-viewer's loopback `/command` adapter. Hosted callers
|
|
9
|
+
* supply the queue-derived absolute deadline; BYOC callers retain the method
|
|
10
|
+
* defaults. Stable upstream lifecycle codes survive unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sendBBViewerCommand(port: number, method: string, params: Record<string, unknown>, deadlineUnixMs?: number): Promise<Record<string, unknown>>;
|
|
13
|
+
/**
|
|
14
|
+
* Parse one bb-viewer command response without exposing a malformed response
|
|
15
|
+
* body. Exported as the protocol-adapter seam for deterministic regression
|
|
16
|
+
* tests; callers use {@link ViewerCommandError} to preserve stable codes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseBBViewerCommandResponse(status: number, text: string, method: string): Record<string, unknown>;
|
|
19
|
+
//# sourceMappingURL=bb-viewer-command-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bb-viewer-command-client.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/bb-viewer-command-client.ts"],"names":[],"mappings":"AAMA;;6DAE6D;AAC7D,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,8EAA8E;AAC9E,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,MAAM,EACvB,GAAG,SAAa,GACf,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAqBlC;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmCzB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { viewerReadinessDeadlineError } from './viewer-command-deadline.js';
|
|
2
|
+
import { ViewerCommandError } from './viewer-command-error.js';
|
|
3
|
+
const BB_VIEWER_COMMAND_TIMEOUT_MS = 15_000;
|
|
4
|
+
const BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS = 30_000;
|
|
5
|
+
/** Protocol 2 answer/switch wait for a bound first frame. Keep their caller
|
|
6
|
+
* budget above bb-viewer's 25s total readiness watchdog so its stable typed
|
|
7
|
+
* failure wins before the daemon aborts the HTTP request. */
|
|
8
|
+
export function bbViewerCommandTimeoutMs(method) {
|
|
9
|
+
return method === 'answer' || method === 'switch'
|
|
10
|
+
? BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS
|
|
11
|
+
: BB_VIEWER_COMMAND_TIMEOUT_MS;
|
|
12
|
+
}
|
|
13
|
+
/** Shrink bb-viewer's command watchdog to the remaining hosted-pod budget. */
|
|
14
|
+
export function bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs, now = Date.now()) {
|
|
15
|
+
const configured = bbViewerCommandTimeoutMs(method);
|
|
16
|
+
if (deadlineUnixMs === undefined)
|
|
17
|
+
return configured;
|
|
18
|
+
const remaining = deadlineUnixMs - now;
|
|
19
|
+
if (remaining <= 0) {
|
|
20
|
+
throw viewerReadinessDeadlineError(`before bb-viewer ${method}`);
|
|
21
|
+
}
|
|
22
|
+
return Math.max(1, Math.min(configured, remaining));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* POST one command to bb-viewer's loopback `/command` adapter. Hosted callers
|
|
26
|
+
* supply the queue-derived absolute deadline; BYOC callers retain the method
|
|
27
|
+
* defaults. Stable upstream lifecycle codes survive unchanged.
|
|
28
|
+
*/
|
|
29
|
+
export async function sendBBViewerCommand(port, method, params, deadlineUnixMs) {
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeoutMs = bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs);
|
|
32
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
33
|
+
try {
|
|
34
|
+
const resp = await fetch(`http://127.0.0.1:${port}/command`, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37
|
+
body: JSON.stringify({ method, params }),
|
|
38
|
+
signal: controller.signal,
|
|
39
|
+
});
|
|
40
|
+
const text = await resp.text();
|
|
41
|
+
return parseBBViewerCommandResponse(resp.status, text, method);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (controller.signal.aborted && (method === 'answer' || method === 'switch')) {
|
|
45
|
+
throw viewerReadinessDeadlineError(`while waiting for bb-viewer ${method}`);
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse one bb-viewer command response without exposing a malformed response
|
|
55
|
+
* body. Exported as the protocol-adapter seam for deterministic regression
|
|
56
|
+
* tests; callers use {@link ViewerCommandError} to preserve stable codes.
|
|
57
|
+
*/
|
|
58
|
+
export function parseBBViewerCommandResponse(status, text, method) {
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = text ? JSON.parse(text) : {};
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned invalid JSON`);
|
|
65
|
+
}
|
|
66
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
67
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid response`);
|
|
68
|
+
}
|
|
69
|
+
const envelope = parsed;
|
|
70
|
+
if (envelope.error) {
|
|
71
|
+
const code = typeof envelope.error.code === 'string' && envelope.error.code.trim()
|
|
72
|
+
? envelope.error.code.trim()
|
|
73
|
+
: undefined;
|
|
74
|
+
const message = typeof envelope.error.message === 'string' && envelope.error.message.trim()
|
|
75
|
+
? envelope.error.message
|
|
76
|
+
: `bb-viewer ${method} failed`;
|
|
77
|
+
throw new ViewerCommandError(message, code);
|
|
78
|
+
}
|
|
79
|
+
if (status < 200 || status >= 300) {
|
|
80
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned ${status}`);
|
|
81
|
+
}
|
|
82
|
+
if (envelope.result === undefined || envelope.result === null) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
if (typeof envelope.result !== 'object' || Array.isArray(envelope.result)) {
|
|
86
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid result`);
|
|
87
|
+
}
|
|
88
|
+
return envelope.result;
|
|
89
|
+
}
|
|
@@ -78,7 +78,9 @@ export declare class BrowserProfileManager {
|
|
|
78
78
|
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
79
79
|
* subprocesses and viewer tab navigation.
|
|
80
80
|
*/
|
|
81
|
-
handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig
|
|
81
|
+
handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig, execution?: {
|
|
82
|
+
deadlineUnixMs?: number;
|
|
83
|
+
}): Promise<Record<string, unknown>>;
|
|
82
84
|
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
83
85
|
private cdpEndpoint;
|
|
84
86
|
private lastCdpEndpoint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -271,8 +271,8 @@ export class BrowserProfileManager {
|
|
|
271
271
|
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
272
272
|
* subprocesses and viewer tab navigation.
|
|
273
273
|
*/
|
|
274
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
275
|
-
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
|
|
274
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
275
|
+
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn, execution);
|
|
276
276
|
}
|
|
277
277
|
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
278
278
|
async cdpEndpoint() {
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { BrowserTargetRegistry } from './browser-target-registry.js';
|
|
2
|
+
export { bbViewerCommandEffectiveTimeoutMs, bbViewerCommandTimeoutMs, parseBBViewerCommandResponse, } from './bb-viewer-command-client.js';
|
|
2
3
|
/** Optional managed TURN relay config carried on a `stream.start` command. */
|
|
3
4
|
export interface ViewerTurnConfig {
|
|
4
5
|
url: string;
|
|
5
6
|
username?: string;
|
|
6
7
|
credential?: string;
|
|
7
8
|
}
|
|
9
|
+
export interface ViewerCommandExecutionBudget {
|
|
10
|
+
/** Absolute pod execution deadline, already capped to leave the reply reserve. */
|
|
11
|
+
deadlineUnixMs?: number;
|
|
12
|
+
}
|
|
8
13
|
/**
|
|
9
14
|
* The slice of BrowserProfileManager the viewer streamer depends on: the
|
|
10
15
|
* account-scoped bb-browser /command path and the daemon lifecycle helpers.
|
|
@@ -63,7 +68,7 @@ export declare class BrowserViewerStreamer {
|
|
|
63
68
|
* partial result. `data.input` / `data.turn` / `data.session_id` come from
|
|
64
69
|
* MachineBrowserProfileViewerData.
|
|
65
70
|
*/
|
|
66
|
-
handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig): Promise<Record<string, unknown>>;
|
|
71
|
+
handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig, execution?: ViewerCommandExecutionBudget): Promise<Record<string, unknown>>;
|
|
67
72
|
/**
|
|
68
73
|
* kick — server-side per-viewer disconnect (design §3.6 PR7). Terminates ONE
|
|
69
74
|
* viewer's WebRTC session: stop its bb-viewer streamer (killing the process
|
|
@@ -128,6 +133,7 @@ export declare class BrowserViewerStreamer {
|
|
|
128
133
|
* (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
|
|
129
134
|
*/
|
|
130
135
|
private bringTargetToFront;
|
|
136
|
+
private assertViewerExecutionDeadline;
|
|
131
137
|
/**
|
|
132
138
|
* Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
|
|
133
139
|
* close) to bb-browser scoped to the profile's account. This lets the human
|
|
@@ -199,12 +205,6 @@ export declare class BrowserViewerStreamer {
|
|
|
199
205
|
* shutdown() abandons the timer — acceptable, the TERM was still sent.
|
|
200
206
|
*/
|
|
201
207
|
private killChild;
|
|
202
|
-
/**
|
|
203
|
-
* POST a command to the profile's bb-viewer `/command` endpoint. Parses the
|
|
204
|
-
* `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
|
|
205
|
-
* the inner result, throwing the error message on failure.
|
|
206
|
-
*/
|
|
207
|
-
private streamerCommand;
|
|
208
208
|
/**
|
|
209
209
|
* Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
|
|
210
210
|
* clears any kick marker for the profile: a (re)kill establishes a clean
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-viewer-streamer.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-viewer-streamer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser-viewer-streamer.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-viewer-streamer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { sendBBViewerCommand } from './bb-viewer-command-client.js';
|
|
2
3
|
import { findFreePort, formatErrorForLog, sleep } from './subprocess.js';
|
|
4
|
+
import { viewerReadinessDeadlineError } from './viewer-command-deadline.js';
|
|
5
|
+
export { bbViewerCommandEffectiveTimeoutMs, bbViewerCommandTimeoutMs, parseBBViewerCommandResponse, } from './bb-viewer-command-client.js';
|
|
3
6
|
const BB_VIEWER_HEALTH_TIMEOUT_MS = 10_000;
|
|
4
|
-
const BB_VIEWER_COMMAND_TIMEOUT_MS = 15_000;
|
|
5
7
|
// A streamer whose session never received a stream.answer is abandoned: the
|
|
6
8
|
// client vanished mid-handshake (hard tab close before its session id arrived)
|
|
7
9
|
// and can never send a scoped stream.close. Reap it so the bb-viewer process +
|
|
@@ -58,7 +60,7 @@ export class BrowserViewerStreamer {
|
|
|
58
60
|
* partial result. `data.input` / `data.turn` / `data.session_id` come from
|
|
59
61
|
* MachineBrowserProfileViewerData.
|
|
60
62
|
*/
|
|
61
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
63
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
62
64
|
if (!profileId)
|
|
63
65
|
throw new Error('browser profile id is required');
|
|
64
66
|
// Reject a kicked viewer's straggler commands (design §3.6 PR7). After a kick
|
|
@@ -75,11 +77,11 @@ export class BrowserViewerStreamer {
|
|
|
75
77
|
case 'stream.start':
|
|
76
78
|
return this.viewerStreamStart(profileId, sessionId, turn);
|
|
77
79
|
case 'stream.answer':
|
|
78
|
-
return this.viewerStreamAnswer(profileId, sessionId, input);
|
|
80
|
+
return this.viewerStreamAnswer(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
79
81
|
case 'stream.close':
|
|
80
82
|
return this.viewerStreamClose(profileId, sessionId, input);
|
|
81
83
|
case 'stream.switch':
|
|
82
|
-
return this.viewerStreamSwitch(profileId, sessionId, input);
|
|
84
|
+
return this.viewerStreamSwitch(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
83
85
|
case 'kick':
|
|
84
86
|
return this.viewerKick(profileId, sessionId, input);
|
|
85
87
|
case 'close':
|
|
@@ -145,7 +147,7 @@ export class BrowserViewerStreamer {
|
|
|
145
147
|
await this.bringTargetToFront(cdpUrl);
|
|
146
148
|
const streamer = await this.spawnStreamer(profileId, sessionId, turn);
|
|
147
149
|
try {
|
|
148
|
-
const result = await
|
|
150
|
+
const result = await sendBBViewerCommand(streamer.port, 'connect', { cdpUrl });
|
|
149
151
|
// Tell the client the EXACT tab being streamed so it targets nav commands
|
|
150
152
|
// (open/back/forward/reload) at the same tab — no client-side guessing.
|
|
151
153
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
@@ -173,18 +175,24 @@ export class BrowserViewerStreamer {
|
|
|
173
175
|
* Rejects if the streamer's sessionId no longer matches (a stale viewer
|
|
174
176
|
* session from a superseded stream.start).
|
|
175
177
|
*/
|
|
176
|
-
async viewerStreamAnswer(profileId, sessionId, input) {
|
|
178
|
+
async viewerStreamAnswer(profileId, sessionId, input, deadlineUnixMs) {
|
|
177
179
|
const streamer = this.streamers.get(profileId);
|
|
178
180
|
if (!streamer)
|
|
179
181
|
throw new Error('no active viewer streamer for profile');
|
|
180
182
|
if (streamer.sessionId !== sessionId)
|
|
181
183
|
throw new Error('stale viewer session');
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
184
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'before bb-viewer answer');
|
|
185
|
+
// Mark BEFORE the (up to 30s) bb-viewer readiness await: an answer accepted
|
|
186
|
+
// near the reap deadline must not be torn down mid-processing by the timer.
|
|
187
|
+
// A failed answer leaves the streamer un-reaped, which is fine — the client
|
|
188
|
+
// surfaces the typed error and the next stream.start replaces it anyway.
|
|
186
189
|
streamer.answered = true;
|
|
187
|
-
|
|
190
|
+
const result = await sendBBViewerCommand(streamer.port, 'answer', input ?? {}, deadlineUnixMs);
|
|
191
|
+
// The fetch promise can settle in the same event-loop turn as its abort
|
|
192
|
+
// timer. Re-check the absolute boundary before acknowledging readiness so a
|
|
193
|
+
// late microtask can never turn an expired command into a false success.
|
|
194
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'after bb-viewer answer');
|
|
195
|
+
return result;
|
|
188
196
|
}
|
|
189
197
|
/**
|
|
190
198
|
* stream.close — best-effort stop the streamer, then kill + remove it. A
|
|
@@ -205,7 +213,7 @@ export class BrowserViewerStreamer {
|
|
|
205
213
|
return { ok: true, stale: true };
|
|
206
214
|
}
|
|
207
215
|
try {
|
|
208
|
-
await
|
|
216
|
+
await sendBBViewerCommand(streamer.port, 'stop', {});
|
|
209
217
|
}
|
|
210
218
|
catch (err) {
|
|
211
219
|
this.host.log.warn(`[bb-viewer] stop command failed for ${profileId} (killing anyway): ${formatErrorForLog(err)}`);
|
|
@@ -223,7 +231,7 @@ export class BrowserViewerStreamer {
|
|
|
223
231
|
* the stream.start response), so a session-less switch is by definition not
|
|
224
232
|
* the current viewer.
|
|
225
233
|
*/
|
|
226
|
-
async viewerStreamSwitch(profileId, sessionId, input) {
|
|
234
|
+
async viewerStreamSwitch(profileId, sessionId, input, deadlineUnixMs) {
|
|
227
235
|
const streamer = this.streamers.get(profileId);
|
|
228
236
|
if (!streamer)
|
|
229
237
|
throw new Error('no active viewer streamer for profile');
|
|
@@ -233,10 +241,14 @@ export class BrowserViewerStreamer {
|
|
|
233
241
|
if (tab === undefined || tab === null || tab === '') {
|
|
234
242
|
throw new Error('stream.switch requires input.tab');
|
|
235
243
|
}
|
|
244
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'before resolving the switch target');
|
|
236
245
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId, tab), `viewer stream.switch ${profileId}`);
|
|
246
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'after resolving the switch target');
|
|
237
247
|
// Foreground the new tab so Chrome composites it (see viewerStreamStart).
|
|
238
|
-
await this.bringTargetToFront(cdpUrl);
|
|
239
|
-
|
|
248
|
+
await this.bringTargetToFront(cdpUrl, deadlineUnixMs);
|
|
249
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'before bb-viewer switch');
|
|
250
|
+
const result = await sendBBViewerCommand(streamer.port, 'switch', { cdpUrl }, deadlineUnixMs);
|
|
251
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'after bb-viewer switch');
|
|
240
252
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
241
253
|
return { ...result, streamed_tab_id: streamer.streamedTabId };
|
|
242
254
|
}
|
|
@@ -267,7 +279,7 @@ export class BrowserViewerStreamer {
|
|
|
267
279
|
}
|
|
268
280
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId), `viewer close re-switch ${profileId}`);
|
|
269
281
|
await this.bringTargetToFront(cdpUrl);
|
|
270
|
-
const sw = await
|
|
282
|
+
const sw = await sendBBViewerCommand(streamer.port, 'switch', { cdpUrl });
|
|
271
283
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
272
284
|
return { ...result, ...sw, streamed_tab_id: streamer.streamedTabId };
|
|
273
285
|
}
|
|
@@ -279,15 +291,19 @@ export class BrowserViewerStreamer {
|
|
|
279
291
|
* must not block the stream. host:port + targetId are parsed from the cdpUrl
|
|
280
292
|
* (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
|
|
281
293
|
*/
|
|
282
|
-
async bringTargetToFront(cdpUrl) {
|
|
294
|
+
async bringTargetToFront(cdpUrl, deadlineUnixMs) {
|
|
283
295
|
const m = /^ws:\/\/([^/]+)\/devtools\/page\/(.+)$/.exec(cdpUrl);
|
|
284
296
|
if (!m)
|
|
285
297
|
return;
|
|
286
298
|
const [, hostPort, targetId] = m;
|
|
299
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, 'before foregrounding the switch target');
|
|
300
|
+
const timeoutMs = deadlineUnixMs === undefined
|
|
301
|
+
? 5_000
|
|
302
|
+
: Math.max(1, Math.min(5_000, deadlineUnixMs - Date.now()));
|
|
287
303
|
try {
|
|
288
304
|
const resp = await fetch(`http://${hostPort}/json/activate/${targetId}`, {
|
|
289
305
|
method: 'GET',
|
|
290
|
-
signal: AbortSignal.timeout(
|
|
306
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
291
307
|
});
|
|
292
308
|
if (!resp.ok) {
|
|
293
309
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} returned ${resp.status} (continuing)`);
|
|
@@ -297,6 +313,11 @@ export class BrowserViewerStreamer {
|
|
|
297
313
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} failed (continuing): ${formatErrorForLog(err)}`);
|
|
298
314
|
}
|
|
299
315
|
}
|
|
316
|
+
assertViewerExecutionDeadline(deadlineUnixMs, phase) {
|
|
317
|
+
if (deadlineUnixMs !== undefined && Date.now() >= deadlineUnixMs) {
|
|
318
|
+
throw viewerReadinessDeadlineError(phase);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
300
321
|
/**
|
|
301
322
|
* Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
|
|
302
323
|
* close) to bb-browser scoped to the profile's account. This lets the human
|
|
@@ -537,35 +558,6 @@ export class BrowserViewerStreamer {
|
|
|
537
558
|
killTimer.unref?.();
|
|
538
559
|
child.once('exit', () => clearTimeout(killTimer));
|
|
539
560
|
}
|
|
540
|
-
/**
|
|
541
|
-
* POST a command to the profile's bb-viewer `/command` endpoint. Parses the
|
|
542
|
-
* `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
|
|
543
|
-
* the inner result, throwing the error message on failure.
|
|
544
|
-
*/
|
|
545
|
-
async streamerCommand(streamer, method, params) {
|
|
546
|
-
const controller = new AbortController();
|
|
547
|
-
const timer = setTimeout(() => controller.abort(), BB_VIEWER_COMMAND_TIMEOUT_MS);
|
|
548
|
-
try {
|
|
549
|
-
const resp = await fetch(`http://127.0.0.1:${streamer.port}/command`, {
|
|
550
|
-
method: 'POST',
|
|
551
|
-
headers: { 'Content-Type': 'application/json' },
|
|
552
|
-
body: JSON.stringify({ method, params }),
|
|
553
|
-
signal: controller.signal,
|
|
554
|
-
});
|
|
555
|
-
const text = await resp.text();
|
|
556
|
-
const parsed = (text ? JSON.parse(text) : {});
|
|
557
|
-
if (parsed.error) {
|
|
558
|
-
throw new Error(parsed.error.message || `bb-viewer ${method} failed`);
|
|
559
|
-
}
|
|
560
|
-
if (!resp.ok) {
|
|
561
|
-
throw new Error(`bb-viewer ${method} returned ${resp.status}: ${text}`);
|
|
562
|
-
}
|
|
563
|
-
return parsed.result ?? {};
|
|
564
|
-
}
|
|
565
|
-
finally {
|
|
566
|
-
clearTimeout(timer);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
561
|
/**
|
|
570
562
|
* Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
|
|
571
563
|
* clears any kick marker for the profile: a (re)kill establishes a clean
|
|
@@ -58,6 +58,7 @@ export interface ClipProviderOptions {
|
|
|
58
58
|
username?: string;
|
|
59
59
|
credential?: string;
|
|
60
60
|
};
|
|
61
|
+
dispatchDeadlineUnixMs?: number;
|
|
61
62
|
}) => Promise<Record<string, unknown>>;
|
|
62
63
|
}
|
|
63
64
|
export declare class ClipProvider {
|
|
@@ -125,8 +126,8 @@ export declare class ClipProvider {
|
|
|
125
126
|
/**
|
|
126
127
|
* Handle a hosted-browser live-viewer command relayed by clip-service over the
|
|
127
128
|
* stream and reply on the same stream (design §3.4). Always answers the
|
|
128
|
-
* request/reply bridge exactly once — `{result}` on success,
|
|
129
|
-
* on failure — and never throws into the message loop, mirroring the BYOC
|
|
129
|
+
* request/reply bridge exactly once — `{result}` on success,
|
|
130
|
+
* `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
|
|
130
131
|
* daemon's handleBrowserProfileViewer (which replies over HTTP instead).
|
|
131
132
|
*/
|
|
132
133
|
private handleViewerCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clip-provider.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"clip-provider.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
* flags=0x00 data, flags=0x02 end-of-stream (trailers)
|
|
15
15
|
*/
|
|
16
16
|
import * as http2 from 'node:http2';
|
|
17
|
-
import { ClipCommandError } from './process.js';
|
|
18
17
|
import { isBrowserDependencyName } from './browser-dependency.js';
|
|
18
|
+
import { ClipCommandError } from './process.js';
|
|
19
|
+
import { isViewerReadinessCommand, parseViewerDispatchDeadlineUnixMs, viewerReadinessDeadlineError, } from './viewer-command-deadline.js';
|
|
20
|
+
import { viewerCommandFailure } from './viewer-command-error.js';
|
|
19
21
|
// ---------------------------------------------------------------------------
|
|
20
22
|
// Connect envelope helpers
|
|
21
23
|
// ---------------------------------------------------------------------------
|
|
@@ -463,8 +465,8 @@ export class ClipProvider {
|
|
|
463
465
|
/**
|
|
464
466
|
* Handle a hosted-browser live-viewer command relayed by clip-service over the
|
|
465
467
|
* stream and reply on the same stream (design §3.4). Always answers the
|
|
466
|
-
* request/reply bridge exactly once — `{result}` on success,
|
|
467
|
-
* on failure — and never throws into the message loop, mirroring the BYOC
|
|
468
|
+
* request/reply bridge exactly once — `{result}` on success,
|
|
469
|
+
* `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
|
|
468
470
|
* daemon's handleBrowserProfileViewer (which replies over HTTP instead).
|
|
469
471
|
*/
|
|
470
472
|
async handleViewerCommand(cmd) {
|
|
@@ -478,6 +480,10 @@ export class ClipProvider {
|
|
|
478
480
|
return;
|
|
479
481
|
}
|
|
480
482
|
try {
|
|
483
|
+
const dispatchDeadlineUnixMs = parseViewerDispatchDeadlineUnixMs(cmd.dispatchDeadlineUnixMs);
|
|
484
|
+
if (isViewerReadinessCommand(cmd.command) && dispatchDeadlineUnixMs === undefined) {
|
|
485
|
+
throw viewerReadinessDeadlineError('because the ProviderStream command has no authenticated deadline');
|
|
486
|
+
}
|
|
481
487
|
let input;
|
|
482
488
|
if (cmd.input) {
|
|
483
489
|
const decoded = Buffer.from(cmd.input, 'base64').toString('utf-8');
|
|
@@ -494,13 +500,14 @@ export class ClipProvider {
|
|
|
494
500
|
command: cmd.command,
|
|
495
501
|
input,
|
|
496
502
|
turn: cmd.turn,
|
|
503
|
+
dispatchDeadlineUnixMs,
|
|
497
504
|
});
|
|
498
505
|
await this.sendViewerReply(requestId, { result });
|
|
499
506
|
}
|
|
500
507
|
catch (err) {
|
|
501
|
-
const
|
|
502
|
-
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
|
|
503
|
-
await this.sendViewerReply(requestId, { error:
|
|
508
|
+
const failure = viewerCommandFailure(err);
|
|
509
|
+
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${failure.message}`);
|
|
510
|
+
await this.sendViewerReply(requestId, { error: failure }).catch(() => { });
|
|
504
511
|
}
|
|
505
512
|
}
|
|
506
513
|
/** Send a hosted-viewer reply (base64-encoded JSON body) on the stream. */
|