@mindexec/cli 0.2.45 → 0.2.46

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 (21) hide show
  1. package/package.json +1 -1
  2. package/remote-hub.js +119 -12
  3. package/scripts/remote-http-smoke.mjs +54 -0
  4. package/scripts/remote-hub-smoke.mjs +42 -0
  5. package/server.js +115 -23
  6. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +31 -14
  7. package/wwwroot/_framework/MindExecution.Core.xg9yy9l5dz.dll +0 -0
  8. package/wwwroot/_framework/{MindExecution.Kernel.z56elxihok.dll → MindExecution.Kernel.erg96341xf.dll} +0 -0
  9. package/wwwroot/_framework/{MindExecution.Plugins.Admin.p5cs4ap87v.dll → MindExecution.Plugins.Admin.11j9vpdm9u.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Business.s35og5uz44.dll → MindExecution.Plugins.Business.oyskf08knn.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Concept.j63qelz8rk.dll → MindExecution.Plugins.Concept.keia4ox68c.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Directory.y74f55e8x3.dll → MindExecution.Plugins.Directory.7pus9p63ym.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.8djc50fh8g.dll → MindExecution.Plugins.PlanMaster.wr3pupzfyo.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.h6y03asuzq.dll → MindExecution.Plugins.YouTube.kpfew1eggc.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Shared.z07jle70qs.dll → MindExecution.Shared.kzibxbai3y.dll} +0 -0
  16. package/wwwroot/_framework/MindExecution.Web.6fjnkr9ty4.dll +0 -0
  17. package/wwwroot/_framework/blazor.boot.json +21 -21
  18. package/wwwroot/service-worker-assets.js +23 -23
  19. package/wwwroot/service-worker.js +1 -1
  20. package/wwwroot/_framework/MindExecution.Core.6rfnfdndxq.dll +0 -0
  21. package/wwwroot/_framework/MindExecution.Web.gq00wm14q3.dll +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.45",
3
+ "version": "0.2.46",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/remote-hub.js CHANGED
@@ -19,6 +19,7 @@ const REMOTE_PROTOCOL_VERSION = 1;
19
19
  const MAX_SYNTHETIC_DEVICES = 1000;
20
20
  const DEFAULT_HOST_TARGET_LEASE_MS = 30000;
21
21
  const SYNTHETIC_FRAME_DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
22
+ const SYNTHETIC_FRAME_PAYLOAD = Buffer.from(SYNTHETIC_FRAME_DATA_URL.split(',')[1], 'base64');
22
23
 
