@mindexec/cli 0.2.23 → 0.2.25

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.
Files changed (95) hide show
  1. package/package.json +1 -1
  2. package/remote-hub.js +134 -1
  3. package/scripts/remote-fleet-render-smoke.mjs +87 -53
  4. package/scripts/remote-http-smoke.mjs +219 -39
  5. package/scripts/remote-hub-smoke.mjs +139 -0
  6. package/server.js +20 -1
  7. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +18 -1
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +670 -395
  9. package/wwwroot/_framework/{Microsoft.CSharp.qrvp77qmhs.dll → Microsoft.CSharp.8bsm8vx6su.dll} +0 -0
  10. package/wwwroot/_framework/MindExecution.Core.6rfnfdndxq.dll +0 -0
  11. package/wwwroot/_framework/{MindExecution.Kernel.badrt1tkvv.dll → MindExecution.Kernel.z56elxihok.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Admin.73w1bvz4r1.dll → MindExecution.Plugins.Admin.p5cs4ap87v.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.Business.dvd82y422m.dll → MindExecution.Plugins.Business.s35og5uz44.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.Concept.m3ukc0xvom.dll → MindExecution.Plugins.Concept.zczca3fsxz.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.Directory.23tm2uvfvu.dll → MindExecution.Plugins.Directory.y74f55e8x3.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.8nrc7ge4ob.dll → MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.3ox59073d8.dll → MindExecution.Plugins.YouTube.8nz4wv2nsj.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Shared.va1gxp0crd.dll → MindExecution.Shared.v6ani8nfp8.dll} +0 -0
  19. package/wwwroot/_framework/MindExecution.Web.0cs29v57jl.dll +0 -0
  20. package/wwwroot/_framework/{System.Collections.x53e19vfsj.dll → System.Collections.23yxgetbju.dll} +0 -0
  21. package/wwwroot/_framework/{System.Collections.Concurrent.y1zmvuyipi.dll → System.Collections.Concurrent.vow3rm1dku.dll} +0 -0
  22. package/wwwroot/_framework/{System.Collections.Immutable.ug3j698qms.dll → System.Collections.Immutable.ap9596utv5.dll} +0 -0
  23. package/wwwroot/_framework/{System.Collections.NonGeneric.h66hj3863h.dll → System.Collections.NonGeneric.npjkaz40oc.dll} +0 -0
  24. package/wwwroot/_framework/{System.Collections.Specialized.umr3y27ntj.dll → System.Collections.Specialized.ugkjbs6p02.dll} +0 -0
  25. package/wwwroot/_framework/{System.ComponentModel.Annotations.tz6gnt4ebt.dll → System.ComponentModel.Annotations.clwo0z2oyu.dll} +0 -0
  26. package/wwwroot/_framework/{System.ComponentModel.Primitives.j7tiphu4rg.dll → System.ComponentModel.Primitives.end4v0xe2c.dll} +0 -0
  27. package/wwwroot/_framework/{System.ComponentModel.TypeConverter.ujlztox1gx.dll → System.ComponentModel.TypeConverter.tgtp5sm4iv.dll} +0 -0
  28. package/wwwroot/_framework/{System.ComponentModel.x9xz0ojfb6.dll → System.ComponentModel.wupoltkk1t.dll} +0 -0
  29. package/wwwroot/_framework/{System.Console.ijzpqmj7ne.dll → System.Console.9dik0wogo2.dll} +0 -0
  30. package/wwwroot/_framework/{System.Data.Common.1r0sqffq1p.dll → System.Data.Common.4v8jejsiu0.dll} +0 -0
  31. package/wwwroot/_framework/{System.Diagnostics.DiagnosticSource.9upoqwq09o.dll → System.Diagnostics.DiagnosticSource.e2q75ondtq.dll} +0 -0
  32. package/wwwroot/_framework/{System.Diagnostics.Process.m99azzntjm.dll → System.Diagnostics.Process.9736yjnxs8.dll} +0 -0
  33. package/wwwroot/_framework/{System.Diagnostics.TraceSource.pl7wv26myr.dll → System.Diagnostics.TraceSource.h9al53gbbw.dll} +0 -0
  34. package/wwwroot/_framework/{System.Diagnostics.Tracing.crlhfx6tut.dll → System.Diagnostics.Tracing.h4bcp2fo98.dll} +0 -0
  35. package/wwwroot/_framework/{System.Drawing.mi7d8hwowb.dll → System.Drawing.64oovy8qts.dll} +0 -0
  36. package/wwwroot/_framework/{System.Drawing.Primitives.22e4y9ikq9.dll → System.Drawing.Primitives.o6jiqpgbgl.dll} +0 -0
  37. package/wwwroot/_framework/{System.Formats.Asn1.jx23sjiqnn.dll → System.Formats.Asn1.rylx5ipd40.dll} +0 -0
  38. package/wwwroot/_framework/{System.IO.Compression.6fyoii3uej.dll → System.IO.Compression.iceabaupns.dll} +0 -0
  39. package/wwwroot/_framework/{System.IO.Pipelines.vg77t4cd4d.dll → System.IO.Pipelines.uw8csd3mlz.dll} +0 -0
  40. package/wwwroot/_framework/{System.Linq.Expressions.24xqiypwdt.dll → System.Linq.Expressions.ty95ava37f.dll} +0 -0
  41. package/wwwroot/_framework/{System.Linq.Queryable.hvd01d6rsa.dll → System.Linq.Queryable.hs2195jrwy.dll} +0 -0
  42. package/wwwroot/_framework/{System.Linq.1bkoxlqgmq.dll → System.Linq.hssodjwmlf.dll} +0 -0
  43. package/wwwroot/_framework/{System.Memory.8dx3lwgym4.dll → System.Memory.1k78n7wdxb.dll} +0 -0
  44. package/wwwroot/_framework/{System.Net.Http.eitrz660my.dll → System.Net.Http.3br8rfql4c.dll} +0 -0
  45. package/wwwroot/_framework/{System.Net.Http.Json.3mhdm9l1rf.dll → System.Net.Http.Json.860wbh17d8.dll} +0 -0
  46. package/wwwroot/_framework/{System.Net.NetworkInformation.3pkuofcv9r.dll → System.Net.NetworkInformation.e3wr00853o.dll} +0 -0
  47. package/wwwroot/_framework/{System.Net.Ping.8clj5pklrp.dll → System.Net.Ping.r5cw4mf1a4.dll} +0 -0
  48. package/wwwroot/_framework/{System.Net.Primitives.qrp4wcjz1p.dll → System.Net.Primitives.ksxwiwlvhu.dll} +0 -0
  49. package/wwwroot/_framework/{System.Net.WebSockets.qp6u31zvm5.dll → System.Net.WebSockets.6rt3n3gl2q.dll} +0 -0
  50. package/wwwroot/_framework/{System.Net.WebSockets.Client.2u6pv01g69.dll → System.Net.WebSockets.Client.z3usrzo7rz.dll} +0 -0
  51. package/wwwroot/_framework/{System.Numerics.Vectors.kc7ufp2j4l.dll → System.Numerics.Vectors.lbdzx8reja.dll} +0 -0
  52. package/wwwroot/_framework/{System.ObjectModel.qv82fot1ib.dll → System.ObjectModel.yct1gdirzf.dll} +0 -0
  53. package/wwwroot/_framework/{System.Private.CoreLib.rkafq04oma.dll → System.Private.CoreLib.ns29bor93l.dll} +0 -0
  54. package/wwwroot/_framework/{System.Private.Uri.t9542hmr6j.dll → System.Private.Uri.zp9kmg0z93.dll} +0 -0
  55. package/wwwroot/_framework/{System.Private.Xml.Linq.n8n3ptrbwu.dll → System.Private.Xml.Linq.lgv2n0akl4.dll} +0 -0
  56. package/wwwroot/_framework/{System.Private.Xml.rxd3tytisn.dll → System.Private.Xml.dpsk8g304y.dll} +0 -0
  57. package/wwwroot/_framework/{System.Reflection.Emit.ILGeneration.stxyk8zoo1.dll → System.Reflection.Emit.ILGeneration.1tcuz2cmbk.dll} +0 -0
  58. package/wwwroot/_framework/{System.Reflection.Emit.Lightweight.6xrd5v8vg0.dll → System.Reflection.Emit.Lightweight.ddt2wylovg.dll} +0 -0
  59. package/wwwroot/_framework/{System.Reflection.Emit.9tjhp6y0j3.dll → System.Reflection.Emit.d8vkadiwhg.dll} +0 -0
  60. package/wwwroot/_framework/{System.Reflection.Primitives.wgn8fpwwvv.dll → System.Reflection.Primitives.cpsl71xd1z.dll} +0 -0
  61. package/wwwroot/_framework/{System.Runtime.InteropServices.te07xr2we9.dll → System.Runtime.InteropServices.31vjgsfk1o.dll} +0 -0
  62. package/wwwroot/_framework/{System.Runtime.InteropServices.JavaScript.sliym526xh.dll → System.Runtime.InteropServices.JavaScript.pn2wvizzet.dll} +0 -0
  63. package/wwwroot/_framework/{System.Runtime.InteropServices.RuntimeInformation.oji7zut14z.dll → System.Runtime.InteropServices.RuntimeInformation.l5rk496q70.dll} +0 -0
  64. package/wwwroot/_framework/{System.Runtime.Intrinsics.507y4h8nzq.dll → System.Runtime.Intrinsics.0zee9qcqfy.dll} +0 -0
  65. package/wwwroot/_framework/{System.Runtime.Loader.v7gk4bse0k.dll → System.Runtime.Loader.xw2jr9wl92.dll} +0 -0
  66. package/wwwroot/_framework/{System.Runtime.Numerics.eqy5xjv3nd.dll → System.Runtime.Numerics.ezj1dfyvbj.dll} +0 -0
  67. package/wwwroot/_framework/{System.Runtime.Serialization.Formatters.zpkrub8lab.dll → System.Runtime.Serialization.Formatters.0y019e9zkr.dll} +0 -0
  68. package/wwwroot/_framework/{System.Runtime.Serialization.Primitives.vhkpnbxjip.dll → System.Runtime.Serialization.Primitives.bia5wb62c8.dll} +0 -0
  69. package/wwwroot/_framework/{System.Runtime.jn319d5nyg.dll → System.Runtime.h9cduidfkh.dll} +0 -0
  70. package/wwwroot/_framework/{System.Security.Claims.0ztig1q9vo.dll → System.Security.Claims.2r18wim2rl.dll} +0 -0
  71. package/wwwroot/_framework/{System.Security.Cryptography.vttizqc9ho.dll → System.Security.Cryptography.qqgybpoucx.dll} +0 -0
  72. package/wwwroot/_framework/{System.Text.Encoding.Extensions.utdd47ny8f.dll → System.Text.Encoding.Extensions.9t3hs6kyll.dll} +0 -0
  73. package/wwwroot/_framework/{System.Text.Encodings.Web.wah8r1zoe0.dll → System.Text.Encodings.Web.wftjfh2crk.dll} +0 -0
  74. package/wwwroot/_framework/{System.Text.Json.kxlfxj0wrs.dll → System.Text.Json.4s25e3op6i.dll} +0 -0
  75. package/wwwroot/_framework/{System.Text.RegularExpressions.dbqn58klox.dll → System.Text.RegularExpressions.cvkox11l6l.dll} +0 -0
  76. package/wwwroot/_framework/{System.Threading.Channels.hfa7j0uv2w.dll → System.Threading.Channels.419493szqu.dll} +0 -0
  77. package/wwwroot/_framework/{System.Threading.Thread.caul0pdqul.dll → System.Threading.Thread.xhmys87xh5.dll} +0 -0
  78. package/wwwroot/_framework/{System.Threading.42ao9vi047.dll → System.Threading.b66vzsz9g1.dll} +0 -0
  79. package/wwwroot/_framework/{System.Transactions.Local.fimi2hamzo.dll → System.Transactions.Local.7zfffdvnwf.dll} +0 -0
  80. package/wwwroot/_framework/{System.Web.HttpUtility.gq8yz50p2e.dll → System.Web.HttpUtility.9up6xfbtuq.dll} +0 -0
  81. package/wwwroot/_framework/{System.Xml.Linq.kitin4zjoj.dll → System.Xml.Linq.n5rzv9nbf7.dll} +0 -0
  82. package/wwwroot/_framework/{System.Xml.ReaderWriter.kzvw3qgxb0.dll → System.Xml.ReaderWriter.ag8pilllob.dll} +0 -0
  83. package/wwwroot/_framework/{System.Xml.XDocument.c539ki6cuq.dll → System.Xml.XDocument.sn51jas17n.dll} +0 -0
  84. package/wwwroot/_framework/{System.m05i39uvk9.dll → System.brmz7yk5qh.dll} +0 -0
  85. package/wwwroot/_framework/blazor.boot.json +161 -161
  86. package/wwwroot/_framework/dotnet.js +1 -1
  87. package/wwwroot/_framework/{dotnet.native.vz0adxojrz.wasm → dotnet.native.boem75ye5i.wasm} +0 -0
  88. package/wwwroot/_framework/{dotnet.native.xsn1d6x2kd.js → dotnet.native.qc8g39g30v.js} +1 -1
  89. package/wwwroot/_framework/{dotnet.runtime.dstopyvqzi.js → dotnet.runtime.opaiwunc3t.js} +1 -1
  90. package/wwwroot/_framework/{netstandard.0xet7jg7ky.dll → netstandard.yvr3prsx0x.dll} +0 -0
  91. package/wwwroot/index.html +1 -1
  92. package/wwwroot/service-worker-assets.js +166 -166
  93. package/wwwroot/service-worker.js +1 -1
  94. package/wwwroot/_framework/MindExecution.Core.c9fyqe953v.dll +0 -0
  95. package/wwwroot/_framework/MindExecution.Web.jmawk7z8d3.dll +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/remote-hub.js CHANGED
