@mindexec/cli 0.2.22 → 0.2.23

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.22",
3
+ "version": "0.2.23",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -7,7 +7,7 @@ import vm from 'node:vm';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { createRemoteHub } from '../remote-hub.js';
9
9
 
10
- const SYNTHETIC_COUNT = Number(process.env.REMOTE_FLEET_RENDER_SMOKE_COUNT || 250);
10
+ const SYNTHETIC_COUNT = Number(process.env.REMOTE_FLEET_RENDER_SMOKE_COUNT || 500);
11
11
 
12
12
  function dataAttributeToDatasetKey(name) {
13
13
  return String(name || '').replace(/^data-/, '').replace(/-([a-z])/g, (_, char) => char.toUpperCase());
@@ -627,6 +627,18 @@ try {
627
627
  };
628
628
  }
629
629
 
630
+ if (methodName === 'DispatchRemoteFleetTaskFromJs') {
631
+ const useAiAssist = args[3] === true;
632
+ const total = useAiAssist ? aiCount : connectedCount;
633
+ return {
634
+ success: true,
635
+ total,
636
+ queued: total,
637
+ failed: 0,
638
+ approvalLevel: useAiAssist ? 'ai-assist' : 'task-only'
639
+ };
640
+ }
641
+
630
642
  return { success: true };
631
643
  }
632
644
  };
