@mindexec/cli 0.2.97 → 0.2.99

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.
Files changed (18) hide show
  1. package/package.json +1 -1
  2. package/scripts/remote-fleet-render-smoke.mjs +10 -5
  3. package/server.js +48 -6
  4. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +237 -180
  5. package/wwwroot/_framework/{MindExecution.Core.eyl4o4qxg8.dll → MindExecution.Core.cdplo34sb2.dll} +0 -0
  6. package/wwwroot/_framework/{MindExecution.Kernel.hxyapaztgr.dll → MindExecution.Kernel.7k773jan4j.dll} +0 -0
  7. package/wwwroot/_framework/{MindExecution.Plugins.Admin.7kemovesa7.dll → MindExecution.Plugins.Admin.nr5ioavpvn.dll} +0 -0
  8. package/wwwroot/_framework/{MindExecution.Plugins.Business.t370in2g1b.dll → MindExecution.Plugins.Business.qq63kom73x.dll} +0 -0
  9. package/wwwroot/_framework/{MindExecution.Plugins.Concept.oqhv1116kb.dll → MindExecution.Plugins.Concept.w4znwcrl6x.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Directory.0v80a0hi9m.dll → MindExecution.Plugins.Directory.u6p6pxpwxp.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.hn5sfx4z2l.dll → MindExecution.Plugins.PlanMaster.0w4vr40lnz.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.c8sc0tlc6o.dll → MindExecution.Plugins.YouTube.sdl5n9uv68.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Shared.osv9nbh3ym.dll → MindExecution.Shared.fprczldv6z.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Web.q1jwri2r1p.dll → MindExecution.Web.xhlvm1d8ex.dll} +0 -0
  15. package/wwwroot/_framework/blazor.boot.json +21 -21
  16. package/wwwroot/index.html +3 -3
  17. package/wwwroot/service-worker-assets.js +24 -24
  18. package/wwwroot/service-worker.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.97",
3
+ "version": "0.2.99",
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);
@@ -745,7 +745,10 @@ try {
745
745
  assert.equal(bodyView.dataset.remoteFleetDensity, 'cards');
746
746
  assert.match(bodyView.querySelector('[data-remote-fleet-device-grid="true"]')?.style.cssText || '', /minmax\(132px,\s*1fr\)/);
747
747
  cards = bodyView.querySelectorAll('article[data-device-id]');
748
- const patchTarget = devices.find(device => device.ThumbnailEnabled === true && device.LiveStreamActive !== true);
748
+ const patchTarget = devices.find(device =>
749
+ device.Connected === true
750
+ && device.ThumbnailEnabled === true
751
+ && device.LiveStreamActive !== true);
749
752
  assert.ok(patchTarget);
750
753
  const patchCard = Array.from(cards).find(card => card.dataset.deviceId === patchTarget.DeviceId);
751
754
  const patchPreview = patchCard?.querySelector('[data-remote-fleet-device-preview="tile"]');
@@ -765,7 +768,9 @@ try {
765
768
  assert.equal(patchPreview.dataset.remoteFleetPendingFrameUrl || '', '');
766
769
  assert.ok(patchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]'));
767
770
  assert.equal(patchPreview.querySelector('img[data-remote-fleet-frame-image="true"]')?.dataset.remoteFleetFrameUrl, patchUrl);
768
- const alternateDevice = devices.find(device => device.DeviceId !== focusedDevice.DeviceId);
771
+ const alternateDevice = devices.find(device =>
772
+ device.Connected === true
773
+ && device.DeviceId !== focusedDevice.DeviceId);
769
774
  assert.ok(alternateDevice);
770
775
  const alternateCard = Array.from(cards).find(card => card.dataset.deviceId === alternateDevice.DeviceId);
771
776
  assert.ok(alternateCard);
@@ -777,7 +782,7 @@ try {
777
782
  assert.equal(selectedAfterClick?.getAttribute('aria-pressed'), 'true');
778
783
  assert.ok(selectedAfterClick?.querySelector('[data-remote-fleet-selected-indicator="true"]'));
779
784
  const livePanel = bodyView.querySelector('[data-remote-fleet-live-panel="true"]');
780
- assert.equal(livePanel?.dataset.deviceId, focusedDevice.DeviceId);
785
+ assert.equal(livePanel, null);
781
786
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-visible"]'), null);
782
787
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-connected"]'), null);
783
788
  assert.equal(bodyView.querySelector('[data-remote-fleet-task-input="true"]'), null);
@@ -811,7 +816,7 @@ try {
811
816
  enabled: false
812
817
  });
813
818
 
814
- assert.ok(livePanel?.textContent.includes('RemoteFast 20 fps'), livePanel?.textContent || bodyView.textContent);
819
+ assert.equal(bodyView.querySelector('[data-remote-fleet-live-panel="true"]'), null);
815
820
  assert.equal(bodyView.querySelector('code'), null);
816
821
  assert.equal(bodyView.textContent.includes('npx @mindexec/remote connect'), false);
817
822
  assert.equal(bodyView.querySelector('[data-remote-fleet-action="copy-command"]'), null);
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,16 +12974,23 @@
12974
12974
  }
12975
12975
 
12976
12976
  const REMOTE_FLEET_MONITOR_REFRESH_MS = 10000;
12977
- const REMOTE_FLEET_LIVE_REFRESH_MS = 5000;
12978
- const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = 50;
12977
+ const REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS = 2500;
12978
+ const REMOTE_FLEET_LIVE_REFRESH_MS = 30000;
12979
+ const REMOTE_FLEET_FOCUSED_LIVE_FPS = 10;
12980
+ const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_FOCUSED_LIVE_FPS);
12979
12981
  const REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS = 2000;
