@mindexec/cli 0.2.285 → 0.2.286

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.285",
3
+ "version": "0.2.286",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -713,6 +713,26 @@ async function main() {
713
713
  ? { status: status.payload.remoteRegistryFollower, agent: agent.payload }
714
714
  : null;
715
715
  }, 9000, `registry inactive keeps ready data-plane agent\n${client.details()}`);
716
+
717
+ const inactivePid = Number(restartedAgent.pid || 0);
718
+ killProcess(inactivePid);
719
+ const recoveredInactiveAgent = await waitForManagedAgentRestart(
720
+ client,
721
+ hostBEndpoint,
722
+ inactivePid,
723
+ 'host-b after inactive target recovery',
724
+ 12000);
725
+ assert.equal(recoveredInactiveAgent.usingNpx, false, JSON.stringify(recoveredInactiveAgent));
726
+ assert.match(String(recoveredInactiveAgent.launcher || ''), /mindexec-remote-fast/i);
727
+ assert.equal(recoveredInactiveAgent.leaseId, 'lease-b');
728
+ await waitForConnectedDevice(hostB, 'host-b after inactive target recovery');
729
+ await waitFor(async () => {
730
+ const status = await fetchJson(`${client.baseUrl}/api/status`);
731
+ return status.payload?.remoteRegistryFollower?.reason === 'registry-inactive-agent-recovered'
732
+ && String(status.payload?.remoteRegistryFollower?.targetEndpoint || '') === hostBEndpoint
733
+ ? status.payload.remoteRegistryFollower
734
+ : null;
735
+ }, 4000, `inactive target recovered managed agent\n${client.details()}`);
716
736
  const finalStatus = await fetchJson(`${client.baseUrl}/api/status`);
717
737
  assert.ok(finalStatus.payload?.remoteRegistryRealtime?.changes >= 2, JSON.stringify(finalStatus.payload?.remoteRegistryRealtime));
718
738
  assert.ok(finalStatus.payload?.remoteRegistryRealtime?.wakeups >= 2, JSON.stringify(finalStatus.payload?.remoteRegistryRealtime));
package/server.js CHANGED
@@ -3505,6 +3505,9 @@ const REMOTE_REGISTRY_FOLLOWER_MISSING_SESSION_FAST_RETRY_COUNT = Math.max(
3505
3505
  Number(process.env.MINDEXEC_REMOTE_REGISTRY_MISSING_SESSION_FAST_RETRY_COUNT || 5) || 5);
3506
3506
  const REMOTE_REGISTRY_FOLLOWER_INACTIVE_STOP_COUNT = Math.max(2, Number(process.env.MINDEXEC_REMOTE_REGISTRY_INACTIVE_STOP_COUNT || 8) || 8);
3507
3507
  const REMOTE_REGISTRY_FOLLOWER_INACTIVE_GRACE_MS = Math.max(500, Number(process.env.MINDEXEC_REMOTE_REGISTRY_INACTIVE_GRACE_MS || 60000) || 60000);
3508
+ const REMOTE_REGISTRY_INACTIVE_TARGET_RECOVERY_MS = Math.max(
3509
+ REMOTE_REGISTRY_FOLLOWER_INACTIVE_GRACE_MS,
3510
+ Number(process.env.MINDEXEC_REMOTE_REGISTRY_INACTIVE_TARGET_RECOVERY_MS || (10 * 60 * 1000)) || (10 * 60 * 1000));
3508
3511
  const REMOTE_REGISTRY_SESSION_REFRESH_LEAD_MS = Math.max(15_000, Number(process.env.MINDEXEC_REMOTE_REGISTRY_SESSION_REFRESH_LEAD_MS || 120_000) || 120_000);
3509
3512
  const REMOTE_REGISTRY_REALTIME_ENABLED = REMOTE_REGISTRY_FOLLOWER_ENABLED
3510
3513
  && !/^(0|false|no|off)$/i.test(String(process.env.MINDEXEC_REMOTE_REGISTRY_REALTIME || 'true'));