@@ -717,6 +729,47 @@ try {
717
729
  assert.match(feedback.textContent, new RegExp(`Queued ${connectedCount - 1}/${connectedCount} visible remote task\\(s\\); 1 failed`));
718
730
  assert.match(feedback.textContent, /device-ai-assist-unavailable/);
719
731
 
732
+ const activeSelects = bodyView.querySelectorAll('select');
733
+ assert.equal(activeSelects.length, 4);
734
+ const [aiFilterSelect] = activeSelects;
735
+ const aiToggle = bodyView.querySelector('[data-remote-fleet-ai-toggle="true"]');
736
+ assert.ok(aiToggle);
737
+ aiFilterSelect.value = 'ai';
738
+ aiFilterSelect.dispatchEvent({ type: 'change' });
739
+ aiToggle.checked = true;
740
+ aiToggle.dispatchEvent({ type: 'change' });
741
+ 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\\)`));
772
+
720
773
  const deviceBody = document.createElement('div');
721
774
  document.body.appendChild(deviceBody);
722
775
  manager.renderRemoteFleetDeviceForTest(deviceBody, buildDeviceNode(focusedDevice, hub.getStatus()));
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
8
8
 
9
9
  const BRIDGE_TOKEN = 'remote-http-smoke-token';
10
10
  const PAIR_TOKEN = 'remote-http-pair-token';
11
- const SYNTHETIC_COUNT = Number(process.env.REMOTE_HTTP_SMOKE_COUNT || 250);
11
+ const SYNTHETIC_COUNT = Number(process.env.REMOTE_HTTP_SMOKE_COUNT || 500);
12
12
 
13
13
  function wait(ms) {
14
14
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -148,9 +148,9 @@ try {
148
148
 
149
149
  const connectedTargets = devicesResult.payload.devices
150
150
  .filter(device => device.connected && device.capabilities?.taskDispatch)
151
- .slice(0, 200)
151
+ .slice(0, 500)
152
152
  .map(device => device.deviceId);
153
- assert.ok(connectedTargets.length >= 100);
153
+ assert.ok(connectedTargets.length >= 400);
154
154
 
155
155
  const batch = await fetchJson(`${baseUrl}/api/remote/tasks`, {
156
156
  method: 'POST',
@@ -171,6 +171,32 @@ try {
171
171
  assert.equal(batch.payload?.batch?.failed, 0);
172
172
  assert.equal(batch.payload?.batch?.status, 'completed');
173
173
 
174
+ const aiTargets = devicesResult.payload.devices
175
+ .filter(device => device.connected && device.capabilities?.taskDispatch && device.capabilities?.aiAssist)
176
+ .map(device => device.deviceId);
177
+ assert.ok(aiTargets.length >= 200);
178
+ const allAiBatch = await fetchJson(`${baseUrl}/api/remote/tasks`, {
179
+ method: 'POST',
180
+ token: BRIDGE_TOKEN,
181
+ body: JSON.stringify({
182
+ allConnected: true,
183
+ title: 'HTTP smoke all AI task batch',
184
+ instruction: 'HTTP smoke AI batch: summarize fleet condition only on AI-capable agents.',
185
+ approvalLevel: 'ai-assist'
186
+ })
187
+ });
188
+ assert.equal(allAiBatch.ok, true, JSON.stringify(allAiBatch.payload));
189
+ assert.equal(allAiBatch.payload?.ok, true);
190
+ assert.equal(allAiBatch.payload?.approvalLevel, 'ai-assist');
191
+ assert.equal(allAiBatch.payload?.total, aiTargets.length);
192
+ assert.equal(allAiBatch.payload?.queued, aiTargets.length);
193
+ assert.equal(allAiBatch.payload?.batch?.completed, aiTargets.length);
194
+ assert.equal(allAiBatch.payload?.batch?.failed, 0);
195
+ assert.equal(allAiBatch.payload?.batch?.status, 'completed');
196
+ assert.deepEqual(
197
+ [...(allAiBatch.payload?.results || []).map(result => result.deviceId)].sort(),
198
+ [...aiTargets].sort());
199
+
174
200
  const aiTarget = devicesResult.payload.devices.find(device =>
175
201
  device.connected && device.capabilities?.taskDispatch && device.capabilities?.aiAssist);
176
202
  assert.ok(aiTarget);
@@ -3,7 +3,7 @@
3
3
  import assert from 'assert/strict';
4
4
  import { createRemoteHub } from '../remote-hub.js';
5
5
 
6
- const SYNTHETIC_COUNT = Number(process.env.REMOTE_HUB_SCALE_SMOKE_COUNT || 250);
6
+ const SYNTHETIC_COUNT = Number(process.env.REMOTE_HUB_SCALE_SMOKE_COUNT || 500);
7
7
 
8
8
  const hub = createRemoteHub({
9
9
  env: {
@@ -76,8 +76,8 @@ try {
76
76
 
77
77
  const connectedTargets = hub.listDevices()
78
78
  .filter(device => device.connected && device.capabilities?.taskDispatch)
79
- .slice(0, 200);
80
- assert.ok(connectedTargets.length >= 100);
79
+ .slice(0, 500);
80
+ assert.ok(connectedTargets.length >= 400);
81
81
  const scaleBatch = hub.requestAgentTaskBatch(connectedTargets.map(device => device.deviceId), {
82
82
  instruction: 'Synthetic scale task: report current status to the manager.',
83
83
  title: 'Scale task batch'
package/server.js CHANGED
@@ -7038,15 +7038,21 @@ app.post('/api/remote/tasks', (req, res) => {
7038
7038
  ? req.body.deviceIds.map(value => String(value || '').trim()).filter(Boolean)
7039
7039
  : [];
7040
7040
  const allConnected = req.body?.allConnected !== false;
7041
+ const approvalLevel = req.body?.approvalLevel === 'ai-assist' ? 'ai-assist' : 'task-only';
7041
7042
  const devices = remoteHub.listDevices();
7042
7043
  const targetIds = requestedDeviceIds.length > 0
7043
7044
  ? requestedDeviceIds
7044
- : (allConnected ? devices.filter(device => device.connected).map(device => device.deviceId) : []);
7045
+ : (allConnected
7046
+ ? devices
7047
+ .filter(device => device.connected)
7048
+ .filter(device => approvalLevel !== 'ai-assist' || isRemoteCapabilityEnabled(device, 'aiAssist'))
7049
+ .map(device => device.deviceId)
7050
+ : []);
7045
7051
  const uniqueTargetIds = [...new Set(targetIds)].slice(0, 500);
7046
7052
  const result = remoteHub.requestAgentTaskBatch(uniqueTargetIds, {
7047
7053
  instruction: req.body?.instruction,
7048
7054
  title: req.body?.title,
7049
- approvalLevel: req.body?.approvalLevel,
7055
+ approvalLevel,
7050
7056
  model: req.body?.model,
7051
7057
  batchId: req.body?.batchId
7052
7058
  });
@@ -7055,11 +7061,24 @@ app.post('/api/remote/tasks', (req, res) => {
7055
7061
  ok: result.ok === true,
7056
7062
  total: uniqueTargetIds.length,
7057
7063
  queued: result.queued || 0,
7058
- approvalLevel: req.body?.approvalLevel === 'ai-assist' ? 'ai-assist' : 'task-only',
7064
+ approvalLevel,
7059
7065
  error: result.ok === true ? undefined : (uniqueTargetIds.length === 0 ? 'no-target-devices' : (result.error || 'no-task-queued'))
7060
7066
  });
7061
7067
  });
7062
7068
 
7069
+ function isRemoteCapabilityEnabled(device, key) {
7070
+ const value = device?.capabilities?.[key];
7071
+ if (typeof value === 'boolean') {
7072
+ return value;
7073
+ }
7074
+
7075
+ if (typeof value === 'number') {
7076
+ return value !== 0;
7077
+ }
7078
+
7079
+ return /^(1|true|yes|on)$/i.test(String(value || '').trim());
7080
+ }
7081
+
7063
7082
  app.get('/api/remote/devices/:deviceId/thumbnail', (req, res) => {
7064
7083
  res.setHeader('Cache-Control', 'no-store');
7065
7084
  const thumbnail = remoteHub.getDeviceThumbnail(req.params.deviceId);