@mindexec/cli 0.2.16 → 0.2.17

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 (22) hide show
  1. package/package.json +1 -1
  2. package/remote-hub.js +256 -0
  3. package/scripts/remote-fleet-render-smoke.mjs +24 -2
  4. package/scripts/remote-hub-scale-smoke.mjs +14 -7
  5. package/scripts/remote-hub-smoke.mjs +40 -2
  6. package/server.js +13 -14
  7. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +75 -0
  8. package/wwwroot/_framework/MindExecution.Core.ckgjbdvvxl.dll +0 -0
  9. package/wwwroot/_framework/{MindExecution.Kernel.hpf3wr4dv3.dll → MindExecution.Kernel.lm7kgq8l8h.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Admin.y0euqt1gu1.dll → MindExecution.Plugins.Admin.k085kt3dls.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Business.9zzc31j54n.dll → MindExecution.Plugins.Business.dsz9uwkxih.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Concept.sihn2obrrn.dll → MindExecution.Plugins.Concept.6gcazss69r.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.Directory.zwbshhfvpb.dll → MindExecution.Plugins.Directory.ufbriq6js4.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.47f3afsrf8.dll → MindExecution.Plugins.PlanMaster.zqc0kv9n5d.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.rpolgpee6c.dll → MindExecution.Plugins.YouTube.5ojg8vbf9w.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Shared.zm6btxgvlg.dll → MindExecution.Shared.ahf7j9dymw.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Web.7b082n0t0o.dll → MindExecution.Web.6i9vy7xy1k.dll} +0 -0
  18. package/wwwroot/_framework/blazor.boot.json +21 -21
  19. package/wwwroot/index.html +1 -1
  20. package/wwwroot/service-worker-assets.js +24 -24
  21. package/wwwroot/service-worker.js +1 -1
  22. package/wwwroot/_framework/MindExecution.Core.gvaopvh5wg.dll +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/remote-hub.js CHANGED
@@ -12,6 +12,7 @@ const MAX_STREAM_BASE64_CHARS = 3 * 1024 * 1024;
12
12
  const MAX_AGENT_TASK_CHARS = 4000;
13
13
  const MAX_AGENT_TASK_RESULT_CHARS = 3000;
14
14
  const RECENT_TASK_LIMIT = 12;
15
+ const RECENT_TASK_BATCH_LIMIT = 16;
15
16
  const REMOTE_PROTOCOL_VERSION = 1;
16
17
  const MAX_SYNTHETIC_DEVICES = 1000;
17
18
  const SYNTHETIC_FRAME_DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
@@ -154,6 +155,31 @@ function serializeDevice(device) {
154
155
  };
155
156
  }
156
157
 
158
+ function serializeTaskBatch(batch) {
159
+ if (!batch) {
160
+ return null;
161
+ }
162
+
163
+ return {
164
+ batchId: batch.batchId,
165
+ title: batch.title,
166
+ instructionPreview: batch.instructionPreview,
167
+ approvalLevel: batch.approvalLevel,
168
+ status: batch.status,
169
+ requestedAt: batch.requestedAt,
170
+ updatedAt: batch.updatedAt,
171
+ total: batch.total,
172
+ queued: batch.queued,
173
+ pending: batch.pending,
174
+ completed: batch.completed,
175
+ failed: batch.failed,
176
+ timedOut: batch.timedOut,
177
+ results: Array.isArray(batch.results)
178
+ ? batch.results.map(item => ({ ...item }))
179
+ : []
180
+ };
181
+ }
182
+
157
183
  function writeJsonLine(socket, payload) {
158
184
  if (!socket || socket.destroyed) {
159
185
  return false;
@@ -184,6 +210,7 @@ export function createRemoteHub(options = {}) {
184
210
  256);
185
211
 
186
212
  const devices = new Map();
213
+ const taskBatches = new Map();
187
214
  const sockets = new Map();
188
215
  const allSockets = new Set();
189
216
  let server = null;
@@ -229,6 +256,17 @@ export function createRemoteHub(options = {}) {
229
256
  });
230
257
  }
231
258
 
