livedesk 0.1.685 → 0.1.686

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 (31) hide show
  1. package/bin/livedesk.js +1 -0
  2. package/bootstrap/hub-self-agent.js +1 -0
  3. package/client/bin/livedesk-client.js +1 -1
  4. package/client/package.json +5 -5
  5. package/electron/main.mjs +3 -1
  6. package/hub/package.json +32 -32
  7. package/hub/src/console-direct.js +4 -1
  8. package/hub/src/console-direct.test.mjs +70 -4
  9. package/package.json +6 -6
  10. package/runtime-core/package.json +18 -18
  11. package/web/dist/app.html +1 -1
  12. package/web/dist/app.webmanifest +1 -1
  13. package/web/dist/assets/{AgentSettingsTab-C2yWMYVI.js → AgentSettingsTab-BpaAvVmS.js} +1 -1
  14. package/web/dist/assets/{AgentsPage-BlV_riNE.js → AgentsPage-CviietlX.js} +1 -1
  15. package/web/dist/assets/{CaptureGallery-Dbn6VhEu.js → CaptureGallery-v-uzDN4q.js} +1 -1
  16. package/web/dist/assets/HubApp-CEZWMXxq.js +182 -0
  17. package/web/dist/assets/{HubApp-DFLBNvIi.css → HubApp-DdHHVOM0.css} +1 -1
  18. package/web/dist/assets/MonitorStackIcon-JfWhLRS2.js +8 -0
  19. package/web/dist/assets/{SettingsPage-49Z3Hv0p.js → SettingsPage-XqN0yfRR.js} +1 -1
  20. package/web/dist/assets/{ShareFilesPage-BEOcL51r.js → ShareFilesPage-BcxBsXoN.js} +1 -1
  21. package/web/dist/assets/{SupportPage-CB0Hrnri.js → SupportPage-D_60jn3l.js} +1 -1
  22. package/web/dist/assets/{VuvoDeskApp-CFZWGgYH.js → VuvoDeskApp-DsIm_aTa.js} +1 -1
  23. package/web/dist/assets/{app-CoHWIYYA.js → app-DA8I23kG.js} +2 -2
  24. package/web/dist/assets/{main-D83bJX4i.js → main-B1P8Damh.js} +2 -2
  25. package/web/dist/assets/styles-CQz52HHV.css +1 -0
  26. package/web/dist/index.html +3 -3
  27. package/web/dist/sw.js +2 -2
  28. package/web/dist/vuvodesk-build-evidence.json +47 -47
  29. package/web/dist/assets/HubApp-CjBsQESw.js +0 -182
  30. package/web/dist/assets/MonitorStackIcon-BTi3OZ_m.js +0 -8
  31. package/web/dist/assets/styles-BjyLRq6m.css +0 -1
package/bin/livedesk.js CHANGED
@@ -2932,6 +2932,7 @@ async function runManager(args, resolvedRole = null, runtimeLock = null, updateH
2932
2932
  const child = spawn(process.execPath, [hubEntry, ...options.forwarded], {
2933
2933
  env,
2934
2934
  stdio: ['inherit', 'pipe', 'pipe'],
2935
+ detached: process.platform === 'win32' && process.env.LIVEDESK_DESKTOP_HOST === '1' && !debugConsole,
2935
2936
  windowsHide: !debugConsole
2936
2937
  });
2937
2938
  activeChild = child;
@@ -82,6 +82,7 @@ export function buildHubSelfAgentLaunchSpec({
82
82
  options: {
83
83
  env,
84
84
  stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
85
+ detached: process.platform === 'win32' && env.LIVEDESK_DESKTOP_HOST === '1',
85
86
  windowsHide: true
86
87
  },
87
88
  manager
@@ -5370,7 +5370,7 @@ function spawnAgent(command, args, env = process.env, onStart = null) {
5370
5370
  // A dedicated Unix process group lets Ctrl+C, replacement startup, and
5371
5371
  // forced shutdown terminate RemoteFast together with its SCK/FFmpeg
5372
5372
  // descendants without touching npm, the terminal, or unrelated jobs.
5373
- detached: ownsProcessGroup,
5373
+ detached: ownsProcessGroup || isTruthy(env.LIVEDESK_DESKTOP_HOST),
5374
5374
  windowsHide: true
5375
5375
  });
5376
5376
  child.livedeskOwnsProcessGroup = ownsProcessGroup;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.274",
3
+ "version": "0.1.275",
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.469",
46
- "@livedesk/fast-osx-arm64": "0.1.469",
47
- "@livedesk/fast-osx-x64": "0.1.469",
48
- "@livedesk/fast-win-x64": "0.1.469"
45
+ "@livedesk/fast-linux-x64": "0.1.470",
46
+ "@livedesk/fast-osx-arm64": "0.1.470",
47
+ "@livedesk/fast-osx-x64": "0.1.470",
48
+ "@livedesk/fast-win-x64": "0.1.470"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
package/electron/main.mjs CHANGED
@@ -519,7 +519,9 @@ function startRuntime(options = {}) {
519
519
  ...(process.versions.electron && !process.env.LIVEDESK_RUNTIME_NODE ? { ELECTRON_RUN_AS_NODE: '1' } : {})
520
520
  },
521
521
  windowsHide: true,
522
- detached: ownsUnixProcessGroup,
522
+ // Windows DETACHED_PROCESS avoids allocating a hidden conhost. The
523
+ // exact Windows tree tracker still owns this referenced child and its pipes.
524
+ detached: ownsUnixProcessGroup || process.platform === 'win32',
523
525
  stdio: ['ignore', 'pipe', 'pipe']
524
526
  });