12980
12982
  const REMOTE_FLEET_FRAME_CANVAS_MAX_DPR = 2;
12983
+ const REMOTE_FLEET_FRAME_BLOB_CACHE_MS = 10000;
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;
12981
12987
  const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
12982
12988
  const REMOTE_FLEET_TASK_FOLLOW_REFRESH_MS = 2000;
12983
12989
  const REMOTE_FLEET_TASK_FOLLOW_MAX_TICKS = 60;
12984
12990
  const REMOTE_FLEET_HOST_LEASE_REFRESH_MS = 10000;
12985
12991
  const remoteFleetHostLeaseTimers = new Map();
12986
12992
  const remoteFleetLocalHostTargets = new Map();
12993
+ const remoteFleetFrameBlobCache = new Map();
12987
12994
  let activeRemoteFleetControlPopup = null;
12988
12995
 
12989
12996
  function findRemoteFleetBodyByNodeId(nodeId) {
@@ -13206,6 +13213,10 @@
13206
13213
  return getRemoteFleetDeviceField(device, 'aiAssistEnabled', 'AiAssistEnabled', false) === true;
13207
13214
  }
13208
13215
 
13216
+ function isRemoteFleetLiveCapable(device) {
13217
+ return getRemoteFleetDeviceField(device, 'liveStreamEnabled', 'LiveStreamEnabled', false) === true;
13218
+ }
13219
+
13209
13220
  function hasRemoteFleetThumbnail(device) {
13210
13221
  return isRemoteFleetFrameSource(getRemoteFleetFrameSource(device, 'thumbnail'));
13211
13222
  }
@@ -13291,6 +13302,10 @@
13291
13302
  return patches;
13292
13303
  }
13293
13304
 
13305
+ function shouldDisplayRemoteFleetMonitorDevice(device) {
13306
+ return isRemoteFleetDeviceConnected(device);
13307
+ }
13308
+
13294
13309
  function ensureRemoteFleetFrameImage(preview, frame) {
13295
13310
  let image = preview?.querySelector?.('img[data-remote-fleet-frame-image="true"]');
13296
13311
  if (image) {
@@ -13431,6 +13446,42 @@
13431
13446
  return true;
13432
13447
  }
13433
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
+
13434
13485
  async function loadRemoteFleetFrameBitmap(frame) {
13435
13486
  if (!frame || !isRemoteFleetFrameSource(frame.frameUrl)) {
13436
13487
  return null;
@@ -13440,16 +13491,46 @@
13440
13491
  return null;
13441
13492
  }
13442
13493
 
13443
- const response = await fetch(frame.frameUrl, {
13444
- cache: 'no-store',
13445
- credentials: 'omit'
13446
- });
13447
- if (!response.ok) {
13448
- throw new Error(`remote-frame-fetch-${response.status}`);
13494
+ const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
13495
+ for (const [key, entry] of remoteFleetFrameBlobCache) {
13496
+ if (!entry || entry.expiresAt <= now || remoteFleetFrameBlobCache.size > REMOTE_FLEET_FRAME_BLOB_CACHE_LIMIT) {
13497
+ remoteFleetFrameBlobCache.delete(key);
13498
+ }
13499
+ }
13500
+
13501
+ const identity = getRemoteFleetFrameIdentity(frame);
13502
+ let entry = remoteFleetFrameBlobCache.get(identity);
13503
+ if (!entry) {
13504
+ const promise = fetch(frame.frameUrl, {
13505
+ cache: 'no-store',
13506
+ credentials: 'omit'
13507
+ })
13508
+ .then(response => {
13509
+ if (!response.ok) {
13510
+ throw new Error(`remote-frame-fetch-${response.status}`);
13511
+ }
13512
+ return response.blob();
13513
+ })
13514
+ .catch(error => {
13515
+ remoteFleetFrameBlobCache.delete(identity);
13516
+ throw error;
13517
+ });
13518
+
13519
+ entry = {
13520
+ promise,
13521
+ expiresAt: now + REMOTE_FLEET_FRAME_BLOB_CACHE_MS
13522
+ };
13523
+ remoteFleetFrameBlobCache.set(identity, entry);
13524
+ } else {
13525
+ entry.expiresAt = now + REMOTE_FLEET_FRAME_BLOB_CACHE_MS;
13449
13526
  }
13450
13527
 
13451
- const blob = await response.blob();
13452
- 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);
13453
13534
  }
13454
13535
 
13455
13536
  function getRemoteFleetFrameIdentity(frame) {
@@ -13684,15 +13765,24 @@
13684
13765
  return;
13685
13766
  }
13686
13767
 
13768
+ const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
13769
+ let settled = false;
13770
+ const complete = (loaded, surface = 'img') => {
13771
+ if (settled) return;
13772
+ settled = true;
13773
+ clearTimeout(timeout);
13774
+ finish(loaded, surface);
13775
+ };
13776
+ const timeout = setTimeout(() => complete(false, 'img-timeout'), timeoutMs);
13687
13777
  const loader = new Image();
13688
13778
  loader.decoding = 'async';
13689
13779
  loader.onload = () => {
13690
13780
  const decoded = typeof loader.decode === 'function'
13691
13781
  ? loader.decode().catch(() => undefined)
13692
13782
  : Promise.resolve();
13693
- decoded.then(() => finish(true, 'img'));
13783
+ decoded.then(() => complete(true, 'img'));
13694
13784
  };
13695
- loader.onerror = () => finish(false, 'img');
13785
+ loader.onerror = () => complete(false, 'img');
13696
13786
  loader.src = frame.frameUrl;
13697
13787
  };
