@tikoci/centrs 0.1.9-next.24 → 0.1.9

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 (71) hide show
  1. package/CHANGELOG.md +121 -6
  2. package/GLOSSARY.txt +9 -1
  3. package/README.md +43 -29
  4. package/commands/api/AGENTS.md +3 -2
  5. package/commands/api/README.md +8 -1
  6. package/commands/btest/README.md +9 -0
  7. package/commands/btest/examples.md +7 -0
  8. package/commands/devices/README.md +160 -30
  9. package/commands/devices/examples.md +168 -2
  10. package/commands/execute/README.md +13 -2
  11. package/commands/explain/README.md +223 -3
  12. package/commands/explain/examples.md +8 -1
  13. package/commands/retrieve/README.md +24 -3
  14. package/commands/retrieve/examples.md +84 -0
  15. package/commands/settings/README.md +6 -0
  16. package/commands/settings/examples.md +17 -0
  17. package/commands/transfer/README.md +25 -0
  18. package/commands/transfer/examples.md +46 -0
  19. package/docs/CLI.md +53 -11
  20. package/docs/MATRIX.md +5 -3
  21. package/docs/errors/cdb/encrypt-failed.md +10 -0
  22. package/docs/errors/cdb/password-required.md +5 -4
  23. package/docs/errors/cdb/plaintext-backups.md +11 -0
  24. package/docs/errors/input/empty-stdin.md +27 -0
  25. package/docs/errors/input/invalid-routeros-path.md +25 -3
  26. package/docs/errors/input/invalid-stdin.md +28 -0
  27. package/docs/errors/usage/stdin-is-tty.md +21 -0
  28. package/package.json +11 -7
  29. package/src/api-fanout.ts +2 -0
  30. package/src/api.ts +12 -0
  31. package/src/btest.ts +7 -0
  32. package/src/cli/api.ts +19 -2
  33. package/src/cli/btest.ts +9 -0
  34. package/src/cli/common.ts +1 -0
  35. package/src/cli/devices.ts +248 -15
  36. package/src/cli/execute.ts +12 -3
  37. package/src/cli/retrieve.ts +9 -0
  38. package/src/cli/settings.ts +28 -6
  39. package/src/cli/terminal.ts +9 -0
  40. package/src/cli/transfer.ts +10 -1
  41. package/src/core/error-catalog.ts +28 -2
  42. package/src/core/menu-path.ts +89 -0
  43. package/src/devices.ts +378 -33
  44. package/src/execute-fanout.ts +2 -0
  45. package/src/execute.ts +7 -0
  46. package/src/explain/blocks.ts +2 -2
  47. package/src/explain/catalog.ts +22 -12
  48. package/src/explain/pasted-prompt.ts +198 -0
  49. package/src/explain/pathresolve.ts +2 -2
  50. package/src/explain/segment.ts +2 -2
  51. package/src/explain/symbols.ts +2 -2
  52. package/src/explain/verbsplit.ts +2 -2
  53. package/src/explain/write.ts +2 -2
  54. package/src/explain.ts +2 -0
  55. package/src/index.ts +5 -0
  56. package/src/mcp/safety.ts +7 -6
  57. package/src/mcp/tools.ts +30 -68
  58. package/src/protocols/adapter.ts +2 -0
  59. package/src/protocols/btest-session.ts +10 -30
  60. package/src/protocols/ec-srp5.ts +19 -10
  61. package/src/protocols/mac-telnet-console.ts +2 -2
  62. package/src/resolver/cdb.ts +66 -9
  63. package/src/resolver/index.ts +1 -0
  64. package/src/resolver/quickchr-provider.ts +5 -0
  65. package/src/resolver/selection.ts +15 -4
  66. package/src/retrieve-fanout.ts +2 -0
  67. package/src/retrieve.ts +11 -0
  68. package/src/settings.ts +24 -8
  69. package/src/terminal.ts +7 -0
  70. package/src/transfer-fanout.ts +2 -0
  71. package/src/transfer.ts +79 -14
