@tikoci/centrs 0.1.7 → 0.1.8
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 +256 -0
- package/GLOSSARY.txt +32 -0
- package/README.md +2 -2
- package/SECURITY.md +1 -1
- package/commands/AGENTS.md +2 -1
- package/commands/api/AGENTS.md +55 -30
- package/commands/api/README.md +123 -41
- package/commands/api/examples.md +205 -17
- package/commands/devices/README.md +11 -3
- package/commands/execute/README.md +31 -3
- package/commands/execute/examples.md +136 -1
- package/commands/explain/README.md +30 -4
- package/commands/mcp/README.md +5 -1
- package/commands/mcp/examples.md +18 -1
- package/commands/retrieve/README.md +348 -4
- package/commands/retrieve/examples.md +422 -5
- package/commands/terminal/README.md +14 -4
- package/commands/terminal/examples.md +22 -3
- package/commands/transfer/README.md +55 -9
- package/commands/transfer/examples.md +106 -7
- package/docs/CLI.md +28 -20
- package/docs/CONSTITUTION.md +30 -10
- package/docs/MATRIX.md +38 -1
- package/docs/errors/README.md +3 -1
- package/docs/errors/input/invalid-query.md +36 -0
- package/docs/errors/input/local-destination.md +20 -0
- package/docs/errors/input/unsupported-query.md +19 -0
- package/docs/errors/internal/byte-string.md +13 -0
- package/docs/errors/settings/invalid-timeout.md +6 -0
- package/docs/errors/transport/stream-overflow.md +21 -0
- package/docs/errors/usage/removed-flag.md +20 -0
- package/docs/errors/validation/menu-unavailable.md +22 -0
- package/docs/errors/validation/not-followable.md +23 -0
- package/docs/errors/validation/package-missing.md +29 -0
- package/docs/errors/validation/unknown-path.md +16 -4
- package/docs/errors/wait/deadline-exceeded.md +14 -0
- package/docs/errors/wait/interrupted.md +10 -0
- package/package.json +14 -11
- package/src/absent-menu.ts +280 -0
- package/src/api-fanout.ts +22 -12
- package/src/api.ts +511 -186
- package/src/btest.ts +19 -17
- package/src/cli/api.ts +126 -34
- package/src/cli/execute.ts +18 -5
- package/src/cli/retrieve.ts +223 -22
- package/src/cli/terminal.ts +10 -5
- package/src/cli/transfer.ts +23 -8
- package/src/core/envelope.ts +14 -0
- package/src/core/error-catalog.ts +56 -1
- package/src/core/inspect.ts +43 -5
- package/src/core/query.ts +508 -0
- package/src/core/routeros-errors.ts +1 -1
- package/src/devices.ts +6 -9
- package/src/discover.ts +7 -8
- package/src/errors.ts +116 -7
- package/src/execute-fanout.ts +13 -8
- package/src/execute.ts +106 -65
- package/src/explain/catalog.ts +58 -10
- package/src/explain/menus.ts +23 -2
- package/src/explain.ts +6 -6
- package/src/index.ts +29 -1
- package/src/mcp/safety.ts +43 -18
- package/src/mcp/tools.ts +14 -17
- package/src/protocols/adapter.ts +224 -118
- package/src/protocols/btest-session.ts +3 -2
- package/src/protocols/endpoint.ts +10 -0
- package/src/protocols/mac-telnet-console.ts +6 -3
- package/src/protocols/native-api.ts +223 -70
- package/src/protocols/sftp.ts +44 -14
- package/src/protocols/ssh.ts +42 -14
- package/src/resolver/cdb.ts +28 -7
- package/src/resolver/index.ts +1 -0
- package/src/resolver/target.ts +53 -7
- package/src/retrieve-fanout.ts +51 -12
- package/src/retrieve-follow.ts +862 -0
- package/src/retrieve-sample.ts +244 -0
- package/src/retrieve-stream.ts +115 -0
- package/src/retrieve-wait.ts +286 -0
- package/src/retrieve.ts +732 -108
- package/src/settings.ts +15 -15
- package/src/transfer-fanout.ts +40 -15
- package/src/transfer.ts +234 -32
package/src/explain/menus.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* the drift gate. The generator (`scripts/gen-explain-menus.ts`) carries the
|
|
6
6
|
* full rationale, the source pin, and why absence here is safe.
|
|
7
7
|
*
|
|
8
|
-
* Union of the `dir` and `path` nodes in
|
|
8
|
+
* Union of the `dir` and `path` nodes in 8 pinned restraml typed trees
|
|
9
9
|
* (`https://tikoci.github.io/restraml/`), all extra-packages builds:
|
|
10
10
|
*
|
|
11
11
|
* | Tree | Arch | RouterOS | Nodes | Containers |
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* | `7.24.4/extra/deep-inspect.arm64.json` | arm64 | 7.24.4 | 42,691 | 575 |
|
|
17
17
|
* | `7.25beta5/extra/deep-inspect.x86.json` | x86 | 7.25beta5 | 41,794 | 557 |
|
|
18
18
|
* | `7.25beta5/extra/deep-inspect.arm64.json` | arm64 | 7.25beta5 | 43,150 | 578 |
|
|
19
|
+
* | `7.26beta1/extra/deep-inspect.x86.json` | x86 | 7.26beta1 | 43,294 | 576 |
|
|
20
|
+
* | `7.26beta1/extra/deep-inspect.arm64.json` | arm64 | 7.26beta1 | 44,650 | 597 |
|
|
19
21
|
*
|
|
20
22
|
* Zero node-type conflicts across those trees — no `dir`↔`cmd` flip across
|
|
21
|
-
*
|
|
23
|
+
* 5 versions or across architectures — which is what makes the union
|
|
22
24
|
* version-less rather than version-keyed. Generation aborts if that ever stops
|
|
23
25
|
* holding.
|
|
24
26
|
*
|
|
@@ -57,6 +59,18 @@ export const MENU_PATHS: ReadonlySet<string> = new Set([
|
|
|
57
59
|
"/certificate/scep-server/ra",
|
|
58
60
|
"/certificate/scep-server/requests",
|
|
59
61
|
"/certificate/settings",
|
|
62
|
+
"/cmr",
|
|
63
|
+
"/cmr/alert",
|
|
64
|
+
"/cmr/client",
|
|
65
|
+
"/cmr/device",
|
|
66
|
+
"/cmr/layout",
|
|
67
|
+
"/cmr/layout/link",
|
|
68
|
+
"/cmr/layout/node",
|
|
69
|
+
"/cmr/upgrade",
|
|
70
|
+
"/cmr/upgrade/job",
|
|
71
|
+
"/cmr/vlan",
|
|
72
|
+
"/cmr/wifi",
|
|
73
|
+
"/cmr/wifi/radio",
|
|
60
74
|
"/console",
|
|
61
75
|
"/console/settings",
|
|
62
76
|
"/container",
|
|
@@ -183,6 +197,8 @@ export const MENU_PATHS: ReadonlySet<string> = new Set([
|
|
|
183
197
|
"/interface/wifi/capsman/remote-cap",
|
|
184
198
|
"/interface/wifi/channel",
|
|
185
199
|
"/interface/wifi/configuration",
|
|
200
|
+
"/interface/wifi/dashboard",
|
|
201
|
+
"/interface/wifi/dashboard/settings",
|
|
186
202
|
"/interface/wifi/datapath",
|
|
187
203
|
"/interface/wifi/interworking",
|
|
188
204
|
"/interface/wifi/network",
|
|
@@ -526,6 +542,8 @@ export const MENU_PATHS: ReadonlySet<string> = new Set([
|
|
|
526
542
|
"/system/clock/manual",
|
|
527
543
|
"/system/console",
|
|
528
544
|
"/system/console/screen",
|
|
545
|
+
"/system/dashboard",
|
|
546
|
+
"/system/dashboard/settings",
|
|
529
547
|
"/system/default-configuration",
|
|
530
548
|
"/system/default-configuration/caps-mode-script",
|
|
531
549
|
"/system/default-configuration/custom-script",
|
|
@@ -583,6 +601,9 @@ export const MENU_PATHS: ReadonlySet<string> = new Set([
|
|
|
583
601
|
"/task",
|
|
584
602
|
"/terminal",
|
|
585
603
|
"/tool",
|
|
604
|
+
"/tool/apptraffic",
|
|
605
|
+
"/tool/apptraffic/stats",
|
|
606
|
+
"/tool/apptraffic/stats/categories",
|
|
586
607
|
"/tool/bandwidth-server",
|
|
587
608
|
"/tool/bandwidth-server/session",
|
|
588
609
|
"/tool/calea",
|
package/src/explain.ts
CHANGED
|
@@ -108,7 +108,11 @@ import {
|
|
|
108
108
|
isWriteShaped,
|
|
109
109
|
} from "./core/execute-command.ts";
|
|
110
110
|
import { toYaml } from "./core/yaml.ts";
|
|
111
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
CentrsError,
|
|
113
|
+
formatCentrsErrorText,
|
|
114
|
+
serializeCentrsError,
|
|
115
|
+
} from "./errors.ts";
|
|
112
116
|
import { argSpans } from "./explain/arg-tokens.ts";
|
|
113
117
|
import {
|
|
114
118
|
type ArgumentKind,
|
|
@@ -2495,11 +2499,7 @@ function renderExplainText(
|
|
|
2495
2499
|
): string {
|
|
2496
2500
|
const lines: string[] = [];
|
|
2497
2501
|
if (!envelope.ok) {
|
|
2498
|
-
lines.push(
|
|
2499
|
-
if (envelope.error.remediation)
|
|
2500
|
-
lines.push(`Fix: ${envelope.error.remediation}`);
|
|
2501
|
-
if (envelope.error.detailsUrl)
|
|
2502
|
-
lines.push(`Details: ${envelope.error.detailsUrl}`);
|
|
2502
|
+
lines.push(formatCentrsErrorText(envelope.error));
|
|
2503
2503
|
lines.push(...tipLines(envelope.tips));
|
|
2504
2504
|
return lines.join("\n");
|
|
2505
2505
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,10 +12,10 @@ export {
|
|
|
12
12
|
type ApiSuccessEnvelope,
|
|
13
13
|
api,
|
|
14
14
|
apiEnvelope,
|
|
15
|
-
apiListen,
|
|
16
15
|
apiMethods,
|
|
17
16
|
apiOutputFormats,
|
|
18
17
|
apiRequestSummaryFromRequest,
|
|
18
|
+
apiStream,
|
|
19
19
|
buildApiBody,
|
|
20
20
|
buildApiErrorEnvelope,
|
|
21
21
|
buildApiErrorEnvelopeFromResolved,
|
|
@@ -85,6 +85,7 @@ export {
|
|
|
85
85
|
normalizeWarnings,
|
|
86
86
|
type SettingSource,
|
|
87
87
|
type SettingSourceKind,
|
|
88
|
+
type ValidationTrace,
|
|
88
89
|
type Warning,
|
|
89
90
|
} from "./core/envelope.ts";
|
|
90
91
|
export {
|
|
@@ -576,24 +577,36 @@ export {
|
|
|
576
577
|
type TargetSelection,
|
|
577
578
|
toCoreSource,
|
|
578
579
|
} from "./resolver/index.ts";
|
|
580
|
+
export type { RetrieveWaitMeta, RetrieveWaitSettings } from "./retrieve.ts";
|
|
579
581
|
export {
|
|
580
582
|
buildResolvedRetrieve,
|
|
581
583
|
buildRetrieveErrorEnvelope,
|
|
582
584
|
buildRetrieveErrorEnvelopeFromResolved,
|
|
585
|
+
FOLLOW_SWEEP_DEFAULT_MS,
|
|
583
586
|
type ResolvedRetrieveRequest,
|
|
584
587
|
type RetrieveEnvelope,
|
|
585
588
|
type RetrieveErrorEnvelope,
|
|
589
|
+
type RetrieveFollowCounts,
|
|
590
|
+
type RetrieveFollowSettings,
|
|
591
|
+
type RetrieveFollowStopReason,
|
|
592
|
+
type RetrieveFollowSummary,
|
|
586
593
|
type RetrieveGlobalContext,
|
|
587
594
|
type RetrieveOperationMeta,
|
|
595
|
+
type RetrieveOptions,
|
|
588
596
|
type RetrieveOutputFormat,
|
|
589
597
|
type RetrieveRequest,
|
|
590
598
|
type RetrieveRequestSummary,
|
|
599
|
+
type RetrieveSampleSettings,
|
|
600
|
+
type RetrieveSampleStopReason,
|
|
601
|
+
type RetrieveSampleSummary,
|
|
602
|
+
type RetrieveStreamMeta,
|
|
591
603
|
type RetrieveSuccessEnvelope,
|
|
592
604
|
type RetrieveWarning,
|
|
593
605
|
renderRetrieveEnvelope,
|
|
594
606
|
resolveRetrieveGlobalContext,
|
|
595
607
|
resolveRetrieveRequest,
|
|
596
608
|
retrieve,
|
|
609
|
+
retrieveEnvelope,
|
|
597
610
|
retrieveOutputFormats,
|
|
598
611
|
runResolvedRetrieve,
|
|
599
612
|
validateRetrieveRequestShape,
|
|
@@ -611,6 +624,17 @@ export {
|
|
|
611
624
|
retrieveFanout,
|
|
612
625
|
retrieveGroup,
|
|
613
626
|
} from "./retrieve-fanout.ts";
|
|
627
|
+
export {
|
|
628
|
+
FOLLOW_BUFFER_LIMIT,
|
|
629
|
+
type RetrieveFollowOptions,
|
|
630
|
+
retrieveFollow,
|
|
631
|
+
} from "./retrieve-follow.ts";
|
|
632
|
+
export {
|
|
633
|
+
type RetrieveSampleOptions,
|
|
634
|
+
retrieveSample,
|
|
635
|
+
} from "./retrieve-sample.ts";
|
|
636
|
+
export { renderRetrieveStreamLine } from "./retrieve-stream.ts";
|
|
637
|
+
export { type RetrieveWaitOptions, retrieveWait } from "./retrieve-wait.ts";
|
|
614
638
|
export {
|
|
615
639
|
buildSettingsErrorEnvelope,
|
|
616
640
|
renderSettingsEnvelope,
|
|
@@ -652,10 +676,12 @@ export {
|
|
|
652
676
|
} from "./terminal.ts";
|
|
653
677
|
export {
|
|
654
678
|
buildTransferErrorEnvelope,
|
|
679
|
+
buildTransferErrorEnvelopeFromResolved,
|
|
655
680
|
FILE_READ_CHUNK_BYTES,
|
|
656
681
|
isTransferWriteVerb,
|
|
657
682
|
normalizeRemotePath,
|
|
658
683
|
REST_FILE_WRITE_CAP_BYTES,
|
|
684
|
+
type ResolvedTransferRequest,
|
|
659
685
|
renderTransferEnvelope,
|
|
660
686
|
resolveTransferRequest,
|
|
661
687
|
runResolvedTransfer,
|
|
@@ -666,11 +692,13 @@ export {
|
|
|
666
692
|
type TransferMethod,
|
|
667
693
|
type TransferOp,
|
|
668
694
|
type TransferOperationMeta,
|
|
695
|
+
type TransferOptions,
|
|
669
696
|
type TransferOutputFormat,
|
|
670
697
|
type TransferRequest,
|
|
671
698
|
type TransferSuccessEnvelope,
|
|
672
699
|
type TransferVerb,
|
|
673
700
|
transfer,
|
|
701
|
+
transferEnvelope,
|
|
674
702
|
transferMethods,
|
|
675
703
|
transferOutputFormats,
|
|
676
704
|
transferVerbs,
|
package/src/mcp/safety.ts
CHANGED
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
* `mcp=rw`, else `cdb/write-not-permitted`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
type DevicesShowItem,
|
|
15
|
+
type LoadedCdb,
|
|
16
|
+
loadCdb,
|
|
17
|
+
showDevice,
|
|
18
|
+
} from "../devices.ts";
|
|
14
19
|
import { CentrsError } from "../errors.ts";
|
|
15
20
|
import type { McpWritePolicy } from "../mcp.ts";
|
|
16
21
|
import { parseCommentKv } from "../resolver/comment-kv.ts";
|
|
@@ -52,30 +57,50 @@ export async function loadAllowlistCdb(
|
|
|
52
57
|
* `cdb/target-not-registered` when no record matches — the friendly,
|
|
53
58
|
* MCP-specific framing of "this device is not on the allowlist", pointing at
|
|
54
59
|
* `centrs_devices add` rather than leaking the CLI's `cdb/not-found-target`.
|
|
60
|
+
*
|
|
61
|
+
* Matches with {@link showDevice}, the matcher the core resolver dials with
|
|
62
|
+
* (target, `identity=`, `mac=`, `ip=`), so the record whose write policy is
|
|
63
|
+
* checked here is the record the call then runs against (#444). A name that
|
|
64
|
+
* matches more than one record fails `identity/ambiguous` rather than taking
|
|
65
|
+
* the first.
|
|
55
66
|
*/
|
|
56
67
|
export function resolveRegisteredTarget(
|
|
57
68
|
cdb: LoadedCdb,
|
|
58
69
|
target: string,
|
|
59
70
|
): RegisteredTarget {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
let entry: DevicesShowItem;
|
|
72
|
+
try {
|
|
73
|
+
entry = showDevice({ cdb, target }).data.entry;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof CentrsError && error.code === "cdb/not-found-target") {
|
|
76
|
+
throw new CentrsError({
|
|
77
|
+
code: "cdb/target-not-registered",
|
|
78
|
+
summary: `Target "${target}" is not registered in the CDB allowlist.`,
|
|
79
|
+
remediation:
|
|
80
|
+
"Register the device first with the `centrs_devices` tool (op `add`) or `centrs devices add`, then retry. The MCP server only acts on CDB-registered targets, by target or by its `identity=`, `mac=` or `ip=` comment key.",
|
|
81
|
+
context: { target, cdbFile: cdb.settings.cdbFile.value },
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// The CLI remediation names `--match`, which no MCP tool takes.
|
|
85
|
+
if (error instanceof CentrsError && error.code === "identity/ambiguous") {
|
|
86
|
+
throw new CentrsError({
|
|
87
|
+
code: error.code,
|
|
88
|
+
summary: error.summary,
|
|
89
|
+
remediation:
|
|
90
|
+
"Retry with one candidate's address from `context.matches[].target` as `target`. If the candidates share that address too, the CDB holds duplicate records for it: remove or edit the extra one with the `centrs_devices` tool (op `remove` or `set`).",
|
|
91
|
+
context: error.context,
|
|
92
|
+
cause: error,
|
|
93
|
+
});
|
|
70
94
|
}
|
|
95
|
+
throw error;
|
|
71
96
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
97
|
+
return {
|
|
98
|
+
target: entry.target,
|
|
99
|
+
user: entry.user,
|
|
100
|
+
group: entry.group,
|
|
101
|
+
recordIndex: entry.cdbRecordIndex,
|
|
102
|
+
writePolicy: readWritePolicy(entry.comment),
|
|
103
|
+
};
|
|
79
104
|
}
|
|
80
105
|
|
|
81
106
|
/**
|
package/src/mcp/tools.ts
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
import type { CommentKvUpdate } from "../resolver/comment-kv.ts";
|
|
37
37
|
import { parseBbox, parseNear } from "../resolver/geo.ts";
|
|
38
38
|
import { parseDuration } from "../resolver/settings.ts";
|
|
39
|
-
import {
|
|
39
|
+
import { retrieveEnvelope } from "../retrieve.ts";
|
|
40
40
|
import {
|
|
41
41
|
buildRetrieveFanoutErrorEnvelope,
|
|
42
42
|
retrieveGroup,
|
|
@@ -193,7 +193,7 @@ export const validateInputShape = {
|
|
|
193
193
|
target: z
|
|
194
194
|
.string()
|
|
195
195
|
.describe(
|
|
196
|
-
"CDB-registered target
|
|
196
|
+
"CDB-registered target: its address (IP / URL / MAC) or its identity=, mac= or ip= comment key. Must be on the allowlist.",
|
|
197
197
|
),
|
|
198
198
|
command: z.string().describe("RouterOS CLI command to dry-run."),
|
|
199
199
|
via: z
|
|
@@ -248,7 +248,9 @@ export const retrieveInputShape = {
|
|
|
248
248
|
target: z
|
|
249
249
|
.string()
|
|
250
250
|
.optional()
|
|
251
|
-
.describe(
|
|
251
|
+
.describe(
|
|
252
|
+
"CDB-registered single target: its address (IP / URL / MAC) or its identity=, mac= or ip= comment key. Use either target or group.",
|
|
253
|
+
),
|
|
252
254
|
group: z
|
|
253
255
|
.string()
|
|
254
256
|
.optional()
|
|
@@ -264,11 +266,13 @@ export const retrieveInputShape = {
|
|
|
264
266
|
.boolean()
|
|
265
267
|
.optional()
|
|
266
268
|
.describe("Return every attribute instead of the default projection."),
|
|
267
|
-
filter: z.string().optional().describe("
|
|
269
|
+
filter: z.string().optional().describe("Alias of `query`."),
|
|
268
270
|
query: z
|
|
269
|
-
.string()
|
|
271
|
+
.union([z.string(), z.array(z.string())])
|
|
270
272
|
.optional()
|
|
271
|
-
.describe(
|
|
273
|
+
.describe(
|
|
274
|
+
"Row filter written as after `print where`, e.g. `type=ether and !disabled`; run on the router, several are AND-ed. No `~` (regex) or `in`.",
|
|
275
|
+
),
|
|
272
276
|
via: z.string().optional().describe("Override transport."),
|
|
273
277
|
timeout: z
|
|
274
278
|
.union([z.string(), z.number()])
|
|
@@ -324,18 +328,11 @@ export async function handleRetrieve(
|
|
|
324
328
|
cdbPassword: config.cdbPassword,
|
|
325
329
|
};
|
|
326
330
|
|
|
331
|
+
if (args.group === undefined) return await retrieveEnvelope(request);
|
|
327
332
|
try {
|
|
328
|
-
|
|
329
|
-
args.group !== undefined
|
|
330
|
-
? await retrieveGroup(request)
|
|
331
|
-
: await retrieve(request);
|
|
332
|
-
return envelope;
|
|
333
|
+
return await retrieveGroup(request);
|
|
333
334
|
} catch (error) {
|
|
334
|
-
|
|
335
|
-
args.group !== undefined
|
|
336
|
-
? buildRetrieveFanoutErrorEnvelope(request, error)
|
|
337
|
-
: buildRetrieveErrorEnvelope(request, error);
|
|
338
|
-
return envelope;
|
|
335
|
+
return buildRetrieveFanoutErrorEnvelope(request, error);
|
|
339
336
|
}
|
|
340
337
|
}
|
|
341
338
|
|
|
@@ -345,7 +342,7 @@ export const executeInputShape = {
|
|
|
345
342
|
target: z
|
|
346
343
|
.string()
|
|
347
344
|
.describe(
|
|
348
|
-
"CDB-registered target. Inline host+credentials are not accepted.",
|
|
345
|
+
"CDB-registered target: its address (IP / URL / MAC) or its identity=, mac= or ip= comment key. Inline host+credentials are not accepted.",
|
|
349
346
|
),
|
|
350
347
|
command: z.string().describe("RouterOS CLI command to run."),
|
|
351
348
|
confirm: z
|