@livedesk/hub 0.1.40 → 0.1.42
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 +2 -2
- package/src/agents/agent-device-scope.js +29 -29
- package/src/agents/agent-manager.js +103 -103
- package/src/agents/agent-permission-store.js +78 -78
- package/src/agents/agent-runtime-error.js +9 -9
- package/src/agents/agent-settings.js +71 -71
- package/src/agents/codex-agent-runtime.js +594 -594
- package/src/agents/codex-mcp-server.js +100 -100
- package/src/filesystem/directory-reader.js +187 -187
- package/src/filesystem/hub-filesystem.js +78 -78
- package/src/filesystem/path-registry.js +78 -78
- package/src/filesystem/roots.js +115 -115
- package/src/frame-packet-contract.mjs +427 -427
- package/src/http/hub-ui-session.js +119 -119
- package/src/live-capture-transition-retry.mjs +297 -297
- package/src/live-desk-update.js +167 -108
- package/src/live-stream-monitor-contract.js +38 -38
- package/src/lzo1x.js +109 -109
- package/src/mode4-atlas-pool.js +137 -137
- package/src/mode4-atlas-sizing.js +32 -32
- package/src/mode4-atlas-worker.js +599 -599
- package/src/mode4-atlas.js +306 -306
- package/src/remote-audio-subscription-contract.mjs +109 -109
- package/src/remote-audio-subscription-contract.test.mjs +72 -72
- package/src/remote-hub.js +140 -111
- package/src/server.js +196 -180
- package/src/settings/effective-device-policy.js +16 -16
- package/src/transport/udp-protocol.js +453 -453
package/src/live-desk-update.js
CHANGED
|
@@ -8,11 +8,11 @@ export const LIVE_DESK_UPDATE_COMMAND = 'livedesk.client-update';
|
|
|
8
8
|
// same device returned, so the Hub-owned compatibility bridge upgrades them.
|
|
9
9
|
export const LIVE_DESK_DEDICATED_CLIENT_UPDATE_MIN_VERSION = '0.1.172';
|
|
10
10
|
export const LIVE_DESK_UPDATE_CLIENT_BATCH_SIZE = 5;
|
|
11
|
-
export const LIVE_DESK_UPDATE_TARGET_TIMEOUT_MS = 480_000;
|
|
12
|
-
export const LIVE_DESK_UPDATE_TIMEOUT_MS = 90 * 60_000;
|
|
13
|
-
export const LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS = 60_000;
|
|
14
|
-
export const LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS = 10_000;
|
|
15
|
-
export const LIVE_DESK_UPDATE_CHECK_INTERVAL_MS = 60_000;
|
|
11
|
+
export const LIVE_DESK_UPDATE_TARGET_TIMEOUT_MS = 480_000;
|
|
12
|
+
export const LIVE_DESK_UPDATE_TIMEOUT_MS = 90 * 60_000;
|
|
13
|
+
export const LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS = 60_000;
|
|
14
|
+
export const LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS = 10_000;
|
|
15
|
+
export const LIVE_DESK_UPDATE_CHECK_INTERVAL_MS = 60_000;
|
|
16
16
|
export const LIVE_DESK_LEGACY_COMMAND_MAX_LENGTH = 3900;
|
|
17
17
|
|
|
18
18
|
function cleanVersion(value) {
|
|
@@ -66,13 +66,17 @@ export function isVersionAtLeast(candidate, required) {
|
|
|
66
66
|
return !!cleanVersion(candidate) && !!cleanVersion(required) && compareVersions(candidate, required) >= 0;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function isDedicatedBootstrapFailure(value) {
|
|
70
|
+
return /^Update worker exited before terminal proof\b/i.test(String(value || '').trim());
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
function encodePowerShell(value) {
|
|
70
74
|
return Buffer.from(String(value || ''), 'utf16le').toString('base64');
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
function buildLegacyPackageSupervisorStarterSource() {
|
|
74
78
|
const source = [
|
|
75
|
-
"const{spawn}=require('node:child_process'),f=require('node:fs'),p=require('node:path'),c=JSON.parse(Buffer.from(process.env.C,'base64')),e={...process.env,LIVEDESK_UPDATE_STARTER_PID:String(process.pid),LIVEDESK_UPDATE_ORIGINAL_CWD:process.cwd()},w=p.join(
|
|
79
|
+
"const{spawn}=require('node:child_process'),f=require('node:fs'),p=require('node:path'),o=require('node:os'),c=JSON.parse(Buffer.from(process.env.C,'base64')),e={...process.env,LIVEDESK_UPDATE_STARTER_PID:String(process.pid),LIVEDESK_UPDATE_ORIGINAL_CWD:process.cwd()},w=p.join(o.tmpdir(),'livedesk-update-cwd','client-'+c[0]),k=p.join(o.tmpdir(),'livedesk-update-cache','client-'+c[0]),u=e.LIVEDESK_NPX_EXECUTABLE,s=process.platform,j=process.execPath,n=u||(s==='win32'?'npx.cmd':'npx'),x=[e.LIVEDESK_NPX_CLI_PATH,e.npm_execpath&&p.join(p.dirname(e.npm_execpath),'npx-cli.js'),u&&p.join(p.dirname(u),'node_modules/npm/bin/npx-cli.js'),p.join(p.dirname(j),'node_modules/npm/bin/npx-cli.js')].find(v=>v&&f.existsSync(v)),a=['-y','--prefer-online','--prefix',w,'--workspaces=false','livedesk@'+c[6],'--internal-legacy-client-update'],z=String.fromCharCode(32),q=v=>'\"'+String(v).replaceAll('\"','\"\"')+'\"';if(!(+c[7]>0))process.exit(1);c[7]=String(Date.now()+Number(c[7]));e.C=Buffer.from(JSON.stringify(c)).toString('base64');f.mkdirSync(w,{recursive:true});if(f.readdirSync(w)[0])process.exit(1);f.mkdirSync(k,{recursive:true});Object.keys(e).forEach(n=>/^(INIT_CWD|npm_config_(cache|local_prefix|workspaces?|include_workspace_root))$/i.test(n)&&Reflect.deleteProperty(e,n));Object.assign(e,{INIT_CWD:w,npm_config_cache:k,npm_config_local_prefix:w,npm_config_workspaces:'false',npm_config_include_workspace_root:'false',LIVEDESK_UPDATE_NEUTRAL_CWD:w});e.i=x?{c:j,a:[x,...a]}:s==='win32'?{c:e.ComSpec||'cmd.exe',a:['/d','/s','/c','call'+z+q(n)+z+a.map(q).join(z)]}:{c:n,a};e.r=spawn(e.i.c,e.i.a,{cwd:w,env:e,detached:true,stdio:'ignore',windowsHide:true});",
|
|
76
80
|
"e.r.on('error',()=>process.exit(1));e.r.unref();"
|
|
77
81
|
].join('');
|
|
78
82
|
if (source.includes(' ')) throw new Error('LiveDesk legacy starter source must not contain spaces.');
|
|
@@ -195,10 +199,10 @@ function statusForRun(run) {
|
|
|
195
199
|
activeCount,
|
|
196
200
|
queuedCount,
|
|
197
201
|
pendingOfflineCount,
|
|
198
|
-
failedCount,
|
|
199
|
-
batchSize: run.batchSize,
|
|
200
|
-
clientRestartStabilityMs: run.clientRestartStabilityMs,
|
|
201
|
-
verifiedClientRestartStabilityMs: run.verifiedClientRestartStabilityMs,
|
|
202
|
+
failedCount,
|
|
203
|
+
batchSize: run.batchSize,
|
|
204
|
+
clientRestartStabilityMs: run.clientRestartStabilityMs,
|
|
205
|
+
verifiedClientRestartStabilityMs: run.verifiedClientRestartStabilityMs,
|
|
202
206
|
latestManagerVersion: run.latestManagerVersion,
|
|
203
207
|
latestClientVersion: run.latestClientVersion,
|
|
204
208
|
error: run.error || '',
|
|
@@ -228,35 +232,45 @@ function resetReconnectCandidate(target) {
|
|
|
228
232
|
target.candidatePid = 0;
|
|
229
233
|
target.candidateConnectedAt = '';
|
|
230
234
|
target.candidateProductVersion = '';
|
|
231
|
-
target.candidateAgentVersion = '';
|
|
232
|
-
target.candidateSupervisorProofId = '';
|
|
233
|
-
target.verificationSource = '';
|
|
234
|
-
target.requiredStabilityMs = 0;
|
|
235
|
-
target.supervisorProofAt = '';
|
|
236
|
-
target.candidateSince = '';
|
|
237
|
-
target.stabilityDeadlineAt = '';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export function createLiveDeskUpdateManager({
|
|
241
|
-
remoteHub,
|
|
242
|
-
currentManagerVersion,
|
|
243
|
-
currentClientVersion,
|
|
235
|
+
target.candidateAgentVersion = '';
|
|
236
|
+
target.candidateSupervisorProofId = '';
|
|
237
|
+
target.verificationSource = '';
|
|
238
|
+
target.requiredStabilityMs = 0;
|
|
239
|
+
target.supervisorProofAt = '';
|
|
240
|
+
target.candidateSince = '';
|
|
241
|
+
target.stabilityDeadlineAt = '';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function createLiveDeskUpdateManager({
|
|
245
|
+
remoteHub,
|
|
246
|
+
currentManagerVersion,
|
|
247
|
+
currentClientVersion,
|
|
248
|
+
excludedDeviceIds = [],
|
|
244
249
|
restartSupported = false,
|
|
245
250
|
requestHubRestart,
|
|
246
251
|
fetchImpl = globalThis.fetch,
|
|
247
252
|
now = () => Date.now(),
|
|
248
253
|
clientBatchSize = LIVE_DESK_UPDATE_CLIENT_BATCH_SIZE,
|
|
249
|
-
targetTimeoutMs = LIVE_DESK_UPDATE_TARGET_TIMEOUT_MS,
|
|
250
|
-
operationTimeoutMs = LIVE_DESK_UPDATE_TIMEOUT_MS,
|
|
251
|
-
clientRestartStabilityMs = LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS,
|
|
252
|
-
verifiedClientRestartStabilityMs = LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS
|
|
253
|
-
}) {
|
|
254
|
+
targetTimeoutMs = LIVE_DESK_UPDATE_TARGET_TIMEOUT_MS,
|
|
255
|
+
operationTimeoutMs = LIVE_DESK_UPDATE_TIMEOUT_MS,
|
|
256
|
+
clientRestartStabilityMs = LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS,
|
|
257
|
+
verifiedClientRestartStabilityMs = LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS
|
|
258
|
+
}) {
|
|
254
259
|
let latestRelease = null;
|
|
255
260
|
let checkError = '';
|
|
256
261
|
let checkPromise = null;
|
|
257
262
|
let run = null;
|
|
258
263
|
let checkTimer = null;
|
|
259
|
-
let runTimer = null;
|
|
264
|
+
let runTimer = null;
|
|
265
|
+
const excludedDeviceIdSet = new Set(
|
|
266
|
+
(Array.isArray(excludedDeviceIds) ? excludedDeviceIds : [excludedDeviceIds])
|
|
267
|
+
.map(value => String(value || '').trim())
|
|
268
|
+
.filter(Boolean)
|
|
269
|
+
);
|
|
270
|
+
const connectedClientDevices = () => connectedDevices(remoteHub)
|
|
271
|
+
.filter(device => !excludedDeviceIdSet.has(String(device?.deviceId || '')));
|
|
272
|
+
const knownOfflineClientDevices = () => knownOfflineDevices(remoteHub)
|
|
273
|
+
.filter(device => !excludedDeviceIdSet.has(String(device?.deviceId || '')));
|
|
260
274
|
const effectiveClientBatchSize = Math.max(
|
|
261
275
|
1,
|
|
262
276
|
Math.min(50, Math.floor(Number(clientBatchSize) || LIVE_DESK_UPDATE_CLIENT_BATCH_SIZE))
|
|
@@ -269,18 +283,18 @@ export function createLiveDeskUpdateManager({
|
|
|
269
283
|
effectiveTargetTimeoutMs,
|
|
270
284
|
Number(operationTimeoutMs) || LIVE_DESK_UPDATE_TIMEOUT_MS
|
|
271
285
|
);
|
|
272
|
-
const effectiveClientRestartStabilityMs = Math.max(
|
|
286
|
+
const effectiveClientRestartStabilityMs = Math.max(
|
|
273
287
|
0,
|
|
274
288
|
Number.isFinite(Number(clientRestartStabilityMs))
|
|
275
289
|
? Number(clientRestartStabilityMs)
|
|
276
|
-
: LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS
|
|
277
|
-
);
|
|
278
|
-
const effectiveVerifiedClientRestartStabilityMs = Math.max(
|
|
279
|
-
5_000,
|
|
280
|
-
Number.isFinite(Number(verifiedClientRestartStabilityMs))
|
|
281
|
-
? Number(verifiedClientRestartStabilityMs)
|
|
282
|
-
: LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS
|
|
283
|
-
);
|
|
290
|
+
: LIVE_DESK_UPDATE_CLIENT_RESTART_STABILITY_MS
|
|
291
|
+
);
|
|
292
|
+
const effectiveVerifiedClientRestartStabilityMs = Math.max(
|
|
293
|
+
5_000,
|
|
294
|
+
Number.isFinite(Number(verifiedClientRestartStabilityMs))
|
|
295
|
+
? Number(verifiedClientRestartStabilityMs)
|
|
296
|
+
: LIVE_DESK_UPDATE_VERIFIED_CLIENT_STABILITY_MS
|
|
297
|
+
);
|
|
284
298
|
|
|
285
299
|
const touch = () => {
|
|
286
300
|
if (run) run.updatedAt = new Date(now()).toISOString();
|
|
@@ -307,7 +321,7 @@ export function createLiveDeskUpdateManager({
|
|
|
307
321
|
const getStatus = () => {
|
|
308
322
|
const managerUpdateAvailable = !!latestRelease
|
|
309
323
|
&& compareVersions(latestRelease.latestManagerVersion, currentManagerVersion) > 0;
|
|
310
|
-
const clientDevices =
|
|
324
|
+
const clientDevices = connectedClientDevices();
|
|
311
325
|
const outdatedClientDevices = latestRelease
|
|
312
326
|
? clientDevices.filter(device => deviceNeedsUpdate(
|
|
313
327
|
device,
|
|
@@ -316,7 +330,7 @@ export function createLiveDeskUpdateManager({
|
|
|
316
330
|
))
|
|
317
331
|
: [];
|
|
318
332
|
const pendingOfflineClientCount = latestRelease
|
|
319
|
-
?
|
|
333
|
+
? knownOfflineClientDevices().filter(device => deviceNeedsUpdate(
|
|
320
334
|
device,
|
|
321
335
|
latestRelease.latestManagerVersion,
|
|
322
336
|
latestRelease.latestClientVersion
|
|
@@ -354,10 +368,10 @@ export function createLiveDeskUpdateManager({
|
|
|
354
368
|
queuedCount: 0,
|
|
355
369
|
pendingOfflineCount: 0,
|
|
356
370
|
failedCount: 0,
|
|
357
|
-
batchSize: effectiveClientBatchSize,
|
|
358
|
-
clientRestartStabilityMs: effectiveClientRestartStabilityMs,
|
|
359
|
-
verifiedClientRestartStabilityMs: effectiveVerifiedClientRestartStabilityMs,
|
|
360
|
-
targets: []
|
|
371
|
+
batchSize: effectiveClientBatchSize,
|
|
372
|
+
clientRestartStabilityMs: effectiveClientRestartStabilityMs,
|
|
373
|
+
verifiedClientRestartStabilityMs: effectiveVerifiedClientRestartStabilityMs,
|
|
374
|
+
targets: []
|
|
361
375
|
})
|
|
362
376
|
};
|
|
363
377
|
};
|
|
@@ -400,8 +414,9 @@ export function createLiveDeskUpdateManager({
|
|
|
400
414
|
if (!run || run.state !== 'failed') return [];
|
|
401
415
|
const currentTime = now();
|
|
402
416
|
const devices = new Map(
|
|
403
|
-
remoteHub.listDevices({ includeDataUrl: false })
|
|
404
|
-
.
|
|
417
|
+
remoteHub.listDevices({ includeDataUrl: false })
|
|
418
|
+
.filter(device => !excludedDeviceIdSet.has(String(device?.deviceId || '')))
|
|
419
|
+
.map(device => [String(device.deviceId), device])
|
|
405
420
|
);
|
|
406
421
|
return run.targets.filter(target => {
|
|
407
422
|
if (target.state !== 'failed' || !target.dispatchedAt) return false;
|
|
@@ -436,9 +451,16 @@ export function createLiveDeskUpdateManager({
|
|
|
436
451
|
return true;
|
|
437
452
|
};
|
|
438
453
|
|
|
439
|
-
const dispatchTarget = (target, device) => {
|
|
454
|
+
const dispatchTarget = (target, device, options = {}) => {
|
|
440
455
|
const dispatchedAtEpochMs = now();
|
|
441
|
-
const
|
|
456
|
+
const requestedTimeoutMs = Math.max(
|
|
457
|
+
10_000,
|
|
458
|
+
Math.min(
|
|
459
|
+
effectiveTargetTimeoutMs,
|
|
460
|
+
Number(options.updateTimeoutMs) || effectiveTargetTimeoutMs
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
const updateDeadlineEpochMs = dispatchedAtEpochMs + requestedTimeoutMs;
|
|
442
464
|
const dispatchedSessionId = String(device?.sessionId || '').trim();
|
|
443
465
|
if (!dispatchedSessionId) {
|
|
444
466
|
target.state = 'failed';
|
|
@@ -448,14 +470,15 @@ export function createLiveDeskUpdateManager({
|
|
|
448
470
|
target.dispatchedSessionId = dispatchedSessionId;
|
|
449
471
|
target.dispatchedPid = Math.max(0, Math.floor(Number(device?.pid) || 0));
|
|
450
472
|
resetReconnectCandidate(target);
|
|
451
|
-
const supportsDedicated =
|
|
473
|
+
const supportsDedicated = options.forceLegacy !== true
|
|
474
|
+
&& device.capabilities?.clientUpdate === true
|
|
452
475
|
&& isVersionAtLeast(device.agentVersion, LIVE_DESK_DEDICATED_CLIENT_UPDATE_MIN_VERSION);
|
|
453
476
|
const payload = {
|
|
454
477
|
targetVersion: run.latestClientVersion,
|
|
455
478
|
targetProductVersion: run.latestManagerVersion,
|
|
456
479
|
managerVersion: run.latestManagerVersion,
|
|
457
480
|
operationId: run.operationId,
|
|
458
|
-
updateTimeoutMs:
|
|
481
|
+
updateTimeoutMs: requestedTimeoutMs
|
|
459
482
|
};
|
|
460
483
|
const result = supportsDedicated
|
|
461
484
|
? remoteHub.sendCommand(device.deviceId, { command: LIVE_DESK_UPDATE_COMMAND, payload })
|
|
@@ -465,7 +488,7 @@ export function createLiveDeskUpdateManager({
|
|
|
465
488
|
run.operationId,
|
|
466
489
|
run.latestClientVersion,
|
|
467
490
|
run.latestManagerVersion,
|
|
468
|
-
|
|
491
|
+
requestedTimeoutMs
|
|
469
492
|
),
|
|
470
493
|
timeoutMs: 30_000
|
|
471
494
|
});
|
|
@@ -500,7 +523,7 @@ export function createLiveDeskUpdateManager({
|
|
|
500
523
|
const activeCount = run.targets.filter(target => target.state === 'waiting').length;
|
|
501
524
|
let availableSlots = Math.max(0, run.batchSize - activeCount);
|
|
502
525
|
if (availableSlots <= 0) return true;
|
|
503
|
-
const devices = new Map(
|
|
526
|
+
const devices = new Map(connectedClientDevices().map(device => [String(device.deviceId), device]));
|
|
504
527
|
for (const target of run.targets) {
|
|
505
528
|
if (availableSlots <= 0) break;
|
|
506
529
|
if (!['queued', 'pending-offline'].includes(target.state)) continue;
|
|
@@ -539,7 +562,7 @@ export function createLiveDeskUpdateManager({
|
|
|
539
562
|
failRun(`Timed out waiting for ${unfinishedCount} client(s) to finish the rollout.`);
|
|
540
563
|
return;
|
|
541
564
|
}
|
|
542
|
-
const devices = new Map(
|
|
565
|
+
const devices = new Map(connectedClientDevices().map(device => [String(device.deviceId), device]));
|
|
543
566
|
for (const target of run.targets) {
|
|
544
567
|
if (target.state === 'failed' || target.state === 'completed') continue;
|
|
545
568
|
const device = devices.get(target.deviceId);
|
|
@@ -549,30 +572,30 @@ export function createLiveDeskUpdateManager({
|
|
|
549
572
|
const connectedAtEpochMs = Date.parse(connectedAt);
|
|
550
573
|
const dispatchedAtEpochMs = Date.parse(target.dispatchedAt || '');
|
|
551
574
|
const candidatePid = Math.max(0, Math.floor(Number(device?.pid) || 0));
|
|
552
|
-
const candidateProductVersion = String(device?.productVersion || '');
|
|
553
|
-
const candidateAgentVersion = String(device?.agentVersion || '');
|
|
554
|
-
const supervisorProof = device?.clientUpdateProof;
|
|
555
|
-
const supervisorProofId = [
|
|
556
|
-
String(supervisorProof?.operationId || ''),
|
|
557
|
-
String(supervisorProof?.attemptId || ''),
|
|
558
|
-
String(supervisorProof?.sessionId || ''),
|
|
559
|
-
String(supervisorProof?.agentPid || ''),
|
|
560
|
-
String(supervisorProof?.launcherPid || ''),
|
|
561
|
-
String(supervisorProof?.completedAt || '')
|
|
562
|
-
].join(':');
|
|
563
|
-
const hasSupervisorProof = !!supervisorProof
|
|
564
|
-
&& String(supervisorProof.operationId || '') === run.operationId
|
|
565
|
-
&& !!String(supervisorProof.attemptId || '')
|
|
566
|
-
&& String(supervisorProof.sessionId || '') === sessionId
|
|
567
|
-
&& Number(supervisorProof.agentPid || 0) === candidatePid
|
|
568
|
-
&& Number(supervisorProof.launcherPid || 0) > 1
|
|
569
|
-
&& cleanVersion(supervisorProof.productVersion) === cleanVersion(candidateProductVersion)
|
|
570
|
-
&& cleanVersion(supervisorProof.agentVersion) === cleanVersion(candidateAgentVersion)
|
|
571
|
-
&& Date.parse(String(supervisorProof.receivedAt || '')) >= connectedAtEpochMs;
|
|
572
|
-
const requiredStabilityMs = hasSupervisorProof
|
|
573
|
-
? run.verifiedClientRestartStabilityMs
|
|
574
|
-
: run.clientRestartStabilityMs;
|
|
575
|
-
const qualifiesForStability = device?.connected === true
|
|
575
|
+
const candidateProductVersion = String(device?.productVersion || '');
|
|
576
|
+
const candidateAgentVersion = String(device?.agentVersion || '');
|
|
577
|
+
const supervisorProof = device?.clientUpdateProof;
|
|
578
|
+
const supervisorProofId = [
|
|
579
|
+
String(supervisorProof?.operationId || ''),
|
|
580
|
+
String(supervisorProof?.attemptId || ''),
|
|
581
|
+
String(supervisorProof?.sessionId || ''),
|
|
582
|
+
String(supervisorProof?.agentPid || ''),
|
|
583
|
+
String(supervisorProof?.launcherPid || ''),
|
|
584
|
+
String(supervisorProof?.completedAt || '')
|
|
585
|
+
].join(':');
|
|
586
|
+
const hasSupervisorProof = !!supervisorProof
|
|
587
|
+
&& String(supervisorProof.operationId || '') === run.operationId
|
|
588
|
+
&& !!String(supervisorProof.attemptId || '')
|
|
589
|
+
&& String(supervisorProof.sessionId || '') === sessionId
|
|
590
|
+
&& Number(supervisorProof.agentPid || 0) === candidatePid
|
|
591
|
+
&& Number(supervisorProof.launcherPid || 0) > 1
|
|
592
|
+
&& cleanVersion(supervisorProof.productVersion) === cleanVersion(candidateProductVersion)
|
|
593
|
+
&& cleanVersion(supervisorProof.agentVersion) === cleanVersion(candidateAgentVersion)
|
|
594
|
+
&& Date.parse(String(supervisorProof.receivedAt || '')) >= connectedAtEpochMs;
|
|
595
|
+
const requiredStabilityMs = hasSupervisorProof
|
|
596
|
+
? run.verifiedClientRestartStabilityMs
|
|
597
|
+
: run.clientRestartStabilityMs;
|
|
598
|
+
const qualifiesForStability = device?.connected === true
|
|
576
599
|
&& !!sessionId
|
|
577
600
|
&& candidatePid > 1
|
|
578
601
|
&& sessionId !== target.dispatchedSessionId
|
|
@@ -581,10 +604,10 @@ export function createLiveDeskUpdateManager({
|
|
|
581
604
|
const sameCandidate = qualifiesForStability
|
|
582
605
|
&& target.candidateSessionId === sessionId
|
|
583
606
|
&& target.candidatePid === candidatePid
|
|
584
|
-
&& target.candidateConnectedAt === connectedAt
|
|
585
|
-
&& target.candidateProductVersion === candidateProductVersion
|
|
586
|
-
&& target.candidateAgentVersion === candidateAgentVersion
|
|
587
|
-
&& target.candidateSupervisorProofId === (hasSupervisorProof ? supervisorProofId : '');
|
|
607
|
+
&& target.candidateConnectedAt === connectedAt
|
|
608
|
+
&& target.candidateProductVersion === candidateProductVersion
|
|
609
|
+
&& target.candidateAgentVersion === candidateAgentVersion
|
|
610
|
+
&& target.candidateSupervisorProofId === (hasSupervisorProof ? supervisorProofId : '');
|
|
588
611
|
|
|
589
612
|
if (!qualifiesForStability) {
|
|
590
613
|
resetReconnectCandidate(target);
|
|
@@ -592,18 +615,18 @@ export function createLiveDeskUpdateManager({
|
|
|
592
615
|
target.candidateSessionId = sessionId;
|
|
593
616
|
target.candidatePid = candidatePid;
|
|
594
617
|
target.candidateConnectedAt = connectedAt;
|
|
595
|
-
target.candidateProductVersion = candidateProductVersion;
|
|
596
|
-
target.candidateAgentVersion = candidateAgentVersion;
|
|
597
|
-
target.candidateSupervisorProofId = hasSupervisorProof ? supervisorProofId : '';
|
|
598
|
-
target.verificationSource = hasSupervisorProof ? 'supervisor-proof' : 'hub-session';
|
|
599
|
-
target.requiredStabilityMs = requiredStabilityMs;
|
|
600
|
-
target.supervisorProofAt = hasSupervisorProof
|
|
601
|
-
? String(supervisorProof.receivedAt || supervisorProof.completedAt || '')
|
|
602
|
-
: '';
|
|
603
|
-
target.candidateSince = new Date(currentTime).toISOString();
|
|
604
|
-
target.stabilityDeadlineAt = new Date(
|
|
605
|
-
currentTime + requiredStabilityMs
|
|
606
|
-
).toISOString();
|
|
618
|
+
target.candidateProductVersion = candidateProductVersion;
|
|
619
|
+
target.candidateAgentVersion = candidateAgentVersion;
|
|
620
|
+
target.candidateSupervisorProofId = hasSupervisorProof ? supervisorProofId : '';
|
|
621
|
+
target.verificationSource = hasSupervisorProof ? 'supervisor-proof' : 'hub-session';
|
|
622
|
+
target.requiredStabilityMs = requiredStabilityMs;
|
|
623
|
+
target.supervisorProofAt = hasSupervisorProof
|
|
624
|
+
? String(supervisorProof.receivedAt || supervisorProof.completedAt || '')
|
|
625
|
+
: '';
|
|
626
|
+
target.candidateSince = new Date(currentTime).toISOString();
|
|
627
|
+
target.stabilityDeadlineAt = new Date(
|
|
628
|
+
currentTime + requiredStabilityMs
|
|
629
|
+
).toISOString();
|
|
607
630
|
}
|
|
608
631
|
|
|
609
632
|
if (qualifiesForStability
|
|
@@ -666,7 +689,7 @@ export function createLiveDeskUpdateManager({
|
|
|
666
689
|
if (!release) return { ok: false, error: checkError || 'LiveDesk update check failed.', ...getStatus() };
|
|
667
690
|
const managerNeedsUpdate = compareVersions(release.latestManagerVersion, currentManagerVersion) > 0;
|
|
668
691
|
const clientPackageNeedsUpdate = compareVersions(release.latestClientVersion, currentClientVersion) > 0;
|
|
669
|
-
const connected =
|
|
692
|
+
const connected = connectedClientDevices();
|
|
670
693
|
const outdatedClients = connected.filter(device => deviceNeedsUpdate(
|
|
671
694
|
device,
|
|
672
695
|
release.latestManagerVersion,
|
|
@@ -688,9 +711,9 @@ export function createLiveDeskUpdateManager({
|
|
|
688
711
|
latestManagerVersion: release.latestManagerVersion,
|
|
689
712
|
latestClientVersion: release.latestClientVersion,
|
|
690
713
|
needsHubRestart,
|
|
691
|
-
batchSize: effectiveClientBatchSize,
|
|
692
|
-
clientRestartStabilityMs: effectiveClientRestartStabilityMs,
|
|
693
|
-
verifiedClientRestartStabilityMs: effectiveVerifiedClientRestartStabilityMs,
|
|
714
|
+
batchSize: effectiveClientBatchSize,
|
|
715
|
+
clientRestartStabilityMs: effectiveClientRestartStabilityMs,
|
|
716
|
+
verifiedClientRestartStabilityMs: effectiveVerifiedClientRestartStabilityMs,
|
|
694
717
|
error: '',
|
|
695
718
|
targets: targets.map(device => ({
|
|
696
719
|
deviceId: String(device.deviceId || ''),
|
|
@@ -709,15 +732,18 @@ export function createLiveDeskUpdateManager({
|
|
|
709
732
|
candidateSessionId: '',
|
|
710
733
|
candidatePid: 0,
|
|
711
734
|
candidateConnectedAt: '',
|
|
712
|
-
candidateProductVersion: '',
|
|
713
|
-
candidateAgentVersion: '',
|
|
714
|
-
candidateSupervisorProofId: '',
|
|
715
|
-
verificationSource: '',
|
|
716
|
-
requiredStabilityMs: 0,
|
|
717
|
-
supervisorProofAt: '',
|
|
718
|
-
candidateSince: '',
|
|
735
|
+
candidateProductVersion: '',
|
|
736
|
+
candidateAgentVersion: '',
|
|
737
|
+
candidateSupervisorProofId: '',
|
|
738
|
+
verificationSource: '',
|
|
739
|
+
requiredStabilityMs: 0,
|
|
740
|
+
supervisorProofAt: '',
|
|
741
|
+
candidateSince: '',
|
|
719
742
|
stabilityDeadlineAt: '',
|
|
720
743
|
completedAt: '',
|
|
744
|
+
bootstrapRetryCount: 0,
|
|
745
|
+
bootstrapRetryAt: '',
|
|
746
|
+
bootstrapRetryReason: '',
|
|
721
747
|
error: ''
|
|
722
748
|
}))
|
|
723
749
|
};
|
|
@@ -745,9 +771,9 @@ export function createLiveDeskUpdateManager({
|
|
|
745
771
|
|
|
746
772
|
const handleRemoteEvent = (type, event) => {
|
|
747
773
|
if (!run || run.state !== 'waiting-for-clients') return;
|
|
748
|
-
if (type === 'RemoteDeviceConnected'
|
|
749
|
-
|| type === 'RemoteDeviceDisconnected'
|
|
750
|
-
|| type === 'RemoteClientUpdateVerified') {
|
|
774
|
+
if (type === 'RemoteDeviceConnected'
|
|
775
|
+
|| type === 'RemoteDeviceDisconnected'
|
|
776
|
+
|| type === 'RemoteClientUpdateVerified') {
|
|
751
777
|
verifyTargets();
|
|
752
778
|
return;
|
|
753
779
|
}
|
|
@@ -760,8 +786,41 @@ export function createLiveDeskUpdateManager({
|
|
|
760
786
|
|| !['dedicated', 'legacy-command-run'].includes(target.method)) return;
|
|
761
787
|
const result = event?.result;
|
|
762
788
|
if (event?.error || result?.ok === false || result?.status === 'failed' || result?.status === 'rejected') {
|
|
789
|
+
const failure = String(event.error || result?.error || result?.message || 'client-update-command-failed').slice(0, 500);
|
|
790
|
+
if (target.method === 'dedicated'
|
|
791
|
+
&& target.bootstrapRetryCount === 0
|
|
792
|
+
&& isDedicatedBootstrapFailure(failure)) {
|
|
793
|
+
const retryDevice = connectedClientDevices()
|
|
794
|
+
.find(device => String(device.deviceId || '') === target.deviceId);
|
|
795
|
+
const remainingMs = Date.parse(target.deadlineAt || '') - now();
|
|
796
|
+
if (retryDevice?.connected === true && remainingMs >= 10_000) {
|
|
797
|
+
target.bootstrapRetryCount = 1;
|
|
798
|
+
target.bootstrapRetryAt = new Date(now()).toISOString();
|
|
799
|
+
target.bootstrapRetryReason = failure;
|
|
800
|
+
target.error = '';
|
|
801
|
+
let retryResult;
|
|
802
|
+
try {
|
|
803
|
+
retryResult = dispatchTarget(target, retryDevice, {
|
|
804
|
+
forceLegacy: true,
|
|
805
|
+
updateTimeoutMs: remainingMs
|
|
806
|
+
});
|
|
807
|
+
} catch (error) {
|
|
808
|
+
target.error = String(error?.message || error || 'client-update-bootstrap-retry-failed').slice(0, 500);
|
|
809
|
+
retryResult = false;
|
|
810
|
+
}
|
|
811
|
+
if (retryResult === true) {
|
|
812
|
+
touch();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
target.state = 'failed';
|
|
816
|
+
if (!target.error) target.error = 'client-update-bootstrap-retry-failed';
|
|
817
|
+
touch();
|
|
818
|
+
failRun(`Client ${target.deviceName || target.deviceId} bootstrap retry failed: ${target.error}`);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
763
822
|
target.state = 'failed';
|
|
764
|
-
target.error =
|
|
823
|
+
target.error = failure;
|
|
765
824
|
touch();
|
|
766
825
|
failRun(target.method === 'dedicated'
|
|
767
826
|
? `Client ${target.deviceName || target.deviceId} rejected the update request: ${target.error}`
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
export const LIVE_STREAM_MONITOR_INDEX_MIN = 0;
|
|
2
|
-
export const LIVE_STREAM_MONITOR_INDEX_MAX = 63;
|
|
3
|
-
|
|
4
|
-
export function parseExactLiveStreamMonitorIndex(value) {
|
|
5
|
-
return typeof value === 'number'
|
|
6
|
-
&& Number.isInteger(value)
|
|
7
|
-
&& value >= LIVE_STREAM_MONITOR_INDEX_MIN
|
|
8
|
-
&& value <= LIVE_STREAM_MONITOR_INDEX_MAX
|
|
9
|
-
? value
|
|
10
|
-
: null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function isReusedLiveStreamFrameReady(result, activeStream, expectedBinding) {
|
|
14
|
-
const expectedMonitorIndex = parseExactLiveStreamMonitorIndex(expectedBinding?.monitorIndex);
|
|
15
|
-
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(activeStream?.monitorIndex);
|
|
16
|
-
const latestFrame = activeStream?.latestFrame;
|
|
17
|
-
const frameMonitorIndex = parseExactLiveStreamMonitorIndex(latestFrame?.monitorIndex);
|
|
18
|
-
const expectedStreamId = String(expectedBinding?.streamId || '');
|
|
19
|
-
const expectedCommandId = String(expectedBinding?.commandId || '');
|
|
20
|
-
const expectedCaptureGeneration = Number(expectedBinding?.captureGeneration || 0);
|
|
21
|
-
|
|
22
|
-
return result?.reused === true
|
|
23
|
-
&& result?.ready === true
|
|
24
|
-
&& Number(activeStream?.framesReceived || 0) > 0
|
|
25
|
-
&& !!expectedStreamId
|
|
26
|
-
&& String(activeStream?.streamId || '') === expectedStreamId
|
|
27
|
-
&& !!expectedCommandId
|
|
28
|
-
&& String(activeStream?.commandId || '') === expectedCommandId
|
|
29
|
-
&& Number.isInteger(expectedCaptureGeneration)
|
|
30
|
-
&& expectedCaptureGeneration > 0
|
|
31
|
-
&& Number(activeStream?.captureGeneration || 0) === expectedCaptureGeneration
|
|
32
|
-
&& latestFrame?.currentGenerationVerified === true
|
|
33
|
-
&& String(latestFrame?.commandId || '') === expectedCommandId
|
|
34
|
-
&& Number(latestFrame?.captureGeneration || 0) === expectedCaptureGeneration
|
|
35
|
-
&& expectedMonitorIndex !== null
|
|
36
|
-
&& activeMonitorIndex === expectedMonitorIndex
|
|
37
|
-
&& frameMonitorIndex === expectedMonitorIndex;
|
|
38
|
-
}
|
|
1
|
+
export const LIVE_STREAM_MONITOR_INDEX_MIN = 0;
|
|
2
|
+
export const LIVE_STREAM_MONITOR_INDEX_MAX = 63;
|
|
3
|
+
|
|
4
|
+
export function parseExactLiveStreamMonitorIndex(value) {
|
|
5
|
+
return typeof value === 'number'
|
|
6
|
+
&& Number.isInteger(value)
|
|
7
|
+
&& value >= LIVE_STREAM_MONITOR_INDEX_MIN
|
|
8
|
+
&& value <= LIVE_STREAM_MONITOR_INDEX_MAX
|
|
9
|
+
? value
|
|
10
|
+
: null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isReusedLiveStreamFrameReady(result, activeStream, expectedBinding) {
|
|
14
|
+
const expectedMonitorIndex = parseExactLiveStreamMonitorIndex(expectedBinding?.monitorIndex);
|
|
15
|
+
const activeMonitorIndex = parseExactLiveStreamMonitorIndex(activeStream?.monitorIndex);
|
|
16
|
+
const latestFrame = activeStream?.latestFrame;
|
|
17
|
+
const frameMonitorIndex = parseExactLiveStreamMonitorIndex(latestFrame?.monitorIndex);
|
|
18
|
+
const expectedStreamId = String(expectedBinding?.streamId || '');
|
|
19
|
+
const expectedCommandId = String(expectedBinding?.commandId || '');
|
|
20
|
+
const expectedCaptureGeneration = Number(expectedBinding?.captureGeneration || 0);
|
|
21
|
+
|
|
22
|
+
return result?.reused === true
|
|
23
|
+
&& result?.ready === true
|
|
24
|
+
&& Number(activeStream?.framesReceived || 0) > 0
|
|
25
|
+
&& !!expectedStreamId
|
|
26
|
+
&& String(activeStream?.streamId || '') === expectedStreamId
|
|
27
|
+
&& !!expectedCommandId
|
|
28
|
+
&& String(activeStream?.commandId || '') === expectedCommandId
|
|
29
|
+
&& Number.isInteger(expectedCaptureGeneration)
|
|
30
|
+
&& expectedCaptureGeneration > 0
|
|
31
|
+
&& Number(activeStream?.captureGeneration || 0) === expectedCaptureGeneration
|
|
32
|
+
&& latestFrame?.currentGenerationVerified === true
|
|
33
|
+
&& String(latestFrame?.commandId || '') === expectedCommandId
|
|
34
|
+
&& Number(latestFrame?.captureGeneration || 0) === expectedCaptureGeneration
|
|
35
|
+
&& expectedMonitorIndex !== null
|
|
36
|
+
&& activeMonitorIndex === expectedMonitorIndex
|
|
37
|
+
&& frameMonitorIndex === expectedMonitorIndex;
|
|
38
|
+
}
|