@mindexec/cli 0.2.137 → 0.2.139
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/package.json +1 -1
- package/scripts/remote-fast-mdm-browser-smoke.mjs +128 -35
- package/scripts/remote-fleet-render-smoke.mjs +6 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +14 -11
- package/wwwroot/service-worker-assets.js +2 -2
- package/wwwroot/service-worker.js +1 -1
package/package.json
CHANGED
|
@@ -13,6 +13,8 @@ const PAIR_TOKEN = 'remote-fast-mdm-browser-pair-token';
|
|
|
13
13
|
const LEASE_ID = 'remote-fast-mdm-browser-lease';
|
|
14
14
|
const LOCAL_BRIDGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
15
|
const REQUESTED_FPS = Number(process.env.MINDEXEC_REMOTE_MDM_BROWSER_REQUEST_FPS || 12);
|
|
16
|
+
const SAMPLE_MS = Number(process.env.MINDEXEC_REMOTE_MDM_BROWSER_SAMPLE_MS || 1500);
|
|
17
|
+
const MIN_PAINT_FPS = Number(process.env.MINDEXEC_REMOTE_MDM_BROWSER_MIN_PAINT_FPS || 8);
|
|
16
18
|
|
|
17
19
|
function wait(ms) {
|
|
18
20
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -161,14 +163,14 @@ async function launchBrowser(chromium) {
|
|
|
161
163
|
throw new Error(`Unable to launch a Chromium browser: ${lastError?.message || 'unknown error'}`);
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
async function
|
|
166
|
+
async function paintRemoteFramesInBrowser(page, wsUrl, deviceId, fps = REQUESTED_FPS) {
|
|
165
167
|
await page.setContent(`
|
|
166
168
|
<!doctype html>
|
|
167
169
|
<meta charset="utf-8">
|
|
168
170
|
<canvas id="screen" width="320" height="180"></canvas>
|
|
169
171
|
`);
|
|
170
172
|
|
|
171
|
-
return await page.evaluate(async ({ wsUrl, deviceId, fps }) => {
|
|
173
|
+
return await page.evaluate(async ({ wsUrl, deviceId, fps, sampleMs, minPaintFps }) => {
|
|
172
174
|
const canvas = document.getElementById('screen');
|
|
173
175
|
const context = canvas.getContext('2d', { alpha: false, desynchronized: true }) || canvas.getContext('2d');
|
|
174
176
|
if (!context) {
|
|
@@ -198,9 +200,112 @@ async function paintRemoteFrameInBrowser(page, wsUrl, deviceId, fps = REQUESTED_
|
|
|
198
200
|
} catch {
|
|
199
201
|
// ignore close failure
|
|
200
202
|
}
|
|
201
|
-
reject(new Error('timed-out-waiting-for-mdm-
|
|
203
|
+
reject(new Error('timed-out-waiting-for-mdm-frames'));
|
|
202
204
|
}, 20000);
|
|
203
205
|
const ws = new WebSocket(wsUrl);
|
|
206
|
+
const painted = [];
|
|
207
|
+
const receivedSeqs = [];
|
|
208
|
+
let firstPaintAt = 0;
|
|
209
|
+
let lastPaintAt = 0;
|
|
210
|
+
let processing = false;
|
|
211
|
+
let pendingPacket = null;
|
|
212
|
+
let settled = false;
|
|
213
|
+
|
|
214
|
+
function finishIfReady() {
|
|
215
|
+
if (settled || painted.length < 2 || !firstPaintAt || !lastPaintAt) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const elapsedMs = lastPaintAt - firstPaintAt;
|
|
220
|
+
if (elapsedMs < sampleMs) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const uniqueSeqs = new Set(painted.map(item => item.frameSeq));
|
|
225
|
+
const paintFps = painted.length > 1
|
|
226
|
+
? ((painted.length - 1) * 1000) / Math.max(1, elapsedMs)
|
|
227
|
+
: 0;
|
|
228
|
+
if (paintFps < minPaintFps) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
settled = true;
|
|
233
|
+
clearTimeout(timeout);
|
|
234
|
+
ws.onclose = null;
|
|
235
|
+
try {
|
|
236
|
+
ws.close();
|
|
237
|
+
} catch {
|
|
238
|
+
// ignore close failure
|
|
239
|
+
}
|
|
240
|
+
resolve({
|
|
241
|
+
framesReceived: receivedSeqs.length,
|
|
242
|
+
framesPainted: painted.length,
|
|
243
|
+
uniqueSeqs: uniqueSeqs.size,
|
|
244
|
+
firstSeq: painted[0]?.frameSeq || 0,
|
|
245
|
+
lastSeq: painted[painted.length - 1]?.frameSeq || 0,
|
|
246
|
+
durationMs: elapsedMs,
|
|
247
|
+
paintFps,
|
|
248
|
+
fps: painted[painted.length - 1]?.fps || 0,
|
|
249
|
+
width: painted[painted.length - 1]?.width || 0,
|
|
250
|
+
height: painted[painted.length - 1]?.height || 0,
|
|
251
|
+
minBytes: Math.min(...painted.map(item => item.byteLength).filter(value => value > 0)),
|
|
252
|
+
maxBytes: Math.max(...painted.map(item => item.byteLength).filter(value => value > 0)),
|
|
253
|
+
mimeType: painted[painted.length - 1]?.mimeType || '',
|
|
254
|
+
transport: painted[painted.length - 1]?.transport || ''
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function paintPacket(packet) {
|
|
259
|
+
const { metadata, payload } = packet;
|
|
260
|
+
const blob = new Blob([payload], { type: metadata.mimeType || 'image/jpeg' });
|
|
261
|
+
const bitmap = await createImageBitmap(blob);
|
|
262
|
+
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
263
|
+
bitmap.close?.();
|
|
264
|
+
const now = performance.now();
|
|
265
|
+
const frameSeq = Number(metadata.frameSeq || 0);
|
|
266
|
+
if (!firstPaintAt) {
|
|
267
|
+
firstPaintAt = now;
|
|
268
|
+
}
|
|
269
|
+
lastPaintAt = now;
|
|
270
|
+
painted.push({
|
|
271
|
+
frameSeq,
|
|
272
|
+
fps: Number(metadata.fps || 0),
|
|
273
|
+
width: Number(metadata.width || 0),
|
|
274
|
+
height: Number(metadata.height || 0),
|
|
275
|
+
byteLength: Number(metadata.byteLength || payload.byteLength || 0),
|
|
276
|
+
mimeType: String(metadata.mimeType || ''),
|
|
277
|
+
transport: String(metadata.transport || '')
|
|
278
|
+
});
|
|
279
|
+
finishIfReady();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function processLatest(packet) {
|
|
283
|
+
pendingPacket = packet;
|
|
284
|
+
if (processing) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
processing = true;
|
|
289
|
+
try {
|
|
290
|
+
while (pendingPacket && !settled) {
|
|
291
|
+
const next = pendingPacket;
|
|
292
|
+
pendingPacket = null;
|
|
293
|
+
await paintPacket(next);
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
settled = true;
|
|
297
|
+
clearTimeout(timeout);
|
|
298
|
+
try {
|
|
299
|
+
ws.close();
|
|
300
|
+
} catch {
|
|
301
|
+
// ignore close failure
|
|
302
|
+
}
|
|
303
|
+
reject(error);
|
|
304
|
+
} finally {
|
|
305
|
+
processing = false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
204
309
|
ws.binaryType = 'arraybuffer';
|
|
205
310
|
ws.onopen = () => {
|
|
206
311
|
ws.send(JSON.stringify({
|
|
@@ -238,39 +343,23 @@ async function paintRemoteFrameInBrowser(page, wsUrl, deviceId, fps = REQUESTED_
|
|
|
238
343
|
throw new Error('binary-ws-metadata-must-not-carry-frame-url-or-data-url');
|
|
239
344
|
}
|
|
240
345
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
context.fillStyle = '#000000';
|
|
244
|
-
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
245
|
-
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
246
|
-
bitmap.close?.();
|
|
247
|
-
const pixel = Array.from(context.getImageData(0, 0, 1, 1).data);
|
|
248
|
-
clearTimeout(timeout);
|
|
249
|
-
ws.onclose = null;
|
|
250
|
-
ws.close();
|
|
251
|
-
resolve({
|
|
252
|
-
frameSeq: Number(metadata.frameSeq || 0),
|
|
253
|
-
fps: Number(metadata.fps || 0),
|
|
254
|
-
width: Number(metadata.width || 0),
|
|
255
|
-
height: Number(metadata.height || 0),
|
|
256
|
-
byteLength: Number(metadata.byteLength || payload.byteLength || 0),
|
|
257
|
-
mimeType: String(metadata.mimeType || ''),
|
|
258
|
-
transport: String(metadata.transport || ''),
|
|
259
|
-
payloadBytes: payload.byteLength,
|
|
260
|
-
pixel
|
|
261
|
-
});
|
|
346
|
+
receivedSeqs.push(Number(metadata.frameSeq || 0));
|
|
347
|
+
await processLatest({ metadata, payload });
|
|
262
348
|
} catch (error) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
349
|
+
if (!settled) {
|
|
350
|
+
settled = true;
|
|
351
|
+
clearTimeout(timeout);
|
|
352
|
+
try {
|
|
353
|
+
ws.close();
|
|
354
|
+
} catch {
|
|
355
|
+
// ignore close failure
|
|
356
|
+
}
|
|
357
|
+
reject(error);
|
|
268
358
|
}
|
|
269
|
-
reject(error);
|
|
270
359
|
}
|
|
271
360
|
};
|
|
272
361
|
});
|
|
273
|
-
}, { wsUrl, deviceId, fps });
|
|
362
|
+
}, { wsUrl, deviceId, fps, sampleMs: SAMPLE_MS, minPaintFps: MIN_PAINT_FPS });
|
|
274
363
|
}
|
|
275
364
|
|
|
276
365
|
async function main() {
|
|
@@ -332,14 +421,18 @@ async function main() {
|
|
|
332
421
|
browser = await launchBrowser(chromium);
|
|
333
422
|
const page = await browser.newPage();
|
|
334
423
|
const wsUrl = `${hostBridge.baseUrl.replace(/^http:/, 'ws:')}/api/remote/frames/ws?token=${encodeURIComponent(BRIDGE_TOKEN)}`;
|
|
335
|
-
const painted = await
|
|
336
|
-
assert.ok(painted.
|
|
424
|
+
const painted = await paintRemoteFramesInBrowser(page, wsUrl, device.deviceId, REQUESTED_FPS);
|
|
425
|
+
assert.ok(painted.framesReceived >= painted.framesPainted, JSON.stringify(painted));
|
|
426
|
+
assert.ok(painted.framesPainted >= Math.max(4, Math.floor((SAMPLE_MS / 1000) * MIN_PAINT_FPS)), JSON.stringify(painted));
|
|
427
|
+
assert.ok(painted.uniqueSeqs >= painted.framesPainted - 1, JSON.stringify(painted));
|
|
428
|
+
assert.ok(painted.lastSeq > painted.firstSeq, JSON.stringify(painted));
|
|
429
|
+
assert.ok(painted.paintFps >= MIN_PAINT_FPS, JSON.stringify(painted));
|
|
337
430
|
assert.equal(painted.fps, REQUESTED_FPS, JSON.stringify(painted));
|
|
338
431
|
assert.ok(painted.width > 0 && painted.height > 0, JSON.stringify(painted));
|
|
339
|
-
assert.ok(painted.
|
|
432
|
+
assert.ok(painted.minBytes > 0 && painted.maxBytes >= painted.minBytes, JSON.stringify(painted));
|
|
340
433
|
assert.match(painted.mimeType, /^image\/(jpeg|png|webp)$/i, JSON.stringify(painted));
|
|
341
434
|
|
|
342
|
-
console.log(`RemoteFast MDM browser smoke OK (seq ${painted.
|
|
435
|
+
console.log(`RemoteFast MDM browser smoke OK (${painted.framesPainted} paints, ${painted.paintFps.toFixed(1)} fps, seq ${painted.firstSeq}-${painted.lastSeq}, ${painted.width}x${painted.height}, ${painted.minBytes}-${painted.maxBytes} bytes)`);
|
|
343
436
|
} finally {
|
|
344
437
|
if (browser) {
|
|
345
438
|
await browser.close().catch(() => undefined);
|
|
@@ -866,9 +866,14 @@ try {
|
|
|
866
866
|
assert.equal(binaryImage?.style.display, 'none');
|
|
867
867
|
assert.equal(diagnostics.imageBitmapCalls.length, imageBitmapCountBeforeBinary + 1);
|
|
868
868
|
assert.equal(diagnostics.objectUrlCalls.length, objectUrlCountBeforeBinary);
|
|
869
|
+
const binaryDrawCalls = document.canvasDrawCalls.slice(drawCountBeforeBinary);
|
|
869
870
|
assert.ok(
|
|
870
|
-
|
|
871
|
+
binaryDrawCalls.some(call => call.op === 'drawImage'),
|
|
871
872
|
'expected binary Blob frame to draw directly to canvas');
|
|
873
|
+
assert.equal(
|
|
874
|
+
binaryDrawCalls.some(call => call.op === 'clearRect' || call.op === 'fillRect'),
|
|
875
|
+
false,
|
|
876
|
+
'binary Blob frame paint must not clear/fill the canvas before drawing');
|
|
872
877
|
const alternateDevice = devices.find(device =>
|
|
873
878
|
device.Connected === true
|
|
874
879
|
&& device.DeviceId !== focusedDevice.DeviceId);
|
|
@@ -13461,7 +13461,7 @@
|
|
|
13461
13461
|
const REMOTE_FLEET_FRAME_BLOB_CACHE_LIMIT = 96;
|
|
13462
13462
|
const REMOTE_FLEET_LIVE_FRAME_DECODE_TIMEOUT_MS = 180;
|
|
13463
13463
|
const REMOTE_FLEET_THUMBNAIL_FRAME_DECODE_TIMEOUT_MS = 1200;
|
|
13464
|
-
const REMOTE_FLEET_BINARY_FRAME_MAX_DECODE_IN_FLIGHT =
|
|
13464
|
+
const REMOTE_FLEET_BINARY_FRAME_MAX_DECODE_IN_FLIGHT = 1;
|
|
13465
13465
|
const REMOTE_FLEET_BINARY_FRAME_WS_RECONNECT_MS = 1500;
|
|
13466
13466
|
const REMOTE_FLEET_BINARY_FRAME_SUBSCRIBE_MS = 1000;
|
|
13467
13467
|
const REMOTE_FLEET_BINARY_FRAME_STALE_FALLBACK_MS = 1400;
|
|
@@ -14269,7 +14269,7 @@
|
|
|
14269
14269
|
width: 100%;
|
|
14270
14270
|
height: 100%;
|
|
14271
14271
|
display: none;
|
|
14272
|
-
background:
|
|
14272
|
+
background: transparent;
|
|
14273
14273
|
`;
|
|
14274
14274
|
if (typeof preview?.insertBefore === 'function') {
|
|
14275
14275
|
preview.insertBefore(canvas, preview.firstChild || null);
|
|
@@ -14361,9 +14361,6 @@
|
|
|
14361
14361
|
|
|
14362
14362
|
context.imageSmoothingEnabled = true;
|
|
14363
14363
|
context.imageSmoothingQuality = 'medium';
|
|
14364
|
-
context.fillStyle = '#020617';
|
|
14365
|
-
context.clearRect(0, 0, size.width, size.height);
|
|
14366
|
-
context.fillRect(0, 0, size.width, size.height);
|
|
14367
14364
|
context.drawImage(bitmap, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
14368
14365
|
canvas._remoteFleetContentRect = {
|
|
14369
14366
|
left: dx / size.dpr,
|
|
@@ -18000,12 +17997,6 @@
|
|
|
18000
17997
|
const hasVisibleLiveTarget = getVisibleLiveFrameDeviceIds().length > 0
|
|
18001
17998
|
|| devices.some(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveCapable(device));
|
|
18002
17999
|
if (hasVisibleLiveTarget || hasActiveLiveStream) {
|
|
18003
|
-
ensureVisibleRemoteFleetLiveStreams().catch(error => {
|
|
18004
|
-
window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
|
|
18005
|
-
nodeId,
|
|
18006
|
-
error: error?.message || String(error || '')
|
|
18007
|
-
});
|
|
18008
|
-
});
|
|
18009
18000
|
const binaryFrameSocketStarted = startRemoteFleetBinaryFrameSocket(bodyView, () => {
|
|
18010
18001
|
const liveIds = getVisibleLiveFrameDeviceIds();
|
|
18011
18002
|
return liveIds.length > 0 ? liveIds : getVisibleFrameDeviceIds();
|
|
@@ -18022,6 +18013,18 @@
|
|
|
18022
18013
|
nodeId,
|
|
18023
18014
|
reason: 'binary-frame-socket-not-started'
|
|
18024
18015
|
});
|
|
18016
|
+
ensureVisibleRemoteFleetLiveStreams().catch(error => {
|
|
18017
|
+
window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
|
|
18018
|
+
nodeId,
|
|
18019
|
+
error: error?.message || String(error || '')
|
|
18020
|
+
});
|
|
18021
|
+
});
|
|
18022
|
+
} else {
|
|
18023
|
+
window.RuntimeTrace?.emit?.('remote.live.wsAutoStartRequested', {
|
|
18024
|
+
nodeId,
|
|
18025
|
+
transport: 'ws-binary',
|
|
18026
|
+
fps: REMOTE_FLEET_MONITOR_LIVE_FPS
|
|
18027
|
+
});
|
|
18025
18028
|
}
|
|
18026
18029
|
bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
|
|
18027
18030
|
if (!document.body.contains(bodyView)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
self.assetsManifest = {
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "7WvF4J3m",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"hash": "sha256-
|
|
89
|
+
"hash": "sha256-8Ndn0qAzhmRH7/OJtB0ve5LG2oN5e+PaAE7c0MyEzHM=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|