@tikoci/centrs 0.1.7 → 0.1.8
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 +256 -0
- package/GLOSSARY.txt +32 -0
- package/README.md +2 -2
- package/SECURITY.md +1 -1
- package/commands/AGENTS.md +2 -1
- package/commands/api/AGENTS.md +55 -30
- package/commands/api/README.md +123 -41
- package/commands/api/examples.md +205 -17
- package/commands/devices/README.md +11 -3
- package/commands/execute/README.md +31 -3
- package/commands/execute/examples.md +136 -1
- package/commands/explain/README.md +30 -4
- package/commands/mcp/README.md +5 -1
- package/commands/mcp/examples.md +18 -1
- package/commands/retrieve/README.md +348 -4
- package/commands/retrieve/examples.md +422 -5
- package/commands/terminal/README.md +14 -4
- package/commands/terminal/examples.md +22 -3
- package/commands/transfer/README.md +55 -9
- package/commands/transfer/examples.md +106 -7
- package/docs/CLI.md +28 -20
- package/docs/CONSTITUTION.md +30 -10
- package/docs/MATRIX.md +38 -1
- package/docs/errors/README.md +3 -1
- package/docs/errors/input/invalid-query.md +36 -0
- package/docs/errors/input/local-destination.md +20 -0
- package/docs/errors/input/unsupported-query.md +19 -0
- package/docs/errors/internal/byte-string.md +13 -0
- package/docs/errors/settings/invalid-timeout.md +6 -0
- package/docs/errors/transport/stream-overflow.md +21 -0
- package/docs/errors/usage/removed-flag.md +20 -0
- package/docs/errors/validation/menu-unavailable.md +22 -0
- package/docs/errors/validation/not-followable.md +23 -0
- package/docs/errors/validation/package-missing.md +29 -0
- package/docs/errors/validation/unknown-path.md +16 -4
- package/docs/errors/wait/deadline-exceeded.md +14 -0
- package/docs/errors/wait/interrupted.md +10 -0
- package/package.json +14 -11
- package/src/absent-menu.ts +280 -0
- package/src/api-fanout.ts +22 -12
- package/src/api.ts +511 -186
- package/src/btest.ts +19 -17
- package/src/cli/api.ts +126 -34
- package/src/cli/execute.ts +18 -5
- package/src/cli/retrieve.ts +223 -22
- package/src/cli/terminal.ts +10 -5
- package/src/cli/transfer.ts +23 -8
- package/src/core/envelope.ts +14 -0
- package/src/core/error-catalog.ts +56 -1
- package/src/core/inspect.ts +43 -5
- package/src/core/query.ts +508 -0
- package/src/core/routeros-errors.ts +1 -1
- package/src/devices.ts +6 -9
- package/src/discover.ts +7 -8
- package/src/errors.ts +116 -7
- package/src/execute-fanout.ts +13 -8
- package/src/execute.ts +106 -65
- package/src/explain/catalog.ts +58 -10
- package/src/explain/menus.ts +23 -2
- package/src/explain.ts +6 -6
- package/src/index.ts +29 -1
- package/src/mcp/safety.ts +43 -18
- package/src/mcp/tools.ts +14 -17
- package/src/protocols/adapter.ts +224 -118
- package/src/protocols/btest-session.ts +3 -2
- package/src/protocols/endpoint.ts +10 -0
- package/src/protocols/mac-telnet-console.ts +6 -3
- package/src/protocols/native-api.ts +223 -70
- package/src/protocols/sftp.ts +44 -14
- package/src/protocols/ssh.ts +42 -14
- package/src/resolver/cdb.ts +28 -7
- package/src/resolver/index.ts +1 -0
- package/src/resolver/target.ts +53 -7
- package/src/retrieve-fanout.ts +51 -12
- package/src/retrieve-follow.ts +862 -0
- package/src/retrieve-sample.ts +244 -0
- package/src/retrieve-stream.ts +115 -0
- package/src/retrieve-wait.ts +286 -0
- package/src/retrieve.ts +732 -108
- package/src/settings.ts +15 -15
- package/src/transfer-fanout.ts +40 -15
- package/src/transfer.ts +234 -32
|
@@ -15,7 +15,10 @@ Conventions (provided by the harness):
|
|
|
15
15
|
- `$A` is `<host>` and `$API_PORT` is the native-API port (`chr.ports.api`) for
|
|
16
16
|
`--via native-api`.
|
|
17
17
|
- `$SRC` is a small local text file (~2 KB) the harness creates; `$TMP` is a
|
|
18
|
-
scratch directory for downloads. `$BIG` is a local file **larger than 60 KB
|
|
18
|
+
scratch directory for downloads. `$BIG` is a local file **larger than 60 KB**:
|
|
19
|
+
every byte value, then deterministic noise, with a UTF-8 `€` straddling each
|
|
20
|
+
32 KiB `/file/read` chunk boundary. `$BIN` is the same shape at exactly 60 KB,
|
|
21
|
+
the largest the `/file/set` write cap allows.
|
|
19
22
|
- Device-side paths are **bare** (a CHR has no `flash` disk, so everything
|
|
20
23
|
persists for the session); the harness removes `centrs-*` test files between
|
|
21
24
|
runs. The CHR free license caps throughput at 1 Mb/s, so fixtures stay small —
|
|
@@ -77,6 +80,15 @@ centrs transfer $R list --name 'centrs-*' --username $U --password $P --json
|
|
|
77
80
|
|
|
78
81
|
Envelope: every row's `name` matches the glob; `centrs-up.txt` is present.
|
|
79
82
|
|
|
83
|
+
### H1. `--host` stands in for `<router>` (#428)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
centrs transfer --host $R list --username $U --password $P --json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`ok: true`, with `data` the device's file list. Nothing comes before the verb,
|
|
90
|
+
so `--host` is the target and there is no CDB lookup.
|
|
91
|
+
|
|
80
92
|
## Validate before write
|
|
81
93
|
|
|
82
94
|
### 6. Refuse overwrite without `--force`
|
|
@@ -202,8 +214,8 @@ centrs transfer $R download centrs-big.bin $TMP/big.bin --username $U --password
|
|
|
202
214
|
```
|
|
203
215
|
|
|
204
216
|
Envelope: `ok: true`, `meta.via="rest-api"`, `data.bytes` equals the seeded size
|
|
205
|
-
(>60 KB). `$TMP/big.bin`
|
|
206
|
-
reassembles correctly across `/file/read` calls.
|
|
217
|
+
(>60 KB). `$TMP/big.bin` has the seed's sha256 — proving the chunked read
|
|
218
|
+
reassembles correctly across `/file/read` calls and keeps every byte value.
|
|
207
219
|
|
|
208
220
|
## Error / transport contract
|
|
209
221
|
|
|
@@ -284,14 +296,46 @@ centrs transfer $A upload $BIG centrs-too-big.txt --via native-api --port $API_P
|
|
|
284
296
|
|
|
285
297
|
Envelope: `ok: false`, `error.code="transport/unsupported-operation"`.
|
|
286
298
|
|
|
299
|
+
## Binary contents (#404)
|
|
300
|
+
|
|
301
|
+
A size check cannot see a same-length substitution, so these compare sha256.
|
|
302
|
+
|
|
303
|
+
### B1. Binary round-trip over REST
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
centrs transfer $R upload $BIN centrs-bin-rest.bin --username $U --password $P --json
|
|
307
|
+
centrs transfer $R download centrs-bin-rest.bin $TMP/bin-rest.bin --username $U --password $P --json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Upload envelope: `data.bytes=60000`, `data.verified="size"`. `$TMP/bin-rest.bin`
|
|
311
|
+
has the sha256 of `$BIN`.
|
|
312
|
+
|
|
313
|
+
### B2. Binary round-trip over native-api
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
centrs transfer $A upload $BIN centrs-bin-nv.bin --via native-api --port $API_PORT --username $U --password $P --json
|
|
317
|
+
centrs transfer $A download centrs-bin-nv.bin $TMP/bin-nv.bin --via native-api --port $API_PORT --username $U --password $P --json
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Same as B1.
|
|
321
|
+
|
|
322
|
+
### B3. Chunked binary read over native-api
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
centrs transfer $A download centrs-big.bin $TMP/nv-big.bin --via native-api --port $API_PORT --username $U --password $P --json
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Runs after S3 seeds `centrs-big.bin`. `$TMP/nv-big.bin` has the sha256 of `$BIG`.
|
|
329
|
+
|
|
287
330
|
## ssh (`--via sftp`)
|
|
288
331
|
|
|
289
332
|
The first SSH consumer: a key-authenticated SFTP round-trip over the host OpenSSH
|
|
290
333
|
`sftp` subsystem. The harness generates an ed25519 keypair, imports the public
|
|
291
334
|
half to the CHR user (`/user ssh-keys import`), and drives `--via sftp --ssh-key`
|
|
292
335
|
against the CHR's forwarded SSH port; `--insecure` accepts the ephemeral host key
|
|
293
|
-
(default is `accept-new` trust-on-first-use).
|
|
294
|
-
|
|
336
|
+
(default is `accept-new` trust-on-first-use). centrs's sftp runs `BatchMode=yes`
|
|
337
|
+
and never sends a password, so key auth is the only path (S7); RouterOS's own
|
|
338
|
+
password policy is a separate fact (S8).
|
|
295
339
|
|
|
296
340
|
### S1. sftp upload (host → device)
|
|
297
341
|
|
|
@@ -315,8 +359,9 @@ centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $K
|
|
|
315
359
|
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure upload $BIG centrs-big.bin --json
|
|
316
360
|
```
|
|
317
361
|
|
|
318
|
-
Envelope: `ok: true`.
|
|
319
|
-
reads back over REST
|
|
362
|
+
Envelope: `ok: true`. An sftp download of it has the sha256 of `$BIG`. This is
|
|
363
|
+
also the seed for examples 17 and B3 (the same file then reads back over REST and
|
|
364
|
+
native-api via chunked `/file/read`).
|
|
320
365
|
|
|
321
366
|
### S4. list over sftp
|
|
322
367
|
|
|
@@ -336,6 +381,60 @@ centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $K
|
|
|
336
381
|
Both `ok: true`. On-device `copy` has no SFTP primitive — `--via sftp copy` returns
|
|
337
382
|
`transport/unsupported-operation` (it stays on rest/native).
|
|
338
383
|
|
|
384
|
+
### S6. A rejected selected key fails despite a trusted agent identity
|
|
385
|
+
|
|
386
|
+
The shared [SSH identity example S5](../execute/examples.md#s5-an-explicit-rejected-key-cannot-fall-back-to-an-unrelated-agent-key)
|
|
387
|
+
runs a real SFTP list with an untrusted `--ssh-key` and a trusted key in a
|
|
388
|
+
private agent. It fails with `auth/failed`; selecting the trusted key or leaving
|
|
389
|
+
the key unset succeeds. Covered by `test/integration/ssh-identity.test.ts`.
|
|
390
|
+
|
|
391
|
+
### S7. A resolved password does not authenticate batch sftp
|
|
392
|
+
|
|
393
|
+
`$PW_USER` has a password, no SSH key, and the default
|
|
394
|
+
`password-authentication=yes-if-no-key`; a plain OpenSSH password login as that
|
|
395
|
+
user succeeds. The host OpenSSH runs with an empty config and no agent.
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $PW_USER --password $PW_PASS --insecure list --json
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Exit 1. Envelope: `ok: false`, `error.code="auth/failed"`,
|
|
402
|
+
`error.context.sshKeySelected=false`, and the resolved meta — `meta.via="ssh"`,
|
|
403
|
+
`meta.target.host`/`port`, `meta.settings.password={kind:"cli",key:"password"}`,
|
|
404
|
+
`meta.operation.auth={username:$PW_USER,passwordProvided:true}`. `tips` is
|
|
405
|
+
`tip/sftp-password-not-used` then `tip/transfer-password-transport`; text output
|
|
406
|
+
lists the same tips. The password appears nowhere in either output.
|
|
407
|
+
|
|
408
|
+
### S8. RouterOS password policy, and the key path that works
|
|
409
|
+
|
|
410
|
+
After `/user ssh-keys import` installs `$KEY.pub` for `$PW_USER`, the plain
|
|
411
|
+
OpenSSH password login is refused (`Permission denied`), and:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $PW_USER --ssh-key $KEY --insecure list --json
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Envelope: `ok: true`. With `/ip ssh set password-authentication=yes`, the
|
|
418
|
+
password login succeeds again with the key still installed.
|
|
419
|
+
|
|
420
|
+
### S9. Group policy refusals are not auth failures
|
|
421
|
+
|
|
422
|
+
Each user holds the installed `$KEY`, and a password is passed too:
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $USER_NO_WRITE --password $PW_PASS --ssh-key $KEY --insecure upload $SRC centrs-391.txt --json
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
| User's group policy | Verb | `error.code` |
|
|
429
|
+
| ------------------- | ---- | ------------ |
|
|
430
|
+
| `ssh,ftp,read` (no `write`) | `upload`, `mkdir` | `routeros/command-failed`, `tips: []` |
|
|
431
|
+
| `ssh,read,write` (no `ftp`) | `list` | `routeros/session-closed` (SSH disconnect reason 7) |
|
|
432
|
+
| `ftp,read,write` (no `ssh`) | `list` | `auth/failed` |
|
|
433
|
+
|
|
434
|
+
Only OpenSSH's pre-login `Permission denied (publickey…)` is `auth/failed`; a
|
|
435
|
+
bare `Permission denied` after login is a policy refusal, so no password tips
|
|
436
|
+
are attached. S7–S9 are covered by `test/integration/ssh-auth-policy.test.ts`.
|
|
437
|
+
|
|
339
438
|
## Target selection (fan-out)
|
|
340
439
|
|
|
341
440
|
These exercise the shared target-selection grammar
|
package/docs/CLI.md
CHANGED
|
@@ -27,12 +27,12 @@ implemented flags live in each command's `commands/<name>/README.md`.
|
|
|
27
27
|
Read RouterOS values through the shared core using the selected protocol.
|
|
28
28
|
|
|
29
29
|
```text
|
|
30
|
-
Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <target...> <routeros-path> [flags] | centrs retrieve --group <name> <routeros-path> [flags]
|
|
30
|
+
Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <target...> <routeros-path> [flags] | centrs retrieve --host <host|url> <routeros-path> [flags] | centrs retrieve --group <name> <routeros-path> [flags]
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
| Flag | Value | Description |
|
|
34
34
|
| ---- | ----- | ----------- |
|
|
35
|
-
| `--via` | `<protocol>` | Pin the protocol selector. Defaults to `rest-api` for retrieve
|
|
35
|
+
| `--via` | `<protocol>` | Pin the protocol selector. Defaults to `rest-api` for retrieve (`--sample` included), and to `native-api` under `--follow`. |
|
|
36
36
|
| `--group` | `<name>` | Fan out across every CDB record in the group (repeatable; de-duped by record index). |
|
|
37
37
|
| `--where` | `<attr>`=`<value>` | Device-class selector over CDB facts + core fields (repeatable, AND-combined). |
|
|
38
38
|
| `--near` | `<lat>`,`<lon>`,`<radius>` | Geo selector: devices whose GPS is within radius (m/km/mi/ft; bare number = km). Lat-first. |
|
|
@@ -41,17 +41,25 @@ Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <targe
|
|
|
41
41
|
| `--default` | | Select the reserved `__default__` record. |
|
|
42
42
|
| `--quickchr` | `<name>` | Target a running quickchr-managed CHR VM by name (repeatable; fans out when repeated). Exclusive of positional targets and CDB selectors; conflicts with direct connection overrides (--host/--port/--username/--password, and --ssh-key where the command has it). |
|
|
43
43
|
| `--concurrency` | `<n>` | Max in-flight targets during fan-out (transport-aware default: rest-api 8, native-api 4). |
|
|
44
|
-
| `--host` | <host\|url> |
|
|
44
|
+
| `--host` | <host\|url> | Host or base URL to dial. With no `<target>` it is the target (no CDB lookup); with one it overrides the resolved host. |
|
|
45
45
|
| `--port` | `<port>` | Override the resolved management port. |
|
|
46
46
|
| `--username / --user / -u` | `<name>` | RouterOS username (aliases `--user`, `-u`). Falls back to `CENTRS_USERNAME`. |
|
|
47
47
|
| `--password` | `<secret>` | RouterOS password. Falls back to `CENTRS_PASSWORD`. |
|
|
48
|
-
| `--timeout` | <ms\|5s> |
|
|
48
|
+
| `--timeout` | <ms\|5s> | Per-request timeout, separate from --wait's overall deadline. REST currently rejects values above 60s. |
|
|
49
49
|
| `--attribute` | `<name>` | Project one attribute. May be repeated. |
|
|
50
50
|
| `--attributes` | `<a,b>` | Project a comma-separated attribute list. |
|
|
51
51
|
| `--all-attributes` | | Request the RouterOS detail/all-attributes shape. |
|
|
52
52
|
| `--list-attributes / --list` | | List inspect-derived attributes without running the data call. |
|
|
53
|
-
| `--query / --filter` | `<expr>` |
|
|
54
|
-
| `--
|
|
53
|
+
| `--query / --filter` | `<expr>` | Row filter written as after `print where` (`disabled`, `mtu>=1500 and !dynamic`), run on the router as an API query; repeatable, AND-ed. No `~` or `in`. |
|
|
54
|
+
| `--follow` | | Keep the menu's rows current over native-api `listen`: snapshot frames, a `synced` line, then a line per change, then a summary (NDJSON for json/yaml/ndjson). Single target, list menus whose print takes follow-only. |
|
|
55
|
+
| `--sample` | `<interval>` | Read the menu again every interval (with a unit, e.g. `5s`): one line per complete read, then a summary. With --wait, sets polling cadence and returns one final envelope. Sees counters and state listen never reports. Single target; rest-api or native-api. |
|
|
56
|
+
| `--wait` | `<deadline>` | Wait for a successful read or a condition until this overall deadline, with a unit (e.g. `2m`). Retries refused, closed, reset and timed-out connections and a down or unreachable host; other errors fail at once. One final envelope; single target. |
|
|
57
|
+
| `--until` | `<expr>` | With --wait: succeed when any selected row matches this --query grammar predicate; repeatable, AND-ed. Returns the matching rows. Already true succeeds on the initial read. |
|
|
58
|
+
| `--until-empty` | | With --wait: succeed on a completed read with zero rows selected by --query. Silence and failed reads never satisfy it. |
|
|
59
|
+
| `--sweep` | `<duration>` | `--follow` only: how often a membership sweep finds removals RouterOS does not send, including silent exits from --query. Needs a unit; default `10s`; `0` turns it off. |
|
|
60
|
+
| `--count` | `<n>` | `--follow`: stop after N live changes (snapshot frames and `synced` do not count). `--sample`: stop after N samples. |
|
|
61
|
+
| `--duration` | `<duration>` | `--follow`/`--sample`: stop after this wall-clock window (e.g. `30s`), counted from the end of validation. |
|
|
62
|
+
| `--format` | <text\|json\|yaml\|ndjson> | Output format for the CLI response. Defaults to text; use --json or --format json for the structured envelope. `ndjson` prints each envelope as one compact line. |
|
|
55
63
|
| `--json` | | Shortcut for `--format json`. |
|
|
56
64
|
| `--max-results` | `<bytes>` | Fail instead of printing output larger than the given byte budget. |
|
|
57
65
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
@@ -65,7 +73,7 @@ Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <targe
|
|
|
65
73
|
Run a RouterOS read or write command via native API, REST, or mac-telnet.
|
|
66
74
|
|
|
67
75
|
```text
|
|
68
|
-
Usage: centrs execute <target> <command> [flags] | centrs execute <target...> -- <command> [flags] | centrs execute --group <name> <command> [flags]
|
|
76
|
+
Usage: centrs execute <target> <command> [flags] | centrs execute <target...> -- <command> [flags] | centrs execute --host <host|url> [--] <command> [flags] | centrs execute --group <name> <command> [flags]
|
|
69
77
|
```
|
|
70
78
|
|
|
71
79
|
| Flag | Value | Description |
|
|
@@ -79,7 +87,7 @@ Usage: centrs execute <target> <command> [flags] | centrs execute <target...> --
|
|
|
79
87
|
| `--default` | | Select the reserved `__default__` record. |
|
|
80
88
|
| `--quickchr` | `<name>` | Target a running quickchr-managed CHR VM by name (repeatable; fans out when repeated). Exclusive of positional targets and CDB selectors; conflicts with direct connection overrides (--host/--port/--username/--password, and --ssh-key where the command has it). |
|
|
81
89
|
| `--concurrency` | `<n>` | Max in-flight targets during fan-out (transport-aware default: rest-api 8, native-api 4). |
|
|
82
|
-
| `--host` | <host\|url> |
|
|
90
|
+
| `--host` | <host\|url> | Host or base URL to dial. With no `<target>` it is the target (no CDB lookup); with one it overrides the resolved host. |
|
|
83
91
|
| `--port` | `<port>` | Override the resolved management port. |
|
|
84
92
|
| `--username / --user / -u` | `<name>` | RouterOS username (aliases `--user`, `-u`). Falls back to `CENTRS_USERNAME`. |
|
|
85
93
|
| `--password` | `<secret>` | RouterOS password. Falls back to `CENTRS_PASSWORD`. |
|
|
@@ -102,7 +110,7 @@ Usage: centrs execute <target> <command> [flags] | centrs execute <target...> --
|
|
|
102
110
|
Structured RouterOS API passthrough (gh-api style) over REST or native API.
|
|
103
111
|
|
|
104
112
|
```text
|
|
105
|
-
Usage: centrs api <router> <endpoint> [flags]
|
|
113
|
+
Usage: centrs api <router> <endpoint> [flags] | centrs api --host <host|url> <endpoint> [flags]
|
|
106
114
|
```
|
|
107
115
|
|
|
108
116
|
| Flag | Value | Description |
|
|
@@ -111,13 +119,13 @@ Usage: centrs api <router> <endpoint> [flags]
|
|
|
111
119
|
| `-f / --field` | `<key=value>` | Body field, repeatable; assembled into the JSON body (verbatim string values). |
|
|
112
120
|
| `-d / --data` | `<json>` | Raw JSON request body. Conflicts with `-f` / `--input`. |
|
|
113
121
|
| `--input` | <file\|-> | Read the raw JSON body from a file or stdin (`-`). |
|
|
114
|
-
| `--query / --filter` | `<expr>` |
|
|
115
|
-
| `--raw-query` | `<word>` | Verbatim RouterOS query word (repeatable)
|
|
122
|
+
| `--query / --filter` | `<expr>` | Row filter written as after `print where` (`disabled`, `mtu>=1500 and !dynamic`), sent as query words; repeatable, AND-ed. No `~` or `in`. |
|
|
123
|
+
| `--raw-query` | `<word>` | Verbatim RouterOS query word (repeatable), e.g. has-property `name`, absence `-name`, or stack ops `#\|` `#&` `#!`. |
|
|
116
124
|
| `--attribute / --proplist` | `<a,b>` | Property projection → `.proplist`. |
|
|
117
125
|
| `--raw` | | Strip the envelope; emit bare RouterOS JSON. Defaults `--validate` to false (an explicit `--validate=true` still runs the gate); does not imply `--yes`. |
|
|
118
126
|
| `--yes` | | Confirm a mutating (non-read) request in non-interactive runs. |
|
|
119
|
-
| `--stream
|
|
120
|
-
| `--count` | `<n>` | Stop a `--stream` after N
|
|
127
|
+
| `--stream` | | Stream the command's native replies (`!re` rows and `!empty`) as NDJSON, then a summary envelope. Never changes the command: a GET streams one print. To follow changes, request `<menu>/listen`, which implies it. |
|
|
128
|
+
| `--count` | `<n>` | Stop a `--stream` after N `!re` rows; `!empty` replies do not count (not the device count= argument). |
|
|
121
129
|
| `--duration` | `<dur>` | Stop a `--stream` after this wall-clock window (e.g. `5s`). |
|
|
122
130
|
| `--via` | <rest-api\|native-api> | Pin the transport; no silent downgrade. Default rest-api. |
|
|
123
131
|
| `--group` | `<name>` | Fan out across every CDB record in the group (repeatable; de-duped by record index). |
|
|
@@ -128,7 +136,7 @@ Usage: centrs api <router> <endpoint> [flags]
|
|
|
128
136
|
| `--default` | | Select the reserved `__default__` record. |
|
|
129
137
|
| `--quickchr` | `<name>` | Target a running quickchr-managed CHR VM by name (repeatable; fans out when repeated). Exclusive of positional targets and CDB selectors; conflicts with direct connection overrides (--host/--port/--username/--password, and --ssh-key where the command has it). |
|
|
130
138
|
| `--concurrency` | `<n>` | Max in-flight targets during fan-out (transport-aware default: rest-api 8, native-api 4). |
|
|
131
|
-
| `--host` | <host\|url> |
|
|
139
|
+
| `--host` | <host\|url> | Host or base URL to dial. With no `<router>` it is the target (no CDB lookup); with one it overrides the resolved host. |
|
|
132
140
|
| `--port` | `<port>` | Override the resolved management port. |
|
|
133
141
|
| `--username / --user / -u` | `<name>` | RouterOS username. Falls back to `CENTRS_USERNAME`. |
|
|
134
142
|
| `--password` | `<secret>` | RouterOS password. Falls back to `CENTRS_PASSWORD`. |
|
|
@@ -138,7 +146,7 @@ Usage: centrs api <router> <endpoint> [flags]
|
|
|
138
146
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
139
147
|
| `--timeout` | `<duration>` | Per-request timeout (for REST, max 60s). |
|
|
140
148
|
| `--validate / --no-validate` | | Run `/console/inspect` validation before the request (default true; `--validate=false` also accepted). |
|
|
141
|
-
| `--format` | <json\|yaml\|text> | Output format. Defaults to json for api; `CENTRS_FORMAT` overrides. |
|
|
149
|
+
| `--format` | <json\|ndjson\|yaml\|text> | Output format. Defaults to json for api; `CENTRS_FORMAT` overrides. |
|
|
142
150
|
| `--json` | | Shortcut for `--format json`. |
|
|
143
151
|
| `--verbose` | | Include additional context in text output. |
|
|
144
152
|
|
|
@@ -147,7 +155,7 @@ Usage: centrs api <router> <endpoint> [flags]
|
|
|
147
155
|
Copy files to/from a RouterOS device and manage device files (rest/native/sftp).
|
|
148
156
|
|
|
149
157
|
```text
|
|
150
|
-
Usage: centrs transfer <router> upload <local> [remote] | download <remote> [local] | list [path] | remove <remote> | mkdir <remote> | copy <src> <dst> [flags]
|
|
158
|
+
Usage: centrs transfer <router>|--host <host> upload <local> [remote] | download <remote> [local] | list [path] | remove <remote> | mkdir <remote> | copy <src> <dst> [flags]
|
|
151
159
|
```
|
|
152
160
|
|
|
153
161
|
| Flag | Value | Description |
|
|
@@ -155,7 +163,7 @@ Usage: centrs transfer <router> upload <local> [remote] | download <remote> [loc
|
|
|
155
163
|
| `--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. |
|
|
156
164
|
| `--ssh-key` | `<path>` | sftp only: explicit private-key path. Falls back to CENTRS_SSH_KEY / the ssh-agent. |
|
|
157
165
|
| `--insecure` | | Accept a self-signed TLS cert (https/api-ssl) or a new SSH host key. Default verifies. |
|
|
158
|
-
| `--force / --overwrite` | |
|
|
166
|
+
| `--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. |
|
|
159
167
|
| `--group` | `<name>` | Fan out across every CDB record in the group (repeatable; de-duped by record index). |
|
|
160
168
|
| `--where` | `<attr>`=`<value>` | Device-class selector over CDB facts + core fields (repeatable, AND-combined). |
|
|
161
169
|
| `--near` | `<lat>`,`<lon>`,`<radius>` | Geo selector: devices whose GPS is within radius (m/km/mi/ft; bare number = km). Lat-first. |
|
|
@@ -169,7 +177,7 @@ Usage: centrs transfer <router> upload <local> [remote] | download <remote> [loc
|
|
|
169
177
|
| `--verify / --no-verify` | <size\|checksum\|off> | Post-transfer integrity check. Default size; `--no-verify` skips. |
|
|
170
178
|
| `--type` | <file\|directory\|disk\|package> | list filter: RouterOS /file row type. |
|
|
171
179
|
| `--name` | `<glob>` | list filter: file-name glob. |
|
|
172
|
-
| `--host` | <host\|url> |
|
|
180
|
+
| `--host` | <host\|url> | Host or base URL to dial. With no `<router>` before the verb it is the target (no CDB lookup); with one it overrides the resolved host. |
|
|
173
181
|
| `--port` | `<port>` | Override the resolved management port. |
|
|
174
182
|
| `--username / --user / -u` | `<name>` | RouterOS username (aliases --user, -u). Falls back to CENTRS_USERNAME. |
|
|
175
183
|
| `--password` | `<secret>` | RouterOS password. Falls back to CENTRS_PASSWORD. |
|
|
@@ -187,13 +195,13 @@ Usage: centrs transfer <router> upload <local> [remote] | download <remote> [loc
|
|
|
187
195
|
Open an interactive RouterOS console (ssh or mac-telnet).
|
|
188
196
|
|
|
189
197
|
```text
|
|
190
|
-
Usage: centrs terminal <router> [flags]
|
|
198
|
+
Usage: centrs terminal <router> [flags] | centrs terminal --host <host> [flags]
|
|
191
199
|
```
|
|
192
200
|
|
|
193
201
|
| Flag | Value | Description |
|
|
194
202
|
| ---- | ----- | ----------- |
|
|
195
203
|
| `--via` | `<method>` | Transport: `ssh` (host target, default) or `mac-telnet` (MAC target, default for a MAC). rest/native have no terminal capability. |
|
|
196
|
-
| `--host` | `<host>` |
|
|
204
|
+
| `--host` | `<host>` | ssh: the host to dial (the target when no `<router>` is given; no CDB lookup). mac-telnet: the UDP delivery host (default L2 broadcast). |
|
|
197
205
|
| `--port` | `<port>` | Override the port: ssh default 22, mac-telnet default 20561. |
|
|
198
206
|
| `--username / --user / -u` | `<name>` | RouterOS username (aliases --user, -u). Falls back to CENTRS_USERNAME. |
|
|
199
207
|
| `--password` | `<secret>` | RouterOS password. Falls back to CENTRS_PASSWORD. |
|
package/docs/CONSTITUTION.md
CHANGED
|
@@ -63,6 +63,22 @@ an ABSTENTION and passes through to the device stage; only a `fail` rejects.
|
|
|
63
63
|
that was skipped and why. A reader must always be able to tell "the device
|
|
64
64
|
accepted it" from "the device was never asked".
|
|
65
65
|
|
|
66
|
+
`meta.validation.result` is a verdict, so it is present only when the gate
|
|
67
|
+
produced one. `failed` means the gate refused the request. A failure after the
|
|
68
|
+
gate passed keeps `passed`. When something stopped the gate before it gave a
|
|
69
|
+
verdict (a `transport/*` or `auth/*` failure, a device error that is not a
|
|
70
|
+
gate answer, or a refusal before the gate ran), `result` and the
|
|
71
|
+
`syntax`/`semantic` summaries are absent. Each stage the gate did not reach is
|
|
72
|
+
then `skipped` with `reason: "not reached: <error.code>"` (GH#435). On an
|
|
73
|
+
`execute` rejection, `syntax: true` survives only for a name-level refusal (an
|
|
74
|
+
unknown attribute in a structured command). Any other device or offline
|
|
75
|
+
refusal is `syntax: false`, and `semantic` is then absent (`not-applicable` for
|
|
76
|
+
script input), because the name-level check was never asked. `api`'s
|
|
77
|
+
structured requests run no `:parse`, so their `syntax: false` means "not run",
|
|
78
|
+
even on success. That overload is part of the review below. This is an
|
|
79
|
+
interim shape: the validation meta across commands is reviewed again with live
|
|
80
|
+
`explain` (#236).
|
|
81
|
+
|
|
66
82
|
`--validate=false` disables **both** stages — one flag, one meaning. A bypass
|
|
67
83
|
that covered only the device stage would leave a caller stranded behind a
|
|
68
84
|
stage-1 defect with no escape at all, and the analyzer is demonstrably wrong in
|
|
@@ -278,7 +294,10 @@ token cannot spoof them), AND-combined across repeats. A call is in **fan-out
|
|
|
278
294
|
mode** when any selector flag is present **or** more than one positional target is
|
|
279
295
|
given; a plain single-positional call stays single-target. The union is
|
|
280
296
|
**de-duped by CDB record index** (ad-hoc literal targets by host) and run through
|
|
281
|
-
the same per-target pipeline.
|
|
297
|
+
the same per-target pipeline. With no `<router>` positional and no selector, the
|
|
298
|
+
`--host` **flag** stands in for the target: a single target with no CDB lookup,
|
|
299
|
+
so no record credential applies. The env var `CENTRS_HOST` never changes how
|
|
300
|
+
positionals split. Each command README states where its `<router>` slot ends.
|
|
282
301
|
|
|
283
302
|
Fan-out output is the locked **`FanoutData`** envelope: `data = { summary, targets[]
|
|
284
303
|
}`, where the outer `ok` reports **orchestration** success and a per-target failure
|
|
@@ -292,9 +311,9 @@ A multi-target **RouterOS write** (e.g. `execute` / `api` add/set/remove across
|
|
|
292
311
|
selection) is gated by **`--yes`**, confirmed **once** up front; when it is missing
|
|
293
312
|
the error names the blast radius (how many routers). The separate **`--force`**
|
|
294
313
|
gate is scoped to destructive **`devices` CDB mutations** (e.g. `devices remove`
|
|
295
|
-
across a group), not RouterOS writes. `terminal`
|
|
296
|
-
`
|
|
297
|
-
`usage/fanout-not-supported`; `api --raw` is rejected in fan-out mode with
|
|
314
|
+
across a group), not RouterOS writes. `terminal`, `api --stream`,
|
|
315
|
+
`retrieve --follow` and `retrieve --sample` are single-session, not fan-out
|
|
316
|
+
surfaces, and reject N>1 with `usage/fanout-not-supported`; `api --raw` is rejected in fan-out mode with
|
|
298
317
|
`usage/conflicting-flags` (it strips the per-target envelope).
|
|
299
318
|
|
|
300
319
|
`--where` filters *which devices* by CDB-stored facts; keep it distinct from
|
|
@@ -372,8 +391,8 @@ Per-operation preferences, downgrade order in parens:
|
|
|
372
391
|
|
|
373
392
|
| Operation | Preferred | Downgrade order |
|
|
374
393
|
| --------- | --------- | --------------- |
|
|
375
|
-
| retrieve | rest-api, native-api; snmp for OID/MIB reads (future) | rest-api, native-api |
|
|
376
|
-
| api | rest-api, native-api; `--stream`
|
|
394
|
+
| retrieve | rest-api, native-api (`--sample` too); snmp for OID/MIB reads (future); `--follow` native-api only | rest-api, native-api (`--follow`: no downgrade) |
|
|
395
|
+
| api | rest-api, native-api; `--stream` and `/listen` native-api only | rest-api, native-api (no silent downgrade) |
|
|
377
396
|
| execute | native-api → rest-api → mac-telnet | native-api, rest-api, mac-telnet, ssh, romon, winbox-terminal |
|
|
378
397
|
| terminal | ssh | mac-telnet (L2 only when ssh fails or MAC given) |
|
|
379
398
|
| transfer | size/direction-aware: rest-api/native-api for ≤60 KB writes & all reads (chunked), sftp for large uploads | sftp ⇄ rest/native by size; scp · fetch · ftp explicit-only |
|
|
@@ -400,7 +419,7 @@ Rules:
|
|
|
400
419
|
- **`measure` (btest) is explicit-only** — never in the auto-select/downgrade
|
|
401
420
|
chain, never a substitute, and no other command rides it.
|
|
402
421
|
- Per-command selection **depth** lives in the command READMEs: `api`'s
|
|
403
|
-
`--stream
|
|
422
|
+
literal `--stream`/NDJSON contract and the REST-cannot-follow constraint
|
|
404
423
|
(`commands/api/README.md`), `transfer`'s size/direction-aware method
|
|
405
424
|
selection and the explicit-only `scp`/`ftp`/`fetch`
|
|
406
425
|
(`commands/transfer/README.md`), SNMP's MIB-cache
|
|
@@ -432,9 +451,10 @@ ecosystem norm* rather than one literal value:
|
|
|
432
451
|
|
|
433
452
|
SSH identity selection: the `sshKey` setting (`--ssh-key`/`CENTRS_SSH_KEY`/CDB
|
|
434
453
|
`ssh-key=`) carries a private-key **path only** — never key material; when unset,
|
|
435
|
-
the host SSH agent / `~/.ssh/config` is used.
|
|
436
|
-
|
|
437
|
-
|
|
454
|
+
the host SSH agent / `~/.ssh/config` is used. Batch SSH clients (sftp, `execute
|
|
455
|
+
--via ssh`) run `BatchMode=yes` and never send a password, so key auth is the
|
|
456
|
+
only batch path. Separately, RouterOS refuses SSH password login once a user has
|
|
457
|
+
a key (`/ip/ssh password-authentication=yes-if-no-key`, the default).
|
|
438
458
|
|
|
439
459
|
## Done definition
|
|
440
460
|
|
package/docs/MATRIX.md
CHANGED
|
@@ -60,6 +60,33 @@ and implementation sequencing in the linked issues.
|
|
|
60
60
|
structured operation surface is `api`.
|
|
61
61
|
- `api --stream` absorbs the former `stream`/`tail` surface; native-api stream
|
|
62
62
|
details are in `commands/api/README.md` and `src/protocols/native-api.ts`.
|
|
63
|
+
#399 extends it to incremental command replies and truthful terminal outcomes.
|
|
64
|
+
#402 makes it literal: following is the `<menu>/listen` endpoint, every wire
|
|
65
|
+
reply (`!empty` included) is a line, and what RouterOS will not report
|
|
66
|
+
(filtered or projected change feeds) is a tip, not a rewrite.
|
|
67
|
+
- `retrieve --follow` (#396) is the stateful follow over native-api: A1
|
|
68
|
+
bootstrap, `synced`, live frames, and an `.id` sweep for removals RouterOS
|
|
69
|
+
does not send. Single-target; `CHR-passed` by
|
|
70
|
+
`test/integration/retrieve-follow.test.ts` (FL1–FL9, 7.23.7 and 7.24.5).
|
|
71
|
+
Filtered follow (#397) uses device-evaluated changed-id membership, guarded
|
|
72
|
+
against stale replies; `test/integration/retrieve-filtered-follow.test.ts`
|
|
73
|
+
covers FQ1–FQ5 on 7.23.8 and 7.24.5, including projected predicates, both membership directions,
|
|
74
|
+
minimal deletes, silent predicate exits and bootstrap/live churn.
|
|
75
|
+
- `retrieve --sample <interval>` repeats the one-shot read on a timer, one
|
|
76
|
+
line per complete read, over rest-api or native-api: the answer for counters
|
|
77
|
+
and state `listen` never reports. RouterOS's `print interval=` was measured
|
|
78
|
+
and not used (no end-of-tick marker). Single-target; `CHR-passed` by
|
|
79
|
+
`test/integration/retrieve-sample.test.ts` (SA1–SA6, 7.23.7 and 7.24.5).
|
|
80
|
+
- `retrieve --query` (#397) filters rows on the router with the `print where`
|
|
81
|
+
grammar, compiled to the API query; `api --query` shares it. One-shot,
|
|
82
|
+
`--sample` and fan-out; `CHR-passed` by
|
|
83
|
+
`test/integration/retrieve-query.test.ts` (QY1–QY7, QA1–QA2, 7.23.7 and
|
|
84
|
+
7.24.5), whose QY1 checks the rows against RouterOS's own `find where`.
|
|
85
|
+
- `retrieve --wait` / `--until` / `--until-empty` (#364) return one bounded
|
|
86
|
+
readiness/condition result over REST or native-api, with elapsed time,
|
|
87
|
+
attempts and completed observations. `test/integration/retrieve-wait.test.ts`
|
|
88
|
+
covers WT1–WT7 on 7.23.8 and 7.24.5, including real reboot readiness and a
|
|
89
|
+
two-CHR OSPF adjacency.
|
|
63
90
|
- `discover / mndp` is grounded by `commands/discover/README.md`,
|
|
64
91
|
`src/data/mndp.ts`, `src/data/mndp-cache.ts`, and
|
|
65
92
|
`test/integration/discover.test.ts`.
|
|
@@ -150,7 +177,17 @@ Wire, auth, TCP/UDP, and CI caveats live in `commands/btest/README.md` and the
|
|
|
150
177
|
|
|
151
178
|
## Current priority
|
|
152
179
|
|
|
153
|
-
**
|
|
180
|
+
**First: the native-api / NDJSON line** (maintainer decision on
|
|
181
|
+
[#396](https://github.com/tikoci/centrs/issues/396), 2026-10-07): agents in the
|
|
182
|
+
field poll with `sleep` where a native follow would answer. In order:
|
|
183
|
+
(1) done: #402, literal `api --stream` with `!empty` frames and
|
|
184
|
+
`--format ndjson`; (2) done: `retrieve --follow` (#396, unfiltered);
|
|
185
|
+
(3) done: `retrieve --sample`; (4) #397: one-shot `retrieve --query` done,
|
|
186
|
+
filtered `--follow` done; #364 (`--wait`/`--until`, same grammar) done;
|
|
187
|
+
(5) #398 and #401. Each slice's contract lives in its command
|
|
188
|
+
README. Live `explain` (below) resumes after this line.
|
|
189
|
+
|
|
190
|
+
**Tightening validation as a whole** is the next track, in the three-step
|
|
154
191
|
order agreed on [#236](https://github.com/tikoci/centrs/issues/236):
|
|
155
192
|
|
|
156
193
|
1. **Done** — fix the offline false rejects so the analyzer can be trusted as a
|
package/docs/errors/README.md
CHANGED
|
@@ -59,10 +59,12 @@ Codes are slash-namespaced `family/slug`. The families and where they originate:
|
|
|
59
59
|
- `transport/*` — connection, TLS, DNS, and timeout plumbing.
|
|
60
60
|
- `usage/*` — how a command was invoked (arity, conflicting flags, confirmation).
|
|
61
61
|
- `validation/*` — the canonicalize → validate gate (`:parse` + `/console/inspect`).
|
|
62
|
+
- `wait/*` — readiness/condition deadlines and cancellation (`retrieveWait`).
|
|
62
63
|
|
|
63
64
|
The hand-enriched pages (richer trigger strings and remediation) currently are
|
|
64
65
|
the `routeros/*` set produced by `src/core/routeros-errors.ts` and the
|
|
65
|
-
`target/*` MAC-resolution pages
|
|
66
|
+
`target/*` MAC-resolution pages and `wait/*` deadline/cancellation pages;
|
|
67
|
+
the rest are generated stubs to be enriched as
|
|
66
68
|
each code is grounded.
|
|
67
69
|
|
|
68
70
|
## RouterOS-fault mapping (`routeros/*`)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# `input/invalid-query`
|
|
2
|
+
|
|
3
|
+
A `--query` expression does not parse as a `print where` condition.
|
|
4
|
+
|
|
5
|
+
## When it happens
|
|
6
|
+
|
|
7
|
+
`retrieve --query` and `api --query` take what you would write after
|
|
8
|
+
`print where`: `name=ether1`, `mtu>=1500 and !dynamic`,
|
|
9
|
+
`(type=ether or type=vlan)`. centrs parses it before anything is sent, then
|
|
10
|
+
runs it on the router as an API query. The error's `context.offset` points at
|
|
11
|
+
the problem in `context.query`.
|
|
12
|
+
|
|
13
|
+
centrs also refuses spellings that RouterOS reads differently from how they
|
|
14
|
+
look, or does not accept:
|
|
15
|
+
|
|
16
|
+
- `!name=value` is `(!name)=value` to RouterOS, which matches by accident.
|
|
17
|
+
Write `!(name=value)` or `name!=value`.
|
|
18
|
+
- `not` is not a RouterOS operator. Use `!`.
|
|
19
|
+
- Conditions separated only by a space. Join them with `and` or `or`.
|
|
20
|
+
- An unquoted `=` inside a value. RouterOS reads `comment=x=yes` as
|
|
21
|
+
`(comment=x)=yes`, and `comment=x!=y` matches nothing. Quote it:
|
|
22
|
+
`comment="x=yes"`.
|
|
23
|
+
- A space after the operator (`comment= x`), which RouterOS rejects too.
|
|
24
|
+
- A string escape RouterOS does not have, such as lowercase hex `\0a` (hex is
|
|
25
|
+
two uppercase digits; `\ff` is `\f` then `f`), or a hex byte above `\7F`,
|
|
26
|
+
which a UTF-8 query value cannot carry.
|
|
27
|
+
|
|
28
|
+
A bare name (`disabled`, `comment`) means what it means after `where`: `yes`
|
|
29
|
+
for a boolean and "is set" for anything else. Only the router knows which a
|
|
30
|
+
property is, so with `--validate=false` a bare name fails here too.
|
|
31
|
+
|
|
32
|
+
## Fix
|
|
33
|
+
|
|
34
|
+
- Quote values that contain spaces: `comment="uplink a"`.
|
|
35
|
+
- Close every `(` and `"`.
|
|
36
|
+
- Write a boolean out as `disabled=yes` if validation is off.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# `input/local-destination`
|
|
2
|
+
|
|
3
|
+
A local download destination cannot be written (missing parent directory, a directory, or no permission).
|
|
4
|
+
|
|
5
|
+
`transfer download` checks the local destination before it reads anything
|
|
6
|
+
from the device. The parent directory must already exist, because centrs does
|
|
7
|
+
not create directories, and the destination itself must not be a directory.
|
|
8
|
+
A path that cannot be checked at all (no permission on a directory along the
|
|
9
|
+
way, a file used as a directory, a symlink loop) fails the same way. A write
|
|
10
|
+
that still fails afterwards (no permission, the directory removed
|
|
11
|
+
mid-transfer) reports this code too, with the operating-system error as the
|
|
12
|
+
cause.
|
|
13
|
+
|
|
14
|
+
## Fix
|
|
15
|
+
|
|
16
|
+
- Create the parent directory first: `mkdir -p DIR`, then retry the download.
|
|
17
|
+
- Name a file, not a directory: `download fw.rsc ./backups/fw.rsc`, not
|
|
18
|
+
`download fw.rsc ./backups/`.
|
|
19
|
+
- Pass `-` as the local path to write the bytes to stdout instead.
|
|
20
|
+
- For a fan-out download, `--out-dir` must be an existing directory.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# `input/unsupported-query`
|
|
2
|
+
|
|
3
|
+
A `--query` uses something the RouterOS API query cannot run.
|
|
4
|
+
|
|
5
|
+
## When it happens
|
|
6
|
+
|
|
7
|
+
`--query` runs on the router as an API query (native `?` words, REST
|
|
8
|
+
`.query`), and that query language has no regex (`~`), no prefix membership
|
|
9
|
+
(`in`), and no script evaluation. A value starting with `$`, `[` or `{`, or a
|
|
10
|
+
`$` inside a quoted value, would need RouterOS's script evaluator. centrs
|
|
11
|
+
refuses these rather than filtering some other way and calling it a router
|
|
12
|
+
filter.
|
|
13
|
+
|
|
14
|
+
## Fix
|
|
15
|
+
|
|
16
|
+
- Read the rows without that condition and filter them yourself.
|
|
17
|
+
- Or run the console form, which returns console text rather than rows:
|
|
18
|
+
`centrs execute <router> '/ip/address/print where interface~"^ether"'`.
|
|
19
|
+
- For a literal `$` in a quoted value, escape it as `\$`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# `internal/byte-string`
|
|
2
|
+
|
|
3
|
+
A binary RouterOS word held a character that is not a byte.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
This is a centrs bug; report it with the command that triggered it.
|
|
8
|
+
|
|
9
|
+
`transfer` moves `/file` contents over REST and native-api as byte strings,
|
|
10
|
+
one string code unit per byte (#404). A code unit above U+00FF has no single
|
|
11
|
+
byte, so centrs refuses the request before it reaches the router rather than
|
|
12
|
+
silently keeping the low byte. See
|
|
13
|
+
[`docs/CONSTITUTION.md`](../../CONSTITUTION.md) for the centrs error contract.
|
|
@@ -18,6 +18,12 @@ Like the other typed-value codes here, this isn't `settings`-exclusive:
|
|
|
18
18
|
flag, so the same failure shape shows up anywhere a duration string is
|
|
19
19
|
resolved, not just through `settings set`.
|
|
20
20
|
|
|
21
|
+
`retrieve --sample <interval>` and `retrieve --follow --sweep <period>` are
|
|
22
|
+
stricter: the value needs a unit (`5s`, `500ms`, `1m`). `--sample` must be
|
|
23
|
+
above zero; `--sweep 0` is allowed and turns the sweep off. A bare `--sample 5` fails here
|
|
24
|
+
because RouterOS reads `interval=5` as five seconds while centrs reads a bare
|
|
25
|
+
number as milliseconds; pass the unit you mean.
|
|
26
|
+
|
|
21
27
|
One write detail worth knowing: `settings set timeout` writes the
|
|
22
28
|
**canonical parsed milliseconds**, not the raw suffixed string you typed —
|
|
23
29
|
`centrs settings set timeout 5s` writes `CENTRS_TIMEOUT=5000` to
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# `transport/stream-overflow`
|
|
2
|
+
|
|
3
|
+
More stream replies were waiting unread than the buffer allows, so centrs ended
|
|
4
|
+
the stream rather than drop any.
|
|
5
|
+
|
|
6
|
+
## When it happens
|
|
7
|
+
|
|
8
|
+
`centrs retrieve <router> <menu> --follow` buffers changes between RouterOS and
|
|
9
|
+
the reader, up to 50,000. The buffer fills when the reader stops reading (a
|
|
10
|
+
stalled pipe), or when a burst arrives while a long snapshot is still loading.
|
|
11
|
+
centrs never drops a change silently, because the state you hold would then
|
|
12
|
+
look current while being wrong. It ends the follow with this error instead, as
|
|
13
|
+
the failed summary (`stopReason: "transport-error"`).
|
|
14
|
+
|
|
15
|
+
## Fix
|
|
16
|
+
|
|
17
|
+
- The state you built from the lines is stale. Follow again: the new follow
|
|
18
|
+
starts with a fresh snapshot.
|
|
19
|
+
- Read lines as they arrive. Don't buffer the whole output before processing.
|
|
20
|
+
- For a very busy menu, follow a narrower menu (for example `/interface/vlan`
|
|
21
|
+
instead of `/interface`).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# `usage/removed-flag`
|
|
2
|
+
|
|
3
|
+
A flag or request field was removed; the error names its replacement.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
Run the replacement printed in the error's remediation.
|
|
8
|
+
|
|
9
|
+
`api --listen` (and the library's `ApiRequest.listen`) is the current case
|
|
10
|
+
(#402). It meant "stream the replies" and also turned a GET of a menu into a
|
|
11
|
+
change subscription. `api` now sends the command as typed:
|
|
12
|
+
|
|
13
|
+
- To follow changes, request the menu's `listen`:
|
|
14
|
+
`centrs api <router> ip/address/listen`. A `/listen` endpoint implies
|
|
15
|
+
`--stream` and native-api.
|
|
16
|
+
- To stream any other command's replies, use `--stream`
|
|
17
|
+
(`ApiRequest.stream` / `apiStream()` in the library).
|
|
18
|
+
|
|
19
|
+
See [`docs/CONSTITUTION.md`](../../CONSTITUTION.md) for the centrs error
|
|
20
|
+
contract.
|