@thinremote/thinr-cli 1.3.4 → 1.4.0
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/CHANGELOG.md +86 -0
- package/README.md +117 -123
- package/bin/thinr.js +43 -105
- package/commands/_shared.js +10 -90
- package/commands/device/_shared.js +2 -2
- package/commands/device/exec.js +2 -2
- package/commands/device/fs.js +2 -2
- package/commands/device/index.js +5 -19
- package/commands/device/logs.js +4 -4
- package/commands/device/status.js +1 -1
- package/commands/device/transfer.js +2 -2
- package/commands/device/update.js +8 -2
- package/commands/fleet/upgrade.js +3 -3
- package/commands/playbook/run.js +2 -2
- package/commands/playbook/validate.js +2 -2
- package/commands/product/_shared.js +1 -1
- package/commands/product/exec.js +2 -2
- package/commands/product/fs.js +2 -2
- package/commands/product/index.js +4 -11
- package/commands/product/logs.js +2 -2
- package/commands/product/metric.js +2 -2
- package/commands/product/playbook.js +5 -5
- package/commands/product/property.js +4 -4
- package/commands/product/push.js +2 -2
- package/commands/product/resource.js +4 -4
- package/commands/product/status.js +3 -3
- package/dist/dashboard.js +12 -12
- package/docs/guides/thinremote-metrics-logs.md +59 -0
- package/docs/guides/thinremote-playbooks.md +133 -0
- package/docs/guides/thinremote-scripts.md +115 -0
- package/docs/guides/thinremote.md +116 -0
- package/lib/command-stream.js +3 -2
- package/lib/console.js +6 -6
- package/lib/constants.js +1 -19
- package/lib/dashboard/event-stream.js +5 -5
- package/lib/device-api.js +4 -4
- package/lib/guides.js +41 -0
- package/lib/identity.js +75 -0
- package/lib/local-files.js +54 -0
- package/lib/mcp/{tools-install.js → agent-install.js} +12 -61
- package/lib/mcp/helpers.js +5 -25
- package/lib/mcp/playbook-ops.js +393 -0
- package/lib/mcp/registry.js +9 -43
- package/lib/mcp/resource-hooks.js +53 -0
- package/lib/mcp/resource-types.js +857 -0
- package/lib/mcp/tools-exec.js +217 -177
- package/lib/monitoring.js +3 -3
- package/lib/playbook/actions.js +30 -18
- package/lib/playbook/expression.js +1 -4
- package/lib/playbook/fleet.js +4 -1
- package/lib/playbook/loader.js +16 -8
- package/lib/playbook/runner.js +40 -8
- package/lib/playbook/schema.js +108 -34
- package/lib/product/log-presets.js +1 -1
- package/lib/product/logs.js +2 -2
- package/lib/product-orchestrator.js +14 -9
- package/lib/product.js +29 -227
- package/lib/proxy.js +4 -4
- package/lib/status.js +13 -5
- package/package.json +4 -2
- package/commands/device/delete.js +0 -53
- package/commands/device/list.js +0 -55
- package/commands/device/property.js +0 -52
- package/commands/device/resource.js +0 -52
- package/commands/logout.js +0 -47
- package/commands/product/list.js +0 -86
- package/commands/product/props.js +0 -125
- package/commands/profile.js +0 -173
- package/lib/alarms.js +0 -223
- package/lib/api.js +0 -89
- package/lib/auth.js +0 -428
- package/lib/bucket.js +0 -82
- package/lib/concurrency.js +0 -52
- package/lib/config.js +0 -230
- package/lib/devices.js +0 -60
- package/lib/errors.js +0 -65
- package/lib/format.js +0 -114
- package/lib/mcp/server.js +0 -67
- package/lib/mcp/tools-alarms.js +0 -507
- package/lib/mcp/tools-buckets.js +0 -99
- package/lib/mcp/tools-devices.js +0 -235
- package/lib/mcp/tools-filesystem.js +0 -281
- package/lib/mcp/tools-logs.js +0 -225
- package/lib/mcp/tools-playbook.js +0 -210
- package/lib/mcp/tools-products.js +0 -1557
- package/lib/mcp/tools-profile.js +0 -635
- package/lib/mcp/tools-profiles.js +0 -30
- package/lib/mcp/tools-properties.js +0 -79
- package/lib/mcp/tools-resources.js +0 -78
- package/lib/mcp/tools-scripts.js +0 -212
- package/lib/mcp/tools-tokens.js +0 -369
- package/lib/output.js +0 -114
- package/lib/paths.js +0 -32
- package/lib/profile.js +0 -657
- package/lib/property.js +0 -75
- package/lib/resource.js +0 -151
- package/lib/storage.js +0 -117
- package/lib/tokens.js +0 -366
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.0] - 2026-10-14
|
|
4
|
+
|
|
5
|
+
Incompatible change of the MCP tools: thinr is now the generic core it
|
|
6
|
+
shares with the `thinger` CLI, `@thinger-io/cli-core` 0.3.0, plus what only
|
|
7
|
+
a device with the agent can do. From 84 tools to 24, and from about 27,600
|
|
8
|
+
to about 8,000 tokens in `tools/list`. Also incompatible for playbooks: the
|
|
9
|
+
step actions `mv` and `rm` are now `move` and `delete`.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The generic core (API client, auth, profiles, REST helpers, generic
|
|
14
|
+
commands and MCP tools) comes from `@thinger-io/cli-core`. thinr supplies
|
|
15
|
+
its identity (`lib/identity.js`) and its own commands, types and tools.
|
|
16
|
+
Profiles and `THINR_*` variables are unchanged.
|
|
17
|
+
- Server resources (devices, products, buckets, tokens, alarms, properties,
|
|
18
|
+
product profiles…) go through `thinr_resource_list` / `_get` / `_create` /
|
|
19
|
+
`_update` / `_delete` / `_clone` / `_call` with a `type`, described by
|
|
20
|
+
`thinr_resource_describe`; device resources through
|
|
21
|
+
`thinr_device_resource_list` / `_call`.
|
|
22
|
+
- ThinRemote's own resources are resource types served by those same
|
|
23
|
+
tools: `device_fs` (a device's files and folders; actions `move`, `push`,
|
|
24
|
+
`pull`), `device_script`, `product_script`, `product_metric`,
|
|
25
|
+
`product_log_source` and `product_playbook` (actions `validate`, `run`,
|
|
26
|
+
`rollout`). Creating one that exists fails, as does updating one that
|
|
27
|
+
does not (a `device_fs` file is written either way). Actions on devices:
|
|
28
|
+
`agent_update` and `agent_install` (the install command).
|
|
29
|
+
- The one tool of thinr's own, `thinr_device_exec`, runs a command on one
|
|
30
|
+
device or on several (`devices`, `product` and `group`, or `all`: every
|
|
31
|
+
device of the ThinRemote products); offline devices are left out unless
|
|
32
|
+
`include_offline`, and named when listed by id; `dry_run` lists the
|
|
33
|
+
targets.
|
|
34
|
+
- Old tools and where they went: `exec` → `device_exec`; `read`, `write`,
|
|
35
|
+
`ls`, `rm`, `mv`, `mkdir`, `push`, `pull` → type `device_fs`;
|
|
36
|
+
`script_*` → type `device_script`; `product_script_*` → type
|
|
37
|
+
`product_script`; `product_metric_*` → type `product_metric`;
|
|
38
|
+
`product_logs_*` → type `product_log_source` (presets in its
|
|
39
|
+
description); `product_playbook_*`, `playbook_schema`,
|
|
40
|
+
`playbook_validate`, `playbook_run` → type `product_playbook`; `update` →
|
|
41
|
+
action `agent_update`; `agent_install_command` → action `agent_install`;
|
|
42
|
+
`product_exec` → `device_exec` with `product` (its `all`, which included
|
|
43
|
+
offline devices, is now `include_offline`); `monitoring` → the device
|
|
44
|
+
resource `monitoring` (live) and the bucket `monitoring` (history);
|
|
45
|
+
`product_write` → a playbook (`write` and `move` steps, rolled out);
|
|
46
|
+
`devices`, `device_info`, `products`, `product_create`… → the
|
|
47
|
+
`resource_*` tools; `profiles` → `profile_list`; `bucket_read` →
|
|
48
|
+
`bucket_data_read`.
|
|
49
|
+
- A playbook run needs one explicit target (`device`, `devices`, `group` or
|
|
50
|
+
`all`): never the whole product by omission. Its variables are checked
|
|
51
|
+
against what the playbook declares, inline ones too.
|
|
52
|
+
- From the MCP server, `push`, `pull` (and playbook `content_file`) only
|
|
53
|
+
reach files inside one local folder, `THINR_MCP_LOCAL_DIR` or where the
|
|
54
|
+
server started; `pull` replaces a file only when asked.
|
|
55
|
+
- Deleting a product also deletes its script storage.
|
|
56
|
+
- MCP calls with a `profile` argument are scoped to that call.
|
|
57
|
+
- `thinr logout` revokes the API token it created at a username/password
|
|
58
|
+
login (`--keep-token` to keep it) and accepts `--all`.
|
|
59
|
+
- Node 20 or later, as cli-core.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- Guides for agents, served by `thinr_doc_read` and as MCP resources:
|
|
64
|
+
`thinremote`, `thinremote-scripts`, `thinremote-playbooks`,
|
|
65
|
+
`thinremote-metrics-logs`; MCP server instructions with a map of where
|
|
66
|
+
each thing is.
|
|
67
|
+
- Reading a device with the agent connected adds `agent.system_info`, its
|
|
68
|
+
file system base path and `agent.where`, the map of its tools.
|
|
69
|
+
- `thinr login --server --username --token` signs in without prompts.
|
|
70
|
+
- `thinr device info`, `create`, `set-product`, `property-set`;
|
|
71
|
+
`thinr product info`, `create`, `delete`; `thinr bucket list`, `read`.
|
|
72
|
+
- From cli-core: dashboards, storage files, bucket data, endpoints, alarm
|
|
73
|
+
rule tests and stats, the server's documentation as MCP resources.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- Reading a device showed no system information: it asked the agent for
|
|
78
|
+
`system_info` instead of `system`.
|
|
79
|
+
|
|
80
|
+
## [1.3.5] - 2026-09-24
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- `thinr_ls` describes what "/" actually resolves to: the agent process's
|
|
85
|
+
working directory, which is normally the filesystem root. 1.3.4 said it was
|
|
86
|
+
a base directory "usually /var/log", which was a stale service definition on
|
|
87
|
+
one device rather than how installs behave.
|
|
88
|
+
|
|
3
89
|
## [1.3.4] - 2026-09-24
|
|
4
90
|
|
|
5
91
|
### Fixed
|
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
|
|
15
|
+
exposes the same operations as 24 typed 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
|
|
@@ -159,6 +159,19 @@ thinr device list --json # JSON envelope with the raw API records
|
|
|
159
159
|
|
|
160
160
|
Combine with the global `-u, --user` flag for admin impersonation.
|
|
161
161
|
|
|
162
|
+
#### Records — `info` / `create` / `delete` / `set-product`
|
|
163
|
+
|
|
164
|
+
```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>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`create` generates random credentials for Generic/MQTT devices unless
|
|
173
|
+
`-c` is given; they are shown once.
|
|
174
|
+
|
|
162
175
|
#### `console`
|
|
163
176
|
|
|
164
177
|
Open an interactive terminal on the device:
|
|
@@ -336,9 +349,9 @@ thinr product exec my-product "apt-get -y upgrade" -c 5 --timeout 600
|
|
|
336
349
|
thinr product exec my-product "systemctl restart agent" -g staging
|
|
337
350
|
```
|
|
338
351
|
|
|
339
|
-
Also exposed
|
|
340
|
-
|
|
341
|
-
|
|
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.
|
|
342
355
|
|
|
343
356
|
#### `playbook`
|
|
344
357
|
|
|
@@ -417,25 +430,25 @@ thinr profile delete <name> [--json]
|
|
|
417
430
|
|
|
418
431
|
### `thinr logout`
|
|
419
432
|
|
|
420
|
-
|
|
433
|
+
Forget the active profile and revoke the API token `thinr` created for it
|
|
434
|
+
at a username/password login:
|
|
421
435
|
|
|
422
436
|
```bash
|
|
423
|
-
thinr logout
|
|
437
|
+
thinr logout # active profile (or --profile <name>)
|
|
438
|
+
thinr logout --all # every profile
|
|
439
|
+
thinr logout --keep-token # leave the token on the server
|
|
424
440
|
```
|
|
425
441
|
|
|
426
|
-
Other profiles are left untouched.
|
|
442
|
+
Other profiles are left untouched. `thinr login` re-authenticates the
|
|
443
|
+
active profile; `--server`, `--username` and `--token` skip the prompts.
|
|
427
444
|
|
|
428
445
|
---
|
|
429
446
|
|
|
430
447
|
## Part 2 — MCP server
|
|
431
448
|
|
|
432
|
-
`thinr` ships an [MCP][mcp] server
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
devices, run shell commands, read/write files, call resources, manage
|
|
436
|
-
products, author and roll out playbooks, triage alarms and rotate
|
|
437
|
-
access tokens through the same API the CLI uses — with stable schemas
|
|
438
|
-
and a consistent error contract.
|
|
449
|
+
`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
|
|
451
|
+
CLI does, through the same API, with stable schemas and one error contract.
|
|
439
452
|
|
|
440
453
|
### Starting the server
|
|
441
454
|
|
|
@@ -443,117 +456,91 @@ and a consistent error contract.
|
|
|
443
456
|
thinr mcp
|
|
444
457
|
```
|
|
445
458
|
|
|
446
|
-
The server speaks [MCP over stdio][mcp-stdio]. Every tool call carries
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
restart.
|
|
459
|
+
The server speaks [MCP over stdio][mcp-stdio]. Every tool call carries its
|
|
460
|
+
own `profile` and `user` arguments, so a single running server handles any
|
|
461
|
+
device of any configured environment without a restart.
|
|
450
462
|
|
|
451
463
|
[mcp-stdio]: https://modelcontextprotocol.io/specification/server/transport#stdio
|
|
452
464
|
|
|
453
|
-
###
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
|
460
|
-
|
|
|
461
|
-
|
|
|
462
|
-
|
|
|
463
|
-
|
|
|
464
|
-
|
|
|
465
|
-
|
|
|
466
|
-
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
|
472
|
-
|
|
|
473
|
-
|
|
|
474
|
-
|
|
|
475
|
-
|
|
|
476
|
-
|
|
|
477
|
-
|
|
|
478
|
-
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
465
|
+
### Tools
|
|
466
|
+
|
|
467
|
+
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_publish`, `thinr_plugin_call`, `thinr_product_resource_call`, `thinr_device_data_send`, `thinr_device_overview` | Storages, web dashboards, 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 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.
|
|
499
|
+
|
|
500
|
+
[cli-core]: https://github.com/thinger-io/thinger-cli-core
|
|
482
501
|
|
|
483
502
|
### Authoring playbooks from natural language
|
|
484
503
|
|
|
485
|
-
Playbooks are
|
|
486
|
-
`
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
switch, and writes a persistent report to `playbooks/runs/` in the
|
|
518
|
-
product file storage.
|
|
519
|
-
|
|
520
|
-
Example prompt the user can give the assistant:
|
|
521
|
-
|
|
522
|
-
> "On product `edge-gateways`, create a playbook called
|
|
523
|
-
> `nginx-upgrade` that updates the nginx package, renders
|
|
524
|
-
> `/etc/nginx/nginx.conf` from a template, and restarts nginx only
|
|
525
|
-
> if the config check passes. Make `worker_connections` an
|
|
526
|
-
> overridable integer variable, default 1024. Then try it on
|
|
527
|
-
> `edge-gw-17` in check mode."
|
|
528
|
-
|
|
529
|
-
A well-behaved assistant translates this into:
|
|
530
|
-
|
|
531
|
-
- `thinr_playbook_schema` → read the authoring contract.
|
|
532
|
-
- Draft YAML → `thinr_playbook_validate` until clean.
|
|
533
|
-
- `thinr_product_playbook_write` with `product="edge-gateways"`,
|
|
534
|
-
`name="nginx-upgrade"`, and the validated content.
|
|
535
|
-
- `thinr_product_playbook_run` with `device="edge-gw-17"` and
|
|
536
|
-
`check: true` to preview what would change.
|
|
537
|
-
|
|
538
|
-
After that a second message like "looks good, roll it out to staging"
|
|
539
|
-
maps to `thinr_product_playbook_rollout` with the matching
|
|
540
|
-
`group` / `filter` and a conservative `batch_size`.
|
|
504
|
+
Playbooks are YAML documents with ordered steps (`exec`, `write`, `move`,
|
|
505
|
+
`delete`, `script_install`…), a `target` and `vars`, that the agent applies
|
|
506
|
+
on each device. With the MCP server connected, an assistant can turn a
|
|
507
|
+
request into a validated playbook stored on the product, without anyone
|
|
508
|
+
writing YAML by hand:
|
|
509
|
+
|
|
510
|
+
1. **Read the format.** `thinr_resource_describe` type `product_playbook`:
|
|
511
|
+
its `playbook_format` lists the document fields, the target, the two
|
|
512
|
+
forms of `vars` (plain and extended) and every step action.
|
|
513
|
+
2. **Draft and validate.** Write the YAML and call `thinr_resource_call`
|
|
514
|
+
type `product_playbook`, action `validate`, with it in `body.content`;
|
|
515
|
+
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
|
|
520
|
+
`body.device`, with `dry_run: true` or `check: true` for a read-only
|
|
521
|
+
preview, then for real.
|
|
522
|
+
5. **Roll out.** Action `rollout` runs it across the fleet in batches with a
|
|
523
|
+
failure-rate stop, and writes a report to `playbooks/runs/` in the
|
|
524
|
+
product storage.
|
|
525
|
+
|
|
526
|
+
Example prompt:
|
|
527
|
+
|
|
528
|
+
> "On product `edge-gateways`, create a playbook called `nginx-upgrade`
|
|
529
|
+
> that updates the nginx package, renders `/etc/nginx/nginx.conf` from a
|
|
530
|
+
> template, and restarts nginx only if the config check passes. Make
|
|
531
|
+
> `worker_connections` an overridable integer variable, default 1024. Then
|
|
532
|
+
> try it on `edge-gw-17` in check mode."
|
|
533
|
+
|
|
534
|
+
A second message like "looks good, roll it out to staging" maps to the
|
|
535
|
+
`rollout` action with `group` and a conservative `batch_size`.
|
|
541
536
|
|
|
542
537
|
### Per-call controls
|
|
543
538
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
`thinr_devices` (optionally with `query` to filter by regex) to
|
|
550
|
-
discover ids.
|
|
551
|
-
- **`user`** — optional admin impersonation; falls back to the
|
|
552
|
-
authenticated user of the active profile.
|
|
553
|
-
- **`profile`** — optional. Switches the active profile for that
|
|
554
|
-
single tool call so an agent can hop between prod and staging
|
|
555
|
-
without restarting the server. List the configured profiles with
|
|
556
|
-
`thinr_profiles`.
|
|
539
|
+
- **`profile`**: optional on every tool. Uses another configured server for
|
|
540
|
+
that call only; `thinr_profile_list` lists them.
|
|
541
|
+
- **`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`.
|
|
557
544
|
|
|
558
545
|
### Integrating with Claude Code
|
|
559
546
|
|
|
@@ -563,9 +550,8 @@ Register the server once with the Claude Code CLI:
|
|
|
563
550
|
claude mcp add thinr -s user -- thinr mcp
|
|
564
551
|
```
|
|
565
552
|
|
|
566
|
-
That's it
|
|
567
|
-
|
|
568
|
-
`profile`) per call.
|
|
553
|
+
That's it: a single MCP server entry handles every device of every
|
|
554
|
+
configured profile.
|
|
569
555
|
|
|
570
556
|
### Integrating with Claude Desktop, Cursor and other MCP hosts
|
|
571
557
|
|
|
@@ -625,13 +611,21 @@ Use `thinr profile use <name>` to change the persisted default, or
|
|
|
625
611
|
```
|
|
626
612
|
thinr-cli/
|
|
627
613
|
├── bin/ # CLI entry point (thinr)
|
|
628
|
-
├── commands/ #
|
|
629
|
-
├── lib/ #
|
|
630
|
-
│ #
|
|
614
|
+
├── commands/ # ThinRemote subcommands (agent, fleet, playbooks…)
|
|
615
|
+
├── lib/ # ThinRemote modules: identity, device-api, proxy,
|
|
616
|
+
│ # console, playbooks, product scripts, MCP tools…
|
|
631
617
|
├── package.json
|
|
632
618
|
└── README.md
|
|
633
619
|
```
|
|
634
620
|
|
|
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
|
|
624
|
+
[@thinger-io/cli-core](https://github.com/thinger-io/cli-core), shared
|
|
625
|
+
with the `thinger` CLI. `lib/identity.js` is what makes it `thinr`: names,
|
|
626
|
+
`~/.config/thinr-cli`, `THINR\__`, the OAuth client and the product
|
|
627
|
+
template.
|
|
628
|
+
|
|
635
629
|
Local development:
|
|
636
630
|
|
|
637
631
|
```bash
|
|
@@ -685,7 +679,7 @@ The script only touches resources it creates itself (identified by
|
|
|
685
679
|
the `playbook-e2e-` product-id prefix). If a run is interrupted
|
|
686
680
|
before the `finally` cleanup executes — e.g. the process is killed
|
|
687
681
|
— list leftovers with `thinr product list --json` and drop each one
|
|
688
|
-
through the MCP `
|
|
682
|
+
through the MCP `thinr_resource_delete` tool with type `product` (from an AI client) or
|
|
689
683
|
via a Node one-liner reusing `deleteProductWithStorage` from
|
|
690
684
|
`lib/product.js`.
|
|
691
685
|
|
package/bin/thinr.js
CHANGED
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
import { readFileSync } from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname, resolve } from 'path';
|
|
6
|
-
import { Command } from 'commander';
|
|
7
6
|
import figlet from 'figlet';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { defineIdentity } from '@thinger-io/cli-core/identity';
|
|
8
|
+
import { runCli } from '@thinger-io/cli-core/cli';
|
|
9
|
+
import { authenticate } from '@thinger-io/cli-core/auth';
|
|
10
|
+
import { configExists } from '@thinger-io/cli-core/config';
|
|
11
|
+
import { accent, info } from '@thinger-io/cli-core/format';
|
|
12
|
+
import { mcpInstructions, thinrIdentity } from '../lib/identity.js';
|
|
13
|
+
import { tools as mcpTools } from '../lib/mcp/registry.js';
|
|
14
|
+
import { resourceHooks } from '../lib/mcp/resource-hooks.js';
|
|
15
|
+
import { resourceActions, resourceTypes } from '../lib/mcp/resource-types.js';
|
|
16
|
+
import { guides } from '../lib/guides.js';
|
|
17
|
+
import { registerDeviceCommands } from '../commands/device/index.js';
|
|
18
|
+
import { registerProductCommands } from '../commands/product/index.js';
|
|
19
|
+
import { fleetCommand } from '../commands/fleet/index.js';
|
|
20
|
+
import { playbookCommand } from '../commands/playbook/index.js';
|
|
21
|
+
import { runDashboard } from '../commands/dashboard.js';
|
|
11
22
|
|
|
12
23
|
// Single source of truth for the version: package.json. Avoids the drift
|
|
13
24
|
// between `package.json#version` and a hardcoded string in this file.
|
|
@@ -15,110 +26,40 @@ const pkg = JSON.parse(
|
|
|
15
26
|
readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8'),
|
|
16
27
|
);
|
|
17
28
|
|
|
18
|
-
// Pick up --profile early so subsequent config reads use the right one.
|
|
19
|
-
// Commander parses options later, but the config is touched before that.
|
|
20
|
-
for (let i = 2; i < process.argv.length; i++) {
|
|
21
|
-
const a = process.argv[i];
|
|
22
|
-
if (a === '--profile' && process.argv[i + 1]) {
|
|
23
|
-
setActiveProfile(process.argv[i + 1]);
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
if (a.startsWith('--profile=')) {
|
|
27
|
-
setActiveProfile(a.slice('--profile='.length));
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Set JSON mode before any command runs so spinners/logs can opt out.
|
|
33
|
-
detectJsonModeFromArgv();
|
|
34
|
-
import { authenticate } from '../lib/auth.js';
|
|
35
|
-
import { deviceCommand } from '../commands/device/index.js';
|
|
36
|
-
import { fleetCommand } from '../commands/fleet/index.js';
|
|
37
|
-
import { logoutCommand } from '../commands/logout.js';
|
|
38
|
-
import { playbookCommand } from '../commands/playbook/index.js';
|
|
39
|
-
import { productCommand } from '../commands/product/index.js';
|
|
40
|
-
import { profileCommand } from '../commands/profile.js';
|
|
41
|
-
import { runDashboard } from '../commands/dashboard.js';
|
|
42
|
-
import { setBaseURL } from '../lib/api.js';
|
|
43
|
-
import { startMCPServer } from '../lib/mcp/server.js';
|
|
44
|
-
|
|
45
29
|
function displayBanner() {
|
|
46
30
|
console.log(accent(figlet.textSync('thinr-cli', { horizontalLayout: 'full' })));
|
|
47
31
|
console.log(info('Thin Remote CLI - Remote management for IoT devices'));
|
|
48
32
|
console.log();
|
|
49
33
|
}
|
|
50
34
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
program
|
|
54
|
-
.name('thinr')
|
|
55
|
-
.description('CLI for ThinRemote - Remote management for IoT devices')
|
|
56
|
-
.version(pkg.version)
|
|
57
|
-
.option('-u, --user <username>', 'API user override (admin impersonation)')
|
|
58
|
-
.option('--profile <name>', 'Configuration profile to use (defaults to the saved default)');
|
|
35
|
+
defineIdentity(thinrIdentity);
|
|
59
36
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
await runCli({
|
|
38
|
+
version: pkg.version,
|
|
39
|
+
mcpTools,
|
|
40
|
+
resourceHooks,
|
|
41
|
+
resourceTypes,
|
|
42
|
+
resourceActions,
|
|
43
|
+
docs: guides,
|
|
44
|
+
mcpInstructions,
|
|
45
|
+
register(program, groups) {
|
|
46
|
+
registerDeviceCommands(groups.device);
|
|
47
|
+
registerProductCommands(groups.product);
|
|
48
|
+
fleetCommand(program);
|
|
49
|
+
playbookCommand(program);
|
|
66
50
|
|
|
67
|
-
program
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
console.log(info('Signed in.'));
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error(`Error: ${error.message}`);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
program
|
|
87
|
-
.command('mcp')
|
|
88
|
-
.helpGroup('Integration:')
|
|
89
|
-
.description('Start the MCP server (stdio) for AI tool integration')
|
|
90
|
-
.action(async () => {
|
|
91
|
-
try {
|
|
92
|
-
await startMCPServer({ version: pkg.version });
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error(`Error: ${error.message}`);
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Handle help
|
|
100
|
-
const originalHelp = program.help;
|
|
101
|
-
program.help = function (cb) {
|
|
102
|
-
displayBanner();
|
|
103
|
-
return originalHelp.call(this, cb);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// initialize API base URL
|
|
107
|
-
if (configExists()) {
|
|
108
|
-
const config = readConfig();
|
|
109
|
-
if (config.server) {
|
|
110
|
-
// Initialize API with the server if configured
|
|
111
|
-
setBaseURL(`https://${config.server}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Handle no command (just "thinr"): launch the dashboard when running
|
|
116
|
-
// interactively. Non-TTY contexts (pipes, scripts, `thinr | cat`) fall
|
|
117
|
-
// back to `--help` so they get something useful instead of an Ink crash.
|
|
118
|
-
if (process.argv.length <= 2) {
|
|
119
|
-
if (!process.stdout.isTTY) {
|
|
120
|
-
program.help();
|
|
121
|
-
} else {
|
|
51
|
+
const originalHelp = program.help;
|
|
52
|
+
program.help = function (cb) {
|
|
53
|
+
displayBanner();
|
|
54
|
+
return originalHelp.call(this, cb);
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
// Just `thinr`: launch the dashboard when running interactively (signing
|
|
58
|
+
// in first if there is no profile yet). Non-TTY contexts (pipes,
|
|
59
|
+
// scripts, `thinr | cat`) fall back to `--help` so they get something
|
|
60
|
+
// useful instead of an Ink crash.
|
|
61
|
+
async onNoCommand(program) {
|
|
62
|
+
if (!process.stdout.isTTY) program.help();
|
|
122
63
|
if (!configExists()) {
|
|
123
64
|
displayBanner();
|
|
124
65
|
try {
|
|
@@ -129,8 +70,5 @@ if (process.argv.length <= 2) {
|
|
|
129
70
|
}
|
|
130
71
|
}
|
|
131
72
|
await runDashboard();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Parse arguments - don't show banner for regular commands
|
|
135
|
-
program.parse(process.argv);
|
|
136
|
-
}
|
|
73
|
+
},
|
|
74
|
+
});
|