@mindexec/cli 0.2.442 → 0.2.444

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.
Files changed (22) hide show
  1. package/lzo1x.js +110 -0
  2. package/mode4-atlas-worker.js +440 -0
  3. package/mode4-atlas.js +130 -0
  4. package/package.json +6 -1
  5. package/remote-hub.js +35 -4
  6. package/server.js +162 -2
  7. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +12 -8
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +75 -2
  9. package/wwwroot/_framework/{MindExecution.Core.nxjlewduna.dll → MindExecution.Core.4moacskf64.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Kernel.vrfi63yg9f.dll → MindExecution.Kernel.pa251cfsz4.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Admin.1rcwhdxl0r.dll → MindExecution.Plugins.Admin.dxyuwmxv1s.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Business.rx0eieg51y.dll → MindExecution.Plugins.Business.7uj5erqhhn.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.Concept.zzczjzhvk1.dll → MindExecution.Plugins.Concept.108rmsxc96.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.Directory.bsorcntiwp.dll → MindExecution.Plugins.Directory.ccsy3g0fe5.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll → MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.1mpn9satft.dll → MindExecution.Plugins.YouTube.q8ml98phj9.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Shared.mpi1gzau9b.dll → MindExecution.Shared.2pywq19zjy.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Web.nxykcyxd02.dll → MindExecution.Web.qucbls1wfr.dll} +0 -0
  19. package/wwwroot/_framework/blazor.boot.json +21 -21
  20. package/wwwroot/index.html +1 -1
  21. package/wwwroot/service-worker-assets.js +25 -25
  22. package/wwwroot/service-worker.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.442",
3
+ "version": "0.2.444",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -10,6 +10,9 @@
10
10
  "launch-bridge.cjs",
11
11
  "server.js",
12
12
  "remote-hub.js",
13
+ "lzo1x.js",
14
+ "mode4-atlas.js",
15
+ "mode4-atlas-worker.js",
13
16
  "codex-runtime.js",
14
17
  "codex-model-catalog.js",
15
18
  "port-guard.cjs",
@@ -59,11 +62,13 @@
59
62
  "node": ">=20"
60
63
  },
