@tikoci/centrs 0.1.3 → 0.1.4

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/GLOSSARY.txt +213 -0
  3. package/LICENSE +21 -0
  4. package/README.md +36 -8
  5. package/commands/AGENTS.md +7 -1
  6. package/commands/api/README.md +19 -18
  7. package/commands/api/examples.md +15 -0
  8. package/commands/btest/README.md +21 -32
  9. package/commands/check/README.md +6 -1
  10. package/commands/discover/README.md +5 -11
  11. package/commands/execute/README.md +41 -29
  12. package/commands/execute/examples.md +41 -3
  13. package/commands/explain/README.md +1716 -0
  14. package/commands/explain/examples.md +606 -0
  15. package/commands/retrieve/README.md +25 -27
  16. package/commands/retrieve/examples.md +16 -0
  17. package/commands/transfer/README.md +28 -20
  18. package/commands/transfer/examples.md +29 -0
  19. package/docs/CLI.md +353 -0
  20. package/docs/CONSTITUTION.md +66 -15
  21. package/docs/MATRIX.md +38 -1
  22. package/docs/errors/README.md +3 -0
  23. package/docs/errors/quickchr/machine-not-found.md +22 -0
  24. package/docs/errors/quickchr/machine-stopped.md +20 -0
  25. package/docs/errors/quickchr/package-unavailable.md +27 -0
  26. package/docs/errors/quickchr/unsupported-via.md +35 -0
  27. package/docs/errors/quickchr/unsupported.md +35 -0
  28. package/docs/errors/routeros/version-unsupported.md +26 -0
  29. package/docs/errors/transport/provider-trust.md +29 -0
  30. package/docs/errors/usage/stdin-ignored.md +28 -0
  31. package/docs/errors/validation/unknown-attribute.md +37 -4
  32. package/package.json +45 -10
  33. package/src/api-fanout.ts +29 -14
  34. package/src/api.ts +97 -54
  35. package/src/cli/api.ts +25 -6
  36. package/src/cli/btest.ts +18 -2
  37. package/src/cli/execute.ts +25 -6
  38. package/src/cli/explain.ts +503 -0
  39. package/src/cli/retrieve.ts +33 -1
  40. package/src/cli/selection.ts +71 -11
  41. package/src/cli/settings.ts +2 -2
  42. package/src/cli/transfer.ts +23 -6
  43. package/src/cli.ts +15 -2
  44. package/src/core/envelope.ts +5 -2
  45. package/src/core/error-catalog.ts +40 -0
  46. package/src/core/preflight.ts +306 -0
  47. package/src/core/routeros-string.ts +24 -0
  48. package/src/errors.ts +1 -0
  49. package/src/execute-fanout.ts +29 -14
  50. package/src/execute.ts +114 -63
  51. package/src/explain/arg-tokens.ts +114 -0
  52. package/src/explain/args.ts +1403 -0
  53. package/src/explain/blocks.ts +132 -0
  54. package/src/explain/brace-slots.ts +143 -0
  55. package/src/explain/brace-tokens.ts +38 -0
  56. package/src/explain/catalog.ts +1345 -0
  57. package/src/explain/coordinates.ts +329 -0
  58. package/src/explain/defects.ts +217 -0
  59. package/src/explain/is-known-menu.ts +17 -0
  60. package/src/explain/menus.ts +664 -0
  61. package/src/explain/operator-tokens.ts +310 -0
  62. package/src/explain/operators.ts +458 -0
  63. package/src/explain/path-tokens.ts +150 -0
  64. package/src/explain/pathresolve.ts +1324 -0
  65. package/src/explain/quoted-string.ts +241 -0
  66. package/src/explain/scope-brace.ts +308 -0
  67. package/src/explain/segment.ts +757 -0
  68. package/src/explain/string-tokens.ts +67 -0
  69. package/src/explain/symbol-values.ts +520 -0
  70. package/src/explain/symbols.ts +1597 -0
  71. package/src/explain/token-ranges.ts +72 -0
  72. package/src/explain/transport.ts +497 -0
  73. package/src/explain/value-tokens.ts +80 -0
  74. package/src/explain/values.ts +357 -0
  75. package/src/explain/verbs.ts +64 -0
  76. package/src/explain/verbsplit.ts +813 -0
  77. package/src/explain/write.ts +815 -0
  78. package/src/explain.ts +2152 -0
  79. package/src/index.ts +206 -4
  80. package/src/protocols/mac-telnet-console.ts +61 -17
  81. package/src/resolver/facts.ts +1 -1
  82. package/src/resolver/index.ts +23 -4
  83. package/src/resolver/quickchr-provider.ts +597 -0
  84. package/src/resolver/selection.ts +99 -12
  85. package/src/resolver/service-endpoint.ts +155 -0
  86. package/src/resolver/target.ts +1 -1
  87. package/src/retrieve-fanout.ts +26 -12
  88. package/src/retrieve.ts +81 -18
  89. package/src/transfer-fanout.ts +42 -25
  90. package/src/transfer.ts +87 -44
