@livedesk/hub 0.1.15 → 0.1.16

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/src/remote-hub.js CHANGED
@@ -644,15 +644,27 @@ function safeTaskData(value) {
644
644
  }
645
645
  }
646
646
 
647
- function normalizeApprovalLevel(value) {
648
- const level = safeString(value, 80).toLowerCase();
649
- if (level === 'read-only') {
650
- return 'read-only';
651
- }
652
- return level === 'ai-assist' ? 'ai-assist' : 'task-only';
653
- }
654
-
655
- const SUPPORTED_AGENT_OPERATIONS = new Set([
647
+ function normalizeApprovalLevel(value) {
648
+ const level = safeString(value, 80).toLowerCase();
649
+ if (level === 'read-only') {
650
+ return 'read-only';
651
+ }
652
+ return 'task-only';
653
+ }
654
+
655
+ const RETIRED_AGENT_TASK_ERROR = 'agent-ai-assist-retired';
656
+
657
+ function getRetiredAgentTaskError(options = {}) {
658
+ const approvalLevel = safeString(options?.approvalLevel, 80).toLowerCase();
659
+ const hasModelSelection = options
660
+ && typeof options === 'object'
661
+ && Object.prototype.hasOwnProperty.call(options, 'model');
662
+ return approvalLevel === 'ai-assist' || hasModelSelection
663
+ ? RETIRED_AGENT_TASK_ERROR
664
+ : '';
665
+ }
666
+
667
+ const SUPPORTED_AGENT_OPERATIONS = new Set([
656
668
  'system.health',
657
669
  'gpu.status',
658
670
  'disk.status',
@@ -2586,13 +2598,12 @@ export function createRemoteHub(options = {}) {
2586
2598
  const count = clampNumber(options.count, 1, MAX_SYNTHETIC_DEVICES, 250);
2587
2599
  const connectedRatio = Math.max(0, Math.min(1, Number(options.connectedRatio ?? 0.88)));
2588
2600
  const thumbnailRatio = Math.max(0, Math.min(1, Number(options.thumbnailRatio ?? 0.7)));
2589
- const aiAssistRatio = Math.max(0, Math.min(1, Number(options.aiAssistRatio ?? 0.35)));
2590
- const liveCount = clampNumber(options.liveCount, 0, Math.min(count, 24), Math.min(6, count));
2601
+ const liveCount = clampNumber(options.liveCount, 0, Math.min(count, 24), Math.min(6, count));
2591
2602
  const replace = options.replace !== false;
2592
2603
  const now = new Date();
2593
2604
  const platforms = ['win32', 'linux', 'darwin'];
2594
2605
  const machineKinds = ['Mac mini', 'Mini PC', 'Client', 'Render node', 'Dev box'];
2595
- const workloadRoles = ['LLM worker', 'Image worker', 'Video worker', 'Idle reserve', 'Build worker'];
2606
+ const workloadRoles = ['Office computer', 'Media workstation', 'Development machine', 'Idle machine', 'Build machine'];
2596
2607
  const gpuNames = ['Apple M-series GPU', 'NVIDIA RTX local', 'Radeon Pro', 'Intel Arc', 'Integrated GPU'];
2597
2608
  const npuNames = ['Apple Neural Engine', 'Ryzen AI NPU', 'Intel AI Boost', 'Qualcomm Hexagon', 'NPU not present'];
2598
2609
 
@@ -2601,15 +2612,13 @@ export function createRemoteHub(options = {}) {
2601
2612
  }
2602
2613
 
2603
2614
  let connected = 0;
2604
- let aiAssist = 0;
2605
- let live = 0;
2615
+ let live = 0;
2606
2616
  for (let index = 0; index < count; index += 1) {
2607
2617
  const ordinal = index + 1;
2608
2618
  const deviceId = `synthetic-${String(ordinal).padStart(4, '0')}`;
2609
2619
  const isConnected = index / count < connectedRatio;
2610
2620
  const hasThumbnail = index / count < thumbnailRatio;
2611
- const hasAiAssist = index / count < aiAssistRatio;
2612
- const isLive = isConnected && index < liveCount;
2621
+ const isLive = isConnected && index < liveCount;
2613
2622
  const seenAt = new Date(now.getTime() - index * 1350).toISOString();
2614
2623
  const connectedAt = new Date(now.getTime() - (index + 8) * 60000).toISOString();
2615
2624
  const platform = platforms[index % platforms.length];
@@ -2631,9 +2640,7 @@ export function createRemoteHub(options = {}) {
2631
2640
  const npuTops = index % 5 === 4 ? 0 : platform === 'darwin' ? 38 : index % 2 === 0 ? 45 : 16;
2632
2641
  const machineKind = machineKinds[index % machineKinds.length];
2633
2642
  const workloadRole = workloadRoles[index % workloadRoles.length];
2634
- const toolBusy = index % 9 === 0;
2635
- const toolOnline = index % 4 !== 3;
2636
- const taskStatus = index % 17 === 0
2643
+ const taskStatus = index % 17 === 0
2637
2644
  ? 'failed'
2638
2645
  : index % 5 === 0
2639
2646
  ? 'completed'
@@ -2647,18 +2654,16 @@ export function createRemoteHub(options = {}) {
2647
2654
  title: taskStatus === 'failed' ? 'Synthetic issue check' : 'Synthetic status task',
2648
2655
  instructionPreview: 'Synthetic fleet scale validation task.',
2649
2656
  status: taskStatus,
2650
- approvalLevel: hasAiAssist ? 'ai-assist' : 'task-only',
2657
+ approvalLevel: 'task-only',
2651
2658
  requestedAt: seenAt,
2652
2659
  sentAt: seenAt,
2653
2660
  updatedAt: seenAt,
2654
- completedAt: taskStatus === 'completed' || taskStatus === 'failed' ? seenAt : '',
2655
- error: taskStatus === 'failed' ? 'Synthetic task failure sample.' : '',
2656
- resultKind: 'synthetic-agent-task',
2657
- resultModel: hasAiAssist ? 'synthetic-ai' : '',
2658
- resultResponseId: hasAiAssist ? `synthetic-response-${ordinal}` : '',
2659
- resultSummary: taskStatus === 'failed'
2660
- ? 'Synthetic task reported a sample issue.'
2661
- : 'Synthetic task completed for scale validation.'
2661
+ completedAt: taskStatus === 'completed' || taskStatus === 'failed' ? seenAt : '',
2662
+ error: taskStatus === 'failed' ? 'Synthetic task failure sample.' : '',
2663
+ resultKind: 'synthetic-agent-task',
2664
+ resultSummary: taskStatus === 'failed'
2665
+ ? 'Synthetic task reported a sample issue.'
2666
+ : 'Synthetic task completed for scale validation.'
2662
2667
  }
2663
2668
  : null;
2664
2669
  const device = {
@@ -2677,11 +2682,8 @@ export function createRemoteHub(options = {}) {
2677
2682
  thumbnail: hasThumbnail,
2678
2683
  control: false,
2679
2684
  liveStream: true,
2680
- computerAgent: true,
2681
- taskDispatch: true,
2682
- aiAssist: hasAiAssist,
2683
- aiModel: hasAiAssist ? 'synthetic-ai' : '',
2684
- aiProvider: hasAiAssist ? 'synthetic' : ''
2685
+ computerAgent: true,
2686
+ taskDispatch: true
2685
2687
  },
2686
2688
  connected: isConnected,
2687
2689
  connectedAt: isConnected ? connectedAt : '',
@@ -2740,31 +2742,12 @@ export function createRemoteHub(options = {}) {
2740
2742
  txMbps: 12 + ((index * 17) % 220),
2741
2743
  usageRatio: Number(Math.min(0.94, ((index * 9) % 81) / 100).toFixed(2))
2742
2744
  },
2743
- workload: {
2744
- role: workloadRole,
2745
- status: taskStatus || (toolBusy ? 'running' : 'idle'),
2746
- title: taskStatus ? 'Fleet validation task' : toolBusy ? 'Processing local job' : 'idle',
2747
- summary: taskStatus ? 'Synthetic status for fleet-scale validation.' : ''
2748
- },
2749
- tools: {
2750
- ollama: {
2751
- status: toolOnline ? (toolBusy ? 'busy' : 'online') : 'offline',
2752
- port: 11434,
2753
- version: toolOnline ? 'local' : ''
2754
- },
2755
- lmStudio: {
2756
- status: index % 5 === 0 ? 'online' : 'offline',
2757
- port: 1234
2758
- },
2759
- comfyui: {
2760
- status: index % 3 === 0 ? (toolBusy ? 'busy' : 'online') : 'offline',
2761
- port: 8188
2762
- },
2763
- stableDiffusion: {
2764
- status: index % 7 === 0 ? 'online' : 'offline',
2765
- port: 7860
2766
- }
2767
- }
2745
+ workload: {
2746
+ role: workloadRole,
2747
+ status: taskStatus || 'idle',
2748
+ title: taskStatus ? 'Fleet validation task' : 'idle',
2749
+ summary: taskStatus ? 'Synthetic status for fleet-scale validation.' : ''
2750
+ }
2768
2751
  },
2769
2752
  latestThumbnail: null,
2770
2753
  latestLiveFrame: null,
@@ -2839,25 +2822,20 @@ export function createRemoteHub(options = {}) {
2839
2822
  if (isConnected) {
2840
2823
  connected += 1;
2841
2824
  }
2842
- if (hasAiAssist) {
2843
- aiAssist += 1;
2844
- }
2845
- devices.set(deviceId, device);
2825
+ devices.set(deviceId, device);
2846
2826
  }
2847
2827
 
2848
2828
  emitRemoteEvent('RemoteSyntheticFleetSeeded', null, {
2849
- count,
2850
- connected,
2851
- aiAssist,
2852
- live
2829
+ count,
2830
+ connected,
2831
+ live
2853
2832
  });
2854
2833
  return {
2855
2834
  ok: true,
2856
2835
  synthetic: true,
2857
- seeded: count,
2858
- connected,
2859
- aiAssist,
2860
- live,
2836
+ seeded: count,
2837
+ connected,
2838
+ live,
2861
2839
  total: devices.size,
2862
2840
  max: MAX_SYNTHETIC_DEVICES
2863
2841
  };
@@ -3754,12 +3732,10 @@ export function createRemoteHub(options = {}) {
3754
3732
  task.updatedAt = now;
3755
3733
  task.completedAt = safeString(result?.completedAt, 80) || now;
3756
3734
  task.error = resultError;
3757
- task.resultSummary = resultError || summarizeTaskResult(result);
3758
- task.resultData = safeTaskData(result?.data);
3759
- task.resultKind = safeString(result?.kind || result?.mode || 'agent-task', 80);
3760
- task.resultModel = safeString(result?.model || result?.aiModel || '', 120);
3761
- task.resultResponseId = safeString(result?.responseId || result?.id || '', 160);
3762
- device.latestTask = task;
3735
+ task.resultSummary = resultError || summarizeTaskResult(result);
3736
+ task.resultData = safeTaskData(result?.data);
3737
+ task.resultKind = safeString(result?.kind || result?.mode || 'agent-task', 80);
3738
+ device.latestTask = task;
3763
3739
  device.pendingTaskCommands.delete(commandId);
3764
3740
  device.counters.taskResultsReceived += 1;
3765
3741
  if (status === 'failed') {
@@ -5862,9 +5838,13 @@ export function createRemoteHub(options = {}) {
5862
5838
  }
5863
5839
 
5864
5840
  function requestAgentTask(deviceId, options = {}) {
5865
- const device = devices.get(String(deviceId || ''));
5866
- const requestedOperation = safeString(options.operation, 80);
5867
- const operation = normalizeAgentOperation(requestedOperation);
5841
+ const retiredRequestError = getRetiredAgentTaskError(options);
5842
+ if (retiredRequestError) {
5843
+ return { ok: false, error: retiredRequestError };
5844
+ }
5845
+ const device = devices.get(String(deviceId || ''));
5846
+ const requestedOperation = safeString(options.operation, 80);
5847
+ const operation = normalizeAgentOperation(requestedOperation);
5868
5848
  if (requestedOperation && !operation) {
5869
5849
  return { ok: false, error: 'unsupported-agent-operation' };
5870
5850
  }
@@ -5882,10 +5862,6 @@ export function createRemoteHub(options = {}) {
5882
5862
 
5883
5863
  const now = new Date().toISOString();
5884
5864
  const approvalLevel = normalizeApprovalLevel(options.approvalLevel);
5885
- if (approvalLevel === 'ai-assist' && !readCapabilityFlag(device.capabilities, 'aiAssist')) {
5886
- return { ok: false, error: 'device-ai-assist-unavailable' };
5887
- }
5888
-
5889
5865
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
5890
5866
  const taskId = safeString(options.taskId, 128) || crypto.randomUUID();
5891
5867
  const title = safeString(options.title, 120)
@@ -5907,24 +5883,12 @@ export function createRemoteHub(options = {}) {
5907
5883
  requestedAt: now,
5908
5884
  sentAt: now,
5909
5885
  updatedAt: now,
5910
- completedAt: now,
5911
- error: '',
5912
- resultKind: approvalLevel === 'ai-assist' ? 'synthetic-ai-assist' : 'synthetic-agent-task',
5913
- resultModel: approvalLevel === 'ai-assist'
5914
- ? safeString(options.model, 120) || 'synthetic-ai'
5915
- : '',
5916
- resultResponseId: approvalLevel === 'ai-assist'
5917
- ? `synthetic-response-${taskId}`
5918
- : '',
5919
- resultSummary: operation === 'process.list'
5920
- ? (safeString(options.targetQuery, 160).toLowerCase() === 'comfyui'
5921
- ? (device.status?.tools?.comfyui?.status === 'online'
5922
- ? 'ComfyUI is running.'
5923
- : 'ComfyUI is not running.')
5924
- : 'Process list collected.')
5925
- : approvalLevel === 'ai-assist'
5926
- ? `Synthetic AI assist completed for ${device.deviceName}.`
5927
- : `Synthetic task accepted by ${device.deviceName}.`,
5886
+ completedAt: now,
5887
+ error: '',
5888
+ resultKind: 'synthetic-agent-task',
5889
+ resultSummary: operation === 'process.list'
5890
+ ? 'Process list collected.'
5891
+ : `Synthetic task accepted by ${device.deviceName}.`,
5928
5892
  resultData: undefined
5929
5893
  };
5930
5894
 
@@ -5964,10 +5928,6 @@ export function createRemoteHub(options = {}) {
5964
5928
 
5965
5929
  const now = new Date().toISOString();
5966
5930
  const approvalLevel = normalizeApprovalLevel(options.approvalLevel);
5967
- if (approvalLevel === 'ai-assist' && !readCapabilityFlag(device.capabilities, 'aiAssist')) {
5968
- return { ok: false, error: 'device-ai-assist-unavailable' };
5969
- }
5970
-
5971
5931
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
5972
5932
  const taskId = safeString(options.taskId, 128) || crypto.randomUUID();
5973
5933
  const title = safeString(options.title, 120)
@@ -5989,14 +5949,12 @@ export function createRemoteHub(options = {}) {
5989
5949
  requestedAt: now,
5990
5950
  sentAt: now,
5991
5951
  updatedAt: now,
5992
- completedAt: '',
5993
- error: '',
5994
- resultKind: '',
5995
- resultModel: '',
5996
- resultResponseId: '',
5997
- resultSummary: '',
5998
- resultData: undefined
5999
- };
5952
+ completedAt: '',
5953
+ error: '',
5954
+ resultKind: '',
5955
+ resultSummary: '',
5956
+ resultData: undefined
5957
+ };
6000
5958
 
6001
5959
  const commandName = operation || 'agent.task';
6002
5960
  const sent = writeJsonLine(device.socket, {
@@ -6012,8 +5970,7 @@ export function createRemoteHub(options = {}) {
6012
5970
  toolArguments: options.toolArguments && typeof options.toolArguments === 'object' ? options.toolArguments : {},
6013
5971
  permissionMode: safeString(options.permissionMode, 40) || 'ask',
6014
5972
  approvalLevel,
6015
- model: safeString(options.model, 120),
6016
- requestedAt: now
5973
+ requestedAt: now
6017
5974
  },
6018
5975
  issuedAt: now
6019
5976
  });
@@ -6035,15 +5992,26 @@ export function createRemoteHub(options = {}) {
6035
5992
  approvalLevel
6036
5993
  });
6037
5994
  return { ok: true, commandId, taskId, approvalLevel };
6038
- }
6039
-
6040
- function requestAgentTaskBatch(deviceIds, options = {}) {
6041
- const targets = [...new Set((deviceIds || [])
6042
- .map(deviceId => safeString(deviceId, 128))
6043
- .filter(Boolean))]
6044
- .slice(0, 500);
6045
-
6046
- if (targets.length === 0) {
5995
+ }
5996
+
5997
+ function requestAgentTaskBatch(deviceIds, options = {}) {
5998
+ const targets = [...new Set((deviceIds || [])
5999
+ .map(deviceId => safeString(deviceId, 128))
6000
+ .filter(Boolean))]
6001
+ .slice(0, 500);
6002
+ const retiredRequestError = getRetiredAgentTaskError(options);
6003
+ if (retiredRequestError) {
6004
+ return {
6005
+ ok: false,
6006
+ error: retiredRequestError,
6007
+ total: targets.length,
6008
+ queued: 0,
6009
+ approvalLevel: normalizeApprovalLevel(options.approvalLevel),
6010
+ results: []
6011
+ };
6012
+ }
6013
+
6014
+ if (targets.length === 0) {
6047
6015
  return {
6048
6016
  ok: false,
6049
6017
  error: 'no-target-devices',
package/src/server.js CHANGED
@@ -1427,10 +1427,10 @@ function normalizeMonitorSelections(value) {
1427
1427
  return result;
1428
1428
  }
1429
1429
 
1430
- function normalizeDeviceIds(value) {
1431
- const raw = Array.isArray(value)
1432
- ? value
1433
- : String(value || '').split(',');
1430
+ function normalizeDeviceIds(value) {
1431
+ const raw = Array.isArray(value)
1432
+ ? value
1433
+ : String(value || '').split(',');
1434
1434
  const ids = [];
1435
1435
  const seen = new Set();
1436
1436
  for (const entry of raw) {
@@ -1444,10 +1444,24 @@ function normalizeDeviceIds(value) {
1444
1444
  break;
1445
1445
  }
1446
1446
  }
1447
- return ids;
1448
- }
1449
-
1450
- function normalizeTransferFiles(value) {
1447
+ return ids;
1448
+ }
1449
+
1450
+ function rejectRetiredAgentTaskRequest(req, res) {
1451
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
1452
+ const approvalLevel = String(body.approvalLevel || '').trim().toLowerCase();
1453
+ const hasModelSelection = Object.prototype.hasOwnProperty.call(body, 'model');
1454
+ if (approvalLevel !== 'ai-assist' && !hasModelSelection) {
1455
+ return false;
1456
+ }
1457
+ res.status(400).json({
1458
+ ok: false,
1459
+ error: 'agent-ai-assist-retired'
1460
+ });
1461
+ return true;
1462
+ }
1463
+
1464
+ function normalizeTransferFiles(value) {
1451
1465
  const rawFiles = Array.isArray(value) ? value : [];
1452
1466
  const files = [];
1453
1467
  let totalBytes = 0;
@@ -2985,7 +2999,7 @@ app.post('/api/settings/agent/run', async (req, res) => {
2985
2999
  noStore(res);
2986
3000
  try {
2987
3001
  if (!(await synchronizeAgentEnablement())) {
2988
- throw new AgentProviderError('agent-ai-disabled', 'Enable AI Agent in Settings before running commands.', { status: 409 });
3002
+ throw new AgentProviderError('agent-ai-disabled', 'Enable Codex Agent in Settings before running commands.', { status: 409 });
2989
3003
  }
2990
3004
  const instruction = typeof req.body?.instruction === 'string' ? req.body.instruction.slice(0, 4000) : '';
2991
3005
  const deviceIds = normalizeDeviceIds(req.body?.deviceIds).slice(0, 500);
@@ -3775,12 +3789,11 @@ app.delete('/api/remote/host-target', async (req, res) => {
3775
3789
 
3776
3790
  app.post('/api/remote/synthetic/seed', (req, res) => {
3777
3791
  noStore(res);
3778
- res.json(remoteHub.seedSyntheticFleet({
3779
- count: req.body?.count,
3780
- connectedRatio: req.body?.connectedRatio,
3781
- thumbnailRatio: req.body?.thumbnailRatio,
3782
- aiAssistRatio: req.body?.aiAssistRatio,
3783
- liveCount: req.body?.liveCount,
3792
+ res.json(remoteHub.seedSyntheticFleet({
3793
+ count: req.body?.count,
3794
+ connectedRatio: req.body?.connectedRatio,
3795
+ thumbnailRatio: req.body?.thumbnailRatio,
3796
+ liveCount: req.body?.liveCount,
3784
3797
  replace: req.body?.replace
3785
3798
  }));
3786
3799
  });
@@ -4075,42 +4088,41 @@ app.post('/api/remote/devices/:deviceId/quick-actions', requireHubFeatureAccess,
4075
4088
  });
4076
4089
 
4077
4090
  app.post('/api/remote/devices/:deviceId/tasks', requireHubFeatureAccess, (req, res) => {
4078
- noStore(res);
4079
- res.json(remoteHub.requestAgentTask(req.params.deviceId, {
4091
+ noStore(res);
4092
+ if (rejectRetiredAgentTaskRequest(req, res)) {
4093
+ return;
4094
+ }
4095
+ res.json(remoteHub.requestAgentTask(req.params.deviceId, {
4080
4096
  instruction: req.body?.instruction,
4081
4097
  title: req.body?.title,
4082
4098
  taskId: req.body?.taskId,
4083
4099
  commandId: req.body?.commandId,
4084
- approvalLevel: req.body?.approvalLevel,
4085
- operation: req.body?.operation,
4086
- targetQuery: req.body?.targetQuery,
4087
- model: req.body?.model
4100
+ approvalLevel: req.body?.approvalLevel,
4101
+ operation: req.body?.operation,
4102
+ targetQuery: req.body?.targetQuery
4088
4103
  }));
4089
4104
  });
4090
4105
 
4091
- app.post('/api/remote/tasks', requireHubFeatureAccess, (req, res) => {
4092
- noStore(res);
4093
- const requestedIds = normalizeDeviceIds(req.body?.deviceIds);
4094
- const approvalLevel = req.body?.approvalLevel === 'ai-assist'
4095
- ? 'ai-assist'
4096
- : req.body?.approvalLevel === 'read-only'
4097
- ? 'read-only'
4098
- : 'task-only';
4106
+ app.post('/api/remote/tasks', requireHubFeatureAccess, (req, res) => {
4107
+ noStore(res);
4108
+ if (rejectRetiredAgentTaskRequest(req, res)) {
4109
+ return;
4110
+ }
4111
+ const requestedIds = normalizeDeviceIds(req.body?.deviceIds);
4112
+ const approvalLevel = req.body?.approvalLevel === 'read-only' ? 'read-only' : 'task-only';
4099
4113
  const devices = remoteHub.listDevices({ includeDataUrl: false });
4100
4114
  const targetIds = requestedIds.length > 0
4101
4115
  ? requestedIds
4102
- : devices
4103
- .filter(device => device.connected)
4104
- .filter(device => approvalLevel !== 'ai-assist' || isCapabilityEnabled(device, 'aiAssist'))
4105
- .map(device => device.deviceId);
4116
+ : devices
4117
+ .filter(device => device.connected)
4118
+ .map(device => device.deviceId);
4106
4119
  const result = remoteHub.requestAgentTaskBatch([...new Set(targetIds)].slice(0, 500), {
4107
4120
  instruction: req.body?.instruction,
4108
4121
  title: req.body?.title,
4109
- approvalLevel,
4110
- operation: req.body?.operation,
4111
- targetQuery: req.body?.targetQuery,
4112
- model: req.body?.model,
4113
- batchId: req.body?.batchId
4122
+ approvalLevel,
4123
+ operation: req.body?.operation,
4124
+ targetQuery: req.body?.targetQuery,
4125
+ batchId: req.body?.batchId
4114
4126
  });
4115
4127
  res.json({
4116
4128
  ...result,