@mindexec/cli 0.2.29 → 0.2.31

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.29",
3
+ "version": "0.2.31",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -412,7 +412,7 @@ function wait(ms = 0) {
412
412
  return new Promise(resolve => setTimeout(resolve, ms));
413
413
  }
414
414
 
415
- function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = []) {
415
+ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = [], lastError = '') {
416
416
  const connected = devices.filter(device => device.Connected).length;
417
417
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
418
418
  const pairToken = hubStatus.pairToken || 'render-smoke-token';
@@ -421,7 +421,7 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
421
421
  : 'inactive';
422
422
  return {
423
423
  id: 'remote-fleet-render-smoke',
424
- contentType: 'memo',
424
+ contentType: 'templateLauncher',
425
425
  metadata: {
426
426
  SemanticType: 'RemoteFleetMonitor',
427
427
  RemoteFleetSchemaVersion: 'remote-fleet@1',
@@ -447,11 +447,37 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
447
447
  RemoteFleetDevicesJson: JSON.stringify(devices),
448
448
  RemoteFleetLatestTaskBatchJson: latestTaskBatch ? JSON.stringify(latestTaskBatch) : '',
449
449
  RemoteFleetRecentTaskBatchesJson: JSON.stringify(recentTaskBatches),
450
- RemoteFleetLastError: ''
450
+ RemoteFleetLastError: String(lastError || '')
451
451
  }
452
452
  };
453
453
  }
454
454
 
