@mindexec/cli 0.2.3 → 0.2.5

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/README.md +20 -0
  2. package/package.json +1 -1
  3. package/remote-hub.js +190 -1
  4. package/scripts/remote-hub-smoke.mjs +64 -1
  5. package/server.js +41 -0
  6. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +226 -1
  7. package/wwwroot/_framework/MindExecution.Core.27f2blpou6.dll +0 -0
  8. package/wwwroot/_framework/{MindExecution.Kernel.mot9nj6bzm.dll → MindExecution.Kernel.9wfplilp5l.dll} +0 -0
  9. package/wwwroot/_framework/{MindExecution.Plugins.Admin.x9v2drg2f7.dll → MindExecution.Plugins.Admin.sb1vkmct0w.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Business.b0kjoyx31x.dll → MindExecution.Plugins.Business.zr7rkofx44.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Concept.6tojojgh1a.dll → MindExecution.Plugins.Concept.g6wd36v92i.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Directory.fqtbuqadsx.dll → MindExecution.Plugins.Directory.bb5flwt0u7.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.j7llfeae6l.dll → MindExecution.Plugins.PlanMaster.me8v9fpgwc.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.yo5fwdhugr.dll → MindExecution.Plugins.YouTube.l811fqx9e0.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Shared.0qi7vbn9a4.dll → MindExecution.Shared.oseamdg577.dll} +0 -0
  16. package/wwwroot/_framework/MindExecution.Web.96r3nnp9is.dll +0 -0
  17. package/wwwroot/_framework/blazor.boot.json +21 -21
  18. package/wwwroot/index.html +1 -1
  19. package/wwwroot/service-worker-assets.js +24 -24
  20. package/wwwroot/service-worker.js +1 -1
  21. package/wwwroot/_framework/MindExecution.Core.5luow1xgjs.dll +0 -0
  22. package/wwwroot/_framework/MindExecution.Web.6cv7ad7rik.dll +0 -0
package/README.md CHANGED
@@ -83,6 +83,26 @@ curl -X POST -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remot
83
83
  curl -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remote/devices/<device-id>/thumbnail
