@tikoci/centrs 0.1.9-next.24 → 0.1.9
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 +121 -6
- package/GLOSSARY.txt +9 -1
- package/README.md +43 -29
- package/commands/api/AGENTS.md +3 -2
- package/commands/api/README.md +8 -1
- package/commands/btest/README.md +9 -0
- package/commands/btest/examples.md +7 -0
- package/commands/devices/README.md +160 -30
- package/commands/devices/examples.md +168 -2
- package/commands/execute/README.md +13 -2
- package/commands/explain/README.md +223 -3
- package/commands/explain/examples.md +8 -1
- package/commands/retrieve/README.md +24 -3
- package/commands/retrieve/examples.md +84 -0
- package/commands/settings/README.md +6 -0
- package/commands/settings/examples.md +17 -0
- package/commands/transfer/README.md +25 -0
- package/commands/transfer/examples.md +46 -0
- package/docs/CLI.md +53 -11
- package/docs/MATRIX.md +5 -3
- package/docs/errors/cdb/encrypt-failed.md +10 -0
- package/docs/errors/cdb/password-required.md +5 -4
- package/docs/errors/cdb/plaintext-backups.md +11 -0
- package/docs/errors/input/empty-stdin.md +27 -0
- package/docs/errors/input/invalid-routeros-path.md +25 -3
- package/docs/errors/input/invalid-stdin.md +28 -0
- package/docs/errors/usage/stdin-is-tty.md +21 -0
- package/package.json +11 -7
- package/src/api-fanout.ts +2 -0
- package/src/api.ts +12 -0
- package/src/btest.ts +7 -0
- package/src/cli/api.ts +19 -2
- package/src/cli/btest.ts +9 -0
- package/src/cli/common.ts +1 -0
- package/src/cli/devices.ts +248 -15
- package/src/cli/execute.ts +12 -3
- package/src/cli/retrieve.ts +9 -0
- package/src/cli/settings.ts +28 -6
- package/src/cli/terminal.ts +9 -0
- package/src/cli/transfer.ts +10 -1
- package/src/core/error-catalog.ts +28 -2
- package/src/core/menu-path.ts +89 -0
- package/src/devices.ts +378 -33
- package/src/execute-fanout.ts +2 -0
- package/src/execute.ts +7 -0
- package/src/explain/blocks.ts +2 -2
- package/src/explain/catalog.ts +22 -12
- package/src/explain/pasted-prompt.ts +198 -0
- package/src/explain/pathresolve.ts +2 -2
- package/src/explain/segment.ts +2 -2
- package/src/explain/symbols.ts +2 -2
- package/src/explain/verbsplit.ts +2 -2
- package/src/explain/write.ts +2 -2
- package/src/explain.ts +2 -0
- package/src/index.ts +5 -0
- package/src/mcp/safety.ts +7 -6
- package/src/mcp/tools.ts +30 -68
- package/src/protocols/adapter.ts +2 -0
- package/src/protocols/btest-session.ts +10 -30
- package/src/protocols/ec-srp5.ts +19 -10
- package/src/protocols/mac-telnet-console.ts +2 -2
- package/src/resolver/cdb.ts +66 -9
- package/src/resolver/index.ts +1 -0
- package/src/resolver/quickchr-provider.ts +5 -0
- package/src/resolver/selection.ts +15 -4
- package/src/retrieve-fanout.ts +2 -0
- package/src/retrieve.ts +11 -0
- package/src/settings.ts +24 -8
- package/src/terminal.ts +7 -0
- package/src/transfer-fanout.ts +2 -0
- package/src/transfer.ts +79 -14
package/docs/CLI.md
CHANGED
|
@@ -11,7 +11,7 @@ implemented flags live in each command's `commands/<name>/README.md`.
|
|
|
11
11
|
| Command | Summary |
|
|
12
12
|
| ------- | ------- |
|
|
13
13
|
| [`retrieve`](#retrieve) | Read RouterOS values through the shared core using the selected protocol. |
|
|
14
|
-
| [`execute`](#execute) | Run a RouterOS read or write command via native API, REST, or mac-telnet. |
|
|
14
|
+
| [`execute`](#execute) | Run a RouterOS read or write command via native API, REST, SSH, or mac-telnet. |
|
|
15
15
|
| [`api`](#api) | Structured RouterOS API passthrough (gh-api style) over REST or native API. |
|
|
16
16
|
| [`transfer`](#transfer) | Copy files to/from a RouterOS device and manage device files (rest/native/sftp). |
|
|
17
17
|
| [`terminal`](#terminal) | Open an interactive RouterOS console (ssh or mac-telnet). |
|
|
@@ -64,13 +64,14 @@ Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <targe
|
|
|
64
64
|
| `--max-results` | `<bytes>` | Fail instead of printing output larger than the given byte budget. |
|
|
65
65
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
66
66
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
67
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
67
68
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
68
69
|
| `--validate / --no-validate` | | Enable or disable inspect-backed preflight validation. |
|
|
69
70
|
| `--verbose` | | Show resolved setting sources in text output, on stderr. |
|
|
70
71
|
|
|
71
72
|
## execute
|
|
72
73
|
|
|
73
|
-
Run a RouterOS read or write command via native API, REST, or mac-telnet.
|
|
74
|
+
Run a RouterOS read or write command via native API, REST, SSH, or mac-telnet.
|
|
74
75
|
|
|
75
76
|
```text
|
|
76
77
|
Usage: centrs execute <target> <command> [flags] | centrs execute <target...> -- <command> [flags] | centrs execute --host <host|url> [--] <command> [flags] | centrs execute --group <name> <command> [flags]
|
|
@@ -78,7 +79,7 @@ Usage: centrs execute <target> <command> [flags] | centrs execute <target...> --
|
|
|
78
79
|
|
|
79
80
|
| Flag | Value | Description |
|
|
80
81
|
| ---- | ----- | ----------- |
|
|
81
|
-
| `--via` | <native-api\|rest-api\|mac-telnet> | Pin the protocol selector; no silent downgrade when set. A bare MAC target defaults to mac-telnet. |
|
|
82
|
+
| `--via` | <native-api\|rest-api\|ssh\|mac-telnet> | Pin the protocol selector; no silent downgrade when set. A bare MAC target defaults to mac-telnet. |
|
|
82
83
|
| `--group` | `<name>` | Fan out across every CDB record in the group (repeatable; de-duped by record index). |
|
|
83
84
|
| `--where` | `<attr>`=`<value>` | Device-class selector over CDB facts + core fields (repeatable, AND-combined). |
|
|
84
85
|
| `--near` | `<lat>`,`<lon>`,`<radius>` | Geo selector: devices whose GPS is within radius (m/km/mi/ft; bare number = km). Lat-first. |
|
|
@@ -97,9 +98,10 @@ Usage: centrs execute <target> <command> [flags] | centrs execute <target...> --
|
|
|
97
98
|
| `--insecure` | | Disable SSH host-key verification (`--via ssh`: accepts changed/impersonated keys, not just new) or accept a self-signed `api-ssl` TLS cert. Default verifies. |
|
|
98
99
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
99
100
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
101
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
100
102
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
101
103
|
| `--timeout` | `<duration>` | Per-request timeout (for REST, max 60s). |
|
|
102
|
-
| `--validate / --no-validate` | | Run RouterOS :parse
|
|
104
|
+
| `--validate / --no-validate` | | Run offline analysis, then RouterOS :parse, plus `/console/inspect` for structured commands over native API/REST (default true; `--validate=false` disables both stages). |
|
|
103
105
|
| `--yes` | | Confirm write-shaped add/set/remove commands in non-interactive runs. |
|
|
104
106
|
| `--max-results` | `<bytes>` | Fail if the rendered envelope exceeds this byte budget. |
|
|
105
107
|
| `--format` | <text\|json\|yaml> | Output format for the CLI response. Defaults to text; use --json or --format json for the structured envelope. |
|
|
@@ -145,9 +147,10 @@ Usage: centrs api <router> <endpoint> [flags] | centrs api --host <host|url> <en
|
|
|
145
147
|
| `--insecure` | | Accept a self-signed `api-ssl`/REST TLS cert. Default verifies. |
|
|
146
148
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
147
149
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
150
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
148
151
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
149
152
|
| `--timeout` | `<duration>` | Per-request timeout (for REST, max 60s). |
|
|
150
|
-
| `--validate / --no-validate` | |
|
|
153
|
+
| `--validate / --no-validate` | | Validate structured requests with `/console/inspect`; `/execute` scripts run offline analysis, then RouterOS :parse (default true; `--validate=false` disables both stages). |
|
|
151
154
|
| `--format` | <json\|ndjson\|yaml\|text> | Output format. Defaults to json for api; `CENTRS_FORMAT` overrides. |
|
|
152
155
|
| `--json` | | Shortcut for `--format json`. |
|
|
153
156
|
| `--verbose` | | Include additional context in text output, on stderr. |
|
|
@@ -162,7 +165,7 @@ Usage: centrs transfer <router>|--host <host> upload <local> [remote] | download
|
|
|
162
165
|
|
|
163
166
|
| Flag | Value | Description |
|
|
164
167
|
| ---- | ----- | ----------- |
|
|
165
|
-
| `--via` | `<method>` | Pin the method: rest, native, or sftp (large transfers); scp/fetch/ftp are not built yet. Auto picks the cheapest by size/direction. |
|
|
168
|
+
| `--via` | `<method>` | Pin the method: rest, native, or sftp (large transfers; `ssh` is an alias); scp/fetch/ftp are not built yet. Auto picks the cheapest by size/direction, after a CDB record's `via=` (ssh→sftp). |
|
|
166
169
|
| `--ssh-key` | `<path>` | sftp only: explicit private-key path. Falls back to CENTRS_SSH_KEY / the ssh-agent. |
|
|
167
170
|
| `--insecure` | | Accept a self-signed TLS cert (https/api-ssl) or a new SSH host key. Default verifies. |
|
|
168
171
|
| `--force / --overwrite` | | Skip the existing-destination check on the device for `upload`/`mkdir`/`copy` (`upload` then overwrites). Default refuses an existing target. `download` always replaces the local file. |
|
|
@@ -189,6 +192,7 @@ Usage: centrs transfer <router>|--host <host> upload <local> [remote] | download
|
|
|
189
192
|
| `--yaml` | | Shortcut for --format yaml. |
|
|
190
193
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
191
194
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
195
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
192
196
|
| `--validate / --no-validate` | | Enable or disable the existence-probe preflight. |
|
|
193
197
|
| `--verbose` | | Show the resolved transport in text output, on stderr. |
|
|
194
198
|
|
|
@@ -212,6 +216,7 @@ Usage: centrs terminal <router> [flags] | centrs terminal --host <host> [flags]
|
|
|
212
216
|
| `--insecure` | | `--via ssh`: disable host-key verification (accepts changed/impersonated keys). Default verifies. |
|
|
213
217
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
214
218
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
219
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
215
220
|
| `--resolve` | <none\|arp> | `--via ssh` only: turn a MAC target into an IP. CDB-first; `arp` opts into the host ARP cache (default none). The mac-telnet default ignores it. |
|
|
216
221
|
| `--format` | <text\|json\|yaml> | Error-envelope format on failure. --json / --yaml shortcuts. |
|
|
217
222
|
| `--json` | | Shortcut for --format json. |
|
|
@@ -258,23 +263,27 @@ Usage: centrs explain [<router>] '<input>' [flags]
|
|
|
258
263
|
Inspect and mutate the CDB-backed device registry. `devices` is the only command that writes the CDB.
|
|
259
264
|
|
|
260
265
|
```text
|
|
261
|
-
Usage: centrs devices <init|list|show|groups|add|edit|set|remove> [args] [flags]
|
|
266
|
+
Usage: centrs devices <init|encrypt|list|show|groups|add|edit|set|remove> [args] [flags]
|
|
262
267
|
```
|
|
263
268
|
|
|
264
269
|
| Flag | Value | Description |
|
|
265
270
|
| ---- | ----- | ----------- |
|
|
271
|
+
| `--encrypt` | | `init` only — require encrypted creation with a non-empty CDB password; refuse an existing open CDB. |
|
|
272
|
+
| `--no-backup` | | `encrypt` only — skip the plaintext pre-conversion snapshot. Without that snapshot, a forgotten password cannot be recovered. |
|
|
266
273
|
| `--cdb-file` | `<path>` | Override the resolved CDB path (default `~/.config/tikoci/winbox.cdb`). |
|
|
267
|
-
| `--cdb-password` | `<secret>` |
|
|
274
|
+
| `--cdb-password` | `<secret>` | CDB password for encrypted reads, writes, explicit encrypted initialization and conversion. Falls back to `CENTRS_CDB_PASSWORD`; a password alone never converts an open CDB. |
|
|
268
275
|
| `--via` | `<protocol>` | Resolve and report the protocol source for provenance examples; no network IO is performed. |
|
|
269
|
-
| `--group` | `<name>` | `list` filter / `add`,`
|
|
276
|
+
| `--group` | `<name>` | `list` filter / `add`,`set` first-class group field for the target entry. |
|
|
270
277
|
| `--where` | `<attr=value>` | `list` only — repeatable device-class filter over CDB facts (AND-combined), e.g. `--where lat=37.7749`. |
|
|
271
278
|
| `--near` | `<lat>`,`<lon>`,`<radius>` | `list` only — GPS filter: devices within radius (m/km/mi/ft; bare number = km). Lat-first. |
|
|
272
279
|
| `--bbox` | `<south>`,`<west>`,`<north>`,`<east>` | `list` only — GPS filter: devices inside the lat-first bounding box. |
|
|
273
280
|
| `--members` | | `groups` only — expand each group's membership. |
|
|
274
|
-
| `--explain` | | `show` only — include the raw `WinBoxCdbRecord` in `data.record
|
|
275
|
-
| `--match` | <user=…\|target=…\|record-type> | `show` — disambiguate when `<router>` matches several records: `user=<name>`, `target=<addr>`, or a record-type token. |
|
|
281
|
+
| `--explain` | | `show` only — include the raw `WinBoxCdbRecord` in `data.record` (its password field reads `<redacted>` unless `--show-password`). |
|
|
282
|
+
| `--match` | <user=…\|target=…\|record-type> | `show`,`set`,`remove` — disambiguate when `<router>` matches several records: `user=<name>`, `target=<addr>`, or a record-type token. |
|
|
276
283
|
| `--user` | `<name>` | `add`,`set` — first-class CDB user field. |
|
|
277
284
|
| `--password` | `<secret>` | `add`,`set` — first-class CDB password field. |
|
|
285
|
+
| `--password-stdin` | | `add`,`set` — read the record password from stdin (one trailing newline stripped) so it never rides argv. Conflicts with `--password`. |
|
|
286
|
+
| `--show-password` | | `show`,`add`,`set` — include the record password in cleartext. By default output carries `passwordSet` and withholds the password. |
|
|
278
287
|
| `--profile` | `<name>` | `add`,`set` — named WinBox profile (Workspace). |
|
|
279
288
|
| `--profile-none` | | `add`,`set` — write the WinBox `<none>` profile sentinel. |
|
|
280
289
|
| `--profile-own` | | `add`,`set` — write the WinBox `<own>` profile sentinel. |
|
|
@@ -291,6 +300,38 @@ Usage: centrs devices <init|list|show|groups|add|edit|set|remove> [args] [flags]
|
|
|
291
300
|
| `--format` | <text\|json\|yaml> | Output format for the CLI response. |
|
|
292
301
|
| `--json` | | Shortcut for `--format json`. |
|
|
293
302
|
|
|
303
|
+
### devices init
|
|
304
|
+
|
|
305
|
+
Create an empty CDB at the resolved path; existing readable files are left unchanged.
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
Usage: centrs devices init [flags]
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
| Flag | Value | Description |
|
|
312
|
+
| ---- | ----- | ----------- |
|
|
313
|
+
| `--encrypt` | | `init` only — require encrypted creation with a non-empty CDB password; refuse an existing open CDB. |
|
|
314
|
+
| `--cdb-file` | `<path>` | Override the resolved CDB path (default `~/.config/tikoci/winbox.cdb`). |
|
|
315
|
+
| `--cdb-password` | `<secret>` | CDB password for encrypted reads, writes, explicit encrypted initialization and conversion. Falls back to `CENTRS_CDB_PASSWORD`; a password alone never converts an open CDB. |
|
|
316
|
+
| `--format` | <text\|json\|yaml> | Output format for the CLI response. |
|
|
317
|
+
| `--json` | | Shortcut for `--format json`. |
|
|
318
|
+
|
|
319
|
+
### devices encrypt
|
|
320
|
+
|
|
321
|
+
Encrypt an existing CDB in place, or verify an already-encrypted file; existing backups remain unchanged.
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
Usage: centrs devices encrypt [flags]
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
| Flag | Value | Description |
|
|
328
|
+
| ---- | ----- | ----------- |
|
|
329
|
+
| `--no-backup` | | `encrypt` only — skip the plaintext pre-conversion snapshot. Without that snapshot, a forgotten password cannot be recovered. |
|
|
330
|
+
| `--cdb-file` | `<path>` | Override the resolved CDB path (default `~/.config/tikoci/winbox.cdb`). |
|
|
331
|
+
| `--cdb-password` | `<secret>` | CDB password for encrypted reads, writes, explicit encrypted initialization and conversion. Falls back to `CENTRS_CDB_PASSWORD`; a password alone never converts an open CDB. |
|
|
332
|
+
| `--format` | <text\|json\|yaml> | Output format for the CLI response. |
|
|
333
|
+
| `--json` | | Shortcut for `--format json`. |
|
|
334
|
+
|
|
294
335
|
## discover
|
|
295
336
|
|
|
296
337
|
Discover RouterOS neighbors over MNDP and optionally save them into the CDB.
|
|
@@ -340,6 +381,7 @@ Usage: centrs btest <client <router>|server> [flags]
|
|
|
340
381
|
| `--port` | `<port>` | Control port. Default 2000. |
|
|
341
382
|
| `--cdb-file` | `<path>` | (client) Read target credentials from a WinBox CDB file. |
|
|
342
383
|
| `--cdb-password` | `<password>` | (client) Decrypt an encrypted WinBox CDB file. |
|
|
384
|
+
| `--match` | <user=name\|target=addr\|record-type> | (client) Pick one of several CDB records saved for `<router>` (as `devices show --match`); `btest server` rejects it. |
|
|
343
385
|
| `--format` | <text\|csv\|json\|yaml> | Output format. Default text. |
|
|
344
386
|
| `--csv` | | Shortcut for `--format csv`. |
|
|
345
387
|
|
package/docs/MATRIX.md
CHANGED
|
@@ -189,7 +189,9 @@ Wire, auth, TCP/UDP, and CI caveats live in `commands/btest/README.md` and the
|
|
|
189
189
|
Live `explain` (#236) is `CHR-passed` on REST and native API: the #491
|
|
190
190
|
foundation, #492's describe and completion facets, and #225's live value types,
|
|
191
191
|
with every applicable example green on stable and long-term CHR. Issue #90
|
|
192
|
-
owns the remaining sequencing, including #507 context
|
|
192
|
+
owns the remaining sequencing, including #507's context surface. Its bounded
|
|
193
|
+
submission matrix is grounded (#574); context selection and binding behavior
|
|
194
|
+
remain follow-up work.
|
|
193
195
|
|
|
194
196
|
**First: `explain`** (maintainer decision 2026-10-16 on
|
|
195
197
|
[#90](https://github.com/tikoci/centrs/issues/90)). In order: make the
|
|
@@ -253,8 +255,8 @@ applicable examples pass on CHR via `bun run test:integration`.
|
|
|
253
255
|
|
|
254
256
|
Open offline follow-ups remain explicit but do not reopen the verified baseline.
|
|
255
257
|
Issue #211 made an unlisted absolute bare path abstain (rule F, R14); rule
|
|
256
|
-
P's closed-world reading is still not adopted (#372).
|
|
257
|
-
|
|
258
|
+
P's closed-world reading is still not adopted (#372). The non-authoritative
|
|
259
|
+
scratch citation cleanup is complete (#272, shipped in #568). Issues #350 (the broad
|
|
258
260
|
optional `=`), #351 (a relative submenu brace read as a command operand)
|
|
259
261
|
and #355 (`'` is not a RouterOS token) are complete. A submenu block after a navigation statement
|
|
260
262
|
(`/ip⏎address { … }`) keeps the pre-#351 reading; see
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# `cdb/encrypt-failed`
|
|
2
|
+
|
|
3
|
+
The existing CDB could not be rewritten with encryption.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
Check directory permissions and available disk space, then retry
|
|
8
|
+
`centrs devices encrypt`. Preserve the original file and any timestamped backups
|
|
9
|
+
before recovery. A failed write may already have created a plaintext backup;
|
|
10
|
+
encryption does not protect that backup.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# `cdb/password-required`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A CDB password is required for decryption or explicit encryption.
|
|
4
4
|
|
|
5
5
|
## Fix
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Pass `--cdb-password` or set `CENTRS_CDB_PASSWORD` to the CDB password.
|
|
8
|
+
`devices init --encrypt` and `devices encrypt` require a non-empty password,
|
|
9
|
+
even when the file is currently open. A password alone never implicitly
|
|
10
|
+
converts an existing open CDB; use `devices encrypt` explicitly.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `cdb/plaintext-backups`
|
|
2
|
+
|
|
3
|
+
CDB backups may still contain plaintext credentials after encryption.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
Apply your own secure backup and retention policy to the `.bak.*` files beside
|
|
8
|
+
the CDB. `devices encrypt` protects only the active CDB and retains every backup,
|
|
9
|
+
including the plaintext snapshot created during conversion. It does not encrypt
|
|
10
|
+
or delete old copies. This warning also accompanies already-encrypted no-op runs,
|
|
11
|
+
since prior backup protection cannot be inferred from the active file.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# `input/empty-stdin`
|
|
2
|
+
|
|
3
|
+
A flag that reads its value from stdin (e.g. `--password-stdin`) received
|
|
4
|
+
nothing.
|
|
5
|
+
|
|
6
|
+
`centrs devices add`/`set --password-stdin` strips one trailing newline (`\n`
|
|
7
|
+
or `\r\n`) and refuses what is left if it is empty. An empty pipe is usually a
|
|
8
|
+
mistake — an unset variable, a missing file, a command that printed nothing —
|
|
9
|
+
so it is not treated as "clear the password".
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Pipe a non-empty value:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
printf '%s' "$ROUTER_PW" | centrs devices set 192.0.2.5 --password-stdin
|
|
17
|
+
centrs devices set 192.0.2.5 --password-stdin < pw.txt
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
To clear a stored password on purpose, say so with the argv flag:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
centrs devices set 192.0.2.5 --password ""
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
See [`commands/devices/README.md`](https://github.com/tikoci/centrs/blob/main/commands/devices/README.md)
|
|
27
|
+
for the record-password channels.
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
# `input/invalid-routeros-path`
|
|
2
2
|
|
|
3
|
-
The RouterOS path is malformed
|
|
3
|
+
The RouterOS path is malformed: not slash-prefixed, a segment that is not a
|
|
4
|
+
menu name, or characters a REST URL would reinterpret.
|
|
5
|
+
|
|
6
|
+
## Typical triggers
|
|
7
|
+
|
|
8
|
+
- A path without a leading slash: `retrieve <router> ip/address`.
|
|
9
|
+
- A `retrieve` or `api` path segment that is not a RouterOS menu name. Menu
|
|
10
|
+
names are letters, digits and hyphens (case folds) (`/interface/6to4`,
|
|
11
|
+
`/ip/dhcp-server`). The check runs on every transport and even with
|
|
12
|
+
`--validate=false`, because a read must only dispatch a read (#586).
|
|
13
|
+
- A REST request path containing `?`, `#`, `\`, a control character, or a
|
|
14
|
+
`.`/`..` segment, including percent-encoded forms such as `%2e%2e`. The
|
|
15
|
+
URL parser would turn these into a query, a fragment
|
|
16
|
+
or a different path, so the request would not reach the path as written.
|
|
17
|
+
Before this guard, `retrieve '/system/reboot#' --no-validate` posted
|
|
18
|
+
`/rest/system/reboot`.
|
|
19
|
+
|
|
20
|
+
`context.path` names the path; `context.invalidSegments` lists the segments
|
|
21
|
+
that failed the menu grammar.
|
|
4
22
|
|
|
5
23
|
## Fix
|
|
6
24
|
|
|
25
|
+
- Write the path as slash-separated menu names: `/ip/address`,
|
|
26
|
+
`/system/resource`.
|
|
27
|
+
- Select rows with `--query` (for example `--query 'name="a#b"'`), not with
|
|
28
|
+
characters in the path.
|
|
29
|
+
|
|
7
30
|
See [`docs/CONSTITUTION.md`](../../CONSTITUTION.md) for the centrs error
|
|
8
|
-
contract.
|
|
9
|
-
for `input/invalid-routeros-path`.
|
|
31
|
+
contract.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# `input/invalid-stdin`
|
|
2
|
+
|
|
3
|
+
A flag that reads its value from stdin (e.g. `--password-stdin`) received
|
|
4
|
+
bytes that are not valid UTF-8 text, or began with a byte-order mark.
|
|
5
|
+
|
|
6
|
+
`centrs devices add`/`set --password-stdin` strips one trailing newline (`\n`
|
|
7
|
+
or `\r\n`) and decodes the rest as UTF-8. The CDB stores record strings as
|
|
8
|
+
UTF-8 and drops a leading byte-order mark (U+FEFF) when it reads them back, so
|
|
9
|
+
malformed bytes or a BOM would store a credential different from what was
|
|
10
|
+
piped in. centrs refuses instead of guessing. The bytes are never echoed.
|
|
11
|
+
|
|
12
|
+
## Fix
|
|
13
|
+
|
|
14
|
+
Pipe the password as plain UTF-8 text:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
printf '%s' "$ROUTER_PW" | centrs devices set 192.0.2.5 --password-stdin
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If the value comes from a file an editor saved with a BOM (common on Windows),
|
|
21
|
+
re-save it as UTF-8 without BOM, or strip the first three bytes:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
tail -c +4 pw.txt | centrs devices set 192.0.2.5 --password-stdin
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
See [`commands/devices/README.md`](https://github.com/tikoci/centrs/blob/main/commands/devices/README.md)
|
|
28
|
+
for the record-password channels.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# `usage/stdin-is-tty`
|
|
2
|
+
|
|
3
|
+
A flag that reads its value from stdin (e.g. `--password-stdin`) was given
|
|
4
|
+
while stdin is a terminal.
|
|
5
|
+
|
|
6
|
+
`--password-stdin` exists for automation: the value arrives on fd 0 so it never
|
|
7
|
+
appears in argv or `ps`. centrs does not prompt for it at a terminal — waiting
|
|
8
|
+
on a keyboard with no prompt would look like a hang, and echoing a typed secret
|
|
9
|
+
would defeat the point. Interactive prompting is tracked separately.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Pipe or redirect the value:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
printf '%s' "$ROUTER_PW" | centrs devices add 192.0.2.5 --user admin --password-stdin
|
|
17
|
+
centrs devices add 192.0.2.5 --user admin --password-stdin < pw.txt
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
See [`commands/devices/README.md`](https://github.com/tikoci/centrs/blob/main/commands/devices/README.md)
|
|
21
|
+
for the record-password channels.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tikoci/centrs",
|
|
3
|
-
"version": "0.1.9
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"repository": {
|
|
@@ -9,21 +9,21 @@
|
|
|
9
9
|
},
|
|
10
10
|
"homepage": "https://github.com/tikoci/centrs#readme",
|
|
11
11
|
"bugs": "https://github.com/tikoci/centrs/issues",
|
|
12
|
-
"packageManager": "bun@1.4.
|
|
12
|
+
"packageManager": "bun@1.4.4",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@biomejs/biome": "2.
|
|
15
|
+
"@biomejs/biome": "2.6.3",
|
|
16
16
|
"@secretlint/secretlint-rule-preset-recommend": "^13.0.7",
|
|
17
|
-
"@types/bun": "1.4.
|
|
17
|
+
"@types/bun": "1.4.4",
|
|
18
18
|
"cloc": "^2.6.0-cloc",
|
|
19
19
|
"cspell": "^10.3.6",
|
|
20
20
|
"has-flag": "4.0.0",
|
|
21
|
-
"markdownlint-cli2": "^0.
|
|
21
|
+
"markdownlint-cli2": "^0.24.0",
|
|
22
22
|
"secretlint": "^13.0.7",
|
|
23
23
|
"typedoc": "^0.28.20"
|
|
24
24
|
},
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"@tikoci/quickchr": "^0.
|
|
26
|
+
"@tikoci/quickchr": "^0.5.2"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"typescript": "^6.0.3"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"description": "All-in-one TypeScript library/CLI/MCP for interacting with MikroTik RouterOS devices (TUI stub)",
|
|
44
44
|
"engines": {
|
|
45
|
-
"bun": ">=1.
|
|
45
|
+
"bun": ">=1.4.4 <2"
|
|
46
46
|
},
|
|
47
47
|
"private": false,
|
|
48
48
|
"files": [
|
|
@@ -118,6 +118,8 @@
|
|
|
118
118
|
"explain:operator-readme:check": "bun run scripts/explain-operator-slice.ts --readme --check",
|
|
119
119
|
"explain:operator-slice": "bun run scripts/explain-operator-slice.ts",
|
|
120
120
|
"explain:probe:brace-slots": "bun run scripts/probes/explain-brace-slot-sweep.ts",
|
|
121
|
+
"explain:probe:context-matrix": "bun run scripts/probes/explain-context-matrix.ts",
|
|
122
|
+
"explain:probe:corrected-anchors": "bun run scripts/probes/explain-corrected-anchors.ts",
|
|
121
123
|
"explain:probe:escapes": "bun run scripts/probes/explain-escape-sweep.ts",
|
|
122
124
|
"explain:probe:highlight-recapture": "bun run scripts/probes/explain-highlight-recapture.ts",
|
|
123
125
|
"explain:probe:operators": "bun run scripts/probes/explain-operator-sweep.ts",
|
|
@@ -125,6 +127,8 @@
|
|
|
125
127
|
"explain:probe:symbol-arms-score": "bun run scripts/probes/explain-symbol-arms-score.ts",
|
|
126
128
|
"explain:probe:symbol-bad-sigil-replay": "bun run scripts/probes/explain-symbol-bad-sigil-replay.ts",
|
|
127
129
|
"explain:probe:symbol-classes-replay": "bun run scripts/probes/explain-symbol-classes-replay.ts",
|
|
130
|
+
"explain:probe:import-blocker-validity": "bun run scripts/probes/explain-import-blocker-validity.ts",
|
|
131
|
+
"explain:probe:import-dry-run": "bun run scripts/probes/explain-import-dry-run.ts",
|
|
128
132
|
"explain:probe:version-floors": "bun run scripts/probes/explain-version-floors.ts",
|
|
129
133
|
"explain:menus:check": "bun run scripts/gen-explain-menus.ts --check",
|
|
130
134
|
"explain:value-census": "bun run scripts/explain-value-census.ts",
|
package/src/api-fanout.ts
CHANGED
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
import { promptForWriteConfirmation } from "./execute.ts";
|
|
63
63
|
import { plannedProtocols, type RouterOsProtocol } from "./protocols/index.ts";
|
|
64
64
|
import {
|
|
65
|
+
assertNoMatchInFanout,
|
|
65
66
|
emptyWriteSelectionError,
|
|
66
67
|
expandSelection,
|
|
67
68
|
isDefaultRecordTarget,
|
|
@@ -213,6 +214,7 @@ export async function apiFanout(
|
|
|
213
214
|
// missing endpoint or an invalid `-X` is an outer error regardless of how many
|
|
214
215
|
// targets the selection resolves to (so an empty selection cannot mask it).
|
|
215
216
|
validateApiRequestShape(request);
|
|
217
|
+
assertNoMatchInFanout(request.match);
|
|
216
218
|
const config = await loadEnvFileDefaults(env);
|
|
217
219
|
const method = parseApiMethod(request.method);
|
|
218
220
|
const requestSummary = apiRequestSummaryFromRequest(request, env);
|
package/src/api.ts
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
isCommandNode,
|
|
46
46
|
pathTokens,
|
|
47
47
|
} from "./core/inspect.ts";
|
|
48
|
+
import { assertRouterOsMenuPath } from "./core/menu-path.ts";
|
|
48
49
|
import {
|
|
49
50
|
compileQueryWords,
|
|
50
51
|
type ParsedQuery,
|
|
@@ -160,6 +161,12 @@ export interface ApiRequest {
|
|
|
160
161
|
verbose?: boolean;
|
|
161
162
|
cdbFile?: string;
|
|
162
163
|
cdbPassword?: string;
|
|
164
|
+
/**
|
|
165
|
+
* `--match` selector for a `<router>` several CDB records share
|
|
166
|
+
* (`user=<name>`, `target=<addr>`, or a record type): the same selector
|
|
167
|
+
* `devices show --match` takes. Single-target only (#583).
|
|
168
|
+
*/
|
|
169
|
+
match?: string;
|
|
163
170
|
/** Opt-in host ARP resolution for a MAC target (`none` default, or `arp`). */
|
|
164
171
|
resolve?: string;
|
|
165
172
|
stdinIsTty?: boolean;
|
|
@@ -937,6 +944,7 @@ export async function resolveApiRequest(
|
|
|
937
944
|
targetInput: request.targetInput,
|
|
938
945
|
cdbFile: request.cdbFile,
|
|
939
946
|
cdbPassword: request.cdbPassword,
|
|
947
|
+
match: request.match,
|
|
940
948
|
},
|
|
941
949
|
env,
|
|
942
950
|
config,
|
|
@@ -2176,6 +2184,10 @@ export function validateApiRequestShape(request: ApiRequest): void {
|
|
|
2176
2184
|
"Pass a path with at least one menu segment, such as `ip/address`.",
|
|
2177
2185
|
});
|
|
2178
2186
|
}
|
|
2187
|
+
// Before transport choice and regardless of --validate: a `#`/`?` in the
|
|
2188
|
+
// path cut `/print` off the REST URL and a GET read posted to the bare
|
|
2189
|
+
// command, rebooting the router (#586).
|
|
2190
|
+
assertRouterOsMenuPath(normalized.path, { endpoint: request.endpoint });
|
|
2179
2191
|
}
|
|
2180
2192
|
|
|
2181
2193
|
/**
|
package/src/btest.ts
CHANGED
|
@@ -113,6 +113,12 @@ export interface BtestClientRequest {
|
|
|
113
113
|
natMode?: boolean;
|
|
114
114
|
cdbFile?: string;
|
|
115
115
|
cdbPassword?: string;
|
|
116
|
+
/**
|
|
117
|
+
* `--match` selector for a `<router>` several CDB records share
|
|
118
|
+
* (`user=<name>`, `target=<addr>`, or a record type): the same selector
|
|
119
|
+
* `devices show --match` takes. Single-target only (#583).
|
|
120
|
+
*/
|
|
121
|
+
match?: string;
|
|
116
122
|
env?: Record<string, string | undefined>;
|
|
117
123
|
signal?: AbortSignal;
|
|
118
124
|
/** Live per-interval frame callback (text/csv streaming). */
|
|
@@ -264,6 +270,7 @@ export async function btestClient(
|
|
|
264
270
|
targetInput: request.targetInput,
|
|
265
271
|
cdbFile: request.cdbFile,
|
|
266
272
|
cdbPassword: request.cdbPassword,
|
|
273
|
+
match: request.match,
|
|
267
274
|
},
|
|
268
275
|
env,
|
|
269
276
|
);
|
package/src/cli/api.ts
CHANGED
|
@@ -23,7 +23,10 @@ import {
|
|
|
23
23
|
formatCentrsErrorText,
|
|
24
24
|
} from "../errors.ts";
|
|
25
25
|
import { describeCentrs } from "../index.ts";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
assertNoMatchInFanout,
|
|
28
|
+
assertNoQuickchrOverrideConflict,
|
|
29
|
+
} from "../resolver/index.ts";
|
|
27
30
|
import {
|
|
28
31
|
type CliCommandMetadata,
|
|
29
32
|
expectValue,
|
|
@@ -165,6 +168,12 @@ export const apiCommand: CliCommandMetadata = {
|
|
|
165
168
|
valueName: "<password>",
|
|
166
169
|
description: "Decrypt an encrypted WinBox CDB file.",
|
|
167
170
|
},
|
|
171
|
+
{
|
|
172
|
+
flag: "--match",
|
|
173
|
+
valueName: "<user=name|target=addr|record-type>",
|
|
174
|
+
description:
|
|
175
|
+
"Pick one of several CDB records saved for <router> (as `devices show --match`); single target only.",
|
|
176
|
+
},
|
|
168
177
|
{
|
|
169
178
|
flag: "--resolve",
|
|
170
179
|
valueName: "<none|arp>",
|
|
@@ -179,7 +188,7 @@ export const apiCommand: CliCommandMetadata = {
|
|
|
179
188
|
{
|
|
180
189
|
flag: "--validate / --no-validate",
|
|
181
190
|
description:
|
|
182
|
-
"
|
|
191
|
+
"Validate structured requests with `/console/inspect`; `/execute` scripts run offline analysis, then RouterOS :parse (default true; `--validate=false` disables both stages).",
|
|
183
192
|
},
|
|
184
193
|
{
|
|
185
194
|
flag: "--format",
|
|
@@ -330,6 +339,9 @@ export function parseApiCliArgs(args: readonly string[]): ApiCliArgs {
|
|
|
330
339
|
case "--cdb-password":
|
|
331
340
|
parsed.cdbPassword = expectValue(args, ++index, arg);
|
|
332
341
|
break;
|
|
342
|
+
case "--match":
|
|
343
|
+
parsed.match = expectValue(args, ++index, arg);
|
|
344
|
+
break;
|
|
333
345
|
case "--resolve":
|
|
334
346
|
parsed.resolve = expectValue(args, ++index, arg);
|
|
335
347
|
break;
|
|
@@ -473,6 +485,11 @@ export async function runApiCli(args: readonly string[]): Promise<number> {
|
|
|
473
485
|
selectionFlags.quickchr[0] ?? "",
|
|
474
486
|
);
|
|
475
487
|
}
|
|
488
|
+
// `--match` + fan-out is refused here too, before `--input -` can block
|
|
489
|
+
// on stdin; `apiFanout` keeps the same check for library callers (#583).
|
|
490
|
+
if (isFanoutMode(selectionFlags, targetPositionals.length)) {
|
|
491
|
+
assertNoMatchInFanout(parsed.match);
|
|
492
|
+
}
|
|
476
493
|
|
|
477
494
|
// Resolve `--input` (file / stdin) before handing the body to the orchestrator.
|
|
478
495
|
if (parsed.inputPath !== undefined) {
|
package/src/cli/btest.ts
CHANGED
|
@@ -145,6 +145,12 @@ export const btestCommand: CliCommandMetadata = {
|
|
|
145
145
|
valueName: "<password>",
|
|
146
146
|
description: "(client) Decrypt an encrypted WinBox CDB file.",
|
|
147
147
|
},
|
|
148
|
+
{
|
|
149
|
+
flag: "--match",
|
|
150
|
+
valueName: "<user=name|target=addr|record-type>",
|
|
151
|
+
description:
|
|
152
|
+
"(client) Pick one of several CDB records saved for <router> (as `devices show --match`); `btest server` rejects it.",
|
|
153
|
+
},
|
|
148
154
|
{
|
|
149
155
|
flag: "--format",
|
|
150
156
|
valueName: `<${btestOutputFormats.join("|")}>`,
|
|
@@ -302,6 +308,9 @@ function parseClientArgs(args: readonly string[]): ClientCliArgs {
|
|
|
302
308
|
case "--cdb-password":
|
|
303
309
|
request.cdbPassword = expectValue(args, ++i, arg);
|
|
304
310
|
break;
|
|
311
|
+
case "--match":
|
|
312
|
+
request.match = expectValue(args, ++i, arg);
|
|
313
|
+
break;
|
|
305
314
|
case "--csv":
|
|
306
315
|
parsed.format = "csv";
|
|
307
316
|
break;
|