@mindexec/cli 0.2.119 → 0.2.121
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 +1 -1
- package/remote-hub.js +5 -7
- package/scripts/remote-http-smoke.mjs +14 -2
- package/scripts/remote-hub-scale-smoke.mjs +6 -1
- package/server.js +531 -50
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +99 -70
- package/wwwroot/_framework/{MindExecution.Shared.e8lipb57xv.dll → MindExecution.Shared.uwkrc41o75.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +3 -3
- package/wwwroot/index.html +3 -3
- package/wwwroot/service-worker-assets.js +6 -6
- package/wwwroot/service-worker.js +1 -1
package/package.json
CHANGED
package/remote-hub.js
CHANGED
|
@@ -372,8 +372,8 @@ function serializeRemoteFrame(frame, deviceId, frameKind, options = {}) {
|
|
|
372
372
|
framePath,
|
|
373
373
|
frameUrl: framePath
|
|
374
374
|
};
|
|
375
|
-
if (options.includeDataUrl
|
|
376
|
-
serialized.dataUrl = dataUrl;
|
|
375
|
+
if (options.includeDataUrl === true) {
|
|
376
|
+
serialized.dataUrl = dataUrl || buildFrameDataUrl(payload, '', publicFrame.mimeType || publicFrame.format || 'image/jpeg');
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
return serialized;
|
|
@@ -1114,7 +1114,7 @@ export function createRemoteHub(options = {}) {
|
|
|
1114
1114
|
|
|
1115
1115
|
function listDevices(options = {}) {
|
|
1116
1116
|
const serializeOptions = {
|
|
1117
|
-
includeDataUrl: options.includeDataUrl
|
|
1117
|
+
includeDataUrl: options.includeDataUrl === true
|
|
1118
1118
|
};
|
|
1119
1119
|
return [...devices.values()]
|
|
1120
1120
|
.map(device => serializeDevice(device, serializeOptions))
|
|
@@ -1969,7 +1969,6 @@ export function createRemoteHub(options = {}) {
|
|
|
1969
1969
|
contentHash,
|
|
1970
1970
|
captureMs: readFrameCaptureMs(message),
|
|
1971
1971
|
sameContentStreak,
|
|
1972
|
-
dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType),
|
|
1973
1972
|
payload,
|
|
1974
1973
|
accessToken: createFrameAccessToken()
|
|
1975
1974
|
};
|
|
@@ -2040,7 +2039,6 @@ export function createRemoteHub(options = {}) {
|
|
|
2040
2039
|
contentHash,
|
|
2041
2040
|
captureMs: readFrameCaptureMs(message),
|
|
2042
2041
|
sameContentStreak,
|
|
2043
|
-
dataUrl: buildFrameDataUrl(framePayload, frameData, mimeType),
|
|
2044
2042
|
payload,
|
|
2045
2043
|
accessToken: createFrameAccessToken()
|
|
2046
2044
|
};
|
|
@@ -2961,14 +2959,14 @@ export function createRemoteHub(options = {}) {
|
|
|
2961
2959
|
function getDeviceLiveFrame(deviceId, options = {}) {
|
|
2962
2960
|
const device = devices.get(String(deviceId || ''));
|
|
2963
2961
|
return serializeRemoteFrame(device?.latestLiveFrame, device?.deviceId, 'live', {
|
|
2964
|
-
includeDataUrl: options.includeDataUrl
|
|
2962
|
+
includeDataUrl: options.includeDataUrl === true
|
|
2965
2963
|
});
|
|
2966
2964
|
}
|
|
2967
2965
|
|
|
2968
2966
|
function getDeviceThumbnail(deviceId, options = {}) {
|
|
2969
2967
|
const device = devices.get(String(deviceId || ''));
|
|
2970
2968
|
return serializeRemoteFrame(device?.latestThumbnail, device?.deviceId, 'thumbnail', {
|
|
2971
|
-
includeDataUrl: options.includeDataUrl
|
|
2969
|
+
includeDataUrl: options.includeDataUrl === true
|
|
2972
2970
|
});
|
|
2973
2971
|
}
|
|
2974
2972
|
|
|
@@ -373,10 +373,16 @@ async function runSyntheticEnabledSmoke() {
|
|
|
373
373
|
});
|
|
374
374
|
assert.equal(thumbnail.ok, true, JSON.stringify(thumbnail.payload));
|
|
375
375
|
assert.equal(thumbnail.payload?.thumbnail?.streamId, 'http-smoke-thumb');
|
|
376
|
-
assert.ok(
|
|
376
|
+
assert.ok(!('dataUrl' in thumbnail.payload.thumbnail));
|
|
377
377
|
assert.ok(String(thumbnail.payload?.thumbnail?.framePath || '').includes(`/api/remote/devices/${encodeURIComponent(thumbnailTarget.deviceId)}/thumbnail?`));
|
|
378
378
|
assert.equal(thumbnail.payload?.thumbnail?.frameUrl, thumbnail.payload?.thumbnail?.framePath);
|
|
379
379
|
|
|
380
|
+
const thumbnailWithData = await fetchJson(`${baseUrl}/api/remote/devices/${encodeURIComponent(thumbnailTarget.deviceId)}/thumbnail?includeDataUrl=true`, {
|
|
381
|
+
token: BRIDGE_TOKEN
|
|
382
|
+
});
|
|
383
|
+
assert.equal(thumbnailWithData.ok, true, JSON.stringify(thumbnailWithData.payload));
|
|
384
|
+
assert.ok(String(thumbnailWithData.payload?.thumbnail?.dataUrl || '').startsWith('data:image/png;base64,'));
|
|
385
|
+
|
|
380
386
|
const thumbnailBinary = await fetchBinary(`${baseUrl}${thumbnail.payload.thumbnail.framePath}`);
|
|
381
387
|
assert.equal(thumbnailBinary.ok, true, `${thumbnailBinary.status} ${thumbnailBinary.contentType}`);
|
|
382
388
|
assert.equal(thumbnailBinary.contentType, 'image/png');
|
|
@@ -433,9 +439,15 @@ async function runSyntheticEnabledSmoke() {
|
|
|
433
439
|
assert.equal(liveFrame.payload?.frame?.streamId, 'http-smoke-live');
|
|
434
440
|
assert.equal(liveFrame.payload?.frame?.mode, 'remote-fast');
|
|
435
441
|
assert.equal(liveFrame.payload?.frame?.fps, 20);
|
|
436
|
-
assert.ok(
|
|
442
|
+
assert.ok(!('dataUrl' in liveFrame.payload.frame));
|
|
437
443
|
assert.ok(String(liveFrame.payload?.frame?.framePath || '').includes(`/api/remote/devices/${encodeURIComponent(liveTarget.deviceId)}/live/frame?`));
|
|
438
444
|
|
|
445
|
+
const liveFrameWithData = await fetchJson(`${baseUrl}/api/remote/devices/${encodeURIComponent(liveTarget.deviceId)}/live/frame?includeDataUrl=true`, {
|
|
446
|
+
token: BRIDGE_TOKEN
|
|
447
|
+
});
|
|
448
|
+
assert.equal(liveFrameWithData.ok, true, JSON.stringify(liveFrameWithData.payload));
|
|
449
|
+
assert.ok(String(liveFrameWithData.payload?.frame?.dataUrl || '').startsWith('data:image/png;base64,'));
|
|
450
|
+
|
|
439
451
|
const liveBinary = await fetchBinary(`${baseUrl}${liveFrame.payload.frame.framePath}`);
|
|
440
452
|
assert.equal(liveBinary.ok, true, `${liveBinary.status} ${liveBinary.contentType}`);
|
|
441
453
|
assert.equal(liveBinary.contentType, 'image/png');
|
|
@@ -37,9 +37,12 @@ try {
|
|
|
37
37
|
assert.equal(devices.length, SYNTHETIC_COUNT);
|
|
38
38
|
assert.equal(devices.filter(device => device.synthetic === true).length, SYNTHETIC_COUNT);
|
|
39
39
|
assert.equal(devices.some(device => device.connected === false), true);
|
|
40
|
-
assert.equal(devices.some(device => device.latestThumbnail
|
|
40
|
+
assert.equal(devices.some(device => device.latestThumbnail && 'dataUrl' in device.latestThumbnail), false);
|
|
41
41
|
assert.equal(devices.some(device => device.activeLiveStream?.active === true), true);
|
|
42
42
|
|
|
43
|
+
const devicesWithDataUrl = hub.listDevices({ includeDataUrl: true });
|
|
44
|
+
assert.equal(devicesWithDataUrl.some(device => device.latestThumbnail?.dataUrl), true);
|
|
45
|
+
|
|
43
46
|
const deviceListPayload = {
|
|
44
47
|
total: devices.length,
|
|
45
48
|
pagination: 'none',
|
|
@@ -58,6 +61,8 @@ try {
|
|
|
58
61
|
});
|
|
59
62
|
assert.equal(thumbnailResult.ok, true);
|
|
60
63
|
assert.equal(hub.getDeviceThumbnail(thumbnailTarget.deviceId).streamId, 'scale-thumb');
|
|
64
|
+
assert.equal('dataUrl' in hub.getDeviceThumbnail(thumbnailTarget.deviceId), false);
|
|
65
|
+
assert.ok(hub.getDeviceThumbnail(thumbnailTarget.deviceId, { includeDataUrl: true }).dataUrl);
|
|
61
66
|
|
|
62
67
|
const liveTarget = devices.find(device => device.connected && device.capabilities?.liveStream);
|
|
63
68
|
assert.ok(liveTarget);
|
package/server.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
import express from 'express';
|
|
10
10
|
import cors from 'cors';
|
|
11
|
-
import { promises as fs, readFileSync, statSync, createReadStream } from 'fs';
|
|
11
|
+
import { promises as fs, readFileSync, statSync, createReadStream, chmodSync } from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
|
-
import { exec, spawn, execFile } from 'child_process';
|
|
13
|
+
import { exec, spawn, spawnSync, execFile } from 'child_process';
|
|
14
14
|
import { promisify } from 'util';
|
|
15
15
|
import os from 'os';
|
|
16
16
|
import multer from 'multer';
|
|
@@ -2907,10 +2907,14 @@ const REMOTE_AGENT_EARLY_EXIT_MS = 1200;
|
|
|
2907
2907
|
const REMOTE_AGENT_READY_TIMEOUT_MS = 5000;
|
|
2908
2908
|
const REMOTE_AGENT_DEFAULT_ENGINE = 'auto';
|
|
2909
2909
|
const REMOTE_AGENT_SYNC_REPORT_LOG_REPEAT_MS = 30000;
|
|
2910
|
+
const REMOTE_AGENT_RACE_START_STAGGER_MS = 120;
|
|
2911
|
+
const REMOTE_AGENT_MAX_PARALLEL_CANDIDATES = 6;
|
|
2910
2912
|
let remoteAgentState = createRemoteAgentIdleState();
|
|
2911
2913
|
let remoteAgentSyncReportState = null;
|
|
2912
2914
|
let remoteAgentSyncReportLogKey = '';
|
|
2913
2915
|
let remoteAgentSyncReportLogAt = 0;
|
|
2916
|
+
let remoteAgentConnectPromise = null;
|
|
2917
|
+
const remoteAgentRecentSuccessfulManagers = new Map();
|
|
2914
2918
|
|
|
2915
2919
|
function createRemoteAgentIdleState(overrides = {}) {
|
|
2916
2920
|
return {
|
|
@@ -3211,6 +3215,46 @@ function createRemoteAgentConnectionKey(manager, leaseId) {
|
|
|
3211
3215
|
.slice(0, 24);
|
|
3212
3216
|
}
|
|
3213
3217
|
|
|
3218
|
+
function getRemoteAgentRecentManagerKey(leaseId = '') {
|
|
3219
|
+
return safeRemoteAgentField(leaseId || 'default', 128) || 'default';
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
function rememberRemoteAgentSuccessfulManager(manager, leaseId = '') {
|
|
3223
|
+
const endpoint = normalizeRemoteManagerEndpoint(manager);
|
|
3224
|
+
if (!endpoint) {
|
|
3225
|
+
return;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
remoteAgentRecentSuccessfulManagers.set(getRemoteAgentRecentManagerKey(leaseId), {
|
|
3229
|
+
endpoint,
|
|
3230
|
+
updatedAt: Date.now()
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
function prioritizeRemoteAgentManagers(managers, leaseId = '') {
|
|
3235
|
+
const normalized = normalizeRemoteManagerEndpointList(managers);
|
|
3236
|
+
if (normalized.length <= 1) {
|
|
3237
|
+
return normalized;
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
const recent = remoteAgentRecentSuccessfulManagers.get(getRemoteAgentRecentManagerKey(leaseId));
|
|
3241
|
+
const recentEndpoint = normalizeRemoteManagerEndpoint(recent?.endpoint);
|
|
3242
|
+
if (!recentEndpoint) {
|
|
3243
|
+
return normalized;
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
const recentIndex = normalized.findIndex(item => item.toLowerCase() === recentEndpoint.toLowerCase());
|
|
3247
|
+
if (recentIndex <= 0) {
|
|
3248
|
+
return normalized;
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
return [
|
|
3252
|
+
normalized[recentIndex],
|
|
3253
|
+
...normalized.slice(0, recentIndex),
|
|
3254
|
+
...normalized.slice(recentIndex + 1)
|
|
3255
|
+
];
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3214
3258
|
function isRemoteAgentProcessRunning() {
|
|
3215
3259
|
const proc = remoteAgentState.proc;
|
|
3216
3260
|
return remoteAgentState.status === 'running'
|
|
@@ -3225,19 +3269,183 @@ function isLocalRemoteHostTargetActive() {
|
|
|
3225
3269
|
&& (!status.hostTargetHostInstanceId || status.hostTargetHostInstanceId === BRIDGE_INSTANCE_ID);
|
|
3226
3270
|
}
|
|
3227
3271
|
|
|
3228
|
-
function
|
|
3229
|
-
const
|
|
3230
|
-
|
|
3231
|
-
|
|
3272
|
+
function getRemoteFastRuntimeId() {
|
|
3273
|
+
const platform = process.platform;
|
|
3274
|
+
const arch = process.arch;
|
|
3275
|
+
if (platform === 'win32' && arch === 'x64') {
|
|
3276
|
+
return 'win-x64';
|
|
3277
|
+
}
|
|
3278
|
+
if (platform === 'darwin' && arch === 'arm64') {
|
|
3279
|
+
return 'osx-arm64';
|
|
3280
|
+
}
|
|
3281
|
+
if (platform === 'darwin' && arch === 'x64') {
|
|
3282
|
+
return 'osx-x64';
|
|
3283
|
+
}
|
|
3284
|
+
return '';
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
function getRemoteFastRuntimeExecutableName() {
|
|
3288
|
+
return process.platform === 'win32'
|
|
3289
|
+
? 'mindexec-remote-fast.exe'
|
|
3290
|
+
: 'mindexec-remote-fast';
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
function getBundledRemoteFastRuntimeDirs() {
|
|
3294
|
+
const rid = getRemoteFastRuntimeId();
|
|
3295
|
+
if (!rid) {
|
|
3296
|
+
return [];
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
return [
|
|
3300
|
+
path.join(BRIDGE_ROOT, 'remote-fast', rid),
|
|
3301
|
+
path.join(BRIDGE_ROOT, 'remote', 'fast', rid),
|
|
3302
|
+
path.join(BRIDGE_ROOT, 'node_modules', '@mindexec', 'remote', 'fast', rid),
|
|
3303
|
+
path.resolve(BRIDGE_ROOT, '..', 'remote', 'fast', rid),
|
|
3304
|
+
path.resolve(BRIDGE_ROOT, '..', 'RemoteAgent', 'fast', rid)
|
|
3305
|
+
];
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
function resolveRemoteFastLauncher() {
|
|
3309
|
+
const rid = getRemoteFastRuntimeId();
|
|
3310
|
+
if (!rid) {
|
|
3311
|
+
return {
|
|
3312
|
+
ok: false,
|
|
3313
|
+
error: `remote-fast-not-packaged:${process.platform}-${process.arch}`
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
const failures = [];
|
|
3318
|
+
const seen = new Set();
|
|
3319
|
+
for (const dir of getBundledRemoteFastRuntimeDirs()) {
|
|
3320
|
+
const resolvedDir = path.resolve(dir);
|
|
3321
|
+
const key = resolvedDir.toLowerCase();
|
|
3322
|
+
if (seen.has(key)) {
|
|
3323
|
+
continue;
|
|
3324
|
+
}
|
|
3325
|
+
seen.add(key);
|
|
3326
|
+
|
|
3327
|
+
const executable = path.join(resolvedDir, getRemoteFastRuntimeExecutableName());
|
|
3328
|
+
try {
|
|
3329
|
+
if (statSync(executable).isFile()) {
|
|
3330
|
+
if (process.platform !== 'win32') {
|
|
3331
|
+
try {
|
|
3332
|
+
const currentMode = statSync(executable).mode;
|
|
3333
|
+
chmodSync(executable, currentMode | 0o755);
|
|
3334
|
+
} catch {
|
|
3335
|
+
// best effort only
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
const preflight = spawnSync(executable, ['--version'], {
|
|
3340
|
+
encoding: 'utf8',
|
|
3341
|
+
windowsHide: true,
|
|
3342
|
+
timeout: 2500
|
|
3343
|
+
});
|
|
3344
|
+
if (preflight.status === 0) {
|
|
3345
|
+
return {
|
|
3346
|
+
ok: true,
|
|
3347
|
+
command: executable,
|
|
3348
|
+
argsPrefix: [],
|
|
3349
|
+
launcher: executable,
|
|
3350
|
+
usingNpx: false,
|
|
3351
|
+
directFast: true,
|
|
3352
|
+
rid
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3355
|
+
failures.push(`${executable}:exit:${preflight.status}`);
|
|
3356
|
+
}
|
|
3357
|
+
} catch {
|
|
3358
|
+
// Try DLL or next directory.
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
const dll = path.join(resolvedDir, 'mindexec-remote-fast.dll');
|
|
3362
|
+
try {
|
|
3363
|
+
if (statSync(dll).isFile()) {
|
|
3364
|
+
const preflight = spawnSync('dotnet', [dll, '--version'], {
|
|
3365
|
+
encoding: 'utf8',
|
|
3366
|
+
windowsHide: true,
|
|
3367
|
+
timeout: 2500
|
|
3368
|
+
});
|
|
3369
|
+
if (preflight.status === 0) {
|
|
3370
|
+
return {
|
|
3371
|
+
ok: true,
|
|
3372
|
+
command: 'dotnet',
|
|
3373
|
+
argsPrefix: [dll],
|
|
3374
|
+
launcher: dll,
|
|
3375
|
+
usingNpx: false,
|
|
3376
|
+
directFast: true,
|
|
3377
|
+
rid
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
failures.push(`${dll}:exit:${preflight.status}`);
|
|
3381
|
+
}
|
|
3382
|
+
} catch {
|
|
3383
|
+
// Try next directory.
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
return {
|
|
3388
|
+
ok: false,
|
|
3389
|
+
error: failures.length > 0
|
|
3390
|
+
? failures.join('; ')
|
|
3391
|
+
: `remote-fast-runtime-missing:${rid}`
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
function resolveLocalRemotePackageLaunchers() {
|
|
3396
|
+
return [
|
|
3397
|
+
path.join(BRIDGE_ROOT, 'remote', 'bin', 'mindexec-remote.js'),
|
|
3398
|
+
path.join(BRIDGE_ROOT, 'node_modules', '@mindexec', 'remote', 'bin', 'mindexec-remote.js'),
|
|
3399
|
+
path.resolve(BRIDGE_ROOT, '..', 'remote', 'bin', 'mindexec-remote.js'),
|
|
3400
|
+
path.resolve(BRIDGE_ROOT, '..', 'RemoteAgent', 'bin', 'mindexec-remote.js')
|
|
3401
|
+
];
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
function resolveRemotePackageLauncher() {
|
|
3405
|
+
const seen = new Set();
|
|
3406
|
+
for (const candidate of resolveLocalRemotePackageLaunchers()) {
|
|
3407
|
+
const resolved = path.resolve(candidate);
|
|
3408
|
+
const key = resolved.toLowerCase();
|
|
3409
|
+
if (seen.has(key)) {
|
|
3410
|
+
continue;
|
|
3411
|
+
}
|
|
3412
|
+
seen.add(key);
|
|
3413
|
+
try {
|
|
3414
|
+
if (statSync(resolved).isFile()) {
|
|
3415
|
+
return {
|
|
3416
|
+
command: process.execPath,
|
|
3417
|
+
argsPrefix: [resolved],
|
|
3418
|
+
launcher: resolved,
|
|
3419
|
+
usingNpx: false,
|
|
3420
|
+
directFast: false
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
} catch {
|
|
3424
|
+
// Try next package layout.
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
return null;
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
function resolveRemoteAgentLauncher(engine = REMOTE_AGENT_DEFAULT_ENGINE) {
|
|
3431
|
+
const normalizedEngine = normalizeRemoteAgentEngine(engine);
|
|
3432
|
+
if (normalizedEngine !== 'node') {
|
|
3433
|
+
const fast = resolveRemoteFastLauncher();
|
|
3434
|
+
if (fast.ok) {
|
|
3435
|
+
return fast;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
if (normalizedEngine === 'fast') {
|
|
3232
3439
|
return {
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
launcher: localLauncher,
|
|
3236
|
-
usingNpx: false
|
|
3440
|
+
ok: false,
|
|
3441
|
+
error: fast.error || 'remote-fast-unavailable'
|
|
3237
3442
|
};
|
|
3238
3443
|
}
|
|
3239
|
-
}
|
|
3240
|
-
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
const localPackage = resolveRemotePackageLauncher();
|
|
3447
|
+
if (localPackage) {
|
|
3448
|
+
return localPackage;
|
|
3241
3449
|
}
|
|
3242
3450
|
|
|
3243
3451
|
const npxCli = resolveNpxCliLauncher();
|
|
@@ -3246,7 +3454,8 @@ function resolveRemoteAgentLauncher() {
|
|
|
3246
3454
|
command: process.execPath,
|
|
3247
3455
|
argsPrefix: [npxCli, '-y', '@mindexec/remote@latest'],
|
|
3248
3456
|
launcher: npxCli,
|
|
3249
|
-
usingNpx: true
|
|
3457
|
+
usingNpx: true,
|
|
3458
|
+
directFast: false
|
|
3250
3459
|
};
|
|
3251
3460
|
}
|
|
3252
3461
|
|
|
@@ -3255,7 +3464,8 @@ function resolveRemoteAgentLauncher() {
|
|
|
3255
3464
|
command: process.env.ComSpec || 'cmd.exe',
|
|
3256
3465
|
argsPrefix: ['/d', '/s', '/c', 'npx.cmd', '-y', '@mindexec/remote@latest'],
|
|
3257
3466
|
launcher: 'cmd.exe /c npx.cmd',
|
|
3258
|
-
usingNpx: true
|
|
3467
|
+
usingNpx: true,
|
|
3468
|
+
directFast: false
|
|
3259
3469
|
};
|
|
3260
3470
|
}
|
|
3261
3471
|
|
|
@@ -3263,7 +3473,8 @@ function resolveRemoteAgentLauncher() {
|
|
|
3263
3473
|
command: 'npx',
|
|
3264
3474
|
argsPrefix: ['-y', '@mindexec/remote@latest'],
|
|
3265
3475
|
launcher: 'npx',
|
|
3266
|
-
usingNpx: true
|
|
3476
|
+
usingNpx: true,
|
|
3477
|
+
directFast: false
|
|
3267
3478
|
};
|
|
3268
3479
|
}
|
|
3269
3480
|
|
|
@@ -3343,11 +3554,11 @@ async function startRemoteAgentConnection(options = {}) {
|
|
|
3343
3554
|
const nodeId = safeRemoteAgentField(options.nodeId, 128);
|
|
3344
3555
|
const source = safeRemoteAgentField(options.source || 'registry', 64);
|
|
3345
3556
|
const engine = normalizeRemoteAgentEngine(options.engine);
|
|
3346
|
-
const managers = normalizeRemoteManagerEndpointList(
|
|
3557
|
+
const managers = prioritizeRemoteAgentManagers(normalizeRemoteManagerEndpointList(
|
|
3347
3558
|
options.manager || options.managerEndpoint || options.endpoint,
|
|
3348
3559
|
options.managerCandidates,
|
|
3349
3560
|
options.endpointCandidates,
|
|
3350
|
-
options.candidates);
|
|
3561
|
+
options.candidates), leaseId);
|
|
3351
3562
|
|
|
3352
3563
|
if (managers.length === 0) {
|
|
3353
3564
|
logWarn('remote', 'managed RemoteAgent connect rejected: invalid manager endpoint');
|
|
@@ -3385,45 +3596,321 @@ async function startRemoteAgentConnection(options = {}) {
|
|
|
3385
3596
|
await stopRemoteAgentConnection('replaced-by-new-target');
|
|
3386
3597
|
}
|
|
3387
3598
|
|
|
3388
|
-
|
|
3599
|
+
if (remoteAgentConnectPromise) {
|
|
3600
|
+
return await remoteAgentConnectPromise;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
remoteAgentConnectPromise = startRemoteAgentConnectionRace({
|
|
3604
|
+
managers,
|
|
3605
|
+
pairToken,
|
|
3606
|
+
leaseId,
|
|
3607
|
+
nodeId,
|
|
3608
|
+
source,
|
|
3609
|
+
engine
|
|
3610
|
+
}).finally(() => {
|
|
3611
|
+
remoteAgentConnectPromise = null;
|
|
3612
|
+
});
|
|
3613
|
+
return await remoteAgentConnectPromise;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
function buildRemoteAgentLaunchArgs(launcher, manager, pairToken, engine) {
|
|
3617
|
+
const args = [
|
|
3618
|
+
...launcher.argsPrefix,
|
|
3619
|
+
'connect',
|
|
3620
|
+
'--manager',
|
|
3621
|
+
manager,
|
|
3622
|
+
'--pair',
|
|
3623
|
+
pairToken
|
|
3624
|
+
];
|
|
3625
|
+
|
|
3626
|
+
if (launcher.directFast === true) {
|
|
3627
|
+
args.push('--transport', 'ws');
|
|
3628
|
+
return args;
|
|
3629
|
+
}
|
|
3630
|
+
|
|
3631
|
+
args.push('--engine', engine);
|
|
3632
|
+
return args;
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
function appendRemoteAgentAttemptOutput(attempt, stream, chunk) {
|
|
3636
|
+
const text = chunk.toString();
|
|
3637
|
+
const key = stream === 'stderr' ? 'stderrTail' : 'stdoutTail';
|
|
3638
|
+
attempt.state[key] = String((attempt.state[key] || '') + text).slice(-REMOTE_AGENT_STDIO_TAIL_CHARS);
|
|
3639
|
+
if (/Connected to RemoteHub/i.test(text)) {
|
|
3640
|
+
attempt.state.ready = true;
|
|
3641
|
+
attempt.state.connectedAt = new Date().toISOString();
|
|
3642
|
+
}
|
|
3643
|
+
attempt.state.updatedAt = new Date().toISOString();
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
function summarizeRemoteAgentAttempt(attempt) {
|
|
3647
|
+
return formatRemoteAgentFailureSummary(attempt?.state || {});
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
function createRemoteAgentAttempt(options = {}) {
|
|
3651
|
+
const manager = normalizeRemoteManagerEndpoint(options.manager);
|
|
3652
|
+
const pairToken = safeRemoteAgentField(options.pairToken || options.pair, 512);
|
|
3653
|
+
const leaseId = safeRemoteAgentField(options.leaseId, 128);
|
|
3654
|
+
const nodeId = safeRemoteAgentField(options.nodeId, 128);
|
|
3655
|
+
const source = safeRemoteAgentField(options.source || 'registry', 64);
|
|
3656
|
+
const engine = normalizeRemoteAgentEngine(options.engine);
|
|
3657
|
+
const managerCandidates = normalizeRemoteManagerEndpointList(options.managerCandidates, manager);
|
|
3658
|
+
|
|
3659
|
+
const connectionKey = createRemoteAgentConnectionKey(manager, leaseId);
|
|
3660
|
+
const launcher = options.launcher || resolveRemoteAgentLauncher(engine);
|
|
3661
|
+
if (launcher?.ok === false) {
|
|
3662
|
+
return {
|
|
3663
|
+
manager,
|
|
3664
|
+
failed: true,
|
|
3665
|
+
error: launcher.error || 'remote-agent-launcher-unavailable',
|
|
3666
|
+
state: createRemoteAgentIdleState({
|
|
3667
|
+
status: 'failed',
|
|
3668
|
+
manager,
|
|
3669
|
+
managerCandidates,
|
|
3670
|
+
leaseId,
|
|
3671
|
+
nodeId,
|
|
3672
|
+
engine,
|
|
3673
|
+
source,
|
|
3674
|
+
connectionKey,
|
|
3675
|
+
lastError: launcher.error || 'remote-agent-launcher-unavailable'
|
|
3676
|
+
})
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
const state = createRemoteAgentIdleState({
|
|
3681
|
+
status: 'starting',
|
|
3682
|
+
manager,
|
|
3683
|
+
managerCandidates,
|
|
3684
|
+
leaseId,
|
|
3685
|
+
nodeId,
|
|
3686
|
+
engine,
|
|
3687
|
+
source,
|
|
3688
|
+
connectionKey,
|
|
3689
|
+
startedAt: new Date().toISOString(),
|
|
3690
|
+
ready: false,
|
|
3691
|
+
connectedAt: '',
|
|
3692
|
+
launcher: launcher.launcher,
|
|
3693
|
+
usingNpx: launcher.usingNpx
|
|
3694
|
+
});
|
|
3695
|
+
|
|
3696
|
+
const args = buildRemoteAgentLaunchArgs(launcher, manager, pairToken, engine);
|
|
3697
|
+
const attempt = {
|
|
3698
|
+
manager,
|
|
3699
|
+
pairToken,
|
|
3700
|
+
leaseId,
|
|
3701
|
+
nodeId,
|
|
3702
|
+
source,
|
|
3703
|
+
engine,
|
|
3704
|
+
launcher,
|
|
3705
|
+
args,
|
|
3706
|
+
state,
|
|
3707
|
+
child: null,
|
|
3708
|
+
failed: false,
|
|
3709
|
+
error: ''
|
|
3710
|
+
};
|
|
3711
|
+
|
|
3712
|
+
try {
|
|
3713
|
+
const child = spawn(launcher.command, args, {
|
|
3714
|
+
cwd: BRIDGE_ROOT,
|
|
3715
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
3716
|
+
windowsHide: true,
|
|
3717
|
+
env: {
|
|
3718
|
+
...process.env,
|
|
3719
|
+
MINDEXEC_REMOTE_AGENT_MANAGED: '1'
|
|
3720
|
+
}
|
|
3721
|
+
});
|
|
3722
|
+
|
|
3723
|
+
attempt.child = child;
|
|
3724
|
+
state.proc = child;
|
|
3725
|
+
state.pid = Number(child.pid || 0);
|
|
3726
|
+
state.status = 'running';
|
|
3727
|
+
state.updatedAt = new Date().toISOString();
|
|
3728
|
+
|
|
3729
|
+
child.stdout.on('data', chunk => appendRemoteAgentAttemptOutput(attempt, 'stdout', chunk));
|
|
3730
|
+
child.stderr.on('data', chunk => appendRemoteAgentAttemptOutput(attempt, 'stderr', chunk));
|
|
3731
|
+
child.once('error', err => {
|
|
3732
|
+
attempt.failed = true;
|
|
3733
|
+
attempt.error = err?.message || String(err);
|
|
3734
|
+
state.status = 'failed';
|
|
3735
|
+
state.lastError = attempt.error;
|
|
3736
|
+
state.updatedAt = new Date().toISOString();
|
|
3737
|
+
});
|
|
3738
|
+
child.once('exit', (code, signal) => {
|
|
3739
|
+
if (state.status === 'running' && state.ready === true) {
|
|
3740
|
+
state.status = code === 0 ? 'exited' : 'failed';
|
|
3741
|
+
} else {
|
|
3742
|
+
attempt.failed = true;
|
|
3743
|
+
state.status = code === 0 ? 'exited' : 'failed';
|
|
3744
|
+
}
|
|
3745
|
+
state.exitCode = Number.isFinite(code) ? code : null;
|
|
3746
|
+
state.signal = signal || '';
|
|
3747
|
+
state.exitedAt = new Date().toISOString();
|
|
3748
|
+
state.updatedAt = state.exitedAt;
|
|
3749
|
+
if (code !== 0 && !state.lastError) {
|
|
3750
|
+
state.lastError = signal ? `signal:${signal}` : `exit:${code}`;
|
|
3751
|
+
}
|
|
3752
|
+
attempt.error = attempt.error || state.lastError || `exit:${code ?? 'unknown'}`;
|
|
3753
|
+
});
|
|
3754
|
+
} catch (err) {
|
|
3755
|
+
attempt.failed = true;
|
|
3756
|
+
attempt.error = err?.message || String(err);
|
|
3757
|
+
state.status = 'failed';
|
|
3758
|
+
state.lastError = attempt.error;
|
|
3759
|
+
state.updatedAt = new Date().toISOString();
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
return attempt;
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
async function stopRemoteAgentAttempt(attempt, reason = 'race-lost') {
|
|
3766
|
+
if (!attempt?.child) {
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3770
|
+
if (attempt.child.exitCode !== null || attempt.child.killed) {
|
|
3771
|
+
return;
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
try {
|
|
3775
|
+
await terminateProcessTree(attempt.child);
|
|
3776
|
+
} catch (err) {
|
|
3777
|
+
logWarn('remote', `managed RemoteAgent cleanup failed ${formatKeyValue('manager', attempt.manager || '-')} ${formatKeyValue('reason', reason)} ${formatKeyValue('error', err?.message || err)}`);
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
async function startRemoteAgentConnectionRace(options = {}) {
|
|
3782
|
+
const managers = normalizeRemoteManagerEndpointList(options.managers).slice(0, REMOTE_AGENT_MAX_PARALLEL_CANDIDATES);
|
|
3783
|
+
const pairToken = safeRemoteAgentField(options.pairToken || options.pair, 512);
|
|
3784
|
+
const leaseId = safeRemoteAgentField(options.leaseId, 128);
|
|
3785
|
+
const nodeId = safeRemoteAgentField(options.nodeId, 128);
|
|
3786
|
+
const source = safeRemoteAgentField(options.source || 'registry', 64);
|
|
3787
|
+
const engine = normalizeRemoteAgentEngine(options.engine);
|
|
3788
|
+
const launcher = resolveRemoteAgentLauncher(engine);
|
|
3789
|
+
if (launcher?.ok === false) {
|
|
3790
|
+
const error = launcher.error || 'remote-agent-launcher-unavailable';
|
|
3791
|
+
remoteAgentState = createRemoteAgentIdleState({
|
|
3792
|
+
status: 'failed',
|
|
3793
|
+
manager: managers[0] || '',
|
|
3794
|
+
managerCandidates: managers,
|
|
3795
|
+
leaseId,
|
|
3796
|
+
nodeId,
|
|
3797
|
+
engine,
|
|
3798
|
+
source,
|
|
3799
|
+
lastError: error,
|
|
3800
|
+
updatedAt: new Date().toISOString()
|
|
3801
|
+
});
|
|
3802
|
+
emitBridgeEvent('RemoteAgentFailed', serializeRemoteAgentState());
|
|
3803
|
+
return { ok: false, error, agent: serializeRemoteAgentState() };
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
remoteAgentState = createRemoteAgentIdleState({
|
|
3807
|
+
status: 'starting',
|
|
3808
|
+
manager: managers[0] || '',
|
|
3809
|
+
managerCandidates: managers,
|
|
3810
|
+
leaseId,
|
|
3811
|
+
nodeId,
|
|
3812
|
+
engine,
|
|
3813
|
+
source,
|
|
3814
|
+
startedAt: new Date().toISOString(),
|
|
3815
|
+
launcher: 'race',
|
|
3816
|
+
usingNpx: false
|
|
3817
|
+
});
|
|
3818
|
+
|
|
3819
|
+
const attempts = [];
|
|
3389
3820
|
for (let index = 0; index < managers.length; index += 1) {
|
|
3390
3821
|
const manager = managers[index];
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
'remote');
|
|
3396
|
-
}
|
|
3822
|
+
logEvent(
|
|
3823
|
+
'remote',
|
|
3824
|
+
`managed RemoteAgent race ${index + 1}/${managers.length} ${formatKeyValue('manager', manager)}`,
|
|
3825
|
+
'remote');
|
|
3397
3826
|
|
|
3398
|
-
const
|
|
3827
|
+
const attempt = createRemoteAgentAttempt({
|
|
3399
3828
|
manager,
|
|
3400
3829
|
managerCandidates: managers,
|
|
3401
3830
|
pairToken,
|
|
3402
3831
|
leaseId,
|
|
3403
3832
|
nodeId,
|
|
3404
3833
|
source,
|
|
3405
|
-
engine
|
|
3834
|
+
engine,
|
|
3835
|
+
launcher
|
|
3406
3836
|
});
|
|
3407
|
-
|
|
3408
|
-
|
|
3837
|
+
attempts.push(attempt);
|
|
3838
|
+
|
|
3839
|
+
if (index + 1 < managers.length && REMOTE_AGENT_RACE_START_STAGGER_MS > 0) {
|
|
3840
|
+
await new Promise(resolve => setTimeout(resolve, REMOTE_AGENT_RACE_START_STAGGER_MS));
|
|
3409
3841
|
}
|
|
3842
|
+
}
|
|
3410
3843
|
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3844
|
+
const startedAt = Date.now();
|
|
3845
|
+
let winner = null;
|
|
3846
|
+
while (Date.now() - startedAt < REMOTE_AGENT_READY_TIMEOUT_MS) {
|
|
3847
|
+
winner = attempts.find(attempt =>
|
|
3848
|
+
attempt?.state?.ready === true
|
|
3849
|
+
&& attempt?.child
|
|
3850
|
+
&& attempt.child.exitCode === null
|
|
3851
|
+
&& !attempt.child.killed);
|
|
3852
|
+
if (winner) {
|
|
3853
|
+
break;
|
|
3414
3854
|
}
|
|
3415
3855
|
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3856
|
+
const allFinished = attempts.every(attempt =>
|
|
3857
|
+
attempt.failed === true
|
|
3858
|
+
|| !attempt.child
|
|
3859
|
+
|| attempt.child.exitCode !== null
|
|
3860
|
+
|| attempt.child.killed);
|
|
3861
|
+
if (allFinished) {
|
|
3862
|
+
break;
|
|
3420
3863
|
}
|
|
3864
|
+
|
|
3865
|
+
await new Promise(resolve => setTimeout(resolve, 60));
|
|
3421
3866
|
}
|
|
3422
3867
|
|
|
3868
|
+
if (winner) {
|
|
3869
|
+
for (const attempt of attempts) {
|
|
3870
|
+
if (attempt !== winner) {
|
|
3871
|
+
await stopRemoteAgentAttempt(attempt, 'race-lost');
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
remoteAgentState = winner.state;
|
|
3876
|
+
remoteAgentState.status = 'running';
|
|
3877
|
+
remoteAgentState.proc = winner.child;
|
|
3878
|
+
remoteAgentState.updatedAt = new Date().toISOString();
|
|
3879
|
+
rememberRemoteAgentSuccessfulManager(winner.manager, leaseId);
|
|
3880
|
+
emitBridgeEvent('RemoteAgentStarted', serializeRemoteAgentState());
|
|
3881
|
+
logEvent(
|
|
3882
|
+
'remote',
|
|
3883
|
+
`managed RemoteAgent connected ${formatKeyValue('manager', winner.manager)} ${formatKeyValue('launcher', winner.launcher?.launcher || '-')}`,
|
|
3884
|
+
'remote');
|
|
3885
|
+
return { ok: true, alreadyRunning: false, agent: serializeRemoteAgentState() };
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
const lastAttempt = attempts.findLast?.(attempt => attempt?.error || attempt?.state?.lastError)
|
|
3889
|
+
|| attempts[attempts.length - 1]
|
|
3890
|
+
|| null;
|
|
3891
|
+
for (const attempt of attempts) {
|
|
3892
|
+
await stopRemoteAgentAttempt(attempt, 'race-failed');
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
const error = lastAttempt
|
|
3896
|
+
? summarizeRemoteAgentAttempt(lastAttempt)
|
|
3897
|
+
: 'all-manager-candidates-failed';
|
|
3898
|
+
remoteAgentState = createRemoteAgentIdleState({
|
|
3899
|
+
status: 'failed',
|
|
3900
|
+
manager: managers[0] || '',
|
|
3901
|
+
managerCandidates: managers,
|
|
3902
|
+
leaseId,
|
|
3903
|
+
nodeId,
|
|
3904
|
+
engine,
|
|
3905
|
+
source,
|
|
3906
|
+
lastError: error,
|
|
3907
|
+
updatedAt: new Date().toISOString()
|
|
3908
|
+
});
|
|
3909
|
+
emitBridgeEvent('RemoteAgentFailed', serializeRemoteAgentState());
|
|
3423
3910
|
return {
|
|
3424
3911
|
ok: false,
|
|
3425
|
-
error
|
|
3426
|
-
agent:
|
|
3912
|
+
error,
|
|
3913
|
+
agent: serializeRemoteAgentState()
|
|
3427
3914
|
};
|
|
3428
3915
|
}
|
|
3429
3916
|
|
|
@@ -3445,17 +3932,11 @@ async function startRemoteAgentConnectionAttempt(options = {}) {
|
|
|
3445
3932
|
}
|
|
3446
3933
|
|
|
3447
3934
|
const connectionKey = createRemoteAgentConnectionKey(manager, leaseId);
|
|
3448
|
-
const launcher = resolveRemoteAgentLauncher();
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
manager,
|
|
3454
|
-
'--pair',
|
|
3455
|
-
pairToken,
|
|
3456
|
-
'--engine',
|
|
3457
|
-
engine
|
|
3458
|
-
];
|
|
3935
|
+
const launcher = resolveRemoteAgentLauncher(engine);
|
|
3936
|
+
if (launcher?.ok === false) {
|
|
3937
|
+
return { ok: false, error: launcher.error || 'remote-agent-launcher-unavailable', agent: serializeRemoteAgentState() };
|
|
3938
|
+
}
|
|
3939
|
+
const args = buildRemoteAgentLaunchArgs(launcher, manager, pairToken, engine);
|
|
3459
3940
|
|
|
3460
3941
|
remoteAgentState = createRemoteAgentIdleState({
|
|
3461
3942
|
status: 'starting',
|
|
@@ -8065,7 +8546,7 @@ app.get('/api/remote/status', (req, res) => {
|
|
|
8065
8546
|
|
|
8066
8547
|
app.get('/api/remote/devices', (req, res) => {
|
|
8067
8548
|
res.setHeader('Cache-Control', 'no-store');
|
|
8068
|
-
const includeDataUrl =
|
|
8549
|
+
const includeDataUrl = /^(1|true|yes|on|data|base64)$/i.test(String(req.query?.includeDataUrl ?? req.query?.frameData ?? ''));
|
|
8069
8550
|
const devices = remoteHub.listDevices({ includeDataUrl });
|
|
8070
8551
|
res.json({
|
|
8071
8552
|
total: devices.length,
|
|
@@ -8285,7 +8766,7 @@ function isRemoteCapabilityEnabled(device, key) {
|
|
|
8285
8766
|
}
|
|
8286
8767
|
|
|
8287
8768
|
function includeRemoteFrameDataUrl(req) {
|
|
8288
|
-
return
|
|
8769
|
+
return /^(1|true|yes|on|data|base64)$/i.test(String(req.query?.includeDataUrl ?? req.query?.frameData ?? ''));
|
|
8289
8770
|
}
|
|
8290
8771
|
|
|
8291
8772
|
function sendRemoteFrameBinary(req, res, frameKind) {
|
|
@@ -13463,6 +13463,8 @@
|
|
|
13463
13463
|
const REMOTE_FLEET_THUMBNAIL_FRAME_DECODE_TIMEOUT_MS = 1200;
|
|
13464
13464
|
const REMOTE_FLEET_BINARY_FRAME_WS_RECONNECT_MS = 1500;
|
|
13465
13465
|
const REMOTE_FLEET_BINARY_FRAME_SUBSCRIBE_MS = 1000;
|
|
13466
|
+
const REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL = 8;
|
|
13467
|
+
const REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS = 3000;
|
|
13466
13468
|
const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
|
|
13467
13469
|
const REMOTE_FLEET_TASK_FOLLOW_REFRESH_MS = 2000;
|
|
13468
13470
|
const REMOTE_FLEET_TASK_FOLLOW_MAX_TICKS = 60;
|
|
@@ -13760,15 +13762,13 @@
|
|
|
13760
13762
|
const payload = buffer.slice(4 + metaLength);
|
|
13761
13763
|
const mimeType = String(metadata.mimeType || metadata.format || 'image/jpeg').trim() || 'image/jpeg';
|
|
13762
13764
|
const payloadBlob = new Blob([payload], { type: mimeType });
|
|
13763
|
-
const objectUrl = URL.createObjectURL(payloadBlob);
|
|
13764
13765
|
return {
|
|
13765
13766
|
...metadata,
|
|
13766
13767
|
kind: String(metadata.kind || 'live').toLowerCase() === 'thumbnail' ? 'thumbnail' : 'live',
|
|
13767
13768
|
mimeType,
|
|
13768
|
-
frameUrl:
|
|
13769
|
-
framePath:
|
|
13769
|
+
frameUrl: '',
|
|
13770
|
+
framePath: '',
|
|
13770
13771
|
dataUrl: '',
|
|
13771
|
-
_remoteFleetObjectUrl: objectUrl,
|
|
13772
13772
|
_remoteFleetPayloadBlob: payloadBlob,
|
|
13773
13773
|
_remoteFleetBinaryFrame: true
|
|
13774
13774
|
};
|
|
@@ -14103,6 +14103,7 @@
|
|
|
14103
14103
|
function isRemoteFleetFrameSource(value) {
|
|
14104
14104
|
const source = String(value || '').trim();
|
|
14105
14105
|
if (/^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(source)
|
|
14106
|
+
|| /^blob:/i.test(source)
|
|
14106
14107
|
|| /^\/api\/remote\//i.test(source)) {
|
|
14107
14108
|
return true;
|
|
14108
14109
|
}
|
|
@@ -14370,11 +14371,26 @@
|
|
|
14370
14371
|
}
|
|
14371
14372
|
|
|
14372
14373
|
async function loadRemoteFleetFrameBitmap(frame) {
|
|
14373
|
-
if (!frame
|
|
14374
|
+
if (!frame) {
|
|
14375
|
+
return null;
|
|
14376
|
+
}
|
|
14377
|
+
|
|
14378
|
+
if (typeof createImageBitmap !== 'function') {
|
|
14379
|
+
return null;
|
|
14380
|
+
}
|
|
14381
|
+
|
|
14382
|
+
const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
|
|
14383
|
+
if (frame._remoteFleetPayloadBlob) {
|
|
14384
|
+
return await withRemoteFleetFrameTimeout(
|
|
14385
|
+
createImageBitmap(frame._remoteFleetPayloadBlob),
|
|
14386
|
+
timeoutMs);
|
|
14387
|
+
}
|
|
14388
|
+
|
|
14389
|
+
if (!isRemoteFleetFrameSource(frame.frameUrl)) {
|
|
14374
14390
|
return null;
|
|
14375
14391
|
}
|
|
14376
14392
|
|
|
14377
|
-
if (typeof fetch !== 'function'
|
|
14393
|
+
if (typeof fetch !== 'function') {
|
|
14378
14394
|
return null;
|
|
14379
14395
|
}
|
|
14380
14396
|
|
|
@@ -14412,7 +14428,6 @@
|
|
|
14412
14428
|
entry.expiresAt = now + REMOTE_FLEET_FRAME_BLOB_CACHE_MS;
|
|
14413
14429
|
}
|
|
14414
14430
|
|
|
14415
|
-
const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
|
|
14416
14431
|
const blob = await withRemoteFleetFrameTimeout(
|
|
14417
14432
|
entry.promise,
|
|
14418
14433
|
timeoutMs,
|
|
@@ -14426,12 +14441,12 @@
|
|
|
14426
14441
|
String(frame?.kind || '').trim().toLowerCase(),
|
|
14427
14442
|
String(frame?.streamId || '').trim(),
|
|
14428
14443
|
String(Number(frame?.frameSeq || 0) || 0),
|
|
14429
|
-
String(frame?.frameUrl || '').trim()
|
|
14444
|
+
String(frame?.contentHash || frame?.frameUrl || '').trim()
|
|
14430
14445
|
].join('|');
|
|
14431
14446
|
}
|
|
14432
14447
|
|
|
14433
14448
|
function cloneRemoteFleetFramePatch(frame) {
|
|
14434
|
-
|
|
14449
|
+
const patch = {
|
|
14435
14450
|
deviceId: String(frame?.deviceId || '').trim(),
|
|
14436
14451
|
kind: String(frame?.kind || 'thumbnail').trim().toLowerCase(),
|
|
14437
14452
|
frameSeq: Number.isFinite(Number(frame?.frameSeq)) ? Math.floor(Number(frame.frameSeq)) : 0,
|
|
@@ -14443,10 +14458,22 @@
|
|
|
14443
14458
|
captureMs: Number(frame?.captureMs || 0) || 0,
|
|
14444
14459
|
sameContentStreak: Number(frame?.sameContentStreak || 0) || 0
|
|
14445
14460
|
};
|
|
14461
|
+
if (frame?._remoteFleetPayloadBlob) {
|
|
14462
|
+
patch._remoteFleetPayloadBlob = frame._remoteFleetPayloadBlob;
|
|
14463
|
+
}
|
|
14464
|
+
if (frame?._remoteFleetObjectUrl) {
|
|
14465
|
+
patch._remoteFleetObjectUrl = frame._remoteFleetObjectUrl;
|
|
14466
|
+
}
|
|
14467
|
+
if (frame?._remoteFleetBinaryFrame === true) {
|
|
14468
|
+
patch._remoteFleetBinaryFrame = true;
|
|
14469
|
+
}
|
|
14470
|
+
return patch;
|
|
14446
14471
|
}
|
|
14447
14472
|
|
|
14448
14473
|
function isRemoteFleetFrameNewerForPreview(preview, frame, comparePending = false) {
|
|
14449
|
-
|
|
14474
|
+
const hasPayloadBlob = !!frame?._remoteFleetPayloadBlob;
|
|
14475
|
+
const hasFrameSource = isRemoteFleetFrameSource(frame?.frameUrl);
|
|
14476
|
+
if (!preview || !frame || (!hasPayloadBlob && !hasFrameSource)) {
|
|
14450
14477
|
return false;
|
|
14451
14478
|
}
|
|
14452
14479
|
|
|
@@ -14543,23 +14570,7 @@
|
|
|
14543
14570
|
placeholder.remove();
|
|
14544
14571
|
}
|
|
14545
14572
|
|
|
14546
|
-
|
|
14547
|
-
if (previewMode === 'tile') {
|
|
14548
|
-
preview.querySelector?.('[data-remote-fleet-frame-badge="true"]')?.remove();
|
|
14549
|
-
return true;
|
|
14550
|
-
}
|
|
14551
|
-
|
|
14552
|
-
const badge = ensureRemoteFleetFrameBadge(preview, frame);
|
|
14553
|
-
if (badge) {
|
|
14554
|
-
const at = frame.receivedAt || frame.capturedAt || '';
|
|
14555
|
-
badge.textContent = frame.kind === 'live'
|
|
14556
|
-
? `LIVE ${at ? formatRemoteFleetAge(at) : ''}`.trim()
|
|
14557
|
-
: (at ? formatRemoteFleetAge(at) : 'Screen');
|
|
14558
|
-
badge.style.display = 'inline-flex';
|
|
14559
|
-
badge.style.background = frame.kind === 'live'
|
|
14560
|
-
? 'rgba(220, 38, 38, 0.84)'
|
|
14561
|
-
: 'rgba(15, 23, 42, 0.72)';
|
|
14562
|
-
}
|
|
14573
|
+
preview.querySelector?.('[data-remote-fleet-frame-badge="true"]')?.remove();
|
|
14563
14574
|
|
|
14564
14575
|
return true;
|
|
14565
14576
|
}
|
|
@@ -14631,7 +14642,7 @@
|
|
|
14631
14642
|
}
|
|
14632
14643
|
|
|
14633
14644
|
const frame = preview._remoteFleetPendingFrame;
|
|
14634
|
-
if (!frame || !isRemoteFleetFrameSource(frame.frameUrl)) {
|
|
14645
|
+
if (!frame || (!frame._remoteFleetPayloadBlob && !isRemoteFleetFrameSource(frame.frameUrl))) {
|
|
14635
14646
|
clearRemoteFleetPendingFrameDataset(preview, frame);
|
|
14636
14647
|
return;
|
|
14637
14648
|
}
|
|
@@ -14668,6 +14679,25 @@
|
|
|
14668
14679
|
return;
|
|
14669
14680
|
}
|
|
14670
14681
|
|
|
14682
|
+
let fallbackUrl = String(frame.frameUrl || '').trim();
|
|
14683
|
+
if (!isRemoteFleetFrameSource(fallbackUrl)
|
|
14684
|
+
&& frame._remoteFleetPayloadBlob
|
|
14685
|
+
&& typeof URL !== 'undefined'
|
|
14686
|
+
&& typeof URL.createObjectURL === 'function') {
|
|
14687
|
+
try {
|
|
14688
|
+
fallbackUrl = URL.createObjectURL(frame._remoteFleetPayloadBlob);
|
|
14689
|
+
frame.frameUrl = fallbackUrl;
|
|
14690
|
+
frame._remoteFleetObjectUrl = fallbackUrl;
|
|
14691
|
+
} catch {
|
|
14692
|
+
fallbackUrl = '';
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
|
|
14696
|
+
if (!isRemoteFleetFrameSource(fallbackUrl)) {
|
|
14697
|
+
finish(false, 'img-unavailable');
|
|
14698
|
+
return;
|
|
14699
|
+
}
|
|
14700
|
+
|
|
14671
14701
|
const timeoutMs = getRemoteFleetFrameDecodeTimeoutMs(frame);
|
|
14672
14702
|
let settled = false;
|
|
14673
14703
|
const complete = (loaded, surface = 'img') => {
|
|
@@ -14686,7 +14716,7 @@
|
|
|
14686
14716
|
decoded.then(() => complete(true, 'img'));
|
|
14687
14717
|
};
|
|
14688
14718
|
loader.onerror = () => complete(false, 'img');
|
|
14689
|
-
loader.src =
|
|
14719
|
+
loader.src = fallbackUrl;
|
|
14690
14720
|
};
|
|
14691
14721
|
|
|
14692
14722
|
const canvas = ensureRemoteFleetFrameCanvas(preview);
|
|
@@ -14748,7 +14778,7 @@
|
|
|
14748
14778
|
seq: frame.frameSeq,
|
|
14749
14779
|
error: error?.message || String(error || '')
|
|
14750
14780
|
});
|
|
14751
|
-
|
|
14781
|
+
loadFallbackImage();
|
|
14752
14782
|
return;
|
|
14753
14783
|
}
|
|
14754
14784
|
|
|
@@ -14880,11 +14910,10 @@
|
|
|
14880
14910
|
|
|
14881
14911
|
function applyRemoteFleetFramePatchToPreview(preview, frame) {
|
|
14882
14912
|
if (String(frame?.kind || '').toLowerCase() === 'live') {
|
|
14883
|
-
preview.
|
|
14884
|
-
delete preview.dataset.
|
|
14885
|
-
delete preview.dataset.
|
|
14886
|
-
delete preview.dataset.
|
|
14887
|
-
return queueRemoteFleetImageSwap(preview, frame);
|
|
14913
|
+
preview._remoteFleetPendingImageFrame = null;
|
|
14914
|
+
delete preview.dataset.remoteFleetPendingImageKind;
|
|
14915
|
+
delete preview.dataset.remoteFleetPendingImageSeq;
|
|
14916
|
+
delete preview.dataset.remoteFleetPendingImageUrl;
|
|
14888
14917
|
}
|
|
14889
14918
|
|
|
14890
14919
|
return queueRemoteFleetFramePaint(preview, frame);
|
|
@@ -14964,6 +14993,16 @@
|
|
|
14964
14993
|
return;
|
|
14965
14994
|
}
|
|
14966
14995
|
|
|
14996
|
+
const binarySession = bodyView._remoteFleetBinaryFrameSession;
|
|
14997
|
+
const binarySocket = binarySession?.ws || null;
|
|
14998
|
+
if (binarySocket
|
|
14999
|
+
&& typeof WebSocket !== 'undefined'
|
|
15000
|
+
&& (binarySocket.readyState === WebSocket.OPEN || binarySocket.readyState === WebSocket.CONNECTING)) {
|
|
15001
|
+
lastTick = timestamp;
|
|
15002
|
+
bodyView._remoteFleetFrameLoopRaf = requestRemoteFleetFrameLoopFrame(loop);
|
|
15003
|
+
return;
|
|
15004
|
+
}
|
|
15005
|
+
|
|
14967
15006
|
if (timestamp - lastTick >= intervalMs && bodyView._remoteFleetFrameRefreshInFlight !== true) {
|
|
14968
15007
|
lastTick = timestamp;
|
|
14969
15008
|
bodyView._remoteFleetFrameRefreshInFlight = true;
|
|
@@ -15993,29 +16032,6 @@
|
|
|
15993
16032
|
`;
|
|
15994
16033
|
preview.appendChild(placeholder);
|
|
15995
16034
|
}
|
|
15996
|
-
const previewBadge = document.createElement('span');
|
|
15997
|
-
previewBadge.dataset.remoteFleetFrameBadge = 'true';
|
|
15998
|
-
previewBadge.textContent = hasLiveFrame
|
|
15999
|
-
? `LIVE ${previewAt ? formatRemoteFleetAge(previewAt) : ''}`.trim()
|
|
16000
|
-
: (previewAt ? formatRemoteFleetAge(previewAt) : (hubStatus === 'online' ? 'Pinned' : 'Hub offline'));
|
|
16001
|
-
previewBadge.style.cssText = `
|
|
16002
|
-
position: absolute;
|
|
16003
|
-
left: 7px;
|
|
16004
|
-
bottom: 7px;
|
|
16005
|
-
max-width: calc(100% - 14px);
|
|
16006
|
-
padding: 4px 7px;
|
|
16007
|
-
border-radius: 999px;
|
|
16008
|
-
background: ${hasLiveFrame ? 'rgba(220, 38, 38, 0.84)' : 'rgba(15, 23, 42, 0.74)'};
|
|
16009
|
-
color: #e2e8f0;
|
|
16010
|
-
font-size: 9px;
|
|
16011
|
-
font-weight: 950;
|
|
16012
|
-
line-height: 1;
|
|
16013
|
-
overflow: hidden;
|
|
16014
|
-
text-overflow: ellipsis;
|
|
16015
|
-
white-space: nowrap;
|
|
16016
|
-
letter-spacing: 0;
|
|
16017
|
-
`;
|
|
16018
|
-
preview.appendChild(previewBadge);
|
|
16019
16035
|
bodyView.appendChild(preview);
|
|
16020
16036
|
|
|
16021
16037
|
const stats = document.createElement('div');
|
|
@@ -17109,35 +17125,39 @@
|
|
|
17109
17125
|
return ids.slice(0, 120);
|
|
17110
17126
|
};
|
|
17111
17127
|
const ensureVisibleRemoteFleetLiveStreams = async () => {
|
|
17112
|
-
if (!(bodyView.
|
|
17113
|
-
bodyView.
|
|
17128
|
+
if (!(bodyView._remoteFleetAutoLiveStartInFlight instanceof Set)) {
|
|
17129
|
+
bodyView._remoteFleetAutoLiveStartInFlight = new Set();
|
|
17114
17130
|
}
|
|
17131
|
+
if (!(bodyView._remoteFleetAutoLiveStartAttemptAt instanceof Map)) {
|
|
17132
|
+
bodyView._remoteFleetAutoLiveStartAttemptAt = new Map();
|
|
17133
|
+
}
|
|
17134
|
+
const now = Date.now();
|
|
17115
17135
|
const targets = getVisibleLiveDevices()
|
|
17116
17136
|
.filter(device => {
|
|
17117
17137
|
const deviceId = getRemoteFleetDeviceId(device);
|
|
17138
|
+
const lastAttemptAt = Number(bodyView._remoteFleetAutoLiveStartAttemptAt.get(deviceId) || 0);
|
|
17118
17139
|
return deviceId
|
|
17119
17140
|
&& !isRemoteFleetLiveActive(device)
|
|
17120
|
-
&& !bodyView.
|
|
17141
|
+
&& !bodyView._remoteFleetAutoLiveStartInFlight.has(deviceId)
|
|
17142
|
+
&& (now - lastAttemptAt >= REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS);
|
|
17121
17143
|
});
|
|
17122
17144
|
if (targets.length === 0) {
|
|
17123
17145
|
return null;
|
|
17124
17146
|
}
|
|
17125
17147
|
|
|
17126
|
-
if (!(bodyView._remoteFleetAutoLiveStartInFlight instanceof Set)) {
|
|
17127
|
-
bodyView._remoteFleetAutoLiveStartInFlight = new Set();
|
|
17128
|
-
}
|
|
17129
|
-
|
|
17130
17148
|
let started = 0;
|
|
17131
17149
|
let lastResult = null;
|
|
17132
|
-
|
|
17150
|
+
const queue = targets.slice(0, 120);
|
|
17151
|
+
const startOne = async target => {
|
|
17133
17152
|
const deviceId = getRemoteFleetDeviceId(target);
|
|
17134
17153
|
if (!deviceId || bodyView._remoteFleetAutoLiveStartInFlight.has(deviceId)) {
|
|
17135
|
-
|
|
17154
|
+
return;
|
|
17136
17155
|
}
|
|
17137
17156
|
|
|
17138
17157
|
bodyView._remoteFleetAutoLiveStartInFlight.add(deviceId);
|
|
17158
|
+
bodyView._remoteFleetAutoLiveStartAttemptAt.set(deviceId, Date.now());
|
|
17139
17159
|
try {
|
|
17140
|
-
const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
|
|
17160
|
+
const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId, REMOTE_FLEET_MONITOR_LIVE_FPS);
|
|
17141
17161
|
lastResult = result;
|
|
17142
17162
|
const success = isRemoteFleetResultSuccess(result);
|
|
17143
17163
|
window.RuntimeTrace?.emit?.('remote.live.autoStart', {
|
|
@@ -17150,12 +17170,21 @@
|
|
|
17150
17170
|
});
|
|
17151
17171
|
if (success) {
|
|
17152
17172
|
started += 1;
|
|
17153
|
-
bodyView._remoteFleetAutoLiveStartedIds.add(deviceId);
|
|
17154
17173
|
}
|
|
17155
17174
|
} finally {
|
|
17156
17175
|
bodyView._remoteFleetAutoLiveStartInFlight.delete(deviceId);
|
|
17157
17176
|
}
|
|
17158
|
-
}
|
|
17177
|
+
};
|
|
17178
|
+
|
|
17179
|
+
const workerCount = Math.min(REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL, queue.length);
|
|
17180
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
17181
|
+
while (queue.length > 0) {
|
|
17182
|
+
const target = queue.shift();
|
|
17183
|
+
if (target) {
|
|
17184
|
+
await startOne(target);
|
|
17185
|
+
}
|
|
17186
|
+
}
|
|
17187
|
+
}));
|
|
17159
17188
|
|
|
17160
17189
|
if (started > 0) {
|
|
17161
17190
|
try {
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mainAssemblyName": "MindExecution.Web",
|
|
3
3
|
"resources": {
|
|
4
|
-
"hash": "sha256-
|
|
4
|
+
"hash": "sha256-o8W1U0lBmrbs5unXcwG+/IEh2KogSA7Rk3MqjQnNfGU=",
|
|
5
5
|
"fingerprinting": {
|
|
6
6
|
"Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
|
|
7
7
|
"Markdig.d1j7v41cl1.dll": "Markdig.dll",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"MindExecution.Plugins.Directory.jc0g7fvyzv.dll": "MindExecution.Plugins.Directory.dll",
|
|
132
132
|
"MindExecution.Plugins.PlanMaster.2sugb66h95.dll": "MindExecution.Plugins.PlanMaster.dll",
|
|
133
133
|
"MindExecution.Plugins.YouTube.vy0nsbcoo4.dll": "MindExecution.Plugins.YouTube.dll",
|
|
134
|
-
"MindExecution.Shared.
|
|
134
|
+
"MindExecution.Shared.uwkrc41o75.dll": "MindExecution.Shared.dll",
|
|
135
135
|
"MindExecution.Web.994sb3nzuy.dll": "MindExecution.Web.dll",
|
|
136
136
|
"dotnet.js": "dotnet.js",
|
|
137
137
|
"dotnet.native.qc8g39g30v.js": "dotnet.native.js",
|
|
@@ -282,7 +282,7 @@
|
|
|
282
282
|
"MindExecution.Kernel.3zom09ir6j.dll": "sha256-78vR2SNkF9FwdQv0fEItSyz4HzgyzwGUROJAnpripPA=",
|
|
283
283
|
"MindExecution.Plugins.Concept.lxunwcdjgq.dll": "sha256-WVyvjaxW6UoGQvGW0icChLsXwYVCcdn0BLTftzlkCKc=",
|
|
284
284
|
"MindExecution.Plugins.PlanMaster.2sugb66h95.dll": "sha256-Lp0AieRHDo2H4Yl0Il1pPruy9nFUHJsX9t1AblXGD6Y=",
|
|
285
|
-
"MindExecution.Shared.
|
|
285
|
+
"MindExecution.Shared.uwkrc41o75.dll": "sha256-YjF+7VcGo3+nB0fsa2nh3AiaRyrHS1AL6d6Mq4RWKqo=",
|
|
286
286
|
"MindExecution.Web.994sb3nzuy.dll": "sha256-jQshtLmhEC4xenzdD9PqHjaCuKvJf/frzaKBi2weDAg="
|
|
287
287
|
},
|
|
288
288
|
"lazyAssembly": {
|
package/wwwroot/index.html
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<title>MindExec | Run your ideas as AI task graphs</title>
|
|
8
8
|
<meta name="description" content="MindExec is an AI execution canvas for solo builders, researchers, developers, and creators. Start with free browser tools, then move serious work into saved MindCanvas projects." />
|
|
9
9
|
<base href="/" />
|
|
10
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-
|
|
11
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-
|
|
10
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-mdm-live-paint-v576" />
|
|
11
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-live-paint-v576" />
|
|
12
12
|
<!-- ?쇄뼹??Font Awesome (local) ?쇄뼹??-->
|
|
13
13
|
<link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
|
|
14
14
|
<!-- ?꿎뼯??-->
|
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
const base = '_content/MindExecution.Shared/js/';
|
|
582
|
-
const scriptVersion = '20260616-
|
|
582
|
+
const scriptVersion = '20260616-mdm-live-paint-v576';
|
|
583
583
|
const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
|
|
584
584
|
console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
|
|
585
585
|
const criticalScripts = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
self.assetsManifest = {
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "dvBBmOSo",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"hash": "sha256-
|
|
89
|
+
"hash": "sha256-f0C7hF8MEs79aW0LHHOPm+FeBP7cvy/GxhypXzccmd4=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -442,8 +442,8 @@
|
|
|
442
442
|
"url": "_framework/MindExecution.Plugins.YouTube.vy0nsbcoo4.dll"
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
|
-
"hash": "sha256-
|
|
446
|
-
"url": "_framework/MindExecution.Shared.
|
|
445
|
+
"hash": "sha256-YjF+7VcGo3+nB0fsa2nh3AiaRyrHS1AL6d6Mq4RWKqo=",
|
|
446
|
+
"url": "_framework/MindExecution.Shared.uwkrc41o75.dll"
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
449
|
"hash": "sha256-jQshtLmhEC4xenzdD9PqHjaCuKvJf/frzaKBi2weDAg=",
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
"url": "_framework/Websocket.Client.vapounvmnl.dll"
|
|
771
771
|
},
|
|
772
772
|
{
|
|
773
|
-
"hash": "sha256-
|
|
773
|
+
"hash": "sha256-lL/HCjpHZ3+EWJkTvNj72sH8NLl/eSU2UZDTr6bP99c=",
|
|
774
774
|
"url": "_framework/blazor.boot.json"
|
|
775
775
|
},
|
|
776
776
|
{
|
|
@@ -834,7 +834,7 @@
|
|
|
834
834
|
"url": "image-manifest.json"
|
|
835
835
|
},
|
|
836
836
|
{
|
|
837
|
-
"hash": "sha256-
|
|
837
|
+
"hash": "sha256-HElWHU9ioUT0cuuCKMD/40OJ15fyVOJNYjuApu3T4mI=",
|
|
838
838
|
"url": "index.html"
|
|
839
839
|
},
|
|
840
840
|
{
|