@mindexec/cli 0.2.5 → 0.2.7

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 (22) hide show
  1. package/README.md +12 -0
  2. package/package.json +6 -6
  3. package/remote-hub.js +167 -0
  4. package/scripts/remote-hub-smoke.mjs +54 -0
  5. package/server.js +28 -0
  6. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +722 -78
  7. package/wwwroot/_framework/MindExecution.Core.pnw79cgqjx.dll +0 -0
  8. package/wwwroot/_framework/{MindExecution.Kernel.9wfplilp5l.dll → MindExecution.Kernel.dt3w864bqn.dll} +0 -0
  9. package/wwwroot/_framework/{MindExecution.Plugins.Admin.sb1vkmct0w.dll → MindExecution.Plugins.Admin.z93cu32xru.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Business.zr7rkofx44.dll → MindExecution.Plugins.Business.b6da8sg85t.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Concept.g6wd36v92i.dll → MindExecution.Plugins.Concept.mjooiqft9j.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Directory.bb5flwt0u7.dll → MindExecution.Plugins.Directory.rjod6rdmly.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.me8v9fpgwc.dll → MindExecution.Plugins.PlanMaster.1dcrzhsegj.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.l811fqx9e0.dll → MindExecution.Plugins.YouTube.k75qxhbpp8.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Shared.oseamdg577.dll → MindExecution.Shared.y3eqxd3mvo.dll} +0 -0
  16. package/wwwroot/_framework/MindExecution.Web.wou9x6mn2f.dll +0 -0
  17. package/wwwroot/_framework/blazor.boot.json +21 -21
  18. package/wwwroot/index.html +1 -1
  19. package/wwwroot/service-worker-assets.js +24 -24
  20. package/wwwroot/service-worker.js +1 -1
  21. package/wwwroot/_framework/MindExecution.Core.27f2blpou6.dll +0 -0
  22. package/wwwroot/_framework/MindExecution.Web.96r3nnp9is.dll +0 -0
package/README.md CHANGED
@@ -83,6 +83,18 @@ curl -X POST -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remot
83
83
  curl -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remote/devices/<device-id>/thumbnail