84
84
  ```
85
85
 
86
+ Queue a safe task-only instruction for one device or all connected devices:
87
+
88
+ ```bash
89
+ curl -X POST -H "X-Bridge-Token: <bridge-token>" -H "Content-Type: application/json" \
90
+ -d "{\"instruction\":\"Check the desktop and report status.\"}" \
91
+ http://127.0.0.1:5147/api/remote/devices/<device-id>/tasks
92
+
93
+ curl -X POST -H "X-Bridge-Token: <bridge-token>" -H "Content-Type: application/json" \
94
+ -d "{\"instruction\":\"Prepare a short status report.\",\"allConnected\":true}" \
95
+ http://127.0.0.1:5147/api/remote/tasks
96
+ ```
97
+
98
+ Queue an opt-in text-only AI assist task for agents started with `--ai`:
99
+
100
+ ```bash
101
+ curl -X POST -H "X-Bridge-Token: <bridge-token>" -H "Content-Type: application/json" \
102
+ -d "{\"instruction\":\"Summarize what this computer should do next.\",\"allConnected\":true,\"approvalLevel\":\"ai-assist\"}" \
103
+ http://127.0.0.1:5147/api/remote/tasks
104
+ ```
105
+
86
106
  ?먮뒗 ?꾩뿭 ?ㅼ튂 ???ㅽ뻾?⑸땲??
87
107
 
88
108
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/remote-hub.js CHANGED
@@ -7,6 +7,9 @@ const DEFAULT_REMOTE_HUB_HOST = '127.0.0.1';
7
7
  const DEFAULT_HEARTBEAT_MS = 5000;
8
8
  const MAX_LINE_CHARS = 4 * 1024 * 1024;
9
9
  const MAX_THUMBNAIL_BASE64_CHARS = 3 * 1024 * 1024;
10
+ const MAX_AGENT_TASK_CHARS = 4000;
11
+ const MAX_AGENT_TASK_RESULT_CHARS = 3000;
12
+ const RECENT_TASK_LIMIT = 12;
10
13
  const REMOTE_PROTOCOL_VERSION = 1;
11
14
 
12
15
  function isEnabledValue(value, fallback = true) {
@@ -38,6 +41,26 @@ function safeString(value, maxLength = 200) {
38
41
  return String(value ?? '').replace(/[\r\n\t]/g, ' ').trim().slice(0, maxLength);
39
42
  }
40
43
 
44
+ function safeText(value, maxLength = 1000) {
45
+ return String(value ?? '').replace(/\0/g, '').trim().slice(0, maxLength);
46
+ }
47
+
48
+ function normalizeApprovalLevel(value) {
49
+ const level = safeString(value, 80).toLowerCase();
50
+ return level === 'ai-assist' ? 'ai-assist' : 'task-only';
51
+ }
52
+
53
+ function readCapabilityFlag(capabilities, key) {
54
+ const value = capabilities?.[key];
55
+ if (typeof value === 'boolean') {
56
+ return value;
57
+ }
58
+ if (typeof value === 'number') {
59
+ return value !== 0;
60
+ }
61
+ return /^(1|true|yes|on)$/i.test(String(value || '').trim());
62
+ }
63
+
41
64
  function normalizeDeviceId(value) {
42
65
  const id = safeString(value, 128).replace(/[^a-zA-Z0-9_.:-]/g, '-');
43
66
  return id || crypto.randomUUID();
@@ -95,6 +118,10 @@ function serializeDevice(device) {
95
118
  remoteAddress: device.remoteAddress,
96
119
  remotePort: device.remotePort,
97
120
  latestThumbnail: device.latestThumbnail ? { ...device.latestThumbnail } : null,
121
+ latestTask: device.latestTask ? { ...device.latestTask } : null,
122
+ recentTasks: Array.isArray(device.recentTasks)
123
+ ? device.recentTasks.map(task => ({ ...task }))
124
+ : [],
98
125
  status: { ...device.status },
99
126
  counters: { ...device.counters }
100
127
  };
@@ -222,13 +249,19 @@ export function createRemoteHub(options = {}) {
222
249
  remotePort: socket.remotePort || 0,
223
250
  status: {},
224
251
  latestThumbnail: null,
252
+ latestTask: null,
253
+ recentTasks: [],
254
+ pendingTaskCommands: new Map(),
225
255
  counters: {
226
256
  messagesReceived: 1,
227
257
  statusReceived: 0,
228
258
  commandsSent: 0,
229
259
  commandResultsReceived: 0,
230
260
  thumbnailFramesReceived: 0,
231
- thumbnailFramesDropped: 0
261
+ thumbnailFramesDropped: 0,
262
+ tasksQueued: 0,
263
+ taskResultsReceived: 0,
264
+ taskResultsFailed: 0
232
265
  }
233
266
  };
234
267
 
@@ -268,6 +301,79 @@ export function createRemoteHub(options = {}) {
268
301
  emitRemoteEvent('RemoteDeviceDisconnected', device, { reason });
269
302
  }
270
303
 
304
+ function rememberDeviceTask(device, task) {
305
+ if (!device || !task) {
306
+ return null;
307
+ }
308
+
309
+ const existingIndex = device.recentTasks.findIndex(item =>
310
+ item.taskId === task.taskId || item.commandId === task.commandId);
311
+ if (existingIndex >= 0) {
312
+ device.recentTasks.splice(existingIndex, 1);
313
+ }
314
+
315
+ device.recentTasks.unshift(task);
316
+ if (device.recentTasks.length > RECENT_TASK_LIMIT) {
317
+ device.recentTasks.length = RECENT_TASK_LIMIT;
318
+ }
319
+
320
+ device.latestTask = task;
321
+ if (task.commandId) {
322
+ device.pendingTaskCommands.set(task.commandId, task);
323
+ }
324
+
325
+ return task;
326
+ }
327
+
328
+ function summarizeTaskResult(result) {
329
+ if (result && typeof result === 'object') {
330
+ return safeText(
331
+ result.summary
332
+ || result.message
333
+ || result.output
334
+ || result.result
335
+ || JSON.stringify(result),
336
+ MAX_AGENT_TASK_RESULT_CHARS);
337
+ }
338
+
339
+ return safeText(result ?? '', MAX_AGENT_TASK_RESULT_CHARS);
340
+ }
341
+
342
+ function applyTaskResult(device, commandId, result, error) {
343
+ if (!device || !commandId) {
344
+ return null;
345
+ }
346
+
347
+ const resultTaskId = result && typeof result === 'object'
348
+ ? safeString(result.taskId, 128)
349
+ : '';
350
+ const task = resultTaskId
351
+ ? device.recentTasks.find(item => item.taskId === resultTaskId)
352
+ : device.pendingTaskCommands.get(commandId);
353
+ if (!task) {
354
+ return null;
355
+ }
356
+
357
+ const now = device.lastSeenAt || new Date().toISOString();
358
+ const status = error
359
+ ? 'failed'
360
+ : safeString(result?.status, 40) || 'completed';
361
+ task.status = status;
362
+ task.updatedAt = now;
363
+ task.completedAt = safeString(result?.completedAt, 80) || now;
364
+ task.error = error;
365
+ task.resultSummary = error || summarizeTaskResult(result);
366
+ task.resultKind = safeString(result?.kind || result?.mode || 'agent-task', 80);
367
+ device.latestTask = task;
368
+ device.pendingTaskCommands.delete(commandId);
369
+ device.counters.taskResultsReceived += 1;
370
+ if (error) {
371
+ device.counters.taskResultsFailed += 1;
372
+ }
373
+
374
+ return task;
375
+ }
376
+
271
377
  function handleAgentMessage(socket, state, message) {
272
378
  if (!message || typeof message !== 'object') {
273
379
  return;
@@ -312,6 +418,19 @@ export function createRemoteHub(options = {}) {
312
418
  break;
313
419
  case 'command.result':
314
420
  device.counters.commandResultsReceived += 1;
421
+ {
422
+ const commandId = safeString(message.commandId, 128);
423
+ const error = safeString(message.error, 500);
424
+ const task = applyTaskResult(device, commandId, message.result ?? null, error);
425
+ if (task) {
426
+ emitRemoteEvent('RemoteTaskResult', device, {
427
+ commandId,
428
+ taskId: task.taskId,
429
+ status: task.status,
430
+ error: task.error
431
+ });
432
+ }
433
+ }
315
434
  emitRemoteEvent('RemoteCommandResult', device, {
316
435
  commandId: safeString(message.commandId, 128),
317
436
  result: message.result ?? null,
@@ -529,6 +648,75 @@ export function createRemoteHub(options = {}) {
529
648
  return { ok: true, commandId };
530
649
  }
531
650
 
651
+ function requestAgentTask(deviceId, options = {}) {
652
+ const device = devices.get(String(deviceId || ''));
653
+ if (!device?.socket || device.socket.destroyed || !device.connected) {
654
+ return { ok: false, error: 'device-not-connected' };
655
+ }
656
+
657
+ const instruction = safeText(options.instruction, MAX_AGENT_TASK_CHARS);
658
+ if (!instruction) {
659
+ return { ok: false, error: 'missing-instruction' };
660
+ }
661
+
662
+ const now = new Date().toISOString();
663
+ const approvalLevel = normalizeApprovalLevel(options.approvalLevel);
664
+ if (approvalLevel === 'ai-assist' && !readCapabilityFlag(device.capabilities, 'aiAssist')) {
665
+ return { ok: false, error: 'device-ai-assist-unavailable' };
666
+ }
667
+
668
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
669
+ const taskId = safeString(options.taskId, 128) || crypto.randomUUID();
670
+ const title = safeString(options.title, 120)
671
+ || safeString(instruction.split(/\r?\n/)[0], 120)
672
+ || 'Remote task';
673
+ const task = {
674
+ taskId,
675
+ commandId,
676
+ title,
677
+ instructionPreview: safeText(instruction, 320),
678
+ status: 'queued',
679
+ approvalLevel,
680
+ requestedAt: now,
681
+ sentAt: now,
682
+ updatedAt: now,
683
+ completedAt: '',
684
+ error: '',
685
+ resultKind: '',
686
+ resultSummary: ''
687
+ };
688
+
689
+ const sent = writeJsonLine(device.socket, {
690
+ type: 'command',
691
+ commandId,
692
+ command: 'agent.task',
693
+ payload: {
694
+ taskId,
695
+ title,
696
+ instruction,
697
+ approvalLevel,
698
+ model: safeString(options.model, 120),
699
+ requestedAt: now
700
+ },
701
+ issuedAt: now
702
+ });
703
+
704
+ if (!sent) {
705
+ return { ok: false, error: 'device-not-connected' };
706
+ }
707
+
708
+ device.counters.commandsSent += 1;
709
+ device.counters.tasksQueued += 1;
710
+ rememberDeviceTask(device, task);
711
+ emitRemoteEvent('RemoteTaskQueued', device, {
712
+ commandId,
713
+ taskId,
714
+ title,
715
+ approvalLevel
716
+ });
717
+ return { ok: true, commandId, taskId, approvalLevel };
718
+ }
719
+
532
720
  function requestThumbnail(deviceId, options = {}) {
533
721
  return sendCommand(deviceId, {
534
722
  command: 'thumbnail.capture',
@@ -555,6 +743,7 @@ export function createRemoteHub(options = {}) {
555
743
  listDevices,
556
744
  disconnectDevice,
557
745
  sendCommand,
746
+ requestAgentTask,
558
747
  requestThumbnail,
559
748
  getDeviceThumbnail,
560
749
  getPairToken: () => pairToken
@@ -61,7 +61,10 @@ try {
61
61
  status: true,
62
62
  thumbnail: false,
63
63
  control: false,
64
- computerAgent: false
64
+ computerAgent: true,
65
+ taskDispatch: true,
66
+ aiAssist: true,
67
+ aiModel: 'fake-ai'
65
68
  }
66
69
  });
67
70
  writeJsonLine(socket, {
@@ -109,6 +112,66 @@ try {
109
112
  assert.equal(thumbnailDevice.latestThumbnail.streamId, 'smoke-thumb');
110
113
  assert.equal(thumbnailDevice.counters.thumbnailFramesReceived, 1);
111
114
 
115
+ const taskCommand = hub.requestAgentTask('smoke-device', {
116
+ instruction: 'Summarize current desktop status for the manager.',
117
+ title: 'Smoke task'
118
+ });
119
+ assert.equal(taskCommand.ok, true);
120
+ writeJsonLine(socket, {
121
+ type: 'command.result',
122
+ commandId: taskCommand.commandId,
123
+ result: {
124
+ kind: 'agent.task',
125
+ taskId: taskCommand.taskId,
126
+ status: 'completed',
127
+ summary: 'Smoke task accepted.',
128
+ completedAt: new Date().toISOString()
129
+ }
130
+ });
131
+
132
+ const taskDevice = await waitFor(() => {
133
+ const current = hub.listDevices();
134
+ return current[0]?.latestTask?.taskId === taskCommand.taskId
135
+ && current[0]?.latestTask?.status === 'completed'
136
+ ? current[0]
137
+ : null;
138
+ });
139
+ assert.equal(taskDevice.latestTask.resultSummary, 'Smoke task accepted.');
140
+ assert.equal(taskDevice.counters.tasksQueued, 1);
141
+ assert.equal(taskDevice.counters.taskResultsReceived, 1);
142
+
143
+ const aiTaskCommand = hub.requestAgentTask('smoke-device', {
144
+ instruction: 'Use AI assist to summarize the smoke test state.',
145
+ title: 'Smoke AI task',
146
+ approvalLevel: 'ai-assist'
147
+ });
148
+ assert.equal(aiTaskCommand.ok, true);
149
+ assert.equal(aiTaskCommand.approvalLevel, 'ai-assist');
150
+ writeJsonLine(socket, {
151
+ type: 'command.result',
152
+ commandId: aiTaskCommand.commandId,
153
+ result: {
154
+ kind: 'agent.task',
155
+ mode: 'ai-assist',
156
+ taskId: aiTaskCommand.taskId,
157
+ status: 'completed',
158
+ summary: 'Smoke AI task accepted.',
159
+ completedAt: new Date().toISOString()
160
+ }
161
+ });
162
+
163
+ const aiTaskDevice = await waitFor(() => {
164
+ const current = hub.listDevices();
165
+ return current[0]?.latestTask?.taskId === aiTaskCommand.taskId
166
+ && current[0]?.latestTask?.approvalLevel === 'ai-assist'
167
+ && current[0]?.latestTask?.status === 'completed'
168
+ ? current[0]
169
+ : null;
170
+ });
171
+ assert.equal(aiTaskDevice.latestTask.resultSummary, 'Smoke AI task accepted.');
172
+ assert.equal(aiTaskDevice.counters.tasksQueued, 2);
173
+ assert.equal(aiTaskDevice.counters.taskResultsReceived, 2);
174
+
112
175
  socket.destroy();
113
176
  await waitFor(() => hub.listDevices()[0]?.connected === false);
114
177
  console.log('RemoteHub smoke OK');
package/server.js CHANGED
@@ -6987,6 +6987,47 @@ app.post('/api/remote/devices/:deviceId/ping', (req, res) => {
6987
6987
  }));
6988
6988
  });
6989
6989
 
6990
+ app.post('/api/remote/devices/:deviceId/tasks', (req, res) => {
6991
+ res.json(remoteHub.requestAgentTask(req.params.deviceId, {
6992
+ instruction: req.body?.instruction,
6993
+ title: req.body?.title,
6994
+ taskId: req.body?.taskId,
6995
+ commandId: req.body?.commandId,
6996
+ approvalLevel: req.body?.approvalLevel,
6997
+ model: req.body?.model
6998
+ }));
6999
+ });
7000
+
7001
+ app.post('/api/remote/tasks', (req, res) => {
7002
+ const requestedDeviceIds = Array.isArray(req.body?.deviceIds)
7003
+ ? req.body.deviceIds.map(value => String(value || '').trim()).filter(Boolean)
7004
+ : [];
7005
+ const allConnected = req.body?.allConnected !== false;
7006
+ const devices = remoteHub.listDevices();
7007
+ const targetIds = requestedDeviceIds.length > 0
7008
+ ? requestedDeviceIds
7009
+ : (allConnected ? devices.filter(device => device.connected).map(device => device.deviceId) : []);
7010
+ const uniqueTargetIds = [...new Set(targetIds)].slice(0, 500);
7011
+ const results = uniqueTargetIds.map(deviceId => ({
7012
+ deviceId,
7013
+ ...remoteHub.requestAgentTask(deviceId, {
7014
+ instruction: req.body?.instruction,
7015
+ title: req.body?.title,
7016
+ approvalLevel: req.body?.approvalLevel,
7017
+ model: req.body?.model
7018
+ })
7019
+ }));
7020
+ const queued = results.filter(result => result.ok === true).length;
7021
+ res.json({
7022
+ ok: queued > 0,
7023
+ total: uniqueTargetIds.length,
7024
+ queued,
7025
+ approvalLevel: req.body?.approvalLevel === 'ai-assist' ? 'ai-assist' : 'task-only',
7026
+ results,
7027
+ error: queued > 0 ? undefined : (uniqueTargetIds.length === 0 ? 'no-target-devices' : 'no-task-queued')
7028
+ });
7029
+ });
7030
+
6990
7031
  app.get('/api/remote/devices/:deviceId/thumbnail', (req, res) => {
6991
7032
  res.setHeader('Cache-Control', 'no-store');
6992
7033
  const thumbnail = remoteHub.getDeviceThumbnail(req.params.deviceId);
@@ -12160,6 +12160,95 @@
12160
12160
  commandRow.appendChild(refreshButton);
12161
12161
  bodyView.appendChild(commandRow);
12162
12162
 
12163
+ const taskRow = document.createElement('div');
12164
+ taskRow.style.cssText = `
12165
+ display: grid;
12166
+ grid-template-columns: minmax(0, 1fr) auto auto;
12167
+ gap: 8px;
12168
+ align-items: stretch;
12169
+ flex: 0 0 auto;
12170
+ `;
12171
+ const taskInput = document.createElement('textarea');
12172
+ taskInput.dataset.remoteFleetTaskInput = 'true';
12173
+ taskInput.placeholder = 'Task for remote agents';
12174
+ taskInput.rows = 2;
12175
+ taskInput.style.cssText = `
12176
+ width: 100%;
12177
+ min-width: 0;
12178
+ height: 54px;
12179
+ resize: none;
12180
+ border-radius: 7px;
12181
+ border: 1px solid rgba(148, 163, 184, 0.34);
12182
+ background: rgba(255, 255, 255, 0.92);
12183
+ color: #0f172a;
12184
+ padding: 8px 10px;
12185
+ font-size: 12px;
12186
+ line-height: 1.35;
12187
+ font-weight: 700;
12188
+ letter-spacing: 0;
12189
+ outline: none;
12190
+ pointer-events: auto;
12191
+ `;
12192
+ const aiToggleLabel = document.createElement('label');
12193
+ aiToggleLabel.title = 'Use AI assist on agents that expose it';
12194
+ aiToggleLabel.style.cssText = `
12195
+ display: inline-flex;
12196
+ align-items: center;
12197
+ justify-content: center;
12198
+ gap: 6px;
12199
+ min-width: 54px;
12200
+ height: 54px;
12201
+ padding: 0 9px;
12202
+ border-radius: 7px;
12203
+ border: 1px solid rgba(14, 165, 233, 0.32);
12204
+ background: rgba(240, 249, 255, 0.88);
12205
+ color: #0369a1;
12206
+ font-size: 11px;
12207
+ font-weight: 900;
12208
+ letter-spacing: 0;
12209
+ cursor: pointer;
12210
+ pointer-events: auto;
12211
+ user-select: none;
12212
+ `;
12213
+ const aiToggle = document.createElement('input');
12214
+ aiToggle.type = 'checkbox';
12215
+ aiToggle.dataset.remoteFleetAiToggle = 'true';
12216
+ aiToggle.style.cssText = `
12217
+ width: 14px;
12218
+ height: 14px;
12219
+ margin: 0;
12220
+ accent-color: #0284c7;
12221
+ `;
12222
+ const aiToggleText = document.createElement('span');
12223
+ aiToggleText.textContent = 'AI';
12224
+ aiToggleLabel.appendChild(aiToggle);
12225
+ aiToggleLabel.appendChild(aiToggleText);
12226
+ const sendConnectedButton = createRemoteFleetButton('Send connected', 'Dispatch task to all connected task-capable devices', 'task-connected');
12227
+ sendConnectedButton.style.height = '54px';
12228
+ taskRow.appendChild(taskInput);
12229
+ taskRow.appendChild(aiToggleLabel);
12230
+ taskRow.appendChild(sendConnectedButton);
12231
+ bodyView.appendChild(taskRow);
12232
+
12233
+ const taskFeedback = document.createElement('div');
12234
+ taskFeedback.dataset.remoteFleetTaskFeedback = 'true';
12235
+ taskFeedback.style.cssText = `
12236
+ display: none;
12237
+ flex: 0 0 auto;
12238
+ min-height: 22px;
12239
+ padding: 5px 8px;
12240
+ border-radius: 7px;
12241
+ background: rgba(37, 99, 235, 0.08);
12242
+ color: #1d4ed8;
12243
+ font-size: 11px;
12244
+ font-weight: 800;
12245
+ line-height: 1.25;
12246
+ overflow: hidden;
12247
+ text-overflow: ellipsis;
12248
+ white-space: nowrap;
12249
+ `;
12250
+ bodyView.appendChild(taskFeedback);
12251
+
12163
12252
  if (lastError.trim()) {
12164
12253
  const errorEl = document.createElement('div');
12165
12254
  errorEl.textContent = lastError;
@@ -12220,6 +12309,15 @@
12220
12309
  const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
12221
12310
  const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
12222
12311
  const hasThumbnail = /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(thumbnailDataUrl);
12312
+ const taskEnabled = device?.computerAgentEnabled === true || device?.ComputerAgentEnabled === true;
12313
+ const aiAssistEnabled = device?.aiAssistEnabled === true || device?.AiAssistEnabled === true;
12314
+ const aiModel = String(device?.aiModel || device?.AiModel || '');
12315
+ const latestTaskStatus = String(device?.latestTaskStatus || device?.LatestTaskStatus || '');
12316
+ const latestTaskTitle = String(device?.latestTaskTitle || device?.LatestTaskTitle || '');
12317
+ const latestTaskApproval = String(device?.latestTaskApprovalLevel || device?.LatestTaskApprovalLevel || '');
12318
+ const latestTaskUpdatedAt = String(device?.latestTaskUpdatedAt || device?.LatestTaskUpdatedAt || '');
12319
+ const latestTaskError = String(device?.latestTaskError || device?.LatestTaskError || '');
12320
+ const latestTaskResult = String(device?.latestTaskResultSummary || device?.LatestTaskResultSummary || '');
12223
12321
  const card = document.createElement('article');
12224
12322
  card.dataset.deviceId = deviceId;
12225
12323
  card.style.cssText = `
