@mindexec/cli 0.2.8 → 0.2.9

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.8",
3
+ "version": "0.2.9",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -12167,6 +12167,68 @@
12167
12167
  return getRemoteFleetDeviceField(device, 'thumbnailEnabled', 'ThumbnailEnabled', false) === true;
12168
12168
  }
12169
12169
 
12170
+ function getRemoteFleetGroupInfo(device, groupMode) {
12171
+ const mode = String(groupMode || 'none');
12172
+ const connected = isRemoteFleetDeviceConnected(device);
12173
+ const latestTaskStatus = String(getRemoteFleetDeviceField(device, 'latestTaskStatus', 'LatestTaskStatus', '')).toLowerCase();
12174
+ const latestTaskError = String(getRemoteFleetDeviceField(device, 'latestTaskError', 'LatestTaskError', ''));
12175
+ const platform = String(getRemoteFleetDeviceField(device, 'platform', 'Platform', '') || 'unknown').toLowerCase();
12176
+
12177
+ switch (mode) {
12178
+ case 'status':
12179
+ return connected
12180
+ ? { key: 'status-online', label: 'Online', order: 0 }
12181
+ : { key: 'status-offline', label: 'Offline', order: 9 };
12182
+ case 'activity':
12183
+ if (isRemoteFleetLiveActive(device)) {
12184
+ return { key: 'activity-live', label: 'Live streams', order: 0 };
12185
+ }
12186
+ if (latestTaskError || latestTaskStatus === 'failed') {
12187
+ return { key: 'activity-issues', label: 'Needs attention', order: 1 };
12188
+ }
12189
+ if (latestTaskStatus && latestTaskStatus !== 'completed') {
12190
+ return { key: 'activity-working', label: 'Task in progress', order: 2 };
12191
+ }
12192
+ if (hasRemoteFleetThumbnail(device) || hasRemoteFleetLiveFrame(device)) {
12193
+ return { key: 'activity-frames', label: 'Recent frames', order: 3 };
12194
+ }
12195
+ return connected
12196
+ ? { key: 'activity-idle', label: 'Online idle', order: 4 }
12197
+ : { key: 'activity-offline', label: 'Offline', order: 9 };
12198
+ case 'platform':
12199
+ return {
12200
+ key: `platform-${platform || 'unknown'}`,
12201
+ label: platform ? platform.toUpperCase() : 'Unknown platform',
12202
+ order: connected ? 0 : 8
12203
+ };
12204
+ case 'capability':
12205
+ if (!connected) {
12206
+ return { key: 'capability-offline', label: 'Offline', order: 9 };
12207
+ }
12208
+ if (isRemoteFleetDeviceAiCapable(device)) {
12209
+ return { key: 'capability-ai', label: 'AI assist ready', order: 0 };
12210
+ }
12211
+ if (isRemoteFleetDeviceTaskCapable(device)) {
12212
+ return { key: 'capability-task', label: 'Task capable', order: 1 };
12213
+ }
12214
+ if (isRemoteFleetThumbnailCapable(device)) {
12215
+ return { key: 'capability-monitor', label: 'Monitor only', order: 2 };
12216
+ }
12217
+ return { key: 'capability-status', label: 'Status only', order: 3 };
12218
+ case 'none':
12219
+ default:
12220
+ return { key: '', label: '', order: 0 };
12221
+ }
12222
+ }
12223
+
12224
+ function compareRemoteFleetGroups(left, right, groupMode) {
12225
+ const leftGroup = getRemoteFleetGroupInfo(left, groupMode);
12226
+ const rightGroup = getRemoteFleetGroupInfo(right, groupMode);
12227
+ return (leftGroup.order - rightGroup.order)
12228
+ || leftGroup.label.localeCompare(rightGroup.label)
12229
+ || leftGroup.key.localeCompare(rightGroup.key);
12230
+ }
12231
+
12170
12232
  function getRemoteFleetTimestampMs(value) {
12171
12233
  const timestamp = Date.parse(String(value || ''));
12172
12234
  return Number.isFinite(timestamp) ? timestamp : 0;
@@ -12246,12 +12308,18 @@
12246
12308
  const searchState = bodyView.dataset.remoteFleetSearch || '';
12247
12309
  const filterState = bodyView.dataset.remoteFleetFilter || 'all';
12248
12310
  const sortState = bodyView.dataset.remoteFleetSort || 'status';
12311
+ const groupState = bodyView.dataset.remoteFleetGroup || 'none';
12249
12312
  const densityState = bodyView.dataset.remoteFleetDensity || 'cards';
12250
12313
  const aiAssistState = bodyView.dataset.remoteFleetAiAssist === 'true';
12251
12314
  const autoMonitorState = bodyView.dataset.remoteFleetAutoMonitor !== 'false';
12252
12315
  const focusState = bodyView.dataset.remoteFleetFocusDeviceId || '';
12253
- const devices = [...parseRemoteFleetDevices(nodeModel)]
12316
+ const sortedDevices = [...parseRemoteFleetDevices(nodeModel)]
12254
12317
  .sort((left, right) => compareRemoteFleetDevices(left, right, sortState));
12318
+ const devices = groupState === 'none'
12319
+ ? sortedDevices
12320
+ : [...sortedDevices].sort((left, right) =>
12321
+ compareRemoteFleetGroups(left, right, groupState)
12322
+ || compareRemoteFleetDevices(left, right, sortState));
12255
12323
  const hasActiveLiveStream = devices.some(isRemoteFleetLiveActive);
12256
12324
  const hasMonitorTargets = devices.some(device =>
12257
12325
  isRemoteFleetDeviceConnected(device) && isRemoteFleetThumbnailCapable(device));
@@ -12376,7 +12444,7 @@
12376
12444
  const filterRow = document.createElement('div');
12377
12445
  filterRow.style.cssText = `
12378
12446
  display: grid;
12379
- grid-template-columns: minmax(150px, 1.2fr) minmax(96px, 0.7fr) minmax(96px, 0.7fr) minmax(84px, 0.55fr) auto;
12447
+ grid-template-columns: minmax(140px, 1.25fr) minmax(86px, 0.62fr) minmax(86px, 0.62fr) minmax(98px, 0.7fr) minmax(78px, 0.5fr) auto;
12380
12448
  gap: 8px;
12381
12449
  align-items: center;
12382
12450
  flex: 0 0 auto;
@@ -12424,6 +12492,14 @@
12424
12492
  { value: 'task', label: 'Task' }
12425
12493
  ], sortState, 'Device sort');
12426
12494
 
12495
+ const groupSelect = createRemoteFleetSelect([
12496
+ { value: 'none', label: 'No group' },
12497
+ { value: 'status', label: 'Status' },
12498
+ { value: 'activity', label: 'Activity' },
12499
+ { value: 'platform', label: 'Platform' },
12500
+ { value: 'capability', label: 'Capability' }
12501
+ ], groupState, 'Device grouping');
12502
+
12427
12503
  const densitySelect = createRemoteFleetSelect([
12428
12504
  { value: 'cards', label: 'Cards' },
12429
12505
  { value: 'dense', label: 'Dense' }
@@ -12446,6 +12522,7 @@
12446
12522
  filterRow.appendChild(searchInput);
12447
12523
  filterRow.appendChild(filterSelect);
12448
12524
  filterRow.appendChild(sortSelect);
12525
+ filterRow.appendChild(groupSelect);
12449
12526
  filterRow.appendChild(densitySelect);
12450
12527
  filterRow.appendChild(matchCount);
12451
12528
  bodyView.appendChild(filterRow);
@@ -12726,6 +12803,25 @@
12726
12803
  `;
12727
12804
  grid.appendChild(empty);
12728
12805
  } else {
12806
+ const groupStats = new Map();
12807
+ if (groupState !== 'none') {
12808
+ devices.forEach(device => {
12809
+ const group = getRemoteFleetGroupInfo(device, groupState);
12810
+ if (!group.key) return;
12811
+ const stat = groupStats.get(group.key) || {
12812
+ label: group.label,
12813
+ total: 0,
12814
+ connected: 0
12815
+ };
12816
+ stat.total += 1;
12817
+ if (isRemoteFleetDeviceConnected(device)) {
12818
+ stat.connected += 1;
12819
+ }
12820
+ groupStats.set(group.key, stat);
12821
+ });
12822
+ }
12823
+
12824
+ let lastGroupKey = '';
12729
12825
  devices.forEach(device => {
12730
12826
  const connectedDevice = isRemoteFleetDeviceConnected(device);
12731
12827
  const name = getRemoteFleetDeviceName(device);
@@ -12753,9 +12849,44 @@
12753
12849
  const latestTaskUpdatedAt = String(device?.latestTaskUpdatedAt || device?.LatestTaskUpdatedAt || '');
12754
12850
  const latestTaskError = String(device?.latestTaskError || device?.LatestTaskError || '');
12755
12851
  const latestTaskResult = String(device?.latestTaskResultSummary || device?.LatestTaskResultSummary || '');
12852
+ const groupInfo = getRemoteFleetGroupInfo(device, groupState);
12853
+ if (groupState !== 'none' && groupInfo.key && groupInfo.key !== lastGroupKey) {
12854
+ const stat = groupStats.get(groupInfo.key) || { label: groupInfo.label, total: 0, connected: 0 };
12855
+ const groupHeader = document.createElement('div');
12856
+ groupHeader.dataset.remoteFleetGroupHeader = 'true';
12857
+ groupHeader.dataset.remoteFleetGroupKey = groupInfo.key;
12858
+ groupHeader.style.cssText = `
12859
+ grid-column: 1 / -1;
12860
+ display: flex;
12861
+ align-items: center;
12862
+ justify-content: space-between;
12863
+ gap: 10px;
12864
+ min-height: 30px;
12865
+ padding: 6px 9px;
12866
+ border-radius: 7px;
12867
+ border: 1px solid rgba(148, 163, 184, 0.22);
12868
+ background: rgba(226, 232, 240, 0.58);
12869
+ color: #334155;
12870
+ font-size: 11px;
12871
+ font-weight: 950;
12872
+ letter-spacing: 0;
12873
+ `;
12874
+ const groupLabel = document.createElement('span');
12875
+ groupLabel.textContent = stat.label || groupInfo.label;
12876
+ groupLabel.style.cssText = 'min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
12877
+ const groupCount = document.createElement('span');
12878
+ groupCount.dataset.remoteFleetGroupCount = 'true';
12879
+ groupCount.textContent = `${stat.total} device${stat.total === 1 ? '' : 's'}${stat.connected !== stat.total ? ` - ${stat.connected} online` : ''}`;
12880
+ groupCount.style.cssText = 'flex:0 0 auto;color:#64748b;font-size:10px;font-weight:900;white-space:nowrap;';
12881
+ groupHeader.appendChild(groupLabel);
12882
+ groupHeader.appendChild(groupCount);
12883
+ grid.appendChild(groupHeader);
12884
+ lastGroupKey = groupInfo.key;
12885
+ }
12756
12886
  const card = document.createElement('article');
12757
12887
  card.dataset.deviceId = deviceId;
12758
12888
  card.dataset.remoteFleetSearchText = buildRemoteFleetSearchText(device);
12889
+ card.dataset.remoteFleetGroupKey = groupInfo.key || '';
12759
12890
  card.dataset.remoteFleetConnected = connectedDevice ? 'true' : 'false';
12760
12891
  card.dataset.remoteFleetTaskCapable = taskEnabled ? 'true' : 'false';
12761
12892
  card.dataset.remoteFleetAiCapable = aiAssistEnabled ? 'true' : 'false';
@@ -13064,7 +13195,7 @@
13064
13195
  bodyView.appendChild(grid);
13065
13196
 
13066
13197
  const footer = document.createElement('div');
13067
- footer.textContent = `Endpoint ${endpoint} - all devices, no paging - ${autoMonitorState ? 'auto monitor' : 'manual'} - refreshed ${formatRemoteFleetAge(refreshedAt)}`;
13198
+ footer.textContent = `Endpoint ${endpoint} - all devices, no paging - group ${groupState} - ${autoMonitorState ? 'auto monitor' : 'manual'} - refreshed ${formatRemoteFleetAge(refreshedAt)}`;
13068
13199
  footer.style.cssText = `
13069
13200
  flex: 0 0 auto;
13070
13201
  color: #64748b;
@@ -13157,6 +13288,18 @@
13157
13288
  }
13158
13289
  });
13159
13290
 
13291
+ grid.querySelectorAll('[data-remote-fleet-group-header="true"]').forEach(header => {
13292
+ const key = String(header.dataset.remoteFleetGroupKey || '');
13293
+ const groupCards = getDeviceCards().filter(card => String(card.dataset.remoteFleetGroupKey || '') === key);
13294
+ const visibleCards = groupCards.filter(card => card.style.display !== 'none').length;
13295
+ header.style.display = visibleCards > 0 ? 'flex' : 'none';
13296
+ const countEl = header.querySelector('[data-remote-fleet-group-count="true"]');
13297
+ if (countEl) {
13298
+ countEl.textContent = `${visibleCards}/${groupCards.length}`;
13299
+ countEl.title = `${visibleCards} visible in this group, ${groupCards.length} total`;
13300
+ }
13301
+ });
13302
+
13160
13303
  const noMatch = grid.querySelector('[data-remote-fleet-no-match="true"]');
13161
13304
  if (noMatch) {
13162
13305
  noMatch.style.display = devices.length > 0 && visible === 0 ? 'flex' : 'none';
@@ -13182,11 +13325,12 @@
13182
13325
  bodyView.dataset.remoteFleetSearch = searchText;
13183
13326
  bodyView.dataset.remoteFleetFilter = filterMode;
13184
13327
  bodyView.dataset.remoteFleetSort = String(sortSelect.value || 'status');
13328
+ bodyView.dataset.remoteFleetGroup = String(groupSelect.value || 'none');
13185
13329
  bodyView.dataset.remoteFleetDensity = String(densitySelect.value || 'cards');
13186
13330
  bodyView.dataset.remoteFleetAiAssist = wantsAi ? 'true' : 'false';
13187
13331
  };
13188
13332
 
13189
- [searchInput, filterSelect, sortSelect, densitySelect, aiToggle].forEach(control => {
13333
+ [searchInput, filterSelect, sortSelect, groupSelect, densitySelect, aiToggle].forEach(control => {
13190
13334
  ['mousedown', 'mouseup', 'click', 'dblclick', 'keydown'].forEach(eventName => {
13191
13335
  control.addEventListener(eventName, event => event.stopPropagation());
13192
13336
  });
@@ -13208,6 +13352,10 @@
13208
13352
  bodyView.dataset.remoteFleetSort = String(sortSelect.value || 'status');
13209
13353
  renderRemoteFleetMonitor(bodyView, nodeModel);
13210
13354
  });
13355
+ groupSelect.addEventListener('change', () => {
13356
+ bodyView.dataset.remoteFleetGroup = String(groupSelect.value || 'none');
13357
+ renderRemoteFleetMonitor(bodyView, nodeModel);
13358
+ });
13211
13359
  densitySelect.addEventListener('change', () => {
13212
13360
  bodyView.dataset.remoteFleetDensity = String(densitySelect.value || 'cards');
13213
13361
  renderRemoteFleetMonitor(bodyView, nodeModel);
@@ -558,7 +558,7 @@
558
558
  }
559
559
 
560
560
  const base = '_content/MindExecution.Shared/js/';
561
- const scriptVersion = '20260612-remote-auto-monitor-v468';
561
+ const scriptVersion = '20260612-remote-grouping-v469';
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": "ok6mf/2a",
2
+ "version": "f0W9Xlyj",
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-9f7DLlG6ShPyYhu/Db97YKVoFr+RhJC2Ko9E4NsL9Lc=",
89
+ "hash": "sha256-PUVvy0IwiS6uV3XxWr2shsyWGD3tgG9LmarQERuoboY=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -834,7 +834,7 @@
834
834
  "url": "image-manifest.json"
835
835
  },
836
836
  {
837
- "hash": "sha256-e/vzCKU0rJNUBs7+YbA+jxOZd53T9CbUxhBAJ0yi6oM=",
837
+ "hash": "sha256-N8LbhFnpFjOzfff/nGijpdKbc+/wg9vtlFm7ZpJtkK0=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: ok6mf/2a */
1
+ /* Manifest version: f0W9Xlyj */
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