livedesk 0.1.447 → 0.1.448
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/bin/livedesk.js +150 -17
- package/client/README.md +3 -3
- package/client/bin/livedesk-client-node.js +4 -3
- package/client/package.json +5 -5
- package/hub/package.json +1 -1
- package/hub/src/agents/agent-manager.js +132 -175
- package/hub/src/agents/agent-settings.js +7 -68
- package/hub/src/agents/agent-tool-registry.js +1 -1
- package/hub/src/agents/codex-agent-runtime.js +2 -2
- package/hub/src/remote-hub.js +92 -124
- package/hub/src/server.js +50 -38
- package/package.json +6 -6
- package/web/dist/assets/{icons-BIyRF-ou.js → icons-CTHUbfAT.js} +1 -1
- package/web/dist/assets/index-DKPdDVYW.js +25 -0
- package/web/dist/assets/{react-C7a9r614.js → react-BOnb-QRJ.js} +1 -1
- package/web/dist/index.html +3 -3
- package/hub/src/agents/opencode-go-provider.js +0 -260
- package/hub/src/agents/secret-store.js +0 -165
- package/web/dist/assets/index-5opXQY8X.js +0 -25
|
@@ -3,13 +3,11 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
5
|
export const AGENT_PROVIDER_CODEX = 'codex';
|
|
6
|
-
export const AGENT_PROVIDER_OPENCODE_GO = 'opencode-go';
|
|
7
6
|
export const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_CODEX;
|
|
8
|
-
export const DEFAULT_AGENT_BASE_URL = 'https://opencode.ai/zen/go/v1';
|
|
9
7
|
export const DEFAULT_CODEX_MODEL_ID = '';
|
|
10
8
|
export const DEFAULT_AGENT_WORKSPACE = path.join(os.homedir(), '.livedesk', 'agent-workspace');
|
|
11
9
|
export const DEFAULT_AGENT_SETTINGS = Object.freeze({
|
|
12
|
-
settingsVersion:
|
|
10
|
+
settingsVersion: 3,
|
|
13
11
|
enabled: true,
|
|
14
12
|
provider: DEFAULT_AGENT_PROVIDER,
|
|
15
13
|
codexModelId: DEFAULT_CODEX_MODEL_ID,
|
|
@@ -24,34 +22,13 @@ export const DEFAULT_AGENT_SETTINGS = Object.freeze({
|
|
|
24
22
|
codexShowDetailedEvents: true,
|
|
25
23
|
codexNetworkAccessEnabled: false,
|
|
26
24
|
codexWebSearchMode: 'disabled',
|
|
27
|
-
|
|
28
|
-
generateSummary: true,
|
|
29
|
-
fallbackSummary: true,
|
|
30
|
-
includeRawDeviceDetails: false,
|
|
31
|
-
maxConcurrentRequests: 2,
|
|
32
|
-
baseUrl: DEFAULT_AGENT_BASE_URL,
|
|
33
|
-
defaultModelId: 'deepseek-v4-flash',
|
|
34
|
-
planModelId: '',
|
|
35
|
-
summaryModelId: '',
|
|
36
|
-
reasoningLevel: 'auto',
|
|
37
|
-
planTemperature: 0.1,
|
|
38
|
-
summaryTemperature: 0.2,
|
|
39
|
-
topP: 1,
|
|
40
|
-
planMaxOutputTokens: 1024,
|
|
41
|
-
summaryMaxOutputTokens: 1024,
|
|
42
|
-
planTimeoutMs: 60000,
|
|
43
|
-
summaryTimeoutMs: 60000,
|
|
44
|
-
retryCount: 1,
|
|
45
|
-
retryDelayMs: 1000,
|
|
46
|
-
streamSummary: false
|
|
25
|
+
maxConcurrentRequests: 2
|
|
47
26
|
});
|
|
48
27
|
|
|
49
|
-
const PROVIDERS = new Set([AGENT_PROVIDER_CODEX, AGENT_PROVIDER_OPENCODE_GO]);
|
|
50
28
|
const REASONING_EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
51
29
|
const SANDBOX_MODES = new Set(['read-only', 'workspace-write', 'danger-full-access']);
|
|
52
30
|
const APPROVAL_POLICIES = new Set(['never', 'on-request', 'on-failure', 'untrusted']);
|
|
53
31
|
const WEB_SEARCH_MODES = new Set(['disabled', 'cached', 'live']);
|
|
54
|
-
const LEGACY_REASONING_LEVELS = new Set(['auto', 'low', 'medium', 'high']);
|
|
55
32
|
|
|
56
33
|
export class AgentSettingsError extends Error {
|
|
57
34
|
constructor(code, message) {
|
|
@@ -84,19 +61,6 @@ function enumValue(value, allowed, fallback, code) {
|
|
|
84
61
|
return normalized;
|
|
85
62
|
}
|
|
86
63
|
|
|
87
|
-
function normalizeBaseUrl(value) {
|
|
88
|
-
const candidate = stringValue(value, DEFAULT_AGENT_SETTINGS.baseUrl, 500).replace(/\/+$/, '');
|
|
89
|
-
try {
|
|
90
|
-
const parsed = new URL(candidate);
|
|
91
|
-
if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) throw new Error('invalid protocol');
|
|
92
|
-
parsed.hash = '';
|
|
93
|
-
parsed.search = '';
|
|
94
|
-
return parsed.toString().replace(/\/+$/, '');
|
|
95
|
-
} catch {
|
|
96
|
-
throw new AgentSettingsError('agent-invalid-base-url', 'Base URL must be a valid HTTP or HTTPS URL.');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
64
|
function normalizeWorkingDirectory(value) {
|
|
101
65
|
// The agent runtime owns this directory. Do not allow a browser/API caller
|
|
102
66
|
// to move Codex into a user project or an arbitrary filesystem root.
|
|
@@ -105,18 +69,12 @@ function normalizeWorkingDirectory(value) {
|
|
|
105
69
|
|
|
106
70
|
export function normalizeAgentSettings(value = {}) {
|
|
107
71
|
const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
108
|
-
// The pre-Codex settings file was written by LiveDesk itself and had no
|
|
109
|
-
// migration marker. Keep the legacy values but move that first-run config
|
|
110
|
-
// to the new Codex default.
|
|
111
|
-
const migratingLegacy = Number(source.settingsVersion || 0) < 2 && source.provider === AGENT_PROVIDER_OPENCODE_GO;
|
|
112
|
-
const provider = migratingLegacy
|
|
113
|
-
? DEFAULT_AGENT_PROVIDER
|
|
114
|
-
: enumValue(source.provider, PROVIDERS, DEFAULT_AGENT_PROVIDER, 'agent-unsupported-provider');
|
|
115
|
-
const reasoningLevel = enumValue(source.reasoningLevel, LEGACY_REASONING_LEVELS, DEFAULT_AGENT_SETTINGS.reasoningLevel, 'agent-invalid-reasoning-level');
|
|
116
72
|
return {
|
|
117
|
-
settingsVersion:
|
|
73
|
+
settingsVersion: 3,
|
|
118
74
|
enabled: booleanValue(source.enabled, DEFAULT_AGENT_SETTINGS.enabled),
|
|
119
|
-
provider
|
|
75
|
+
// LiveDesk has one Agent runtime. Old provider values are deliberately
|
|
76
|
+
// normalized to Codex so an upgrade cannot reactivate a retired provider.
|
|
77
|
+
provider: AGENT_PROVIDER_CODEX,
|
|
120
78
|
codexModelId: stringValue(source.codexModelId, DEFAULT_AGENT_SETTINGS.codexModelId, 160),
|
|
121
79
|
codexReasoningEffort: enumValue(source.codexReasoningEffort, REASONING_EFFORTS, DEFAULT_AGENT_SETTINGS.codexReasoningEffort, 'agent-invalid-reasoning-effort'),
|
|
122
80
|
// These are safety settings, not user-controlled execution toggles. They
|
|
@@ -131,26 +89,7 @@ export function normalizeAgentSettings(value = {}) {
|
|
|
131
89
|
codexShowDetailedEvents: booleanValue(source.codexShowDetailedEvents, DEFAULT_AGENT_SETTINGS.codexShowDetailedEvents),
|
|
132
90
|
codexNetworkAccessEnabled: false,
|
|
133
91
|
codexWebSearchMode: 'disabled',
|
|
134
|
-
|
|
135
|
-
generateSummary: booleanValue(source.generateSummary, DEFAULT_AGENT_SETTINGS.generateSummary),
|
|
136
|
-
fallbackSummary: booleanValue(source.fallbackSummary, DEFAULT_AGENT_SETTINGS.fallbackSummary),
|
|
137
|
-
includeRawDeviceDetails: booleanValue(source.includeRawDeviceDetails, DEFAULT_AGENT_SETTINGS.includeRawDeviceDetails),
|
|
138
|
-
maxConcurrentRequests: numberValue(source.maxConcurrentRequests, 1, 8, DEFAULT_AGENT_SETTINGS.maxConcurrentRequests, true),
|
|
139
|
-
baseUrl: normalizeBaseUrl(source.baseUrl ?? DEFAULT_AGENT_SETTINGS.baseUrl),
|
|
140
|
-
defaultModelId: stringValue(source.defaultModelId, DEFAULT_AGENT_SETTINGS.defaultModelId, 160),
|
|
141
|
-
planModelId: stringValue(source.planModelId, DEFAULT_AGENT_SETTINGS.planModelId, 160),
|
|
142
|
-
summaryModelId: stringValue(source.summaryModelId, DEFAULT_AGENT_SETTINGS.summaryModelId, 160),
|
|
143
|
-
reasoningLevel,
|
|
144
|
-
planTemperature: numberValue(source.planTemperature, 0, 2, DEFAULT_AGENT_SETTINGS.planTemperature),
|
|
145
|
-
summaryTemperature: numberValue(source.summaryTemperature, 0, 2, DEFAULT_AGENT_SETTINGS.summaryTemperature),
|
|
146
|
-
topP: numberValue(source.topP, 0, 1, DEFAULT_AGENT_SETTINGS.topP),
|
|
147
|
-
planMaxOutputTokens: numberValue(source.planMaxOutputTokens, 128, 16384, DEFAULT_AGENT_SETTINGS.planMaxOutputTokens, true),
|
|
148
|
-
summaryMaxOutputTokens: numberValue(source.summaryMaxOutputTokens, 128, 16384, DEFAULT_AGENT_SETTINGS.summaryMaxOutputTokens, true),
|
|
149
|
-
planTimeoutMs: numberValue(source.planTimeoutMs, 5000, 180000, DEFAULT_AGENT_SETTINGS.planTimeoutMs, true),
|
|
150
|
-
summaryTimeoutMs: numberValue(source.summaryTimeoutMs, 5000, 180000, DEFAULT_AGENT_SETTINGS.summaryTimeoutMs, true),
|
|
151
|
-
retryCount: numberValue(source.retryCount, 0, 4, DEFAULT_AGENT_SETTINGS.retryCount, true),
|
|
152
|
-
retryDelayMs: numberValue(source.retryDelayMs, 100, 10000, DEFAULT_AGENT_SETTINGS.retryDelayMs, true),
|
|
153
|
-
streamSummary: booleanValue(source.streamSummary, DEFAULT_AGENT_SETTINGS.streamSummary)
|
|
92
|
+
maxConcurrentRequests: numberValue(source.maxConcurrentRequests, 1, 8, DEFAULT_AGENT_SETTINGS.maxConcurrentRequests, true)
|
|
154
93
|
};
|
|
155
94
|
}
|
|
156
95
|
|
|
@@ -79,7 +79,7 @@ export const AGENT_TOOL_DEFINITIONS = Object.freeze([
|
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
name: 'livedesk.list_processes',
|
|
82
|
-
description: 'Read process status from the selected Clients. Use processName
|
|
82
|
+
description: 'Read process status from the selected Clients. Use processName when the request names a specific process.',
|
|
83
83
|
category: 'read',
|
|
84
84
|
readOnly: true,
|
|
85
85
|
mutating: false,
|
|
@@ -472,7 +472,7 @@ export function createCodexAgentRuntime({
|
|
|
472
472
|
'Never use arbitrary MCP servers, change permissions, forge approvals, request credentials, or invent a tool result.',
|
|
473
473
|
`The Hub has fixed this run to permission mode ${permissionPolicy?.mode || 'ask'} and enforces the policy independently of your instructions.`,
|
|
474
474
|
'Use only the selected connected device IDs below. If the Hub asks for user approval, wait for that approval result and do not work around it.',
|
|
475
|
-
'You may perform multiple safe read-only checks when the request requires a sequence. For example, find Clients
|
|
475
|
+
'You may perform multiple safe read-only checks when the request requires a sequence. For example, find Clients missing a named process, then check a related service only on those Clients.',
|
|
476
476
|
`Selected device IDs: ${JSON.stringify(deviceIds)}`,
|
|
477
477
|
'Return a concise Korean or English summary grounded only in tool results. Do not invent results.',
|
|
478
478
|
`User request: ${safeText(instruction, 4000)}`
|
|
@@ -649,7 +649,7 @@ export function createCodexAgentRuntime({
|
|
|
649
649
|
const deviceIds = safeAgentDeviceIds(input.deviceIds);
|
|
650
650
|
if (deviceIds.length === 0) throw new AgentProviderError('agent-no-target-devices', 'Select at least one connected device.', { status: 400 });
|
|
651
651
|
const settings = await settingsStore.get();
|
|
652
|
-
if (!settings.enabled) throw new AgentProviderError('agent-ai-disabled', 'Agent
|
|
652
|
+
if (!settings.enabled) throw new AgentProviderError('agent-ai-disabled', 'Codex Agent is disabled in Settings.', { status: 409 });
|
|
653
653
|
assertSecurityConfiguration();
|
|
654
654
|
pruneRuns();
|
|
655
655
|
if (runs.size >= MAX_RUNS) throw new AgentProviderError('agent-run-limit', 'The LiveDesk Agent run history is full.', { status: 429, retryable: true });
|
package/hub/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
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const
|
|
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
|
|
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 = ['
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
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 ||
|
|
2746
|
-
title: taskStatus ? 'Fleet validation task' :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5866
|
-
|
|
5867
|
-
|
|
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:
|
|
5913
|
-
|
|
5914
|
-
?
|
|
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
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
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
|
-
|
|
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 (
|
|
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/hub/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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
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,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"livedeskClientVersion": "0.1.
|
|
3
|
+
"version": "0.1.448",
|
|
4
|
+
"livedeskClientVersion": "0.1.203",
|
|
5
5
|
"buildFlavor": "production",
|
|
6
6
|
"description": "LiveDesk Hub and client launcher",
|
|
7
7
|
"type": "module",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"ws": "^8.18.3"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
54
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
55
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
56
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
53
|
+
"@livedesk/fast-linux-x64": "0.1.411",
|
|
54
|
+
"@livedesk/fast-osx-arm64": "0.1.411",
|
|
55
|
+
"@livedesk/fast-osx-x64": "0.1.411",
|
|
56
|
+
"@livedesk/fast-win-x64": "0.1.411"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public"
|