@mindexec/cli 0.2.445 → 0.2.446
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 +5 -2
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +170 -5
- package/scripts/remote-fast-live-rate-smoke.mjs +1 -0
- package/server.js +2 -2
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +165 -10
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +47 -58
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4 -24
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +31 -18
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +6 -3
- package/wwwroot/_framework/{MindExecution.Core.4moacskf64.dll → MindExecution.Core.djyzj5mxyk.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.pa251cfsz4.dll → MindExecution.Kernel.6e5cj9aijz.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.dxyuwmxv1s.dll → MindExecution.Plugins.Admin.siulh9tfrf.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.7uj5erqhhn.dll → MindExecution.Plugins.Business.8cjtrc7qd3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.108rmsxc96.dll → MindExecution.Plugins.Concept.rpp78mc75u.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.ccsy3g0fe5.dll → MindExecution.Plugins.Directory.wfv0ff9v4u.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll → MindExecution.Plugins.PlanMaster.jdppnbd6g4.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.q8ml98phj9.dll → MindExecution.Plugins.YouTube.a43f2q3pkw.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.2pywq19zjy.dll → MindExecution.Shared.gwtyj0e9lw.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.qucbls1wfr.dll → MindExecution.Web.j3zaapcxtt.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +21 -21
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +28 -28
- package/wwwroot/service-worker.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindexec/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.446",
|
|
4
4
|
"description": "MindExec local runtime and bridge CLI",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -71,10 +71,13 @@
|
|
|
71
71
|
"ffmpeg-static": "^5.3.0",
|
|
72
72
|
"multer": "^2.2.0",
|
|
73
73
|
"playwright-core": "^1.53.0",
|
|
74
|
-
"sharp": "^0.
|
|
74
|
+
"sharp": "^0.35.3",
|
|
75
75
|
"web-tree-sitter": "^0.22.6",
|
|
76
76
|
"ws": "^8.16.0"
|
|
77
77
|
},
|
|
78
|
+
"overrides": {
|
|
79
|
+
"body-parser": "1.20.6"
|
|
80
|
+
},
|
|
78
81
|
"bin": {
|
|
79
82
|
"mindexec": "launch-bridge.cjs",
|
|
80
83
|
"mind-bridge": "launch-bridge.cjs"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/remote-hub.js
CHANGED
|
@@ -212,6 +212,30 @@ function getSupportedRemoteFrameModeProfiles() {
|
|
|
212
212
|
.map(serializeRemoteFrameModeProfile);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
function resolveDeviceRemoteFrameMode(device, requestedMode, fallbackMode = DEFAULT_REMOTE_FRAME_MODE) {
|
|
216
|
+
const requested = normalizeRemoteFrameMode(requestedMode, fallbackMode);
|
|
217
|
+
if (device?.frameModesAdvertised === false) {
|
|
218
|
+
return normalizeRemoteFrameMode(device?.frameProtocol?.defaultMode, DEFAULT_REMOTE_FRAME_MODE);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const supportedModes = new Set(
|
|
222
|
+
(Array.isArray(device?.frameModes) ? device.frameModes : [])
|
|
223
|
+
.filter(profile => profile?.implemented !== false)
|
|
224
|
+
.map(profile => normalizeRemoteFrameMode(profile?.mode || profile?.frameMode || profile?.profile, ''))
|
|
225
|
+
.filter(Boolean));
|
|
226
|
+
|
|
227
|
+
if (supportedModes.size === 0 || supportedModes.has(requested)) {
|
|
228
|
+
return requested;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const deviceDefault = normalizeRemoteFrameMode(device?.frameProtocol?.defaultMode, fallbackMode);
|
|
232
|
+
if (supportedModes.has(deviceDefault)) {
|
|
233
|
+
return deviceDefault;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return supportedModes.values().next().value || normalizeRemoteFrameMode(fallbackMode);
|
|
237
|
+
}
|
|
238
|
+
|
|
215
239
|
function buildRemoteFrameProtocolDescriptor() {
|
|
216
240
|
return {
|
|
217
241
|
protocol: REMOTE_FRAME_PROTOCOL,
|
|
@@ -1125,6 +1149,12 @@ export function createRemoteHub(options = {}) {
|
|
|
1125
1149
|
const taskBatches = new Map();
|
|
1126
1150
|
const sockets = new Map();
|
|
1127
1151
|
const allSockets = new Set();
|
|
1152
|
+
const sideChannelSockets = {
|
|
1153
|
+
input: new Map(),
|
|
1154
|
+
frame: new Map(),
|
|
1155
|
+
audio: new Map(),
|
|
1156
|
+
file: new Map()
|
|
1157
|
+
};
|
|
1128
1158
|
const duplicateDeviceLogAt = new Map();
|
|
1129
1159
|
let server = null;
|
|
1130
1160
|
let started = false;
|
|
@@ -1523,6 +1553,9 @@ export function createRemoteHub(options = {}) {
|
|
|
1523
1553
|
|
|
1524
1554
|
function closeExistingDeviceSocket(deviceId, nextSessionId) {
|
|
1525
1555
|
const existing = devices.get(deviceId);
|
|
1556
|
+
for (const channel of Object.keys(sideChannelSockets)) {
|
|
1557
|
+
closeSideChannel(existing, channel, 'replaced-by-new-session');
|
|
1558
|
+
}
|
|
1526
1559
|
if (!existing?.socket || existing.socket.destroyed) {
|
|
1527
1560
|
return;
|
|
1528
1561
|
}
|
|
@@ -1537,6 +1570,37 @@ export function createRemoteHub(options = {}) {
|
|
|
1537
1570
|
existing.socket.destroy();
|
|
1538
1571
|
}
|
|
1539
1572
|
|
|
1573
|
+
function closeSideChannel(device, channel, reason = `${channel}-socket-closed`) {
|
|
1574
|
+
const socketKey = `${channel}Socket`;
|
|
1575
|
+
const socket = device?.[socketKey];
|
|
1576
|
+
if (!socket) return;
|
|
1577
|
+
device[socketKey] = null;
|
|
1578
|
+
sideChannelSockets[channel]?.delete(socket);
|
|
1579
|
+
try {
|
|
1580
|
+
writeJsonLine(socket, { type: 'disconnect', channel, reason });
|
|
1581
|
+
} catch {
|
|
1582
|
+
// Best-effort notice before closing the side channel.
|
|
1583
|
+
}
|
|
1584
|
+
try {
|
|
1585
|
+
socket.destroy?.();
|
|
1586
|
+
} catch {
|
|
1587
|
+
// Ignore close failures.
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function detachSideChannel(socket, channel, reason = `${channel}-socket-closed`) {
|
|
1592
|
+
const channelMap = sideChannelSockets[channel];
|
|
1593
|
+
const deviceId = channelMap?.get(socket);
|
|
1594
|
+
channelMap?.delete(socket);
|
|
1595
|
+
if (!deviceId) return;
|
|
1596
|
+
const device = devices.get(deviceId);
|
|
1597
|
+
const socketKey = `${channel}Socket`;
|
|
1598
|
+
if (!device || device[socketKey] !== socket) return;
|
|
1599
|
+
device[socketKey] = null;
|
|
1600
|
+
device[`${channel}LastSeenAt`] = new Date().toISOString();
|
|
1601
|
+
emitRemoteEvent(`Remote${channel[0].toUpperCase()}${channel.slice(1)}SocketDisconnected`, device, { reason });
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1540
1604
|
function getDeviceActivityMs(device) {
|
|
1541
1605
|
return Math.max(
|
|
1542
1606
|
Date.parse(device?.lastSeenAt || '') || 0,
|
|
@@ -1841,6 +1905,7 @@ export function createRemoteHub(options = {}) {
|
|
|
1841
1905
|
protocolVersion: Number.isFinite(Number(hello.protocolVersion)) ? Math.max(1, Math.floor(Number(hello.protocolVersion))) : 1,
|
|
1842
1906
|
frameProtocol: frameProtocol || buildRemoteFrameProtocolDescriptor(),
|
|
1843
1907
|
frameModes: frameModes.length > 0 ? frameModes : getSupportedRemoteFrameModeProfiles(),
|
|
1908
|
+
frameModesAdvertised: frameModes.length > 0,
|
|
1844
1909
|
capabilities,
|
|
1845
1910
|
connected: true,
|
|
1846
1911
|
connectedAt: now,
|
|
@@ -1853,6 +1918,10 @@ export function createRemoteHub(options = {}) {
|
|
|
1853
1918
|
status: {},
|
|
1854
1919
|
latestThumbnail: null,
|
|
1855
1920
|
latestLiveFrame: null,
|
|
1921
|
+
inputSocket: null,
|
|
1922
|
+
frameSocket: null,
|
|
1923
|
+
audioSocket: null,
|
|
1924
|
+
fileSocket: null,
|
|
1856
1925
|
recentFramePayloads: {
|
|
1857
1926
|
thumbnail: [],
|
|
1858
1927
|
live: []
|
|
@@ -1885,6 +1954,40 @@ export function createRemoteHub(options = {}) {
|
|
|
1885
1954
|
return device;
|
|
1886
1955
|
}
|
|
1887
1956
|
|
|
1957
|
+
function attachSideChannel(socket, hello, channel) {
|
|
1958
|
+
const deviceId = normalizeDeviceId(hello.deviceId);
|
|
1959
|
+
const device = devices.get(deviceId);
|
|
1960
|
+
if (!device || !device.connected || !device.socket || device.socket.destroyed) {
|
|
1961
|
+
writeJsonLine(socket, { type: 'error', error: 'device-not-connected' });
|
|
1962
|
+
socket.destroy();
|
|
1963
|
+
return null;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
closeSideChannel(device, channel, `replaced-by-new-${channel}-socket`);
|
|
1967
|
+
const now = new Date().toISOString();
|
|
1968
|
+
device[`${channel}Socket`] = socket;
|
|
1969
|
+
device[`${channel}ConnectedAt`] = now;
|
|
1970
|
+
device[`${channel}LastSeenAt`] = now;
|
|
1971
|
+
sideChannelSockets[channel].set(socket, deviceId);
|
|
1972
|
+
writeJsonLine(socket, {
|
|
1973
|
+
type: 'welcome',
|
|
1974
|
+
channel,
|
|
1975
|
+
protocol: REMOTE_AGENT_PROTOCOL,
|
|
1976
|
+
protocolVersion: REMOTE_PROTOCOL_VERSION,
|
|
1977
|
+
sessionId: device.sessionId,
|
|
1978
|
+
deviceId,
|
|
1979
|
+
...(channel === 'frame'
|
|
1980
|
+
? {
|
|
1981
|
+
frameProtocol: buildRemoteFrameProtocolDescriptor(),
|
|
1982
|
+
frameModes: getSupportedRemoteFrameModeProfiles()
|
|
1983
|
+
}
|
|
1984
|
+
: {}),
|
|
1985
|
+
serverTime: now
|
|
1986
|
+
});
|
|
1987
|
+
emitRemoteEvent(`Remote${channel[0].toUpperCase()}${channel.slice(1)}SocketConnected`, device);
|
|
1988
|
+
return device;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1888
1991
|
function detachSocket(socket, reason = 'socket-closed') {
|
|
1889
1992
|
const deviceId = sockets.get(socket);
|
|
1890
1993
|
sockets.delete(socket);
|
|
@@ -1906,6 +2009,9 @@ export function createRemoteHub(options = {}) {
|
|
|
1906
2009
|
device.activeLiveStream.stoppedAt = device.disconnectedAt;
|
|
1907
2010
|
device.activeLiveStream.stopReason = reason;
|
|
1908
2011
|
}
|
|
2012
|
+
for (const channel of Object.keys(sideChannelSockets)) {
|
|
2013
|
+
closeSideChannel(device, channel, reason);
|
|
2014
|
+
}
|
|
1909
2015
|
failAllPendingTasks(device, 'device-disconnected');
|
|
1910
2016
|
logWarn('remote', `device disconnected ${device.deviceName} (${deviceId}): ${reason}`);
|
|
1911
2017
|
emitRemoteEvent('RemoteDeviceDisconnected', device, { reason });
|
|
@@ -2570,6 +2676,16 @@ export function createRemoteHub(options = {}) {
|
|
|
2570
2676
|
return;
|
|
2571
2677
|
}
|
|
2572
2678
|
|
|
2679
|
+
const channel = safeString(message.channel || message.Channel, 40).toLowerCase();
|
|
2680
|
+
if (Object.hasOwn(sideChannelSockets, channel)) {
|
|
2681
|
+
const device = attachSideChannel(socket, message, channel);
|
|
2682
|
+
if (!device) return;
|
|
2683
|
+
state.authenticated = true;
|
|
2684
|
+
state.device = device;
|
|
2685
|
+
state.sideChannel = channel;
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2573
2689
|
const device = attachDevice(socket, message);
|
|
2574
2690
|
if (!device) {
|
|
2575
2691
|
return;
|
|
@@ -2586,6 +2702,17 @@ export function createRemoteHub(options = {}) {
|
|
|
2586
2702
|
return;
|
|
2587
2703
|
}
|
|
2588
2704
|
|
|
2705
|
+
if (state.sideChannel) {
|
|
2706
|
+
device[`${state.sideChannel}LastSeenAt`] = new Date().toISOString();
|
|
2707
|
+
if (state.sideChannel === 'input' && message.type === 'input.error') {
|
|
2708
|
+
emitRemoteEvent('RemoteInputError', device, {
|
|
2709
|
+
inputSeq: Number(message.inputSeq || 0) || 0,
|
|
2710
|
+
error: safeString(message.error, 600) || 'remote-input-failed'
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2589
2716
|
device.counters.messagesReceived += 1;
|
|
2590
2717
|
device.lastSeenAt = new Date().toISOString();
|
|
2591
2718
|
|
|
@@ -2698,7 +2825,11 @@ export function createRemoteHub(options = {}) {
|
|
|
2698
2825
|
const detach = reason => {
|
|
2699
2826
|
allSockets.delete(socket);
|
|
2700
2827
|
clearTimeout(helloTimer);
|
|
2701
|
-
|
|
2828
|
+
if (state.sideChannel) {
|
|
2829
|
+
detachSideChannel(socket, state.sideChannel, reason);
|
|
2830
|
+
} else {
|
|
2831
|
+
detachSocket(socket, reason);
|
|
2832
|
+
}
|
|
2702
2833
|
};
|
|
2703
2834
|
|
|
2704
2835
|
socket.onCloseMessage = reason => detach(reason || 'websocket-closed');
|
|
@@ -2867,12 +2998,20 @@ export function createRemoteHub(options = {}) {
|
|
|
2867
2998
|
socket.on('close', () => {
|
|
2868
2999
|
allSockets.delete(socket);
|
|
2869
3000
|
clearTimeout(helloTimer);
|
|
2870
|
-
|
|
3001
|
+
if (state.sideChannel) {
|
|
3002
|
+
detachSideChannel(socket, state.sideChannel);
|
|
3003
|
+
} else {
|
|
3004
|
+
detachSocket(socket);
|
|
3005
|
+
}
|
|
2871
3006
|
});
|
|
2872
3007
|
socket.on('error', err => {
|
|
2873
3008
|
allSockets.delete(socket);
|
|
2874
3009
|
clearTimeout(helloTimer);
|
|
2875
|
-
|
|
3010
|
+
if (state.sideChannel) {
|
|
3011
|
+
detachSideChannel(socket, state.sideChannel, err?.message || 'socket-error');
|
|
3012
|
+
} else {
|
|
3013
|
+
detachSocket(socket, err?.message || 'socket-error');
|
|
3014
|
+
}
|
|
2876
3015
|
});
|
|
2877
3016
|
}
|
|
2878
3017
|
|
|
@@ -2936,6 +3075,7 @@ export function createRemoteHub(options = {}) {
|
|
|
2936
3075
|
}
|
|
2937
3076
|
|
|
2938
3077
|
sockets.clear();
|
|
3078
|
+
Object.values(sideChannelSockets).forEach(channelMap => channelMap.clear());
|
|
2939
3079
|
if (!server) {
|
|
2940
3080
|
started = false;
|
|
2941
3081
|
return;
|
|
@@ -3058,10 +3198,29 @@ export function createRemoteHub(options = {}) {
|
|
|
3058
3198
|
return { ok: false, error: 'missing-input-type' };
|
|
3059
3199
|
}
|
|
3060
3200
|
|
|
3201
|
+
const inputSocket = device.inputSocket;
|
|
3202
|
+
if (inputSocket && !inputSocket.destroyed) {
|
|
3203
|
+
const sent = writeJsonLine(inputSocket, {
|
|
3204
|
+
type: 'input.control',
|
|
3205
|
+
payload: {
|
|
3206
|
+
...normalized,
|
|
3207
|
+
hubForwardedAtEpochMs: Date.now(),
|
|
3208
|
+
issuedAt: normalized.issuedAt || new Date().toISOString()
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
if (sent) {
|
|
3212
|
+
device.counters.commandsSent += 1;
|
|
3213
|
+
device.inputLastSeenAt = new Date().toISOString();
|
|
3214
|
+
return { ok: true, inputSocket: true };
|
|
3215
|
+
}
|
|
3216
|
+
detachSideChannel(inputSocket, 'input', 'input-socket-write-failed');
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3061
3219
|
return sendCommand(deviceId, {
|
|
3062
3220
|
command: 'input.control',
|
|
3063
3221
|
payload: {
|
|
3064
3222
|
...normalized,
|
|
3223
|
+
hubForwardedAtEpochMs: Date.now(),
|
|
3065
3224
|
issuedAt: normalized.issuedAt || new Date().toISOString()
|
|
3066
3225
|
}
|
|
3067
3226
|
});
|
|
@@ -3285,7 +3444,10 @@ export function createRemoteHub(options = {}) {
|
|
|
3285
3444
|
const now = new Date().toISOString();
|
|
3286
3445
|
const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
|
|
3287
3446
|
const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
|
|
3288
|
-
const requestedMode =
|
|
3447
|
+
const requestedMode = resolveDeviceRemoteFrameMode(
|
|
3448
|
+
device,
|
|
3449
|
+
options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo'),
|
|
3450
|
+
streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
|
|
3289
3451
|
const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
|
|
3290
3452
|
const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
|
|
3291
3453
|
const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
|
|
@@ -3342,7 +3504,10 @@ export function createRemoteHub(options = {}) {
|
|
|
3342
3504
|
const now = new Date().toISOString();
|
|
3343
3505
|
const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
|
|
3344
3506
|
const streamPurpose = safeString(options.streamPurpose, 32).toLowerCase();
|
|
3345
|
-
const requestedMode =
|
|
3507
|
+
const requestedMode = resolveDeviceRemoteFrameMode(
|
|
3508
|
+
device,
|
|
3509
|
+
options.mode || (streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo'),
|
|
3510
|
+
streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
|
|
3346
3511
|
const fps = clampNumber(options.fps, 1, 30, streamPurpose === 'control' ? 30 : 20);
|
|
3347
3512
|
const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
|
|
3348
3513
|
const transfer = buildRemoteFrameTransferDescriptor(requestedMode, streamPurpose === 'control' ? 'mode3-h264-hw' : 'mode2-lzo');
|
|
@@ -293,6 +293,7 @@ async function main() {
|
|
|
293
293
|
const live = await fetchJson(`${hostBridge.baseUrl}/api/remote/devices/${encodeURIComponent(device.deviceId)}/live/start`, {
|
|
294
294
|
method: 'POST',
|
|
295
295
|
body: JSON.stringify({
|
|
296
|
+
mode: 'remote-fast',
|
|
296
297
|
fps: REQUESTED_FPS,
|
|
297
298
|
maxWidth: 960,
|
|
298
299
|
maxHeight: 540,
|
package/server.js
CHANGED
|
@@ -4354,7 +4354,7 @@ function appendRemoteAgentOutput(stream, chunk, stateConnectionKey = '') {
|
|
|
4354
4354
|
const text = chunk.toString();
|
|
4355
4355
|
const key = stream === 'stderr' ? 'stderrTail' : 'stdoutTail';
|
|
4356
4356
|
remoteAgentState[key] = String((remoteAgentState[key] || '') + text).slice(-REMOTE_AGENT_STDIO_TAIL_CHARS);
|
|
4357
|
-
if (/Connected to RemoteHub/i.test(text)) {
|
|
4357
|
+
if (/Connected to (?:RemoteHub|LiveDesk Hub)/i.test(text)) {
|
|
4358
4358
|
remoteAgentState.ready = true;
|
|
4359
4359
|
remoteAgentState.connectedAt = new Date().toISOString();
|
|
4360
4360
|
remoteAgentState.needsReconnect = false;
|
|
@@ -5222,7 +5222,7 @@ function appendRemoteAgentAttemptOutput(attempt, stream, chunk) {
|
|
|
5222
5222
|
const text = chunk.toString();
|
|
5223
5223
|
const key = stream === 'stderr' ? 'stderrTail' : 'stdoutTail';
|
|
5224
5224
|
attempt.state[key] = String((attempt.state[key] || '') + text).slice(-REMOTE_AGENT_STDIO_TAIL_CHARS);
|
|
5225
|
-
if (/Connected to RemoteHub/i.test(text)) {
|
|
5225
|
+
if (/Connected to (?:RemoteHub|LiveDesk Hub)/i.test(text)) {
|
|
5226
5226
|
attempt.state.ready = true;
|
|
5227
5227
|
attempt.state.connectedAt = new Date().toISOString();
|
|
5228
5228
|
attempt.state.needsReconnect = false;
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
const NODE_VIEW_HOST_HYBRID = 'hybrid';
|
|
45
45
|
const NODE_VIEW_RENDERER_VERSION = 'shared-node-view-v1';
|
|
46
46
|
const CSS3D_ONLY_NODE_RENDERING = true;
|
|
47
|
+
const NATIVE_TEXT_SELECTION_CONTENT_SELECTORS = '.node-response, .code-body, .text-content, .markdown-body, .csv-table-scroll, .csv-table-cell, textarea, .map-node-memo__title, .map-node-memo__body, .map-node-memo__body-view, .map-node-memo__body-view *';
|
|
47
48
|
const CSV_TABLE_MAX_RENDER_ROWS = 500;
|
|
48
49
|
const CSV_TABLE_MAX_RENDER_COLUMNS = 80;
|
|
49
50
|
const REMOTE_FLEET_DISPLAY_NAME = 'Multi Desktop Monitor';
|
|
@@ -826,6 +827,19 @@
|
|
|
826
827
|
cursor: grab;
|
|
827
828
|
}
|
|
828
829
|
|
|
830
|
+
.css3d-resolution-wrapper.is-text-selection-owner .node-response,
|
|
831
|
+
.css3d-resolution-wrapper.is-text-selection-owner .code-body,
|
|
832
|
+
.css3d-resolution-wrapper.is-text-selection-owner .text-content,
|
|
833
|
+
.css3d-resolution-wrapper.is-text-selection-owner .markdown-body,
|
|
834
|
+
.css3d-resolution-wrapper.is-text-selection-owner .csv-table-scroll,
|
|
835
|
+
.css3d-resolution-wrapper.is-text-selection-owner textarea,
|
|
836
|
+
.css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__title,
|
|
837
|
+
.css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__body,
|
|
838
|
+
.css3d-resolution-wrapper.is-text-selection-owner .map-node-memo__body-view {
|
|
839
|
+
user-select: text !important;
|
|
840
|
+
-webkit-user-select: text !important;
|
|
841
|
+
}
|
|
842
|
+
|
|
829
843
|
.css3d-resolution-wrapper.node-type-image,
|
|
830
844
|
.css3d-resolution-wrapper.node-type-video,
|
|
831
845
|
.css3d-resolution-wrapper.node-type-embed,
|
|
@@ -5160,6 +5174,38 @@
|
|
|
5160
5174
|
return activeId === String(nodeId || '').trim();
|
|
5161
5175
|
}
|
|
5162
5176
|
|
|
5177
|
+
function setNativeTextSelectionOwnerClass(module, nodeId = null) {
|
|
5178
|
+
if (!module) return false;
|
|
5179
|
+
const nextId = String(nodeId || '').trim();
|
|
5180
|
+
const previousId = String(module._nativeTextSelectionOwnerNodeId || '').trim();
|
|
5181
|
+
if (previousId === nextId) return true;
|
|
5182
|
+
|
|
5183
|
+
const getWrapper = id => {
|
|
5184
|
+
const entry = id ? module.nodeObjectsById?.get?.(id) : null;
|
|
5185
|
+
return entry?.cssObject?.element?.closest?.('.css3d-resolution-wrapper')
|
|
5186
|
+
|| entry?.cssObject?.element
|
|
5187
|
+
|| null;
|
|
5188
|
+
};
|
|
5189
|
+
const previousWrapper = getWrapper(previousId);
|
|
5190
|
+
previousWrapper?.classList?.remove?.('is-text-selection-owner');
|
|
5191
|
+
if (previousWrapper?.querySelectorAll) {
|
|
5192
|
+
previousWrapper.querySelectorAll(NATIVE_TEXT_SELECTION_CONTENT_SELECTORS).forEach(element => {
|
|
5193
|
+
element.style.userSelect = 'none';
|
|
5194
|
+
element.style.webkitUserSelect = 'none';
|
|
5195
|
+
});
|
|
5196
|
+
}
|
|
5197
|
+
const nextWrapper = getWrapper(nextId);
|
|
5198
|
+
nextWrapper?.classList?.add?.('is-text-selection-owner');
|
|
5199
|
+
if (nextWrapper?.querySelectorAll) {
|
|
5200
|
+
nextWrapper.querySelectorAll(NATIVE_TEXT_SELECTION_CONTENT_SELECTORS).forEach(element => {
|
|
5201
|
+
element.style.userSelect = 'text';
|
|
5202
|
+
element.style.webkitUserSelect = 'text';
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5205
|
+
module._nativeTextSelectionOwnerNodeId = nextId || null;
|
|
5206
|
+
return true;
|
|
5207
|
+
}
|
|
5208
|
+
|
|
5163
5209
|
function activateNativeTextSelectionSource(module, nodeId) {
|
|
5164
5210
|
const id = String(nodeId || '').trim();
|
|
5165
5211
|
if (!module || !id) {
|
|
@@ -5167,6 +5213,7 @@
|
|
|
5167
5213
|
}
|
|
5168
5214
|
|
|
5169
5215
|
module._nativeTextSelectionSourceNodeId = id;
|
|
5216
|
+
setNativeTextSelectionOwnerClass(module, id);
|
|
5170
5217
|
ensureTextOverlaySourceVisible(module, id);
|
|
5171
5218
|
return true;
|
|
5172
5219
|
}
|
|
@@ -5179,6 +5226,7 @@
|
|
|
5179
5226
|
}
|
|
5180
5227
|
|
|
5181
5228
|
module._nativeTextSelectionSourceNodeId = null;
|
|
5229
|
+
setNativeTextSelectionOwnerClass(module, null);
|
|
5182
5230
|
return true;
|
|
5183
5231
|
}
|
|
5184
5232
|
|
|
@@ -14923,6 +14971,7 @@
|
|
|
14923
14971
|
const remoteFleetHostLeaseTimers = new Map();
|
|
14924
14972
|
const remoteFleetLocalHostTargets = new Map();
|
|
14925
14973
|
const remoteFleetBinaryFrameSessions = new Map();
|
|
14974
|
+
const remoteFleetAtlasSessions = new Map();
|
|
14926
14975
|
const remoteFleetLatestBinaryFrames = new Map();
|
|
14927
14976
|
const remoteFleetFrameBlobCache = new Map();
|
|
14928
14977
|
let remoteFleetBridgeStatusPromise = null;
|
|
@@ -15137,6 +15186,7 @@
|
|
|
15137
15186
|
closeRemoteFleetControlNodeSession(bodyView, 'clear');
|
|
15138
15187
|
if (options.keepBinaryFrameSocket !== true) {
|
|
15139
15188
|
releaseRemoteFleetBinaryFrameSocket(bodyView);
|
|
15189
|
+
releaseRemoteFleetAtlasSocket(bodyView);
|
|
15140
15190
|
}
|
|
15141
15191
|
if (bodyView._remoteFleetLiveRefreshTimer) {
|
|
15142
15192
|
clearInterval(bodyView._remoteFleetLiveRefreshTimer);
|
|
@@ -15252,6 +15302,17 @@
|
|
|
15252
15302
|
return url.toString();
|
|
15253
15303
|
}
|
|
15254
15304
|
|
|
15305
|
+
function buildRemoteFleetAtlasWsUrl(status) {
|
|
15306
|
+
const baseUrl = String(status?.__remoteFleetBridgeBaseUrl || window.location?.origin || '').trim();
|
|
15307
|
+
const path = String(status?.remoteAtlasWsPath || '/api/remote/atlas/ws').trim() || '/api/remote/atlas/ws';
|
|
15308
|
+
if (!baseUrl) return '';
|
|
15309
|
+
const url = new URL(path, baseUrl);
|
|
15310
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
15311
|
+
const bridgeToken = String(status?.bridgeToken || '').trim();
|
|
15312
|
+
if (bridgeToken) url.searchParams.set('token', bridgeToken);
|
|
15313
|
+
return url.toString();
|
|
15314
|
+
}
|
|
15315
|
+
|
|
15255
15316
|
async function parseRemoteFleetBinaryFrameMessage(data) {
|
|
15256
15317
|
let buffer = null;
|
|
15257
15318
|
if (data instanceof ArrayBuffer) {
|
|
@@ -15923,6 +15984,100 @@
|
|
|
15923
15984
|
}
|
|
15924
15985
|
}
|
|
15925
15986
|
|
|
15987
|
+
function paintRemoteFleetAtlasFrame(session, frame) {
|
|
15988
|
+
const atlasCanvas = session?.atlasCanvas;
|
|
15989
|
+
const tiles = Array.isArray(session?.layout?.tiles) ? session.layout.tiles : [];
|
|
15990
|
+
if (!atlasCanvas || tiles.length === 0) return;
|
|
15991
|
+
const targets = new Set();
|
|
15992
|
+
session.bodies.forEach(body => getRemoteFleetFramePatchTargets(body).forEach(target => targets.add(target)));
|
|
15993
|
+
const tileByDevice = new Map(tiles.map(tile => [String(tile.deviceId || '').trim(), tile]));
|
|
15994
|
+
targets.forEach(target => {
|
|
15995
|
+
target.querySelectorAll('[data-remote-fleet-device-preview]').forEach(preview => {
|
|
15996
|
+
const tile = tileByDevice.get(String(preview.dataset.deviceId || '').trim());
|
|
15997
|
+
if (!tile) return;
|
|
15998
|
+
const canvas = ensureRemoteFleetFrameCanvas(preview);
|
|
15999
|
+
const width = Math.max(1, Number(tile.width || 0) | 0);
|
|
16000
|
+
const height = Math.max(1, Number(tile.height || 0) | 0);
|
|
16001
|
+
canvas.width = width;
|
|
16002
|
+
canvas.height = height;
|
|
16003
|
+
const context = canvas.getContext('2d', { alpha: false, desynchronized: true });
|
|
16004
|
+
context?.drawImage(atlasCanvas, Number(tile.x || 0), Number(tile.y || 0), width, height, 0, 0, width, height);
|
|
16005
|
+
canvas.style.display = 'block';
|
|
16006
|
+
const image = preview.querySelector?.('img[data-remote-fleet-frame-image="true"]');
|
|
16007
|
+
if (image) image.style.display = 'none';
|
|
16008
|
+
preview.dataset.remoteFleetFrameKind = 'live';
|
|
16009
|
+
preview.dataset.remoteFleetFrameSeq = String(frame.frameSeq || 0);
|
|
16010
|
+
preview.dataset.remoteFleetFrameUrl = '';
|
|
16011
|
+
preview.dataset.remoteFleetHasFrame = 'true';
|
|
16012
|
+
});
|
|
16013
|
+
});
|
|
16014
|
+
window.RuntimeTrace?.emit?.('remote.atlas.painted', { nodeId: session.nodeId, frameSeq: frame.frameSeq || 0, tiles: tiles.length });
|
|
16015
|
+
}
|
|
16016
|
+
|
|
16017
|
+
function startRemoteFleetAtlasSocket(bodyView, getDeviceIds, options = {}) {
|
|
16018
|
+
const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
|
|
16019
|
+
if (!nodeId || typeof WebSocket !== 'function') return false;
|
|
16020
|
+
let session = remoteFleetAtlasSessions.get(nodeId);
|
|
16021
|
+
if (!session) {
|
|
16022
|
+
session = { nodeId, bodies: new Set(), ws: null, wsOpening: false, getDeviceIds: null, layout: null, atlasCanvas: document.createElement('canvas'), fallbackStarted: false };
|
|
16023
|
+
remoteFleetAtlasSessions.set(nodeId, session);
|
|
16024
|
+
}
|
|
16025
|
+
session.bodies.add(bodyView);
|
|
16026
|
+
session.getDeviceIds = getDeviceIds;
|
|
16027
|
+
session.options = { fps: 20, width: Number(options.width || 1920) || 1920, height: Number(options.height || 1080) || 1080, tileWidth: Number(options.tileWidth || 320) || 320, tileHeight: Number(options.tileHeight || 180) || 180 };
|
|
16028
|
+
const fallback = () => {
|
|
16029
|
+
if (session.fallbackStarted) return;
|
|
16030
|
+
session.fallbackStarted = true;
|
|
16031
|
+
try { session.ws?.close?.(); } catch { /* encoder failure fallback */ }
|
|
16032
|
+
session.bodies.forEach(body => startRemoteFleetBinaryFrameSocket(body, session.getDeviceIds, { autoStartLive: true, fps: 20, maxWidth: 960, maxHeight: 540, quality: 60, mode: 'mode2-lzo', streamPurpose: 'wall' }));
|
|
16033
|
+
window.RuntimeTrace?.emit?.('remote.atlas.fallback', { nodeId: session.nodeId, mode: 'mode2-lzo' });
|
|
16034
|
+
};
|
|
16035
|
+
session.fallback = fallback;
|
|
16036
|
+
if (session.ws || session.wsOpening) return true;
|
|
16037
|
+
session.wsOpening = true;
|
|
16038
|
+
getRemoteFleetBridgeStatusForFrames().then(status => {
|
|
16039
|
+
const wsUrl = buildRemoteFleetAtlasWsUrl(status);
|
|
16040
|
+
if (!wsUrl) throw new Error('remote-atlas-ws-url-unavailable');
|
|
16041
|
+
const ws = new WebSocket(wsUrl);
|
|
16042
|
+
session.ws = ws;
|
|
16043
|
+
ws.binaryType = 'arraybuffer';
|
|
16044
|
+
ws.onopen = () => {
|
|
16045
|
+
session.wsOpening = false;
|
|
16046
|
+
ws.send(JSON.stringify({ type: 'configure', deviceIds: getRemoteFleetBinaryFrameDeviceIds(session), fps: 20, width: session.options.width, height: session.options.height, tileWidth: session.options.tileWidth, tileHeight: session.options.tileHeight }));
|
|
16047
|
+
window.RuntimeTrace?.emit?.('remote.atlas.wsOpen', { nodeId: session.nodeId });
|
|
16048
|
+
};
|
|
16049
|
+
ws.onmessage = event => {
|
|
16050
|
+
if (typeof event.data === 'string') {
|
|
16051
|
+
try {
|
|
16052
|
+
const payload = JSON.parse(event.data);
|
|
16053
|
+
if (payload?.type === 'Mode4AtlasLayout') session.layout = payload;
|
|
16054
|
+
if (payload?.type === 'Mode4AtlasStatus' && (payload.state === 'error' || payload.error)) fallback();
|
|
16055
|
+
} catch { /* ignore status text */ }
|
|
16056
|
+
return;
|
|
16057
|
+
}
|
|
16058
|
+
parseRemoteFleetBinaryFrameMessage(event.data).then(frame => {
|
|
16059
|
+
if (!frame || String(frame.frameMode || frame.mode || '').toLowerCase() !== 'mode4-h264-atlas') return;
|
|
16060
|
+
session.atlasCanvas.width = Number(frame.width || session.options.width) || session.options.width;
|
|
16061
|
+
session.atlasCanvas.height = Number(frame.height || session.options.height) || session.options.height;
|
|
16062
|
+
window.MindExecRemoteCodecs?.decodeH264Frame?.(session.atlasCanvas, frame, () => paintRemoteFleetAtlasFrame(session, frame));
|
|
16063
|
+
}).catch(() => undefined);
|
|
16064
|
+
};
|
|
16065
|
+
ws.onerror = () => { fallback(); try { ws.close(); } catch {} };
|
|
16066
|
+
ws.onclose = () => { session.wsOpening = false; if (session.ws === ws) session.ws = null; if (!session.fallbackStarted) fallback(); };
|
|
16067
|
+
}).catch(error => { session.wsOpening = false; fallback(); window.RuntimeTrace?.emit?.('remote.atlas.wsOpenFailed', { nodeId, error: error?.message || String(error || '') }); });
|
|
16068
|
+
return true;
|
|
16069
|
+
}
|
|
16070
|
+
|
|
16071
|
+
function releaseRemoteFleetAtlasSocket(bodyView) {
|
|
16072
|
+
const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
|
|
16073
|
+
const session = nodeId ? remoteFleetAtlasSessions.get(nodeId) : null;
|
|
16074
|
+
if (!session) return;
|
|
16075
|
+
session.bodies.delete(bodyView);
|
|
16076
|
+
if (session.bodies.size > 0) return;
|
|
16077
|
+
try { session.ws?.close?.(); } catch {}
|
|
16078
|
+
remoteFleetAtlasSessions.delete(nodeId);
|
|
16079
|
+
}
|
|
16080
|
+
|
|
15926
16081
|
function startRemoteFleetBinaryFrameSocket(bodyView, getDeviceIds, options = {}) {
|
|
15927
16082
|
const nodeId = String(bodyView?.dataset?.nodeId || '').trim();
|
|
15928
16083
|
if (!nodeId || typeof WebSocket !== 'function') {
|
|
@@ -20712,22 +20867,20 @@
|
|
|
20712
20867
|
|| hasActiveLiveStream
|
|
20713
20868
|
|| (autoMonitorState && hasVisibleFrameTarget);
|
|
20714
20869
|
if (shouldUseBinaryFrameSocket) {
|
|
20715
|
-
const
|
|
20870
|
+
const atlasSocketStarted = startRemoteFleetAtlasSocket(bodyView, () => {
|
|
20716
20871
|
const liveIds = getVisibleLiveFrameDeviceIds();
|
|
20717
20872
|
return liveIds.length > 0 ? liveIds : getVisibleFrameDeviceIds();
|
|
20718
20873
|
}, {
|
|
20719
|
-
autoStartLive: true,
|
|
20720
20874
|
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
streamPurpose: 'wall'
|
|
20875
|
+
width: 1920,
|
|
20876
|
+
height: 1080,
|
|
20877
|
+
tileWidth: 320,
|
|
20878
|
+
tileHeight: 180
|
|
20726
20879
|
});
|
|
20727
|
-
if (!
|
|
20880
|
+
if (!atlasSocketStarted) {
|
|
20728
20881
|
window.RuntimeTrace?.emit?.('remote.frame.wsUnavailable', {
|
|
20729
20882
|
nodeId,
|
|
20730
|
-
reason: '
|
|
20883
|
+
reason: 'atlas-frame-socket-not-started'
|
|
20731
20884
|
});
|
|
20732
20885
|
ensureVisibleRemoteFleetLiveStreams().catch(error => {
|
|
20733
20886
|
window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
|
|
@@ -20738,7 +20891,7 @@
|
|
|
20738
20891
|
} else {
|
|
20739
20892
|
window.RuntimeTrace?.emit?.('remote.live.wsAutoStartRequested', {
|
|
20740
20893
|
nodeId,
|
|
20741
|
-
transport: 'ws-
|
|
20894
|
+
transport: 'ws-atlas-h264',
|
|
20742
20895
|
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
20743
20896
|
visibleFrameTargets: visibleFrameDeviceIds.length,
|
|
20744
20897
|
visibleLiveTargets: getVisibleLiveFrameDeviceIds().length
|
|
@@ -20758,6 +20911,7 @@
|
|
|
20758
20911
|
}, REMOTE_FLEET_LIVE_REFRESH_MS);
|
|
20759
20912
|
} else {
|
|
20760
20913
|
releaseRemoteFleetBinaryFrameSocket(bodyView);
|
|
20914
|
+
releaseRemoteFleetAtlasSocket(bodyView);
|
|
20761
20915
|
if (autoMonitorState && hasMonitorTargets) {
|
|
20762
20916
|
startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
|
|
20763
20917
|
startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'visible-devices');
|
|
@@ -25016,6 +25170,7 @@
|
|
|
25016
25170
|
window.MindMapCss3DManager = {
|
|
25017
25171
|
activateSelectableTextOverlay: activateSelectableTextOverlay,
|
|
25018
25172
|
activateNativeTextSelectionSource: activateNativeTextSelectionSource,
|
|
25173
|
+
setNativeTextSelectionOwnerClass: setNativeTextSelectionOwnerClass,
|
|
25019
25174
|
beginNodeEditingOverlay: beginNodeEditingOverlay,
|
|
25020
25175
|
bindMemoWheelRouting: bindMemoWheelRouting,
|
|
25021
25176
|
bindNoteEditingWheelZoom: bindNoteEditingWheelZoom,
|