@daniel.stefan/metalink 1.3.1

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 (333) hide show
  1. package/README.md +160 -0
  2. package/package.json +96 -0
  3. package/packages/cli/dist/bin/cli.d.ts +3 -0
  4. package/packages/cli/dist/bin/cli.d.ts.map +1 -0
  5. package/packages/cli/dist/bin/cli.js +4 -0
  6. package/packages/cli/dist/bin/cli.js.map +1 -0
  7. package/packages/cli/dist/commands/config/init.d.ts +9 -0
  8. package/packages/cli/dist/commands/config/init.d.ts.map +1 -0
  9. package/packages/cli/dist/commands/config/init.js +38 -0
  10. package/packages/cli/dist/commands/config/init.js.map +1 -0
  11. package/packages/cli/dist/commands/config/validate.d.ts +9 -0
  12. package/packages/cli/dist/commands/config/validate.d.ts.map +1 -0
  13. package/packages/cli/dist/commands/config/validate.js +34 -0
  14. package/packages/cli/dist/commands/config/validate.js.map +1 -0
  15. package/packages/cli/dist/commands/daemon/restart.d.ts +15 -0
  16. package/packages/cli/dist/commands/daemon/restart.d.ts.map +1 -0
  17. package/packages/cli/dist/commands/daemon/restart.js +184 -0
  18. package/packages/cli/dist/commands/daemon/restart.js.map +1 -0
  19. package/packages/cli/dist/commands/daemon/start.d.ts +7 -0
  20. package/packages/cli/dist/commands/daemon/start.d.ts.map +1 -0
  21. package/packages/cli/dist/commands/daemon/start.js +85 -0
  22. package/packages/cli/dist/commands/daemon/start.js.map +1 -0
  23. package/packages/cli/dist/commands/daemon/status.d.ts +7 -0
  24. package/packages/cli/dist/commands/daemon/status.d.ts.map +1 -0
  25. package/packages/cli/dist/commands/daemon/status.js +69 -0
  26. package/packages/cli/dist/commands/daemon/status.js.map +1 -0
  27. package/packages/cli/dist/commands/daemon/stop.d.ts +8 -0
  28. package/packages/cli/dist/commands/daemon/stop.d.ts.map +1 -0
  29. package/packages/cli/dist/commands/daemon/stop.js +77 -0
  30. package/packages/cli/dist/commands/daemon/stop.js.map +1 -0
  31. package/packages/cli/dist/commands/import/mcpm.d.ts +10 -0
  32. package/packages/cli/dist/commands/import/mcpm.d.ts.map +1 -0
  33. package/packages/cli/dist/commands/import/mcpm.js +58 -0
  34. package/packages/cli/dist/commands/import/mcpm.js.map +1 -0
  35. package/packages/cli/dist/commands/safety/add-risky-pattern.d.ts +16 -0
  36. package/packages/cli/dist/commands/safety/add-risky-pattern.d.ts.map +1 -0
  37. package/packages/cli/dist/commands/safety/add-risky-pattern.js +72 -0
  38. package/packages/cli/dist/commands/safety/add-risky-pattern.js.map +1 -0
  39. package/packages/cli/dist/commands/safety/add-risky.d.ts +12 -0
  40. package/packages/cli/dist/commands/safety/add-risky.d.ts.map +1 -0
  41. package/packages/cli/dist/commands/safety/add-risky.js +52 -0
  42. package/packages/cli/dist/commands/safety/add-risky.js.map +1 -0
  43. package/packages/cli/dist/commands/safety/add-safe-pattern.d.ts +16 -0
  44. package/packages/cli/dist/commands/safety/add-safe-pattern.d.ts.map +1 -0
  45. package/packages/cli/dist/commands/safety/add-safe-pattern.js +72 -0
  46. package/packages/cli/dist/commands/safety/add-safe-pattern.js.map +1 -0
  47. package/packages/cli/dist/commands/safety/add-safe.d.ts +12 -0
  48. package/packages/cli/dist/commands/safety/add-safe.d.ts.map +1 -0
  49. package/packages/cli/dist/commands/safety/add-safe.js +52 -0
  50. package/packages/cli/dist/commands/safety/add-safe.js.map +1 -0
  51. package/packages/cli/dist/commands/safety/check.d.ts +9 -0
  52. package/packages/cli/dist/commands/safety/check.d.ts.map +1 -0
  53. package/packages/cli/dist/commands/safety/check.js +36 -0
  54. package/packages/cli/dist/commands/safety/check.js.map +1 -0
  55. package/packages/cli/dist/commands/safety/export.d.ts +10 -0
  56. package/packages/cli/dist/commands/safety/export.d.ts.map +1 -0
  57. package/packages/cli/dist/commands/safety/export.js +48 -0
  58. package/packages/cli/dist/commands/safety/export.js.map +1 -0
  59. package/packages/cli/dist/commands/safety/import.d.ts +12 -0
  60. package/packages/cli/dist/commands/safety/import.d.ts.map +1 -0
  61. package/packages/cli/dist/commands/safety/import.js +78 -0
  62. package/packages/cli/dist/commands/safety/import.js.map +1 -0
  63. package/packages/cli/dist/commands/safety/index.d.ts +8 -0
  64. package/packages/cli/dist/commands/safety/index.d.ts.map +1 -0
  65. package/packages/cli/dist/commands/safety/index.js +46 -0
  66. package/packages/cli/dist/commands/safety/index.js.map +1 -0
  67. package/packages/cli/dist/commands/safety/list.d.ts +6 -0
  68. package/packages/cli/dist/commands/safety/list.d.ts.map +1 -0
  69. package/packages/cli/dist/commands/safety/list.js +77 -0
  70. package/packages/cli/dist/commands/safety/list.js.map +1 -0
  71. package/packages/cli/dist/commands/safety/remove.d.ts +9 -0
  72. package/packages/cli/dist/commands/safety/remove.d.ts.map +1 -0
  73. package/packages/cli/dist/commands/safety/remove.js +46 -0
  74. package/packages/cli/dist/commands/safety/remove.js.map +1 -0
  75. package/packages/cli/dist/commands/safety/reset.d.ts +9 -0
  76. package/packages/cli/dist/commands/safety/reset.d.ts.map +1 -0
  77. package/packages/cli/dist/commands/safety/reset.js +46 -0
  78. package/packages/cli/dist/commands/safety/reset.js.map +1 -0
  79. package/packages/cli/dist/commands/safety/validate.d.ts +9 -0
  80. package/packages/cli/dist/commands/safety/validate.d.ts.map +1 -0
  81. package/packages/cli/dist/commands/safety/validate.js +51 -0
  82. package/packages/cli/dist/commands/safety/validate.js.map +1 -0
  83. package/packages/cli/dist/commands/secret/get.d.ts +9 -0
  84. package/packages/cli/dist/commands/secret/get.d.ts.map +1 -0
  85. package/packages/cli/dist/commands/secret/get.js +26 -0
  86. package/packages/cli/dist/commands/secret/get.js.map +1 -0
  87. package/packages/cli/dist/commands/secret/set.d.ts +10 -0
  88. package/packages/cli/dist/commands/secret/set.d.ts.map +1 -0
  89. package/packages/cli/dist/commands/secret/set.js +22 -0
  90. package/packages/cli/dist/commands/secret/set.js.map +1 -0
  91. package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.d.ts +2 -0
  92. package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.d.ts.map +1 -0
  93. package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.js +234 -0
  94. package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.js.map +1 -0
  95. package/packages/cli/dist/commands/server/add.d.ts +14 -0
  96. package/packages/cli/dist/commands/server/add.d.ts.map +1 -0
  97. package/packages/cli/dist/commands/server/add.js +86 -0
  98. package/packages/cli/dist/commands/server/add.js.map +1 -0
  99. package/packages/cli/dist/commands/server/available.d.ts +10 -0
  100. package/packages/cli/dist/commands/server/available.d.ts.map +1 -0
  101. package/packages/cli/dist/commands/server/available.js +62 -0
  102. package/packages/cli/dist/commands/server/available.js.map +1 -0
  103. package/packages/cli/dist/commands/server/debug.d.ts +18 -0
  104. package/packages/cli/dist/commands/server/debug.d.ts.map +1 -0
  105. package/packages/cli/dist/commands/server/debug.js +165 -0
  106. package/packages/cli/dist/commands/server/debug.js.map +1 -0
  107. package/packages/cli/dist/commands/server/info.d.ts +13 -0
  108. package/packages/cli/dist/commands/server/info.d.ts.map +1 -0
  109. package/packages/cli/dist/commands/server/info.js +62 -0
  110. package/packages/cli/dist/commands/server/info.js.map +1 -0
  111. package/packages/cli/dist/commands/server/list.d.ts +10 -0
  112. package/packages/cli/dist/commands/server/list.d.ts.map +1 -0
  113. package/packages/cli/dist/commands/server/list.js +105 -0
  114. package/packages/cli/dist/commands/server/list.js.map +1 -0
  115. package/packages/cli/dist/commands/server/refresh-tools.d.ts +13 -0
  116. package/packages/cli/dist/commands/server/refresh-tools.d.ts.map +1 -0
  117. package/packages/cli/dist/commands/server/refresh-tools.js +46 -0
  118. package/packages/cli/dist/commands/server/refresh-tools.js.map +1 -0
  119. package/packages/cli/dist/commands/server/remove.d.ts +12 -0
  120. package/packages/cli/dist/commands/server/remove.d.ts.map +1 -0
  121. package/packages/cli/dist/commands/server/remove.js +39 -0
  122. package/packages/cli/dist/commands/server/remove.js.map +1 -0
  123. package/packages/cli/dist/commands/server/restart.d.ts +9 -0
  124. package/packages/cli/dist/commands/server/restart.d.ts.map +1 -0
  125. package/packages/cli/dist/commands/server/restart.js +30 -0
  126. package/packages/cli/dist/commands/server/restart.js.map +1 -0
  127. package/packages/cli/dist/commands/server/start.d.ts +9 -0
  128. package/packages/cli/dist/commands/server/start.d.ts.map +1 -0
  129. package/packages/cli/dist/commands/server/start.js +37 -0
  130. package/packages/cli/dist/commands/server/start.js.map +1 -0
  131. package/packages/cli/dist/commands/server/status.d.ts +9 -0
  132. package/packages/cli/dist/commands/server/status.d.ts.map +1 -0
  133. package/packages/cli/dist/commands/server/status.js +30 -0
  134. package/packages/cli/dist/commands/server/status.js.map +1 -0
  135. package/packages/cli/dist/commands/server/stop.d.ts +9 -0
  136. package/packages/cli/dist/commands/server/stop.d.ts.map +1 -0
  137. package/packages/cli/dist/commands/server/stop.js +31 -0
  138. package/packages/cli/dist/commands/server/stop.js.map +1 -0
  139. package/packages/cli/dist/commands/server/validate.d.ts +15 -0
  140. package/packages/cli/dist/commands/server/validate.d.ts.map +1 -0
  141. package/packages/cli/dist/commands/server/validate.js +87 -0
  142. package/packages/cli/dist/commands/server/validate.js.map +1 -0
  143. package/packages/cli/dist/commands/stdio.d.ts +36 -0
  144. package/packages/cli/dist/commands/stdio.d.ts.map +1 -0
  145. package/packages/cli/dist/commands/stdio.js +85 -0
  146. package/packages/cli/dist/commands/stdio.js.map +1 -0
  147. package/packages/cli/dist/commands/tool/execute-confirm.d.ts +12 -0
  148. package/packages/cli/dist/commands/tool/execute-confirm.d.ts.map +1 -0
  149. package/packages/cli/dist/commands/tool/execute-confirm.js +98 -0
  150. package/packages/cli/dist/commands/tool/execute-confirm.js.map +1 -0
  151. package/packages/cli/dist/commands/tool/execute.d.ts +12 -0
  152. package/packages/cli/dist/commands/tool/execute.d.ts.map +1 -0
  153. package/packages/cli/dist/commands/tool/execute.js +55 -0
  154. package/packages/cli/dist/commands/tool/execute.js.map +1 -0
  155. package/packages/cli/dist/commands/tool/list.d.ts +13 -0
  156. package/packages/cli/dist/commands/tool/list.d.ts.map +1 -0
  157. package/packages/cli/dist/commands/tool/list.js +91 -0
  158. package/packages/cli/dist/commands/tool/list.js.map +1 -0
  159. package/packages/cli/dist/commands/tool/search.d.ts +12 -0
  160. package/packages/cli/dist/commands/tool/search.d.ts.map +1 -0
  161. package/packages/cli/dist/commands/tool/search.js +87 -0
  162. package/packages/cli/dist/commands/tool/search.js.map +1 -0
  163. package/packages/cli/dist/index.d.ts +2 -0
  164. package/packages/cli/dist/index.d.ts.map +1 -0
  165. package/packages/cli/dist/index.js +9 -0
  166. package/packages/cli/dist/index.js.map +1 -0
  167. package/packages/cli/dist/utils/daemon-checker.d.ts +18 -0
  168. package/packages/cli/dist/utils/daemon-checker.d.ts.map +1 -0
  169. package/packages/cli/dist/utils/daemon-checker.js +69 -0
  170. package/packages/cli/dist/utils/daemon-checker.js.map +1 -0
  171. package/packages/cli/dist/utils/daemon-endpoint.d.ts +7 -0
  172. package/packages/cli/dist/utils/daemon-endpoint.d.ts.map +1 -0
  173. package/packages/cli/dist/utils/daemon-endpoint.js +13 -0
  174. package/packages/cli/dist/utils/daemon-endpoint.js.map +1 -0
  175. package/packages/cli/dist/utils/get-configured-port.d.ts +43 -0
  176. package/packages/cli/dist/utils/get-configured-port.d.ts.map +1 -0
  177. package/packages/cli/dist/utils/get-configured-port.js +141 -0
  178. package/packages/cli/dist/utils/get-configured-port.js.map +1 -0
  179. package/packages/cli/dist/utils/stdio-bridge.d.ts +48 -0
  180. package/packages/cli/dist/utils/stdio-bridge.d.ts.map +1 -0
  181. package/packages/cli/dist/utils/stdio-bridge.js +181 -0
  182. package/packages/cli/dist/utils/stdio-bridge.js.map +1 -0
  183. package/packages/cli/package.json +48 -0
  184. package/packages/core/dist/config/defaults.d.ts +36 -0
  185. package/packages/core/dist/config/defaults.d.ts.map +1 -0
  186. package/packages/core/dist/config/defaults.js +324 -0
  187. package/packages/core/dist/config/defaults.js.map +1 -0
  188. package/packages/core/dist/config/index.d.ts +9 -0
  189. package/packages/core/dist/config/index.d.ts.map +1 -0
  190. package/packages/core/dist/config/index.js +14 -0
  191. package/packages/core/dist/config/index.js.map +1 -0
  192. package/packages/core/dist/config/loader.d.ts +269 -0
  193. package/packages/core/dist/config/loader.d.ts.map +1 -0
  194. package/packages/core/dist/config/loader.js +777 -0
  195. package/packages/core/dist/config/loader.js.map +1 -0
  196. package/packages/core/dist/config/registry.d.ts +212 -0
  197. package/packages/core/dist/config/registry.d.ts.map +1 -0
  198. package/packages/core/dist/config/registry.js +754 -0
  199. package/packages/core/dist/config/registry.js.map +1 -0
  200. package/packages/core/dist/config/schema.d.ts +4352 -0
  201. package/packages/core/dist/config/schema.d.ts.map +1 -0
  202. package/packages/core/dist/config/schema.js +267 -0
  203. package/packages/core/dist/config/schema.js.map +1 -0
  204. package/packages/core/dist/daemon.d.ts +7 -0
  205. package/packages/core/dist/daemon.d.ts.map +1 -0
  206. package/packages/core/dist/daemon.js +116 -0
  207. package/packages/core/dist/daemon.js.map +1 -0
  208. package/packages/core/dist/http-client-retry.d.ts +67 -0
  209. package/packages/core/dist/http-client-retry.d.ts.map +1 -0
  210. package/packages/core/dist/http-client-retry.js +133 -0
  211. package/packages/core/dist/http-client-retry.js.map +1 -0
  212. package/packages/core/dist/http-client-updated.d.ts +147 -0
  213. package/packages/core/dist/http-client-updated.d.ts.map +1 -0
  214. package/packages/core/dist/http-client-updated.js +452 -0
  215. package/packages/core/dist/http-client-updated.js.map +1 -0
  216. package/packages/core/dist/http-client.d.ts +207 -0
  217. package/packages/core/dist/http-client.d.ts.map +1 -0
  218. package/packages/core/dist/http-client.js +704 -0
  219. package/packages/core/dist/http-client.js.map +1 -0
  220. package/packages/core/dist/index.d.ts +13 -0
  221. package/packages/core/dist/index.d.ts.map +1 -0
  222. package/packages/core/dist/index.js +23 -0
  223. package/packages/core/dist/index.js.map +1 -0
  224. package/packages/core/dist/logging/index.d.ts +46 -0
  225. package/packages/core/dist/logging/index.d.ts.map +1 -0
  226. package/packages/core/dist/logging/index.js +74 -0
  227. package/packages/core/dist/logging/index.js.map +1 -0
  228. package/packages/core/dist/metrics/index.d.ts +339 -0
  229. package/packages/core/dist/metrics/index.d.ts.map +1 -0
  230. package/packages/core/dist/metrics/index.js +792 -0
  231. package/packages/core/dist/metrics/index.js.map +1 -0
  232. package/packages/core/dist/plugins/index.d.ts +49 -0
  233. package/packages/core/dist/plugins/index.d.ts.map +1 -0
  234. package/packages/core/dist/plugins/index.js +82 -0
  235. package/packages/core/dist/plugins/index.js.map +1 -0
  236. package/packages/core/dist/secrets/index.d.ts +6 -0
  237. package/packages/core/dist/secrets/index.d.ts.map +1 -0
  238. package/packages/core/dist/secrets/index.js +5 -0
  239. package/packages/core/dist/secrets/index.js.map +1 -0
  240. package/packages/core/dist/secrets/keyring.d.ts +54 -0
  241. package/packages/core/dist/secrets/keyring.d.ts.map +1 -0
  242. package/packages/core/dist/secrets/keyring.js +141 -0
  243. package/packages/core/dist/secrets/keyring.js.map +1 -0
  244. package/packages/core/dist/server/batch-executor.d.ts +83 -0
  245. package/packages/core/dist/server/batch-executor.d.ts.map +1 -0
  246. package/packages/core/dist/server/batch-executor.js +291 -0
  247. package/packages/core/dist/server/batch-executor.js.map +1 -0
  248. package/packages/core/dist/server/circuit-breaker.d.ts +215 -0
  249. package/packages/core/dist/server/circuit-breaker.d.ts.map +1 -0
  250. package/packages/core/dist/server/circuit-breaker.js +330 -0
  251. package/packages/core/dist/server/circuit-breaker.js.map +1 -0
  252. package/packages/core/dist/server/client-detection.d.ts +40 -0
  253. package/packages/core/dist/server/client-detection.d.ts.map +1 -0
  254. package/packages/core/dist/server/client-detection.js +242 -0
  255. package/packages/core/dist/server/client-detection.js.map +1 -0
  256. package/packages/core/dist/server/client-profiles.d.ts +102 -0
  257. package/packages/core/dist/server/client-profiles.d.ts.map +1 -0
  258. package/packages/core/dist/server/client-profiles.js +254 -0
  259. package/packages/core/dist/server/client-profiles.js.map +1 -0
  260. package/packages/core/dist/server/http.d.ts +386 -0
  261. package/packages/core/dist/server/http.d.ts.map +1 -0
  262. package/packages/core/dist/server/http.js +4253 -0
  263. package/packages/core/dist/server/http.js.map +1 -0
  264. package/packages/core/dist/server/index.d.ts +7 -0
  265. package/packages/core/dist/server/index.d.ts.map +1 -0
  266. package/packages/core/dist/server/index.js +6 -0
  267. package/packages/core/dist/server/index.js.map +1 -0
  268. package/packages/core/dist/server/manager.d.ts +458 -0
  269. package/packages/core/dist/server/manager.d.ts.map +1 -0
  270. package/packages/core/dist/server/manager.js +3255 -0
  271. package/packages/core/dist/server/manager.js.map +1 -0
  272. package/packages/core/dist/server/managers/HttpConnectionManager.d.ts +69 -0
  273. package/packages/core/dist/server/managers/HttpConnectionManager.d.ts.map +1 -0
  274. package/packages/core/dist/server/managers/HttpConnectionManager.js +214 -0
  275. package/packages/core/dist/server/managers/HttpConnectionManager.js.map +1 -0
  276. package/packages/core/dist/server/managers/ProcessManager.d.ts +128 -0
  277. package/packages/core/dist/server/managers/ProcessManager.d.ts.map +1 -0
  278. package/packages/core/dist/server/managers/ProcessManager.js +443 -0
  279. package/packages/core/dist/server/managers/ProcessManager.js.map +1 -0
  280. package/packages/core/dist/server/managers/SchemaCacheManager.d.ts +152 -0
  281. package/packages/core/dist/server/managers/SchemaCacheManager.d.ts.map +1 -0
  282. package/packages/core/dist/server/managers/SchemaCacheManager.js +426 -0
  283. package/packages/core/dist/server/managers/SchemaCacheManager.js.map +1 -0
  284. package/packages/core/dist/server/managers/index.d.ts +9 -0
  285. package/packages/core/dist/server/managers/index.d.ts.map +1 -0
  286. package/packages/core/dist/server/managers/index.js +9 -0
  287. package/packages/core/dist/server/managers/index.js.map +1 -0
  288. package/packages/core/dist/server/metrics.d.ts +134 -0
  289. package/packages/core/dist/server/metrics.d.ts.map +1 -0
  290. package/packages/core/dist/server/metrics.js +273 -0
  291. package/packages/core/dist/server/metrics.js.map +1 -0
  292. package/packages/core/dist/server/prompts.d.ts +58 -0
  293. package/packages/core/dist/server/prompts.d.ts.map +1 -0
  294. package/packages/core/dist/server/prompts.js +405 -0
  295. package/packages/core/dist/server/prompts.js.map +1 -0
  296. package/packages/core/dist/server/protocol-versions.d.ts +49 -0
  297. package/packages/core/dist/server/protocol-versions.d.ts.map +1 -0
  298. package/packages/core/dist/server/protocol-versions.js +173 -0
  299. package/packages/core/dist/server/protocol-versions.js.map +1 -0
  300. package/packages/core/dist/server/resources.d.ts +64 -0
  301. package/packages/core/dist/server/resources.d.ts.map +1 -0
  302. package/packages/core/dist/server/resources.js +243 -0
  303. package/packages/core/dist/server/resources.js.map +1 -0
  304. package/packages/core/dist/server/schema-store.d.ts +84 -0
  305. package/packages/core/dist/server/schema-store.d.ts.map +1 -0
  306. package/packages/core/dist/server/schema-store.js +234 -0
  307. package/packages/core/dist/server/schema-store.js.map +1 -0
  308. package/packages/core/dist/server/schema-validator.d.ts +51 -0
  309. package/packages/core/dist/server/schema-validator.d.ts.map +1 -0
  310. package/packages/core/dist/server/schema-validator.js +208 -0
  311. package/packages/core/dist/server/schema-validator.js.map +1 -0
  312. package/packages/core/dist/server/token-calculator.d.ts +44 -0
  313. package/packages/core/dist/server/token-calculator.d.ts.map +1 -0
  314. package/packages/core/dist/server/token-calculator.js +53 -0
  315. package/packages/core/dist/server/token-calculator.js.map +1 -0
  316. package/packages/core/dist/server/types.d.ts +45 -0
  317. package/packages/core/dist/server/types.d.ts.map +1 -0
  318. package/packages/core/dist/server/types.js +5 -0
  319. package/packages/core/dist/server/types.js.map +1 -0
  320. package/packages/core/dist/utils/file-lock.d.ts +73 -0
  321. package/packages/core/dist/utils/file-lock.d.ts.map +1 -0
  322. package/packages/core/dist/utils/file-lock.js +235 -0
  323. package/packages/core/dist/utils/file-lock.js.map +1 -0
  324. package/packages/core/package.json +36 -0
  325. package/packages/dashboard/dist/assets/index-B7hvkCMu.css +1 -0
  326. package/packages/dashboard/dist/assets/index-yZhLPpzr.js +1 -0
  327. package/packages/dashboard/dist/index.html +14 -0
  328. package/packages/dashboard/package.json +24 -0
  329. package/packages/shared/dist/version.d.ts +2 -0
  330. package/packages/shared/dist/version.d.ts.map +1 -0
  331. package/packages/shared/dist/version.js +2 -0
  332. package/packages/shared/dist/version.js.map +1 -0
  333. package/packages/shared/package.json +18 -0
