@tikoci/centrs 0.1.1 → 0.1.3
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 +107 -2
- package/GLOSSARY.txt +87 -0
- package/README.md +25 -9
- package/SECURITY.md +3 -3
- package/commands/AGENTS.md +3 -2
- package/commands/api/AGENTS.md +212 -0
- package/commands/api/README.md +177 -0
- package/commands/api/examples.md +428 -0
- package/commands/btest/README.md +56 -31
- package/commands/btest/examples.md +35 -10
- package/commands/check/README.md +371 -53
- package/commands/check/examples.md +390 -0
- package/commands/devices/README.md +177 -37
- package/commands/devices/examples.md +148 -0
- package/commands/execute/README.md +48 -7
- package/commands/execute/examples.md +90 -6
- package/commands/retrieve/README.md +37 -51
- package/commands/retrieve/examples.md +40 -9
- package/commands/settings/README.md +286 -22
- package/commands/settings/examples.md +408 -0
- package/commands/terminal/README.md +6 -0
- package/commands/transfer/README.md +32 -2
- package/commands/transfer/examples.md +66 -0
- package/docs/CONSTITUTION.md +40 -15
- package/docs/MATRIX.md +69 -142
- package/docs/errors/README.md +2 -1
- package/docs/errors/cdb/empty-selection.md +19 -0
- package/docs/errors/input/incomplete-gps.md +9 -0
- package/docs/errors/input/invalid-altitude.md +9 -0
- package/docs/errors/input/invalid-bbox.md +9 -0
- package/docs/errors/input/invalid-coordinate.md +9 -0
- package/docs/errors/input/invalid-radius.md +9 -0
- package/docs/errors/internal/settings-failed.md +9 -0
- package/docs/errors/routeros/btest-connection-count-single-stream.md +22 -0
- package/docs/errors/routeros/btest-udp-tx-size-ignored.md +15 -0
- package/docs/errors/settings/consequential-value.md +26 -0
- package/docs/errors/settings/invalid-boolean.md +21 -3
- package/docs/errors/settings/invalid-format.md +18 -3
- package/docs/errors/settings/invalid-integer.md +18 -3
- package/docs/errors/settings/invalid-timeout.md +20 -3
- package/docs/errors/settings/invalid-via.md +22 -3
- package/docs/errors/settings/reserved-key.md +29 -0
- package/docs/errors/settings/skip-env-file-active.md +25 -0
- package/docs/errors/settings/unknown-key.md +25 -0
- package/docs/errors/usage/fanout-not-supported.md +16 -0
- package/docs/errors/usage/invalid-method.md +23 -0
- package/package.json +9 -9
- package/src/api-fanout.ts +454 -0
- package/src/api.ts +1627 -0
- package/src/btest.ts +61 -1
- package/src/cli/api.ts +660 -0
- package/src/cli/btest.ts +20 -2
- package/src/cli/common.ts +75 -0
- package/src/cli/devices.ts +235 -6
- package/src/cli/discover.ts +3 -1
- package/src/cli/execute.ts +107 -6
- package/src/cli/mcp.ts +8 -1
- package/src/cli/retrieve.ts +135 -90
- package/src/cli/selection.ts +200 -0
- package/src/cli/settings.ts +286 -0
- package/src/cli/terminal.ts +22 -7
- package/src/cli/transfer.ts +171 -47
- package/src/cli.ts +29 -0
- package/src/core/error-catalog.ts +69 -0
- package/src/core/fanout.ts +301 -0
- package/src/core/inspect.ts +170 -0
- package/src/devices.ts +212 -20
- package/src/discover.ts +7 -2
- package/src/execute-fanout.ts +438 -0
- package/src/execute.ts +134 -70
- package/src/index.ts +166 -9
- package/src/mcp/config.ts +12 -2
- package/src/mcp/tools.ts +137 -2
- package/src/protocols/adapter.ts +357 -9
- package/src/protocols/btest-session.ts +279 -46
- package/src/protocols/btest.ts +6 -2
- package/src/protocols/index.ts +11 -5
- package/src/protocols/native-api.ts +223 -30
- package/src/resolver/cdb.ts +10 -2
- package/src/resolver/comment-kv.ts +58 -4
- package/src/resolver/config-file.ts +136 -0
- package/src/resolver/facts.ts +78 -0
- package/src/resolver/geo.ts +453 -0
- package/src/resolver/index.ts +46 -0
- package/src/resolver/mac.ts +12 -3
- package/src/resolver/selection.ts +304 -0
- package/src/resolver/settings.ts +54 -5
- package/src/resolver/target.ts +20 -2
- package/src/retrieve-fanout.ts +232 -268
- package/src/retrieve.ts +69 -94
- package/src/settings.ts +1078 -0
- package/src/terminal.ts +11 -1
- package/src/transfer-fanout.ts +527 -0
- package/src/transfer.ts +61 -11
- package/commands/stream/README.md +0 -70
package/src/retrieve.ts
CHANGED
|
@@ -6,6 +6,15 @@ import type {
|
|
|
6
6
|
SettingSource as CoreSettingSource,
|
|
7
7
|
EnvelopeValidationMeta,
|
|
8
8
|
} from "./core/envelope.ts";
|
|
9
|
+
import {
|
|
10
|
+
extractCompletionNames,
|
|
11
|
+
inspectChildren,
|
|
12
|
+
inspectChildrenOrEmpty,
|
|
13
|
+
inspectCompletions,
|
|
14
|
+
isArgumentNode,
|
|
15
|
+
isCommandNode,
|
|
16
|
+
pathTokens,
|
|
17
|
+
} from "./core/inspect.ts";
|
|
9
18
|
import { CentrsError, serializeCentrsError } from "./errors.ts";
|
|
10
19
|
import {
|
|
11
20
|
createProtocolAdapter,
|
|
@@ -19,6 +28,7 @@ import {
|
|
|
19
28
|
import {
|
|
20
29
|
type CdbResolution,
|
|
21
30
|
isIpTransport,
|
|
31
|
+
loadEnvFileDefaults,
|
|
22
32
|
parseDuration,
|
|
23
33
|
parseResolvePolicy,
|
|
24
34
|
type ResolvedAuth,
|
|
@@ -101,20 +111,6 @@ export type RetrieveSuccessEnvelope = CentrsSuccessEnvelope<
|
|
|
101
111
|
>;
|
|
102
112
|
export type RetrieveErrorEnvelope = CentrsErrorEnvelope<RetrieveOperationMeta>;
|
|
103
113
|
|
|
104
|
-
interface InspectChildItem {
|
|
105
|
-
type?: string;
|
|
106
|
-
name?: string;
|
|
107
|
-
"node-type"?: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
interface InspectCompletionItem {
|
|
111
|
-
type?: string;
|
|
112
|
-
name?: string;
|
|
113
|
-
completion?: string;
|
|
114
|
-
value?: string;
|
|
115
|
-
text?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
114
|
interface RetrieveInspection {
|
|
119
115
|
command: "get" | "print";
|
|
120
116
|
}
|
|
@@ -623,9 +619,10 @@ export function buildResolvedRetrieve(
|
|
|
623
619
|
cdbResolution: CdbResolution | undefined,
|
|
624
620
|
attributeSelections: readonly string[],
|
|
625
621
|
macResolution?: { mac: string; ip: string },
|
|
622
|
+
config: Record<string, string | undefined> = {},
|
|
626
623
|
): ResolvedRetrieveRequest {
|
|
627
|
-
const via = resolveProtocol(request, env, cdbResolution);
|
|
628
|
-
const format = resolveFormat(request, env);
|
|
624
|
+
const via = resolveProtocol(request, env, cdbResolution, config);
|
|
625
|
+
const format = resolveFormat(request, env, config);
|
|
629
626
|
const validate = resolveBooleanSetting(
|
|
630
627
|
request.validate,
|
|
631
628
|
env,
|
|
@@ -633,18 +630,22 @@ export function buildResolvedRetrieve(
|
|
|
633
630
|
true,
|
|
634
631
|
"validate",
|
|
635
632
|
cdbResolution?.overrides.validate,
|
|
633
|
+
config,
|
|
636
634
|
);
|
|
637
635
|
const timeoutMs = resolveTimeoutSetting(
|
|
638
636
|
request.timeout,
|
|
639
637
|
env,
|
|
640
638
|
via.value,
|
|
641
639
|
cdbResolution?.overrides.timeoutMs,
|
|
640
|
+
config,
|
|
642
641
|
);
|
|
643
642
|
const maxResultsBytes = resolveOptionalIntegerSetting(
|
|
644
643
|
request.maxResultsBytes,
|
|
645
644
|
env,
|
|
646
645
|
"CENTRS_MAX_RESULTS",
|
|
647
646
|
"max-results",
|
|
647
|
+
undefined,
|
|
648
|
+
config,
|
|
648
649
|
);
|
|
649
650
|
const target = resolveTarget(
|
|
650
651
|
{
|
|
@@ -696,22 +697,33 @@ export function resolveRetrieveGlobalContext(
|
|
|
696
697
|
request: RetrieveRequest,
|
|
697
698
|
env: Record<string, string | undefined>,
|
|
698
699
|
attributeSelections: readonly string[],
|
|
700
|
+
config: Record<string, string | undefined> = {},
|
|
699
701
|
): RetrieveGlobalContext {
|
|
700
|
-
const via = resolveProtocol(request, env);
|
|
701
|
-
const format = resolveFormat(request, env);
|
|
702
|
+
const via = resolveProtocol(request, env, undefined, config);
|
|
703
|
+
const format = resolveFormat(request, env, config);
|
|
702
704
|
const validate = resolveBooleanSetting(
|
|
703
705
|
request.validate,
|
|
704
706
|
env,
|
|
705
707
|
"CENTRS_VALIDATE",
|
|
706
708
|
true,
|
|
707
709
|
"validate",
|
|
710
|
+
undefined,
|
|
711
|
+
config,
|
|
712
|
+
);
|
|
713
|
+
const timeoutMs = resolveTimeoutSetting(
|
|
714
|
+
request.timeout,
|
|
715
|
+
env,
|
|
716
|
+
via.value,
|
|
717
|
+
undefined,
|
|
718
|
+
config,
|
|
708
719
|
);
|
|
709
|
-
const timeoutMs = resolveTimeoutSetting(request.timeout, env, via.value);
|
|
710
720
|
const maxResultsBytes = resolveOptionalIntegerSetting(
|
|
711
721
|
request.maxResultsBytes,
|
|
712
722
|
env,
|
|
713
723
|
"CENTRS_MAX_RESULTS",
|
|
714
724
|
"max-results",
|
|
725
|
+
undefined,
|
|
726
|
+
config,
|
|
715
727
|
);
|
|
716
728
|
|
|
717
729
|
return {
|
|
@@ -744,6 +756,7 @@ export async function resolveRetrieveRequest(
|
|
|
744
756
|
env: Record<string, string | undefined>,
|
|
745
757
|
): Promise<ResolvedRetrieveRequest> {
|
|
746
758
|
const attributeSelections = validateRetrieveRequestShape(request);
|
|
759
|
+
const config = await loadEnvFileDefaults(env);
|
|
747
760
|
|
|
748
761
|
const cdbResolution = await resolveCdb(
|
|
749
762
|
{
|
|
@@ -752,12 +765,14 @@ export async function resolveRetrieveRequest(
|
|
|
752
765
|
cdbPassword: request.cdbPassword,
|
|
753
766
|
},
|
|
754
767
|
env,
|
|
768
|
+
config,
|
|
755
769
|
);
|
|
756
770
|
|
|
757
771
|
const macResolution = await resolveMacForRetrieve(
|
|
758
772
|
request,
|
|
759
773
|
env,
|
|
760
774
|
cdbResolution,
|
|
775
|
+
config,
|
|
761
776
|
);
|
|
762
777
|
|
|
763
778
|
return buildResolvedRetrieve(
|
|
@@ -766,6 +781,7 @@ export async function resolveRetrieveRequest(
|
|
|
766
781
|
cdbResolution,
|
|
767
782
|
attributeSelections,
|
|
768
783
|
macResolution,
|
|
784
|
+
config,
|
|
769
785
|
);
|
|
770
786
|
}
|
|
771
787
|
|
|
@@ -779,8 +795,9 @@ export async function resolveMacForRetrieve(
|
|
|
779
795
|
request: RetrieveRequest,
|
|
780
796
|
env: Record<string, string | undefined>,
|
|
781
797
|
cdb?: CdbResolution,
|
|
798
|
+
config: Record<string, string | undefined> = {},
|
|
782
799
|
): Promise<{ mac: string; ip: string } | undefined> {
|
|
783
|
-
if (!isIpTransport(resolveProtocol(request, env, cdb).value)) {
|
|
800
|
+
if (!isIpTransport(resolveProtocol(request, env, cdb, config).value)) {
|
|
784
801
|
return undefined;
|
|
785
802
|
}
|
|
786
803
|
return resolveMacTarget({
|
|
@@ -788,7 +805,10 @@ export async function resolveMacForRetrieve(
|
|
|
788
805
|
targetInput: request.targetInput,
|
|
789
806
|
cdbTarget: cdb?.target,
|
|
790
807
|
env,
|
|
791
|
-
|
|
808
|
+
config,
|
|
809
|
+
policy: parseResolvePolicy(
|
|
810
|
+
request.resolve ?? env["CENTRS_RESOLVE"] ?? config["CENTRS_RESOLVE"],
|
|
811
|
+
),
|
|
792
812
|
operation: "retrieve",
|
|
793
813
|
});
|
|
794
814
|
}
|
|
@@ -797,6 +817,7 @@ function resolveProtocol(
|
|
|
797
817
|
request: RetrieveRequest,
|
|
798
818
|
env: Record<string, string | undefined>,
|
|
799
819
|
cdb?: CdbResolution,
|
|
820
|
+
config: Record<string, string | undefined> = {},
|
|
800
821
|
): ResolvedSetting<RouterOsProtocol> {
|
|
801
822
|
const via = resolveStringSetting(
|
|
802
823
|
request.via,
|
|
@@ -806,6 +827,7 @@ function resolveProtocol(
|
|
|
806
827
|
"via",
|
|
807
828
|
undefined,
|
|
808
829
|
cdb?.overrides.via,
|
|
830
|
+
config,
|
|
809
831
|
);
|
|
810
832
|
if (!via) {
|
|
811
833
|
throw new CentrsError({
|
|
@@ -864,6 +886,7 @@ function resolveProtocol(
|
|
|
864
886
|
function resolveFormat(
|
|
865
887
|
request: RetrieveRequest,
|
|
866
888
|
env: Record<string, string | undefined>,
|
|
889
|
+
config: Record<string, string | undefined> = {},
|
|
867
890
|
): ResolvedSetting<RetrieveOutputFormat> {
|
|
868
891
|
return resolveStringSetting(
|
|
869
892
|
request.format,
|
|
@@ -872,45 +895,25 @@ function resolveFormat(
|
|
|
872
895
|
"text",
|
|
873
896
|
"format",
|
|
874
897
|
parseOutputFormat,
|
|
898
|
+
undefined,
|
|
899
|
+
config,
|
|
875
900
|
) as ResolvedSetting<RetrieveOutputFormat>;
|
|
876
901
|
}
|
|
877
902
|
|
|
878
903
|
/**
|
|
879
|
-
*
|
|
880
|
-
* {@link
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
* surface as-is.
|
|
904
|
+
* Resolve the read command (`print` vs `get`) for a path via a `request=child`
|
|
905
|
+
* existence probe. Uses {@link inspectChildrenOrEmpty} so an invalid path (a
|
|
906
|
+
* native trap, or REST's empty child list) flattens to no children and surfaces
|
|
907
|
+
* as a single `validation/unknown-path` here rather than leaking a transport
|
|
908
|
+
* trap. Attribute/completion discovery does NOT use the swallowing probe — traps
|
|
909
|
+
* there surface as-is.
|
|
886
910
|
*/
|
|
887
|
-
async function inspectChildrenForProbe(
|
|
888
|
-
backend: ProtocolAdapter,
|
|
889
|
-
path: string,
|
|
890
|
-
): Promise<unknown[]> {
|
|
891
|
-
try {
|
|
892
|
-
return await backend.inspect("child", path);
|
|
893
|
-
} catch (error) {
|
|
894
|
-
if (
|
|
895
|
-
error instanceof CentrsError &&
|
|
896
|
-
(error.code === "routeros/api-trap" ||
|
|
897
|
-
error.code === "routeros/unknown-path")
|
|
898
|
-
) {
|
|
899
|
-
return [];
|
|
900
|
-
}
|
|
901
|
-
throw error;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
911
|
async function inspectRetrievePath(
|
|
906
912
|
resolved: ResolvedRetrieveRequest,
|
|
907
913
|
backend: ProtocolAdapter,
|
|
908
914
|
): Promise<RetrieveInspection> {
|
|
909
|
-
const
|
|
910
|
-
const rootChildren =
|
|
911
|
-
backend,
|
|
912
|
-
pathToInspectString(pathTokens),
|
|
913
|
-
)) as InspectChildItem[];
|
|
915
|
+
const tokens = pathTokens(resolved.path);
|
|
916
|
+
const rootChildren = await inspectChildrenOrEmpty(backend, tokens);
|
|
914
917
|
|
|
915
918
|
const supportsPrint = rootChildren.some((child) =>
|
|
916
919
|
isCommandNode(child, "print"),
|
|
@@ -943,21 +946,24 @@ async function inspectAttributes(
|
|
|
943
946
|
inspection: RetrieveInspection,
|
|
944
947
|
backend: ProtocolAdapter,
|
|
945
948
|
): Promise<string[]> {
|
|
946
|
-
const
|
|
949
|
+
const tokens = pathTokens(resolved.path);
|
|
947
950
|
const argument = inspection.command === "get" ? "value-name" : "proplist";
|
|
948
|
-
const completionRows =
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
951
|
+
const completionRows = await inspectCompletions(backend, [
|
|
952
|
+
...tokens,
|
|
953
|
+
inspection.command,
|
|
954
|
+
argument,
|
|
955
|
+
]);
|
|
956
|
+
const completions = [
|
|
957
|
+
...new Set(extractCompletionNames(completionRows)),
|
|
958
|
+
].sort();
|
|
953
959
|
if (completions.length > 0) {
|
|
954
960
|
return completions;
|
|
955
961
|
}
|
|
956
962
|
|
|
957
|
-
const commandChildren =
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
)
|
|
963
|
+
const commandChildren = await inspectChildren(backend, [
|
|
964
|
+
...tokens,
|
|
965
|
+
inspection.command,
|
|
966
|
+
]);
|
|
961
967
|
return commandChildren
|
|
962
968
|
.filter(isArgumentNode)
|
|
963
969
|
.map((child) => child.name)
|
|
@@ -985,35 +991,6 @@ async function executeRetrieve(
|
|
|
985
991
|
});
|
|
986
992
|
}
|
|
987
993
|
|
|
988
|
-
function pathTokensForInspect(path: string): string[] {
|
|
989
|
-
return path.split("/").filter(Boolean);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
function isCommandNode(
|
|
993
|
-
child: InspectChildItem,
|
|
994
|
-
name: "get" | "print",
|
|
995
|
-
): boolean {
|
|
996
|
-
return (
|
|
997
|
-
child.name === name &&
|
|
998
|
-
(child.type === "cmd" || child["node-type"] === "cmd")
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
function isArgumentNode(child: InspectChildItem): boolean {
|
|
1003
|
-
return child.type === "arg" || child["node-type"] === "arg";
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
function extractCompletionNames(
|
|
1007
|
-
rows: readonly InspectCompletionItem[],
|
|
1008
|
-
): string[] {
|
|
1009
|
-
const names = rows
|
|
1010
|
-
.flatMap((row) => [row.completion, row.name, row.value, row.text])
|
|
1011
|
-
.filter((value): value is string => typeof value === "string")
|
|
1012
|
-
.map((value) => value.replace(/=.*$/, "").trim())
|
|
1013
|
-
.filter((value) => value.length > 0);
|
|
1014
|
-
return [...new Set(names)].sort();
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
994
|
function isKnownSingletonPath(path: string): boolean {
|
|
1018
995
|
return ["/system/resource", "/system/identity"].includes(
|
|
1019
996
|
path.replace(/\/$/, ""),
|
|
@@ -1043,6 +1020,7 @@ function resolveTimeoutSetting(
|
|
|
1043
1020
|
env: Record<string, string | undefined>,
|
|
1044
1021
|
via: RouterOsProtocol,
|
|
1045
1022
|
commentKv?: ResolvedSetting<number>,
|
|
1023
|
+
config: Record<string, string | undefined> = {},
|
|
1046
1024
|
): ResolvedSetting<number> {
|
|
1047
1025
|
const resolved = resolveStringSetting(
|
|
1048
1026
|
timeout === undefined ? undefined : String(timeout),
|
|
@@ -1063,6 +1041,7 @@ function resolveTimeoutSetting(
|
|
|
1063
1041
|
return parsed;
|
|
1064
1042
|
},
|
|
1065
1043
|
commentKv,
|
|
1044
|
+
config,
|
|
1066
1045
|
);
|
|
1067
1046
|
if (!resolved) {
|
|
1068
1047
|
throw new Error("timeout resolution produced no value");
|
|
@@ -1122,10 +1101,6 @@ function parseOutputFormat(value: string): RetrieveOutputFormat {
|
|
|
1122
1101
|
});
|
|
1123
1102
|
}
|
|
1124
1103
|
|
|
1125
|
-
function pathToInspectString(path: readonly string[]): string {
|
|
1126
|
-
return path.join(",");
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
1104
|
function countResultObjects(data: unknown): number {
|
|
1130
1105
|
if (Array.isArray(data)) {
|
|
1131
1106
|
return data.length;
|