@tikoci/centrs 0.1.0 → 0.1.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 +73 -2
- package/GLOSSARY.txt +16 -0
- package/README.md +1 -1
- package/commands/AGENTS.md +3 -2
- package/commands/api/AGENTS.md +212 -0
- package/commands/api/README.md +159 -0
- package/commands/api/examples.md +325 -0
- package/commands/btest/README.md +56 -31
- package/commands/btest/examples.md +35 -10
- package/commands/devices/README.md +26 -0
- package/commands/discover/README.md +17 -8
- package/commands/discover/examples.md +10 -5
- package/commands/execute/README.md +9 -3
- package/commands/retrieve/README.md +1 -1
- package/docs/CONSTITUTION.md +6 -6
- package/docs/MATRIX.md +46 -15
- package/docs/errors/routeros/btest-connection-count-single-stream.md +22 -0
- package/docs/errors/routeros/btest-udp-tx-size-ignored.md +15 -0
- package/docs/errors/usage/invalid-method.md +23 -0
- package/package.json +5 -5
- package/src/api.ts +1555 -0
- package/src/btest.ts +61 -1
- package/src/cli/api.ts +536 -0
- package/src/cli/btest.ts +45 -1
- package/src/cli/common.ts +64 -0
- package/src/cli/discover.ts +26 -0
- package/src/cli/execute.ts +30 -4
- package/src/cli/missing-target.ts +171 -0
- package/src/cli/retrieve.ts +32 -14
- package/src/cli/terminal.ts +18 -4
- package/src/cli/transfer.ts +37 -9
- package/src/cli.ts +22 -0
- package/src/core/error-catalog.ts +14 -0
- package/src/core/fanout.ts +160 -0
- package/src/core/inspect.ts +170 -0
- package/src/discover.ts +96 -23
- package/src/execute.ts +14 -47
- package/src/index.ts +61 -8
- package/src/protocols/adapter.ts +328 -0
- package/src/protocols/btest-session.ts +279 -46
- package/src/protocols/btest.ts +6 -2
- package/src/protocols/native-api.ts +223 -30
- package/src/retrieve-fanout.ts +13 -141
- package/src/retrieve.ts +30 -87
- package/commands/stream/README.md +0 -70
|
@@ -52,6 +52,12 @@ Status: `rest-api`, `native-api`, `mac-telnet`, and `ssh` are `CHR-passed` (see
|
|
|
52
52
|
else passes its `detail`/body plus the HTTP status through
|
|
53
53
|
`mapRouterOsError({ transport: "rest-api", httpStatus })`. There is no longer
|
|
54
54
|
a hand-rolled `routeros/*` table on the REST side.
|
|
55
|
+
- **Run-time fault mapping — later (JG-28).** `mapRouterOsError`'s table covers
|
|
56
|
+
the faults seen so far; a known gap is errors that *pass* `:parse`/validation
|
|
57
|
+
but fail at **run time** (a syntactically valid command the router rejects when
|
|
58
|
+
executed). `tikoci/vscode-tikbook` has an error-string mapping for these worth
|
|
59
|
+
porting into `src/core/routeros-errors.ts`. Background research, not yet
|
|
60
|
+
scheduled — captured here so it isn't lost (no GitHub issue by design).
|
|
55
61
|
- Output is *string*-shaped for script-mode; richer parsing is a future
|
|
56
62
|
concern. The envelope must still distinguish RouterOS errors from successful
|
|
57
63
|
runs (a 200 with a RouterOS error string is still an error — see
|
|
@@ -195,9 +201,9 @@ Protocol selection: an unresolved MAC target auto-selects mac-telnet (covered by
|
|
|
195
201
|
## Open shape questions
|
|
196
202
|
|
|
197
203
|
- ~~How to expose multi-line / async / progress output~~ — **resolved:**
|
|
198
|
-
open-ended follow/streaming reads are
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
open-ended follow/streaming reads are `api … --stream` (NDJSON stream of
|
|
205
|
+
envelopes; see `commands/api/README.md`). `execute` stays single-shot
|
|
206
|
+
read/write; it does not grow a follow mode.
|
|
201
207
|
- ~~Whether `--strict` should reject "succeeded with stderr-like content."~~ —
|
|
202
208
|
**decided:** `--strict` promotes any warning on an otherwise-successful run to
|
|
203
209
|
`ok: false` (nonzero exit), like `-Werror`; the default stays lenient
|
|
@@ -45,7 +45,7 @@ centrs retrieve <router> snmp <oid|MIB name> [flags]
|
|
|
45
45
|
| `--attributes <a,b,c>` | Alias for `--attribute` with comma-separated input. |
|
|
46
46
|
| `--all-attributes` | Equivalent to RouterOS `details=true`. Mutually exclusive with `--attribute(s)`. |
|
|
47
47
|
| `--list-attributes` (alias `--list`) | Return the available attribute names for the path. No `print`/`get` is run. |
|
|
48
|
-
| `--once` | Bounded single read of a monitor-style menu (RouterOS `once`): returns **one** envelope and never follows. Open-ended follow is
|
|
48
|
+
| `--once` | Bounded single read of a monitor-style menu (RouterOS `once`): returns **one** envelope and never follows. Open-ended follow is `api … --stream`. See constitution: protocol selection. |
|
|
49
49
|
| `--query <expr>` | RouterOS-side **row** filter (maps to `.query`), repeatable; returns matching rows as an **array**. This is how you read one row by name (`--query name=ether1`). **Not Implemented** yet — returns `validation/not-implemented`. |
|
|
50
50
|
| `--filter <expr>` | RouterOS row filter; same `.query` mapping and not-implemented handling as `--query`. |
|
|
51
51
|
| `--where <attr>=<value>` | Device-class selector: fan out across CDB records whose stored fact/comment-kv matches (e.g. `--where board=RB5009`). Repeatable (AND). Filters *which devices*, not RouterOS rows. See constitution: target selection. |
|
package/docs/CONSTITUTION.md
CHANGED
|
@@ -228,8 +228,8 @@ repeats. The union is **de-duped by CDB record index** and run through the same
|
|
|
228
228
|
per-target pipeline; the `data` array is reassembled in resolved record order,
|
|
229
229
|
not completion order, so repeated runs diff cleanly. Destructive multi-target
|
|
230
230
|
operations (e.g. `devices remove` across a group) require `--force`. `terminal`
|
|
231
|
-
and single-session `stream` are not fan-out surfaces and reject
|
|
232
|
-
`usage/fanout-not-supported`.
|
|
231
|
+
and a single-session `api --stream` follow are not fan-out surfaces and reject
|
|
232
|
+
N>1 with `usage/fanout-not-supported`.
|
|
233
233
|
|
|
234
234
|
`--where` filters *which devices* by CDB-stored facts; keep it distinct from
|
|
235
235
|
`retrieve`/`execute`'s `--query`/`--filter`, which filter *RouterOS rows* in the
|
|
@@ -266,7 +266,7 @@ Per-operation preferences, downgrade order in parens:
|
|
|
266
266
|
| Operation | Preferred | Downgrade order |
|
|
267
267
|
| --------- | --------- | --------------- |
|
|
268
268
|
| retrieve | rest-api, native-api; snmp for OID/MIB reads (future) | rest-api, native-api |
|
|
269
|
-
|
|
|
269
|
+
| api | rest-api, native-api; `--stream` follow native-api only | rest-api, native-api (no silent downgrade) |
|
|
270
270
|
| execute | native-api → rest-api → mac-telnet | native-api, rest-api, mac-telnet, ssh, romon, winbox-terminal |
|
|
271
271
|
| terminal | ssh | mac-telnet (L2 only when ssh fails or MAC given) |
|
|
272
272
|
| 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 |
|
|
@@ -288,9 +288,9 @@ Rules:
|
|
|
288
288
|
access needs explicit ARP opt-in.
|
|
289
289
|
- **`measure` (btest) is explicit-only** — never in the auto-select/downgrade
|
|
290
290
|
chain, never a substitute, and no other command rides it.
|
|
291
|
-
- Per-command selection **depth** lives in the command READMEs: `
|
|
292
|
-
follow/NDJSON contract and the REST-cannot-follow constraint
|
|
293
|
-
(`commands/
|
|
291
|
+
- Per-command selection **depth** lives in the command READMEs: `api`'s
|
|
292
|
+
`--stream` follow/NDJSON contract and the REST-cannot-follow constraint
|
|
293
|
+
(`commands/api/README.md`), `transfer`'s size/direction-aware method
|
|
294
294
|
selection and the explicit-only `scp`/`ftp`/`fetch`
|
|
295
295
|
(`commands/transfer/README.md`), SNMP's MIB-cache
|
|
296
296
|
(`commands/retrieve/README.md`), and btest's port/auth
|
package/docs/MATRIX.md
CHANGED
|
@@ -24,7 +24,7 @@ A cell advances only with the matching evidence in the same change.
|
|
|
24
24
|
| Command | rest-api | native-api | ssh | mac-telnet | snmp | mndp | romon | winbox-terminal |
|
|
25
25
|
| -------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ---------------- |
|
|
26
26
|
| retrieve | `CHR-passed` | `CHR-passed` | — | — | `not-started` | — | — | — |
|
|
27
|
-
|
|
|
27
|
+
| api | `CHR-passed` | `CHR-passed` | — | — | — | — | — | — |
|
|
28
28
|
| execute | `CHR-passed` | `CHR-passed` | `CHR-passed` | `CHR-passed` | — | — | `not-started` | `not-started` |
|
|
29
29
|
| terminal | — | — | `CHR-passed` | `CHR-passed` | — | — | — | — |
|
|
30
30
|
| transfer | `CHR-passed` | `CHR-passed` | `CHR-passed` | — | — | — | — | — |
|
|
@@ -56,13 +56,26 @@ verbose REST `board-name`), and the L2 validation policy are documented in
|
|
|
56
56
|
`commands/discover/README.md`.
|
|
57
57
|
|
|
58
58
|
There is no `update` command: `execute` is the single read/write surface for
|
|
59
|
-
RouterOS add/set/remove, and `retrieve` stays read-only.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
RouterOS *CLI-shaped* add/set/remove, and `retrieve` stays read-only. The `api`
|
|
60
|
+
command is the structured one-command-per-operation surface (the verb trichotomy
|
|
61
|
+
in `commands/api/README.md`); it can also write. See `docs/CONSTITUTION.md`
|
|
62
|
+
(protocol selection).
|
|
63
|
+
|
|
64
|
+
`api` is `CHR-passed` for `rest-api` and `native-api` (single-target): endpoint
|
|
65
|
+
normalization, the method→verb map, the `/console/inspect` gate, write
|
|
66
|
+
confirmation, `.query`/`.proplist`, `-f`/`-d`/`--input` bodies, `--raw`,
|
|
67
|
+
`/execute` script-run (`=as-string=` for sync output on both transports), and
|
|
68
|
+
open-ended `--stream` follow (native-api only — `NativeApiSession.listen()` +
|
|
69
|
+
`/cancel`, NDJSON envelope frames with a terminating summary; `--via rest-api
|
|
70
|
+
--stream` errors `transport/capability-unsupported`), green on CHR 7.23.1 via
|
|
71
|
+
`test/integration/api.test.ts` + `api-native.test.ts` + `api-listen.test.ts`.
|
|
72
|
+
Still deferred (own phase): multi-target fan-out (`--stream` is single-session).
|
|
73
|
+
|
|
74
|
+
`api` **absorbs** the former `stream` command (folded — no separate row): the
|
|
75
|
+
read-only follow surface is `api <router> <endpoint> --stream` (alias `--listen`;
|
|
76
|
+
the `/listen` endpoint form infers it), native-api only, since REST's 60s cap
|
|
77
|
+
cannot follow. `centrs stream`/`centrs tail` now error with a pointer to
|
|
78
|
+
`api --stream`.
|
|
66
79
|
|
|
67
80
|
`transfer` is `CHR-passed` for `rest-api`/`native-api` and for **sftp** (the
|
|
68
81
|
`ssh` column's first method): `src/transfer.ts` + `src/cli/transfer.ts`
|
|
@@ -156,7 +169,7 @@ core and carry their own state, tracked here:
|
|
|
156
169
|
|
|
157
170
|
| Surface | State | Spec | Notes |
|
|
158
171
|
| ------- | ----- | ---- | ----- |
|
|
159
|
-
| api
|
|
172
|
+
| ts-api | `CHR-passed` | `src/index.ts` | Root **TypeScript library** surface; everything else adapts it. (Renamed from `api (TS)` to avoid colliding with the `api` **command** row in the grid above — the command is a verb, this is the library.) |
|
|
160
173
|
| cli | `coded` | `src/cli/` | `retrieve`/`execute`/`transfer`/`terminal`/`devices`/`discover`/`btest` wired. |
|
|
161
174
|
| mcp | `CHR-passed` | `commands/mcp/` | Scoped-verb stdio MCP server; CDB-as-allowlist safety model. Phase 1 (explain/validate/retrieve/execute, resources) plus first Phase 2 CDB mutation (`centrs_devices add`) green on CHR 7.23 via `test/integration/mcp.test.ts`, including gated write execution. |
|
|
162
175
|
| tui | `not-started` | `src/tui.ts` | Stub. |
|
|
@@ -186,12 +199,30 @@ The wire codec, the EC-SRP5 session/handshake (shared core byte-identical with
|
|
|
186
199
|
mac-telnet's MTWEI, plus the net-new server role), the TCP/UDP data engines, the
|
|
187
200
|
orchestrator + CLI, and the honest grounding caveat (the server gets direct CHR
|
|
188
201
|
evidence over the QEMU SLIRP gateway `10.0.2.2`; the client gets direct CHR
|
|
189
|
-
evidence over a host→guest `tcp:2000` forward
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`
|
|
202
|
+
evidence over a host→guest `tcp:2000` forward (the forward carries TCP; UDP rides
|
|
203
|
+
the guest→host gateway instead, below); UDP-receive was
|
|
204
|
+
previously a soft smoke test because the SLIRP reverse path was believed to block
|
|
205
|
+
real validation and a `connect()` socket filter was silently dropping all received
|
|
206
|
+
datagrams — RouterOS sends UDP data **from a different source port** than the
|
|
207
|
+
negotiated `serverUdpPort`, so the BSD receive filter rejected every packet;
|
|
208
|
+
fixed by removing `connect()` and addressing sends explicitly, validated against
|
|
209
|
+
a real RouterOS device at 163–203 Mbps receive and 104 Mbps bidirectional, and
|
|
210
|
+
**CI now gates UDP client receive/both** — the server→client return rides the
|
|
211
|
+
guest→host SLIRP gateway (`10.0.2.2:clientUdpPort`), needing no UDP forward and no
|
|
212
|
+
quickchr change (#88 closed; the `connect()` filter fix in #86 is what enabled it);
|
|
213
|
+
TCP
|
|
214
|
+
`connection-count` reaches the server's command packet (#84) and centrs opens the
|
|
215
|
+
negotiated extra TCP data connections — multi-connection **fan-out** (#87),
|
|
216
|
+
grounded byte-for-byte against RouterOS 7.23.1 (secondary join
|
|
217
|
+
`[token:u16 BE][0x02][0 …]`) and CHR-gated for the realized fan-out
|
|
218
|
+
(`activeConnections == count`; no throughput-rise assertion — the SLIRP loopback is
|
|
219
|
+
bandwidth-bound); authenticated sessions stay single-stream (warned); TCP
|
|
220
|
+
`direction=both` demuxes the server's
|
|
221
|
+
interleaved status frames so the client paces its TX from feedback and does not
|
|
222
|
+
starve server→client RX (#85 fixed); the Windows unit tier skips UDP-loopback
|
|
223
|
+
tests (#69); NDJSON is not adopted) are
|
|
224
|
+
documented in `commands/btest/README.md` and the `src/protocols/btest.ts`,
|
|
225
|
+
`src/protocols/btest-session.ts`, and `src/protocols/ec-srp5.ts` module headers.
|
|
195
226
|
|
|
196
227
|
## Priority order
|
|
197
228
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# `routeros/btest-connection-count-single-stream`
|
|
2
|
+
|
|
3
|
+
A **warning** (not a failure): `centrs btest client --protocol tcp
|
|
4
|
+
--connection-count <n>` opened **fewer** TCP data connections than `n`. centrs
|
|
5
|
+
does implement the multi-connection fan-out — it opens the extra connections that
|
|
6
|
+
join the test with the server-negotiated session token — but it falls back to a
|
|
7
|
+
single stream when the server negotiates **no** token. The common case is an
|
|
8
|
+
**authenticated (EC-SRP5)** session: centrs does not yet capture the post-auth
|
|
9
|
+
session token, so authenticated tests stay single-stream. A secondary connection
|
|
10
|
+
that fails mid-handshake also reduces the realized count.
|
|
11
|
+
|
|
12
|
+
The warning reports both the requested and the actually-active connection count.
|
|
13
|
+
|
|
14
|
+
## Workaround
|
|
15
|
+
|
|
16
|
+
- For full multi-stream throughput, run the test **unauthenticated**
|
|
17
|
+
(`/tool/bandwidth-server` with `authenticate=no`), where centrs negotiates the
|
|
18
|
+
token and opens all `n` connections.
|
|
19
|
+
- Or run several `centrs btest client` processes in parallel and sum their
|
|
20
|
+
throughput.
|
|
21
|
+
|
|
22
|
+
See `docs/CONSTITUTION.md` for the centrs error/warning contract.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `routeros/btest-udp-tx-size-ignored`
|
|
2
|
+
|
|
3
|
+
A non-fatal warning. For UDP `--direction both`, the btest wire protocol carries
|
|
4
|
+
a **single** `tx-size` field, so the client and the server transmit with the
|
|
5
|
+
same packet size. centrs maps `--local-udp-tx-size` to that field and discards
|
|
6
|
+
`--remote-udp-tx-size`. This warning fires only when both flags are set to
|
|
7
|
+
different values, so you know which size the session actually used.
|
|
8
|
+
|
|
9
|
+
## Fix
|
|
10
|
+
|
|
11
|
+
Drop `--remote-udp-tx-size` for `--direction both`, or set both flags to the
|
|
12
|
+
same value. To size each direction independently, run two sessions:
|
|
13
|
+
`--direction transmit` (uses `--local-udp-tx-size`) and `--direction receive`
|
|
14
|
+
(uses `--remote-udp-tx-size`). See `commands/btest/README.md` and
|
|
15
|
+
[`docs/CONSTITUTION.md`](../../CONSTITUTION.md).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# `usage/invalid-method`
|
|
2
|
+
|
|
3
|
+
The -X/--method value is not a supported HTTP method.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
`centrs api` honors the HTTP method (`-X` / `--method`) literally against
|
|
8
|
+
RouterOS's REST verb map. The accepted methods are `GET`, `PUT`, `PATCH`,
|
|
9
|
+
`DELETE`, and `POST` (case-insensitive):
|
|
10
|
+
|
|
11
|
+
| Method | RouterOS verb |
|
|
12
|
+
| ------ | -------------------- |
|
|
13
|
+
| `GET` | print / get |
|
|
14
|
+
| `PUT` | add (RouterOS create) |
|
|
15
|
+
| `PATCH`| set |
|
|
16
|
+
| `DELETE`| remove |
|
|
17
|
+
| `POST` | run a command |
|
|
18
|
+
|
|
19
|
+
Pass one of those with `-X`, or omit the flag to use the default `GET`. Note the
|
|
20
|
+
trap: **`PUT` is RouterOS's create**, not `POST`.
|
|
21
|
+
|
|
22
|
+
See [`docs/CONSTITUTION.md`](../../CONSTITUTION.md) for the centrs error
|
|
23
|
+
contract.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tikoci/centrs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"repository": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"packageManager": "bun@1.3.13",
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@agent-lint/cli": "^0.8.1",
|
|
15
|
-
"@biomejs/biome": "2.5.
|
|
15
|
+
"@biomejs/biome": "2.5.1",
|
|
16
16
|
"@secretlint/secretlint-rule-preset-recommend": "^13.0.2",
|
|
17
17
|
"@tikoci/quickchr": "^0.4.2",
|
|
18
18
|
"@types/bun": "^1.3.13",
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
"scripts": {
|
|
58
58
|
"ci": "bun run lint && bun run test && bun run build",
|
|
59
59
|
"git:pre-commit": "bun run lint",
|
|
60
|
-
"git:pre-push": "bun run
|
|
60
|
+
"git:pre-push": "bun run lint:git:push",
|
|
61
61
|
"lint": "bun run lint:biome && bun run lint:typecheck",
|
|
62
62
|
"lint:git:commit": "bun run lint",
|
|
63
|
-
"lint:git:push": "bun run ci",
|
|
64
|
-
"lint:ci": "bun run lint && bun run lint:markdownlint && bun run lint:cspell && bun run lint:secretlint
|
|
63
|
+
"lint:git:push": "bun run lint:ci && bun run test && bun run build",
|
|
64
|
+
"lint:ci": "bun run lint && bun run lint:markdownlint && bun run lint:cspell && bun run lint:secretlint",
|
|
65
65
|
"lint:cspell": "cspell lint . --gitignore",
|
|
66
66
|
"lint:cspell:all": "cspell lint . --no-gitignore",
|
|
67
67
|
"lint:markdownlint": "markdownlint-cli2 \"**/*.md\" \"!.scratch/**\"",
|