@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.
Files changed (107) hide show
  1. package/CHANGELOG.md +348 -0
  2. package/GLOSSARY.txt +252 -0
  3. package/LICENSE +21 -0
  4. package/README.md +44 -12
  5. package/commands/AGENTS.md +14 -5
  6. package/commands/api/AGENTS.md +13 -1
  7. package/commands/api/README.md +56 -26
  8. package/commands/api/examples.md +46 -1
  9. package/commands/btest/README.md +21 -32
  10. package/commands/check/README.md +6 -1
  11. package/commands/discover/README.md +5 -11
  12. package/commands/execute/README.md +77 -36
  13. package/commands/execute/examples.md +120 -10
  14. package/commands/explain/README.md +2756 -0
  15. package/commands/explain/examples.md +702 -0
  16. package/commands/mcp/README.md +7 -4
  17. package/commands/mcp/examples.md +2 -2
  18. package/commands/retrieve/README.md +25 -27
  19. package/commands/retrieve/examples.md +16 -0
  20. package/commands/terminal/examples.md +7 -1
  21. package/commands/transfer/README.md +28 -20
  22. package/commands/transfer/examples.md +29 -0
  23. package/docs/CLI.md +353 -0
  24. package/docs/CONSTITUTION.md +138 -30
  25. package/docs/MATRIX.md +92 -18
  26. package/docs/errors/README.md +3 -0
  27. package/docs/errors/quickchr/machine-not-found.md +22 -0
  28. package/docs/errors/quickchr/machine-stopped.md +20 -0
  29. package/docs/errors/quickchr/package-unavailable.md +27 -0
  30. package/docs/errors/quickchr/unsupported-via.md +35 -0
  31. package/docs/errors/quickchr/unsupported.md +35 -0
  32. package/docs/errors/routeros/version-unsupported.md +26 -0
  33. package/docs/errors/transport/provider-trust.md +29 -0
  34. package/docs/errors/usage/stdin-ignored.md +28 -0
  35. package/docs/errors/validation/syntax.md +29 -4
  36. package/docs/errors/validation/unknown-attribute.md +37 -4
  37. package/package.json +56 -10
  38. package/src/api-fanout.ts +46 -18
  39. package/src/api.ts +351 -121
  40. package/src/cli/api.ts +26 -7
  41. package/src/cli/btest.ts +19 -3
  42. package/src/cli/execute.ts +25 -6
  43. package/src/cli/explain.ts +503 -0
  44. package/src/cli/retrieve.ts +33 -1
  45. package/src/cli/selection.ts +71 -11
  46. package/src/cli/settings.ts +2 -2
  47. package/src/cli/terminal.ts +1 -1
  48. package/src/cli/transfer.ts +23 -6
  49. package/src/cli.ts +15 -2
  50. package/src/core/envelope.ts +58 -2
  51. package/src/core/error-catalog.ts +42 -1
  52. package/src/core/execute-command.ts +148 -0
  53. package/src/core/inspect.ts +36 -4
  54. package/src/core/preflight.ts +306 -0
  55. package/src/core/routeros-string.ts +24 -0
  56. package/src/core/version.ts +4 -0
  57. package/src/core/yaml.ts +86 -0
  58. package/src/errors.ts +1 -0
  59. package/src/execute-fanout.ts +47 -18
  60. package/src/execute.ts +339 -307
  61. package/src/explain/arg-tokens.ts +151 -0
  62. package/src/explain/args.ts +1749 -0
  63. package/src/explain/blocks.ts +175 -0
  64. package/src/explain/brace-slots.ts +143 -0
  65. package/src/explain/brace-tokens.ts +39 -0
  66. package/src/explain/catalog.ts +1343 -0
  67. package/src/explain/coordinates.ts +488 -0
  68. package/src/explain/defects.ts +218 -0
  69. package/src/explain/escape-tokens.ts +77 -0
  70. package/src/explain/highlight-projection.ts +308 -0
  71. package/src/explain/is-known-menu.ts +17 -0
  72. package/src/explain/menus.ts +668 -0
  73. package/src/explain/operator-tokens.ts +316 -0
  74. package/src/explain/operators.ts +465 -0
  75. package/src/explain/path-tokens.ts +150 -0
  76. package/src/explain/pathresolve.ts +1569 -0
  77. package/src/explain/quoted-string.ts +331 -0
  78. package/src/explain/scope-brace.ts +461 -0
  79. package/src/explain/segment.ts +1165 -0
  80. package/src/explain/separator.ts +187 -0
  81. package/src/explain/split-owner.ts +194 -0
  82. package/src/explain/string-tokens.ts +77 -0
  83. package/src/explain/symbol-values.ts +556 -0
  84. package/src/explain/symbols.ts +1617 -0
  85. package/src/explain/token-ranges.ts +72 -0
  86. package/src/explain/transport.ts +514 -0
  87. package/src/explain/value-tokens.ts +83 -0
  88. package/src/explain/values.ts +357 -0
  89. package/src/explain/verbs.ts +64 -0
  90. package/src/explain/verbsplit.ts +955 -0
  91. package/src/explain/write.ts +856 -0
  92. package/src/explain.ts +2602 -0
  93. package/src/index.ts +225 -6
  94. package/src/mcp/server.ts +2 -1
  95. package/src/offline-gate.ts +148 -0
  96. package/src/protocols/mac-telnet-console.ts +61 -17
  97. package/src/resolver/facts.ts +1 -1
  98. package/src/resolver/index.ts +23 -4
  99. package/src/resolver/quickchr-provider.ts +597 -0
  100. package/src/resolver/selection.ts +99 -12
  101. package/src/resolver/service-endpoint.ts +155 -0
  102. package/src/resolver/target.ts +1 -1
  103. package/src/retrieve-fanout.ts +27 -13
  104. package/src/retrieve.ts +82 -77
  105. package/src/settings.ts +2 -1
  106. package/src/transfer-fanout.ts +43 -26
  107. 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
+ }