13698
13788
 
@@ -13748,7 +13838,19 @@
13748
13838
  }
13749
13839
  });
13750
13840
  })
13751
- .catch(() => loadFallbackImage());
13841
+ .catch(error => {
13842
+ if (frame.kind === 'live') {
13843
+ window.RuntimeTrace?.emit?.('remote.frame.decodeSkipped', {
13844
+ kind: frame.kind,
13845
+ seq: frame.frameSeq,
13846
+ error: error?.message || String(error || '')
13847
+ });
13848
+ finish(false, 'canvas-timeout');
13849
+ return;
13850
+ }
13851
+
13852
+ loadFallbackImage();
13853
+ });
13752
13854
  }
13753
13855
 
13754
13856
  function queueRemoteFleetFramePaint(preview, frame) {
@@ -14620,7 +14722,7 @@
14620
14722
  const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
14621
14723
  const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
14622
14724
  const thumbnailEnabled = isRemoteFleetThumbnailCapable(device);
14623
- const liveStreamEnabled = getRemoteFleetDeviceField(device, 'liveStreamEnabled', 'LiveStreamEnabled', false) === true;
14725
+ const liveStreamEnabled = isRemoteFleetLiveCapable(device);
14624
14726
  const liveActive = isRemoteFleetLiveActive(device);
14625
14727
  const liveStreamId = String(getRemoteFleetDeviceField(device, 'liveStreamId', 'LiveStreamId', ''));
14626
14728
  const hasLiveFrame = hasRemoteFleetLiveFrame(device);
@@ -15040,6 +15142,7 @@
15040
15142
  }
15041
15143
  bodyView.dataset.remoteFleetTaskFollowActive = latestTaskBatchActive ? 'true' : 'false';
15042
15144
  const sortedDevices = [...parseRemoteFleetDevices(nodeModel)]
15145
+ .filter(shouldDisplayRemoteFleetMonitorDevice)
15043
15146
  .sort((left, right) => compareRemoteFleetDevices(left, right, sortState));
15044
15147
  const devices = groupState === 'none'
15045
15148
  ? sortedDevices
@@ -15065,11 +15168,8 @@
15065
15168
  } else {
15066
15169
  delete bodyView.dataset.remoteFleetSelectedDeviceId;
15067
15170
  }
15068
- const total = Number(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDeviceCount', devices.length));
15069
- const connected = Number(getRemoteFleetMetadataValue(
15070
- nodeModel,
15071
- 'RemoteFleetConnectedDeviceCount',
15072
- devices.filter(isRemoteFleetDeviceConnected).length));
15171
+ const total = devices.length;
15172
+ const connected = devices.filter(isRemoteFleetDeviceConnected).length;
15073
15173
  const taskCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceTaskCapable(device)).length;
15074
15174
  const aiCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceAiCapable(device)).length;
15075
15175
  const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
@@ -15508,150 +15608,6 @@
15508
15608
  bodyView.appendChild(resultPanel);
15509
15609
  }
15510
15610
 