84
84
  ```
85
85
 
86
+ Start, read, and stop the focused view-only RemoteFast live stream:
87
+
88
+ ```bash
89
+ curl -X POST -H "X-Bridge-Token: <bridge-token>" -H "Content-Type: application/json" \
90
+ -d "{\"fps\":12,\"maxWidth\":960,\"maxHeight\":540,\"quality\":60}" \
91
+ http://127.0.0.1:5147/api/remote/devices/<device-id>/live/start
92
+
93
+ curl -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remote/devices/<device-id>/live/frame
94
+
95
+ curl -X POST -H "X-Bridge-Token: <bridge-token>" http://127.0.0.1:5147/api/remote/devices/<device-id>/live/stop
96
+ ```
97
+
86
98
  Queue a safe task-only instruction for one device or all connected devices:
87
99
 
88
100
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -8,9 +8,9 @@
8
8
  "start-bridge.bat",
9
9
  "start-bridge.sh",
10
10
  "launch-bridge.cjs",
11
- "server.js",
12
- "remote-hub.js",
13
- "codex-runtime.js",
11
+ "server.js",
12
+ "remote-hub.js",
13
+ "codex-runtime.js",
14
14
  "port-guard.cjs",
15
15
  "wwwroot/",
16
16
  "scripts/",
@@ -20,8 +20,8 @@
20
20
  "scripts": {
21
21
  "start": "node launch-bridge.cjs",
22
22
  "dev": "node launch-bridge.cjs --watch",
23
- "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/remote-hub-smoke.mjs",
24
- "test:remote": "node scripts/remote-hub-smoke.mjs",
23
+ "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/remote-hub-smoke.mjs",
24
+ "test:remote": "node scripts/remote-hub-smoke.mjs",
25
25
  "pack:dry": "npm pack --dry-run",
26
26
  "setup:grammars": "node scripts/setup-tree-sitter-grammars.mjs",
27
27
  "postinstall": "npm run setup:grammars"
package/remote-hub.js CHANGED
@@ -7,6 +7,7 @@ const DEFAULT_REMOTE_HUB_HOST = '127.0.0.1';
7
7
  const DEFAULT_HEARTBEAT_MS = 5000;
8
8
  const MAX_LINE_CHARS = 4 * 1024 * 1024;
9
9
  const MAX_THUMBNAIL_BASE64_CHARS = 3 * 1024 * 1024;
10
+ const MAX_STREAM_BASE64_CHARS = 3 * 1024 * 1024;
10
11
  const MAX_AGENT_TASK_CHARS = 4000;
11
12
  const MAX_AGENT_TASK_RESULT_CHARS = 3000;
12
13
  const RECENT_TASK_LIMIT = 12;
@@ -118,6 +119,8 @@ function serializeDevice(device) {
118
119
  remoteAddress: device.remoteAddress,
119
120
  remotePort: device.remotePort,
120
121
  latestThumbnail: device.latestThumbnail ? { ...device.latestThumbnail } : null,
122
+ latestLiveFrame: device.latestLiveFrame ? { ...device.latestLiveFrame } : null,
123
+ activeLiveStream: device.activeLiveStream ? { ...device.activeLiveStream } : null,
121
124
  latestTask: device.latestTask ? { ...device.latestTask } : null,
122
125
  recentTasks: Array.isArray(device.recentTasks)
123
126
  ? device.recentTasks.map(task => ({ ...task }))
@@ -249,6 +252,8 @@ export function createRemoteHub(options = {}) {
249
252
  remotePort: socket.remotePort || 0,
250
253
  status: {},
251
254
  latestThumbnail: null,
255
+ latestLiveFrame: null,
256
+ activeLiveStream: null,
252
257
  latestTask: null,
253
258
  recentTasks: [],
254
259
  pendingTaskCommands: new Map(),
@@ -259,6 +264,10 @@ export function createRemoteHub(options = {}) {
259
264
  commandResultsReceived: 0,
260
265
  thumbnailFramesReceived: 0,
261
266
  thumbnailFramesDropped: 0,
267
+ liveFramesReceived: 0,
268
+ liveFramesDropped: 0,
269
+ liveStreamsStarted: 0,
270
+ liveStreamsStopped: 0,
262
271
  tasksQueued: 0,
263
272
  taskResultsReceived: 0,
264
273
  taskResultsFailed: 0
@@ -297,6 +306,11 @@ export function createRemoteHub(options = {}) {
297
306
  device.socket = null;
298
307
  device.disconnectedAt = new Date().toISOString();
299
308
  device.lastDisconnectReason = reason;
309
+ if (device.activeLiveStream) {
310
+ device.activeLiveStream.active = false;
311
+ device.activeLiveStream.stoppedAt = device.disconnectedAt;
312
+ device.activeLiveStream.stopReason = reason;
313
+ }
300
314
  logWarn('remote', `device disconnected ${device.deviceName} (${deviceId}): ${reason}`);
301
315
  emitRemoteEvent('RemoteDeviceDisconnected', device, { reason });
302
316
  }
@@ -475,6 +489,62 @@ export function createRemoteHub(options = {}) {
475
489
  });
476
490
  break;
477
491
  }
492
+ case 'stream.frame': {
493
+ const frameData = safeString(message.data, MAX_STREAM_BASE64_CHARS + 1);
494
+ const frameSeq = Number(message.frameSeq);
495
+ const streamId = safeString(message.streamId, 128) || 'live';
496
+ if (!device.activeLiveStream?.active || device.activeLiveStream.streamId !== streamId) {
497
+ device.counters.liveFramesDropped += 1;
498
+ emitRemoteEvent('RemoteFrameDropped', device, {
499
+ reason: 'stale-live-stream-frame',
500
+ streamId,
501
+ frameSeq: Number.isFinite(frameSeq) ? frameSeq : null
502
+ });
503
+ break;
504
+ }
505
+
506
+ if (!frameData || frameData.length > MAX_STREAM_BASE64_CHARS || !Number.isFinite(frameSeq)) {
507
+ device.counters.liveFramesDropped += 1;
508
+ emitRemoteEvent('RemoteFrameDropped', device, {
509
+ reason: 'invalid-live-frame',
510
+ streamId,
511
+ frameSeq: Number.isFinite(frameSeq) ? frameSeq : null
512
+ });
513
+ break;
514
+ }
515
+
516
+ const mimeType = safeString(message.mimeType || message.format || 'image/jpeg', 80) || 'image/jpeg';
517
+ const capturedAt = safeString(message.capturedAt, 80) || device.lastSeenAt;
518
+ device.latestLiveFrame = {
519
+ streamId,
520
+ frameSeq,
521
+ commandId: safeString(message.commandId, 128),
522
+ width: Number.isFinite(Number(message.width)) ? Number(message.width) : 0,
523
+ height: Number.isFinite(Number(message.height)) ? Number(message.height) : 0,
524
+ mimeType,
525
+ format: mimeType,
526
+ mode: safeString(message.mode || device.activeLiveStream.mode || 'remote-fast', 80),
527
+ fps: Number.isFinite(Number(message.fps)) ? Number(message.fps) : device.activeLiveStream.fps,
528
+ capturedAt,
529
+ receivedAt: device.lastSeenAt,
530
+ byteLength: Math.floor(frameData.length * 3 / 4),
531
+ dataUrl: frameData.startsWith('data:')
532
+ ? frameData
533
+ : `data:${mimeType};base64,${frameData}`
534
+ };
535
+ device.activeLiveStream.lastFrameAt = device.lastSeenAt;
536
+ device.activeLiveStream.lastFrameSeq = frameSeq;
537
+ device.activeLiveStream.framesReceived = (device.activeLiveStream.framesReceived || 0) + 1;
538
+ device.counters.liveFramesReceived += 1;
539
+ emitRemoteEvent('RemoteFrameReceived', device, {
540
+ streamId,
541
+ frameSeq,
542
+ width: device.latestLiveFrame.width,
543
+ height: device.latestLiveFrame.height,
544
+ mode: device.latestLiveFrame.mode
545
+ });
546
+ break;
547
+ }
478
548
  default:
479
549
  emitRemoteEvent('RemoteAgentMessageIgnored', device, {
480
550
  messageType: safeString(message.type, 80)
@@ -731,6 +801,100 @@ export function createRemoteHub(options = {}) {
731
801
  });
732
802
  }
733
803
 
804
+ function startLiveStream(deviceId, options = {}) {
805
+ const device = devices.get(String(deviceId || ''));
806
+ if (!device?.socket || device.socket.destroyed || !device.connected) {
807
+ return { ok: false, error: 'device-not-connected' };
808
+ }
809
+ if (!readCapabilityFlag(device.capabilities, 'liveStream')) {
810
+ return { ok: false, error: 'device-live-stream-unavailable' };
811
+ }
812
+
813
+ const now = new Date().toISOString();
814
+ const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
815
+ const fps = clampNumber(options.fps, 1, 24, 12);
816
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
817
+ const result = sendCommand(deviceId, {
818
+ command: 'stream.start',
819
+ commandId,
820
+ payload: {
821
+ streamId,
822
+ mode: safeString(options.mode, 80) || 'remote-fast',
823
+ fps,
824
+ maxWidth: clampNumber(options.maxWidth, 320, 2560, 960),
825
+ maxHeight: clampNumber(options.maxHeight, 180, 1440, 540),
826
+ quality: clampNumber(options.quality, 20, 95, 60),
827
+ requestedAt: now
828
+ }
829
+ });
830
+
831
+ if (!result.ok) {
832
+ return result;
833
+ }
834
+
835
+ device.activeLiveStream = {
836
+ streamId,
837
+ commandId,
838
+ active: true,
839
+ mode: 'remote-fast',
840
+ fps,
841
+ startedAt: now,
842
+ stoppedAt: '',
843
+ stopReason: '',
844
+ lastFrameAt: '',
845
+ lastFrameSeq: 0,
846
+ framesReceived: 0
847
+ };
848
+ device.counters.liveStreamsStarted += 1;
849
+ emitRemoteEvent('RemoteLiveStreamStarted', device, {
850
+ streamId,
851
+ commandId,
852
+ fps
853
+ });
854
+
855
+ return { ok: true, commandId, streamId, fps };
856
+ }
857
+
858
+ function stopLiveStream(deviceId, options = {}) {
859
+ const device = devices.get(String(deviceId || ''));
860
+ if (!device?.socket || device.socket.destroyed || !device.connected) {
861
+ return { ok: false, error: 'device-not-connected' };
862
+ }
863
+
864
+ const streamId = safeString(options.streamId, 128) || device.activeLiveStream?.streamId || '';
865
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
866
+ const result = sendCommand(deviceId, {
867
+ command: 'stream.stop',
868
+ commandId,
869
+ payload: {
870
+ streamId,
871
+ requestedAt: new Date().toISOString()
872
+ }
873
+ });
874
+
875
+ if (!result.ok) {
876
+ return result;
877
+ }
878
+
879
+ if (device.activeLiveStream && (!streamId || device.activeLiveStream.streamId === streamId)) {
880
+ device.activeLiveStream.active = false;
881
+ device.activeLiveStream.stoppedAt = new Date().toISOString();
882
+ device.activeLiveStream.stopReason = 'manager-request';
883
+ }
884
+ device.counters.liveStreamsStopped += 1;
885
+ emitRemoteEvent('RemoteLiveStreamStopped', device, {
886
+ streamId,
887
+ commandId
888
+ });
889
+
890
+ return { ok: true, commandId, streamId };
891
+ }
892
+
893
+ function getDeviceLiveFrame(deviceId) {
894
+ const device = devices.get(String(deviceId || ''));
895
+ return device?.latestLiveFrame ? { ...device.latestLiveFrame } : null;
896
+ }
897
+
734
898
  function getDeviceThumbnail(deviceId) {
735
899
  const device = devices.get(String(deviceId || ''));
736
900
  return device?.latestThumbnail ? { ...device.latestThumbnail } : null;
@@ -745,6 +909,9 @@ export function createRemoteHub(options = {}) {
745
909
  sendCommand,
746
910
  requestAgentTask,
747
911
  requestThumbnail,
912
+ startLiveStream,
913
+ stopLiveStream,
914
+ getDeviceLiveFrame,
748
915
  getDeviceThumbnail,
749
916
  getPairToken: () => pairToken
750
917
  };
@@ -61,6 +61,7 @@ try {
61
61
  status: true,
62
62
  thumbnail: false,
63
63
  control: false,
64
+ liveStream: true,
64
65
  computerAgent: true,
65
66
  taskDispatch: true,
66
67
  aiAssist: true,
@@ -112,6 +113,59 @@ try {
112
113
  assert.equal(thumbnailDevice.latestThumbnail.streamId, 'smoke-thumb');
113
114
  assert.equal(thumbnailDevice.counters.thumbnailFramesReceived, 1);
114
115
 
116
+ const liveCommand = hub.startLiveStream('smoke-device', {
117
+ streamId: 'smoke-live',
118
+ fps: 5,
119
+ maxWidth: 320,
120
+ maxHeight: 180,
121
+ quality: 50
122
+ });
123
+ assert.equal(liveCommand.ok, true);
124
+ writeJsonLine(socket, {
125
+ type: 'stream.frame',
126
+ commandId: liveCommand.commandId,
127
+ streamId: 'smoke-live',
128
+ frameSeq: 2,
129
+ width: 2,
130
+ height: 1,
131
+ mimeType: 'image/png',
132
+ mode: 'remote-fast',
133
+ fps: 5,
134
+ capturedAt: new Date().toISOString(),
135
+ data: 'iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC'
136
+ });
137
+
138
+ const liveDevice = await waitFor(() => {
139
+ const current = hub.listDevices();
140
+ return current[0]?.latestLiveFrame?.streamId === 'smoke-live' ? current[0] : null;
141
+ });
142
+ assert.equal(liveDevice.activeLiveStream.active, true);
143
+ assert.equal(liveDevice.latestLiveFrame.mode, 'remote-fast');
144
+ assert.equal(liveDevice.counters.liveFramesReceived, 1);
145
+
146
+ const staleFrameBefore = liveDevice.counters.liveFramesDropped;
147
+ writeJsonLine(socket, {
148
+ type: 'stream.frame',
149
+ streamId: 'stale-live',
150
+ frameSeq: 3,
151
+ width: 2,
152
+ height: 1,
153
+ mimeType: 'image/png',
154
+ capturedAt: new Date().toISOString(),
155
+ data: 'iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mP8z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC'
156
+ });
157
+ const staleDropDevice = await waitFor(() => {
158
+ const current = hub.listDevices();
159
+ return current[0]?.counters?.liveFramesDropped > staleFrameBefore ? current[0] : null;
160
+ });
161
+ assert.equal(staleDropDevice.latestLiveFrame.streamId, 'smoke-live');
162
+
163
+ const stopLiveCommand = hub.stopLiveStream('smoke-device', {
164
+ streamId: 'smoke-live'
165
+ });
166
+ assert.equal(stopLiveCommand.ok, true);
167
+ assert.equal(hub.listDevices()[0].activeLiveStream.active, false);
168
+
115
169
  const taskCommand = hub.requestAgentTask('smoke-device', {
116
170
  instruction: 'Summarize current desktop status for the manager.',
117
171
  title: 'Smoke task'
package/server.js CHANGED
@@ -7048,6 +7048,34 @@ app.post('/api/remote/devices/:deviceId/thumbnail/request', (req, res) => {
7048
7048
  }));
7049
7049
  });
7050
7050
 
7051
+ app.get('/api/remote/devices/:deviceId/live/frame', (req, res) => {
7052
+ res.setHeader('Cache-Control', 'no-store');
7053
+ const frame = remoteHub.getDeviceLiveFrame(req.params.deviceId);
7054
+ if (!frame) {
7055
+ res.status(404).json({ ok: false, error: 'live-frame-not-available' });
7056
+ return;
7057
+ }
7058
+
7059
+ res.json({ ok: true, frame });
7060
+ });
7061
+
7062
+ app.post('/api/remote/devices/:deviceId/live/start', (req, res) => {
7063
+ res.json(remoteHub.startLiveStream(req.params.deviceId, {
7064
+ streamId: req.body?.streamId,
7065
+ mode: req.body?.mode,
7066
+ fps: req.body?.fps,
7067
+ maxWidth: req.body?.maxWidth,
7068
+ maxHeight: req.body?.maxHeight,
7069
+ quality: req.body?.quality
7070
+ }));
7071
+ });
7072
+
7073
+ app.post('/api/remote/devices/:deviceId/live/stop', (req, res) => {
7074
+ res.json(remoteHub.stopLiveStream(req.params.deviceId, {
7075
+ streamId: req.body?.streamId
7076
+ }));
7077
+ });
7078
+
7051
7079
  app.get('/api/codex/capabilities', async (req, res) => {
7052
7080
  try {
7053
7081
  res.json(await getCodexRuntime().getCapabilities());