@mindexec/cli 0.2.2 → 0.2.3
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/README.md +38 -0
- package/package.json +6 -4
- package/remote-hub.js +571 -0
- package/scripts/remote-hub-smoke.mjs +117 -0
- package/server.js +108 -28
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +11 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +550 -2
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +3 -1
- package/wwwroot/_framework/MindExecution.Core.5luow1xgjs.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.gwwc40sc45.dll → MindExecution.Kernel.mot9nj6bzm.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.0jgrn1sckv.dll → MindExecution.Plugins.Admin.x9v2drg2f7.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.13mme2qcag.dll → MindExecution.Plugins.Business.b0kjoyx31x.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.9al2g3v3f9.dll → MindExecution.Plugins.Concept.6tojojgh1a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.3w4t6n3se0.dll → MindExecution.Plugins.Directory.fqtbuqadsx.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.vfmfbygv5y.dll → MindExecution.Plugins.PlanMaster.j7llfeae6l.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.32jyiqs383.dll → MindExecution.Plugins.YouTube.yo5fwdhugr.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.7ttmykvopx.dll → MindExecution.Shared.0qi7vbn9a4.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Web.6cv7ad7rik.dll +0 -0
- package/wwwroot/_framework/blazor.boot.json +21 -21
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +26 -26
- package/wwwroot/service-worker.js +1 -1
- package/wwwroot/_framework/MindExecution.Core.1q1trifbuu.dll +0 -0
- package/wwwroot/_framework/MindExecution.Web.ozzcqp30uy.dll +0 -0
|
@@ -3400,6 +3400,7 @@
|
|
|
3400
3400
|
const MEDIA_GENERATION_STATUS_METADATA_KEY = 'MediaGenerationStatus';
|
|
3401
3401
|
const MEDIA_GENERATION_STATUS_MESSAGE_METADATA_KEY = 'MediaGenerationStatusMessage';
|
|
3402
3402
|
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
3403
|
+
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
3403
3404
|
const AUTOMATION_NODE_KIND_METADATA_KEY = 'AutomationNodeKind';
|
|
3404
3405
|
const AUTOMATION_NODE_LABEL_METADATA_KEY = 'AutomationNodeLabel';
|
|
3405
3406
|
const AUTOMATION_NODE_DESCRIPTION_METADATA_KEY = 'AutomationNodeDescription';
|
|
@@ -3569,6 +3570,10 @@
|
|
|
3569
3570
|
return getNodeSemanticType(nodeModel) === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
3570
3571
|
}
|
|
3571
3572
|
|
|
3573
|
+
function isRemoteFleetMonitorNode(nodeModel) {
|
|
3574
|
+
return getNodeSemanticType(nodeModel) === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3572
3577
|
function isBusinessAutomationContextSourceNode(nodeModel) {
|
|
3573
3578
|
if (!nodeModel || isBusinessAutomationNode(nodeModel)) {
|
|
3574
3579
|
return false;
|
|
@@ -3582,7 +3587,8 @@
|
|
|
3582
3587
|
const semanticType = getNodeSemanticType(nodeModel);
|
|
3583
3588
|
return semanticType === 'MindCanvasAgent'
|
|
3584
3589
|
|| semanticType === 'AgentCommand'
|
|
3585
|
-
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
3590
|
+
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
3591
|
+
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
3586
3592
|
}
|
|
3587
3593
|
|
|
3588
3594
|
function allowsMemoExternalChrome(nodeModel) {
|
|
@@ -11834,6 +11840,18 @@
|
|
|
11834
11840
|
function renderMemoBodyView(bodyView, nodeModel, content) {
|
|
11835
11841
|
if (!bodyView) return;
|
|
11836
11842
|
|
|
11843
|
+
if (isRemoteFleetMonitorNode(nodeModel)) {
|
|
11844
|
+
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
11845
|
+
return;
|
|
11846
|
+
}
|
|
11847
|
+
|
|
11848
|
+
bodyView.classList.remove('map-node-remote-fleet__body');
|
|
11849
|
+
bodyView.style.cssText = `
|
|
11850
|
+
flex: 1 1 auto;
|
|
11851
|
+
min-height: 0;
|
|
11852
|
+
pointer-events: auto;
|
|
11853
|
+
`;
|
|
11854
|
+
|
|
11837
11855
|
const nextValue = String(content || '');
|
|
11838
11856
|
bodyView.dataset.src = nextValue;
|
|
11839
11857
|
|
|
@@ -11916,6 +11934,533 @@
|
|
|
11916
11934
|
}, 220);
|
|
11917
11935
|
}
|
|
11918
11936
|
|
|
11937
|
+
function getRemoteFleetMetadataValue(nodeModel, key, fallback = '') {
|
|
11938
|
+
const metadata = getNodeMetadata(nodeModel) || {};
|
|
11939
|
+
const value = metadata[key];
|
|
11940
|
+
return value === undefined || value === null ? fallback : String(value);
|
|
11941
|
+
}
|
|
11942
|
+
|
|
11943
|
+
function parseRemoteFleetDevices(nodeModel) {
|
|
11944
|
+
const raw = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDevicesJson', '[]');
|
|
11945
|
+
if (!raw.trim()) {
|
|
11946
|
+
return [];
|
|
11947
|
+
}
|
|
11948
|
+
|
|
11949
|
+
try {
|
|
11950
|
+
const parsed = JSON.parse(raw);
|
|
11951
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
11952
|
+
} catch {
|
|
11953
|
+
return [];
|
|
11954
|
+
}
|
|
11955
|
+
}
|
|
11956
|
+
|
|
11957
|
+
function formatRemoteFleetNumber(value, digits = 0) {
|
|
11958
|
+
const number = Number(value);
|
|
11959
|
+
return Number.isFinite(number) ? number.toFixed(digits) : '-';
|
|
11960
|
+
}
|
|
11961
|
+
|
|
11962
|
+
function formatRemoteFleetPercent(value) {
|
|
11963
|
+
const number = Number(value);
|
|
11964
|
+
if (!Number.isFinite(number)) {
|
|
11965
|
+
return '-';
|
|
11966
|
+
}
|
|
11967
|
+
|
|
11968
|
+
return `${Math.round(Math.max(0, Math.min(1, number)) * 100)}%`;
|
|
11969
|
+
}
|
|
11970
|
+
|
|
11971
|
+
function formatRemoteFleetDuration(seconds) {
|
|
11972
|
+
const value = Number(seconds);
|
|
11973
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
11974
|
+
return '-';
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11977
|
+
const days = Math.floor(value / 86400);
|
|
11978
|
+
const hours = Math.floor((value % 86400) / 3600);
|
|
11979
|
+
const minutes = Math.floor((value % 3600) / 60);
|
|
11980
|
+
if (days > 0) {
|
|
11981
|
+
return `${days}d ${hours}h`;
|
|
11982
|
+
}
|
|
11983
|
+
if (hours > 0) {
|
|
11984
|
+
return `${hours}h ${minutes}m`;
|
|
11985
|
+
}
|
|
11986
|
+
return `${minutes}m`;
|
|
11987
|
+
}
|
|
11988
|
+
|
|
11989
|
+
function formatRemoteFleetAge(value) {
|
|
11990
|
+
const timestamp = Date.parse(String(value || ''));
|
|
11991
|
+
if (!Number.isFinite(timestamp)) {
|
|
11992
|
+
return '-';
|
|
11993
|
+
}
|
|
11994
|
+
|
|
11995
|
+
const seconds = Math.max(0, Math.round((Date.now() - timestamp) / 1000));
|
|
11996
|
+
if (seconds < 60) {
|
|
11997
|
+
return 'now';
|
|
11998
|
+
}
|
|
11999
|
+
if (seconds < 3600) {
|
|
12000
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
12001
|
+
}
|
|
12002
|
+
if (seconds < 86400) {
|
|
12003
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
12004
|
+
}
|
|
12005
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
12006
|
+
}
|
|
12007
|
+
|
|
12008
|
+
function createRemoteFleetStat(label, value, tone = 'default') {
|
|
12009
|
+
const item = document.createElement('div');
|
|
12010
|
+
item.style.cssText = `
|
|
12011
|
+
display: flex;
|
|
12012
|
+
flex-direction: column;
|
|
12013
|
+
gap: 2px;
|
|
12014
|
+
min-width: 0;
|
|
12015
|
+
padding: 8px 10px;
|
|
12016
|
+
border-radius: 8px;
|
|
12017
|
+
background: ${tone === 'online' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(15, 23, 42, 0.05)'};
|
|
12018
|
+
border: 1px solid ${tone === 'online' ? 'rgba(16, 185, 129, 0.22)' : 'rgba(148, 163, 184, 0.22)'};
|
|
12019
|
+
`;
|
|
12020
|
+
|
|
12021
|
+
const labelEl = document.createElement('span');
|
|
12022
|
+
labelEl.textContent = label;
|
|
12023
|
+
labelEl.style.cssText = `
|
|
12024
|
+
color: #64748b;
|
|
12025
|
+
font-size: 10px;
|
|
12026
|
+
font-weight: 800;
|
|
12027
|
+
letter-spacing: 0;
|
|
12028
|
+
text-transform: uppercase;
|
|
12029
|
+
`;
|
|
12030
|
+
|
|
12031
|
+
const valueEl = document.createElement('strong');
|
|
12032
|
+
valueEl.textContent = value;
|
|
12033
|
+
valueEl.style.cssText = `
|
|
12034
|
+
color: ${tone === 'online' ? '#047857' : '#0f172a'};
|
|
12035
|
+
font-size: 18px;
|
|
12036
|
+
line-height: 1.1;
|
|
12037
|
+
font-weight: 900;
|
|
12038
|
+
letter-spacing: 0;
|
|
12039
|
+
overflow: hidden;
|
|
12040
|
+
text-overflow: ellipsis;
|
|
12041
|
+
white-space: nowrap;
|
|
12042
|
+
`;
|
|
12043
|
+
|
|
12044
|
+
item.appendChild(labelEl);
|
|
12045
|
+
item.appendChild(valueEl);
|
|
12046
|
+
return item;
|
|
12047
|
+
}
|
|
12048
|
+
|
|
12049
|
+
function createRemoteFleetButton(label, title, action) {
|
|
12050
|
+
const button = document.createElement('button');
|
|
12051
|
+
button.type = 'button';
|
|
12052
|
+
button.dataset.remoteFleetAction = action;
|
|
12053
|
+
button.textContent = label;
|
|
12054
|
+
button.title = title || label;
|
|
12055
|
+
button.setAttribute('aria-label', title || label);
|
|
12056
|
+
button.style.cssText = `
|
|
12057
|
+
display: inline-flex;
|
|
12058
|
+
align-items: center;
|
|
12059
|
+
justify-content: center;
|
|
12060
|
+
min-width: 0;
|
|
12061
|
+
height: 28px;
|
|
12062
|
+
padding: 0 10px;
|
|
12063
|
+
border-radius: 7px;
|
|
12064
|
+
border: 1px solid rgba(37, 99, 235, 0.32);
|
|
12065
|
+
background: #ffffff;
|
|
12066
|
+
color: #1d4ed8;
|
|
12067
|
+
font-size: 11px;
|
|
12068
|
+
font-weight: 800;
|
|
12069
|
+
letter-spacing: 0;
|
|
12070
|
+
cursor: pointer;
|
|
12071
|
+
pointer-events: auto;
|
|
12072
|
+
`;
|
|
12073
|
+
return button;
|
|
12074
|
+
}
|
|
12075
|
+
|
|
12076
|
+
async function syncRemoteFleetNodeStateFromResult(result) {
|
|
12077
|
+
const nodeState = result?.nodeState || result?.refresh?.nodeState;
|
|
12078
|
+
if (nodeState && window.mindMap?.syncNodeStates) {
|
|
12079
|
+
await window.mindMap.syncNodeStates([nodeState]);
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
|
|
12083
|
+
function renderRemoteFleetMonitor(bodyView, nodeModel) {
|
|
12084
|
+
if (!bodyView) return;
|
|
12085
|
+
|
|
12086
|
+
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
12087
|
+
const devices = parseRemoteFleetDevices(nodeModel);
|
|
12088
|
+
const total = Number(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDeviceCount', devices.length));
|
|
12089
|
+
const connected = Number(getRemoteFleetMetadataValue(
|
|
12090
|
+
nodeModel,
|
|
12091
|
+
'RemoteFleetConnectedDeviceCount',
|
|
12092
|
+
devices.filter(device => device?.connected === true || device?.Connected === true).length));
|
|
12093
|
+
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
12094
|
+
const command = getRemoteFleetMetadataValue(
|
|
12095
|
+
nodeModel,
|
|
12096
|
+
'RemoteFleetConnectCommand',
|
|
12097
|
+
`npx @mindexec/remote connect --manager ${endpoint} --pair <pair-token>`);
|
|
12098
|
+
const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
|
|
12099
|
+
const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
|
|
12100
|
+
const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
|
|
12101
|
+
|
|
12102
|
+
bodyView.dataset.src = `remote-fleet:${refreshedAt}:${devices.length}:${connected}`;
|
|
12103
|
+
bodyView.classList.add('map-node-remote-fleet__body');
|
|
12104
|
+
bodyView.innerHTML = '';
|
|
12105
|
+
bodyView.style.cssText = `
|
|
12106
|
+
flex: 1 1 auto;
|
|
12107
|
+
min-height: 0;
|
|
12108
|
+
pointer-events: auto;
|
|
12109
|
+
display: flex;
|
|
12110
|
+
flex-direction: column;
|
|
12111
|
+
gap: 10px;
|
|
12112
|
+
padding: 12px 14px 14px;
|
|
12113
|
+
overflow: hidden;
|
|
12114
|
+
background: linear-gradient(180deg, rgba(248, 250, 252, 0.96), rgba(241, 245, 249, 0.92));
|
|
12115
|
+
`;
|
|
12116
|
+
|
|
12117
|
+
const top = document.createElement('div');
|
|
12118
|
+
top.style.cssText = `
|
|
12119
|
+
display: grid;
|
|
12120
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
12121
|
+
gap: 8px;
|
|
12122
|
+
flex: 0 0 auto;
|
|
12123
|
+
`;
|
|
12124
|
+
top.appendChild(createRemoteFleetStat('Hub', hubStatus === 'online' ? 'Online' : 'Offline', hubStatus === 'online' ? 'online' : 'default'));
|
|
12125
|
+
top.appendChild(createRemoteFleetStat('Connected', `${Number.isFinite(connected) ? connected : 0}/${Number.isFinite(total) ? total : devices.length}`, connected > 0 ? 'online' : 'default'));
|
|
12126
|
+
top.appendChild(createRemoteFleetStat('Mode', 'All', 'default'));
|
|
12127
|
+
top.appendChild(createRemoteFleetStat('Paging', 'None', 'default'));
|
|
12128
|
+
bodyView.appendChild(top);
|
|
12129
|
+
|
|
12130
|
+
const commandRow = document.createElement('div');
|
|
12131
|
+
commandRow.style.cssText = `
|
|
12132
|
+
display: grid;
|
|
12133
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
12134
|
+
gap: 8px;
|
|
12135
|
+
align-items: center;
|
|
12136
|
+
flex: 0 0 auto;
|
|
12137
|
+
`;
|
|
12138
|
+
|
|
12139
|
+
const commandText = document.createElement('code');
|
|
12140
|
+
commandText.textContent = command;
|
|
12141
|
+
commandText.style.cssText = `
|
|
12142
|
+
min-width: 0;
|
|
12143
|
+
overflow: hidden;
|
|
12144
|
+
text-overflow: ellipsis;
|
|
12145
|
+
white-space: nowrap;
|
|
12146
|
+
padding: 8px 10px;
|
|
12147
|
+
border-radius: 7px;
|
|
12148
|
+
background: rgba(15, 23, 42, 0.92);
|
|
12149
|
+
color: #e2e8f0;
|
|
12150
|
+
font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
|
|
12151
|
+
font-size: 11px;
|
|
12152
|
+
line-height: 1.2;
|
|
12153
|
+
`;
|
|
12154
|
+
commandText.title = command;
|
|
12155
|
+
|
|
12156
|
+
const copyButton = createRemoteFleetButton('Copy', 'Copy agent command', 'copy-command');
|
|
12157
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh remote devices', 'refresh');
|
|
12158
|
+
commandRow.appendChild(commandText);
|
|
12159
|
+
commandRow.appendChild(copyButton);
|
|
12160
|
+
commandRow.appendChild(refreshButton);
|
|
12161
|
+
bodyView.appendChild(commandRow);
|
|
12162
|
+
|
|
12163
|
+
if (lastError.trim()) {
|
|
12164
|
+
const errorEl = document.createElement('div');
|
|
12165
|
+
errorEl.textContent = lastError;
|
|
12166
|
+
errorEl.style.cssText = `
|
|
12167
|
+
flex: 0 0 auto;
|
|
12168
|
+
padding: 8px 10px;
|
|
12169
|
+
border-radius: 7px;
|
|
12170
|
+
background: rgba(248, 113, 113, 0.12);
|
|
12171
|
+
color: #991b1b;
|
|
12172
|
+
font-size: 12px;
|
|
12173
|
+
line-height: 1.35;
|
|
12174
|
+
font-weight: 700;
|
|
12175
|
+
overflow-wrap: break-word;
|
|
12176
|
+
`;
|
|
12177
|
+
bodyView.appendChild(errorEl);
|
|
12178
|
+
}
|
|
12179
|
+
|
|
12180
|
+
const grid = document.createElement('div');
|
|
12181
|
+
grid.style.cssText = `
|
|
12182
|
+
flex: 1 1 auto;
|
|
12183
|
+
min-height: 0;
|
|
12184
|
+
overflow-y: auto;
|
|
12185
|
+
overflow-x: hidden;
|
|
12186
|
+
display: grid;
|
|
12187
|
+
grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
|
|
12188
|
+
align-content: start;
|
|
12189
|
+
gap: 8px;
|
|
12190
|
+
padding-right: 4px;
|
|
12191
|
+
`;
|
|
12192
|
+
|
|
12193
|
+
if (devices.length === 0) {
|
|
12194
|
+
const empty = document.createElement('div');
|
|
12195
|
+
empty.textContent = 'No devices connected yet.';
|
|
12196
|
+
empty.style.cssText = `
|
|
12197
|
+
grid-column: 1 / -1;
|
|
12198
|
+
display: flex;
|
|
12199
|
+
align-items: center;
|
|
12200
|
+
min-height: 94px;
|
|
12201
|
+
padding: 14px;
|
|
12202
|
+
border-radius: 8px;
|
|
12203
|
+
border: 1px dashed rgba(100, 116, 139, 0.36);
|
|
12204
|
+
color: #475569;
|
|
12205
|
+
font-size: 13px;
|
|
12206
|
+
font-weight: 800;
|
|
12207
|
+
background: rgba(255, 255, 255, 0.74);
|
|
12208
|
+
`;
|
|
12209
|
+
grid.appendChild(empty);
|
|
12210
|
+
} else {
|
|
12211
|
+
devices.forEach(device => {
|
|
12212
|
+
const connectedDevice = device?.connected === true || device?.Connected === true;
|
|
12213
|
+
const name = String(device?.name || device?.Name || device?.hostname || device?.Hostname || device?.deviceId || device?.DeviceId || 'device');
|
|
12214
|
+
const platform = [device?.platform || device?.Platform, device?.arch || device?.Arch]
|
|
12215
|
+
.filter(Boolean)
|
|
12216
|
+
.join(' / ') || 'unknown';
|
|
12217
|
+
const release = String(device?.release || device?.Release || '');
|
|
12218
|
+
const deviceId = String(device?.deviceId || device?.DeviceId || '');
|
|
12219
|
+
const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
|
|
12220
|
+
const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
|
|
12221
|
+
const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
|
|
12222
|
+
const hasThumbnail = /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(thumbnailDataUrl);
|
|
12223
|
+
const card = document.createElement('article');
|
|
12224
|
+
card.dataset.deviceId = deviceId;
|
|
12225
|
+
card.style.cssText = `
|
|
12226
|
+
display: flex;
|
|
12227
|
+
flex-direction: column;
|
|
12228
|
+
gap: 8px;
|
|
12229
|
+
min-width: 0;
|
|
12230
|
+
min-height: 134px;
|
|
12231
|
+
padding: 10px;
|
|
12232
|
+
border-radius: 8px;
|
|
12233
|
+
background: #ffffff;
|
|
12234
|
+
border: 1px solid ${connectedDevice ? 'rgba(16, 185, 129, 0.34)' : 'rgba(148, 163, 184, 0.28)'};
|
|
12235
|
+
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.06);
|
|
12236
|
+
`;
|
|
12237
|
+
|
|
12238
|
+
const preview = document.createElement('div');
|
|
12239
|
+
preview.style.cssText = `
|
|
12240
|
+
position: relative;
|
|
12241
|
+
width: 100%;
|
|
12242
|
+
aspect-ratio: 16 / 9;
|
|
12243
|
+
overflow: hidden;
|
|
12244
|
+
border-radius: 7px;
|
|
12245
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
12246
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
12247
|
+
`;
|
|
12248
|
+
if (hasThumbnail) {
|
|
12249
|
+
const image = document.createElement('img');
|
|
12250
|
+
image.src = thumbnailDataUrl;
|
|
12251
|
+
image.alt = `${name} thumbnail`;
|
|
12252
|
+
image.loading = 'lazy';
|
|
12253
|
+
image.decoding = 'async';
|
|
12254
|
+
image.style.cssText = `
|
|
12255
|
+
width: 100%;
|
|
12256
|
+
height: 100%;
|
|
12257
|
+
object-fit: cover;
|
|
12258
|
+
display: block;
|
|
12259
|
+
`;
|
|
12260
|
+
preview.appendChild(image);
|
|
12261
|
+
} else {
|
|
12262
|
+
const placeholder = document.createElement('div');
|
|
12263
|
+
placeholder.textContent = thumbnailEnabled ? 'No frame yet' : 'Status only';
|
|
12264
|
+
placeholder.style.cssText = `
|
|
12265
|
+
position: absolute;
|
|
12266
|
+
inset: 0;
|
|
12267
|
+
display: flex;
|
|
12268
|
+
align-items: center;
|
|
12269
|
+
justify-content: center;
|
|
12270
|
+
color: rgba(226, 232, 240, 0.78);
|
|
12271
|
+
font-size: 11px;
|
|
12272
|
+
font-weight: 900;
|
|
12273
|
+
letter-spacing: 0;
|
|
12274
|
+
`;
|
|
12275
|
+
preview.appendChild(placeholder);
|
|
12276
|
+
}
|
|
12277
|
+
if (thumbnailCapturedAt) {
|
|
12278
|
+
const badge = document.createElement('span');
|
|
12279
|
+
badge.textContent = formatRemoteFleetAge(thumbnailCapturedAt);
|
|
12280
|
+
badge.style.cssText = `
|
|
12281
|
+
position: absolute;
|
|
12282
|
+
right: 6px;
|
|
12283
|
+
bottom: 6px;
|
|
12284
|
+
max-width: calc(100% - 12px);
|
|
12285
|
+
padding: 3px 6px;
|
|
12286
|
+
border-radius: 999px;
|
|
12287
|
+
background: rgba(15, 23, 42, 0.74);
|
|
12288
|
+
color: #e2e8f0;
|
|
12289
|
+
font-size: 9px;
|
|
12290
|
+
font-weight: 900;
|
|
12291
|
+
line-height: 1;
|
|
12292
|
+
overflow: hidden;
|
|
12293
|
+
text-overflow: ellipsis;
|
|
12294
|
+
white-space: nowrap;
|
|
12295
|
+
`;
|
|
12296
|
+
preview.appendChild(badge);
|
|
12297
|
+
}
|
|
12298
|
+
card.appendChild(preview);
|
|
12299
|
+
|
|
12300
|
+
const cardHeader = document.createElement('div');
|
|
12301
|
+
cardHeader.style.cssText = 'display:flex;align-items:flex-start;gap:8px;min-width:0;';
|
|
12302
|
+
const dot = document.createElement('span');
|
|
12303
|
+
dot.style.cssText = `
|
|
12304
|
+
flex: 0 0 auto;
|
|
12305
|
+
width: 9px;
|
|
12306
|
+
height: 9px;
|
|
12307
|
+
margin-top: 4px;
|
|
12308
|
+
border-radius: 999px;
|
|
12309
|
+
background: ${connectedDevice ? '#10b981' : '#94a3b8'};
|
|
12310
|
+
box-shadow: 0 0 0 4px ${connectedDevice ? 'rgba(16,185,129,0.14)' : 'rgba(148,163,184,0.14)'};
|
|
12311
|
+
`;
|
|
12312
|
+
|
|
12313
|
+
const titleBox = document.createElement('div');
|
|
12314
|
+
titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:2px;';
|
|
12315
|
+
const title = document.createElement('strong');
|
|
12316
|
+
title.textContent = name;
|
|
12317
|
+
title.title = name;
|
|
12318
|
+
title.style.cssText = `
|
|
12319
|
+
color: #0f172a;
|
|
12320
|
+
font-size: 13px;
|
|
12321
|
+
font-weight: 900;
|
|
12322
|
+
line-height: 1.2;
|
|
12323
|
+
overflow: hidden;
|
|
12324
|
+
text-overflow: ellipsis;
|
|
12325
|
+
white-space: nowrap;
|
|
12326
|
+
letter-spacing: 0;
|
|
12327
|
+
`;
|
|
12328
|
+
const subtitle = document.createElement('span');
|
|
12329
|
+
subtitle.textContent = release ? `${platform} ${release}` : platform;
|
|
12330
|
+
subtitle.title = subtitle.textContent;
|
|
12331
|
+
subtitle.style.cssText = `
|
|
12332
|
+
color: #64748b;
|
|
12333
|
+
font-size: 11px;
|
|
12334
|
+
line-height: 1.2;
|
|
12335
|
+
overflow: hidden;
|
|
12336
|
+
text-overflow: ellipsis;
|
|
12337
|
+
white-space: nowrap;
|
|
12338
|
+
letter-spacing: 0;
|
|
12339
|
+
`;
|
|
12340
|
+
titleBox.appendChild(title);
|
|
12341
|
+
titleBox.appendChild(subtitle);
|
|
12342
|
+
cardHeader.appendChild(dot);
|
|
12343
|
+
cardHeader.appendChild(titleBox);
|
|
12344
|
+
card.appendChild(cardHeader);
|
|
12345
|
+
|
|
12346
|
+
const metrics = document.createElement('div');
|
|
12347
|
+
metrics.style.cssText = `
|
|
12348
|
+
display: grid;
|
|
12349
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
12350
|
+
gap: 6px;
|
|
12351
|
+
`;
|
|
12352
|
+
const addMetric = (label, value) => {
|
|
12353
|
+
const metric = document.createElement('div');
|
|
12354
|
+
metric.style.cssText = `
|
|
12355
|
+
min-width: 0;
|
|
12356
|
+
padding: 6px 7px;
|
|
12357
|
+
border-radius: 7px;
|
|
12358
|
+
background: rgba(241, 245, 249, 0.84);
|
|
12359
|
+
`;
|
|
12360
|
+
const labelEl = document.createElement('div');
|
|
12361
|
+
labelEl.textContent = label;
|
|
12362
|
+
labelEl.style.cssText = 'color:#64748b;font-size:9px;font-weight:800;letter-spacing:0;text-transform:uppercase;';
|
|
12363
|
+
const valueEl = document.createElement('div');
|
|
12364
|
+
valueEl.textContent = value;
|
|
12365
|
+
valueEl.title = value;
|
|
12366
|
+
valueEl.style.cssText = 'color:#0f172a;font-size:12px;font-weight:900;letter-spacing:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
|
|
12367
|
+
metric.appendChild(labelEl);
|
|
12368
|
+
metric.appendChild(valueEl);
|
|
12369
|
+
metrics.appendChild(metric);
|
|
12370
|
+
};
|
|
12371
|
+
addMetric('Seen', formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt));
|
|
12372
|
+
addMetric('Uptime', formatRemoteFleetDuration(device?.uptimeSec ?? device?.UptimeSec));
|
|
12373
|
+
addMetric('Mem', formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio));
|
|
12374
|
+
addMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
|
|
12375
|
+
card.appendChild(metrics);
|
|
12376
|
+
|
|
12377
|
+
const actions = document.createElement('div');
|
|
12378
|
+
actions.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;';
|
|
12379
|
+
const status = document.createElement('span');
|
|
12380
|
+
status.textContent = connectedDevice ? 'Connected' : 'Offline';
|
|
12381
|
+
status.style.cssText = `
|
|
12382
|
+
min-width: 0;
|
|
12383
|
+
color: ${connectedDevice ? '#047857' : '#64748b'};
|
|
12384
|
+
font-size: 11px;
|
|
12385
|
+
font-weight: 900;
|
|
12386
|
+
overflow: hidden;
|
|
12387
|
+
text-overflow: ellipsis;
|
|
12388
|
+
white-space: nowrap;
|
|
12389
|
+
`;
|
|
12390
|
+
actions.appendChild(status);
|
|
12391
|
+
if (connectedDevice && deviceId) {
|
|
12392
|
+
if (thumbnailEnabled) {
|
|
12393
|
+
const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
|
|
12394
|
+
thumbnailButton.dataset.deviceId = deviceId;
|
|
12395
|
+
thumbnailButton.style.height = '24px';
|
|
12396
|
+
thumbnailButton.style.fontSize = '10px';
|
|
12397
|
+
actions.appendChild(thumbnailButton);
|
|
12398
|
+
}
|
|
12399
|
+
const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
|
|
12400
|
+
pingButton.dataset.deviceId = deviceId;
|
|
12401
|
+
pingButton.style.height = '24px';
|
|
12402
|
+
pingButton.style.fontSize = '10px';
|
|
12403
|
+
actions.appendChild(pingButton);
|
|
12404
|
+
}
|
|
12405
|
+
card.appendChild(actions);
|
|
12406
|
+
grid.appendChild(card);
|
|
12407
|
+
});
|
|
12408
|
+
}
|
|
12409
|
+
|
|
12410
|
+
bodyView.appendChild(grid);
|
|
12411
|
+
|
|
12412
|
+
const footer = document.createElement('div');
|
|
12413
|
+
footer.textContent = `Endpoint ${endpoint} · refreshed ${formatRemoteFleetAge(refreshedAt)}`;
|
|
12414
|
+
footer.style.cssText = `
|
|
12415
|
+
flex: 0 0 auto;
|
|
12416
|
+
color: #64748b;
|
|
12417
|
+
font-size: 11px;
|
|
12418
|
+
font-weight: 700;
|
|
12419
|
+
line-height: 1.2;
|
|
12420
|
+
overflow: hidden;
|
|
12421
|
+
text-overflow: ellipsis;
|
|
12422
|
+
white-space: nowrap;
|
|
12423
|
+
`;
|
|
12424
|
+
bodyView.appendChild(footer);
|
|
12425
|
+
|
|
12426
|
+
copyButton.addEventListener('click', event => {
|
|
12427
|
+
event.preventDefault();
|
|
12428
|
+
event.stopPropagation();
|
|
12429
|
+
navigator.clipboard?.writeText?.(command).catch(() => { });
|
|
12430
|
+
});
|
|
12431
|
+
|
|
12432
|
+
refreshButton.addEventListener('click', async event => {
|
|
12433
|
+
event.preventDefault();
|
|
12434
|
+
event.stopPropagation();
|
|
12435
|
+
refreshButton.disabled = true;
|
|
12436
|
+
const result = await invokeDotNetAsync('RefreshRemoteFleetMonitorNodeFromJs', nodeId);
|
|
12437
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12438
|
+
refreshButton.disabled = false;
|
|
12439
|
+
});
|
|
12440
|
+
|
|
12441
|
+
grid.querySelectorAll('[data-remote-fleet-action="ping-device"]').forEach(button => {
|
|
12442
|
+
button.addEventListener('click', async event => {
|
|
12443
|
+
event.preventDefault();
|
|
12444
|
+
event.stopPropagation();
|
|
12445
|
+
button.disabled = true;
|
|
12446
|
+
const result = await invokeDotNetAsync('PingRemoteFleetDeviceFromJs', nodeId, button.dataset.deviceId || '');
|
|
12447
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12448
|
+
button.disabled = false;
|
|
12449
|
+
});
|
|
12450
|
+
});
|
|
12451
|
+
|
|
12452
|
+
grid.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').forEach(button => {
|
|
12453
|
+
button.addEventListener('click', async event => {
|
|
12454
|
+
event.preventDefault();
|
|
12455
|
+
event.stopPropagation();
|
|
12456
|
+
button.disabled = true;
|
|
12457
|
+
const result = await invokeDotNetAsync('RequestRemoteFleetThumbnailFromJs', nodeId, button.dataset.deviceId || '');
|
|
12458
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12459
|
+
button.disabled = false;
|
|
12460
|
+
});
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
12463
|
+
|
|
11919
12464
|
function bindMemoNodeEvents(container, nodeModel) {
|
|
11920
12465
|
if (!container || !nodeModel) return;
|
|
11921
12466
|
ensureMindCanvasAgentConsoleResize(container, nodeModel);
|
|
@@ -12147,11 +12692,12 @@
|
|
|
12147
12692
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
12148
12693
|
const mindCanvasAgent = isMindCanvasAgentNode(nodeModel);
|
|
12149
12694
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
12695
|
+
const remoteFleet = isRemoteFleetMonitorNode(nodeModel);
|
|
12150
12696
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
12151
12697
|
|
|
12152
12698
|
const container = document.createElement('div');
|
|
12153
12699
|
container.id = `node-${nodeModel.id}`;
|
|
12154
|
-
container.className = `map-node css3d-dynamic-node map-node-memo${agentStyledMemo ? ' map-node-agent' : ''}${businessAutomation ? ' map-node-automation' : ''}`;
|
|
12700
|
+
container.className = `map-node css3d-dynamic-node map-node-memo${agentStyledMemo ? ' map-node-agent' : ''}${businessAutomation ? ' map-node-automation' : ''}${remoteFleet ? ' map-node-remote-fleet' : ''}`;
|
|
12155
12701
|
container.dataset.nodeId = nodeModel.id;
|
|
12156
12702
|
container.dataset.semanticType = getNodeSemanticType(nodeModel);
|
|
12157
12703
|
container.dataset.agentConsoleOpen = isMindCanvasAgentConsoleOpen(nodeModel) ? 'true' : 'false';
|
|
@@ -15290,10 +15836,12 @@
|
|
|
15290
15836
|
const iconKey = getMemoIconKey(nodeModel);
|
|
15291
15837
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
15292
15838
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
15839
|
+
const remoteFleet = isRemoteFleetMonitorNode(nodeModel);
|
|
15293
15840
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
15294
15841
|
|
|
15295
15842
|
memoEl.classList.toggle('map-node-agent', agentStyledMemo);
|
|
15296
15843
|
memoEl.classList.toggle('map-node-automation', businessAutomation);
|
|
15844
|
+
memoEl.classList.toggle('map-node-remote-fleet', remoteFleet);
|
|
15297
15845
|
memoEl.dataset.semanticType = getNodeSemanticType(nodeModel);
|
|
15298
15846
|
memoEl.dataset.agentPlanOpen = getRenderedMindCanvasAgentPlanOpen(memoEl, nodeModel) ? 'true' : 'false';
|
|
15299
15847
|
memoEl.dataset.agentConsoleOpen = getRenderedMindCanvasAgentConsoleOpen(memoEl, nodeModel) ? 'true' : 'false';
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
59
|
+
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
59
60
|
const AUTOMATION_INPUT_PINS_METADATA_KEY = 'AutomationInputPins';
|
|
60
61
|
const AUTOMATION_OUTPUT_PINS_METADATA_KEY = 'AutomationOutputPins';
|
|
61
62
|
const AUTOMATION_PIN_RAIL_WIDTH = 118;
|
|
@@ -101,7 +102,8 @@
|
|
|
101
102
|
const semanticType = getNodeSemanticType(nodeModel);
|
|
102
103
|
return semanticType === 'MindCanvasAgent'
|
|
103
104
|
|| semanticType === 'AgentCommand'
|
|
104
|
-
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
105
|
+
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
106
|
+
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
function isAgentNodeModel(nodeModel) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|