@andrian.yablonskyy/thub-agent 1.1.20 → 1.1.22

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrian.yablonskyy/thub-agent",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "TestHub Agent CLI — the single entry point CI/CD and developers use to submit and follow test jobs",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "Andrian Yablonskyy <andrian.yablonskyy@gmail.com>",
package/src/cli.js CHANGED
@@ -514,10 +514,19 @@ program
514
514
  session.on('status', (s) => {
515
515
  until = s.until;
516
516
  });
517
+ // The camera's state on the Client (README §5.7): said when it changes.
518
+ const cameraState = (c) => (c ? (c.streaming ? 'streaming' : c.error ? `not streaming — ${c.error}` : 'starting…') : null);
519
+ let lastCamera = null;
517
520
  session.on('devices', (devices) => {
521
+ const camera = cameraState(devices.camera);
518
522
  if (listening){
523
+ if (camera && camera !== lastCamera){
524
+ log(`Video: ${camera}`);
525
+ }
526
+ lastCamera = camera;
519
527
  return;
520
528
  }
529
+ lastCamera = camera;
521
530
  listening = (async () => {
522
531
  const local = await listenLocal(session, devices, {
523
532
  host: opts.bind, uartBase: opts.uartPort, gdbBase: opts.gdbPort, target: opts.target, log
@@ -579,7 +588,9 @@ function printConnection(name, link, until, ports, devices, opts, video){
579
588
  }
580
589
  }
581
590
  if (video){
582
- lines.push(`Video ${devices.camera.name} H.264 (MPEG-TS) ${video.url}`,
591
+ const c = devices.camera,
592
+ state = c.streaming === false && c.error ? ` (not streaming — ${c.error})` : c.streaming === false ? ' (starting…)' : '';
593
+ lines.push(`Video ${c.name} H.264 (MPEG-TS) ${video.url}${state}`,
583
594
  ` vlc ${video.url} · ffplay -fflags nobuffer ${video.url}` +
584
595
  (video.udp ? `\n UDP: ${video.udp} (ffplay -fflags nobuffer ${video.udp})` : ' · UDP too: --video-udp 5600'));
585
596
  }
package/src/remote.js CHANGED
@@ -23,8 +23,13 @@ const net = require('node:net'),
23
23
  // Without a ping from the Coordinator (every 30 s) for this long, the
24
24
  // connection is taken for dead and made again.
25
25
  const PING_TIMEOUT_MS = 75_000,
26
- // HTTP answers to the upgrade that won't change by trying again.
27
- FINAL_STATUSES = new Set([401, 403, 404, 409]);
26
+ // HTTP answers to the upgrade that won't change by trying again: a
27
+ // refusal, or — a redirect, 426 — a reverse proxy that doesn't pass
28
+ // WebSocket upgrades on to the Coordinator.
29
+ FINAL_STATUSES = new Set([401, 403, 404, 409, 426]),
30
+ isFinal = (status) => FINAL_STATUSES.has(status) || (status >= 300 && status < 400),
31
+ PROXY_HINT = 'a reverse proxy in front of the Coordinator isn\'t passing WebSocket upgrades on to it ' +
32
+ '(Help → Reserved boards → Client host setup)';
28
33
 
29
34
  // One WebSocket to the reservation's own link (/api/v1/remote/<reservation>,
30
35
  // from GET /resources), made again whenever it drops (1 s, 2 s, 4 s … 30 s
@@ -99,7 +104,14 @@ class RemoteSession extends EventEmitter{
99
104
  catch {
100
105
  error = null;
101
106
  }
102
- refused = { status: res.statusCode, error: error || `HTTP ${res.statusCode}` };
107
+ const status = res.statusCode,
108
+ location = res.headers.location ? ` → ${res.headers.location}` : '';
109
+ refused = {
110
+ status,
111
+ error: error || (status >= 300 && status < 400
112
+ ? `The Coordinator answered HTTP ${status}${location} instead of opening a WebSocket: ${PROXY_HINT}`
113
+ : `HTTP ${status}`)
114
+ };
103
115
  ws.terminate();
104
116
  });
105
117
  });
@@ -116,7 +128,7 @@ class RemoteSession extends EventEmitter{
116
128
  if (this.stopped){
117
129
  return;
118
130
  }
119
- if ((refused && FINAL_STATUSES.has(refused.status)) || code === REMOTE_CLOSE.RESERVATION_ENDED || code === REMOTE_CLOSE.REPLACED){
131
+ if ((refused && isFinal(refused.status)) || code === REMOTE_CLOSE.RESERVATION_ENDED || code === REMOTE_CLOSE.REPLACED){
120
132
  this.stopped = true;
121
133
  const message = reason || 'The Coordinator ended the connection';
122
134
  for (const ch of [...this.channels.keys()]){