15511
- if (focusedDevice) {
15512
- const focusedId = getRemoteFleetDeviceId(focusedDevice);
15513
- const focusedName = getRemoteFleetDeviceName(focusedDevice);
15514
- const liveActive = isRemoteFleetLiveActive(focusedDevice);
15515
- const liveFrameSource = getRemoteFleetFrameSource(focusedDevice, 'live');
15516
- const liveFrameAt = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', ''));
15517
- const liveStreamId = String(getRemoteFleetDeviceField(focusedDevice, 'liveStreamId', 'LiveStreamId', ''));
15518
- const hasLiveFrame = hasRemoteFleetLiveFrame(focusedDevice);
15519
- const livePanel = document.createElement('section');
15520
- livePanel.dataset.remoteFleetLivePanel = 'true';
15521
- livePanel.dataset.deviceId = focusedId;
15522
- livePanel.style.cssText = `
15523
- flex: 0 0 auto;
15524
- display: grid;
15525
- grid-template-columns: minmax(220px, 0.95fr) minmax(0, 1.25fr);
15526
- gap: 10px;
15527
- min-height: 170px;
15528
- padding: 10px;
15529
- border-radius: 8px;
15530
- border: 1px solid rgba(14, 165, 233, 0.24);
15531
- background: rgba(255, 255, 255, 0.82);
15532
- `;
15533
-
15534
- const livePreview = document.createElement('div');
15535
- livePreview.dataset.remoteFleetDevicePreview = 'focused-live';
15536
- livePreview.dataset.deviceId = focusedId;
15537
- livePreview.dataset.remoteFleetFrameKind = hasLiveFrame ? 'live' : '';
15538
- livePreview.dataset.remoteFleetFrameSeq = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameSeq', 'LiveFrameSeq', 0));
15539
- livePreview.style.cssText = `
15540
- position: relative;
15541
- min-width: 0;
15542
- aspect-ratio: 16 / 9;
15543
- overflow: hidden;
15544
- border-radius: 7px;
15545
- background: linear-gradient(135deg, #111827 0%, #1f2937 100%);
15546
- border: 1px solid rgba(15, 23, 42, 0.16);
15547
- `;
15548
- if (hasLiveFrame) {
15549
- const image = document.createElement('img');
15550
- image.dataset.remoteFleetFrameImage = 'true';
15551
- image.dataset.remoteFleetFrameKind = 'live';
15552
- image.dataset.remoteFleetFrameSeq = livePreview.dataset.remoteFleetFrameSeq;
15553
- image.src = liveFrameSource;
15554
- image.alt = `${focusedName} live screen`;
15555
- image.decoding = 'async';
15556
- image.style.cssText = `
15557
- width: 100%;
15558
- height: 100%;
15559
- object-fit: cover;
15560
- display: block;
15561
- `;
15562
- livePreview.appendChild(image);
15563
- } else {
15564
- const placeholder = document.createElement('div');
15565
- placeholder.dataset.remoteFleetScreenPlaceholder = 'true';
15566
- placeholder.textContent = liveActive ? 'Waiting for live frame' : 'Live view idle';
15567
- placeholder.style.cssText = `
15568
- position: absolute;
15569
- inset: 0;
15570
- display: flex;
15571
- align-items: center;
15572
- justify-content: center;
15573
- color: rgba(226, 232, 240, 0.82);
15574
- font-size: 12px;
15575
- font-weight: 900;
15576
- letter-spacing: 0;
15577
- `;
15578
- livePreview.appendChild(placeholder);
15579
- }
15580
- const liveBadge = document.createElement('span');
15581
- liveBadge.dataset.remoteFleetFrameBadge = 'true';
15582
- liveBadge.textContent = liveActive
15583
- ? `LIVE ${liveFrameAt ? formatRemoteFleetAge(liveFrameAt) : ''}`.trim()
15584
- : 'FOCUS';
15585
- liveBadge.style.cssText = `
15586
- position: absolute;
15587
- left: 7px;
15588
- top: 7px;
15589
- max-width: calc(100% - 14px);
15590
- padding: 4px 7px;
15591
- border-radius: 999px;
15592
- background: ${liveActive ? 'rgba(220, 38, 38, 0.86)' : 'rgba(15, 23, 42, 0.72)'};
15593
- color: #ffffff;
15594
- font-size: 9px;
15595
- font-weight: 950;
15596
- line-height: 1;
15597
- overflow: hidden;
15598
- text-overflow: ellipsis;
15599
- white-space: nowrap;
15600
- letter-spacing: 0;
15601
- `;
15602
- livePreview.appendChild(liveBadge);
15603
-
15604
- const liveInfo = document.createElement('div');
15605
- liveInfo.style.cssText = `
15606
- min-width: 0;
15607
- display: flex;
15608
- flex-direction: column;
15609
- justify-content: space-between;
15610
- gap: 8px;
15611
- `;
15612
- const liveTitle = document.createElement('div');
15613
- liveTitle.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:3px;';
15614
- const liveName = document.createElement('strong');
15615
- liveName.textContent = focusedName;
15616
- liveName.title = focusedName;
15617
- 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;';
15618
- const liveMeta = document.createElement('span');
15619
- liveMeta.textContent = liveActive
15620
- ? `RemoteFast ${getRemoteFleetDeviceField(focusedDevice, 'liveStreamFps', 'LiveStreamFps', 0) || 20} fps`
15621
- : 'View-only focused screen stream';
15622
- 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;';
15623
- liveTitle.appendChild(liveName);
15624
- liveTitle.appendChild(liveMeta);
15625
-
15626
- const liveActions = document.createElement('div');
15627
- liveActions.style.cssText = 'display:flex;align-items:center;gap:8px;flex-wrap:wrap;';
15628
- if (isRemoteFleetDeviceConnected(focusedDevice) && getRemoteFleetDeviceField(focusedDevice, 'liveStreamEnabled', 'LiveStreamEnabled', false) === true) {
15629
- const startLiveButton = createRemoteFleetButton(liveActive ? 'Restart live' : 'Start live', 'Start focused view-only live stream', 'live-start');
15630
- startLiveButton.dataset.deviceId = focusedId;
15631
- startLiveButton.style.height = '30px';
15632
- liveActions.appendChild(startLiveButton);
15633
- if (liveActive) {
15634
- const stopLiveButton = createRemoteFleetButton('Stop live', 'Stop focused live stream', 'live-stop');
15635
- stopLiveButton.dataset.deviceId = focusedId;
15636
- stopLiveButton.dataset.streamId = liveStreamId;
15637
- stopLiveButton.style.height = '30px';
15638
- stopLiveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
15639
- stopLiveButton.style.color = '#b91c1c';
15640
- liveActions.appendChild(stopLiveButton);
15641
- }
15642
- } else {
15643
- const disabled = document.createElement('span');
15644
- disabled.textContent = 'Live unavailable';
15645
- disabled.style.cssText = 'color:#64748b;font-size:11px;font-weight:900;';
15646
- liveActions.appendChild(disabled);
15647
- }
15648
- liveInfo.appendChild(liveTitle);
15649
- liveInfo.appendChild(liveActions);
15650
- livePanel.appendChild(livePreview);
15651
- livePanel.appendChild(liveInfo);
15652
- bodyView.appendChild(livePanel);
15653
- }
15654
-
15655
15611
  const createDevicePreview = (device, mode = 'tile') => {
15656
15612
  const name = getRemoteFleetDeviceName(device);
15657
15613
  const connectedDevice = isRemoteFleetDeviceConnected(device);
@@ -15783,7 +15739,7 @@
15783
15739
  .join(' / ') || 'unknown';
15784
15740
  const release = String(device?.release || device?.Release || '');
15785
15741
  const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
15786
- const liveStreamEnabled = device?.liveStreamEnabled === true || device?.LiveStreamEnabled === true;
15742
+ const liveStreamEnabled = isRemoteFleetLiveCapable(device);
15787
15743
  const liveStreamActive = isRemoteFleetLiveActive(device);
15788
15744
  const liveStreamId = String(device?.liveStreamId || device?.LiveStreamId || '');
15789
15745
  const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
@@ -15999,7 +15955,7 @@
15999
15955
  const connectedDevice = isRemoteFleetDeviceConnected(device);
16000
15956
  const name = getRemoteFleetDeviceName(device);
16001
15957
  const deviceId = getRemoteFleetDeviceId(device);
16002
- const liveStreamEnabled = device?.liveStreamEnabled === true || device?.LiveStreamEnabled === true;
15958
+ const liveStreamEnabled = isRemoteFleetLiveCapable(device);
16003
15959
  const liveStreamActive = isRemoteFleetLiveActive(device);
16004
15960
  const hasThumbnail = hasRemoteFleetThumbnail(device);
16005
15961
  const hasLiveFrame = hasRemoteFleetLiveFrame(device);
@@ -16181,6 +16137,12 @@
16181
16137
  const readTaskInstruction = () => String(taskInput.value || '').trim();
16182
16138
  const useAiAssist = () => aiToggle.checked === true;
16183
16139
  const getDeviceCards = () => Array.from(grid.querySelectorAll('article[data-device-id]'));
16140
+ const pushUniqueFrameDeviceId = (ids, device) => {
16141
+ const id = getRemoteFleetDeviceId(device);
16142
+ if (id && !ids.includes(id)) {
16143
+ ids.push(id);
16144
+ }
16145
+ };
16184
16146
  const getVisibleFrameDeviceIds = () => {
16185
16147
  const ids = getDeviceCards()
16186
16148
  .filter(card => card.style.display !== 'none')
@@ -16194,6 +16156,70 @@
16194
16156
  }
16195
16157
  return Array.from(new Set(ids)).slice(0, 120);
16196
16158
  };
