@thinremote/thinr-cli 1.5.0 → 1.6.1

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 (46) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +167 -120
  3. package/bin/thinr.js +3 -58
  4. package/commands/_shared.js +1 -0
  5. package/commands/{device/update.js → agent/device.js} +10 -26
  6. package/commands/agent/fleet.js +254 -0
  7. package/commands/agent/index.js +79 -0
  8. package/commands/device/console.js +1 -1
  9. package/commands/device/exec.js +1 -1
  10. package/commands/device/fs.js +6 -6
  11. package/commands/device/index.js +4 -6
  12. package/commands/device/logs.js +1 -1
  13. package/commands/device/proxy.js +15 -6
  14. package/commands/device/status.js +1 -1
  15. package/commands/device/transfer.js +2 -2
  16. package/commands/product/exec.js +1 -1
  17. package/commands/product/fs.js +3 -3
  18. package/commands/product/index.js +5 -4
  19. package/commands/product/logs.js +35 -36
  20. package/commands/product/metric.js +24 -14
  21. package/commands/product/playbook.js +10 -5
  22. package/commands/product/property.js +4 -14
  23. package/commands/product/push.js +1 -1
  24. package/commands/product/resource.js +56 -56
  25. package/commands/product/status.js +1 -1
  26. package/docs/guides/thinremote-metrics-logs.md +21 -22
  27. package/docs/guides/thinremote-playbooks.md +15 -11
  28. package/docs/guides/thinremote-scripts.md +11 -10
  29. package/docs/guides/thinremote.md +53 -42
  30. package/lib/agent-releases.js +1 -1
  31. package/lib/cli.js +172 -0
  32. package/lib/constants.js +1 -1
  33. package/lib/guides.js +1 -1
  34. package/lib/identity.js +8 -6
  35. package/lib/mcp/{tools-exec.js → device-exec.js} +84 -55
  36. package/lib/mcp/helpers.js +2 -2
  37. package/lib/mcp/resource-hooks.js +104 -6
  38. package/lib/mcp/resource-types.js +114 -227
  39. package/lib/playbook/actions.js +1 -1
  40. package/lib/playbook/schema.js +1 -1
  41. package/lib/product/log-presets.js +1 -1
  42. package/lib/product.js +26 -0
  43. package/package.json +2 -2
  44. package/commands/fleet/index.js +0 -17
  45. package/commands/fleet/upgrade.js +0 -275
  46. package/lib/mcp/registry.js +0 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,118 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.1] - 2026-10-31