@@ -12374,10 +12472,59 @@
12374
12472
  addMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
12375
12473
  card.appendChild(metrics);
12376
12474
 
12475
+ if (latestTaskStatus || latestTaskTitle || latestTaskResult || latestTaskError) {
12476
+ const taskBox = document.createElement('div');
12477
+ const taskTone = latestTaskError || latestTaskStatus === 'failed'
12478
+ ? 'error'
12479
+ : (latestTaskStatus === 'completed' ? 'done' : 'pending');
12480
+ taskBox.style.cssText = `
12481
+ display: flex;
12482
+ flex-direction: column;
12483
+ gap: 3px;
12484
+ min-width: 0;
12485
+ padding: 7px 8px;
12486
+ border-radius: 7px;
12487
+ background: ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.12)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
12488
+ border: 1px solid ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.24)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.20)' : 'rgba(37, 99, 235, 0.16)'};
12489
+ `;
12490
+ const taskLine = document.createElement('div');
12491
+ const taskModeLabel = latestTaskApproval === 'ai-assist' ? 'AI' : 'Task';
12492
+ taskLine.textContent = `${taskModeLabel} ${latestTaskStatus || 'task'}${latestTaskUpdatedAt ? ` - ${formatRemoteFleetAge(latestTaskUpdatedAt)}` : ''}`;
12493
+ taskLine.style.cssText = `
12494
+ color: ${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};
12495
+ font-size: 10px;
12496
+ font-weight: 900;
12497
+ line-height: 1.2;
12498
+ overflow: hidden;
12499
+ text-overflow: ellipsis;
12500
+ white-space: nowrap;
12501
+ letter-spacing: 0;
12502
+ `;
12503
+ const taskSummary = document.createElement('div');
12504
+ taskSummary.textContent = latestTaskError || latestTaskResult || latestTaskTitle || 'Task queued';
12505
+ taskSummary.title = taskSummary.textContent;
12506
+ taskSummary.style.cssText = `
12507
+ color: #334155;
12508
+ font-size: 10px;
12509
+ font-weight: 700;
12510
+ line-height: 1.25;
12511
+ overflow: hidden;
12512
+ display: -webkit-box;
12513
+ -webkit-line-clamp: 2;
12514
+ -webkit-box-orient: vertical;
12515
+ letter-spacing: 0;
12516
+ `;
12517
+ taskBox.appendChild(taskLine);
12518
+ taskBox.appendChild(taskSummary);
12519
+ card.appendChild(taskBox);
12520
+ }
12521
+
12377
12522
  const actions = document.createElement('div');
