@tikoci/centrs 0.1.3 → 0.1.6
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 +348 -0
- package/GLOSSARY.txt +252 -0
- package/LICENSE +21 -0
- package/README.md +44 -12
- package/commands/AGENTS.md +14 -5
- package/commands/api/AGENTS.md +13 -1
- package/commands/api/README.md +56 -26
- package/commands/api/examples.md +46 -1
- package/commands/btest/README.md +21 -32
- package/commands/check/README.md +6 -1
- package/commands/discover/README.md +5 -11
- package/commands/execute/README.md +77 -36
- package/commands/execute/examples.md +120 -10
- package/commands/explain/README.md +2756 -0
- package/commands/explain/examples.md +702 -0
- package/commands/mcp/README.md +7 -4
- package/commands/mcp/examples.md +2 -2
- package/commands/retrieve/README.md +25 -27
- package/commands/retrieve/examples.md +16 -0
- package/commands/terminal/examples.md +7 -1
- package/commands/transfer/README.md +28 -20
- package/commands/transfer/examples.md +29 -0
- package/docs/CLI.md +353 -0
- package/docs/CONSTITUTION.md +138 -30
- package/docs/MATRIX.md +92 -18
- package/docs/errors/README.md +3 -0
- package/docs/errors/quickchr/machine-not-found.md +22 -0
- package/docs/errors/quickchr/machine-stopped.md +20 -0
- package/docs/errors/quickchr/package-unavailable.md +27 -0
- package/docs/errors/quickchr/unsupported-via.md +35 -0
- package/docs/errors/quickchr/unsupported.md +35 -0
- package/docs/errors/routeros/version-unsupported.md +26 -0
- package/docs/errors/transport/provider-trust.md +29 -0
- package/docs/errors/usage/stdin-ignored.md +28 -0
- package/docs/errors/validation/syntax.md +29 -4
- package/docs/errors/validation/unknown-attribute.md +37 -4
- package/package.json +56 -10
- package/src/api-fanout.ts +46 -18
- package/src/api.ts +351 -121
- package/src/cli/api.ts +26 -7
- package/src/cli/btest.ts +19 -3
- package/src/cli/execute.ts +25 -6
- package/src/cli/explain.ts +503 -0
- package/src/cli/retrieve.ts +33 -1
- package/src/cli/selection.ts +71 -11
- package/src/cli/settings.ts +2 -2
- package/src/cli/terminal.ts +1 -1
- package/src/cli/transfer.ts +23 -6
- package/src/cli.ts +15 -2
- package/src/core/envelope.ts +58 -2
- package/src/core/error-catalog.ts +42 -1
- package/src/core/execute-command.ts +148 -0
- package/src/core/inspect.ts +36 -4
- package/src/core/preflight.ts +306 -0
- package/src/core/routeros-string.ts +24 -0
- package/src/core/version.ts +4 -0
- package/src/core/yaml.ts +86 -0
- package/src/errors.ts +1 -0
- package/src/execute-fanout.ts +47 -18
- package/src/execute.ts +339 -307
- package/src/explain/arg-tokens.ts +151 -0
- package/src/explain/args.ts +1749 -0
- package/src/explain/blocks.ts +175 -0
- package/src/explain/brace-slots.ts +143 -0
- package/src/explain/brace-tokens.ts +39 -0
- package/src/explain/catalog.ts +1343 -0
- package/src/explain/coordinates.ts +488 -0
- package/src/explain/defects.ts +218 -0
- package/src/explain/escape-tokens.ts +77 -0
- package/src/explain/highlight-projection.ts +308 -0
- package/src/explain/is-known-menu.ts +17 -0
- package/src/explain/menus.ts +668 -0
- package/src/explain/operator-tokens.ts +316 -0
- package/src/explain/operators.ts +465 -0
- package/src/explain/path-tokens.ts +150 -0
- package/src/explain/pathresolve.ts +1569 -0
- package/src/explain/quoted-string.ts +331 -0
- package/src/explain/scope-brace.ts +461 -0
- package/src/explain/segment.ts +1165 -0
- package/src/explain/separator.ts +187 -0
- package/src/explain/split-owner.ts +194 -0
- package/src/explain/string-tokens.ts +77 -0
- package/src/explain/symbol-values.ts +556 -0
- package/src/explain/symbols.ts +1617 -0
- package/src/explain/token-ranges.ts +72 -0
- package/src/explain/transport.ts +514 -0
- package/src/explain/value-tokens.ts +83 -0
- package/src/explain/values.ts +357 -0
- package/src/explain/verbs.ts +64 -0
- package/src/explain/verbsplit.ts +955 -0
- package/src/explain/write.ts +856 -0
- package/src/explain.ts +2602 -0
- package/src/index.ts +225 -6
- package/src/mcp/server.ts +2 -1
- package/src/offline-gate.ts +148 -0
- package/src/protocols/mac-telnet-console.ts +61 -17
- package/src/resolver/facts.ts +1 -1
- package/src/resolver/index.ts +23 -4
- package/src/resolver/quickchr-provider.ts +597 -0
- package/src/resolver/selection.ts +99 -12
- package/src/resolver/service-endpoint.ts +155 -0
- package/src/resolver/target.ts +1 -1
- package/src/retrieve-fanout.ts +27 -13
- package/src/retrieve.ts +82 -77
- package/src/settings.ts +2 -1
- package/src/transfer-fanout.ts +43 -26
- package/src/transfer.ts +88 -45
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B2 arg fill — claims argument names and their `=` on the residual.
|
|
3
|
+
*
|
|
4
|
+
* This is the second B2 fill of #264, added after the operator fill. It runs
|
|
5
|
+
* **after** the proof-only spans (`comment` + `variable-*`) and **before** the
|
|
6
|
+
* operator fill, so the fill order is structural: `spans` claims first, then
|
|
7
|
+
* `arg`, then `operator` sees only what neither wanted. That ordering is why the
|
|
8
|
+
* operator fill can abstain on `, / = -` outside `( )`, on bytes glued after
|
|
9
|
+
* an argument `=`, and on bytes glued into an argument name — those bytes
|
|
10
|
+
* belong to a later fill, not to a smarter operator scanner.
|
|
11
|
+
*
|
|
12
|
+
* The source is `src/explain/args.ts`'s `Argument` — `span` and `valueSpan`
|
|
13
|
+
* already rebased into document analyzed-byte space by `src/explain.ts`. The
|
|
14
|
+
* name run is `[span.start, valueSpan.start - 1)` and the `=` is the single
|
|
15
|
+
* byte at `valueSpan.start - 1`; the value itself is `valueSpan` and is left
|
|
16
|
+
* for the next fill (see #293). A `positional` has no `name` and a `query` is a
|
|
17
|
+
* `?` word whose interior `args.ts` explicitly refuses to split (see
|
|
18
|
+
* `Argument.value`'s doc comment), so both are ignored on `kind` before any
|
|
19
|
+
* span is read — the `=` is derived from the token shape, never by scanning for
|
|
20
|
+
* the byte.
|
|
21
|
+
*
|
|
22
|
+
* `Argument.value` is not `Argument.text` and is never read: `value` is absent
|
|
23
|
+
* whenever the source spells a substitution or an escape this phase does not
|
|
24
|
+
* decode. This slice claims `span`/`valueSpan` *positions* only.
|
|
25
|
+
*
|
|
26
|
+
* The candidates are the SKIP-TOLERANT reading (#316), not `statement.arguments`:
|
|
27
|
+
* a token whose value only the device knows still has a `name=` run that is
|
|
28
|
+
* plainly an argument name, and abstaining on the whole statement because of its
|
|
29
|
+
* value left 48.2% of the corpus's argument lists unpainted. `value` was never
|
|
30
|
+
* read here (see below), so nothing about this fill's claim changes — only how
|
|
31
|
+
* many tokens reach it.
|
|
32
|
+
*
|
|
33
|
+
* Two things still reach it as nothing at all. A statement whose bytes were
|
|
34
|
+
* normalized is not addressable: `explain.ts` already refuses those (`its text
|
|
35
|
+
* was normalized`), and no token is rebased. And a WITHDRAWN reading — a
|
|
36
|
+
* second command-shaped run (#311) or a gate-parity disagreement — contributes
|
|
37
|
+
* nothing either: centrs cannot say which bytes are that command's arguments,
|
|
38
|
+
* and painting `name=` runs inside it would be that same claim in lexical
|
|
39
|
+
* clothing.
|
|
40
|
+
*
|
|
41
|
+
* **B5 split the `=` from the name** (#264). #293 emitted one `arg` class for
|
|
42
|
+
* both — emit first, name later — and two measurements then priced that merge:
|
|
43
|
+
* the `=` was the ENTIRE `arg` disagreement against the device, which reads a
|
|
44
|
+
* separator as `syntax-meta`, and it is not recoverable from the envelope,
|
|
45
|
+
* because a large minority of the runs this fill paints belong to a statement
|
|
46
|
+
* whose strict `arguments` reading refused — so there is no published
|
|
47
|
+
* `ExplainArgumentToken.valueSpan` to subtract one from. The spellings that
|
|
48
|
+
* dominate that share are the block binders (`do=`, `else=`, `in=`), which no
|
|
49
|
+
* strict reading ever publishes. The name run stays `arg`; the single `=` byte
|
|
50
|
+
* is `arg-sep`. Byte coverage does not move — this is a retag, and both runs
|
|
51
|
+
* keep `e11`, because the evidence is how `args.ts` located the bytes, not what
|
|
52
|
+
* they are called.
|
|
53
|
+
*
|
|
54
|
+
* The figures are deliberately not restated here: `bun run explain:arg-reach`
|
|
55
|
+
* re-derives the envelope gap and the leading spellings, and the agreement
|
|
56
|
+
* report re-derives the device cell. Both move when the corpus or the capture
|
|
57
|
+
* slice moves, and a count pasted into this header would not.
|
|
58
|
+
* `commands/explain/README.md` -> *The vocabulary, and what earns a class*
|
|
59
|
+
* carries them next to the generated blocks that keep them honest.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
import type { ExplainArgumentToken, ExplainToken } from "../explain.ts";
|
|
63
|
+
import { clipToResidual } from "./token-ranges.ts";
|
|
64
|
+
|
|
65
|
+
export type ArgCandidate = ExplainArgumentToken;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Argument name + `=` spans on the residual.
|
|
69
|
+
*
|
|
70
|
+
* `analyzed` is the ASCII-normalized document text (only used for length
|
|
71
|
+
* checks); `residual` is the gap set left by earlier fills (sorted, no
|
|
72
|
+
* overlaps); `candidates` are the already-rebased `Argument` tokens of the
|
|
73
|
+
* statements `explain.ts` decided may be painted. Every emitted span's bytes are fully inside
|
|
74
|
+
* `residual`, sorted by `start`, non-overlapping, and carry `ev: "e11"` with
|
|
75
|
+
* `class: "arg"` on the name run and `class: "arg-sep"` on the `=`.
|
|
76
|
+
*/
|
|
77
|
+
export function argSpans(
|
|
78
|
+
analyzed: string,
|
|
79
|
+
residual: readonly { start: number; end: number }[],
|
|
80
|
+
candidates: readonly ArgCandidate[],
|
|
81
|
+
): ExplainToken[] {
|
|
82
|
+
const len = analyzed.length;
|
|
83
|
+
if (len === 0 || residual.length === 0 || candidates.length === 0) return [];
|
|
84
|
+
|
|
85
|
+
const out: ExplainToken[] = [];
|
|
86
|
+
|
|
87
|
+
for (const candidate of candidates) {
|
|
88
|
+
if (candidate.kind !== "attribute") continue;
|
|
89
|
+
if (candidate.valueSpan === undefined) continue;
|
|
90
|
+
if (candidate.name === undefined) continue;
|
|
91
|
+
const nameStart = candidate.span.start;
|
|
92
|
+
const eqEnd = candidate.valueSpan.start;
|
|
93
|
+
const eqStart = eqEnd - 1;
|
|
94
|
+
const nameEnd = eqStart;
|
|
95
|
+
// Defensive: token shape must give non-empty name and single-byte `=`.
|
|
96
|
+
if (
|
|
97
|
+
!Number.isInteger(nameStart) ||
|
|
98
|
+
!Number.isInteger(nameEnd) ||
|
|
99
|
+
!Number.isInteger(eqStart) ||
|
|
100
|
+
!Number.isInteger(eqEnd)
|
|
101
|
+
)
|
|
102
|
+
continue;
|
|
103
|
+
if (nameStart < 0 || eqEnd > len) continue;
|
|
104
|
+
// Non-empty name. This is also what rejects `valueSpan.start === 0`, where
|
|
105
|
+
// `eqStart` would be -1: `nameEnd` is -1 too, so any `nameStart >= 0` trips
|
|
106
|
+
// it. `analyzed[-1]` on the next line would be `undefined` and continue
|
|
107
|
+
// anyway, so the order is safe either way — but the name check is the one
|
|
108
|
+
// that carries the intent.
|
|
109
|
+
if (nameStart >= nameEnd) continue;
|
|
110
|
+
// The whole token's `=` byte must be `=` in the analyzed text — a cheap
|
|
111
|
+
// shape check that catches a caller that passed un-rebased or misaligned
|
|
112
|
+
// spans. Not a scan for `=`; the position is derived from `valueSpan`.
|
|
113
|
+
if (analyzed[eqStart] !== "=") continue;
|
|
114
|
+
// Also ensure the candidate lies fully inside the analyzed length.
|
|
115
|
+
if (candidate.span.start < 0 || candidate.span.end > len) continue;
|
|
116
|
+
if (candidate.valueSpan.start < 0 || candidate.valueSpan.end > len)
|
|
117
|
+
continue;
|
|
118
|
+
|
|
119
|
+
// Two clips, not one, and in that order: the name run `[nameStart, eqStart)`
|
|
120
|
+
// then the single `=` byte `[eqStart, eqEnd)`. Each is offered to the
|
|
121
|
+
// residual on its own, so a `variable-*` span claiming only the `=` still
|
|
122
|
+
// leaves the name run behind and vice versa — the residual hole does the
|
|
123
|
+
// splitting, exactly as when the two were clipped together.
|
|
124
|
+
//
|
|
125
|
+
// Splitting here is also what makes cross-candidate fusion structurally
|
|
126
|
+
// impossible rather than merely unreachable. Two `arg` runs can never be
|
|
127
|
+
// adjacent, because every candidate puts an `arg-sep` byte after its name;
|
|
128
|
+
// coalescing adjacent runs globally would otherwise merge two distinct
|
|
129
|
+
// attributes into one token if `lexArguments` ever stopped guaranteeing a
|
|
130
|
+
// separator between them.
|
|
131
|
+
for (const r of clipToResidual(nameStart, eqStart, residual)) {
|
|
132
|
+
out.push({
|
|
133
|
+
start: r.start,
|
|
134
|
+
end: r.end,
|
|
135
|
+
class: "arg" as const,
|
|
136
|
+
ev: "e11",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
for (const r of clipToResidual(eqStart, eqEnd, residual)) {
|
|
140
|
+
out.push({
|
|
141
|
+
start: r.start,
|
|
142
|
+
end: r.end,
|
|
143
|
+
class: "arg-sep" as const,
|
|
144
|
+
ev: "e11",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
out.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
150
|
+
return out;
|
|
151
|
+
}
|