@mindexec/cli 0.2.98 → 0.2.100

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.98",
3
+ "version": "0.2.100",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -701,7 +701,7 @@ try {
701
701
  manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
702
702
 
703
703
  let cards = bodyView.querySelectorAll('article[data-device-id]');
704
- assert.equal(cards.length, SYNTHETIC_COUNT);
704
+ assert.equal(cards.length, connectedCount);
705
705
  assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]'), null);
706
706
  assert.equal(bodyView.querySelector('[data-remote-fleet-search="true"]'), null);
707
707
  assert.equal(bodyView.querySelectorAll('select').length, 0);
@@ -723,18 +723,14 @@ try {
723
723
  assert.equal(statusFooter.querySelectorAll('[data-remote-fleet-status-item]').length, 5);
724
724
  assert.ok(statusFooter.querySelector('[data-remote-fleet-status-item="route"]'));
725
725
  const initialDetailPanel = bodyView.querySelector('[data-remote-fleet-detail-panel="true"]');
726
- assert.equal(initialDetailPanel?.dataset.deviceId, focusedDevice.DeviceId);
726
+ assert.equal(initialDetailPanel, null);
727
727
  const initialSelectedCard = bodyView.querySelector(`article[data-device-id="${focusedDevice.DeviceId}"]`);
728
- assert.equal(initialSelectedCard?.dataset.remoteFleetSelected, 'true');
729
- assert.equal(initialSelectedCard?.getAttribute('aria-pressed'), 'true');
730
- assert.ok(initialSelectedCard?.querySelector('[data-remote-fleet-selected-indicator="true"]'));
731
- assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').length, 1);
728
+ assert.ok(initialSelectedCard);
729
+ assert.equal(initialSelectedCard?.querySelector('[data-remote-fleet-selected-indicator="true"]'), null);
730
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').length, 0);
732
731
  assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').length, 0);
733
- assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').length, focusedDevice.Connected && focusedDevice.ThumbnailEnabled ? 1 : 0);
734
- assert.ok(initialDetailPanel?.textContent.includes('Seen'));
735
- assert.ok(initialDetailPanel?.textContent.includes('Uptime'));
736
- assert.ok(initialDetailPanel?.textContent.includes('Mem'));
737
- assert.ok(initialDetailPanel?.textContent.includes('Load'));
732
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').length, 0);
733
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-frame-badge="true"]').length, 0);
738
734
  assert.ok(cards[0]?.querySelector('[data-remote-fleet-device-preview="tile"]'));
739
735
  assert.equal(/\b(Seen|Uptime|Mem|Load)\b/.test(cards[0]?.textContent || ''), false);
740
736
  assert.match(deviceGrid.style.cssText, /minmax\(132px,\s*1fr\)/);
@@ -745,7 +741,10 @@ try {
745
741
  assert.equal(bodyView.dataset.remoteFleetDensity, 'cards');
746
742
  assert.match(bodyView.querySelector('[data-remote-fleet-device-grid="true"]')?.style.cssText || '', /minmax\(132px,\s*1fr\)/);
747
743
  cards = bodyView.querySelectorAll('article[data-device-id]');
748
- const patchTarget = devices.find(device => device.ThumbnailEnabled === true && device.LiveStreamActive !== true);
744
+ const patchTarget = devices.find(device =>
745
+ device.Connected === true
746
+ && device.ThumbnailEnabled === true
747
+ && device.LiveStreamActive !== true);
749
748
  assert.ok(patchTarget);
750
749
  const patchCard = Array.from(cards).find(card => card.dataset.deviceId === patchTarget.DeviceId);
751
750
  const patchPreview = patchCard?.querySelector('[data-remote-fleet-device-preview="tile"]');
@@ -765,19 +764,21 @@ try {
765
764
  assert.equal(patchPreview.dataset.remoteFleetPendingFrameUrl || '', '');
766
765
  assert.ok(patchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]'));
767
766
  assert.equal(patchPreview.querySelector('img[data-remote-fleet-frame-image="true"]')?.dataset.remoteFleetFrameUrl, patchUrl);
768
- const alternateDevice = devices.find(device => device.DeviceId !== focusedDevice.DeviceId);
767
+ assert.equal(patchPreview.querySelector('[data-remote-fleet-frame-badge="true"]'), null);
768
+ const alternateDevice = devices.find(device =>
769
+ device.Connected === true
770
+ && device.DeviceId !== focusedDevice.DeviceId);
769
771
  assert.ok(alternateDevice);
770
772
  const alternateCard = Array.from(cards).find(card => card.dataset.deviceId === alternateDevice.DeviceId);
771
773
  assert.ok(alternateCard);
772
774
  alternateCard.dispatchEvent({ type: 'click' });
773
775
  assert.equal(bodyView.dataset.remoteFleetSelectedDeviceId, alternateDevice.DeviceId);
774
- assert.equal(bodyView.querySelector('[data-remote-fleet-detail-panel="true"]')?.dataset.deviceId, alternateDevice.DeviceId);
776
+ assert.equal(bodyView.querySelector('[data-remote-fleet-detail-panel="true"]'), null);
775
777
  const selectedAfterClick = bodyView.querySelector(`article[data-device-id="${alternateDevice.DeviceId}"]`);
776
- assert.equal(selectedAfterClick?.dataset.remoteFleetSelected, 'true');
777
- assert.equal(selectedAfterClick?.getAttribute('aria-pressed'), 'true');
778
- assert.ok(selectedAfterClick?.querySelector('[data-remote-fleet-selected-indicator="true"]'));
778
+ assert.ok(selectedAfterClick);
779
+ assert.equal(selectedAfterClick?.querySelector('[data-remote-fleet-selected-indicator="true"]'), null);
779
780
  const livePanel = bodyView.querySelector('[data-remote-fleet-live-panel="true"]');
780
- assert.equal(livePanel?.dataset.deviceId, focusedDevice.DeviceId);
781
+ assert.equal(livePanel, null);
781
782
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-visible"]'), null);
782
783
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-connected"]'), null);
783
784
  assert.equal(bodyView.querySelector('[data-remote-fleet-task-input="true"]'), null);
@@ -811,28 +812,24 @@ try {
811
812
  enabled: false
812
813
  });
813
814
 
814
- assert.ok(livePanel?.textContent.includes('RemoteFast 20 fps'), livePanel?.textContent || bodyView.textContent);
815
+ assert.equal(bodyView.querySelector('[data-remote-fleet-live-panel="true"]'), null);
815
816
  assert.equal(bodyView.querySelector('code'), null);
816
817
  assert.equal(bodyView.textContent.includes('npx @mindexec/remote connect'), false);
817
818
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="copy-command"]'), null);
818
819
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="refresh"]'), null);
819
820
  assert.equal(bodyView.querySelector('[data-remote-fleet-auto-toggle="true"]'), null);
820
- assert.ok(bodyView.textContent.includes('synthetic-render-ai'));
821
- assert.ok(bodyView.textContent.includes('Task timed out waiting for the agent response.'));
821
+ assert.equal(bodyView.textContent.includes('synthetic-render-ai'), false);
822
+ assert.equal(bodyView.textContent.includes('Task timed out waiting for the agent response.'), false);
822
823
  const batchSummary = bodyView.querySelector('[data-remote-fleet-task-batch="true"]');
823
- assert.ok(batchSummary);
824
- assert.ok(batchSummary.textContent.includes('Render smoke batch'));
825
- assert.ok(batchSummary.textContent.includes('208/210 done'));
826
- assert.ok(batchSummary.textContent.includes('1 timed out'));
824
+ assert.equal(batchSummary, null);
827
825
  const batchHistory = bodyView.querySelector('[data-remote-fleet-task-history="true"]');
828
- assert.ok(batchHistory);
829
- assert.ok(batchHistory.textContent.includes('Previous AI batch'));
830
- assert.ok(batchHistory.textContent.includes('42/42 done'));
826
+ assert.equal(batchHistory, null);
831
827
  const resultPanel = bodyView.querySelector('[data-remote-fleet-task-results="true"]');
832
- assert.ok(resultPanel);
833
- assert.ok(resultPanel.textContent.includes('synthetic-render-ai'));
834
- assert.ok(resultPanel.textContent.includes('Overview AI result visible'));
828
+ assert.equal(resultPanel, null);
835
829
  assert.ok(devices.some(device => /^synthetic-response-/.test(device.LatestTaskResultResponseId)));
