@mindexec/cli 0.2.156 → 0.2.158

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.156",
3
+ "version": "0.2.158",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -585,15 +585,15 @@ function encodeRemoteBinaryFrame(metadata, payload = new Uint8Array([0xff, 0xd8,
585
585
  return buffer;
586
586
  }
587
587
 
588
- function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = [], lastError = '') {
588
+ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = [], lastError = '', nodeId = 'remote-fleet-render-smoke') {
589
589
  const connected = devices.filter(device => device.Connected).length;
590
590
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
591
591
  const pairToken = hubStatus.pairToken || 'render-smoke-token';
592
592
  const hostTargetState = hubStatus.hostTargetActive
593
- ? (hubStatus.hostTargetNodeId === 'remote-fleet-render-smoke' ? 'hosting' : 'other-monitor')
593
+ ? (hubStatus.hostTargetNodeId === nodeId ? 'hosting' : 'other-monitor')
594
594
  : 'inactive';
595
595
  return {
596
- id: 'remote-fleet-render-smoke',
596
+ id: nodeId,
597
597
  contentType: 'templateLauncher',
598
598
  metadata: {
599
599
  SemanticType: 'RemoteFleetMonitor',
@@ -625,10 +625,10 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
625
625
  };
626
626
  }
627
627
 
628
- function createRemoteFleetTemplateShell(document, focusDeviceId = '') {
628
+ function createRemoteFleetTemplateShell(document, focusDeviceId = '', nodeId = 'remote-fleet-render-smoke') {
629
629
  const nodeShell = document.createElement('div');
630
630
  nodeShell.setAttribute('class', 'map-node-template-card map-node-remote-fleet');
631
- nodeShell.dataset.nodeId = 'remote-fleet-render-smoke';
631
+ nodeShell.dataset.nodeId = nodeId;
632
632
  const shell = document.createElement('div');
633
633
  shell.setAttribute('class', 'template-card__shell template-card__shell--remote-fleet');
634
634
  const header = document.createElement('div');
@@ -642,7 +642,7 @@ function createRemoteFleetTemplateShell(document, focusDeviceId = '') {
642
642
  header.appendChild(titleWrap);
643
643
  const bodyView = document.createElement('div');
644
644
  bodyView.setAttribute('class', 'template-card__remote-fleet-body map-node-remote-fleet__body');
645
- bodyView.dataset.nodeId = 'remote-fleet-render-smoke';
645
+ bodyView.dataset.nodeId = nodeId;
646
646
  bodyView.dataset.remoteFleetAutoMonitor = 'false';
647
647
  if (focusDeviceId) {
648
648
  bodyView.dataset.remoteFleetFocusDeviceId = focusDeviceId;
@@ -1107,6 +1107,36 @@ try {
1107
1107
  assert.equal(rerenderedPatchPreview.dataset.remoteFleetFrameSeq, String(websocketFrameSeq));
1108
1108
  assert.equal(rerenderedPatchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]')?.style.display, 'block');
1109
1109
 
1110
+ const otherBoardNodeId = 'remote-fleet-render-smoke-other-board';
1111
+ const { nodeShell: otherBoardShell, bodyView: otherBoardBodyView } =
1112
+ createRemoteFleetTemplateShell(document, focusedDevice.DeviceId, otherBoardNodeId);
1113
+ document.body.appendChild(otherBoardShell);
1114
+ manager.renderRemoteFleetMonitorForTest(
1115
+ otherBoardBodyView,
1116
+ buildMonitorNode(
1117
+ devices,
1118
+ hub.getStatus({ includeSecrets: true }),
1119
+ latestTaskBatch,
1120
+ recentTaskBatches,
1121
+ '',
1122
+ otherBoardNodeId));
1123
+ await wait(30);
1124
+ const otherBoardPatchPreview = otherBoardBodyView
1125
+ .querySelector(`article[data-device-id="${patchTarget.DeviceId}"]`)
1126
+ ?.querySelector('[data-remote-fleet-device-preview="tile"]');
1127
+ assert.ok(otherBoardPatchPreview);
1128
+ assert.equal(
1129
+ otherBoardPatchPreview.dataset.remoteFleetFrameSeq,
1130
+ String(websocketFrameSeq),
1131
+ 'a different-board MDM should prime from the device-global latest binary frame cache');
1132
+ assert.equal(
1133
+ otherBoardPatchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]')?.style.display,
1134
+ 'block');
1135
+ assert.ok(diagnostics.runtimeTrace.some(event =>
1136
+ event.type === 'remote.frame.wsCachePrimed'
1137
+ && event.nodeId === otherBoardNodeId));
1138
+ otherBoardShell.remove();
1139
+
1110
1140
  const controlCard = bodyView.querySelector(`article[data-device-id="${patchTarget.DeviceId}"]`);
1111
1141
  assert.ok(controlCard);
1112
1142
  controlCard.dispatchEvent({ type: 'dblclick', button: 0 });
@@ -2282,9 +2282,26 @@ body.is-panning .mind-map-text-overlay-v2-card.is-passive .mind-map-text-overlay
2282
2282
  .css3d-resolution-wrapper.node-type-templatelauncher > .map-node-template-card {
2283
2283
  border-radius: 8px !important;
2284
2284
  background: #f8fafc;
2285
- box-shadow:
2286
- 0 18px 38px rgba(15, 23, 42, 0.16),
2287
- 0 0 0 1px rgba(15, 23, 42, 0.08);
2285
+ box-shadow: none !important;
2286
+ }
2287
+
2288
+ /* Template launcher and Multi Desktop Monitor own their visual chrome on the
2289
+ card itself. Keep the CSS3D wrapper neutral so motion CSS3D and the passive
2290
+ DOM overlay do not stack different selection glows. */
2291
+ .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2292
+ body.is-panning .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2293
+ body.is-zooming .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2294
+ body.mindcanvas-is-moving .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2295
+ body.mindcanvas-is-dense .css3d-resolution-wrapper.node-type-templatelauncher.selected {
2296
+ outline: none !important;
2297
+ -webkit-box-shadow: none !important;
2298
+ box-shadow: none !important;
2299
+ -webkit-filter: none !important;
2300
+ filter: none !important;
2301
+ }
2302
+
2303
+ .css3d-resolution-wrapper.node-type-templatelauncher.selected > .map-node-template-card {
2304
+ box-shadow: none !important;
2288
2305
  }
2289
2306
 
2290
2307
  .map-node-template-card,
@@ -5040,6 +5040,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5040
5040
  const hasLiveInteractiveDomOverlay =
5041
5041
  useTextOverlayV2 &&
5042
5042
  window.MindMapTextOverlayV2?.hasLiveInteractiveOverlay?.(this) === true;
5043
+ const hasPassiveDomOverlaySurface =
5044
+ shouldRenderPassiveDomTextOverlay ||
5045
+ hasLiveInteractiveDomOverlay;
5043
5046
  const overlayV2State = useTextOverlayV2 ? (this._textOverlayV2State || null) : null;
5044
5047
  const hasOverlayV2DirtyState = !!(
5045
5048
  overlayV2State && (
@@ -5088,11 +5091,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5088
5091
  this._overlayDebugRefreshAgeMs = timeSinceLastPassiveOverlayRefresh;
5089
5092
  this._overlayDebugRefreshDeferred = shouldDeferInteractivePassiveOverlayRefresh;
5090
5093
  const isPassiveOverlayIdleDelayActive =
5091
- shouldRenderPassiveDomTextOverlay &&
5094
+ hasPassiveDomOverlaySurface &&
5092
5095
  Number(this._passiveOverlayResumeAt || 0) > frameStart;
5093
5096
  const shouldSuppressPassiveOverlayDuringCameraMotion =
5094
5097
  useTextOverlayV2 &&
5095
- shouldRenderPassiveDomTextOverlay &&
5098
+ hasPassiveDomOverlaySurface &&
5096
5099
  (isPassiveOverlayIdleDelayActive ||
5097
5100
  this.isZooming === true ||
5098
5101
  this.isPanning === true ||
@@ -5157,7 +5160,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5157
5160
  !shouldUsePassiveOverlayCameraFastPath;
5158
5161
  const shouldUsePassiveOverlayMotionHint =
5159
5162
  useTextOverlayV2 &&
5160
- shouldRenderPassiveDomTextOverlay &&
5163
+ hasPassiveDomOverlaySurface &&
5161
5164
  !isPassiveOverlaySuppressed &&
5162
5165
  !hasOverlayFocus &&
5163
5166
  !hasOverlayV2DirtyState &&
@@ -797,24 +797,24 @@
797
797
 
798
798
  /* Preserve agent memo chrome during camera motion so middle-button panning
799
799
  does not visibly weaken their glow or console surfaces. */
800
- body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher),
801
- body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher),
802
- body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher),
803
- body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) {
800
+ body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent),
801
+ body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent),
802
+ body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent),
803
+ body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent) {
804
804
  transition: none !important;
805
805
  -webkit-transition: none !important;
806
806
  filter: none !important;
807
807
  -webkit-filter: none !important;
808
808
  }
809
809
 
810
- body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) .css3d-dynamic-node,
811
- body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) .css3d-dynamic-node,
812
- body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) .css3d-dynamic-node,
813
- body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) .css3d-dynamic-node,
814
- body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) [id^="css3d-node-"],
815
- body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) [id^="css3d-node-"],
816
- body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) [id^="css3d-node-"],
817
- body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent):not(.node-type-templatelauncher) [id^="css3d-node-"] {
810
+ body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent) .css3d-dynamic-node,
811
+ body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent) .css3d-dynamic-node,
812
+ body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent) .css3d-dynamic-node,
813
+ body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent) .css3d-dynamic-node,
814
+ body.is-zooming .css3d-resolution-wrapper:not(.selection-style-agent) [id^="css3d-node-"],
815
+ body.is-panning .css3d-resolution-wrapper:not(.selection-style-agent) [id^="css3d-node-"],
816
+ body.mindcanvas-is-moving .css3d-resolution-wrapper:not(.selection-style-agent) [id^="css3d-node-"],
817
+ body.mindcanvas-is-dense .css3d-resolution-wrapper:not(.selection-style-agent) [id^="css3d-node-"] {
818
818
  box-shadow: none !important;
819
819
  filter: none !important;
820
820
  -webkit-filter: none !important;
@@ -4013,7 +4013,9 @@
4013
4013
 
4014
4014
  function isLiveOverlayPortalNode(nodeModel) {
4015
4015
  const contentType = String(nodeModel?.contentType ?? nodeModel?.ContentType ?? '').trim().toLowerCase();
4016
- return contentType === CSV_TABLE_CONTENT_TYPE || isTemplateLauncherNode(nodeModel);
4016
+ return contentType === CSV_TABLE_CONTENT_TYPE
4017
+ || isTemplateLauncherNode(nodeModel)
4018
+ || isBusinessAutomationNode(nodeModel);
4017
4019
  }
4018
4020
 
4019
4021
  function isBusinessAutomationContextSourceNode(nodeModel) {
@@ -13509,6 +13511,7 @@
13509
13511
  const REMOTE_FLEET_BINARY_FRAME_CONNECT_GRACE_MS = 1600;
13510
13512
  const REMOTE_FLEET_BINARY_FRAME_CACHE_MS = 10000;
13511
13513
  const REMOTE_FLEET_BINARY_FRAME_CACHE_LIMIT = 240;
13514
+ const REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS = 8000;
13512
13515
  const REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL = 8;
13513
13516
  const REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS = 3000;
13514
13517
  const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
@@ -13520,6 +13523,7 @@
13520
13523
  const remoteFleetHostLeaseTimers = new Map();
13521
13524
  const remoteFleetLocalHostTargets = new Map();
13522
13525
  const remoteFleetBinaryFrameSessions = new Map();
13526
+ const remoteFleetLatestBinaryFrames = new Map();
13523
13527
  const remoteFleetFrameBlobCache = new Map();
13524
13528
  let remoteFleetBridgeStatusPromise = null;
13525
13529
  let activeRemoteFleetControlPopup = null;
@@ -13946,36 +13950,85 @@
13946
13950
  }
13947
13951
  }