23
24
  function isEnabledValue(value, fallback = true) {
24
25
  if (value === undefined || value === null || value === '') {
@@ -122,7 +123,52 @@ function parseJsonLine(line) {
122
123
  return JSON.parse(text);
123
124
  }
124
125
 
125
- function serializeDevice(device) {
126
+ function createFrameAccessToken() {
127
+ return crypto.randomBytes(18).toString('base64url');
128
+ }
129
+
130
+ function buildRemoteFramePath(deviceId, frameKind, frame) {
131
+ const token = safeString(frame?.accessToken, 128);
132
+ const frameSeq = Number(frame?.frameSeq);
133
+ if (!deviceId || !token || !Number.isFinite(frameSeq)) {
134
+ return '';
135
+ }
136
+
137
+ const endpoint = frameKind === 'thumbnail' ? 'thumbnail' : 'live/frame';
138
+ const params = new URLSearchParams({
139
+ format: 'binary',
140
+ seq: String(Math.floor(frameSeq)),
141
+ token
142
+ });
143
+ return `/api/remote/devices/${encodeURIComponent(deviceId)}/${endpoint}?${params.toString()}`;
144
+ }
145
+
146
+ function serializeRemoteFrame(frame, deviceId, frameKind, options = {}) {
147
+ if (!frame) {
148
+ return null;
149
+ }
150
+
151
+ const {
152
+ payload,
153
+ accessToken,
154
+ dataUrl,
155
+ ...publicFrame
156
+ } = frame;
157
+
158
+ const framePath = buildRemoteFramePath(deviceId, frameKind, frame);
159
+ const serialized = {
160
+ ...publicFrame,
161
+ framePath,
162
+ frameUrl: framePath
163
+ };
164
+ if (options.includeDataUrl !== false) {
165
+ serialized.dataUrl = dataUrl;
166
+ }
167
+
168
+ return serialized;
169
+ }
170
+
171
+ function serializeDevice(device, options = {}) {
126
172
  if (!device) {
127
173
  return null;
128
174
  }
@@ -145,8 +191,8 @@ function serializeDevice(device) {
145
191
  lastDisconnectReason: device.lastDisconnectReason,
146
192
  remoteAddress: device.remoteAddress,
147
193
  remotePort: device.remotePort,
148
- latestThumbnail: device.latestThumbnail ? { ...device.latestThumbnail } : null,
149
- latestLiveFrame: device.latestLiveFrame ? { ...device.latestLiveFrame } : null,
194
+ latestThumbnail: serializeRemoteFrame(device.latestThumbnail, device.deviceId, 'thumbnail', options),
195
+ latestLiveFrame: serializeRemoteFrame(device.latestLiveFrame, device.deviceId, 'live', options),
150
196
  activeLiveStream: device.activeLiveStream ? { ...device.activeLiveStream } : null,
151
197
  latestTask: device.latestTask ? { ...device.latestTask } : null,
152
198
  synthetic: device.synthetic === true,
@@ -376,9 +422,12 @@ export function createRemoteHub(options = {}) {
376
422
  };
377
423
  }
378
424
 
379
- function listDevices() {
425
+ function listDevices(options = {}) {
426
+ const serializeOptions = {
427
+ includeDataUrl: options.includeDataUrl !== false
428
+ };
380
429
  return [...devices.values()]
381
- .map(serializeDevice)
430
+ .map(device => serializeDevice(device, serializeOptions))
382
431
  .filter(Boolean)
383
432
  .sort((a, b) => {
384
433
  const nameCompare = String(a.deviceName || '').localeCompare(String(b.deviceName || ''));
@@ -427,7 +476,10 @@ export function createRemoteHub(options = {}) {
427
476
  capturedAt: now,
428
477
  receivedAt: now,
429
478
  byteLength: 68,
430
- dataUrl: SYNTHETIC_FRAME_DATA_URL
479
+ transport: 'synthetic',
480
+ dataUrl: SYNTHETIC_FRAME_DATA_URL,
481
+ payload: SYNTHETIC_FRAME_PAYLOAD,
482
+ accessToken: createFrameAccessToken()
431
483
  };
432
484
  }
433
485
 
@@ -1095,6 +1147,19 @@ export function createRemoteHub(options = {}) {
1095
1147
  : `data:${mimeType};base64,${frameData}`;
1096
1148
  }
1097
1149
 
1150
+ function buildFramePayloadBuffer(framePayload, frameData) {
1151
+ if (Buffer.isBuffer(framePayload)) {
1152
+ return Buffer.from(framePayload);
1153
+ }
1154
+
1155
+ const raw = String(frameData || '');
1156
+ const commaIndex = raw.indexOf(',');
1157
+ const base64 = raw.startsWith('data:') && commaIndex >= 0
1158
+ ? raw.slice(commaIndex + 1)
1159
+ : raw;
1160
+ return Buffer.from(base64, 'base64');
1161
+ }
1162
+
1098
1163
  function applyThumbnailFrame(device, message, framePayload, transport = 'json-base64') {
1099
1164
  const frameData = Buffer.isBuffer(framePayload)
1100
1165
  ? ''
@@ -1115,6 +1180,7 @@ export function createRemoteHub(options = {}) {
1115
1180
 
1116
1181
  const mimeType = safeString(message.mimeType || message.format || 'image/jpeg', 80) || 'image/jpeg';
1117
1182
  const capturedAt = safeString(message.capturedAt, 80) || device.lastSeenAt;
1183
+ const payload = buildFramePayloadBuffer(framePayload, frameData);
1118
1184
  device.latestThumbnail = {
1119
1185
  streamId: safeString(message.streamId, 128) || 'thumbnail',
1120
1186
  frameSeq,
@@ -1127,7 +1193,9 @@ export function createRemoteHub(options = {}) {
1127
1193
  receivedAt: device.lastSeenAt,
1128
1194
  byteLength,
1129
1195
  transport,
1130
- dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType)
1196
+ dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType),
1197
+ payload,
1198
+ accessToken: createFrameAccessToken()
1131
1199
  };
1132
1200
  device.counters.thumbnailFramesReceived += 1;
1133
1201
  emitRemoteEvent('RemoteFrameReceived', device, {
@@ -1173,6 +1241,7 @@ export function createRemoteHub(options = {}) {
1173
1241
 
1174
1242
  const mimeType = safeString(message.mimeType || message.format || 'image/jpeg', 80) || 'image/jpeg';
1175
1243
  const capturedAt = safeString(message.capturedAt, 80) || device.lastSeenAt;
1244
+ const payload = buildFramePayloadBuffer(framePayload, frameData);
1176
1245
  device.latestLiveFrame = {
1177
1246
  streamId,
1178
1247
  frameSeq,
@@ -1187,7 +1256,9 @@ export function createRemoteHub(options = {}) {
1187
1256
  receivedAt: device.lastSeenAt,
1188
1257
  byteLength,
1189
1258
  transport,
1190
- dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType)
1259
+ dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType),
1260
+ payload,
1261
+ accessToken: createFrameAccessToken()
1191
1262
  };
1192
1263
  device.activeLiveStream.lastFrameAt = device.lastSeenAt;
1193
1264
  device.activeLiveStream.lastFrameSeq = frameSeq;
@@ -1904,14 +1975,49 @@ export function createRemoteHub(options = {}) {
1904
1975
  return { ok: true, commandId, streamId };
1905
1976
  }
1906
1977
 
1907
- function getDeviceLiveFrame(deviceId) {
1978
+ function getDeviceLiveFrame(deviceId, options = {}) {
1908
1979
  const device = devices.get(String(deviceId || ''));
1909
- return device?.latestLiveFrame ? { ...device.latestLiveFrame } : null;
1980
+ return serializeRemoteFrame(device?.latestLiveFrame, device?.deviceId, 'live', {
1981
+ includeDataUrl: options.includeDataUrl !== false
1982
+ });
1983
+ }
1984
+
1985
+ function getDeviceThumbnail(deviceId, options = {}) {
1986
+ const device = devices.get(String(deviceId || ''));
1987
+ return serializeRemoteFrame(device?.latestThumbnail, device?.deviceId, 'thumbnail', {
1988
+ includeDataUrl: options.includeDataUrl !== false
1989
+ });
1910
1990
  }
1911
1991
 
1912
- function getDeviceThumbnail(deviceId) {
1992
+ function getFramePayload(deviceId, frameKind, options = {}) {
1913
1993
  const device = devices.get(String(deviceId || ''));
1914
- return device?.latestThumbnail ? { ...device.latestThumbnail } : null;
1994
+ const frame = frameKind === 'thumbnail'
1995
+ ? device?.latestThumbnail
1996
+ : device?.latestLiveFrame;
1997
+ if (!frame || !Buffer.isBuffer(frame.payload)) {
1998
+ return null;
1999
+ }
2000
+
2001
+ const requestedSeq = Number(options.frameSeq);
2002
+ if (Number.isFinite(requestedSeq) && Math.floor(requestedSeq) !== frame.frameSeq) {
2003
+ return null;
2004
+ }
2005
+
2006
+ const token = safeString(options.token, 128);
2007
+ if (token && !timingSafeStringEqual(token, frame.accessToken)) {
2008
+ return null;
2009
+ }
2010
+
2011
+ if (options.requireToken === true && !token) {
2012
+ return null;
2013
+ }
2014
+
2015
+ return {
2016
+ frame: serializeRemoteFrame(frame, device.deviceId, frameKind, { includeDataUrl: false }),
2017
+ payload: frame.payload,
2018
+ mimeType: safeString(frame.mimeType || frame.format || 'application/octet-stream', 120) || 'application/octet-stream',
2019
+ byteLength: frame.payload.length
2020
+ };
1915
2021
  }
1916
2022
 
1917
2023
  return {
@@ -1931,6 +2037,7 @@ export function createRemoteHub(options = {}) {
1931
2037
  stopLiveStream,
1932
2038
  getDeviceLiveFrame,
1933
2039
  getDeviceThumbnail,
2040
+ getFramePayload,
1934
2041
  seedSyntheticFleet,
1935
2042
  clearSyntheticFleet,
1936
2043
  getPairToken: () => pairToken
@@ -54,6 +54,26 @@ async function fetchJson(url, options = {}) {
54
54
  };
55
55
  }
56
56
 
57
+ async function fetchBinary(url, options = {}) {
58
+ const response = await fetch(url, {
59
+ ...options,
60
+ headers: {
61
+ Accept: 'image/png,image/jpeg,image/webp,image/*',
62
+ ...(options.token ? { 'X-Bridge-Token': options.token } : {}),
63
+ ...(options.headers || {})
64
+ }
65
+ });
66
+
67
+ return {
68
+ status: response.status,
69
+ ok: response.ok,
70
+ contentType: response.headers.get('content-type') || '',
71
+ frameSeq: response.headers.get('x-mindexec-remote-frame-seq') || '',
72
+ streamId: response.headers.get('x-mindexec-remote-stream-id') || '',
73
+ payload: Buffer.from(await response.arrayBuffer())
74
+ };
75
+ }
76
+
57
77
  async function waitForBridge(baseUrl, getFailureDetails, bridgeToken = BRIDGE_TOKEN) {
58
78
  const startedAt = Date.now();
59
79
  while (Date.now() - startedAt < 30000) {
@@ -339,6 +359,23 @@ async function runSyntheticEnabledSmoke() {
339
359
  assert.equal(thumbnail.ok, true, JSON.stringify(thumbnail.payload));
340
360
  assert.equal(thumbnail.payload?.thumbnail?.streamId, 'http-smoke-thumb');
341
361
  assert.ok(String(thumbnail.payload?.thumbnail?.dataUrl || '').startsWith('data:image/png;base64,'));
362
+ assert.ok(String(thumbnail.payload?.thumbnail?.framePath || '').includes(`/api/remote/devices/${encodeURIComponent(thumbnailTarget.deviceId)}/thumbnail?`));
363
+ assert.equal(thumbnail.payload?.thumbnail?.frameUrl, thumbnail.payload?.thumbnail?.framePath);
364
+
365
+ const thumbnailBinary = await fetchBinary(`${baseUrl}${thumbnail.payload.thumbnail.framePath}`);
366
+ assert.equal(thumbnailBinary.ok, true, `${thumbnailBinary.status} ${thumbnailBinary.contentType}`);
367
+ assert.equal(thumbnailBinary.contentType, 'image/png');
368
+ assert.equal(thumbnailBinary.streamId, 'http-smoke-thumb');
369
+ assert.ok(thumbnailBinary.payload.length > 0);
370
+
371
+ const badThumbnailBinary = await fetchBinary(`${baseUrl}${thumbnail.payload.thumbnail.framePath.replace(/token=[^&]+/, 'token=wrong-token')}`);
372
+ assert.equal(badThumbnailBinary.status, 401);
373
+
374
+ const devicesUrlOnlyResult = await fetchJson(`${baseUrl}/api/remote/devices?frameData=url`, { token: BRIDGE_TOKEN });
375
+ assert.equal(devicesUrlOnlyResult.ok, true, JSON.stringify(devicesUrlOnlyResult.payload));
376
+ const urlOnlyThumbnailDevice = devicesUrlOnlyResult.payload?.devices?.find(device => device.deviceId === thumbnailTarget.deviceId);
377
+ assert.ok(urlOnlyThumbnailDevice?.latestThumbnail?.framePath);
378
+ assert.ok(!('dataUrl' in urlOnlyThumbnailDevice.latestThumbnail));
342
379
 
343
380
  const liveTarget = devicesResult.payload.devices.find(device =>
344
381
  device.connected && device.capabilities?.liveStream);
@@ -364,6 +401,23 @@ async function runSyntheticEnabledSmoke() {
364
401
  assert.equal(liveFrame.payload?.frame?.streamId, 'http-smoke-live');
365
402
  assert.equal(liveFrame.payload?.frame?.mode, 'remote-fast');
366
403
  assert.equal(liveFrame.payload?.frame?.fps, 20);
404
+ assert.ok(String(liveFrame.payload?.frame?.dataUrl || '').startsWith('data:image/png;base64,'));
405
+ assert.ok(String(liveFrame.payload?.frame?.framePath || '').includes(`/api/remote/devices/${encodeURIComponent(liveTarget.deviceId)}/live/frame?`));
406
+
407
+ const liveBinary = await fetchBinary(`${baseUrl}${liveFrame.payload.frame.framePath}`);
408
+ assert.equal(liveBinary.ok, true, `${liveBinary.status} ${liveBinary.contentType}`);
409
+ assert.equal(liveBinary.contentType, 'image/png');
410
+ assert.equal(liveBinary.streamId, 'http-smoke-live');
411
+ assert.ok(liveBinary.payload.length > 0);
412
+
413
+ const liveBinaryWithBridgeToken = await fetchBinary(
414
+ `${baseUrl}/api/remote/devices/${encodeURIComponent(liveTarget.deviceId)}/live/frame?format=binary`,
415
+ { token: BRIDGE_TOKEN });
416
+ assert.equal(liveBinaryWithBridgeToken.ok, true, `${liveBinaryWithBridgeToken.status} ${liveBinaryWithBridgeToken.contentType}`);
417
+ assert.equal(liveBinaryWithBridgeToken.contentType, 'image/png');
418
+
419
+ const badLiveSeq = await fetchBinary(`${baseUrl}${liveFrame.payload.frame.framePath.replace(/seq=\d+/, 'seq=999999')}`);
420
+ assert.equal(badLiveSeq.status, 401);
367
421
 
368
422
  const liveStop = await fetchJson(`${baseUrl}/api/remote/devices/${encodeURIComponent(liveTarget.deviceId)}/live/stop`, {
369
423
  method: 'POST',
@@ -152,6 +152,27 @@ try {
152
152
  assert.equal(binaryThumbnailDevice.latestThumbnail.transport, 'binary');
153
153
  assert.equal(binaryThumbnailDevice.latestThumbnail.byteLength, smokePngFrame.length);
154
154
  assert.equal(binaryThumbnailDevice.counters.thumbnailFramesReceived, 2);
155
+ const serializedBinaryThumbnail = hub.getDeviceThumbnail('smoke-device', { includeDataUrl: false });
156
+ assert.equal(serializedBinaryThumbnail.streamId, 'smoke-thumb-binary');
157
+ assert.equal(serializedBinaryThumbnail.frameSeq, 4);
158
+ assert.ok(serializedBinaryThumbnail.framePath.includes('/api/remote/devices/smoke-device/thumbnail?'));
159
+ assert.ok(!('dataUrl' in serializedBinaryThumbnail));
160
+ assert.ok(!('payload' in serializedBinaryThumbnail));
161
+ assert.ok(!('accessToken' in serializedBinaryThumbnail));
162
+ const serializedThumbnailUrl = new URL(serializedBinaryThumbnail.framePath, 'http://127.0.0.1');
163
+ const thumbnailPayload = hub.getFramePayload('smoke-device', 'thumbnail', {
164
+ token: serializedThumbnailUrl.searchParams.get('token'),
165
+ frameSeq: serializedThumbnailUrl.searchParams.get('seq'),
166
+ requireToken: true
167
+ });
168
+ assert.equal(thumbnailPayload?.mimeType, 'image/png');
169
+ assert.equal(thumbnailPayload?.byteLength, smokePngFrame.length);
170
+ assert.equal(Buffer.compare(thumbnailPayload.payload, smokePngFrame), 0);
171
+ assert.equal(hub.getFramePayload('smoke-device', 'thumbnail', {
172
+ token: 'wrong-token',
173
+ frameSeq: 4,
174
+ requireToken: true
175
+ }), null);
155
176
 
156
177
  const liveCommand = hub.startLiveStream('smoke-device', {
157
178
  streamId: 'smoke-live',
@@ -204,6 +225,27 @@ try {
204
225
  assert.equal(binaryLiveDevice.latestLiveFrame.transport, 'binary');
205
226
  assert.equal(binaryLiveDevice.latestLiveFrame.byteLength, smokePngFrame.length);
206
227
  assert.equal(binaryLiveDevice.counters.liveFramesReceived, 2);
228
+ const serializedBinaryLiveFrame = hub.getDeviceLiveFrame('smoke-device', { includeDataUrl: false });
229
+ assert.equal(serializedBinaryLiveFrame.streamId, 'smoke-live');
230
+ assert.equal(serializedBinaryLiveFrame.frameSeq, 5);
231
+ assert.ok(serializedBinaryLiveFrame.framePath.includes('/api/remote/devices/smoke-device/live/frame?'));
232
+ assert.ok(!('dataUrl' in serializedBinaryLiveFrame));
233
+ assert.ok(!('payload' in serializedBinaryLiveFrame));
234
+ assert.ok(!('accessToken' in serializedBinaryLiveFrame));
235
+ const serializedLiveUrl = new URL(serializedBinaryLiveFrame.framePath, 'http://127.0.0.1');
236
+ const livePayload = hub.getFramePayload('smoke-device', 'live', {
237
+ token: serializedLiveUrl.searchParams.get('token'),
238
+ frameSeq: serializedLiveUrl.searchParams.get('seq'),
239
+ requireToken: true
240
+ });
241
+ assert.equal(livePayload?.mimeType, 'image/png');
242
+ assert.equal(livePayload?.byteLength, smokePngFrame.length);
243
+ assert.equal(Buffer.compare(livePayload.payload, smokePngFrame), 0);
244
+ assert.equal(hub.getFramePayload('smoke-device', 'live', {
245
+ token: serializedLiveUrl.searchParams.get('token'),
246
+ frameSeq: 4,
247
+ requireToken: true
248
+ }), null);
207
249
 
208
250
  const staleFrameBefore = liveDevice.counters.liveFramesDropped;
209
251
  writeJsonLine(socket, {
package/server.js CHANGED
@@ -1756,20 +1756,69 @@ const PROTECTED_BRIDGE_ROUTES = [
1756
1756
  { method: 'POST', exact: '/api/tool/trace' }
1757
1757
  ];
1758
1758
 
1759
- function getBridgeTokenFromRequest(req) {
1760
- const headerToken = String(req.get(bridgeTokenHeader) || '').trim();
1761
- if (headerToken) {
1762
- return headerToken;
1763
- }
1759
+ function getBridgeTokenFromRequest(req) {
1760
+ const headerToken = String(req.get(bridgeTokenHeader) || '').trim();
1761
+ if (headerToken) {
1762
+ return headerToken;
1763
+ }
1764
1764
 
1765
1765
  const authorization = String(req.get('authorization') || '').trim();
1766
- const bearerMatch = authorization.match(/^Bearer\s+(.+)$/i);
1767
- return bearerMatch ? bearerMatch[1].trim() : '';
1768
- }
1769
-
1770
- function isProtectedBridgeRoute(method, requestPath) {
1771
- const normalizedMethod = String(method || '').toUpperCase();
1772
- const normalizedPath = String(requestPath || '').toLowerCase();
1766
+ const bearerMatch = authorization.match(/^Bearer\s+(.+)$/i);
1767
+ return bearerMatch ? bearerMatch[1].trim() : '';
1768
+ }
1769
+
1770
+ function isBridgeTokenAuthorized(req) {
1771
+ return !bridgeAuthRequired || getBridgeTokenFromRequest(req) === bridgeToken;
1772
+ }
1773
+
1774
+ function wantsRemoteFrameBinaryRequest(req) {
1775
+ const format = String(req.query?.format || req.query?.frameData || '').trim().toLowerCase();
1776
+ if (format === 'binary' || format === 'image' || format === 'raw') {
1777
+ return true;
1778
+ }
1779
+
1780
+ const accept = String(req.get('accept') || '').toLowerCase();
1781
+ return /\bimage\//.test(accept) && !accept.includes('application/json');
1782
+ }
1783
+
1784
+ function getRemoteFrameTokenRequest(req) {
1785
+ if (String(req.method || '').toUpperCase() !== 'GET' || !wantsRemoteFrameBinaryRequest(req)) {
1786
+ return null;
1787
+ }
1788
+
1789
+ const match = String(req.path || '').match(/^\/api\/remote\/devices\/([^/]+)\/(thumbnail|live\/frame)$/i);
1790
+ if (!match) {
1791
+ return null;
1792
+ }
1793
+
1794
+ try {
1795
+ return {
1796
+ deviceId: decodeURIComponent(match[1]),
1797
+ frameKind: match[2].toLowerCase() === 'thumbnail' ? 'thumbnail' : 'live',
1798
+ token: String(req.query?.token || '').trim(),
1799
+ frameSeq: req.query?.seq
1800
+ };
1801
+ } catch {
1802
+ return null;
1803
+ }
1804
+ }
1805
+
1806
+ function isAuthorizedRemoteFrameTokenRequest(req) {
1807
+ const frameRequest = getRemoteFrameTokenRequest(req);
1808
+ if (!frameRequest?.token) {
1809
+ return false;
1810
+ }
1811
+
1812
+ return !!remoteHub.getFramePayload(frameRequest.deviceId, frameRequest.frameKind, {
1813
+ token: frameRequest.token,
1814
+ frameSeq: frameRequest.frameSeq,
1815
+ requireToken: true
1816
+ });
1817
+ }
1818
+
1819
+ function isProtectedBridgeRoute(method, requestPath) {
1820
+ const normalizedMethod = String(method || '').toUpperCase();
1821
+ const normalizedPath = String(requestPath || '').toLowerCase();
1773
1822
 
1774
1823
  return PROTECTED_BRIDGE_ROUTES.some((rule) => {
1775
1824
  if (rule.method && rule.method !== normalizedMethod) {
@@ -1786,13 +1835,13 @@ function isProtectedBridgeRoute(method, requestPath) {
1786
1835
 
1787
1836
  return false;
1788
1837
  });
1789
- }
1790
-
1791
- function requireBridgeToken(req, res, next) {
1792
- if (!bridgeAuthRequired || getBridgeTokenFromRequest(req) === bridgeToken) {
1793
- next();
1794
- return;
1795
- }
1838
+ }
1839
+
1840
+ function requireBridgeToken(req, res, next) {
1841
+ if (isBridgeTokenAuthorized(req) || isAuthorizedRemoteFrameTokenRequest(req)) {
1842
+ next();
1843
+ return;
1844
+ }
1796
1845
 
1797
1846
  res.status(401).json({
1798
1847
  error: 'Bridge token required',
@@ -6967,7 +7016,8 @@ app.get('/api/remote/status', (req, res) => {
6967
7016
 
6968
7017
  app.get('/api/remote/devices', (req, res) => {
6969
7018
  res.setHeader('Cache-Control', 'no-store');
6970
- const devices = remoteHub.listDevices();
7019
+ const includeDataUrl = !/^(0|false|no|url|metadata)$/i.test(String(req.query?.includeDataUrl ?? req.query?.frameData ?? ''));
7020
+ const devices = remoteHub.listDevices({ includeDataUrl });
6971
7021
  res.json({
6972
7022
  total: devices.length,
6973
7023
  pagination: 'none',
@@ -7058,7 +7108,7 @@ app.post('/api/remote/tasks', (req, res) => {
7058
7108
  : [];
7059
7109
  const allConnected = req.body?.allConnected !== false;
7060
7110
  const approvalLevel = req.body?.approvalLevel === 'ai-assist' ? 'ai-assist' : 'task-only';
7061
- const devices = remoteHub.listDevices();
7111
+ const devices = remoteHub.listDevices({ includeDataUrl: false });
7062
7112
  const targetIds = requestedDeviceIds.length > 0
7063
7113
  ? requestedDeviceIds
7064
7114
  : (allConnected
@@ -7098,9 +7148,44 @@ function isRemoteCapabilityEnabled(device, key) {
7098
7148
  return /^(1|true|yes|on)$/i.test(String(value || '').trim());
7099
7149
  }
7100
7150
 
7151
+ function includeRemoteFrameDataUrl(req) {
7152
+ return !/^(0|false|no|url|metadata)$/i.test(String(req.query?.includeDataUrl ?? req.query?.frameData ?? ''));
7153
+ }
7154
+
7155
+ function sendRemoteFrameBinary(req, res, frameKind) {
7156
+ const bridgeAuthorized = isBridgeTokenAuthorized(req);
7157
+ const payload = remoteHub.getFramePayload(req.params.deviceId, frameKind, {
7158
+ token: bridgeAuthorized ? '' : req.query?.token,
7159
+ frameSeq: req.query?.seq,
7160
+ requireToken: !bridgeAuthorized
7161
+ });
7162
+ if (!payload) {
7163
+ res.status(bridgeAuthorized ? 404 : 403).json({
7164
+ ok: false,
7165
+ error: bridgeAuthorized ? 'frame-payload-not-available' : 'invalid-frame-token'
7166
+ });
7167
+ return true;
7168
+ }
7169
+
7170
+ res.setHeader('Cache-Control', 'no-store');
7171
+ res.setHeader('Content-Type', payload.mimeType);
7172
+ res.setHeader('Content-Length', String(payload.byteLength));
7173
+ res.setHeader('X-MindExec-Remote-Frame-Seq', String(payload.frame?.frameSeq || ''));
7174
+ res.setHeader('X-MindExec-Remote-Stream-Id', String(payload.frame?.streamId || ''));
7175
+ res.send(payload.payload);
7176
+ return true;
7177
+ }
7178
+
7101
7179
  app.get('/api/remote/devices/:deviceId/thumbnail', (req, res) => {
7102
7180
  res.setHeader('Cache-Control', 'no-store');
7103
- const thumbnail = remoteHub.getDeviceThumbnail(req.params.deviceId);
7181
+ if (wantsRemoteFrameBinaryRequest(req)) {
7182
+ sendRemoteFrameBinary(req, res, 'thumbnail');
7183
+ return;
7184
+ }
7185
+
7186
+ const thumbnail = remoteHub.getDeviceThumbnail(req.params.deviceId, {
7187
+ includeDataUrl: includeRemoteFrameDataUrl(req)
7188
+ });
7104
7189
  if (!thumbnail) {
7105
7190
  res.status(404).json({ ok: false, error: 'thumbnail-not-available' });
7106
7191
  return;
@@ -7120,7 +7205,14 @@ app.post('/api/remote/devices/:deviceId/thumbnail/request', (req, res) => {
7120
7205
 
7121
7206
  app.get('/api/remote/devices/:deviceId/live/frame', (req, res) => {
7122
7207
  res.setHeader('Cache-Control', 'no-store');
7123
- const frame = remoteHub.getDeviceLiveFrame(req.params.deviceId);
7208
+ if (wantsRemoteFrameBinaryRequest(req)) {
7209
+ sendRemoteFrameBinary(req, res, 'live');
7210
+ return;
7211
+ }
7212
+
7213
+ const frame = remoteHub.getDeviceLiveFrame(req.params.deviceId, {
7214
+ includeDataUrl: includeRemoteFrameDataUrl(req)
7215
+ });
7124
7216
  if (!frame) {
7125
7217
  res.status(404).json({ ok: false, error: 'live-frame-not-available' });
7126
7218
  return;
@@ -12957,13 +12957,30 @@
12957
12957
  }
12958
12958
 
12959
12959
  function hasRemoteFleetThumbnail(device) {
12960
- const dataUrl = String(getRemoteFleetDeviceField(device, 'thumbnailDataUrl', 'ThumbnailDataUrl', ''));
12961
- return /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(dataUrl);
12960
+ return isRemoteFleetFrameSource(getRemoteFleetFrameSource(device, 'thumbnail'));
12962
12961
  }
12963
12962
 
12964
12963
  function hasRemoteFleetLiveFrame(device) {
12965
- const dataUrl = String(getRemoteFleetDeviceField(device, 'liveFrameDataUrl', 'LiveFrameDataUrl', ''));
12966
- return /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(dataUrl);
12964
+ return isRemoteFleetFrameSource(getRemoteFleetFrameSource(device, 'live'));
12965
+ }
12966
+
12967
+ function isRemoteFleetFrameSource(value) {
12968
+ const source = String(value || '').trim();
12969
+ return /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(source)
12970
+ || /^https?:\/\/(?:127(?:\.\d{1,3}){3}|localhost|\[::1\])(?::\d+)?\/api\/remote\//i.test(source)
12971
+ || /^\/api\/remote\//i.test(source);
12972
+ }
12973
+
12974
+ function getRemoteFleetFrameSource(device, frameKind) {
12975
+ if (frameKind === 'live') {
12976
+ return String(
12977
+ getRemoteFleetDeviceField(device, 'liveFrameUrl', 'LiveFrameUrl', '')
12978
+ || getRemoteFleetDeviceField(device, 'liveFrameDataUrl', 'LiveFrameDataUrl', ''));
12979
+ }
12980
+
12981
+ return String(
12982
+ getRemoteFleetDeviceField(device, 'thumbnailFrameUrl', 'ThumbnailFrameUrl', '')
12983
+ || getRemoteFleetDeviceField(device, 'thumbnailDataUrl', 'ThumbnailDataUrl', ''));
12967
12984
  }
12968
12985
 
12969
12986
  function isRemoteFleetLiveActive(device) {
@@ -13245,9 +13262,9 @@
13245
13262
  const liveStreamId = String(getRemoteFleetDeviceField(device, 'liveStreamId', 'LiveStreamId', ''));
13246
13263
  const hasLiveFrame = hasRemoteFleetLiveFrame(device);
13247
13264
  const hasThumbnail = hasRemoteFleetThumbnail(device);
13248
- const previewDataUrl = hasLiveFrame
13249
- ? String(getRemoteFleetDeviceField(device, 'liveFrameDataUrl', 'LiveFrameDataUrl', ''))
13250
- : String(getRemoteFleetDeviceField(device, 'thumbnailDataUrl', 'ThumbnailDataUrl', ''));
13265
+ const previewSource = hasLiveFrame
13266
+ ? getRemoteFleetFrameSource(device, 'live')
13267
+ : getRemoteFleetFrameSource(device, 'thumbnail');
13251
13268
  const previewAt = hasLiveFrame
13252
13269
  ? String(getRemoteFleetDeviceField(device, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', ''))
13253
13270
  : String(getRemoteFleetDeviceField(device, 'thumbnailReceivedAt', 'ThumbnailReceivedAt',
@@ -13309,7 +13326,7 @@
13309
13326
  `;
13310
13327
  if (hasLiveFrame || hasThumbnail) {
13311
13328
  const image = document.createElement('img');
13312
- image.src = previewDataUrl;
13329
+ image.src = previewSource;
13313
13330
  image.alt = `${name} screen`;
13314
13331
  image.loading = 'lazy';
13315
13332
  image.decoding = 'async';
@@ -14104,7 +14121,7 @@
14104
14121
  const focusedId = getRemoteFleetDeviceId(focusedDevice);
14105
14122
  const focusedName = getRemoteFleetDeviceName(focusedDevice);
14106
14123
  const liveActive = isRemoteFleetLiveActive(focusedDevice);
14107
- const liveFrameDataUrl = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameDataUrl', 'LiveFrameDataUrl', ''));
14124
+ const liveFrameSource = getRemoteFleetFrameSource(focusedDevice, 'live');
14108
14125
  const liveFrameAt = String(getRemoteFleetDeviceField(focusedDevice, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', ''));
14109
14126
  const liveStreamId = String(getRemoteFleetDeviceField(focusedDevice, 'liveStreamId', 'LiveStreamId', ''));
14110
14127
  const hasLiveFrame = hasRemoteFleetLiveFrame(focusedDevice);
@@ -14135,7 +14152,7 @@
14135
14152
  `;
14136
14153
  if (hasLiveFrame) {
14137
14154
  const image = document.createElement('img');
14138
- image.src = liveFrameDataUrl;
14155
+ image.src = liveFrameSource;
14139
14156
  image.alt = `${focusedName} live screen`;
14140
14157
  image.decoding = 'async';
14141
14158
  image.style.cssText = `
@@ -14238,13 +14255,13 @@
14238
14255
  const createDevicePreview = (device, mode = 'tile') => {
14239
14256
  const name = getRemoteFleetDeviceName(device);
14240
14257
  const connectedDevice = isRemoteFleetDeviceConnected(device);
14241
- const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
14242
14258
  const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
14243
- const liveFrameDataUrl = String(device?.liveFrameDataUrl || device?.LiveFrameDataUrl || '');
14244
14259
  const liveFrameReceivedAt = String(device?.liveFrameReceivedAt || device?.LiveFrameReceivedAt || '');
14245
14260
  const hasThumbnail = hasRemoteFleetThumbnail(device);
14246
14261
  const hasLiveFrame = hasRemoteFleetLiveFrame(device);
14247
- const previewDataUrl = hasLiveFrame ? liveFrameDataUrl : thumbnailDataUrl;
14262
+ const previewSource = hasLiveFrame
14263
+ ? getRemoteFleetFrameSource(device, 'live')
14264
+ : getRemoteFleetFrameSource(device, 'thumbnail');
14248
14265
  const previewAt = hasLiveFrame ? liveFrameReceivedAt : thumbnailCapturedAt;
14249
14266
  const isDetail = mode === 'detail';
14250
14267
 
@@ -14262,7 +14279,7 @@
14262
14279
 
14263
14280
  if (hasLiveFrame || hasThumbnail) {
14264
14281
  const image = document.createElement('img');
14265
- image.src = previewDataUrl;
14282
+ image.src = previewSource;
14266
14283
  image.alt = hasLiveFrame ? `${name} live frame` : `${name} thumbnail`;
14267
14284
  image.loading = 'lazy';
14268
14285
  image.decoding = 'async';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-jka0i4EGJ6iC6sOZFGMzWKJ2qqciCFD5pIUmTZ59t9I=",
4
+ "hash": "sha256-85uTc0gcvy3kY8SBQJxkQmIrLfblhTpX8tZWjJP3QnE=",
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.ns29bor93l.dll": "System.Private.CoreLib.dll",
126
- "MindExecution.Core.6rfnfdndxq.dll": "MindExecution.Core.dll",
127
- "MindExecution.Kernel.z56elxihok.dll": "MindExecution.Kernel.dll",
128
- "MindExecution.Plugins.Admin.p5cs4ap87v.dll": "MindExecution.Plugins.Admin.dll",
129
- "MindExecution.Plugins.Business.s35og5uz44.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.j63qelz8rk.dll": "MindExecution.Plugins.Concept.dll",
131
- "MindExecution.Plugins.Directory.y74f55e8x3.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.8djc50fh8g.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.h6y03asuzq.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.z07jle70qs.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.gq00wm14q3.dll": "MindExecution.Web.dll",
126
+ "MindExecution.Core.xg9yy9l5dz.dll": "MindExecution.Core.dll",
127
+ "MindExecution.Kernel.erg96341xf.dll": "MindExecution.Kernel.dll",
128
+ "MindExecution.Plugins.Admin.11j9vpdm9u.dll": "MindExecution.Plugins.Admin.dll",
129
+ "MindExecution.Plugins.Business.oyskf08knn.dll": "MindExecution.Plugins.Business.dll",
130
+ "MindExecution.Plugins.Concept.keia4ox68c.dll": "MindExecution.Plugins.Concept.dll",
131
+ "MindExecution.Plugins.Directory.7pus9p63ym.dll": "MindExecution.Plugins.Directory.dll",
132
+ "MindExecution.Plugins.PlanMaster.wr3pupzfyo.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.kpfew1eggc.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.kzibxbai3y.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.6fjnkr9ty4.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.6rfnfdndxq.dll": "sha256-giL4rreoKsQoQ5gkLusNx7oL3w1l2UH52TYY1KBoIfQ=",
282
- "MindExecution.Kernel.z56elxihok.dll": "sha256-STATJelRGcW9SDGgsw6YmQi6tkQje52dy4lyT3QU4qs=",
283
- "MindExecution.Plugins.Concept.j63qelz8rk.dll": "sha256-ZftqLWcODF69nab4DCcdrKLyTbZmu9x1E6UZj95Lwg0=",
284
- "MindExecution.Plugins.PlanMaster.8djc50fh8g.dll": "sha256-DEK18qiIEbs60obGiRs+Cmr327gBOBqEGpdculGvnpo=",
285
- "MindExecution.Shared.z07jle70qs.dll": "sha256-rbi/VtbMCVX+T5mPBzy0KWJlu3VI/w5cILfCOhFXb2E=",
286
- "MindExecution.Web.gq00wm14q3.dll": "sha256-SLQj0JfkJi2Krlha+Mhem6+EG90bv7pMbiQkzs3kxJA="
281
+ "MindExecution.Core.xg9yy9l5dz.dll": "sha256-cRrcPtFn2SUkdgWA68l7+CQbGso1QWczG5az1KYpLUU=",
282
+ "MindExecution.Kernel.erg96341xf.dll": "sha256-5hDopeSFSFHb5tDeLFj0jrSt6XRq4SYxB8nyFJ6H9RQ=",
283
+ "MindExecution.Plugins.Concept.keia4ox68c.dll": "sha256-rO8bWy0n6NZKqMzFpcIjy/p1kG8mPSuTUZ+UMTWugf8=",
284
+ "MindExecution.Plugins.PlanMaster.wr3pupzfyo.dll": "sha256-pE7MM4cMUkHzg5wTQtq8Vgk+RSjlQtO44T9AwQsYDqw=",
285
+ "MindExecution.Shared.kzibxbai3y.dll": "sha256-4CE9pNlfu9pIL+wwsbaU4ARQLcYelkl2sqkwAJD9sD4=",
286
+ "MindExecution.Web.6fjnkr9ty4.dll": "sha256-7o2Y3AvthOWiFF9+lHnqahGmcN/0tDuBtJhxGdcMN44="
287
287
  },
288
288
  "lazyAssembly": {
289
- "MindExecution.Plugins.Admin.p5cs4ap87v.dll": "sha256-jahiJxaiE8hwMyRcg6rZGo4WBhBGFyAHYhOqlKjawWg=",
290
- "MindExecution.Plugins.Business.s35og5uz44.dll": "sha256-KOyk9eC6E/Gyfz2uWucHQmAayMQbShhLTqEymZFleh4=",
291
- "MindExecution.Plugins.Directory.y74f55e8x3.dll": "sha256-4gMuhIPLiX31U+jwhDT83rSPqZINONadmW+cvujfq1g=",
292
- "MindExecution.Plugins.YouTube.h6y03asuzq.dll": "sha256-CTh0qytJGaYVCK0/3dOK0w8iIqQNYHmSZ5NZJW9nNgA="
289
+ "MindExecution.Plugins.Admin.11j9vpdm9u.dll": "sha256-lZvp68Zm2iFwt93gRGlHDKVnywJT2hHxA3JHu4Z3PfA=",
290
+ "MindExecution.Plugins.Business.oyskf08knn.dll": "sha256-oL53lWZ8Lp8lZuqc3UP+qIGSEVKJvVvfov+2xyQ3INg=",
291
+ "MindExecution.Plugins.Directory.7pus9p63ym.dll": "sha256-a1eUvklwiLwuCPBk05omRoirn1tRqVgjogVW8/UAAuc=",
292
+ "MindExecution.Plugins.YouTube.kpfew1eggc.dll": "sha256-COvvpOYO8nsEePBr3CCF4Wv0CWfvvOAoXn54tmikwwc="
293
293
  }
294
294
  },
295
295
  "cacheBootResources": true,
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "tIrmi759",
2
+ "version": "nO5c8M3Z",
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-I1t1myZd+2qeNcLM4ZSYetN/rCdWG1MtqBW+yKmeDfU=",
89
+ "hash": "sha256-2LFL3oGey+usyB7lpywzpKYMhBmlMhrXi9IZdtdUJZU=",
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-giL4rreoKsQoQ5gkLusNx7oL3w1l2UH52TYY1KBoIfQ=",
414
- "url": "_framework/MindExecution.Core.6rfnfdndxq.dll"
413
+ "hash": "sha256-cRrcPtFn2SUkdgWA68l7+CQbGso1QWczG5az1KYpLUU=",
414
+ "url": "_framework/MindExecution.Core.xg9yy9l5dz.dll"
415
415
  },
416
416
  {
417
- "hash": "sha256-STATJelRGcW9SDGgsw6YmQi6tkQje52dy4lyT3QU4qs=",
418
- "url": "_framework/MindExecution.Kernel.z56elxihok.dll"
417
+ "hash": "sha256-5hDopeSFSFHb5tDeLFj0jrSt6XRq4SYxB8nyFJ6H9RQ=",
418
+ "url": "_framework/MindExecution.Kernel.erg96341xf.dll"
419
419
  },
420
420
  {
421
- "hash": "sha256-jahiJxaiE8hwMyRcg6rZGo4WBhBGFyAHYhOqlKjawWg=",
422
- "url": "_framework/MindExecution.Plugins.Admin.p5cs4ap87v.dll"
421
+ "hash": "sha256-lZvp68Zm2iFwt93gRGlHDKVnywJT2hHxA3JHu4Z3PfA=",
422
+ "url": "_framework/MindExecution.Plugins.Admin.11j9vpdm9u.dll"
423
423
  },
424
424
  {
425
- "hash": "sha256-KOyk9eC6E/Gyfz2uWucHQmAayMQbShhLTqEymZFleh4=",
426
- "url": "_framework/MindExecution.Plugins.Business.s35og5uz44.dll"
425
+ "hash": "sha256-oL53lWZ8Lp8lZuqc3UP+qIGSEVKJvVvfov+2xyQ3INg=",
426
+ "url": "_framework/MindExecution.Plugins.Business.oyskf08knn.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-ZftqLWcODF69nab4DCcdrKLyTbZmu9x1E6UZj95Lwg0=",
430
- "url": "_framework/MindExecution.Plugins.Concept.j63qelz8rk.dll"
429
+ "hash": "sha256-rO8bWy0n6NZKqMzFpcIjy/p1kG8mPSuTUZ+UMTWugf8=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.keia4ox68c.dll"
431
431
  },
432
432
  {
433
- "hash": "sha256-4gMuhIPLiX31U+jwhDT83rSPqZINONadmW+cvujfq1g=",
434
- "url": "_framework/MindExecution.Plugins.Directory.y74f55e8x3.dll"
433
+ "hash": "sha256-a1eUvklwiLwuCPBk05omRoirn1tRqVgjogVW8/UAAuc=",
434
+ "url": "_framework/MindExecution.Plugins.Directory.7pus9p63ym.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-DEK18qiIEbs60obGiRs+Cmr327gBOBqEGpdculGvnpo=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.8djc50fh8g.dll"
437
+ "hash": "sha256-pE7MM4cMUkHzg5wTQtq8Vgk+RSjlQtO44T9AwQsYDqw=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.wr3pupzfyo.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-CTh0qytJGaYVCK0/3dOK0w8iIqQNYHmSZ5NZJW9nNgA=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.h6y03asuzq.dll"
441
+ "hash": "sha256-COvvpOYO8nsEePBr3CCF4Wv0CWfvvOAoXn54tmikwwc=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.kpfew1eggc.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-rbi/VtbMCVX+T5mPBzy0KWJlu3VI/w5cILfCOhFXb2E=",
446
- "url": "_framework/MindExecution.Shared.z07jle70qs.dll"
445
+ "hash": "sha256-4CE9pNlfu9pIL+wwsbaU4ARQLcYelkl2sqkwAJD9sD4=",
446
+ "url": "_framework/MindExecution.Shared.kzibxbai3y.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-SLQj0JfkJi2Krlha+Mhem6+EG90bv7pMbiQkzs3kxJA=",
450
- "url": "_framework/MindExecution.Web.gq00wm14q3.dll"
449
+ "hash": "sha256-7o2Y3AvthOWiFF9+lHnqahGmcN/0tDuBtJhxGdcMN44=",
450
+ "url": "_framework/MindExecution.Web.6fjnkr9ty4.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-eLT3oE2JQu/kbNXd/DuHPNKXRyAQELmLJSyKy++j40U=",
773
+ "hash": "sha256-ZSebVg6+73YAEhmuXnGeKZGeTBILK2WJbFuyP6Ydv2Q=",
774
774
  "url": "_framework/blazor.boot.json"
775
775
  },
776
776
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: tIrmi759 */
1
+ /* Manifest version: nO5c8M3Z */
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