12378
12523
  actions.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;';
12379
12524
  const status = document.createElement('span');
12380
- status.textContent = connectedDevice ? 'Connected' : 'Offline';
12525
+ status.textContent = connectedDevice
12526
+ ? (aiAssistEnabled ? `AI ${aiModel || 'ready'}` : 'Connected')
12527
+ : 'Offline';
12381
12528
  status.style.cssText = `
12382
12529
  min-width: 0;
12383
12530
  color: ${connectedDevice ? '#047857' : '#64748b'};
@@ -12389,6 +12536,13 @@
12389
12536
  `;
12390
12537
  actions.appendChild(status);
12391
12538
  if (connectedDevice && deviceId) {
12539
+ if (taskEnabled) {
12540
+ const taskButton = createRemoteFleetButton('Task', 'Dispatch task to this device', 'task-device');
12541
+ taskButton.dataset.deviceId = deviceId;
12542
+ taskButton.style.height = '24px';
12543
+ taskButton.style.fontSize = '10px';
12544
+ actions.appendChild(taskButton);
12545
+ }
12392
12546
  if (thumbnailEnabled) {
12393
12547
  const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
12394
12548
  thumbnailButton.dataset.deviceId = deviceId;
@@ -12429,6 +12583,50 @@
12429
12583
  navigator.clipboard?.writeText?.(command).catch(() => { });
12430
12584
  });
