@andrian.yablonskyy/thub-agent 1.1.22 → 1.1.24

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 (3) hide show
  1. package/RELEASE.md +48 -0
  2. package/package.json +2 -2
  3. package/src/cli.js +16 -5
package/RELEASE.md CHANGED
@@ -2,8 +2,56 @@
2
2
 
3
3
  What changed in each release, newest first. `## Unreleased` collects the changes since the version on npm; `bin/publish` turns that heading into the version and date it releases.
4
4
 
5
+ ## 1.1.24 — 2026-10-16
6
+
7
+ ### Added
8
+
9
+ - **`thub connect --json` streams NDJSON events** for tools such as the VS Code extension. The first line is `{"event": "ready", …}`: the same object as before, with `event` added, so existing readers still work. Then there's one line per change:
10
+ - `status` (`waiting` / `ready`, with the reservation's end);
11
+ - `disconnected` (retry delay, reason) and `connected` (`again`);
12
+ - `video` (the camera streams, or why not);
13
+ - `fatal` (e.g. *The reservation ended*), just before it exits.
14
+
15
+ The human messages still go to stderr.
16
+ - **`thub whoami --json`**: whose key it is (`kind`, `user` or `name`, `group`), as the Agent API's `/me` returns it.
17
+
18
+ ## 1.1.23 — 2026-10-15
19
+
20
+ ### Changed
21
+
22
+ - Requires thub-common 2.2.4.
23
+
24
+ ## 1.1.22 — 2026-10-15
25
+
26
+ ### Changed
27
+
28
+ - **`thub connect` shows the camera's state.** The Video line says whether the board's camera is streaming, and if not, why, for example `(not streaming — ffmpeg isn't installed on this Client host …)`. Each change is printed as it happens (`Video: streaming`, `Video: not streaming — …`), with no viewer needed. Needs Client 1.1.26.
29
+
30
+ ## 1.1.21 — 2026-10-15
31
+
32
+ ### Fixed
33
+
34
+ - **`thub connect` and `thub console` behind a proxy without WebSocket support** stop with the reason instead of retrying forever: *The Coordinator answered HTTP 302 → /login instead of opening a WebSocket: a reverse proxy in front of the Coordinator isn't passing WebSocket upgrades …* The same goes for `426` (Coordinator 1.1.41). Other refusals (`401`, `403`, `404`, `409`) were already final.
35
+
5
36
  ## 1.1.20 — 2026-10-15
6
37
 
38
+ ### Added
39
+
40
+ - **`thub reserve <client> [--for <dur>]`** holds a runner for your own jobs (README §5.5).
41
+ - **Duration:** whole minutes (`30m`, `2h`; 1 h by default), up to the runner's maximum. Running it again extends the reservation from now.
42
+ - **Output:** the end time, how to use the runner, and the reservation's own remote access link.
43
+ - **Release:** **`thub release <client>`** ends it; maintainers and admins can end anyone's.
44
+ - **`thub resources`** has a RESERVED column (who, until when). With `--json`, each runner has its `reservation`, and for your own reservation its `id` and `link`.
45
+ - **`thub connect <client>`** keeps a live connection to your reserved board (README §5.6) and prints that reservation's link and a local port per device:
46
+ - **UARTs:** `127.0.0.1:5001` and up (`--uart-port`), for socat, picocom or pyserial's `socket://`.
47
+ - **ST-Links:** a GDB port each, `127.0.0.1:3333` and up (`--gdb-port`). Each connection starts its own OpenOCD on the Client; `--target <name>` gives the OpenOCD target.
48
+ - **Camera:** if the board has one (README §5.7), `http://127.0.0.1:8090/<reservation id>/video.ts` (`--video-port`) for VLC, ffplay or mpv. The URL is that reservation's own. `--video-udp <port>` also sends it as UDP.
49
+ - **Reconnecting:** it reconnects by itself (1 s, 2 s … 30 s). Serial terminals and video viewers stay connected through a reconnect; GDB has to connect again.
50
+ - **Exiting:** it exits with the reason when the reservation ends.
51
+ - **Options:** `--bind` and `--json`.
52
+ - **`thub console <client> [--uart <n>]`** puts one UART of your reserved board in the terminal. Ctrl-] quits.
53
+ - New dependency: `ws` 8.21.3.
54
+
7
55
  ### Changed
