@mindexec/cli 0.2.155 → 0.2.156
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindexec/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.156",
|
|
4
4
|
"description": "MindExec local runtime and bridge CLI",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node launch-bridge.cjs",
|
|
23
23
|
"dev": "node launch-bridge.cjs --watch",
|
|
24
|
-
"test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
|
|
24
|
+
"test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
|
|
25
25
|
"test:auth": "node scripts/auth-session-smoke.mjs",
|
|
26
26
|
"test:remote": "node scripts/remote-hub-smoke.mjs",
|
|
27
27
|
"test:remote:scale": "node scripts/remote-hub-scale-smoke.mjs",
|
|
28
28
|
"test:remote:render": "node scripts/remote-fleet-render-smoke.mjs",
|
|
29
|
-
"test:remote:http": "node scripts/remote-http-smoke.mjs",
|
|
30
|
-
"test:remote:frame-ws": "node scripts/remote-frame-ws-smoke.mjs",
|
|
31
|
-
"test:remote:input-ws": "node scripts/remote-input-ws-smoke.mjs",
|
|
32
|
-
"test:remote:agent-ws": "node scripts/remote-agent-ws-smoke.mjs",
|
|
29
|
+
"test:remote:http": "node scripts/remote-http-smoke.mjs",
|
|
30
|
+
"test:remote:frame-ws": "node scripts/remote-frame-ws-smoke.mjs",
|
|
31
|
+
"test:remote:input-ws": "node scripts/remote-input-ws-smoke.mjs",
|
|
32
|
+
"test:remote:agent-ws": "node scripts/remote-agent-ws-smoke.mjs",
|
|
33
33
|
"test:remote:managed": "node scripts/remote-agent-managed-smoke.mjs",
|
|
34
34
|
"test:remote:registry": "node scripts/remote-registry-follower-smoke.mjs",
|
|
35
35
|
"test:remote:package": "node scripts/remote-agent-package-smoke.mjs",
|
|
@@ -464,6 +464,25 @@ async function main() {
|
|
|
464
464
|
&& request.pathname === '/rest/v1/rpc/set_remote_host_target').length;
|
|
465
465
|
return publishCount >= 2 ? publishCount : null;
|
|
466
466
|
}, 8000, `host-target auto renew publish\n${renewHost.details()}`);
|
|
467
|
+
const beforeResumePublishCount = fakeSupabase.requests.filter(request =>
|
|
468
|
+
request.method === 'POST'
|
|
469
|
+
&& request.pathname === '/rest/v1/rpc/set_remote_host_target').length;
|
|
470
|
+
const renewResume = await fetchJson(`${renewHost.baseUrl}/api/remote/system/resume`, {
|
|
471
|
+
method: 'POST',
|
|
472
|
+
token: BRIDGE_TOKEN,
|
|
473
|
+
body: JSON.stringify({
|
|
474
|
+
driftMs: 65000
|
|
475
|
+
})
|
|
476
|
+
});
|
|
477
|
+
assert.equal(renewResume.ok, true, JSON.stringify(renewResume.payload));
|
|
478
|
+
assert.equal(renewResume.payload?.ok, true, JSON.stringify(renewResume.payload));
|
|
479
|
+
assert.equal(renewResume.payload?.remoteSystemResume?.localHost, true, JSON.stringify(renewResume.payload?.remoteSystemResume));
|
|
480
|
+
await waitFor(() => {
|
|
481
|
+
const publishCount = fakeSupabase.requests.filter(request =>
|
|
482
|
+
request.method === 'POST'
|
|
483
|
+
&& request.pathname === '/rest/v1/rpc/set_remote_host_target').length;
|
|
484
|
+
return publishCount > beforeResumePublishCount ? publishCount : null;
|
|
485
|
+
}, 8000, `host-target resume republish\n${renewHost.details()}`);
|
|
467
486
|
const renewStatus = await fetchJson(`${renewHost.baseUrl}/api/status`);
|
|
468
487
|
assert.equal(renewStatus.payload?.remoteHostTargetRenew?.status, 'active', JSON.stringify(renewStatus.payload?.remoteHostTargetRenew));
|
|
469
488
|
assert.equal(renewStatus.payload?.remoteHostTargetRenew?.endpoint, renewPublicEndpoint, JSON.stringify(renewStatus.payload?.remoteHostTargetRenew));
|
|
@@ -523,6 +542,24 @@ async function main() {
|
|
|
523
542
|
const firstStatus = await fetchJson(`${client.baseUrl}/api/status`);
|
|
524
543
|
assert.equal(firstStatus.payload?.remoteRegistryRealtime?.subscribed, true, JSON.stringify(firstStatus.payload?.remoteRegistryRealtime));
|
|
525
544
|
assert.ok(fakeSupabase.realtimeClients.size >= 1, 'expected LocalBridge realtime watcher to connect');
|
|
545
|
+
const firstPid = Number(firstAgent.pid || 0);
|
|
546
|
+
assert.ok(firstPid > 0, JSON.stringify(firstAgent));
|
|
547
|
+
|
|
548
|
+
const clientResume = await fetchJson(`${client.baseUrl}/api/remote/system/resume`, {
|
|
549
|
+
method: 'POST',
|
|
550
|
+
token: BRIDGE_TOKEN,
|
|
551
|
+
body: JSON.stringify({
|
|
552
|
+
driftMs: 65000
|
|
553
|
+
})
|
|
554
|
+
});
|
|
555
|
+
assert.equal(clientResume.ok, true, JSON.stringify(clientResume.payload));
|
|
556
|
+
assert.equal(clientResume.payload?.ok, true, JSON.stringify(clientResume.payload));
|
|
557
|
+
assert.equal(clientResume.payload?.remoteSystemResume?.registryAgentRestarted, true, JSON.stringify(clientResume.payload?.remoteSystemResume));
|
|
558
|
+
const resumedAgent = await waitForManagedAgentRestart(client, hostAEndpoint, firstPid, 'host-a after resume', 12000);
|
|
559
|
+
assert.equal(resumedAgent.usingNpx, false, JSON.stringify(resumedAgent));
|
|
560
|
+
assert.match(String(resumedAgent.launcher || ''), /mindexec-remote-fast/i);
|
|
561
|
+
assert.equal(resumedAgent.leaseId, 'lease-a');
|
|
562
|
+
await waitForConnectedDevice(hostA, 'host-a after resume');
|
|
526
563
|
|
|
527
564
|
currentTarget = createRegistryTarget({
|
|
528
565
|
endpoint: hostBEndpoint,
|
package/server.js
CHANGED
|
@@ -3374,6 +3374,10 @@ const REMOTE_HOST_TARGET_RENEW_MS = Math.max(
|
|
|
3374
3374
|
Math.floor(REMOTE_HOST_TARGET_LEASE_MS / 2),
|
|
3375
3375
|
Number(process.env.MINDEXEC_REMOTE_HOST_TARGET_RENEW_MS || 25000) || 25000));
|
|
3376
3376
|
const REMOTE_HOST_TARGET_RENEW_LOG_REPEAT_MS = 60000;
|
|
3377
|
+
const REMOTE_SYSTEM_RESUME_CHECK_MS = Math.max(1000, Number(process.env.MINDEXEC_REMOTE_RESUME_CHECK_MS || 5000) || 5000);
|
|
3378
|
+
const REMOTE_SYSTEM_RESUME_DRIFT_MS = Math.max(
|
|
3379
|
+
REMOTE_SYSTEM_RESUME_CHECK_MS + 5000,
|
|
3380
|
+
Number(process.env.MINDEXEC_REMOTE_RESUME_DRIFT_MS || 20000) || 20000);
|
|
3377
3381
|
let remoteAgentState = createRemoteAgentIdleState();
|
|
3378
3382
|
let remoteAgentSyncReportState = null;
|
|
3379
3383
|
let remoteAgentSyncReportLogKey = '';
|
|
@@ -3396,10 +3400,27 @@ let remoteRegistryRealtimeHeartbeatTimer = null;
|
|
|
3396
3400
|
let remoteRegistryRealtimeReconnectTimer = null;
|
|
3397
3401
|
let remoteRegistryRealtimeReconnectContext = null;
|
|
3398
3402
|
let remoteRegistryRealtimeLastWakeAt = 0;
|
|
3403
|
+
let remoteSupabaseRuntimeConfigCache = null;
|
|
3399
3404
|
let remoteHostTargetRenewTimer = null;
|
|
3400
3405
|
let remoteHostTargetRenewInFlight = false;
|
|
3401
3406
|
let remoteHostTargetRenewLogKey = '';
|
|
3402
3407
|
let remoteHostTargetRenewLogAt = 0;
|
|
3408
|
+
let remoteSystemResumeTimer = null;
|
|
3409
|
+
let remoteSystemResumeExpectedAt = 0;
|
|
3410
|
+
let remoteSystemResumeInFlight = false;
|
|
3411
|
+
let remoteSystemResumeState = {
|
|
3412
|
+
enabled: true,
|
|
3413
|
+
status: 'idle',
|
|
3414
|
+
reason: '',
|
|
3415
|
+
trigger: '',
|
|
3416
|
+
lastDetectedAt: '',
|
|
3417
|
+
lastCompletedAt: '',
|
|
3418
|
+
lastError: '',
|
|
3419
|
+
driftMs: 0,
|
|
3420
|
+
localHost: false,
|
|
3421
|
+
registryAgentRestarted: false,
|
|
3422
|
+
disconnectedDevices: 0
|
|
3423
|
+
};
|
|
3403
3424
|
let remoteRegistryFollowerState = {
|
|
3404
3425
|
enabled: REMOTE_REGISTRY_FOLLOWER_ENABLED,
|
|
3405
3426
|
status: REMOTE_REGISTRY_FOLLOWER_ENABLED ? 'idle' : 'disabled',
|
|
@@ -4762,6 +4783,23 @@ function updateRemoteHostTargetRenewState(patch = {}) {
|
|
|
4762
4783
|
emitBridgeEvent('RemoteHostTargetRenewUpdated', serializeRemoteHostTargetRenewState());
|
|
4763
4784
|
}
|
|
4764
4785
|
|
|
4786
|
+
function serializeRemoteSystemResumeState() {
|
|
4787
|
+
return {
|
|
4788
|
+
...remoteSystemResumeState,
|
|
4789
|
+
checkMs: REMOTE_SYSTEM_RESUME_CHECK_MS,
|
|
4790
|
+
driftThresholdMs: REMOTE_SYSTEM_RESUME_DRIFT_MS
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
function updateRemoteSystemResumeState(patch = {}) {
|
|
4795
|
+
remoteSystemResumeState = {
|
|
4796
|
+
...remoteSystemResumeState,
|
|
4797
|
+
...patch,
|
|
4798
|
+
enabled: true
|
|
4799
|
+
};
|
|
4800
|
+
emitBridgeEvent('RemoteSystemResumeUpdated', serializeRemoteSystemResumeState());
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4765
4803
|
function serializeRemoteRegistryRealtimeState() {
|
|
4766
4804
|
return {
|
|
4767
4805
|
...remoteRegistryRealtimeState,
|
|
@@ -5189,12 +5227,19 @@ function readSupabaseRuntimeConfig() {
|
|
|
5189
5227
|
|| process.env.MINDEXEC_SUPABASE_KEY
|
|
5190
5228
|
|| '').trim();
|
|
5191
5229
|
if (envUrl && envKey) {
|
|
5192
|
-
|
|
5230
|
+
remoteSupabaseRuntimeConfigCache = {
|
|
5231
|
+
url: envUrl.replace(/\/+$/, ''),
|
|
5232
|
+
key: envKey,
|
|
5233
|
+
source: 'env'
|
|
5234
|
+
};
|
|
5235
|
+
return { ...remoteSupabaseRuntimeConfigCache };
|
|
5193
5236
|
}
|
|
5194
5237
|
|
|
5195
5238
|
const candidates = [
|
|
5196
5239
|
path.join(WEB_APP_ROOT || '', 'appsettings.json'),
|
|
5197
5240
|
path.join(DEFAULT_WEB_APP_ROOT, 'appsettings.json'),
|
|
5241
|
+
path.join(BRIDGE_ROOT, 'wwwroot', 'appsettings.json'),
|
|
5242
|
+
path.join(process.cwd(), 'wwwroot', 'appsettings.json'),
|
|
5198
5243
|
path.resolve(BRIDGE_ROOT, '..', 'MindExecution.Web', 'wwwroot', 'appsettings.json')
|
|
5199
5244
|
].filter(Boolean);
|
|
5200
5245
|
|
|
@@ -5204,13 +5249,25 @@ function readSupabaseRuntimeConfig() {
|
|
|
5204
5249
|
const url = String(payload?.Supabase?.Url || '').trim().replace(/\/+$/, '');
|
|
5205
5250
|
const key = String(payload?.Supabase?.Key || payload?.Supabase?.AnonKey || payload?.Supabase?.PublishableKey || '').trim();
|
|
5206
5251
|
if (url && key) {
|
|
5207
|
-
|
|
5252
|
+
remoteSupabaseRuntimeConfigCache = {
|
|
5253
|
+
url,
|
|
5254
|
+
key,
|
|
5255
|
+
source: candidate
|
|
5256
|
+
};
|
|
5257
|
+
return { ...remoteSupabaseRuntimeConfigCache };
|
|
5208
5258
|
}
|
|
5209
5259
|
} catch {
|
|
5210
5260
|
// Try the next appsettings candidate.
|
|
5211
5261
|
}
|
|
5212
5262
|
}
|
|
5213
5263
|
|
|
5264
|
+
if (remoteSupabaseRuntimeConfigCache?.url && remoteSupabaseRuntimeConfigCache?.key) {
|
|
5265
|
+
return {
|
|
5266
|
+
...remoteSupabaseRuntimeConfigCache,
|
|
5267
|
+
cached: true
|
|
5268
|
+
};
|
|
5269
|
+
}
|
|
5270
|
+
|
|
5214
5271
|
return { url: '', key: '' };
|
|
5215
5272
|
}
|
|
5216
5273
|
|
|
@@ -5603,6 +5660,145 @@ function stopRemoteHostTargetRenew(reason = 'stopped') {
|
|
|
5603
5660
|
});
|
|
5604
5661
|
}
|
|
5605
5662
|
|
|
5663
|
+
function disconnectRemoteHubDevicesForResume(reason = 'system-resume') {
|
|
5664
|
+
const devices = remoteHub.listDevices();
|
|
5665
|
+
let disconnected = 0;
|
|
5666
|
+
for (const device of devices) {
|
|
5667
|
+
if (device?.connected !== true || !device?.deviceId) {
|
|
5668
|
+
continue;
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5671
|
+
if (remoteHub.disconnectDevice(device.deviceId, reason)) {
|
|
5672
|
+
disconnected += 1;
|
|
5673
|
+
}
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5676
|
+
return disconnected;
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
async function reconcileRemoteAfterSystemResume(trigger = 'timer-drift', driftMs = 0) {
|
|
5680
|
+
if (remoteSystemResumeInFlight || isShuttingDown) {
|
|
5681
|
+
return;
|
|
5682
|
+
}
|
|
5683
|
+
|
|
5684
|
+
remoteSystemResumeInFlight = true;
|
|
5685
|
+
try {
|
|
5686
|
+
const localHub = remoteHub.getStatus({ includeSecrets: true });
|
|
5687
|
+
const isLocalHost = localHub?.hostTargetActive === true && !!localHub.hostTargetNodeId;
|
|
5688
|
+
const wasRegistryAgentRunning = isRemoteAgentRegistryOwned();
|
|
5689
|
+
const disconnectedDevices = isLocalHost
|
|
5690
|
+
? disconnectRemoteHubDevicesForResume('system-resume')
|
|
5691
|
+
: 0;
|
|
5692
|
+
|
|
5693
|
+
updateRemoteSystemResumeState({
|
|
5694
|
+
status: 'detected',
|
|
5695
|
+
reason: 'system-resume',
|
|
5696
|
+
trigger,
|
|
5697
|
+
lastDetectedAt: new Date().toISOString(),
|
|
5698
|
+
lastError: '',
|
|
5699
|
+
driftMs: Math.round(Number(driftMs) || 0),
|
|
5700
|
+
localHost: isLocalHost,
|
|
5701
|
+
registryAgentRestarted: wasRegistryAgentRunning,
|
|
5702
|
+
disconnectedDevices
|
|
5703
|
+
});
|
|
5704
|
+
|
|
5705
|
+
logEvent(
|
|
5706
|
+
'remote',
|
|
5707
|
+
`system resume detected ${formatKeyValue('trigger', trigger)} ${formatKeyValue('driftMs', Math.round(Number(driftMs) || 0))} ${formatKeyValue('host', isLocalHost ? 'yes' : 'no')} ${formatKeyValue('agent', wasRegistryAgentRunning ? 'registry' : '-')}`,
|
|
5708
|
+
'remote');
|
|
5709
|
+
|
|
5710
|
+
closeRemoteRegistryRealtime('system-resume');
|
|
5711
|
+
|
|
5712
|
+
if (wasRegistryAgentRunning) {
|
|
5713
|
+
await stopRemoteAgentConnection('system-resume-reconnect');
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
if (isLocalHost) {
|
|
5717
|
+
updateRemoteHostTargetRenewState({
|
|
5718
|
+
status: 'waking',
|
|
5719
|
+
reason: 'system-resume',
|
|
5720
|
+
nodeId: localHub.hostTargetNodeId,
|
|
5721
|
+
leaseId: localHub.hostTargetLeaseId,
|
|
5722
|
+
hostInstanceId: localHub.hostTargetHostInstanceId || localHub.hostInstanceId,
|
|
5723
|
+
endpoint: localHub.hostTargetEndpoint,
|
|
5724
|
+
endpointCandidates: localHub.hostTargetEndpointCandidates || [],
|
|
5725
|
+
expiresAt: localHub.hostTargetExpiresAt,
|
|
5726
|
+
lastError: ''
|
|
5727
|
+
});
|
|
5728
|
+
await runRemoteHostTargetRenewOnce('system-resume', { takeover: false });
|
|
5729
|
+
}
|
|
5730
|
+
|
|
5731
|
+
updateRemoteRegistryFollowerState({
|
|
5732
|
+
status: 'waking',
|
|
5733
|
+
reason: 'system-resume',
|
|
5734
|
+
lastError: '',
|
|
5735
|
+
lastAttemptAt: new Date().toISOString()
|
|
5736
|
+
});
|
|
5737
|
+
await wakeRemoteRegistryFollower('system-resume');
|
|
5738
|
+
|
|
5739
|
+
updateRemoteSystemResumeState({
|
|
5740
|
+
status: 'reconciled',
|
|
5741
|
+
reason: 'system-resume',
|
|
5742
|
+
lastCompletedAt: new Date().toISOString(),
|
|
5743
|
+
lastError: ''
|
|
5744
|
+
});
|
|
5745
|
+
|
|
5746
|
+
emitBridgeEvent('RemoteSystemResumeReconciled', {
|
|
5747
|
+
trigger,
|
|
5748
|
+
driftMs: Math.round(Number(driftMs) || 0),
|
|
5749
|
+
localHost: isLocalHost,
|
|
5750
|
+
registryAgentRestarted: wasRegistryAgentRunning,
|
|
5751
|
+
disconnectedDevices
|
|
5752
|
+
});
|
|
5753
|
+
} catch (error) {
|
|
5754
|
+
logWarn('remote', `system resume reconcile failed: ${error?.message || error}`);
|
|
5755
|
+
updateRemoteSystemResumeState({
|
|
5756
|
+
status: 'error',
|
|
5757
|
+
reason: 'system-resume-failed',
|
|
5758
|
+
lastError: error?.message || String(error || ''),
|
|
5759
|
+
lastCompletedAt: new Date().toISOString()
|
|
5760
|
+
});
|
|
5761
|
+
updateRemoteRegistryFollowerState({
|
|
5762
|
+
status: 'error',
|
|
5763
|
+
reason: 'system-resume-failed',
|
|
5764
|
+
lastError: error?.message || String(error || ''),
|
|
5765
|
+
lastAttemptAt: new Date().toISOString()
|
|
5766
|
+
});
|
|
5767
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS, 'system-resume-error');
|
|
5768
|
+
} finally {
|
|
5769
|
+
remoteSystemResumeInFlight = false;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
|
|
5773
|
+
function startRemoteSystemResumeMonitor() {
|
|
5774
|
+
if (remoteSystemResumeTimer || isShuttingDown) {
|
|
5775
|
+
return;
|
|
5776
|
+
}
|
|
5777
|
+
|
|
5778
|
+
remoteSystemResumeExpectedAt = Date.now() + REMOTE_SYSTEM_RESUME_CHECK_MS;
|
|
5779
|
+
remoteSystemResumeTimer = setInterval(() => {
|
|
5780
|
+
const now = Date.now();
|
|
5781
|
+
const driftMs = now - remoteSystemResumeExpectedAt;
|
|
5782
|
+
remoteSystemResumeExpectedAt = now + REMOTE_SYSTEM_RESUME_CHECK_MS;
|
|
5783
|
+
if (driftMs < REMOTE_SYSTEM_RESUME_DRIFT_MS) {
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5786
|
+
|
|
5787
|
+
reconcileRemoteAfterSystemResume('timer-drift', driftMs).catch(error => {
|
|
5788
|
+
logWarn('remote', `system resume handler failed: ${error?.message || error}`);
|
|
5789
|
+
});
|
|
5790
|
+
}, REMOTE_SYSTEM_RESUME_CHECK_MS);
|
|
5791
|
+
remoteSystemResumeTimer?.unref?.();
|
|
5792
|
+
}
|
|
5793
|
+
|
|
5794
|
+
function stopRemoteSystemResumeMonitor() {
|
|
5795
|
+
if (remoteSystemResumeTimer) {
|
|
5796
|
+
clearInterval(remoteSystemResumeTimer);
|
|
5797
|
+
remoteSystemResumeTimer = null;
|
|
5798
|
+
}
|
|
5799
|
+
remoteSystemResumeExpectedAt = 0;
|
|
5800
|
+
}
|
|
5801
|
+
|
|
5606
5802
|
async function runRemoteHostTargetRenewOnce(trigger = 'timer', options = {}) {
|
|
5607
5803
|
if (!REMOTE_HOST_TARGET_AUTO_RENEW_ENABLED || remoteHostTargetRenewInFlight) {
|
|
5608
5804
|
return serializeRemoteHostTargetRenewState();
|
|
@@ -10509,6 +10705,7 @@ app.get('/api/status', async (req, res) => {
|
|
|
10509
10705
|
remoteRegistryFollower: serializeRemoteRegistryFollowerState(),
|
|
10510
10706
|
remoteRegistryRealtime: serializeRemoteRegistryRealtimeState(),
|
|
10511
10707
|
remoteHostTargetRenew: serializeRemoteHostTargetRenewState(),
|
|
10708
|
+
remoteSystemResume: serializeRemoteSystemResumeState(),
|
|
10512
10709
|
shellJobsPath: '/api/shell/jobs',
|
|
10513
10710
|
companyCore: {
|
|
10514
10711
|
baseUrl: companyCoreBaseUrl,
|
|
@@ -10551,6 +10748,26 @@ app.get('/api/remote/status', (req, res) => {
|
|
|
10551
10748
|
res.json(remoteHub.getStatus({ includeSecrets: true }));
|
|
10552
10749
|
});
|
|
10553
10750
|
|
|
10751
|
+
app.post('/api/remote/system/resume', async (req, res) => {
|
|
10752
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
10753
|
+
try {
|
|
10754
|
+
await reconcileRemoteAfterSystemResume('manual-api', Number(req.body?.driftMs || 0));
|
|
10755
|
+
res.json({
|
|
10756
|
+
ok: true,
|
|
10757
|
+
remoteSystemResume: serializeRemoteSystemResumeState(),
|
|
10758
|
+
remoteRegistryFollower: serializeRemoteRegistryFollowerState(),
|
|
10759
|
+
remoteHostTargetRenew: serializeRemoteHostTargetRenewState(),
|
|
10760
|
+
remoteAgent: serializeRemoteAgentState()
|
|
10761
|
+
});
|
|
10762
|
+
} catch (err) {
|
|
10763
|
+
res.status(500).json({
|
|
10764
|
+
ok: false,
|
|
10765
|
+
error: err?.message || String(err),
|
|
10766
|
+
remoteSystemResume: serializeRemoteSystemResumeState()
|
|
10767
|
+
});
|
|
10768
|
+
}
|
|
10769
|
+
});
|
|
10770
|
+
|
|
10554
10771
|
app.get('/api/remote/devices', (req, res) => {
|
|
10555
10772
|
res.setHeader('Cache-Control', 'no-store');
|
|
10556
10773
|
const includeDataUrl = /^(1|true|yes|on|data|base64)$/i.test(String(req.query?.includeDataUrl ?? req.query?.frameData ?? ''));
|
|
@@ -12313,8 +12530,9 @@ async function startBridgeServer() {
|
|
|
12313
12530
|
: `${tone('app', 'muted')} ${tone('not packaged', 'warn')}`,
|
|
12314
12531
|
`${tone('listen', 'muted')} ${tone(`http://127.0.0.1:${PORT}`, 'path')}`,
|
|
12315
12532
|
`${tone('stop', 'muted')} Ctrl+C`
|
|
12316
|
-
]);
|
|
12533
|
+
]);
|
|
12317
12534
|
startRemoteRegistryFollower();
|
|
12535
|
+
startRemoteSystemResumeMonitor();
|
|
12318
12536
|
});
|
|
12319
12537
|
}
|
|
12320
12538
|
|
|
@@ -12382,6 +12600,12 @@ async function shutdownBridge(signal) {
|
|
|
12382
12600
|
// Ignore registry follower shutdown errors
|
|
12383
12601
|
}
|
|
12384
12602
|
|
|
12603
|
+
try {
|
|
12604
|
+
stopRemoteSystemResumeMonitor();
|
|
12605
|
+
} catch {
|
|
12606
|
+
// Ignore system resume monitor shutdown errors
|
|
12607
|
+
}
|
|
12608
|
+
|
|
12385
12609
|
try {
|
|
12386
12610
|
stopRemoteHostTargetRenew('bridge-shutdown');
|
|
12387
12611
|
} catch {
|