12431
12585
 
12586
+ const setTaskFeedback = (message, tone = 'info') => {
12587
+ taskFeedback.textContent = message || '';
12588
+ taskFeedback.style.display = message ? 'block' : 'none';
12589
+ taskFeedback.style.background = tone === 'error'
12590
+ ? 'rgba(248, 113, 113, 0.12)'
12591
+ : tone === 'success'
12592
+ ? 'rgba(16, 185, 129, 0.10)'
12593
+ : 'rgba(37, 99, 235, 0.08)';
12594
+ taskFeedback.style.color = tone === 'error'
12595
+ ? '#991b1b'
12596
+ : tone === 'success'
12597
+ ? '#047857'
12598
+ : '#1d4ed8';
12599
+ };
12600
+
12601
+ const readTaskInstruction = () => String(taskInput.value || '').trim();
12602
+ const useAiAssist = () => aiToggle.checked === true;
12603
+
12604
+ sendConnectedButton.addEventListener('click', async event => {
12605
+ event.preventDefault();
12606
+ event.stopPropagation();
12607
+ const instruction = readTaskInstruction();
12608
+ if (!instruction) {
12609
+ setTaskFeedback('Write a task first.', 'error');
12610
+ taskInput.focus();
12611
+ return;
12612
+ }
12613
+
12614
+ sendConnectedButton.disabled = true;
12615
+ setTaskFeedback('Dispatching task to connected devices...');
12616
+ try {
12617
+ const result = await invokeDotNetAsync('DispatchRemoteFleetTaskFromJs', nodeId, '', instruction, useAiAssist());
12618
+ await syncRemoteFleetNodeStateFromResult(result);
12619
+ if (result?.success) {
12620
+ const mode = useAiAssist() ? 'AI task' : 'remote task';
12621
+ setTaskFeedback(`Queued ${result.queued || result.total || 1} ${mode}(s).`, 'success');
12622
+ } else {
12623
+ setTaskFeedback(result?.error || 'Task dispatch failed.', 'error');
12624
+ }
12625
+ } finally {
12626
+ sendConnectedButton.disabled = false;
12627
+ }
12628
+ });
12629
+
12432
12630
  refreshButton.addEventListener('click', async event => {
12433
12631
  event.preventDefault();
12434
12632
  event.stopPropagation();
@@ -12449,6 +12647,33 @@
12449
12647
  });