@@ -16,11 +16,17 @@ selection**, examples F1–F5). `romon` and `winbox-terminal` remain
16
16
  - Mirror RouterOS `execute` semantics. Input is a CLI string; output is
17
17
  console-shaped text (or structured records for path+verb writes) wrapped in
18
18
  the standard envelope. This includes write-shaped add/set/remove.
19
- - Validation is two-stage (see constitution: validation): `:put [:parse
20
- "<cmd>"]` is a **syntax** gate only — it does not catch unknown attributes or
21
- bad values. Semantic validation needs `/console/inspect` or the server's own
22
- re-validation on the write round-trip. A clean parse is necessary, not
23
- sufficient.
19
+ - Validation is two-stage (see constitution: validation; grounded on CHR 7.23.3,
20
+ GH#230): a single `:put [:parse "<cmd>"]` covers both the syntax and the
21
+ unknown-attribute (name-level) gate on **every** transport — `:parse` never
22
+ throws; its return value's printed form (`(evl …)` / `(evl bad parameter …)` /
23
+ `(<%% bad command name …)`) is read from the REST/native `ret` *and* the
24
+ console output. That check is necessary, not sufficient: value-type errors pass
25
+ it, and the `/console/inspect` / server re-validation semantic checks are
26
+ separate.
27
+ The outer RouterOS string literal escapes backslash, quote, and dollar so
28
+ validation parses the caller's exact command rather than expanding variables
29
+ in the wrapper.
24
30
  - centrs owns the **script-vs-structured gate** (`canonicalizeExecuteCommand` +
25
31
  `isWriteShaped` in `src/execute.ts`): it decides which validation runs and
26
32
  whether the write-confirm prompt fires. The shared `rosetta`/`lsp-routeros-ts`
@@ -102,30 +108,31 @@ selection**, examples F1–F5). `romon` and `winbox-terminal` remain
102
108
 
103
109
  ## Flags
104
110
 
