livedesk 0.1.677 → 0.1.678

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 (29) hide show
  1. package/bootstrap/hub-self-agent.js +8 -8
  2. package/electron/desktop-auto-start.mjs +13 -1
  3. package/electron/desktop-clipboard-owner.mjs +1 -1
  4. package/electron/electron-builder.mac-arm64-dev.yml +0 -4
  5. package/electron/electron-builder.mac-arm64.yml +3 -2
  6. package/electron/electron-builder.mac-x64-dev.yml +0 -4
  7. package/electron/electron-builder.mac-x64.yml +3 -2
  8. package/electron/electron-builder.yml +8 -3
  9. package/electron/main.mjs +25 -14
  10. package/electron/oauth-pkce.mjs +11 -2
  11. package/package.json +1 -1
  12. package/web/dist/app.html +1 -1
  13. package/web/dist/app.webmanifest +1 -1
  14. package/web/dist/assets/{AgentSettingsTab-CJVcqwK4.js → AgentSettingsTab-WHlPq5WO.js} +1 -1
  15. package/web/dist/assets/{AgentsPage-DiwWdXn1.js → AgentsPage-CdnqnRRy.js} +1 -1
  16. package/web/dist/assets/{CaptureGallery-CHPGRuhP.js → CaptureGallery-r0hgHzWE.js} +1 -1
  17. package/web/dist/assets/{HubApp-Bu2-NnqZ.js → HubApp-D1-hYJ7F.js} +3 -3
  18. package/web/dist/assets/{MonitorStackIcon-D7bHJ5H2.js → MonitorStackIcon-D1k9mjDe.js} +1 -1
  19. package/web/dist/assets/{SettingsPage-BeRqzRSb.js → SettingsPage-BML3sxcB.js} +1 -1
  20. package/web/dist/assets/{ShareFilesPage-B62qFADD.js → ShareFilesPage-5yMuAAX8.js} +1 -1
  21. package/web/dist/assets/{SupportPage-gaJlNoST.js → SupportPage-BYJoKypB.js} +1 -1
  22. package/web/dist/assets/{VuvoDeskApp-BFW79ykd.js → VuvoDeskApp-w5anguIc.js} +1 -1
  23. package/web/dist/assets/{app-Ds4GFCMd.js → app-jQLAg5d2.js} +2 -2
  24. package/web/dist/assets/{main-B91B01m-.js → main-BB-Bb6Vm.js} +2 -2
  25. package/web/dist/desktop-auth-callback.html +1 -1
  26. package/web/dist/desktop-auth-callback.js +11 -7
  27. package/web/dist/index.html +2 -2
  28. package/web/dist/sw.js +2 -2
  29. package/web/dist/vuvodesk-build-evidence.json +42 -42