16159
+ const getFocusedLiveFrameDeviceIds = () => {
16160
+ const ids = [];
16161
+ if (selectedDevice
16162
+ && isRemoteFleetDeviceConnected(selectedDevice)
16163
+ && (isRemoteFleetLiveCapable(selectedDevice) || isRemoteFleetLiveActive(selectedDevice))) {
16164
+ pushUniqueFrameDeviceId(ids, selectedDevice);
16165
+ }
16166
+ if (focusedDevice
16167
+ && isRemoteFleetDeviceConnected(focusedDevice)
16168
+ && (isRemoteFleetLiveCapable(focusedDevice) || isRemoteFleetLiveActive(focusedDevice))) {
16169
+ pushUniqueFrameDeviceId(ids, focusedDevice);
16170
+ }
16171
+ const autoLiveDevice = getAutoLiveDevice();
16172
+ if (autoLiveDevice
16173
+ && isRemoteFleetDeviceConnected(autoLiveDevice)
16174
+ && isRemoteFleetLiveCapable(autoLiveDevice)) {
16175
+ pushUniqueFrameDeviceId(ids, autoLiveDevice);
16176
+ }
16177
+ devices
16178
+ .filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveActive(device))
16179
+ .slice(0, 6)
16180
+ .forEach(device => pushUniqueFrameDeviceId(ids, device));
16181
+ return ids.slice(0, 8);
16182
+ };
16183
+ const getAutoLiveDevice = () => {
16184
+ if (selectedDevice && isRemoteFleetDeviceConnected(selectedDevice) && isRemoteFleetLiveCapable(selectedDevice)) {
16185
+ return selectedDevice;
16186
+ }
16187
+ if (focusedDevice && isRemoteFleetDeviceConnected(focusedDevice) && isRemoteFleetLiveCapable(focusedDevice)) {
16188
+ return focusedDevice;
16189
+ }
16190
+ return devices.find(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveCapable(device)) || null;
16191
+ };
16192
+ const ensureFocusedRemoteFleetLiveStream = async () => {
16193
+ const target = getAutoLiveDevice();
16194
+ if (!target || isRemoteFleetLiveActive(target)) {
16195
+ return null;
16196
+ }
16197
+
16198
+ const deviceId = getRemoteFleetDeviceId(target);
16199
+ if (!deviceId || bodyView._remoteFleetAutoLiveStartInFlight === deviceId) {
16200
+ return null;
16201
+ }
16202
+
16203
+ bodyView._remoteFleetAutoLiveStartInFlight = deviceId;
16204
+ try {
16205
+ const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
16206
+ window.RuntimeTrace?.emit?.('remote.live.autoStart', {
16207
+ nodeId,
16208
+ deviceId,
16209
+ success: isRemoteFleetResultSuccess(result),
16210
+ fps: result?.fps || result?.Fps || 0,
16211
+ error: result?.error || result?.Error || ''
16212
+ });
16213
+ if (isRemoteFleetResultSuccess(result)) {
16214
+ await syncRemoteFleetNodeStateFromResult(result);
16215
+ }
16216
+ return result;
16217
+ } finally {
16218
+ if (bodyView._remoteFleetAutoLiveStartInFlight === deviceId) {
16219
+ bodyView._remoteFleetAutoLiveStartInFlight = '';
16220
+ }
16221
+ }
16222
+ };
16197
16223
  const refreshRemoteFleetNode = async () => {
16198
16224
  if (bodyView._remoteFleetRefreshInFlight === true) {
16199
16225
  return null;
@@ -16213,6 +16239,39 @@
16213
16239
  applyRemoteFleetFramePatches(bodyView, normalizeRemoteFleetDeviceFramePatches(result));
16214
16240
  return result;
16215
16241
  };
16242
+ const refreshRemoteFleetFocusedLiveFrames = async () => {
16243
+ const deviceIds = getFocusedLiveFrameDeviceIds();
16244
+ if (deviceIds.length === 0) {
16245
+ return null;
16246
+ }
16247
+
16248
+ const result = await invokeDotNetAsync('RefreshRemoteFleetFramesFromJs', nodeId, deviceIds, false);
16249
+ const patches = normalizeRemoteFleetDeviceFramePatches(result)
16250
+ .filter(frame => String(frame.kind || '').toLowerCase() === 'live');
16251
+ applyRemoteFleetFramePatches(bodyView, patches);
16252
+ return result;
16253
+ };
16254
+ const startRemoteFleetMonitorRefreshTimer = (intervalMs, reason = 'monitor') => {
16255
+ const interval = Math.max(1000, Number(intervalMs) || REMOTE_FLEET_MONITOR_REFRESH_MS);
16256
+ bodyView.dataset.remoteFleetRefreshReason = reason;
16257
+ bodyView._remoteFleetMonitorRefreshTimer = setInterval(async () => {
16258
+ if (!document.body.contains(bodyView)) {
16259
+ clearRemoteFleetTimers(bodyView);
16260
+ return;
16261
+ }
16262
+
16263
+ try {
16264
+ await refreshRemoteFleetNode();
16265
+ } catch (error) {
16266
+ window.RuntimeTrace?.emit?.('remote.monitor.refreshFailed', {
16267
+ nodeId,
16268
+ reason,
16269
+ error: error?.message || String(error || '')
16270
+ });
16271
+ clearRemoteFleetTimers(bodyView);
16272
+ }
16273
+ }, interval);
16274
+ };
16216
16275
  const prepareRemoteFleetTaskFollow = batch => {
16217
16276
  if (!isRemoteFleetTaskBatchActive(batch)) {
16218
16277
  bodyView.dataset.remoteFleetTaskFollowActive = 'false';
@@ -16514,13 +16573,13 @@
16514
16573
  error: result?.error || result?.Error || ''
16515
16574
  });
16516
16575
  rememberRemoteFleetLocalHostTarget(nodeId, enabled === true, result);
16517
- await syncRemoteFleetNodeStateFromResult(result);
16518
16576
  if (quiet) {
16519
16577
  if (!isRemoteFleetResultSuccess(result) || !isRemoteFleetResultActive(result)) {
16520
16578
  stopRemoteFleetHostLeaseTimer(nodeId);
16521
16579
  }
16522
16580
  return result;
16523
16581
  }
16582
+ await syncRemoteFleetNodeStateFromResult(result);
16524
16583
  if (isRemoteFleetResultSuccess(result) && enabled !== true) {
16525
16584
  stopRemoteFleetHostLeaseTimer(nodeId);
16526
16585
  }
@@ -16704,8 +16763,15 @@
16704
16763
  startRemoteFleetHostLeaseTimer(nodeId, () => setRemoteFleetHostTarget(true, { quiet: true, renew: true }));
16705
16764
  }