@@ -6096,6 +6099,8 @@ function normalizeRemoteRegistryTarget(row) {
6096
6099
  const hostInstanceId = String(readRegistryTargetField(row, 'host_instance_id', 'hostInstanceId', 'HostInstanceId') || '').trim();
6097
6100
  const expiresAt = String(readRegistryTargetField(row, 'expires_at', 'expiresAt', 'ExpiresAt') || '').trim();
6098
6101
  const expiresAtMs = Date.parse(expiresAt);
6102
+ const updatedAt = String(readRegistryTargetField(row, 'updated_at', 'updatedAt', 'UpdatedAt') || '').trim();
6103
+ const updatedAtMs = Date.parse(updatedAt);
6099
6104
 
6100
6105
  return {
6101
6106
  active,
@@ -6106,7 +6111,9 @@ function normalizeRemoteRegistryTarget(row) {
6106
6111
  nodeId,
6107
6112
  hostInstanceId,
6108
6113
  expiresAt,
6109
- expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : 0
6114
+ expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : 0,
6115
+ updatedAt,
6116
+ updatedAtMs: Number.isFinite(updatedAtMs) ? updatedAtMs : 0
6110
6117
  };
6111
6118
  }
6112
6119
 
@@ -6114,6 +6121,20 @@ function isRemoteRegistryTargetExpired(target) {
6114
6121
  return !target?.expiresAtMs || target.expiresAtMs <= Date.now();
6115
6122
  }
6116
6123
 
6124
+ function isRecoverableInactiveRemoteRegistryTarget(target) {
6125
+ if (!target || target.active === true || !isRemoteRegistryTargetExpired(target)) {
6126
+ return false;
6127
+ }
6128
+
6129
+ const managers = normalizeRemoteManagerEndpointList(target.endpointCandidates, target.endpoint);
6130
+ if (managers.length === 0 || !target.pairToken || !target.leaseId || !target.nodeId) {
6131
+ return false;
6132
+ }
6133
+
6134
+ const anchorMs = Math.max(Number(target.expiresAtMs || 0), Number(target.updatedAtMs || 0));
6135
+ return anchorMs > 0 && Date.now() - anchorMs <= REMOTE_REGISTRY_INACTIVE_TARGET_RECOVERY_MS;
6136
+ }
6137
+
6117
6138
  function isRemoteRegistryTargetSameAsLocalHost(localHub, target) {
6118
6139
  const localLeaseId = safeRemoteAgentField(localHub?.hostTargetLeaseId, 128);
6119
6140
  const localHostInstanceId = safeRemoteAgentField(localHub?.hostTargetHostInstanceId || localHub?.hostInstanceId, 128);
@@ -6405,15 +6426,17 @@ function stopRemoteHostTargetRenew(reason = 'stopped') {
6405
6426
  clearTimeout(remoteHostTargetRenewTimer);
6406
6427
  remoteHostTargetRenewTimer = null;
6407
6428
  }
6429
+ const keepRecoverableLease = /^(no-local-host-target|expired-local-host-target)$/i.test(String(reason || ''))
6430
+ && !!remoteHostTargetRenewState.nodeId;
6408
6431
  updateRemoteHostTargetRenewState({
6409
6432
  status: REMOTE_HOST_TARGET_AUTO_RENEW_ENABLED ? 'idle' : 'disabled',
6410
6433
  reason,
6411
- nodeId: '',
6412
- leaseId: '',
6413
- hostInstanceId: '',
6414
- endpoint: '',
6415
- endpointCandidates: [],
6416
- expiresAt: '',
6434
+ nodeId: keepRecoverableLease ? remoteHostTargetRenewState.nodeId : '',
6435
+ leaseId: keepRecoverableLease ? remoteHostTargetRenewState.leaseId : '',
6436
+ hostInstanceId: keepRecoverableLease ? remoteHostTargetRenewState.hostInstanceId : '',
6437
+ endpoint: keepRecoverableLease ? remoteHostTargetRenewState.endpoint : '',
6438
+ endpointCandidates: keepRecoverableLease ? remoteHostTargetRenewState.endpointCandidates : [],
6439
+ expiresAt: keepRecoverableLease ? remoteHostTargetRenewState.expiresAt : '',
6417
6440
  lastError: ''
6418
6441
  });
6419
6442
  }
@@ -7029,28 +7052,48 @@ async function runRemoteRegistryFollowerOnce(trigger = 'timer') {
7029
7052
  ? 'registry-inactive'
7030
7053
  : 'no-active-target';
7031
7054
  const inactive = rememberRemoteRegistryInactiveTarget(inactiveReason);
7032
- const recovery = inactive.agentKept
7055
+ let recovery = inactive.agentKept
7033
7056
  ? await maybeRestartKeptRemoteAgentForMissingRegistryTarget(inactiveReason)
7034
7057
  : null;
7035
- if (inactive.confirmedInactive && isRemoteAgentRegistryOwned()) {
7058
+ if (!inactive.agentKept && isRecoverableInactiveRemoteRegistryTarget(target)) {
7059
+ const recoveryManagers = normalizeRemoteManagerEndpointList(target.endpointCandidates, target.endpoint);
7060
+ logWarn(
7061
+ 'remote',
7062
+ `registry target inactive but recently recoverable; reconnecting previous manager ${formatKeyValue('reason', inactiveReason)} ${formatKeyValue('manager', recoveryManagers[0] || '-')}`);
7063
+ recovery = await startRemoteAgentConnection({
7064
+ manager: recoveryManagers[0],
7065
+ managerCandidates: recoveryManagers,
7066
+ pairToken: target.pairToken,
7067
+ leaseId: target.leaseId,
7068
+ nodeId: target.nodeId,
7069
+ engine: 'auto',
7070
+ source: 'local-bridge-registry-inactive-recovery'
7071
+ });
7072
+ }
7073
+ if (inactive.confirmedInactive && recovery?.ok !== true && isRemoteAgentRegistryOwned()) {
7036
7074
  await stopRemoteAgentConnection('registry-inactive');
7037
7075
  }
7038
- const keptManager = inactive.agentKept ? safeRemoteAgentField(remoteAgentState.manager, 160) : '';
7039
- const keptCandidates = inactive.agentKept ? normalizeRemoteManagerEndpointList(remoteAgentState.managerCandidates, remoteAgentState.manager) : [];
7076
+ const recoveredAgent = recovery?.ok === true && isRemoteAgentRegistryOwned() && isRemoteAgentProcessRunning();
7077
+ const agentKept = inactive.agentKept || recoveredAgent;
7078
+ const dataPlaneReady = recoveredAgent ? isRemoteAgentReadyDataPlaneAlive() : inactive.dataPlaneReady;
7079
+ const keptManager = agentKept ? safeRemoteAgentField(remoteAgentState.manager, 160) : '';
7080
+ const keptCandidates = agentKept ? normalizeRemoteManagerEndpointList(remoteAgentState.managerCandidates, remoteAgentState.manager) : [];
7040
7081
  updateRemoteRegistryFollowerState({
7041
- status: inactive.agentKept ? 'waiting' : 'idle',
7042
- reason: inactive.agentKept ? `${inactiveReason}-agent-kept` : inactiveReason,
7082
+ status: recoveredAgent ? 'connected' : (agentKept ? 'waiting' : 'idle'),
7083
+ reason: recovery?.ok === true
7084
+ ? `${inactiveReason}-agent-recovered`
7085
+ : (agentKept ? `${inactiveReason}-agent-kept` : inactiveReason),
7043
7086
  authenticated: true,
7044
7087
  lastAttemptAt: attemptedAt,
7045
- lastSuccessAt: attemptedAt,
7088
+ lastSuccessAt: recoveredAgent ? new Date().toISOString() : attemptedAt,
7046
7089
  targetEndpoint: keptManager,
7047
7090
  targetEndpointCandidates: keptCandidates,
7048
- targetLeaseId: inactive.agentKept ? safeRemoteAgentField(remoteAgentState.leaseId, 128) : '',
7049
- targetNodeId: inactive.agentKept ? safeRemoteAgentField(remoteAgentState.nodeId, 128) : '',
7091
+ targetLeaseId: agentKept ? safeRemoteAgentField(remoteAgentState.leaseId, 128) : '',
7092
+ targetNodeId: agentKept ? safeRemoteAgentField(remoteAgentState.nodeId, 128) : '',
7050
7093
  inactiveCount: inactive.count,
7051
7094
  inactiveElapsedMs: inactive.elapsedMs,
7052
- agentKept: inactive.agentKept,
7053
- dataPlaneReady: inactive.dataPlaneReady,
7095
+ agentKept,
7096
+ dataPlaneReady,
7054
7097
  lastError: recovery?.ok === false ? (recovery.error || 'missing-target-agent-recovery-failed') : ''
7055
7098
  });
7056
7099
  await reportRemoteRegistryFollowerSync({
@@ -7058,21 +7101,21 @@ async function runRemoteRegistryFollowerOnce(trigger = 'timer') {
7058
7101
  skipped: true,
7059
7102
  reason: recovery?.ok === true
7060
7103
  ? `${inactiveReason}-agent-recovered`
7061
- : (inactive.agentKept ? `${inactiveReason}-agent-kept` : inactiveReason),
7104
+ : (agentKept ? `${inactiveReason}-agent-kept` : inactiveReason),
7062
7105
  error: recovery?.ok === false ? recovery.error : '',
7063
7106
  trigger,
7064
7107
  authenticated: true,
7065
7108
  targetEndpoint: keptManager,
7066
7109
  targetEndpointCandidates: keptCandidates,
7067
- targetLeaseId: inactive.agentKept ? remoteAgentState.leaseId : '',
7068
- targetNodeId: inactive.agentKept ? remoteAgentState.nodeId : '',
7110
+ targetLeaseId: agentKept ? remoteAgentState.leaseId : '',
7111
+ targetNodeId: agentKept ? remoteAgentState.nodeId : '',
7069
7112
  inactiveCount: inactive.count,
7070
7113
  inactiveElapsedMs: inactive.elapsedMs,
7071
- agentKept: inactive.agentKept,
7072
- dataPlaneReady: inactive.dataPlaneReady
7114
+ agentKept,
7115
+ dataPlaneReady
7073
7116
  });
7074
7117
  scheduleRemoteRegistryFollower(
7075
- inactive.agentKept && !inactive.dataPlaneReady
7118
+ (agentKept && !dataPlaneReady) || recovery?.ok === false
7076
7119
  ? REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS
7077
7120
  : REMOTE_REGISTRY_FOLLOWER_POLL_MS,
7078
7121
  recovery?.ok === true ? 'missing-target-agent-recovered' : 'no-target');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-4ns18VrUu0pBDI842quwLhm/+HKTrGh/OLelhZ3am1k=",
4
+ "hash": "sha256-qAJZS2gJhj1UEBPo3ENSQdIHVUddkYUsKaosccaB7nY=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -132,7 +132,7 @@
132
132
  "MindExecution.Plugins.PlanMaster.w9msy01tp9.dll": "MindExecution.Plugins.PlanMaster.dll",
133
133
  "MindExecution.Plugins.YouTube.iob4817wtf.dll": "MindExecution.Plugins.YouTube.dll",
134
134
  "MindExecution.Shared.lvqhaceogf.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.1otc7eb4qb.dll": "MindExecution.Web.dll",
135
+ "MindExecution.Web.f4nix6qubf.dll": "MindExecution.Web.dll",
136
136
  "dotnet.js": "dotnet.js",
137
137
  "dotnet.native.qc8g39g30v.js": "dotnet.native.js",
138
138
  "dotnet.native.boem75ye5i.wasm": "dotnet.native.wasm",
@@ -284,7 +284,7 @@
284
284
  "MindExecution.Plugins.Concept.cjp3ty08t0.dll": "sha256-+FZtYvFT7aqaThaaATz4C7YmMZEDy3z8i2JcplL+fJs=",
285
285
  "MindExecution.Plugins.PlanMaster.w9msy01tp9.dll": "sha256-xBGl/hx1xJ1XCAy1wfZJv0/LSsm825PrufniNvTFpdk=",
286
286
  "MindExecution.Shared.lvqhaceogf.dll": "sha256-UXxhTxZwmtsqxr4Tf3DmjWKLbihsP91u88dZhsubRQU=",
287
- "MindExecution.Web.1otc7eb4qb.dll": "sha256-MTDmkh2BIm9fbntoCRCa6/Mt3rKi+dxD00cxRf671m8="
287
+ "MindExecution.Web.f4nix6qubf.dll": "sha256-j/4kL30Kb8VYCRXGcr0Ireb4S3IDzFuKmP9oZxdmMVI="
288
288
  },
289
289
  "lazyAssembly": {
290
290
  "MindExecution.Plugins.Admin.m5d94977um.dll": "sha256-Jt1vsUZkolnZk+PcV3ysNyL99SfPUVcUHwvBQ/Y+pR8=",
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "/Swsi38c",
2
+ "version": "Xn/pulp5",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -446,8 +446,8 @@
446
446
  "url": "_framework/MindExecution.Shared.lvqhaceogf.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-MTDmkh2BIm9fbntoCRCa6/Mt3rKi+dxD00cxRf671m8=",
450
- "url": "_framework/MindExecution.Web.1otc7eb4qb.dll"
449
+ "hash": "sha256-j/4kL30Kb8VYCRXGcr0Ireb4S3IDzFuKmP9oZxdmMVI=",
450
+ "url": "_framework/MindExecution.Web.f4nix6qubf.dll"
451
451
  },
452
452
  {
453
453
  "hash": "sha256-IsZJ91/OW+fHzNqIgEc7Y072ns8z9dGritiSyvR9Wgc=",
@@ -770,7 +770,7 @@
770
770
  "url": "_framework/Websocket.Client.vapounvmnl.dll"
771
771
  },
772
772
  {
773
- "hash": "sha256-vHiMEaxrdO/yUO6InjfIIJ5Myy9/9LX0UkZolE4HAps=",
773
+ "hash": "sha256-YBSr20c7qRbSof/FmV2prtF/F52GFhVGyW27i1MjBis=",
774
774
  "url": "_framework/blazor.boot.json"
775
775
  },
776
776
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: /Swsi38c */
1
+ /* Manifest version: Xn/pulp5 */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4