4
+
5
+ ### Fixed
6
+
7
+ - `thinr device tcp|tls|http -p` is described as what it is: the port
8
+ of the relay on the server (random between 50000 and 51000 by default),
9
+ not a local port.
10
+ - From `@thinger-io/cli-core` 0.5.1: the browser login on a server that does
11
+ not know the CLI's OAuth client says so and offers username and password
12
+ or a token, and the host `update_settings` action is described as the
13
+ merge the server does.
14
+ - Requires `@thinger-io/cli-core` 0.5.1.
15
+
16
+ ## [1.6.0] - 2026-10-27
17
+
18
+ Every command follows one grammar, the one of `@thinger-io/cli-core` 0.5.0
19
+ shared with the `thinger` CLI, noun first:
20
+ `thinr <noun> [<noun>…] <verb> [<parent ids>…] [<id>] [options]`. Nouns
21
+ nest under their parent (`device property`, `product metric`), ids are
22
+ positional and named after their noun (`<device>`, `<product>`), and the
23
+ options keep one meaning everywhere (`-j` JSON, `-s` a body field, `-f` one
24
+ field of the answer, `-o` a file, `-y` no questions). Several commands of
25
+ 1.5 change form: see the table below. The old forms are gone, not aliased.
26
+
27
+ The MCP server serves eight tools instead of 24: `thinr_resource_*` reach
28
+ everything by `type`, records are addressed with `ids`, and the shell
29
+ command is the `exec` action of type `device`. The old tool names and the
30
+ `parent`/`id` arguments are gone, not aliased: see the second table below.
31
+
32
+ ### Added
33
+
34
+ - `thinr agent check <device>` and `thinr agent update <device>`, and
35
+ `thinr agent update --all` for the whole fleet (with the options of the
36
+ old `fleet upgrade`: `-p`, `-g`, `--batch-size`, `--no-canary`,
37
+ `--continue-on-error`, `--dry-run`, `-y`).
38
+ - From `@thinger-io/cli-core` 0.5.0: commands for every resource type
39
+ (`list|get|create|update|delete|clone|describe`, `set` on every kind of
40
+ property, the type's actions), `thinr api <path>`,
41
+ `thinr bucket write|export`, `thinr storage file list|upload|download|delete`,
42
+ `thinr device send|overview`, `thinr device resource list|call`,
43
+ `thinr product apply --from`, `thinr product profile api call`,
44
+ `thinr plugin call`; `--search`, `--filter`, `--count` and `--index` on
45
+ `device list`, `product list` and `bucket list`; `thinr doc list|get`.
46
+ - MCP: `resource_describe` shows each type's ids, actions, permissions and
47
+ ready-to-send examples; `exec` on several devices, playbook runs on
48
+ several devices and rollouts answer their targets first and run with
49
+ `confirm: true` (or ask through the client when it supports it);
50
+ `thinr mcp --read-only` (or `THINR_MCP_READ_ONLY=1`) serves only what
51
+ reads. The product properties `logs` and `dashboard_metrics` are checked
52
+ before they are written, and `resource_describe` type `product_property`
53
+ shows their shape.
54
+
55
+ ### Changed (breaking)
56
+
57
+ | 1.5 | 1.6 |
58
+ | ------------------------------------------------------- | ----------------------------------------------------------- |
59
+ | `thinr device list <pattern>` | `thinr device list --search <text>` |
60
+ | `thinr device info <device>` | `thinr device get <device>` |
61
+ | `thinr device property <device>` | `thinr device property list <device>` |
62
+ | `thinr device property <device> <prop>` | `thinr device property get <device> <property>` |
63
+ | `thinr device property-set <device> <p> <v>` | `thinr device property set <device> <property> <value>` |
64
+ | `thinr device resource <device>` | `thinr device resource list <device>` |
65
+ | `thinr device resource <device> <r> -i k=v` | `thinr device resource call <device> <resource> -s k=v` |
66
+ | `thinr device update check <device>` | `thinr agent check <device>` |
67
+ | `thinr device update apply <device>` | `thinr agent update <device>` |
68
+ | `thinr fleet upgrade [options]` | `thinr agent update --all [options]` |
69
+ | `thinr product info <product>` | `thinr product get <product>` |
70
+ | `thinr product property-list <product>` | `thinr product property list <product>` |
71
+ | `thinr product property-get <product> <p>` | `thinr product property get <product> <property>` |
72
+ | `thinr product property-set <product> <p> <v>` | `thinr product property set <product> <property> <value>` |
73
+ | `thinr product property-delete <product> <p>` | `thinr product property delete <product> <property> -y` |
74
+ | `thinr product property <product> <prop>` (fan-out) | `thinr product device-property <product> <property>` |
75
+ | `thinr product resource <product> <r> -i k=v` (fan-out) | `thinr product device-resource <product> <resource> -s k=v` |
76
+ | `thinr product metric-list <product>` | `thinr product metric list <product>` |
77
+ | `thinr product metric-set <product> <name>` | `thinr product metric set <product> <name>` |
78
+ | `thinr product metric-delete <product> <name>` | `thinr product metric delete <product> <name>` |
79
+ | `thinr product logs add <product> <name>` | `thinr product logs create <product> <name>` |
80
+ | `thinr product logs show <product> <name>` | `thinr product logs get <product> <name>` |
81
+ | `thinr product logs remove\|rm <product> <name>` | `thinr product logs delete <product> <name>` |
82
+
83
+ - `thinr fleet` is gone: `agent update --all` took its only command.
84
+ - `device property get` and `product property get` print the property
85
+ record, with its value under `value`: `-f` paths start there
86
+ (`-f value.setpoint` where 1.5 took `-f setpoint`). `product property
87
+ delete` asks unless `-y`.
88
+ - `product metric delete`, `product logs delete` and
89
+ `product playbook delete` ask for confirmation too: scripts pass `-y`.
90
+ - `device resource call` and `product device-resource` take their input
91
+ from `--from <file>` and `-s, --set key=value` (`-i` is gone); `-s`
92
+ values are parsed as JSON when they are.
93
+ - `product metric set` takes `-j, --json` (it took only `--json`).
94
+ - The help groups the root commands as Resources, Administration,
95
+ Configuration, Integration and Operations, and the verbs of a noun as
96
+ Records, Data, Actions and Subresources, before thinr's own groups.
97
+ - The MCP tools (the commands keep their forms and `-j` outputs):
98
+
99
+ | 1.5 tool | 1.6 call |
100
+ | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
101
+ | `thinr_resource_* parent "gw_1" id "/etc/hosts"` | `thinr_resource_* ids {"device": "gw_1", "path": "/etc/hosts"}` |
102
+ | `thinr_device_exec device` … | `thinr_resource_call type "device" action "exec" ids {device} body {command, …}` |
103
+ | `thinr_device_exec devices\|product\|group\|all` … | `thinr_resource_call type "device" action "exec" body {command, devices\|product\|group\|all, …}` |
104
+ | `type "device_script"` / `"product_script"` id | ids `{device, script}` / `{product, script}` (body `name` on create) |
105
+ | `type "product_playbook"` id | ids `{product, playbook}` |
106
+ | `type "product_metric"` | `type "product_property"` ids `{product, property: "dashboard_metrics"}`, the whole array as value |
107
+ | `type "product_log_source"` | `type "product_property"` ids `{product, property: "logs"}`, `{sources, default}` as value |
108
+ | `thinr_bucket_data_read` / `_write` | `thinr_resource_list` / `_create` `type "bucket_data" ids {bucket}` |
109
+ | `thinr_device_resource_list` / `_call` | `thinr_resource_list type "device_resource"` / `_call action "call" ids {device, resource}` |
110
+ | `thinr_storage_file_*` | `thinr_resource_* type "storage_file" ids {storage, file}` |
111
+ | `thinr_doc_read`, `thinr_profile_list` | `thinr_resource_get type "doc" ids {doc}`, `thinr_resource_list type "profile"` |
112
+ | `thinr_dashboard_*`, `thinr_plugin_call`, `thinr_product_resource_call`, `thinr_device_data_send`, `thinr_device_overview` | `thinr_resource_call` actions `dashboard` `init\|dev\|publish`, `plugin` `call`, `product_profile_api` `call`, `device` `send`, `device` `overview` |
113
+
114
+ - Requires `@thinger-io/cli-core` 0.5.0.
115
+
3
116
  ## [1.5.0] - 2026-10-16
4
117
 
5
118
  ### Added
package/README.md CHANGED
@@ -12,7 +12,7 @@ Two operating modes:
12
12
 
13
13
  - **CLI** — interactive or scripted device management from a shell.
14
14
  - **MCP server** — a stdio [Model Context Protocol][mcp] server that
15
- exposes the same operations as 24 typed tools for AI clients like
15
+ exposes the same operations as eight resource tools for AI clients like
16
16
  Claude Code, Claude Desktop and Cursor.
17
17
 
18
18
  Both modes share the same auth, profiles, and device API layer, so
@@ -28,8 +28,9 @@ task at hand.
28
28
  - [Global options and environment variables](#global-options-and-environment-variables)
29
29
  - [Part 1 — CLI](#part-1--cli)
30
30
  - [JSON output](#json-output)
31
- - [`thinr device <action> [<deviceId>]`](#thinr-device-action-deviceid)
32
- - [`thinr product <action> <productId>`](#thinr-product-action-productid)
31
+ - [`thinr device`](#thinr-device)
32
+ - [`thinr product`](#thinr-product)
33
+ - [`thinr agent`](#thinr-agent)
33
34
  - [`thinr playbook <action>`](#thinr-playbook-action)
34
35
  - [`thinr profile`](#thinr-profile)
35
36
  - [`thinr logout`](#thinr-logout)
@@ -95,13 +96,18 @@ These apply to every subcommand:
95
96
 
96
97
  ## Part 1 — CLI
97
98
 
98
- All device- and product-scoped actions follow a **subcommand-first**
99
- shape: the action comes before the target id, matching patterns like
100
- `kubectl describe pod <name>` or `git remote add <name> <url>`.
99
+ Every command follows one grammar, shared with the `thinger` CLI: nouns
100
+ first, then a verb, then the ids from the outermost to the innermost, then
101
+ the options (`thinr device property get <device> <property>`). Records
102
+ take the same verbs everywhere (`list`, `get`, `create`, `update`,
103
+ `delete`, `clone`, `set`, `describe`), a body comes from `--from <file>`
104
+ and `-s, --set key=value`, and `-j, --json` gives the JSON envelope below.
105
+ `thinr --help` lists the nouns; `thinr <noun> --help` their verbs.
101
106
 
102
107
  ```
103
- thinr device <action> <deviceId> [args…] [options]
104
- thinr product <action> <productId> [args…] [options]
108
+ thinr device <verb> <device> [args…] [options]
109
+ thinr product <verb> <product> [args…] [options]
110
+ thinr device property <verb> <device> <property> [options]
105
111
  ```
106
112
 
107
113
  ### JSON output
@@ -138,35 +144,33 @@ don't change their behaviour — they don't produce a discrete result.
138
144
  `exec` buffers stdout/stderr when `--json` is set and emits a single
139
145
  envelope on exit.
140
146
 
141
- ### `thinr device <action> [<deviceId>]`
147
+ ### `thinr device`
142
148
 
143
- Most actions take a `<deviceId>` as their first positional. The `list`
144
- action is the exception — it operates on the fleet, with an optional
145
- `<pattern>` to filter results.
149
+ Most verbs take a `<device>` as their first positional; `list` works on
150
+ the whole account.
146
151
 
147
152
  #### `list`
148
153
 
149
- List every device visible to the active profile, or filter with a
150
- case-insensitive regex (matched against device id and name on the
151
- server side):
154
+ List every device visible to the active profile, or search and filter
155
+ them on the server:
152
156
 
153
157
  ```bash
154
- thinr device list # every device
155
- thinr device list supermicro # only ids/names containing "supermicro"
156
- thinr device list "^revpi|jetson" # regex alternation works too
157
- thinr device list --json # JSON envelope with the raw API records
158
+ thinr device list # every device
159
+ thinr device list --search supermicro # ids/names containing "supermicro"
160
+ thinr device list --filter product=my-product # filters are repeatable
161
+ thinr device list --json # JSON envelope with the raw API records
158
162
  ```
159
163
 
160
164
  Combine with the global `-u, --user` flag for admin impersonation.
161
165
 
162
- #### Records — `info` / `create` / `delete` / `set-product`
166
+ #### Records — `get` / `create` / `update` / `delete` / `set-product`
163
167
 
164
168
  ```bash
165
- thinr device info <deviceId> # server record as JSON
166
- thinr device create <deviceId> [-t Generic|MQTT|HTTP|Virtual|Subdevice] [-p product]
167
- thinr device delete <deviceId> [-y]
168
- thinr device set-product <deviceId> <productId> # "" to unassign
169
- thinr device property-set <deviceId> <property> <json>
169
+ thinr device get <device> # server record as JSON (-f <path> for one field)
170
+ thinr device create <device> [-t Generic|MQTT|HTTP|Virtual|Subdevice] [-p product] [-n name]
171
+ thinr device update <device> -s name="Boiler 1"
172
+ thinr device delete <device> [-y]
173
+ thinr device set-product <device> <product> # "" to unassign
170
174
  ```
171
175
 
172
176
  `create` generates random credentials for Generic/MQTT devices unless
@@ -177,7 +181,7 @@ thinr device property-set <deviceId> <property> <json>
177
181
  Open an interactive terminal on the device:
178
182
 
179
183
  ```bash
180
- thinr device console <deviceId>
184
+ thinr device console <device>
181
185
  ```
182
186
 
183
187
  #### Proxies — `tcp` / `tls` / `http`
@@ -186,9 +190,9 @@ Create a TCP, TLS, or HTTP proxy to a remote endpoint reachable from
186
190
  the device:
187
191
 
188
192
  ```bash
189
- thinr device tcp <deviceId> [target] [options]
190
- thinr device tls <deviceId> [target] [options]
191
- thinr device http <deviceId> [target] [options]
193
+ thinr device tcp <device> [target] [options]
194
+ thinr device tls <device> [target] [options]
195
+ thinr device http <device> [target] [options]
192
196
  ```
193
197
 
194
198
  `target` is the remote address and port on the device side. Defaults
@@ -216,24 +220,24 @@ thinr device http RevPi20679 http://192.168.1.45:8080
216
220
  #### `status`
217
221
 
218
222
  ```bash
219
- thinr device status <deviceId> [-j, --json]
223
+ thinr device status <device> [-j, --json]
220
224
  ```
221
225
 
222
226
  Returns the device's connection stats (uptime, tx/rx, last-seen).
223
227
 
224
228
  #### `property`
225
229
 
226
- List every property of a device, or read one by id:
230
+ The properties of a device are records of their own:
227
231
 
228
232
  ```bash
229
- thinr device property <deviceId> # list
230
- thinr device property <deviceId> <propertyId> [options] # read
233
+ thinr device property list <device>
234
+ thinr device property get <device> <property> [-f value.threshold]
235
+ thinr device property set <device> <property> '{"threshold": 30}' # created when missing
236
+ thinr device property delete <device> <property> [-y]
231
237
  ```
232
238
 
233
- Options:
234
-
235
- - `-j, --json`: Output as JSON
236
- - `-f, --field <path>`: Extract a sub-field via dot path (`-f data.value`)
239
+ `-f, --field <path>` prints one field of the record: the value of a
240
+ property is under `value`.
237
241
 
238
242
  #### `resource`
239
243
 
@@ -241,22 +245,21 @@ List the resources exposed by the device (with their `in`/`out`
241
245
  schemas when advertised), or call one:
242
246
 
243
247
  ```bash
244
- thinr device resource <deviceId> # list
245
- thinr device resource <deviceId> <resourceId> [options] # call
248
+ thinr device resource list <device>
249
+ thinr device resource call <device> <resource> # read its output
250
+ thinr device resource call <device> <resource> -s value=true # run it with an input
246
251
  ```
247
252
 
248
- Options:
249
-
250
- - `-i, --input <key=value>`: Resource input (repeatable)
251
- - `-j, --json`: Output as JSON
252
- - `-f, --field <path>`: Extract a sub-field from the result (dot path)
253
+ `call` takes the input from `--from <file>` (`-` for stdin) and
254
+ `-s, --set key=value` (repeatable, values parsed as JSON when they are),
255
+ and prints the answer, or one field of it with `-f, --field <path>`.
253
256
 
254
257
  #### `exec`
255
258
 
256
259
  Run a shell command on the device and stream stdout/stderr back:
257
260
 
258
261
  ```bash
259
- thinr device exec <deviceId> "<command>" [options]
262
+ thinr device exec <device> "<command>" [options]
260
263
  ```
261
264
 
262
265
  Options:
@@ -267,28 +270,18 @@ Options:
267
270
 
268
271
  The process exits with the remote command's exit code.
269
272
 
270
- #### `update`
271
-
272
- Check for or apply an agent update on the device:
273
-
274
- ```bash
275
- thinr device update check <deviceId> [options]
276
- thinr device update apply <deviceId> [options]
277
- ```
278
-
279
- Options:
273
+ ### `thinr product`
280
274
 
281
- - `--channel <name>`: Update channel (default: `latest`)
282
- - `-j, --json`: Output as JSON
275
+ Besides its records (`list`, `get`, `create`, `update`, `delete`,
276
+ `export`, `apply`, `profile`…), a product fans operations out across
277
+ every device that belongs to it.
283
278
 
284
- ### `thinr product <action> <productId>`
279
+ #### `device-property`
285
280
 
286
- Fan out an operation across every device that belongs to a product.
287
-
288
- #### `property`
281
+ Read a property on every device of the product:
289
282
 
290
283
  ```bash
291
- thinr product property <productId> <propertyId> [options]
284
+ thinr product device-property <product> <property> [options]
292
285
  ```
293
286
 
294
287
  Options:
@@ -299,10 +292,12 @@ Options:
299
292
  - `-a, --all`: Include offline devices (default: only active)
300
293
  - `-g, --group <group>`: Filter devices by asset group
301
294
 
302
- #### `resource`
295
+ #### `device-resource`
296
+
297
+ Call a resource on every active device of the product:
303
298
 
304
299
  ```bash
305
- thinr product resource <productId> <resource> [options]
300
+ thinr product device-resource <product> <resource> [options]
306
301
  ```
307
302
 
308
303
  Options:
@@ -310,10 +305,28 @@ Options:
310
305
  - `-j, --json`: Output as JSON (`results[]` array, same shape as above)
311
306
  - `-f, --field <path>`: Extract a sub-field from each result (dot path)
312
307
  - `-g, --group <group>`: Filter devices by asset group
313
- - `-i, --input <key=value>`: Resource input (repeatable)
308
+ - `--from <file>`, `-s, --set <key=value>`: Resource input (`-s` is
309
+ repeatable)
314
310
 
315
- Offline devices are skipped for `resource` — the server would reject
316
- the call anyway.
311
+ Offline devices are skipped for `device-resource` — the server would
312
+ reject the call anyway.
313
+
314
+ #### `metric` and `logs`
315
+
316
+ What thinr's terminal dashboard shows for the devices of a product:
317
+
318
+ ```bash
319
+ thinr product metric list <product>
320
+ thinr product metric set <product> <name> [options]
321
+ thinr product metric delete <product> <name> [-y]
322
+
323
+ thinr product logs presets # patterns shipped with thinr
324
+ thinr product logs list <product>
325
+ thinr product logs get <product> <name>
326
+ thinr product logs create <product> <name> [options]
327
+ thinr product logs delete <product> <name> [-y]
328
+ thinr product logs set-default <product> <name>
329
+ ```
317
330
 
318
331
  #### `exec`
319
332
 
@@ -322,7 +335,7 @@ product and report a per-device status table plus each device's
322
335
  output:
323
336
 
324
337
  ```bash
325
- thinr product exec <productId> <command...> [options]
338
+ thinr product exec <product> <command...> [options]
326
339
  ```
327
340
 
328
341
  Options:
@@ -349,9 +362,9 @@ thinr product exec my-product "apt-get -y upgrade" -c 5 --timeout 600
349
362
  thinr product exec my-product "systemctl restart agent" -g staging
350
363
  ```
351
364
 
352
- Also exposed to MCP clients as `thinr_device_exec` with `product` (and
353
- `group`), so agents run a fleet operation with a single call instead of
354
- looping per device.
365
+ Also exposed to MCP clients as the `exec` action of type `device` with
366
+ body `product` (and `group`), so agents run a fleet operation with a
367
+ single call instead of looping per device.
355
368
 
356
369
  #### `playbook`
357
370
 
@@ -359,11 +372,11 @@ Manage playbooks stored on a product and run them against one device
359
372
  or the whole fleet:
360
373
 
361
374
  ```bash
362
- thinr product playbook list <productId> [--json]
363
- thinr product playbook upload <productId> <name> [file] [--description <text>] [--skip-validation] [--json]
364
- thinr product playbook download <productId> <name> [-o <file>] [--json]
365
- thinr product playbook delete <productId> <name> [--json]
366
- thinr product playbook run <productId> <name> (--device <id> | --fleet) [options]
375
+ thinr product playbook list <product> [--json]
376
+ thinr product playbook upload <product> <name> [file] [--description <text>] [--skip-validation] [--json]
377
+ thinr product playbook download <product> <name> [-o <file>] [--json]
378
+ thinr product playbook delete <product> <name> [-y] [--json]
379
+ thinr product playbook run <product> <name> (--device <id> | --fleet) [options]
367
380
  ```
368
381
 
369
382
  `upload` reads the YAML from a file, or from stdin when the path is
@@ -400,6 +413,28 @@ Each fleet rollout writes a persistent JSON report to
400
413
  storage, capturing resolved variables, per-device outcomes, and any
401
414
  abort reason.
402
415
 
416
+ ### `thinr agent`
417
+
418
+ The ThinRemote agent running on the devices: check whether a device has a
419
+ newer release, and update one device or the whole fleet.
420
+
421
+ ```bash
422
+ thinr agent check <device> [--channel latest] [-j]
423
+ thinr agent update <device> [--channel latest] [-j]
424
+ thinr agent update --all [-p <product>] [-g <group>] [options]
425
+ ```
426
+
427
+ `update --all` updates every outdated device: a canary first, then in
428
+ batches. Options (only with `--all`):
429
+
430
+ - `-p, --product <id>`, `-g, --group <id>` — narrow the fleet.
431
+ - `--batch-size <n>` — devices updated in parallel once the canary
432
+ passes (default: 5).
433
+ - `--no-canary` — everything in batches from the start.
434
+ - `--continue-on-error` — keep rolling after a batch failure.
435
+ - `--dry-run` — list what would be updated, without updating it.
436
+ - `-y, --yes` — no confirmation (required in non-interactive shells).
437
+
403
438
  ### `thinr playbook <action>`
404
439
 
405
440
  Run or validate a local playbook YAML file (not stored on a product).
@@ -447,7 +482,7 @@ active profile; `--server`, `--username` and `--token` skip the prompts.
447
482
  ## Part 2 — MCP server
448
483
 
449
484
  `thinr` ships an [MCP][mcp] server for AI clients (Claude Code, Claude
450
- Desktop, Cursor and any other MCP host): 24 tools that reach everything the
485
+ Desktop, Cursor and any other MCP host): eight tools that reach everything the
451
486
  CLI does, through the same API, with stable schemas and one error contract.
452
487
 
453
488
  ### Starting the server
@@ -465,37 +500,47 @@ device of any configured environment without a restart.
465
500
  ### Tools
466
501
 
467
502
  thinr is built on [`@thinger-io/cli-core`][cli-core], the core it shares
468
- with the `thinger` CLI, and serves its 23 generic tools with the `thinr_`
469
- prefix plus one of its own:
470
-
471
- | Tools | What for |
472
- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
473
- | `thinr_resource_describe`, `_list`, `_get`, `_create`, `_update`, `_delete`, `_clone`, `_call` | Every resource type: the server's and ThinRemote's (below), and their actions |
474
- | `thinr_device_resource_list`, `thinr_device_resource_call` | What a connected device exposes: agent resources (`monitoring`, `cmd`…) and its scripts |
475
- | `thinr_bucket_data_read`, `thinr_bucket_data_write` | Time series, monitoring history included (bucket `monitoring`) |
476
- | `thinr_storage_file_*`, `thinr_dashboard_init` / `_dev` / `_publish`, `thinr_plugin_call`, `thinr_product_resource_call`, `thinr_device_data_send`, `thinr_device_overview` | Storages, web dashboards (create, develop against the server, publish), plugins, product APIs, HTTP devices, account usage |
477
- | `thinr_doc_read`, `thinr_profile_list` | Guides for agents (`thinremote`, `thinremote-scripts`, `thinremote-playbooks`, `thinremote-metrics-logs`, and the platform's: `concepts`, `product-code`, `server-admin`, `dashboard-sdk`) and profiles |
478
- | `thinr_device_exec` | A shell command on one device, or on several at once (`devices`, `product`, `group`, `all`) |
479
-
480
- ThinRemote adds its own resource types to the generic tools, so they are
481
- listed, read, written and deleted like a device or a token:
482
-
483
- | Type | Parent | What it is | Actions (`thinr_resource_call`) |
484
- | -------------------- | ------- | ------------------------------------------------------------------------------ | ------------------------------- |
485
- | `device_fs` | device | Files and folders of the device (id: the path) | `move`, `push`, `pull` |
486
- | `device_script` | device | Scripts of the agent, each a resource of the device | |
487
- | `product_script` | product | Scripts run on any device of the product, kept once in its storage | |
488
- | `product_metric` | product | Metrics of thinr's terminal dashboard | |
489
- | `product_log_source` | product | Log sources an operator streams from a device | |
490
- | `product_playbook` | product | Playbooks: YAML steps validated, tried on one device and rolled out in batches | `validate`, `run`, `rollout` |
491
-
492
- and two actions to the server's `device` type: `agent_update` (check for or
493
- install a new agent) and `agent_install` (the command that installs the
494
- agent on a new machine). Live monitoring is the device resource
495
- `monitoring`; its history, the bucket `monitoring`.
496
-
497
- Full input schemas come with the standard MCP `tools/list`, and
498
- `thinr_resource_describe` gives each type's fields and actions.
503
+ with the `thinger` CLI, and serves its eight tools with the `thinr_`
504
+ prefix: `thinr_resource_describe`, `_list`, `_get`, `_create`, `_update`,
505
+ `_delete`, `_clone` and `_call` (actions). Each takes a `type` and the
506
+ record's `ids`, one key per level (`{"device": "gw_1"}`,
507
+ `{"device": "gw_1", "path": "/etc/hosts"}`,
508
+ `{"product": "gateways", "playbook": "app-config"}`); the records they
509
+ answer carry their ids.
510
+
511
+ | Types | What for |
512
+ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
513
+ | The server's (`device`, `product`, `bucket`, `token`, `alarm_rule`…) | Every resource of the account and their actions |
514
+ | `device_resource` (list; action `call`) | What a connected device exposes: agent resources (`monitoring`, `cmd`…) and its scripts |
515
+ | `bucket_data` (list reads, create writes) | Time series, monitoring history included (bucket `monitoring`) |
516
+ | `storage_file`; `dashboard` actions `init` / `dev` / `publish`; others | Storages, web dashboards (create, develop against the server, publish), plugins (`plugin` `call`), product APIs (`product_profile_api` `call`), HTTP devices (`device` `send`), account usage (`device` `overview`) |
517
+ | `doc`, `profile` | Guides for agents (`thinremote`, `thinremote-scripts`, `thinremote-playbooks`, `thinremote-metrics-logs`, and the platform's: `concepts`, `product-code`, `server-admin`, `dashboard-sdk`) and profiles |
518
+
519
+ ThinRemote adds its own resource types, listed, read, written and deleted
520
+ like a device or a token:
521
+
522
+ | Type | ids | What it is | Actions (`thinr_resource_call`) |
523
+ | ------------------ | --------------------- | ------------------------------------------------------------------------------ | ------------------------------- |
524
+ | `device_fs` | `device`, `path` | Files and folders of the device | `move`, `push`, `pull` |
525
+ | `device_script` | `device`, `script` | Scripts of the agent, each a resource of the device | |
526
+ | `product_script` | `product`, `script` | Scripts run on any device of the product, kept once in its storage | |
527
+ | `product_playbook` | `product`, `playbook` | Playbooks: YAML steps validated, tried on one device and rolled out in batches | `validate`, `run`, `rollout` |
528
+
529
+ and three actions to the server's `device` type: `exec` (a shell command
530
+ on one device, `ids.device`, or on several at once: body `devices`,
531
+ `product`, `group`, `all`), `agent_update` (check for or install a new
532
+ agent) and `agent_install` (the command that installs the agent on a new
533
+ machine). Live monitoring is the device resource `monitoring`; its
534
+ history, the bucket `monitoring`. The fleet metrics and log sources of
535
+ thinr's terminal dashboard are the product properties `dashboard_metrics`
536
+ and `logs`, checked before they are written.
537
+
538
+ `exec` on several devices, playbook runs on several devices and rollouts
539
+ answer their targets first and run with `confirm: true` (clients that
540
+ support elicitation ask the user instead). `thinr mcp --read-only` (or
541
+ `THINR_MCP_READ_ONLY=1`) serves only what reads. Full input schemas come
542
+ with the standard MCP `tools/list`, and `thinr_resource_describe` gives
543
+ each type's ids, fields, actions and examples.
499
544
 
500
545
  [cli-core]: https://github.com/thinger-io/thinger-cli-core
501
546
 
@@ -513,15 +558,16 @@ writing YAML by hand:
513
558
  2. **Draft and validate.** Write the YAML and call `thinr_resource_call`
514
559
  type `product_playbook`, action `validate`, with it in `body.content`;
515
560
  fix what it reports until it is clean.
516
- 3. **Save.** `thinr_resource_create` type `product_playbook`, parent the
517
- product, id a short name, body `{content}`. It is validated again and
518
- nothing is saved if invalid.
519
- 4. **Pre-flight on one device.** Action `run` with the playbook's id and
561
+ 3. **Save.** `thinr_resource_create` type `product_playbook`, ids
562
+ `{"product", "playbook": <a short name>}`, body `{content}`. It is
563
+ validated again and nothing is saved if invalid.
564
+ 4. **Pre-flight on one device.** Action `run` with `ids.playbook` and
520
565
  `body.device`, with `dry_run: true` or `check: true` for a read-only
521
566
  preview, then for real.
522
567
  5. **Roll out.** Action `rollout` runs it across the fleet in batches with a
523
568
  failure-rate stop, and writes a report to `playbooks/runs/` in the
524
- product storage.
569
+ product storage. It answers its plan first and runs with
570
+ `confirm: true` once the user agrees.
525
571
 
526
572
  Example prompt:
527
573
 
@@ -537,10 +583,10 @@ A second message like "looks good, roll it out to staging" maps to the
537
583
  ### Per-call controls
538
584
 
539
585
  - **`profile`**: optional on every tool. Uses another configured server for
540
- that call only; `thinr_profile_list` lists them.
586
+ that call only; `thinr_resource_list` type `profile` lists them.
541
587
  - **`user`**: optional admin impersonation; defaults to the profile's user.
542
- - Devices are named per call (`device`, or `parent` for a device's
543
- resources): list them with `thinr_resource_list` type `device`.
588
+ - Devices are named per call (`ids.device`): list them with
589
+ `thinr_resource_list` type `device`.
544
590
 
545
591
  ### Integrating with Claude Code
546
592
 
@@ -611,16 +657,17 @@ Use `thinr profile use <name>` to change the persisted default, or
611
657
  ```
612
658
  thinr-cli/
613
659
  ├── bin/ # CLI entry point (thinr)
614
- ├── commands/ # ThinRemote subcommands (agent, fleet, playbooks…)
660
+ ├── commands/ # ThinRemote subcommands (agent, device, product, playbooks…)
615
661
  ├── lib/ # ThinRemote modules: identity, device-api, proxy,
616
662
  │ # console, playbooks, product scripts, MCP tools…
617
663
  ├── package.json
618
664
  └── README.md
619
665
  ```
620
666
 
621
- The generic part — API client, auth, profiles, REST helpers, the generic
622
- commands (login, logout, profile, device list/info/property/resource,
623
- product list/property-\_, bucket) and the generic MCP tools — comes from
667
+ The generic part — API client, auth, profiles, REST helpers, the command
668
+ grammar, the generic commands (login, logout, profile, api, the records of
669
+ every resource type, device resource, bucket, storage…) and the generic MCP
670
+ tools — comes from
624
671
  [@thinger-io/cli-core](https://github.com/thinger-io/cli-core), shared
625
672
  with the `thinger` CLI. `lib/identity.js` is what makes it `thinr`: names,
626
673
  `~/.config/thinr-cli`, `THINR\__`, the OAuth client and the product