8
56
 
9
57
  - Requires thub-common 2.2.3.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrian.yablonskyy/thub-agent",
3
- "version": "1.1.22",
3
+ "version": "1.1.24",
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>",
@@ -15,7 +15,7 @@
15
15
  "lint:fix": "eslint . --fix"
16
16
  },
17
17
  "dependencies": {
18
- "@andrian.yablonskyy/thub-common": "^2.2.3",
18
+ "@andrian.yablonskyy/thub-common": "^2.2.4",
19
19
  "commander": "^13.1.0",
20
20
  "ws": "8.21.3"
21
21
  },
package/src/cli.js CHANGED
@@ -487,7 +487,7 @@ program
487
487
  .option('--bind <address>', 'Local address to listen on (another than loopback shares the board with your network)', '127.0.0.1')
488
488
  .option('--video-port <port>', 'Local HTTP port of the board\'s USB camera stream, if it has one', (v) => Number(v), 8090)
489
489
  .option('--video-udp <port>', 'Also send the camera stream as UDP (MPEG-TS) to this local port, e.g. 5600', (v) => Number(v))
490
- .option('--json', 'Print the ports as JSON once connected', false)
490
+ .option('--json', 'NDJSON on stdout: "ready" with the ports once connected, then status/disconnected/connected/video/fatal events', false)
491
491
  .action(async (name, opts) => {
492
492
  try {
493
493
  if (opts.target !== undefined && !OPENOCD_TARGET_RE.test(opts.target)){
@@ -500,7 +500,16 @@ program
500
500
  }
501
501
  }
502
502
  const session = await remoteSession(name),
503
- log = (m) => console.error(m);
503
+ log = (m) => console.error(m),
504
+ // --json: NDJSON on stdout — `ready` (the ports, below), then one line
505
+ // per change, for tools such as the VS Code extension. The human
506
+ // messages still go to stderr.
507
+ emit = opts.json ? (e) => console.log(JSON.stringify(e)) : () => {};
508
+ session.on('status', (st) => emit({ event: 'status', state: st.state, client: st.client, until: st.until }));
509
+ session.on('disconnected', ({ retryMs, reason }) => emit({ event: 'disconnected', retryMs, reason: reason || null }));
510
+ session.on('connected', ({ again }) => emit({ event: 'connected', again }));
511
+ session.on('fatal', (message) => emit({ event: 'fatal', message }));
512
+ session.on('devices', (d) => d.camera && emit({ event: 'video', streaming: Boolean(d.camera.streaming), error: d.camera.error || null }));
504
513
  let listening = null,
505
514
  until = null;
506
515
  const cleanup = () => {
@@ -564,7 +573,7 @@ program
564
573
  function printConnection(name, link, until, ports, devices, opts, video){
565
574
  if (opts.json){
566
575
  return console.log(JSON.stringify({
567
- client: name, link, until, ports: ports.map((p) => ({ kind: p.kind, ...p.device, host: opts.bind, port: p.port })),
576
+ event: 'ready', client: name, link, until, ports: ports.map((p) => ({ kind: p.kind, ...p.device, host: opts.bind, port: p.port })),
568
577
  video: video ? { camera: devices.camera.name, url: video.url, udp: video.udp } : null
569
578
  }));
570
579
  }
@@ -730,9 +739,11 @@ function describeMe(me){
730
739
  program
731
740
  .command('whoami')
732
741
  .description('Show whose access key this Agent uses')
733
- .action(async () => {
742
+ .option('--json', 'Machine-readable output', false)
743
+ .action(async (opts) => {
734
744
  try {
735
- console.log(describeMe(await client().get('/me')));
745
+ const me = await client().get('/me');
746
+ console.log(opts.json ? JSON.stringify(me) : describeMe(me));
736
747
  }
737
748
  catch (err){
738
749
  fail(err);