@tikoci/centrs 0.1.0

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 (182) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/GLOSSARY.txt +420 -0
  3. package/README.md +196 -0
  4. package/SECURITY.md +30 -0
  5. package/commands/AGENTS.md +50 -0
  6. package/commands/btest/README.md +257 -0
  7. package/commands/btest/examples.md +190 -0
  8. package/commands/check/README.md +59 -0
  9. package/commands/devices/README.md +405 -0
  10. package/commands/devices/examples.md +488 -0
  11. package/commands/discover/README.md +170 -0
  12. package/commands/discover/examples.md +98 -0
  13. package/commands/execute/README.md +208 -0
  14. package/commands/execute/examples.md +353 -0
  15. package/commands/mcp/README.md +260 -0
  16. package/commands/mcp/examples.md +158 -0
  17. package/commands/retrieve/README.md +209 -0
  18. package/commands/retrieve/examples.md +306 -0
  19. package/commands/settings/README.md +69 -0
  20. package/commands/stream/README.md +70 -0
  21. package/commands/terminal/README.md +177 -0
  22. package/commands/terminal/examples.md +144 -0
  23. package/commands/transfer/README.md +315 -0
  24. package/commands/transfer/examples.md +368 -0
  25. package/docs/CONSTITUTION.md +417 -0
  26. package/docs/MATRIX.md +224 -0
  27. package/docs/errors/README.md +74 -0
  28. package/docs/errors/auth/failed.md +9 -0
  29. package/docs/errors/cdb/already-exists.md +9 -0
  30. package/docs/errors/cdb/created.md +9 -0
  31. package/docs/errors/cdb/decrypt-failed.md +9 -0
  32. package/docs/errors/cdb/empty-group.md +9 -0
  33. package/docs/errors/cdb/invalid-option.md +9 -0
  34. package/docs/errors/cdb/invalid-record.md +9 -0
  35. package/docs/errors/cdb/not-found-target.md +9 -0
  36. package/docs/errors/cdb/not-found.md +9 -0
  37. package/docs/errors/cdb/override-applied.md +9 -0
  38. package/docs/errors/cdb/parse-failed.md +9 -0
  39. package/docs/errors/cdb/password-not-needed.md +9 -0
  40. package/docs/errors/cdb/password-required.md +9 -0
  41. package/docs/errors/cdb/reserved-key.md +9 -0
  42. package/docs/errors/cdb/reserved-option.md +9 -0
  43. package/docs/errors/cdb/target-not-registered.md +9 -0
  44. package/docs/errors/cdb/unknown-field.md +9 -0
  45. package/docs/errors/cdb/unknown-option.md +9 -0
  46. package/docs/errors/cdb/write-not-permitted.md +9 -0
  47. package/docs/errors/discover/broadcast-unavailable.md +9 -0
  48. package/docs/errors/discover/failed.md +9 -0
  49. package/docs/errors/identity/ambiguous.md +9 -0
  50. package/docs/errors/identity/no-match.md +9 -0
  51. package/docs/errors/input/invalid-command.md +9 -0
  52. package/docs/errors/input/invalid-match.md +9 -0
  53. package/docs/errors/input/invalid-path.md +9 -0
  54. package/docs/errors/input/invalid-routeros-path.md +9 -0
  55. package/docs/errors/input/invalid-target.md +9 -0
  56. package/docs/errors/input/local-file-not-found.md +9 -0
  57. package/docs/errors/input/mac-address.md +9 -0
  58. package/docs/errors/input/max-results-exceeded.md +9 -0
  59. package/docs/errors/internal/devices-failed.md +9 -0
  60. package/docs/errors/internal/mcp-start.md +9 -0
  61. package/docs/errors/internal/mcp-tool.md +9 -0
  62. package/docs/errors/internal/native-api-length.md +9 -0
  63. package/docs/errors/internal/unhandled.md +9 -0
  64. package/docs/errors/internal/unreachable.md +9 -0
  65. package/docs/errors/mndp/encode-failed.md +9 -0
  66. package/docs/errors/mndp/listen-failed.md +9 -0
  67. package/docs/errors/mndp/malformed.md +9 -0
  68. package/docs/errors/routeros/api-fatal.md +9 -0
  69. package/docs/errors/routeros/api-protocol.md +9 -0
  70. package/docs/errors/routeros/api-trap.md +9 -0
  71. package/docs/errors/routeros/btest-protocol.md +12 -0
  72. package/docs/errors/routeros/btest-too-many-sessions.md +9 -0
  73. package/docs/errors/routeros/command-failed.md +14 -0
  74. package/docs/errors/routeros/ec-srp5-protocol.md +11 -0
  75. package/docs/errors/routeros/error.md +9 -0
  76. package/docs/errors/routeros/invalid-value.md +14 -0
  77. package/docs/errors/routeros/mac-telnet-error.md +9 -0
  78. package/docs/errors/routeros/mac-telnet-not-ready.md +9 -0
  79. package/docs/errors/routeros/mac-telnet-protocol.md +9 -0
  80. package/docs/errors/routeros/mac-telnet-unsupported-auth.md +9 -0
  81. package/docs/errors/routeros/protocol-not-implemented.md +9 -0
  82. package/docs/errors/routeros/request-failed.md +9 -0
  83. package/docs/errors/routeros/session-closed.md +13 -0
  84. package/docs/errors/routeros/unknown-attribute.md +13 -0
  85. package/docs/errors/routeros/unknown-path.md +15 -0
  86. package/docs/errors/routeros/unsupported-capability.md +9 -0
  87. package/docs/errors/settings/invalid-boolean.md +9 -0
  88. package/docs/errors/settings/invalid-format.md +9 -0
  89. package/docs/errors/settings/invalid-integer.md +9 -0
  90. package/docs/errors/settings/invalid-timeout.md +9 -0
  91. package/docs/errors/settings/invalid-via.md +9 -0
  92. package/docs/errors/settings/unsafe-protocol-blocked.md +9 -0
  93. package/docs/errors/target/mac-not-in-arp.md +14 -0
  94. package/docs/errors/target/mac-required.md +23 -0
  95. package/docs/errors/target/mac-unresolved.md +17 -0
  96. package/docs/errors/target/unresolved.md +9 -0
  97. package/docs/errors/transport/auth-failed.md +9 -0
  98. package/docs/errors/transport/auto-method.md +9 -0
  99. package/docs/errors/transport/capability-unsupported.md +22 -0
  100. package/docs/errors/transport/checksum-unavailable.md +9 -0
  101. package/docs/errors/transport/connection-closed.md +9 -0
  102. package/docs/errors/transport/connection-refused.md +9 -0
  103. package/docs/errors/transport/dns.md +9 -0
  104. package/docs/errors/transport/host-key-mismatch.md +9 -0
  105. package/docs/errors/transport/incomplete-transfer.md +9 -0
  106. package/docs/errors/transport/insecure-trust.md +9 -0
  107. package/docs/errors/transport/local-tool-missing.md +11 -0
  108. package/docs/errors/transport/network.md +9 -0
  109. package/docs/errors/transport/timeout.md +9 -0
  110. package/docs/errors/transport/tls-certificate.md +9 -0
  111. package/docs/errors/transport/unreachable.md +9 -0
  112. package/docs/errors/transport/unsupported-operation.md +9 -0
  113. package/docs/errors/usage/confirmation-required.md +9 -0
  114. package/docs/errors/usage/conflicting-flags.md +9 -0
  115. package/docs/errors/usage/invalid-concurrency.md +9 -0
  116. package/docs/errors/usage/missing-group.md +9 -0
  117. package/docs/errors/usage/not-implemented.md +9 -0
  118. package/docs/errors/usage/target-exists.md +9 -0
  119. package/docs/errors/usage/timeout-out-of-range.md +9 -0
  120. package/docs/errors/validation/not-implemented.md +9 -0
  121. package/docs/errors/validation/option.md +9 -0
  122. package/docs/errors/validation/syntax.md +9 -0
  123. package/docs/errors/validation/unknown-attribute.md +9 -0
  124. package/docs/errors/validation/unknown-path.md +9 -0
  125. package/package.json +96 -0
  126. package/src/AGENTS.md +7 -0
  127. package/src/btest.ts +906 -0
  128. package/src/cli/btest.ts +455 -0
  129. package/src/cli/common.ts +53 -0
  130. package/src/cli/devices.ts +527 -0
  131. package/src/cli/discover.ts +186 -0
  132. package/src/cli/execute.ts +333 -0
  133. package/src/cli/mcp.ts +109 -0
  134. package/src/cli/retrieve.ts +383 -0
  135. package/src/cli/terminal.ts +303 -0
  136. package/src/cli/transfer.ts +471 -0
  137. package/src/cli.ts +115 -0
  138. package/src/core/envelope.ts +221 -0
  139. package/src/core/error-catalog.ts +469 -0
  140. package/src/core/routeros-errors.ts +268 -0
  141. package/src/data/AGENTS.md +11 -0
  142. package/src/data/dude-db.ts +0 -0
  143. package/src/data/mndp-cache.ts +132 -0
  144. package/src/data/mndp.ts +405 -0
  145. package/src/data/winbox-cdb-write.ts +200 -0
  146. package/src/data/winbox-cdb.ts +913 -0
  147. package/src/devices-schema.ts +107 -0
  148. package/src/devices.ts +1717 -0
  149. package/src/discover.ts +802 -0
  150. package/src/errors.ts +189 -0
  151. package/src/execute.ts +1350 -0
  152. package/src/index.ts +353 -0
  153. package/src/mcp/config.ts +66 -0
  154. package/src/mcp/resources.ts +98 -0
  155. package/src/mcp/safety.ts +119 -0
  156. package/src/mcp/server.ts +225 -0
  157. package/src/mcp/tools.ts +657 -0
  158. package/src/mcp.ts +33 -0
  159. package/src/protocols/AGENTS.md +14 -0
  160. package/src/protocols/adapter.ts +866 -0
  161. package/src/protocols/btest-session.ts +1493 -0
  162. package/src/protocols/btest.ts +447 -0
  163. package/src/protocols/ec-srp5.ts +466 -0
  164. package/src/protocols/index.ts +118 -0
  165. package/src/protocols/mac-telnet-console.ts +655 -0
  166. package/src/protocols/mac-telnet.ts +1299 -0
  167. package/src/protocols/mtwei.ts +47 -0
  168. package/src/protocols/native-api.ts +796 -0
  169. package/src/protocols/sftp.ts +427 -0
  170. package/src/protocols/ssh.ts +297 -0
  171. package/src/resolver/cdb.ts +580 -0
  172. package/src/resolver/comment-kv.ts +360 -0
  173. package/src/resolver/index.ts +74 -0
  174. package/src/resolver/mac.ts +227 -0
  175. package/src/resolver/settings.ts +228 -0
  176. package/src/resolver/target.ts +442 -0
  177. package/src/retrieve-fanout.ts +533 -0
  178. package/src/retrieve.ts +1215 -0
  179. package/src/terminal.ts +444 -0
  180. package/src/transfer.ts +1554 -0
  181. package/src/tui.ts +22 -0
  182. package/src/webproxy.ts +16 -0
