@mindexec/cli 0.2.444 → 0.2.446

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 (26) hide show
  1. package/package.json +6 -3
  2. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  3. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  4. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  5. package/remote-hub.js +170 -5
  6. package/scripts/remote-fast-live-rate-smoke.mjs +1 -0
  7. package/server.js +12 -12
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +165 -10
  9. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +47 -58
  10. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4 -24
  11. package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +31 -18
  12. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +6 -3
  13. package/wwwroot/_framework/{MindExecution.Core.4moacskf64.dll → MindExecution.Core.djyzj5mxyk.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Kernel.pa251cfsz4.dll → MindExecution.Kernel.6e5cj9aijz.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.Admin.dxyuwmxv1s.dll → MindExecution.Plugins.Admin.siulh9tfrf.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.Business.7uj5erqhhn.dll → MindExecution.Plugins.Business.8cjtrc7qd3.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Plugins.Concept.108rmsxc96.dll → MindExecution.Plugins.Concept.rpp78mc75u.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Plugins.Directory.ccsy3g0fe5.dll → MindExecution.Plugins.Directory.wfv0ff9v4u.dll} +0 -0
  19. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll → MindExecution.Plugins.PlanMaster.jdppnbd6g4.dll} +0 -0
  20. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.q8ml98phj9.dll → MindExecution.Plugins.YouTube.a43f2q3pkw.dll} +0 -0
  21. package/wwwroot/_framework/{MindExecution.Shared.2pywq19zjy.dll → MindExecution.Shared.gwtyj0e9lw.dll} +0 -0
  22. package/wwwroot/_framework/{MindExecution.Web.qucbls1wfr.dll → MindExecution.Web.j3zaapcxtt.dll} +0 -0
  23. package/wwwroot/_framework/blazor.boot.json +21 -21
  24. package/wwwroot/index.html +1 -1
  25. package/wwwroot/service-worker-assets.js +28 -28
  26. 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.444",
3
+ "version": "0.2.446",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "scripts": {
26
26
  "start": "node launch-bridge.cjs",
27
27
  "dev": "node launch-bridge.cjs --watch",
28
- "test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check codex-model-catalog.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/npm-fresh-install-smoke.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/codex-model-catalog-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
28
+ "test:syntax": "node --check server.js && node --check remote-hub.js && node --check lzo1x.js && node --check mode4-atlas.js && node --check mode4-atlas-worker.js && node --check codex-runtime.js && node --check codex-model-catalog.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/npm-fresh-install-smoke.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/codex-model-catalog-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
29
29
  "test:codex-model-catalog": "node scripts/codex-model-catalog-smoke.mjs",
30
30
  "test:codex-model-catalog:live": "node scripts/codex-model-catalog-smoke.mjs --live",
31
31
  "test:auth": "node scripts/auth-session-smoke.mjs",
@@ -71,10 +71,13 @@
71
71
  "ffmpeg-static": "^5.3.0",
72
72
  "multer": "^2.2.0",
73
73
  "playwright-core": "^1.53.0",
74
- "sharp": "^0.33.2",
74
+ "sharp": "^0.35.3",
75
75
  "web-tree-sitter": "^0.22.6",
76
76
  "ws": "^8.16.0"
77
77
  },
