@mindexec/cli 0.2.25 → 0.2.26

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.25",
3
+ "version": "0.2.26",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -20,11 +20,11 @@
20
20
  "scripts": {
21
21
  "start": "node launch-bridge.cjs",
22
22
  "dev": "node launch-bridge.cjs --watch",
23
- "test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.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",
24
- "test:remote": "node scripts/remote-hub-smoke.mjs",
25
- "test:remote:scale": "node scripts/remote-hub-scale-smoke.mjs",
26
- "test:remote:render": "node scripts/remote-fleet-render-smoke.mjs",
27
- "test:remote:http": "node scripts/remote-http-smoke.mjs",
23
+ "test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.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",
24
+ "test:remote": "node scripts/remote-hub-smoke.mjs",
25
+ "test:remote:scale": "node scripts/remote-hub-scale-smoke.mjs",
26
+ "test:remote:render": "node scripts/remote-fleet-render-smoke.mjs",
27
+ "test:remote:http": "node scripts/remote-http-smoke.mjs",
28
28
  "pack:dry": "npm pack --dry-run",
29
29
  "setup:grammars": "node scripts/setup-tree-sitter-grammars.mjs",
30
30
  "postinstall": "npm run setup:grammars"
@@ -557,7 +557,6 @@ try {
557
557
  const rawDevices = hub.listDevices();
558
558
  const devices = rawDevices.map(projectDevice);
559
559
  const connectedCount = devices.filter(device => device.Connected).length;
560
- const offlineCount = devices.length - connectedCount;
561
560
  const aiCount = devices.filter(device => device.Connected && device.AiAssistEnabled).length;
562
561
  const focusedDevice = devices.find(device => device.Connected && device.LiveStreamActive)
563
562
  || devices.find(device => device.Connected);
@@ -667,16 +666,34 @@ try {
667
666
  };
668
667
  manager.setModuleForTest({ dotNetHelper });
669
668
 
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);
670
683
  const bodyView = document.createElement('div');
671
684
  bodyView.dataset.remoteFleetAutoMonitor = 'false';
672
685
  bodyView.dataset.remoteFleetFocusDeviceId = focusedDevice.DeviceId;
673
- document.body.appendChild(bodyView);
686
+ nodeShell.appendChild(header);
687
+ nodeShell.appendChild(bodyView);
688
+ document.body.appendChild(nodeShell);
674
689
 
675
690
  manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
676
691
 
677
692
  let cards = bodyView.querySelectorAll('article[data-device-id]');
678
693
  assert.equal(cards.length, SYNTHETIC_COUNT);
679
- assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${SYNTHETIC_COUNT}/${SYNTHETIC_COUNT}`);
694
+ assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]'), null);
695
+ assert.equal(bodyView.querySelector('[data-remote-fleet-search="true"]'), null);
696
+ assert.equal(bodyView.querySelectorAll('select').length, 0);
680
697
  assert.ok(bodyView.querySelector('[data-remote-fleet-device-grid="true"]'));
681
698
  const initialDetailPanel = bodyView.querySelector('[data-remote-fleet-detail-panel="true"]');
682
699
  assert.equal(initialDetailPanel?.dataset.deviceId, focusedDevice.DeviceId);
@@ -702,9 +719,12 @@ try {
702
719
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-connected"]'), null);
703
720
  assert.equal(bodyView.querySelector('[data-remote-fleet-task-input="true"]'), null);
704
721
  assert.equal(bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]'), null);
705
- assert.ok(bodyView.textContent.includes('all devices, no paging'));
706
- assert.equal(bodyView.querySelector('[data-remote-fleet-host-state="true"]')?.textContent.includes('Host: Inactive'), true);
707
- const initialHostButton = bodyView.querySelector('[data-remote-fleet-action="set-host"]');
722
+ assert.equal(bodyView.textContent.includes('all devices, no paging'), false);
723
+ assert.equal(bodyView.textContent.includes('Host: Inactive'), false);
724
+ assert.equal(bodyView.textContent.includes('No screen'), false);
725
+ assert.equal(bodyView.textContent.includes('@mindexec/cli'), false);
726
+ assert.equal(nodeShell.querySelector('[data-remote-fleet-host-indicator="true"]')?.dataset.remoteFleetHostActive, 'false');
727
+ const initialHostButton = nodeShell.querySelector('[data-remote-fleet-action="set-host"]');
708
728
  assert.equal(initialHostButton?.textContent, 'Set Host');
709
729
  initialHostButton.dispatchEvent({ type: 'click' });
710
730
  await wait();
@@ -719,9 +739,10 @@ try {
719
739
  leaseMs: 30000
720
740
  });
721
741
  manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
722
- assert.equal(bodyView.querySelector('[data-remote-fleet-host-state="true"]')?.textContent.includes('Host: Active'), true);
723
- assert.equal(bodyView.querySelector('[data-remote-fleet-action="set-host"]')?.textContent, 'Hosting');
724
- const stopHostButton = bodyView.querySelector('[data-remote-fleet-action="stop-host"]');
742
+ assert.equal(bodyView.textContent.includes('Host: Active'), false);
743
+ assert.equal(nodeShell.querySelector('[data-remote-fleet-host-indicator="true"]')?.dataset.remoteFleetHostActive, 'true');
744
+ assert.equal(nodeShell.querySelector('[data-remote-fleet-action="set-host"]')?.textContent, 'Set Host');
745
+ const stopHostButton = nodeShell.querySelector('[data-remote-fleet-action="stop-host"]');
725
746
  assert.ok(stopHostButton);
726
747
  const stopHostStart = dotNetCalls.length;
727
748
  stopHostButton.dispatchEvent({ type: 'click' });
@@ -733,7 +754,6 @@ try {
733
754
  assert.equal(stopHostCall.args[0], 'remote-fleet-render-smoke');
734
755
  assert.equal(stopHostCall.args[1], false);
735
756
 
736
- assert.match(bodyView.querySelector('[data-remote-fleet-manager-version="true"]')?.textContent || '', /^@mindexec\/cli render-smoke-manager - 127\.0\.0\.1:\d+$/);
737
757
  assert.ok(livePanel?.textContent.includes('RemoteFast 20 fps'), livePanel?.textContent || bodyView.textContent);
738
758
  assert.equal(bodyView.querySelector('code'), null);
739
759
  assert.equal(bodyView.textContent.includes('npx @mindexec/remote connect'), false);
@@ -761,49 +781,31 @@ try {
761
781
  assert.equal(bodyView.dataset.remoteFleetTaskFollowKey, 'render-smoke-batch');
762
782
  assert.ok(Number(bodyView.dataset.remoteFleetTaskFollowTicks || '0') >= 1);
763
783
 
764
- const searchInput = bodyView.querySelector('[data-remote-fleet-search="true"]');
765
- searchInput.value = 'synthetic pc 0001';
766
- searchInput.dispatchEvent({ type: 'input' });
767
- cards = bodyView.querySelectorAll('article[data-device-id]');
768
- assert.equal(cards.filter(card => card.style.display !== 'none').length, 1);
769
- assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `1/${SYNTHETIC_COUNT}`);
770
-
771
- const selects = bodyView.querySelectorAll('select');
772
- assert.equal(selects.length, 4);
773
- const [filterSelect, , groupSelect] = selects;
774
- searchInput.value = '';
775
- searchInput.dispatchEvent({ type: 'input' });
776
- filterSelect.value = 'offline';
777
- filterSelect.dispatchEvent({ type: 'change' });
778
- assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${offlineCount}/${SYNTHETIC_COUNT}`);
779
-
780
- filterSelect.value = 'ai';
781
- filterSelect.dispatchEvent({ type: 'change' });
782
- assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${aiCount}/${SYNTHETIC_COUNT}`);
783
-
784
- filterSelect.value = 'all';
785
- filterSelect.dispatchEvent({ type: 'change' });
786
- groupSelect.value = 'status';
787
- groupSelect.dispatchEvent({ type: 'change' });
788
- assert.equal(bodyView.dataset.remoteFleetGroup, 'status');
789
- assert.ok(bodyView.querySelectorAll('[data-remote-fleet-group-header="true"]').length >= 2);
790
- assert.equal(bodyView.querySelectorAll('article[data-device-id]').length, SYNTHETIC_COUNT);
791
-
792
784
  const feedback = bodyView.querySelector('[data-remote-fleet-task-feedback="true"]');
793
785
  assert.ok(feedback);
794
- assert.equal(feedback.style.display, 'none');
795
786
 
796
- const activeSelects = bodyView.querySelectorAll('select');
797
- assert.equal(activeSelects.length, 4);
798
- const [aiFilterSelect] = activeSelects;
799
787
  assert.equal(bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]'), null);
800
- aiFilterSelect.value = 'ai';
801
- aiFilterSelect.dispatchEvent({ type: 'change' });
802
- assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${aiCount}/${SYNTHETIC_COUNT}`);
803
788
  assert.equal(dotNetCalls.some(call =>
804
789
  call.methodName === 'DispatchRemoteFleetTaskBatchFromJs'
805
790
  || call.methodName === 'DispatchRemoteFleetTaskFromJs'), false);
