@tikoci/centrs 0.1.4 → 0.1.7
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 +417 -1
- package/GLOSSARY.txt +40 -1
- package/README.md +16 -8
- package/commands/AGENTS.md +7 -4
- package/commands/api/AGENTS.md +13 -1
- package/commands/api/README.md +50 -10
- package/commands/api/examples.md +100 -2
- package/commands/devices/README.md +10 -1
- package/commands/devices/examples.md +33 -2
- package/commands/execute/README.md +68 -25
- package/commands/execute/examples.md +168 -7
- package/commands/explain/README.md +1206 -155
- package/commands/explain/examples.md +99 -3
- package/commands/mcp/README.md +9 -5
- package/commands/mcp/examples.md +2 -2
- package/commands/retrieve/examples.md +20 -0
- package/commands/terminal/examples.md +7 -1
- package/docs/CLI.md +3 -3
- package/docs/CONSTITUTION.md +83 -21
- package/docs/MATRIX.md +89 -52
- package/docs/errors/cdb/create-failed.md +20 -0
- package/docs/errors/cdb/file-exists.md +23 -0
- package/docs/errors/input/invalid-target.md +25 -3
- package/docs/errors/routeros/invalid-value.md +3 -2
- package/docs/errors/transport/cancel-unacknowledged.md +24 -0
- package/docs/errors/validation/syntax.md +29 -4
- package/package.json +13 -2
- package/src/api-fanout.ts +17 -4
- package/src/api.ts +346 -75
- package/src/cli/api.ts +1 -1
- package/src/cli/btest.ts +1 -1
- package/src/cli/devices.ts +16 -3
- package/src/cli/explain.ts +1 -1
- package/src/cli/terminal.ts +1 -1
- package/src/core/envelope.ts +53 -0
- package/src/core/error-catalog.ts +16 -1
- package/src/core/execute-command.ts +455 -0
- package/src/core/inspect.ts +58 -26
- package/src/core/routeros-errors.ts +66 -0
- package/src/core/version.ts +4 -0
- package/src/core/yaml.ts +86 -0
- package/src/data/winbox-cdb-write.ts +22 -8
- package/src/devices.ts +142 -28
- package/src/execute-fanout.ts +19 -5
- package/src/execute.ts +247 -295
- package/src/explain/arg-tokens.ts +62 -25
- package/src/explain/args.ts +398 -52
- package/src/explain/blocks.ts +54 -11
- package/src/explain/brace-tokens.ts +4 -3
- package/src/explain/catalog.ts +42 -44
- package/src/explain/coordinates.ts +159 -0
- package/src/explain/defects.ts +2 -1
- package/src/explain/escape-tokens.ts +77 -0
- package/src/explain/highlight-projection.ts +308 -0
- package/src/explain/menus.ts +9 -5
- package/src/explain/operator-tokens.ts +13 -7
- package/src/explain/operators.ts +27 -20
- package/src/explain/path-tokens.ts +1 -1
- package/src/explain/pathresolve.ts +358 -113
- package/src/explain/quoted-string.ts +110 -20
- package/src/explain/scope-brace.ts +175 -22
- package/src/explain/segment.ts +429 -21
- package/src/explain/separator.ts +187 -0
- package/src/explain/split-owner.ts +194 -0
- package/src/explain/string-tokens.ts +14 -4
- package/src/explain/symbol-values.ts +209 -173
- package/src/explain/symbols.ts +21 -1
- package/src/explain/transport.ts +17 -0
- package/src/explain/value-tokens.ts +15 -12
- package/src/explain/verbsplit.ts +165 -23
- package/src/explain/write.ts +47 -6
- package/src/explain.ts +522 -72
- package/src/index.ts +21 -2
- package/src/mcp/server.ts +2 -1
- package/src/mcp/tools.ts +1 -1
- package/src/offline-gate.ts +148 -0
- package/src/protocols/adapter.ts +12 -1
- package/src/protocols/mac-telnet-console.ts +6 -4
- package/src/protocols/native-api.ts +29 -3
- package/src/resolver/target.ts +32 -12
- package/src/retrieve-fanout.ts +1 -1
- package/src/retrieve.ts +18 -63
- package/src/settings.ts +2 -1
- package/src/transfer-fanout.ts +1 -1
- package/src/transfer.ts +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# `cdb/file-exists`
|
|
2
|
+
|
|
3
|
+
`devices init` found a CDB already at the resolved location and wrote nothing.
|
|
4
|
+
|
|
5
|
+
This is a warning on a successful result, not an error. Like `git init`,
|
|
6
|
+
`centrs devices init` never overwrites an existing CDB. When the file there
|
|
7
|
+
already loads as a CDB, `init` succeeds with `data.created: false` and
|
|
8
|
+
`data.recordCount` set to the number of records it holds, and leaves the file
|
|
9
|
+
byte-for-byte untouched. A bootstrap script can therefore run `init`
|
|
10
|
+
unconditionally.
|
|
11
|
+
|
|
12
|
+
A file at that path that does not load as a CDB is not reported here. `init`
|
|
13
|
+
fails with the load error instead (`cdb/parse-failed`, or
|
|
14
|
+
`cdb/password-required` for an encrypted CDB without `--cdb-password`), and
|
|
15
|
+
still leaves the file alone.
|
|
16
|
+
|
|
17
|
+
## Fix
|
|
18
|
+
|
|
19
|
+
Usually nothing: the CDB you asked for exists.
|
|
20
|
+
|
|
21
|
+
- To see what it holds: `centrs devices list --cdb-file PATH`.
|
|
22
|
+
- To start fresh, pick a different path (`centrs devices init --cdb-file
|
|
23
|
+
NEW_PATH`), or move the existing file aside yourself; centrs never deletes it.
|
|
@@ -2,8 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
The target string is invalid for the requested operation.
|
|
4
4
|
|
|
5
|
+
A command's `<router>` is looked up in the CDB first: its `target`, or an
|
|
6
|
+
`identity=` / `mac=` / `ip=` comment lookup key. When nothing matches, centrs
|
|
7
|
+
treats the string as a host. This error means it is not a valid host either,
|
|
8
|
+
or it is a URL that carries a path. Common causes:
|
|
9
|
+
|
|
10
|
+
- **`quickchr:NAME`**: there is no `quickchr:` target prefix. A quickchr VM is
|
|
11
|
+
selected with a flag: `--quickchr NAME`.
|
|
12
|
+
- **A typo or stray characters**, such as spaces or an extra `:` (`a b`,
|
|
13
|
+
`bad:host:name`).
|
|
14
|
+
- **A base URL with a path**, such as `http://192.0.2.1/rest`. The RouterOS
|
|
15
|
+
menu path is a separate argument.
|
|
16
|
+
|
|
17
|
+
The rejected string is in the error's `context.target`.
|
|
18
|
+
|
|
5
19
|
## Fix
|
|
6
20
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
21
|
+
Pass one of the accepted target forms:
|
|
22
|
+
|
|
23
|
+
| Form | Example |
|
|
24
|
+
| ---- | ------- |
|
|
25
|
+
| CDB target or lookup key | `edge1` (see `centrs devices list`) |
|
|
26
|
+
| IP address or hostname, optional `:port` | `192.0.2.1`, `router.example:8080` |
|
|
27
|
+
| Base URL without a path | `https://192.0.2.1:8443` |
|
|
28
|
+
| quickchr VM | `--quickchr NAME` (a flag, not a prefix) |
|
|
29
|
+
|
|
30
|
+
With an explicit `--cdb-file` and no `__default__` fallback record, a string
|
|
31
|
+
that matches no record stops earlier with `cdb/not-found-target`.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# `routeros/invalid-value`
|
|
2
2
|
|
|
3
|
-
RouterOS rejected
|
|
4
|
-
|
|
3
|
+
RouterOS rejected a supplied value. When RouterOS names the offending argument,
|
|
4
|
+
it is captured in `context.argument`.
|
|
5
5
|
|
|
6
6
|
## Typical RouterOS strings
|
|
7
7
|
|
|
8
8
|
- `invalid value for argument <name>`
|
|
9
9
|
- `invalid value of <name>`
|
|
10
|
+
- `input does not match ...`
|
|
10
11
|
|
|
11
12
|
## Fix
|
|
12
13
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# `transport/cancel-unacknowledged`
|
|
2
|
+
|
|
3
|
+
RouterOS did not acknowledge a stream's /cancel within the timeout; centrs ended
|
|
4
|
+
the stream and closed the session locally.
|
|
5
|
+
|
|
6
|
+
## When it happens
|
|
7
|
+
|
|
8
|
+
This is a **warning** on the summary envelope of `centrs api … --stream`, not a
|
|
9
|
+
failure. The stream stopped for its normal reason (`--duration` elapsed, or
|
|
10
|
+
Ctrl-C), and centrs sent RouterOS `/cancel` for the `/listen`. A cooperating
|
|
11
|
+
router answers with an `interrupted` trap and `!done` within milliseconds. This
|
|
12
|
+
one did not answer within `--timeout`, so centrs stopped waiting, closed the
|
|
13
|
+
native API session and finished.
|
|
14
|
+
|
|
15
|
+
The summary's `stopReason` is still the reason the stream stopped. The warning
|
|
16
|
+
records that the router never confirmed the cancel, so the stop was local.
|
|
17
|
+
|
|
18
|
+
## Fix
|
|
19
|
+
|
|
20
|
+
- Usually nothing: the stream ended when you asked, and the session is closed.
|
|
21
|
+
- If it repeats against one router, the API connection is likely stalled (a
|
|
22
|
+
router under load, or a firewall or NAT device holding the TCP session).
|
|
23
|
+
Check the router's CPU and the path to it before relying on long-lived
|
|
24
|
+
streams there.
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
# `validation/syntax`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The command syntax was rejected by the validation gate — offline analysis, or
|
|
4
|
+
the RouterOS `:parse` probe.
|
|
5
|
+
|
|
6
|
+
The gate has two stages
|
|
7
|
+
([`docs/CONSTITUTION.md` → Validation is the product](../../CONSTITUTION.md#validation-is-the-product)),
|
|
8
|
+
and `error.context.validationStage` says which one spoke:
|
|
9
|
+
|
|
10
|
+
- **`"offline"`** — centrs rejected the input before any connection was opened,
|
|
11
|
+
and `meta.validation.stages[]` shows the device stage `skipped`: the router was
|
|
12
|
+
never asked. The **corpus-gated analyzer** is the whole of this stage, and
|
|
13
|
+
`error.context.validationSource` names it. `error.context.span` carries the
|
|
14
|
+
offending byte range (end-exclusive) and `error.context.diagnostics[]` the
|
|
15
|
+
analyzer's own `explain/<analyzer>/<slug>` codes.
|
|
16
|
+
|
|
17
|
+
A quote-balance supplement used to sit beside it for the unterminated `"` or
|
|
18
|
+
`'` the analyzer passed, reporting no span. It is gone (GH#355): the analyzer
|
|
19
|
+
rejects a bare `'` itself now, with the byte span, and the supplement's own
|
|
20
|
+
model of `'` as a delimiter was false-rejecting input RouterOS accepts.
|
|
21
|
+
- **absent** — RouterOS itself rejected it, through `:put [:parse "…"]`. When the
|
|
22
|
+
device reported a `(line N column M)`, it is on `error.position`, which is
|
|
23
|
+
RouterOS's authoritative 1-based **byte** column and is never synthesized by
|
|
24
|
+
centrs. An offline rejection never carries `error.position`.
|
|
4
25
|
|
|
5
26
|
## Fix
|
|
6
27
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
28
|
+
Run `centrs explain '<command>'` to see the offending bytes in context, fix the
|
|
29
|
+
RouterOS CLI syntax — quotes and bracketed expressions most often — and retry.
|
|
30
|
+
|
|
31
|
+
`--validate=false` bypasses **both** stages. Use it only to probe a RouterOS edge
|
|
32
|
+
centrs reads wrong, never to work around a rejection you believe is correct: if
|
|
33
|
+
the validator rejects something a real router accepts, the validator is the bug
|
|
34
|
+
(constitution: validation is the product).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tikoci/centrs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"repository": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"exports": {
|
|
32
32
|
".": "./src/index.ts",
|
|
33
33
|
"./cli": "./src/cli.ts",
|
|
34
|
+
"./explain": "./src/explain.ts",
|
|
34
35
|
"./mcp": "./src/mcp.ts",
|
|
35
36
|
"./tui": "./src/tui.ts",
|
|
36
37
|
"./webproxy": "./src/webproxy.ts",
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
"lint": "bun run lint:biome && bun run lint:typecheck",
|
|
65
66
|
"lint:git:commit": "bun run lint:biome",
|
|
66
67
|
"lint:git:push": "bun run lint:ci",
|
|
67
|
-
"lint:ci": "bun run lint && bun run docs:cli:check && bun run explain:value-census:readme:check && bun run explain:token-census:readme:check && bun run explain:operator-readme:check && bun run lint:markdownlint && bun run lint:cspell && bun run lint:secretlint",
|
|
68
|
+
"lint:ci": "bun run lint && bun run docs:cli:check && bun run explain:value-census:readme:check && bun run explain:token-census:readme:check && bun run explain:highlight-agreement:readme:check && bun run explain:operator-readme:check && bun run explain:bracket-equals:readme:check && bun run lint:markdownlint && bun run lint:cspell && bun run lint:secretlint",
|
|
68
69
|
"lint:cspell": "cspell lint . --gitignore",
|
|
69
70
|
"lint:cspell:all": "cspell lint . --no-gitignore",
|
|
70
71
|
"lint:markdownlint": "markdownlint-cli2 \"**/*.md\" \"!.scratch/**\"",
|
|
@@ -92,9 +93,18 @@
|
|
|
92
93
|
"docs:cli": "bun run scripts/gen-cli-docs.ts",
|
|
93
94
|
"docs:cli:check": "bun run scripts/gen-cli-docs.ts --check",
|
|
94
95
|
"docs:errors": "bun run scripts/gen-error-pages.ts",
|
|
96
|
+
"explain:bracket-equals": "bun run scripts/explain-bracket-equals.ts",
|
|
97
|
+
"explain:bracket-equals:check": "bun run scripts/explain-bracket-equals.ts --check",
|
|
98
|
+
"explain:bracket-equals:readme": "bun run scripts/explain-bracket-equals.ts --readme",
|
|
99
|
+
"explain:bracket-equals:readme:check": "bun run scripts/explain-bracket-equals.ts --readme --check",
|
|
100
|
+
"explain:browser-consumer": "bun run scripts/explain-browser-consumer.ts",
|
|
95
101
|
"explain:catalog": "bun run scripts/gen-explain-catalog.ts",
|
|
96
102
|
"explain:catalog:check": "bun run scripts/gen-explain-catalog.ts --check",
|
|
97
103
|
"explain:corpus-census": "bun run scripts/explain-corpus-census.ts",
|
|
104
|
+
"explain:highlight-agreement": "bun run scripts/explain-highlight-agreement.ts",
|
|
105
|
+
"explain:highlight-agreement:check": "bun run scripts/explain-highlight-agreement.ts --check",
|
|
106
|
+
"explain:highlight-agreement:readme": "bun run scripts/explain-highlight-agreement.ts --readme",
|
|
107
|
+
"explain:highlight-agreement:readme:check": "bun run scripts/explain-highlight-agreement.ts --readme --check",
|
|
98
108
|
"explain:highlight-slice": "bun run scripts/explain-highlight-slice.ts",
|
|
99
109
|
"explain:menus": "bun run scripts/gen-explain-menus.ts",
|
|
100
110
|
"explain:operator-census": "bun run scripts/explain-operator-census.ts",
|
|
@@ -116,6 +126,7 @@
|
|
|
116
126
|
"explain:value-census:check": "bun run scripts/explain-value-census.ts --check",
|
|
117
127
|
"explain:value-census:readme": "bun run scripts/explain-value-census.ts --readme",
|
|
118
128
|
"explain:value-census:readme:check": "bun run scripts/explain-value-census.ts --readme --check",
|
|
129
|
+
"explain:arg-reach": "bun run scripts/explain-arg-reach-sweep.ts",
|
|
119
130
|
"explain:token-census": "bun run scripts/explain-token-census.ts",
|
|
120
131
|
"explain:token-census:check": "bun run scripts/explain-token-census.ts --check",
|
|
121
132
|
"explain:token-census:readme": "bun run scripts/explain-token-census.ts --readme",
|
package/src/api-fanout.ts
CHANGED
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
CentrsSuccessEnvelope,
|
|
40
40
|
FanoutData,
|
|
41
41
|
FanoutSummary,
|
|
42
|
+
ValidationTrace,
|
|
42
43
|
} from "./core/envelope.ts";
|
|
43
44
|
import {
|
|
44
45
|
buildFanoutEnvelope,
|
|
@@ -49,6 +50,7 @@ import {
|
|
|
49
50
|
runFanout,
|
|
50
51
|
summarizeFanout,
|
|
51
52
|
} from "./core/fanout.ts";
|
|
53
|
+
import { toYaml } from "./core/yaml.ts";
|
|
52
54
|
import { CentrsError, serializeCentrsError } from "./errors.ts";
|
|
53
55
|
import { promptForWriteConfirmation } from "./execute.ts";
|
|
54
56
|
import { plannedProtocols, type RouterOsProtocol } from "./protocols/index.ts";
|
|
@@ -61,7 +63,6 @@ import {
|
|
|
61
63
|
type SelectionResolveInput,
|
|
62
64
|
type TargetSelection,
|
|
63
65
|
} from "./resolver/index.ts";
|
|
64
|
-
import { toYaml } from "./retrieve.ts";
|
|
65
66
|
|
|
66
67
|
/** Compact description of the selector that produced the fan-out. */
|
|
67
68
|
export interface ApiSelectionSummary {
|
|
@@ -246,6 +247,11 @@ export async function apiFanout(
|
|
|
246
247
|
await assertFanoutWriteConfirmed(request, requestSummary, method, expansion);
|
|
247
248
|
|
|
248
249
|
const execute = internals.execute ?? runResolvedApi;
|
|
250
|
+
// Per-target validation trace. `execute` and `onExecuteError` are separate
|
|
251
|
+
// callbacks that share only the resolved request, so the request object is
|
|
252
|
+
// the key: without it a per-target error envelope would reconstruct
|
|
253
|
+
// `meta.validation` instead of reporting the gate that actually ran (GH#354).
|
|
254
|
+
const traces = new WeakMap<ResolvedApiRequest, ValidationTrace>();
|
|
249
255
|
const sleep = internals.sleep ?? defaultFanoutSleep;
|
|
250
256
|
|
|
251
257
|
const targets = await runFanout<
|
|
@@ -292,11 +298,18 @@ export async function apiFanout(
|
|
|
292
298
|
target: memberTargetMeta(member),
|
|
293
299
|
warnings: member.kind === "cdb" ? [...member.resolution.warnings] : [],
|
|
294
300
|
}),
|
|
295
|
-
execute: async (resolved, member) =>
|
|
296
|
-
|
|
301
|
+
execute: async (resolved, member) => {
|
|
302
|
+
const trace: ValidationTrace = {};
|
|
303
|
+
traces.set(resolved, trace);
|
|
304
|
+
return relabelAdhocMember(await execute(resolved, trace), member);
|
|
305
|
+
},
|
|
297
306
|
onExecuteError: (resolved, member, error) =>
|
|
298
307
|
relabelAdhocMember(
|
|
299
|
-
buildApiErrorEnvelopeFromResolved(
|
|
308
|
+
buildApiErrorEnvelopeFromResolved(
|
|
309
|
+
resolved,
|
|
310
|
+
error,
|
|
311
|
+
traces.get(resolved),
|
|
312
|
+
),
|
|
300
313
|
member,
|
|
301
314
|
),
|
|
302
315
|
sleep,
|