16706
16765
 
16707
- if (hasActiveLiveStream) {
16708
- startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_LIVE_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(false));
16766
+ const hasFocusedLiveTarget = getFocusedLiveFrameDeviceIds().length > 0 || !!getAutoLiveDevice();
16767
+ if (hasFocusedLiveTarget || hasActiveLiveStream) {
16768
+ ensureFocusedRemoteFleetLiveStream().catch(error => {
16769
+ window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
16770
+ nodeId,
16771
+ error: error?.message || String(error || '')
16772
+ });
16773
+ });
16774
+ startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_LIVE_FRAME_REFRESH_MS, () => refreshRemoteFleetFocusedLiveFrames());
16709
16775
  bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
16710
16776
  if (!document.body.contains(bodyView)) {
16711
16777
  clearRemoteFleetTimers(bodyView);
@@ -16720,18 +16786,9 @@
16720
16786
  }, REMOTE_FLEET_LIVE_REFRESH_MS);
16721
16787
  } else if (autoMonitorState && hasMonitorTargets) {
16722
16788
  startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
16723
- bodyView._remoteFleetMonitorRefreshTimer = setInterval(async () => {
16724
- if (!document.body.contains(bodyView)) {
16725
- clearRemoteFleetTimers(bodyView);
16726
- return;
16727
- }
16728
-
16729
- try {
16730
- await refreshRemoteFleetNode();
16731
- } catch {
16732
- clearRemoteFleetTimers(bodyView);
16733
- }
16734
- }, REMOTE_FLEET_MONITOR_REFRESH_MS);
16789
+ startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'visible-devices');
16790
+ } else if (autoMonitorState) {
16791
+ startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS, 'empty-discovery');
16735
16792
  }
16736
16793
  }