806
791
 
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);
801
+ document.body.appendChild(emptyShell);
802
+ manager.renderRemoteFleetMonitorForTest(emptyBody, buildMonitorNode([], hub.getStatus({ includeSecrets: true })));
803
+ assert.equal(emptyBody.querySelectorAll('[data-remote-fleet-empty-screen="true"]').length, 6);
804
+ assert.equal(emptyBody.textContent.includes('No devices connected yet.'), false);
805
+ assert.equal(emptyBody.textContent.includes('Select a screen'), false);
806
+ assert.equal(emptyBody.textContent.includes('No screen'), false);
807
+ assert.equal(emptyBody.textContent.includes('Endpoint 127.0.0.1'), false);
808
+
807
809
  const deviceBody = document.createElement('div');
808
810
  document.body.appendChild(deviceBody);
809
811
  manager.renderRemoteFleetDeviceForTest(deviceBody, buildDeviceNode(focusedDevice, hub.getStatus()));
@@ -3551,6 +3551,7 @@
3551
3551
  { key: 'decision', iconClass: 'fa-solid fa-code-branch' },
3552
3552
  { key: 'user-check', iconClass: 'fa-solid fa-user-check' },
3553
3553
  { key: 'output', iconClass: 'fa-solid fa-file-export' },
3554
+ { key: 'network-wired', iconClass: 'fa-solid fa-network-wired' },
3554
3555
  { key: 'robot', iconClass: 'fa-solid fa-robot' }
