@livedesk/hub 0.1.17 → 0.1.19
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/package.json
CHANGED
|
@@ -42,7 +42,11 @@ export function createAgentManager({
|
|
|
42
42
|
|
|
43
43
|
async function publicSettings() {
|
|
44
44
|
const current = await settings.get();
|
|
45
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
codexMaxTurns: _codexMaxTurns,
|
|
47
|
+
provider: _provider,
|
|
48
|
+
...exposedSettings
|
|
49
|
+
} = current;
|
|
46
50
|
const codex = await getCodexStatus();
|
|
47
51
|
const securityStatus = runtime?.getSecurityStatus?.() || {
|
|
48
52
|
isolatedCodexHome: false,
|
|
@@ -55,7 +59,6 @@ export function createAgentManager({
|
|
|
55
59
|
};
|
|
56
60
|
return {
|
|
57
61
|
...exposedSettings,
|
|
58
|
-
provider: AGENT_PROVIDER_CODEX,
|
|
59
62
|
codexInstallation: codex.installed ? 'installed' : 'not-installed',
|
|
60
63
|
codexAuth: codex.authenticated,
|
|
61
64
|
codexStatus: codex.status,
|
|
@@ -3,10 +3,9 @@ import { access, link, mkdir, stat, unlink } from 'node:fs/promises';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
|
-
import { AgentProviderError } from './provider-errors.js';
|
|
8
|
-
import {
|
|
9
|
-
import { AGENT_TOOL_NAMES } from './agent-tool-registry.js';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { AgentProviderError } from './provider-errors.js';
|
|
8
|
+
import { AGENT_TOOL_NAMES } from './agent-tool-registry.js';
|
|
10
9
|
import { createAgentPermissionPolicy, hashAgentPermissionPolicy } from './agent-permissions.js';
|
|
11
10
|
|
|
12
11
|
const require = createRequire(import.meta.url);
|
|
@@ -427,11 +426,10 @@ export function createCodexAgentRuntime({
|
|
|
427
426
|
}
|
|
428
427
|
|
|
429
428
|
function publicRun(run) {
|
|
430
|
-
return {
|
|
431
|
-
runId: run.runId,
|
|
432
|
-
status: run.status,
|
|
433
|
-
|
|
434
|
-
instruction: run.instruction,
|
|
429
|
+
return {
|
|
430
|
+
runId: run.runId,
|
|
431
|
+
status: run.status,
|
|
432
|
+
instruction: run.instruction,
|
|
435
433
|
threadId: run.threadId,
|
|
436
434
|
finalResponse: run.finalResponse,
|
|
437
435
|
error: run.error,
|
|
@@ -654,10 +652,9 @@ export function createCodexAgentRuntime({
|
|
|
654
652
|
const permissionPolicy = freezeAgentPermissionPolicy(input.permissionPolicy && typeof input.permissionPolicy === 'object'
|
|
655
653
|
? input.permissionPolicy
|
|
656
654
|
: createAgentPermissionPolicy({ mode: input.permissionMode || 'safe-auto', deviceIds, maxToolCalls: settings.codexMaxToolCalls }));
|
|
657
|
-
const run = {
|
|
658
|
-
runId: crypto.randomUUID(),
|
|
659
|
-
status: 'queued',
|
|
660
|
-
provider: AGENT_PROVIDER_CODEX,
|
|
655
|
+
const run = {
|
|
656
|
+
runId: crypto.randomUUID(),
|
|
657
|
+
status: 'queued',
|
|
661
658
|
instruction,
|
|
662
659
|
deviceIds,
|
|
663
660
|
threadId: '',
|
package/src/remote-hub.js
CHANGED
|
@@ -4232,9 +4232,11 @@ export function createRemoteHub(options = {}) {
|
|
|
4232
4232
|
const captureTimingAvailable = hasFrameCaptureTiming(message);
|
|
4233
4233
|
const videoTransport = transport === 'udp-p2p'
|
|
4234
4234
|
? 'p2p-udp'
|
|
4235
|
-
: transport === '
|
|
4236
|
-
? '
|
|
4237
|
-
: '
|
|
4235
|
+
: transport === 'relay-binary'
|
|
4236
|
+
? 'encrypted-relay'
|
|
4237
|
+
: transport === 'ws-binary'
|
|
4238
|
+
? 'direct-ws'
|
|
4239
|
+
: 'direct-tcp';
|
|
4238
4240
|
device.latestLiveFrame = {
|
|
4239
4241
|
streamId,
|
|
4240
4242
|
frameSeq,
|
|
@@ -4322,9 +4324,16 @@ export function createRemoteHub(options = {}) {
|
|
|
4322
4324
|
deltaRefreshTileCount: Number.isFinite(Number(message.deltaRefreshTileCount)) ? Number(message.deltaRefreshTileCount) : 0,
|
|
4323
4325
|
deltaTotalTiles: Number.isFinite(Number(message.deltaTotalTiles)) ? Number(message.deltaTotalTiles) : 0,
|
|
4324
4326
|
videoTransport,
|
|
4325
|
-
frameTransportActive: safeString(message.frameTransportActive, 20)
|
|
4327
|
+
frameTransportActive: safeString(message.frameTransportActive, 20)
|
|
4328
|
+
|| (videoTransport === 'p2p-udp' ? 'p2p' : videoTransport === 'encrypted-relay' ? 'relay' : 'direct'),
|
|
4326
4329
|
frameTransportProtocol: safeString(message.frameTransportProtocol, 20)
|
|
4327
|
-
|| (videoTransport === 'p2p-udp'
|
|
4330
|
+
|| (videoTransport === 'p2p-udp'
|
|
4331
|
+
? 'udp'
|
|
4332
|
+
: videoTransport === 'encrypted-relay'
|
|
4333
|
+
? 'relay'
|
|
4334
|
+
: videoTransport === 'direct-ws'
|
|
4335
|
+
? 'ws'
|
|
4336
|
+
: 'tcp'),
|
|
4328
4337
|
frameTransportState: safeString(message.frameTransportState, 20) || 'active',
|
|
4329
4338
|
frameTransportP2pReady: message.frameTransportP2pReady === true,
|
|
4330
4339
|
frameTransportReason: safeString(message.frameTransportReason, 240),
|
|
@@ -4518,7 +4527,11 @@ export function createRemoteHub(options = {}) {
|
|
|
4518
4527
|
}
|
|
4519
4528
|
|
|
4520
4529
|
const frameKind = safeString(header.frameKind || header.kind || header.frameType || '', 40).toLowerCase();
|
|
4521
|
-
const binaryTransport =
|
|
4530
|
+
const binaryTransport = socket.__liveDeskRelayControl === true
|
|
4531
|
+
? 'relay-binary'
|
|
4532
|
+
: state.binaryTransport === 'ws-binary'
|
|
4533
|
+
? 'ws-binary'
|
|
4534
|
+
: 'binary';
|
|
4522
4535
|
if (frameKind === 'thumbnail' || header.type === 'thumbnail.binary') {
|
|
4523
4536
|
applyThumbnailFrame(device, header, framePayload, binaryTransport);
|
|
4524
4537
|
return;
|
package/src/server.js
CHANGED
|
@@ -2127,9 +2127,11 @@ function buildRemoteFrameBinaryPacket(frameEvent, diagnostics = {}) {
|
|
|
2127
2127
|
transport: frame.transport || '',
|
|
2128
2128
|
videoTransport: frame.videoTransport || (frame.transport === 'udp-p2p'
|
|
2129
2129
|
? 'p2p-udp'
|
|
2130
|
-
: frame.transport === '
|
|
2131
|
-
? '
|
|
2132
|
-
: '
|
|
2130
|
+
: frame.transport === 'relay-binary'
|
|
2131
|
+
? 'encrypted-relay'
|
|
2132
|
+
: frame.transport === 'ws-binary'
|
|
2133
|
+
? 'direct-ws'
|
|
2134
|
+
: 'direct-tcp'),
|
|
2133
2135
|
frameTransportActive: frame.frameTransportActive || '',
|
|
2134
2136
|
frameTransportProtocol: frame.frameTransportProtocol || '',
|
|
2135
2137
|
frameTransportState: frame.frameTransportState || '',
|
|
@@ -2,10 +2,10 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import { lookup } from 'node:dns/promises';
|
|
3
3
|
import dgram from 'node:dgram';
|
|
4
4
|
import {
|
|
5
|
-
UDP_MAX_DATAGRAM_BYTES,
|
|
6
|
-
UDP_P2P_PROTOCOL,
|
|
7
|
-
UdpFrameReassembler,
|
|
8
|
-
UdpReplayWindow,
|
|
5
|
+
UDP_MAX_DATAGRAM_BYTES,
|
|
6
|
+
UDP_P2P_PROTOCOL,
|
|
7
|
+
UdpFrameReassembler,
|
|
8
|
+
UdpReplayWindow,
|
|
9
9
|
decodeRendezvousMessage,
|
|
10
10
|
decodeUdpPacket,
|
|
11
11
|
encodeRendezvousMessage,
|
|
@@ -52,7 +52,7 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
52
52
|
const socket = dgram.createSocket('udp4');
|
|
53
53
|
const sessions = new Map();
|
|
54
54
|
let frameHandler = () => {};
|
|
55
|
-
let eventHandler = () => {};
|
|
55
|
+
let eventHandler = () => {};
|
|
56
56
|
let started = false;
|
|
57
57
|
let boundPort = requestedPort;
|
|
58
58
|
let sequence = crypto.randomInt(1, 0x7fffffff);
|
|
@@ -65,20 +65,20 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
65
65
|
if (trace) logEvent('udp', `${type} ${JSON.stringify({ deviceId: event.deviceId || '', state: event.state || '', reason: event.reason || '' })}`);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
function nextSequence() {
|
|
69
|
-
sequence = (sequence + 1) >>> 0;
|
|
70
|
-
return sequence;
|
|
71
|
-
}
|
|
72
|
-
|
|
68
|
+
function nextSequence() {
|
|
69
|
+
sequence = (sequence + 1) >>> 0;
|
|
70
|
+
return sequence;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
73
|
function sendPacket(session, type, header = {}, payload = Buffer.alloc(0), address = session.clientEndpoint || session.peerEndpoint) {
|
|
74
74
|
if (!address || !session) return false;
|
|
75
|
-
let packet;
|
|
76
|
-
try {
|
|
77
|
-
packet = encodeUdpPacket({ type, sessionId: session.sessionId, key: session.key, sequence: nextSequence(), header, payload });
|
|
78
|
-
} catch (error) {
|
|
79
|
-
logWarn('udp', `UDP packet rejected device=${session.deviceId}: ${error?.message || error}`);
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
75
|
+
let packet;
|
|
76
|
+
try {
|
|
77
|
+
packet = encodeUdpPacket({ type, sessionId: session.sessionId, key: session.key, sequence: nextSequence(), header, payload });
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logWarn('udp', `UDP packet rejected device=${session.deviceId}: ${error?.message || error}`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
82
|
socket.send(packet, address.port, address.address);
|
|
83
83
|
return true;
|
|
84
84
|
}
|
|
@@ -288,9 +288,9 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
288
288
|
keyBase64: key.toString('base64'),
|
|
289
289
|
rendezvousToken: crypto.randomBytes(16).toString('hex'),
|
|
290
290
|
sendControl,
|
|
291
|
-
clientEndpoint: null,
|
|
292
|
-
peerEndpoint: null,
|
|
293
|
-
replay: new UdpReplayWindow(),
|
|
291
|
+
clientEndpoint: null,
|
|
292
|
+
peerEndpoint: null,
|
|
293
|
+
replay: new UdpReplayWindow(),
|
|
294
294
|
reassembler: new UdpFrameReassembler({ maxPending: maxPendingFrames, timeoutMs: frameTimeoutMs }),
|
|
295
295
|
ready: false,
|
|
296
296
|
createdAt: Date.now(),
|
|
@@ -333,8 +333,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
333
333
|
return {
|
|
334
334
|
enabled,
|
|
335
335
|
preferred: preferP2p,
|
|
336
|
-
state: session.ready ? 'udp-ready' : 'udp-session-created',
|
|
337
|
-
ready: session.ready,
|
|
336
|
+
state: session.ready ? 'udp-ready' : 'udp-session-created',
|
|
337
|
+
ready: session.ready,
|
|
338
338
|
sessionId: session.sessionId,
|
|
339
339
|
lastPacketAt: session.lastPacketAt ? new Date(session.lastPacketAt).toISOString() : '',
|
|
340
340
|
framesReceived: session.framesReceived,
|
|
@@ -2,9 +2,11 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import { inflateRawSync } from 'node:zlib';
|
|
3
3
|
|
|
4
4
|
export const UDP_P2P_PROTOCOL = 'livedesk.udp.p2p.v1';
|
|
5
|
-
export const UDP_P2P_VERSION = 1;
|
|
6
|
-
export const UDP_MAX_DATAGRAM_BYTES = 1200;
|
|
7
|
-
|
|
5
|
+
export const UDP_P2P_VERSION = 1;
|
|
6
|
+
export const UDP_MAX_DATAGRAM_BYTES = 1200;
|
|
7
|
+
const UDP_SERIAL_MODULUS = 0x1_0000_0000;
|
|
8
|
+
const UDP_SERIAL_HALF_RANGE = 0x8000_0000;
|
|
9
|
+
export const UDP_PACKET_TYPES = Object.freeze({
|
|
8
10
|
probe: 1,
|
|
9
11
|
'probe-ack': 2,
|
|
10
12
|
ready: 3,
|
|
@@ -83,13 +85,21 @@ function decodeFramedPayload(payload, legacyHeader = null) {
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
function packetTypeCode(type) {
|
|
88
|
+
function packetTypeCode(type) {
|
|
87
89
|
const code = typeof type === 'number' ? type : UDP_PACKET_TYPES[String(type || '').toLowerCase()];
|
|
88
90
|
if (!code || !UDP_PACKET_TYPE_NAMES.has(code)) throw new Error('invalid-udp-packet-type');
|
|
89
91
|
return code;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function normalizeSequence(value) {
|
|
95
|
+
const sequence = Number(value);
|
|
96
|
+
if (!Number.isInteger(sequence) || sequence < 0 || sequence >= UDP_SERIAL_MODULUS) {
|
|
97
|
+
throw new Error('invalid-udp-packet-sequence');
|
|
98
|
+
}
|
|
99
|
+
return sequence;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function encodeHeader(header) {
|
|
93
103
|
const bytes = Buffer.from(JSON.stringify(header && typeof header === 'object' ? header : {}), 'utf8');
|
|
94
104
|
if (bytes.length > UDP_MAX_HEADER_BYTES) throw new Error('udp-packet-header-too-large');
|
|
95
105
|
return bytes;
|
|
@@ -103,9 +113,9 @@ export function createUdpSessionKey() {
|
|
|
103
113
|
return crypto.randomBytes(32);
|
|
104
114
|
}
|
|
105
115
|
|
|
106
|
-
export function encodeUdpPacket({ type, sessionId, key, sequence = 0, header = {}, payload = Buffer.alloc(0) }) {
|
|
107
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
108
|
-
const sessionBytes = Buffer.from(normalizedSessionId, 'utf8');
|
|
116
|
+
export function encodeUdpPacket({ type, sessionId, key, sequence = 0, header = {}, payload = Buffer.alloc(0) }) {
|
|
117
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
118
|
+
const sessionBytes = Buffer.from(normalizedSessionId, 'utf8');
|
|
109
119
|
const headerBytes = encodeHeader(header);
|
|
110
120
|
const payloadBytes = asBuffer(payload);
|
|
111
121
|
if (payloadBytes.length > UDP_MAX_PAYLOAD_BYTES) throw new Error('udp-packet-payload-too-large');
|
|
@@ -117,8 +127,8 @@ export function encodeUdpPacket({ type, sessionId, key, sequence = 0, header = {
|
|
|
117
127
|
preamble[6] = sessionBytes.length;
|
|
118
128
|
preamble.writeUInt16BE(headerBytes.length, 7);
|
|
119
129
|
preamble.writeUInt16BE(payloadBytes.length, 9);
|
|
120
|
-
preamble.writeUInt32BE((Number(sequence) >>> 0), 11);
|
|
121
|
-
const nonce = crypto.randomBytes(UDP_NONCE_BYTES);
|
|
130
|
+
preamble.writeUInt32BE((Number(sequence) >>> 0), 11);
|
|
131
|
+
const nonce = crypto.randomBytes(UDP_NONCE_BYTES);
|
|
122
132
|
const associatedData = Buffer.concat([preamble, nonce, sessionBytes, headerBytes]);
|
|
123
133
|
const cipher = crypto.createCipheriv('aes-256-gcm', normalizeKey(key), nonce);
|
|
124
134
|
cipher.setAAD(associatedData);
|
|
@@ -169,26 +179,41 @@ export function decodeUdpPacket(packet, key) {
|
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
181
|
|
|
172
|
-
export class UdpReplayWindow {
|
|
182
|
+
export class UdpReplayWindow {
|
|
173
183
|
constructor(size = 512) {
|
|
174
184
|
this.size = Math.max(32, Math.min(4096, Math.floor(Number(size) || 512)));
|
|
175
185
|
this.highest = -1;
|
|
176
186
|
this.seen = new Set();
|
|
177
187
|
}
|
|
178
188
|
|
|
179
|
-
accept(sequence) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
accept(sequence) {
|
|
190
|
+
let value;
|
|
191
|
+
try {
|
|
192
|
+
value = normalizeSequence(sequence);
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (this.seen.has(value)) return false;
|
|
197
|
+
if (this.highest >= 0) {
|
|
198
|
+
const forward = (value - this.highest + UDP_SERIAL_MODULUS) % UDP_SERIAL_MODULUS;
|
|
199
|
+
if (forward === 0 || forward === UDP_SERIAL_HALF_RANGE) return false;
|
|
200
|
+
if (forward > UDP_SERIAL_HALF_RANGE) {
|
|
201
|
+
const age = (this.highest - value + UDP_SERIAL_MODULUS) % UDP_SERIAL_MODULUS;
|
|
202
|
+
if (age > this.size) return false;
|
|
203
|
+
} else {
|
|
204
|
+
this.highest = value;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
this.highest = value;
|
|
208
|
+
}
|
|
209
|
+
this.seen.add(value);
|
|
210
|
+
for (const item of this.seen) {
|
|
211
|
+
const age = (this.highest - item + UDP_SERIAL_MODULUS) % UDP_SERIAL_MODULUS;
|
|
212
|
+
if (age > this.size) this.seen.delete(item);
|
|
213
|
+
}
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
192
217
|
|
|
193
218
|
export class UdpFrameReassembler {
|
|
194
219
|
constructor({ maxPending = 2, timeoutMs = 500, maxFrameBytes = 8 * 1024 * 1024 } = {}) {
|