@modelprofile.com/browser-runtime 5.1.0 → 5.2.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/changelog.md +14 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.runtime.d.ts +26 -4
- package/dist_ts/classes.runtime.js +172 -33
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/interfaces.d.ts +14 -1
- package/package.json +4 -5
- package/readme.md +10 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.runtime.ts +180 -33
- package/ts/index.ts +2 -0
- package/ts/interfaces.ts +17 -0
- package/readme.hints.md +0 -60
package/dist_ts/index.d.ts
CHANGED
|
@@ -8,4 +8,4 @@ export { BrowserRuntimeError } from './errors.js';
|
|
|
8
8
|
export { createBrowserRuntimeMcpHttpHandler } from './mcp.js';
|
|
9
9
|
export type { TAcquireBrowserRuntimeLeaseOptions, IBrowserRuntimeLeaseRenewalOptions, } from './classes.runtime.js';
|
|
10
10
|
export type { TBrowserRuntimeErrorCode } from './errors.js';
|
|
11
|
-
export type { IAttachTrustedFramedPeerOptions, IApplyBrowserAttachmentBindingRequest, IBrowserAttachmentBinding, IBrowserActionStateResult, IBrowserArtifactMetadata, IBrowserArtifactStoreOptions, IBrowserMcpAuthenticatedBinding, TBrowserCapabilityAuthorizationRequest, TBrowserCapabilityDescriptor, IBrowserClickAction, IBrowserEgressConnectionOptions, IBrowserEgressProxyOptions, IBrowserEgressProxyStats, IBrowserFlexCapabilityBinding, IBrowserFlexSessionId, IBrowserCodexSessionId, IBrowserFillAction, IBrowserNavigateAction, IBrowserObservationResult, IBrowserOpenCodeSessionId, IBrowserPressAction, TBrowserRuntimeAuditEvent, TBrowserRuntimeLeaseAuthority, TBrowserRuntimeOperationClassification, TBrowserRuntimeOperationIdentity, IBrowserRuntimeFlexToolProviderOptions, IBrowserRuntimeFrameSubscription, IBrowserRuntimeFramedClientOptions, IBrowserRuntimeMcpOptions, IBrowserRuntimeOperationOptions, IBrowserRuntimeOptions, IBrowserRuntimeScreencastOptions, IBrowserResourceKey, IBrowserResourceRegistration, IBrowserRuntimeState, IBrowserRuntimeViewportResult, IBrowserRuntimeTabState, IBrowserScreenshotAction, IBrowserScreenshotResult, IBrowserSnapshotAction, ICreateBrowserResourceRequest, TIssueBrowserCapabilityRequest, TIssuedBrowserCapability, TQualifiedBrowserSessionId, IRegisterBrowserResourceRequest, IResolvedBrowserFlexCapability, TBrowserActorRole, TBrowserAgentAction, TBrowserAgentActionResult, TBrowserCapabilitySource, TBrowserDnsResolver, TBrowserEgressConnector, TBrowserHarnessId, TBrowserRuntimeEvent, } from './interfaces.js';
|
|
11
|
+
export type { IAttachTrustedFramedPeerOptions, IApplyBrowserAttachmentBindingRequest, IBrowserAttachmentBinding, IBrowserActionStateResult, IBrowserArtifactMetadata, IBrowserArtifactStoreOptions, IBrowserMcpAuthenticatedBinding, TBrowserCapabilityAuthorizationRequest, TBrowserCapabilityDescriptor, IBrowserClickAction, IBrowserEgressConnectionOptions, IBrowserEgressProxyOptions, IBrowserEgressProxyStats, IBrowserFlexCapabilityBinding, IBrowserFlexSessionId, IBrowserCodexSessionId, IBrowserFillAction, IBrowserNavigateAction, IBrowserObservationResult, IBrowserOpenCodeSessionId, IBrowserPressAction, TBrowserRuntimeAuditEvent, TBrowserRuntimeLeaseAuthority, TBrowserRuntimeOperationClassification, TBrowserRuntimeOperationIdentity, IBrowserRuntimeFlexToolProviderOptions, IBrowserRuntimeFrameSubscription, IBrowserRuntimeFramedClientOptions, IBrowserRuntimeMcpOptions, IBrowserRuntimeOperationOptions, IBrowserRuntimeOptions, IBrowserRuntimeScreencastOptions, IBrowserRuntimeEventSubscriptionOptions, TBrowserRuntimeVideoOffer, IBrowserResourceKey, IBrowserResourceRegistration, IBrowserRuntimeState, IBrowserRuntimeViewportResult, IBrowserRuntimeTabState, IBrowserScreenshotAction, IBrowserScreenshotResult, IBrowserSnapshotAction, ICreateBrowserResourceRequest, TIssueBrowserCapabilityRequest, TIssuedBrowserCapability, TQualifiedBrowserSessionId, IRegisterBrowserResourceRequest, IResolvedBrowserFlexCapability, TBrowserActorRole, TBrowserAgentAction, TBrowserAgentActionResult, TBrowserCapabilitySource, TBrowserDnsResolver, TBrowserEgressConnector, TBrowserHarnessId, TBrowserRuntimeEvent, } from './interfaces.js';
|
package/dist_ts/interfaces.d.ts
CHANGED
|
@@ -101,7 +101,7 @@ export type TBrowserRuntimeOperationIdentity = TReadonlyBrowserCapabilityAuthori
|
|
|
101
101
|
readonly classification: TBrowserRuntimeOperationClassification;
|
|
102
102
|
readonly startedAt: number;
|
|
103
103
|
};
|
|
104
|
-
export type TBrowserRuntimeOperationClassification = 'raw-input' | 'frame-stream' | 'viewport' | 'navigation' | 'tab' | 'agent-action';
|
|
104
|
+
export type TBrowserRuntimeOperationClassification = 'raw-input' | 'frame-stream' | 'video-peer' | 'viewport' | 'navigation' | 'tab' | 'agent-action';
|
|
105
105
|
/** The viewport accepted by the shared resource, which may be smaller than one viewer's request. */
|
|
106
106
|
export interface IBrowserRuntimeViewportResult {
|
|
107
107
|
viewport: plugins.smartpuppeteer.ILiveBrowserViewport;
|
|
@@ -126,6 +126,11 @@ export interface ILiveBrowserSessionLike {
|
|
|
126
126
|
getState(): plugins.smartpuppeteer.ILiveBrowserState;
|
|
127
127
|
getProcessState(): plugins.smartpuppeteer.ILiveBrowserProcessState;
|
|
128
128
|
onEvent(listener: plugins.smartpuppeteer.TLiveBrowserEventListener): () => void;
|
|
129
|
+
setFrameCaptureEnabled(enabled: boolean, options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<void>;
|
|
130
|
+
openVideoPeer(peerId: string, options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<plugins.smartpuppeteer.ILiveVideoOffer>;
|
|
131
|
+
answerVideoPeer(peerId: string, negotiationId: string, description: plugins.smartpuppeteer.ILiveVideoDescription, options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<void>;
|
|
132
|
+
closeVideoPeer(peerId: string): Promise<void>;
|
|
133
|
+
getVideoStatistics(peerId: string): Promise<plugins.smartpuppeteer.ILiveVideoStatistics>;
|
|
129
134
|
refreshScreencast(options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<plugins.smartpuppeteer.ILiveBrowserFrameIdentity>;
|
|
130
135
|
acknowledgeFrame(request: plugins.smartpuppeteer.ILiveBrowserFrameAcknowledgementRequest): Promise<plugins.smartpuppeteer.ILiveBrowserFrameAcknowledgement>;
|
|
131
136
|
createTab(options?: plugins.smartpuppeteer.ILiveBrowserCreateTabOptions, operationOptions?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<plugins.smartpuppeteer.ILiveBrowserTabState>;
|
|
@@ -193,6 +198,8 @@ export interface IBrowserRuntimeOptions {
|
|
|
193
198
|
maxOutstandingFrames?: number;
|
|
194
199
|
frameAcknowledgementTimeoutMs?: number;
|
|
195
200
|
screencast?: IBrowserRuntimeScreencastOptions;
|
|
201
|
+
/** Trusted host configuration; defaults to direct ICE and automatic GPU support. */
|
|
202
|
+
video?: plugins.smartpuppeteer.ILiveVideoOptions;
|
|
196
203
|
egress?: Omit<IBrowserEgressProxyOptions, 'projectId' | 'browserResourceId'>;
|
|
197
204
|
artifacts?: Omit<IBrowserArtifactStoreOptions, 'rootDirectory'>;
|
|
198
205
|
}
|
|
@@ -206,7 +213,13 @@ export interface IBrowserRuntimeTabState {
|
|
|
206
213
|
appliedViewportRevision: number;
|
|
207
214
|
streaming: boolean;
|
|
208
215
|
}
|
|
216
|
+
export interface IBrowserRuntimeEventSubscriptionOptions {
|
|
217
|
+
/** Set false for native video viewers. Defaults to true for image-frame consumers. */
|
|
218
|
+
includeFrames?: boolean;
|
|
219
|
+
}
|
|
220
|
+
export type TBrowserRuntimeVideoOffer = Omit<plugins.smartpuppeteer.ILiveVideoOffer, 'peerId'>;
|
|
209
221
|
export interface IBrowserRuntimeState {
|
|
222
|
+
videoAcceleration?: plugins.smartpuppeteer.ILiveVideoAcceleration;
|
|
210
223
|
status: 'stopped' | 'starting' | 'running' | 'stopping';
|
|
211
224
|
activeTabId: string | null;
|
|
212
225
|
viewportRevision: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/browser-runtime",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Parent-owned, resource-centric Chromium runtime with revisioned attachment fencing, authenticated human and agent control, fail-closed egress, bounded artifacts, and Flex/MCP adapters.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"@push.rocks/smartagent": "^6.0.0",
|
|
31
31
|
"@push.rocks/smartipc": "^2.6.0",
|
|
32
32
|
"@push.rocks/smartmcp": "^0.3.0",
|
|
33
|
-
"@push.rocks/smartpuppeteer": "^2.
|
|
33
|
+
"@push.rocks/smartpuppeteer": "^2.8.1",
|
|
34
34
|
"ipaddr.js": "^2.5.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@git.zone/tsbuild": "4.4.
|
|
37
|
+
"@git.zone/tsbuild": "4.4.3",
|
|
38
38
|
"@git.zone/tsrun": "2.0.6",
|
|
39
|
-
"@git.zone/tstest": "
|
|
39
|
+
"@git.zone/tstest": "6.0.0",
|
|
40
40
|
"@types/node": "26.4.1"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"dist_ts/**/*",
|
|
45
45
|
".smartconfig.json",
|
|
46
46
|
"readme.md",
|
|
47
|
-
"readme.hints.md",
|
|
48
47
|
"changelog.md",
|
|
49
48
|
"license.md"
|
|
50
49
|
],
|
package/readme.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Parent-owned, resource-centric Chromium runtime with revisioned attachment fencing, authenticated human and agent control, fail-closed egress, bounded artifacts, and Flex/MCP adapters.
|
|
4
4
|
|
|
5
|
+
## Native video viewers
|
|
6
|
+
|
|
7
|
+
Human leases expose `openVideoPeer(options?)`, `answerVideoPeer(negotiationId, description, options?)`, `closeVideoPeer(options?)`, and `getVideoStatistics(options?)`. Each operation runs through the resource's bounded, authorized and audited operation queue. Peer IDs are private to the lease; offers expose the negotiation ID and exact tab/generation/viewport source identity. Agent leases cannot open media peers. Releasing or revoking a human lease closes only its peer, preserving other viewers and agent work. Failed peer cleanup remains owned for a subsequent release attempt.
|
|
8
|
+
|
|
9
|
+
Set `video` on `BrowserRuntime` for trusted host limits or ICE configuration. Defaults use direct connections with no external STUN/TURN service and automatic GPU support. The private capture extension is loaded without weakening ordinary page proxy, DNS, permission or WebRTC confinement. `state.videoAcceleration` and peer statistics report actual browser acceleration/encoder support.
|
|
10
|
+
|
|
11
|
+
Native viewers should call `lease.subscribeEvents(listener, { includeFrames: false })`. They receive state and errors without generating JPEG traffic. Existing image consumers retain the default `includeFrames: true`; capture starts for the first such subscriber and stops after the last closes. Agent snapshots remain independent of continuous capture. Image subscription cleanup is serialized, coalesced and retryable; a failed disable is retained until cleanup succeeds or its exact browser incarnation terminates.
|
|
12
|
+
|
|
5
13
|
## Issue Reporting and Security
|
|
6
14
|
|
|
7
15
|
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
@@ -132,7 +140,7 @@ All participants enter one strict resource FIFO. `maxQueuedOperationsPerLease` d
|
|
|
132
140
|
|
|
133
141
|
`setViewport()` stores the viewer's preferred width, height and device scale factor and returns `IBrowserRuntimeViewportResult` with the effective `viewport` and `viewportRevision`. The effective values are the componentwise minima across active viewers that have supplied a preference. Unchanged effective sizes do not restart the stream. Removing a viewer recomputes the viewport in the resource FIFO. Held keys and buttons are tracked per participant; leaving releases only inputs that no other participant holds. Viewport and tab/navigation transitions clear held input before changing the target. Resource-owned departure cleanup is bounded and audited as `releaseParticipant`; it does not call the external `beforeOperation` gate after the participant has lost access.
|
|
134
142
|
|
|
135
|
-
`beforeOperation` is an optional awaited fail-closed gate. It receives the complete immutable authority, operation/capability/lease IDs, action, classification, start time, and an `AbortSignal` after dequeue but before the browser side effect starts. Classifications are `raw-input`, `frame-stream`, `viewport`, `navigation`, `tab`, and `agent-action`. Hosts that require durable attempt-before-side-effect auditing should persist the attempt there. Rejection denies the operation. The default `beforeOperationTimeoutMs` is 10,000 milliseconds and accepts values from 100 through 120,000; timeout fails with `TIMEOUT`. The terminal `audit` callback remains a best-effort `completed`/`failed` notification correlated by the same operation ID and classification and runs after bounded operation cleanup releases or fences the exact reservation.
|
|
143
|
+
`beforeOperation` is an optional awaited fail-closed gate. It receives the complete immutable authority, operation/capability/lease IDs, action, classification, start time, and an `AbortSignal` after dequeue but before the browser side effect starts. Classifications are `raw-input`, `frame-stream`, `video-peer`, `viewport`, `navigation`, `tab`, and `agent-action`. Hosts that require durable attempt-before-side-effect auditing should persist the attempt there. Rejection denies the operation. The default `beforeOperationTimeoutMs` is 10,000 milliseconds and accepts values from 100 through 120,000; timeout fails with `TIMEOUT`. The terminal `audit` callback remains a best-effort `completed`/`failed` notification correlated by the same operation ID and classification and runs after bounded operation cleanup releases or fences the exact reservation.
|
|
136
144
|
|
|
137
145
|
`lease.getAuthority()` returns an immutable process-local lease authority containing the runtime authority ID, exported `authorityGeneration`, incarnation generation, and complete capability binding. Every snapshot also binds the exact `capabilityId` and `leaseId`. `lease.isAuthorityCurrent(authority)` performs an exact synchronous revalidation suitable for a host-owned virtual stream. These values are not durable Controller state and do not replace attachment checks against the Controller database.
|
|
138
146
|
|
|
@@ -142,7 +150,7 @@ Each human participant may have one independent frame subscription with a bounde
|
|
|
142
150
|
|
|
143
151
|
An oversized frame is dropped, not fatal. `maxFrameBytes` defaults to 4 MiB and accepts values from 64 KiB through 16 MiB. A frame above the bound is acknowledged toward SmartPuppeteer in the background, never enters the application window, and is reported once on the subscription as `{ type: 'error', error: { code: 'FRAME_TOO_LARGE', fatal: false, tabId } }`. The sequence watermark advances past the dropped frame so later frames remain valid and continue to be delivered.
|
|
144
152
|
|
|
145
|
-
`screencast` tunes the Chromium
|
|
153
|
+
`screencast` tunes the Chromium image capture that starts when an image-frame subscriber joins. `quality` is the JPEG quality from 0 through 100 and defaults to 70. `maxWidth` and `maxHeight` accept values from 1 through 4,096 each, their product must not exceed 8,294,400 pixels (SmartPuppeteer's ceiling, so 3,840×2,160 is accepted and 4,096×4,096 is rejected with `INVALID_INPUT`), they default to 2,560 and 1,600, and they make Chromium scale captured frames to fit. `everyNthFrame` accepts values from 1 through 60 and defaults to 1. `firstFrameTimeoutMs` accepts values from 1,000 through 60,000 and is left to SmartPuppeteer's default when omitted. Runtime forwards these values together with `maxOutstandingFrames`; the defaults keep frames from device-pixel-ratio 2 viewers well below `maxFrameBytes`.
|
|
146
154
|
|
|
147
155
|
Human state and error events retain an optional diagnostic `message` bounded to 2,048 characters. A fatal producer error synchronously invalidates all current participants before their captured subscribers receive the error. Cleanup is coalesced for that exact incarnation and completes before affected operations settle.
|
|
148
156
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/browser-runtime',
|
|
6
|
-
version: '5.
|
|
6
|
+
version: '5.2.0',
|
|
7
7
|
description: 'Parent-owned, resource-centric Chromium runtime with revisioned attachment fencing, authenticated human and agent control, fail-closed egress, bounded artifacts, and Flex/MCP adapters.'
|
|
8
8
|
}
|
package/ts/classes.runtime.ts
CHANGED
|
@@ -29,6 +29,8 @@ import type {
|
|
|
29
29
|
IBrowserResourceRegistration,
|
|
30
30
|
IBrowserRuntimeState,
|
|
31
31
|
IBrowserRuntimeViewportResult,
|
|
32
|
+
IBrowserRuntimeEventSubscriptionOptions,
|
|
33
|
+
TBrowserRuntimeVideoOffer,
|
|
32
34
|
IBrowserScreenshotResult,
|
|
33
35
|
ICreateBrowserResourceRequest,
|
|
34
36
|
TQualifiedBrowserSessionId,
|
|
@@ -102,6 +104,7 @@ interface INormalizedRuntimeOptions {
|
|
|
102
104
|
maxOutstandingFrames: number;
|
|
103
105
|
frameAcknowledgementTimeoutMs: number;
|
|
104
106
|
screencast: INormalizedScreencastOptions;
|
|
107
|
+
video: Required<plugins.smartpuppeteer.ILiveVideoOptions>;
|
|
105
108
|
egress: NonNullable<IBrowserRuntimeOptions['egress']>;
|
|
106
109
|
ownership?: IBrowserRuntimeTestingOptions['ownership'];
|
|
107
110
|
beforeLeasePublication?: IBrowserRuntimeTestingOptions['beforeLeasePublication'];
|
|
@@ -138,6 +141,9 @@ interface ILeaseRecord {
|
|
|
138
141
|
released: boolean;
|
|
139
142
|
releasePromise?: Promise<void>;
|
|
140
143
|
preferredViewport?: plugins.smartpuppeteer.ILiveBrowserViewport;
|
|
144
|
+
videoPeerId: string;
|
|
145
|
+
videoPeerMayExist: boolean;
|
|
146
|
+
frameCaptureUsed: boolean;
|
|
141
147
|
heldKeys: Map<string, plugins.smartpuppeteer.ILiveBrowserKeyInput>;
|
|
142
148
|
heldButtons: Map<string, plugins.smartpuppeteer.ILiveBrowserMouseInput>;
|
|
143
149
|
}
|
|
@@ -175,7 +181,7 @@ interface IQueuedOperationRecord {
|
|
|
175
181
|
state: 'queued' | 'starting' | 'active' | 'settled';
|
|
176
182
|
onQueuedAbort?: () => void;
|
|
177
183
|
/** Resource-owned cleanup, admitted only for this exact released participant. */
|
|
178
|
-
participantCleanup?:
|
|
184
|
+
participantCleanup?: 'participant' | 'subscription' | false;
|
|
179
185
|
}
|
|
180
186
|
|
|
181
187
|
interface IOutstandingFrameRecord {
|
|
@@ -194,6 +200,10 @@ type TFrameAcknowledgementOutcome =
|
|
|
194
200
|
interface IFrameSubscriptionRecord {
|
|
195
201
|
lease: ILeaseRecord;
|
|
196
202
|
listener: (event: TBrowserRuntimeEvent) => void;
|
|
203
|
+
includeFrames: boolean;
|
|
204
|
+
session: ILiveBrowserSessionLike;
|
|
205
|
+
closePromise?: Promise<void>;
|
|
206
|
+
closeComplete?: boolean;
|
|
197
207
|
outstanding: Map<string, IOutstandingFrameRecord>;
|
|
198
208
|
highestSequence: number;
|
|
199
209
|
closed: boolean;
|
|
@@ -240,6 +250,7 @@ interface IResourceSlot {
|
|
|
240
250
|
operationQueue: IQueuedOperationRecord[];
|
|
241
251
|
operationSchedulerRunning: boolean;
|
|
242
252
|
frameSubscriptions: Map<string, IFrameSubscriptionRecord>;
|
|
253
|
+
frameCaptureEnabled?: boolean;
|
|
243
254
|
highestFrameSequence: number;
|
|
244
255
|
producerAcknowledgements: Set<Promise<TFrameAcknowledgementOutcome>>;
|
|
245
256
|
frameRefresh?: IFrameRefreshRecord;
|
|
@@ -558,6 +569,7 @@ export class BrowserRuntime {
|
|
|
558
569
|
10_000,
|
|
559
570
|
),
|
|
560
571
|
screencast,
|
|
572
|
+
video: plugins.smartpuppeteer.normalizeLiveVideoOptions(options.video ?? {}),
|
|
561
573
|
egress: options.egress ?? {},
|
|
562
574
|
ownership: testingOptions?.ownership,
|
|
563
575
|
beforeLeasePublication: testingOptions?.beforeLeasePublication,
|
|
@@ -1352,6 +1364,46 @@ export class BrowserRuntime {
|
|
|
1352
1364
|
});
|
|
1353
1365
|
}
|
|
1354
1366
|
|
|
1367
|
+
/** @internal */
|
|
1368
|
+
public async leaseOpenVideoPeer(lease: ILeaseRecord, options: IBrowserRuntimeOperationOptions = {}): Promise<TBrowserRuntimeVideoOffer> {
|
|
1369
|
+
this.requireHuman(lease);
|
|
1370
|
+
return this.runOperation(lease, 'openVideoPeer', 'video-peer', options, async (signal, session) => {
|
|
1371
|
+
lease.videoPeerMayExist = true;
|
|
1372
|
+
const { peerId: _peerId, ...offer } = await session.openVideoPeer(lease.videoPeerId, { signal });
|
|
1373
|
+
return offer;
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/** @internal */
|
|
1378
|
+
public async leaseAnswerVideoPeer(lease: ILeaseRecord, negotiationId: string,
|
|
1379
|
+
description: plugins.smartpuppeteer.ILiveVideoDescription, options: IBrowserRuntimeOperationOptions = {}): Promise<void> {
|
|
1380
|
+
this.requireHuman(lease);
|
|
1381
|
+
const id = validateBoundedString(negotiationId, 'negotiationId', 1, 128);
|
|
1382
|
+
if (id !== negotiationId) throw new BrowserRuntimeError('INVALID_INPUT');
|
|
1383
|
+
const record = validateExactKeys(description, ['type', 'sdp'], 'video answer');
|
|
1384
|
+
const answer = { type: record.type, sdp: record.sdp } as plugins.smartpuppeteer.ILiveVideoDescription;
|
|
1385
|
+
try { plugins.smartpuppeteer.validateLiveVideoDescription(answer, 'answer'); }
|
|
1386
|
+
catch { throw new BrowserRuntimeError('INVALID_INPUT'); }
|
|
1387
|
+
return this.runOperation(lease, 'answerVideoPeer', 'video-peer', options,
|
|
1388
|
+
async (signal, session) => session.answerVideoPeer(lease.videoPeerId, id, answer, { signal }));
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/** @internal */
|
|
1392
|
+
public async leaseCloseVideoPeer(lease: ILeaseRecord, options: IBrowserRuntimeOperationOptions = {}): Promise<void> {
|
|
1393
|
+
this.requireHuman(lease);
|
|
1394
|
+
return this.runOperation(lease, 'closeVideoPeer', 'video-peer', options, async (_signal, session) => {
|
|
1395
|
+
await session.closeVideoPeer(lease.videoPeerId);
|
|
1396
|
+
lease.videoPeerMayExist = false;
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/** @internal */
|
|
1401
|
+
public async leaseGetVideoStatistics(lease: ILeaseRecord, options: IBrowserRuntimeOperationOptions = {}): Promise<plugins.smartpuppeteer.ILiveVideoStatistics> {
|
|
1402
|
+
this.requireHuman(lease);
|
|
1403
|
+
return this.runOperation(lease, 'getVideoStatistics', 'video-peer', options,
|
|
1404
|
+
async (_signal, session) => session.getVideoStatistics(lease.videoPeerId));
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1355
1407
|
private validatePreferredViewport(
|
|
1356
1408
|
value: plugins.smartpuppeteer.ILiveBrowserViewport,
|
|
1357
1409
|
): plugins.smartpuppeteer.ILiveBrowserViewport {
|
|
@@ -1402,7 +1454,7 @@ export class BrowserRuntime {
|
|
|
1402
1454
|
this.requireValidLease(lease);
|
|
1403
1455
|
const slot = lease.slot;
|
|
1404
1456
|
const subscription = slot.frameSubscriptions.get(lease.leaseId);
|
|
1405
|
-
if (!subscription || subscription.closed || subscription.lease !== lease) {
|
|
1457
|
+
if (!subscription || subscription.closed || !subscription.includeFrames || subscription.lease !== lease) {
|
|
1406
1458
|
throw new BrowserRuntimeError('BUSY');
|
|
1407
1459
|
}
|
|
1408
1460
|
let refresh: IFrameRefreshRecord | undefined;
|
|
@@ -1599,34 +1651,65 @@ export class BrowserRuntime {
|
|
|
1599
1651
|
public async subscribeLeaseEvents(
|
|
1600
1652
|
lease: ILeaseRecord,
|
|
1601
1653
|
listener: (event: TBrowserRuntimeEvent) => void,
|
|
1654
|
+
options: IBrowserRuntimeEventSubscriptionOptions = {},
|
|
1602
1655
|
): Promise<IBrowserRuntimeFrameSubscription> {
|
|
1603
1656
|
this.requireHuman(lease);
|
|
1604
1657
|
if (typeof listener !== 'function') throw new BrowserRuntimeError('INVALID_INPUT');
|
|
1658
|
+
const values = validateExactKeys(options, ['includeFrames'], 'event subscription');
|
|
1659
|
+
if (values.includeFrames !== undefined && typeof values.includeFrames !== 'boolean') {
|
|
1660
|
+
throw new BrowserRuntimeError('INVALID_INPUT');
|
|
1661
|
+
}
|
|
1605
1662
|
const slot = lease.slot;
|
|
1606
1663
|
const release = slot.mutex.tryAcquire();
|
|
1607
1664
|
if (!release) throw new BrowserRuntimeError('BUSY');
|
|
1608
|
-
let subscription: IFrameSubscriptionRecord;
|
|
1609
1665
|
try {
|
|
1610
1666
|
this.requireValidLease(lease);
|
|
1611
|
-
if (
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1667
|
+
if (values.includeFrames === false) {
|
|
1668
|
+
if (slot.frameSubscriptions.has(lease.leaseId)) throw new BrowserRuntimeError('BUSY');
|
|
1669
|
+
const subscription: IFrameSubscriptionRecord = {
|
|
1670
|
+
lease, listener, includeFrames: false, session: slot.session!,
|
|
1671
|
+
outstanding: new Map(), highestSequence: 0, closed: false,
|
|
1672
|
+
};
|
|
1673
|
+
slot.frameSubscriptions.set(lease.leaseId, subscription);
|
|
1674
|
+
this.pushToSubscription(subscription, { type: 'state', state: this.resourceState(slot.session!.getState()) });
|
|
1675
|
+
return { close: () => this.closeFrameSubscription(subscription) };
|
|
1676
|
+
}
|
|
1677
|
+
} finally { release(); }
|
|
1678
|
+
let subscription: IFrameSubscriptionRecord | undefined;
|
|
1679
|
+
try {
|
|
1680
|
+
return await this.runOperation(lease, 'subscribeEvents', 'frame-stream', {}, async (signal, session) => {
|
|
1681
|
+
if (slot.frameSubscriptions.has(lease.leaseId)) throw new BrowserRuntimeError('BUSY');
|
|
1682
|
+
subscription = {
|
|
1683
|
+
lease, listener, includeFrames: values.includeFrames !== false, session,
|
|
1684
|
+
outstanding: new Map(), highestSequence: 0, closed: false,
|
|
1685
|
+
};
|
|
1686
|
+
slot.frameSubscriptions.set(lease.leaseId, subscription);
|
|
1687
|
+
if (subscription.includeFrames) {
|
|
1688
|
+
lease.frameCaptureUsed = true;
|
|
1689
|
+
await this.reconcileFrameCapture(slot, session, signal);
|
|
1690
|
+
}
|
|
1691
|
+
this.pushToSubscription(subscription, { type: 'state', state: this.resourceState(session.getState()) });
|
|
1692
|
+
const ownedSubscription = subscription;
|
|
1693
|
+
return { close: () => this.closeFrameSubscription(ownedSubscription) };
|
|
1623
1694
|
});
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1695
|
+
} catch (error) {
|
|
1696
|
+
if (subscription) {
|
|
1697
|
+
this.detachFrameSubscription(subscription);
|
|
1698
|
+
await this.revokeCapabilityRecord(lease.capability);
|
|
1699
|
+
}
|
|
1700
|
+
throw error;
|
|
1626
1701
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
private async reconcileFrameCapture(slot: IResourceSlot, session: ILiveBrowserSessionLike, signal: AbortSignal): Promise<void> {
|
|
1705
|
+
const enabled = [...slot.frameSubscriptions.values()].some((subscription) => (
|
|
1706
|
+
!subscription.closed && !subscription.lease.released && subscription.includeFrames
|
|
1707
|
+
));
|
|
1708
|
+
if (slot.frameCaptureEnabled === enabled) return;
|
|
1709
|
+
// A failed/aborted CDP transition may have reached the browser; cleanup must reconcile it.
|
|
1710
|
+
slot.frameCaptureEnabled = undefined;
|
|
1711
|
+
await session.setFrameCaptureEnabled(enabled, { signal });
|
|
1712
|
+
slot.frameCaptureEnabled = enabled;
|
|
1630
1713
|
}
|
|
1631
1714
|
|
|
1632
1715
|
/** @internal */
|
|
@@ -1891,6 +1974,9 @@ export class BrowserRuntime {
|
|
|
1891
1974
|
controller: new AbortController(),
|
|
1892
1975
|
authorityGeneration: slot.authorityGeneration,
|
|
1893
1976
|
released: false,
|
|
1977
|
+
videoPeerId: randomId(24),
|
|
1978
|
+
videoPeerMayExist: false,
|
|
1979
|
+
frameCaptureUsed: false,
|
|
1894
1980
|
heldKeys: new Map(),
|
|
1895
1981
|
heldButtons: new Map(),
|
|
1896
1982
|
};
|
|
@@ -1926,8 +2012,9 @@ export class BrowserRuntime {
|
|
|
1926
2012
|
forceNoSandbox: false,
|
|
1927
2013
|
usePipe: true,
|
|
1928
2014
|
allowEvaluation: true,
|
|
2015
|
+
video: this.options.video,
|
|
1929
2016
|
screencast: {
|
|
1930
|
-
maxOutstandingFrames: this.options.maxOutstandingFrames,
|
|
2017
|
+
enabled: false, maxOutstandingFrames: this.options.maxOutstandingFrames,
|
|
1931
2018
|
quality: this.options.screencast.quality,
|
|
1932
2019
|
maxWidth: this.options.screencast.maxWidth,
|
|
1933
2020
|
maxHeight: this.options.screencast.maxHeight,
|
|
@@ -1945,7 +2032,6 @@ export class BrowserRuntime {
|
|
|
1945
2032
|
'--disable-quic',
|
|
1946
2033
|
'--disable-dns-prefetch',
|
|
1947
2034
|
'--disable-background-networking',
|
|
1948
|
-
'--disable-extensions',
|
|
1949
2035
|
'--webrtc-ip-handling-policy=disable_non_proxied_udp',
|
|
1950
2036
|
'--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1',
|
|
1951
2037
|
],
|
|
@@ -1998,6 +2084,7 @@ export class BrowserRuntime {
|
|
|
1998
2084
|
}
|
|
1999
2085
|
if (probeSignal.aborted) throw probeSignal.reason;
|
|
2000
2086
|
slot.session = session;
|
|
2087
|
+
slot.frameCaptureEnabled = false;
|
|
2001
2088
|
slot.proxy = proxy;
|
|
2002
2089
|
slot.profileDirectory = profileDirectory;
|
|
2003
2090
|
slot.unsubscribeSession = unsubscribe;
|
|
@@ -2059,7 +2146,7 @@ export class BrowserRuntime {
|
|
|
2059
2146
|
operationOptions: IBrowserRuntimeOperationOptions,
|
|
2060
2147
|
execute: (signal: AbortSignal, session: ILiveBrowserSessionLike) => Promise<T>,
|
|
2061
2148
|
finalize?: () => Promise<void>,
|
|
2062
|
-
participantCleanup = false,
|
|
2149
|
+
participantCleanup: 'participant' | 'subscription' | false = false,
|
|
2063
2150
|
): Promise<T> {
|
|
2064
2151
|
if (!operationOptions || typeof operationOptions !== 'object') {
|
|
2065
2152
|
throw new BrowserRuntimeError('INVALID_INPUT');
|
|
@@ -2622,6 +2709,7 @@ export class BrowserRuntime {
|
|
|
2622
2709
|
|
|
2623
2710
|
private resourceState(state: plugins.smartpuppeteer.ILiveBrowserState): IBrowserRuntimeState {
|
|
2624
2711
|
return {
|
|
2712
|
+
...(state.videoAcceleration ? { videoAcceleration: { ...state.videoAcceleration } } : {}),
|
|
2625
2713
|
status: state.status,
|
|
2626
2714
|
activeTabId: state.activeTabId ? truncateString(state.activeTabId, 128) : null,
|
|
2627
2715
|
viewportRevision: state.viewportRevision,
|
|
@@ -2665,6 +2753,7 @@ export class BrowserRuntime {
|
|
|
2665
2753
|
slot.frameRefresh
|
|
2666
2754
|
|| slot.frameSubscriptions.get(lease.leaseId) !== subscription
|
|
2667
2755
|
|| subscription.closed
|
|
2756
|
+
|| !subscription.includeFrames
|
|
2668
2757
|
|| subscription.lease !== lease
|
|
2669
2758
|
|| slot.leases.get(lease.leaseId) !== lease
|
|
2670
2759
|
|| slot.session !== session
|
|
@@ -2946,6 +3035,7 @@ export class BrowserRuntime {
|
|
|
2946
3035
|
if (event.frame.data.byteLength > this.options.maxFrameBytes) {
|
|
2947
3036
|
if (refresh) this.failFrameRefresh(refresh, 'FRAME_TOO_LARGE');
|
|
2948
3037
|
for (const subscription of slot.frameSubscriptions.values()) {
|
|
3038
|
+
if (!subscription.includeFrames) continue;
|
|
2949
3039
|
this.pushToSubscription(subscription, {
|
|
2950
3040
|
type: 'error',
|
|
2951
3041
|
error: { code: 'FRAME_TOO_LARGE', fatal: false, tabId: acknowledgement.tabId },
|
|
@@ -2964,7 +3054,7 @@ export class BrowserRuntime {
|
|
|
2964
3054
|
}
|
|
2965
3055
|
const key = this.frameAcknowledgementKey(acknowledgement);
|
|
2966
3056
|
for (const subscription of [...slot.frameSubscriptions.values()]) {
|
|
2967
|
-
if (subscription.closed) continue;
|
|
3057
|
+
if (subscription.closed || !subscription.includeFrames) continue;
|
|
2968
3058
|
while (subscription.outstanding.size >= this.options.maxOutstandingFrames) {
|
|
2969
3059
|
const oldestKey = subscription.outstanding.keys().next().value!;
|
|
2970
3060
|
this.trackCleanup(this.retireOutstandingFrame(subscription, oldestKey).then(() => undefined));
|
|
@@ -3027,20 +3117,53 @@ export class BrowserRuntime {
|
|
|
3027
3117
|
return true;
|
|
3028
3118
|
} catch {
|
|
3029
3119
|
// A failed participant transport cannot revoke another participant or kill the producer.
|
|
3030
|
-
this.
|
|
3120
|
+
this.detachFrameSubscription(subscription);
|
|
3031
3121
|
this.trackCleanup(this.revokeCapabilityRecord(subscription.lease.capability));
|
|
3032
3122
|
return false;
|
|
3033
3123
|
}
|
|
3034
3124
|
}
|
|
3035
3125
|
|
|
3036
3126
|
private async closeFrameSubscription(subscription: IFrameSubscriptionRecord): Promise<void> {
|
|
3037
|
-
if (subscription.
|
|
3127
|
+
if (subscription.closeComplete) return;
|
|
3128
|
+
if (subscription.closePromise) return subscription.closePromise;
|
|
3129
|
+
this.detachFrameSubscription(subscription, false);
|
|
3130
|
+
const lease = subscription.lease;
|
|
3131
|
+
const slot = lease.slot;
|
|
3132
|
+
const operation = (async () => {
|
|
3133
|
+
if (subscription.includeFrames && slot.session === subscription.session) {
|
|
3134
|
+
if (lease.released) await this.releaseLeaseRecord(lease);
|
|
3135
|
+
else if (this.lifecycleState === 'running' && !slot.permanentlyFenced && !slot.retirementPending
|
|
3136
|
+
&& slot.fencingGeneration === undefined && slot.terminationFenceGeneration === undefined) {
|
|
3137
|
+
await this.runOperation(lease, 'unsubscribeFrames', 'frame-stream', {}, async (signal, session) => {
|
|
3138
|
+
await this.reconcileFrameCapture(slot, session, signal);
|
|
3139
|
+
this.detachFrameSubscription(subscription);
|
|
3140
|
+
subscription.closeComplete = true;
|
|
3141
|
+
}, undefined, 'subscription');
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
this.detachFrameSubscription(subscription);
|
|
3145
|
+
subscription.closeComplete = true;
|
|
3146
|
+
})();
|
|
3147
|
+
subscription.closePromise = operation;
|
|
3148
|
+
try { await operation; }
|
|
3149
|
+
catch (error) {
|
|
3150
|
+
// Producer failure may supersede a queued unsubscribe. Its exact incarnation's
|
|
3151
|
+
// confirmed termination completes capture cleanup without issuing another toggle.
|
|
3152
|
+
const failure = slot.frameFailure;
|
|
3153
|
+
if (failure?.session === subscription.session) await failure.promise;
|
|
3154
|
+
if (slot.session === subscription.session || !lease.released) throw error;
|
|
3155
|
+
this.detachFrameSubscription(subscription);
|
|
3156
|
+
subscription.closeComplete = true;
|
|
3157
|
+
} finally { subscription.closePromise = undefined; }
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
private detachFrameSubscription(subscription: IFrameSubscriptionRecord, remove = true): void {
|
|
3038
3161
|
subscription.closed = true;
|
|
3039
3162
|
const slot = subscription.lease.slot;
|
|
3040
3163
|
if (slot.frameRefresh?.subscription === subscription) {
|
|
3041
3164
|
this.abandonFrameRefresh(slot, slot.frameRefresh);
|
|
3042
3165
|
}
|
|
3043
|
-
if (slot.frameSubscriptions.get(subscription.lease.leaseId) === subscription) {
|
|
3166
|
+
if (remove && slot.frameSubscriptions.get(subscription.lease.leaseId) === subscription) {
|
|
3044
3167
|
slot.frameSubscriptions.delete(subscription.lease.leaseId);
|
|
3045
3168
|
}
|
|
3046
3169
|
for (const frame of subscription.outstanding.values()) {
|
|
@@ -3199,10 +3322,10 @@ export class BrowserRuntime {
|
|
|
3199
3322
|
this.trackCleanup(operation);
|
|
3200
3323
|
}
|
|
3201
3324
|
|
|
3202
|
-
private requireQueuedLease(lease: ILeaseRecord, participantCleanup = false): void {
|
|
3325
|
+
private requireQueuedLease(lease: ILeaseRecord, participantCleanup: 'participant' | 'subscription' | false = false): void {
|
|
3203
3326
|
if (!participantCleanup) return this.requireValidLease(lease);
|
|
3204
3327
|
if (
|
|
3205
|
-
!lease.released || lease.slot.leases.get(lease.leaseId) !== lease
|
|
3328
|
+
(participantCleanup === 'participant' && !lease.released) || lease.slot.leases.get(lease.leaseId) !== lease
|
|
3206
3329
|
|| lease.capability.lease !== lease || !lease.slot.session
|
|
3207
3330
|
) throw new BrowserRuntimeError('CAPABILITY_REVOKED');
|
|
3208
3331
|
}
|
|
@@ -3367,14 +3490,20 @@ export class BrowserRuntime {
|
|
|
3367
3490
|
if (
|
|
3368
3491
|
this.lifecycleState === 'running' && slot.session && !slot.permanentlyFenced && !slot.retirementPending
|
|
3369
3492
|
&& slot.fencingGeneration === undefined && slot.terminationFenceGeneration === undefined
|
|
3370
|
-
&& (lease.preferredViewport || lease.heldKeys.size || lease.heldButtons.size
|
|
3493
|
+
&& (lease.preferredViewport || lease.heldKeys.size || lease.heldButtons.size
|
|
3494
|
+
|| lease.videoPeerMayExist || lease.frameCaptureUsed)
|
|
3371
3495
|
) {
|
|
3372
3496
|
await this.runOperation(lease, 'releaseParticipant', 'viewport', {},
|
|
3373
3497
|
async (signal, session) => {
|
|
3498
|
+
if (lease.videoPeerMayExist) {
|
|
3499
|
+
await session.closeVideoPeer(lease.videoPeerId);
|
|
3500
|
+
lease.videoPeerMayExist = false;
|
|
3501
|
+
}
|
|
3502
|
+
if (lease.frameCaptureUsed) await this.reconcileFrameCapture(slot, session, signal);
|
|
3374
3503
|
await this.releaseParticipantInput(lease, session);
|
|
3375
3504
|
await this.applySharedViewport(slot, session, signal);
|
|
3376
3505
|
lease.preferredViewport = undefined;
|
|
3377
|
-
}, undefined,
|
|
3506
|
+
}, undefined, 'participant');
|
|
3378
3507
|
}
|
|
3379
3508
|
if (slot.permanentlyFenced && (slot.session || slot.proxy || slot.profileDirectory)) {
|
|
3380
3509
|
await this.terminateSlotSession(slot);
|
|
@@ -3392,7 +3521,7 @@ export class BrowserRuntime {
|
|
|
3392
3521
|
|
|
3393
3522
|
private async closeFrameSubscriptionIfLease(lease: ILeaseRecord): Promise<void> {
|
|
3394
3523
|
const subscription = lease.slot.frameSubscriptions.get(lease.leaseId);
|
|
3395
|
-
if (subscription)
|
|
3524
|
+
if (subscription) this.detachFrameSubscription(subscription);
|
|
3396
3525
|
}
|
|
3397
3526
|
|
|
3398
3527
|
private async quiesceSlot(slot: IResourceSlot): Promise<void> {
|
|
@@ -4243,8 +4372,26 @@ export class BrowserRuntimeLease {
|
|
|
4243
4372
|
|
|
4244
4373
|
public subscribeEvents(
|
|
4245
4374
|
listener: (event: TBrowserRuntimeEvent) => void,
|
|
4375
|
+
options?: IBrowserRuntimeEventSubscriptionOptions,
|
|
4246
4376
|
): Promise<IBrowserRuntimeFrameSubscription> {
|
|
4247
|
-
return this.runtime.subscribeLeaseEvents(this.record, listener);
|
|
4377
|
+
return this.runtime.subscribeLeaseEvents(this.record, listener, options);
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
public openVideoPeer(options?: IBrowserRuntimeOperationOptions): Promise<TBrowserRuntimeVideoOffer> {
|
|
4381
|
+
return this.runtime.leaseOpenVideoPeer(this.record, options);
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4384
|
+
public answerVideoPeer(negotiationId: string, description: plugins.smartpuppeteer.ILiveVideoDescription,
|
|
4385
|
+
options?: IBrowserRuntimeOperationOptions): Promise<void> {
|
|
4386
|
+
return this.runtime.leaseAnswerVideoPeer(this.record, negotiationId, description, options);
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
public closeVideoPeer(options?: IBrowserRuntimeOperationOptions): Promise<void> {
|
|
4390
|
+
return this.runtime.leaseCloseVideoPeer(this.record, options);
|
|
4391
|
+
}
|
|
4392
|
+
|
|
4393
|
+
public getVideoStatistics(options?: IBrowserRuntimeOperationOptions): Promise<plugins.smartpuppeteer.ILiveVideoStatistics> {
|
|
4394
|
+
return this.runtime.leaseGetVideoStatistics(this.record, options);
|
|
4248
4395
|
}
|
|
4249
4396
|
|
|
4250
4397
|
public acknowledgeFrame(
|
package/ts/index.ts
CHANGED
|
@@ -48,6 +48,8 @@ export type {
|
|
|
48
48
|
IBrowserRuntimeOperationOptions,
|
|
49
49
|
IBrowserRuntimeOptions,
|
|
50
50
|
IBrowserRuntimeScreencastOptions,
|
|
51
|
+
IBrowserRuntimeEventSubscriptionOptions,
|
|
52
|
+
TBrowserRuntimeVideoOffer,
|
|
51
53
|
IBrowserResourceKey,
|
|
52
54
|
IBrowserResourceRegistration,
|
|
53
55
|
IBrowserRuntimeState,
|
package/ts/interfaces.ts
CHANGED
|
@@ -133,6 +133,7 @@ export type TBrowserRuntimeOperationIdentity = TReadonlyBrowserCapabilityAuthori
|
|
|
133
133
|
export type TBrowserRuntimeOperationClassification =
|
|
134
134
|
| 'raw-input'
|
|
135
135
|
| 'frame-stream'
|
|
136
|
+
| 'video-peer'
|
|
136
137
|
| 'viewport'
|
|
137
138
|
| 'navigation'
|
|
138
139
|
| 'tab'
|
|
@@ -167,6 +168,12 @@ export interface ILiveBrowserSessionLike {
|
|
|
167
168
|
getState(): plugins.smartpuppeteer.ILiveBrowserState;
|
|
168
169
|
getProcessState(): plugins.smartpuppeteer.ILiveBrowserProcessState;
|
|
169
170
|
onEvent(listener: plugins.smartpuppeteer.TLiveBrowserEventListener): () => void;
|
|
171
|
+
setFrameCaptureEnabled(enabled: boolean, options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<void>;
|
|
172
|
+
openVideoPeer(peerId: string, options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<plugins.smartpuppeteer.ILiveVideoOffer>;
|
|
173
|
+
answerVideoPeer(peerId: string, negotiationId: string, description: plugins.smartpuppeteer.ILiveVideoDescription,
|
|
174
|
+
options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<void>;
|
|
175
|
+
closeVideoPeer(peerId: string): Promise<void>;
|
|
176
|
+
getVideoStatistics(peerId: string): Promise<plugins.smartpuppeteer.ILiveVideoStatistics>;
|
|
170
177
|
refreshScreencast(
|
|
171
178
|
options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions,
|
|
172
179
|
): Promise<plugins.smartpuppeteer.ILiveBrowserFrameIdentity>;
|
|
@@ -291,6 +298,8 @@ export interface IBrowserRuntimeOptions {
|
|
|
291
298
|
maxOutstandingFrames?: number;
|
|
292
299
|
frameAcknowledgementTimeoutMs?: number;
|
|
293
300
|
screencast?: IBrowserRuntimeScreencastOptions;
|
|
301
|
+
/** Trusted host configuration; defaults to direct ICE and automatic GPU support. */
|
|
302
|
+
video?: plugins.smartpuppeteer.ILiveVideoOptions;
|
|
294
303
|
egress?: Omit<IBrowserEgressProxyOptions, 'projectId' | 'browserResourceId'>;
|
|
295
304
|
artifacts?: Omit<IBrowserArtifactStoreOptions, 'rootDirectory'>;
|
|
296
305
|
}
|
|
@@ -306,7 +315,15 @@ export interface IBrowserRuntimeTabState {
|
|
|
306
315
|
streaming: boolean;
|
|
307
316
|
}
|
|
308
317
|
|
|
318
|
+
export interface IBrowserRuntimeEventSubscriptionOptions {
|
|
319
|
+
/** Set false for native video viewers. Defaults to true for image-frame consumers. */
|
|
320
|
+
includeFrames?: boolean;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export type TBrowserRuntimeVideoOffer = Omit<plugins.smartpuppeteer.ILiveVideoOffer, 'peerId'>;
|
|
324
|
+
|
|
309
325
|
export interface IBrowserRuntimeState {
|
|
326
|
+
videoAcceleration?: plugins.smartpuppeteer.ILiveVideoAcceleration;
|
|
310
327
|
status: 'stopped' | 'starting' | 'running' | 'stopping';
|
|
311
328
|
activeTabId: string | null;
|
|
312
329
|
viewportRevision: number;
|