105
- | Flag | Behavior |
111
+ Implemented flags are generated from the CLI metadata into
112
+ [`docs/CLI.md` → execute](../../docs/CLI.md#execute); this file does not
113
+ duplicate that table. Behavior notes the generated reference cannot carry:
114
+
115
+ - `--validate` is transport-appropriate but now one gate on every transport —
116
+ a single `:put [:parse ...]` that is read as a value (GH#230): REST/native
117
+ read the `ret` text (HTTP 200 / `as-string`); mac-telnet and ssh read the
118
+ console's printed value.
119
+ - `--format` defaults to `text`; `CENTRS_FORMAT` sets the default.
120
+ - `--quickchr` resolves host/port/auth from the live `@tikoci/quickchr`
121
+ (0.4.5+, optional dependency) descriptor, bypassing CDB/env resolution for
122
+ those fields.
123
+ - `--` — use it when the command contains tokens that would otherwise be
124
+ claimed as centrs flags; otherwise the command must be quoted as a single
125
+ argument. In fan-out, positional targets come BEFORE `--` and the command
126
+ AFTER it (see **Target selection**).
127
+
128
+ ### Designed, not implemented
129
+
130
+ Spec-tier flags with no implementation yet — today they fail as unknown flags:
131
+
132
+ | Flag | Designed behavior |
106
133
  | ------------------------------- | ------------------------------------------------------------------------------------- |
107
- | `--via <protocol>` | Pin the transport (`native-api`, `rest-api`, `ssh`, or `mac-telnet`). No silent downgrade. A bare MAC target defaults to `mac-telnet`. |
108
- | `--host <host\|url>` | Override the resolved host or base URL for the target. |
109
- | `--port <n>` | Override the resolved management port. |
110
- | `--username` (alias `--user`, `-u`) / `--password` | RouterOS credentials; fall back to `CENTRS_USERNAME` / `CENTRS_PASSWORD`. |
111
- | `--ssh-key <path>` | `--via ssh`: explicit private-key path. Falls back to `CENTRS_SSH_KEY` / the ssh-agent. |
112
- | `--insecure` | Accept a new SSH host key (`--via ssh`) or a self-signed `api-ssl` TLS cert. Default verifies. |
113
- | `--cdb-file` / `--cdb-password` | Read target credentials from (and decrypt) a WinBox CDB file. |
114
- | `--resolve <none\|arp>` | Resolve a MAC-address target to an IP via the host ARP cache. Default `none`. |
115
- | `--timeout <duration>` | Per-request timeout. REST: ≤ 60s; other transports may allow longer. |
116
- | `--validate[=false]` | Run transport-appropriate preflight validation before execution (default `true`): REST/native use `:parse` + `/console/inspect`; mac-telnet and ssh use a single console `:parse` (covers syntax + unknown-attribute). |
117
134
  | `--strict` | Treat any warning on a successful run as an error (`ok: false`, nonzero exit), like `-Werror`. Default is lenient (`ok: true` + `warnings`). |
118
- | `--yes` | Confirm write-shaped add/set/remove commands in non-interactive runs. |
119
- | `--max-bytes <n>` | Byte budget for the rendered envelope; excess output is truncated with a warning + `meta.truncated` (not an error), matching `retrieve`. (Renamed from `--max-results`.) |
120
- | `--format <text\|json\|yaml>` | Output format (alias `--json`). Defaults to `text`; `CENTRS_FORMAT` sets the default. |
121
- | `--group <name>` | Run across every CDB record in WinBox group `<name>`. Repeatable, combinable with `<target>` positionals plus `--where`/`--all`/`--default`; de-duped by record index. See **Target selection**. |
122
- | `--where <attr>=<value>` | Device-class selector — run across every CDB record whose stored fact/comment-kv matches (e.g. `--where board=RB5009 /system/package/check-for-updates`). Repeatable (AND). See constitution: target selection. |
123
- | `--all` | Run across every CDB record (excludes the reserved `__default__`). See **Target selection**. |
124
- | `--default` | Select the reserved `__default__` record. See **Target selection**. |
125
- | `--near <lat>,<lon>,<radius>` | Geo selector — run across CDB records whose GPS is within the great-circle radius (units `m`/`km`/`mi`/`ft`; bare = km). Lat-first. Union predicate; geo-less devices never match. See **Target selection**. |
126
- | `--bbox <south>,<west>,<north>,<east>` | Geo selector — run across CDB records whose GPS is inside the lat-first bounding box. Union predicate. See **Target selection**. |
127
- | `--concurrency <n>` | Max in-flight targets during fan-out (integer ≥ 1). Transport-aware default: `rest-api` 8, others 4. |
128
- | `--` | End-of-options marker. Every token after `--` is taken as the literal RouterOS command, even flag-shaped ones — e.g. `centrs execute $R -- /interface print where disabled=yes`. In fan-out, positional targets come BEFORE `--` and the command AFTER it (`centrs execute r1 r2 -- /system/reboot`). Use it when the command contains tokens that would otherwise be claimed as centrs flags; otherwise the command must be quoted as a single argument. |
135
+ | `--max-bytes <n>` | Byte budget for the rendered envelope; excess output is truncated with a warning + `meta.truncated` (not an error), matching `retrieve`. (Will replace the implemented `--max-results`, which fails instead of truncating.) |
129
136
 
130
137
  ## SSH key selection
131
138
 
@@ -174,6 +181,11 @@ many tokens):
174
181
  - `execute r1 /system/resource/print` — a single positional target with no
175
182
  selector stays **single-target** (legacy split: first positional is the target,
176
183
  the rest is the command), never `FanoutData`.
184
+ - `execute --quickchr lab /system/resource/print` — the quickchr
185
+ named-live-provider claims the target slots like a selector (positionals are
186
+ the command; no `--` needed). One `--quickchr` stays single-target; repeating
187
+ it fans out; mixing it with positionals or CDB selectors is
188
+ `usage/conflicting-flags` (constitution: resolution providers).
177
189
 
178
190
  Because `execute` is the primary **write** path, a write-shaped fan-out
179
191
  (add/set/remove) is gated by **`--yes`**, confirmed **once** up front; without it
@@ -213,7 +225,7 @@ How it is wired:
213
225
  `transport/capability-unsupported` (mac-telnet is execute/terminal only).
214
226
  - **Validation is one console `:parse`.** Over the console, `:put [:parse "<cmd>"]`
215
227
  prints both `syntax error` and `bad parameter <name>`, so a single gate covers
216
- syntax **and** the unknown-attribute (semantic) check — no `/console/inspect`
228
+ syntax **and** the unknown-attribute (name-level) check — no `/console/inspect`
217
229
  table parsing. Maps to `validation/syntax` / `validation/unknown-attribute`,
218
230
  same codes as REST/native (see `docs/CONSTITUTION.md`, validation).
219
231
  - **Auth (grounded):** MTWEI is required; a stock device offers a 16-byte MD5 salt
@@ -243,7 +255,7 @@ Protocol selection: an unresolved MAC target auto-selects mac-telnet (covered by
243
255
  **decided:** `--strict` promotes any warning on an otherwise-successful run to
244
256
  `ok: false` (nonzero exit), like `-Werror`; the default stays lenient
245
257
  (`ok: true` with the warning in the `warnings` channel). Tracked code
246
- follow-up; the flag is in the table above.
258
+ follow-up; the flag is under **Designed, not implemented** above.
247
259
 
248
260
  These are deferred refinements to the already-grounded single-shot envelope,
249
261
  not blockers for the `rest-api`/`native-api` cells.
@@ -62,14 +62,16 @@ The command is a script-shaped console expression, not a slash path plus verb,
62
62
  so REST must use the `/rest/execute` fallback.
63
63
 
64
64
  ```bash
65
- centrs execute $R ':put [/system/identity/get name]' --via rest-api --username $U --password $P
65
+ centrs execute $R ':local a true; :if ($a = true) do={:put [/system/identity/get name]}' --via rest-api --username $U --password $P
66
66
  ```
67
67
 
68
68
  Envelope: `ok: true`, `data` is string-shaped (`data.output` or `data.ret`) and
69
69
  contains the CHR identity name, `meta.via=rest-api`, and
70
70
  `meta.validation.syntax=true`. `meta.validation.semantic` is absent or marked
71
71
  `not-applicable` because `/console/inspect` validates path-shaped commands, not
72
- arbitrary script expressions.
72
+ arbitrary script expressions. The `$a` variable must survive the validation
73
+ wrapper: if that outer string expands it before `:parse`, this example fails even
74
+ though RouterOS accepts the original command.
73
75
 
74
76
  ### 5. Syntax reject from `:put [:parse ...]`
75
77
 
@@ -95,7 +97,7 @@ centrs execute $R '/ip/address/add address=198.51.100.11/32 interface=ether1 no-
95
97
  ```
96
98
 
97
99
  Envelope: `ok: false`, `error.code=validation/unknown-attribute`,
98
- `error.cause.attribute="no-such-arg"` (or equivalent structured field),
100
+ `error.context.parameter="no-such-arg"`,
99
101
  `meta.validation.syntax=true`, `meta.validation.source=/console/inspect`, and
100
102
  no address is added. This example is the product claim: syntax validation and
101
103
  semantic validation are separate gates.
@@ -435,3 +437,39 @@ Envelope: `meta.via=mac-telnet` when `$MAC` is not resolved from CDB and no
435
437
  `--host`/`--port`). If the caller wants IP-level REST or native API execution,
436
438
  they must opt into MAC-to-IP resolution (`--resolve arp` + `--via …`) before
437
439
  protocol selection.
440
+
441
+ ## quickchr targets (#134)
442
+
443
+ `$NAME` is the machine name of a running quickchr-managed CHR. Host/port/auth
444
+ come from the live descriptor — the CDB, `CENTRS_HOST`-style env, and the
445
+ `__default__` ladder are bypassed for those fields. Covered by
446
+ `test/integration/quickchr-target.test.ts`.
447
+
448
+ ### Q1. Run a command on a quickchr target (native API default)
449
+
450
+ ```bash
451
+ centrs execute --quickchr $NAME ':put [/system/identity/get name]' --json
452
+ ```
453
+
454
+ Envelope: `ok: true`, `meta.via=native-api` (execute's normal default),
455
+ `meta.target.source.kind=provider`, `meta.target.identity=$NAME`.
456
+
457
+ ### Q2. A `--via` the descriptor does not forward → `quickchr/unsupported-via`
458
+
459
+ ```bash
460
+ centrs execute --quickchr $NAME ':put 1' --via mac-telnet --json
461
+ ```
462
+
463
+ Envelope: `ok: false`, `error.code=quickchr/unsupported-via` — quickchr v1
464
+ forwards `rest-api`/`native-api`/`ssh` only; centrs never falls back to another
465
+ transport.
466
+
467
+ ### Q3. Repeated `--quickchr` fans out; an unknown machine is an inner failure
468
+
469
+ ```bash
470
+ centrs execute --quickchr $NAME --quickchr no-such-machine -- ':put 1' --json
471
+ ```
472
+
473
+ Envelope: outer `ok: true`, `data.summary = { total: 2, ok: 1, failed: 1 }`;
474
+ the unknown machine's inner error is `quickchr/machine-not-found`. Exit code 2
475
+ (partial).