package/src/index.ts ADDED
@@ -0,0 +1,353 @@
1
+ export const centrsVersion = "0.1.0";
2
+
3
+ export {
4
+ BTEST_CLIENT_CSV_HEADER,
5
+ BTEST_SERVER_CSV_HEADER,
6
+ type BtestClientData,
7
+ type BtestClientEnvelope,
8
+ type BtestClientOperationMeta,
9
+ type BtestClientRequest,
10
+ type BtestOutputFormat,
11
+ type BtestReportRecord,
12
+ type BtestServerData,
13
+ type BtestServerEnvelope,
14
+ type BtestServerOperationMeta,
15
+ type BtestServerRequest,
16
+ type BtestSessionRecord,
17
+ btestClient,
18
+ btestClientCsvRow,
19
+ btestClientSummaryLine,
20
+ btestOutputFormats,
21
+ btestServer,
22
+ btestServerCsvRow,
23
+ btestServerSummaryLine,
24
+ formatBtestReportText,
25
+ formatBtestSessionText,
26
+ renderBtestClientEnvelope,
27
+ renderBtestServerEnvelope,
28
+ } from "./btest.ts";
29
+ export {
30
+ type CentrsEnvelope,
31
+ type CentrsErrorEnvelope,
32
+ type CentrsSuccessEnvelope,
33
+ type CommonSettingsMeta,
34
+ type EnvelopeMeta,
35
+ type EnvelopeTargetMeta,
36
+ type EnvelopeValidationMeta,
37
+ type FanoutData,
38
+ type FanoutSummary,
39
+ normalizeWarnings,
40
+ type SettingSource,
41
+ type SettingSourceKind,
42
+ type Warning,
43
+ } from "./core/envelope.ts";
44
+ export {
45
+ encodeMndpPacket,
46
+ MNDP_BROADCAST_ADDRESS,
47
+ MNDP_IPV6_MULTICAST_ADDRESS,
48
+ MNDP_PORT,
49
+ type MndpNeighbor,
50
+ type MndpTlvInput,
51
+ type MndpUnknownTlv,
52
+ mndpNeighborKey,
53
+ mndpRefreshPacket,
54
+ mndpTlvType,
55
+ parseMndpPacket,
56
+ } from "./data/mndp.ts";
57
+ export {
58
+ MNDP_CACHE_DEFAULT_TTL_MS,
59
+ MndpCache,
60
+ type MndpCacheEntry,
61
+ type MndpCacheOptions,
62
+ } from "./data/mndp-cache.ts";
63
+ export {
64
+ type AnalyzeEncryptedWinBoxCdbOptions,
65
+ analyzeEncryptedWinBoxCdb,
66
+ type BuildWinBoxCdbEntryInput,
67
+ buildWinBoxCdbEntryRecord,
68
+ decodeWinBoxCdbEntries,
69
+ decodeWinBoxCdbEntry,
70
+ decryptWinBoxCdb,
71
+ type EncryptedWinBoxCdbFile,
72
+ type EncryptWinBoxCdbOptions,
73
+ encodeOpenWinBoxCdb,
74
+ encodeWinBoxCdbField,
75
+ encodeWinBoxCdbRecord,
76
+ encryptWinBoxCdb,
77
+ isWinBoxCdbEncrypted,
78
+ isWinBoxCdbOpen,
79
+ type OpenWinBoxCdbFile,
80
+ parseWinBoxCdb,
81
+ parseWinBoxCdbRecord,
82
+ WINBOX_CDB_ENCRYPTED_SALT_LENGTH,
83
+ WINBOX_CDB_RC4_DROP_BYTES,
84
+ WINBOX_CDB_SAVED_PASSWORD_FLAG,
85
+ type WinBoxCdbEncryptedAnalysis,
86
+ type WinBoxCdbEncryptedBlockAlignment,
87
+ type WinBoxCdbEntry,
88
+ type WinBoxCdbField,
89
+ type WinBoxCdbFieldValue,
90
+ type WinBoxCdbFile,
91
+ type WinBoxCdbRecord,
92
+ WinBoxCdbWrongPasswordError,
93
+ winBoxCdbFieldTag,
94
+ winBoxCdbRecordType,
95
+ } from "./data/winbox-cdb.ts";
96
+ export {
97
+ listWinBoxCdbBackups,
98
+ WINBOX_CDB_DEFAULT_BACKUP_RETENTION,
99
+ type WriteWinBoxCdbOptions,
100
+ type WriteWinBoxCdbResult,
101
+ writeWinBoxCdb,
102
+ } from "./data/winbox-cdb-write.ts";
103
+ export {
104
+ type AddDeviceArgs,
105
+ addDevice,
106
+ buildDevicesErrorEnvelope,
107
+ type DevicesCommand,
108
+ type DevicesEnvelope,
109
+ type DevicesGroupSummary,
110
+ type DevicesListItem,
111
+ type DevicesMutationData,
112
+ type DevicesMutationEnvelope,
113
+ type DevicesOperationMeta,
114
+ type DevicesOutputFormat,
115
+ type DevicesShowEnvelopeData,
116
+ type DevicesShowItem,
117
+ type DevicesWarning,
118
+ defaultCdbPath,
119
+ devicesOutputFormats,
120
+ editInteractiveOnlyError,
121
+ type LoadCdbOptions,
122
+ type LoadedCdb,
123
+ listDevices,
124
+ listGroups,
125
+ loadCdb,
126
+ type RemoveDeviceArgs,
127
+ recordTypeFromName,
128
+ recordTypeName,
129
+ removeDevice,
130
+ renderDevicesEnvelope,
131
+ resolveDevicesSettings,
132
+ type SetDeviceArgs,
133
+ setDevice,
134
+ showDevice,
135
+ } from "./devices.ts";
136
+ export {
137
+ DISCOVER_DEFAULT_GROUP,
138
+ DISCOVER_DEFAULT_REFRESH_INTERVAL_MS,
139
+ DISCOVER_DEFAULT_TIMEOUT_MS,
140
+ type DiscoverData,
141
+ type DiscoverEnvelope,
142
+ type DiscoverNeighborRecord,
143
+ type DiscoverOperationMeta,
144
+ type DiscoverOptions,
145
+ type DiscoverOutputFormat,
146
+ type DiscoverSaveRecord,
147
+ type DiscoverSaveSummary,
148
+ discover,
149
+ discoverNeighborTarget,
150
+ discoverOutputFormats,
151
+ formatMndpProvenanceComment,
152
+ type ListenMndpOptions,
153
+ type ListenMndpResult,
154
+ listenMndp,
155
+ renderDiscoverEnvelope,
156
+ type SaveDiscoveredNeighborsArgs,
157
+ type SaveDiscoveredNeighborsResult,
158
+ saveDiscoveredNeighbors,
159
+ } from "./discover.ts";
160
+ export {
161
+ asCentrsError,
162
+ CentrsError,
163
+ type CentrsErrorCode,
164
+ type SerializedCentrsError,
165
+ serializeCentrsError,
166
+ } from "./errors.ts";
167
+ export {
168
+ buildExecuteErrorEnvelope,
169
+ buildExecuteErrorEnvelopeFromResolved,
170
+ canonicalizeExecuteCommand,
171
+ type ExecuteEnvelope,
172
+ type ExecuteErrorEnvelope,
173
+ type ExecuteOperationMeta,
174
+ type ExecuteOutputFormat,
175
+ type ExecuteRequest,
176
+ type ExecuteRequestSummary,
177
+ type ExecuteSuccessEnvelope,
178
+ execute,
179
+ executeEnvelope,
180
+ executeOutputFormats,
181
+ isWriteShaped,
182
+ renderExecuteEnvelope,
183
+ resolveExecuteRequest,
184
+ runResolvedExecute,
185
+ validateExecuteRequestShape,
186
+ } from "./execute.ts";
187
+ export {
188
+ createProtocolAdapter,
189
+ getProtocolPlan,
190
+ type ProtocolAdapter,
191
+ type ProtocolAdapterCapabilities,
192
+ type ProtocolAdapterConfig,
193
+ type ProtocolCapability,
194
+ type ProtocolExecuteRequest,
195
+ type ProtocolExecuteResult,
196
+ type ProtocolPlan,
197
+ plannedProtocols,
198
+ protocolPlans,
199
+ protocolsWithCapability,
200
+ type RetrieveListOptions,
201
+ type RouterOsProtocol,
202
+ } from "./protocols/index.ts";
203
+ export {
204
+ applyCommentKv,
205
+ type CdbGroupExpansion,
206
+ type CdbGroupResolveInput,
207
+ type CdbGroupTarget,
208
+ type CdbResolution,
209
+ type CdbResolveInput,
210
+ type CommentKvKey,
211
+ type CommentKvLayer,
212
+ type CommentKvOverrides,
213
+ type CommentKvResult,
214
+ type CommentKvUpdate,
215
+ type CommentKvWarning,
216
+ coerceCommentKv,
217
+ commentKvAllowlist,
218
+ commentKvReservedKeys,
219
+ defaultPortForScheme,
220
+ expandCdbGroup,
221
+ parseBoolean,
222
+ parseCommentKv,
223
+ parseDuration,
224
+ parseHostCandidate,
225
+ type ResolvedAuth,
226
+ type ResolvedSetting,
227
+ type ResolvedTarget,
228
+ type ResolverSettingSource,
229
+ type ResolverSettingSourceKind,
230
+ type ResolverWarning,
231
+ renderCommentKvToken,
232
+ resolveAuth,
233
+ resolveBooleanSetting,
234
+ resolveCdb,
235
+ resolveOptionalIntegerSetting,
236
+ resolveStringSetting,
237
+ resolveTarget,
238
+ type TargetResolveInput,
239
+ toCoreSource,
240
+ } from "./resolver/index.ts";
241
+ export {
242
+ buildResolvedRetrieve,
243
+ buildRetrieveErrorEnvelope,
244
+ buildRetrieveErrorEnvelopeFromResolved,
245
+ type ResolvedRetrieveRequest,
246
+ type RetrieveEnvelope,
247
+ type RetrieveErrorEnvelope,
248
+ type RetrieveGlobalContext,
249
+ type RetrieveOperationMeta,
250
+ type RetrieveOutputFormat,
251
+ type RetrieveRequest,
252
+ type RetrieveRequestSummary,
253
+ type RetrieveSuccessEnvelope,
254
+ type RetrieveWarning,
255
+ renderRetrieveEnvelope,
256
+ resolveRetrieveGlobalContext,
257
+ resolveRetrieveRequest,
258
+ retrieve,
259
+ retrieveOutputFormats,
260
+ runResolvedRetrieve,
261
+ validateRetrieveRequestShape,
262
+ } from "./retrieve.ts";
263
+ export {
264
+ buildRetrieveFanoutErrorEnvelope,
265
+ isRetryableFanoutError,
266
+ RETRIEVE_FANOUT_BACKOFF_BASE_MS,
267
+ RETRIEVE_FANOUT_CONCURRENCY_DEFAULTS,
268
+ RETRIEVE_FANOUT_MAX_RETRIES,
269
+ RETRIEVE_FANOUT_RETRYABLE_CODES,
270
+ type RetrieveFanoutData,
271
+ type RetrieveFanoutEnvelope,
272
+ type RetrieveFanoutErrorEnvelope,
273
+ type RetrieveFanoutOperationMeta,
274
+ type RetrieveGroupInternals,
275
+ renderRetrieveFanoutEnvelope,
276
+ resolveFanoutConcurrency,
277
+ retrieveGroup,
278
+ runBoundedPool,
279
+ summarizeFanout,
280
+ } from "./retrieve-fanout.ts";
281
+ export {
282
+ buildSshTerminalArgv,
283
+ buildTerminalErrorEnvelope,
284
+ resolveTerminalRequest,
285
+ runTerminal,
286
+ type TerminalErrorEnvelope,
287
+ type TerminalIo,
288
+ type TerminalRequest,
289
+ } from "./terminal.ts";
290
+ export {
291
+ buildTransferErrorEnvelope,
292
+ FILE_READ_CHUNK_BYTES,
293
+ normalizeRemotePath,
294
+ REST_FILE_WRITE_CAP_BYTES,
295
+ renderTransferEnvelope,
296
+ resolveTransferRequest,
297
+ runResolvedTransfer,
298
+ selectTransferMethod,
299
+ type TransferData,
300
+ type TransferEnvelope,
301
+ type TransferErrorEnvelope,
302
+ type TransferMethod,
303
+ type TransferOp,
304
+ type TransferOperationMeta,
305
+ type TransferOutputFormat,
306
+ type TransferRequest,
307
+ type TransferSuccessEnvelope,
308
+ type TransferVerb,
309
+ transfer,
310
+ transferMethods,
311
+ transferOutputFormats,
312
+ transferVerbs,
313
+ validateTransferRequestShape,
314
+ } from "./transfer.ts";
315
+
316
+ import { plannedProtocols, type RouterOsProtocol } from "./protocols/index.ts";
317
+
318
+ export const plannedSurfaces = ["api", "cli", "tui", "mcp", "proxy"] as const;
319
+ export const plannedDeviceSources = [
320
+ "explicit-input",
321
+ "environment",
322
+ "winbox-cdb",
323
+ "dude-db",
324
+ "mndp",
325
+ ] as const;
326
+
327
+ export type CentrsSurface = (typeof plannedSurfaces)[number];
328
+ export type DeviceSource = (typeof plannedDeviceSources)[number];
329
+
330
+ export interface ProjectSummary {
331
+ name: "centrs";
332
+ version: string;
333
+ description: string;
334
+ surfaces: readonly CentrsSurface[];
335
+ protocols: readonly RouterOsProtocol[];
336
+ deviceSources: readonly DeviceSource[];
337
+ }
338
+
339
+ export const projectSummary: ProjectSummary = {
340
+ name: "centrs",
341
+ version: centrsVersion,
342
+ description:
343
+ "RouterOS interaction hub for typed APIs, CLI, TUI, MCP, and proxy frontends.",
344
+ surfaces: plannedSurfaces,
345
+ protocols: plannedProtocols,
346
+ deviceSources: plannedDeviceSources,
347
+ };
348
+
349
+ export function describeCentrs(
350
+ summary: ProjectSummary = projectSummary,
351
+ ): string {
352
+ return `${summary.name} ${summary.version}: ${summary.description}`;
353
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Runtime configuration for the centrs MCP server.
3
+ *
4
+ * The MCP surface is an adapter over the same validated core the CLI/API use;
5
+ * its authorization boundary is the CDB (the device allowlist). This module
6
+ * resolves where that CDB lives and the two global toggles the server honors,
7
+ * with the usual centrs precedence: explicit args → environment → built-in
8
+ * default.
9
+ */
10
+
11
+ import { defaultCdbPath } from "../devices.ts";
12
+
13
+ /**
14
+ * Reserved CDB record name (future work, not yet honored) that will supply
15
+ * fallback metadata + credentials for a resolved device that has none of its
16
+ * own. Precedence once implemented: per-call args → ENV → matched device
17
+ * record → this `__default__` record → built-in default. Tracked in
18
+ * `commands/mcp/README.md` (Future work) and `docs/MATRIX.md`.
19
+ */
20
+ export const DEFAULT_DEVICE_NAME = "__default__";
21
+
22
+ /** Environment variable reserved for future inline host+credential targets. */
23
+ export const ENV_ALLOW_ADHOC = "CENTRS_MCP_ALLOW_ADHOC";
24
+
25
+ const ENV_CDB_FILE = "CENTRS_CDB_FILE";
26
+ const ENV_CDB_PASSWORD = "CENTRS_CDB_PASSWORD";
27
+
28
+ export interface CentrsMcpConfig {
29
+ /** Absolute path to the CDB that is the device allowlist. */
30
+ cdbFile: string;
31
+ /** True when `--cdb-file`/`CENTRS_CDB_FILE` set the path explicitly. */
32
+ cdbFileExplicit: boolean;
33
+ /** CDB decrypt password, when the CDB on disk is encrypted. */
34
+ cdbPassword?: string;
35
+ /**
36
+ * Off-by-default escape hatch reserved for future MCP schemas that accept
37
+ * inline host+credentials. Phase 1 tools expose only CDB target strings.
38
+ */
39
+ allowAdhocTargets: boolean;
40
+ /** Environment snapshot the core resolvers read (defaults to `Bun.env`). */
41
+ env: Record<string, string | undefined>;
42
+ }
43
+
44
+ export interface ResolveMcpConfigArgs {
45
+ cdbFile?: string;
46
+ cdbPassword?: string;
47
+ allowAdhocTargets?: boolean;
48
+ env?: Record<string, string | undefined>;
49
+ }
50
+
51
+ export function resolveMcpConfig(
52
+ args: ResolveMcpConfigArgs = {},
53
+ ): CentrsMcpConfig {
54
+ const env = args.env ?? Bun.env;
55
+ const explicitPath = args.cdbFile ?? env[ENV_CDB_FILE] ?? undefined;
56
+ const cdbFile = explicitPath ?? defaultCdbPath(env);
57
+ const allowAdhocTargets =
58
+ args.allowAdhocTargets ?? env[ENV_ALLOW_ADHOC] === "1";
59
+ return {
60
+ cdbFile,
61
+ cdbFileExplicit: explicitPath !== undefined,
62
+ cdbPassword: args.cdbPassword ?? env[ENV_CDB_PASSWORD] ?? undefined,
63
+ allowAdhocTargets,
64
+ env,
65
+ };
66
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * MCP resources for centrs.
3
+ *
4
+ * Two read-only resources back the server's `instructions` so an agent can see
5
+ * the allowlist and the error contract without trial and error:
6
+ *
7
+ * - `centrs://devices` — the active CDB's registered devices (the allowlist),
8
+ * including each record's resolved MCP write policy. Never includes passwords.
9
+ * - `centrs://errors` — the centrs error-code catalog: each known `family/code`
10
+ * with its stable details URL.
11
+ *
12
+ * The builders are pure (return plain JSON-able objects) so they are unit
13
+ * testable; `./server.ts` registers them as MCP resources and serializes the
14
+ * payload as JSON text.
15
+ */
16
+
17
+ import {
18
+ detailsUrlForCode,
19
+ ERROR_DETAILS_BASE_URL,
20
+ errorCatalog,
21
+ } from "../core/error-catalog.ts";
22
+ import type { CentrsMcpConfig } from "./config.ts";
23
+ import { loadAllowlistCdb, readWritePolicy } from "./safety.ts";
24
+
25
+ export const DEVICES_RESOURCE_URI = "centrs://devices";
26
+ export const ERRORS_RESOURCE_URI = "centrs://errors";
27
+
28
+ export interface DeviceResourceItem {
29
+ target: string;
30
+ group: string;
31
+ user: string;
32
+ recordType: number;
33
+ writePolicy: "ro" | "rw";
34
+ }
35
+
36
+ export interface DevicesResource {
37
+ cdbFile: string;
38
+ count: number;
39
+ devices: readonly DeviceResourceItem[];
40
+ }
41
+
42
+ /**
43
+ * Snapshot the CDB allowlist for the `centrs://devices` resource. Reads the
44
+ * per-device `mcp` write policy from each record's comment. No passwords.
45
+ */
46
+ export async function buildDevicesResource(
47
+ config: CentrsMcpConfig,
48
+ ): Promise<DevicesResource> {
49
+ const cdb = await loadAllowlistCdb(config);
50
+ const devices: DeviceResourceItem[] = [];
51
+ for (const entry of cdb.entries) {
52
+ if (!entry) {
53
+ continue;
54
+ }
55
+ devices.push({
56
+ target: entry.target,
57
+ group: entry.group,
58
+ user: entry.user,
59
+ recordType: entry.recordType,
60
+ writePolicy: readWritePolicy(entry.comment),
61
+ });
62
+ }
63
+ return {
64
+ cdbFile: cdb.settings.cdbFile.value,
65
+ count: devices.length,
66
+ devices,
67
+ };
68
+ }
69
+
70
+ export interface ErrorCatalogItem {
71
+ code: string;
72
+ summary: string;
73
+ detailsUrl: string;
74
+ }
75
+
76
+ export interface ErrorsResource {
77
+ detailsBaseUrl: string;
78
+ count: number;
79
+ errors: readonly ErrorCatalogItem[];
80
+ }
81
+
82
+ /**
83
+ * Build the `centrs://errors` resource payload from the single error catalog
84
+ * (`src/core/error-catalog.ts`), so the MCP-exposed catalog can never drift
85
+ * from the codes the rest of centrs produces.
86
+ */
87
+ export function buildErrorsResource(): ErrorsResource {
88
+ const errors: ErrorCatalogItem[] = errorCatalog.map((item) => ({
89
+ code: item.code,
90
+ summary: item.summary,
91
+ detailsUrl: detailsUrlForCode(item.code),
92
+ }));
93
+ return {
94
+ detailsBaseUrl: ERROR_DETAILS_BASE_URL,
95
+ count: errors.length,
96
+ errors,
97
+ };
98
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The MCP authorization boundary: the CDB is the allowlist.
3
+ *
4
+ * Every device-touching MCP tool resolves its `target` through this layer
5
+ * before any core call. A target that is not a CDB record is rejected with
6
+ * `cdb/target-not-registered` (fix → `centrs_devices add`); credentials never
7
+ * pass through the agent because they live in the CDB. Per-device write policy
8
+ * is CDB data: the comment-kv key `mcp` (`ro` default, `rw` to permit writes).
9
+ * Write-shaped `centrs_execute` calls require the resolved record to be
10
+ * `mcp=rw`, else `cdb/write-not-permitted`.
11
+ */
12
+
13
+ import { type LoadedCdb, loadCdb } from "../devices.ts";
14
+ import { CentrsError } from "../errors.ts";
15
+ import type { McpWritePolicy } from "../mcp.ts";
16
+ import { parseCommentKv } from "../resolver/comment-kv.ts";
17
+ import type { CentrsMcpConfig } from "./config.ts";
18
+
19
+ export interface RegisteredTarget {
20
+ /** The CDB target string (host / IP / URL / MAC). */
21
+ target: string;
22
+ user: string;
23
+ group: string;
24
+ /** Index of the matched record in the loaded CDB. */
25
+ recordIndex: number;
26
+ /** Resolved per-device MCP write policy. */
27
+ writePolicy: McpWritePolicy;
28
+ }
29
+
30
+ /**
31
+ * Read the per-device MCP write policy from a CDB record comment. Defaults to
32
+ * `ro`; only an explicit `mcp=rw` opts a device into write access.
33
+ */
34
+ export function readWritePolicy(comment: string): McpWritePolicy {
35
+ const parsed = parseCommentKv(comment);
36
+ return parsed.values.mcp === "rw" ? "rw" : "ro";
37
+ }
38
+
39
+ /** Load the CDB that backs the allowlist for this server instance. */
40
+ export async function loadAllowlistCdb(
41
+ config: CentrsMcpConfig,
42
+ ): Promise<LoadedCdb> {
43
+ return loadCdb({
44
+ cdbFile: config.cdbFile,
45
+ cdbPassword: config.cdbPassword,
46
+ env: config.env,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Resolve a target against the CDB allowlist. Throws
52
+ * `cdb/target-not-registered` when no record matches — the friendly,
53
+ * MCP-specific framing of "this device is not on the allowlist", pointing at
54
+ * `centrs_devices add` rather than leaking the CLI's `cdb/not-found-target`.
55
+ */
56
+ export function resolveRegisteredTarget(
57
+ cdb: LoadedCdb,
58
+ target: string,
59
+ ): RegisteredTarget {
60
+ for (let index = 0; index < cdb.entries.length; index += 1) {
61
+ const entry = cdb.entries[index];
62
+ if (entry && entry.target === target) {
63
+ return {
64
+ target,
65
+ user: entry.user,
66
+ group: entry.group,
67
+ recordIndex: index,
68
+ writePolicy: readWritePolicy(entry.comment),
69
+ };
70
+ }
71
+ }
72
+ throw new CentrsError({
73
+ code: "cdb/target-not-registered",
74
+ summary: `Target "${target}" is not registered in the CDB allowlist.`,
75
+ remediation:
76
+ "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.",
77
+ context: { target, cdbFile: cdb.settings.cdbFile.value },
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Confirm at least one CDB record belongs to a group before a group fanout.
83
+ * Throws `cdb/target-not-registered` when the group is empty so an agent gets
84
+ * the same allowlist framing as a single-target miss.
85
+ */
86
+ export function resolveRegisteredGroup(cdb: LoadedCdb, group: string): number {
87
+ let members = 0;
88
+ for (const entry of cdb.entries) {
89
+ if (entry && entry.group === group) {
90
+ members += 1;
91
+ }
92
+ }
93
+ if (members === 0) {
94
+ throw new CentrsError({
95
+ code: "cdb/target-not-registered",
96
+ summary: `No CDB records are in group "${group}".`,
97
+ remediation:
98
+ "Add devices to the group with `centrs devices add --group`, or pick a group that has members. The MCP server only acts on CDB-registered targets.",
99
+ context: { group, cdbFile: cdb.settings.cdbFile.value },
100
+ });
101
+ }
102
+ return members;
103
+ }
104
+
105
+ /**
106
+ * Gate a write-shaped call on the resolved record's CDB policy. Throws
107
+ * `cdb/write-not-permitted` when the device is `mcp=ro` (the default).
108
+ */
109
+ export function assertWritePermitted(target: RegisteredTarget): void {
110
+ if (target.writePolicy !== "rw") {
111
+ throw new CentrsError({
112
+ code: "cdb/write-not-permitted",
113
+ summary: `Writes are not permitted for "${target.target}" (CDB policy mcp=${target.writePolicy}).`,
114
+ remediation:
115
+ "Opt the device into MCP writes by setting its CDB record to `mcp=rw` (e.g. `centrs devices set <target> mcp=rw`), then retry with confirm:true. The CDB policy is authoritative even when confirm is set.",
116
+ context: { target: target.target, writePolicy: target.writePolicy },
117
+ });
118
+ }
119
+ }