@@ -0,0 +1,89 @@
1
+ /**
2
+ * RouterOS menu-path grammar for the read surfaces (`retrieve`, `api`) and
3
+ * the REST URL guard every REST request passes through (#586).
4
+ *
5
+ * Every dir and command name in the RouterOS 7.9–7.26 command tree (rosetta
6
+ * v0.12.0, 579 dirs / 5370 commands) is letters, digits and hyphens,
7
+ * starting with a letter or digit (`/interface/6to4`); case folds. Anything else
8
+ * in a menu segment is not a menu, and some of it changes what the request
9
+ * addresses: in a REST URL, `#` and `?` turn the appended `/print` into a
10
+ * fragment or query, so `retrieve '/system/reboot#'` posted to
11
+ * `/rest/system/reboot` and rebooted the router (CHR 7.24.5).
12
+ */
13
+
14
+ import { CentrsError } from "../errors.ts";
15
+ import { pathTokens } from "./inspect.ts";
16
+
17
+ const MENU_SEGMENT = /^[a-z0-9][a-z0-9-]*$/;
18
+
19
+ /** The menu segments of `path` that are not RouterOS menu or command names. */
20
+ export function invalidMenuSegments(path: string): string[] {
21
+ // Case folds, as RouterOS menus do (`src/explain/menus.ts` isMenuPath);
22
+ // letter case cannot change how a URL parses, so it is not a hazard.
23
+ return pathTokens(path).filter(
24
+ (segment) => !MENU_SEGMENT.test(segment.toLowerCase()),
25
+ );
26
+ }
27
+
28
+ /**
29
+ * Reject a read-surface path whose segments are not RouterOS menu names.
30
+ * Runs before any network work and regardless of `--validate`: that flag
31
+ * skips command validation, not the guarantee that a read dispatches a read.
32
+ */
33
+ export function assertRouterOsMenuPath(
34
+ path: string,
35
+ context: { readonly flag?: string; readonly endpoint?: string } = {},
36
+ ): void {
37
+ const invalid = invalidMenuSegments(path);
38
+ if (invalid.length === 0) {
39
+ return;
40
+ }
41
+ throw new CentrsError({
42
+ code: "input/invalid-routeros-path",
43
+ summary: `RouterOS path ${JSON.stringify(path)} has a segment that is not a menu name: ${invalid.map((segment) => JSON.stringify(segment)).join(", ")}.`,
44
+ remediation:
45
+ "Write the menu path with letters, digits and hyphens between slashes, such as `/ip/address` or `/system/resource`. Filter rows with `--query`, not characters in the path.",
46
+ context: { path, invalidSegments: invalid, ...context },
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Characters and segments that make a WHATWG URL address something other
52
+ * than the path as written: `?` starts a query, `#` a fragment, `\` is read
53
+ * as `/`, tabs and newlines are stripped, and `.`/`..` segments collapse
54
+ * (also percent-encoded: `%2e%2e`, `.%2E`).
55
+ * RouterOS does not decode percent-escapes in REST paths (`reboot%23` is
56
+ * "no such command or directory" on CHR 7.24.5), so the rest is inert.
57
+ */
58
+ function hasUrlStructuralCharacter(path: string): boolean {
59
+ for (const char of path) {
60
+ const code = char.charCodeAt(0);
61
+ if (char === "?" || char === "#" || char === "\\") return true;
62
+ if (code <= 0x1f || code === 0x7f) return true;
63
+ }
64
+ return false;
65
+ }
66
+
67
+ /** `.`/`..` and the percent-encoded forms WHATWG also collapses (`%2e%2e`, `.%2E`). */
68
+ const DOT_SEGMENT = /^(?:\.|%2e){1,2}$/i;
69
+
70
+ /**
71
+ * Refuse a REST request path that the URL parser would rewrite. This is the
72
+ * backstop under every REST caller (retrieve, api, execute, transfer, explain)
73
+ * so an appended `/print` can never be cut off before dispatch.
74
+ */
75
+ export function assertRestRequestPath(path: string): void {
76
+ const dotSegment = path
77
+ .split("/")
78
+ .some((segment) => DOT_SEGMENT.test(segment));
79
+ if (!hasUrlStructuralCharacter(path) && !dotSegment) {
80
+ return;
81
+ }
82
+ throw new CentrsError({
83
+ code: "input/invalid-routeros-path",
84
+ summary: `Refusing REST request path ${JSON.stringify(path)}: it contains characters the URL would reinterpret (?, #, \\, control characters, or a . / .. segment), so the request would not reach the path as written.`,
85
+ remediation:
86
+ "Remove the character from the path. Pass filters with `--query` and values as fields; names containing these characters can be selected with a query instead of the URL path.",
87
+ context: { path, via: "rest-api" },
88
+ });
89
+ }