830
+ await wait();
831
+ const liveStartCalls = dotNetCalls.filter(call => call.methodName === 'StartRemoteFleetLiveStreamFromJs');
832
+ assert.ok(liveStartCalls.length > 1, `expected multiple visible live starts, got ${liveStartCalls.length}`);
836
833
  await wait(320);
837
834
  assert.ok(dotNetCalls.some(call => call.methodName === 'RefreshRemoteFleetMonitorNodeFromJs'));
838
835
  assert.equal(bodyView.dataset.remoteFleetTaskFollowKey, 'render-smoke-batch');
package/server.js CHANGED
@@ -2978,6 +2978,12 @@ function isRemoteAgentProcessRunning() {
2978
2978
  && !proc.killed;
2979
2979
  }
2980
2980
 
2981
+ function isLocalRemoteHostTargetActive() {
2982
+ const status = remoteHub.getStatus({ includeSecrets: false });
2983
+ return status?.hostTargetActive === true
2984
+ && (!status.hostTargetHostInstanceId || status.hostTargetHostInstanceId === BRIDGE_INSTANCE_ID);
2985
+ }
2986
+
2981
2987
  function resolveRemoteAgentLauncher() {
2982
2988
  const localLauncher = path.join(BRIDGE_ROOT, 'node_modules', '@mindexec', 'remote', 'bin', 'mindexec-remote.js');
2983
2989
  try {
@@ -3112,6 +3118,22 @@ async function startRemoteAgentConnection(options = {}) {
3112
3118
  return { ok: false, error: 'missing-pair-token', agent: serializeRemoteAgentState() };
3113
3119
  }
3114
3120
 
3121
+ if (isLocalRemoteHostTargetActive()) {
3122
+ if (isRemoteAgentProcessRunning()) {
3123
+ await stopRemoteAgentConnection('local-host-target-active');
3124
+ }
3125
+ logEvent(
3126
+ 'remote',
3127
+ 'managed RemoteAgent connect skipped because this bridge is the active host target',
3128
+ 'remote');
3129
+ return {
3130
+ ok: true,
3131
+ skipped: true,
3132
+ reason: 'local-host-target-active',
3133
+ agent: serializeRemoteAgentState()
3134
+ };
3135
+ }
3136
+
3115
3137
  const activeConnectionKeys = new Set(managers.map(manager => createRemoteAgentConnectionKey(manager, leaseId)));
3116
3138
  if (isRemoteAgentProcessRunning()
3117
3139
  && remoteAgentState.ready === true
@@ -7831,13 +7853,33 @@ app.post('/api/remote/frames', (req, res) => {
7831
7853
  });
7832
7854
  });
7833
7855
 
7834
- app.post('/api/remote/host-target', (req, res) => {
7856
+ app.post('/api/remote/host-target', async (req, res) => {
7835
7857
  res.setHeader('Cache-Control', 'no-store');
7836
- res.json(remoteHub.setHostTarget({
7837
- enabled: req.body?.enabled !== false,
7838
- nodeId: req.body?.nodeId,
7839
- leaseMs: req.body?.leaseMs
7840
- }));
7858
+ try {
7859
+ const result = remoteHub.setHostTarget({
7860
+ enabled: req.body?.enabled !== false,
7861
+ nodeId: req.body?.nodeId,
7862
+ leaseMs: req.body?.leaseMs
7863
+ });
7864
+ if (result?.ok === true && result?.active === true && isLocalRemoteHostTargetActive()) {
7865
+ if (isRemoteAgentProcessRunning()) {
7866
+ await stopRemoteAgentConnection('local-host-target-active');
7867
+ logEvent('remote', 'managed RemoteAgent held stopped while local host target is active', 'remote');
7868
+ }
7869
+ }
7870
+ res.json({
7871
+ ...result,
7872
+ agent: serializeRemoteAgentState()
7873
+ });
7874
+ } catch (err) {
7875
+ logError('remote', 'remote host target update failed.', err);
7876
+ res.status(500).json({
7877
+ ok: false,
7878
+ active: false,
7879
+ error: err?.message || String(err),
7880
+ agent: serializeRemoteAgentState()
7881
+ });
7882
+ }
7841
7883
  });
7842
7884
 
7843
7885
  app.delete('/api/remote/host-target', (req, res) => {
@@ -12974,13 +12974,16 @@
12974
12974
  }
12975
12975
 
12976
12976
  const REMOTE_FLEET_MONITOR_REFRESH_MS = 10000;
12977
+ const REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS = 2500;
12977
12978
  const REMOTE_FLEET_LIVE_REFRESH_MS = 30000;
12978
- const REMOTE_FLEET_FOCUSED_LIVE_FPS = 10;
12979
- const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_FOCUSED_LIVE_FPS);
12979
+ const REMOTE_FLEET_MONITOR_LIVE_FPS = 10;
12980
+ const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_MONITOR_LIVE_FPS);
12980
12981
  const REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS = 2000;
12981
12982
  const REMOTE_FLEET_FRAME_CANVAS_MAX_DPR = 2;
12982
12983
  const REMOTE_FLEET_FRAME_BLOB_CACHE_MS = 10000;
12983
12984
  const REMOTE_FLEET_FRAME_BLOB_CACHE_LIMIT = 96;
12985
+ const REMOTE_FLEET_LIVE_FRAME_DECODE_TIMEOUT_MS = 180;
12986
+ const REMOTE_FLEET_THUMBNAIL_FRAME_DECODE_TIMEOUT_MS = 1200;
12984
12987
  const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
12985
12988
  const REMOTE_FLEET_TASK_FOLLOW_REFRESH_MS = 2000;
12986
12989
  const REMOTE_FLEET_TASK_FOLLOW_MAX_TICKS = 60;
@@ -13299,6 +13302,10 @@
13299
13302
  return patches;
13300
13303
  }
13301
13304
 
13305
+ function shouldDisplayRemoteFleetMonitorDevice(device) {
13306
+ return isRemoteFleetDeviceConnected(device);
13307
+ }
13308
+
13302
13309
  function ensureRemoteFleetFrameImage(preview, frame) {
13303
13310
  let image = preview?.querySelector?.('img[data-remote-fleet-frame-image="true"]');
13304
13311
  if (image) {
@@ -13439,6 +13446,42 @@
13439
13446
  return true;
13440
13447
  }
13441
13448
 
13449
+ function getRemoteFleetFrameDecodeTimeoutMs(frame) {
13450
+ return String(frame?.kind || '').toLowerCase() === 'live'
13451
+ ? REMOTE_FLEET_LIVE_FRAME_DECODE_TIMEOUT_MS
13452
+ : REMOTE_FLEET_THUMBNAIL_FRAME_DECODE_TIMEOUT_MS;
13453
+ }
13454
+
13455
+ function withRemoteFleetFrameTimeout(promise, timeoutMs, onTimeout) {
13456
+ const duration = Math.max(50, Number(timeoutMs) || REMOTE_FLEET_LIVE_FRAME_DECODE_TIMEOUT_MS);
13457
+ return new Promise((resolve, reject) => {
13458
+ let settled = false;
13459
+ const timer = setTimeout(() => {
13460
+ if (settled) return;
13461
+ settled = true;
13462
+ try {
13463
+ onTimeout?.();
13464
+ } catch {
13465
+ // best effort only
13466
+ }
13467
+ reject(new Error('remote-frame-decode-timeout'));
13468
+ }, duration);
13469
+ Promise.resolve(promise)
13470
+ .then(value => {
13471
+ if (settled) return;
13472
+ settled = true;
13473
+ clearTimeout(timer);
13474
+ resolve(value);
13475
+ })
13476
+ .catch(error => {
13477
+ if (settled) return;
13478
+ settled = true;
13479
+ clearTimeout(timer);
13480
+ reject(error);
13481
+ });
13482
+ });
13483
+ }
13484
+
13442
13485
  async function loadRemoteFleetFrameBitmap(frame) {
13443
13486
  if (!frame || !isRemoteFleetFrameSource(frame.frameUrl)) {
13444
13487
  return null;
@@ -13482,8 +13525,12 @@
13482
13525
  entry.expiresAt = now + REMOTE_FLEET_FRAME_BLOB_CACHE_MS;
13483
13526
  }
13484
13527
 
13485
- const blob = await entry.promise;
13486
- return createImageBitmap(blob);
13528
+ const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
13529
+ const blob = await withRemoteFleetFrameTimeout(
13530
+ entry.promise,
13531
+ timeoutMs,
13532
+ () => remoteFleetFrameBlobCache.delete(identity));
13533
+ return await withRemoteFleetFrameTimeout(createImageBitmap(blob), timeoutMs);
13487
13534
  }
13488
13535
 
13489
13536
  function getRemoteFleetFrameIdentity(frame) {
@@ -13602,6 +13649,12 @@
13602
13649
  placeholder.remove();
13603
13650
  }
13604
13651
 
13652
+ const previewMode = String(preview?.dataset?.remoteFleetDevicePreview || '');
13653
+ if (previewMode === 'tile') {
13654
+ preview.querySelector?.('[data-remote-fleet-frame-badge="true"]')?.remove();
13655
+ return true;
13656
+ }
13657
+
13605
13658
  const badge = ensureRemoteFleetFrameBadge(preview, frame);
13606
13659
  if (badge) {
13607
13660
  const at = frame.receivedAt || frame.capturedAt || '';
@@ -13718,15 +13771,24 @@
13718
13771
  return;
13719
13772
  }
13720
13773
 
13774
+ const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
13775
+ let settled = false;
13776
+ const complete = (loaded, surface = 'img') => {
13777
+ if (settled) return;
13778
+ settled = true;
13779
+ clearTimeout(timeout);
13780
+ finish(loaded, surface);
13781
+ };
13782
+ const timeout = setTimeout(() => complete(false, 'img-timeout'), timeoutMs);
13721
13783
  const loader = new Image();
13722
13784
  loader.decoding = 'async';
13723
13785
  loader.onload = () => {
13724
13786
  const decoded = typeof loader.decode === 'function'
13725
13787
  ? loader.decode().catch(() => undefined)
13726
13788
  : Promise.resolve();
13727
- decoded.then(() => finish(true, 'img'));
13789
+ decoded.then(() => complete(true, 'img'));
13728
13790
  };
13729
- loader.onerror = () => finish(false, 'img');
13791
+ loader.onerror = () => complete(false, 'img');
13730
13792
  loader.src = frame.frameUrl;
13731
13793
  };
13732
13794
 
@@ -13782,7 +13844,19 @@
13782
13844
  }