3555
3556
  ];
3556
3557
 
@@ -12230,6 +12231,86 @@
12230
12231
  return button;
12231
12232
  }
12232
12233
 
12234
+ function attachRemoteFleetTitleActions(bodyView, hostButton, stopHostButton, hostState) {
12235
+ const container = bodyView?.closest?.('.map-node-remote-fleet');
12236
+ const header = container?.querySelector?.('.map-node-memo__header') || null;
12237
+ const existingActions = header?.querySelector?.('[data-remote-fleet-title-actions="true"]') || null;
12238
+ existingActions?.remove?.();
12239
+
12240
+ const titleActions = document.createElement('div');
12241
+ titleActions.dataset.remoteFleetTitleActions = 'true';
12242
+ titleActions.style.cssText = `
12243
+ display: inline-flex;
12244
+ align-items: center;
12245
+ justify-content: flex-end;
12246
+ gap: 7px;
12247
+ min-width: 0;
12248
+ pointer-events: auto;
12249
+ `;
12250
+
12251
+ const normalizedState = String(hostState || 'inactive').trim().toLowerCase();
12252
+ const active = normalizedState === 'hosting';
12253
+ const indicator = document.createElement('span');
12254
+ indicator.dataset.remoteFleetHostIndicator = 'true';
12255
+ indicator.dataset.remoteFleetHostActive = active ? 'true' : 'false';
12256
+ indicator.title = active ? 'Host active' : 'Host inactive';
12257
+ indicator.style.cssText = `
12258
+ flex: 0 0 auto;
12259
+ width: 9px;
12260
+ height: 9px;
12261
+ border-radius: 999px;
12262
+ background: ${active ? '#2563eb' : '#94a3b8'};
12263
+ box-shadow: ${active ? '0 0 0 4px rgba(37, 99, 235, 0.14)' : '0 0 0 4px rgba(148, 163, 184, 0.14)'};
12264
+ `;
12265
+
12266
+ titleActions.appendChild(indicator);
12267
+ titleActions.appendChild(hostButton);
12268
+ if (stopHostButton) {
12269
+ titleActions.appendChild(stopHostButton);
12270
+ }
12271
+
12272
+ if (header) {
12273
+ header.style.gridTemplateColumns = '46px minmax(0, 1fr) auto';
12274
+ header.appendChild(titleActions);
12275
+ } else {
12276
+ titleActions.style.alignSelf = 'flex-end';
12277
+ bodyView?.prepend?.(titleActions);
12278
+ }
12279
+
12280
+ return titleActions;
12281
+ }
12282
+
12283
+ function createRemoteFleetEmptyScreens() {
12284
+ const shell = document.createElement('section');
12285
+ shell.dataset.remoteFleetEmptyScreens = 'true';
12286
+ shell.style.cssText = `
12287
+ flex: 1 1 auto;
12288
+ min-height: 0;
12289
+ overflow: hidden;
12290
+ display: grid;
12291
+ grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
12292
+ align-content: start;
12293
+ gap: 10px;
12294
+ padding: 2px 4px 6px 0;
12295
+ `;
12296
+
12297
+ for (let index = 0; index < 6; index += 1) {
12298
+ const screen = document.createElement('div');
12299
+ screen.dataset.remoteFleetEmptyScreen = 'true';
12300
+ screen.style.cssText = `
12301
+ aspect-ratio: 16 / 9;
12302
+ min-height: 96px;
12303
+ border-radius: 8px;
12304
+ border: 1px solid rgba(203, 213, 225, 0.72);
12305
+ background: #ffffff;
12306
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96), 0 8px 18px rgba(15, 23, 42, 0.04);
12307
+ `;
12308
+ shell.appendChild(screen);
12309
+ }
12310
+
12311
+ return shell;
12312
+ }
12313
+
12233
12314
  function createRemoteFleetSelect(options, value, title) {
12234
12315
  const select = document.createElement('select');
12235
12316
  select.title = title || '';
@@ -12624,11 +12705,11 @@
12624
12705
 
12625
12706
  const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
12626
12707
  const device = parseRemoteFleetPinnedDevice(nodeModel);
12627
- const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
12628
12708
  const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
12629
12709
  const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
12630
12710
  const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
12631
12711
  const deviceId = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetPinnedDeviceId', device ? getRemoteFleetDeviceId(device) : '');
12712
+ const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
12632
12713
 
12633
12714
  bodyView.dataset.src = `remote-device:${deviceId}:${refreshedAt}`;
12634
12715
  bodyView.classList.add('map-node-remote-fleet__body');
@@ -13173,18 +13254,11 @@
13173
13254
  devices.filter(isRemoteFleetDeviceConnected).length));
