@thinremote/thinr-cli 1.0.2 → 1.1.0-alpha.2
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 +90 -0
- package/README.md +362 -87
- package/bin/thinr.js +51 -11
- package/commands/device/_shared.js +48 -0
- package/commands/device/console.js +15 -0
- package/commands/device/exec.js +76 -0
- package/commands/device/index.js +31 -0
- package/commands/device/list.js +43 -0
- package/commands/device/property.js +50 -0
- package/commands/device/proxy.js +42 -0
- package/commands/device/resource.js +51 -0
- package/commands/device/status.js +26 -0
- package/commands/device/update.js +53 -0
- package/commands/logout.js +12 -5
- package/commands/product.js +146 -177
- package/commands/profile.js +105 -0
- package/lib/api.js +54 -31
- package/lib/auth.js +225 -309
- package/lib/config.js +176 -45
- package/lib/console.js +87 -108
- package/lib/device-api.js +257 -0
- package/lib/devices.js +69 -0
- package/lib/errors.js +53 -0
- package/lib/mcp/helpers.js +34 -0
- package/lib/mcp/registry.js +32 -0
- package/lib/mcp/server.js +67 -0
- package/lib/mcp/tools-devices.js +194 -0
- package/lib/mcp/tools-exec.js +213 -0
- package/lib/mcp/tools-filesystem.js +163 -0
- package/lib/mcp/tools-products.js +232 -0
- package/lib/mcp/tools-profiles.js +30 -0
- package/lib/mcp/tools-properties.js +79 -0
- package/lib/mcp/tools-resources.js +76 -0
- package/lib/mcp/tools-scripts.js +172 -0
- package/lib/monitoring.js +72 -0
- package/lib/output.js +114 -0
- package/lib/product.js +373 -0
- package/lib/property.js +41 -57
- package/lib/proxy.js +58 -90
- package/lib/resource.js +106 -72
- package/lib/status.js +7 -33
- package/lib/storage.js +128 -0
- package/package.json +68 -37
- package/.github/workflows/publish.yml +0 -79
- package/commands/device.js +0 -268
- package/lib/device.js +0 -76
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.0] - 2026-04-16
|
|
4
|
+
|
|
5
|
+
Major release: MCP server for AI clients, subcommand-first CLI, JSON
|
|
6
|
+
output, multi-profile configuration, product-level fan-outs, and a
|
|
7
|
+
consistent error taxonomy across both interfaces.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **MCP server** (`thinr mcp`) exposing ~26 typed tools over stdio for AI
|
|
12
|
+
clients (Claude Code, Claude Desktop, and any other MCP host). Covers
|
|
13
|
+
discovery, shell `exec`, filesystem (`read`/`write`/`ls`/`mkdir`/`delete`/
|
|
14
|
+
`move`), typed `resource` calls, properties, scripts (device- and
|
|
15
|
+
product-level), products, monitoring and agent updates, plus a profiles
|
|
16
|
+
lookup. MCP errors carry the same `{ code }` taxonomy as the CLI JSON
|
|
17
|
+
mode (exposed on `_meta`) so clients can pattern-match without parsing
|
|
18
|
+
human strings.
|
|
19
|
+
- **Subcommand-first CLI UX**: `thinr device <action> <deviceId>` /
|
|
20
|
+
`thinr product <action> <productId>` replaces the previous top-level
|
|
21
|
+
flag shape. Matches `kubectl` / `git` idioms.
|
|
22
|
+
- `thinr device list [pattern]` with case-insensitive regex filtering on
|
|
23
|
+
the server side (replaces the old `thinr devices`).
|
|
24
|
+
- `thinr device exec <deviceId> <command...>` with streaming stdout/stderr,
|
|
25
|
+
SIGINT cancellation, `--json` envelope, and `--legacy` one-shot fallback
|
|
26
|
+
for older agents.
|
|
27
|
+
- `thinr device update check|apply` for managing agent upgrades.
|
|
28
|
+
- `thinr device resource` with per-resource input/output schema
|
|
29
|
+
introspection, and `thinr product resource` / `thinr product property`
|
|
30
|
+
for fleet fan-outs.
|
|
31
|
+
- **Multi-profile configuration store** (`~/.config/thinr-cli/config.json`):
|
|
32
|
+
one profile per server, selected via `--profile`, the `THINR_PROFILE`
|
|
33
|
+
env var, or a per-call MCP parameter. Legacy single-file configs are
|
|
34
|
+
detected and migrated in-memory on read.
|
|
35
|
+
- `thinr profile list|current|use|delete` for managing configured
|
|
36
|
+
environments.
|
|
37
|
+
- `--json` / `-j` output envelope on every data-producing command with a
|
|
38
|
+
stable `{ code }` taxonomy: `not_configured`, `not_found`,
|
|
39
|
+
`unauthorized`, `server_error`, `network_error`, `input_error`,
|
|
40
|
+
`timeout`, `cancelled`, `error`.
|
|
41
|
+
- `-u, --user` admin impersonation flag plumbed through every
|
|
42
|
+
device-scoped operation (CLI + MCP).
|
|
43
|
+
- `THINR_INSECURE=1` env var to accept self-signed TLS certificates
|
|
44
|
+
against non-localhost hosts (localhost / 127.0.0.1 are always
|
|
45
|
+
accepted).
|
|
46
|
+
- Device property setter via PUT (`setDeviceProperty` in the CLI,
|
|
47
|
+
`thinr_property_set` in MCP).
|
|
48
|
+
- `thinr_device_info` MCP tool surfaces server-side product, asset group
|
|
49
|
+
and description alongside the agent's own `system_info`.
|
|
50
|
+
- Refresh-token flow on 401s: the CLI transparently swaps expired access
|
|
51
|
+
tokens for a fresh one when a `refresh_token` is on record, and fails
|
|
52
|
+
cleanly (with an actionable `unauthorized` error) when it isn't.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- **MCP server internals**: tool schema and handler now paired in a
|
|
57
|
+
registry, so they cannot drift apart. Tools grouped by area under
|
|
58
|
+
`lib/mcp/tools-*.js`, with `server.js` reduced to a thin bootstrap.
|
|
59
|
+
- **CLI `device` command** split into one file per subcommand under
|
|
60
|
+
`commands/device/` (previously a single 17 KB module).
|
|
61
|
+
- `lib/device.js` renamed to `lib/devices.js` (plural: operations on the
|
|
62
|
+
fleet), paired with the low-level `lib/device-api.js`.
|
|
63
|
+
- Interactive handlers (auth / console / proxy) no longer call
|
|
64
|
+
`process.exit` from `lib/*`; the CLI entry owns the exit code.
|
|
65
|
+
- Centralised API error classification in `lib/errors.js` — one
|
|
66
|
+
`{ message, code, status }` contract consumed by both CLI JSON mode
|
|
67
|
+
and MCP `_meta`.
|
|
68
|
+
- Re-thrown auth errors chain the original `cause` for better
|
|
69
|
+
debugging.
|
|
70
|
+
- Bad `-i key=value` values surface as a clean Commander
|
|
71
|
+
`InvalidArgumentError` instead of a stack trace.
|
|
72
|
+
- Node.js engine requirement bumped to **`>=18`**; `.nvmrc` pinned to 20.
|
|
73
|
+
|
|
74
|
+
### Removed
|
|
75
|
+
|
|
76
|
+
- `thinr devices` (use `thinr device list` instead).
|
|
77
|
+
- `thinr device mcp <deviceId>` (use `thinr mcp` with per-call `device`
|
|
78
|
+
parameter).
|
|
79
|
+
- `thinr device env` launcher.
|
|
80
|
+
|
|
81
|
+
### Tooling
|
|
82
|
+
|
|
83
|
+
- ESLint 10 (flat config) + Prettier 3 with `eslint-config-prettier`
|
|
84
|
+
bridge. Scripts: `npm run lint` / `lint:fix` / `format` /
|
|
85
|
+
`format:check`.
|
|
86
|
+
- TypeScript `checkJs` via per-file `// @ts-check` opt-in; 21 files
|
|
87
|
+
under `lib/mcp/` and `commands/device/` pass type-check today. Script:
|
|
88
|
+
`npm run typecheck`.
|
|
89
|
+
- Unit tests with `node --test` (zero deps): MCP registry invariants +
|
|
90
|
+
error helpers. Script: `npm test`.
|
|
91
|
+
|
|
3
92
|
## [1.0.2] - 2025-07-21
|
|
4
93
|
|
|
5
94
|
### Fixed
|
|
@@ -31,6 +120,7 @@
|
|
|
31
120
|
- Property listing and retrieval
|
|
32
121
|
- Device resource listing and execution
|
|
33
122
|
|
|
123
|
+
[1.1.0]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.2...1.1.0
|
|
34
124
|
[1.0.2]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.1...1.0.2
|
|
35
125
|
[1.0.1]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.0...1.0.1
|
|
36
126
|
[1.0.0]: https://github.com/Thin-Remote/thinr-cli/tag/1.0.0
|
package/README.md
CHANGED
|
@@ -1,18 +1,45 @@
|
|
|
1
|
-
# Thinr
|
|
1
|
+
# Thinr — ThinRemote CLI
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src='https://s3.us-east-1.amazonaws.com/thinremote.io.files/share-image.svg' alt="Thinger
|
|
4
|
+
<img src='https://s3.us-east-1.amazonaws.com/thinremote.io.files/share-image.svg' alt="Thinger">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
A
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
|
|
7
|
+
A command-line tool for managing remote IoT devices on the ThinRemote
|
|
8
|
+
platform, and an MCP server that lets AI assistants drive those
|
|
9
|
+
devices programmatically.
|
|
10
|
+
|
|
11
|
+
Two operating modes:
|
|
12
|
+
|
|
13
|
+
- **CLI** — interactive or scripted device management from a shell.
|
|
14
|
+
- **MCP server** — a stdio [Model Context Protocol][mcp] server that
|
|
15
|
+
exposes the same operations as typed tools for AI clients like
|
|
16
|
+
Claude Code.
|
|
17
|
+
|
|
18
|
+
Both modes share the same auth, profiles, and device API layer, so
|
|
19
|
+
you configure the tool once and use it in whichever mode fits the
|
|
20
|
+
task at hand.
|
|
21
|
+
|
|
22
|
+
[mcp]: https://modelcontextprotocol.io/
|
|
23
|
+
|
|
24
|
+
## Table of contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Initial setup](#initial-setup)
|
|
28
|
+
- [Global options and environment variables](#global-options-and-environment-variables)
|
|
29
|
+
- [Part 1 — CLI](#part-1--cli)
|
|
30
|
+
- [JSON output](#json-output)
|
|
31
|
+
- [`thinr device <action> [<deviceId>]`](#thinr-device-action-deviceid)
|
|
32
|
+
- [`thinr product <action> <productId>`](#thinr-product-action-productid)
|
|
33
|
+
- [`thinr profile`](#thinr-profile)
|
|
34
|
+
- [`thinr logout`](#thinr-logout)
|
|
35
|
+
- [Part 2 — MCP server](#part-2--mcp-server)
|
|
36
|
+
- [Starting the server](#starting-the-server)
|
|
37
|
+
- [Tool catalog](#tool-catalog)
|
|
38
|
+
- [Per-call controls](#per-call-controls)
|
|
39
|
+
- [Integrating with Claude Code](#integrating-with-claude-code)
|
|
40
|
+
- [Profiles and multi-account use](#profiles-and-multi-account-use)
|
|
41
|
+
- [Development](#development)
|
|
42
|
+
- [License](#license)
|
|
16
43
|
|
|
17
44
|
## Installation
|
|
18
45
|
|
|
@@ -22,7 +49,7 @@ A modern command-line interface for managing remote devices through the ThinRemo
|
|
|
22
49
|
npm install -g @thinremote/thinr-cli
|
|
23
50
|
```
|
|
24
51
|
|
|
25
|
-
### From
|
|
52
|
+
### From source
|
|
26
53
|
|
|
27
54
|
```bash
|
|
28
55
|
git clone https://github.com/Thin-Remote/thinr-cli.git
|
|
@@ -31,174 +58,422 @@ npm install
|
|
|
31
58
|
npm link
|
|
32
59
|
```
|
|
33
60
|
|
|
34
|
-
|
|
61
|
+
Requires Node.js ≥ 18.
|
|
35
62
|
|
|
36
|
-
|
|
63
|
+
## Initial setup
|
|
37
64
|
|
|
38
|
-
Run the CLI
|
|
65
|
+
Run the CLI once with no arguments to authenticate:
|
|
39
66
|
|
|
40
67
|
```bash
|
|
41
68
|
thinr
|
|
42
69
|
```
|
|
43
70
|
|
|
44
|
-
You'll be prompted
|
|
71
|
+
You'll be prompted for the ThinRemote server, and can authenticate
|
|
72
|
+
with either username/password (OAuth 2.0 device flow) or a pre-issued
|
|
73
|
+
token. On success a profile is created at
|
|
74
|
+
`~/.config/thinr-cli/config.json`, named after the server hostname,
|
|
75
|
+
and marked as the default.
|
|
76
|
+
|
|
77
|
+
Every later command reads that profile automatically. Logging into a
|
|
78
|
+
second server creates a second profile side-by-side; see
|
|
79
|
+
[Profiles and multi-account use](#profiles-and-multi-account-use).
|
|
80
|
+
|
|
81
|
+
## Global options and environment variables
|
|
82
|
+
|
|
83
|
+
These apply to every subcommand:
|
|
84
|
+
|
|
85
|
+
| Flag | Env var | Meaning |
|
|
86
|
+
| ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
87
|
+
| `--profile <name>` | `THINR_PROFILE` | Use a non-default profile for this invocation. |
|
|
88
|
+
| `-u, --user <name>` | — | Admin impersonation: act on behalf of another user (requires admin privileges on the server). |
|
|
89
|
+
| — | `THINR_INSECURE=1` | Accept self-signed TLS certificates globally. Localhost / 127.0.0.1 are always accepted; this flag extends the relaxation to any host, useful for LAN dev servers. |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Part 1 — CLI
|
|
94
|
+
|
|
95
|
+
All device- and product-scoped actions follow a **subcommand-first**
|
|
96
|
+
shape: the action comes before the target id, matching patterns like
|
|
97
|
+
`kubectl describe pod <name>` or `git remote add <name> <url>`.
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
thinr device <action> <deviceId> [args…] [options]
|
|
101
|
+
thinr product <action> <productId> [args…] [options]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### JSON output
|
|
105
|
+
|
|
106
|
+
Every data-producing command accepts `-j, --json` and writes a single
|
|
107
|
+
envelope to stdout so scripts can pipe it to `jq`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{ "ok": true, "data": <payload> }
|
|
111
|
+
{ "ok": false, "error": { "message": "...", "code": "<code>" } }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Process exit codes always reflect success (`0`) or failure (non-zero),
|
|
115
|
+
independently of the envelope.
|
|
116
|
+
|
|
117
|
+
Emitted error `code` values:
|
|
118
|
+
|
|
119
|
+
| code | meaning |
|
|
120
|
+
| ---------------- | ------------------------------------------------- |
|
|
121
|
+
| `not_configured` | CLI has no saved profile — run `thinr` to set up. |
|
|
122
|
+
| `not_found` | Device, property, resource or profile not found. |
|
|
123
|
+
| `unauthorized` | Token expired or insufficient permissions. |
|
|
124
|
+
| `server_error` | Non-success HTTP response from the server. |
|
|
125
|
+
| `network_error` | No response from the server. |
|
|
126
|
+
| `input_error` | Bad or missing CLI argument. |
|
|
127
|
+
| `timeout` | Command timed out on the device (`exec`). |
|
|
128
|
+
| `cancelled` | User interrupted with Ctrl+C (`exec`). |
|
|
129
|
+
| `error` | Fallback for anything uncategorised. |
|
|
130
|
+
|
|
131
|
+
Spinners and progress messages are suppressed in JSON mode so the
|
|
132
|
+
output is valid JSON without wrappers. Interactive commands
|
|
133
|
+
(`console`, `tcp`/`tls`/`http`) accept `--json` silently but
|
|
134
|
+
don't change their behaviour — they don't produce a discrete result.
|
|
135
|
+
`exec` buffers stdout/stderr when `--json` is set and emits a single
|
|
136
|
+
envelope on exit.
|
|
137
|
+
|
|
138
|
+
### `thinr device <action> [<deviceId>]`
|
|
139
|
+
|
|
140
|
+
Most actions take a `<deviceId>` as their first positional. The `list`
|
|
141
|
+
action is the exception — it operates on the fleet, with an optional
|
|
142
|
+
`<pattern>` to filter results.
|
|
45
143
|
|
|
46
|
-
|
|
144
|
+
#### `list`
|
|
47
145
|
|
|
48
|
-
|
|
146
|
+
List every device visible to the active profile, or filter with a
|
|
147
|
+
case-insensitive regex (matched against device id and name on the
|
|
148
|
+
server side):
|
|
49
149
|
|
|
50
|
-
|
|
150
|
+
```bash
|
|
151
|
+
thinr device list # every device
|
|
152
|
+
thinr device list supermicro # only ids/names containing "supermicro"
|
|
153
|
+
thinr device list "^revpi|jetson" # regex alternation works too
|
|
154
|
+
thinr device list --json # JSON envelope with the raw API records
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Combine with the global `-u, --user` flag for admin impersonation.
|
|
51
158
|
|
|
52
|
-
|
|
159
|
+
#### `console`
|
|
160
|
+
|
|
161
|
+
Open an interactive terminal on the device:
|
|
53
162
|
|
|
54
163
|
```bash
|
|
55
|
-
thinr device <deviceId>
|
|
164
|
+
thinr device console <deviceId>
|
|
56
165
|
```
|
|
57
166
|
|
|
58
|
-
|
|
167
|
+
#### Proxies — `tcp` / `tls` / `http`
|
|
59
168
|
|
|
60
|
-
Create a TCP or HTTP proxy to a remote
|
|
169
|
+
Create a TCP, TLS, or HTTP proxy to a remote endpoint reachable from
|
|
170
|
+
the device:
|
|
61
171
|
|
|
62
172
|
```bash
|
|
63
|
-
thinr device <deviceId>
|
|
64
|
-
thinr device <deviceId>
|
|
65
|
-
thinr device
|
|
173
|
+
thinr device tcp <deviceId> [target] [options]
|
|
174
|
+
thinr device tls <deviceId> [target] [options]
|
|
175
|
+
thinr device http <deviceId> [target] [options]
|
|
66
176
|
```
|
|
67
177
|
|
|
68
|
-
|
|
178
|
+
`target` is the remote address and port on the device side. Defaults
|
|
179
|
+
when omitted: `tcp` → 22, `tls` → 443, `http` → 80, against
|
|
180
|
+
localhost.
|
|
69
181
|
|
|
70
182
|
Options:
|
|
71
|
-
|
|
72
|
-
-
|
|
183
|
+
|
|
184
|
+
- `-p, --port <port>`: Local port to bind (default: random in 50000–51000)
|
|
185
|
+
- `--no-open`: Do not open the browser automatically (http only)
|
|
73
186
|
|
|
74
187
|
Examples:
|
|
188
|
+
|
|
75
189
|
```bash
|
|
76
|
-
#
|
|
77
|
-
thinr device RevPi20679
|
|
190
|
+
# TCP proxy to SSH
|
|
191
|
+
thinr device tcp RevPi20679 22
|
|
78
192
|
|
|
79
|
-
#
|
|
80
|
-
thinr device RevPi20679
|
|
193
|
+
# HTTP proxy to a specific port
|
|
194
|
+
thinr device http RevPi20679 8080
|
|
81
195
|
|
|
82
|
-
#
|
|
83
|
-
thinr device RevPi20679 http
|
|
196
|
+
# HTTP proxy to a specific address behind the device
|
|
197
|
+
thinr device http RevPi20679 http://192.168.1.45:8080
|
|
84
198
|
```
|
|
85
199
|
|
|
86
|
-
|
|
200
|
+
#### `status`
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
thinr device status <deviceId> [-j, --json]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Returns the device's connection stats (uptime, tx/rx, last-seen).
|
|
207
|
+
|
|
208
|
+
#### `property`
|
|
87
209
|
|
|
88
|
-
|
|
210
|
+
List every property of a device, or read one by id:
|
|
89
211
|
|
|
90
212
|
```bash
|
|
91
|
-
thinr device <deviceId>
|
|
213
|
+
thinr device property <deviceId> # list
|
|
214
|
+
thinr device property <deviceId> <propertyId> [options] # read
|
|
92
215
|
```
|
|
93
216
|
|
|
94
217
|
Options:
|
|
95
|
-
- `-j, --json`: Output the status in JSON format
|
|
96
218
|
|
|
97
|
-
|
|
219
|
+
- `-j, --json`: Output as JSON
|
|
220
|
+
- `-f, --field <path>`: Extract a sub-field via dot path (`-f data.value`)
|
|
98
221
|
|
|
99
|
-
|
|
222
|
+
#### `resource`
|
|
223
|
+
|
|
224
|
+
List the resources exposed by the device (with their `in`/`out`
|
|
225
|
+
schemas when advertised), or call one:
|
|
100
226
|
|
|
101
227
|
```bash
|
|
102
|
-
thinr device <deviceId>
|
|
228
|
+
thinr device resource <deviceId> # list
|
|
229
|
+
thinr device resource <deviceId> <resourceId> [options] # call
|
|
103
230
|
```
|
|
104
231
|
|
|
105
|
-
|
|
232
|
+
Options:
|
|
233
|
+
|
|
234
|
+
- `-i, --input <key=value>`: Resource input (repeatable)
|
|
235
|
+
- `-j, --json`: Output as JSON
|
|
236
|
+
- `-f, --field <path>`: Extract a sub-field from the result (dot path)
|
|
237
|
+
|
|
238
|
+
#### `exec`
|
|
239
|
+
|
|
240
|
+
Run a shell command on the device and stream stdout/stderr back:
|
|
106
241
|
|
|
107
242
|
```bash
|
|
108
|
-
thinr device <deviceId>
|
|
243
|
+
thinr device exec <deviceId> "<command>" [options]
|
|
109
244
|
```
|
|
110
245
|
|
|
111
246
|
Options:
|
|
112
|
-
- `-j, --json`: Output the status in JSON format
|
|
113
|
-
- `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
|
|
114
247
|
|
|
115
|
-
|
|
248
|
+
- `-j, --json`: Buffer output and emit a single envelope
|
|
249
|
+
`{stdout, stderr, exitCode}` on exit
|
|
250
|
+
- `--legacy`: Use the non-streaming one-shot API (older agents)
|
|
251
|
+
|
|
252
|
+
The process exits with the remote command's exit code.
|
|
116
253
|
|
|
117
|
-
|
|
254
|
+
#### `update`
|
|
255
|
+
|
|
256
|
+
Check for or apply an agent update on the device:
|
|
118
257
|
|
|
119
258
|
```bash
|
|
120
|
-
thinr device <deviceId>
|
|
259
|
+
thinr device update check <deviceId> [options]
|
|
260
|
+
thinr device update apply <deviceId> [options]
|
|
121
261
|
```
|
|
122
262
|
|
|
123
263
|
Options:
|
|
124
|
-
- `-j, --json`: Output the status in JSON format
|
|
125
264
|
|
|
126
|
-
|
|
265
|
+
- `--channel <name>`: Update channel (default: `latest`)
|
|
266
|
+
- `-j, --json`: Output as JSON
|
|
267
|
+
|
|
268
|
+
### `thinr product <action> <productId>`
|
|
269
|
+
|
|
270
|
+
Fan out an operation across every device that belongs to a product.
|
|
271
|
+
|
|
272
|
+
#### `property`
|
|
127
273
|
|
|
128
274
|
```bash
|
|
129
|
-
thinr
|
|
275
|
+
thinr product property <productId> <propertyId> [options]
|
|
130
276
|
```
|
|
131
277
|
|
|
132
278
|
Options:
|
|
133
|
-
- `-i, --input`: Input for the resource (e.g., -i param1=value1 -i param2=value2)
|
|
134
|
-
- `-j, --json`: Output the status in JSON format
|
|
135
|
-
- `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
|
|
136
279
|
|
|
137
|
-
|
|
280
|
+
- `-j, --json`: Output as JSON. Emits one envelope with a `results[]`
|
|
281
|
+
array, one entry per device, each with its own `ok` flag.
|
|
282
|
+
- `-f, --field <path>`: Extract a sub-field from each property (dot path)
|
|
283
|
+
- `-a, --all`: Include offline devices (default: only active)
|
|
284
|
+
- `-g, --group <group>`: Filter devices by asset group
|
|
138
285
|
|
|
139
|
-
|
|
286
|
+
#### `resource`
|
|
140
287
|
|
|
141
288
|
```bash
|
|
142
|
-
thinr product <productId>
|
|
143
|
-
thinr product <productId> resource [options]
|
|
289
|
+
thinr product resource <productId> <resource> [options]
|
|
144
290
|
```
|
|
145
291
|
|
|
146
292
|
Options:
|
|
147
|
-
- `-j, --json`: Output the status in JSON format
|
|
148
|
-
- `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
|
|
149
293
|
|
|
150
|
-
|
|
151
|
-
- `-
|
|
294
|
+
- `-j, --json`: Output as JSON (`results[]` array, same shape as above)
|
|
295
|
+
- `-f, --field <path>`: Extract a sub-field from each result (dot path)
|
|
296
|
+
- `-g, --group <group>`: Filter devices by asset group
|
|
297
|
+
- `-i, --input <key=value>`: Resource input (repeatable)
|
|
152
298
|
|
|
299
|
+
Offline devices are skipped for `resource` — the server would reject
|
|
300
|
+
the call anyway.
|
|
153
301
|
|
|
154
|
-
###
|
|
302
|
+
### `thinr profile`
|
|
155
303
|
|
|
156
|
-
|
|
304
|
+
Manage the profile store (see [Profiles and multi-account
|
|
305
|
+
use](#profiles-and-multi-account-use)):
|
|
157
306
|
|
|
158
307
|
```bash
|
|
159
|
-
thinr --
|
|
160
|
-
thinr
|
|
161
|
-
thinr
|
|
308
|
+
thinr profile list [--json]
|
|
309
|
+
thinr profile current [--json]
|
|
310
|
+
thinr profile use <name> [--json]
|
|
311
|
+
thinr profile delete <name> [--json]
|
|
162
312
|
```
|
|
163
313
|
|
|
164
|
-
|
|
314
|
+
### `thinr logout`
|
|
165
315
|
|
|
166
|
-
|
|
316
|
+
Remove the active profile's credentials and config:
|
|
167
317
|
|
|
318
|
+
```bash
|
|
319
|
+
thinr logout
|
|
168
320
|
```
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
321
|
+
|
|
322
|
+
Other profiles are left untouched.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Part 2 — MCP server
|
|
327
|
+
|
|
328
|
+
`thinr` ships an [MCP][mcp] server that exposes the CLI's device
|
|
329
|
+
operations as a set of typed tools. AI clients (Claude Code, Claude
|
|
330
|
+
Desktop, and any other MCP-compatible host) can then list devices,
|
|
331
|
+
run shell commands, read/write files, call resources, and manage
|
|
332
|
+
products through the same API the CLI uses — with stable schemas and
|
|
333
|
+
a consistent error contract.
|
|
334
|
+
|
|
335
|
+
### Starting the server
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
thinr mcp [-d, --device <deviceId>]
|
|
175
339
|
```
|
|
176
340
|
|
|
177
|
-
|
|
341
|
+
The server speaks [MCP over stdio][mcp-stdio]. `-d/--device` sets a
|
|
342
|
+
default device that tools can omit in each call; `--user` can be
|
|
343
|
+
passed through (via the global flag) to impersonate another account.
|
|
344
|
+
|
|
345
|
+
[mcp-stdio]: https://modelcontextprotocol.io/specification/server/transport#stdio
|
|
346
|
+
|
|
347
|
+
### Tool catalog
|
|
348
|
+
|
|
349
|
+
Roughly 28 tools, grouped by capability. Every tool accepts optional
|
|
350
|
+
`device`, `user`, and `profile` arguments, so a single session can
|
|
351
|
+
target any device/account/environment without restart.
|
|
352
|
+
|
|
353
|
+
| Area | Tools |
|
|
354
|
+
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
355
|
+
| Discovery | `thinr_devices` (with optional `query` for regex filtering), `thinr_device_info`, `thinr_profiles` |
|
|
356
|
+
| Shell | `thinr_exec` (buffered), with streaming stdout/stderr |
|
|
357
|
+
| Filesystem | `thinr_read`, `thinr_write`, `thinr_ls`, `thinr_mkdir`, `thinr_delete`, `thinr_move` |
|
|
358
|
+
| Resources | `thinr_resource_list` (with `in`/`out` schemas), `thinr_resource_call` |
|
|
359
|
+
| Properties | `thinr_property_get`, `thinr_property_set` |
|
|
360
|
+
| Scripts (device) | `thinr_script_list`, `thinr_script_write`, `thinr_script_delete` |
|
|
361
|
+
| Monitoring and update | `thinr_monitoring`, `thinr_update` |
|
|
362
|
+
| Products | `thinr_products`, `thinr_product_delete`, `thinr_device_set_product` |
|
|
363
|
+
| Product scripts | `thinr_product_script_list`, `thinr_product_script_read`, `thinr_product_script_write`, `thinr_product_script_delete` |
|
|
364
|
+
|
|
365
|
+
Full input/output schemas are published via standard MCP
|
|
366
|
+
`list_tools`; the client will show them when you connect.
|
|
367
|
+
|
|
368
|
+
### Per-call controls
|
|
369
|
+
|
|
370
|
+
The server starts with no per-session pin: every tool call must say
|
|
371
|
+
which device it's for, and may optionally override the account or
|
|
372
|
+
profile for that single call.
|
|
373
|
+
|
|
374
|
+
- **`device`** — required for any device-scoped tool. Use
|
|
375
|
+
`thinr_devices` (optionally with `query` to filter by regex) to
|
|
376
|
+
discover ids.
|
|
377
|
+
- **`user`** — optional admin impersonation; falls back to the
|
|
378
|
+
authenticated user of the active profile.
|
|
379
|
+
- **`profile`** — optional. Switches the active profile for that
|
|
380
|
+
single tool call so an agent can hop between prod and staging
|
|
381
|
+
without restarting the server. List the configured profiles with
|
|
382
|
+
`thinr_profiles`.
|
|
383
|
+
|
|
384
|
+
### Integrating with Claude Code
|
|
385
|
+
|
|
386
|
+
Register the server once with the Claude Code CLI:
|
|
178
387
|
|
|
179
388
|
```bash
|
|
180
|
-
|
|
181
|
-
npm link
|
|
389
|
+
claude mcp add thinr -s user -- thinr mcp
|
|
182
390
|
```
|
|
183
391
|
|
|
184
|
-
|
|
392
|
+
That's it — a single MCP server entry handles every device of the
|
|
393
|
+
active profile. Tool calls pass `device` (and optionally `user` /
|
|
394
|
+
`profile`) per call.
|
|
395
|
+
|
|
396
|
+
Other MCP hosts follow the same pattern — they just need the command
|
|
397
|
+
`thinr mcp` (optionally with `-d <deviceId>`) over stdio.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Profiles and multi-account use
|
|
402
|
+
|
|
403
|
+
`~/.config/thinr-cli/config.json` stores any number of profiles:
|
|
404
|
+
|
|
405
|
+
```json
|
|
406
|
+
{
|
|
407
|
+
"default": "perf.aws.thinger.io",
|
|
408
|
+
"profiles": {
|
|
409
|
+
"perf.aws.thinger.io": { "server": "...", "username": "...", "token": "..." },
|
|
410
|
+
"staging.local": { "server": "...", "username": "...", "token": "..." }
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
The active profile is picked, in order:
|
|
416
|
+
|
|
417
|
+
1. `--profile <name>` flag on the command line.
|
|
418
|
+
2. `THINR_PROFILE` environment variable.
|
|
419
|
+
3. The `default` field in the config file.
|
|
420
|
+
4. The sole profile, if there's only one.
|
|
421
|
+
|
|
422
|
+
Logging in to a new server creates a new profile whose name is the
|
|
423
|
+
server hostname (so it never clobbers an existing one). Legacy
|
|
424
|
+
single-profile files from older versions of the CLI are detected on
|
|
425
|
+
read and migrated in-memory without a manual step.
|
|
426
|
+
|
|
427
|
+
Use `thinr profile use <name>` to change the persisted default, or
|
|
428
|
+
`--profile <name>` / `THINR_PROFILE=<name>` for one-off runs.
|
|
429
|
+
|
|
430
|
+
## Development
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
thinr-cli/
|
|
434
|
+
├── bin/ # CLI entry point (thinr)
|
|
435
|
+
├── commands/ # Commander subcommand modules
|
|
436
|
+
├── lib/ # Core: api, auth, config, device-api, errors,
|
|
437
|
+
│ # output, mcp-server, proxy, console, …
|
|
438
|
+
├── package.json
|
|
439
|
+
└── README.md
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Local development:
|
|
185
443
|
|
|
186
444
|
```bash
|
|
187
|
-
npm
|
|
445
|
+
npm install
|
|
446
|
+
npm link # makes `thinr` available on $PATH
|
|
447
|
+
node bin/thinr.js # or run directly without linking
|
|
188
448
|
```
|
|
189
449
|
|
|
450
|
+
There are no automated tests yet — contributions welcome.
|
|
451
|
+
|
|
190
452
|
## License
|
|
191
453
|
|
|
192
454
|
<a href="http://opensource.org/">
|
|
193
455
|
<img style="float: right;" width="100px" height="137px" src="https://opensource.org/wp-content/uploads/2009/06/OSI_Standard_Logo_0.svg">
|
|
194
456
|
</a>
|
|
195
457
|
|
|
196
|
-
|
|
458
|
+
Released under the [MIT License](http://opensource.org/licenses/MIT).
|
|
197
459
|
|
|
198
460
|
Copyright © [Thinger.io](http://thinger.io)
|
|
199
461
|
|
|
200
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
462
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
463
|
+
a copy of this software and associated documentation files (the
|
|
464
|
+
"Software"), to deal in the Software without restriction, including
|
|
465
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
466
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
467
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
468
|
+
the following conditions:
|
|
469
|
+
|
|
470
|
+
The above copyright notice and this permission notice shall be
|
|
471
|
+
included in all copies or substantial portions of the Software.
|
|
472
|
+
|
|
473
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
474
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
475
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
476
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
477
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
478
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
479
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|