@@ -15,6 +15,7 @@ const RECENT_TASK_LIMIT = 12;
15
15
  const RECENT_TASK_BATCH_LIMIT = 16;
16
16
  const REMOTE_PROTOCOL_VERSION = 1;
17
17
  const MAX_SYNTHETIC_DEVICES = 1000;
18
+ const DEFAULT_HOST_TARGET_LEASE_MS = 30000;
18
19
  const SYNTHETIC_FRAME_DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
19
20
 
20
21
  function isEnabledValue(value, fallback = true) {
@@ -205,6 +206,8 @@ export function createRemoteHub(options = {}) {
205
206
  50,
206
207
  30 * 60 * 1000,
207
208
  DEFAULT_AGENT_TASK_TIMEOUT_MS);
209
+ const managerPackage = safeString(options.managerPackage ?? env.MINDEXEC_MANAGER_PACKAGE ?? '@mindexec/cli', 128);
210
+ const managerVersion = safeString(options.managerVersion ?? env.MINDEXEC_MANAGER_VERSION ?? '', 64);
208
211
  const pairToken = safeString(
209
212
  env.REMOTE_HUB_PAIR_TOKEN || env.MINDEXEC_REMOTE_PAIR_TOKEN || crypto.randomBytes(6).toString('hex'),
210
213
  256);
@@ -217,9 +220,129 @@ export function createRemoteHub(options = {}) {
217
220
  let started = false;
218
221
  let boundPort = requestedPort;
219
222
  let lastError = '';
223
+ let hostTarget = null;
224
+
225
+ function getAgentEndpoint() {
226
+ return `${host}:${boundPort || requestedPort}`;
227
+ }
228
+
229
+ function getActiveHostTarget() {
230
+ if (!hostTarget) {
231
+ return null;
232
+ }
233
+
234
+ const expiresMs = Date.parse(hostTarget.expiresAt || '');
235
+ if (Number.isFinite(expiresMs) && expiresMs <= Date.now()) {
236
+ const expiredTarget = hostTarget;
237
+ hostTarget = null;
238
+ emitRemoteEvent('RemoteHostTargetExpired', null, {
239
+ hostTarget: {
240
+ ...expiredTarget,
241
+ active: false
242
+ }
243
+ });
244
+ return null;
245
+ }
246
+
247
+ return hostTarget;
248
+ }
249
+
250
+ function serializeHostTarget(target = getActiveHostTarget()) {
251
+ if (!target) {
252
+ return {
253
+ active: false,
254
+ nodeId: '',
255
+ leaseId: '',
256
+ endpoint: '',
257
+ activatedAt: '',
258
+ updatedAt: '',
259
+ expiresAt: ''
260
+ };
261
+ }
262
+
263
+ return {
264
+ active: true,
265
+ nodeId: target.nodeId,
266
+ leaseId: target.leaseId,
267
+ endpoint: target.endpoint,
268
+ activatedAt: target.activatedAt,
269
+ updatedAt: target.updatedAt,
270
+ expiresAt: target.expiresAt
271
+ };
272
+ }
273
+
274
+ function setHostTarget(options = {}) {
275
+ const enabled = options.enabled !== false;
276
+ const nodeId = safeString(options.nodeId, 128);
277
+ if (!enabled) {
278
+ const previous = getActiveHostTarget();
279
+ if (!previous) {
280
+ return {
281
+ ok: true,
282
+ active: false,
283
+ hostTarget: serializeHostTarget(null)
284
+ };
285
+ }
286
+
287
+ if (nodeId && previous.nodeId && nodeId !== previous.nodeId) {
288
+ return {
289
+ ok: false,
290
+ error: 'host-target-node-mismatch',
291
+ active: true,
292
+ hostTarget: serializeHostTarget(previous)
293
+ };
294
+ }
295
+
296
+ hostTarget = null;
297
+ emitRemoteEvent('RemoteHostTargetCleared', null, {
298
+ hostTarget: {
299
+ ...serializeHostTarget(previous),
300
+ active: false
301
+ }
302
+ });
303
+ return {
304
+ ok: true,
305
+ active: false,
306
+ hostTarget: serializeHostTarget(null)
307
+ };
308
+ }
309
+
310
+ if (!nodeId) {
311
+ return {
312
+ ok: false,
313
+ error: 'missing-node-id',
314
+ active: false,
315
+ hostTarget: serializeHostTarget()
316
+ };
317
+ }
318
+
319
+ const now = new Date();
320
+ const leaseMs = clampNumber(options.leaseMs, 5000, 10 * 60 * 1000, DEFAULT_HOST_TARGET_LEASE_MS);
321
+ const previous = getActiveHostTarget();
322
+ const activeSameNode = previous?.nodeId === nodeId;
323
+ hostTarget = {
324
+ nodeId,
325
+ leaseId: activeSameNode && previous?.leaseId ? previous.leaseId : crypto.randomUUID(),
326
+ endpoint: getAgentEndpoint(),
327
+ activatedAt: activeSameNode && previous?.activatedAt ? previous.activatedAt : now.toISOString(),
328
+ updatedAt: now.toISOString(),
329
+ expiresAt: new Date(now.getTime() + leaseMs).toISOString()
330
+ };
331
+
332
+ emitRemoteEvent('RemoteHostTargetSet', null, {
333
+ hostTarget: serializeHostTarget(hostTarget),
334
+ replacedNodeId: previous && previous.nodeId !== nodeId ? previous.nodeId : ''
335
+ });
336
+ return {
337
+ ok: true,
338
+ active: true,
339
+ hostTarget: serializeHostTarget(hostTarget)
340
+ };
341
+ }
220
342
 
221
343
  function getStatus({ includeSecrets = false } = {}) {
222
344
  const connectedDevices = [...devices.values()].filter(device => device.connected).length;
345
+ const activeHostTarget = serializeHostTarget();
223
346
  return {
224
347
  enabled,
225
348
  started,
@@ -229,14 +352,23 @@ export function createRemoteHub(options = {}) {
229
352
  protocolVersion: REMOTE_PROTOCOL_VERSION,
230
353
  heartbeatMs,
231
354
  taskTimeoutMs,
355
+ managerPackage,
356
+ managerVersion,
232
357
  agentPackage: '@mindexec/remote',
233
- agentEndpoint: `${host}:${boundPort || requestedPort}`,
358
+ agentEndpoint: getAgentEndpoint(),
234
359
  pairToken: includeSecrets ? pairToken : undefined,
235
360
  pairTokenPreview: maskToken(pairToken),
236
361
  deviceCount: devices.size,
237
362
  connectedDeviceCount: connectedDevices,
238
363
  canvasDeviceListMode: 'all-devices',
239
364
  canvasPagination: 'none',
365
+ hostTargetActive: activeHostTarget.active,
366
+ hostTargetNodeId: activeHostTarget.nodeId,
367
+ hostTargetLeaseId: activeHostTarget.leaseId,
368
+ hostTargetEndpoint: activeHostTarget.endpoint,
369
+ hostTargetActivatedAt: activeHostTarget.activatedAt,
370
+ hostTargetUpdatedAt: activeHostTarget.updatedAt,
371
+ hostTargetExpiresAt: activeHostTarget.expiresAt,
240
372
  externalExposure: host === '0.0.0.0' || host === '::',
241
373
  lastError
242
374
  };
@@ -1686,6 +1818,7 @@ export function createRemoteHub(options = {}) {
1686
1818
  sendCommand,
1687
1819
  requestAgentTask,
1688
1820
  requestAgentTaskBatch,
1821
+ setHostTarget,
1689
1822
  requestThumbnail,
1690
1823
  startLiveStream,
1691
1824
  stopLiveStream,
@@ -416,6 +416,9 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
416
416
  const connected = devices.filter(device => device.Connected).length;
417
417
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
418
418
  const pairToken = hubStatus.pairToken || 'render-smoke-token';
419
+ const hostTargetState = hubStatus.hostTargetActive
420
+ ? (hubStatus.hostTargetNodeId === 'remote-fleet-render-smoke' ? 'hosting' : 'other-monitor')
421
+ : 'inactive';
419
422
  return {
420
423
  id: 'remote-fleet-render-smoke',
421
424
  contentType: 'memo',
@@ -426,12 +429,21 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
426
429
  RemoteFleetLastRefreshAtUtc: new Date().toISOString(),
427
430
  RemoteFleetHubStatus: 'online',
428
431
  RemoteFleetHubEndpoint: endpoint,
432
+ RemoteFleetManagerPackage: hubStatus.managerPackage || '@mindexec/cli',
433
+ RemoteFleetManagerVersion: hubStatus.managerVersion || 'render-smoke-manager',
429
434
  RemoteFleetAgentPackage: '@mindexec/remote',
430
435
  RemoteFleetPairTokenPreview: '<pair-token>',
431
436
  RemoteFleetConnectCommand: `npx @mindexec/remote connect --manager ${endpoint} --pair ${pairToken}`,
432
437
  RemoteFleetDeviceCount: String(devices.length),
433
438
  RemoteFleetConnectedDeviceCount: String(connected),
434
439
  RemoteFleetCanvasPagination: 'none',
440
+ RemoteFleetHostTargetState: hostTargetState,
441
+ RemoteFleetHostTargetNodeId: hubStatus.hostTargetNodeId || '',
442
+ RemoteFleetHostTargetLeaseId: hubStatus.hostTargetLeaseId || '',
443
+ RemoteFleetHostTargetEndpoint: hubStatus.hostTargetEndpoint || endpoint,
444
+ RemoteFleetHostTargetActivatedAtUtc: hubStatus.hostTargetActivatedAt || '',
445
+ RemoteFleetHostTargetUpdatedAtUtc: hubStatus.hostTargetUpdatedAt || '',
446
+ RemoteFleetHostTargetExpiresAtUtc: hubStatus.hostTargetExpiresAt || '',
435
447
  RemoteFleetDevicesJson: JSON.stringify(devices),
436
448
  RemoteFleetLatestTaskBatchJson: latestTaskBatch ? JSON.stringify(latestTaskBatch) : '',
437
449
  RemoteFleetRecentTaskBatchesJson: JSON.stringify(recentTaskBatches),
@@ -452,6 +464,8 @@ function buildDeviceNode(device, hubStatus) {
452
464
  RemoteFleetLastRefreshAtUtc: new Date().toISOString(),
453
465
  RemoteFleetHubStatus: 'online',
454
466
  RemoteFleetHubEndpoint: endpoint,
467
+ RemoteFleetManagerPackage: hubStatus.managerPackage || '@mindexec/cli',
468
+ RemoteFleetManagerVersion: hubStatus.managerVersion || 'render-smoke-manager',
455
469
  RemoteFleetAgentPackage: '@mindexec/remote',
456
470
  RemoteFleetPinnedDeviceId: device.DeviceId,
457
471
  RemoteFleetPinnedDeviceJson: JSON.stringify(device),
@@ -495,6 +509,8 @@ function createThreeStub() {
495
509
  }
496
510
 
497
511
  const hub = createRemoteHub({
512
+ managerPackage: '@mindexec/cli',
513
+ managerVersion: 'render-smoke-manager',
498
514
  env: {
499
515
  MINDEXEC_REMOTE_HUB: '1',
500
516
  REMOTE_HUB_HOST: '127.0.0.1',
@@ -639,6 +655,13 @@ try {
639
655
  };
640
656
  }
641
657
 
658
+ if (methodName === 'SetRemoteFleetHostFromJs') {
659
+ return {
660
+ success: true,
661
+ active: args[1] === true
662
+ };
663
+ }
664
+
642
665
  return { success: true };
643
666
  }
644
667
  };
@@ -654,15 +677,69 @@ try {
654
677
  let cards = bodyView.querySelectorAll('article[data-device-id]');
655
678
  assert.equal(cards.length, SYNTHETIC_COUNT);
656
679
  assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${SYNTHETIC_COUNT}/${SYNTHETIC_COUNT}`);
657
- assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').length, SYNTHETIC_COUNT);
658
- assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').length, connectedCount);
659
- assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').length, devices.filter(device => device.Connected && device.ThumbnailEnabled).length);
680
+ assert.ok(bodyView.querySelector('[data-remote-fleet-device-grid="true"]'));
681
+ const initialDetailPanel = bodyView.querySelector('[data-remote-fleet-detail-panel="true"]');
682
+ assert.equal(initialDetailPanel?.dataset.deviceId, focusedDevice.DeviceId);
683
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').length, 1);
684
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').length, 0);
685
+ assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').length, focusedDevice.Connected && focusedDevice.ThumbnailEnabled ? 1 : 0);
686
+ assert.ok(initialDetailPanel?.textContent.includes('Seen'));
687
+ assert.ok(initialDetailPanel?.textContent.includes('Uptime'));
688
+ assert.ok(initialDetailPanel?.textContent.includes('Mem'));
689
+ assert.ok(initialDetailPanel?.textContent.includes('Load'));
690
+ assert.ok(cards[0]?.querySelector('[data-remote-fleet-device-preview="tile"]'));
691
+ assert.equal(/\b(Seen|Uptime|Mem|Load)\b/.test(cards[0]?.textContent || ''), false);
692
+ const alternateDevice = devices.find(device => device.DeviceId !== focusedDevice.DeviceId);
693
+ assert.ok(alternateDevice);
694
+ const alternateCard = Array.from(cards).find(card => card.dataset.deviceId === alternateDevice.DeviceId);
695
+ assert.ok(alternateCard);
696
+ alternateCard.dispatchEvent({ type: 'click' });
697
+ assert.equal(bodyView.dataset.remoteFleetSelectedDeviceId, alternateDevice.DeviceId);
698
+ assert.equal(bodyView.querySelector('[data-remote-fleet-detail-panel="true"]')?.dataset.deviceId, alternateDevice.DeviceId);
660
699
  const livePanel = bodyView.querySelector('[data-remote-fleet-live-panel="true"]');
661
700
  assert.equal(livePanel?.dataset.deviceId, focusedDevice.DeviceId);
662
- assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-visible"]')?.disabled, false);
701
+ assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-visible"]'), null);
702
+ assert.equal(bodyView.querySelector('[data-remote-fleet-action="task-connected"]'), null);
703
+ assert.equal(bodyView.querySelector('[data-remote-fleet-task-input="true"]'), null);
704
+ assert.equal(bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]'), null);
663
705
  assert.ok(bodyView.textContent.includes('all devices, no paging'));
706
+ assert.equal(bodyView.querySelector('[data-remote-fleet-host-state="true"]')?.textContent.includes('Host: Inactive'), true);
707
+ const initialHostButton = bodyView.querySelector('[data-remote-fleet-action="set-host"]');
708
+ assert.equal(initialHostButton?.textContent, 'Set Host');
709
+ initialHostButton.dispatchEvent({ type: 'click' });
710
+ await wait();
711
+ const setHostCall = dotNetCalls.find(call => call.methodName === 'SetRemoteFleetHostFromJs');
712
+ assert.ok(setHostCall);
713
+ assert.equal(setHostCall.args[0], 'remote-fleet-render-smoke');
714
+ assert.equal(setHostCall.args[1], true);
715
+
716
+ hub.setHostTarget({
717
+ nodeId: 'remote-fleet-render-smoke',
718
+ enabled: true,
719
+ leaseMs: 30000
720
+ });
721
+ manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
722
+ assert.equal(bodyView.querySelector('[data-remote-fleet-host-state="true"]')?.textContent.includes('Host: Active'), true);
723
+ assert.equal(bodyView.querySelector('[data-remote-fleet-action="set-host"]')?.textContent, 'Hosting');
724
+ const stopHostButton = bodyView.querySelector('[data-remote-fleet-action="stop-host"]');
725
+ assert.ok(stopHostButton);
726
+ const stopHostStart = dotNetCalls.length;
727
+ stopHostButton.dispatchEvent({ type: 'click' });
728
+ await wait();
729
+ const stopHostCall = dotNetCalls
730
+ .slice(stopHostStart)
731
+ .find(call => call.methodName === 'SetRemoteFleetHostFromJs');
732
+ assert.ok(stopHostCall);
733
+ assert.equal(stopHostCall.args[0], 'remote-fleet-render-smoke');
734
+ assert.equal(stopHostCall.args[1], false);
735
+
736
+ assert.match(bodyView.querySelector('[data-remote-fleet-manager-version="true"]')?.textContent || '', /^@mindexec\/cli render-smoke-manager - 127\.0\.0\.1:\d+$/);
664
737
  assert.ok(livePanel?.textContent.includes('RemoteFast 20 fps'), livePanel?.textContent || bodyView.textContent);
665
- assert.ok(bodyView.querySelector('code')?.textContent.includes('npx @mindexec/remote connect'));
738
+ assert.equal(bodyView.querySelector('code'), null);
739
+ assert.equal(bodyView.textContent.includes('npx @mindexec/remote connect'), false);
740
+ assert.equal(bodyView.querySelector('[data-remote-fleet-action="copy-command"]'), null);
741
+ assert.equal(bodyView.querySelector('[data-remote-fleet-action="refresh"]'), null);
742
+ assert.equal(bodyView.querySelector('[data-remote-fleet-auto-toggle="true"]'), null);
666
743
  assert.ok(bodyView.textContent.includes('synthetic-render-ai'));
667
744
  assert.ok(bodyView.textContent.includes('Task timed out waiting for the agent response.'));
668
745
  const batchSummary = bodyView.querySelector('[data-remote-fleet-task-batch="true"]');
@@ -712,63 +789,20 @@ try {
712
789
  assert.ok(bodyView.querySelectorAll('[data-remote-fleet-group-header="true"]').length >= 2);
713
790
  assert.equal(bodyView.querySelectorAll('article[data-device-id]').length, SYNTHETIC_COUNT);
714
791
 
715
- const taskInput = bodyView.querySelector('[data-remote-fleet-task-input="true"]');
716
- const sendVisibleButton = bodyView.querySelector('[data-remote-fleet-action="task-visible"]');
717
- assert.ok(taskInput);
718
- assert.ok(sendVisibleButton);
719
- taskInput.value = 'Dispatch smoke partial failure';
720
- sendVisibleButton.dispatchEvent({ type: 'click' });
721
- await wait();
722
- const batchCall = dotNetCalls.find(call => call.methodName === 'DispatchRemoteFleetTaskBatchFromJs');
723
- assert.ok(batchCall);
724
- const batchTargetIds = Array.isArray(batchCall.args[1]) ? batchCall.args[1] : [];
725
- assert.equal(batchTargetIds.length, connectedCount);
726
792
  const feedback = bodyView.querySelector('[data-remote-fleet-task-feedback="true"]');
727
793
  assert.ok(feedback);
728
- assert.equal(feedback.style.display, 'block');
729
- assert.match(feedback.textContent, new RegExp(`Queued ${connectedCount - 1}/${connectedCount} visible remote task\\(s\\); 1 failed`));
730
- assert.match(feedback.textContent, /device-ai-assist-unavailable/);
794
+ assert.equal(feedback.style.display, 'none');
731
795
 
732
796
  const activeSelects = bodyView.querySelectorAll('select');
733
797
  assert.equal(activeSelects.length, 4);
734
798
  const [aiFilterSelect] = activeSelects;
735
- const aiToggle = bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]');
736
- assert.ok(aiToggle);
799
+ assert.equal(bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]'), null);
737
800
  aiFilterSelect.value = 'ai';
738
801
  aiFilterSelect.dispatchEvent({ type: 'change' });
739
- aiToggle.checked = true;
740
- aiToggle.dispatchEvent({ type: 'change' });
741
802
  assert.equal(bodyView.querySelector('[data-remote-fleet-match-count="true"]')?.textContent, `${aiCount}/${SYNTHETIC_COUNT}`);
742
- assert.equal(sendVisibleButton.disabled, false);
743
- taskInput.value = 'Dispatch visible AI smoke';
744
- const aiDispatchCallStart = dotNetCalls.length;
745
- sendVisibleButton.dispatchEvent({ type: 'click' });
746
- await wait();
747
- const aiBatchCall = dotNetCalls
748
- .slice(aiDispatchCallStart)
749
- .find(call => call.methodName === 'DispatchRemoteFleetTaskBatchFromJs');
750
- assert.ok(aiBatchCall);
751
- const aiTargetIds = Array.isArray(aiBatchCall.args[1]) ? aiBatchCall.args[1] : [];
752
- const expectedAiTargetIds = devices
753
- .filter(device => device.Connected && device.AiAssistEnabled)
754
- .map(device => device.DeviceId)
755
- .sort();
756
- assert.deepEqual([...aiTargetIds].sort(), expectedAiTargetIds);
757
- assert.equal(aiBatchCall.args[3], true);
758
-
759
- const sendConnectedButton = bodyView.querySelector('[data-remote-fleet-action="task-connected"]');
760
- assert.ok(sendConnectedButton);
761
- taskInput.value = 'Dispatch all AI smoke';
762
- const allAiDispatchCallStart = dotNetCalls.length;
763
- sendConnectedButton.dispatchEvent({ type: 'click' });
764
- await wait();
765
- const allAiCall = dotNetCalls
766
- .slice(allAiDispatchCallStart)
767
- .find(call => call.methodName === 'DispatchRemoteFleetTaskFromJs');
768
- assert.ok(allAiCall);
769
- assert.equal(allAiCall.args[1], '');
770
- assert.equal(allAiCall.args[3], true);
771
- assert.match(feedback.textContent, new RegExp(`Queued ${aiCount}/${aiCount} AI task\\(s\\)`));
803
+ assert.equal(dotNetCalls.some(call =>
804
+ call.methodName === 'DispatchRemoteFleetTaskBatchFromJs'
805
+ || call.methodName === 'DispatchRemoteFleetTaskFromJs'), false);
772
806
 
773
807
  const deviceBody = document.createElement('div');
774
808
  document.body.appendChild(deviceBody);