livedesk 0.1.645 → 0.1.647

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 (25) hide show
  1. package/bootstrap/update-host-client.mjs +33 -17
  2. package/client/bin/livedesk-client-update-bootstrap.cjs +179 -11
  3. package/client/package.json +5 -5
  4. package/hub/package.json +1 -1
  5. package/hub/src/live-desk-update.js +60 -23
  6. package/hub/src/remote-hub.js +20 -14
  7. package/package.json +6 -6
  8. package/web/dist/app.html +3 -3
  9. package/web/dist/app.webmanifest +1 -1
  10. package/web/dist/assets/{AgentSettingsTab-CmKXvygQ.js → AgentSettingsTab-B1bvLJ9e.js} +1 -1
  11. package/web/dist/assets/{AgentsPage-Xi5F-GyN.js → AgentsPage-CL6TsMAN.js} +1 -1
  12. package/web/dist/assets/{App-M0W5ZMPW.js → App-CWoSKFT8.js} +3 -3
  13. package/web/dist/assets/{CaptureGallery-B74AiXzV.js → CaptureGallery-Bi-MwMXp.js} +1 -1
  14. package/web/dist/assets/{DesktopUpdatePanel--7icZPSX.js → DesktopUpdatePanel-CXnMexHh.js} +1 -1
  15. package/web/dist/assets/{LiveDeskApp-6q_tFtUe.js → LiveDeskApp-oy2vBKdt.js} +1 -1
  16. package/web/dist/assets/{SettingsPage-C8H1s5dx.js → SettingsPage-CQFGyhVp.js} +1 -1
  17. package/web/dist/assets/{SettingsTabs-BmHlySum.js → SettingsTabs-Y9F71NI9.js} +1 -1
  18. package/web/dist/assets/{ShareFilesPage-B1tlWhVN.js → ShareFilesPage-DmuK6o2s.js} +1 -1
  19. package/web/dist/assets/{SupportPage-tctk_fWU.js → SupportPage-CggTgNNZ.js} +1 -1
  20. package/web/dist/assets/{app-DLXscHD0.js → app-CcsIHPoF.js} +1 -1
  21. package/web/dist/assets/{main-DxZScFjV.js → main-Don8EM7A.js} +2 -2
  22. package/web/dist/assets/{styles-D75uKZbK.js → styles-CcZi_0ll.js} +1 -1
  23. package/web/dist/index.html +2 -2
  24. package/web/dist/livedesk-build-evidence.json +50 -50
  25. package/web/dist/sw.js +1 -1
@@ -19,7 +19,7 @@ export function isValidUpdateHostEnvironmentEntry(key, value) {
19
19
  && Buffer.byteLength(String(value ?? ''), 'utf8') <= UPDATE_HOST_MAX_ENVIRONMENT_VALUE_BYTES;
20
20
  }
21
21
 
