livedesk 0.1.224 → 0.1.225

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.
@@ -672,6 +672,7 @@ function createDeviceCounters(overrides = {}) {
672
672
  audioStreamsStopped: 0,
673
673
  audioFramesReceived: 0,
674
674
  audioFramesDropped: 0,
675
+ audioStatusReceived: 0,
675
676
  tasksQueued: 0,
676
677
  taskResultsReceived: 0,
677
678
  taskResultsFailed: 0,
@@ -958,6 +959,7 @@ function serializeDevice(device, options = {}) {
958
959
  byteLength: device.latestAudioFrame.byteLength
959
960
  }
960
961
  : null,
962
+ latestAudioStatus: device.latestAudioStatus ? { ...device.latestAudioStatus } : null,
961
963
  latestTask: device.latestTask ? { ...device.latestTask } : null,
962
964
  synthetic: device.synthetic === true,
963
965
  recentTasks: Array.isArray(device.recentTasks)
@@ -2456,6 +2458,7 @@ export function createRemoteHub(options = {}) {
2456
2458
  activeLiveStream: null,
2457
2459
  activeAudioStream: null,
2458
2460
  latestAudioFrame: null,
2461
+ latestAudioStatus: null,
2459
2462
  latestTask,
2460
2463
  recentTasks: latestTask ? [latestTask] : [],
2461
2464
  pendingTaskCommands: new Map(),
@@ -2621,6 +2624,7 @@ export function createRemoteHub(options = {}) {
2621
2624
  activeLiveStream: null,
2622
2625
  activeAudioStream: null,
2623
2626
  latestAudioFrame: null,
2627
+ latestAudioStatus: null,
2624
2628
  latestTask: null,
2625
2629
  recentTasks: [],
2626
2630
  pendingTaskCommands: new Map(),
@@ -3590,6 +3594,50 @@ export function createRemoteHub(options = {}) {
3590
3594
  return true;
3591
3595
  }
3592
3596
 
3597
+ function applyAudioStatus(device, message, framePayload, transport = 'binary') {
3598
+ if (!Buffer.isBuffer(framePayload) || framePayload.length < 1) {
3599
+ device.counters.audioFramesDropped += 1;
3600
+ return false;
3601
+ }
3602
+
3603
+ const streamId = safeString(message.streamId, 128) || device.activeAudioStream?.streamId || 'audio';
3604
+ const status = {
3605
+ streamId,
3606
+ commandId: safeString(message.commandId, 128),
3607
+ captureAlive: message.captureAlive === true,
3608
+ helperAlive: message.helperAlive === true,
3609
+ helperPid: Number(message.helperPid || 0) || 0,
3610
+ captureGeneration: Number(message.captureGeneration || 0) || 0,
3611
+ lastPcmAtEpochMs: Number(message.lastPcmAtEpochMs || 0) || 0,
3612
+ silentForMs: Math.max(0, Number(message.silentForMs || 0) || 0),
3613
+ restartCount: Math.max(0, Number(message.restartCount || 0) || 0),
3614
+ error: safeString(message.error, 500),
3615
+ statusAtEpochMs: Number(message.statusAtEpochMs || 0) || Date.now(),
3616
+ receivedAt: new Date().toISOString(),
3617
+ transport
3618
+ };
3619
+ device.latestAudioStatus = status;
3620
+ if (device.activeAudioStream && device.activeAudioStream.streamId === streamId) {
3621
+ device.activeAudioStream.lastStatusAt = status.receivedAt;
3622
+ device.activeAudioStream.captureAlive = status.captureAlive;
3623
+ device.activeAudioStream.helperAlive = status.helperAlive;
3624
+ device.activeAudioStream.captureGeneration = status.captureGeneration;
3625
+ device.activeAudioStream.lastPcmAtEpochMs = status.lastPcmAtEpochMs;
3626
+ device.activeAudioStream.silentForMs = status.silentForMs;
3627
+ device.activeAudioStream.restartCount = status.restartCount;
3628
+ device.activeAudioStream.audioError = status.error;
3629
+ }
3630
+ device.counters.audioStatusReceived += 1;
3631
+ emitAudio({
3632
+ deviceId: device.deviceId,
3633
+ audioStatus: true,
3634
+ frame: status,
3635
+ payload: framePayload,
3636
+ byteLength: framePayload.length
3637
+ });
3638
+ return true;
3639
+ }
3640
+
3593
3641
  function handleAgentBinaryFrame(socket, state, header, framePayload) {
3594
3642
  if (!state.authenticated || !state.device) {
3595
3643
  writeJsonLine(socket, { type: 'error', error: 'hello-required' });
@@ -3632,6 +3680,11 @@ export function createRemoteHub(options = {}) {
3632
3680
  return;
3633
3681
  }
3634
3682
 
3683
+ if (frameKind === 'audio-status' || header.type === 'audio.status.binary') {
3684
+ applyAudioStatus(device, header, framePayload, 'binary');
3685
+ return;
3686
+ }
3687
+
3635
3688
  emitRemoteEvent('RemoteAgentMessageIgnored', device, {
3636
3689
  messageType: safeString(header.type, 80),
3637
3690
  frameKind
@@ -3650,7 +3703,12 @@ export function createRemoteHub(options = {}) {
3650
3703
  && header.isKeyFrame !== true
3651
3704
  && chunkType !== 'key';
3652
3705
  });
3653
- const index = deltaIndex >= 0 ? deltaIndex : 0;
3706
+ const nonStatusIndex = state.binaryQueue.findIndex(item => {
3707
+ const header = item?.header || {};
3708
+ const frameKind = safeString(header.frameKind || header.kind || header.frameType || '', 40).toLowerCase();
3709
+ return frameKind !== 'audio-status' && header.type !== 'audio.status.binary';
3710
+ });
3711
+ const index = deltaIndex >= 0 ? deltaIndex : nonStatusIndex >= 0 ? nonStatusIndex : 0;
3654
3712
  state.binaryQueue.splice(index, 1);
3655
3713
  state.binaryQueueDrops = Number(state.binaryQueueDrops || 0) + 1;
3656
3714
  }
package/hub/src/server.js CHANGED
@@ -1354,24 +1354,38 @@ function buildRemoteFrameBinaryPacket(frameEvent, diagnostics = {}) {
1354
1354
  return Buffer.concat([header, metaBuffer, payload], 4 + metaBuffer.length + payload.length);
1355
1355
  }
1356
1356
 
1357
- function buildRemoteAudioBinaryPacket(audioEvent) {
1358
- const payload = audioEvent?.payload;
1359
- if (!Buffer.isBuffer(payload) || payload.length === 0) {
1360
- return null;
1361
- }
1362
- const frame = audioEvent.frame || {};
1363
- const metadata = {
1364
- type: 'remote.audio.binary',
1365
- deviceId: audioEvent.deviceId || frame.deviceId || '',
1366
- frameSeq: Number(frame.frameSeq || 0) || 0,
1367
- streamId: frame.streamId || '',
1357
+ function buildRemoteAudioBinaryPacket(audioEvent) {
1358
+ const payload = audioEvent?.payload;
1359
+ if (!Buffer.isBuffer(payload) || payload.length === 0) {
1360
+ return null;
1361
+ }
1362
+ const frame = audioEvent.frame || {};
1363
+ const audioStatus = audioEvent.audioStatus === true;
1364
+ const metadata = {
1365
+ type: audioStatus ? 'remote.audio.status' : 'remote.audio.binary',
1366
+ frameKind: audioStatus ? 'audio-status' : 'audio',
1367
+ deviceId: audioEvent.deviceId || frame.deviceId || '',
1368
+ frameSeq: Number(frame.frameSeq || 0) || 0,
1369
+ streamId: frame.streamId || '',
1368
1370
  mimeType: audioEvent.mimeType || frame.mimeType || frame.format || 'audio/webm;codecs=opus',
1369
1371
  sampleRate: Number(frame.sampleRate || 0) || 48000,
1370
1372
  channels: Number(frame.channels || 0) || 2,
1371
1373
  capturedAt: frame.capturedAt || '',
1372
1374
  receivedAt: frame.receivedAt || '',
1373
- byteLength: Number(audioEvent.byteLength || payload.length) || payload.length
1374
- };
1375
+ byteLength: Number(audioEvent.byteLength || payload.length) || payload.length,
1376
+ ...(audioStatus ? {
1377
+ commandId: frame.commandId || '',
1378
+ captureAlive: frame.captureAlive === true,
1379
+ helperAlive: frame.helperAlive === true,
1380
+ helperPid: Number(frame.helperPid || 0) || 0,
1381
+ captureGeneration: Number(frame.captureGeneration || 0) || 0,
1382
+ lastPcmAtEpochMs: Number(frame.lastPcmAtEpochMs || 0) || 0,
1383
+ silentForMs: Math.max(0, Number(frame.silentForMs || 0) || 0),
1384
+ restartCount: Math.max(0, Number(frame.restartCount || 0) || 0),
1385
+ error: String(frame.error || ''),
1386
+ statusAtEpochMs: Number(frame.statusAtEpochMs || 0) || Date.now()
1387
+ } : {})
1388
+ };
1375
1389
  const metaBuffer = Buffer.from(JSON.stringify(metadata), 'utf8');
1376
1390
  const header = Buffer.allocUnsafe(4);
1377
1391
  header.writeUInt32BE(metaBuffer.length, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.224",
3
+ "version": "0.1.225",
4
4
  "description": "LiveDesk Hub and client launcher",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
34
- "@livedesk/client": "0.1.127",
34
+ "@livedesk/client": "0.1.128",
35
35
  "@openai/codex-sdk": "0.144.5",
36
36
  "cors": "^2.8.5",
37
37
  "express": "^4.21.2",