@modelprofile.com/browser-runtime 4.1.2 → 4.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 +19 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.runtime.js +38 -5
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/interfaces.d.ts +8 -0
- package/package.json +2 -2
- package/readme.hints.md +4 -1
- package/readme.md +8 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.runtime.ts +83 -9
- package/ts/index.ts +1 -0
- package/ts/interfaces.ts +9 -0
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 } 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, IBrowserFillAction, IBrowserNavigateAction, IBrowserObservationResult, IBrowserOpenCodeSessionId, IBrowserPressAction, TBrowserRuntimeAuditEvent, TBrowserRuntimeLeaseAuthority, TBrowserRuntimeOperationClassification, TBrowserRuntimeOperationIdentity, IBrowserRuntimeFlexToolProviderOptions, IBrowserRuntimeFrameSubscription, IBrowserRuntimeFramedClientOptions, IBrowserRuntimeMcpOptions, IBrowserRuntimeOperationOptions, IBrowserRuntimeOptions, IBrowserResourceKey, IBrowserResourceRegistration, IBrowserRuntimeState, 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, IBrowserFillAction, IBrowserNavigateAction, IBrowserObservationResult, IBrowserOpenCodeSessionId, IBrowserPressAction, TBrowserRuntimeAuditEvent, TBrowserRuntimeLeaseAuthority, TBrowserRuntimeOperationClassification, TBrowserRuntimeOperationIdentity, IBrowserRuntimeFlexToolProviderOptions, IBrowserRuntimeFrameSubscription, IBrowserRuntimeFramedClientOptions, IBrowserRuntimeMcpOptions, IBrowserRuntimeOperationOptions, IBrowserRuntimeOptions, IBrowserRuntimeScreencastOptions, IBrowserResourceKey, IBrowserResourceRegistration, IBrowserRuntimeState, 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
|
@@ -147,6 +147,13 @@ export interface IBrowserRuntimeEnvironment {
|
|
|
147
147
|
platform: NodeJS.Platform;
|
|
148
148
|
uid: number | undefined;
|
|
149
149
|
}
|
|
150
|
+
export interface IBrowserRuntimeScreencastOptions {
|
|
151
|
+
quality?: number;
|
|
152
|
+
maxWidth?: number;
|
|
153
|
+
maxHeight?: number;
|
|
154
|
+
everyNthFrame?: number;
|
|
155
|
+
firstFrameTimeoutMs?: number;
|
|
156
|
+
}
|
|
150
157
|
export interface IBrowserRuntimeOptions {
|
|
151
158
|
runtimeDirectory: string;
|
|
152
159
|
authorizeCapability(request: TBrowserCapabilityAuthorizationRequest, signal: AbortSignal): Promise<boolean> | boolean;
|
|
@@ -176,6 +183,7 @@ export interface IBrowserRuntimeOptions {
|
|
|
176
183
|
maxFrameBytes?: number;
|
|
177
184
|
maxOutstandingFrames?: number;
|
|
178
185
|
frameAcknowledgementTimeoutMs?: number;
|
|
186
|
+
screencast?: IBrowserRuntimeScreencastOptions;
|
|
179
187
|
egress?: Omit<IBrowserEgressProxyOptions, 'projectId' | 'browserResourceId'>;
|
|
180
188
|
artifacts?: Omit<IBrowserArtifactStoreOptions, 'rootDirectory'>;
|
|
181
189
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/browser-runtime",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.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,7 +30,7 @@
|
|
|
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.7.0",
|
|
34
34
|
"ipaddr.js": "^2.5.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/readme.hints.md
CHANGED
|
@@ -47,7 +47,10 @@ Durable implementation findings for `@modelprofile.com/browser-runtime`.
|
|
|
47
47
|
- Framed messages contain no identity selectors. The parent binds project, resource, attachment authority/revision, actor, peer, role, source, qualified session, Flex scope, channel, and exact run out of band.
|
|
48
48
|
- One qualified session can own multiple resource-specific framed channels. Capability revocation owns server-peer disconnect; peer-initiated release/close revokes only that channel's capability without recursively disconnecting itself.
|
|
49
49
|
- Framed request counts and queued write bytes are bounded. Runtime shutdown has a bounded caller-visible cleanup deadline while retaining in-flight cleanup ownership for retry.
|
|
50
|
-
- Frame delivery owns a bounded map of exact identities and configures SmartPuppeteer's separate private CDP-to-ack map to the same limit. Equal limits prevent private eviction while the application map has spare capacity, but private-first retirement at full capacity can make an exact acknowledgement fulfill `{ accepted: false }`. Both fulfilled booleans settle the application entry without proving successful upstream retirement;
|
|
50
|
+
- Frame delivery owns a bounded map of exact identities and configures SmartPuppeteer's separate private CDP-to-ack map to the same limit. Equal limits prevent private eviction while the application map has spare capacity, but private-first retirement at full capacity can make an exact acknowledgement fulfill `{ accepted: false }`. Both fulfilled booleans settle the application entry without proving successful upstream retirement; a rejected or timed-out acknowledgement operation toward SmartPuppeteer fails closed. Failed delivery (a throwing listener) deliberately fails the lease regardless of a fulfilled boolean. There is no cumulative frame protocol.
|
|
51
|
+
- A late application acknowledgement (`frameAcknowledgementTimeoutMs`, default 10 s) is retired through `retireOutstandingFrame(subscription, key, false)`, the same path as oldest-first eviction: the entry is acknowledged toward SmartPuppeteer and the lease is untouched. It must never route through `handleFrameFailure`; one slow viewer acknowledgement previously fenced the slot and terminated the whole Chromium incarnation. A later explicit acknowledgement of the retired identity returns `false`.
|
|
52
|
+
- A frame above `maxFrameBytes` is dropped, acknowledged in the background with `failLease=false`, and reported as `FRAME_TOO_LARGE` with `fatal: false`. `highestSequence` is advanced before the size check so the following frames stay valid. During an active refresh the oversized frame still fails the refresh with `FRAME_TOO_LARGE`, but the pushed error event is non-fatal.
|
|
53
|
+
- `screencast` (`quality` 0..100 default 70, `maxWidth`/`maxHeight` 1..4096 each with `maxWidth * maxHeight <= 8_294_400` and defaults 2560/1600, `everyNthFrame` 1..60 default 1, `firstFrameTimeoutMs` 1000..60000 default left to SmartPuppeteer) is validated in the constructor with `validateExactKeys` plus the integer validators and forwarded to `LiveBrowserSession` next to `maxOutstandingFrames`. The ceilings deliberately match `@push.rocks/smartpuppeteer` 2.6's `LiveBrowserSession` validation (`maxViewportPixelArea = 8294400`, `maxTimeoutMs = 60000`); a looser runtime bound would let `new BrowserRuntime()` succeed and every incarnation launch fail instead. Without `quality`/`maxWidth`/`maxHeight`, SmartPuppeteer's defaults (quality 80, unbounded size) produced multi-megabyte frames on device-pixel-ratio 2 viewers.
|
|
51
54
|
- During refresh, application entries plus refresh-owned acknowledgement promises share the same hard frame-window bound. ACK ownership is fixed at admission, refresh failures defer until the operation reservation is gone, and the exact failure fence completes before terminal audit, caller settlement, or FIFO advancement. Lifecycle teardown explicitly abandons an exact refresh record so stale state cannot retain `BUSY` admission.
|
|
52
55
|
- Runtime state and error events preserve diagnostic messages only after truncating them to 2,048 characters. The first fatal session error invalidates exact capability admission synchronously, delivers once to the captured exact subscriber, and schedules revocation in `finally` so a throwing listener cannot retain authority.
|
|
53
56
|
- Flex resolves only a capability token, and the provider's run must exactly match its trusted framed client. SmartAgent exposes exactly navigate, snapshot, screenshot, click, fill, and press.
|
package/readme.md
CHANGED
|
@@ -122,11 +122,17 @@ Operations from one valid lease enter a strict bounded FIFO. `maxQueuedOperation
|
|
|
122
122
|
|
|
123
123
|
`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.
|
|
124
124
|
|
|
125
|
-
Human frame delivery keeps a bounded exact-identity application window. `maxOutstandingFrames` defaults to 4 and accepts values from 1 through 32. Runtime passes the same bound to SmartPuppeteer's private CDP-to-ack window; the windows remain separately owned, but equal limits prevent private eviction while the application window still has spare capacity. At full capacity SmartPuppeteer retires its oldest private entry before publishing the next frame, so a later exact acknowledgement can fulfill with `{ accepted: false }`. A fulfilled `true` or `false` settles the Runtime entry locally without fencing; `false` does not prove that upstream retirement succeeded. Rejected or timed-out acknowledgement operations fail closed, as
|
|
125
|
+
Human frame delivery keeps a bounded exact-identity application window. `maxOutstandingFrames` defaults to 4 and accepts values from 1 through 32. Runtime passes the same bound to SmartPuppeteer's private CDP-to-ack window; the windows remain separately owned, but equal limits prevent private eviction while the application window still has spare capacity. At full capacity SmartPuppeteer retires its oldest private entry before publishing the next frame, so a later exact acknowledgement can fulfill with `{ accepted: false }`. A fulfilled `true` or `false` settles the Runtime entry locally without fencing; `false` does not prove that upstream retirement succeeded. Rejected or timed-out acknowledgement operations toward SmartPuppeteer fail closed, as does failed-delivery retirement regardless of a fulfilled boolean. Frame acknowledgement stays outside the operation FIFO.
|
|
126
|
+
|
|
127
|
+
A late application acknowledgement is not a lease failure. `frameAcknowledgementTimeoutMs` defaults to 10,000 milliseconds and accepts values from 100 through 60,000; when a delivered frame is still unacknowledged after that time, Runtime retires the entry through the same path as oldest-first window eviction: it acknowledges the frame toward SmartPuppeteer and leaves the lease, subscription, and incarnation untouched. A later explicit acknowledgement of that retired identity returns `false`. The same timeout still bounds each acknowledgement operation toward SmartPuppeteer, and that bound remains fail closed.
|
|
128
|
+
|
|
129
|
+
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.
|
|
130
|
+
|
|
131
|
+
`screencast` tunes the Chromium screencast that SmartPuppeteer starts for every incarnation. `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`.
|
|
126
132
|
|
|
127
133
|
Human state and error events retain an optional diagnostic `message` bounded to 2,048 characters. A fatal session error synchronously invalidates the exact capability before its captured subscriber is notified once, then revokes and cleans up the lease even when that subscriber throws.
|
|
128
134
|
|
|
129
|
-
`humanLease.refreshFrameStream(options?: IBrowserRuntimeOperationOptions): Promise<void>` requires the lease's exact active frame subscription and enters the same audited FIFO as other browser mutations. Runtime retires the old application window, asks SmartPuppeteer for a new screencast generation, and succeeds only after the exact first new-generation frame was delivered through that subscription and validated against the current tab, viewport, lease, session, and incarnation. Existing and refresh-owned acknowledgement work share `maxOutstandingFrames`; synchronous listener acknowledgements cannot create an unbounded promise set. A missing or pre-execution-replaced subscription fails with `BUSY`. Refresh protocol, delivery, retirement, or acknowledgement failure returns `FRAME_STREAM_FAILED` or `FRAME_TOO_LARGE` only after the exact incarnation is fenced;
|
|
135
|
+
`humanLease.refreshFrameStream(options?: IBrowserRuntimeOperationOptions): Promise<void>` requires the lease's exact active frame subscription and enters the same audited FIFO as other browser mutations. Runtime retires the old application window, asks SmartPuppeteer for a new screencast generation, and succeeds only after the exact first new-generation frame was delivered through that subscription and validated against the current tab, viewport, lease, session, and incarnation. Existing and refresh-owned acknowledgement work share `maxOutstandingFrames`; synchronous listener acknowledgements cannot create an unbounded promise set. A missing or pre-execution-replaced subscription fails with `BUSY`. Refresh protocol, delivery, retirement, or acknowledgement failure returns `FRAME_STREAM_FAILED` or `FRAME_TOO_LARGE` only after the exact incarnation is fenced; an oversized frame during an active refresh still fails that refresh with `FRAME_TOO_LARGE` while the subscription's error event itself stays `fatal: false`. Stale authority returns `ABORTED` without touching its replacement. Pass `options.signal` for caller cancellation; active cancellation preserves a successfully restored stream.
|
|
130
136
|
|
|
131
137
|
## Trusted Pipe And Flex
|
|
132
138
|
|
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: '4.
|
|
6
|
+
version: '4.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
|
@@ -55,6 +55,17 @@ import {
|
|
|
55
55
|
} from './utils.js';
|
|
56
56
|
import { BrowserRuntimeFramedServerPeer } from './classes.framed.js';
|
|
57
57
|
|
|
58
|
+
// @push.rocks/smartpuppeteer LiveBrowserSession rejects screencast bounds above this area.
|
|
59
|
+
const maxScreencastPixelArea = 8_294_400;
|
|
60
|
+
|
|
61
|
+
interface INormalizedScreencastOptions {
|
|
62
|
+
quality: number;
|
|
63
|
+
maxWidth: number;
|
|
64
|
+
maxHeight: number;
|
|
65
|
+
everyNthFrame: number;
|
|
66
|
+
firstFrameTimeoutMs?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
interface INormalizedRuntimeOptions {
|
|
59
70
|
runtimeDirectory: string;
|
|
60
71
|
authorizeCapability: IBrowserRuntimeOptions['authorizeCapability'];
|
|
@@ -89,6 +100,7 @@ interface INormalizedRuntimeOptions {
|
|
|
89
100
|
maxFrameBytes: number;
|
|
90
101
|
maxOutstandingFrames: number;
|
|
91
102
|
frameAcknowledgementTimeoutMs: number;
|
|
103
|
+
screencast: INormalizedScreencastOptions;
|
|
92
104
|
egress: NonNullable<IBrowserRuntimeOptions['egress']>;
|
|
93
105
|
ownership?: IBrowserRuntimeTestingOptions['ownership'];
|
|
94
106
|
beforeLeasePublication?: IBrowserRuntimeTestingOptions['beforeLeasePublication'];
|
|
@@ -295,6 +307,61 @@ export class BrowserRuntime {
|
|
|
295
307
|
capabilityMaximumTtlMs,
|
|
296
308
|
5 * 60_000,
|
|
297
309
|
);
|
|
310
|
+
const screencastOptions = options.screencast === undefined
|
|
311
|
+
? {}
|
|
312
|
+
: validateExactKeys(
|
|
313
|
+
options.screencast,
|
|
314
|
+
['quality', 'maxWidth', 'maxHeight', 'everyNthFrame', 'firstFrameTimeoutMs'],
|
|
315
|
+
'screencast',
|
|
316
|
+
);
|
|
317
|
+
// Bounds mirror what @push.rocks/smartpuppeteer's LiveBrowserSession accepts, so a runtime
|
|
318
|
+
// that constructs successfully can never launch incarnations that fail on these values.
|
|
319
|
+
const screencast: INormalizedScreencastOptions = {
|
|
320
|
+
quality: validateOptionalInteger(
|
|
321
|
+
screencastOptions.quality,
|
|
322
|
+
'screencast.quality',
|
|
323
|
+
0,
|
|
324
|
+
100,
|
|
325
|
+
70,
|
|
326
|
+
),
|
|
327
|
+
maxWidth: validateOptionalInteger(
|
|
328
|
+
screencastOptions.maxWidth,
|
|
329
|
+
'screencast.maxWidth',
|
|
330
|
+
1,
|
|
331
|
+
4096,
|
|
332
|
+
2560,
|
|
333
|
+
),
|
|
334
|
+
maxHeight: validateOptionalInteger(
|
|
335
|
+
screencastOptions.maxHeight,
|
|
336
|
+
'screencast.maxHeight',
|
|
337
|
+
1,
|
|
338
|
+
4096,
|
|
339
|
+
1600,
|
|
340
|
+
),
|
|
341
|
+
everyNthFrame: validateOptionalInteger(
|
|
342
|
+
screencastOptions.everyNthFrame,
|
|
343
|
+
'screencast.everyNthFrame',
|
|
344
|
+
1,
|
|
345
|
+
60,
|
|
346
|
+
1,
|
|
347
|
+
),
|
|
348
|
+
...(screencastOptions.firstFrameTimeoutMs === undefined
|
|
349
|
+
? {}
|
|
350
|
+
: {
|
|
351
|
+
firstFrameTimeoutMs: validateInteger(
|
|
352
|
+
screencastOptions.firstFrameTimeoutMs,
|
|
353
|
+
'screencast.firstFrameTimeoutMs',
|
|
354
|
+
1000,
|
|
355
|
+
60_000,
|
|
356
|
+
),
|
|
357
|
+
}),
|
|
358
|
+
};
|
|
359
|
+
if (screencast.maxWidth * screencast.maxHeight > maxScreencastPixelArea) {
|
|
360
|
+
throw new BrowserRuntimeError(
|
|
361
|
+
'INVALID_INPUT',
|
|
362
|
+
`screencast.maxWidth * screencast.maxHeight must not exceed ${maxScreencastPixelArea} pixels`,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
298
365
|
const environment = testingOptions?.environment ?? {
|
|
299
366
|
platform: process.platform,
|
|
300
367
|
uid: process.getuid?.(),
|
|
@@ -472,6 +539,7 @@ export class BrowserRuntime {
|
|
|
472
539
|
60_000,
|
|
473
540
|
10_000,
|
|
474
541
|
),
|
|
542
|
+
screencast,
|
|
475
543
|
egress: options.egress ?? {},
|
|
476
544
|
ownership: testingOptions?.ownership,
|
|
477
545
|
beforeLeasePublication: testingOptions?.beforeLeasePublication,
|
|
@@ -1677,6 +1745,13 @@ export class BrowserRuntime {
|
|
|
1677
1745
|
allowEvaluation: true,
|
|
1678
1746
|
screencast: {
|
|
1679
1747
|
maxOutstandingFrames: this.options.maxOutstandingFrames,
|
|
1748
|
+
quality: this.options.screencast.quality,
|
|
1749
|
+
maxWidth: this.options.screencast.maxWidth,
|
|
1750
|
+
maxHeight: this.options.screencast.maxHeight,
|
|
1751
|
+
everyNthFrame: this.options.screencast.everyNthFrame,
|
|
1752
|
+
...(this.options.screencast.firstFrameTimeoutMs === undefined
|
|
1753
|
+
? {}
|
|
1754
|
+
: { firstFrameTimeoutMs: this.options.screencast.firstFrameTimeoutMs }),
|
|
1680
1755
|
},
|
|
1681
1756
|
launchOptions: {
|
|
1682
1757
|
headless: true,
|
|
@@ -2613,7 +2688,9 @@ export class BrowserRuntime {
|
|
|
2613
2688
|
): void {
|
|
2614
2689
|
if (frame.timer || subscription.closed || !subscription.outstanding.has(key)) return;
|
|
2615
2690
|
const timer = setTimeout(() => {
|
|
2616
|
-
|
|
2691
|
+
// A late application acknowledgement retires the entry exactly like oldest-first
|
|
2692
|
+
// eviction: the frame is acknowledged toward the session and the lease is untouched.
|
|
2693
|
+
this.trackCleanup(this.retireOutstandingFrame(subscription, key, false).then(() => undefined));
|
|
2617
2694
|
}, this.options.frameAcknowledgementTimeoutMs);
|
|
2618
2695
|
timer.unref();
|
|
2619
2696
|
frame.timer = timer;
|
|
@@ -2725,23 +2802,20 @@ export class BrowserRuntime {
|
|
|
2725
2802
|
}
|
|
2726
2803
|
subscription.highestSequence = acknowledgement.sequence;
|
|
2727
2804
|
if (event.frame.data.byteLength > this.options.maxFrameBytes) {
|
|
2805
|
+
// An oversized frame is dropped, not fatal: the sequence watermark already advanced so
|
|
2806
|
+
// later frames stay valid, and the subscriber is told without losing its lease.
|
|
2728
2807
|
if (refresh) {
|
|
2729
2808
|
this.failFrameRefresh(refresh, 'FRAME_TOO_LARGE');
|
|
2730
2809
|
this.pushToSubscription(subscription, {
|
|
2731
2810
|
type: 'error',
|
|
2732
|
-
error: { code: 'FRAME_TOO_LARGE', fatal:
|
|
2811
|
+
error: { code: 'FRAME_TOO_LARGE', fatal: false, tabId: event.frame.tabId },
|
|
2733
2812
|
});
|
|
2734
2813
|
return;
|
|
2735
2814
|
}
|
|
2736
|
-
this.trackCleanup(this.acknowledgeFrameInBackground(
|
|
2737
|
-
slot,
|
|
2738
|
-
session,
|
|
2739
|
-
acknowledgement,
|
|
2740
|
-
true,
|
|
2741
|
-
));
|
|
2815
|
+
this.trackCleanup(this.acknowledgeFrameInBackground(slot, session, acknowledgement));
|
|
2742
2816
|
this.pushToSubscription(subscription, {
|
|
2743
2817
|
type: 'error',
|
|
2744
|
-
error: { code: 'FRAME_TOO_LARGE', fatal:
|
|
2818
|
+
error: { code: 'FRAME_TOO_LARGE', fatal: false, tabId: event.frame.tabId },
|
|
2745
2819
|
});
|
|
2746
2820
|
return;
|
|
2747
2821
|
}
|
package/ts/index.ts
CHANGED
package/ts/interfaces.ts
CHANGED
|
@@ -236,6 +236,14 @@ export interface IBrowserRuntimeEnvironment {
|
|
|
236
236
|
uid: number | undefined;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
export interface IBrowserRuntimeScreencastOptions {
|
|
240
|
+
quality?: number;
|
|
241
|
+
maxWidth?: number;
|
|
242
|
+
maxHeight?: number;
|
|
243
|
+
everyNthFrame?: number;
|
|
244
|
+
firstFrameTimeoutMs?: number;
|
|
245
|
+
}
|
|
246
|
+
|
|
239
247
|
export interface IBrowserRuntimeOptions {
|
|
240
248
|
runtimeDirectory: string;
|
|
241
249
|
authorizeCapability(
|
|
@@ -271,6 +279,7 @@ export interface IBrowserRuntimeOptions {
|
|
|
271
279
|
maxFrameBytes?: number;
|
|
272
280
|
maxOutstandingFrames?: number;
|
|
273
281
|
frameAcknowledgementTimeoutMs?: number;
|
|
282
|
+
screencast?: IBrowserRuntimeScreencastOptions;
|
|
274
283
|
egress?: Omit<IBrowserEgressProxyOptions, 'projectId' | 'browserResourceId'>;
|
|
275
284
|
artifacts?: Omit<IBrowserArtifactStoreOptions, 'rootDirectory'>;
|
|
276
285
|
}
|