livedesk 0.1.685 → 0.1.687
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/livedesk.js +1 -0
- package/bootstrap/hub-self-agent.js +1 -0
- package/client/bin/livedesk-client.js +1 -1
- package/client/package.json +5 -5
- package/electron/main.mjs +3 -1
- package/hub/package.json +32 -32
- package/hub/src/console-direct.js +4 -1
- package/hub/src/console-direct.test.mjs +70 -4
- package/package.json +6 -6
- package/runtime-core/package.json +18 -18
- package/web/dist/app.html +1 -1
- package/web/dist/app.webmanifest +1 -1
- package/web/dist/assets/{AgentSettingsTab-C2yWMYVI.js → AgentSettingsTab-BPJB7HBg.js} +1 -1
- package/web/dist/assets/{AgentsPage-BlV_riNE.js → AgentsPage-D5vBDaHb.js} +1 -1
- package/web/dist/assets/{CaptureGallery-Dbn6VhEu.js → CaptureGallery-C96kEwrY.js} +1 -1
- package/web/dist/assets/HubApp-DYI-2XLQ.js +182 -0
- package/web/dist/assets/{HubApp-DFLBNvIi.css → HubApp-DdHHVOM0.css} +1 -1
- package/web/dist/assets/MonitorStackIcon-BnaeWQfq.js +8 -0
- package/web/dist/assets/{SettingsPage-49Z3Hv0p.js → SettingsPage-CZsZ0lSF.js} +1 -1
- package/web/dist/assets/{ShareFilesPage-BEOcL51r.js → ShareFilesPage-sIpV9WUY.js} +1 -1
- package/web/dist/assets/{SupportPage-CB0Hrnri.js → SupportPage-CYJYbUNg.js} +1 -1
- package/web/dist/assets/{VuvoDeskApp-CFZWGgYH.js → VuvoDeskApp-Bfbh_0oM.js} +1 -1
- package/web/dist/assets/{app-CoHWIYYA.js → app-B1qgBgVG.js} +2 -2
- package/web/dist/assets/main-BkFGhybc.js +2 -0
- package/web/dist/assets/styles-CQz52HHV.css +1 -0
- package/web/dist/index.html +3 -3
- package/web/dist/sw.js +2 -2
- package/web/dist/vuvodesk-build-evidence.json +50 -50
- package/web/dist/assets/HubApp-CjBsQESw.js +0 -182
- package/web/dist/assets/MonitorStackIcon-BTi3OZ_m.js +0 -8
- package/web/dist/assets/main-D83bJX4i.js +0 -2
- 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;
|
|
@@ -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;
|
package/client/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
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.
|
|
46
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
47
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
48
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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.
|
|
4
|
-
"livedeskClientVersion": "0.1.
|
|
3
|
+
"version": "0.1.687",
|
|
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.
|
|
58
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
59
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
60
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
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-
|
|
55
|
+
<script type="module" crossorigin src="/assets/app-B1qgBgVG.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>
|
package/web/dist/app.webmanifest
CHANGED
|
@@ -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-
|
|
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-BnaeWQfq.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-
|
|
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-BnaeWQfq.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-
|
|
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-DYI-2XLQ.js";import"./pwa-bootstrap-ljOQTt2e.js";import"./preload-helper-DD1OZmLC.js";import"./MonitorStackIcon-BnaeWQfq.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};
|