13948
13952
 
13953
+ function pruneRemoteFleetGlobalBinaryFrameCache() {
13954
+ const now = getRemoteFleetFrameNow();
13955
+ for (const [deviceId, entry] of remoteFleetLatestBinaryFrames) {
13956
+ if (!deviceId
13957
+ || !entry
13958
+ || !entry.frame
13959
+ || now - Number(entry.seenAt || 0) > REMOTE_FLEET_BINARY_FRAME_CACHE_MS
13960
+ || remoteFleetLatestBinaryFrames.size > REMOTE_FLEET_BINARY_FRAME_CACHE_LIMIT) {
13961
+ remoteFleetLatestBinaryFrames.delete(deviceId);
13962
+ }
13963
+ }
13964
+ }
13965
+
13949
13966
  function rememberRemoteFleetBinaryFrame(session, frame) {
13950
13967
  const deviceId = String(frame?.deviceId || '').trim();
13951
- if (!session || !deviceId || !frame?._remoteFleetPayloadBlob) {
13968
+ if (!deviceId || !frame?._remoteFleetPayloadBlob) {
13952
13969
  return;
13953
13970
  }
13954
13971
 
13955
- if (!(session.latestBinaryFrames instanceof Map)) {
13956
- session.latestBinaryFrames = new Map();
13957
- }
13958
-
13959
- pruneRemoteFleetBinaryFrameCache(session);
13960
- session.latestBinaryFrames.set(deviceId, {
13972
+ const entry = {
13961
13973
  seenAt: getRemoteFleetFrameNow(),
13962
13974
  frame: cloneRemoteFleetFramePatch(frame)
13963
- });
13975
+ };
13976
+
13977
+ if (session) {
13978
+ if (!(session.latestBinaryFrames instanceof Map)) {
13979
+ session.latestBinaryFrames = new Map();
13980
+ }
13981
+
13982
+ pruneRemoteFleetBinaryFrameCache(session);
13983
+ session.latestBinaryFrames.set(deviceId, {
13984
+ seenAt: entry.seenAt,
13985
+ frame: cloneRemoteFleetFramePatch(frame)
13986
+ });
13987
+ }
13988
+
13989
+ pruneRemoteFleetGlobalBinaryFrameCache();
13990
+ remoteFleetLatestBinaryFrames.set(deviceId, entry);
13964
13991
  }
13965
13992
 
13966
13993
  function applyRemoteFleetBinaryFrameCacheToBody(bodyView, session) {
13967
- if (!bodyView || !(session?.latestBinaryFrames instanceof Map) || session.latestBinaryFrames.size === 0) {
13994
+ if (!bodyView) {
13968
13995
  return 0;
13969
13996
  }
13970
13997
 
13971
- pruneRemoteFleetBinaryFrameCache(session);
13972
- const patches = [];
13973
- for (const entry of session.latestBinaryFrames.values()) {
13974
- if (entry?.frame) {
13975
- patches.push(cloneRemoteFleetFramePatch(entry.frame));
13998
+ const patchesByDeviceId = new Map();
13999
+ const allowedDeviceIds = new Set(getRemoteFleetBinaryFrameDeviceIds(session));
14000
+ const addFrame = entry => {
14001
+ const frame = entry?.frame;
14002
+ const deviceId = String(frame?.deviceId || '').trim();
14003
+ if (!deviceId || !frame) {
14004
+ return;
13976
14005
  }
14006
+
14007
+ if (allowedDeviceIds.size > 0 && !allowedDeviceIds.has(deviceId)) {
14008
+ return;
14009
+ }
14010
+
14011
+ const existing = patchesByDeviceId.get(deviceId);
14012
+ const existingSeq = Number(existing?.frameSeq || 0);
14013
+ const nextSeq = Number(frame.frameSeq || 0);
14014
+ if (!existing || nextSeq >= existingSeq) {
14015
+ patchesByDeviceId.set(deviceId, cloneRemoteFleetFramePatch(frame));
14016
+ }
14017
+ };
14018
+
14019
+ if (session?.latestBinaryFrames instanceof Map && session.latestBinaryFrames.size > 0) {
14020
+ pruneRemoteFleetBinaryFrameCache(session);
14021
+ for (const entry of session.latestBinaryFrames.values()) {
14022
+ addFrame(entry);
14023
+ }
14024
+ }
14025
+
14026
+ pruneRemoteFleetGlobalBinaryFrameCache();
14027
+ for (const entry of remoteFleetLatestBinaryFrames.values()) {
14028
+ addFrame(entry);
13977
14029
  }
13978
14030
 
14031
+ const patches = Array.from(patchesByDeviceId.values());
13979
14032
  return patches.length > 0
13980
14033
  ? applyRemoteFleetFramePatches(bodyView, patches)
13981
14034
  : 0;
@@ -14254,6 +14307,69 @@
14254
14307
  }, REMOTE_FLEET_BINARY_FRAME_WS_RECONNECT_MS);
14255
14308
  }
14256
14309
 
14310
+ function cancelRemoteFleetBinaryFrameSessionRelease(session) {
14311
+ if (!session?.releaseTimer) {
14312
+ return;
14313
+ }
14314
+
14315
+ clearTimeout(session.releaseTimer);
14316
+ session.releaseTimer = null;
14317
+ }
14318
+
14319
+ function closeRemoteFleetBinaryFrameSession(session, reason = 'close') {
14320
+ if (!session) {
14321
+ return;
14322
+ }
14323
+
14324
+ cancelRemoteFleetBinaryFrameSessionRelease(session);
14325
+ if (session.reconnectTimer) {
14326
+ clearTimeout(session.reconnectTimer);
14327
+ session.reconnectTimer = null;
14328
+ }
14329
+ if (session.subscriptionTimer) {
14330
+ clearInterval(session.subscriptionTimer);
14331
+ session.subscriptionTimer = null;
14332
+ }
14333
+ if (session.ws) {
14334
+ try {
14335
+ session.ws.close();
14336
+ } catch {
14337
+ // Ignore WebSocket close failures.
14338
+ }
14339
+ session.ws = null;
14340
+ }
14341
+ remoteFleetBinaryFrameSessions.delete(session.nodeId);
14342
+ window.RuntimeTrace?.emit?.('remote.frame.wsSessionClosed', {
14343
+ nodeId: session.nodeId,
14344
+ reason
14345
+ });
14346
+ }
14347
+
14348
+ function scheduleRemoteFleetBinaryFrameSessionRelease(session, reason = 'idle') {
14349
+ if (!session
14350
+ || session.releaseTimer
14351
+ || session.bodies.size > 0
14352
+ || (session.controlSessions instanceof Set && session.controlSessions.size > 0)) {
14353
+ return;
14354
+ }
14355
+
14356
+ session.releaseTimer = setTimeout(() => {
14357
+ session.releaseTimer = null;
14358
+ if (session.bodies.size > 0
14359
+ || (session.controlSessions instanceof Set && session.controlSessions.size > 0)) {
14360
+ return;
14361
+ }
14362
+
14363
+ closeRemoteFleetBinaryFrameSession(session, reason);
14364
+ }, REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS);
14365
+ session.releaseTimer?.unref?.();
14366
+ window.RuntimeTrace?.emit?.('remote.frame.wsSessionIdle', {
14367
+ nodeId: session.nodeId,
14368
+ reason,
14369
+ ttlMs: REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS
14370
+ });
14371
+ }
14372
+
14257
14373
  async function openRemoteFleetBinaryFrameSocket(session) {
14258
14374
  if (!session || (session.bodies.size === 0 && (!session.controlSessions || session.controlSessions.size === 0)) || typeof WebSocket !== 'function') {
14259
14375
  return false;
@@ -14303,7 +14419,17 @@
14303
14419
 
14304
14420
  parseRemoteFleetBinaryFrameMessage(event.data)
14305
14421
  .then(frame => {
14306
- if (!frame || (session.bodies.size === 0 && (!session.controlSessions || session.controlSessions.size === 0))) {
14422
+ if (!frame) {
14423
+ return;
14424
+ }
14425
+
14426
+ const hasFrameConsumers =
14427
+ session.bodies.size > 0
14428
+ || (session.controlSessions instanceof Set && session.controlSessions.size > 0);
14429
+ if (!hasFrameConsumers) {
14430
+ const frameAt = getRemoteFleetFrameNow();
14431
+ session.lastBinaryFrameAt = frameAt;
14432
+ rememberRemoteFleetBinaryFrame(session, frame);
14307
14433
  if (frame?._remoteFleetObjectUrl && typeof URL !== 'undefined' && typeof URL.revokeObjectURL === 'function') {
14308
14434
  URL.revokeObjectURL(frame._remoteFleetObjectUrl);
14309
14435
  }
@@ -14406,6 +14532,7 @@
14406
14532
  bodies: new Set(),
14407
14533
  ws: null,
14408
14534
  reconnectTimer: null,
14535
+ releaseTimer: null,
14409
14536
  subscriptionTimer: null,
14410
14537
  subscriptionKey: '',
14411
14538
  latestBinaryFrames: new Map(),
@@ -14415,6 +14542,7 @@
14415
14542
  remoteFleetBinaryFrameSessions.set(nodeId, session);
14416
14543
  }
14417
14544
 
14545
+ cancelRemoteFleetBinaryFrameSessionRelease(session);
14418
14546
  session.bodies.add(bodyView);
14419
14547
  session.getDeviceIds = getDeviceIds;
14420
14548
  session.liveOptions = {
@@ -14426,7 +14554,14 @@
14426
14554
  mode: String(options.mode || 'remote-fast')
14427
14555
  };
14428
14556
  bodyView._remoteFleetBinaryFrameSession = session;
14429
- applyRemoteFleetBinaryFrameCacheToBody(bodyView, session);
14557
+ const primedFrames = applyRemoteFleetBinaryFrameCacheToBody(bodyView, session);
14558
+ if (primedFrames > 0) {
14559
+ bodyView._remoteFleetLastBinaryFrameAt = getRemoteFleetFrameNow();
14560
+ window.RuntimeTrace?.emit?.('remote.frame.wsCachePrimed', {
14561
+ nodeId,
14562
+ count: primedFrames
14563
+ });
14564
+ }
14430
14565
  openRemoteFleetBinaryFrameSocket(session).catch(() => undefined);
14431
14566
  return true;
14432
14567
  }
@@ -14444,23 +14579,7 @@
14444
14579
  return;
14445
14580
  }
14446
14581
 
14447
- if (session.reconnectTimer) {
14448
- clearTimeout(session.reconnectTimer);
14449
- session.reconnectTimer = null;
14450
- }
14451
- if (session.subscriptionTimer) {
14452
- clearInterval(session.subscriptionTimer);
14453
- session.subscriptionTimer = null;
14454
- }
14455
- if (session.ws) {
14456
- try {
14457
- session.ws.close();
14458
- } catch {
14459
- // Ignore WebSocket close failures.
14460
- }
14461
- session.ws = null;
14462
- }
14463
- remoteFleetBinaryFrameSessions.delete(session.nodeId);
14582
+ scheduleRemoteFleetBinaryFrameSessionRelease(session, 'body-detached');
14464
14583
  }
14465
14584
 
14466
14585
  function requestRemoteFleetFrameLoopFrame(callback) {
@@ -15827,23 +15946,7 @@
15827
15946
  refreshRemoteFleetBinaryFrameSubscription(binarySession, true);
15828
15947
 
15829
15948
  if (binarySession.bodies.size === 0 && binarySession.controlSessions.size === 0) {
15830
- if (binarySession.reconnectTimer) {
15831
- clearTimeout(binarySession.reconnectTimer);
15832
- binarySession.reconnectTimer = null;
15833
- }
15834
- if (binarySession.subscriptionTimer) {
15835
- clearInterval(binarySession.subscriptionTimer);
15836
- binarySession.subscriptionTimer = null;
15837
- }
15838
- if (binarySession.ws) {
15839
- try {
15840
- binarySession.ws.close();
15841
- } catch {
15842
- // Ignore WebSocket close failures.
15843
- }
15844
- binarySession.ws = null;
15845
- }
15846
- remoteFleetBinaryFrameSessions.delete(binarySession.nodeId);
15949
+ scheduleRemoteFleetBinaryFrameSessionRelease(binarySession, 'control-detached');
15847
15950
  }
15848
15951
  }
15849
15952
 
@@ -374,6 +374,17 @@
374
374
  return getNodeContentType(model) === 'templatelauncher';
375
375
  }
376
376
 
377
+ function isBusinessAutomationModel(model) {
378
+ return getNodeSemanticType(model) === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
379
+ }
380
+
381
+ function isLivePortalCssLodModel(model) {
382
+ const contentType = getNodeContentType(model);
383
+ return isTemplateLauncherModel(model) ||
384
+ contentType === 'csv-table' ||
385
+ isBusinessAutomationModel(model);
386
+ }
387
+
377
388
  function isRemoteFleetNodeModel(model) {
378
389
  const semanticType = getNodeSemanticType(model);
379
390
  return semanticType === REMOTE_FLEET_MONITOR_SEMANTIC_TYPE ||
@@ -4238,7 +4249,7 @@
4238
4249
 
4239
4250
  const entry = nodeObjectsById.get(nodeId);
4240
4251
  if (!entry?.cssObject || !supportsCss3dNodeModel(entry.model)) continue;
4241
- if (isTemplateLauncherModel(entry.model)) continue;
4252
+ if (isLivePortalCssLodModel(entry.model)) continue;
4242
4253
  if (shouldShowAgentConsoleInLodBand(lodBand) && hasAgentConsoleOpenForEntry(entry)) continue;
4243
4254
 
4244
4255
  nextWarmIds.add(nodeId);
@@ -4268,7 +4279,7 @@
4268
4279
  const nodeId = warmIds[i];
4269
4280
  if (keepAttachedIds?.has(nodeId)) continue;
4270
4281
  const entry = nodeObjectsById.get(nodeId);
4271
- if (isTemplateLauncherModel(entry?.model)) continue;
4282
+ if (isLivePortalCssLodModel(entry?.model)) continue;
4272
4283
  this._detachCss3dObject(entry);
4273
4284
  }
4274
4285
  }
@@ -4401,7 +4412,7 @@
4401
4412
  if (!nodeObjectsById) return;
4402
4413
 
4403
4414
  for (const entry of nodeObjectsById.values()) {
4404
- if (!entry || !isTemplateLauncherModel(entry.model)) continue;
4415
+ if (!entry || !isLivePortalCssLodModel(entry.model)) continue;
4405
4416
 
4406
4417
  const nodeId = String(entry.model?.id || entry.model?.Id || entry.glObject?.userData?.nodeId || '').trim();
4407
4418
  if (isCss3dRendererEnabled(module)) {
@@ -4886,7 +4897,7 @@
4886
4897
  let count = 0;
4887
4898
  for (const nodeId of Array.from(this._lodTemplateCssNodeIds)) {
4888
4899
  const entry = nodeObjectsById.get(nodeId) || null;
4889
- if (!entry || !isTemplateLauncherModel(entry.model)) {
4900
+ if (!entry || !isLivePortalCssLodModel(entry.model)) {
4890
4901
  this._lodTemplateCssNodeIds.delete(nodeId);
4891
4902
  continue;
4892
4903
  }
@@ -16,6 +16,7 @@
16
16
  const LIVE_TEMPLATE_OVERLAY_MODE = 'live-template';
17
17
  const CSV_TABLE_CONTENT_TYPE = 'csv-table';
18
18
  const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
19
+ const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
19
20
  const log = DEBUG ? console.log.bind(console, '[MindMapTextOverlayV2]') : () => { };
20
21
  let _projCornerTopLeft = null;
21
22
  let _projCornerTopRight = null;
@@ -87,7 +88,8 @@
87
88
  const semanticType = getNodeSemanticType(entry);
88
89
  return type === 'templatelauncher'
89
90
  || type === CSV_TABLE_CONTENT_TYPE
90
- || semanticType === REMOTE_FLEET_SEMANTIC_TYPE;
91
+ || semanticType === REMOTE_FLEET_SEMANTIC_TYPE
92
+ || semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
91
93
  }
92
94
 
93
95
  function isLiveInteractiveOverlayMode(mode) {
@@ -99,7 +101,7 @@
99
101
  const semanticType = getNodeSemanticType(entry);
100
102
  return semanticType === 'MindCanvasAgent'
101
103
  || semanticType === 'AgentCommand'
102
- || semanticType === 'BusinessAutomationNode';
104
+ || semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
103
105
  }
104
106
 
105
107
  function isDomOverlayDebugEnabled(module) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-H62hFd8/JAFraA8jNVN3s3GNyvSJa5o8b6zHgtuC/es=",
4
+ "hash": "sha256-oN9NB5XxbZ2xSDkf/iKkLdth/StnRySGcRVgEHblI6U=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -132,7 +132,7 @@
132
132
  "MindExecution.Plugins.PlanMaster.0yfii1nh3p.dll": "MindExecution.Plugins.PlanMaster.dll",
133
133
  "MindExecution.Plugins.YouTube.y4vxx807fo.dll": "MindExecution.Plugins.YouTube.dll",
134
134
  "MindExecution.Shared.50ptsgk9eg.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.hztii75ar1.dll": "MindExecution.Web.dll",
135
+ "MindExecution.Web.hucq2w2ism.dll": "MindExecution.Web.dll",
136
136
  "dotnet.js": "dotnet.js",
137
137
  "dotnet.native.qc8g39g30v.js": "dotnet.native.js",
138
138
  "dotnet.native.boem75ye5i.wasm": "dotnet.native.wasm",
@@ -283,7 +283,7 @@
283
283
  "MindExecution.Plugins.Concept.4ncya2e794.dll": "sha256-gKad12tQh1p7kInUiSIECobEZiD4VIuxmmskGE3ambo=",
284
284
  "MindExecution.Plugins.PlanMaster.0yfii1nh3p.dll": "sha256-OMcIYPKBnnJCW6QRN8nGykgCloUl1FRnFx6lma85ZGs=",
285
285
  "MindExecution.Shared.50ptsgk9eg.dll": "sha256-1X+o7c7jCm4MVorCrq52beiyMe+C6qFw6EyIGfZGQdo=",
286
- "MindExecution.Web.hztii75ar1.dll": "sha256-s/mh0EOyp5uxPxzn6b4blKD/zAAA5oKCjFDe0tCgc9g="
286
+ "MindExecution.Web.hucq2w2ism.dll": "sha256-iSV90XAYUoYq7Rfm7gf4tzJBsIy2zbfVfxadAFS8pSY="
287
287
  },
288
288
  "lazyAssembly": {
289
289
  "MindExecution.Plugins.Admin.bk23zzy6wc.dll": "sha256-uz7Nz0OZ8BBESp2Nr9YocyzV0dUrn7szPuD4/m/RWL8=",
@@ -7,8 +7,8 @@
7
7
  <title>MindExec | Run your ideas as AI task graphs</title>
8
8
  <meta name="description" content="MindExec is an AI execution canvas for solo builders, researchers, developers, and creators. Start with free browser tools, then move serious work into saved MindCanvas projects." />
9
9
  <base href="/" />
10
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260617-remote-control-node-v587" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-remote-control-node-v587" />
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260617-mdm-tab-fast-v591" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-mdm-tab-fast-v591" />
12
12
  <!-- ?쇄뼹??Font Awesome (local) ?쇄뼹??-->
13
13
  <link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
14
14
  <!-- ?꿎뼯??-->
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260617-remote-control-node-v587';
582
+ const scriptVersion = '20260617-mdm-tab-fast-v591';
583
583
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
584
584
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
585
585
  const criticalScripts = [
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "EpMH+qVw",
2
+ "version": "2guH6APA",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -42,7 +42,7 @@
42
42
  "url": "_content/MindExecution.Shared/css/app.css"
43
43
  },
44
44
  {
45
- "hash": "sha256-Z/qOemFxT1byK3tSyusCzWleiFLxcMRdsEc/0DoSPbk=",
45
+ "hash": "sha256-FxtluumoQV2QXTCFtSR+/dc0Xp/CFDcF1cXkaeikt4s=",
46
46
  "url": "_content/MindExecution.Shared/css/mind-map-overrides.css"
47
47
  },
48
48
  {
@@ -78,7 +78,7 @@
78
78
  "url": "_content/MindExecution.Shared/js/marked.min.js"
79
79
  },
80
80
  {
81
- "hash": "sha256-y/ehxOpFseX3lMFu+VGjbL4lTMmQ0kEmj5QIRldR2S4=",
81
+ "hash": "sha256-xTWG0IO6nYqtcvUgWluJHWfalJjI4MN86PHUMmtpECs=",
82
82
  "url": "_content/MindExecution.Shared/js/mind-map-core.js"
83
83
  },
84
84
  {
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-7XM3q7xvvWVxDjSBniKorjQgNjyhOcAz2WNqg8OHOa0=",
89
+ "hash": "sha256-piKsRYcnSTaqs+vd7QATTt7cGBN5hHPbY6P8Xjl2J6k=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -114,7 +114,7 @@
114
114
  "url": "_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js"
115
115
  },
116
116
  {
117
- "hash": "sha256-/I+97B4sBuC4ON9Ytp2crB+yaSXnxgr1wq35P/GZ4O0=",
117
+ "hash": "sha256-Lo7v8nqlOhYydM8OzuMLHllA29+Q76y/PIV+QaB1uyo=",
118
118
  "url": "_content/MindExecution.Shared/js/mind-map-lod-renderer.js"
119
119
  },
120
120
  {
@@ -154,7 +154,7 @@
154
154
  "url": "_content/MindExecution.Shared/js/mind-map-text-lod-system.js"
155
155
  },
156
156
  {
157
- "hash": "sha256-qwNb+PcesE9rgfkjiJrUOWHz/J2vwDUb6wzJ3j5Zj90=",
157
+ "hash": "sha256-TYsOWDglKs9IpaKCvKCpmRhjYRx32npfGkCFbplRM3s=",
158
158
  "url": "_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js"
159
159
  },
160
160
  {
@@ -446,8 +446,8 @@
446
446
  "url": "_framework/MindExecution.Shared.50ptsgk9eg.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-s/mh0EOyp5uxPxzn6b4blKD/zAAA5oKCjFDe0tCgc9g=",
450
- "url": "_framework/MindExecution.Web.hztii75ar1.dll"
449
+ "hash": "sha256-iSV90XAYUoYq7Rfm7gf4tzJBsIy2zbfVfxadAFS8pSY=",
450
+ "url": "_framework/MindExecution.Web.hucq2w2ism.dll"
451
451
  },
452
452
  {
453
453
  "hash": "sha256-IsZJ91/OW+fHzNqIgEc7Y072ns8z9dGritiSyvR9Wgc=",
@@ -770,7 +770,7 @@
770
770
  "url": "_framework/Websocket.Client.vapounvmnl.dll"
771
771
  },
772
772
  {
773
- "hash": "sha256-pG5Qn7IywPsHJ02D/12EcGeCG65zRYsIE4kfzFBZv3I=",
773
+ "hash": "sha256-l0xPjAbrLfbdmB69gqtRn+JMPssryiM0fh1uDxZ/NNo=",
774
774
  "url": "_framework/blazor.boot.json"
775
775
  },
776
776
  {
@@ -834,7 +834,7 @@
834
834
  "url": "image-manifest.json"
835
835
  },
836
836
  {
837
- "hash": "sha256-RmtnourcT6RHyaHx4CcQLxRf5QOIMFMIfF4OZC0W07k=",
837
+ "hash": "sha256-hq0qYVhXCK09hkKo9EkKj84gbxqFCuf2l4XAWk1gfL0=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: EpMH+qVw */
1
+ /* Manifest version: 2guH6APA */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4