525
527
  child.livedeskWindowsSpawnedAtMs = runtimeLaunchStartedAt;
package/hub/package.json CHANGED
@@ -1,33 +1,33 @@
1
- {
2
- "name": "@livedesk/hub",
3
- "version": "0.1.69",
4
- "description": "VuvoDesk local Hub API and browser frame bridge",
5
- "type": "module",
6
- "main": "src/server.js",
7
- "files": [
8
- "src/",
9
- "README.md"
10
- ],
11
- "scripts": {
12
- "dev": "node src/server.js",
13
- "start": "node src/server.js",
14
- "check": "node --check src/server.js && node --check src/remote-hub.js && node --check src/remote-clipboard-contract.mjs && node --check src/console-direct.js",
15
- "prepublishOnly": "node ../../scripts/vuvodesk-release-git-gate.mjs"
16
- },
17
- "dependencies": {
18
- "@ffmpeg-installer/ffmpeg": "^1.1.0",
1
+ {
2
+ "name": "@livedesk/hub",
3
+ "version": "0.1.70",
4
+ "description": "VuvoDesk local Hub API and browser frame bridge",
5
+ "type": "module",
6
+ "main": "src/server.js",
7
+ "files": [
8
+ "src/",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "dev": "node src/server.js",
13
+ "start": "node src/server.js",
14
+ "check": "node --check src/server.js && node --check src/remote-hub.js && node --check src/remote-clipboard-contract.mjs && node --check src/console-direct.js",
15
+ "prepublishOnly": "node ../../scripts/vuvodesk-release-git-gate.mjs"
16
+ },
17
+ "dependencies": {
18
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
19
19
  "@livedesk/runtime-core": "0.1.9",
20
- "@openai/codex-sdk": "0.145.0",
21
- "cors": "^2.8.5",
22
- "express": "^4.21.2",
23
- "node-datachannel": "0.33.0",
24
- "path-to-regexp": "0.1.13",
25
- "ws": "^8.18.3"
26
- },
27
- "engines": {
28
- "node": ">=20"
29
- },
30
- "publishConfig": {
31
- "access": "public"
32
- }
33
- }
20
+ "@openai/codex-sdk": "0.145.0",
21
+ "cors": "^2.8.5",
22
+ "express": "^4.21.2",
23
+ "node-datachannel": "0.33.0",
24
+ "path-to-regexp": "0.1.13",
25
+ "ws": "^8.18.3"
26
+ },
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ }
33
+ }
@@ -739,7 +739,10 @@ export function createHubConsoleDirect(options = {}) {
739
739
  // Node Buffer shape. Share the exact encoded bytes without copying or
740
740
  // retaining a second application queue.
741
741
  const nativeChunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
742
- if (channelState.channel.sendMessageBinary(nativeChunk) !== true) throw new Error('send-rejected');
742
+ // libdatachannel returns false when it accepted the chunk into its
743
+ // bounded send buffer. Only an exception means this send failed.
744
+ // Retrying duplicates a fragment; closing here truncates the frame.
745
+ channelState.channel.sendMessageBinary(nativeChunk);
743
746
  } catch (error) {
744
747
  dataChannelSendFailures += 1;
745
748
  lastDataChannelSendFailure = Object.freeze({
@@ -293,6 +293,64 @@ function offer(signal, connectionId = CONNECTION_ONE, sdp = 'v=0\r\na=fake-offer
293
293
  return FakePeerConnection.instances.at(-1);
294
294
  }
295
295
 
296
+ test('native buffered sends complete each media message without closing input or control', async () => {
297
+ const { direct, signal } = await connectedDirect();
298
+ try {
299
+ const peer = offer(signal);
300
+ const control = new FakeDataChannel('livedesk-control-v1');
301
+ const nativeSend = FakeDataChannel.prototype.sendMessageBinary;
302
+ const acceptBuffered = function (value) {
303
+ nativeSend.call(this, value);
304
+ this.buffered += value.byteLength;
305
+ return false; // libdatachannel accepted and buffered this exact chunk.
306
+ };
307
+ control.sendMessageBinary = acceptBuffered;
308
+ peer.emitDataChannel(control);
309
+ control.open();
310
+ assert.equal(peer.closed, false, 'buffered direct-ready is not a send failure');
311
+ assert.equal(JSON.parse(decodeWireMessages(control.sent)[0].data).type, 'direct-ready');
312
+ const input = new FakeDataChannel(`livedesk-ws-v1:${CHANNEL_ID_TWO}:input`);
313
+ peer.emitDataChannel(input);
314
+ input.open();
315
+ const lane = new FakeDataChannel(`livedesk-ws-v1:${CHANNEL_ID}:frame`);
316
+ lane.sendMessageBinary = acceptBuffered;
317
+ peer.emitDataChannel(lane);
318
+ lane.open();
319
+ sendWire(control, {
320
+ type: 'ws-open', connectionId: CONNECTION_ONE, hubEpoch: HUB_EPOCH,
321
+ channelId: CHANNEL_ID, purpose: 'frame', path: '/api/remote/frames/ws'
322
+ });
323
+ const local = FakeLocalSocket.instances[0];
324
+ local.open();
325
+ for (let frame = 0; frame < 30; frame += 1) {
326
+ lane.buffered = 0;
327
+ local.receive(Buffer.alloc(74 * 1024, frame), true);
328
+ assert.equal(lane.closed, false, `transient congestion must preserve frame ${frame}`);
329
+ }
330
+ const received = decodeWireMessages(lane.sent);
331
+ assert.equal(received.length, 30);
332
+ for (let frame = 0; frame < 30; frame += 1) {
333
+ assert.deepEqual(Buffer.from(received[frame].data), Buffer.alloc(74 * 1024, frame));
334
+ }
335
+ assert.equal(local.readyState, FakeSocket.OPEN);
336
+ assert.equal(input.closed, false);
337
+ assert.equal(control.closed, false);
338
+ assert.equal(peer.closed, false);
339
+ assert.equal(direct.inspect().dataChannelSendFailures, 0);
340
+ assert.equal(direct.inspect().dataChannelBackpressureCloses, 0);
341
+ lane.sendMessageBinary = () => { throw new Error('native-send-failed'); };
342
+ local.receive(Buffer.alloc(10), true);
343
+ assert.equal(lane.closed, true, 'an actual native exception still retires the exact lane');
344
+ assert.equal(control.closed, false);
345
+ assert.equal(input.closed, false);
346
+ assert.equal(direct.inspect().dataChannelSendFailures, 1);
347
+ } finally {
348
+ direct.close();
349
+ assert.equal(direct.inspect().resourceTimers, 0);
350
+ assert.equal(direct.inspect().retainedAssemblyBytes, 0);
351
+ }
352
+ });
353
+
296
354
  test('STUN-only peer answers and rejects stale owner callbacks after replacement', async () => {
297
355
  const { direct, signal } = await connectedDirect();
298
356
  const firstPeer = offer(signal, CONNECTION_ONE);
@@ -1449,7 +1507,7 @@ test('server session refresh preserves peers while logout uses the revoking clos
1449
1507
  assert.doesNotMatch(logoutBlock, /hubConsoleDirect\?\.refresh\(\)/);
1450
1508
  });
1451
1509
 
1452
- test('native node-datachannel peers carry the fragmented control wire over loopback', { timeout: 8_000 }, async () => {
1510
+ test('native node-datachannel buffered sends deliver the complete fragmented wire over loopback', { timeout: 8_000 }, async t => {
1453
1511
  let offerer = null;
1454
1512
  let answerer = null;
1455
1513
  let offerChannel = null;
@@ -1458,6 +1516,7 @@ test('native node-datachannel peers carry the fragmented control wire over loopb
1458
1516
  let openTimer = null;
1459
1517
  let messageTimer = null;
1460
1518
  try {
1519
+ nodeDataChannel.setSctpSettings({ sendBufferSize: 64 * 1024 });
1461
1520
  offerer = new nodeDataChannel.PeerConnection('console-direct-native-offerer', { iceServers: [] });
1462
1521
  answerer = new nodeDataChannel.PeerConnection('console-direct-native-answerer', { iceServers: [] });
1463
1522
  offerer.onLocalDescription((sdp, type) => {
@@ -1494,17 +1553,23 @@ test('native node-datachannel peers carry the fragmented control wire over loopb
1494
1553
  clearTimeout(openTimer);
1495
1554
  openTimer = null;
1496
1555
  const payload = JSON.stringify({ type: 'direct-ready', connectionId: CONNECTION_ONE, hubEpoch: HUB_EPOCH });
1497
- const chunks = encodeDirectConsoleWireMessage(payload.repeat(400), {
1556
+ const expected = payload.repeat(20_000);
1557
+ const chunks = encodeDirectConsoleWireMessage(expected, {
1498
1558
  messageId: 77,
1499
1559
  kind: 'control'
1500
1560
  });
1501
1561
  assert.ok(chunks.length > 1);
1502
- for (const chunk of chunks) assert.equal(offerChannel.sendMessageBinary(Buffer.from(chunk)), true);
1562
+ let bufferedSends = 0;
1563
+ for (const chunk of chunks) {
1564
+ if (offerChannel.sendMessageBinary(Buffer.from(chunk)) === false) bufferedSends += 1;
1565
+ }
1566
+ assert.ok(bufferedSends > 0, 'the native congestion path must actually be exercised');
1503
1567
  const message = await received;
1504
1568
  clearTimeout(messageTimer);
1505
1569
  messageTimer = null;
1506
1570
  assert.equal(message.kind, 'control');
1507
- assert.equal(message.data, payload.repeat(400));
1571
+ assert.equal(message.data, expected);
1572
+ t.diagnostic(`native buffered sends=${bufferedSends}; complete bytes=${message.byteLength}; chunks=${chunks.length}`);
1508
1573
  } finally {
1509
1574
  if (openTimer) clearTimeout(openTimer);
1510
1575
  if (messageTimer) clearTimeout(messageTimer);
@@ -1515,5 +1580,6 @@ test('native node-datachannel peers carry the fragmented control wire over loopb
1515
1580
  try { answerer?.close(); } catch {}
1516
1581
  await new Promise(resolve => setTimeout(resolve, 100));
1517
1582
  nodeDataChannel.cleanup();
1583
+ nodeDataChannel.setSctpSettings({});
1518
1584
  }
1519
1585
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.685",
4
- "livedeskClientVersion": "0.1.274",
3
+ "version": "0.1.686",
4
+ "livedeskClientVersion": "0.1.275",
5
5
  "buildFlavor": "production",
6
6
  "description": "VuvoDesk Hub and client launcher",
7
7
  "type": "module",
@@ -54,10 +54,10 @@
54
54
  "ws": "^8.18.3"
55
55
  },
56
56
  "optionalDependencies": {
57
- "@livedesk/fast-linux-x64": "0.1.469",
58
- "@livedesk/fast-osx-arm64": "0.1.469",
59
- "@livedesk/fast-osx-x64": "0.1.469",
60
- "@livedesk/fast-win-x64": "0.1.469"
57
+ "@livedesk/fast-linux-x64": "0.1.470",
58
+ "@livedesk/fast-osx-arm64": "0.1.470",
59
+ "@livedesk/fast-osx-x64": "0.1.470",
60
+ "@livedesk/fast-win-x64": "0.1.470"
61
61
  },
62
62
  "publishConfig": {
63
63
  "access": "public"
@@ -1,20 +1,20 @@
1
- {
2
- "name": "@livedesk/runtime-core",
1
+ {
2
+ "name": "@livedesk/runtime-core",
3
3
  "version": "0.1.9",
4
- "description": "Shared VuvoDesk runtime role and lifecycle contracts",
5
- "type": "module",
6
- "main": "src/index.js",
7
- "files": [
8
- "src/"
9
- ],
10
- "scripts": {
4
+ "description": "Shared VuvoDesk runtime role and lifecycle contracts",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "files": [
8
+ "src/"
9
+ ],
10
+ "scripts": {
11
11
  "check": "node --check src/index.js && node --check src/direct-secure-transport.js && node --check src/console-direct-wire.js",
12
- "prepublishOnly": "node ../../scripts/vuvodesk-release-git-gate.mjs"
13
- },
14
- "engines": {
15
- "node": ">=20"
16
- },
17
- "publishConfig": {
18
- "access": "public"
19
- }
20
- }
12
+ "prepublishOnly": "node ../../scripts/vuvodesk-release-git-gate.mjs"
13
+ },
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ }
20
+ }
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-CoHWIYYA.js" data-vuvodesk-entry></script>
55
+ <script type="module" crossorigin src="/assets/app-DA8I23kG.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.685",
7
+ "start_url": "/app?pwa=0.1.686",
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-BTi3OZ_m.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-JfWhLRS2.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-BTi3OZ_m.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-JfWhLRS2.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-CjBsQESw.js";import"./pwa-bootstrap-ljOQTt2e.js";import"./preload-helper-DD1OZmLC.js";import"./MonitorStackIcon-BTi3OZ_m.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-CEZWMXxq.js";import"./pwa-bootstrap-ljOQTt2e.js";import"./preload-helper-DD1OZmLC.js";import"./MonitorStackIcon-JfWhLRS2.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};