455
+ function createRemoteFleetTemplateShell(document, focusDeviceId = '') {
456
+ const nodeShell = document.createElement('div');
457
+ nodeShell.setAttribute('class', 'map-node-template-card map-node-remote-fleet');
458
+ const shell = document.createElement('div');
459
+ shell.setAttribute('class', 'template-card__shell template-card__shell--remote-fleet');
460
+ const header = document.createElement('div');
461
+ header.setAttribute('class', 'template-card__header template-card__header--remote-fleet');
462
+ const icon = document.createElement('div');
463
+ icon.setAttribute('class', 'template-card__icon template-card__icon--remote-fleet');
464
+ const titleWrap = document.createElement('div');
465
+ titleWrap.setAttribute('class', 'template-card__title-block');
466
+ titleWrap.textContent = 'Remote Fleet Monitor';
467
+ header.appendChild(icon);
468
+ header.appendChild(titleWrap);
469
+ const bodyView = document.createElement('div');
470
+ bodyView.setAttribute('class', 'template-card__remote-fleet-body');
471
+ bodyView.dataset.remoteFleetAutoMonitor = 'false';
472
+ if (focusDeviceId) {
473
+ bodyView.dataset.remoteFleetFocusDeviceId = focusDeviceId;
474
+ }
475
+ shell.appendChild(header);
476
+ shell.appendChild(bodyView);
477
+ nodeShell.appendChild(shell);
478
+ return { nodeShell, bodyView };
479
+ }
480
+
455
481
  function buildDeviceNode(device, hubStatus) {
456
482
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
457
483
  return {
@@ -666,25 +692,7 @@ try {
666
692
  };
667
693
  manager.setModuleForTest({ dotNetHelper });
668
694
 
669
- const nodeShell = document.createElement('div');
670
- nodeShell.setAttribute('class', 'map-node-remote-fleet map-node-memo');
671
- const header = document.createElement('div');
672
- header.setAttribute('class', 'map-node-memo__header');
673
- const iconWrap = document.createElement('div');
674
- iconWrap.setAttribute('class', 'map-node-memo__icon-wrap');
675
- const iconButton = document.createElement('button');
676
- iconButton.setAttribute('class', 'map-node-memo__icon-button');
677
- iconWrap.appendChild(iconButton);
678
- const titleWrap = document.createElement('div');
679
- titleWrap.setAttribute('class', 'map-node-memo__title-wrap');
680
- titleWrap.textContent = 'Remote Fleet Monitor';
681
- header.appendChild(iconWrap);
682
- header.appendChild(titleWrap);
683
- const bodyView = document.createElement('div');
684
- bodyView.dataset.remoteFleetAutoMonitor = 'false';
685
- bodyView.dataset.remoteFleetFocusDeviceId = focusedDevice.DeviceId;
686
- nodeShell.appendChild(header);
687
- nodeShell.appendChild(bodyView);
695
+ const { nodeShell, bodyView } = createRemoteFleetTemplateShell(document, focusedDevice.DeviceId);
688
696
  document.body.appendChild(nodeShell);
689
697
 
690
698
  manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
@@ -695,6 +703,13 @@ try {
695
703
  assert.equal(bodyView.querySelector('[data-remote-fleet-search="true"]'), null);
696
704
  assert.equal(bodyView.querySelectorAll('select').length, 0);
697
705
  assert.ok(bodyView.querySelector('[data-remote-fleet-device-grid="true"]'));
706
+ const statusFooter = bodyView.querySelector('[data-remote-fleet-status-rail="true"]');
707
+ assert.ok(statusFooter);
708
+ const bodyChildOrder = Array.from(bodyView.children).map(child =>
709
+ Object.keys(child.dataset || {}).join(',') || child.tagName).join('|');
710
+ assert.ok(bodyChildOrder.endsWith('remoteFleetStatusRail'), bodyChildOrder);
711
+ assert.ok(statusFooter.style.cssText.includes('flex-direction: row'));
712
+ assert.equal(statusFooter.querySelectorAll('[data-remote-fleet-status-item]').length, 4);
698
713
  const initialDetailPanel = bodyView.querySelector('[data-remote-fleet-detail-panel="true"]');
699
714
  assert.equal(initialDetailPanel?.dataset.deviceId, focusedDevice.DeviceId);
700
715
  assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').length, 1);
@@ -753,6 +768,10 @@ try {
753
768
  assert.ok(stopHostCall);
754
769
  assert.equal(stopHostCall.args[0], 'remote-fleet-render-smoke');
755
770
  assert.equal(stopHostCall.args[1], false);
771
+ hub.setHostTarget({
772
+ nodeId: 'remote-fleet-render-smoke',
773
+ enabled: false
774
+ });
756
775
 
757
776
  assert.ok(livePanel?.textContent.includes('RemoteFast 20 fps'), livePanel?.textContent || bodyView.textContent);
758
777
  assert.equal(bodyView.querySelector('code'), null);
@@ -789,23 +808,30 @@ try {
789
808
  call.methodName === 'DispatchRemoteFleetTaskBatchFromJs'
790
809
  || call.methodName === 'DispatchRemoteFleetTaskFromJs'), false);
791
810
 
792
- const emptyShell = document.createElement('div');
793
- emptyShell.setAttribute('class', 'map-node-remote-fleet map-node-memo');
794
- const emptyHeader = document.createElement('div');
795
- emptyHeader.setAttribute('class', 'map-node-memo__header');
796
- emptyHeader.appendChild(document.createElement('div'));
797
- emptyHeader.appendChild(document.createElement('div'));
798
- const emptyBody = document.createElement('div');
799
- emptyShell.appendChild(emptyHeader);
800
- emptyShell.appendChild(emptyBody);
811
+ const { nodeShell: emptyShell, bodyView: emptyBody } = createRemoteFleetTemplateShell(document);
801
812
  document.body.appendChild(emptyShell);
802
813
  manager.renderRemoteFleetMonitorForTest(emptyBody, buildMonitorNode([], hub.getStatus({ includeSecrets: true })));
803
814
  assert.equal(emptyBody.querySelectorAll('[data-remote-fleet-empty-screen="true"]').length, 6);
815
+ assert.ok(Array.from(emptyBody.children).map(child =>
816
+ Object.keys(child.dataset || {}).join(',') || child.tagName).join('|').endsWith('remoteFleetStatusRail'));
804
817
  assert.equal(emptyBody.textContent.includes('No devices connected yet.'), false);
805
818
  assert.equal(emptyBody.textContent.includes('Select a screen'), false);
806
819
  assert.equal(emptyBody.textContent.includes('No screen'), false);
807
820
  assert.equal(emptyBody.textContent.includes('Endpoint 127.0.0.1'), false);
808
821
 
822
+ const { nodeShell: errorShell, bodyView: errorBody } = createRemoteFleetTemplateShell(document);
823
+ document.body.appendChild(errorShell);
824
+ manager.renderRemoteFleetMonitorForTest(errorBody, buildMonitorNode([], hub.getStatus({ includeSecrets: true }), null, [], 'RemoteHub is unavailable. Start LocalBridge and retry.'));
825
+ const errorFooter = errorBody.querySelector('[data-remote-fleet-status-rail="true"]');
826
+ const footerError = errorFooter?.querySelector('[data-remote-fleet-status-error="true"]');
827
+ assert.ok(errorFooter);
828
+ assert.ok(footerError);
829
+ assert.ok(Array.from(errorBody.children).map(child =>
830
+ Object.keys(child.dataset || {}).join(',') || child.tagName).join('|').endsWith('remoteFleetStatusRail'));
831
+ assert.equal(footerError.textContent.includes('RemoteHub is unavailable'), true);
832
+ assert.equal(Array.from(errorBody.children).some(child =>
833
+ child !== errorFooter && (child.textContent || '').includes('RemoteHub is unavailable')), false);
834
+
809
835
  const deviceBody = document.createElement('div');
810
836
  document.body.appendChild(deviceBody);
811
837
  manager.renderRemoteFleetDeviceForTest(deviceBody, buildDeviceNode(focusedDevice, hub.getStatus()));
@@ -815,6 +841,12 @@ try {
815
841
  assert.ok(deviceBody.textContent.includes('Task timed out waiting for the agent response.'));
816
842
  assert.ok(deviceBody.textContent.includes(focusedDevice.DeviceId));
817
843
 
844
+ nodeShell.remove();
845
+ emptyShell.remove();
846
+ errorShell.remove();
847
+ deviceBody.remove();
848
+ await wait(2200);
849
+
818
850
  console.log(`RemoteFleet render smoke OK (${SYNTHETIC_COUNT} synthetic devices, ${connectedCount} connected, ${aiCount} AI-ready)`);
819
851
  } finally {
820
852
  await hub.close();
@@ -2480,6 +2480,31 @@ body.is-panning .mind-map-text-overlay-v2-card.is-passive .mind-map-text-overlay
2480
2480
  white-space: nowrap;
2481
2481
  }
2482
2482
 
2483
+ .map-node-template-card.map-node-remote-fleet .template-card__shell--remote-fleet {
2484
+ gap: 10px;
2485
+ padding: 14px;
2486
+ background: #f8fafc;
2487
+ }
2488
+
2489
+ .map-node-template-card.map-node-remote-fleet .template-card__header--remote-fleet {
2490
+ align-items: center;
2491
+ }
2492
+
2493
+ .map-node-template-card.map-node-remote-fleet .template-card__icon--remote-fleet {
2494
+ border-color: rgba(37, 99, 235, 0.22);
2495
+ background: #eff6ff;
2496
+ color: #2563eb;
2497
+ }
2498
+
2499
+ .map-node-template-card.map-node-remote-fleet .template-card__remote-fleet-body {
2500
+ flex: 1 1 auto;
2501
+ min-height: 0;
2502
+ overflow: hidden;
2503
+ border: 1px solid rgba(148, 163, 184, 0.28);
2504
+ border-radius: 8px;
2505
+ background: #ffffff;
2506
+ }
2507
+
2483
2508
  .template-card__generate:hover {
2484
2509
  background: #115e59;
2485
2510
  }
@@ -3587,7 +3587,7 @@
3587
3587
  }
3588
3588
 
3589
3589
  function isBusinessAutomationContextSourceNode(nodeModel) {
3590
- if (!nodeModel || isBusinessAutomationNode(nodeModel)) {
3590
+ if (!nodeModel || isBusinessAutomationNode(nodeModel) || isRemoteFleetNode(nodeModel)) {
3591
3591
  return false;
3592
3592
  }
3593
3593
 
@@ -3600,7 +3600,6 @@
3600
3600
  return semanticType === 'MindCanvasAgent'
3601
3601
  || semanticType === 'AgentCommand'
3602
3602
  || semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
3603
- || semanticType === REMOTE_FLEET_SEMANTIC_TYPE
3604
3603
  || semanticType === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
3605
3604
  }
3606
3605
 
@@ -11644,6 +11643,10 @@
11644
11643
  }
11645
11644
 
11646
11645
  function createTemplateLauncherNodeElement(nodeModel) {
11646
+ if (isRemoteFleetMonitorNode(nodeModel)) {
11647
+ return createRemoteFleetTemplateNodeElement(nodeModel);
11648
+ }
11649
+
11647
11650
  const width = Number(nodeModel.width ?? nodeModel.Width ?? 560);
11648
11651
  const height = Number(nodeModel.height ?? nodeModel.Height ?? 620);
11649
11652
  const config = getTemplateCardConfig(nodeModel);
@@ -11734,6 +11737,54 @@
11734
11737
  return container;
11735
11738
  }
11736
11739
 
11740
+ function createRemoteFleetTemplateNodeElement(nodeModel) {
11741
+ const width = Number(nodeModel.width ?? nodeModel.Width ?? 960);
11742
+ const height = Number(nodeModel.height ?? nodeModel.Height ?? 620);
11743
+ const title = String(nodeModel.prompt ?? nodeModel.Prompt ?? 'Remote Fleet Monitor').trim()
11744
+ || 'Remote Fleet Monitor';
11745
+
11746
+ const container = document.createElement('div');
11747
+ container.id = `node-${nodeModel.id}`;
11748
+ container.className = 'map-node css3d-dynamic-node map-node-template-card map-node-remote-fleet';
11749
+ container.dataset.nodeId = nodeModel.id;
11750
+ container.dataset.contentType = 'templateLauncher';
11751
+ container.dataset.semanticType = getNodeSemanticType(nodeModel);
11752
+ container.style.cssText = `
11753
+ width: ${width}px;
11754
+ height: ${height}px;
11755
+ box-sizing: border-box;
11756
+ overflow: hidden;
11757
+ pointer-events: auto;
11758
+ `;
11759
+
11760
+ const shell = document.createElement('div');
11761
+ shell.className = 'template-card__shell template-card__shell--remote-fleet';
11762
+
11763
+ const header = document.createElement('div');
11764
+ header.className = 'template-card__header template-card__header--remote-fleet';
11765
+
11766
+ const icon = document.createElement('div');
11767
+ icon.className = 'template-card__icon template-card__icon--remote-fleet';
11768
+ icon.innerHTML = '<i class="fa-solid fa-network-wired" aria-hidden="true"></i>';
11769
+ header.appendChild(icon);
11770
+
11771
+ const titleBlock = document.createElement('div');
11772
+ titleBlock.className = 'template-card__title-block';
11773
+ titleBlock.appendChild(createTemplateCardText('div', 'template-card__eyebrow', 'Remote'));
11774
+ titleBlock.appendChild(createTemplateCardText('div', 'template-card__title', title));
11775
+ header.appendChild(titleBlock);
11776
+ shell.appendChild(header);
11777
+
11778
+ const bodyView = document.createElement('div');
11779
+ bodyView.className = 'template-card__remote-fleet-body markdown-body mind-map-text-scrollable-view';
11780
+ bodyView.dataset.nodeId = nodeModel.id;
11781
+ renderRemoteFleetMonitor(bodyView, nodeModel);
11782
+ shell.appendChild(bodyView);
11783
+
11784
+ container.appendChild(shell);
11785
+ return container;
11786
+ }
11787
+
11737
11788
  function stopMemoEvent(event) {
11738
11789
  if (event.button === 1 || event.button === 2) {
11739
11790
  return;
@@ -12208,43 +12259,79 @@
12208
12259
  return item;
12209
12260
  }
12210
12261
 
12211
- function createRemoteFleetStatusRail(items) {
12262
+ function createRemoteFleetStatusRail(items, errorMessage = '') {
12212
12263
  const rail = document.createElement('aside');
12213
12264
  rail.dataset.remoteFleetStatusRail = 'true';
12214
12265
  rail.style.cssText = `
12215
12266
  flex: 0 0 auto;
12216
- align-self: flex-start;
12267
+ align-self: stretch;
12217
12268
  display: flex;
12218
- flex-direction: column;
12219
- gap: 3px;
12220
- min-width: 132px;
12221
- max-width: min(100%, 176px);
12222
- padding: 5px 6px;
12269
+ flex-direction: row;
12270
+ align-items: center;
12271
+ justify-content: flex-end;
12272
+ gap: 6px;
12273
+ width: 100%;
12274
+ min-width: 0;
12275
+ min-height: 28px;
12276
+ padding: 4px 6px;
12223
12277
  border-radius: 7px;
12224
12278
  border: 1px solid rgba(148, 163, 184, 0.22);
12225
12279
  background: rgba(255, 255, 255, 0.70);
12226
12280
  `;
12227
12281
 
12282
+ const normalizedError = String(errorMessage || '').trim();
12283
+ if (normalizedError) {
12284
+ const error = document.createElement('div');
12285
+ error.dataset.remoteFleetStatusError = 'true';
12286
+ error.textContent = normalizedError;
12287
+ error.title = normalizedError;
12288
+ error.style.cssText = `
12289
+ flex: 1 1 auto;
12290
+ min-width: 0;
12291
+ display: inline-flex;
12292
+ align-items: center;
12293
+ height: 22px;
12294
+ padding: 0 9px;
12295
+ border-radius: 999px;
12296
+ border: 1px solid rgba(148, 163, 184, 0.22);
12297
+ background: rgba(15, 23, 42, 0.05);
12298
+ color: #334155;
12299
+ font-size: 10px;
12300
+ font-weight: 850;
12301
+ line-height: 1;
12302
+ letter-spacing: 0;
12303
+ overflow: hidden;
12304
+ text-overflow: ellipsis;
12305
+ white-space: nowrap;
12306
+ `;
12307
+ rail.appendChild(error);
12308
+ }
12309
+
12228
12310
  (items || []).forEach(item => {
12229
12311
  const tone = String(item?.tone || 'default');
12230
12312
  const row = document.createElement('div');
12231
12313
  row.dataset.remoteFleetStatusItem = item?.key || '';
12232
12314
  row.style.cssText = `
12233
- display: grid;
12234
- grid-template-columns: 7px minmax(0, 1fr) auto;
12315
+ flex: 0 0 auto;
12316
+ display: inline-flex;
12235
12317
  align-items: center;
12236
- gap: 5px;
12237
- min-height: 16px;
12318
+ gap: 4px;
12319
+ height: 22px;
12238
12320
  min-width: 0;
12321
+ padding: 0 7px;
12322
+ border-radius: 999px;
12323
+ background: ${tone === 'online' ? 'rgba(16, 185, 129, 0.09)' : 'rgba(15, 23, 42, 0.04)'};
12324
+ border: 1px solid ${tone === 'online' ? 'rgba(16, 185, 129, 0.18)' : 'rgba(148, 163, 184, 0.18)'};
12239
12325
  `;
12240
12326
 
12241
12327
  const dot = document.createElement('span');
12242
12328
  dot.style.cssText = `
12243
- width: 7px;
12244
- height: 7px;
12329
+ flex: 0 0 auto;
12330
+ width: 6px;
12331
+ height: 6px;
12245
12332
  border-radius: 999px;
12246
12333
  background: ${tone === 'online' ? '#10b981' : tone === 'warn' ? '#f59e0b' : '#94a3b8'};
12247
- box-shadow: ${tone === 'online' ? '0 0 0 3px rgba(16, 185, 129, 0.12)' : 'none'};
12334
+ box-shadow: ${tone === 'online' ? '0 0 0 2px rgba(16, 185, 129, 0.11)' : 'none'};
12248
12335
  `;
12249
12336
 
12250
12337
  const labelEl = document.createElement('span');
@@ -12270,7 +12357,7 @@
12270
12357
  font-weight: 950;
12271
12358
  line-height: 1;
12272
12359
  letter-spacing: 0;
12273
- max-width: 76px;
12360
+ max-width: 86px;
12274
12361
  overflow: hidden;
12275
12362
  text-overflow: ellipsis;
12276
12363
  white-space: nowrap;
@@ -12314,7 +12401,9 @@
12314
12401
 
12315
12402
  function attachRemoteFleetTitleActions(bodyView, hostButton, stopHostButton, hostState) {
12316
12403
  const container = bodyView?.closest?.('.map-node-remote-fleet');
12317
- const header = container?.querySelector?.('.map-node-memo__header') || null;
12404
+ const header = container?.querySelector?.('.template-card__header')
12405
+ || container?.querySelector?.('.map-node-memo__header')
12406
+ || null;
12318
12407
  const existingActions = header?.querySelector?.('[data-remote-fleet-title-actions="true"]') || null;
12319
12408
  existingActions?.remove?.();
12320
12409
 
@@ -12351,7 +12440,9 @@
12351
12440
  }
12352
12441
 
12353
12442
  if (header) {
12354
- header.style.gridTemplateColumns = '46px minmax(0, 1fr) auto';
12443
+ header.style.gridTemplateColumns = header.classList?.contains?.('template-card__header')
12444
+ ? '42px minmax(0, 1fr) auto'
12445
+ : '46px minmax(0, 1fr) auto';
12355
12446
  header.appendChild(titleActions);
12356
12447
  } else {
12357
12448
  titleActions.style.alignSelf = 'flex-end';
@@ -13408,7 +13499,7 @@
13408
13499
  value: String(aiCapableCount),
13409
13500
  tone: aiCapableCount > 0 ? 'online' : 'default'
13410
13501
  }
13411
- ]);
13502
+ ], lastError);
13412
13503
 
13413
13504
  const filterRow = document.createElement('div');
13414
13505
  filterRow.style.cssText = `
@@ -13890,23 +13981,6 @@
13890
13981
  bodyView.appendChild(livePanel);
13891
13982
  }
13892
13983
 
13893
- if (lastError.trim()) {
13894
- const errorEl = document.createElement('div');
13895
- errorEl.textContent = lastError;
13896
- errorEl.style.cssText = `
13897
- flex: 0 0 auto;
13898
- padding: 8px 10px;
13899
- border-radius: 7px;
13900
- background: rgba(248, 113, 113, 0.12);
13901
- color: #991b1b;
13902
- font-size: 12px;
13903
- line-height: 1.35;
13904
- font-weight: 700;
13905
- overflow-wrap: break-word;
13906
- `;
13907
- bodyView.appendChild(errorEl);
13908
- }
13909
-
13910
13984
  const createDevicePreview = (device, mode = 'tile') => {
13911
13985
  const name = getRemoteFleetDeviceName(device);
13912
13986
  const connectedDevice = isRemoteFleetDeviceConnected(device);
@@ -15094,6 +15168,10 @@
15094
15168
  }
15095
15169
 
15096
15170
  function createMemoNodeElement(nodeModel) {
15171
+ if (isRemoteFleetMonitorNode(nodeModel)) {
15172
+ return createRemoteFleetTemplateNodeElement(nodeModel);
15173
+ }
15174
+
15097
15175
  const width = Number(nodeModel.width || 320);
15098
15176
  const height = Number(nodeModel.height || 240);
15099
15177
  const title = nodeModel.prompt ?? nodeModel.Prompt ?? '';
@@ -17428,6 +17506,10 @@
17428
17506
  const metadata = nodeModel.metadata ?? nodeModel.Metadata ?? {};
17429
17507
  const agentId = metadata?.agentId ?? metadata?.AgentId;
17430
17508
  const isAgentNode = nodeTypeLower.startsWith('agent_') || (agentId !== undefined && agentId !== null && String(agentId) !== '');
17509
+ if (isRemoteFleetMonitorNode(nodeModel)) {
17510
+ return createRemoteFleetTemplateNodeElement(nodeModel);
17511
+ }
17512
+
17431
17513
  if (contentTypeLower === 'templatelauncher') {
17432
17514
  return createTemplateLauncherNodeElement(nodeModel);
17433
17515
  }
@@ -17853,7 +17935,10 @@
17853
17935
  // ▼▼▼ [New] 템플릿이 없으면 동적으로 생성 (text/markdown 타입용) ▼▼▼
17854
17936
  // [FIX] hybrid overlay가 기대하는 구조를 보장하기 위해 text/markdown/note/memo는 동적 DOM을 강제합니다.
17855
17937
  const contentTypeLower = String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase();
17856
- if (contentTypeLower === 'text'
17938
+ const remoteFleetMonitor = isRemoteFleetMonitorNode(nodeModel);
17939
+ const visualContentTypeLower = remoteFleetMonitor ? 'templatelauncher' : contentTypeLower;
17940
+ if (remoteFleetMonitor
17941
+ || contentTypeLower === 'text'
17857
17942
  || contentTypeLower === 'markdown'
17858
17943
  || contentTypeLower === 'note'
17859
17944
  || contentTypeLower === 'memo'
@@ -17872,8 +17957,8 @@
17872
17957
  // But usually 'note' comes from Razor. If missing, we create it.
17873
17958
  const dynamicTypes = ['text', 'markdown', 'code', 'note', 'memo', 'templatelauncher', 'image', 'video', 'embed'];
17874
17959
 
17875
- if (dynamicTypes.includes(contentTypeLower)) {
17876
- log(`[MindMapCss3DManager] Creating dynamic DOM element for ${contentTypeLower} node ${nodeModel.id}`);
17960
+ if (remoteFleetMonitor || dynamicTypes.includes(contentTypeLower)) {
17961
+ log(`[MindMapCss3DManager] Creating dynamic DOM element for ${visualContentTypeLower || contentTypeLower} node ${nodeModel.id}`);
17877
17962
  templateElement = createDynamicNodeElement(nodeModel);
17878
17963
  isDynamicallyCreated = true;
17879
17964
  } else {
@@ -17971,7 +18056,7 @@
17971
18056
  // ▲▲▲ [Perf] ▲▲▲
17972
18057
 
17973
18058
  const wrapper = document.createElement('div');
17974
- const wrapperTypeClass = `node-type-${contentTypeLower || 'unknown'}`;
18059
+ const wrapperTypeClass = `node-type-${visualContentTypeLower || 'unknown'}`;
17975
18060
  wrapper.className = `css3d-resolution-wrapper ${wrapperTypeClass}`;
17976
18061
  wrapper.style.width = `${width * resolutionScale}px`; // ★ [Fix] Scale wrapper size
17977
18062
  wrapper.style.height = `${height * resolutionScale}px`; // ★ [Fix] Scale wrapper size
@@ -18044,18 +18129,23 @@
18044
18129
  });
18045
18130
  }
18046
18131
 
18047
- if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'memo') {
18132
+ if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'memo'
18133
+ && !isRemoteFleetMonitorNode(nodeModel)) {
18048
18134
  bindMemoNodeEvents(clonedElement, nodeModel);
18049
18135
  }
18050
- if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'templatelauncher') {
18136
+ if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'templatelauncher'
18137
+ && !isRemoteFleetMonitorNode(nodeModel)) {
18051
18138
  bindTemplateLauncherNodeEvents(clonedElement, nodeModel);
18052
18139
  }
18053
18140
  wrapper.appendChild(clonedElement);
18054
- if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() !== 'templatelauncher') {
18141
+ if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() !== 'templatelauncher'
18142
+ || isRemoteFleetMonitorNode(nodeModel)) {
18055
18143
  appendCss3dResizeHitZones(wrapper, nodeModel);
18056
18144
  }
18057
18145
 
18058
- if (String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'memo' || isAgentStyledMemoNode(nodeModel)) {
18146
+ if ((String(nodeModel.contentType ?? nodeModel.ContentType ?? '').toLowerCase() === 'memo'
18147
+ && !isRemoteFleetMonitorNode(nodeModel))
18148
+ || isAgentStyledMemoNode(nodeModel)) {
18059
18149
  applyMemoWrapperTheme(clonedElement, getMemoColorKey(nodeModel));
18060
18150
  }
18061
18151
 
@@ -18234,6 +18324,55 @@
18234
18324
  dividerEl.style.display = prompt ? (isLoading || hasContent ? 'block' : 'none') : 'none';
18235
18325
  }
18236
18326
 
18327
+ if (isRemoteFleetMonitorNode(nodeModel)) {
18328
+ el.classList.remove('node-type-memo', 'has-context-source-pins');
18329
+ el.classList.add('node-type-templatelauncher');
18330
+ el.dataset.contentType = 'templateLauncher';
18331
+ el.style.overflow = 'hidden';
18332
+
18333
+ let card = el.querySelector('.map-node-template-card.map-node-remote-fleet');
18334
+ if (!card) {
18335
+ card = createRemoteFleetTemplateNodeElement(nodeModel);
18336
+ card.id = `css3d-node-${nodeId}`;
18337
+ card.style.display = '';
18338
+ card.style.position = 'absolute';
18339
+ card.style.left = '0px';
18340
+ card.style.top = '0px';
18341
+ card.style.transformOrigin = '0 0';
18342
+ card.style.transition = 'none';
18343
+ card.style.webkitTransition = 'none';
18344
+ const firstChild = el.firstElementChild;
18345
+ if (firstChild) {
18346
+ el.replaceChild(card, firstChild);
18347
+ } else {
18348
+ el.prepend(card);
18349
+ }
18350
+ }
18351
+
18352
+ card.dataset.nodeId = nodeId;
18353
+ card.dataset.semanticType = getNodeSemanticType(nodeModel);
18354
+ card.style.width = `${worldWidth}px`;
18355
+ card.style.height = `${worldHeight}px`;
18356
+ card.style.minWidth = `${worldWidth}px`;
18357
+ card.style.minHeight = `${worldHeight}px`;
18358
+ card.style.maxWidth = `${worldWidth}px`;
18359
+ card.style.maxHeight = `${worldHeight}px`;
18360
+ card.style.transform = `scale(${resolutionScale})`;
18361
+ card.style.transformOrigin = '0 0';
18362
+
18363
+ const titleEl = card.querySelector('.template-card__title');
18364
+ if (titleEl) {
18365
+ const nextTitle = String(prompt || 'Remote Fleet Monitor').trim() || 'Remote Fleet Monitor';
18366
+ if (titleEl.textContent !== nextTitle) {
18367
+ titleEl.textContent = nextTitle;
18368
+ }
18369
+ }
18370
+
18371
+ const bodyView = card.querySelector('.template-card__remote-fleet-body');
18372
+ renderRemoteFleetMonitor(bodyView, nodeModel);
18373
+ return;
18374
+ }
18375
+
18237
18376
  if (contentTypeLower === 'memo') {
18238
18377
  const memoEl = el.querySelector('.map-node-memo');
18239
18378
  if (!memoEl) return;
@@ -104,7 +104,6 @@
104
104
  return semanticType === 'MindCanvasAgent'
105
105
  || semanticType === 'AgentCommand'
106
106
  || semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
107
- || semanticType === REMOTE_FLEET_SEMANTIC_TYPE
108
107
  || semanticType === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
109
108
  }
110
109
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-scsV40Knf96MECCS5TE6GBMBRc/aVh7dlYynMn/8Zjs=",
4
+ "hash": "sha256-3BNGeO97GJzZPWJKcxft//YyUghoJWfEleoPXAWQph8=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -127,12 +127,12 @@
127
127
  "MindExecution.Kernel.z56elxihok.dll": "MindExecution.Kernel.dll",
128
128
  "MindExecution.Plugins.Admin.p5cs4ap87v.dll": "MindExecution.Plugins.Admin.dll",
129
129
  "MindExecution.Plugins.Business.s35og5uz44.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.zczca3fsxz.dll": "MindExecution.Plugins.Concept.dll",
130
+ "MindExecution.Plugins.Concept.faidxooqle.dll": "MindExecution.Plugins.Concept.dll",
131
131
  "MindExecution.Plugins.Directory.y74f55e8x3.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.8nz4wv2nsj.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.ihh8mkcn5x.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.0cs29v57jl.dll": "MindExecution.Web.dll",
132
+ "MindExecution.Plugins.PlanMaster.xfcd3pwd7p.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.78s7dilkza.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.hse89ibcyq.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.p1v0ug5h4f.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",
@@ -280,16 +280,16 @@
280
280
  "netstandard.yvr3prsx0x.dll": "sha256-EksNn8Luo4bOWqJ6X7dIe9qG9oOqwOVzjH2xYyMNi+E=",
281
281
  "MindExecution.Core.6rfnfdndxq.dll": "sha256-giL4rreoKsQoQ5gkLusNx7oL3w1l2UH52TYY1KBoIfQ=",
282
282
  "MindExecution.Kernel.z56elxihok.dll": "sha256-STATJelRGcW9SDGgsw6YmQi6tkQje52dy4lyT3QU4qs=",
283
- "MindExecution.Plugins.Concept.zczca3fsxz.dll": "sha256-A2RXdS42+UEr63Y54dc1OVeyowZIF7zUghMKuE2x11w=",
284
- "MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll": "sha256-Z6B9yXEfheo7LEUPAbjvd5xfl1B218Gu4EVRfleOvWg=",
285
- "MindExecution.Shared.ihh8mkcn5x.dll": "sha256-es97rs5TlDn5BgQqMBlWgIB0cflLmVW2shEwS3Wbajs=",
286
- "MindExecution.Web.0cs29v57jl.dll": "sha256-EG83w5NgZ5KzydPFrgZjTWm9BJJoX8RrOhqX81B2KPs="
283
+ "MindExecution.Plugins.Concept.faidxooqle.dll": "sha256-177f2QdWAeNbJvUOInKfZ/A6AGWzRqbge3x6ePcBDpI=",
284
+ "MindExecution.Plugins.PlanMaster.xfcd3pwd7p.dll": "sha256-PaeGgCgIzD09cq0h3SywgwHpIOkmyZ9bV3ArDCOWbpo=",
285
+ "MindExecution.Shared.hse89ibcyq.dll": "sha256-HyIJGWol5LmyLiefR4XpyKXtgpidf6Vr4pN/0hPskHA=",
286
+ "MindExecution.Web.p1v0ug5h4f.dll": "sha256-07TGezQJ2CbX3lpW2Qou4T/rJi4GFWmBv545YHiXfVk="
287
287
  },
288
288
  "lazyAssembly": {
289
289
  "MindExecution.Plugins.Admin.p5cs4ap87v.dll": "sha256-jahiJxaiE8hwMyRcg6rZGo4WBhBGFyAHYhOqlKjawWg=",
290
290
  "MindExecution.Plugins.Business.s35og5uz44.dll": "sha256-KOyk9eC6E/Gyfz2uWucHQmAayMQbShhLTqEymZFleh4=",
291
291
  "MindExecution.Plugins.Directory.y74f55e8x3.dll": "sha256-4gMuhIPLiX31U+jwhDT83rSPqZINONadmW+cvujfq1g=",
292
- "MindExecution.Plugins.YouTube.8nz4wv2nsj.dll": "sha256-dGlPW4FbXRcNEJi+KMYkd7CfAN2jv3Lp39KVp5wzu70="
292
+ "MindExecution.Plugins.YouTube.78s7dilkza.dll": "sha256-p4/WQZuUrLU9A/tUFh4J9I5vw15MjWZ3Tvs0v1oPbhY="
293
293
  }
294
294
  },
295
295
  "cacheBootResources": true,
@@ -558,7 +558,7 @@
558
558
  }
559
559
 
560
560
  const base = '_content/MindExecution.Shared/js/';
561
- const scriptVersion = '20260613-remote-status-rail-v495';
561
+ const scriptVersion = '20260613-remote-status-footer-v498';
562
562
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
563
563
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
564
564
  const criticalScripts = [
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "RMyUd5ec",
2
+ "version": "bj3XSoYW",
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-8c4EBD7ciaWPLPydbFB44+kg/t5LYC0wVUO7GM0eHHs=",
45
+ "hash": "sha256-SwiYCegnTRFedhTd9xEwvhhTJc7L4RbYTrP8KnQ+yjE=",
46
46
  "url": "_content/MindExecution.Shared/css/mind-map-overrides.css"
47
47
  },
48
48
  {
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-S8H0KuKhx8Fhbt07w1LBGxnBjT4km99JHDxzrHVFVho=",
89
+ "hash": "sha256-wA7GDKMviFGPIS+XIFaOKL57XgwYI5rm8uCzK3lbJ4Q=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -134,7 +134,7 @@
134
134
  "url": "_content/MindExecution.Shared/js/mind-map-node-search-worker.js"
135
135
  },
136
136
  {
137
- "hash": "sha256-VTAgwmg02CIzBkl/jETeQ45ZTIx/QEfxdeeMnklK2xI=",
137
+ "hash": "sha256-tcCpe0R8MVaT0B7nTN9owN5u6Nv547c0UTMGKnfiXpo=",
138
138
  "url": "_content/MindExecution.Shared/js/mind-map-nodes.js"
139
139
  },
140
140
  {
@@ -426,28 +426,28 @@
426
426
  "url": "_framework/MindExecution.Plugins.Business.s35og5uz44.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-A2RXdS42+UEr63Y54dc1OVeyowZIF7zUghMKuE2x11w=",
430
- "url": "_framework/MindExecution.Plugins.Concept.zczca3fsxz.dll"
429
+ "hash": "sha256-177f2QdWAeNbJvUOInKfZ/A6AGWzRqbge3x6ePcBDpI=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.faidxooqle.dll"
431
431
  },
432
432
  {
433
433
  "hash": "sha256-4gMuhIPLiX31U+jwhDT83rSPqZINONadmW+cvujfq1g=",
434
434
  "url": "_framework/MindExecution.Plugins.Directory.y74f55e8x3.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-Z6B9yXEfheo7LEUPAbjvd5xfl1B218Gu4EVRfleOvWg=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll"
437
+ "hash": "sha256-PaeGgCgIzD09cq0h3SywgwHpIOkmyZ9bV3ArDCOWbpo=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.xfcd3pwd7p.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-dGlPW4FbXRcNEJi+KMYkd7CfAN2jv3Lp39KVp5wzu70=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.8nz4wv2nsj.dll"
441
+ "hash": "sha256-p4/WQZuUrLU9A/tUFh4J9I5vw15MjWZ3Tvs0v1oPbhY=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.78s7dilkza.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-es97rs5TlDn5BgQqMBlWgIB0cflLmVW2shEwS3Wbajs=",
446
- "url": "_framework/MindExecution.Shared.ihh8mkcn5x.dll"
445
+ "hash": "sha256-HyIJGWol5LmyLiefR4XpyKXtgpidf6Vr4pN/0hPskHA=",
446
+ "url": "_framework/MindExecution.Shared.hse89ibcyq.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-EG83w5NgZ5KzydPFrgZjTWm9BJJoX8RrOhqX81B2KPs=",
450
- "url": "_framework/MindExecution.Web.0cs29v57jl.dll"
449
+ "hash": "sha256-07TGezQJ2CbX3lpW2Qou4T/rJi4GFWmBv545YHiXfVk=",
450
+ "url": "_framework/MindExecution.Web.p1v0ug5h4f.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-F7qorSpP5AuTT8FXK8Ya++UIan89IZHyuh+KPEHYUoA=",
773
+ "hash": "sha256-c2H0026f7tTv+gbbQCZt1/VMd200vEuF95yAiIZ1ydU=",
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-t14gn/m71KgCPH/qD7JKdV9e5WD4DsQVpId/vGYdorE=",
837
+ "hash": "sha256-DnjQ0S/JwJSnNvxVlB8B3VSxBcrtCmulbGRdjQZqsLg=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: RMyUd5ec */
1
+ /* Manifest version: bj3XSoYW */
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