@modelprofile.com/browser-runtime 5.6.0 → 5.7.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/changelog.md +12 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.runtime.d.ts +1 -0
- package/dist_ts/classes.runtime.js +19 -2
- package/dist_ts/confinement.js +24 -3
- package/dist_ts/interfaces.d.ts +1 -0
- package/package.json +2 -2
- package/readme.md +13 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.runtime.ts +20 -1
- package/ts/confinement.ts +27 -2
- package/ts/interfaces.ts +1 -0
package/dist_ts/confinement.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { BrowserRuntimeError } from './errors.js';
|
|
3
|
+
const restoredProbeState = (before, after) => {
|
|
4
|
+
const restored = structuredClone(after);
|
|
5
|
+
const previousTab = before.tabs.find(tab => tab.id === before.activeTabId);
|
|
6
|
+
const currentTab = restored.tabs.find(tab => tab.id === before.activeTabId);
|
|
7
|
+
const previousSource = before.videoSource;
|
|
8
|
+
const currentSource = restored.videoSource;
|
|
9
|
+
// Native capture must reselect the original surface after an inactive probe
|
|
10
|
+
// window closes. Only its presentation epoch may advance; all document,
|
|
11
|
+
// viewport, tab, acceleration and source-identity fields still compare exactly.
|
|
12
|
+
if (previousTab && currentTab && previousSource?.frameAlignment === 2 && currentSource?.frameAlignment === 2
|
|
13
|
+
&& previousSource.tabId === previousTab.id && currentSource.tabId === currentTab.id
|
|
14
|
+
&& previousSource.generation === previousTab.generation && currentSource.generation === currentTab.generation
|
|
15
|
+
&& Number.isSafeInteger(currentTab.generation) && currentTab.generation >= previousTab.generation
|
|
16
|
+
&& Number.isInteger(previousSource.rtpTimestampFloor) && Number.isInteger(currentSource.rtpTimestampFloor)
|
|
17
|
+
&& currentSource.rtpTimestampFloor >= 0 && currentSource.rtpTimestampFloor <= 0xffffffff
|
|
18
|
+
&& ((currentSource.rtpTimestampFloor - previousSource.rtpTimestampFloor) >>> 0) < 0x80000000) {
|
|
19
|
+
currentTab.generation = previousTab.generation;
|
|
20
|
+
currentSource.generation = previousSource.generation;
|
|
21
|
+
currentSource.rtpTimestampFloor = previousSource.rtpTimestampFloor;
|
|
22
|
+
}
|
|
23
|
+
return JSON.stringify(restored) === JSON.stringify(before);
|
|
24
|
+
};
|
|
3
25
|
const listenOnUnsupportedPort = async (allowedPorts, onRequest) => {
|
|
4
26
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
5
27
|
const server = plugins.http.createServer((_request, response) => {
|
|
@@ -21,7 +43,6 @@ const listenOnUnsupportedPort = async (allowedPorts, onRequest) => {
|
|
|
21
43
|
};
|
|
22
44
|
export const runProductionConfinementProbe = async (context) => {
|
|
23
45
|
const initialState = context.session.getState();
|
|
24
|
-
const initialStateJson = JSON.stringify(initialState);
|
|
25
46
|
let probeTabId;
|
|
26
47
|
let probeFailed = false;
|
|
27
48
|
let probeError;
|
|
@@ -82,7 +103,7 @@ export const runProductionConfinementProbe = async (context) => {
|
|
|
82
103
|
}
|
|
83
104
|
await closeFixture().catch((error) => cleanupErrors.push(error));
|
|
84
105
|
try {
|
|
85
|
-
if (
|
|
106
|
+
if (!restoredProbeState(initialState, context.session.getState())) {
|
|
86
107
|
cleanupErrors.push(new BrowserRuntimeError('CONFINEMENT_FAILED'));
|
|
87
108
|
}
|
|
88
109
|
}
|
|
@@ -101,4 +122,4 @@ export const runProductionConfinementProbe = async (context) => {
|
|
|
101
122
|
if (probeFailed)
|
|
102
123
|
throw probeError;
|
|
103
124
|
};
|
|
104
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
125
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmluZW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jb25maW5lbWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFbEQsTUFBTSxrQkFBa0IsR0FBRyxDQUN6QixNQUFnRCxFQUNoRCxLQUErQyxFQUN0QyxFQUFFO0lBQ1gsTUFBTSxRQUFRLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3hDLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDM0UsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM1RSxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDO0lBQzFDLE1BQU0sYUFBYSxHQUFHLFFBQVEsQ0FBQyxXQUFXLENBQUM7SUFDM0MsNEVBQTRFO0lBQzVFLHdFQUF3RTtJQUN4RSxnRkFBZ0Y7SUFDaEYsSUFBSSxXQUFXLElBQUksVUFBVSxJQUFJLGNBQWMsRUFBRSxjQUFjLEtBQUssQ0FBQyxJQUFJLGFBQWEsRUFBRSxjQUFjLEtBQUssQ0FBQztXQUN2RyxjQUFjLENBQUMsS0FBSyxLQUFLLFdBQVcsQ0FBQyxFQUFFLElBQUksYUFBYSxDQUFDLEtBQUssS0FBSyxVQUFVLENBQUMsRUFBRTtXQUNoRixjQUFjLENBQUMsVUFBVSxLQUFLLFdBQVcsQ0FBQyxVQUFVLElBQUksYUFBYSxDQUFDLFVBQVUsS0FBSyxVQUFVLENBQUMsVUFBVTtXQUMxRyxN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
|
package/dist_ts/interfaces.d.ts
CHANGED
|
@@ -225,6 +225,7 @@ export interface IBrowserRuntimeEventSubscriptionOptions {
|
|
|
225
225
|
export type TBrowserRuntimeVideoOffer = Omit<plugins.smartpuppeteer.ILiveVideoOffer, 'peerId'>;
|
|
226
226
|
export interface IBrowserRuntimeState {
|
|
227
227
|
videoAcceleration?: plugins.smartpuppeteer.ILiveVideoAcceleration;
|
|
228
|
+
videoSource?: plugins.smartpuppeteer.ILiveVideoSource;
|
|
228
229
|
status: 'stopped' | 'starting' | 'running' | 'stopping';
|
|
229
230
|
activeTabId: string | null;
|
|
230
231
|
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.7.1",
|
|
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.13.1",
|
|
34
34
|
"ipaddr.js": "^2.5.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/readme.md
CHANGED
|
@@ -34,6 +34,19 @@ Human leases expose `openVideoPeer(options?)`, `answerVideoPeer(negotiationId, d
|
|
|
34
34
|
|
|
35
35
|
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.
|
|
36
36
|
|
|
37
|
+
`video: { backend: 'native', maxFrameRate: 30 }` selects SmartPuppeteer's Rust/NVIDIA
|
|
38
|
+
capture and NVENC sender. Chromium remains the default backend. Native viewers must
|
|
39
|
+
forward `state.videoSource` unchanged, including its RTP presentation fence and coded
|
|
40
|
+
frame alignment. The native viewport negotiates device scale 1 and fits the configured
|
|
41
|
+
capture limits while preserving the preferred aspect ratio. The returned viewport owns
|
|
42
|
+
input coordinates. Unsupported receiver codec limits fail negotiation explicitly.
|
|
43
|
+
|
|
44
|
+
Forward DOM pointer `event.timeStamp` as `dispatchMouse().timestampMs` to preserve the
|
|
45
|
+
occurrence spacing of a gesture delivered in a transport burst. Participant changes reset
|
|
46
|
+
the native timing history because viewer clocks have different origins; synthesized
|
|
47
|
+
button releases omit timestamps. Existing operation ordering and held-button ownership
|
|
48
|
+
remain authoritative.
|
|
49
|
+
|
|
37
50
|
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.
|
|
38
51
|
|
|
39
52
|
## Issue Reporting and Security
|
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.7.1',
|
|
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
|
@@ -265,6 +265,7 @@ interface IResourceSlot {
|
|
|
265
265
|
profileDirectory?: string;
|
|
266
266
|
unsubscribeSession?: () => void;
|
|
267
267
|
leases: Map<string, ILeaseRecord>;
|
|
268
|
+
lastMouseTimestampLeaseId?: string;
|
|
268
269
|
operations: Set<IOperationRecord>;
|
|
269
270
|
inputCleanup?: { session: ILiveBrowserSessionLike; incarnationGeneration: number; promise: Promise<void> };
|
|
270
271
|
operationScheduler: BrowserOperationScheduler<IQueuedOperationRecord>;
|
|
@@ -1597,7 +1598,16 @@ export class BrowserRuntime {
|
|
|
1597
1598
|
width: Math.min(...preferences.map((value) => value.width)),
|
|
1598
1599
|
height: Math.min(...preferences.map((value) => value.height)),
|
|
1599
1600
|
deviceScaleFactor: Math.min(...preferences.map((value) => value.deviceScaleFactor)),
|
|
1600
|
-
} : current.viewport;
|
|
1601
|
+
} : { ...current.viewport };
|
|
1602
|
+
if (this.options.video.backend === 'native') {
|
|
1603
|
+
// A native desktop has physical pixels. Negotiate the accepted viewport
|
|
1604
|
+
// here so all viewers use the same layout and coordinate space.
|
|
1605
|
+
const scale = Math.min(1, this.options.video.maxWidth / viewport.width,
|
|
1606
|
+
this.options.video.maxHeight / viewport.height);
|
|
1607
|
+
viewport.width = Math.max(1, Math.floor(viewport.width * scale));
|
|
1608
|
+
viewport.height = Math.max(1, Math.floor(viewport.height * scale));
|
|
1609
|
+
viewport.deviceScaleFactor = 1;
|
|
1610
|
+
}
|
|
1601
1611
|
if (
|
|
1602
1612
|
viewport.width !== current.viewport.width || viewport.height !== current.viewport.height
|
|
1603
1613
|
|| viewport.deviceScaleFactor !== current.viewport.deviceScaleFactor
|
|
@@ -1674,6 +1684,9 @@ export class BrowserRuntime {
|
|
|
1674
1684
|
): Promise<void> {
|
|
1675
1685
|
this.requireHuman(lease);
|
|
1676
1686
|
const mouse = action === 'dispatchMouse' ? input as plugins.smartpuppeteer.ILiveBrowserMouseInput : undefined;
|
|
1687
|
+
if (mouse?.timestampMs !== undefined && (!Number.isFinite(mouse.timestampMs) || mouse.timestampMs < 0)) {
|
|
1688
|
+
throw new BrowserRuntimeError('INVALID_INPUT');
|
|
1689
|
+
}
|
|
1677
1690
|
// Drag paths and shared held input have observable intermediate positions.
|
|
1678
1691
|
// Only explicit hover submissions can replace another pending hover.
|
|
1679
1692
|
const hoverKey = operationOptions.coalesceHoverMoves && mouse?.type === 'move'
|
|
@@ -1703,11 +1716,16 @@ export class BrowserRuntime {
|
|
|
1703
1716
|
throw new BrowserRuntimeError('QUOTA_EXCEEDED');
|
|
1704
1717
|
}
|
|
1705
1718
|
if (mouse.type === 'down') lease.heldButtons.set(key, { ...mouse });
|
|
1719
|
+
const previousClock = lease.slot.lastMouseTimestampLeaseId;
|
|
1720
|
+
// Viewer performance clocks have unrelated origins. An untimed event
|
|
1721
|
+
// resets native gesture pacing when another participant takes over.
|
|
1706
1722
|
await session.dispatchMouse({ ...mouse,
|
|
1723
|
+
timestampMs: previousClock && previousClock !== lease.leaseId ? undefined : mouse.timestampMs,
|
|
1707
1724
|
buttons: this.sharedMouseButtons(lease.slot, mouse.tabId,
|
|
1708
1725
|
mouse.type === 'up' ? { lease, key } : undefined),
|
|
1709
1726
|
modifiers: this.sharedModifiers(lease.slot, mouse.tabId, mouse.modifiers),
|
|
1710
1727
|
});
|
|
1728
|
+
lease.slot.lastMouseTimestampLeaseId = lease.leaseId;
|
|
1711
1729
|
if (mouse.type === 'up') lease.heldButtons.delete(key);
|
|
1712
1730
|
} else if (action === 'dispatchWheel') {
|
|
1713
1731
|
const wheel = input as plugins.smartpuppeteer.ILiveBrowserWheelInput;
|
|
@@ -2920,6 +2938,7 @@ export class BrowserRuntime {
|
|
|
2920
2938
|
private resourceState(state: plugins.smartpuppeteer.ILiveBrowserState): IBrowserRuntimeState {
|
|
2921
2939
|
return {
|
|
2922
2940
|
...(state.videoAcceleration ? { videoAcceleration: { ...state.videoAcceleration } } : {}),
|
|
2941
|
+
...(state.videoSource ? { videoSource: structuredClone(state.videoSource) } : {}),
|
|
2923
2942
|
status: state.status,
|
|
2924
2943
|
activeTabId: state.activeTabId ? truncateString(state.activeTabId, 128) : null,
|
|
2925
2944
|
viewportRevision: state.viewportRevision,
|
package/ts/confinement.ts
CHANGED
|
@@ -2,6 +2,32 @@ import * as plugins from './plugins.js';
|
|
|
2
2
|
import type { IBrowserConfinementProbeContext } from './interfaces.js';
|
|
3
3
|
import { BrowserRuntimeError } from './errors.js';
|
|
4
4
|
|
|
5
|
+
const restoredProbeState = (
|
|
6
|
+
before: plugins.smartpuppeteer.ILiveBrowserState,
|
|
7
|
+
after: plugins.smartpuppeteer.ILiveBrowserState,
|
|
8
|
+
): boolean => {
|
|
9
|
+
const restored = structuredClone(after);
|
|
10
|
+
const previousTab = before.tabs.find(tab => tab.id === before.activeTabId);
|
|
11
|
+
const currentTab = restored.tabs.find(tab => tab.id === before.activeTabId);
|
|
12
|
+
const previousSource = before.videoSource;
|
|
13
|
+
const currentSource = restored.videoSource;
|
|
14
|
+
// Native capture must reselect the original surface after an inactive probe
|
|
15
|
+
// window closes. Only its presentation epoch may advance; all document,
|
|
16
|
+
// viewport, tab, acceleration and source-identity fields still compare exactly.
|
|
17
|
+
if (previousTab && currentTab && previousSource?.frameAlignment === 2 && currentSource?.frameAlignment === 2
|
|
18
|
+
&& previousSource.tabId === previousTab.id && currentSource.tabId === currentTab.id
|
|
19
|
+
&& previousSource.generation === previousTab.generation && currentSource.generation === currentTab.generation
|
|
20
|
+
&& Number.isSafeInteger(currentTab.generation) && currentTab.generation >= previousTab.generation
|
|
21
|
+
&& Number.isInteger(previousSource.rtpTimestampFloor) && Number.isInteger(currentSource.rtpTimestampFloor)
|
|
22
|
+
&& currentSource.rtpTimestampFloor! >= 0 && currentSource.rtpTimestampFloor! <= 0xffffffff
|
|
23
|
+
&& ((currentSource.rtpTimestampFloor! - previousSource.rtpTimestampFloor!) >>> 0) < 0x80000000) {
|
|
24
|
+
currentTab.generation = previousTab.generation;
|
|
25
|
+
currentSource.generation = previousSource.generation;
|
|
26
|
+
currentSource.rtpTimestampFloor = previousSource.rtpTimestampFloor;
|
|
27
|
+
}
|
|
28
|
+
return JSON.stringify(restored) === JSON.stringify(before);
|
|
29
|
+
};
|
|
30
|
+
|
|
5
31
|
const listenOnUnsupportedPort = async (
|
|
6
32
|
allowedPorts: ReadonlySet<number>,
|
|
7
33
|
onRequest: () => void,
|
|
@@ -29,7 +55,6 @@ export const runProductionConfinementProbe = async (
|
|
|
29
55
|
context: IBrowserConfinementProbeContext,
|
|
30
56
|
): Promise<void> => {
|
|
31
57
|
const initialState = context.session.getState();
|
|
32
|
-
const initialStateJson = JSON.stringify(initialState);
|
|
33
58
|
let probeTabId: string | undefined;
|
|
34
59
|
let probeFailed = false;
|
|
35
60
|
let probeError: unknown;
|
|
@@ -112,7 +137,7 @@ export const runProductionConfinementProbe = async (
|
|
|
112
137
|
}
|
|
113
138
|
await closeFixture().catch((error) => cleanupErrors.push(error));
|
|
114
139
|
try {
|
|
115
|
-
if (
|
|
140
|
+
if (!restoredProbeState(initialState, context.session.getState())) {
|
|
116
141
|
cleanupErrors.push(new BrowserRuntimeError('CONFINEMENT_FAILED'));
|
|
117
142
|
}
|
|
118
143
|
} catch (error) {
|
package/ts/interfaces.ts
CHANGED
|
@@ -333,6 +333,7 @@ export type TBrowserRuntimeVideoOffer = Omit<plugins.smartpuppeteer.ILiveVideoOf
|
|
|
333
333
|
|
|
334
334
|
export interface IBrowserRuntimeState {
|
|
335
335
|
videoAcceleration?: plugins.smartpuppeteer.ILiveVideoAcceleration;
|
|
336
|
+
videoSource?: plugins.smartpuppeteer.ILiveVideoSource;
|
|
336
337
|
status: 'stopped' | 'starting' | 'running' | 'stopping';
|
|
337
338
|
activeTabId: string | null;
|
|
338
339
|
viewportRevision: number;
|