16737
16794
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-S5i7mkxpxBzpYozfgxXvhV6AnfLnexeTJfewiJ8jOtA=",
4
+ "hash": "sha256-we9qenVtePc2CABTfoN+/D96wdPuqIl4CgKuk3znAUA=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -123,16 +123,16 @@
123
123
  "System.brmz7yk5qh.dll": "System.dll",
124
124
  "netstandard.yvr3prsx0x.dll": "netstandard.dll",
125
125
  "System.Private.CoreLib.c1dbswx1b2.dll": "System.Private.CoreLib.dll",
126
- "MindExecution.Core.eyl4o4qxg8.dll": "MindExecution.Core.dll",
127
- "MindExecution.Kernel.hxyapaztgr.dll": "MindExecution.Kernel.dll",
128
- "MindExecution.Plugins.Admin.7kemovesa7.dll": "MindExecution.Plugins.Admin.dll",
129
- "MindExecution.Plugins.Business.t370in2g1b.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.oqhv1116kb.dll": "MindExecution.Plugins.Concept.dll",
131
- "MindExecution.Plugins.Directory.0v80a0hi9m.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.hn5sfx4z2l.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.c8sc0tlc6o.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.osv9nbh3ym.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.q1jwri2r1p.dll": "MindExecution.Web.dll",
126
+ "MindExecution.Core.cdplo34sb2.dll": "MindExecution.Core.dll",
127
+ "MindExecution.Kernel.7k773jan4j.dll": "MindExecution.Kernel.dll",
128
+ "MindExecution.Plugins.Admin.nr5ioavpvn.dll": "MindExecution.Plugins.Admin.dll",
129
+ "MindExecution.Plugins.Business.qq63kom73x.dll": "MindExecution.Plugins.Business.dll",
130
+ "MindExecution.Plugins.Concept.w4znwcrl6x.dll": "MindExecution.Plugins.Concept.dll",
131
+ "MindExecution.Plugins.Directory.u6p6pxpwxp.dll": "MindExecution.Plugins.Directory.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",
@@ -278,18 +278,18 @@
278
278
  "System.Xml.XDocument.sn51jas17n.dll": "sha256-GNI2kFgFmPTwzuzwUn8gxK+AzGLUWRJFdg9JzIbrybQ=",
279
279
  "System.brmz7yk5qh.dll": "sha256-CfM2miyj1KHApFmqMdLYWio3S/jrdON2pW9Xr2nTwlo=",
280
280
  "netstandard.yvr3prsx0x.dll": "sha256-EksNn8Luo4bOWqJ6X7dIe9qG9oOqwOVzjH2xYyMNi+E=",
281
- "MindExecution.Core.eyl4o4qxg8.dll": "sha256-JtBC04XSxkMOl0cZOA2ZWvyV88yYPyU0nkIbBCF9Jtc=",
282
- "MindExecution.Kernel.hxyapaztgr.dll": "sha256-q59U8/001rTsB1m+pkA4PkI9a9lvf5fijDWKXbN7uQ4=",
283
- "MindExecution.Plugins.Concept.oqhv1116kb.dll": "sha256-kEAfhSpocd0AdwocZnMU2+v0W2y6HJV7TRiOB6FbmDY=",
284
- "MindExecution.Plugins.PlanMaster.hn5sfx4z2l.dll": "sha256-Y2F3UYa2JgW9pCxL/YlCHo6gUohqHjQ4Z59cL/fdDBU=",
285
- "MindExecution.Shared.osv9nbh3ym.dll": "sha256-yKMhEPXMN1XCDYyFMEbhio9mvn7j2JkPK/upMkWEfOE=",
286
- "MindExecution.Web.q1jwri2r1p.dll": "sha256-bovOzmI58+BvUxaKQrGot/z4TEAg+VMmAkhiLkkfg1k="
281
+ "MindExecution.Core.cdplo34sb2.dll": "sha256-sLc0yEeGYQhFPxxNKLQsHllAhwjKhNn+pfgRY0bHbpo=",
282
+ "MindExecution.Kernel.7k773jan4j.dll": "sha256-B5H6hzB6g8sWLqXUoXifv8CbLqVt7g1lLkkinTBKF8E=",
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
- "MindExecution.Plugins.Admin.7kemovesa7.dll": "sha256-N8cYgaOErldVhLpnvnh4R9kf7f7fmt+16KhBWlnfG1I=",
290
- "MindExecution.Plugins.Business.t370in2g1b.dll": "sha256-B66uDzMZQfzN3fDnRSM1TvWh0uZjD6AVfUvJAyuGMdI=",
291
- "MindExecution.Plugins.Directory.0v80a0hi9m.dll": "sha256-JPn9UkEDTmb2CO4o/CnuG76R4RgQStGYZ6jH5pzblXk=",
292
- "MindExecution.Plugins.YouTube.c8sc0tlc6o.dll": "sha256-7P2anWE7Z/EXJfm/2CSQ1SWL1qPRtZG+gMwZEeqQaKM="
289
+ "MindExecution.Plugins.Admin.nr5ioavpvn.dll": "sha256-TWIfHGuEUUD1JtyuhfH4NM5JkvqAt8KcDVrWxvmwnVc=",
290
+ "MindExecution.Plugins.Business.qq63kom73x.dll": "sha256-4lgIcKclWQIjijrBJJFu84fgAH4MsDCN/myhG/K9OOU=",
291
+ "MindExecution.Plugins.Directory.u6p6pxpwxp.dll": "sha256-BjJ6/XNfp5wd/1RTdh99X9RR0I1GV6t4bp5A+RlAK9s=",
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-host-route-v556" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-host-route-v556" />
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-mdm-latest-frame-v559" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-latest-frame-v559" />
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-host-route-v556';
582
+ const scriptVersion = '20260616-mdm-latest-frame-v559';
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": "CHEnV4yd",
2
+ "version": "EUMTR2WK",
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-C/+w78To8XgmLIpNeOK3HtqkdKCQKklm792P0hSCaMk=",
89
+ "hash": "sha256-moU5KG/6LqgoCKviugaQ703R/oh1u8n08Xkoih7tDUg=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -410,44 +410,44 @@
410
410
  "url": "_framework/MimeMapping.og9ys58ylm.dll"