13783
13845
  });
13784
13846
  })
13785
- .catch(() => loadFallbackImage());
13847
+ .catch(error => {
13848
+ if (frame.kind === 'live') {
13849
+ window.RuntimeTrace?.emit?.('remote.frame.decodeSkipped', {
13850
+ kind: frame.kind,
13851
+ seq: frame.frameSeq,
13852
+ error: error?.message || String(error || '')
13853
+ });
13854
+ finish(false, 'canvas-timeout');
13855
+ return;
13856
+ }
13857
+
13858
+ loadFallbackImage();
13859
+ });
13786
13860
  }
13787
13861
 
13788
13862
  function queueRemoteFleetFramePaint(preview, frame) {
@@ -15068,12 +15142,14 @@
15068
15142
  const recentTaskBatches = parseRemoteFleetRecentTaskBatches(nodeModel);
15069
15143
  const latestTaskBatchKey = getRemoteFleetTaskBatchKey(latestTaskBatch);
15070
15144
  const latestTaskBatchActive = isRemoteFleetTaskBatchActive(latestTaskBatch);
15145
+ const showTaskSummarySurfaces = false;
15071
15146
  if (latestTaskBatchKey && bodyView.dataset.remoteFleetTaskFollowKey !== latestTaskBatchKey) {
15072
15147
  bodyView.dataset.remoteFleetTaskFollowKey = latestTaskBatchKey;
15073
15148
  bodyView.dataset.remoteFleetTaskFollowTicks = '0';
15074
15149
  }
15075
15150
  bodyView.dataset.remoteFleetTaskFollowActive = latestTaskBatchActive ? 'true' : 'false';
15076
15151
  const sortedDevices = [...parseRemoteFleetDevices(nodeModel)]
15152
+ .filter(shouldDisplayRemoteFleetMonitorDevice)
15077
15153
  .sort((left, right) => compareRemoteFleetDevices(left, right, sortState));
15078
15154
  const devices = groupState === 'none'
15079
15155
  ? sortedDevices
@@ -15099,11 +15175,8 @@
15099
15175
  } else {
15100
15176
  delete bodyView.dataset.remoteFleetSelectedDeviceId;
15101
15177
  }
15102
- const total = Number(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDeviceCount', devices.length));
15103
- const connected = Number(getRemoteFleetMetadataValue(
15104
- nodeModel,
15105
- 'RemoteFleetConnectedDeviceCount',
15106
- devices.filter(isRemoteFleetDeviceConnected).length));
15178
+ const total = devices.length;
15179
+ const connected = devices.filter(isRemoteFleetDeviceConnected).length;
15107
15180
  const taskCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceTaskCapable(device)).length;
15108
15181
  const aiCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceAiCapable(device)).length;
15109
15182
  const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
@@ -15376,7 +15449,7 @@
15376
15449
  `;
15377
15450
  bodyView.appendChild(taskFeedback);
15378
15451
 
15379
- if (latestTaskBatch) {
15452
+ if (showTaskSummarySurfaces && latestTaskBatch) {
15380
15453
  const batchTotal = Number(latestTaskBatch.total ?? latestTaskBatch.Total ?? 0);
15381
15454
  const batchQueued = Number(latestTaskBatch.queued ?? latestTaskBatch.Queued ?? 0);
15382
15455
  const batchPending = Number(latestTaskBatch.pending ?? latestTaskBatch.Pending ?? 0);
@@ -15437,7 +15510,7 @@
15437
15510
  const visibleRecentBatches = recentTaskBatches
15438
15511
  .filter(batch => batch && String(batch.batchId ?? batch.BatchId ?? '').trim())
15439
15512
  .slice(0, 3);
15440
- if (visibleRecentBatches.length > 0) {
15513
+ if (showTaskSummarySurfaces && visibleRecentBatches.length > 0) {
15441
15514
  const history = document.createElement('div');
15442
15515
  history.dataset.remoteFleetTaskHistory = 'true';
15443
15516
  history.style.cssText = `
@@ -15504,7 +15577,7 @@
15504
15577
  .filter(item => item.status || item.title || item.summary || item.error)
15505
15578
  .sort((left, right) => right.updatedMs - left.updatedMs)
15506
15579
  .slice(0, 4);