12450
12648
  });
12451
12649
 
12650
+ grid.querySelectorAll('[data-remote-fleet-action="task-device"]').forEach(button => {
12651
+ button.addEventListener('click', async event => {
12652
+ event.preventDefault();
12653
+ event.stopPropagation();
12654
+ const instruction = readTaskInstruction();
12655
+ if (!instruction) {
12656
+ setTaskFeedback('Write a task first.', 'error');
12657
+ taskInput.focus();
12658
+ return;
12659
+ }
12660
+
12661
+ button.disabled = true;
12662
+ setTaskFeedback('Dispatching task to device...');
12663
+ try {
12664
+ const result = await invokeDotNetAsync('DispatchRemoteFleetTaskFromJs', nodeId, button.dataset.deviceId || '', instruction, useAiAssist());
12665
+ await syncRemoteFleetNodeStateFromResult(result);
12666
+ if (result?.success) {
12667
+ setTaskFeedback(useAiAssist() ? 'Queued AI task.' : 'Queued remote task.', 'success');
12668
+ } else {
12669
+ setTaskFeedback(result?.error || 'Task dispatch failed.', 'error');
12670
+ }
12671
+ } finally {
12672
+ button.disabled = false;
12673
+ }
12674
+ });
12675
+ });
12676
+
12452
12677
  grid.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').forEach(button => {