22
- function updateHostEnvironmentPriority(key) {
22
+ function updateHostEnvironmentPriority(key) {
23
23
  const normalized = String(key || '').toUpperCase();
24
24
  if (normalized.startsWith('LIVEDESK_')
25
25
  || normalized.startsWith('LIVE_DESK_')
@@ -34,27 +34,43 @@ function updateHostEnvironmentPriority(key) {
34
34
  || normalized.startsWith('CODEX_')) {
35
35
  return 1;
36
36
  }
37
- return 2;
38
- }
39
-
40
- export function buildUpdateHostWorkerEnvironment(baseEnv = process.env, requiredEnv = {}) {
41
- const requiredEntries = Object.entries(requiredEnv || {}).map(([key, value]) => [String(key), String(value ?? '')]);
42
- for (const [key, value] of requiredEntries) {
43
- if (!isValidUpdateHostEnvironmentEntry(key, value)) {
44
- throw new Error(`invalid-required-update-host-environment-entry:${key}`);
45
- }
46
- }
47
- if (requiredEntries.length > UPDATE_HOST_MAX_ENVIRONMENT_KEYS) {
37
+ return 2;
38
+ }
39
+
40
+ function deduplicateUpdateHostEnvironmentEntries(entries) {
41
+ const byNormalizedKey = new Map();
42
+ for (const [rawKey, rawValue] of entries || []) {
43
+ const key = String(rawKey);
44
+ const value = String(rawValue ?? '');
45
+ const normalizedKey = key.toUpperCase();
46
+ const existing = byNormalizedKey.get(normalizedKey);
47
+ if (!existing || key < existing[0]) {
48
+ byNormalizedKey.set(normalizedKey, [key, value]);
49
+ }
50
+ }
51
+ return [...byNormalizedKey.values()];
52
+ }
53
+
54
+ export function buildUpdateHostWorkerEnvironment(baseEnv = process.env, requiredEnv = {}) {
55
+ const rawRequiredEntries = Object.entries(requiredEnv || {})
56
+ .map(([key, value]) => [String(key), String(value ?? '')]);
57
+ for (const [key, value] of rawRequiredEntries) {
58
+ if (!isValidUpdateHostEnvironmentEntry(key, value)) {
59
+ throw new Error(`invalid-required-update-host-environment-entry:${key}`);
60
+ }
61
+ }
62
+ const requiredEntries = deduplicateUpdateHostEnvironmentEntries(rawRequiredEntries);
63
+ if (requiredEntries.length > UPDATE_HOST_MAX_ENVIRONMENT_KEYS) {
48
64
  throw new Error('invalid-required-update-host-environment-count');
49
65
  }
50
66
 
51
67
  const requiredKeys = new Set(requiredEntries.map(([key]) => key.toUpperCase()));
52
68
  const availableBaseEntries = UPDATE_HOST_MAX_ENVIRONMENT_KEYS - requiredEntries.length;
53
- const baseEntries = Object.entries(baseEnv || {})
54
- .filter(([key, value]) => value != null
55
- && !requiredKeys.has(String(key).toUpperCase())
56
- && isValidUpdateHostEnvironmentEntry(key, value))
57
- .map(([key, value]) => [String(key), String(value)])
69
+ const baseEntries = deduplicateUpdateHostEnvironmentEntries(Object.entries(baseEnv || {})
70
+ .filter(([key, value]) => value != null
71
+ && !requiredKeys.has(String(key).toUpperCase())
72
+ && isValidUpdateHostEnvironmentEntry(key, value))
73
+ .map(([key, value]) => [String(key), String(value)]))
58
74
  .sort((left, right) => {
59
75
  const priorityDifference = updateHostEnvironmentPriority(left[0]) - updateHostEnvironmentPriority(right[0]);
60
76
  if (priorityDifference) return priorityDifference;
@@ -3,9 +3,12 @@ const net = require('node:net');
3
3
  const os = require('node:os');
4
4
  const path = require('node:path');
5
5
 
6
- const UPDATE_HOST_PROTOCOL_VERSION = 1;
7
- const UPDATE_HOST_MAX_MESSAGE_BYTES = 2 * 1024 * 1024;
8
- const bootstrapPath = String(process.env.LIVEDESK_CLIENT_UPDATE_BOOTSTRAP_PATH || '').trim();
6
+ const UPDATE_HOST_PROTOCOL_VERSION = 1;
7
+ const UPDATE_HOST_MAX_MESSAGE_BYTES = 2 * 1024 * 1024;
8
+ const UPDATE_HOST_MAX_ENVIRONMENT_KEYS = 512;
9
+ const UPDATE_HOST_MAX_ENVIRONMENT_VALUE_BYTES = 128 * 1024;
10
+ const UPDATE_HOST_ENVIRONMENT_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_()]*$/;
11
+ const bootstrapPath = String(process.env.LIVEDESK_CLIENT_UPDATE_BOOTSTRAP_PATH || '').trim();
9
12
  if (bootstrapPath) {
10
13
  try { fs.rmSync(bootstrapPath, { force: true }); } catch { /* operation-owned temporary file */ }
11
14
  }
@@ -24,8 +27,169 @@ const targetProductVersion = cleanVersion(
24
27
  );
25
28
  const updateDeadlineEpochMs = Number(process.env.LIVEDESK_CLIENT_UPDATE_DEADLINE_EPOCH_MS || 0);
26
29
  const versionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
27
- const stateLockPath = `${statePath}.lock`;
28
- const lockWaitBuffer = new Int32Array(new SharedArrayBuffer(4));
30
+ const stateLockPath = `${statePath}.lock`;
31
+ const lockWaitBuffer = new Int32Array(new SharedArrayBuffer(4));
32
+
33
+ function isValidUpdateHostEnvironmentEntry(key, value) {
34
+ return UPDATE_HOST_ENVIRONMENT_KEY_PATTERN.test(String(key || ''))
35
+ && Buffer.byteLength(String(value ?? ''), 'utf8') <= UPDATE_HOST_MAX_ENVIRONMENT_VALUE_BYTES;
36
+ }
37
+
38
+ function updateHostEnvironmentPriority(key) {
39
+ const normalized = String(key || '').toUpperCase();
40
+ if (normalized.startsWith('LIVEDESK_')
41
+ || normalized.startsWith('LIVE_DESK_')
42
+ || normalized.startsWith('REMOTE_HUB_')) {
43
+ return 0;
44
+ }
45
+ if (/^(PATH|PATHEXT|SYSTEMROOT|WINDIR|COMSPEC|TEMP|TMP|TMPDIR|HOME|HOMEDRIVE|HOMEPATH|USERPROFILE|APPDATA|LOCALAPPDATA|LANG|LC_ALL|TZ|PORT)$/.test(normalized)
46
+ || normalized.startsWith('NPM_')
47
+ || normalized.startsWith('NODE_')
48
+ || normalized.endsWith('_PROXY')
49
+ || normalized.startsWith('OPENAI_')
50
+ || normalized.startsWith('CODEX_')) {
51
+ return 1;
52
+ }
53
+ return 2;
54
+ }
55
+
56
+ function deduplicateUpdateHostEnvironmentEntries(entries) {
57
+ const byNormalizedKey = new Map();
58
+ for (const [rawKey, rawValue] of entries || []) {
59
+ const key = String(rawKey);
60
+ const value = String(rawValue ?? '');
61
+ const normalizedKey = key.toUpperCase();
62
+ const existing = byNormalizedKey.get(normalizedKey);
63
+ if (!existing || key < existing[0]) {
64
+ byNormalizedKey.set(normalizedKey, [key, value]);
65
+ }
66
+ }
67
+ return [...byNormalizedKey.values()];
68
+ }
69
+
70
+ function buildUpdateHostWorkerEnvironment(baseEnv = {}, requiredEnv = {}) {
71
+ const rawRequiredEntries = Object.entries(requiredEnv || {})
72
+ .map(([key, value]) => [String(key), String(value ?? '')]);
73
+ for (const [key, value] of rawRequiredEntries) {
74
+ if (!isValidUpdateHostEnvironmentEntry(key, value)) {
75
+ throw new Error(`invalid-required-update-host-environment-entry:${key}`);
76
+ }
77
+ }
78
+ const requiredEntries = deduplicateUpdateHostEnvironmentEntries(rawRequiredEntries);
79
+ if (requiredEntries.length > UPDATE_HOST_MAX_ENVIRONMENT_KEYS) {
80
+ throw new Error('invalid-required-update-host-environment-count');
81
+ }
82
+
83
+ const requiredKeys = new Set(requiredEntries.map(([key]) => key.toUpperCase()));
84
+ const availableBaseEntries = UPDATE_HOST_MAX_ENVIRONMENT_KEYS - requiredEntries.length;
85
+ const baseEntries = deduplicateUpdateHostEnvironmentEntries(Object.entries(baseEnv || {})
86
+ .filter(([key, value]) => value != null
87
+ && !requiredKeys.has(String(key).toUpperCase())
88
+ && isValidUpdateHostEnvironmentEntry(key, value))
89
+ .map(([key, value]) => [String(key), String(value)]))
90
+ .sort((left, right) => {
91
+ const priorityDifference = updateHostEnvironmentPriority(left[0])
92
+ - updateHostEnvironmentPriority(right[0]);
93
+ if (priorityDifference) return priorityDifference;
94
+ return left[0] < right[0] ? -1 : (left[0] > right[0] ? 1 : 0);
95
+ })
96
+ .slice(0, availableBaseEntries);
97
+
98
+ return Object.fromEntries([...baseEntries, ...requiredEntries]);
99
+ }
100
+
101
+ function requiredUpdateHostEnvironment(env) {
102
+ const exactRequiredNames = new Set([
103
+ 'INIT_CWD',
104
+ 'NPM_CONFIG_LOCAL_PREFIX',
105
+ 'NPM_CONFIG_WORKSPACES',
106
+ 'NPM_CONFIG_INCLUDE_WORKSPACE_ROOT',
107
+ 'LIVEDESK_NODE_EXECUTABLE',
108
+ 'LIVEDESK_NPX_CLI_PATH',
109
+ 'LIVEDESK_NPX_EXECUTABLE',
110
+ 'LIVEDESK_UPDATE_WAIT_PID',
111
+ 'LIVEDESK_UPDATE_AGENT_PID',
112
+ 'LIVEDESK_UPDATE_STARTER_PID',
113
+ 'LIVEDESK_UPDATE_NEUTRAL_CWD',
114
+ 'LIVEDESK_UPDATE_ORIGINAL_CWD',
115
+ 'LIVEDESK_UPDATE_HOST_AVAILABLE',
116
+ 'LIVEDESK_UPDATE_HOST_ENDPOINT',
117
+ 'LIVEDESK_UPDATE_HOST_STATUS_PATH',
118
+ 'LIVEDESK_UPDATE_HOST_PROTOCOL_VERSION',
119
+ 'LIVEDESK_CLIENT_UPDATE_TARGET_VERSION',
120
+ 'LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION',
121
+ 'LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION',
122
+ 'LIVEDESK_CLIENT_UPDATE_CURRENT_PRODUCT_VERSION',
123
+ 'LIVEDESK_CLIENT_UPDATE_CURRENT_AGENT_VERSION',
124
+ 'LIVEDESK_CLIENT_UPDATE_LOCAL_LAUNCHER',
125
+ 'LIVEDESK_CLIENT_UPDATE_OPERATION_ID',
126
+ 'LIVEDESK_CLIENT_UPDATE_DEADLINE_EPOCH_MS',
127
+ 'LIVEDESK_CLIENT_UPDATE_STATE_PATH',
128
+ 'LIVEDESK_CLIENT_UPDATE_CWD',
129
+ 'LIVEDESK_CLIENT_UPDATE_ARGS_BASE64',
130
+ 'LIVEDESK_CLIENT_PARENT_PID',
131
+ 'LIVEDESK_CLIENT_UPDATE_AGENT_PID',
132
+ 'LIVEDESK_CLIENT_MANAGER',
133
+ 'LIVEDESK_CLIENT_PAIR_TOKEN',
134
+ 'LIVEDESK_DEVICE_ID',
135
+ 'LIVEDESK_CLIENT_SLOT',
136
+ 'LIVEDESK_CLIENT_NAME',
137
+ 'LIVEDESK_CLIENT_ENGINE',
138
+ 'LIVEDESK_CLIENT_AUTH_HOST',
139
+ 'LIVEDESK_CLIENT_AUTH_PORT',
140
+ 'LIVEDESK_CLIENT_RUNTIME_HOST',
141
+ 'LIVEDESK_CLIENT_RUNTIME_PORT',
142
+ 'LIVEDESK_LOCAL_RUNTIME_PORT',
143
+ 'LIVEDESK_CLIENT_STATE_DIR',
144
+ 'LIVEDESK_STATE_DIR',
145
+ 'LIVEDESK_CLIENT_FILES_DIR',
146
+ 'LIVEDESK_CLIENT_TRANSPORT',
147
+ 'LIVEDESK_CLIENT_RELAY',
148
+ 'LIVEDESK_CLIENT_DIRECT_CONNECT_BUDGET_MS',
149
+ 'LIVEDESK_CLIENT_UDP_ENABLED',
150
+ 'LIVEDESK_DIRECT_CONNECT_BUDGET_MS',
151
+ 'LIVEDESK_UDP_ENABLED',
152
+ 'LIVEDESK_CLIENT_HEARTBEAT',
153
+ 'LIVEDESK_CLIENT_THUMBNAIL',
154
+ 'LIVEDESK_CLIENT_LIVE',
155
+ 'LIVEDESK_CLIENT_CONTROL',
156
+ 'LIVEDESK_CLIENT_AUDIO',
157
+ 'LIVEDESK_CLIENT_TASKS',
158
+ 'LIVEDESK_CLIENT_TRACE_FRAMES',
159
+ 'LIVEDESK_CLIENT_FAKE_FRAME',
160
+ 'LIVEDESK_CLIENT_FAKE_THUMBNAIL',
161
+ 'LIVEDESK_CLIENT_REQUIRE_FAST',
162
+ 'LIVEDESK_CLIENT_FAST_DISABLE_DOTNET',
163
+ 'LIVEDESK_CLIENT_FAST_PREFLIGHT_CACHE',
164
+ 'LIVEDESK_CLIENT_PACKAGE_VERSION',
165
+ 'LIVEDESK_NPM_LAUNCHER_VERSION',
166
+ 'LIVEDESK_UNIFIED_LAUNCHER_ENTRY',
167
+ 'LIVEDESK_SKIP_BROWSER_OPEN',
168
+ 'REMOTE_HUB_PORT',
169
+ 'MINDEXEC_REMOTE_AUDIO',
170
+ 'MINDEXEC_REMOTE_CONTROL',
171
+ 'MINDEXEC_REMOTE_ENGINE',
172
+ 'MINDEXEC_REMOTE_FAKE_FRAME',
173
+ 'MINDEXEC_REMOTE_FAKE_THUMBNAIL',
174
+ 'MINDEXEC_REMOTE_FAST_DISABLE_DOTNET',
175
+ 'MINDEXEC_REMOTE_FILES_DIR',
176
+ 'MINDEXEC_REMOTE_LIVE',
177
+ 'MINDEXEC_REMOTE_MANAGER',
178
+ 'MINDEXEC_REMOTE_NAME',
179
+ 'MINDEXEC_REMOTE_PAIR_TOKEN',
180
+ 'MINDEXEC_REMOTE_SLOT',
181
+ 'MINDEXEC_REMOTE_TASKS',
182
+ 'MINDEXEC_REMOTE_THUMBNAIL',
183
+ 'MINDEXEC_REMOTE_TRACE_FRAMES',
184
+ 'MINDEXEC_REMOTE_TRANSPORT'
185
+ ]);
186
+ return Object.fromEntries(Object.entries(env || {}).filter(([key, value]) => {
187
+ const normalized = String(key || '').toUpperCase();
188
+ return value != null
189
+ && isValidUpdateHostEnvironmentEntry(key, value)
190
+ && exactRequiredNames.has(normalized);
191
+ }));
192
+ }
29
193
 
30
194
  function readJson(filePath) {
31
195
  try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return null; }
@@ -284,11 +448,15 @@ async function main() {
284
448
  }
285
449
  env.INIT_CWD = neutralCwd;
286
450
  env.npm_config_local_prefix = neutralCwd;
287
- env.npm_config_workspaces = 'false';
288
- env.npm_config_include_workspace_root = 'false';
289
-
290
- const invocation = buildWorkerInvocation(env);
291
- if (!path.isAbsolute(invocation.command) || !fs.existsSync(invocation.command)) {
451
+ env.npm_config_workspaces = 'false';
452
+ env.npm_config_include_workspace_root = 'false';
453
+
454
+ const invocation = buildWorkerInvocation(env);
455
+ const workerEnvironment = buildUpdateHostWorkerEnvironment(
456
+ env,
457
+ requiredUpdateHostEnvironment(env)
458
+ );
459
+ if (!path.isAbsolute(invocation.command) || !fs.existsSync(invocation.command)) {
292
460
  throw new Error('VuvoDesk could not prepare an absolute Client update worker command for the independent Update Host.');
293
461
  }
294
462
  const result = await submitUpdateHostJob({
@@ -298,7 +466,7 @@ async function main() {
298
466
  command: invocation.command,
299
467
  args: invocation.args,
300
468
  cwd: neutralCwd,
301
- env,
469
+ env: workerEnvironment,
302
470
  resultPath: statePath,
303
471
  requestedAt: new Date().toISOString(),
304
472
  expiresAt: new Date(updateDeadlineEpochMs).toISOString()
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.270",
3
+ "version": "0.1.271",
4
4
  "description": "VuvoDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,10 +42,10 @@
42
42
  "ws": "^8.18.3"
43
43
  },
44
44
  "optionalDependencies": {
45
- "@livedesk/fast-linux-x64": "0.1.465",
46
- "@livedesk/fast-osx-arm64": "0.1.465",
47
- "@livedesk/fast-osx-x64": "0.1.465",
48
- "@livedesk/fast-win-x64": "0.1.465"
45
+ "@livedesk/fast-linux-x64": "0.1.466",
46
+ "@livedesk/fast-osx-arm64": "0.1.466",
47
+ "@livedesk/fast-osx-x64": "0.1.466",
48
+ "@livedesk/fast-win-x64": "0.1.466"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
package/hub/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/hub",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "VuvoDesk local Hub API and browser frame bridge",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -66,9 +66,27 @@ export function isVersionAtLeast(candidate, required) {
66
66
  return !!cleanVersion(candidate) && !!cleanVersion(required) && compareVersions(candidate, required) >= 0;
67
67
  }
68
68
 
69
- function isDedicatedBootstrapFailure(value) {
70
- return /^Update worker exited before terminal proof\b/i.test(String(value || '').trim());
71
- }
69
+ function isDedicatedBootstrapFailure(value) {
70
+ const failure = String(value || '').trim();
71
+ return /^Update worker exited before terminal proof\b/i.test(failure)
72
+ || failure === 'invalid-update-host-environment-entry';
73
+ }
74
+
75
+ function describeCommandResultEvidence(result) {
76
+ const data = result?.data;
77
+ if (!data || typeof data !== 'object') return '';
78
+ const evidence = [];
79
+ const shell = String(data.shell || '').trim().toLowerCase();
80
+ if (/^[a-z0-9._-]{1,32}$/.test(shell)) evidence.push(`shell=${shell}`);
81
+ const exitCode = Number(data.exitCode);
82
+ if (Number.isInteger(exitCode) && Math.abs(exitCode) <= 0x7fffffff) evidence.push(`exit=${exitCode}`);
83
+ if (typeof data.timedOut === 'boolean') evidence.push(`timedOut=${data.timedOut}`);
84
+ const durationMs = Number(data.durationMs);
85
+ if (Number.isFinite(durationMs) && durationMs >= 0 && durationMs <= 24 * 60 * 60_000) {
86
+ evidence.push(`durationMs=${Math.round(durationMs)}`);
87
+ }
88
+ return evidence.length > 0 ? ` [${evidence.join(' ')}]` : '';
89
+ }
72
90
 
73
91
  function encodePowerShell(value) {
74
92
  return Buffer.from(String(value || ''), 'utf16le').toString('base64');
@@ -451,16 +469,29 @@ export function createLiveDeskUpdateManager({
451
469
  return true;
452
470
  };
453
471
 
454
- const dispatchTarget = (target, device, options = {}) => {
455
- const dispatchedAtEpochMs = now();
456
- const requestedTimeoutMs = Math.max(
457
- 10_000,
458
- Math.min(
459
- effectiveTargetTimeoutMs,
460
- Number(options.updateTimeoutMs) || effectiveTargetTimeoutMs
461
- )
462
- );
463
- const updateDeadlineEpochMs = dispatchedAtEpochMs + requestedTimeoutMs;
472
+ const dispatchTarget = (target, device, options = {}) => {
473
+ const dispatchedAtEpochMs = now();
474
+ const requestedDeadlineEpochMs = Date.parse(String(options.deadlineAt || ''));
475
+ const usesExistingDeadline = Number.isFinite(requestedDeadlineEpochMs)
476
+ && requestedDeadlineEpochMs > 0;
477
+ const remainingExistingDeadlineMs = requestedDeadlineEpochMs - dispatchedAtEpochMs;
478
+ if (usesExistingDeadline && remainingExistingDeadlineMs < 10_000) {
479
+ target.state = 'failed';
480
+ target.error = 'client-update-deadline-insufficient';
481
+ return false;
482
+ }
483
+ const requestedTimeoutMs = usesExistingDeadline
484
+ ? Math.min(effectiveTargetTimeoutMs, remainingExistingDeadlineMs)
485
+ : Math.max(
486
+ 10_000,
487
+ Math.min(
488
+ effectiveTargetTimeoutMs,
489
+ Number(options.updateTimeoutMs) || effectiveTargetTimeoutMs
490
+ )
491
+ );
492
+ const updateDeadlineEpochMs = usesExistingDeadline
493
+ ? requestedDeadlineEpochMs
494
+ : dispatchedAtEpochMs + requestedTimeoutMs;
464
495
  const dispatchedSessionId = String(device?.sessionId || '').trim();
465
496
  if (!dispatchedSessionId) {
466
497
  target.state = 'failed';
@@ -784,12 +815,18 @@ export function createLiveDeskUpdateManager({
784
815
  if (!target
785
816
  || target.state !== 'waiting'
786
817
  || !['dedicated', 'legacy-command-run'].includes(target.method)) return;
787
- const result = event?.result;
788
- if (event?.error || result?.ok === false || result?.status === 'failed' || result?.status === 'rejected') {
789
- const failure = String(event.error || result?.error || result?.message || 'client-update-command-failed').slice(0, 500);
790
- if (target.method === 'dedicated'
791
- && target.bootstrapRetryCount === 0
792
- && isDedicatedBootstrapFailure(failure)) {
818
+ const result = event?.result;
819
+ if (event?.error || result?.ok === false || result?.status === 'failed' || result?.status === 'rejected') {
820
+ const failureReason = String(
821
+ event.error || result?.error || result?.message || 'client-update-command-failed'
822
+ ).slice(0, 500);
823
+ const failure = (
824
+ failureReason
825
+ + describeCommandResultEvidence(result)
826
+ ).slice(0, 500);
827
+ if (target.method === 'dedicated'
828
+ && target.bootstrapRetryCount === 0
829
+ && isDedicatedBootstrapFailure(failureReason)) {
793
830
  const retryDevice = connectedClientDevices()
794
831
  .find(device => String(device.deviceId || '') === target.deviceId);
795
832
  const remainingMs = Date.parse(target.deadlineAt || '') - now();
@@ -800,10 +837,10 @@ export function createLiveDeskUpdateManager({
800
837
  target.error = '';
801
838
  let retryResult;
802
839
  try {
803
- retryResult = dispatchTarget(target, retryDevice, {
804
- forceLegacy: true,
805
- updateTimeoutMs: remainingMs
806
- });
840
+ retryResult = dispatchTarget(target, retryDevice, {
841
+ forceLegacy: true,
842
+ deadlineAt: target.deadlineAt
843
+ });
807
844
  } catch (error) {
808
845
  target.error = String(error?.message || error || 'client-update-bootstrap-retry-failed').slice(0, 500);
809
846
  retryResult = false;
@@ -9784,25 +9784,31 @@ export function createRemoteHub(options = {}) {
9784
9784
  return { ok: false, error: 'device-not-connected' };
9785
9785
  }
9786
9786
 
9787
- const command = safeString(options.command, 20000);
9788
- if (!command) return { ok: false, error: 'client-update-command-missing' };
9789
- const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
9790
- const timeoutMs = clampNumber(options.timeoutMs, 1000, 120000, 30000);
9791
- const sent = writeJsonLine(device.socket, {
9787
+ const command = safeString(options.command, 20000);
9788
+ if (!command) return { ok: false, error: 'client-update-command-missing' };
9789
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
9790
+ const timeoutMs = clampNumber(options.timeoutMs, 1000, 120000, 30000);
9791
+ const legacyCommandShell = ['win32', 'windows']
9792
+ .includes(safeString(device.platform, 32).toLowerCase())
9793
+ ? 'cmd'
9794
+ : 'auto';
9795
+ const sent = writeJsonLine(device.socket, {
9792
9796
  type: 'command',
9793
9797
  commandId,
9794
- command: 'command.run',
9795
- payload: {
9796
- command,
9797
- timeoutMs,
9798
- permissionMode: 'full-access',
9798
+ command: 'command.run',
9799
+ payload: {
9800
+ command,
9801
+ shell: legacyCommandShell,
9802
+ timeoutMs,
9803
+ permissionMode: 'full-access',
9799
9804
  // RemoteFast releases before 0.1.170 read command.run inputs
9800
9805
  // from toolArguments, while Node and current Agents accept the
9801
9806
  // direct payload fields. Carry both during the update bridge.
9802
- toolArguments: {
9803
- command,
9804
- timeoutMs
9805
- }
9807
+ toolArguments: {
9808
+ command,
9809
+ shell: legacyCommandShell,
9810
+ timeoutMs
9811
+ }
9806
9812
  },
9807
9813
  issuedAt: new Date().toISOString()
9808
9814
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.645",
4
- "livedeskClientVersion": "0.1.270",
3
+ "version": "0.1.647",
4
+ "livedeskClientVersion": "0.1.271",
5
5
  "buildFlavor": "production",
6
6
  "description": "VuvoDesk Hub and client launcher",
7
7
  "type": "module",
@@ -52,10 +52,10 @@
52
52
  "ws": "^8.18.3"
53
53
  },
54
54
  "optionalDependencies": {
55
- "@livedesk/fast-linux-x64": "0.1.465",
56
- "@livedesk/fast-osx-arm64": "0.1.465",
57
- "@livedesk/fast-osx-x64": "0.1.465",
58
- "@livedesk/fast-win-x64": "0.1.465"
55
+ "@livedesk/fast-linux-x64": "0.1.466",
56
+ "@livedesk/fast-osx-arm64": "0.1.466",
57
+ "@livedesk/fast-osx-x64": "0.1.466",
58
+ "@livedesk/fast-win-x64": "0.1.466"
59
59
  },
60
60
  "publishConfig": {
61
61
  "access": "public"
package/web/dist/app.html CHANGED
@@ -52,13 +52,13 @@
52
52
  .vuvodesk-static-boot [hidden] { display: none; }
53
53
  @keyframes vuvodesk-static-boot-spin { to { transform: rotate(360deg); } }
54
54
  </style>
55
- <script type="module" crossorigin src="/assets/app-DLXscHD0.js" data-vuvodesk-entry></script>
55
+ <script type="module" crossorigin src="/assets/app-CcsIHPoF.js" data-vuvodesk-entry></script>
56
56
  <link rel="modulepreload" crossorigin href="/assets/icons-CacEbnZu.js">
57
57
  <link rel="modulepreload" crossorigin href="/assets/react-Cfedt3N5.js">
58
- <link rel="modulepreload" crossorigin href="/assets/styles-D75uKZbK.js">
58
+ <link rel="modulepreload" crossorigin href="/assets/styles-CcZi_0ll.js">
59
59
  <link rel="modulepreload" crossorigin href="/assets/supabase-C7qjtLN3.js">
60
60
  <link rel="modulepreload" crossorigin href="/assets/frame-lifecycle-esOdP-EY.js">
61
- <link rel="modulepreload" crossorigin href="/assets/App-M0W5ZMPW.js">
61
+ <link rel="modulepreload" crossorigin href="/assets/App-CWoSKFT8.js">
62
62
  <link rel="stylesheet" crossorigin href="/assets/styles-BbMJwyw_.css">
63
63
  <link rel="stylesheet" crossorigin href="/assets/App-DFLBNvIi.css">
64
64
  </head>
@@ -4,7 +4,7 @@
4
4
  "short_name": "VuvoDesk",
5
5
  "description": "Monitor and control your VuvoDesk computers from your phone.",
6
6
  "lang": "en",
7
- "start_url": "/app?pwa=0.1.645",
7
+ "start_url": "/app?pwa=0.1.647",
8
8
  "scope": "/",
9
9
  "display": "standalone",
10
10
  "orientation": "any",
@@ -1 +1 @@
1
- import{h as x,j as t}from"./styles-D75uKZbK.js";import{a as c,a5 as A,k as q,a6 as I,a7 as z}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const E={enabled:!0};function w(a){return{enabled:a?.enabled===!0}}async function L(a,p,f){let l=null;for(let s=0;s<2;s+=1){const g=await x("/api/settings",{signal:f},p);if(g.settings.agent?.enabled===a)return;try{await x("/api/settings",{method:"PATCH",signal:f,body:JSON.stringify({revision:g.revision,agent:{enabled:a}})},p);return}catch(i){if(l=i,s!==0||!String(i instanceof Error?i.message:i).startsWith("409 "))throw i}}throw l}function F({hubUrl:a,showToast:p}){const[f,l]=c.useState(E),[s,g]=c.useState(null),[i,m]=c.useState(""),[j,v]=c.useState(""),[y,d]=c.useState(""),b=c.useRef(0),u=c.useRef(null);c.useEffect(()=>{let e=!0;u.current?.abort();const n=new AbortController;u.current=n;const h=++b.current;return g(null),l(E),m(""),d(""),v(""),x("/api/settings/agent",{signal:n.signal},a).then(o=>{!e||n.signal.aborted||h!==b.current||(g(o.settings),l(w(o.settings)))}).catch(o=>{e&&!n.signal.aborted&&h===b.current&&d(o instanceof Error?o.message:String(o))}),()=>{e=!1,n.abort(),u.current===n&&(u.current=null)}},[a]);const k=(e,n)=>l(h=>({...h,[e]:n})),C=e=>{g(e),l(w(e))},S=()=>({generation:b.current,controller:u.current}),r=e=>e.generation===b.current&&e.controller!==null&&e.controller===u.current&&!e.controller.signal.aborted,R=async()=>{if(!s){d("Agent settings are not available yet.");return}const e=S();if(r(e)){m("save"),d(""),v("");try{if(await L(f.enabled,a,e.controller.signal),!r(e))return;const n=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(n.settings),v("Codex Agent settings saved.")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},D=async()=>{const e=S();if(r(e)){m("test"),d(""),v("");try{const n=await x("/api/settings/agent/test",{method:"POST",signal:e.controller.signal,body:JSON.stringify({})},a);if(!r(e))return;const h=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(h.settings);const o=Number.isFinite(n.connection?.latencyMs)?Math.max(0,Math.round(n.connection.latencyMs)):null;p(`Codex Agent connection verified${o!==null?` in ${o} ms.`:"."}`,"success")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},T=s?.codexInstallation==="installed"&&s.codexAuth==="signed-in",N=s!==null;return t.jsxs("div",{className:"settings-tab-content agent-settings-content",children:[t.jsxs("section",{className:"settings-section-card agent-enable-card",children:[t.jsxs("label",{className:"settings-toggle agent-enable-toggle",children:[t.jsx("input",{type:"checkbox",checked:f.enabled,onChange:e=>k("enabled",e.target.checked),disabled:!N||i!==""}),t.jsx("span",{children:"Enable Codex Agent"})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," Enabled by default. Agent commands use only the Codex account signed in on this Hub."]})]}),y&&t.jsx("div",{className:"agent-settings-alert error",role:"alert",children:y}),j&&t.jsx("div",{className:"agent-settings-alert success",role:"status",children:j}),t.jsxs("section",{className:"settings-section-card",children:[t.jsxs("div",{className:"settings-section-heading",children:[t.jsxs("div",{children:[t.jsx("span",{children:"Connection"}),t.jsx("h3",{children:"Codex SDK / CLI"})]}),t.jsx(q,{size:20})]}),t.jsxs("div",{className:"agent-connection-row",children:[t.jsx("span",{className:`agent-connection-dot ${T?"connected":s?.codexAuth==="not-signed-in"?"unavailable":""}`}),t.jsx("span",{children:s?.codexInstallation==="not-installed"?"Codex SDK not installed":s?.codexAuth==="signed-in"?"Codex CLI signed in":s?.codexAuth==="not-signed-in"?"Codex CLI sign-in required":"Codex status not tested"}),t.jsxs("button",{className:"settings-reset",onClick:()=>{D()},disabled:i!=="",type:"button",children:[t.jsx(I,{size:15})," ",i==="test"?"Testing":"Test connection"]})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," The Hub uses the installed Codex SDK / CLI to plan approved commands and run them through VuvoDesk Agent tools on selected computers. VuvoDesk never asks for separate credentials."]})]}),t.jsx("div",{className:"settings-action-row agent-settings-actions",children:t.jsxs("button",{className:"text-button primary",onClick:()=>{R()},disabled:!N||i!=="",type:"button",children:[t.jsx(z,{size:15})," ",i==="save"?"Saving":"Save Agent settings"]})})]})}export{F as AgentSettingsTab};
1
+ import{h as x,j as t}from"./styles-CcZi_0ll.js";import{a as c,a5 as A,k as q,a6 as I,a7 as z}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const E={enabled:!0};function w(a){return{enabled:a?.enabled===!0}}async function L(a,p,f){let l=null;for(let s=0;s<2;s+=1){const g=await x("/api/settings",{signal:f},p);if(g.settings.agent?.enabled===a)return;try{await x("/api/settings",{method:"PATCH",signal:f,body:JSON.stringify({revision:g.revision,agent:{enabled:a}})},p);return}catch(i){if(l=i,s!==0||!String(i instanceof Error?i.message:i).startsWith("409 "))throw i}}throw l}function F({hubUrl:a,showToast:p}){const[f,l]=c.useState(E),[s,g]=c.useState(null),[i,m]=c.useState(""),[j,v]=c.useState(""),[y,d]=c.useState(""),b=c.useRef(0),u=c.useRef(null);c.useEffect(()=>{let e=!0;u.current?.abort();const n=new AbortController;u.current=n;const h=++b.current;return g(null),l(E),m(""),d(""),v(""),x("/api/settings/agent",{signal:n.signal},a).then(o=>{!e||n.signal.aborted||h!==b.current||(g(o.settings),l(w(o.settings)))}).catch(o=>{e&&!n.signal.aborted&&h===b.current&&d(o instanceof Error?o.message:String(o))}),()=>{e=!1,n.abort(),u.current===n&&(u.current=null)}},[a]);const k=(e,n)=>l(h=>({...h,[e]:n})),C=e=>{g(e),l(w(e))},S=()=>({generation:b.current,controller:u.current}),r=e=>e.generation===b.current&&e.controller!==null&&e.controller===u.current&&!e.controller.signal.aborted,R=async()=>{if(!s){d("Agent settings are not available yet.");return}const e=S();if(r(e)){m("save"),d(""),v("");try{if(await L(f.enabled,a,e.controller.signal),!r(e))return;const n=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(n.settings),v("Codex Agent settings saved.")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},D=async()=>{const e=S();if(r(e)){m("test"),d(""),v("");try{const n=await x("/api/settings/agent/test",{method:"POST",signal:e.controller.signal,body:JSON.stringify({})},a);if(!r(e))return;const h=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(h.settings);const o=Number.isFinite(n.connection?.latencyMs)?Math.max(0,Math.round(n.connection.latencyMs)):null;p(`Codex Agent connection verified${o!==null?` in ${o} ms.`:"."}`,"success")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},T=s?.codexInstallation==="installed"&&s.codexAuth==="signed-in",N=s!==null;return t.jsxs("div",{className:"settings-tab-content agent-settings-content",children:[t.jsxs("section",{className:"settings-section-card agent-enable-card",children:[t.jsxs("label",{className:"settings-toggle agent-enable-toggle",children:[t.jsx("input",{type:"checkbox",checked:f.enabled,onChange:e=>k("enabled",e.target.checked),disabled:!N||i!==""}),t.jsx("span",{children:"Enable Codex Agent"})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," Enabled by default. Agent commands use only the Codex account signed in on this Hub."]})]}),y&&t.jsx("div",{className:"agent-settings-alert error",role:"alert",children:y}),j&&t.jsx("div",{className:"agent-settings-alert success",role:"status",children:j}),t.jsxs("section",{className:"settings-section-card",children:[t.jsxs("div",{className:"settings-section-heading",children:[t.jsxs("div",{children:[t.jsx("span",{children:"Connection"}),t.jsx("h3",{children:"Codex SDK / CLI"})]}),t.jsx(q,{size:20})]}),t.jsxs("div",{className:"agent-connection-row",children:[t.jsx("span",{className:`agent-connection-dot ${T?"connected":s?.codexAuth==="not-signed-in"?"unavailable":""}`}),t.jsx("span",{children:s?.codexInstallation==="not-installed"?"Codex SDK not installed":s?.codexAuth==="signed-in"?"Codex CLI signed in":s?.codexAuth==="not-signed-in"?"Codex CLI sign-in required":"Codex status not tested"}),t.jsxs("button",{className:"settings-reset",onClick:()=>{D()},disabled:i!=="",type:"button",children:[t.jsx(I,{size:15})," ",i==="test"?"Testing":"Test connection"]})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," The Hub uses the installed Codex SDK / CLI to plan approved commands and run them through VuvoDesk Agent tools on selected computers. VuvoDesk never asks for separate credentials."]})]}),t.jsx("div",{className:"settings-action-row agent-settings-actions",children:t.jsxs("button",{className:"text-button primary",onClick:()=>{R()},disabled:!N||i!=="",type:"button",children:[t.jsx(z,{size:15})," ",i==="save"?"Saving":"Save Agent settings"]})})]})}export{F as AgentSettingsTab};
@@ -1,4 +1,4 @@
1
- import{j as t,e as ht,h as F}from"./styles-D75uKZbK.js";import{a as d,m as yt,n as Ce,o as vt,l as ee,X as xe,p as wt,q as kt,s as Je,B as bt,t as Ct,u as Se,S as H,v as xt,w as St,x as _e,b as jt,y as At,k as Ze,z as Rt,N as Nt,F as Pt,H as It,I as Tt,J as Et,U as ue,c as Xe}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const Ot=d.forwardRef(function({value:s,onChange:n,onSubmit:r,onOpenSuggestedTasks:i,suggestedOpen:f=!1,suggestedTaskCount:c,disabled:v=!1,placeholder:S="Ask VuvoDesk to work across your machines...",compact:g=!1},x){return t.jsxs("form",{className:"agents-composer",onSubmit:w=>{w.preventDefault(),r()},children:[t.jsx("button",{className:`agents-suggested-trigger ${f?"active":""}`,type:"button","aria-label":c===void 0?"Open Suggested tasks":`Open Suggested tasks (${c} available)`,"aria-expanded":f,onClick:i,disabled:v,children:t.jsx(yt,{size:17})}),t.jsx("textarea",{ref:x,value:s,onChange:w=>n(w.target.value.slice(0,4e3)),placeholder:S,"aria-label":"Ask VuvoDesk",rows:g?1:2,disabled:v}),t.jsxs("button",{className:"agents-run-button",type:"submit",disabled:v||!s.trim(),children:[v?t.jsx(Ce,{className:"spin",size:16}):t.jsx(vt,{size:16}),v?"Running":"Run"]})]})}),$t=["read","processControl","serviceControl","applicationControl","fileRead","fileWrite","fileDelete","shell","script","softwareInstall","network","systemPower","systemConfiguration","userAccount"];function de(e){return e.deviceName?.trim()||e.hostname?.trim()||e.deviceId}function pe(e){return e.connected!==!0?!1:e.synthetic===!0?!0:!e.channels||e.channels.control===!0}function Dt(e){return e==="running"||e==="completed"||e==="failed"||e==="cancelled"?e:"queued"}function _t(e){switch(e){case"process.list":return"Process list";case"system.health":return"System health";case"gpu.status":return"GPU status";case"disk.status":return"Disk status";case"service.status":return"Service status";case"diagnostics.collect":return"Diagnostics";case"process.control":return"Process control";case"service.control":return"Service control";case"application.launch":return"Launch application";case"application.close":return"Close application";case"file.read":return"Read file";case"file.write":return"Write file";case"file.delete":return"Delete file";case"file.list":return"List directory";case"command.run":return"Run command";case"script.run":return"Run script";case"software.install":return"Install software";case"network.status":return"Network status";case"system.power":return"Power action";case"system.configure":return"System configuration";case"logs.collect":return"Collect logs"}}const zt=64*1024,ze="[Earlier log output hidden by PWA display limit.]",Qe=new TextEncoder,Mt=new TextDecoder("utf-8",{fatal:!0}),Me=16*1024,Lt=200,Le="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Ft="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Bt="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|credential|password|secret|cookie)",Ut=/\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{8,}|(?:AKIA|ASIA)[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|npm_[A-Za-z0-9]{8,}|xox[a-z]-[A-Za-z0-9-]{8,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,})\b/g;function qt(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}function Fe(e){return Qe.encode(e).byteLength}function Be(e,s){const n=Qe.encode(e);if(n.byteLength<=s)return{value:e,truncated:!1};let r=n.byteLength-s;for(;r<n.byteLength&&(n[r]&192)===128;)r+=1;let i=Mt.decode(n.subarray(r));const f=i.indexOf(`
1
+ import{j as t,e as ht,h as F}from"./styles-CcZi_0ll.js";import{a as d,m as yt,n as Ce,o as vt,l as ee,X as xe,p as wt,q as kt,s as Je,B as bt,t as Ct,u as Se,S as H,v as xt,w as St,x as _e,b as jt,y as At,k as Ze,z as Rt,N as Nt,F as Pt,H as It,I as Tt,J as Et,U as ue,c as Xe}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const Ot=d.forwardRef(function({value:s,onChange:n,onSubmit:r,onOpenSuggestedTasks:i,suggestedOpen:f=!1,suggestedTaskCount:c,disabled:v=!1,placeholder:S="Ask VuvoDesk to work across your machines...",compact:g=!1},x){return t.jsxs("form",{className:"agents-composer",onSubmit:w=>{w.preventDefault(),r()},children:[t.jsx("button",{className:`agents-suggested-trigger ${f?"active":""}`,type:"button","aria-label":c===void 0?"Open Suggested tasks":`Open Suggested tasks (${c} available)`,"aria-expanded":f,onClick:i,disabled:v,children:t.jsx(yt,{size:17})}),t.jsx("textarea",{ref:x,value:s,onChange:w=>n(w.target.value.slice(0,4e3)),placeholder:S,"aria-label":"Ask VuvoDesk",rows:g?1:2,disabled:v}),t.jsxs("button",{className:"agents-run-button",type:"submit",disabled:v||!s.trim(),children:[v?t.jsx(Ce,{className:"spin",size:16}):t.jsx(vt,{size:16}),v?"Running":"Run"]})]})}),$t=["read","processControl","serviceControl","applicationControl","fileRead","fileWrite","fileDelete","shell","script","softwareInstall","network","systemPower","systemConfiguration","userAccount"];function de(e){return e.deviceName?.trim()||e.hostname?.trim()||e.deviceId}function pe(e){return e.connected!==!0?!1:e.synthetic===!0?!0:!e.channels||e.channels.control===!0}function Dt(e){return e==="running"||e==="completed"||e==="failed"||e==="cancelled"?e:"queued"}function _t(e){switch(e){case"process.list":return"Process list";case"system.health":return"System health";case"gpu.status":return"GPU status";case"disk.status":return"Disk status";case"service.status":return"Service status";case"diagnostics.collect":return"Diagnostics";case"process.control":return"Process control";case"service.control":return"Service control";case"application.launch":return"Launch application";case"application.close":return"Close application";case"file.read":return"Read file";case"file.write":return"Write file";case"file.delete":return"Delete file";case"file.list":return"List directory";case"command.run":return"Run command";case"script.run":return"Run script";case"software.install":return"Install software";case"network.status":return"Network status";case"system.power":return"Power action";case"system.configure":return"System configuration";case"logs.collect":return"Collect logs"}}const zt=64*1024,ze="[Earlier log output hidden by PWA display limit.]",Qe=new TextEncoder,Mt=new TextDecoder("utf-8",{fatal:!0}),Me=16*1024,Lt=200,Le="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Ft="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Bt="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|credential|password|secret|cookie)",Ut=/\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{8,}|(?:AKIA|ASIA)[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|npm_[A-Za-z0-9]{8,}|xox[a-z]-[A-Za-z0-9-]{8,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,})\b/g;function qt(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}function Fe(e){return Qe.encode(e).byteLength}function Be(e,s){const n=Qe.encode(e);if(n.byteLength<=s)return{value:e,truncated:!1};let r=n.byteLength-s;for(;r<n.byteLength&&(n[r]&192)===128;)r+=1;let i=Mt.decode(n.subarray(r));const f=i.indexOf(`
2
2
  `);return f>=0&&f<i.length-1&&(i=i.slice(f+1)),{value:i,truncated:!0}}function et(e){return e.replace(/-----BEGIN [^-\r\n]*PRIVATE KEY-----[\s\S]*?-----END [^-\r\n]*PRIVATE KEY-----/gi,"[REDACTED PRIVATE KEY]").replace(/\b(?:proxy-)?authorization\s*[:=]\s*[^\r\n]*|\b(?:set-)?cookie\s*[:=]\s*[^\r\n]*/gi,"credential-header=[REDACTED]").replace(/\bBearer\s+(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,"Bearer [REDACTED]").replace(new RegExp(`("${Le}"\\s*:\\s*")[^"\\r\\n]*(")`,"gi"),"$1[REDACTED]$2").replace(new RegExp(`('${Le}'\\s*:\\s*')[^'\\r\\n]*(')`,"gi"),"$1[REDACTED]$2").replace(new RegExp(`((?:[A-Za-z0-9.-]+[_-])?${Ft}\\s*[:=]\\s*)(?!\\[REDACTED\\])[^\\s,;&]+`,"gi"),"$1[REDACTED]").replace(new RegExp(`([?&]${Bt}=)[^&\\s]+`,"gi"),"$1[REDACTED]").replace(/\b(?:eyJ|[A-Za-z0-9_-]{8,}\.)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,"[REDACTED TOKEN]").replace(Ut,"[REDACTED KEY]")}function Gt(e){return typeof e!="string"?"source unknown":et(e).replace(/[\u0000-\u001f\u007f]/g," ").trim().slice(0,64)||"source unknown"}function Ue(e){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=0?Math.floor(e):null}function Vt(e){if(typeof e!="string"||e.length===0)return{output:"",displayTruncated:!1};const s=Be(e.replace(/\r\n?/g,`
3
3
  `),zt);let n=et(s.value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g," ").trim(),r=s.truncated;const i=n?n.split(`
4
4
  `):[];if(i.length>Lt-1&&(n=i.slice(-199).join(`