15507
- if (recentTaskResults.length > 0) {
15580
+ if (showTaskSummarySurfaces && recentTaskResults.length > 0) {
15508
15581
  const resultPanel = document.createElement('div');
15509
15582
  resultPanel.dataset.remoteFleetTaskResults = 'true';
15510
15583
  resultPanel.style.cssText = `
@@ -15542,150 +15615,6 @@
15542
15615
  bodyView.appendChild(resultPanel);
15543
15616
  }
15544
15617
 
15545
- if (focusedDevice) {
15546
- const focusedId = getRemoteFleetDeviceId(focusedDevice);
15547
- const focusedName = getRemoteFleetDeviceName(focusedDevice);
15548
- const liveActive = isRemoteFleetLiveActive(focusedDevice);
15549
- const liveFrameSource = getRemoteFleetFrameSource(focusedDevice, 'live');
15550
- const liveFrameAt = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', ''));
15551
- const liveStreamId = String(getRemoteFleetDeviceField(focusedDevice, 'liveStreamId', 'LiveStreamId', ''));
15552
- const hasLiveFrame = hasRemoteFleetLiveFrame(focusedDevice);
15553
- const livePanel = document.createElement('section');
15554
- livePanel.dataset.remoteFleetLivePanel = 'true';
15555
- livePanel.dataset.deviceId = focusedId;
15556
- livePanel.style.cssText = `
15557
- flex: 0 0 auto;
15558
- display: grid;
15559
- grid-template-columns: minmax(220px, 0.95fr) minmax(0, 1.25fr);
15560
- gap: 10px;
15561
- min-height: 170px;
15562
- padding: 10px;
15563
- border-radius: 8px;
15564
- border: 1px solid rgba(14, 165, 233, 0.24);
15565
- background: rgba(255, 255, 255, 0.82);
15566
- `;
15567
-
15568
- const livePreview = document.createElement('div');
15569
- livePreview.dataset.remoteFleetDevicePreview = 'focused-live';
15570
- livePreview.dataset.deviceId = focusedId;
15571
- livePreview.dataset.remoteFleetFrameKind = hasLiveFrame ? 'live' : '';
15572
- livePreview.dataset.remoteFleetFrameSeq = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameSeq', 'LiveFrameSeq', 0));
15573
- livePreview.style.cssText = `
15574
- position: relative;
15575
- min-width: 0;
15576
- aspect-ratio: 16 / 9;
15577
- overflow: hidden;
15578
- border-radius: 7px;
15579
- background: linear-gradient(135deg, #111827 0%, #1f2937 100%);
15580
- border: 1px solid rgba(15, 23, 42, 0.16);
15581
- `;
15582
- if (hasLiveFrame) {
15583
- const image = document.createElement('img');
15584
- image.dataset.remoteFleetFrameImage = 'true';
15585
- image.dataset.remoteFleetFrameKind = 'live';
15586
- image.dataset.remoteFleetFrameSeq = livePreview.dataset.remoteFleetFrameSeq;
15587
- image.src = liveFrameSource;
15588
- image.alt = `${focusedName} live screen`;
15589
- image.decoding = 'async';
15590
- image.style.cssText = `
15591
- width: 100%;
15592
- height: 100%;
15593
- object-fit: cover;
15594
- display: block;
15595
- `;
15596
- livePreview.appendChild(image);
15597
- } else {
15598
- const placeholder = document.createElement('div');
15599
- placeholder.dataset.remoteFleetScreenPlaceholder = 'true';
15600
- placeholder.textContent = liveActive ? 'Waiting for live frame' : 'Live view idle';
15601
- placeholder.style.cssText = `
15602
- position: absolute;
15603
- inset: 0;
15604
- display: flex;
15605
- align-items: center;
15606
- justify-content: center;
15607
- color: rgba(226, 232, 240, 0.82);
15608
- font-size: 12px;
15609
- font-weight: 900;
15610
- letter-spacing: 0;
15611
- `;
15612
- livePreview.appendChild(placeholder);
15613
- }
15614
- const liveBadge = document.createElement('span');
15615
- liveBadge.dataset.remoteFleetFrameBadge = 'true';
15616
- liveBadge.textContent = liveActive
15617
- ? `LIVE ${liveFrameAt ? formatRemoteFleetAge(liveFrameAt) : ''}`.trim()
15618
- : 'FOCUS';
15619
- liveBadge.style.cssText = `
15620
- position: absolute;
15621
- left: 7px;
15622
- top: 7px;
15623
- max-width: calc(100% - 14px);
15624
- padding: 4px 7px;
15625
- border-radius: 999px;
15626
- background: ${liveActive ? 'rgba(220, 38, 38, 0.86)' : 'rgba(15, 23, 42, 0.72)'};
15627
- color: #ffffff;
15628
- font-size: 9px;
15629
- font-weight: 950;
15630
- line-height: 1;
15631
- overflow: hidden;
15632
- text-overflow: ellipsis;
15633
- white-space: nowrap;
15634
- letter-spacing: 0;
15635
- `;
15636
- livePreview.appendChild(liveBadge);
15637
-
15638
- const liveInfo = document.createElement('div');
15639
- liveInfo.style.cssText = `
15640
- min-width: 0;
15641
- display: flex;
15642
- flex-direction: column;
15643
- justify-content: space-between;
15644
- gap: 8px;
15645
- `;
15646
- const liveTitle = document.createElement('div');
15647
- liveTitle.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:3px;';
15648
- const liveName = document.createElement('strong');
15649
- liveName.textContent = focusedName;
15650
- liveName.title = focusedName;
15651
- liveName.style.cssText = 'color:#0f172a;font-size:15px;font-weight:950;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
15652
- const liveMeta = document.createElement('span');
15653
- liveMeta.textContent = liveActive
15654
- ? `RemoteFast ${getRemoteFleetDeviceField(focusedDevice, 'liveStreamFps', 'LiveStreamFps', 0) || 20} fps`
15655
- : 'View-only focused screen stream';
15656
- liveMeta.style.cssText = 'color:#475569;font-size:11px;font-weight:800;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
15657
- liveTitle.appendChild(liveName);
15658
- liveTitle.appendChild(liveMeta);
15659
-
15660
- const liveActions = document.createElement('div');
15661
- liveActions.style.cssText = 'display:flex;align-items:center;gap:8px;flex-wrap:wrap;';
15662
- if (isRemoteFleetDeviceConnected(focusedDevice) && isRemoteFleetLiveCapable(focusedDevice)) {
15663
- const startLiveButton = createRemoteFleetButton(liveActive ? 'Restart live' : 'Start live', 'Start focused view-only live stream', 'live-start');
15664
- startLiveButton.dataset.deviceId = focusedId;
15665
- startLiveButton.style.height = '30px';
15666
- liveActions.appendChild(startLiveButton);
15667
- if (liveActive) {
15668
- const stopLiveButton = createRemoteFleetButton('Stop live', 'Stop focused live stream', 'live-stop');
15669
- stopLiveButton.dataset.deviceId = focusedId;
15670
- stopLiveButton.dataset.streamId = liveStreamId;
15671
- stopLiveButton.style.height = '30px';
15672
- stopLiveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
15673
- stopLiveButton.style.color = '#b91c1c';
15674
- liveActions.appendChild(stopLiveButton);
15675
- }
15676
- } else {
15677
- const disabled = document.createElement('span');
15678
- disabled.textContent = 'Live unavailable';
15679
- disabled.style.cssText = 'color:#64748b;font-size:11px;font-weight:900;';
15680
- liveActions.appendChild(disabled);
15681
- }
15682
- liveInfo.appendChild(liveTitle);
15683
- liveInfo.appendChild(liveActions);
15684
- livePanel.appendChild(livePreview);
15685
- livePanel.appendChild(liveInfo);
15686
- bodyView.appendChild(livePanel);
15687
- }
15688
-
15689
15618
  const createDevicePreview = (device, mode = 'tile') => {
15690
15619
  const name = getRemoteFleetDeviceName(device);
15691
15620
  const connectedDevice = isRemoteFleetDeviceConnected(device);
@@ -15696,7 +15625,6 @@
15696
15625
  const previewSource = hasLiveFrame
15697
15626
  ? getRemoteFleetFrameSource(device, 'live')
15698
15627
  : getRemoteFleetFrameSource(device, 'thumbnail');
15699
- const previewAt = hasLiveFrame ? liveFrameReceivedAt : thumbnailCapturedAt;
15700
15628
  const isDetail = mode === 'detail';
15701
15629
 
15702
15630
  const preview = document.createElement('div');
@@ -15743,231 +15671,9 @@
15743
15671
  preview.appendChild(placeholder);
15744
15672
  }
15745
15673
 
15746
- const dot = document.createElement('span');
15747
- dot.style.cssText = `
15748
- position: absolute;
15749
- left: ${isDetail ? '9px' : '6px'};
15750
- top: ${isDetail ? '9px' : '6px'};
15751
- width: ${isDetail ? '10px' : '8px'};
15752
- height: ${isDetail ? '10px' : '8px'};
15753
- border-radius: 999px;
15754
- background: ${connectedDevice ? '#10b981' : '#94a3b8'};
15755
- box-shadow: 0 0 0 3px ${connectedDevice ? 'rgba(16,185,129,0.20)' : 'rgba(148,163,184,0.18)'};
15756
- `;
15757
- preview.appendChild(dot);
15758
-
15759
- if (hasLiveFrame || (isDetail && previewAt)) {
15760
- const badge = document.createElement('span');
15761
- badge.dataset.remoteFleetFrameBadge = 'true';
15762
- badge.textContent = hasLiveFrame
15763
- ? (isDetail && previewAt ? `LIVE ${formatRemoteFleetAge(previewAt)}` : 'LIVE')
15764
- : formatRemoteFleetAge(previewAt);
15765
- badge.style.cssText = `
15766
- position: absolute;
15767
- right: ${isDetail ? '9px' : '6px'};
15768
- bottom: ${isDetail ? '9px' : '6px'};
15769
- max-width: calc(100% - ${isDetail ? '18px' : '12px'});
15770
- padding: ${isDetail ? '4px 7px' : '3px 6px'};
15771
- border-radius: 999px;
15772
- background: ${hasLiveFrame ? 'rgba(220, 38, 38, 0.84)' : 'rgba(15, 23, 42, 0.72)'};
15773
- color: #e2e8f0;
15774
- font-size: ${isDetail ? '9px' : '8px'};
15775
- font-weight: 950;
15776
- line-height: 1;
15777
- overflow: hidden;
15778
- text-overflow: ellipsis;
15779
- white-space: nowrap;
15780
- letter-spacing: 0;
15781
- `;
15782
- preview.appendChild(badge);
15783
- }
15784
-
15785
15674
  return preview;
15786
15675
  };
15787
15676
 
15788
- const createSelectedDevicePanel = device => {
15789
- const panel = document.createElement('aside');
15790
- panel.dataset.remoteFleetDetailPanel = 'true';
15791
- panel.style.cssText = `
15792
- min-width: 0;
15793
- min-height: 0;
15794
- overflow-y: auto;
15795
- display: flex;
15796
- flex-direction: column;
15797
- gap: 9px;
15798
- padding: 10px;
15799
- border-radius: 8px;
15800
- border: 1px solid rgba(148, 163, 184, 0.28);
15801
- background: rgba(255, 255, 255, 0.86);
15802
- `;
15803
-
15804
- if (!device) {
15805
- const empty = document.createElement('div');
15806
- empty.textContent = 'Select a screen';
15807
- empty.style.cssText = 'display:flex;align-items:center;justify-content:center;min-height:120px;color:#64748b;font-size:12px;font-weight:900;';
15808
- panel.appendChild(empty);
15809
- return panel;
15810
- }
15811
-
15812
- const deviceId = getRemoteFleetDeviceId(device);
15813
- const name = getRemoteFleetDeviceName(device);
15814
- const connectedDevice = isRemoteFleetDeviceConnected(device);
15815
- const platform = [device?.platform || device?.Platform, device?.arch || device?.Arch]
15816
- .filter(Boolean)
15817
- .join(' / ') || 'unknown';
15818
- const release = String(device?.release || device?.Release || '');
15819
- const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
15820
- const liveStreamEnabled = isRemoteFleetLiveCapable(device);
15821
- const liveStreamActive = isRemoteFleetLiveActive(device);
15822
- const liveStreamId = String(device?.liveStreamId || device?.LiveStreamId || '');
15823
- const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
15824
- const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
15825
- const aiModel = String(device?.aiModel || device?.AiModel || '');
15826
- const latestTaskStatus = String(device?.latestTaskStatus || device?.LatestTaskStatus || '');
15827
- const latestTaskTitle = String(device?.latestTaskTitle || device?.LatestTaskTitle || '');
15828
- const latestTaskApproval = String(device?.latestTaskApprovalLevel || device?.LatestTaskApprovalLevel || '');
15829
- const latestTaskUpdatedAt = String(device?.latestTaskUpdatedAt || device?.LatestTaskUpdatedAt || '');
15830
- const latestTaskError = String(device?.latestTaskError || device?.LatestTaskError || '');
15831
- const latestTaskResultModel = String(device?.latestTaskResultModel || device?.LatestTaskResultModel || '');
15832
- const latestTaskResultResponseId = String(device?.latestTaskResultResponseId || device?.LatestTaskResultResponseId || '');
15833
- const latestTaskResult = String(device?.latestTaskResultSummary || device?.LatestTaskResultSummary || '');
15834
- const statusText = connectedDevice
15835
- ? (liveStreamActive ? 'Live' : (aiAssistEnabled ? `AI ${aiModel || 'ready'}` : 'Connected'))
15836
- : 'Offline';
15837
-
15838
- panel.dataset.deviceId = deviceId;
15839
- const detailPreview = createDevicePreview(device, 'detail');
15840
- detailPreview.style.cursor = 'pointer';
15841
- detailPreview.addEventListener('dblclick', event => {
15842
- event.preventDefault();
15843
- event.stopPropagation();
15844
- bodyView.dataset.remoteFleetSelectedDeviceId = deviceId;
15845
- openRemoteFleetControlPopup(bodyView, nodeModel, deviceId);
15846
- });
15847
- panel.appendChild(detailPreview);
15848
-
15849
- const header = document.createElement('div');
15850
- header.style.cssText = 'display:flex;align-items:flex-start;justify-content:space-between;gap:8px;min-width:0;';
15851
- const titleBox = document.createElement('div');
15852
- titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:3px;';
15853
- const title = document.createElement('strong');
15854
- title.textContent = name;
15855
- title.title = name;
15856
- title.style.cssText = 'color:#0f172a;font-size:14px;font-weight:950;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
15857
- const subtitle = document.createElement('span');
15858
- subtitle.textContent = release ? `${platform} ${release}` : platform;
15859
- subtitle.title = subtitle.textContent;
15860
- subtitle.style.cssText = 'color:#64748b;font-size:11px;font-weight:750;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
15861
- titleBox.appendChild(title);
15862
- titleBox.appendChild(subtitle);
15863
- const status = document.createElement('span');
15864
- status.textContent = statusText;
15865
- status.style.cssText = `
15866
- flex: 0 0 auto;
15867
- max-width: 82px;
15868
- padding: 4px 7px;
15869
- border-radius: 999px;
15870
- background: ${liveStreamActive ? 'rgba(254, 226, 226, 0.92)' : (connectedDevice ? 'rgba(209, 250, 229, 0.92)' : 'rgba(226, 232, 240, 0.92)')};
15871
- color: ${liveStreamActive ? '#b91c1c' : (connectedDevice ? '#047857' : '#475569')};
15872
- font-size: 9px;
15873
- font-weight: 950;
15874
- line-height: 1;
15875
- overflow: hidden;
15876
- text-overflow: ellipsis;
15877
- white-space: nowrap;
15878
- letter-spacing: 0;
15879
- `;
15880
- header.appendChild(titleBox);
15881
- header.appendChild(status);
15882
- panel.appendChild(header);
15883
-
15884
- const metrics = document.createElement('div');
15885
- metrics.style.cssText = 'display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;';
15886
- const addDetailMetric = (label, value) => {
15887
- const metric = document.createElement('div');
15888
- metric.style.cssText = 'min-width:0;padding:7px 8px;border-radius:7px;background:rgba(241,245,249,0.86);border:1px solid rgba(148,163,184,0.16);';
15889
- const labelEl = document.createElement('div');
15890
- labelEl.textContent = label;
15891
- labelEl.style.cssText = 'color:#64748b;font-size:9px;font-weight:850;letter-spacing:0;text-transform:uppercase;';
15892
- const valueEl = document.createElement('div');
15893
- valueEl.textContent = value;
15894
- valueEl.title = value;
15895
- valueEl.style.cssText = 'color:#0f172a;font-size:12px;font-weight:950;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
15896
- metric.appendChild(labelEl);
15897
- metric.appendChild(valueEl);
15898
- metrics.appendChild(metric);
15899
- };
15900
- addDetailMetric('Seen', formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt));
15901
- addDetailMetric('Uptime', formatRemoteFleetDuration(device?.uptimeSec ?? device?.UptimeSec));
15902
- addDetailMetric('Mem', formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio));
15903
- addDetailMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
15904
- panel.appendChild(metrics);
15905
-
15906
- if (latestTaskStatus || latestTaskTitle || latestTaskResult || latestTaskError) {
15907
- const taskBox = document.createElement('div');
15908
- const taskTone = latestTaskError || latestTaskStatus === 'failed'
15909
- ? 'error'
15910
- : (latestTaskStatus === 'completed' ? 'done' : 'pending');
15911
- taskBox.style.cssText = `
15912
- display: flex;
15913
- flex-direction: column;
15914
- gap: 4px;
15915
- min-width: 0;
15916
- padding: 8px 9px;
15917
- border-radius: 7px;
15918
- background: ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.12)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
15919
- border: 1px solid ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.24)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.20)' : 'rgba(37, 99, 235, 0.16)'};
15920
- `;
15921
- const taskLine = document.createElement('div');
15922
- const taskModeLabel = latestTaskApproval === 'ai-assist' ? 'AI' : 'Task';
15923
- taskLine.textContent = `${taskModeLabel} ${latestTaskStatus || 'task'}${latestTaskUpdatedAt ? ` - ${formatRemoteFleetAge(latestTaskUpdatedAt)}` : ''}${latestTaskResultModel ? ` - ${latestTaskResultModel}` : ''}`;
15924
- taskLine.title = latestTaskResultResponseId
15925
- ? `${taskLine.textContent} (${latestTaskResultResponseId})`
15926
- : taskLine.textContent;
15927
- taskLine.style.cssText = `color:${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};font-size:10px;font-weight:950;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
15928
- const taskSummary = document.createElement('div');
15929
- taskSummary.textContent = formatRemoteFleetTaskError(latestTaskError) || latestTaskResult || latestTaskTitle || 'Task queued';
15930
- taskSummary.title = taskSummary.textContent;
15931
- taskSummary.style.cssText = 'color:#334155;font-size:10px;font-weight:750;line-height:1.25;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;letter-spacing:0;';
15932
- taskBox.appendChild(taskLine);
15933
- taskBox.appendChild(taskSummary);
15934
- panel.appendChild(taskBox);
15935
- }
15936
-
15937
- const actions = document.createElement('div');
15938
- actions.style.cssText = 'display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:auto;';
15939
- if (deviceId) {
15940
- const pinButton = createRemoteFleetButton('Pin', 'Pin this device as a canvas node', 'pin-device');
15941
- pinButton.dataset.deviceId = deviceId;
15942
- actions.appendChild(pinButton);
15943
- }
15944
- if (connectedDevice && deviceId) {
15945
- const focusButton = createRemoteFleetButton('Focus', 'Show this device in focused live panel', 'live-focus');
15946
- focusButton.dataset.deviceId = deviceId;
15947
- actions.appendChild(focusButton);
15948
- if (liveStreamEnabled) {
15949
- const liveButton = createRemoteFleetButton(liveStreamActive ? 'Stop' : 'Live', liveStreamActive ? 'Stop live stream' : 'Start focused live stream', liveStreamActive ? 'live-stop' : 'live-start');
15950
- liveButton.dataset.deviceId = deviceId;
15951
- liveButton.dataset.streamId = liveStreamId;
15952
- if (liveStreamActive) {
15953
- liveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
15954
- liveButton.style.color = '#b91c1c';
15955
- }
15956
- actions.appendChild(liveButton);
15957
- }
15958
- if (thumbnailEnabled) {
15959
- const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
15960
- thumbnailButton.dataset.deviceId = deviceId;
15961
- actions.appendChild(thumbnailButton);
15962
- }
15963
- const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
15964
- pingButton.dataset.deviceId = deviceId;
15965
- actions.appendChild(pingButton);
15966
- }
15967
- panel.appendChild(actions);
15968
- return panel;
15969
- };
15970
-
15971
15677
  const tileMetrics = getRemoteFleetTileMetrics(densityState);