411
411
  },
412
412
  {
413
- "hash": "sha256-JtBC04XSxkMOl0cZOA2ZWvyV88yYPyU0nkIbBCF9Jtc=",
414
- "url": "_framework/MindExecution.Core.eyl4o4qxg8.dll"
413
+ "hash": "sha256-sLc0yEeGYQhFPxxNKLQsHllAhwjKhNn+pfgRY0bHbpo=",
414
+ "url": "_framework/MindExecution.Core.cdplo34sb2.dll"
415
415
  },
416
416
  {
417
- "hash": "sha256-q59U8/001rTsB1m+pkA4PkI9a9lvf5fijDWKXbN7uQ4=",
418
- "url": "_framework/MindExecution.Kernel.hxyapaztgr.dll"
417
+ "hash": "sha256-B5H6hzB6g8sWLqXUoXifv8CbLqVt7g1lLkkinTBKF8E=",
418
+ "url": "_framework/MindExecution.Kernel.7k773jan4j.dll"
419
419
  },
420
420
  {
421
- "hash": "sha256-N8cYgaOErldVhLpnvnh4R9kf7f7fmt+16KhBWlnfG1I=",
422
- "url": "_framework/MindExecution.Plugins.Admin.7kemovesa7.dll"
421
+ "hash": "sha256-TWIfHGuEUUD1JtyuhfH4NM5JkvqAt8KcDVrWxvmwnVc=",
422
+ "url": "_framework/MindExecution.Plugins.Admin.nr5ioavpvn.dll"
423
423
  },
424
424
  {
425
- "hash": "sha256-B66uDzMZQfzN3fDnRSM1TvWh0uZjD6AVfUvJAyuGMdI=",
426
- "url": "_framework/MindExecution.Plugins.Business.t370in2g1b.dll"
425
+ "hash": "sha256-4lgIcKclWQIjijrBJJFu84fgAH4MsDCN/myhG/K9OOU=",
426
+ "url": "_framework/MindExecution.Plugins.Business.qq63kom73x.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-kEAfhSpocd0AdwocZnMU2+v0W2y6HJV7TRiOB6FbmDY=",
430
- "url": "_framework/MindExecution.Plugins.Concept.oqhv1116kb.dll"
429
+ "hash": "sha256-oNGAmbTjkFdVfZeYbc4jYWtyQa3c8Tle5LIXoFye4yo=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.w4znwcrl6x.dll"
431
431
  },
432
432
  {
433
- "hash": "sha256-JPn9UkEDTmb2CO4o/CnuG76R4RgQStGYZ6jH5pzblXk=",
434
- "url": "_framework/MindExecution.Plugins.Directory.0v80a0hi9m.dll"
433
+ "hash": "sha256-BjJ6/XNfp5wd/1RTdh99X9RR0I1GV6t4bp5A+RlAK9s=",
434
+ "url": "_framework/MindExecution.Plugins.Directory.u6p6pxpwxp.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-Y2F3UYa2JgW9pCxL/YlCHo6gUohqHjQ4Z59cL/fdDBU=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.hn5sfx4z2l.dll"
437
+ "hash": "sha256-gPWCEzDE7cJLOFTJOhja3JXJzkHFWd4dzfRdsdTxwps=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.0w4vr40lnz.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-7P2anWE7Z/EXJfm/2CSQ1SWL1qPRtZG+gMwZEeqQaKM=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.c8sc0tlc6o.dll"
441
+ "hash": "sha256-gNZE7M6cqiF9aEJYdZtUzt5bYPkh5Be7LKuyh286u2Q=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.sdl5n9uv68.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-yKMhEPXMN1XCDYyFMEbhio9mvn7j2JkPK/upMkWEfOE=",
446
- "url": "_framework/MindExecution.Shared.osv9nbh3ym.dll"
445
+ "hash": "sha256-veGmVlpQw2b1zDaUWReHRSLbiZ2IloG79uo1GLlduEo=",
446
+ "url": "_framework/MindExecution.Shared.fprczldv6z.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-bovOzmI58+BvUxaKQrGot/z4TEAg+VMmAkhiLkkfg1k=",
450
- "url": "_framework/MindExecution.Web.q1jwri2r1p.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-Ep+Ov81XThtcE2HBQd0whA+KI3/0iJDNLBCtGNYC4TI=",
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-CMNyRVOFnluZqvSs3T+1/tCH4AvEwjV6UNWBUvplyuA=",
837
+ "hash": "sha256-FjUaVgGOa4fyf3cF3WIvk0qYkTDuxmB0aE3BTcR7aYU=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: CHEnV4yd */
1
+ /* Manifest version: EUMTR2WK */
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