61
64
  "dependencies": {
65
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
62
66
  "@mindexec/remote": "^0.1.17",
63
67
  "@openai/codex-sdk": "^0.144.1",
64
68
  "chokidar": "^3.6.0",
65
69
  "cors": "^2.8.5",
66
70
  "express": "^4.18.2",
71
+ "ffmpeg-static": "^5.3.0",
67
72
  "multer": "^2.2.0",
68
73
  "playwright-core": "^1.53.0",
69
74
  "sharp": "^0.33.2",
package/remote-hub.js CHANGED
@@ -60,6 +60,17 @@ const REMOTE_FRAME_MODE_PROFILES = Object.freeze({
60
60
  modeVersion: 1,
61
61
  implemented: true
62
62
  }),
63
+ 'mode2-lzo': Object.freeze({
64
+ mode: 'mode2-lzo',
65
+ label: 'Mode 2 RGB565 LZO Wall',
66
+ transport: 'ws-binary',
67
+ encoding: 'image-raw',
68
+ codec: 'rgb565',
69
+ compression: 'lzo1x',
70
+ profile: 'mode2-lzo-rgb565-v1',
71
+ modeVersion: 2,
72
+ implemented: true
73
+ }),
63
74
  'mode3-h264-hw': Object.freeze({
64
75
  mode: 'mode3-h264-hw',
65
76
  label: 'Mode 3 H.264 Control',
@@ -103,6 +114,13 @@ const REMOTE_FRAME_MODE_ALIASES = new Map([
103
114
  ['remotequality', 'remote-quality'],
104
115
  ['remote_quality', 'remote-quality'],
105
116
  ['remote-quality', 'remote-quality'],
117
+ ['2', 'mode2-lzo'],
118
+ ['mode2', 'mode2-lzo'],
119
+ ['mode-2', 'mode2-lzo'],
120
+ ['mode2-lzo', 'mode2-lzo'],
121
+ ['lzo', 'mode2-lzo'],
122
+ ['lzo1x', 'mode2-lzo'],
123
+ ['rgb565', 'mode2-lzo'],
106
124
  ['mode3-h264', 'mode3-h264-hw'],
107
125
  ['mode3-h264-hw', 'mode3-h264-hw'],
108
126
  ['h264', 'mode3-h264-hw'],
@@ -2449,6 +2467,12 @@ export function createRemoteHub(options = {}) {
2449
2467
  fps: Number.isFinite(Number(message.fps)) ? Number(message.fps) : device.activeLiveStream.fps,
2450
2468
  streamPurpose: safeString(message.streamPurpose, 32),
2451
2469
  monitorIndex: Number.isFinite(Number(message.monitorIndex)) ? Number(message.monitorIndex) : 0,
2470
+ monitorCount: Number.isFinite(Number(message.monitorCount)) ? Number(message.monitorCount) : 0,
2471
+ pixelFormat: safeString(message.pixelFormat, 24),
2472
+ bytesPerPixel: Number.isFinite(Number(message.bytesPerPixel)) ? Number(message.bytesPerPixel) : 0,
2473
+ uncompressedByteLength: Number.isFinite(Number(message.uncompressedByteLength)) ? Number(message.uncompressedByteLength) : 0,
2474
+ sourceWidth: Number.isFinite(Number(message.sourceWidth)) ? Number(message.sourceWidth) : 0,
2475
+ sourceHeight: Number.isFinite(Number(message.sourceHeight)) ? Number(message.sourceHeight) : 0,
2452
2476
  h264Format: safeString(message.h264Format, 24),
2453
2477
  isKeyFrame: message.isKeyFrame === true,
2454
2478
  chunkType: safeString(message.chunkType, 24),
@@ -3260,9 +3284,11 @@ export function createRemoteHub(options = {}) {
3260
3284
 
3261
3285
  const now = new Date().toISOString();
3262
3286
  const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3263
- const fps = clampNumber(options.fps, 1, 30, 20);
3287
+ const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
3288
+ const requestedMode = options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3289
+ const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
3264
3290
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3265
- const transfer = buildRemoteFrameTransferDescriptor(options.mode || DEFAULT_REMOTE_FRAME_MODE);
3291
+ const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3266
3292
  device.counters.commandsSent += 1;
3267
3293
  device.activeLiveStream = {
3268
3294
  streamId,
@@ -3281,6 +3307,7 @@ export function createRemoteHub(options = {}) {
3281
3307
  transferProtocolVersion: transfer.transferProtocolVersion,
3282
3308
  handshake: transfer.handshake,
3283
3309
  fps,
3310
+ streamPurpose,
3284
3311
  startedAt: now,
3285
3312
  stoppedAt: '',
3286
3313
  stopReason: '',
@@ -3314,9 +3341,11 @@ export function createRemoteHub(options = {}) {
3314
3341
 
3315
3342
  const now = new Date().toISOString();
3316
3343
  const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3317
- const fps = clampNumber(options.fps, 1, 30, 20);
3344
+ const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
3345
+ const requestedMode = options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3346
+ const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
3318
3347
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3319
- const transfer = buildRemoteFrameTransferDescriptor(options.mode || DEFAULT_REMOTE_FRAME_MODE);
3348
+ const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3320
3349
  const result = sendCommand(deviceId, {
3321
3350
  command: 'stream.start',
3322
3351
  commandId,
@@ -3331,6 +3360,7 @@ export function createRemoteHub(options = {}) {
3331
3360
  transferProtocol: transfer.transferProtocol,
3332
3361
  transferProtocolVersion: transfer.transferProtocolVersion,
3333
3362
  handshake: transfer.handshake,
3363
+ streamPurpose,
3334
3364
  fps,
3335
3365
  maxWidth: clampNumber(options.maxWidth, 320, 2560, 960),
3336
3366
  maxHeight: clampNumber(options.maxHeight, 180, 1440, 540),
@@ -3360,6 +3390,7 @@ export function createRemoteHub(options = {}) {
3360
3390
  transferProtocolVersion: transfer.transferProtocolVersion,
3361
3391
  handshake: transfer.handshake,
3362
3392
  fps,
3393
+ streamPurpose,
3363
3394
  startedAt: now,
3364
3395
  stoppedAt: '',
3365
3396
  stopReason: '',
package/server.js CHANGED
@@ -24,6 +24,7 @@ import { fileURLToPath } from 'url';
24
24
  import { createCodexRuntime } from './codex-runtime.js';
25
25
  import { createCodexModelCatalog } from './codex-model-catalog.js';
26
26
  import { createRemoteHub } from './remote-hub.js';
27
+ import { Mode4AtlasSession } from './mode4-atlas.js';
27
28
  import portGuard from './port-guard.cjs';
28
29
 
29
30
  const execAsync = promisify(exec);
@@ -2399,12 +2400,15 @@ app.get('/api/local-files/:id', async (req, res) => {
2399
2400
  const httpServer = createServer(app);
2400
2401
  const wss = new WebSocketServer({ noServer: true });
2401
2402
  const remoteFrameWss = new WebSocketServer({ noServer: true });
2403
+ const remoteAtlasWss = new WebSocketServer({ noServer: true });
2402
2404
  const remoteInputWss = new WebSocketServer({ noServer: true });
2403
2405
  const wsClients = new Set();
2404
2406
  const remoteFrameClients = new Set();
2407
+ const remoteAtlasClients = new Map();
2405
2408
  const remoteInputClients = new Set();
2406
2409
  const shellJobs = new Map();
2407
2410
  let remoteFrameClientSeq = 0;
2411
+ let remoteAtlasClientSeq = 0;
2408
2412
  let remoteInputClientSeq = 0;
2409
2413
  const remoteFrameWsDiagnostics = {
2410
2414
  opened: 0,
@@ -2480,6 +2484,14 @@ httpServer.on('upgrade', (req, socket, head) => {
2480
2484
  return;
2481
2485
  }
2482
2486
 
2487
+ if (parsed.pathname === '/api/remote/atlas/ws'
2488
+ && (!bridgeAuthRequired || token === bridgeToken)) {
2489
+ remoteAtlasWss.handleUpgrade(req, socket, head, (ws) => {
2490
+ remoteAtlasWss.emit('connection', ws, req);
2491
+ });
2492
+ return;
2493
+ }
2494
+
2483
2495
  if (parsed.pathname === '/api/remote/input/ws'
2484
2496
  && (!bridgeAuthRequired || token === bridgeToken)) {
2485
2497
  remoteInputWss.handleUpgrade(req, socket, head, (ws) => {
@@ -2581,8 +2593,9 @@ function clampRemoteFrameWsNumber(value, min, max, fallback) {
2581
2593
  }
2582
2594
 
2583
2595
  function normalizeRemoteFrameWsLiveOptions(payload = {}) {
2584
- const mode = String(payload.frameMode || payload.mode || 'remote-fast').trim() || 'remote-fast';
2585
2596
  const streamPurpose = String(payload.streamPurpose || '').trim().toLowerCase();
2597
+ const defaultMode = streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo';
2598
+ const mode = String(payload.frameMode || payload.mode || defaultMode).trim() || defaultMode;
2586
2599
  const defaultFps = streamPurpose === 'control' || mode === 'mode3-h264-hw' ? 30 : REMOTE_FRAME_WS_DEFAULT_FPS;
2587
2600
  return {
2588
2601
  fps: clampRemoteFrameWsNumber(payload.fps, 1, 30, defaultFps),
@@ -2725,6 +2738,12 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
2725
2738
  sameContentStreak: Number(frame.sameContentStreak || 0) || 0,
2726
2739
  streamPurpose: frame.streamPurpose || '',
2727
2740
  monitorIndex: Number.isFinite(Number(frame.monitorIndex)) ? Number(frame.monitorIndex) : 0,
2741
+ monitorCount: Number.isFinite(Number(frame.monitorCount)) ? Number(frame.monitorCount) : 0,
2742
+ pixelFormat: frame.pixelFormat || '',
2743
+ bytesPerPixel: Number.isFinite(Number(frame.bytesPerPixel)) ? Number(frame.bytesPerPixel) : 0,
2744
+ uncompressedByteLength: Number.isFinite(Number(frame.uncompressedByteLength)) ? Number(frame.uncompressedByteLength) : 0,
2745
+ sourceWidth: Number.isFinite(Number(frame.sourceWidth)) ? Number(frame.sourceWidth) : 0,
2746
+ sourceHeight: Number.isFinite(Number(frame.sourceHeight)) ? Number(frame.sourceHeight) : 0,
2728
2747
  codec: frame.codec || '',
2729
2748
  h264Format: frame.h264Format || '',
2730
2749
  isKeyFrame: frame.isKeyFrame === true,
@@ -2733,6 +2752,15 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
2733
2752
  durationUs: Number.isFinite(Number(frame.durationUs)) ? Number(frame.durationUs) : 0,
2734
2753
  streamFrameSeq: Number.isFinite(Number(frame.streamFrameSeq)) ? Number(frame.streamFrameSeq) : 0,
2735
2754
  commandId: frame.commandId || ''
2755
+ ,layoutVersion: Number(frame.layoutVersion || 0) || 0
2756
+ ,atlasComposeMs: Number(frame.atlasComposeMs || 0) || 0
2757
+ ,atlasPoolStarved: Number(frame.atlasPoolStarved || 0) || 0
2758
+ ,atlasReadyTiles: Number(frame.atlasReadyTiles || 0) || 0
2759
+ ,atlasTileCount: Number(frame.atlasTileCount || 0) || 0
2760
+ ,hardwareEncoder: frame.hardwareEncoder || ''
2761
+ ,platformProfile: frame.platformProfile || ''
2762
+ ,workerGeneration: Number(frame.workerGeneration || 0) || 0
2763
+ ,encoderGeneration: Number(frame.encoderGeneration || 0) || 0
2736
2764
  };
2737
2765
  const metaBuffer = Buffer.from(JSON.stringify(metadata), 'utf8');
2738
2766
  const header = Buffer.allocUnsafe(4);
@@ -2740,7 +2768,84 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
2740
2768
  return Buffer.concat([header, metaBuffer, payload], header.length + metaBuffer.length + payload.length);
2741
2769
  }
2742
2770
 
2771
+ function sendMode4AtlasFrame(ws, output) {
2772
+ if (!ws || ws.readyState !== 1 || !output?.payload?.length) return;
2773
+ const metadata = output.metadata || {};
2774
+ const frameSeq = Number(metadata.frameSeq || 0) || 0;
2775
+ const isKeyFrame = metadata.isKeyFrame === true;
2776
+ const workerGeneration = Math.max(1, Number(metadata.workerGeneration || 1) || 1);
2777
+ const encoderGeneration = Math.max(1, Number(metadata.encoderGeneration || 1) || 1);
2778
+ const streamId = `${ws.remoteAtlasStreamId}-w${workerGeneration}-e${encoderGeneration}`;
2779
+ const width = Number(metadata.width || 1920) || 1920;
2780
+ const height = Number(metadata.height || 1080) || 1080;
2781
+ const frameEvent = {
2782
+ kind: 'live',
2783
+ deviceId: '__mindexec_mode4_atlas__',
2784
+ mimeType: 'video/h264',
2785
+ byteLength: output.payload.length,
2786
+ payload: output.payload,
2787
+ frame: {
2788
+ deviceId: '__mindexec_mode4_atlas__', frameSeq,
2789
+ streamFrameSeq: Number(metadata.streamFrameSeq || frameSeq) || frameSeq,
2790
+ streamId, commandId: streamId, width, height,
2791
+ sourceWidth: width, sourceHeight: height,
2792
+ mimeType: 'video/h264', mode: 'mode4-h264-atlas', frameMode: 'mode4-h264-atlas',
2793
+ frameProfile: 'mode4-h264-atlas-v1', encoding: 'video-atlas', codec: 'h264',
2794
+ compression: 'video/h264', h264Format: 'annexb', isKeyFrame,
2795
+ chunkType: isKeyFrame ? 'key' : 'delta',
2796
+ timestampUs: Number(metadata.timestampUs || 0), durationUs: Number(metadata.durationUs || 0),
2797
+ fps: Number(metadata.fps || 20), capturedAt: new Date().toISOString(), receivedAt: new Date().toISOString(),
2798
+ hardwareEncoder: String(metadata.hardwareEncoder || ''), platformProfile: 'mode4-hub-atlas-worker',
2799
+ workerGeneration, encoderGeneration, layoutVersion: Number(metadata.layoutVersion || 0),
2800
+ atlasComposeMs: Number(metadata.composeMs || 0), atlasPoolStarved: Number(metadata.poolStarved || 0),
2801
+ atlasReadyTiles: Number(metadata.readyTileCount || 0), atlasTileCount: Number(metadata.tileCount || 0)
2802
+ }
2803
+ };
2804
+ const packet = buildRemoteFrameBinaryPacket(frameEvent);
2805
+ if (!packet || ws.bufferedAmount > 8 * 1024 * 1024) return;
2806
+ try { ws.send(packet, { binary: true }); } catch { /* client closed */ }
2807
+ }
2808
+
2809
+ function normalizeRemoteAtlasMonitorSelections(value) {
2810
+ if (!value || typeof value !== 'object') return {};
2811
+ const result = {};
2812
+ for (const [key, entry] of Object.entries(value)) result[String(key)] = Math.max(0, Number(entry) || 0);
2813
+ return result;
2814
+ }
2815
+
2816
+ function configureMode4Atlas(ws, payload = {}) {
2817
+ const deviceIds = normalizeRemoteFrameWsDeviceIds(payload.deviceIds ?? payload.devices ?? payload.deviceId).slice(0, 100);
2818
+ const monitorSelections = normalizeRemoteAtlasMonitorSelections(payload.monitorSelections);
2819
+ const sharpTiles = Number(payload.tileWidth) >= 480 || Number(payload.tileHeight) >= 270;
2820
+ const tileWidth = sharpTiles ? 480 : 320;
2821
+ const tileHeight = sharpTiles ? 270 : 180;
2822
+ ws.remoteAtlasDeviceIds = new Set(deviceIds);
2823
+ ws.remoteAtlasInputOptions = { tileWidth, tileHeight, monitorSelections };
2824
+ ws.remoteAtlasSession.configure({
2825
+ deviceIds,
2826
+ width: clampRemoteFrameWsNumber(payload.width, 640, 3840, 1920),
2827
+ height: clampRemoteFrameWsNumber(payload.height, 360, 2160, 1080),
2828
+ tileWidth, tileHeight,
2829
+ fps: clampRemoteFrameWsNumber(payload.fps, 1, 30, 20)
2830
+ });
2831
+ for (const deviceId of deviceIds) startMode4AtlasInput(ws, deviceId, 'atlas-configure');
2832
+ sendRemoteFrameClientJson(ws, { type: 'Mode4AtlasSubscription', deviceIds, inputMode: 'mode2-lzo', outputMode: 'mode4-h264-atlas', tileWidth, tileHeight, timestamp: new Date().toISOString() });
2833
+ }
2834
+
2835
+ function startMode4AtlasInput(ws, deviceId, reason = 'atlas-configure') {
2836
+ const options = ws?.remoteAtlasInputOptions;
2837
+ if (!options || !ws.remoteAtlasDeviceIds?.has(deviceId)) return { ok: false, error: 'atlas-device-not-configured' };
2838
+ return remoteHub.startLiveStream(deviceId, {
2839
+ fps: 8, maxWidth: options.tileWidth, maxHeight: options.tileHeight, quality: 60,
2840
+ mode: 'mode2-lzo', frameMode: 'mode2-lzo', streamPurpose: 'atlas',
2841
+ monitorIndex: Number(options.monitorSelections?.[deviceId] || 0),
2842
+ streamId: `atlas-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
2843
+ restartReason: reason
2844
+ });
2845
+ }
2846
+
2743
2847
  function broadcastRemoteBinaryFrame(frameEvent) {
2848
+ for (const session of remoteAtlasClients.values()) session.ingest(frameEvent);
2744
2849
  const frame = frameEvent?.frame || {};
2745
2850
  remoteFrameWsDiagnostics.framesReceived += 1;
2746
2851
  remoteFrameWsDiagnostics.lastFrameAt = new Date().toISOString();
@@ -2901,6 +3006,44 @@ remoteFrameWss.on('connection', (ws, req) => {
2901
3006
  });
2902
3007
  });
2903
3008
 
3009
+ remoteAtlasWss.on('connection', (ws) => {
3010
+ ws.binaryType = 'arraybuffer';
3011
+ ws.remoteAtlasClientId = `ratlas-${++remoteAtlasClientSeq}`;
3012
+ ws.remoteAtlasStreamId = `mode4-atlas-${crypto.randomUUID()}`;
3013
+ try { ws._socket?.setNoDelay?.(true); } catch { /* ignore */ }
3014
+ const session = new Mode4AtlasSession({
3015
+ onFrame: output => sendMode4AtlasFrame(ws, output),
3016
+ onLayout: layout => sendRemoteFrameClientJson(ws, { ...layout, type: 'Mode4AtlasLayout' }),
3017
+ onStatus: status => sendRemoteFrameClientJson(ws, { ...status, type: 'Mode4AtlasStatus' })
3018
+ });
3019
+ ws.remoteAtlasSession = session;
3020
+ remoteAtlasClients.set(ws, session);
3021
+ let cleaned = false;
3022
+ const cleanup = () => {
3023
+ if (cleaned) return;
3024
+ cleaned = true;
3025
+ remoteAtlasClients.delete(ws);
3026
+ session.close();
3027
+ ws.remoteAtlasSession = null;
3028
+ };
3029
+ ws.on('message', data => {
3030
+ try {
3031
+ const payload = JSON.parse(Buffer.isBuffer(data) ? data.toString('utf8') : String(data || ''));
3032
+ if (payload?.type === 'subscribe' || payload?.type === 'configure') configureMode4Atlas(ws, payload);
3033
+ } catch {
3034
+ sendRemoteFrameClientJson(ws, { type: 'Mode4AtlasError', error: 'invalid-message' });
3035
+ }
3036
+ });
3037
+ ws.on('close', cleanup);
3038
+ ws.on('error', cleanup);
3039
+ sendRemoteFrameClientJson(ws, {
3040
+ type: 'Mode4AtlasSocketReady',
3041
+ protocol: 'mindexec.remote.atlas.h264.v1',
3042
+ clientId: ws.remoteAtlasClientId,
3043
+ timestamp: new Date().toISOString()
3044
+ });
3045
+ });
3046
+
2904
3047
  function sendRemoteInputClientJson(ws, payload) {
2905
3048
  if (!ws || ws.readyState !== WebSocket.OPEN) {
2906
3049
  return false;
@@ -12411,6 +12554,7 @@ app.get('/api/status', async (req, res) => {
12411
12554
  wsToken,
12412
12555
  wsPath: '/events',
12413
12556
  remoteFrameWsPath: '/api/remote/frames/ws',
12557
+ remoteAtlasWsPath: '/api/remote/atlas/ws',
12414
12558
  remoteInputWsPath: '/api/remote/input/ws',
12415
12559
  bridgeToken,
12416
12560
  bridgeTokenHeader,
@@ -12819,9 +12963,11 @@ app.get('/api/remote/devices/:deviceId/live/frame', (req, res) => {
12819
12963
  });
12820
12964
 
12821
12965
  app.post('/api/remote/devices/:deviceId/live/start', (req, res) => {
12966
+ const streamPurpose = String(req.body?.streamPurpose || '').trim().toLowerCase();
12822
12967
  res.json(remoteHub.startLiveStream(req.params.deviceId, {
12823
12968
  streamId: req.body?.streamId,
12824
- mode: req.body?.frameMode || req.body?.mode || 'mode3-h264-hw',
12969
+ mode: req.body?.frameMode || req.body?.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo'),
12970
+ streamPurpose,
12825
12971
  fps: req.body?.fps,
12826
12972
  maxWidth: req.body?.maxWidth,
12827
12973
  maxHeight: req.body?.maxHeight,
@@ -14333,6 +14479,14 @@ async function shutdownBridge(signal) {
14333
14479
  }
14334
14480
  }
14335
14481
 
14482
+ for (const client of remoteAtlasClients.keys()) {
14483
+ try { client.close(); } catch { /* Ignore close race */ }
14484
+ }
14485
+ for (const session of remoteAtlasClients.values()) {
14486
+ try { session.close(); } catch { /* Ignore atlas worker shutdown errors */ }
14487
+ }
14488
+ remoteAtlasClients.clear();
14489
+
14336
14490
  for (const client of remoteInputClients) {
14337
14491
  try {
14338
14492
  client.close();
@@ -14347,6 +14501,12 @@ async function shutdownBridge(signal) {
14347
14501
  // Ignore if already closed
14348
14502
  }
14349
14503
 
14504
+ try {
14505
+ remoteAtlasWss.close();
14506
+ } catch {
14507
+ // Ignore if already closed
14508
+ }
14509
+
14350
14510
  try {
14351
14511
  remoteInputWss.close();
14352
14512
  } catch {
@@ -15955,7 +15955,7 @@
15955
15955
  maxWidth: Number(options.maxWidth || 960) || 960,
15956
15956
  maxHeight: Number(options.maxHeight || 540) || 540,
15957
15957
  quality: Number(options.quality || 60) || 60,
15958
- mode: String(options.mode || 'mode3-h264-hw'),
15958
+ mode: String(options.mode || 'mode2-lzo'),
15959
15959
  streamPurpose: String(options.streamPurpose || 'wall')
15960
15960
  };
15961
15961
  bodyView._remoteFleetBinaryFrameSession = session;
@@ -17192,13 +17192,15 @@
17192
17192
  function applyRemoteFleetFramePatchToPreview(preview, frame) {
17193
17193
  if (frame?._remoteFleetBinaryFrame === true
17194
17194
  && frame?._remoteFleetPayloadBlob
17195
- && String(frame.codec || '').toLowerCase().includes('h264')
17196
- && window.MindExecRemoteCodecs?.decodeH264Frame) {
17195
+ && (String(frame.codec || '').toLowerCase().includes('h264')
17196
+ || (String(frame.frameMode || frame.mode || '').toLowerCase() === 'mode2-lzo'
17197
+ && String(frame.compression || '').toLowerCase() === 'lzo1x'))
17198
+ && window.MindExecRemoteCodecs?.decodeFrame) {
17197
17199
  const canvas = ensureRemoteFleetFrameCanvas(preview);
17198
17200
  if (canvas) {
17199
17201
  canvas.style.display = 'block';
17200
17202
  preview.querySelector?.('img[data-remote-fleet-frame-image="true"]')?.style.setProperty('display', 'none');
17201
- window.MindExecRemoteCodecs.decodeH264Frame(canvas, frame).catch?.(() => undefined);
17203
+ window.MindExecRemoteCodecs.decodeFrame(canvas, frame).catch?.(() => undefined);
17202
17204
  preview.dataset.remoteFleetFrameKind = String(frame.kind || 'live').toLowerCase();
17203
17205
  preview.dataset.remoteFleetFrameSeq = String(frame.frameSeq || 0);
17204
17206
  return true;
@@ -17535,9 +17537,11 @@
17535
17537
  }
17536
17538
 
17537
17539
  if (frame._remoteFleetPayloadBlob
17538
- && String(frame.codec || '').toLowerCase().includes('h264')
17539
- && window.MindExecRemoteCodecs?.decodeH264Frame) {
17540
- const painted = await window.MindExecRemoteCodecs.decodeH264Frame(session.canvas, frame);
17540
+ && (String(frame.codec || '').toLowerCase().includes('h264')
17541
+ || (String(frame.frameMode || frame.mode || '').toLowerCase() === 'mode2-lzo'
17542
+ && String(frame.compression || '').toLowerCase() === 'lzo1x'))
17543
+ && window.MindExecRemoteCodecs?.decodeFrame) {
17544
+ const painted = await window.MindExecRemoteCodecs.decodeFrame(session.canvas, frame);
17541
17545
  if (painted && session.active) {
17542
17546
  session.canvas.dataset.remoteFleetFrameKind = 'live';
17543
17547
  session.canvas.dataset.remoteFleetFrameSeq = String(frameSeq || 0);
@@ -20717,7 +20721,7 @@
20717
20721
  maxWidth: 960,
20718
20722
  maxHeight: 540,
20719
20723
  quality: 60,
20720
- mode: 'mode3-h264-hw',
20724
+ mode: 'mode2-lzo',
20721
20725
  streamPurpose: 'wall'
20722
20726
  });
20723
20727
  if (!binaryFrameSocketStarted) {
@@ -3,6 +3,7 @@
3
3
 
4
4
  const decoderByCanvas = new WeakMap();
5
5
  const MAX_DECODE_QUEUE = 3;
6
+ const rawFrameByCanvas = new WeakMap();
6
7
 
7
8
  function emit(type, data) {
8
9
  window.RuntimeTrace?.emit?.(type, data);
@@ -30,6 +31,73 @@
30
31
  state.pending = 0;
31
32
  }
32
33
 
34
+ // LiveDesk Mode 2 payloads are LZO1X-compressed little-endian RGB565.
35
+ // Keep this decoder local to the wall path so H.264 control surfaces remain
36
+ // fully independent.
37
+ function decodeLzo1x(input, outputLength) {
38
+ if (!(input instanceof Uint8Array) || input.length < 3) throw new Error('Invalid LZO1X block');
39
+ const output = new Uint8Array(outputLength);
40
+ const s = { i: 0, o: 0 };
41
+ const need = (n) => { if (s.i + n > input.length) throw new Error('LZO input overrun'); };
42
+ const literals = (n) => {
43
+ need(n);
44
+ if (s.o + n > output.length) throw new Error('LZO output overrun');
45
+ output.set(input.subarray(s.i, s.i + n), s.o); s.i += n; s.o += n;
46
+ };
47
+ const ext = (basis) => { let zeros = 0; while (true) { need(1); if (input[s.i] !== 0) break; zeros++; s.i++; } return zeros * 255 + basis + input[s.i++]; };
48
+ const match = (at, length) => {
49
+ const distance = s.o - at;
50
+ if (distance <= 0 || s.o + length > output.length) throw new Error('LZO match overrun');
51
+ for (let n = 0; n < length; n++) output[s.o++] = output[at + (n % distance)];
52
+ };
53
+ let state = 0; let matchLength = 0;
54
+ if (input[0] >= 22) { s.i++; literals(input[0] - 17); state = 4; }
55
+ else if (input[0] >= 18) { s.i++; state = input[0] - 17; literals(state); }
56
+ while (true) {
57
+ need(1); const instruction = input[s.i++]; let nextState; let at;
58
+ if ((instruction & 0xc0) !== 0) {
59
+ need(1); at = s.o - ((input[s.i++] << 3) + ((instruction >> 2) & 7) + 1); matchLength = (instruction >> 5) + 1; nextState = instruction & 3;
60
+ } else if ((instruction & 0x20) !== 0) {
61
+ matchLength = (instruction & 0x1f) + 2; if (matchLength === 2) matchLength += ext(31); need(2); const encoded = input[s.i++] | (input[s.i++] << 8); at = s.o - ((encoded >> 2) + 1); nextState = encoded & 3;
62
+ } else if ((instruction & 0x10) !== 0) {
63
+ matchLength = (instruction & 7) + 2; if (matchLength === 2) matchLength += ext(7); need(2); const encoded = input[s.i++] | (input[s.i++] << 8); at = s.o - (((instruction & 8) << 11) + (encoded >> 2)); nextState = encoded & 3; if (at === s.o) break; at -= 0x4000;
64
+ } else if (state === 0) {
65
+ let length = instruction + 3; if (length === 3) length += ext(15); literals(length); state = 4; continue;
66
+ } else {
67
+ need(1); nextState = instruction & 3; at = s.o - ((instruction >> 2) + (input[s.i++] << 2) + (state === 4 ? 2049 : 1)); matchLength = state === 4 ? 3 : 2;
68
+ }
69
+ if (at < 0 || s.o + matchLength + nextState > output.length) throw new Error('LZO lookbehind overrun');
70
+ match(at, matchLength); state = nextState; literals(nextState);
71
+ }
72
+ if (matchLength !== 3 || s.i !== input.length || s.o !== output.length) throw new Error('Incomplete LZO block');
73
+ return output;
74
+ }
75
+
76
+ async function decodeMode2Frame(canvas, frame) {
77
+ const payload = frame?._remoteFleetPayloadBlob;
78
+ if (!canvas || !payload) return false;
79
+ const width = Math.max(1, Number(frame.width || 0) | 0);
80
+ const height = Math.max(1, Number(frame.height || 0) | 0);
81
+ const expected = width * height * 2;
82
+ if (Number(frame.bytesPerPixel || 2) !== 2 || (Number(frame.uncompressedByteLength || expected) !== expected)) return false;
83
+ const state = rawFrameByCanvas.get(canvas) || { lastSeq: 0 };
84
+ const seq = Number(frame.streamFrameSeq || frame.frameSeq || 0) || 0;
85
+ if (seq > 0 && seq <= state.lastSeq) return true;
86
+ const compressed = new Uint8Array(await payload.arrayBuffer());
87
+ const rgb565 = decodeLzo1x(compressed, expected);
88
+ const rgba = new Uint8ClampedArray(width * height * 4);
89
+ for (let source = 0, target = 0; source < rgb565.length; source += 2, target += 4) {
90
+ const pixel = rgb565[source] | (rgb565[source + 1] << 8);
91
+ const r = (pixel >> 11) & 31; const g = (pixel >> 5) & 63; const b = pixel & 31;
92
+ rgba[target] = (r << 3) | (r >> 2); rgba[target + 1] = (g << 2) | (g >> 4); rgba[target + 2] = (b << 3) | (b >> 2); rgba[target + 3] = 255;
93
+ }
94
+ canvas.width = width; canvas.height = height;
95
+ canvas.getContext('2d', { alpha: false, desynchronized: true })?.putImageData(new ImageData(rgba, width, height), 0, 0);
96
+ state.lastSeq = seq; rawFrameByCanvas.set(canvas, state);
97
+ emit('remote.mode2.paint', { frameSeq: seq, width, height });
98
+ return true;
99
+ }
100
+
33
101
  function ensureDecoder(canvas, frame, state) {
34
102
  if (typeof VideoDecoder !== 'function' || typeof EncodedVideoChunk !== 'function') {
35
103
  return null;
@@ -88,9 +156,14 @@
88
156
 
89
157
  async function decode(canvas, frame) {
90
158
  const payload = frame?._remoteFleetPayloadBlob;
91
- if (!canvas || !payload || !String(frame?.codec || '').toLowerCase().includes('h264')) {
159
+ if (!canvas || !payload) {
92
160
  return false;
93
161
  }
162
+ const mode = String(frame?.frameMode || frame?.mode || '').toLowerCase();
163
+ if (mode === 'mode2-lzo' || String(frame?.compression || '').toLowerCase() === 'lzo1x' || String(frame?.mimeType || '').toLowerCase() === 'application/vnd.livedesk.lzo-rgb565') {
164
+ try { return await decodeMode2Frame(canvas, frame); } catch (error) { emit('remote.mode2.decodeFailed', { error: error?.message || String(error || '') }); return false; }
165
+ }
166
+ if (!String(frame?.codec || '').toLowerCase().includes('h264')) return false;
94
167
  const state = getDecoderState(canvas);
95
168
  const seq = Number(frame.streamFrameSeq || frame.frameSeq || 0) || 0;
96
169
  if (seq > 0 && seq <= state.lastSeq) return true;
@@ -122,5 +195,5 @@
122
195
  }
123
196
  }
124
197
 
125
- window.MindExecRemoteCodecs = Object.freeze({ decodeH264Frame: decode });
198
+ window.MindExecRemoteCodecs = Object.freeze({ decodeH264Frame: decode, decodeFrame: decode });
126
199
  })();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-WNzAmWuVhn9JyBiHwwIttruk/GbsVNnakpghIldcLg8=",
4
+ "hash": "sha256-vyIwPs5NXG0WvUn1crlwB/OCRyxH6tUNE8CXfOaHuOI=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -122,16 +122,16 @@
122
122
  "System.brmz7yk5qh.dll": "System.dll",
123
123
  "netstandard.b50t77veor.dll": "netstandard.dll",
124
124
  "System.Private.CoreLib.g6ztz7cmo2.dll": "System.Private.CoreLib.dll",
125
- "MindExecution.Core.nxjlewduna.dll": "MindExecution.Core.dll",
126
- "MindExecution.Kernel.vrfi63yg9f.dll": "MindExecution.Kernel.dll",
127
- "MindExecution.Plugins.Admin.1rcwhdxl0r.dll": "MindExecution.Plugins.Admin.dll",
128
- "MindExecution.Plugins.Business.rx0eieg51y.dll": "MindExecution.Plugins.Business.dll",
129
- "MindExecution.Plugins.Concept.zzczjzhvk1.dll": "MindExecution.Plugins.Concept.dll",
130
- "MindExecution.Plugins.Directory.bsorcntiwp.dll": "MindExecution.Plugins.Directory.dll",
131
- "MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll": "MindExecution.Plugins.PlanMaster.dll",
132
- "MindExecution.Plugins.YouTube.1mpn9satft.dll": "MindExecution.Plugins.YouTube.dll",
133
- "MindExecution.Shared.mpi1gzau9b.dll": "MindExecution.Shared.dll",
134
- "MindExecution.Web.nxykcyxd02.dll": "MindExecution.Web.dll",
125
+ "MindExecution.Core.4moacskf64.dll": "MindExecution.Core.dll",
126
+ "MindExecution.Kernel.pa251cfsz4.dll": "MindExecution.Kernel.dll",
127
+ "MindExecution.Plugins.Admin.dxyuwmxv1s.dll": "MindExecution.Plugins.Admin.dll",
128
+ "MindExecution.Plugins.Business.7uj5erqhhn.dll": "MindExecution.Plugins.Business.dll",
129
+ "MindExecution.Plugins.Concept.108rmsxc96.dll": "MindExecution.Plugins.Concept.dll",
130
+ "MindExecution.Plugins.Directory.ccsy3g0fe5.dll": "MindExecution.Plugins.Directory.dll",
131
+ "MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll": "MindExecution.Plugins.PlanMaster.dll",
132
+ "MindExecution.Plugins.YouTube.q8ml98phj9.dll": "MindExecution.Plugins.YouTube.dll",
133
+ "MindExecution.Shared.2pywq19zjy.dll": "MindExecution.Shared.dll",
134
+ "MindExecution.Web.qucbls1wfr.dll": "MindExecution.Web.dll",
135
135
  "dotnet.native.566r55w3xu.js": "dotnet.native.js",
136
136
  "dotnet.native.x6q5aixc38.wasm": "dotnet.native.wasm",
137
137
  "dotnet.js": "dotnet.js",
@@ -276,18 +276,18 @@
276
276
  "System.Xml.XDocument.sn51jas17n.dll": "sha256-GNI2kFgFmPTwzuzwUn8gxK+AzGLUWRJFdg9JzIbrybQ=",
277
277
  "System.brmz7yk5qh.dll": "sha256-CfM2miyj1KHApFmqMdLYWio3S/jrdON2pW9Xr2nTwlo=",
278
278
  "netstandard.b50t77veor.dll": "sha256-//jQGOXjb8ET8WtXgOJrAxLx6E7zDJ5RjRRutwkvmxo=",
279
- "MindExecution.Core.nxjlewduna.dll": "sha256-K/aYKYR7u21eWdPhsuyT3DfAcldItQ9fUJDpyAW1Yk8=",
280
- "MindExecution.Kernel.vrfi63yg9f.dll": "sha256-pQ3AG4I0yHOE+LjWf41pPTzD6WAPx5QEVQR99tgmjv8=",
281
- "MindExecution.Plugins.Business.rx0eieg51y.dll": "sha256-wXky9gfkYuS9LpiAJcaHL2ySg388QTlafZ7dDYaYGkc=",
282
- "MindExecution.Plugins.Concept.zzczjzhvk1.dll": "sha256-caSJdsMUA/y2XoLlC69Ml7LVPyEklu9iUqK8LbLH/Ac=",
283
- "MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll": "sha256-NsaH6mfDGUtq96FRS/yR+b5QyLzjVWublyCQBKugpss=",
284
- "MindExecution.Shared.mpi1gzau9b.dll": "sha256-Bk/J98QCHrurXfAfiETiavqv5g5jXQ73yvEdYibuIrU=",
285
- "MindExecution.Web.nxykcyxd02.dll": "sha256-JW4hqFR8NT2yFKIvMflIhM8Wks9Dec4w+0ofv69tU6w="
279
+ "MindExecution.Core.4moacskf64.dll": "sha256-5OiBWkHcOvL4NsaHtQ7O3z5N1Ba7nTRi3xkehUMMgkE=",
280
+ "MindExecution.Kernel.pa251cfsz4.dll": "sha256-4dswyE6zmKAS+CnExpbXK871UMxIp+a7SPMIMwmZ+2Q=",
281
+ "MindExecution.Plugins.Business.7uj5erqhhn.dll": "sha256-URKvk2etEOiEa/URXJpkYx7aJcPQd1Xs0Yxn3Uqg/ok=",
282
+ "MindExecution.Plugins.Concept.108rmsxc96.dll": "sha256-QTd+zvSVcfwz3gYaKZ3pP0wgxrj9R1aP6qQKsByDRR0=",
283
+ "MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll": "sha256-M3Fvm8WU5xbx24cTyACzH1IuZXuIY06dkeCfNCgeUT4=",
284
+ "MindExecution.Shared.2pywq19zjy.dll": "sha256-Wjp+pPwb4TDj0YtYTzvNQGIzQI7xAg9DeyO0g/jxyxU=",
285
+ "MindExecution.Web.qucbls1wfr.dll": "sha256-0kpuM4z2XvRReo2Eiy7cJ/SAfoF/dVnn6WChaz//lGc="
286
286
  },
287
287
  "lazyAssembly": {
288
- "MindExecution.Plugins.Admin.1rcwhdxl0r.dll": "sha256-jV0N8WevIxIidWNinjH1vZ+NS+eKshBtlZpvb6vCfmg=",
289
- "MindExecution.Plugins.Directory.bsorcntiwp.dll": "sha256-O07lrIPzxWQRivTDifZBsSoG+ZMptwdu2Ey2e9sF0M0=",
290
- "MindExecution.Plugins.YouTube.1mpn9satft.dll": "sha256-l6UnB6LB9UHscjF/q5j8F2fKCunQ63w6mkwEEuWK3tg="
288
+ "MindExecution.Plugins.Admin.dxyuwmxv1s.dll": "sha256-URTQOP1nUuBC2leUgGdGWb+rc/1blCxbpbhRGE1MW/I=",
289
+ "MindExecution.Plugins.Directory.ccsy3g0fe5.dll": "sha256-BFk4mut2rsioBC9unD8+BoxnoJqTJlS5uZV7uXr8i5E=",
290
+ "MindExecution.Plugins.YouTube.q8ml98phj9.dll": "sha256-2vzs6nizBsYO1JjXPxdSi6O7uyZLpqFblwE0q09mvfw="
291
291
  }
292
292
  },
293
293
  "cacheBootResources": true,
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260717-livedesk-h264-transport-v936';
582
+ const scriptVersion = '20260717-livedesk-wall-lzo-control-h264-v937';
583
583
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
584
584
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
585
585
  const criticalScripts = [