15972
15678
  const tileMinWidth = tileMetrics.tileMinWidth;
15973
15679
  const tileMinHeight = tileMetrics.tileMinHeight;
@@ -15979,9 +15685,8 @@
15979
15685
  flex: 1 1 auto;
15980
15686
  min-height: 0;
15981
15687
  overflow: hidden;
15982
- display: grid;
15983
- grid-template-columns: minmax(0, 1fr) minmax(230px, 270px);
15984
- gap: 10px;
15688
+ display: flex;
15689
+ flex-direction: column;
15985
15690
  align-items: stretch;
15986
15691
  box-sizing: border-box;
15987
15692
  `;
@@ -16092,7 +15797,7 @@
16092
15797
  card.dataset.remoteFleetAction = 'select-device';
16093
15798
  card.setAttribute('role', 'button');
16094
15799
  card.setAttribute('aria-pressed', isSelected ? 'true' : 'false');
16095
- card.setAttribute('aria-label', `Show details for ${name}`);
15800
+ card.setAttribute('aria-label', `Open remote control for ${name}`);
16096
15801
  card.tabIndex = 0;
16097
15802
  card.title = name;
16098
15803
  card.style.cssText = `
@@ -16109,37 +15814,13 @@
16109
15814
  padding: 2px;
16110
15815
  border-radius: 8px;