13174
13255
  const taskCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceTaskCapable(device)).length;
13175
13256
  const aiCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceAiCapable(device)).length;
13176
- const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
13177
- const managerPackage = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetManagerPackage', '@mindexec/cli');
13178
- const managerVersion = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetManagerVersion', '');
13179
13257
  const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
13180
13258
  const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
13181
13259
  const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
13182
13260
  const hostTargetState = String(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHostTargetState', 'inactive')).trim().toLowerCase();
13183
- const hostTargetNodeId = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHostTargetNodeId', '');
13184
- const hostTargetEndpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHostTargetEndpoint', endpoint);
13185
- const hostTargetExpiresAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHostTargetExpiresAtUtc', '');
13186
13261
  const isHostingTarget = hostTargetState === 'hosting';
13187
- const otherMonitorHosting = hostTargetState === 'other-monitor';
13188
13262
 
13189
13263
  if (!isHostingTarget) {
13190
13264
  stopRemoteFleetHostLeaseTimer(nodeId);
@@ -13206,78 +13280,8 @@
13206
13280
  background: linear-gradient(180deg, rgba(248, 250, 252, 0.96), rgba(241, 245, 249, 0.92));
13207
13281
  `;
13208
13282
 
13209
- const headerRow = document.createElement('div');
13210
- headerRow.style.cssText = `
13211
- display: flex;
13212
- align-items: flex-start;
13213
- justify-content: space-between;
13214
- gap: 10px;
13215
- flex: 0 0 auto;
13216
- min-width: 0;
13217
- `;
13218
- const headerMeta = document.createElement('div');
13219
- headerMeta.style.cssText = `
13220
- min-width: 0;
13221
- display: flex;
13222
- flex-direction: column;
13223
- gap: 4px;
13224
- `;
13225
- const managerRow = document.createElement('div');
13226
- managerRow.dataset.remoteFleetManagerVersion = 'true';
13227
- managerRow.textContent = `${managerPackage}${managerVersion ? ` ${managerVersion}` : ''} - ${endpoint}`;
13228
- managerRow.title = managerRow.textContent;
13229
- managerRow.style.cssText = `
13230
- flex: 0 0 auto;
13231
- min-width: 0;
13232
- overflow: hidden;
13233
- text-overflow: ellipsis;
13234
- white-space: nowrap;
13235
- color: #475569;
13236
- font-size: 10px;
13237
- font-weight: 850;
13238
- line-height: 1.2;
13239
- letter-spacing: 0;
13240
- `;
13241
-
13242
- const hostStatusRow = document.createElement('div');
13243
- hostStatusRow.dataset.remoteFleetHostState = 'true';
13244
- const hostStatusText = isHostingTarget
13245
- ? 'Host: Active'
13246
- : otherMonitorHosting
13247
- ? 'Host: Other monitor'
13248
- : 'Host: Inactive';
13249
- const hostLeaseText = isHostingTarget && hostTargetExpiresAt
13250
- ? ` - lease ${formatRemoteFleetTimeUntil(hostTargetExpiresAt)}`
13251
- : '';
13252
- hostStatusRow.textContent = `${hostStatusText}${hostLeaseText}`;
13253
- hostStatusRow.title = otherMonitorHosting
13254
- ? `Active host monitor: ${hostTargetNodeId || 'unknown'}`
13255
- : `Endpoint ${hostTargetEndpoint || endpoint}`;
13256
- hostStatusRow.style.cssText = `
13257
- flex: 0 0 auto;
13258
- min-width: 0;
13259
- overflow: hidden;
13260
- text-overflow: ellipsis;
13261
- white-space: nowrap;
13262
- color: ${isHostingTarget ? '#047857' : otherMonitorHosting ? '#7c2d12' : '#64748b'};
13263
- font-size: 10px;
13264
- font-weight: 900;
13265
- line-height: 1.2;
13266
- letter-spacing: 0;
13267
- `;
13268
- headerMeta.appendChild(managerRow);
13269
- headerMeta.appendChild(hostStatusRow);
13270
-
13271
- const hostActions = document.createElement('div');
13272
- hostActions.style.cssText = `
13273
- flex: 0 0 auto;
13274
- display: inline-flex;
13275
- align-items: center;
13276
- justify-content: flex-end;
13277
- gap: 6px;
13278
- `;
13279
13283
  const hostButton = createRemoteFleetButton(
13280
- isHostingTarget ? 'Hosting' : 'Set Host',
13284
+ 'Set Host',
13281
13285
  isHostingTarget
13282
13286
  ? 'Renew this monitor as the active host target for remote agents.'
13283
13287
  : 'Set this monitor as the active host target for remote agents.',
@@ -13285,9 +13289,9 @@
13285
13289
  hostButton.dataset.remoteFleetHostActive = isHostingTarget ? 'true' : 'false';
13286
13290
  hostButton.style.height = '30px';
13287
13291
  hostButton.style.minWidth = '78px';
13288
- hostButton.style.borderColor = isHostingTarget ? 'rgba(16, 185, 129, 0.38)' : 'rgba(37, 99, 235, 0.32)';
13289
- hostButton.style.background = isHostingTarget ? 'rgba(236, 253, 245, 0.96)' : '#ffffff';
13290
- hostButton.style.color = isHostingTarget ? '#047857' : '#1d4ed8';
13292
+ hostButton.style.borderColor = isHostingTarget ? 'rgba(37, 99, 235, 0.44)' : 'rgba(37, 99, 235, 0.32)';
13293
+ hostButton.style.background = isHostingTarget ? 'rgba(239, 246, 255, 0.98)' : '#ffffff';
13294
+ hostButton.style.color = '#1d4ed8';
13291
13295
  let stopHostButton = null;
13292
13296
  if (isHostingTarget) {
13293
13297
  stopHostButton = createRemoteFleetButton('Stop', 'Stop using this monitor as the host target.', 'stop-host');
@@ -13296,13 +13300,7 @@
13296
13300
  stopHostButton.style.color = '#b91c1c';
13297
13301
  stopHostButton.style.background = 'rgba(254, 242, 242, 0.94)';
13298
13302
  }
13299
- hostActions.appendChild(hostButton);
13300
- if (stopHostButton) {
13301
- hostActions.appendChild(stopHostButton);
13302
- }
13303
- headerRow.appendChild(headerMeta);
13304
- headerRow.appendChild(hostActions);
13305
- bodyView.appendChild(headerRow);
13303
+ attachRemoteFleetTitleActions(bodyView, hostButton, stopHostButton, hostTargetState);
13306
13304
 
13307
13305
  const top = document.createElement('div');
13308
13306
  top.style.cssText = `
@@ -13329,7 +13327,7 @@
13329
13327
  const searchInput = document.createElement('input');
13330
13328
  searchInput.type = 'search';
13331
13329
  searchInput.value = searchState;
13332
- searchInput.placeholder = 'Search devices';
13330
+ searchInput.placeholder = '';
13333
13331
  searchInput.dataset.remoteFleetSearch = 'true';
13334
13332
  searchInput.autocomplete = 'off';
13335
13333
  searchInput.spellcheck = false;
@@ -13401,7 +13399,7 @@
13401
13399
  filterRow.appendChild(groupSelect);
13402
13400
  filterRow.appendChild(densitySelect);
13403
13401
  filterRow.appendChild(matchCount);
13404
- bodyView.appendChild(filterRow);
13402
+ filterRow.dataset.remoteFleetAdvancedFilters = 'hidden';
13405
13403
 
13406
13404
  const taskRow = document.createElement('div');
13407
13405
  taskRow.style.cssText = `
@@ -13817,8 +13815,6 @@
13817
13815
  const createDevicePreview = (device, mode = 'tile') => {
13818
13816
  const name = getRemoteFleetDeviceName(device);
13819
13817
  const connectedDevice = isRemoteFleetDeviceConnected(device);
13820
- const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
13821
- const liveStreamEnabled = device?.liveStreamEnabled === true || device?.LiveStreamEnabled === true;
13822
13818
  const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
13823
13819
  const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
13824
13820
  const liveFrameDataUrl = String(device?.liveFrameDataUrl || device?.LiveFrameDataUrl || '');
@@ -13837,7 +13833,7 @@
13837
13833
  aspect-ratio: 16 / 9;
13838
13834
  overflow: hidden;
13839
13835
  border-radius: ${isDetail ? '8px' : '6px'};
13840
- background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
13836
+ background: ${(hasLiveFrame || hasThumbnail) ? 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)' : '#ffffff'};
13841
13837
  border: 1px solid rgba(15, 23, 42, ${isDetail ? '0.16' : '0.10'});
13842
13838
  `;
13843
13839
 
@@ -13856,17 +13852,11 @@
13856
13852
  preview.appendChild(image);
13857
13853
  } else {
13858
13854
  const placeholder = document.createElement('div');
13859
- placeholder.textContent = thumbnailEnabled || liveStreamEnabled ? 'No screen' : 'No screen';
13855
+ placeholder.dataset.remoteFleetScreenPlaceholder = 'true';
13860
13856
  placeholder.style.cssText = `
13861
13857
  position: absolute;
13862
13858
  inset: 0;
13863
- display: flex;
13864
- align-items: center;
13865
- justify-content: center;
13866
- color: rgba(226, 232, 240, 0.80);
13867
- font-size: ${isDetail ? '12px' : '10px'};
13868
- font-weight: 900;
13869
- letter-spacing: 0;
13859
+ background: #ffffff;
13870
13860
  `;
13871
13861
  preview.appendChild(placeholder);
13872
13862
  }
@@ -14112,24 +14102,7 @@
14112
14102
  padding-right: 4px;
14113
14103
  `;
14114
14104
 
14115
- if (devices.length === 0) {
14116
- const empty = document.createElement('div');
14117
- empty.textContent = 'No devices connected yet.';
14118
- empty.style.cssText = `
14119
- grid-column: 1 / -1;
14120
- display: flex;
14121
- align-items: center;
14122
- min-height: 94px;
14123
- padding: 14px;
14124
- border-radius: 8px;
14125
- border: 1px dashed rgba(100, 116, 139, 0.36);
14126
- color: #475569;
14127
- font-size: 13px;
14128
- font-weight: 800;
14129
- background: rgba(255, 255, 255, 0.74);
14130
- `;
14131
- grid.appendChild(empty);
14132
- } else {
14105
+ if (devices.length > 0) {
14133
14106
  const groupStats = new Map();
14134
14107
  if (groupState !== 'none') {
14135
14108
  devices.forEach(device => {
@@ -14252,24 +14225,16 @@
14252
14225
  grid.appendChild(noMatch);
14253
14226
  }
14254
14227
 
14255
- monitorWorkspace.appendChild(grid);
14256
- monitorWorkspace.appendChild(createSelectedDevicePanel(selectedDevice));
14228
+ if (devices.length === 0) {
14229
+ monitorWorkspace.style.display = 'flex';
14230
+ monitorWorkspace.style.flexDirection = 'column';
14231
+ monitorWorkspace.appendChild(createRemoteFleetEmptyScreens());
14232
+ } else {
14233
+ monitorWorkspace.appendChild(grid);
14234
+ monitorWorkspace.appendChild(createSelectedDevicePanel(selectedDevice));
14235
+ }
14257
14236
  bodyView.appendChild(monitorWorkspace);
14258
14237
 
14259
- const footer = document.createElement('div');
14260
- footer.textContent = `Endpoint ${endpoint} - all devices, no paging - group ${groupState} - ${autoMonitorState ? 'auto monitor' : 'manual'} - refreshed ${formatRemoteFleetAge(refreshedAt)}`;
14261
- footer.style.cssText = `
14262
- flex: 0 0 auto;
14263
- color: #64748b;
14264
- font-size: 11px;
14265
- font-weight: 700;
14266
- line-height: 1.2;
14267
- overflow: hidden;
14268
- text-overflow: ellipsis;
14269
- white-space: nowrap;
14270
- `;
14271
- bodyView.appendChild(footer);
14272
-
14273
14238
  const setTaskFeedback = (message, tone = 'info') => {
14274
14239
  taskFeedback.textContent = message || '';
14275
14240
  taskFeedback.style.display = message ? 'block' : 'none';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-8opnBRmWTSsO9dqkwmfxCVP/1IWjG5lnkDoRFXzPaIg=",
4
+ "hash": "sha256-scsV40Knf96MECCS5TE6GBMBRc/aVh7dlYynMn/8Zjs=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -131,7 +131,7 @@
131
131
  "MindExecution.Plugins.Directory.y74f55e8x3.dll": "MindExecution.Plugins.Directory.dll",
132
132
  "MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll": "MindExecution.Plugins.PlanMaster.dll",
133
133
  "MindExecution.Plugins.YouTube.8nz4wv2nsj.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.v6ani8nfp8.dll": "MindExecution.Shared.dll",
134
+ "MindExecution.Shared.ihh8mkcn5x.dll": "MindExecution.Shared.dll",
135
135
  "MindExecution.Web.0cs29v57jl.dll": "MindExecution.Web.dll",
136
136
  "dotnet.js": "dotnet.js",
137
137
  "dotnet.native.qc8g39g30v.js": "dotnet.native.js",
@@ -282,7 +282,7 @@
282
282
  "MindExecution.Kernel.z56elxihok.dll": "sha256-STATJelRGcW9SDGgsw6YmQi6tkQje52dy4lyT3QU4qs=",
283
283
  "MindExecution.Plugins.Concept.zczca3fsxz.dll": "sha256-A2RXdS42+UEr63Y54dc1OVeyowZIF7zUghMKuE2x11w=",
284
284
  "MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll": "sha256-Z6B9yXEfheo7LEUPAbjvd5xfl1B218Gu4EVRfleOvWg=",
285
- "MindExecution.Shared.v6ani8nfp8.dll": "sha256-VzXMEqJZy84RMk0aBnLB415/HS+q83nl7RTXiNLvlPU=",
285
+ "MindExecution.Shared.ihh8mkcn5x.dll": "sha256-es97rs5TlDn5BgQqMBlWgIB0cflLmVW2shEwS3Wbajs=",
286
286
  "MindExecution.Web.0cs29v57jl.dll": "sha256-EG83w5NgZ5KzydPFrgZjTWm9BJJoX8RrOhqX81B2KPs="
287
287
  },
288
288
  "lazyAssembly": {
@@ -558,7 +558,7 @@
558
558
  }
559
559
 
560
560
  const base = '_content/MindExecution.Shared/js/';
561
- const scriptVersion = '20260612-remote-clean-monitor-v491';
561
+ const scriptVersion = '20260613-remote-screen-monitor-v492';
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": "GmythO0P",
2
+ "version": "pdZBY4HV",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-wensAgsKs3nWcJ2Ei0LTHd5i4b+baUwmP5UbK1MLG9I=",
89
+ "hash": "sha256-evnO3l4V8QouakV955IY87wMR6kYuXQU5LEAjOjONO0=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -442,8 +442,8 @@
442
442
  "url": "_framework/MindExecution.Plugins.YouTube.8nz4wv2nsj.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-VzXMEqJZy84RMk0aBnLB415/HS+q83nl7RTXiNLvlPU=",
446
- "url": "_framework/MindExecution.Shared.v6ani8nfp8.dll"
445
+ "hash": "sha256-es97rs5TlDn5BgQqMBlWgIB0cflLmVW2shEwS3Wbajs=",
446
+ "url": "_framework/MindExecution.Shared.ihh8mkcn5x.dll"
447
447
  },
448
448
  {
449
449
  "hash": "sha256-EG83w5NgZ5KzydPFrgZjTWm9BJJoX8RrOhqX81B2KPs=",
@@ -770,7 +770,7 @@
770
770
  "url": "_framework/Websocket.Client.vapounvmnl.dll"
771
771
  },
772
772
  {
773
- "hash": "sha256-SnKuukkikWBroUqaw7qwE4nOor1NpE81zg2DHpQCNtw=",
773
+ "hash": "sha256-F7qorSpP5AuTT8FXK8Ya++UIan89IZHyuh+KPEHYUoA=",
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-OrDc5s2gYCFEgTQDxl3FK0xyRLULLSusjX3jgP+azHQ=",
837
+ "hash": "sha256-BUjUsG/CTn5wyAwuO52yc0McqhRH8QVA7dDCN+LuIBE=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: GmythO0P */
1
+ /* Manifest version: pdZBY4HV */
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