78
+ "overrides": {
79
+ "body-parser": "1.20.6"
80
+ },
78
81
  "bin": {
79
82
  "mindexec": "launch-bridge.cjs",
80
83
  "mind-bridge": "launch-bridge.cjs"
package/remote-hub.js CHANGED
@@ -212,6 +212,30 @@ function getSupportedRemoteFrameModeProfiles() {
212
212
  .map(serializeRemoteFrameModeProfile);
213
213
  }
214
214
 
215
+ function resolveDeviceRemoteFrameMode(device, requestedMode, fallbackMode = DEFAULT_REMOTE_FRAME_MODE) {
216
+ const requested = normalizeRemoteFrameMode(requestedMode, fallbackMode);
217
+ if (device?.frameModesAdvertised === false) {
218
+ return normalizeRemoteFrameMode(device?.frameProtocol?.defaultMode, DEFAULT_REMOTE_FRAME_MODE);
219
+ }
220
+
221
+ const supportedModes = new Set(
222
+ (Array.isArray(device?.frameModes) ? device.frameModes : [])
223
+ .filter(profile => profile?.implemented !== false)
224
+ .map(profile => normalizeRemoteFrameMode(profile?.mode || profile?.frameMode || profile?.profile, ''))
225
+ .filter(Boolean));
226
+
227
+ if (supportedModes.size === 0 || supportedModes.has(requested)) {
228
+ return requested;
229
+ }
230
+
231
+ const deviceDefault = normalizeRemoteFrameMode(device?.frameProtocol?.defaultMode, fallbackMode);
232
+ if (supportedModes.has(deviceDefault)) {
233
+ return deviceDefault;
234
+ }
235
+
236
+ return supportedModes.values().next().value || normalizeRemoteFrameMode(fallbackMode);
237
+ }
238
+
215
239
  function buildRemoteFrameProtocolDescriptor() {
216
240
  return {
217
241
  protocol: REMOTE_FRAME_PROTOCOL,
@@ -1125,6 +1149,12 @@ export function createRemoteHub(options = {}) {
1125
1149
  const taskBatches = new Map();
1126
1150
  const sockets = new Map();
1127
1151
  const allSockets = new Set();
1152
+ const sideChannelSockets = {
1153
+ input: new Map(),
1154
+ frame: new Map(),
1155
+ audio: new Map(),
1156
+ file: new Map()
1157
+ };
1128
1158
  const duplicateDeviceLogAt = new Map();
1129
1159
  let server = null;
1130
1160
  let started = false;
@@ -1523,6 +1553,9 @@ export function createRemoteHub(options = {}) {
1523
1553
 
1524
1554
  function closeExistingDeviceSocket(deviceId, nextSessionId) {
1525
1555
  const existing = devices.get(deviceId);
1556
+ for (const channel of Object.keys(sideChannelSockets)) {
1557
+ closeSideChannel(existing, channel, 'replaced-by-new-session');
1558
+ }
1526
1559
  if (!existing?.socket || existing.socket.destroyed) {
1527
1560
  return;
1528
1561
  }
@@ -1537,6 +1570,37 @@ export function createRemoteHub(options = {}) {
1537
1570
  existing.socket.destroy();
1538
1571
  }
1539
1572
 
1573
+ function closeSideChannel(device, channel, reason = `${channel}-socket-closed`) {
1574
+ const socketKey = `${channel}Socket`;
1575
+ const socket = device?.[socketKey];
1576
+ if (!socket) return;
1577
+ device[socketKey] = null;
1578
+ sideChannelSockets[channel]?.delete(socket);
1579
+ try {
1580
+ writeJsonLine(socket, { type: 'disconnect', channel, reason });
1581
+ } catch {
1582
+ // Best-effort notice before closing the side channel.
1583
+ }
1584
+ try {
1585
+ socket.destroy?.();
1586
+ } catch {
1587
+ // Ignore close failures.
1588
+ }
1589
+ }
1590
+
1591
+ function detachSideChannel(socket, channel, reason = `${channel}-socket-closed`) {
1592
+ const channelMap = sideChannelSockets[channel];
1593
+ const deviceId = channelMap?.get(socket);
1594
+ channelMap?.delete(socket);
1595
+ if (!deviceId) return;
1596
+ const device = devices.get(deviceId);
1597
+ const socketKey = `${channel}Socket`;
1598
+ if (!device || device[socketKey] !== socket) return;
1599
+ device[socketKey] = null;
1600
+ device[`${channel}LastSeenAt`] = new Date().toISOString();
1601
+ emitRemoteEvent(`Remote${channel[0].toUpperCase()}${channel.slice(1)}SocketDisconnected`, device, { reason });
1602
+ }
1603
+
1540
1604
  function getDeviceActivityMs(device) {
1541
1605
  return Math.max(
1542
1606
  Date.parse(device?.lastSeenAt || '') || 0,
@@ -1841,6 +1905,7 @@ export function createRemoteHub(options = {}) {
1841
1905
  protocolVersion: Number.isFinite(Number(hello.protocolVersion)) ? Math.max(1, Math.floor(Number(hello.protocolVersion))) : 1,
1842
1906
  frameProtocol: frameProtocol || buildRemoteFrameProtocolDescriptor(),
1843
1907
  frameModes: frameModes.length > 0 ? frameModes : getSupportedRemoteFrameModeProfiles(),
1908
+ frameModesAdvertised: frameModes.length > 0,
1844
1909
  capabilities,
1845
1910
  connected: true,
1846
1911
  connectedAt: now,
@@ -1853,6 +1918,10 @@ export function createRemoteHub(options = {}) {
1853
1918
  status: {},
1854
1919
  latestThumbnail: null,
1855
1920
  latestLiveFrame: null,
1921
+ inputSocket: null,
1922
+ frameSocket: null,
1923
+ audioSocket: null,
1924
+ fileSocket: null,
1856
1925
  recentFramePayloads: {
1857
1926
  thumbnail: [],
1858
1927
  live: []
@@ -1885,6 +1954,40 @@ export function createRemoteHub(options = {}) {
1885
1954
  return device;
1886
1955
  }
1887
1956
 
1957
+ function attachSideChannel(socket, hello, channel) {
1958
+ const deviceId = normalizeDeviceId(hello.deviceId);
1959
+ const device = devices.get(deviceId);
1960
+ if (!device || !device.connected || !device.socket || device.socket.destroyed) {
1961
+ writeJsonLine(socket, { type: 'error', error: 'device-not-connected' });
1962
+ socket.destroy();
1963
+ return null;
1964
+ }
1965
+
1966
+ closeSideChannel(device, channel, `replaced-by-new-${channel}-socket`);
1967
+ const now = new Date().toISOString();
1968
+ device[`${channel}Socket`] = socket;
1969
+ device[`${channel}ConnectedAt`] = now;
1970
+ device[`${channel}LastSeenAt`] = now;
1971
+ sideChannelSockets[channel].set(socket, deviceId);
1972
+ writeJsonLine(socket, {
1973
+ type: 'welcome',
1974
+ channel,
1975
+ protocol: REMOTE_AGENT_PROTOCOL,
1976
+ protocolVersion: REMOTE_PROTOCOL_VERSION,
1977
+ sessionId: device.sessionId,
1978
+ deviceId,
1979
+ ...(channel === 'frame'
1980
+ ? {
1981
+ frameProtocol: buildRemoteFrameProtocolDescriptor(),
1982
+ frameModes: getSupportedRemoteFrameModeProfiles()
1983
+ }
1984
+ : {}),
1985
+ serverTime: now
1986
+ });
1987
+ emitRemoteEvent(`Remote${channel[0].toUpperCase()}${channel.slice(1)}SocketConnected`, device);
1988
+ return device;
1989
+ }
1990
+
1888
1991
  function detachSocket(socket, reason = 'socket-closed') {
1889
1992
  const deviceId = sockets.get(socket);
1890
1993
  sockets.delete(socket);
@@ -1906,6 +2009,9 @@ export function createRemoteHub(options = {}) {
1906
2009
  device.activeLiveStream.stoppedAt = device.disconnectedAt;
1907
2010
  device.activeLiveStream.stopReason = reason;
1908
2011
  }
2012
+ for (const channel of Object.keys(sideChannelSockets)) {
2013
+ closeSideChannel(device, channel, reason);
2014
+ }
1909
2015
  failAllPendingTasks(device, 'device-disconnected');
1910
2016
  logWarn('remote', `device disconnected ${device.deviceName} (${deviceId}): ${reason}`);
1911
2017
  emitRemoteEvent('RemoteDeviceDisconnected', device, { reason });
@@ -2570,6 +2676,16 @@ export function createRemoteHub(options = {}) {
2570
2676
  return;
2571
2677
  }
2572
2678
 
2679
+ const channel = safeString(message.channel || message.Channel, 40).toLowerCase();
2680
+ if (Object.hasOwn(sideChannelSockets, channel)) {
2681
+ const device = attachSideChannel(socket, message, channel);
2682
+ if (!device) return;
2683
+ state.authenticated = true;
2684
+ state.device = device;
2685
+ state.sideChannel = channel;
2686
+ return;
2687
+ }
2688
+
2573
2689
  const device = attachDevice(socket, message);
2574
2690
  if (!device) {
2575
2691
  return;
@@ -2586,6 +2702,17 @@ export function createRemoteHub(options = {}) {
2586
2702
  return;
2587
2703
  }
2588
2704
 
2705
+ if (state.sideChannel) {
2706
+ device[`${state.sideChannel}LastSeenAt`] = new Date().toISOString();
2707
+ if (state.sideChannel === 'input' && message.type === 'input.error') {
2708
+ emitRemoteEvent('RemoteInputError', device, {
2709
+ inputSeq: Number(message.inputSeq || 0) || 0,
2710
+ error: safeString(message.error, 600) || 'remote-input-failed'
2711
+ });
2712
+ }
2713
+ return;
2714
+ }
2715
+
2589
2716
  device.counters.messagesReceived += 1;
2590
2717
  device.lastSeenAt = new Date().toISOString();
2591
2718
 
@@ -2698,7 +2825,11 @@ export function createRemoteHub(options = {}) {
2698
2825
  const detach = reason => {
2699
2826
  allSockets.delete(socket);
2700
2827
  clearTimeout(helloTimer);
2701
- detachSocket(socket, reason);
2828
+ if (state.sideChannel) {
2829
+ detachSideChannel(socket, state.sideChannel, reason);
2830
+ } else {
2831
+ detachSocket(socket, reason);
2832
+ }
2702
2833
  };
2703
2834
 
2704
2835
  socket.onCloseMessage = reason => detach(reason || 'websocket-closed');
@@ -2867,12 +2998,20 @@ export function createRemoteHub(options = {}) {
2867
2998
  socket.on('close', () => {
2868
2999
  allSockets.delete(socket);
2869
3000
  clearTimeout(helloTimer);
2870
- detachSocket(socket);
3001
+ if (state.sideChannel) {
3002
+ detachSideChannel(socket, state.sideChannel);
3003
+ } else {
3004
+ detachSocket(socket);
3005
+ }
2871
3006
  });
2872
3007
  socket.on('error', err => {
2873
3008
  allSockets.delete(socket);
2874
3009
  clearTimeout(helloTimer);
2875
- detachSocket(socket, err?.message || 'socket-error');
3010
+ if (state.sideChannel) {
3011
+ detachSideChannel(socket, state.sideChannel, err?.message || 'socket-error');
3012
+ } else {
3013
+ detachSocket(socket, err?.message || 'socket-error');
3014
+ }
2876
3015
  });
2877
3016
  }
2878
3017
 
@@ -2936,6 +3075,7 @@ export function createRemoteHub(options = {}) {
2936
3075
  }
2937
3076
 
2938
3077
  sockets.clear();
3078
+ Object.values(sideChannelSockets).forEach(channelMap => channelMap.clear());
2939
3079
  if (!server) {
2940
3080
  started = false;
2941
3081
  return;
@@ -3058,10 +3198,29 @@ export function createRemoteHub(options = {}) {
3058
3198
  return { ok: false, error: 'missing-input-type' };
3059
3199
  }
3060
3200
 
3201
+ const inputSocket = device.inputSocket;
3202
+ if (inputSocket && !inputSocket.destroyed) {
3203
+ const sent = writeJsonLine(inputSocket, {
3204
+ type: 'input.control',
3205
+ payload: {
3206
+ ...normalized,
3207
+ hubForwardedAtEpochMs: Date.now(),
3208
+ issuedAt: normalized.issuedAt || new Date().toISOString()
3209
+ }
3210
+ });
3211
+ if (sent) {
3212
+ device.counters.commandsSent += 1;
3213
+ device.inputLastSeenAt = new Date().toISOString();
3214
+ return { ok: true, inputSocket: true };
3215
+ }
3216
+ detachSideChannel(inputSocket, 'input', 'input-socket-write-failed');
3217
+ }
3218
+
3061
3219
  return sendCommand(deviceId, {
3062
3220
  command: 'input.control',
3063
3221
  payload: {
3064
3222
  ...normalized,
3223
+ hubForwardedAtEpochMs: Date.now(),
3065
3224
  issuedAt: normalized.issuedAt || new Date().toISOString()
3066
3225
  }
3067
3226
  });
@@ -3285,7 +3444,10 @@ export function createRemoteHub(options = {}) {
3285
3444
  const now = new Date().toISOString();
3286
3445
  const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3287
3446
  const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
3288
- const requestedMode = options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3447
+ const requestedMode = resolveDeviceRemoteFrameMode(
3448
+ device,
3449
+ options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo'),
3450
+ streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3289
3451
  const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
3290
3452
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3291
3453
  const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
@@ -3342,7 +3504,10 @@ export function createRemoteHub(options = {}) {
3342
3504
  const now = new Date().toISOString();
3343
3505
  const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3344
3506
  const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
3345
- const requestedMode = options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3507
+ const requestedMode = resolveDeviceRemoteFrameMode(
3508
+ device,
3509
+ options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo'),
3510
+ streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
3346
3511
  const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
3347
3512
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3348
3513
  const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
@@ -293,6 +293,7 @@ async function main() {
293
293
  const live = await fetchJson(`${hostBridge.baseUrl}/api/remote/devices/${encodeURIComponent(device.deviceId)}/live/start`, {
294
294
  method: 'POST',
295
295
  body: JSON.stringify({
296
+ mode: 'remote-fast',
296
297
  fps: REQUESTED_FPS,
297
298
  maxWidth: 960,
298
299
  maxHeight: 540,
package/server.js CHANGED
@@ -2751,16 +2751,16 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
2751
2751
  timestampUs: Number.isFinite(Number(frame.timestampUs)) ? Number(frame.timestampUs) : 0,
2752
2752
  durationUs: Number.isFinite(Number(frame.durationUs)) ? Number(frame.durationUs) : 0,
2753
2753
  streamFrameSeq: Number.isFinite(Number(frame.streamFrameSeq)) ? Number(frame.streamFrameSeq) : 0,
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
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
2764
2764
  };
2765
2765
  const metaBuffer = Buffer.from(JSON.stringify(metadata), 'utf8');
2766
2766
  const header = Buffer.allocUnsafe(4);
@@ -4354,7 +4354,7 @@ function appendRemoteAgentOutput(stream, chunk, stateConnectionKey = '') {
4354
4354
  const text = chunk.toString();
4355
4355
  const key = stream === 'stderr' ? 'stderrTail' : 'stdoutTail';
4356
4356
  remoteAgentState[key] = String((remoteAgentState[key] || '') + text).slice(-REMOTE_AGENT_STDIO_TAIL_CHARS);
4357
- if (/Connected to RemoteHub/i.test(text)) {
4357
+ if (/Connected to (?:RemoteHub|LiveDesk Hub)/i.test(text)) {
4358
4358
  remoteAgentState.ready = true;
4359
4359
  remoteAgentState.connectedAt = new Date().toISOString();
4360
4360
  remoteAgentState.needsReconnect = false;
@@ -5222,7 +5222,7 @@ function appendRemoteAgentAttemptOutput(attempt, stream, chunk) {
5222
5222
  const text = chunk.toString();
5223
5223
  const key = stream === 'stderr' ? 'stderrTail' : 'stdoutTail';
5224
5224
  attempt.state[key] = String((attempt.state[key] || '') + text).slice(-REMOTE_AGENT_STDIO_TAIL_CHARS);
5225
- if (/Connected to RemoteHub/i.test(text)) {
5225
+ if (/Connected to (?:RemoteHub|LiveDesk Hub)/i.test(text)) {
5226
5226
  attempt.state.ready = true;
5227
5227
  attempt.state.connectedAt = new Date().toISOString();
5228
5228
  attempt.state.needsReconnect = false;
@@ -44,6 +44,7 @@
44
44
  const NODE_VIEW_HOST_HYBRID = 'hybrid';
45
45
  const NODE_VIEW_RENDERER_VERSION = 'shared-node-view-v1';
46
46
  const CSS3D_ONLY_NODE_RENDERING = true;
47
+ const NATIVE_TEXT_SELECTION_CONTENT_SELECTORS = '.node-response, .code-body, .text-content, .markdown-body, .csv-table-scroll, .csv-table-cell, textarea, .map-node-memo__title, .map-node-memo__body, .map-node-memo__body-view, .map-node-memo__body-view *';
47
48
  const CSV_TABLE_MAX_RENDER_ROWS = 500;
48
49
  const CSV_TABLE_MAX_RENDER_COLUMNS = 80;
49
50
  const REMOTE_FLEET_DISPLAY_NAME = 'Multi Desktop Monitor';
@@ -826,6 +827,19 @@
826
827
  cursor: grab;
827
828
  }
828
829
 
830
+ .css3d-resolution-wrapper.is-text-selection-owner .node-response,
831
+ .css3d-resolution-wrapper.is-text-selection-owner .code-body,
832
+ .css3d-resolution-wrapper.is-text-selection-owner .text-content,
833
+ .css3d-resolution-wrapper.is-text-selection-owner .markdown-body,
834
+ .css3d-resolution-wrapper.is-text-selection-owner .csv-table-scroll,
835
+ .css3d-resolution-wrapper.is-text-selection-owner textarea,
836
+ .css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__title,
837
+ .css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__body,
838
+ .css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__body-view {
839
+ user-select: text !important;
840
+ -webkit-user-select: text !important;
841
+ }
842
+
829
843
  .css3d-resolution-wrapper.node-type-image,
830
844
  .css3d-resolution-wrapper.node-type-video,
831
845
  .css3d-resolution-wrapper.node-type-embed,
@@ -5160,6 +5174,38 @@
5160
5174
  return activeId === String(nodeId || '').trim();
5161
5175
  }
5162
5176
 
5177
+ function setNativeTextSelectionOwnerClass(module, nodeId = null) {
5178
+ if (!module) return false;
5179
+ const nextId = String(nodeId || '').trim();
5180
+ const previousId = String(module._nativeTextSelectionOwnerNodeId || '').trim();
5181
+ if (previousId === nextId) return true;
5182
+
5183
+ const getWrapper = id => {
5184
+ const entry = id ? module.nodeObjectsById?.get?.(id) : null;
5185
+ return entry?.cssObject?.element?.closest?.('.css3d-resolution-wrapper')
5186
+ || entry?.cssObject?.element
5187
+ || null;
5188
+ };
5189
+ const previousWrapper = getWrapper(previousId);
5190
+ previousWrapper?.classList?.remove?.('is-text-selection-owner');
5191
+ if (previousWrapper?.querySelectorAll) {
5192
+ previousWrapper.querySelectorAll(NATIVE_TEXT_SELECTION_CONTENT_SELECTORS).forEach(element => {
5193
+ element.style.userSelect = 'none';
5194
+ element.style.webkitUserSelect = 'none';
5195
+ });
5196
+ }
5197
+ const nextWrapper = getWrapper(nextId);
5198
+ nextWrapper?.classList?.add?.('is-text-selection-owner');
5199
+ if (nextWrapper?.querySelectorAll) {
5200
+ nextWrapper.querySelectorAll(NATIVE_TEXT_SELECTION_CONTENT_SELECTORS).forEach(element => {
5201
+ element.style.userSelect = 'text';
5202
+ element.style.webkitUserSelect = 'text';
5203
+ });
5204
+ }
5205
+ module._nativeTextSelectionOwnerNodeId = nextId || null;
5206
+ return true;
5207
+ }
5208
+
5163
5209
  function activateNativeTextSelectionSource(module, nodeId) {
5164
5210
  const id = String(nodeId || '').trim();
5165
5211
  if (!module || !id) {
@@ -5167,6 +5213,7 @@
5167
5213
  }
5168
5214
 
5169
5215
  module._nativeTextSelectionSourceNodeId = id;
5216
+ setNativeTextSelectionOwnerClass(module, id);
5170
5217
  ensureTextOverlaySourceVisible(module, id);
5171
5218
  return true;
5172
5219
  }
@@ -5179,6 +5226,7 @@
5179
5226
  }
5180
5227
 
5181
5228
  module._nativeTextSelectionSourceNodeId = null;
5229
+ setNativeTextSelectionOwnerClass(module, null);
5182
5230
  return true;
5183
5231
  }
5184
5232
 
@@ -14923,6 +14971,7 @@
14923
14971
  const remoteFleetHostLeaseTimers = new Map();
14924
14972
  const remoteFleetLocalHostTargets = new Map();
14925
14973
  const remoteFleetBinaryFrameSessions = new Map();
14974
+ const remoteFleetAtlasSessions = new Map();
14926
14975
  const remoteFleetLatestBinaryFrames = new Map();
14927
14976
  const remoteFleetFrameBlobCache = new Map();
14928
14977
  let remoteFleetBridgeStatusPromise = null;
@@ -15137,6 +15186,7 @@
15137
15186
  closeRemoteFleetControlNodeSession(bodyView, 'clear');
15138
15187
  if (options.keepBinaryFrameSocket !== true) {
15139
15188
  releaseRemoteFleetBinaryFrameSocket(bodyView);
15189
+ releaseRemoteFleetAtlasSocket(bodyView);
15140
15190
  }
15141
15191
  if (bodyView._remoteFleetLiveRefreshTimer) {
15142
15192
  clearInterval(bodyView._remoteFleetLiveRefreshTimer);
@@ -15252,6 +15302,17 @@
15252
15302
  return url.toString();
15253
15303
  }
15254
15304
 
15305
+ function buildRemoteFleetAtlasWsUrl(status) {
15306
+ const baseUrl = String(status?.__remoteFleetBridgeBaseUrl || window.location?.origin || '').trim();
15307
+ const path = String(status?.remoteAtlasWsPath || '/api/remote/atlas/ws').trim() || '/api/remote/atlas/ws';
15308
+ if (!baseUrl) return '';
15309
+ const url = new URL(path, baseUrl);
15310
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
15311
+ const bridgeToken = String(status?.bridgeToken || '').trim();
15312
+ if (bridgeToken) url.searchParams.set('token', bridgeToken);
15313
+ return url.toString();
15314
+ }
15315
+
15255
15316
  async function parseRemoteFleetBinaryFrameMessage(data) {
15256
15317
  let buffer = null;
15257
15318
  if (data instanceof ArrayBuffer) {
@@ -15923,6 +15984,100 @@
15923
15984
  }
15924
15985
  }
15925
15986
 
15987
+ function paintRemoteFleetAtlasFrame(session, frame) {
15988
+ const atlasCanvas = session?.atlasCanvas;
15989
+ const tiles = Array.isArray(session?.layout?.tiles) ? session.layout.tiles : [];
15990
+ if (!atlasCanvas || tiles.length === 0) return;
15991
+ const targets = new Set();
15992
+ session.bodies.forEach(body => getRemoteFleetFramePatchTargets(body).forEach(target => targets.add(target)));
15993
+ const tileByDevice = new Map(tiles.map(tile => [String(tile.deviceId || '').trim(), tile]));
15994
+ targets.forEach(target => {
15995
+ target.querySelectorAll('[data-remote-fleet-device-preview]').forEach(preview => {
15996
+ const tile = tileByDevice.get(String(preview.dataset.deviceId || '').trim());
15997
+ if (!tile) return;
15998
+ const canvas = ensureRemoteFleetFrameCanvas(preview);
15999
+ const width = Math.max(1, Number(tile.width || 0) | 0);
16000
+ const height = Math.max(1, Number(tile.height || 0) | 0);
16001
+ canvas.width = width;
16002
+ canvas.height = height;
16003
+ const context = canvas.getContext('2d', { alpha: false, desynchronized: true });
16004
+ context?.drawImage(atlasCanvas, Number(tile.x || 0), Number(tile.y || 0), width, height, 0, 0, width, height);
16005
+ canvas.style.display = 'block';
16006
+ const image = preview.querySelector?.('img[data-remote-fleet-frame-image="true"]');
16007
+ if (image) image.style.display = 'none';
16008
+ preview.dataset.remoteFleetFrameKind = 'live';
16009
+ preview.dataset.remoteFleetFrameSeq = String(frame.frameSeq || 0);
16010
+ preview.dataset.remoteFleetFrameUrl = '';
16011
+ preview.dataset.remoteFleetHasFrame = 'true';
16012
+ });
16013
+ });
16014
+ window.RuntimeTrace?.emit?.('remote.atlas.painted', { nodeId: session.nodeId, frameSeq: frame.frameSeq || 0, tiles: tiles.length });
16015
+ }
16016
+
16017
+ function startRemoteFleetAtlasSocket(bodyView, getDeviceIds, options = {}) {
16018
+ const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
16019
+ if (!nodeId || typeof WebSocket !== 'function') return false;
16020
+ let session = remoteFleetAtlasSessions.get(nodeId);
16021
+ if (!session) {
16022
+ session = { nodeId, bodies: new Set(), ws: null, wsOpening: false, getDeviceIds: null, layout: null, atlasCanvas: document.createElement('canvas'), fallbackStarted: false };
16023
+ remoteFleetAtlasSessions.set(nodeId, session);
16024
+ }
16025
+ session.bodies.add(bodyView);
16026
+ session.getDeviceIds = getDeviceIds;
16027
+ session.options = { fps: 20, width: Number(options.width || 1920) || 1920, height: Number(options.height || 1080) || 1080, tileWidth: Number(options.tileWidth || 320) || 320, tileHeight: Number(options.tileHeight || 180) || 180 };
16028
+ const fallback = () => {
16029
+ if (session.fallbackStarted) return;
16030
+ session.fallbackStarted = true;
16031
+ try { session.ws?.close?.(); } catch { /* encoder failure fallback */ }
16032
+ session.bodies.forEach(body => startRemoteFleetBinaryFrameSocket(body, session.getDeviceIds, { autoStartLive: true, fps: 20, maxWidth: 960, maxHeight: 540, quality: 60, mode: 'mode2-lzo', streamPurpose: 'wall' }));
16033
+ window.RuntimeTrace?.emit?.('remote.atlas.fallback', { nodeId: session.nodeId, mode: 'mode2-lzo' });
16034
+ };
16035
+ session.fallback = fallback;
16036
+ if (session.ws || session.wsOpening) return true;
16037
+ session.wsOpening = true;
16038
+ getRemoteFleetBridgeStatusForFrames().then(status => {
16039
+ const wsUrl = buildRemoteFleetAtlasWsUrl(status);
16040
+ if (!wsUrl) throw new Error('remote-atlas-ws-url-unavailable');
16041
+ const ws = new WebSocket(wsUrl);
16042
+ session.ws = ws;
16043
+ ws.binaryType = 'arraybuffer';
16044
+ ws.onopen = () => {
16045
+ session.wsOpening = false;
16046
+ ws.send(JSON.stringify({ type: 'configure', deviceIds: getRemoteFleetBinaryFrameDeviceIds(session), fps: 20, width: session.options.width, height: session.options.height, tileWidth: session.options.tileWidth, tileHeight: session.options.tileHeight }));
16047
+ window.RuntimeTrace?.emit?.('remote.atlas.wsOpen', { nodeId: session.nodeId });
16048
+ };
16049
+ ws.onmessage = event => {
16050
+ if (typeof event.data === 'string') {
16051
+ try {
16052
+ const payload = JSON.parse(event.data);
16053
+ if (payload?.type === 'Mode4AtlasLayout') session.layout = payload;
16054
+ if (payload?.type === 'Mode4AtlasStatus' && (payload.state === 'error' || payload.error)) fallback();
16055
+ } catch { /* ignore status text */ }
16056
+ return;
16057
+ }
16058
+ parseRemoteFleetBinaryFrameMessage(event.data).then(frame => {
16059
+ if (!frame || String(frame.frameMode || frame.mode || '').toLowerCase() !== 'mode4-h264-atlas') return;
16060
+ session.atlasCanvas.width = Number(frame.width || session.options.width) || session.options.width;
16061
+ session.atlasCanvas.height = Number(frame.height || session.options.height) || session.options.height;
16062
+ window.MindExecRemoteCodecs?.decodeH264Frame?.(session.atlasCanvas, frame, () => paintRemoteFleetAtlasFrame(session, frame));
16063
+ }).catch(() => undefined);
16064
+ };
16065
+ ws.onerror = () => { fallback(); try { ws.close(); } catch {} };
16066
+ ws.onclose = () => { session.wsOpening = false; if (session.ws === ws) session.ws = null; if (!session.fallbackStarted) fallback(); };
16067
+ }).catch(error => { session.wsOpening = false; fallback(); window.RuntimeTrace?.emit?.('remote.atlas.wsOpenFailed', { nodeId, error: error?.message || String(error || '') }); });
16068
+ return true;
16069
+ }
16070
+
16071
+ function releaseRemoteFleetAtlasSocket(bodyView) {
16072
+ const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
16073
+ const session = nodeId ? remoteFleetAtlasSessions.get(nodeId) : null;
16074
+ if (!session) return;
16075
+ session.bodies.delete(bodyView);
16076
+ if (session.bodies.size > 0) return;
16077
+ try { session.ws?.close?.(); } catch {}
16078
+ remoteFleetAtlasSessions.delete(nodeId);
16079
+ }
16080
+
15926
16081
  function startRemoteFleetBinaryFrameSocket(bodyView, getDeviceIds, options = {}) {
15927
16082
  const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
15928
16083
  if (!nodeId || typeof WebSocket !== 'function') {
@@ -20712,22 +20867,20 @@
20712
20867
  || hasActiveLiveStream
20713
20868
  || (autoMonitorState && hasVisibleFrameTarget);
20714
20869
  if (shouldUseBinaryFrameSocket) {
20715
- const binaryFrameSocketStarted = startRemoteFleetBinaryFrameSocket(bodyView, () => {
20870
+ const atlasSocketStarted = startRemoteFleetAtlasSocket(bodyView, () => {
20716
20871
  const liveIds = getVisibleLiveFrameDeviceIds();
20717
20872
  return liveIds.length > 0 ? liveIds : getVisibleFrameDeviceIds();
20718
20873
  }, {
20719
- autoStartLive: true,
20720
20874
  fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
20721
- maxWidth: 960,
20722
- maxHeight: 540,
20723
- quality: 60,
20724
- mode: 'mode2-lzo',
20725
- streamPurpose: 'wall'
20875
+ width: 1920,
20876
+ height: 1080,
20877
+ tileWidth: 320,
20878
+ tileHeight: 180
20726
20879
  });
20727
- if (!binaryFrameSocketStarted) {
20880
+ if (!atlasSocketStarted) {
20728
20881
  window.RuntimeTrace?.emit?.('remote.frame.wsUnavailable', {
20729
20882
  nodeId,
20730
- reason: 'binary-frame-socket-not-started'
20883
+ reason: 'atlas-frame-socket-not-started'
20731
20884
  });
20732
20885
  ensureVisibleRemoteFleetLiveStreams().catch(error => {
20733
20886
  window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
@@ -20738,7 +20891,7 @@
20738
20891
  } else {
20739
20892
  window.RuntimeTrace?.emit?.('remote.live.wsAutoStartRequested', {
20740
20893
  nodeId,
20741
- transport: 'ws-binary',
20894
+ transport: 'ws-atlas-h264',
20742
20895
  fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
20743
20896
  visibleFrameTargets: visibleFrameDeviceIds.length,
20744
20897
  visibleLiveTargets: getVisibleLiveFrameDeviceIds().length
@@ -20758,6 +20911,7 @@
20758
20911
  }, REMOTE_FLEET_LIVE_REFRESH_MS);
20759
20912
  } else {
20760
20913
  releaseRemoteFleetBinaryFrameSocket(bodyView);
20914
+ releaseRemoteFleetAtlasSocket(bodyView);
20761
20915
  if (autoMonitorState && hasMonitorTargets) {
20762
20916
  startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
20763
20917
  startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'visible-devices');
@@ -25016,6 +25170,7 @@
25016
25170
  window.MindMapCss3DManager = {
25017
25171
  activateSelectableTextOverlay: activateSelectableTextOverlay,
25018
25172
  activateNativeTextSelectionSource: activateNativeTextSelectionSource,
25173
+ setNativeTextSelectionOwnerClass: setNativeTextSelectionOwnerClass,
25019
25174
  beginNodeEditingOverlay: beginNodeEditingOverlay,
25020
25175
  bindMemoWheelRouting: bindMemoWheelRouting,
25021
25176
  bindNoteEditingWheelZoom: bindNoteEditingWheelZoom,