16111
15816
  background: #ffffff;
16112
- border: 2px solid ${isSelected ? 'rgba(37, 99, 235, 0.76)' : (connectedDevice ? 'rgba(16, 185, 129, 0.30)' : 'rgba(148, 163, 184, 0.24)')};
16113
- box-shadow: ${isSelected ? '0 0 0 3px rgba(37, 99, 235, 0.15), 0 10px 22px rgba(15, 23, 42, 0.08)' : '0 8px 18px rgba(15, 23, 42, 0.05)'};
15817
+ border: 1px solid ${connectedDevice ? 'rgba(16, 185, 129, 0.28)' : 'rgba(148, 163, 184, 0.22)'};
15818
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.05);
16114
15819
  cursor: pointer;
16115
15820
  pointer-events: auto;
16116
15821
  user-select: none;
16117
15822
  `;
16118
15823
  card.appendChild(createDevicePreview(device, 'tile'));
16119
- if (isSelected) {
16120
- const selectedIndicator = document.createElement('span');
16121
- selectedIndicator.dataset.remoteFleetSelectedIndicator = 'true';
16122
- selectedIndicator.innerHTML = '<i class="fa-solid fa-check" aria-hidden="true"></i>';
16123
- selectedIndicator.title = 'Selected screen';
16124
- selectedIndicator.style.cssText = `
16125
- position: absolute;
16126
- right: 6px;
16127
- top: 6px;
16128
- width: 18px;
16129
- height: 18px;
16130
- display: inline-flex;
16131
- align-items: center;
16132
- justify-content: center;
16133
- border-radius: 999px;
16134
- background: #2563eb;
16135
- color: #ffffff;
16136
- font-size: 9px;
16137
- line-height: 1;
16138
- box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16), 0 8px 16px rgba(15, 23, 42, 0.16);
16139
- pointer-events: none;
16140
- `;
16141
- card.appendChild(selectedIndicator);
16142
- }
16143
15824
  grid.appendChild(card);
16144
15825
  });
16145
15826
 
@@ -16168,7 +15849,6 @@
16168
15849
  monitorWorkspace.appendChild(createRemoteFleetEmptyScreens(nodeModel, densityState));
16169
15850
  } else {
16170
15851
  monitorWorkspace.appendChild(grid);
16171
- monitorWorkspace.appendChild(createSelectedDevicePanel(selectedDevice));
16172
15852
  }
16173
15853
  bodyView.appendChild(monitorWorkspace);
16174
15854
  bodyView.appendChild(statusRail);
@@ -16226,71 +15906,90 @@
16226
15906
  .filter(card => card.style.display !== 'none')
16227
15907
  .map(card => String(card.dataset.deviceId || '').trim())
16228
15908
  .filter(Boolean);
16229
- if (focusedDevice) {
16230
- const focusedId = getRemoteFleetDeviceId(focusedDevice);
16231
- if (focusedId) {
16232
- ids.unshift(focusedId);
16233
- }
16234
- }
16235
15909
  return Array.from(new Set(ids)).slice(0, 120);
16236
15910
  };
16237
- const getFocusedLiveFrameDeviceIds = () => {
15911
+ const getVisibleLiveDevices = () => {
15912
+ const visibleIds = new Set(getVisibleFrameDeviceIds());
15913
+ return devices
15914
+ .filter(device => {
15915
+ const id = getRemoteFleetDeviceId(device);
15916
+ return id
15917
+ && visibleIds.has(id)
15918
+ && isRemoteFleetDeviceConnected(device)
15919
+ && isRemoteFleetLiveCapable(device);
15920
+ })
15921
+ .slice(0, 120);
15922
+ };
15923
+ const getVisibleLiveFrameDeviceIds = () => {
16238
15924
  const ids = [];
16239
- if (selectedDevice
16240
- && isRemoteFleetDeviceConnected(selectedDevice)
16241
- && (isRemoteFleetLiveCapable(selectedDevice) || isRemoteFleetLiveActive(selectedDevice))) {
16242
- pushUniqueFrameDeviceId(ids, selectedDevice);
16243
- }
16244
- if (focusedDevice
16245
- && isRemoteFleetDeviceConnected(focusedDevice)
16246
- && (isRemoteFleetLiveCapable(focusedDevice) || isRemoteFleetLiveActive(focusedDevice))) {
16247
- pushUniqueFrameDeviceId(ids, focusedDevice);
16248
- }
15925
+ getVisibleLiveDevices().forEach(device => pushUniqueFrameDeviceId(ids, device));
16249
15926
  devices
16250
15927
  .filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveActive(device))
16251
- .slice(0, 6)
16252
15928
  .forEach(device => pushUniqueFrameDeviceId(ids, device));
16253
- return ids.slice(0, 8);
16254
- };
16255
- const getAutoLiveDevice = () => {
16256
- if (selectedDevice && isRemoteFleetDeviceConnected(selectedDevice) && isRemoteFleetLiveCapable(selectedDevice)) {
16257
- return selectedDevice;
16258
- }
16259
- if (focusedDevice && isRemoteFleetDeviceConnected(focusedDevice) && isRemoteFleetLiveCapable(focusedDevice)) {
16260
- return focusedDevice;
16261
- }
16262
- return null;
15929
+ return ids.slice(0, 120);
16263
15930
  };
16264
- const ensureFocusedRemoteFleetLiveStream = async () => {
16265
- const target = getAutoLiveDevice();
16266
- if (!target || isRemoteFleetLiveActive(target)) {
15931
+ const ensureVisibleRemoteFleetLiveStreams = async () => {
15932
+ if (!(bodyView._remoteFleetAutoLiveStartedIds instanceof Set)) {
15933
+ bodyView._remoteFleetAutoLiveStartedIds = new Set();
15934
+ }
15935
+ const targets = getVisibleLiveDevices()
15936
+ .filter(device => {
15937
+ const deviceId = getRemoteFleetDeviceId(device);
15938
+ return deviceId
15939
+ && !isRemoteFleetLiveActive(device)
15940
+ && !bodyView._remoteFleetAutoLiveStartedIds.has(deviceId);
15941
+ });
15942
+ if (targets.length === 0) {
16267
15943
  return null;
16268
15944
  }
16269
15945
 
16270
- const deviceId = getRemoteFleetDeviceId(target);
16271
- if (!deviceId || bodyView._remoteFleetAutoLiveStartInFlight === deviceId) {
16272
- return null;
15946
+ if (!(bodyView._remoteFleetAutoLiveStartInFlight instanceof Set)) {
15947
+ bodyView._remoteFleetAutoLiveStartInFlight = new Set();
16273
15948
  }
16274
15949
 
16275
- bodyView._remoteFleetAutoLiveStartInFlight = deviceId;
16276
- try {
16277
- const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
16278
- window.RuntimeTrace?.emit?.('remote.live.autoStart', {
16279
- nodeId,
16280
- deviceId,
16281
- success: isRemoteFleetResultSuccess(result),
16282
- fps: result?.fps || result?.Fps || 0,
16283
- error: result?.error || result?.Error || ''
16284
- });
16285
- if (isRemoteFleetResultSuccess(result)) {
16286
- await syncRemoteFleetNodeStateFromResult(result);
15950
+ let started = 0;
15951
+ let lastResult = null;
15952
+ for (const target of targets) {
15953
+ const deviceId = getRemoteFleetDeviceId(target);
15954
+ if (!deviceId || bodyView._remoteFleetAutoLiveStartInFlight.has(deviceId)) {
15955
+ continue;
16287
15956
  }
16288
- return result;
16289
- } finally {
16290
- if (bodyView._remoteFleetAutoLiveStartInFlight === deviceId) {
16291
- bodyView._remoteFleetAutoLiveStartInFlight = '';
15957
+
15958
+ bodyView._remoteFleetAutoLiveStartInFlight.add(deviceId);
15959
+ try {
15960
+ const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
15961
+ lastResult = result;
15962
+ const success = isRemoteFleetResultSuccess(result);
15963
+ window.RuntimeTrace?.emit?.('remote.live.autoStart', {
15964
+ nodeId,
15965
+ deviceId,
15966
+ success,
15967
+ fps: result?.fps || result?.Fps || 0,
15968
+ mode: 'visible-grid',
15969
+ error: result?.error || result?.Error || ''
15970
+ });
15971
+ if (success) {
15972
+ started += 1;
15973
+ bodyView._remoteFleetAutoLiveStartedIds.add(deviceId);
15974
+ }
15975
+ } finally {
15976
+ bodyView._remoteFleetAutoLiveStartInFlight.delete(deviceId);
15977
+ }
15978
+ }
15979
+
15980
+ if (started > 0) {
15981
+ try {
15982
+ await refreshRemoteFleetNode();
15983
+ } catch (error) {
15984
+ window.RuntimeTrace?.emit?.('remote.live.autoRefreshFailed', {
15985
+ nodeId,
15986
+ started,
15987
+ error: error?.message || String(error || '')
15988
+ });
16292
15989
  }
16293
15990
  }
15991
+
15992
+ return lastResult || { success: started > 0, started };
16294
15993
  };
16295
15994
  const refreshRemoteFleetNode = async () => {
16296
15995
  if (bodyView._remoteFleetRefreshInFlight === true) {
@@ -16311,8 +16010,8 @@
16311
16010
  applyRemoteFleetFramePatches(bodyView, normalizeRemoteFleetDeviceFramePatches(result));
16312
16011
  return result;
16313
16012
  };
16314
- const refreshRemoteFleetFocusedLiveFrames = async () => {
16315
- const deviceIds = getFocusedLiveFrameDeviceIds();
16013
+ const refreshRemoteFleetVisibleLiveFrames = async () => {
16014
+ const deviceIds = getVisibleLiveFrameDeviceIds();
16316
16015
  if (deviceIds.length === 0) {
16317
16016
  return null;
16318
16017
  }
@@ -16323,6 +16022,27 @@
16323
16022
  applyRemoteFleetFramePatches(bodyView, patches);
16324
16023
  return result;
16325
16024
  };
16025
+ const startRemoteFleetMonitorRefreshTimer = (intervalMs, reason = 'monitor') => {
16026
+ const interval = Math.max(1000, Number(intervalMs) || REMOTE_FLEET_MONITOR_REFRESH_MS);
16027
+ bodyView.dataset.remoteFleetRefreshReason = reason;
16028
+ bodyView._remoteFleetMonitorRefreshTimer = setInterval(async () => {
16029
+ if (!document.body.contains(bodyView)) {
16030
+ clearRemoteFleetTimers(bodyView);
16031
+ return;
16032
+ }
16033
+
16034
+ try {
16035
+ await refreshRemoteFleetNode();
16036
+ } catch (error) {
16037
+ window.RuntimeTrace?.emit?.('remote.monitor.refreshFailed', {
16038
+ nodeId,
16039
+ reason,
16040
+ error: error?.message || String(error || '')
16041
+ });
16042
+ clearRemoteFleetTimers(bodyView);
16043
+ }
16044
+ }, interval);
16045
+ };
16326
16046
  const prepareRemoteFleetTaskFollow = batch => {
16327
16047
  if (!isRemoteFleetTaskBatchActive(batch)) {
16328
16048
  bodyView.dataset.remoteFleetTaskFollowActive = 'false';
@@ -16517,7 +16237,6 @@
16517
16237
  const deviceId = String(card.dataset.deviceId || '').trim();
16518
16238
  if (!deviceId) return;
16519
16239
  bodyView.dataset.remoteFleetSelectedDeviceId = deviceId;
16520
- renderRemoteFleetMonitor(bodyView, nodeModel);
16521
16240
  };
16522
16241
  card.addEventListener('click', selectCard);
16523
16242
  card.addEventListener('keydown', event => {
@@ -16814,15 +16533,16 @@
16814
16533
  startRemoteFleetHostLeaseTimer(nodeId, () => setRemoteFleetHostTarget(true, { quiet: true, renew: true }));
16815
16534
  }
16816
16535
 
16817
- const hasFocusedLiveTarget = getFocusedLiveFrameDeviceIds().length > 0 || !!getAutoLiveDevice();
16818
- if (hasFocusedLiveTarget || hasActiveLiveStream) {
16819
- ensureFocusedRemoteFleetLiveStream().catch(error => {
16536
+ const hasVisibleLiveTarget = getVisibleLiveFrameDeviceIds().length > 0
16537
+ || devices.some(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveCapable(device));
16538
+ if (hasVisibleLiveTarget || hasActiveLiveStream) {
16539
+ ensureVisibleRemoteFleetLiveStreams().catch(error => {
16820
16540
  window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
16821
16541
  nodeId,
16822
16542
  error: error?.message || String(error || '')
16823
16543
  });
16824
16544
  });
16825
- startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_LIVE_FRAME_REFRESH_MS, () => refreshRemoteFleetFocusedLiveFrames());
16545
+ startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_LIVE_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleLiveFrames());
16826
16546
  bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
16827
16547
  if (!document.body.contains(bodyView)) {
16828
16548
  clearRemoteFleetTimers(bodyView);
@@ -16837,18 +16557,9 @@
16837
16557
  }, REMOTE_FLEET_LIVE_REFRESH_MS);
16838
16558
  } else if (autoMonitorState && hasMonitorTargets) {
16839
16559
  startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
16840
- bodyView._remoteFleetMonitorRefreshTimer = setInterval(async () => {
16841
- if (!document.body.contains(bodyView)) {
16842
- clearRemoteFleetTimers(bodyView);
16843
- return;
16844
- }
16845
-
16846
- try {
16847
- await refreshRemoteFleetNode();
16848
- } catch {
16849
- clearRemoteFleetTimers(bodyView);
16850
- }
16851
- }, REMOTE_FLEET_MONITOR_REFRESH_MS);
16560
+ startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'visible-devices');
16561
+ } else if (autoMonitorState) {
16562
+ startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS, 'empty-discovery');
16852
16563
  }
16853
16564
  }
16854
16565
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-aehUBy4ZKXMCMfDXLIq7332pdALPad8G/hoFfyL1YcY=",
4
+ "hash": "sha256-we9qenVtePc2CABTfoN+/D96wdPuqIl4CgKuk3znAUA=",
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.7k773jan4j.dll": "MindExecution.Kernel.dll",
128
128
  "MindExecution.Plugins.Admin.nr5ioavpvn.dll": "MindExecution.Plugins.Admin.dll",
129
129
  "MindExecution.Plugins.Business.qq63kom73x.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.y4ipk4t13u.dll": "MindExecution.Plugins.Concept.dll",
130
+ "MindExecution.Plugins.Concept.w4znwcrl6x.dll": "MindExecution.Plugins.Concept.dll",
131
131
  "MindExecution.Plugins.Directory.u6p6pxpwxp.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.3gd195fq5t.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.sb0cb9cans.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.ib8s249hay.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.bfrevq2q4y.dll": "MindExecution.Web.dll",
132
+ "MindExecution.Plugins.PlanMaster.0w4vr40lnz.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.sdl5n9uv68.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.fprczldv6z.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.xhlvm1d8ex.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.cdplo34sb2.dll": "sha256-sLc0yEeGYQhFPxxNKLQsHllAhwjKhNn+pfgRY0bHbpo=",
282
282
  "MindExecution.Kernel.7k773jan4j.dll": "sha256-B5H6hzB6g8sWLqXUoXifv8CbLqVt7g1lLkkinTBKF8E=",
283
- "MindExecution.Plugins.Concept.y4ipk4t13u.dll": "sha256-ElyrQZwOP6pofpDFl6jxXzggowpO1TJO2Rg7ipnXrVE=",
284
- "MindExecution.Plugins.PlanMaster.3gd195fq5t.dll": "sha256-U0cqGsVxa696XZuKmipYMdlZ3Da761XTPmB5H+7oX7I=",
285
- "MindExecution.Shared.ib8s249hay.dll": "sha256-HrtN17KC7pAEJ7wf9AihBGmtftOOKR+S1iaj4FTtlwU=",
286
- "MindExecution.Web.bfrevq2q4y.dll": "sha256-qbbJ5AlXO8D8IEMvXHyA9a64eJEidL8Ksn4a/SD7pu8="
283
+ "MindExecution.Plugins.Concept.w4znwcrl6x.dll": "sha256-oNGAmbTjkFdVfZeYbc4jYWtyQa3c8Tle5LIXoFye4yo=",
284
+ "MindExecution.Plugins.PlanMaster.0w4vr40lnz.dll": "sha256-gPWCEzDE7cJLOFTJOhja3JXJzkHFWd4dzfRdsdTxwps=",
285
+ "MindExecution.Shared.fprczldv6z.dll": "sha256-veGmVlpQw2b1zDaUWReHRSLbiZ2IloG79uo1GLlduEo=",
286
+ "MindExecution.Web.xhlvm1d8ex.dll": "sha256-azcD8wLJIxuNSplzTVvh2ndLwdcu20xz1t8Jh23Dr7Q="
287
287
  },
288
288
  "lazyAssembly": {
289
289
  "MindExecution.Plugins.Admin.nr5ioavpvn.dll": "sha256-TWIfHGuEUUD1JtyuhfH4NM5JkvqAt8KcDVrWxvmwnVc=",
290
290
  "MindExecution.Plugins.Business.qq63kom73x.dll": "sha256-4lgIcKclWQIjijrBJJFu84fgAH4MsDCN/myhG/K9OOU=",
291
291
  "MindExecution.Plugins.Directory.u6p6pxpwxp.dll": "sha256-BjJ6/XNfp5wd/1RTdh99X9RR0I1GV6t4bp5A+RlAK9s=",
292
- "MindExecution.Plugins.YouTube.sb0cb9cans.dll": "sha256-WIQjip313fTDOOZ82NrcgM3dSrtgE8+A3grbIyBDMDE="
292
+ "MindExecution.Plugins.YouTube.sdl5n9uv68.dll": "sha256-gNZE7M6cqiF9aEJYdZtUzt5bYPkh5Be7LKuyh286u2Q="
293
293
  }
294
294
  },
295
295
  "cacheBootResources": true,
@@ -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=20260616-mdm-live-10fps-v557" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-live-10fps-v557" />
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-mdm-screen-wall-v560" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-screen-wall-v560" />
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 = '20260616-mdm-live-10fps-v557';
582
+ const scriptVersion = '20260616-mdm-screen-wall-v560';
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": "ubjvvUaQ",
2
+ "version": "i4nijHuW",
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-jqoAthq+GECm6zHrHdOnHyBxLrin0deFSEmEXlCpIqg=",
89
+ "hash": "sha256-MWVSM/VwUCyDL+ohY/7e6qDkkosGiE9E3t3DbiGJJEs=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -426,28 +426,28 @@
426
426
  "url": "_framework/MindExecution.Plugins.Business.qq63kom73x.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-ElyrQZwOP6pofpDFl6jxXzggowpO1TJO2Rg7ipnXrVE=",
430
- "url": "_framework/MindExecution.Plugins.Concept.y4ipk4t13u.dll"
429
+ "hash": "sha256-oNGAmbTjkFdVfZeYbc4jYWtyQa3c8Tle5LIXoFye4yo=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.w4znwcrl6x.dll"
431
431
  },
432
432
  {
433
433
  "hash": "sha256-BjJ6/XNfp5wd/1RTdh99X9RR0I1GV6t4bp5A+RlAK9s=",
434
434
  "url": "_framework/MindExecution.Plugins.Directory.u6p6pxpwxp.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-U0cqGsVxa696XZuKmipYMdlZ3Da761XTPmB5H+7oX7I=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.3gd195fq5t.dll"
437
+ "hash": "sha256-gPWCEzDE7cJLOFTJOhja3JXJzkHFWd4dzfRdsdTxwps=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.0w4vr40lnz.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-WIQjip313fTDOOZ82NrcgM3dSrtgE8+A3grbIyBDMDE=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.sb0cb9cans.dll"
441
+ "hash": "sha256-gNZE7M6cqiF9aEJYdZtUzt5bYPkh5Be7LKuyh286u2Q=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.sdl5n9uv68.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-HrtN17KC7pAEJ7wf9AihBGmtftOOKR+S1iaj4FTtlwU=",
446
- "url": "_framework/MindExecution.Shared.ib8s249hay.dll"
445
+ "hash": "sha256-veGmVlpQw2b1zDaUWReHRSLbiZ2IloG79uo1GLlduEo=",
446
+ "url": "_framework/MindExecution.Shared.fprczldv6z.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-qbbJ5AlXO8D8IEMvXHyA9a64eJEidL8Ksn4a/SD7pu8=",
450
- "url": "_framework/MindExecution.Web.bfrevq2q4y.dll"
449
+ "hash": "sha256-azcD8wLJIxuNSplzTVvh2ndLwdcu20xz1t8Jh23Dr7Q=",
450
+ "url": "_framework/MindExecution.Web.xhlvm1d8ex.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-lwdIDm7MRgjojZnnKDxY0mgEXEahNVpbFzIiLxbPh9k=",
773
+ "hash": "sha256-vjnwzCMMAs+8it6vEUPeOD7pI/ekFwMr484KaDdpugE=",
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-oH92qacgFgianR88nktzfiIq92f7S1DnX+p/mh/U5yQ=",
837
+ "hash": "sha256-sZOOfmQyxo9FNFTY9d3r4uYgRhh9Z2b5ve3569JDamU=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: ubjvvUaQ */
1
+ /* Manifest version: i4nijHuW */
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