@@ -0,0 +1,754 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { EventEmitter } from 'events';
4
+ import { ServerConfigSchema } from './schema.js';
5
+ import { FileLock } from '../utils/file-lock.js';
6
+ /**
7
+ * Security configuration for registry operations.
8
+ * Whitelist of allowed commands to prevent arbitrary command execution.
9
+ *
10
+ * SECURITY NOTE (OWASP CWE-78: OS Command Injection Prevention):
11
+ * - Only package runners and runtimes are whitelisted
12
+ * - 'bash' and 'sh' are EXPLICITLY PROHIBITED to prevent shell command injection
13
+ * - If registry is compromised, attackers cannot execute arbitrary shell commands
14
+ * - All server commands must use absolute paths or whitelisted runners
15
+ *
16
+ * @see https://owasp.org/www-community/attacks/Command_Injection
17
+ */
18
+ export const ALLOWED_COMMANDS = [
19
+ 'npx', // npm package runner - executes specific npm packages
20
+ 'uvx', // uv package runner - Python package execution
21
+ 'node', // Node.js runtime - requires explicit script path
22
+ 'python3', // Python 3 runtime - requires explicit script path
23
+ 'python', // Python runtime alias - requires explicit script path
24
+ 'mcp-grafana', // Specific MCP server binary (pre-approved)
25
+ // SECURITY: 'bash' and 'sh' REMOVED - shell execution prohibited
26
+ // Rationale: Shell access allows arbitrary command injection if registry is compromised
27
+ // Alternative: Use absolute paths (e.g., /usr/local/bin/myserver) for custom servers
28
+ ];
29
+ /**
30
+ * Patterns that indicate dangerous shell metacharacters or injection attempts.
31
+ * Used by validateServerArguments() to detect malicious input.
32
+ *
33
+ * @see https://owasp.org/www-community/attacks/Command_Injection
34
+ */
35
+ export const DANGEROUS_PATTERNS = [
36
+ /;/, // Command chaining
37
+ /\|/, // Pipe operator
38
+ /&/, // Background/chaining
39
+ /`/, // Command substitution (backticks)
40
+ /\$\(/, // Command substitution $(...)
41
+ /\$\{/, // Variable expansion ${...}
42
+ />/, // Output redirection
43
+ /</, // Input redirection
44
+ /\n/, // Newline injection
45
+ /\r/, // Carriage return injection
46
+ ];
47
+ /**
48
+ * SECURITY (CWE-78): Dangerous flags that could execute arbitrary code.
49
+ * These flags are blocked even for whitelisted commands to prevent
50
+ * command injection via flags like `node --eval "malicious code"`.
51
+ *
52
+ * @see https://owasp.org/www-community/attacks/Command_Injection
53
+ */
54
+ export const DANGEROUS_FLAGS = {
55
+ 'node': [
56
+ '--eval', '-e', // Run inline JavaScript code
57
+ '--print', '-p', // Evaluate and print expression
58
+ '--require', '-r', // Preload module (can run arbitrary code)
59
+ '--import', // ESM module preloading
60
+ '--input-type', // Interpret stdin as module (bypass detection)
61
+ ],
62
+ 'python': [
63
+ '-c', '--command', // Run inline Python code
64
+ '-m', // Run module as script (can be abused)
65
+ ],
66
+ 'python3': [
67
+ '-c', '--command', // Run inline Python code
68
+ '-m', // Run module as script (can be abused)
69
+ ],
70
+ 'npx': [
71
+ '--yes', '-y', // Auto-install without confirmation (malicious packages)
72
+ ],
73
+ };
74
+ /**
75
+ * Check if any argument contains a dangerous flag for the given command.
76
+ * Prevents command injection via flags like `node --eval "malicious code"`.
77
+ *
78
+ * SECURITY (CWE-78): This function blocks dangerous interpreter flags that
79
+ * could be used to run arbitrary code even when the command itself is whitelisted.
80
+ *
81
+ * @param command - The command being validated (e.g., 'node', 'python3')
82
+ * @param args - Array of command arguments
83
+ * @param serverName - Optional server name for audit logging
84
+ * @throws Error if a dangerous flag is detected
85
+ *
86
+ * @see https://owasp.org/www-community/attacks/Command_Injection
87
+ */
88
+ export function validateCommandFlags(command, args, serverName) {
89
+ const dangerousForCommand = DANGEROUS_FLAGS[command];
90
+ if (!dangerousForCommand)
91
+ return; // No restrictions for this command
92
+ const timestamp = new Date().toISOString();
93
+ for (const arg of args) {
94
+ // Check exact match and prefix match (e.g., --eval=code, -e=code)
95
+ for (const dangerousFlag of dangerousForCommand) {
96
+ if (arg === dangerousFlag || arg.startsWith(`${dangerousFlag}=`)) {
97
+ logSecurityEvent({
98
+ timestamp,
99
+ eventType: 'DANGEROUS_FLAG_BLOCKED',
100
+ severity: 'CRITICAL',
101
+ details: `Dangerous flag '${dangerousFlag}' blocked for command '${command}'. ` +
102
+ `This flag could be used to run arbitrary code.`,
103
+ serverName,
104
+ command,
105
+ // Note: Not logging full args array to avoid leaking potential attack payloads
106
+ });
107
+ throw new Error(`SECURITY VIOLATION: Flag '${dangerousFlag}' is not allowed for command '${command}'. ` +
108
+ `This flag could be used to run arbitrary code and is blocked for security reasons.`);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Log a security audit event.
115
+ * In production, this should be connected to a SIEM or audit log system.
116
+ *
117
+ * @param entry - Security audit log entry
118
+ */
119
+ export function logSecurityEvent(entry) {
120
+ const logMessage = `[SECURITY ${entry.severity}] ${entry.eventType}: ${entry.details}`;
121
+ // Always log to console with timestamp
122
+ if (entry.severity === 'CRITICAL' || entry.severity === 'HIGH') {
123
+ console.error(logMessage, {
124
+ timestamp: entry.timestamp,
125
+ serverName: entry.serverName,
126
+ command: entry.command,
127
+ // Intentionally not logging args for security (may contain sensitive data)
128
+ });
129
+ }
130
+ else {
131
+ console.warn(logMessage, {
132
+ timestamp: entry.timestamp,
133
+ serverName: entry.serverName,
134
+ });
135
+ }
136
+ }
137
+ /**
138
+ * Validate server command against security rules.
139
+ * Throws an error if command is not whitelisted or contains dangerous patterns.
140
+ *
141
+ * SECURITY: This is a critical function for preventing command injection.
142
+ *
143
+ * @param command - The command to execute (e.g., 'npx', 'node')
144
+ * @param args - Command arguments array
145
+ * @param serverName - Name of the server (for audit logging)
146
+ * @throws Error if validation fails
147
+ */
148
+ export function validateServerCommand(command, args, serverName) {
149
+ const timestamp = new Date().toISOString();
150
+ // Check 1: Is it a whitelisted command?
151
+ if (!ALLOWED_COMMANDS.includes(command)) {
152
+ // Special handling for shell commands - log as CRITICAL
153
+ if (command === 'bash' || command === 'sh' || command === 'zsh' || command === 'csh') {
154
+ logSecurityEvent({
155
+ timestamp,
156
+ eventType: 'SHELL_COMMAND_BLOCKED',
157
+ severity: 'CRITICAL',
158
+ details: `Shell command '${command}' is prohibited. Use package runners or absolute paths instead.`,
159
+ serverName,
160
+ command,
161
+ });
162
+ throw new Error(`SECURITY VIOLATION: Shell command '${command}' is not allowed. ` +
163
+ `Shell execution is prohibited to prevent command injection attacks. ` +
164
+ `Use 'npx', 'uvx', 'node', or 'python3' with explicit script paths.`);
165
+ }
166
+ logSecurityEvent({
167
+ timestamp,
168
+ eventType: 'COMMAND_VALIDATION_FAILED',
169
+ severity: 'HIGH',
170
+ details: `Command '${command}' not whitelisted. Allowed: ${ALLOWED_COMMANDS.join(', ')}`,
171
+ serverName,
172
+ command,
173
+ });
174
+ throw new Error(`Command '${command}' not whitelisted. Allowed commands: ${ALLOWED_COMMANDS.join(', ')}`);
175
+ }
176
+ // Check 2: SECURITY (CWE-78) - Block dangerous flags that could run arbitrary code
177
+ validateCommandFlags(command, args, serverName);
178
+ // Check 3: For node/python - first argument should be an absolute path or package name
179
+ if (['node', 'python3', 'python'].includes(command)) {
180
+ if (args.length === 0) {
181
+ logSecurityEvent({
182
+ timestamp,
183
+ eventType: 'COMMAND_VALIDATION_FAILED',
184
+ severity: 'MEDIUM',
185
+ details: `${command} requires a script path argument`,
186
+ serverName,
187
+ command,
188
+ });
189
+ throw new Error(`${command} requires a script path argument`);
190
+ }
191
+ const scriptPath = args[0];
192
+ // Allow: absolute paths, package names starting with @, relative paths from current dir
193
+ const isAbsolutePath = scriptPath.startsWith('/') || scriptPath.startsWith('~');
194
+ const isPackageName = scriptPath.startsWith('@') || /^[a-z0-9-]+$/i.test(scriptPath);
195
+ const isRelativeFromCurrent = scriptPath.startsWith('./');
196
+ // Reject paths starting with .. (path traversal attempt)
197
+ if (scriptPath.startsWith('..')) {
198
+ logSecurityEvent({
199
+ timestamp,
200
+ eventType: 'PATH_TRAVERSAL_DETECTED',
201
+ severity: 'HIGH',
202
+ details: `Script path starts with '..' which indicates path traversal: ${scriptPath}`,
203
+ serverName,
204
+ command,
205
+ args,
206
+ });
207
+ throw new Error(`Script path must not start with '..'. Use absolute paths or paths relative to current directory (./)`);
208
+ }
209
+ // Warn but allow relative paths from current directory
210
+ if (!isAbsolutePath && !isPackageName && !isRelativeFromCurrent) {
211
+ console.warn(`[SECURITY] Non-absolute script path: ${scriptPath}. Consider using absolute paths for security.`);
212
+ }
213
+ }
214
+ // Check 3: No path traversal in any arguments
215
+ for (const arg of args) {
216
+ if (arg.includes('../') || arg.includes('..\\')) {
217
+ logSecurityEvent({
218
+ timestamp,
219
+ eventType: 'PATH_TRAVERSAL_DETECTED',
220
+ severity: 'HIGH',
221
+ details: `Path traversal detected in argument: ${arg}`,
222
+ serverName,
223
+ command,
224
+ args,
225
+ });
226
+ throw new Error(`Path traversal detected in argument: ${arg}`);
227
+ }
228
+ // Check 4: No dangerous shell metacharacters in arguments
229
+ for (const pattern of DANGEROUS_PATTERNS) {
230
+ if (pattern.test(arg)) {
231
+ logSecurityEvent({
232
+ timestamp,
233
+ eventType: 'DANGEROUS_PATTERN_DETECTED',
234
+ severity: 'HIGH',
235
+ details: `Dangerous pattern detected in argument: ${arg}`,
236
+ serverName,
237
+ command,
238
+ args,
239
+ });
240
+ throw new Error(`Dangerous shell metacharacter detected in argument: ${arg}. ` +
241
+ 'Arguments must not contain shell operators (;|&`$<>).');
242
+ }
243
+ }
244
+ }
245
+ }
246
+ /**
247
+ * Validate registry integrity - checks all server configurations for security issues.
248
+ * This should be called when loading the registry to detect tampering.
249
+ *
250
+ * @param registry - The registry object containing servers array
251
+ * @returns Object with valid flag and any warnings/errors found
252
+ */
253
+ export async function validateRegistryIntegrity(registry) {
254
+ const timestamp = new Date().toISOString();
255
+ const warnings = [];
256
+ const errors = [];
257
+ for (const server of registry.servers) {
258
+ // Validate server name (alphanumeric, hyphens, underscores only)
259
+ if (!/^[a-zA-Z0-9_-]+$/.test(server.name)) {
260
+ errors.push(`Server name '${server.name}' contains invalid characters. Use alphanumeric, hyphens, or underscores only.`);
261
+ }
262
+ // Validate command if present (stdio server)
263
+ if (server.command) {
264
+ // Check for shell commands
265
+ if (['bash', 'sh', 'zsh', 'csh'].includes(server.command)) {
266
+ errors.push(`Server '${server.name}' uses prohibited shell command '${server.command}'. ` +
267
+ `Shell commands are blocked to prevent command injection.`);
268
+ logSecurityEvent({
269
+ timestamp,
270
+ eventType: 'SHELL_COMMAND_BLOCKED',
271
+ severity: 'CRITICAL',
272
+ details: `Registry contains shell command for server '${server.name}'`,
273
+ serverName: server.name,
274
+ command: server.command,
275
+ });
276
+ }
277
+ // Check command whitelist
278
+ if (!ALLOWED_COMMANDS.includes(server.command)) {
279
+ warnings.push(`Server '${server.name}' uses non-whitelisted command '${server.command}'. ` +
280
+ `Consider using whitelisted commands: ${ALLOWED_COMMANDS.join(', ')}`);
281
+ }
282
+ // Validate arguments
283
+ if (server.args) {
284
+ // SECURITY (CWE-78): Check for dangerous flags that could run arbitrary code
285
+ const dangerousForCommand = DANGEROUS_FLAGS[server.command];
286
+ if (dangerousForCommand) {
287
+ for (const arg of server.args) {
288
+ for (const dangerousFlag of dangerousForCommand) {
289
+ if (arg === dangerousFlag || arg.startsWith(`${dangerousFlag}=`)) {
290
+ errors.push(`Server '${server.name}' uses dangerous flag '${dangerousFlag}' for command '${server.command}'. ` +
291
+ `This flag is blocked to prevent arbitrary code running.`);
292
+ logSecurityEvent({
293
+ timestamp,
294
+ eventType: 'DANGEROUS_FLAG_BLOCKED',
295
+ severity: 'CRITICAL',
296
+ details: `Registry contains dangerous flag '${dangerousFlag}' for server '${server.name}'`,
297
+ serverName: server.name,
298
+ command: server.command,
299
+ });
300
+ }
301
+ }
302
+ }
303
+ }
304
+ // Check for path traversal and dangerous patterns
305
+ for (const arg of server.args) {
306
+ if (arg.includes('../') || arg.includes('..\\')) {
307
+ errors.push(`Server '${server.name}' has path traversal in argument: ${arg}`);
308
+ }
309
+ for (const pattern of DANGEROUS_PATTERNS) {
310
+ if (pattern.test(arg)) {
311
+ errors.push(`Server '${server.name}' has dangerous pattern in argument: ${arg}`);
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ // Validate environment variables
318
+ if (server.env) {
319
+ for (const [key, value] of Object.entries(server.env)) {
320
+ // Check for path traversal in env values
321
+ if (typeof value === 'string' && (value.includes('../') || value.includes('..\\'))) {
322
+ warnings.push(`Server '${server.name}' has path traversal in env var '${key}'`);
323
+ }
324
+ // Don't log actual env values - they may contain secrets
325
+ }
326
+ }
327
+ }
328
+ const valid = errors.length === 0;
329
+ logSecurityEvent({
330
+ timestamp,
331
+ eventType: valid ? 'INTEGRITY_CHECK_PASSED' : 'INTEGRITY_CHECK_FAILED',
332
+ severity: valid ? 'LOW' : 'HIGH',
333
+ details: `Registry integrity check: ${errors.length} errors, ${warnings.length} warnings`,
334
+ });
335
+ return { valid, warnings, errors };
336
+ }
337
+ /**
338
+ * RegistryManager handles atomic read/write operations on mcp-registry.json.
339
+ * Ensures data integrity through file locking and rolling backups.
340
+ *
341
+ * Events emitted:
342
+ * - server:added { server: ServerConfig, timestamp: number }
343
+ * - server:removed { name: string, timestamp: number }
344
+ * - registry:saved { path: string, timestamp: number }
345
+ * - registry:error { error: string, path: string }
346
+ */
347
+ export class RegistryManager extends EventEmitter {
348
+ /**
349
+ * Get the default persistent registry path (~/.config/metalink/mcp-registry.json)
350
+ * Respects METALINK_CONFIG_DIR environment variable if set
351
+ */
352
+ static getDefaultRegistryPath() {
353
+ const configDir = process.env.METALINK_CONFIG_DIR ||
354
+ path.join(process.env.HOME || process.env.USERPROFILE || '~', '.config', 'metalink');
355
+ return path.join(configDir, 'mcp-registry.json');
356
+ }
357
+ constructor(registryPath = '') {
358
+ super();
359
+ this.maxBackups = 3;
360
+ // Use persistent config dir by default if no path provided
361
+ const resolvedPath = registryPath || RegistryManager.getDefaultRegistryPath();
362
+ this.registryPath = path.resolve(resolvedPath);
363
+ this.lock = new FileLock(this.registryPath);
364
+ }
365
+ /**
366
+ * Read and parse the registry file.
367
+ * Does not require lock (read-only operation).
368
+ */
369
+ async readRegistry() {
370
+ try {
371
+ const content = await fs.readFile(this.registryPath, 'utf8');
372
+ const parsed = JSON.parse(content);
373
+ if (!Array.isArray(parsed.servers)) {
374
+ throw new Error('Invalid registry format: servers must be an array');
375
+ }
376
+ return parsed;
377
+ }
378
+ catch (error) {
379
+ if (error.code === 'ENOENT') {
380
+ // Return empty registry if file doesn't exist yet
381
+ return { servers: [] };
382
+ }
383
+ throw error;
384
+ }
385
+ }
386
+ /**
387
+ * Get all servers from registry.
388
+ */
389
+ async getServers() {
390
+ const registry = await this.readRegistry();
391
+ return registry.servers;
392
+ }
393
+ /**
394
+ * Get a single server by name.
395
+ */
396
+ async getServer(name) {
397
+ const servers = await this.getServers();
398
+ return servers.find(s => s.name === name) || null;
399
+ }
400
+ /**
401
+ * Validate a server configuration.
402
+ * Returns { valid: true } or { valid: false, errors: [...] }
403
+ *
404
+ * SECURITY: Uses validateServerCommand() for comprehensive security checks.
405
+ */
406
+ async validateServer(config, options = {}) {
407
+ // Validate schema
408
+ const schemaResult = ServerConfigSchema.safeParse(config);
409
+ if (!schemaResult.success) {
410
+ const errors = schemaResult.error.errors.map(e => {
411
+ return `${e.path.join('.')}: ${e.message}`;
412
+ });
413
+ return { valid: false, errors };
414
+ }
415
+ const server = schemaResult.data;
416
+ const errors = [];
417
+ // Validate command (only for stdio servers, and unless allowAnyCommand is true)
418
+ if (!options.allowAnyCommand) {
419
+ if (server.transport === 'stdio' || server.transport === undefined) {
420
+ // For stdio servers, validate the command using security-hardened function
421
+ const stdioServer = server;
422
+ // SECURITY: Use validateServerCommand for comprehensive checks
423
+ try {
424
+ validateServerCommand(stdioServer.command, stdioServer.args || [], server.name);
425
+ }
426
+ catch (validationError) {
427
+ errors.push(validationError instanceof Error
428
+ ? validationError.message
429
+ : String(validationError));
430
+ }
431
+ }
432
+ else {
433
+ // For HTTP servers, check if mcp-remote proxy is available
434
+ if (!ALLOWED_COMMANDS.includes('mcp-remote')) {
435
+ errors.push(`HTTP server '${server.name}' requires 'mcp-remote' proxy to be enabled`);
436
+ }
437
+ }
438
+ }
439
+ // Validate environment variables (no path traversal)
440
+ for (const [key, value] of Object.entries(server.env || {})) {
441
+ if (typeof value !== 'string') {
442
+ errors.push(`Environment variable '${key}' must be a string`);
443
+ continue;
444
+ }
445
+ if (value.includes('../') || value.includes('..\\')) {
446
+ errors.push(`Environment variable '${key}' contains path traversal`);
447
+ }
448
+ // SECURITY: Check for dangerous patterns in env values
449
+ for (const pattern of DANGEROUS_PATTERNS) {
450
+ if (pattern.test(value)) {
451
+ // Only warn for env vars (may have legitimate uses like URLs with &)
452
+ // But log it for security monitoring
453
+ console.warn(`[SECURITY] Server '${server.name}' env var '${key}' contains pattern that may indicate injection`);
454
+ break;
455
+ }
456
+ }
457
+ }
458
+ return errors.length > 0 ? { valid: false, errors } : { valid: true };
459
+ }
460
+ /**
461
+ * Add a new server to the registry.
462
+ * Validates config, checks uniqueness, acquires lock, writes atomically.
463
+ *
464
+ * @throws Error if validation fails, server already exists, or write fails
465
+ */
466
+ async addServer(config, options = {}) {
467
+ const { allowAnyCommand = false, timeout = 5000 } = options;
468
+ // Validate configuration
469
+ const validation = await this.validateServer(config, { allowAnyCommand });
470
+ if (!validation.valid) {
471
+ const errorMsg = validation.errors.join('; ');
472
+ throw new Error(`Server validation failed: ${errorMsg}`);
473
+ }
474
+ const server = ServerConfigSchema.parse(config);
475
+ // Acquire lock
476
+ await this.lock.acquire(timeout);
477
+ try {
478
+ // Read current registry (double-check uniqueness under lock)
479
+ const registry = await this.readRegistry();
480
+ if (registry.servers.some(s => s.name === server.name)) {
481
+ throw new Error(`Server '${server.name}' already exists in registry`);
482
+ }
483
+ // Add server
484
+ registry.servers.push(server);
485
+ // Write atomically with backup
486
+ await this.writeRegistry(registry);
487
+ // Emit event
488
+ this.emit('server:added', {
489
+ server,
490
+ timestamp: Date.now(),
491
+ });
492
+ return server;
493
+ }
494
+ finally {
495
+ await this.lock.release();
496
+ }
497
+ }
498
+ /**
499
+ * Remove a server from the registry by name.
500
+ * Acquires lock, removes server, writes atomically.
501
+ *
502
+ * @throws Error if server not found or write fails
503
+ */
504
+ async removeServer(name, options = {}) {
505
+ const { timeout = 5000 } = options;
506
+ // Acquire lock
507
+ await this.lock.acquire(timeout);
508
+ try {
509
+ // Read current registry
510
+ const registry = await this.readRegistry();
511
+ const initialCount = registry.servers.length;
512
+ // Remove server
513
+ registry.servers = registry.servers.filter(s => s.name !== name);
514
+ // Check if server was found
515
+ if (registry.servers.length === initialCount) {
516
+ throw new Error(`Server '${name}' not found in registry`);
517
+ }
518
+ // Write atomically with backup
519
+ await this.writeRegistry(registry);
520
+ // Emit event
521
+ this.emit('server:removed', {
522
+ name,
523
+ timestamp: Date.now(),
524
+ });
525
+ }
526
+ finally {
527
+ await this.lock.release();
528
+ }
529
+ }
530
+ /**
531
+ * Update an existing server configuration.
532
+ * Validates config, replaces server, writes atomically.
533
+ */
534
+ async updateServer(name, config, options = {}) {
535
+ const { allowAnyCommand = false, timeout = 5000 } = options;
536
+ // Validate new configuration
537
+ const validation = await this.validateServer(config, { allowAnyCommand });
538
+ if (!validation.valid) {
539
+ const errorMsg = validation.errors.join('; ');
540
+ throw new Error(`Server validation failed: ${errorMsg}`);
541
+ }
542
+ const newConfig = ServerConfigSchema.parse(config);
543
+ // Acquire lock
544
+ await this.lock.acquire(timeout);
545
+ try {
546
+ // Read current registry
547
+ const registry = await this.readRegistry();
548
+ const serverIndex = registry.servers.findIndex(s => s.name === name);
549
+ if (serverIndex === -1) {
550
+ throw new Error(`Server '${name}' not found in registry`);
551
+ }
552
+ // Replace server (name must match)
553
+ if (newConfig.name !== name) {
554
+ throw new Error(`Cannot change server name from '${name}' to '${newConfig.name}'`);
555
+ }
556
+ registry.servers[serverIndex] = newConfig;
557
+ // Write atomically with backup
558
+ await this.writeRegistry(registry);
559
+ // Emit event
560
+ this.emit('server:updated', {
561
+ server: newConfig,
562
+ timestamp: Date.now(),
563
+ });
564
+ return newConfig;
565
+ }
566
+ finally {
567
+ await this.lock.release();
568
+ }
569
+ }
570
+ /**
571
+ * Internal method to write registry with backup and rollback on failure.
572
+ */
573
+ async writeRegistry(registry) {
574
+ const content = JSON.stringify(registry, null, 2);
575
+ try {
576
+ // Create backup before write
577
+ await this.createBackup();
578
+ // Atomic write with temp file + rename
579
+ const dir = path.dirname(this.registryPath);
580
+ const tempPath = path.join(dir, `.registry.tmp.${Date.now()}`);
581
+ try {
582
+ // Write to temp file
583
+ await fs.writeFile(tempPath, content, 'utf8');
584
+ // Atomic rename
585
+ await fs.rename(tempPath, this.registryPath);
586
+ // Clean up old backups
587
+ await this.rotateBackups();
588
+ // Emit success event
589
+ this.emit('registry:saved', {
590
+ path: this.registryPath,
591
+ timestamp: Date.now(),
592
+ });
593
+ }
594
+ catch (error) {
595
+ // Cleanup temp file on error
596
+ try {
597
+ await fs.unlink(tempPath);
598
+ }
599
+ catch {
600
+ // Ignore cleanup errors
601
+ }
602
+ throw error;
603
+ }
604
+ }
605
+ catch (error) {
606
+ // Emit error event
607
+ this.emit('registry:error', {
608
+ error: error.message,
609
+ path: this.registryPath,
610
+ timestamp: Date.now(),
611
+ });
612
+ throw error;
613
+ }
614
+ }
615
+ /**
616
+ * Create a backup of the current registry.
617
+ * Backups are named: registry.json.backup, registry.json.backup.1, etc.
618
+ */
619
+ async createBackup() {
620
+ try {
621
+ const backupPath = `${this.registryPath}.backup`;
622
+ await fs.copyFile(this.registryPath, backupPath);
623
+ }
624
+ catch (error) {
625
+ if (error.code !== 'ENOENT') {
626
+ // Only log, don't throw - backup failure shouldn't block writes
627
+ console.warn('Failed to create backup:', error);
628
+ }
629
+ }
630
+ }
631
+ /**
632
+ * Rotate backups to keep only the last N backups.
633
+ * Keeps: .backup, .backup.1, .backup.2 (maxBackups = 3)
634
+ */
635
+ async rotateBackups() {
636
+ const dir = path.dirname(this.registryPath);
637
+ const filename = path.basename(this.registryPath);
638
+ try {
639
+ // Remove oldest backup if we've exceeded max
640
+ const oldestBackup = `${this.registryPath}.backup.${this.maxBackups}`;
641
+ try {
642
+ await fs.unlink(oldestBackup);
643
+ }
644
+ catch (error) {
645
+ if (error.code !== 'ENOENT') {
646
+ throw error;
647
+ }
648
+ }
649
+ // Shift backups: .backup.1 → .backup.2, .backup → .backup.1
650
+ for (let i = this.maxBackups - 1; i >= 1; i--) {
651
+ const oldPath = `${this.registryPath}.backup${i === 1 ? '' : '.' + (i - 1)}`;
652
+ const newPath = `${this.registryPath}.backup.${i}`;
653
+ try {
654
+ await fs.rename(oldPath, newPath);
655
+ }
656
+ catch (error) {
657
+ if (error.code !== 'ENOENT') {
658
+ throw error;
659
+ }
660
+ }
661
+ }
662
+ }
663
+ catch (error) {
664
+ console.warn('Failed to rotate backups:', error);
665
+ // Don't throw - backup rotation failure shouldn't block writes
666
+ }
667
+ }
668
+ /**
669
+ * Restore registry from backup.
670
+ * Useful for rollback on catastrophic failure.
671
+ */
672
+ async restoreFromBackup(backupIndex = 0) {
673
+ const backupPath = backupIndex === 0
674
+ ? `${this.registryPath}.backup`
675
+ : `${this.registryPath}.backup.${backupIndex}`;
676
+ try {
677
+ await fs.copyFile(backupPath, this.registryPath);
678
+ this.emit('registry:restored', {
679
+ from: backupPath,
680
+ to: this.registryPath,
681
+ timestamp: Date.now(),
682
+ });
683
+ }
684
+ catch (error) {
685
+ if (error.code === 'ENOENT') {
686
+ throw new Error(`Backup not found: ${backupPath}`);
687
+ }
688
+ throw error;
689
+ }
690
+ }
691
+ /**
692
+ * Get backup file info for monitoring/debugging.
693
+ */
694
+ async getBackupInfo() {
695
+ const backups = [];
696
+ for (let i = 0; i < this.maxBackups; i++) {
697
+ const backupPath = i === 0
698
+ ? `${this.registryPath}.backup`
699
+ : `${this.registryPath}.backup.${i}`;
700
+ try {
701
+ const stat = await fs.stat(backupPath);
702
+ backups.push({
703
+ path: backupPath,
704
+ size: stat.size,
705
+ modified: stat.mtime,
706
+ });
707
+ }
708
+ catch (error) {
709
+ if (error.code !== 'ENOENT') {
710
+ console.warn(`Failed to stat backup ${backupPath}:`, error);
711
+ }
712
+ }
713
+ }
714
+ return backups;
715
+ }
716
+ /**
717
+ * Get registry file info.
718
+ */
719
+ async getRegistryInfo() {
720
+ try {
721
+ const stat = await fs.stat(this.registryPath);
722
+ return {
723
+ path: this.registryPath,
724
+ size: stat.size,
725
+ exists: true,
726
+ };
727
+ }
728
+ catch (error) {
729
+ if (error.code === 'ENOENT') {
730
+ return {
731
+ path: this.registryPath,
732
+ size: 0,
733
+ exists: false,
734
+ };
735
+ }
736
+ throw error;
737
+ }
738
+ }
739
+ }
740
+ /**
741
+ * Singleton instance for global registry management.
742
+ * Usage: import { registryManager } from './registry';
743
+ */
744
+ let globalRegistry = null;
745
+ export function getRegistryManager(registryPath) {
746
+ if (!globalRegistry) {
747
+ globalRegistry = new RegistryManager(registryPath);
748
+ }
749
+ return globalRegistry;
750
+ }
751
+ export function resetRegistryManager() {
752
+ globalRegistry = null;
753
+ }
754
+ //# sourceMappingURL=registry.js.map