@@ -119,7 +119,7 @@ export function createHubSelfAgentSupervisor({
119
119
  Math.max(100, Number(restartMaxDelayMs) || 8_000),
120
120
  Math.max(100, Number(restartBaseDelayMs) || 1_000) * (2 ** Math.min(3, consecutiveFailures - 1))
121
121
  );
122
- report(`[LiveDesk Hub] Self Agent stopped unexpectedly; retrying in ${delayMs}ms.`, true);
122
+ report(`[VuvoDesk Hub] Self Agent stopped unexpectedly; retrying in ${delayMs}ms.`, true);
123
123
  restartTimer = setTimeout(() => {
124
124
  restartTimer = null;
125
125
  start();
@@ -136,7 +136,7 @@ export function createHubSelfAgentSupervisor({
136
136
  try {
137
137
  spawned = spawnImpl(launch.command, launch.args, launch.options);
138
138
  } catch (error) {
139
- report(`[LiveDesk Hub] Self Agent launch failed: ${error?.message || error}.`, true);
139
+ report(`[VuvoDesk Hub] Self Agent launch failed: ${error?.message || error}.`, true);
140
140
  scheduleRestart();
141
141
  return false;
142
142
  }
@@ -148,13 +148,13 @@ export function createHubSelfAgentSupervisor({
148
148
  spawned.on?.('message', message => {
149
149
  if (message?.type !== SELF_AGENT_READY_MESSAGE) return;
150
150
  consecutiveFailures = 0;
151
- report(`[LiveDesk Hub] Self Agent ready pid=${spawned.pid || 'unknown'} manager=${launch.manager}.`);
151
+ report(`[VuvoDesk Hub] Self Agent ready pid=${spawned.pid || 'unknown'} manager=${launch.manager}.`);
152
152
  });
153
153
  spawned.once?.('error', error => {
154
154
  if (settled) return;
155
155
  settled = true;
156
156
  if (child === spawned) child = null;
157
- report(`[LiveDesk Hub] Self Agent process error: ${error?.message || error}.`, true);
157
+ report(`[VuvoDesk Hub] Self Agent process error: ${error?.message || error}.`, true);
158
158
  scheduleRestart();
159
159
  });
160
160
  spawned.once?.('exit', (code, signal) => {
@@ -163,13 +163,13 @@ export function createHubSelfAgentSupervisor({
163
163
  if (child === spawned) child = null;
164
164
  if (!stopping) {
165
165
  report(
166
- `[LiveDesk Hub] Self Agent exited code=${code ?? 'none'} signal=${signal || 'none'}.`,
166
+ `[VuvoDesk Hub] Self Agent exited code=${code ?? 'none'} signal=${signal || 'none'}.`,
167
167
  true
168
168
  );
169
169
  scheduleRestart();
170
170
  }
171
171
  });
172
- report(`[LiveDesk Hub] Starting Self Agent pid=${spawned.pid || 'pending'} manager=${launch.manager}.`);
172
+ report(`[VuvoDesk Hub] Starting Self Agent pid=${spawned.pid || 'pending'} manager=${launch.manager}.`);
173
173
  return true;
174
174
  };
175
175
 
@@ -202,7 +202,7 @@ export function createHubSelfAgentSupervisor({
202
202
  reason: normalizeText(reason).slice(0, 120)
203
203
  });
204
204
  } catch (error) {
205
- report(`[LiveDesk Hub] Self Agent shutdown message failed: ${error?.message || error}.`, true);
205
+ report(`[VuvoDesk Hub] Self Agent shutdown message failed: ${error?.message || error}.`, true);
206
206
  }
207
207
  hardStopTimer = setTimeout(() => {
208
208
  if (ownedChild.exitCode !== null || ownedChild.signalCode) {
@@ -210,7 +210,7 @@ export function createHubSelfAgentSupervisor({
210
210
  return;
211
211
  }
212
212
  report(
213
- `[LiveDesk Hub] Self Agent pid=${ownedChild.pid || 'unknown'} exceeded graceful shutdown; force-stopping that exact child root.`,
213
+ `[VuvoDesk Hub] Self Agent pid=${ownedChild.pid || 'unknown'} exceeded graceful shutdown; force-stopping that exact child root.`,
214
214
  true
215
215
  );
216
216
  try {
@@ -3,7 +3,8 @@ import { homedir } from 'node:os';
3
3
  import { dirname, join, resolve, win32 } from 'node:path';
4
4
 
5
5
  export const DESKTOP_AUTO_START_ARGUMENT = '--autostart';
6
- export const DESKTOP_AUTO_START_ENTRY_NAME = 'LiveDesk Desktop';
6
+ export const DESKTOP_AUTO_START_ENTRY_NAME = 'VuvoDesk Desktop';
7
+ export const LEGACY_DESKTOP_AUTO_START_ENTRY_NAMES = Object.freeze(['LiveDesk Desktop']);
7
8
  const DESKTOP_AUTO_START_PREFERENCE_VERSION = 1;
8
9
  const SUPPORTED_PLATFORMS = new Set(['win32', 'darwin', 'linux']);
9
10
  const AUTO_START_LOG_ORIGINS = new Set(['startup', 'settings', 'tray']);
@@ -165,6 +166,17 @@ export function createDesktopAutoStartOwner({
165
166
  return;
166
167
  }
167
168
  if (platform === 'win32') {
169
+ for (const legacyName of LEGACY_DESKTOP_AUTO_START_ENTRY_NAMES) {
170
+ app.setLoginItemSettings({
171
+ openAtLogin: false,
172
+ name: legacyName,
173
+ ...windowsLoginOptions
174
+ });
175
+ }
176
+ log(
177
+ `desktop-auto-start event=legacy-cleanup platform=win32 attempted=true count=${LEGACY_DESKTOP_AUTO_START_ENTRY_NAMES.length}`,
178
+ { source: 'desktop-auto-start' }
179
+ );
168
180
  app.setLoginItemSettings({
169
181
  openAtLogin: enabled,
170
182
  name: DESKTOP_AUTO_START_ENTRY_NAME,
@@ -361,7 +361,7 @@ export function createDesktopClipboardOwner({
361
361
  writeOwners.clear();
362
362
  // File clipboard entries refer to these exact local paths. Preserve the
363
363
  // two bounded committed stages so Explorer/Finder can still paste after
364
- // the LiveDesk window is hidden or the app exits.
364
+ // the VuvoDesk window is hidden or the app exits.
365
365
  };
366
366
 
367
367
  return {
@@ -4,10 +4,6 @@ directories:
4
4
  output: dist/desktop-dev
5
5
  extraMetadata:
6
6
  desktopUpdateChannel: dev
7
- mac:
8
- identity: "-"
9
- hardenedRuntime: false
10
- notarize: false
11
7
  publish:
12
8
  provider: generic
13
9
  url: https://livedesk-desktop-updates.lovecrdm.workers.dev/dev/macos/arm64
@@ -1,5 +1,6 @@
1
- extends: file:packages/vuvodesk/electron/electron-builder.yml
2
- files:
1
+ extends: file:packages/vuvodesk/electron/electron-builder.yml
2
+ appId: com.gn.vuvodesk
3
+ files:
3
4
  - bin/**
4
5
  - bootstrap/**
5
6
  - client/**
@@ -4,10 +4,6 @@ directories:
4
4
  output: dist/desktop-dev
5
5
  extraMetadata:
6
6
  desktopUpdateChannel: dev
7
- mac:
8
- identity: "-"
9
- hardenedRuntime: false
10
- notarize: false
11
7
  publish:
12
8
  provider: generic
13
9
  url: https://livedesk-desktop-updates.lovecrdm.workers.dev/dev/macos/x64
@@ -1,5 +1,6 @@
1
- extends: file:packages/vuvodesk/electron/electron-builder.yml
2
- files:
1
+ extends: file:packages/vuvodesk/electron/electron-builder.yml
2
+ appId: com.gn.vuvodesk
3
+ files:
3
4
  - bin/**
4
5
  - bootstrap/**
5
6
  - client/**
@@ -1,6 +1,11 @@
1
- appId: com.livedesk.desktop
2
- productName: VuvoDesk
3
- artifactName: VuvoDesk-${version}-${arch}.${ext}
1
+ appId: com.livedesk.desktop
2
+ productName: VuvoDesk
3
+ artifactName: VuvoDesk-${version}-${arch}.${ext}
4
+ protocols:
5
+ - name: VuvoDesk
6
+ schemes:
7
+ - vuvodesk
8
+ - livedesk
4
9
  asar: true
5
10
  beforePack: packages/vuvodesk/electron/before-pack.cjs
6
11
  afterPack: packages/vuvodesk/electron/after-pack.cjs
package/electron/main.mjs CHANGED
@@ -61,6 +61,7 @@ import { createDesktopAutoStartOwner } from './desktop-auto-start.mjs';
61
61
  app.commandLine.appendSwitch('disable-features', 'AudioServiceOutOfProcess');
62
62
 
63
63
  const APP_NAME = 'VuvoDesk Desktop';
64
+ const DESKTOP_PROTOCOL_SCHEMES = Object.freeze(['vuvodesk', 'livedesk']);
64
65
  const appWindowTitle = () => `${APP_NAME} · ${app.getVersion()}`;
65
66
  const LOCAL_RUNTIME_PORT = Number(process.env.LIVEDESK_LOCAL_RUNTIME_PORT || 5179);
66
67
  const LOCAL_RUNTIME_URL = `http://127.0.0.1:${Number.isInteger(LOCAL_RUNTIME_PORT) && LOCAL_RUNTIME_PORT > 0 ? LOCAL_RUNTIME_PORT : 5179}/`;
@@ -243,10 +244,12 @@ function isAllowedRuntimeUrl(value) {
243
244
  }
244
245
  }
245
246
 
246
- function isLiveDeskAuthCallbackUrl(value) {
247
- try {
248
- const url = new URL(String(value || ''));
249
- return url.protocol === 'livedesk:' && url.hostname === 'auth' && url.pathname === '/callback';
247
+ function isDesktopAuthCallbackUrl(value) {
248
+ try {
249
+ const url = new URL(String(value || ''));
250
+ return DESKTOP_PROTOCOL_SCHEMES.includes(url.protocol.replace(/:$/, ''))
251
+ && url.hostname === 'auth'
252
+ && url.pathname === '/callback';
250
253
  } catch {
251
254
  return false;
252
255
  }
@@ -1137,9 +1140,11 @@ async function beginGoogleAuth() {
1137
1140
  }
1138
1141
  }
1139
1142
 
1140
- async function handleProtocolUrl(rawUrl) {
1141
- if (!isLiveDeskAuthCallbackUrl(rawUrl)) return false;
1142
- const request = pendingOAuth;
1143
+ async function handleProtocolUrl(rawUrl) {
1144
+ if (!isDesktopAuthCallbackUrl(rawUrl)) return false;
1145
+ const callbackScheme = new URL(String(rawUrl)).protocol.replace(/:$/, '');
1146
+ log(`desktop-protocol event=callback scheme=${callbackScheme}`, { source: 'desktop-protocol' });
1147
+ const request = pendingOAuth;
1143
1148
  if (!request) return true;
1144
1149
  if (Date.now() - request.startedAt > OAUTH_PENDING_TTL_MS) {
1145
1150
  pendingOAuth = null;
@@ -1507,8 +1512,8 @@ function createTray() {
1507
1512
  }
1508
1513
 
1509
1514
  app.on('open-url', (event, url) => {
1510
- event.preventDefault();
1511
- if (!isLiveDeskAuthCallbackUrl(url)) return;
1515
+ event.preventDefault();
1516
+ if (!isDesktopAuthCallbackUrl(url)) return;
1512
1517
  if (app.isReady()) void handleProtocolUrl(url);
1513
1518
  else pendingProtocolUrl = url;
1514
1519
  });
@@ -1522,13 +1527,19 @@ electronAutoUpdater.on('before-quit-for-update', () => {
1522
1527
  if (!app.requestSingleInstanceLock()) {
1523
1528
  app.quit();
1524
1529
  } else {
1525
- app.on('second-instance', (_event, commandLine) => {
1526
- const protocolUrl = commandLine.find(argument => isLiveDeskAuthCallbackUrl(argument));
1530
+ app.on('second-instance', (_event, commandLine) => {
1531
+ const protocolUrl = commandLine.find(argument => isDesktopAuthCallbackUrl(argument));
1527
1532
  if (protocolUrl) void handleProtocolUrl(protocolUrl);
1528
1533
  showWindow();
1529
- });
1534
+ });
1530
1535
  app.whenReady().then(async () => {
1531
- app.setAsDefaultProtocolClient('livedesk');
1536
+ for (const scheme of DESKTOP_PROTOCOL_SCHEMES) {
1537
+ const registered = app.setAsDefaultProtocolClient(scheme);
1538
+ log(`desktop-protocol event=register scheme=${scheme} registered=${registered === true}`, {
1539
+ source: 'desktop-protocol',
1540
+ level: registered === true ? 'info' : 'warn'
1541
+ });
1542
+ }
1532
1543
  removeLegacyStartupEntries();
1533
1544
  migrateLegacyPlaintextSession();
1534
1545
  desktopAutoStartOwner = createDesktopAutoStartOwner({
@@ -1630,7 +1641,7 @@ if (!app.requestSingleInstanceLock()) {
1630
1641
  pendingOAuth = { state, codeVerifier, authorizationUrl: '', startedAt: Date.now(), exchanging: false };
1631
1642
  }
1632
1643
  }
1633
- const initialProtocolUrl = pendingProtocolUrl || process.argv.find(argument => isLiveDeskAuthCallbackUrl(argument));
1644
+ const initialProtocolUrl = pendingProtocolUrl || process.argv.find(argument => isDesktopAuthCallbackUrl(argument));
1634
1645
  pendingProtocolUrl = '';
1635
1646
  if (initialProtocolUrl) void handleProtocolUrl(initialProtocolUrl);
1636
1647
  });
@@ -1,7 +1,14 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import { AUTH_REQUEST_TIMEOUT_MS, fetchAuthResponse } from '../runtime-core/src/auth-http.js';
3
3
 
4
- export { AUTH_REQUEST_TIMEOUT_MS } from '../runtime-core/src/auth-http.js';
4
+ export { AUTH_REQUEST_TIMEOUT_MS } from '../runtime-core/src/auth-http.js';
5
+
6
+ export const VUVODESK_DESKTOP_AUTH_SCHEME = 'vuvodesk:';
7
+ export const LEGACY_DESKTOP_AUTH_SCHEME = 'livedesk:';
8
+ const TRUSTED_DESKTOP_AUTH_SCHEMES = new Set([
9
+ VUVODESK_DESKTOP_AUTH_SCHEME,
10
+ LEGACY_DESKTOP_AUTH_SCHEME
11
+ ]);
5
12
 
6
13
  function base64Url(value) {
7
14
  return Buffer.from(value).toString('base64url');
@@ -47,7 +54,9 @@ export function parsePkceCallbackUrl(value, expectedState, { allowMissingState =
47
54
  } catch {
48
55
  throw new Error('oauth-callback-url-invalid');
49
56
  }
50
- if (url.protocol !== 'livedesk:' || url.hostname !== 'auth' || url.pathname !== '/callback') {
57
+ if (!TRUSTED_DESKTOP_AUTH_SCHEMES.has(url.protocol)
58
+ || url.hostname !== 'auth'
59
+ || url.pathname !== '/callback') {
51
60
  throw new Error('oauth-callback-url-untrusted');
52
61
  }
53
62
  const state = url.searchParams.get('state') || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.677",
3
+ "version": "0.1.678",
4
4
  "livedeskClientVersion": "0.1.274",
5
5
  "buildFlavor": "production",
6
6
  "description": "VuvoDesk Hub and client launcher",
package/web/dist/app.html CHANGED
@@ -52,7 +52,7 @@
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-Ds4GFCMd.js" data-vuvodesk-entry></script>
55
+ <script type="module" crossorigin src="/assets/app-jQLAg5d2.js" data-vuvodesk-entry></script>
56
56
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-DD1OZmLC.js">
57
57
  <link rel="modulepreload" crossorigin href="/assets/pwa-bootstrap-ljOQTt2e.js">
58
58
  </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.677",
7
+ "start_url": "/app?pwa=0.1.678",
8
8
  "scope": "/",
9
9
  "display": "standalone",
10
10
  "orientation": "any",
@@ -1 +1 @@
1
- import{j as t}from"./pwa-startup-DvbrYEQi.js";import{r as c,d as A,S as q,T as I,e as z}from"./icons-CuXJn-Hz.js";import{h as x}from"./MonitorStackIcon-D7bHJ5H2.js";import"./pwa-bootstrap-ljOQTt2e.js";const E={enabled:!0};function w(i){return{enabled:i?.enabled===!0}}async function L(i,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===i)return;try{await x("/api/settings",{method:"PATCH",signal:f,body:JSON.stringify({revision:g.revision,agent:{enabled:i}})},p);return}catch(a){if(l=a,s!==0||!String(a instanceof Error?a.message:a).startsWith("409 "))throw a}}throw l}function H({hubUrl:i,showToast:p}){const[f,l]=c.useState(E),[s,g]=c.useState(null),[a,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},i).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)}},[i]);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,i,e.controller.signal),!r(e))return;const n=await x("/api/settings/agent",{signal:e.controller.signal},i);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("")}}},T=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({})},i);if(!r(e))return;const h=await x("/api/settings/agent",{signal:e.controller.signal},i);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("")}}},D=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||a!==""}),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 ${D?"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:()=>{T()},disabled:a!=="",type:"button",children:[t.jsx(I,{size:15})," ",a==="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||a!=="",type:"button",children:[t.jsx(z,{size:15})," ",a==="save"?"Saving":"Save Agent settings"]})})]})}export{H as AgentSettingsTab};
1
+ import{j as t}from"./pwa-startup-DvbrYEQi.js";import{r as c,d as A,S as q,T as I,e as z}from"./icons-CuXJn-Hz.js";import{h as x}from"./MonitorStackIcon-D1k9mjDe.js";import"./pwa-bootstrap-ljOQTt2e.js";const E={enabled:!0};function w(i){return{enabled:i?.enabled===!0}}async function L(i,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===i)return;try{await x("/api/settings",{method:"PATCH",signal:f,body:JSON.stringify({revision:g.revision,agent:{enabled:i}})},p);return}catch(a){if(l=a,s!==0||!String(a instanceof Error?a.message:a).startsWith("409 "))throw a}}throw l}function H({hubUrl:i,showToast:p}){const[f,l]=c.useState(E),[s,g]=c.useState(null),[a,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},i).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)}},[i]);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,i,e.controller.signal),!r(e))return;const n=await x("/api/settings/agent",{signal:e.controller.signal},i);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("")}}},T=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({})},i);if(!r(e))return;const h=await x("/api/settings/agent",{signal:e.controller.signal},i);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("")}}},D=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||a!==""}),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 ${D?"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:()=>{T()},disabled:a!=="",type:"button",children:[t.jsx(I,{size:15})," ",a==="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||a!=="",type:"button",children:[t.jsx(z,{size:15})," ",a==="save"?"Saving":"Save Agent settings"]})})]})}export{H as AgentSettingsTab};
@@ -1,4 +1,4 @@
1
- import{j as t}from"./pwa-startup-DvbrYEQi.js";import{r as d,ae as ht,af as Ce,z as yt,G as ee,X as xe,ag as vt,ah as wt,ai as Ze,aj as kt,ak as bt,al as Se,b as H,am as Ct,p as xt,an as _e,o as St,ao as jt,S as Je,ap as At,a9 as Rt,c as Nt,aq as Pt,ar as It,A as Tt,as as ue,M as Xe}from"./icons-CuXJn-Hz.js";import{r as Et,h as F}from"./MonitorStackIcon-D7bHJ5H2.js";import"./pwa-bootstrap-ljOQTt2e.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(ht,{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(yt,{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 Mt=64*1024,Me="[Earlier log output hidden by PWA display limit.]",Qe=new TextEncoder,zt=new TextDecoder("utf-8",{fatal:!0}),ze=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)",qt=/\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 Ut(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=zt.decode(n.subarray(r));const f=i.indexOf(`
1
+ import{j as t}from"./pwa-startup-DvbrYEQi.js";import{r as d,ae as ht,af as Ce,z as yt,G as ee,X as xe,ag as vt,ah as wt,ai as Ze,aj as kt,ak as bt,al as Se,b as H,am as Ct,p as xt,an as _e,o as St,ao as jt,S as Je,ap as At,a9 as Rt,c as Nt,aq as Pt,ar as It,A as Tt,as as ue,M as Xe}from"./icons-CuXJn-Hz.js";import{r as Et,h as F}from"./MonitorStackIcon-D1k9mjDe.js";import"./pwa-bootstrap-ljOQTt2e.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(ht,{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(yt,{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 Mt=64*1024,Me="[Earlier log output hidden by PWA display limit.]",Qe=new TextEncoder,zt=new TextDecoder("utf-8",{fatal:!0}),ze=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)",qt=/\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 Ut(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=zt.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(qt,"[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 qe(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
  `),Mt);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(`
@@ -1 +1 @@
1
- import{j as e}from"./pwa-startup-DvbrYEQi.js";import{r as i,R as x,E as j,D as u,f as g,h as f}from"./icons-CuXJn-Hz.js";import{l as b,c as d,d as y}from"./HubApp-Bu2-NnqZ.js";import"./pwa-bootstrap-ljOQTt2e.js";import"./preload-helper-DD1OZmLC.js";import"./MonitorStackIcon-D7bHJ5H2.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-D91nchvm.js";function z({hubUrl:s,onToast:n}){const[o,m]=i.useState([]),[h,c]=i.useState(!0),r=i.useCallback(async()=>{c(!0);try{m((await b(s)).captures||[])}catch(t){n(t instanceof Error?t.message:String(t))}finally{c(!1)}},[s,n]);i.useEffect(()=>{r()},[r]);const p=async t=>{const a=d(t.id,s);if(navigator.share)try{await navigator.share({title:t.fileName,url:a});return}catch{}window.open(a,"_blank","noopener,noreferrer")};return e.jsxs("section",{className:"capture-gallery",children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsx("span",{className:"settings-eyebrow",children:"Capture Gallery"}),e.jsx("h2",{children:"Saved captures"})]}),e.jsxs("button",{className:"text-button",onClick:()=>{r()},type:"button",children:[e.jsx(x,{size:15})," Refresh"]})]}),h?e.jsx("p",{className:"settings-help",children:"Loading captures..."}):o.length===0?e.jsx("p",{className:"settings-help",children:"No captures saved yet."}):e.jsx("div",{className:"capture-gallery-grid",children:o.map(t=>{const a=d(t.id,s);return e.jsxs("article",{className:"capture-gallery-card",children:[t.mimeType.startsWith("image/")?e.jsx("img",{src:a,alt:t.target.label}):e.jsx("video",{src:a,controls:!0,preload:"metadata"}),e.jsxs("div",{className:"capture-gallery-card-body",children:[e.jsx("strong",{children:t.fileName}),e.jsxs("small",{children:[t.target.label," · ",new Date(t.createdAt).toLocaleString()," · ",Math.ceil(t.sizeBytes/1024)," KB"]}),e.jsxs("div",{children:[e.jsx("button",{className:"icon-button",title:"Open","aria-label":"Open",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),type:"button",children:e.jsx(j,{size:15})}),e.jsx("a",{className:"icon-button",title:"Download","aria-label":"Download",href:`${a}?download=1`,download:t.fileName,children:e.jsx(u,{size:15})}),e.jsx("button",{className:"icon-button",title:"Share","aria-label":"Share",onClick:()=>{p(t)},type:"button",children:e.jsx(g,{size:15})}),e.jsx("button",{className:"icon-button danger",title:"Delete","aria-label":"Delete",onClick:()=>{window.confirm(`Delete ${t.fileName}?`)&&y(t.id,s).then(()=>r()).catch(l=>n(l instanceof Error?l.message:String(l)))},type:"button",children:e.jsx(f,{size:15})})]})]})]},t.id)})})]})}export{z as CaptureGallery};
1
+ import{j as e}from"./pwa-startup-DvbrYEQi.js";import{r as i,R as x,E as j,D as u,f as g,h as f}from"./icons-CuXJn-Hz.js";import{l as b,c as d,d as y}from"./HubApp-D1-hYJ7F.js";import"./pwa-bootstrap-ljOQTt2e.js";import"./preload-helper-DD1OZmLC.js";import"./MonitorStackIcon-D1k9mjDe.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-D91nchvm.js";function z({hubUrl:s,onToast:n}){const[o,m]=i.useState([]),[h,c]=i.useState(!0),r=i.useCallback(async()=>{c(!0);try{m((await b(s)).captures||[])}catch(t){n(t instanceof Error?t.message:String(t))}finally{c(!1)}},[s,n]);i.useEffect(()=>{r()},[r]);const p=async t=>{const a=d(t.id,s);if(navigator.share)try{await navigator.share({title:t.fileName,url:a});return}catch{}window.open(a,"_blank","noopener,noreferrer")};return e.jsxs("section",{className:"capture-gallery",children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsx("span",{className:"settings-eyebrow",children:"Capture Gallery"}),e.jsx("h2",{children:"Saved captures"})]}),e.jsxs("button",{className:"text-button",onClick:()=>{r()},type:"button",children:[e.jsx(x,{size:15})," Refresh"]})]}),h?e.jsx("p",{className:"settings-help",children:"Loading captures..."}):o.length===0?e.jsx("p",{className:"settings-help",children:"No captures saved yet."}):e.jsx("div",{className:"capture-gallery-grid",children:o.map(t=>{const a=d(t.id,s);return e.jsxs("article",{className:"capture-gallery-card",children:[t.mimeType.startsWith("image/")?e.jsx("img",{src:a,alt:t.target.label}):e.jsx("video",{src:a,controls:!0,preload:"metadata"}),e.jsxs("div",{className:"capture-gallery-card-body",children:[e.jsx("strong",{children:t.fileName}),e.jsxs("small",{children:[t.target.label," · ",new Date(t.createdAt).toLocaleString()," · ",Math.ceil(t.sizeBytes/1024)," KB"]}),e.jsxs("div",{children:[e.jsx("button",{className:"icon-button",title:"Open","aria-label":"Open",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),type:"button",children:e.jsx(j,{size:15})}),e.jsx("a",{className:"icon-button",title:"Download","aria-label":"Download",href:`${a}?download=1`,download:t.fileName,children:e.jsx(u,{size:15})}),e.jsx("button",{className:"icon-button",title:"Share","aria-label":"Share",onClick:()=>{p(t)},type:"button",children:e.jsx(g,{size:15})}),e.jsx("button",{className:"icon-button danger",title:"Delete","aria-label":"Delete",onClick:()=>{window.confirm(`Delete ${t.fileName}?`)&&y(t.id,s).then(()=>r()).catch(l=>n(l instanceof Error?l.message:String(l)))},type:"button",children:e.jsx(f,{size:15})})]})]})]},t.id)})})]})}export{z as CaptureGallery};