12453
12678
  button.addEventListener('click', async event => {
12454
12679
  event.preventDefault();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-O+rTW41RGhoEFv77enci/rzrHD3OdR8jVue4Ekv/BXc=",
4
+ "hash": "sha256-pNivN7EJaBIokgTOUsXUgk3uMPTdJMPxWR5WEc3XWNk=",
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.5luow1xgjs.dll": "MindExecution.Core.dll",
127
- "MindExecution.Kernel.mot9nj6bzm.dll": "MindExecution.Kernel.dll",
128
- "MindExecution.Plugins.Admin.x9v2drg2f7.dll": "MindExecution.Plugins.Admin.dll",
129
- "MindExecution.Plugins.Business.b0kjoyx31x.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.6tojojgh1a.dll": "MindExecution.Plugins.Concept.dll",
131
- "MindExecution.Plugins.Directory.fqtbuqadsx.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.j7llfeae6l.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.yo5fwdhugr.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.0qi7vbn9a4.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.6cv7ad7rik.dll": "MindExecution.Web.dll",
126
+ "MindExecution.Core.27f2blpou6.dll": "MindExecution.Core.dll",
127
+ "MindExecution.Kernel.9wfplilp5l.dll": "MindExecution.Kernel.dll",
128
+ "MindExecution.Plugins.Admin.sb1vkmct0w.dll": "MindExecution.Plugins.Admin.dll",
129
+ "MindExecution.Plugins.Business.zr7rkofx44.dll": "MindExecution.Plugins.Business.dll",
130
+ "MindExecution.Plugins.Concept.g6wd36v92i.dll": "MindExecution.Plugins.Concept.dll",
131
+ "MindExecution.Plugins.Directory.bb5flwt0u7.dll": "MindExecution.Plugins.Directory.dll",
132
+ "MindExecution.Plugins.PlanMaster.me8v9fpgwc.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.l811fqx9e0.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.oseamdg577.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.96r3nnp9is.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.5luow1xgjs.dll": "sha256-2YRYhyo44+pE+OT0e2NSDCcxNji6d55ITkrE/pj7xL0=",
282
- "MindExecution.Kernel.mot9nj6bzm.dll": "sha256-ej5PzXIwPCOmBctW5KddMd2Lotqv5PPwPrVPMHRrfVc=",
283
- "MindExecution.Plugins.Concept.6tojojgh1a.dll": "sha256-pnckzioW3S/Gd8Zgwvm6KqHzCMEUAmO2pXcs1ilG2XQ=",
284
- "MindExecution.Plugins.PlanMaster.j7llfeae6l.dll": "sha256-YRqzOjhkhZKW1kpttYBKFTB9kUpO2khBvAyayRzy4YE=",
285
- "MindExecution.Shared.0qi7vbn9a4.dll": "sha256-iJXZ6p8LtuUPrlFwKFxkMntcxXsKARiL/YO/UWnFOBU=",
286
- "MindExecution.Web.6cv7ad7rik.dll": "sha256-Zmi8Ley2/DFOiUNHrQ1NrsYzza6LRGdMdPmpZ6SlKWk="
281
+ "MindExecution.Core.27f2blpou6.dll": "sha256-bqjP4oZY0UUVrmz+2Ctlf51S2Uo5Y/AKoPqoHy4pOnw=",
282
+ "MindExecution.Kernel.9wfplilp5l.dll": "sha256-uWkVGORohTBq4U8aic2oWXUFy9SnLejWJO5jA4AYUv4=",
283
+ "MindExecution.Plugins.Concept.g6wd36v92i.dll": "sha256-2DAMmcKQQlL5nDSjz2wZpO3rsJZCKV47BD19znoecJc=",
284
+ "MindExecution.Plugins.PlanMaster.me8v9fpgwc.dll": "sha256-ThFpjeyaMI19u6kLpApau9CFMDFKu0qAXvW45raEf4A=",
285
+ "MindExecution.Shared.oseamdg577.dll": "sha256-aNNAxyXR5QAJc8/7NHb0SkPoCV/ZfKxUpI1EutwVPoU=",
286
+ "MindExecution.Web.96r3nnp9is.dll": "sha256-Ua8v/da0JRYdWnLlYTo7RN8X+fLqj0Fx2ep9gtN0GS4="
287
287
  },
288
288
  "lazyAssembly": {
289
- "MindExecution.Plugins.Admin.x9v2drg2f7.dll": "sha256-K/zYBndcqJXnUSVnQd6BwD/XxIQ3kTa9QKYfLV/grpE=",
290
- "MindExecution.Plugins.Business.b0kjoyx31x.dll": "sha256-9WMgmkFQqcrhPdkhbn8emFOJN1JHjbXRsOrW/XslYuM=",
291
- "MindExecution.Plugins.Directory.fqtbuqadsx.dll": "sha256-R9GNp8lYTA83muajc5V/BTbTSVuuO0ateoMiNc8O9g0=",
292
- "MindExecution.Plugins.YouTube.yo5fwdhugr.dll": "sha256-ks7QMZUaNrwB2Il1bP77dJ2jpbRv+CRuAj8v4cFvk7s="
289
+ "MindExecution.Plugins.Admin.sb1vkmct0w.dll": "sha256-iDuN0GDy4cf1SBO0F9oNo6GO3m98b3TfcSaQWtQK5q8=",
290
+ "MindExecution.Plugins.Business.zr7rkofx44.dll": "sha256-H4Q/ZDzQZhQkspAue90LyhKxSx6KmvoqYZrQYavlsN4=",
291
+ "MindExecution.Plugins.Directory.bb5flwt0u7.dll": "sha256-WbJHd3y3nymvVCvYGNYYw7/ywI8NraYmG8Y2IFch1u8=",
292
+ "MindExecution.Plugins.YouTube.l811fqx9e0.dll": "sha256-e9zKVUmEONig5ScJXvPEPBQf+MAVyfEqyCTncHnO8Y0="
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-thumb-v463';
561
+ const scriptVersion = '20260612-remote-ai-v465';
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": "kF7vg+wU",
2
+ "version": "wLEvF+gp",
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-D+RFf7H+V5VEKkPJfNGy7IiWQdR3oYnJ1ooa7i344ww=",
89
+ "hash": "sha256-hBSas6gvVUMu0hBpxyG+W6xv8+NN7JcjiSq0+C6ZjGU=",
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-2YRYhyo44+pE+OT0e2NSDCcxNji6d55ITkrE/pj7xL0=",
414
- "url": "_framework/MindExecution.Core.5luow1xgjs.dll"
413
+ "hash": "sha256-bqjP4oZY0UUVrmz+2Ctlf51S2Uo5Y/AKoPqoHy4pOnw=",
414
+ "url": "_framework/MindExecution.Core.27f2blpou6.dll"
415
415
  },
416
416
  {
417
- "hash": "sha256-ej5PzXIwPCOmBctW5KddMd2Lotqv5PPwPrVPMHRrfVc=",
418
- "url": "_framework/MindExecution.Kernel.mot9nj6bzm.dll"
417
+ "hash": "sha256-uWkVGORohTBq4U8aic2oWXUFy9SnLejWJO5jA4AYUv4=",
418
+ "url": "_framework/MindExecution.Kernel.9wfplilp5l.dll"
419
419
  },
420
420
  {
421
- "hash": "sha256-K/zYBndcqJXnUSVnQd6BwD/XxIQ3kTa9QKYfLV/grpE=",
422
- "url": "_framework/MindExecution.Plugins.Admin.x9v2drg2f7.dll"
421
+ "hash": "sha256-iDuN0GDy4cf1SBO0F9oNo6GO3m98b3TfcSaQWtQK5q8=",
422
+ "url": "_framework/MindExecution.Plugins.Admin.sb1vkmct0w.dll"
423
423
  },
424
424
  {
425
- "hash": "sha256-9WMgmkFQqcrhPdkhbn8emFOJN1JHjbXRsOrW/XslYuM=",
426
- "url": "_framework/MindExecution.Plugins.Business.b0kjoyx31x.dll"
425
+ "hash": "sha256-H4Q/ZDzQZhQkspAue90LyhKxSx6KmvoqYZrQYavlsN4=",
426
+ "url": "_framework/MindExecution.Plugins.Business.zr7rkofx44.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-pnckzioW3S/Gd8Zgwvm6KqHzCMEUAmO2pXcs1ilG2XQ=",
430
- "url": "_framework/MindExecution.Plugins.Concept.6tojojgh1a.dll"
429
+ "hash": "sha256-2DAMmcKQQlL5nDSjz2wZpO3rsJZCKV47BD19znoecJc=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.g6wd36v92i.dll"
431
431
  },
432
432
  {
433
- "hash": "sha256-R9GNp8lYTA83muajc5V/BTbTSVuuO0ateoMiNc8O9g0=",
434
- "url": "_framework/MindExecution.Plugins.Directory.fqtbuqadsx.dll"
433
+ "hash": "sha256-WbJHd3y3nymvVCvYGNYYw7/ywI8NraYmG8Y2IFch1u8=",
434
+ "url": "_framework/MindExecution.Plugins.Directory.bb5flwt0u7.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-YRqzOjhkhZKW1kpttYBKFTB9kUpO2khBvAyayRzy4YE=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.j7llfeae6l.dll"
437
+ "hash": "sha256-ThFpjeyaMI19u6kLpApau9CFMDFKu0qAXvW45raEf4A=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.me8v9fpgwc.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-ks7QMZUaNrwB2Il1bP77dJ2jpbRv+CRuAj8v4cFvk7s=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.yo5fwdhugr.dll"
441
+ "hash": "sha256-e9zKVUmEONig5ScJXvPEPBQf+MAVyfEqyCTncHnO8Y0=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.l811fqx9e0.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-iJXZ6p8LtuUPrlFwKFxkMntcxXsKARiL/YO/UWnFOBU=",
446
- "url": "_framework/MindExecution.Shared.0qi7vbn9a4.dll"
445
+ "hash": "sha256-aNNAxyXR5QAJc8/7NHb0SkPoCV/ZfKxUpI1EutwVPoU=",
446
+ "url": "_framework/MindExecution.Shared.oseamdg577.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-Zmi8Ley2/DFOiUNHrQ1NrsYzza6LRGdMdPmpZ6SlKWk=",
450
- "url": "_framework/MindExecution.Web.6cv7ad7rik.dll"
449
+ "hash": "sha256-Ua8v/da0JRYdWnLlYTo7RN8X+fLqj0Fx2ep9gtN0GS4=",
450
+ "url": "_framework/MindExecution.Web.96r3nnp9is.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-8zIv4wl1NzISQFFiuDooCDM8G/D4O+dZZVbdToxKOKU=",
773
+ "hash": "sha256-kTyP4Fsv/AOm0/MqoZ+M1/9yt5ZgNrXfJE8PEsMaTqQ=",
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-/aDhdXrjdOK/zJR6oXMIZoF8WvryE+s7wxPwDa5w9xE=",
837
+ "hash": "sha256-Cf/mSFH1jP/r7yYXdv2Fyy4jqSjg5ouxZIKPgeRRLQI=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: kF7vg+wU */
1
+ /* Manifest version: wLEvF+gp */
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