@mindexec/cli 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/remote-hub.js +429 -15
- package/scripts/remote-hub-scale-smoke.mjs +109 -0
- package/server.js +32 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +563 -6
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +3 -1
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.mjooiqft9j.dll → MindExecution.Plugins.Concept.sj3ujw4e72.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1dcrzhsegj.dll → MindExecution.Plugins.PlanMaster.ec8ccog2o3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.k75qxhbpp8.dll → MindExecution.Plugins.YouTube.mk7a07org5.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.y3eqxd3mvo.dll → MindExecution.Shared.mc9gi9kvqg.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.wou9x6mn2f.dll → MindExecution.Web.n70z8m41l3.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +11 -11
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +15 -15
- package/wwwroot/service-worker.js +1 -1
|
@@ -3401,6 +3401,7 @@
|
|
|
3401
3401
|
const MEDIA_GENERATION_STATUS_MESSAGE_METADATA_KEY = 'MediaGenerationStatusMessage';
|
|
3402
3402
|
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
3403
3403
|
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
3404
|
+
const REMOTE_FLEET_DEVICE_SEMANTIC_TYPE = 'RemoteFleetDevice';
|
|
3404
3405
|
const AUTOMATION_NODE_KIND_METADATA_KEY = 'AutomationNodeKind';
|
|
3405
3406
|
const AUTOMATION_NODE_LABEL_METADATA_KEY = 'AutomationNodeLabel';
|
|
3406
3407
|
const AUTOMATION_NODE_DESCRIPTION_METADATA_KEY = 'AutomationNodeDescription';
|
|
@@ -3574,6 +3575,16 @@
|
|
|
3574
3575
|
return getNodeSemanticType(nodeModel) === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
3575
3576
|
}
|
|
3576
3577
|
|
|
3578
|
+
function isRemoteFleetDeviceNode(nodeModel) {
|
|
3579
|
+
return getNodeSemanticType(nodeModel) === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
function isRemoteFleetNode(nodeModel) {
|
|
3583
|
+
const semanticType = getNodeSemanticType(nodeModel);
|
|
3584
|
+
return semanticType === REMOTE_FLEET_SEMANTIC_TYPE
|
|
3585
|
+
|| semanticType === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3577
3588
|
function isBusinessAutomationContextSourceNode(nodeModel) {
|
|
3578
3589
|
if (!nodeModel || isBusinessAutomationNode(nodeModel)) {
|
|
3579
3590
|
return false;
|
|
@@ -3588,7 +3599,8 @@
|
|
|
3588
3599
|
return semanticType === 'MindCanvasAgent'
|
|
3589
3600
|
|| semanticType === 'AgentCommand'
|
|
3590
3601
|
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
3591
|
-
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE
|
|
3602
|
+
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE
|
|
3603
|
+
|| semanticType === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
|
|
3592
3604
|
}
|
|
3593
3605
|
|
|
3594
3606
|
function allowsMemoExternalChrome(nodeModel) {
|
|
@@ -11845,6 +11857,11 @@
|
|
|
11845
11857
|
return;
|
|
11846
11858
|
}
|
|
11847
11859
|
|
|
11860
|
+
if (isRemoteFleetDeviceNode(nodeModel)) {
|
|
11861
|
+
renderRemoteFleetDevice(bodyView, nodeModel);
|
|
11862
|
+
return;
|
|
11863
|
+
}
|
|
11864
|
+
|
|
11848
11865
|
bodyView.classList.remove('map-node-remote-fleet__body');
|
|
11849
11866
|
bodyView.style.cssText = `
|
|
11850
11867
|
flex: 1 1 auto;
|
|
@@ -11954,6 +11971,22 @@
|
|
|
11954
11971
|
}
|
|
11955
11972
|
}
|
|
11956
11973
|
|
|
11974
|
+
function parseRemoteFleetPinnedDevice(nodeModel) {
|
|
11975
|
+
const raw = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetPinnedDeviceJson', '{}');
|
|
11976
|
+
if (!raw.trim()) {
|
|
11977
|
+
return null;
|
|
11978
|
+
}
|
|
11979
|
+
|
|
11980
|
+
try {
|
|
11981
|
+
const parsed = JSON.parse(raw);
|
|
11982
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
11983
|
+
? parsed
|
|
11984
|
+
: null;
|
|
11985
|
+
} catch {
|
|
11986
|
+
return null;
|
|
11987
|
+
}
|
|
11988
|
+
}
|
|
11989
|
+
|
|
11957
11990
|
function formatRemoteFleetNumber(value, digits = 0) {
|
|
11958
11991
|
const number = Number(value);
|
|
11959
11992
|
return Number.isFinite(number) ? number.toFixed(digits) : '-';
|
|
@@ -12300,6 +12333,501 @@
|
|
|
12300
12333
|
}
|
|
12301
12334
|
}
|
|
12302
12335
|
|
|
12336
|
+
function renderRemoteFleetDevice(bodyView, nodeModel) {
|
|
12337
|
+
if (!bodyView) return;
|
|
12338
|
+
clearRemoteFleetTimers(bodyView);
|
|
12339
|
+
|
|
12340
|
+
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
12341
|
+
const device = parseRemoteFleetPinnedDevice(nodeModel);
|
|
12342
|
+
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
12343
|
+
const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
|
|
12344
|
+
const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
|
|
12345
|
+
const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
|
|
12346
|
+
const deviceId = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetPinnedDeviceId', device ? getRemoteFleetDeviceId(device) : '');
|
|
12347
|
+
|
|
12348
|
+
bodyView.dataset.src = `remote-device:${deviceId}:${refreshedAt}`;
|
|
12349
|
+
bodyView.classList.add('map-node-remote-fleet__body');
|
|
12350
|
+
bodyView.innerHTML = '';
|
|
12351
|
+
bodyView.style.cssText = `
|
|
12352
|
+
flex: 1 1 auto;
|
|
12353
|
+
min-height: 0;
|
|
12354
|
+
pointer-events: auto;
|
|
12355
|
+
display: flex;
|
|
12356
|
+
flex-direction: column;
|
|
12357
|
+
gap: 10px;
|
|
12358
|
+
padding: 12px 14px 14px;
|
|
12359
|
+
overflow: hidden;
|
|
12360
|
+
background: linear-gradient(180deg, rgba(248, 250, 252, 0.96), rgba(236, 253, 245, 0.78));
|
|
12361
|
+
`;
|
|
12362
|
+
|
|
12363
|
+
const feedback = document.createElement('div');
|
|
12364
|
+
feedback.dataset.remoteFleetDeviceFeedback = 'true';
|
|
12365
|
+
feedback.style.cssText = `
|
|
12366
|
+
display: none;
|
|
12367
|
+
flex: 0 0 auto;
|
|
12368
|
+
min-height: 22px;
|
|
12369
|
+
padding: 5px 8px;
|
|
12370
|
+
border-radius: 7px;
|
|
12371
|
+
background: rgba(37, 99, 235, 0.08);
|
|
12372
|
+
color: #1d4ed8;
|
|
12373
|
+
font-size: 11px;
|
|
12374
|
+
font-weight: 800;
|
|
12375
|
+
line-height: 1.25;
|
|
12376
|
+
overflow: hidden;
|
|
12377
|
+
text-overflow: ellipsis;
|
|
12378
|
+
white-space: nowrap;
|
|
12379
|
+
`;
|
|
12380
|
+
const setFeedback = (message, tone = 'info') => {
|
|
12381
|
+
feedback.textContent = message || '';
|
|
12382
|
+
feedback.style.display = message ? 'block' : 'none';
|
|
12383
|
+
feedback.style.background = tone === 'error'
|
|
12384
|
+
? 'rgba(248, 113, 113, 0.12)'
|
|
12385
|
+
: tone === 'success'
|
|
12386
|
+
? 'rgba(16, 185, 129, 0.10)'
|
|
12387
|
+
: 'rgba(37, 99, 235, 0.08)';
|
|
12388
|
+
feedback.style.color = tone === 'error'
|
|
12389
|
+
? '#991b1b'
|
|
12390
|
+
: tone === 'success'
|
|
12391
|
+
? '#047857'
|
|
12392
|
+
: '#1d4ed8';
|
|
12393
|
+
};
|
|
12394
|
+
|
|
12395
|
+
const refreshRemoteDeviceNode = async () => {
|
|
12396
|
+
if (bodyView._remoteFleetRefreshInFlight === true) {
|
|
12397
|
+
return null;
|
|
12398
|
+
}
|
|
12399
|
+
|
|
12400
|
+
bodyView._remoteFleetRefreshInFlight = true;
|
|
12401
|
+
try {
|
|
12402
|
+
const result = await invokeDotNetAsync('RefreshRemoteFleetDeviceNodeFromJs', nodeId);
|
|
12403
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12404
|
+
return result;
|
|
12405
|
+
} finally {
|
|
12406
|
+
bodyView._remoteFleetRefreshInFlight = false;
|
|
12407
|
+
}
|
|
12408
|
+
};
|
|
12409
|
+
|
|
12410
|
+
if (!device || !deviceId) {
|
|
12411
|
+
const empty = document.createElement('div');
|
|
12412
|
+
empty.textContent = lastError || 'Pinned remote device snapshot is unavailable.';
|
|
12413
|
+
empty.style.cssText = `
|
|
12414
|
+
flex: 1 1 auto;
|
|
12415
|
+
display: flex;
|
|
12416
|
+
align-items: center;
|
|
12417
|
+
justify-content: center;
|
|
12418
|
+
min-height: 120px;
|
|
12419
|
+
padding: 14px;
|
|
12420
|
+
border-radius: 8px;
|
|
12421
|
+
border: 1px dashed rgba(100, 116, 139, 0.36);
|
|
12422
|
+
color: #475569;
|
|
12423
|
+
font-size: 13px;
|
|
12424
|
+
font-weight: 850;
|
|
12425
|
+
text-align: center;
|
|
12426
|
+
background: rgba(255, 255, 255, 0.74);
|
|
12427
|
+
`;
|
|
12428
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this pinned device', 'refresh-device');
|
|
12429
|
+
refreshButton.style.height = '32px';
|
|
12430
|
+
refreshButton.addEventListener('click', async event => {
|
|
12431
|
+
event.preventDefault();
|
|
12432
|
+
event.stopPropagation();
|
|
12433
|
+
refreshButton.disabled = true;
|
|
12434
|
+
try {
|
|
12435
|
+
await refreshRemoteDeviceNode();
|
|
12436
|
+
} finally {
|
|
12437
|
+
refreshButton.disabled = false;
|
|
12438
|
+
}
|
|
12439
|
+
});
|
|
12440
|
+
bodyView.appendChild(empty);
|
|
12441
|
+
bodyView.appendChild(refreshButton);
|
|
12442
|
+
bodyView.appendChild(feedback);
|
|
12443
|
+
return;
|
|
12444
|
+
}
|
|
12445
|
+
|
|
12446
|
+
const name = getRemoteFleetDeviceName(device);
|
|
12447
|
+
const connected = isRemoteFleetDeviceConnected(device);
|
|
12448
|
+
const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
|
|
12449
|
+
const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
|
|
12450
|
+
const thumbnailEnabled = isRemoteFleetThumbnailCapable(device);
|
|
12451
|
+
const liveStreamEnabled = getRemoteFleetDeviceField(device, 'liveStreamEnabled', 'LiveStreamEnabled', false) === true;
|
|
12452
|
+
const liveActive = isRemoteFleetLiveActive(device);
|
|
12453
|
+
const liveStreamId = String(getRemoteFleetDeviceField(device, 'liveStreamId', 'LiveStreamId', ''));
|
|
12454
|
+
const hasLiveFrame = hasRemoteFleetLiveFrame(device);
|
|
12455
|
+
const hasThumbnail = hasRemoteFleetThumbnail(device);
|
|
12456
|
+
const previewDataUrl = hasLiveFrame
|
|
12457
|
+
? String(getRemoteFleetDeviceField(device, 'liveFrameDataUrl', 'LiveFrameDataUrl', ''))
|
|
12458
|
+
: String(getRemoteFleetDeviceField(device, 'thumbnailDataUrl', 'ThumbnailDataUrl', ''));
|
|
12459
|
+
const previewAt = hasLiveFrame
|
|
12460
|
+
? String(getRemoteFleetDeviceField(device, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', ''))
|
|
12461
|
+
: String(getRemoteFleetDeviceField(device, 'thumbnailReceivedAt', 'ThumbnailReceivedAt',
|
|
12462
|
+
getRemoteFleetDeviceField(device, 'thumbnailCapturedAt', 'ThumbnailCapturedAt', '')));
|
|
12463
|
+
const platform = [
|
|
12464
|
+
getRemoteFleetDeviceField(device, 'platform', 'Platform', 'unknown'),
|
|
12465
|
+
getRemoteFleetDeviceField(device, 'arch', 'Arch', '')
|
|
12466
|
+
].filter(Boolean).join(' / ');
|
|
12467
|
+
const release = String(getRemoteFleetDeviceField(device, 'release', 'Release', ''));
|
|
12468
|
+
const latestTaskStatus = String(getRemoteFleetDeviceField(device, 'latestTaskStatus', 'LatestTaskStatus', ''));
|
|
12469
|
+
const latestTaskTitle = String(getRemoteFleetDeviceField(device, 'latestTaskTitle', 'LatestTaskTitle', ''));
|
|
12470
|
+
const latestTaskApproval = String(getRemoteFleetDeviceField(device, 'latestTaskApprovalLevel', 'LatestTaskApprovalLevel', ''));
|
|
12471
|
+
const latestTaskUpdatedAt = String(getRemoteFleetDeviceField(device, 'latestTaskUpdatedAt', 'LatestTaskUpdatedAt', ''));
|
|
12472
|
+
const latestTaskError = String(getRemoteFleetDeviceField(device, 'latestTaskError', 'LatestTaskError', ''));
|
|
12473
|
+
const latestTaskResult = String(getRemoteFleetDeviceField(device, 'latestTaskResultSummary', 'LatestTaskResultSummary', ''));
|
|
12474
|
+
|
|
12475
|
+
const header = document.createElement('div');
|
|
12476
|
+
header.style.cssText = 'display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex:0 0 auto;';
|
|
12477
|
+
const titleBox = document.createElement('div');
|
|
12478
|
+
titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:3px;';
|
|
12479
|
+
const title = document.createElement('strong');
|
|
12480
|
+
title.textContent = name;
|
|
12481
|
+
title.title = `${name} (${deviceId})`;
|
|
12482
|
+
title.style.cssText = 'color:#0f172a;font-size:16px;font-weight:950;line-height:1.12;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
|
|
12483
|
+
const subtitle = document.createElement('span');
|
|
12484
|
+
subtitle.textContent = `${connected ? 'Online' : 'Offline'} - ${release ? `${platform} ${release}` : platform}`;
|
|
12485
|
+
subtitle.title = subtitle.textContent;
|
|
12486
|
+
subtitle.style.cssText = `color:${connected ? '#047857' : '#64748b'};font-size:11px;font-weight:850;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
|
|
12487
|
+
titleBox.appendChild(title);
|
|
12488
|
+
titleBox.appendChild(subtitle);
|
|
12489
|
+
const statusBadge = document.createElement('span');
|
|
12490
|
+
statusBadge.textContent = liveActive ? 'LIVE' : (connected ? 'READY' : 'OFFLINE');
|
|
12491
|
+
statusBadge.style.cssText = `
|
|
12492
|
+
flex: 0 0 auto;
|
|
12493
|
+
padding: 5px 8px;
|
|
12494
|
+
border-radius: 999px;
|
|
12495
|
+
background: ${liveActive ? 'rgba(220, 38, 38, 0.12)' : connected ? 'rgba(16, 185, 129, 0.12)' : 'rgba(100, 116, 139, 0.12)'};
|
|
12496
|
+
color: ${liveActive ? '#b91c1c' : connected ? '#047857' : '#475569'};
|
|
12497
|
+
font-size: 10px;
|
|
12498
|
+
font-weight: 950;
|
|
12499
|
+
letter-spacing: 0;
|
|
12500
|
+
`;
|
|
12501
|
+
header.appendChild(titleBox);
|
|
12502
|
+
header.appendChild(statusBadge);
|
|
12503
|
+
bodyView.appendChild(header);
|
|
12504
|
+
|
|
12505
|
+
const preview = document.createElement('div');
|
|
12506
|
+
preview.style.cssText = `
|
|
12507
|
+
position: relative;
|
|
12508
|
+
flex: 0 0 auto;
|
|
12509
|
+
width: 100%;
|
|
12510
|
+
aspect-ratio: 16 / 9;
|
|
12511
|
+
overflow: hidden;
|
|
12512
|
+
border-radius: 8px;
|
|
12513
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
12514
|
+
border: 1px solid rgba(15, 23, 42, 0.16);
|
|
12515
|
+
`;
|
|
12516
|
+
if (hasLiveFrame || hasThumbnail) {
|
|
12517
|
+
const image = document.createElement('img');
|
|
12518
|
+
image.src = previewDataUrl;
|
|
12519
|
+
image.alt = `${name} screen`;
|
|
12520
|
+
image.loading = 'lazy';
|
|
12521
|
+
image.decoding = 'async';
|
|
12522
|
+
image.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;';
|
|
12523
|
+
preview.appendChild(image);
|
|
12524
|
+
} else {
|
|
12525
|
+
const placeholder = document.createElement('div');
|
|
12526
|
+
placeholder.textContent = thumbnailEnabled ? 'No frame yet' : 'Status only';
|
|
12527
|
+
placeholder.style.cssText = `
|
|
12528
|
+
position: absolute;
|
|
12529
|
+
inset: 0;
|
|
12530
|
+
display: flex;
|
|
12531
|
+
align-items: center;
|
|
12532
|
+
justify-content: center;
|
|
12533
|
+
color: rgba(226, 232, 240, 0.78);
|
|
12534
|
+
font-size: 12px;
|
|
12535
|
+
font-weight: 900;
|
|
12536
|
+
letter-spacing: 0;
|
|
12537
|
+
`;
|
|
12538
|
+
preview.appendChild(placeholder);
|
|
12539
|
+
}
|
|
12540
|
+
const previewBadge = document.createElement('span');
|
|
12541
|
+
previewBadge.textContent = hasLiveFrame
|
|
12542
|
+
? `LIVE ${previewAt ? formatRemoteFleetAge(previewAt) : ''}`.trim()
|
|
12543
|
+
: (previewAt ? formatRemoteFleetAge(previewAt) : (hubStatus === 'online' ? 'Pinned' : 'Hub offline'));
|
|
12544
|
+
previewBadge.style.cssText = `
|
|
12545
|
+
position: absolute;
|
|
12546
|
+
left: 7px;
|
|
12547
|
+
bottom: 7px;
|
|
12548
|
+
max-width: calc(100% - 14px);
|
|
12549
|
+
padding: 4px 7px;
|
|
12550
|
+
border-radius: 999px;
|
|
12551
|
+
background: ${hasLiveFrame ? 'rgba(220, 38, 38, 0.84)' : 'rgba(15, 23, 42, 0.74)'};
|
|
12552
|
+
color: #e2e8f0;
|
|
12553
|
+
font-size: 9px;
|
|
12554
|
+
font-weight: 950;
|
|
12555
|
+
line-height: 1;
|
|
12556
|
+
overflow: hidden;
|
|
12557
|
+
text-overflow: ellipsis;
|
|
12558
|
+
white-space: nowrap;
|
|
12559
|
+
letter-spacing: 0;
|
|
12560
|
+
`;
|
|
12561
|
+
preview.appendChild(previewBadge);
|
|
12562
|
+
bodyView.appendChild(preview);
|
|
12563
|
+
|
|
12564
|
+
const stats = document.createElement('div');
|
|
12565
|
+
stats.style.cssText = 'display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;flex:0 0 auto;';
|
|
12566
|
+
stats.appendChild(createRemoteFleetStat('Seen', formatRemoteFleetAge(getRemoteFleetDeviceField(device, 'lastSeenAt', 'LastSeenAt', '')), connected ? 'online' : 'default'));
|
|
12567
|
+
stats.appendChild(createRemoteFleetStat('Uptime', formatRemoteFleetDuration(getRemoteFleetDeviceField(device, 'uptimeSec', 'UptimeSec', Number.NaN))));
|
|
12568
|
+
stats.appendChild(createRemoteFleetStat('Mem', formatRemoteFleetPercent(getRemoteFleetDeviceField(device, 'usedMemRatio', 'UsedMemRatio', Number.NaN))));
|
|
12569
|
+
stats.appendChild(createRemoteFleetStat('Load', formatRemoteFleetNumber(getRemoteFleetDeviceField(device, 'load1', 'Load1', Number.NaN), 2)));
|
|
12570
|
+
bodyView.appendChild(stats);
|
|
12571
|
+
|
|
12572
|
+
if (latestTaskStatus || latestTaskTitle || latestTaskResult || latestTaskError) {
|
|
12573
|
+
const taskBox = document.createElement('div');
|
|
12574
|
+
const taskTone = latestTaskError || latestTaskStatus === 'failed'
|
|
12575
|
+
? 'error'
|
|
12576
|
+
: (latestTaskStatus === 'completed' ? 'done' : 'pending');
|
|
12577
|
+
taskBox.style.cssText = `
|
|
12578
|
+
display: flex;
|
|
12579
|
+
flex-direction: column;
|
|
12580
|
+
gap: 3px;
|
|
12581
|
+
min-width: 0;
|
|
12582
|
+
padding: 8px 9px;
|
|
12583
|
+
border-radius: 8px;
|
|
12584
|
+
background: ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.12)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
|
|
12585
|
+
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)'};
|
|
12586
|
+
flex: 0 0 auto;
|
|
12587
|
+
`;
|
|
12588
|
+
const taskLine = document.createElement('div');
|
|
12589
|
+
const taskModeLabel = latestTaskApproval === 'ai-assist' ? 'AI' : 'Task';
|
|
12590
|
+
taskLine.textContent = `${taskModeLabel} ${latestTaskStatus || 'task'}${latestTaskUpdatedAt ? ` - ${formatRemoteFleetAge(latestTaskUpdatedAt)}` : ''}`;
|
|
12591
|
+
taskLine.style.cssText = `color:${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};font-size:10px;font-weight:900;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
|
|
12592
|
+
const taskSummary = document.createElement('div');
|
|
12593
|
+
taskSummary.textContent = latestTaskError || latestTaskResult || latestTaskTitle || 'Task queued';
|
|
12594
|
+
taskSummary.title = taskSummary.textContent;
|
|
12595
|
+
taskSummary.style.cssText = 'color:#334155;font-size:11px;font-weight:750;line-height:1.3;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;letter-spacing:0;';
|
|
12596
|
+
taskBox.appendChild(taskLine);
|
|
12597
|
+
taskBox.appendChild(taskSummary);
|
|
12598
|
+
bodyView.appendChild(taskBox);
|
|
12599
|
+
}
|
|
12600
|
+
|
|
12601
|
+
const taskInput = document.createElement('textarea');
|
|
12602
|
+
taskInput.dataset.remoteFleetDeviceTaskInput = 'true';
|
|
12603
|
+
taskInput.placeholder = 'Task for this computer';
|
|
12604
|
+
taskInput.rows = 2;
|
|
12605
|
+
taskInput.style.cssText = `
|
|
12606
|
+
width: 100%;
|
|
12607
|
+
min-width: 0;
|
|
12608
|
+
height: 50px;
|
|
12609
|
+
resize: none;
|
|
12610
|
+
border-radius: 8px;
|
|
12611
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
12612
|
+
background: rgba(255, 255, 255, 0.92);
|
|
12613
|
+
color: #0f172a;
|
|
12614
|
+
padding: 8px 10px;
|
|
12615
|
+
font-size: 12px;
|
|
12616
|
+
line-height: 1.35;
|
|
12617
|
+
font-weight: 700;
|
|
12618
|
+
letter-spacing: 0;
|
|
12619
|
+
outline: none;
|
|
12620
|
+
pointer-events: auto;
|
|
12621
|
+
`;
|
|
12622
|
+
bodyView.appendChild(taskInput);
|
|
12623
|
+
|
|
12624
|
+
const actions = document.createElement('div');
|
|
12625
|
+
actions.style.cssText = 'display:flex;align-items:center;gap:7px;flex-wrap:wrap;flex:0 0 auto;';
|
|
12626
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this pinned device', 'refresh-device');
|
|
12627
|
+
refreshButton.style.height = '30px';
|
|
12628
|
+
actions.appendChild(refreshButton);
|
|
12629
|
+
if (connected && thumbnailEnabled) {
|
|
12630
|
+
const shotButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
|
|
12631
|
+
shotButton.dataset.deviceId = deviceId;
|
|
12632
|
+
shotButton.style.height = '30px';
|
|
12633
|
+
actions.appendChild(shotButton);
|
|
12634
|
+
}
|
|
12635
|
+
if (connected && liveStreamEnabled) {
|
|
12636
|
+
const liveButton = createRemoteFleetButton(liveActive ? 'Stop' : 'Live', liveActive ? 'Stop live stream' : 'Start live stream', liveActive ? 'live-stop' : 'live-start');
|
|
12637
|
+
liveButton.dataset.deviceId = deviceId;
|
|
12638
|
+
liveButton.dataset.streamId = liveStreamId;
|
|
12639
|
+
liveButton.style.height = '30px';
|
|
12640
|
+
if (liveActive) {
|
|
12641
|
+
liveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
|
|
12642
|
+
liveButton.style.color = '#b91c1c';
|
|
12643
|
+
}
|
|
12644
|
+
actions.appendChild(liveButton);
|
|
12645
|
+
}
|
|
12646
|
+
if (connected && taskEnabled) {
|
|
12647
|
+
const taskButton = createRemoteFleetButton(aiAssistEnabled ? 'Task/AI' : 'Task', 'Dispatch task to this device', 'task-device');
|
|
12648
|
+
taskButton.dataset.deviceId = deviceId;
|
|
12649
|
+
taskButton.style.height = '30px';
|
|
12650
|
+
actions.appendChild(taskButton);
|
|
12651
|
+
}
|
|
12652
|
+
if (connected) {
|
|
12653
|
+
const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
|
|
12654
|
+
pingButton.dataset.deviceId = deviceId;
|
|
12655
|
+
pingButton.style.height = '30px';
|
|
12656
|
+
actions.appendChild(pingButton);
|
|
12657
|
+
}
|
|
12658
|
+
const aiToggleLabel = document.createElement('label');
|
|
12659
|
+
aiToggleLabel.title = 'Use AI assist when this agent exposes it';
|
|
12660
|
+
aiToggleLabel.style.cssText = `
|
|
12661
|
+
display: ${aiAssistEnabled ? 'inline-flex' : 'none'};
|
|
12662
|
+
align-items: center;
|
|
12663
|
+
justify-content: center;
|
|
12664
|
+
gap: 6px;
|
|
12665
|
+
height: 30px;
|
|
12666
|
+
padding: 0 9px;
|
|
12667
|
+
border-radius: 7px;
|
|
12668
|
+
border: 1px solid rgba(14, 165, 233, 0.28);
|
|
12669
|
+
background: rgba(240, 249, 255, 0.92);
|
|
12670
|
+
color: #0369a1;
|
|
12671
|
+
font-size: 11px;
|
|
12672
|
+
font-weight: 900;
|
|
12673
|
+
letter-spacing: 0;
|
|
12674
|
+
cursor: pointer;
|
|
12675
|
+
pointer-events: auto;
|
|
12676
|
+
user-select: none;
|
|
12677
|
+
`;
|
|
12678
|
+
const aiToggle = document.createElement('input');
|
|
12679
|
+
aiToggle.type = 'checkbox';
|
|
12680
|
+
aiToggle.dataset.remoteFleetDeviceAiToggle = 'true';
|
|
12681
|
+
aiToggle.style.cssText = 'width:14px;height:14px;margin:0;accent-color:#0284c7;';
|
|
12682
|
+
const aiToggleText = document.createElement('span');
|
|
12683
|
+
aiToggleText.textContent = 'AI';
|
|
12684
|
+
aiToggleLabel.appendChild(aiToggle);
|
|
12685
|
+
aiToggleLabel.appendChild(aiToggleText);
|
|
12686
|
+
actions.appendChild(aiToggleLabel);
|
|
12687
|
+
bodyView.appendChild(actions);
|
|
12688
|
+
bodyView.appendChild(feedback);
|
|
12689
|
+
|
|
12690
|
+
const footer = document.createElement('div');
|
|
12691
|
+
footer.textContent = `Endpoint ${endpoint} - ${deviceId} - refreshed ${formatRemoteFleetAge(refreshedAt)}`;
|
|
12692
|
+
footer.title = footer.textContent;
|
|
12693
|
+
footer.style.cssText = `
|
|
12694
|
+
margin-top: auto;
|
|
12695
|
+
flex: 0 0 auto;
|
|
12696
|
+
color: #64748b;
|
|
12697
|
+
font-size: 10px;
|
|
12698
|
+
font-weight: 750;
|
|
12699
|
+
line-height: 1.2;
|
|
12700
|
+
overflow: hidden;
|
|
12701
|
+
text-overflow: ellipsis;
|
|
12702
|
+
white-space: nowrap;
|
|
12703
|
+
`;
|
|
12704
|
+
bodyView.appendChild(footer);
|
|
12705
|
+
|
|
12706
|
+
if (lastError.trim()) {
|
|
12707
|
+
setFeedback(lastError, 'error');
|
|
12708
|
+
}
|
|
12709
|
+
|
|
12710
|
+
[taskInput, aiToggleLabel, aiToggle, ...Array.from(actions.querySelectorAll('button'))].forEach(control => {
|
|
12711
|
+
if (!control) return;
|
|
12712
|
+
['mousedown', 'mouseup', 'click', 'dblclick', 'keydown'].forEach(eventName => {
|
|
12713
|
+
control.addEventListener(eventName, event => event.stopPropagation());
|
|
12714
|
+
});
|
|
12715
|
+
});
|
|
12716
|
+
|
|
12717
|
+
refreshButton.addEventListener('click', async event => {
|
|
12718
|
+
event.preventDefault();
|
|
12719
|
+
event.stopPropagation();
|
|
12720
|
+
refreshButton.disabled = true;
|
|
12721
|
+
try {
|
|
12722
|
+
await refreshRemoteDeviceNode();
|
|
12723
|
+
} finally {
|
|
12724
|
+
refreshButton.disabled = false;
|
|
12725
|
+
}
|
|
12726
|
+
});
|
|
12727
|
+
|
|
12728
|
+
actions.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').forEach(button => {
|
|
12729
|
+
button.addEventListener('click', async event => {
|
|
12730
|
+
event.preventDefault();
|
|
12731
|
+
event.stopPropagation();
|
|
12732
|
+
button.disabled = true;
|
|
12733
|
+
setFeedback('Requesting thumbnail...');
|
|
12734
|
+
try {
|
|
12735
|
+
const result = await invokeDotNetAsync('RequestRemoteFleetThumbnailFromJs', nodeId, deviceId);
|
|
12736
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12737
|
+
setFeedback(result?.success ? 'Thumbnail refreshed.' : (result?.error || 'Thumbnail failed.'), result?.success ? 'success' : 'error');
|
|
12738
|
+
} finally {
|
|
12739
|
+
button.disabled = false;
|
|
12740
|
+
}
|
|
12741
|
+
});
|
|
12742
|
+
});
|
|
12743
|
+
|
|
12744
|
+
actions.querySelectorAll('[data-remote-fleet-action="live-start"]').forEach(button => {
|
|
12745
|
+
button.addEventListener('click', async event => {
|
|
12746
|
+
event.preventDefault();
|
|
12747
|
+
event.stopPropagation();
|
|
12748
|
+
button.disabled = true;
|
|
12749
|
+
setFeedback('Starting live stream...');
|
|
12750
|
+
try {
|
|
12751
|
+
const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
|
|
12752
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12753
|
+
setFeedback(result?.success ? 'Live stream started.' : (result?.error || 'Live stream failed.'), result?.success ? 'success' : 'error');
|
|
12754
|
+
} finally {
|
|
12755
|
+
button.disabled = false;
|
|
12756
|
+
}
|
|
12757
|
+
});
|
|
12758
|
+
});
|
|
12759
|
+
|
|
12760
|
+
actions.querySelectorAll('[data-remote-fleet-action="live-stop"]').forEach(button => {
|
|
12761
|
+
button.addEventListener('click', async event => {
|
|
12762
|
+
event.preventDefault();
|
|
12763
|
+
event.stopPropagation();
|
|
12764
|
+
button.disabled = true;
|
|
12765
|
+
setFeedback('Stopping live stream...');
|
|
12766
|
+
try {
|
|
12767
|
+
const result = await invokeDotNetAsync('StopRemoteFleetLiveStreamFromJs', nodeId, deviceId, button.dataset.streamId || '');
|
|
12768
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12769
|
+
setFeedback(result?.success ? 'Live stream stopped.' : (result?.error || 'Live stop failed.'), result?.success ? 'success' : 'error');
|
|
12770
|
+
} finally {
|
|
12771
|
+
button.disabled = false;
|
|
12772
|
+
}
|
|
12773
|
+
});
|
|
12774
|
+
});
|
|
12775
|
+
|
|
12776
|
+
actions.querySelectorAll('[data-remote-fleet-action="ping-device"]').forEach(button => {
|
|
12777
|
+
button.addEventListener('click', async event => {
|
|
12778
|
+
event.preventDefault();
|
|
12779
|
+
event.stopPropagation();
|
|
12780
|
+
button.disabled = true;
|
|
12781
|
+
setFeedback('Pinging device...');
|
|
12782
|
+
try {
|
|
12783
|
+
const result = await invokeDotNetAsync('PingRemoteFleetDeviceFromJs', nodeId, deviceId);
|
|
12784
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12785
|
+
setFeedback(result?.success ? 'Ping queued.' : (result?.error || 'Ping failed.'), result?.success ? 'success' : 'error');
|
|
12786
|
+
} finally {
|
|
12787
|
+
button.disabled = false;
|
|
12788
|
+
}
|
|
12789
|
+
});
|
|
12790
|
+
});
|
|
12791
|
+
|
|
12792
|
+
actions.querySelectorAll('[data-remote-fleet-action="task-device"]').forEach(button => {
|
|
12793
|
+
button.addEventListener('click', async event => {
|
|
12794
|
+
event.preventDefault();
|
|
12795
|
+
event.stopPropagation();
|
|
12796
|
+
const instruction = String(taskInput.value || '').trim();
|
|
12797
|
+
if (!instruction) {
|
|
12798
|
+
setFeedback('Write a task first.', 'error');
|
|
12799
|
+
taskInput.focus();
|
|
12800
|
+
return;
|
|
12801
|
+
}
|
|
12802
|
+
|
|
12803
|
+
button.disabled = true;
|
|
12804
|
+
setFeedback('Dispatching task...');
|
|
12805
|
+
try {
|
|
12806
|
+
const result = await invokeDotNetAsync('DispatchRemoteFleetTaskFromJs', nodeId, deviceId, instruction, aiToggle.checked === true);
|
|
12807
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12808
|
+
setFeedback(result?.success ? 'Task queued.' : (result?.error || 'Task dispatch failed.'), result?.success ? 'success' : 'error');
|
|
12809
|
+
} finally {
|
|
12810
|
+
button.disabled = false;
|
|
12811
|
+
}
|
|
12812
|
+
});
|
|
12813
|
+
});
|
|
12814
|
+
|
|
12815
|
+
if (liveActive) {
|
|
12816
|
+
bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
|
|
12817
|
+
if (!document.body.contains(bodyView)) {
|
|
12818
|
+
clearRemoteFleetTimers(bodyView);
|
|
12819
|
+
return;
|
|
12820
|
+
}
|
|
12821
|
+
|
|
12822
|
+
try {
|
|
12823
|
+
await refreshRemoteDeviceNode();
|
|
12824
|
+
} catch {
|
|
12825
|
+
clearRemoteFleetTimers(bodyView);
|
|
12826
|
+
}
|
|
12827
|
+
}, REMOTE_FLEET_LIVE_REFRESH_MS);
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
|
|
12303
12831
|
function renderRemoteFleetMonitor(bodyView, nodeModel) {
|
|
12304
12832
|
if (!bodyView) return;
|
|
12305
12833
|
clearRemoteFleetTimers(bodyView);
|
|
@@ -13116,7 +13644,7 @@
|
|
|
13116
13644
|
}
|
|
13117
13645
|
|
|
13118
13646
|
const actions = document.createElement('div');
|
|
13119
|
-
actions.style.cssText = 'display:flex;align-items:center;
|
|
13647
|
+
actions.style.cssText = 'display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:auto;';
|
|
13120
13648
|
const status = document.createElement('span');
|
|
13121
13649
|
status.textContent = connectedDevice
|
|
13122
13650
|
? (liveStreamActive ? 'Live' : (aiAssistEnabled ? `AI ${aiModel || 'ready'}` : 'Connected'))
|
|
@@ -13131,6 +13659,13 @@
|
|
|
13131
13659
|
white-space: nowrap;
|
|
13132
13660
|
`;
|
|
13133
13661
|
actions.appendChild(status);
|
|
13662
|
+
if (deviceId) {
|
|
13663
|
+
const pinButton = createRemoteFleetButton('Pin', 'Pin this device as a canvas node', 'pin-device');
|
|
13664
|
+
pinButton.dataset.deviceId = deviceId;
|
|
13665
|
+
pinButton.style.height = '24px';
|
|
13666
|
+
pinButton.style.fontSize = '10px';
|
|
13667
|
+
actions.appendChild(pinButton);
|
|
13668
|
+
}
|
|
13134
13669
|
if (connectedDevice && deviceId) {
|
|
13135
13670
|
const focusButton = createRemoteFleetButton('Focus', 'Show this device in focused live panel', 'live-focus');
|
|
13136
13671
|
focusButton.dataset.deviceId = deviceId;
|
|
@@ -13431,6 +13966,28 @@
|
|
|
13431
13966
|
}
|
|
13432
13967
|
});
|
|
13433
13968
|
|
|
13969
|
+
grid.querySelectorAll('[data-remote-fleet-action="pin-device"]').forEach(button => {
|
|
13970
|
+
button.addEventListener('click', async event => {
|
|
13971
|
+
event.preventDefault();
|
|
13972
|
+
event.stopPropagation();
|
|
13973
|
+
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
13974
|
+
if (!deviceId) return;
|
|
13975
|
+
button.disabled = true;
|
|
13976
|
+
setTaskFeedback('Pinning device to canvas...');
|
|
13977
|
+
try {
|
|
13978
|
+
const result = await invokeDotNetAsync('AddRemoteFleetDeviceNodeFromJs', nodeId, deviceId);
|
|
13979
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
13980
|
+
if (result?.success) {
|
|
13981
|
+
setTaskFeedback('Pinned device node.', 'success');
|
|
13982
|
+
} else {
|
|
13983
|
+
setTaskFeedback(result?.error || 'Pin failed.', 'error');
|
|
13984
|
+
}
|
|
13985
|
+
} finally {
|
|
13986
|
+
button.disabled = false;
|
|
13987
|
+
}
|
|
13988
|
+
});
|
|
13989
|
+
});
|
|
13990
|
+
|
|
13434
13991
|
bodyView.querySelectorAll('[data-remote-fleet-action="live-focus"]').forEach(button => {
|
|
13435
13992
|
button.addEventListener('click', event => {
|
|
13436
13993
|
event.preventDefault();
|
|
@@ -13797,7 +14354,7 @@
|
|
|
13797
14354
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
13798
14355
|
const mindCanvasAgent = isMindCanvasAgentNode(nodeModel);
|
|
13799
14356
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
13800
|
-
const remoteFleet =
|
|
14357
|
+
const remoteFleet = isRemoteFleetNode(nodeModel);
|
|
13801
14358
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
13802
14359
|
|
|
13803
14360
|
const container = document.createElement('div');
|
|
@@ -13874,7 +14431,7 @@
|
|
|
13874
14431
|
|
|
13875
14432
|
header.appendChild(iconWrap);
|
|
13876
14433
|
header.appendChild(titleWrap);
|
|
13877
|
-
if (agentStyledMemo && !businessAutomation) {
|
|
14434
|
+
if (agentStyledMemo && !businessAutomation && !remoteFleet) {
|
|
13878
14435
|
const roleSelect = createMindCanvasAgentRoleSelect(nodeModel);
|
|
13879
14436
|
header.appendChild(roleSelect);
|
|
13880
14437
|
}
|
|
@@ -16941,7 +17498,7 @@
|
|
|
16941
17498
|
const iconKey = getMemoIconKey(nodeModel);
|
|
16942
17499
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
16943
17500
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
16944
|
-
const remoteFleet =
|
|
17501
|
+
const remoteFleet = isRemoteFleetNode(nodeModel);
|
|
16945
17502
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
16946
17503
|
|
|
16947
17504
|
memoEl.classList.toggle('map-node-agent', agentStyledMemo);
|
|
@@ -16965,7 +17522,7 @@
|
|
|
16965
17522
|
titleInput.value = nextTitle;
|
|
16966
17523
|
}
|
|
16967
17524
|
disableEditorAssist(titleInput);
|
|
16968
|
-
if (businessAutomation) {
|
|
17525
|
+
if (businessAutomation || remoteFleet) {
|
|
16969
17526
|
memoEl.querySelector('.map-node-memo__agent-role-select')?.remove();
|
|
16970
17527
|
} else {
|
|
16971
17528
|
updateMindCanvasAgentRoleSelect(memoEl, nodeModel);
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
59
59
|
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
60
|
+
const REMOTE_FLEET_DEVICE_SEMANTIC_TYPE = 'RemoteFleetDevice';
|
|
60
61
|
const AUTOMATION_INPUT_PINS_METADATA_KEY = 'AutomationInputPins';
|
|
61
62
|
const AUTOMATION_OUTPUT_PINS_METADATA_KEY = 'AutomationOutputPins';
|
|
62
63
|
const AUTOMATION_PIN_RAIL_WIDTH = 118;
|
|
@@ -103,7 +104,8 @@
|
|
|
103
104
|
return semanticType === 'MindCanvasAgent'
|
|
104
105
|
|| semanticType === 'AgentCommand'
|
|
105
106
|
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
106
|
-
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE
|
|
107
|
+
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE
|
|
108
|
+
|| semanticType === REMOTE_FLEET_DEVICE_SEMANTIC_TYPE;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
function isAgentNodeModel(nodeModel) {
|
|
Binary file
|