259
+ function listTaskBatches() {
260
+ return [...taskBatches.values()]
261
+ .map(serializeTaskBatch)
262
+ .filter(Boolean)
263
+ .sort((left, right) => String(right.requestedAt || '').localeCompare(String(left.requestedAt || '')));
264
+ }
265
+
266
+ function getLatestTaskBatch() {
267
+ return listTaskBatches()[0] || null;
268
+ }
269
+
232
270
  function emitRemoteEvent(type, device = null, extra = {}) {
233
271
  emitEvent(type, {
234
272
  ...extra,
@@ -613,6 +651,162 @@ export function createRemoteHub(options = {}) {
613
651
  return task;
614
652
  }
615
653
 
654
+ function trimTaskBatches() {
655
+ const ordered = [...taskBatches.values()]
656
+ .sort((left, right) => String(right.requestedAt || '').localeCompare(String(left.requestedAt || '')));
657
+ for (const batch of ordered.slice(RECENT_TASK_BATCH_LIMIT)) {
658
+ taskBatches.delete(batch.batchId);
659
+ }
660
+ }
661
+
662
+ function beginTaskBatch(deviceIds, options = {}) {
663
+ const targets = [...new Set((deviceIds || [])
664
+ .map(deviceId => safeString(deviceId, 128))
665
+ .filter(Boolean))]
666
+ .slice(0, 500);
667
+ const now = new Date().toISOString();
668
+ const instruction = safeText(options.instruction, MAX_AGENT_TASK_CHARS);
669
+ const batchId = safeString(options.batchId, 128) || crypto.randomUUID();
670
+ const title = safeString(options.title, 120)
671
+ || safeString(instruction.split(/\r?\n/)[0], 120)
672
+ || 'Remote task batch';
673
+ const batch = {
674
+ batchId,
675
+ title,
676
+ instructionPreview: safeText(instruction, 320),
677
+ approvalLevel: normalizeApprovalLevel(options.approvalLevel),
678
+ status: targets.length > 0 ? 'running' : 'failed',
679
+ requestedAt: now,
680
+ updatedAt: now,
681
+ total: targets.length,
682
+ queued: 0,
683
+ pending: 0,
684
+ completed: 0,
685
+ failed: targets.length > 0 ? 0 : 1,
686
+ timedOut: 0,
687
+ results: targets.map(deviceId => ({
688
+ deviceId,
689
+ ok: false,
690
+ status: 'targeted',
691
+ error: '',
692
+ commandId: '',
693
+ taskId: '',
694
+ approvalLevel: normalizeApprovalLevel(options.approvalLevel),
695
+ updatedAt: now
696
+ }))
697
+ };
698
+
699
+ taskBatches.set(batchId, batch);
700
+ trimTaskBatches();
701
+ return batch;
702
+ }
703
+
704
+ function getTaskBatchItem(batch, deviceId) {
705
+ if (!batch) {
706
+ return null;
707
+ }
708
+
709
+ const id = safeString(deviceId, 128);
710
+ if (!id) {
711
+ return null;
712
+ }
713
+
714
+ let item = batch.results.find(result => result.deviceId === id);
715
+ if (!item) {
716
+ item = {
717
+ deviceId: id,
718
+ ok: false,
719
+ status: 'targeted',
720
+ error: '',
721
+ commandId: '',
722
+ taskId: '',
723
+ approvalLevel: batch.approvalLevel,
724
+ updatedAt: new Date().toISOString()
725
+ };
726
+ batch.results.push(item);
727
+ batch.total = batch.results.length;
728
+ }
729
+
730
+ return item;
731
+ }
732
+
733
+ function recalculateTaskBatch(batch) {
734
+ if (!batch) {
735
+ return null;
736
+ }
737
+
738
+ const results = Array.isArray(batch.results) ? batch.results : [];
739
+ batch.total = results.length;
740
+ batch.queued = results.filter(item => item.ok === true).length;
741
+ batch.completed = results.filter(item => item.status === 'completed').length;
742
+ batch.failed = results.filter(item => item.status === 'failed' || (item.ok === false && !!item.error)).length;
743
+ batch.timedOut = results.filter(item => item.error === 'task-timeout').length;
744
+ batch.pending = results.filter(item =>
745
+ item.ok === true
746
+ && item.status !== 'completed'
747
+ && item.status !== 'failed').length;
748
+ batch.updatedAt = new Date().toISOString();
749
+
750
+ if (batch.total === 0) {
751
+ batch.status = 'failed';
752
+ } else if (batch.completed + batch.failed >= batch.total) {
753
+ batch.status = batch.failed > 0 ? 'completed-with-failures' : 'completed';
754
+ } else if (batch.queued > 0) {
755
+ batch.status = 'running';
756
+ } else {
757
+ batch.status = 'failed';
758
+ }
759
+
760
+ return batch;
761
+ }
762
+
763
+ function syncTaskBatchFromTask(device, task) {
764
+ const batchId = safeString(task?.batchId, 128);
765
+ if (!batchId) {
766
+ return null;
767
+ }
768
+
769
+ const batch = taskBatches.get(batchId);
770
+ if (!batch) {
771
+ return null;
772
+ }
773
+
774
+ const item = getTaskBatchItem(batch, device?.deviceId);
775
+ if (!item) {
776
+ return null;
777
+ }
778
+
779
+ item.ok = true;
780
+ item.status = safeString(task.status, 40) || 'queued';
781
+ item.error = safeString(task.error, 500);
782
+ item.commandId = safeString(task.commandId, 128);
783
+ item.taskId = safeString(task.taskId, 128);
784
+ item.approvalLevel = normalizeApprovalLevel(task.approvalLevel || batch.approvalLevel);
785
+ item.updatedAt = task.updatedAt || task.completedAt || task.sentAt || new Date().toISOString();
786
+ return recalculateTaskBatch(batch);
787
+ }
788
+
789
+ function syncTaskBatchDispatchFailure(batchId, deviceId, error, extra = {}) {
790
+ const batch = taskBatches.get(safeString(batchId, 128));
791
+ if (!batch) {
792
+ return null;
793
+ }
794
+
795
+ const item = getTaskBatchItem(batch, deviceId);
796
+ if (!item) {
797
+ return null;
798
+ }
799
+
800
+ item.ok = false;
801
+ item.status = 'failed';
802
+ item.error = safeString(error, 500) || 'task-dispatch-failed';
803
+ item.commandId = safeString(extra.commandId, 128);
804
+ item.taskId = safeString(extra.taskId, 128);
805
+ item.approvalLevel = normalizeApprovalLevel(extra.approvalLevel || batch.approvalLevel);
806
+ item.updatedAt = new Date().toISOString();
807
+ return recalculateTaskBatch(batch);
808
+ }
809
+
616
810
  function clearTaskTimeout(device, commandId) {
617
811
  const key = safeString(commandId, 128);
618
812
  if (!device?.pendingTaskTimers || !key) {
@@ -653,6 +847,7 @@ export function createRemoteHub(options = {}) {
653
847
  if (task.error === 'task-timeout') {
654
848
  device.counters.taskResultsTimedOut += 1;
655
849
  }
850
+ syncTaskBatchFromTask(device, task);
656
851
  emitRemoteEvent('RemoteTaskResult', device, {
657
852
  commandId: key,
658
853
  taskId: task.taskId,
@@ -739,6 +934,7 @@ export function createRemoteHub(options = {}) {
739
934
  if (error) {
740
935
  device.counters.taskResultsFailed += 1;
741
936
  }
937
+ syncTaskBatchFromTask(device, task);
742
938
 
743
939
  return task;
744
940
  }
@@ -1113,6 +1309,10 @@ export function createRemoteHub(options = {}) {
1113
1309
 
1114
1310
  function requestAgentTask(deviceId, options = {}) {
1115
1311
  const device = devices.get(String(deviceId || ''));
1312
+ if (!device) {
1313
+ return { ok: false, error: 'device-not-found' };
1314
+ }
1315
+
1116
1316
  if (device?.synthetic === true && device.connected) {
1117
1317
  const instruction = safeText(options.instruction, MAX_AGENT_TASK_CHARS);
1118
1318
  if (!instruction) {
@@ -1131,6 +1331,7 @@ export function createRemoteHub(options = {}) {
1131
1331
  || safeString(instruction.split(/\r?\n/)[0], 120)
1132
1332
  || 'Remote task';
1133
1333
  const task = {
1334
+ batchId: safeString(options.batchId, 128),
1134
1335
  taskId,
1135
1336
  commandId,
1136
1337
  title,
@@ -1161,6 +1362,7 @@ export function createRemoteHub(options = {}) {
1161
1362
  device.lastSeenAt = now;
1162
1363
  rememberDeviceTask(device, task);
1163
1364
  device.pendingTaskCommands.delete(commandId);
1365
+ syncTaskBatchFromTask(device, task);
1164
1366
  emitRemoteEvent('RemoteTaskQueued', device, {
1165
1367
  commandId,
1166
1368
  taskId,
@@ -1199,6 +1401,7 @@ export function createRemoteHub(options = {}) {
1199
1401
  || safeString(instruction.split(/\r?\n/)[0], 120)
1200
1402
  || 'Remote task';
1201
1403
  const task = {
1404
+ batchId: safeString(options.batchId, 128),
1202
1405
  taskId,
1203
1406
  commandId,
1204
1407
  title,
@@ -1238,6 +1441,7 @@ export function createRemoteHub(options = {}) {
1238
1441
  device.counters.commandsSent += 1;
1239
1442
  device.counters.tasksQueued += 1;
1240
1443
  rememberDeviceTask(device, task);
1444
+ syncTaskBatchFromTask(device, task);
1241
1445
  scheduleTaskTimeout(device, task);
1242
1446
  emitRemoteEvent('RemoteTaskQueued', device, {
1243
1447
  commandId,
@@ -1248,6 +1452,55 @@ export function createRemoteHub(options = {}) {
1248
1452
  return { ok: true, commandId, taskId, approvalLevel };
1249
1453
  }
1250
1454
 
1455
+ function requestAgentTaskBatch(deviceIds, options = {}) {
1456
+ const targets = [...new Set((deviceIds || [])
1457
+ .map(deviceId => safeString(deviceId, 128))
1458
+ .filter(Boolean))]
1459
+ .slice(0, 500);
1460
+
1461
+ if (targets.length === 0) {
1462
+ return {
1463
+ ok: false,
1464
+ error: 'no-target-devices',
1465
+ total: 0,
1466
+ queued: 0,
1467
+ approvalLevel: normalizeApprovalLevel(options.approvalLevel),
1468
+ results: []
1469
+ };
1470
+ }
1471
+
1472
+ const batch = beginTaskBatch(targets, options);
1473
+ const results = targets.map(deviceId => {
1474
+ const result = requestAgentTask(deviceId, {
1475
+ ...options,
1476
+ batchId: batch.batchId
1477
+ });
1478
+
1479
+ if (result.ok !== true) {
1480
+ syncTaskBatchDispatchFailure(batch.batchId, deviceId, result.error, result);
1481
+ }
1482
+
1483
+ return {
1484
+ deviceId,
1485
+ ...result
1486
+ };
1487
+ });
1488
+
1489
+ const queued = results.filter(result => result.ok === true).length;
1490
+ const updatedBatch = taskBatches.get(batch.batchId) || batch;
1491
+ recalculateTaskBatch(updatedBatch);
1492
+ return {
1493
+ ok: queued > 0,
1494
+ batchId: batch.batchId,
1495
+ total: targets.length,
1496
+ queued,
1497
+ approvalLevel: batch.approvalLevel,
1498
+ results,
1499
+ batch: serializeTaskBatch(updatedBatch),
1500
+ error: queued > 0 ? undefined : 'no-task-queued'
1501
+ };
1502
+ }
1503
+
1251
1504
  function requestThumbnail(deviceId, options = {}) {
1252
1505
  return sendCommand(deviceId, {
1253
1506
  command: 'thumbnail.capture',
@@ -1423,9 +1676,12 @@ export function createRemoteHub(options = {}) {
1423
1676
  close,
1424
1677
  getStatus,
1425
1678
  listDevices,
1679
+ listTaskBatches,
1680
+ getLatestTaskBatch,
1426
1681
  disconnectDevice,
1427
1682
  sendCommand,
1428
1683
  requestAgentTask,
1684
+ requestAgentTaskBatch,
1429
1685
  requestThumbnail,
1430
1686
  startLiveStream,
1431
1687
  stopLiveStream,
@@ -412,7 +412,7 @@ function wait(ms = 0) {
412
412
  return new Promise(resolve => setTimeout(resolve, ms));
413
413
  }
414
414
 
415
- function buildMonitorNode(devices, hubStatus) {
415
+ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null) {
416
416
  const connected = devices.filter(device => device.Connected).length;
417
417
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
418
418
  const pairToken = hubStatus.pairToken || 'render-smoke-token';
@@ -433,6 +433,7 @@ function buildMonitorNode(devices, hubStatus) {
433
433
  RemoteFleetConnectedDeviceCount: String(connected),
434
434
  RemoteFleetCanvasPagination: 'none',
435
435
  RemoteFleetDevicesJson: JSON.stringify(devices),
436
+ RemoteFleetLatestTaskBatchJson: latestTaskBatch ? JSON.stringify(latestTaskBatch) : '',
436
437
  RemoteFleetLastError: ''
437
438
  }
438
439
  };
@@ -540,6 +541,22 @@ try {
540
541
  focusedDevice.LatestTaskUpdatedAt = new Date().toISOString();
541
542
  focusedDevice.LatestTaskError = 'task-timeout';
542
543
  focusedDevice.LatestTaskResultSummary = '';
544
+ const latestTaskBatch = {
545
+ batchId: 'render-smoke-batch',
546
+ title: 'Render smoke batch',
547
+ instructionPreview: 'Render smoke batch instruction preview.',
548
+ approvalLevel: 'ai-assist',
549
+ status: 'running',
550
+ requestedAt: new Date(Date.now() - 2500).toISOString(),
551
+ updatedAt: new Date().toISOString(),
552
+ total: 210,
553
+ queued: 210,
554
+ pending: 1,
555
+ completed: 208,
556
+ failed: 1,
557
+ timedOut: 1,
558
+ results: []
559
+ };
543
560
 
544
561
  const { manager, document } = await loadCss3DManager();
545
562
  assert.equal(typeof manager?.renderRemoteFleetMonitorForTest, 'function');
@@ -573,7 +590,7 @@ try {
573
590
  bodyView.dataset.remoteFleetFocusDeviceId = focusedDevice.DeviceId;
574
591
  document.body.appendChild(bodyView);
575
592
 
576
- manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true })));
593
+ manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch));
577
594
 
578
595
  let cards = bodyView.querySelectorAll('article[data-device-id]');
579
596
  assert.equal(cards.length, SYNTHETIC_COUNT);
@@ -587,6 +604,11 @@ try {
587
604
  assert.ok(bodyView.querySelector('code')?.textContent.includes('npx @mindexec/remote connect'));
588
605
  assert.ok(bodyView.textContent.includes('synthetic-render-ai'));
589
606
  assert.ok(bodyView.textContent.includes('Task timed out waiting for the agent response.'));
607
+ const batchSummary = bodyView.querySelector('[data-remote-fleet-task-batch="true"]');
608
+ assert.ok(batchSummary);
609
+ assert.ok(batchSummary.textContent.includes('Render smoke batch'));
610
+ assert.ok(batchSummary.textContent.includes('208/210 done'));
611
+ assert.ok(batchSummary.textContent.includes('1 timed out'));
590
612
  assert.ok(devices.some(device => /^synthetic-response-/.test(device.LatestTaskResultResponseId)));
591
613
 
592
614
  const searchInput = bodyView.querySelector('[data-remote-fleet-search="true"]');
@@ -77,13 +77,20 @@ try {
77
77
  .filter(device => device.connected && device.capabilities?.taskDispatch)
78
78
  .slice(0, 200);
79
79
  assert.ok(connectedTargets.length >= 100);
80
- const taskResults = connectedTargets.map(device =>
81
- hub.requestAgentTask(device.deviceId, {
82
- instruction: 'Synthetic scale task: report current status to the manager.',
83
- title: 'Scale task'
84
- }));
85
- assert.equal(taskResults.every(result => result.ok === true), true);
86
- assert.equal(taskResults.length, connectedTargets.length);
80
+ const scaleBatch = hub.requestAgentTaskBatch(connectedTargets.map(device => device.deviceId), {
81
+ instruction: 'Synthetic scale task: report current status to the manager.',
82
+ title: 'Scale task batch'
83
+ });
84
+ assert.equal(scaleBatch.ok, true);
85
+ assert.equal(scaleBatch.total, connectedTargets.length);
86
+ assert.equal(scaleBatch.queued, connectedTargets.length);
87
+ assert.equal(scaleBatch.results.every(result => result.ok === true), true);
88
+ assert.equal(scaleBatch.batch?.completed, connectedTargets.length);
89
+ assert.equal(scaleBatch.batch?.failed, 0);
90
+ assert.equal(scaleBatch.batch?.status, 'completed');
91
+ const latestBatch = hub.getLatestTaskBatch();
92
+ assert.equal(latestBatch?.batchId, scaleBatch.batchId);
93
+ assert.equal(latestBatch?.completed, connectedTargets.length);
87
94
 
88
95
  const aiTarget = hub.listDevices().find(device =>
89
96
  device.connected && device.capabilities?.taskDispatch && device.capabilities?.aiAssist);
@@ -231,6 +231,44 @@ try {
231
231
  assert.equal(aiTaskDevice.counters.tasksQueued, 2);
232
232
  assert.equal(aiTaskDevice.counters.taskResultsReceived, 2);
233
233
 
234
+ const batchTask = hub.requestAgentTaskBatch(['smoke-device', 'missing-smoke-device'], {
235
+ instruction: 'Dispatch a smoke batch to one connected and one missing device.',
236
+ title: 'Smoke batch'
237
+ });
238
+ assert.equal(batchTask.ok, true);
239
+ assert.equal(batchTask.total, 2);
240
+ assert.equal(batchTask.queued, 1);
241
+ assert.ok(batchTask.batchId);
242
+ assert.equal(batchTask.batch?.pending, 1);
243
+ assert.equal(batchTask.batch?.failed, 1);
244
+ assert.equal(batchTask.batch?.status, 'running');
245
+ const acceptedBatchItem = batchTask.results.find(result => result.deviceId === 'smoke-device');
246
+ assert.ok(acceptedBatchItem?.commandId);
247
+ writeJsonLine(socket, {
248
+ type: 'command.result',
249
+ commandId: acceptedBatchItem.commandId,
250
+ result: {
251
+ kind: 'agent.task',
252
+ taskId: acceptedBatchItem.taskId,
253
+ status: 'completed',
254
+ summary: 'Smoke batch item accepted.',
255
+ completedAt: new Date().toISOString()
256
+ }
257
+ });
258
+
259
+ const completedBatch = await waitFor(() => {
260
+ const latest = hub.getLatestTaskBatch();
261
+ return latest?.batchId === batchTask.batchId
262
+ && latest.completed === 1
263
+ && latest.failed === 1
264
+ ? latest
265
+ : null;
266
+ });
267
+ assert.equal(completedBatch.pending, 0);
268
+ assert.equal(completedBatch.status, 'completed-with-failures');
269
+ assert.equal(completedBatch.results.length, 2);
270
+ assert.equal(completedBatch.results.find(result => result.deviceId === 'missing-smoke-device')?.error, 'device-not-found');
271
+
234
272
  const timeoutTaskCommand = hub.requestAgentTask('smoke-device', {
235
273
  instruction: 'This task intentionally receives no agent response.',
236
274
  title: 'Timeout task'
@@ -245,8 +283,8 @@ try {
245
283
  }, 1000);
246
284
  assert.equal(timeoutTaskDevice.latestTask.error, 'task-timeout');
247
285
  assert.equal(timeoutTaskDevice.latestTask.resultSummary, 'task-timeout');
248
- assert.equal(timeoutTaskDevice.counters.tasksQueued, 3);
249
- assert.equal(timeoutTaskDevice.counters.taskResultsReceived, 3);
286
+ assert.equal(timeoutTaskDevice.counters.tasksQueued, 4);
287
+ assert.equal(timeoutTaskDevice.counters.taskResultsReceived, 4);
250
288
  assert.equal(timeoutTaskDevice.counters.taskResultsFailed, 1);
251
289
  assert.equal(timeoutTaskDevice.counters.taskResultsTimedOut, 1);
252
290
 
package/server.js CHANGED
@@ -6969,6 +6969,8 @@ app.get('/api/remote/devices', (req, res) => {
6969
6969
  total: devices.length,
6970
6970
  pagination: 'none',
6971
6971
  canvasDeviceListMode: 'all-devices',
6972
+ latestTaskBatch: remoteHub.getLatestTaskBatch(),
6973
+ recentTaskBatches: remoteHub.listTaskBatches(),
6972
6974
  devices
6973
6975
  });
6974
6976
  });
@@ -7040,23 +7042,20 @@ app.post('/api/remote/tasks', (req, res) => {
7040
7042
  ? requestedDeviceIds
7041
7043
  : (allConnected ? devices.filter(device => device.connected).map(device => device.deviceId) : []);
7042
7044
  const uniqueTargetIds = [...new Set(targetIds)].slice(0, 500);
7043
- const results = uniqueTargetIds.map(deviceId => ({
7044
- deviceId,
7045
- ...remoteHub.requestAgentTask(deviceId, {
7046
- instruction: req.body?.instruction,
7047
- title: req.body?.title,
7048
- approvalLevel: req.body?.approvalLevel,
7049
- model: req.body?.model
7050
- })
7051
- }));
7052
- const queued = results.filter(result => result.ok === true).length;
7045
+ const result = remoteHub.requestAgentTaskBatch(uniqueTargetIds, {
7046
+ instruction: req.body?.instruction,
7047
+ title: req.body?.title,
7048
+ approvalLevel: req.body?.approvalLevel,
7049
+ model: req.body?.model,
7050
+ batchId: req.body?.batchId
7051
+ });
7053
7052
  res.json({
7054
- ok: queued > 0,
7053
+ ...result,
7054
+ ok: result.ok === true,
7055
7055
  total: uniqueTargetIds.length,
7056
- queued,
7056
+ queued: result.queued || 0,
7057
7057
  approvalLevel: req.body?.approvalLevel === 'ai-assist' ? 'ai-assist' : 'task-only',
7058
- results,
7059
- error: queued > 0 ? undefined : (uniqueTargetIds.length === 0 ? 'no-target-devices' : 'no-task-queued')
7058
+ error: result.ok === true ? undefined : (uniqueTargetIds.length === 0 ? 'no-target-devices' : (result.error || 'no-task-queued'))
7060
7059
  });
7061
7060
  });
7062
7061
 
@@ -11975,6 +11975,22 @@
11975
11975
  }
11976
11976
  }
11977
11977
 
11978
+ function parseRemoteFleetLatestTaskBatch(nodeModel) {
11979
+ const raw = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLatestTaskBatchJson', '');
11980
+ if (!raw.trim()) {
11981
+ return null;
11982
+ }
11983
+
11984
+ try {
11985
+ const parsed = JSON.parse(raw);
11986
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
11987
+ ? parsed
11988
+ : null;
11989
+ } catch {
11990
+ return null;
11991
+ }
11992
+ }
11993
+
11978
11994
  function parseRemoteFleetPinnedDevice(nodeModel) {
11979
11995
  const raw = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetPinnedDeviceJson', '{}');
11980
11996
  if (!raw.trim()) {
@@ -12868,6 +12884,7 @@
12868
12884
  const aiAssistState = bodyView.dataset.remoteFleetAiAssist === 'true';
12869
12885
  const autoMonitorState = bodyView.dataset.remoteFleetAutoMonitor !== 'false';
12870
12886
  const focusState = bodyView.dataset.remoteFleetFocusDeviceId || '';
12887
+ const latestTaskBatch = parseRemoteFleetLatestTaskBatch(nodeModel);
12871
12888
  const sortedDevices = [...parseRemoteFleetDevices(nodeModel)]
12872
12889
  .sort((left, right) => compareRemoteFleetDevices(left, right, sortState));
12873
12890
  const devices = groupState === 'none'
@@ -13175,6 +13192,64 @@
13175
13192
  `;
13176
13193
  bodyView.appendChild(taskFeedback);
13177
13194
 
13195
+ if (latestTaskBatch) {
13196
+ const batchTotal = Number(latestTaskBatch.total ?? latestTaskBatch.Total ?? 0);
13197
+ const batchQueued = Number(latestTaskBatch.queued ?? latestTaskBatch.Queued ?? 0);
13198
+ const batchPending = Number(latestTaskBatch.pending ?? latestTaskBatch.Pending ?? 0);
13199
+ const batchCompleted = Number(latestTaskBatch.completed ?? latestTaskBatch.Completed ?? 0);
13200
+ const batchFailed = Number(latestTaskBatch.failed ?? latestTaskBatch.Failed ?? 0);
13201
+ const batchTimedOut = Number(latestTaskBatch.timedOut ?? latestTaskBatch.TimedOut ?? 0);
13202
+ const batchStatus = String(latestTaskBatch.status ?? latestTaskBatch.Status ?? '').trim();
13203
+ const batchTitle = String(latestTaskBatch.title ?? latestTaskBatch.Title ?? 'Remote task batch').trim();
13204
+ const batchUpdatedAt = String(latestTaskBatch.updatedAt ?? latestTaskBatch.UpdatedAt ?? latestTaskBatch.requestedAt ?? latestTaskBatch.RequestedAt ?? '').trim();
13205
+ const doneCount = Math.max(0, batchCompleted + batchFailed);
13206
+ const progress = batchTotal > 0 ? Math.max(0, Math.min(100, Math.round(doneCount * 100 / batchTotal))) : 0;
13207
+ const batchTone = batchFailed > 0
13208
+ ? 'error'
13209
+ : batchPending > 0
13210
+ ? 'pending'
13211
+ : 'done';
13212
+ const batchBox = document.createElement('div');
13213
+ batchBox.dataset.remoteFleetTaskBatch = 'true';
13214
+ batchBox.style.cssText = `
13215
+ flex: 0 0 auto;
13216
+ display: grid;
13217
+ grid-template-columns: minmax(0, 1fr) auto;
13218
+ gap: 8px 12px;
13219
+ align-items: center;
13220
+ padding: 8px 10px;
13221
+ border-radius: 8px;
13222
+ background: ${batchTone === 'error' ? 'rgba(248, 113, 113, 0.11)' : batchTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
13223
+ border: 1px solid ${batchTone === 'error' ? 'rgba(248, 113, 113, 0.24)' : batchTone === 'done' ? 'rgba(16, 185, 129, 0.20)' : 'rgba(37, 99, 235, 0.16)'};
13224
+ min-width: 0;
13225
+ `;
13226
+ const batchMain = document.createElement('div');
13227
+ batchMain.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:5px;';
13228
+ const batchLabel = document.createElement('div');
13229
+ batchLabel.textContent = `${batchTitle} - ${batchStatus || 'running'}${batchUpdatedAt ? ` - ${formatRemoteFleetAge(batchUpdatedAt)}` : ''}`;
13230
+ batchLabel.title = batchLabel.textContent;
13231
+ batchLabel.style.cssText = `color:${batchTone === 'error' ? '#991b1b' : batchTone === 'done' ? '#047857' : '#1d4ed8'};font-size:11px;font-weight:950;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
13232
+ const progressTrack = document.createElement('div');
13233
+ progressTrack.style.cssText = 'position:relative;height:7px;border-radius:999px;background:rgba(148,163,184,0.22);overflow:hidden;';
13234
+ const progressBar = document.createElement('div');
13235
+ progressBar.style.cssText = `
13236
+ width: ${progress}%;
13237
+ height: 100%;
13238
+ border-radius: inherit;
13239
+ background: ${batchTone === 'error' ? '#ef4444' : batchTone === 'done' ? '#10b981' : '#2563eb'};
13240
+ `;
13241
+ progressTrack.appendChild(progressBar);
13242
+ batchMain.appendChild(batchLabel);
13243
+ batchMain.appendChild(progressTrack);
13244
+ const batchStats = document.createElement('div');
13245
+ batchStats.textContent = `${batchCompleted}/${batchTotal || batchQueued || 0} done - ${batchPending} pending - ${batchFailed} failed${batchTimedOut > 0 ? ` - ${batchTimedOut} timed out` : ''}`;
13246
+ batchStats.title = batchStats.textContent;
13247
+ batchStats.style.cssText = 'color:#334155;font-size:10px;font-weight:900;line-height:1.25;white-space:nowrap;letter-spacing:0;';
13248
+ batchBox.appendChild(batchMain);
13249
+ batchBox.appendChild(batchStats);
13250
+ bodyView.appendChild(batchBox);
13251
+ }
13252
+
13178
13253
  if (focusedDevice) {
13179
13254
  const focusedId = getRemoteFleetDeviceId(focusedDevice);
13180
13255
  const focusedName = getRemoteFleetDeviceName(focusedDevice);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-EOXWPNQzw1iOoooim3bWEvapz9AQ4G5jOFZbf4RgdGg=",
4
+ "hash": "sha256-x+zuHEgludyG90VHm7vAvh24gxrcXgpLsHEMSmn3EZA=",
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.m05i39uvk9.dll": "System.dll",
124
124
  "netstandard.0xet7jg7ky.dll": "netstandard.dll",
125
125
  "System.Private.CoreLib.rkafq04oma.dll": "System.Private.CoreLib.dll",
126
- "MindExecution.Core.gvaopvh5wg.dll": "MindExecution.Core.dll",
127
- "MindExecution.Kernel.hpf3wr4dv3.dll": "MindExecution.Kernel.dll",
128
- "MindExecution.Plugins.Admin.y0euqt1gu1.dll": "MindExecution.Plugins.Admin.dll",
129
- "MindExecution.Plugins.Business.9zzc31j54n.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.sihn2obrrn.dll": "MindExecution.Plugins.Concept.dll",
131
- "MindExecution.Plugins.Directory.zwbshhfvpb.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.47f3afsrf8.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.rpolgpee6c.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.zm6btxgvlg.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.7b082n0t0o.dll": "MindExecution.Web.dll",
126
+ "MindExecution.Core.ckgjbdvvxl.dll": "MindExecution.Core.dll",
127
+ "MindExecution.Kernel.lm7kgq8l8h.dll": "MindExecution.Kernel.dll",
128
+ "MindExecution.Plugins.Admin.k085kt3dls.dll": "MindExecution.Plugins.Admin.dll",
129
+ "MindExecution.Plugins.Business.dsz9uwkxih.dll": "MindExecution.Plugins.Business.dll",
130
+ "MindExecution.Plugins.Concept.6gcazss69r.dll": "MindExecution.Plugins.Concept.dll",
131
+ "MindExecution.Plugins.Directory.ufbriq6js4.dll": "MindExecution.Plugins.Directory.dll",
132
+ "MindExecution.Plugins.PlanMaster.zqc0kv9n5d.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.5ojg8vbf9w.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.ahf7j9dymw.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.6i9vy7xy1k.dll": "MindExecution.Web.dll",
136
136
  "dotnet.js": "dotnet.js",
137
137
  "dotnet.native.xsn1d6x2kd.js": "dotnet.native.js",
138
138
  "dotnet.native.vz0adxojrz.wasm": "dotnet.native.wasm",
@@ -278,18 +278,18 @@
278
278
  "System.Xml.XDocument.c539ki6cuq.dll": "sha256-MPTRJkptrL9nGa2tl4kF46+wErNUYRPCGblX3ANoKoY=",
279
279
  "System.m05i39uvk9.dll": "sha256-5jDfIdbYAigw7/Q/lMzt5W/+cayGbW9ko9FvuaN1GsQ=",
280
280
  "netstandard.0xet7jg7ky.dll": "sha256-xENDv620uJ8fHwLJ2bdhrTHz4QPjvqXOztnk2a4wr0c=",
281
- "MindExecution.Core.gvaopvh5wg.dll": "sha256-DDLjV7zNlhxfza3LBE+Fbjpke9h8yItLJ3qWzf4Yp74=",
282
- "MindExecution.Kernel.hpf3wr4dv3.dll": "sha256-RV4W3gMSRwcM23iZfhh4tKgdBlTU0Tfuqld6kSEfxhw=",
283
- "MindExecution.Plugins.Concept.sihn2obrrn.dll": "sha256-dMpQJwHdXztt3B/lhRoqgpaiEoO7HbZie+ST5g1BAIk=",
284
- "MindExecution.Plugins.PlanMaster.47f3afsrf8.dll": "sha256-sNW33IqNle0U1uYsT1h4saM9T4UN2hDK13YYWPzOIq4=",
285
- "MindExecution.Shared.zm6btxgvlg.dll": "sha256-WxdOHLbsCW4r0JnzhaIHm4+nSVJW7WbVpjnf1Uwkk2c=",
286
- "MindExecution.Web.7b082n0t0o.dll": "sha256-k/Gq3iOPlGciTli3OjQY+EZ/29D4FlrU0tzk6Vu2b4E="
281
+ "MindExecution.Core.ckgjbdvvxl.dll": "sha256-ZDas50sgMQGCNt0UC1ZErP+uv7PQ7z/skK7zCwnmU2I=",
282
+ "MindExecution.Kernel.lm7kgq8l8h.dll": "sha256-ndscepu8d8g/nrJmBP8VrkIOTIkSA57t5shOq/rp/ys=",
283
+ "MindExecution.Plugins.Concept.6gcazss69r.dll": "sha256-hpCyKxiQxk50zHE8iOWtoIiBapBG83wfaos3jZzeaYg=",
284
+ "MindExecution.Plugins.PlanMaster.zqc0kv9n5d.dll": "sha256-iwATrRDt3Wwr/DKLxUl7GzqwKdhMLZNBKYVJPAkrhdM=",
285
+ "MindExecution.Shared.ahf7j9dymw.dll": "sha256-HlFaI9iZybI0VDe6KpsgRqLb3GTfUVXoU3KMlx0gQ0o=",
286
+ "MindExecution.Web.6i9vy7xy1k.dll": "sha256-IlDxFyeBhZWgici5UD0RtmqRNwynyD6n9Q0IJ8f4eBE="
287
287
  },
288
288
  "lazyAssembly": {
289
- "MindExecution.Plugins.Admin.y0euqt1gu1.dll": "sha256-gqBZLGmu1nofX9cZu7NzT0StnMnDwaG/eLtHfk95Dg8=",
290
- "MindExecution.Plugins.Business.9zzc31j54n.dll": "sha256-JTgGpzDyzUUKzJelltauxS03YhSFpdAt31BBx1Ng4pg=",
291
- "MindExecution.Plugins.Directory.zwbshhfvpb.dll": "sha256-rY/yH9+h3canhVFmdWrcynxahBsnQ4PT5wtCx5OQGFg=",
292
- "MindExecution.Plugins.YouTube.rpolgpee6c.dll": "sha256-X+TTYJklqCsulTbHi89CZkB4lDXfkTLEj6FH5dJZoDs="
289
+ "MindExecution.Plugins.Admin.k085kt3dls.dll": "sha256-LLDLGzE3yaP+rL+8CMZGWBmGQZBfrRWgUMijX/fxTxw=",
290
+ "MindExecution.Plugins.Business.dsz9uwkxih.dll": "sha256-bfvFu7Pq58deN7TnwfhsUZdvWhS4RpA0LdO77UsBnY8=",
291
+ "MindExecution.Plugins.Directory.ufbriq6js4.dll": "sha256-qoxaegrVr9oaXJgQqU7t7CrCf0EYgsd22BcWXg/+91I=",
292
+ "MindExecution.Plugins.YouTube.5ojg8vbf9w.dll": "sha256-a+S4Xha/L7um0foEwb8E0p8VqEw21M6ETzjrEUpIYTk="
293
293
  }
294
294
  },
295
295
  "cacheBootResources": true,
@@ -558,7 +558,7 @@
558
558
  }
559
559
 
560
560
  const base = '_content/MindExecution.Shared/js/';
561
- const scriptVersion = '20260612-remote-task-timeout-v475';
561
+ const scriptVersion = '20260612-remote-task-batch-v476';
562
562
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
563
563
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
564
564
  const criticalScripts = [
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "HOeNy0HC",
2
+ "version": "wUCVSTgn",
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-e/aMJjefLL0i7yi8x7tn/EY9wrD0pIxU42m2QKgIYz4=",
89
+ "hash": "sha256-MaP05yuNb/XsfihViUh/bL9svf11oMZGMZiAWnpm/Gk=",
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-DDLjV7zNlhxfza3LBE+Fbjpke9h8yItLJ3qWzf4Yp74=",
414
- "url": "_framework/MindExecution.Core.gvaopvh5wg.dll"
413
+ "hash": "sha256-ZDas50sgMQGCNt0UC1ZErP+uv7PQ7z/skK7zCwnmU2I=",
414
+ "url": "_framework/MindExecution.Core.ckgjbdvvxl.dll"
415
415
  },
416
416
  {
417
- "hash": "sha256-RV4W3gMSRwcM23iZfhh4tKgdBlTU0Tfuqld6kSEfxhw=",
418
- "url": "_framework/MindExecution.Kernel.hpf3wr4dv3.dll"
417
+ "hash": "sha256-ndscepu8d8g/nrJmBP8VrkIOTIkSA57t5shOq/rp/ys=",
418
+ "url": "_framework/MindExecution.Kernel.lm7kgq8l8h.dll"
419
419
  },
420
420
  {
421
- "hash": "sha256-gqBZLGmu1nofX9cZu7NzT0StnMnDwaG/eLtHfk95Dg8=",
422
- "url": "_framework/MindExecution.Plugins.Admin.y0euqt1gu1.dll"
421
+ "hash": "sha256-LLDLGzE3yaP+rL+8CMZGWBmGQZBfrRWgUMijX/fxTxw=",
422
+ "url": "_framework/MindExecution.Plugins.Admin.k085kt3dls.dll"
423
423
  },
424
424
  {
425
- "hash": "sha256-JTgGpzDyzUUKzJelltauxS03YhSFpdAt31BBx1Ng4pg=",
426
- "url": "_framework/MindExecution.Plugins.Business.9zzc31j54n.dll"
425
+ "hash": "sha256-bfvFu7Pq58deN7TnwfhsUZdvWhS4RpA0LdO77UsBnY8=",
426
+ "url": "_framework/MindExecution.Plugins.Business.dsz9uwkxih.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-dMpQJwHdXztt3B/lhRoqgpaiEoO7HbZie+ST5g1BAIk=",
430
- "url": "_framework/MindExecution.Plugins.Concept.sihn2obrrn.dll"
429
+ "hash": "sha256-hpCyKxiQxk50zHE8iOWtoIiBapBG83wfaos3jZzeaYg=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.6gcazss69r.dll"
431
431
  },
432
432
  {
433
- "hash": "sha256-rY/yH9+h3canhVFmdWrcynxahBsnQ4PT5wtCx5OQGFg=",
434
- "url": "_framework/MindExecution.Plugins.Directory.zwbshhfvpb.dll"
433
+ "hash": "sha256-qoxaegrVr9oaXJgQqU7t7CrCf0EYgsd22BcWXg/+91I=",
434
+ "url": "_framework/MindExecution.Plugins.Directory.ufbriq6js4.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-sNW33IqNle0U1uYsT1h4saM9T4UN2hDK13YYWPzOIq4=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.47f3afsrf8.dll"
437
+ "hash": "sha256-iwATrRDt3Wwr/DKLxUl7GzqwKdhMLZNBKYVJPAkrhdM=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.zqc0kv9n5d.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-X+TTYJklqCsulTbHi89CZkB4lDXfkTLEj6FH5dJZoDs=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.rpolgpee6c.dll"
441
+ "hash": "sha256-a+S4Xha/L7um0foEwb8E0p8VqEw21M6ETzjrEUpIYTk=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.5ojg8vbf9w.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-WxdOHLbsCW4r0JnzhaIHm4+nSVJW7WbVpjnf1Uwkk2c=",
446
- "url": "_framework/MindExecution.Shared.zm6btxgvlg.dll"
445
+ "hash": "sha256-HlFaI9iZybI0VDe6KpsgRqLb3GTfUVXoU3KMlx0gQ0o=",
446
+ "url": "_framework/MindExecution.Shared.ahf7j9dymw.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-k/Gq3iOPlGciTli3OjQY+EZ/29D4FlrU0tzk6Vu2b4E=",
450
- "url": "_framework/MindExecution.Web.7b082n0t0o.dll"
449
+ "hash": "sha256-IlDxFyeBhZWgici5UD0RtmqRNwynyD6n9Q0IJ8f4eBE=",
450
+ "url": "_framework/MindExecution.Web.6i9vy7xy1k.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-sP1JkizV14ASUo9oVPOopzpprSlzkoXZqpimYvOYBIw=",
773
+ "hash": "sha256-fGH3vBiA9K4LY1+siDEi6C1uyYfJgDAywmyOxn+xGY8=",
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-ZWKMY1yo6lJvnKON0C9AmGH2YMwnIs07SFC4mTUDTsQ=",
837
+ "hash": "sha256-2i4bXinqysl6fMxtRowvbfVpJyKIfPSm3wdbCBcX5i4=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: HOeNy0HC */
1
+ /* Manifest version: wUCVSTgn */
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