@sonde/packs 0.1.0 → 0.1.2

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 (377) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +53 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/CHANGELOG.md +19 -0
  5. package/dist/index.d.ts +16 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +40 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/integrations/citrix.d.ts +13 -0
  10. package/dist/integrations/citrix.d.ts.map +1 -0
  11. package/dist/integrations/citrix.js +415 -0
  12. package/dist/integrations/citrix.js.map +1 -0
  13. package/dist/integrations/citrix.test.d.ts +2 -0
  14. package/dist/integrations/citrix.test.d.ts.map +1 -0
  15. package/dist/integrations/citrix.test.js +464 -0
  16. package/dist/integrations/citrix.test.js.map +1 -0
  17. package/dist/integrations/graph.d.ts +9 -0
  18. package/dist/integrations/graph.d.ts.map +1 -0
  19. package/dist/integrations/graph.js +285 -0
  20. package/dist/integrations/graph.js.map +1 -0
  21. package/dist/integrations/graph.test.d.ts +2 -0
  22. package/dist/integrations/graph.test.d.ts.map +1 -0
  23. package/dist/integrations/graph.test.js +356 -0
  24. package/dist/integrations/graph.test.js.map +1 -0
  25. package/dist/integrations/httpbin.d.ts +3 -0
  26. package/dist/integrations/httpbin.d.ts.map +1 -0
  27. package/dist/integrations/httpbin.js +65 -0
  28. package/dist/integrations/httpbin.js.map +1 -0
  29. package/dist/integrations/nutanix.d.ts +18 -0
  30. package/dist/integrations/nutanix.d.ts.map +1 -0
  31. package/dist/integrations/nutanix.js +1116 -0
  32. package/dist/integrations/nutanix.js.map +1 -0
  33. package/dist/integrations/nutanix.test.d.ts +2 -0
  34. package/dist/integrations/nutanix.test.d.ts.map +1 -0
  35. package/dist/integrations/nutanix.test.js +978 -0
  36. package/dist/integrations/nutanix.test.js.map +1 -0
  37. package/dist/integrations/proxmox.d.ts +12 -0
  38. package/dist/integrations/proxmox.d.ts.map +1 -0
  39. package/dist/integrations/proxmox.js +728 -0
  40. package/dist/integrations/proxmox.js.map +1 -0
  41. package/dist/integrations/proxmox.test.d.ts +2 -0
  42. package/dist/integrations/proxmox.test.d.ts.map +1 -0
  43. package/dist/integrations/proxmox.test.js +697 -0
  44. package/dist/integrations/proxmox.test.js.map +1 -0
  45. package/dist/integrations/servicenow.d.ts +3 -0
  46. package/dist/integrations/servicenow.d.ts.map +1 -0
  47. package/dist/integrations/servicenow.js +251 -0
  48. package/dist/integrations/servicenow.js.map +1 -0
  49. package/dist/integrations/servicenow.test.d.ts +2 -0
  50. package/dist/integrations/servicenow.test.d.ts.map +1 -0
  51. package/dist/integrations/servicenow.test.js +217 -0
  52. package/dist/integrations/servicenow.test.js.map +1 -0
  53. package/dist/integrations/splunk.d.ts +9 -0
  54. package/dist/integrations/splunk.d.ts.map +1 -0
  55. package/dist/integrations/splunk.js +237 -0
  56. package/dist/integrations/splunk.js.map +1 -0
  57. package/dist/integrations/splunk.test.d.ts +2 -0
  58. package/dist/integrations/splunk.test.d.ts.map +1 -0
  59. package/dist/integrations/splunk.test.js +323 -0
  60. package/dist/integrations/splunk.test.js.map +1 -0
  61. package/dist/mysql/index.d.ts +3 -0
  62. package/dist/mysql/index.d.ts.map +1 -0
  63. package/dist/mysql/index.js +13 -0
  64. package/dist/mysql/index.js.map +1 -0
  65. package/dist/mysql/manifest.d.ts +3 -0
  66. package/dist/mysql/manifest.d.ts.map +1 -0
  67. package/dist/mysql/manifest.js +69 -0
  68. package/dist/mysql/manifest.js.map +1 -0
  69. package/dist/mysql/probes/databases-list.d.ts +13 -0
  70. package/dist/mysql/probes/databases-list.d.ts.map +1 -0
  71. package/dist/mysql/probes/databases-list.js +31 -0
  72. package/dist/mysql/probes/databases-list.js.map +1 -0
  73. package/dist/mysql/probes/databases-list.test.d.ts +2 -0
  74. package/dist/mysql/probes/databases-list.test.d.ts.map +1 -0
  75. package/dist/mysql/probes/databases-list.test.js +54 -0
  76. package/dist/mysql/probes/databases-list.test.js.map +1 -0
  77. package/dist/mysql/probes/processlist.d.ts +18 -0
  78. package/dist/mysql/probes/processlist.d.ts.map +1 -0
  79. package/dist/mysql/probes/processlist.js +36 -0
  80. package/dist/mysql/probes/processlist.js.map +1 -0
  81. package/dist/mysql/probes/processlist.test.d.ts +2 -0
  82. package/dist/mysql/probes/processlist.test.d.ts.map +1 -0
  83. package/dist/mysql/probes/processlist.test.js +41 -0
  84. package/dist/mysql/probes/processlist.test.js.map +1 -0
  85. package/dist/mysql/probes/status.d.ts +14 -0
  86. package/dist/mysql/probes/status.d.ts.map +1 -0
  87. package/dist/mysql/probes/status.js +40 -0
  88. package/dist/mysql/probes/status.js.map +1 -0
  89. package/dist/mysql/probes/status.test.d.ts +2 -0
  90. package/dist/mysql/probes/status.test.d.ts.map +1 -0
  91. package/dist/mysql/probes/status.test.js +43 -0
  92. package/dist/mysql/probes/status.test.js.map +1 -0
  93. package/dist/nginx/index.d.ts +3 -0
  94. package/dist/nginx/index.d.ts.map +1 -0
  95. package/dist/nginx/index.js +13 -0
  96. package/dist/nginx/index.js.map +1 -0
  97. package/dist/nginx/manifest.d.ts +3 -0
  98. package/dist/nginx/manifest.d.ts.map +1 -0
  99. package/dist/nginx/manifest.js +68 -0
  100. package/dist/nginx/manifest.js.map +1 -0
  101. package/dist/nginx/probes/access-log-tail.d.ts +9 -0
  102. package/dist/nginx/probes/access-log-tail.d.ts.map +1 -0
  103. package/dist/nginx/probes/access-log-tail.js +14 -0
  104. package/dist/nginx/probes/access-log-tail.js.map +1 -0
  105. package/dist/nginx/probes/access-log-tail.test.d.ts +2 -0
  106. package/dist/nginx/probes/access-log-tail.test.d.ts.map +1 -0
  107. package/dist/nginx/probes/access-log-tail.test.js +40 -0
  108. package/dist/nginx/probes/access-log-tail.test.js.map +1 -0
  109. package/dist/nginx/probes/config-test.d.ts +8 -0
  110. package/dist/nginx/probes/config-test.d.ts.map +1 -0
  111. package/dist/nginx/probes/config-test.js +18 -0
  112. package/dist/nginx/probes/config-test.js.map +1 -0
  113. package/dist/nginx/probes/config-test.test.d.ts +2 -0
  114. package/dist/nginx/probes/config-test.test.d.ts.map +1 -0
  115. package/dist/nginx/probes/config-test.test.js +35 -0
  116. package/dist/nginx/probes/config-test.test.js.map +1 -0
  117. package/dist/nginx/probes/error-log-tail.d.ts +9 -0
  118. package/dist/nginx/probes/error-log-tail.d.ts.map +1 -0
  119. package/dist/nginx/probes/error-log-tail.js +14 -0
  120. package/dist/nginx/probes/error-log-tail.js.map +1 -0
  121. package/dist/nginx/probes/error-log-tail.test.d.ts +2 -0
  122. package/dist/nginx/probes/error-log-tail.test.d.ts.map +1 -0
  123. package/dist/nginx/probes/error-log-tail.test.js +34 -0
  124. package/dist/nginx/probes/error-log-tail.test.js.map +1 -0
  125. package/dist/postgres/index.d.ts +3 -0
  126. package/dist/postgres/index.d.ts.map +1 -0
  127. package/dist/postgres/index.js +13 -0
  128. package/dist/postgres/index.js.map +1 -0
  129. package/dist/postgres/manifest.d.ts +3 -0
  130. package/dist/postgres/manifest.d.ts.map +1 -0
  131. package/dist/postgres/manifest.js +90 -0
  132. package/dist/postgres/manifest.js.map +1 -0
  133. package/dist/postgres/probes/connections-active.d.ts +17 -0
  134. package/dist/postgres/probes/connections-active.d.ts.map +1 -0
  135. package/dist/postgres/probes/connections-active.js +37 -0
  136. package/dist/postgres/probes/connections-active.js.map +1 -0
  137. package/dist/postgres/probes/connections-active.test.d.ts +2 -0
  138. package/dist/postgres/probes/connections-active.test.d.ts.map +1 -0
  139. package/dist/postgres/probes/connections-active.test.js +36 -0
  140. package/dist/postgres/probes/connections-active.test.js.map +1 -0
  141. package/dist/postgres/probes/databases-list.d.ts +14 -0
  142. package/dist/postgres/probes/databases-list.d.ts.map +1 -0
  143. package/dist/postgres/probes/databases-list.js +34 -0
  144. package/dist/postgres/probes/databases-list.js.map +1 -0
  145. package/dist/postgres/probes/databases-list.test.d.ts +2 -0
  146. package/dist/postgres/probes/databases-list.test.d.ts.map +1 -0
  147. package/dist/postgres/probes/databases-list.test.js +49 -0
  148. package/dist/postgres/probes/databases-list.test.js.map +1 -0
  149. package/dist/postgres/probes/query-slow.d.ts +17 -0
  150. package/dist/postgres/probes/query-slow.d.ts.map +1 -0
  151. package/dist/postgres/probes/query-slow.js +37 -0
  152. package/dist/postgres/probes/query-slow.js.map +1 -0
  153. package/dist/postgres/probes/query-slow.test.d.ts +2 -0
  154. package/dist/postgres/probes/query-slow.test.d.ts.map +1 -0
  155. package/dist/postgres/probes/query-slow.test.js +30 -0
  156. package/dist/postgres/probes/query-slow.test.js.map +1 -0
  157. package/dist/proxmox/index.d.ts +3 -0
  158. package/dist/proxmox/index.d.ts.map +1 -0
  159. package/dist/proxmox/index.js +23 -0
  160. package/dist/proxmox/index.js.map +1 -0
  161. package/dist/proxmox/manifest.d.ts +3 -0
  162. package/dist/proxmox/manifest.d.ts.map +1 -0
  163. package/dist/proxmox/manifest.js +75 -0
  164. package/dist/proxmox/manifest.js.map +1 -0
  165. package/dist/proxmox/probes/ceph-status.d.ts +36 -0
  166. package/dist/proxmox/probes/ceph-status.d.ts.map +1 -0
  167. package/dist/proxmox/probes/ceph-status.js +71 -0
  168. package/dist/proxmox/probes/ceph-status.js.map +1 -0
  169. package/dist/proxmox/probes/ceph-status.test.d.ts +2 -0
  170. package/dist/proxmox/probes/ceph-status.test.d.ts.map +1 -0
  171. package/dist/proxmox/probes/ceph-status.test.js +115 -0
  172. package/dist/proxmox/probes/ceph-status.test.js.map +1 -0
  173. package/dist/proxmox/probes/cluster-config.d.ts +31 -0
  174. package/dist/proxmox/probes/cluster-config.d.ts.map +1 -0
  175. package/dist/proxmox/probes/cluster-config.js +72 -0
  176. package/dist/proxmox/probes/cluster-config.js.map +1 -0
  177. package/dist/proxmox/probes/cluster-config.test.d.ts +2 -0
  178. package/dist/proxmox/probes/cluster-config.test.d.ts.map +1 -0
  179. package/dist/proxmox/probes/cluster-config.test.js +107 -0
  180. package/dist/proxmox/probes/cluster-config.test.js.map +1 -0
  181. package/dist/proxmox/probes/ha-status.d.ts +18 -0
  182. package/dist/proxmox/probes/ha-status.d.ts.map +1 -0
  183. package/dist/proxmox/probes/ha-status.js +38 -0
  184. package/dist/proxmox/probes/ha-status.js.map +1 -0
  185. package/dist/proxmox/probes/ha-status.test.d.ts +2 -0
  186. package/dist/proxmox/probes/ha-status.test.d.ts.map +1 -0
  187. package/dist/proxmox/probes/ha-status.test.js +66 -0
  188. package/dist/proxmox/probes/ha-status.test.js.map +1 -0
  189. package/dist/proxmox/probes/lvm.d.ts +35 -0
  190. package/dist/proxmox/probes/lvm.d.ts.map +1 -0
  191. package/dist/proxmox/probes/lvm.js +75 -0
  192. package/dist/proxmox/probes/lvm.js.map +1 -0
  193. package/dist/proxmox/probes/lvm.test.d.ts +2 -0
  194. package/dist/proxmox/probes/lvm.test.d.ts.map +1 -0
  195. package/dist/proxmox/probes/lvm.test.js +128 -0
  196. package/dist/proxmox/probes/lvm.test.js.map +1 -0
  197. package/dist/proxmox/probes/lxc-config.d.ts +29 -0
  198. package/dist/proxmox/probes/lxc-config.d.ts.map +1 -0
  199. package/dist/proxmox/probes/lxc-config.js +67 -0
  200. package/dist/proxmox/probes/lxc-config.js.map +1 -0
  201. package/dist/proxmox/probes/lxc-config.test.d.ts +2 -0
  202. package/dist/proxmox/probes/lxc-config.test.d.ts.map +1 -0
  203. package/dist/proxmox/probes/lxc-config.test.js +77 -0
  204. package/dist/proxmox/probes/lxc-config.test.js.map +1 -0
  205. package/dist/proxmox/probes/lxc-list.d.ts +20 -0
  206. package/dist/proxmox/probes/lxc-list.d.ts.map +1 -0
  207. package/dist/proxmox/probes/lxc-list.js +49 -0
  208. package/dist/proxmox/probes/lxc-list.js.map +1 -0
  209. package/dist/proxmox/probes/lxc-list.test.d.ts +2 -0
  210. package/dist/proxmox/probes/lxc-list.test.d.ts.map +1 -0
  211. package/dist/proxmox/probes/lxc-list.test.js +51 -0
  212. package/dist/proxmox/probes/lxc-list.test.js.map +1 -0
  213. package/dist/proxmox/probes/vm-config.d.ts +21 -0
  214. package/dist/proxmox/probes/vm-config.d.ts.map +1 -0
  215. package/dist/proxmox/probes/vm-config.js +58 -0
  216. package/dist/proxmox/probes/vm-config.js.map +1 -0
  217. package/dist/proxmox/probes/vm-config.test.d.ts +2 -0
  218. package/dist/proxmox/probes/vm-config.test.d.ts.map +1 -0
  219. package/dist/proxmox/probes/vm-config.test.js +80 -0
  220. package/dist/proxmox/probes/vm-config.test.js.map +1 -0
  221. package/dist/proxmox/probes/vm-locks.d.ts +16 -0
  222. package/dist/proxmox/probes/vm-locks.d.ts.map +1 -0
  223. package/dist/proxmox/probes/vm-locks.js +35 -0
  224. package/dist/proxmox/probes/vm-locks.js.map +1 -0
  225. package/dist/proxmox/probes/vm-locks.test.d.ts +2 -0
  226. package/dist/proxmox/probes/vm-locks.test.d.ts.map +1 -0
  227. package/dist/proxmox/probes/vm-locks.test.js +54 -0
  228. package/dist/proxmox/probes/vm-locks.test.js.map +1 -0
  229. package/dist/redis/index.d.ts +3 -0
  230. package/dist/redis/index.d.ts.map +1 -0
  231. package/dist/redis/index.js +13 -0
  232. package/dist/redis/index.js.map +1 -0
  233. package/dist/redis/manifest.d.ts +3 -0
  234. package/dist/redis/manifest.d.ts.map +1 -0
  235. package/dist/redis/manifest.js +51 -0
  236. package/dist/redis/manifest.js.map +1 -0
  237. package/dist/redis/probes/info.d.ts +15 -0
  238. package/dist/redis/probes/info.d.ts.map +1 -0
  239. package/dist/redis/probes/info.js +32 -0
  240. package/dist/redis/probes/info.js.map +1 -0
  241. package/dist/redis/probes/info.test.d.ts +2 -0
  242. package/dist/redis/probes/info.test.d.ts.map +1 -0
  243. package/dist/redis/probes/info.test.js +64 -0
  244. package/dist/redis/probes/info.test.js.map +1 -0
  245. package/dist/redis/probes/keys-count.d.ts +13 -0
  246. package/dist/redis/probes/keys-count.d.ts.map +1 -0
  247. package/dist/redis/probes/keys-count.js +24 -0
  248. package/dist/redis/probes/keys-count.js.map +1 -0
  249. package/dist/redis/probes/keys-count.test.d.ts +2 -0
  250. package/dist/redis/probes/keys-count.test.d.ts.map +1 -0
  251. package/dist/redis/probes/keys-count.test.js +37 -0
  252. package/dist/redis/probes/keys-count.test.js.map +1 -0
  253. package/dist/redis/probes/memory-usage.d.ts +16 -0
  254. package/dist/redis/probes/memory-usage.d.ts.map +1 -0
  255. package/dist/redis/probes/memory-usage.js +31 -0
  256. package/dist/redis/probes/memory-usage.js.map +1 -0
  257. package/dist/redis/probes/memory-usage.test.d.ts +2 -0
  258. package/dist/redis/probes/memory-usage.test.d.ts.map +1 -0
  259. package/dist/redis/probes/memory-usage.test.js +48 -0
  260. package/dist/redis/probes/memory-usage.test.js.map +1 -0
  261. package/dist/runbooks/nutanix.d.ts +3 -0
  262. package/dist/runbooks/nutanix.d.ts.map +1 -0
  263. package/dist/runbooks/nutanix.js +619 -0
  264. package/dist/runbooks/nutanix.js.map +1 -0
  265. package/dist/runbooks/nutanix.test.d.ts +2 -0
  266. package/dist/runbooks/nutanix.test.d.ts.map +1 -0
  267. package/dist/runbooks/nutanix.test.js +971 -0
  268. package/dist/runbooks/nutanix.test.js.map +1 -0
  269. package/dist/runbooks/proxmox.d.ts +3 -0
  270. package/dist/runbooks/proxmox.d.ts.map +1 -0
  271. package/dist/runbooks/proxmox.js +451 -0
  272. package/dist/runbooks/proxmox.js.map +1 -0
  273. package/dist/runbooks/proxmox.test.d.ts +2 -0
  274. package/dist/runbooks/proxmox.test.d.ts.map +1 -0
  275. package/dist/runbooks/proxmox.test.js +700 -0
  276. package/dist/runbooks/proxmox.test.js.map +1 -0
  277. package/dist/signatures.d.ts +2 -0
  278. package/dist/signatures.d.ts.map +1 -0
  279. package/dist/signatures.js +2 -0
  280. package/dist/signatures.js.map +1 -0
  281. package/dist/system/index.d.ts.map +1 -1
  282. package/dist/system/index.js +2 -0
  283. package/dist/system/index.js.map +1 -1
  284. package/dist/system/manifest.d.ts.map +1 -1
  285. package/dist/system/manifest.js +19 -1
  286. package/dist/system/manifest.js.map +1 -1
  287. package/dist/system/probes/ping.d.ts +20 -0
  288. package/dist/system/probes/ping.d.ts.map +1 -0
  289. package/dist/system/probes/ping.js +54 -0
  290. package/dist/system/probes/ping.js.map +1 -0
  291. package/dist/system/probes/ping.test.d.ts +2 -0
  292. package/dist/system/probes/ping.test.d.ts.map +1 -0
  293. package/dist/system/probes/ping.test.js +127 -0
  294. package/dist/system/probes/ping.test.js.map +1 -0
  295. package/dist/types.d.ts +53 -0
  296. package/dist/types.d.ts.map +1 -1
  297. package/dist/validation.d.ts +6 -1
  298. package/dist/validation.d.ts.map +1 -1
  299. package/dist/validation.js +10 -1
  300. package/dist/validation.js.map +1 -1
  301. package/package.json +1 -1
  302. package/src/index.ts +60 -6
  303. package/src/integrations/citrix.test.ts +592 -0
  304. package/src/integrations/citrix.ts +553 -0
  305. package/src/integrations/graph.test.ts +478 -0
  306. package/src/integrations/graph.ts +409 -0
  307. package/src/integrations/httpbin.ts +68 -0
  308. package/src/integrations/nutanix.test.ts +1508 -0
  309. package/src/integrations/nutanix.ts +1456 -0
  310. package/src/integrations/proxmox.test.ts +1020 -0
  311. package/src/integrations/proxmox.ts +985 -0
  312. package/src/integrations/servicenow.test.ts +314 -0
  313. package/src/integrations/servicenow.ts +280 -0
  314. package/src/integrations/splunk.test.ts +440 -0
  315. package/src/integrations/splunk.ts +352 -0
  316. package/src/mysql/index.ts +14 -0
  317. package/src/mysql/manifest.ts +70 -0
  318. package/src/mysql/probes/databases-list.test.ts +62 -0
  319. package/src/mysql/probes/databases-list.ts +45 -0
  320. package/src/mysql/probes/processlist.test.ts +47 -0
  321. package/src/mysql/probes/processlist.ts +55 -0
  322. package/src/mysql/probes/status.test.ts +50 -0
  323. package/src/mysql/probes/status.ts +56 -0
  324. package/src/nginx/index.ts +14 -0
  325. package/src/nginx/manifest.ts +69 -0
  326. package/src/nginx/probes/access-log-tail.test.ts +51 -0
  327. package/src/nginx/probes/access-log-tail.ts +23 -0
  328. package/src/nginx/probes/config-test.test.ts +47 -0
  329. package/src/nginx/probes/config-test.ts +24 -0
  330. package/src/nginx/probes/error-log-tail.test.ts +44 -0
  331. package/src/nginx/probes/error-log-tail.ts +23 -0
  332. package/src/postgres/index.ts +14 -0
  333. package/src/postgres/manifest.ts +91 -0
  334. package/src/postgres/probes/connections-active.test.ts +42 -0
  335. package/src/postgres/probes/connections-active.ts +55 -0
  336. package/src/postgres/probes/databases-list.test.ts +57 -0
  337. package/src/postgres/probes/databases-list.ts +49 -0
  338. package/src/postgres/probes/query-slow.test.ts +37 -0
  339. package/src/postgres/probes/query-slow.ts +55 -0
  340. package/src/proxmox/index.ts +24 -0
  341. package/src/proxmox/manifest.ts +76 -0
  342. package/src/proxmox/probes/ceph-status.test.ts +126 -0
  343. package/src/proxmox/probes/ceph-status.ts +116 -0
  344. package/src/proxmox/probes/cluster-config.test.ts +118 -0
  345. package/src/proxmox/probes/cluster-config.ts +97 -0
  346. package/src/proxmox/probes/ha-status.test.ts +76 -0
  347. package/src/proxmox/probes/ha-status.ts +56 -0
  348. package/src/proxmox/probes/lvm.test.ts +140 -0
  349. package/src/proxmox/probes/lvm.ts +121 -0
  350. package/src/proxmox/probes/lxc-config.test.ts +89 -0
  351. package/src/proxmox/probes/lxc-config.ts +90 -0
  352. package/src/proxmox/probes/lxc-list.test.ts +60 -0
  353. package/src/proxmox/probes/lxc-list.ts +67 -0
  354. package/src/proxmox/probes/vm-config.test.ts +93 -0
  355. package/src/proxmox/probes/vm-config.ts +77 -0
  356. package/src/proxmox/probes/vm-locks.test.ts +63 -0
  357. package/src/proxmox/probes/vm-locks.ts +49 -0
  358. package/src/redis/index.ts +14 -0
  359. package/src/redis/manifest.ts +52 -0
  360. package/src/redis/probes/info.test.ts +73 -0
  361. package/src/redis/probes/info.ts +46 -0
  362. package/src/redis/probes/keys-count.test.ts +44 -0
  363. package/src/redis/probes/keys-count.ts +38 -0
  364. package/src/redis/probes/memory-usage.test.ts +54 -0
  365. package/src/redis/probes/memory-usage.ts +46 -0
  366. package/src/runbooks/nutanix.test.ts +1138 -0
  367. package/src/runbooks/nutanix.ts +941 -0
  368. package/src/runbooks/proxmox.test.ts +838 -0
  369. package/src/runbooks/proxmox.ts +626 -0
  370. package/src/signatures.ts +1 -0
  371. package/src/system/index.ts +2 -0
  372. package/src/system/manifest.ts +21 -1
  373. package/src/system/probes/ping.test.ts +163 -0
  374. package/src/system/probes/ping.ts +89 -0
  375. package/src/types.ts +62 -0
  376. package/src/validation.ts +21 -1
  377. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { ExecFn } from '../../types.js';
3
+ import type { VmConfigResult } from './vm-config.js';
4
+ import { parseVmConfig, vmConfig } from './vm-config.js';
5
+
6
+ const SAMPLE_OUTPUT = `boot: order=scsi0;ide2;net0
7
+ cores: 4
8
+ ide2: none,media=cdrom
9
+ memory: 8192
10
+ name: web-server-01
11
+ net0: virtio=AA:BB:CC:DD:EE:FF,bridge=vmbr0,firewall=1
12
+ numa: 0
13
+ ostype: l26
14
+ scsi0: ceph-pool:vm-100-disk-0,iothread=1,size=64G
15
+ scsihw: virtio-scsi-single
16
+ smbios1: uuid=abc-def-123
17
+ sockets: 1
18
+ virtio1: local-lvm:vm-100-disk-1,size=128G`;
19
+
20
+ describe('parseVmConfig', () => {
21
+ it('parses key: value pairs into config map', () => {
22
+ const result = parseVmConfig(SAMPLE_OUTPUT, 100);
23
+ expect(result.vmid).toBe(100);
24
+ expect(result.config.name).toBe('web-server-01');
25
+ expect(result.config.memory).toBe('8192');
26
+ expect(result.config.cores).toBe('4');
27
+ });
28
+
29
+ it('identifies disk entries with storage backend and size', () => {
30
+ const result = parseVmConfig(SAMPLE_OUTPUT, 100);
31
+ expect(result.disks).toHaveLength(2);
32
+
33
+ expect(result.disks[0]).toEqual({
34
+ key: 'scsi0',
35
+ storage: 'ceph-pool',
36
+ volume: 'vm-100-disk-0',
37
+ format: 'raw',
38
+ size: '64G',
39
+ });
40
+
41
+ expect(result.disks[1]).toEqual({
42
+ key: 'virtio1',
43
+ storage: 'local-lvm',
44
+ volume: 'vm-100-disk-1',
45
+ format: 'raw',
46
+ size: '128G',
47
+ });
48
+ });
49
+
50
+ it('warns about local storage disks', () => {
51
+ const result = parseVmConfig(SAMPLE_OUTPUT, 100);
52
+ expect(result.warnings).toHaveLength(1);
53
+ expect(result.warnings[0]).toContain('local-lvm');
54
+ expect(result.warnings[0]).toContain('virtio1');
55
+ });
56
+
57
+ it('skips non-disk config entries like net0', () => {
58
+ const result = parseVmConfig(SAMPLE_OUTPUT, 100);
59
+ const diskKeys = result.disks.map((d) => d.key);
60
+ expect(diskKeys).not.toContain('net0');
61
+ });
62
+
63
+ it('handles empty output', () => {
64
+ const result = parseVmConfig('', 100);
65
+ expect(result.config).toEqual({});
66
+ expect(result.disks).toHaveLength(0);
67
+ });
68
+
69
+ it('detects qcow2 format from filename', () => {
70
+ const output = 'scsi0: local:100/vm-100-disk-0.qcow2,size=32G';
71
+ const result = parseVmConfig(output, 100);
72
+ expect(result.disks[0]?.format).toBe('qcow2');
73
+ });
74
+ });
75
+
76
+ describe('vmConfig handler', () => {
77
+ it('calls qm config with vmid and returns parsed result', async () => {
78
+ const mockExec: ExecFn = async (cmd, args) => {
79
+ expect(cmd).toBe('qm');
80
+ expect(args).toEqual(['config', '100']);
81
+ return SAMPLE_OUTPUT;
82
+ };
83
+
84
+ const result = (await vmConfig({ vmid: 100 }, mockExec)) as VmConfigResult;
85
+ expect(result.vmid).toBe(100);
86
+ expect(result.disks).toHaveLength(2);
87
+ });
88
+
89
+ it('throws when vmid is missing', async () => {
90
+ const mockExec: ExecFn = async () => '';
91
+ await expect(vmConfig({}, mockExec)).rejects.toThrow('vmid parameter is required');
92
+ });
93
+ });
@@ -0,0 +1,77 @@
1
+ import type { ProbeHandler } from '../../types.js';
2
+
3
+ export interface DiskEntry {
4
+ key: string;
5
+ storage: string;
6
+ volume: string;
7
+ format: string;
8
+ size: string;
9
+ }
10
+
11
+ export interface VmConfigResult {
12
+ vmid: number;
13
+ config: Record<string, string>;
14
+ disks: DiskEntry[];
15
+ warnings: string[];
16
+ }
17
+
18
+ /**
19
+ * Runs `qm config {vmid}` and parses key: value output.
20
+ * Identifies disk backends (scsi, ide, virtio, sata, efidisk).
21
+ */
22
+ export const vmConfig: ProbeHandler = async (params, exec) => {
23
+ const vmid = params?.vmid as number;
24
+ if (vmid == null) throw new Error('vmid parameter is required');
25
+
26
+ const stdout = await exec('qm', ['config', String(vmid)]);
27
+ return parseVmConfig(stdout, vmid);
28
+ };
29
+
30
+ export function parseVmConfig(stdout: string, vmid: number): VmConfigResult {
31
+ const config: Record<string, string> = {};
32
+ const lines = stdout.trim().split('\n').filter(Boolean);
33
+
34
+ for (const line of lines) {
35
+ const colonIdx = line.indexOf(':');
36
+ if (colonIdx === -1) continue;
37
+ const key = line.slice(0, colonIdx).trim();
38
+ const value = line.slice(colonIdx + 1).trim();
39
+ config[key] = value;
40
+ }
41
+
42
+ const diskPrefixes = ['scsi', 'ide', 'virtio', 'sata', 'efidisk', 'tpmstate'];
43
+ const disks: DiskEntry[] = [];
44
+ const warnings: string[] = [];
45
+
46
+ for (const [key, value] of Object.entries(config)) {
47
+ const matchesDisk = diskPrefixes.some((p) => key.startsWith(p));
48
+ if (!matchesDisk) continue;
49
+
50
+ const colonIdx = value.indexOf(':');
51
+ if (colonIdx === -1) continue;
52
+
53
+ const storage = value.slice(0, colonIdx);
54
+ const rest = value.slice(colonIdx + 1);
55
+ const commaIdx = rest.indexOf(',');
56
+ const volume = commaIdx > -1 ? rest.slice(0, commaIdx) : rest;
57
+ const optsPart = commaIdx > -1 ? rest.slice(commaIdx + 1) : '';
58
+
59
+ let format = 'raw';
60
+ if (volume.endsWith('.qcow2')) format = 'qcow2';
61
+ else if (volume.endsWith('.vmdk')) format = 'vmdk';
62
+ else if (optsPart.includes('format=qcow2')) format = 'qcow2';
63
+ else if (optsPart.includes('format=vmdk')) format = 'vmdk';
64
+ else if (optsPart.includes('format=raw')) format = 'raw';
65
+
66
+ const sizeMatch = optsPart.match(/size=(\S+)/);
67
+ const size = sizeMatch?.[1] ?? '';
68
+
69
+ disks.push({ key, storage, volume, format, size });
70
+
71
+ if (storage === 'local' || storage === 'local-lvm') {
72
+ warnings.push(`Disk ${key} uses local storage (${storage})`);
73
+ }
74
+ }
75
+
76
+ return { vmid, config, disks, warnings };
77
+ }
@@ -0,0 +1,63 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { ExecFn } from '../../types.js';
3
+ import type { VmLocksResult } from './vm-locks.js';
4
+ import { parseVmLocks, vmLocks } from './vm-locks.js';
5
+
6
+ describe('parseVmLocks', () => {
7
+ it('parses lock files into VM lock entries', () => {
8
+ const stdout = `lock-100.conf
9
+ lock-101.conf
10
+ lock-200.conf`;
11
+ const result = parseVmLocks(stdout);
12
+ expect(result.locks).toHaveLength(3);
13
+ expect(result.locks[0]).toEqual({ vmid: 100, file: 'lock-100.conf' });
14
+ expect(result.locks[2]).toEqual({ vmid: 200, file: 'lock-200.conf' });
15
+ });
16
+
17
+ it('generates warning when locks exist', () => {
18
+ const stdout = 'lock-100.conf\nlock-101.conf';
19
+ const result = parseVmLocks(stdout);
20
+ expect(result.warnings).toHaveLength(1);
21
+ expect(result.warnings[0]).toContain('2 VM(s) locked');
22
+ expect(result.warnings[0]).toContain('100');
23
+ expect(result.warnings[0]).toContain('101');
24
+ });
25
+
26
+ it('returns empty for no lock files', () => {
27
+ const result = parseVmLocks('');
28
+ expect(result.locks).toHaveLength(0);
29
+ expect(result.warnings).toHaveLength(0);
30
+ });
31
+
32
+ it('ignores non-lock files', () => {
33
+ const stdout = `lock-100.conf
34
+ readme.txt
35
+ some-other-file`;
36
+ const result = parseVmLocks(stdout);
37
+ expect(result.locks).toHaveLength(1);
38
+ expect(result.locks[0]?.vmid).toBe(100);
39
+ });
40
+ });
41
+
42
+ describe('vmLocks handler', () => {
43
+ it('calls ls on the lock directory', async () => {
44
+ const mockExec: ExecFn = async (cmd, args) => {
45
+ expect(cmd).toBe('ls');
46
+ expect(args).toEqual(['/run/lock/qemu-server/']);
47
+ return 'lock-100.conf\nlock-101.conf';
48
+ };
49
+
50
+ const result = (await vmLocks(undefined, mockExec)) as VmLocksResult;
51
+ expect(result.locks).toHaveLength(2);
52
+ });
53
+
54
+ it('returns empty when directory does not exist', async () => {
55
+ const mockExec: ExecFn = async () => {
56
+ throw new Error('No such file or directory');
57
+ };
58
+
59
+ const result = (await vmLocks(undefined, mockExec)) as VmLocksResult;
60
+ expect(result.locks).toHaveLength(0);
61
+ expect(result.warnings).toHaveLength(0);
62
+ });
63
+ });
@@ -0,0 +1,49 @@
1
+ import type { ProbeHandler } from '../../types.js';
2
+
3
+ export interface VmLock {
4
+ vmid: number;
5
+ file: string;
6
+ }
7
+
8
+ export interface VmLocksResult {
9
+ locks: VmLock[];
10
+ warnings: string[];
11
+ }
12
+
13
+ /**
14
+ * Lists files in /run/lock/qemu-server/ to find locked VMs.
15
+ * Lock files are named like "lock-100.conf".
16
+ */
17
+ export const vmLocks: ProbeHandler = async (_params, exec) => {
18
+ let stdout: string;
19
+ try {
20
+ stdout = await exec('ls', ['/run/lock/qemu-server/']);
21
+ } catch {
22
+ // Directory may not exist or be empty
23
+ return { locks: [], warnings: [] };
24
+ }
25
+ return parseVmLocks(stdout);
26
+ };
27
+
28
+ export function parseVmLocks(stdout: string): VmLocksResult {
29
+ const files = stdout.trim().split('\n').filter(Boolean);
30
+ const locks: VmLock[] = [];
31
+ const warnings: string[] = [];
32
+
33
+ for (const file of files) {
34
+ // Lock files are typically "lock-{vmid}.conf"
35
+ const match = file.match(/lock-(\d+)\.conf/);
36
+ if (match) {
37
+ const vmid = Number.parseInt(match[1] ?? '', 10);
38
+ if (!Number.isNaN(vmid)) {
39
+ locks.push({ vmid, file });
40
+ }
41
+ }
42
+ }
43
+
44
+ if (locks.length > 0) {
45
+ warnings.push(`${locks.length} VM(s) locked: ${locks.map((l) => l.vmid).join(', ')}`);
46
+ }
47
+
48
+ return { locks, warnings };
49
+ }
@@ -0,0 +1,14 @@
1
+ import type { Pack } from '../types.js';
2
+ import { redisManifest } from './manifest.js';
3
+ import { info } from './probes/info.js';
4
+ import { keysCount } from './probes/keys-count.js';
5
+ import { memoryUsage } from './probes/memory-usage.js';
6
+
7
+ export const redisPack: Pack = {
8
+ manifest: redisManifest,
9
+ handlers: {
10
+ 'redis.info': info,
11
+ 'redis.keys.count': keysCount,
12
+ 'redis.memory.usage': memoryUsage,
13
+ },
14
+ };
@@ -0,0 +1,52 @@
1
+ import type { PackManifest } from '@sonde/shared';
2
+
3
+ export const redisManifest: PackManifest = {
4
+ name: 'redis',
5
+ version: '0.1.0',
6
+ description: 'Redis probes: server info, key count, memory usage',
7
+ requires: {
8
+ groups: [],
9
+ files: [],
10
+ commands: ['redis-cli'],
11
+ },
12
+ probes: [
13
+ {
14
+ name: 'info',
15
+ description: 'Get Redis server info (version, uptime, clients, memory)',
16
+ capability: 'observe',
17
+ params: {
18
+ host: { type: 'string', description: 'Redis host', required: false, default: '127.0.0.1' },
19
+ port: { type: 'number', description: 'Redis port', required: false, default: 6379 },
20
+ },
21
+ timeout: 10_000,
22
+ },
23
+ {
24
+ name: 'keys.count',
25
+ description: 'Count keys per database',
26
+ capability: 'observe',
27
+ params: {
28
+ host: { type: 'string', description: 'Redis host', required: false, default: '127.0.0.1' },
29
+ port: { type: 'number', description: 'Redis port', required: false, default: 6379 },
30
+ },
31
+ timeout: 10_000,
32
+ },
33
+ {
34
+ name: 'memory.usage',
35
+ description: 'Get Redis memory usage statistics',
36
+ capability: 'observe',
37
+ params: {
38
+ host: { type: 'string', description: 'Redis host', required: false, default: '127.0.0.1' },
39
+ port: { type: 'number', description: 'Redis port', required: false, default: 6379 },
40
+ },
41
+ timeout: 10_000,
42
+ },
43
+ ],
44
+ runbook: {
45
+ category: 'redis',
46
+ probes: ['info', 'memory.usage', 'keys.count'],
47
+ parallel: true,
48
+ },
49
+ detect: {
50
+ commands: ['redis-cli'],
51
+ },
52
+ };
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { ExecFn } from '../../types.js';
3
+ import type { RedisInfoResult } from './info.js';
4
+ import { info, parseRedisInfo } from './info.js';
5
+
6
+ const SAMPLE_OUTPUT = `# Server
7
+ redis_version:7.2.4
8
+ uptime_in_seconds:86400
9
+ # Clients
10
+ connected_clients:42
11
+ # Memory
12
+ used_memory_human:12.50M
13
+ used_memory_peak_human:15.00M
14
+ # Stats
15
+ total_connections_received:1500
16
+ total_commands_processed:987654
17
+ # Replication
18
+ role:master`;
19
+
20
+ describe('parseRedisInfo', () => {
21
+ it('parses key info fields', () => {
22
+ const result = parseRedisInfo(SAMPLE_OUTPUT);
23
+ expect(result.version).toBe('7.2.4');
24
+ expect(result.uptimeSeconds).toBe(86400);
25
+ expect(result.connectedClients).toBe(42);
26
+ expect(result.usedMemoryHuman).toBe('12.50M');
27
+ expect(result.usedMemoryPeakHuman).toBe('15.00M');
28
+ expect(result.totalConnectionsReceived).toBe(1500);
29
+ expect(result.totalCommandsProcessed).toBe(987654);
30
+ expect(result.role).toBe('master');
31
+ });
32
+
33
+ it('stores all key-value pairs in raw', () => {
34
+ const result = parseRedisInfo(SAMPLE_OUTPUT);
35
+ expect(result.raw['redis_version']).toBe('7.2.4');
36
+ expect(result.raw['role']).toBe('master');
37
+ });
38
+
39
+ it('handles empty output', () => {
40
+ const result = parseRedisInfo('');
41
+ expect(result.version).toBe('');
42
+ expect(result.uptimeSeconds).toBe(0);
43
+ expect(result.connectedClients).toBe(0);
44
+ expect(result.role).toBe('');
45
+ });
46
+ });
47
+
48
+ describe('info handler', () => {
49
+ it('calls redis-cli with default host/port', async () => {
50
+ const mockExec: ExecFn = async (cmd, args) => {
51
+ expect(cmd).toBe('redis-cli');
52
+ expect(args).toContain('-h');
53
+ expect(args).toContain('127.0.0.1');
54
+ expect(args).toContain('-p');
55
+ expect(args).toContain('6379');
56
+ expect(args).toContain('INFO');
57
+ return SAMPLE_OUTPUT;
58
+ };
59
+
60
+ const result = (await info(undefined, mockExec)) as RedisInfoResult;
61
+ expect(result.version).toBe('7.2.4');
62
+ });
63
+
64
+ it('passes custom host/port', async () => {
65
+ const mockExec: ExecFn = async (cmd, args) => {
66
+ expect(args).toContain('redis.example.com');
67
+ expect(args).toContain('6380');
68
+ return SAMPLE_OUTPUT;
69
+ };
70
+
71
+ await info({ host: 'redis.example.com', port: 6380 }, mockExec);
72
+ });
73
+ });
@@ -0,0 +1,46 @@
1
+ import type { ProbeHandler } from '../../types.js';
2
+
3
+ export interface RedisInfoResult {
4
+ version: string;
5
+ uptimeSeconds: number;
6
+ connectedClients: number;
7
+ usedMemoryHuman: string;
8
+ usedMemoryPeakHuman: string;
9
+ totalConnectionsReceived: number;
10
+ totalCommandsProcessed: number;
11
+ role: string;
12
+ raw: Record<string, string>;
13
+ }
14
+
15
+ export const info: ProbeHandler = async (params, exec) => {
16
+ const host = (params?.host as string) ?? '127.0.0.1';
17
+ const port = String((params?.port as number) ?? 6379);
18
+
19
+ const stdout = await exec('redis-cli', ['-h', host, '-p', port, 'INFO']);
20
+ return parseRedisInfo(stdout);
21
+ };
22
+
23
+ export function parseRedisInfo(stdout: string): RedisInfoResult {
24
+ const raw: Record<string, string> = {};
25
+ for (const line of stdout.split('\n')) {
26
+ const trimmed = line.trim();
27
+ if (!trimmed || trimmed.startsWith('#')) continue;
28
+ const colonIdx = trimmed.indexOf(':');
29
+ if (colonIdx === -1) continue;
30
+ const key = trimmed.slice(0, colonIdx);
31
+ const value = trimmed.slice(colonIdx + 1);
32
+ raw[key] = value;
33
+ }
34
+
35
+ return {
36
+ version: raw['redis_version'] ?? '',
37
+ uptimeSeconds: Number(raw['uptime_in_seconds']) || 0,
38
+ connectedClients: Number(raw['connected_clients']) || 0,
39
+ usedMemoryHuman: raw['used_memory_human'] ?? '',
40
+ usedMemoryPeakHuman: raw['used_memory_peak_human'] ?? '',
41
+ totalConnectionsReceived: Number(raw['total_connections_received']) || 0,
42
+ totalCommandsProcessed: Number(raw['total_commands_processed']) || 0,
43
+ role: raw['role'] ?? '',
44
+ raw,
45
+ };
46
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { ExecFn } from '../../types.js';
3
+ import type { KeysCountResult } from './keys-count.js';
4
+ import { keysCount, parseKeysCount } from './keys-count.js';
5
+
6
+ const SAMPLE_OUTPUT = `# Keyspace
7
+ db0:keys=1234,expires=100,avg_ttl=5000
8
+ db1:keys=567,expires=50,avg_ttl=3000`;
9
+
10
+ describe('parseKeysCount', () => {
11
+ it('parses keyspace databases', () => {
12
+ const result = parseKeysCount(SAMPLE_OUTPUT);
13
+ expect(result.databases).toHaveLength(2);
14
+ expect(result.databases[0]).toEqual({ db: 0, keys: 1234, expires: 100 });
15
+ expect(result.databases[1]).toEqual({ db: 1, keys: 567, expires: 50 });
16
+ expect(result.totalKeys).toBe(1801);
17
+ });
18
+
19
+ it('handles empty output', () => {
20
+ const result = parseKeysCount('');
21
+ expect(result.databases).toEqual([]);
22
+ expect(result.totalKeys).toBe(0);
23
+ });
24
+
25
+ it('handles only header line', () => {
26
+ const result = parseKeysCount('# Keyspace\n');
27
+ expect(result.databases).toEqual([]);
28
+ expect(result.totalKeys).toBe(0);
29
+ });
30
+ });
31
+
32
+ describe('keysCount handler', () => {
33
+ it('calls redis-cli INFO keyspace', async () => {
34
+ const mockExec: ExecFn = async (cmd, args) => {
35
+ expect(cmd).toBe('redis-cli');
36
+ expect(args).toContain('INFO');
37
+ expect(args).toContain('keyspace');
38
+ return SAMPLE_OUTPUT;
39
+ };
40
+
41
+ const result = (await keysCount(undefined, mockExec)) as KeysCountResult;
42
+ expect(result.totalKeys).toBe(1801);
43
+ });
44
+ });
@@ -0,0 +1,38 @@
1
+ import type { ProbeHandler } from '../../types.js';
2
+
3
+ export interface DbKeyCount {
4
+ db: number;
5
+ keys: number;
6
+ expires: number;
7
+ }
8
+
9
+ export interface KeysCountResult {
10
+ databases: DbKeyCount[];
11
+ totalKeys: number;
12
+ }
13
+
14
+ export const keysCount: ProbeHandler = async (params, exec) => {
15
+ const host = (params?.host as string) ?? '127.0.0.1';
16
+ const port = String((params?.port as number) ?? 6379);
17
+
18
+ const stdout = await exec('redis-cli', ['-h', host, '-p', port, 'INFO', 'keyspace']);
19
+ return parseKeysCount(stdout);
20
+ };
21
+
22
+ export function parseKeysCount(stdout: string): KeysCountResult {
23
+ const databases: DbKeyCount[] = [];
24
+ for (const line of stdout.split('\n')) {
25
+ const trimmed = line.trim();
26
+ // Lines like: db0:keys=123,expires=10,avg_ttl=0
27
+ const match = trimmed.match(/^db(\d+):keys=(\d+),expires=(\d+)/);
28
+ if (match) {
29
+ databases.push({
30
+ db: Number(match[1]),
31
+ keys: Number(match[2]),
32
+ expires: Number(match[3]),
33
+ });
34
+ }
35
+ }
36
+ const totalKeys = databases.reduce((sum, d) => sum + d.keys, 0);
37
+ return { databases, totalKeys };
38
+ }
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { ExecFn } from '../../types.js';
3
+ import type { MemoryUsageResult } from './memory-usage.js';
4
+ import { memoryUsage, parseMemoryUsage } from './memory-usage.js';
5
+
6
+ const SAMPLE_OUTPUT = `# Memory
7
+ used_memory:13107200
8
+ used_memory_human:12.50M
9
+ used_memory_peak:15728640
10
+ used_memory_peak_human:15.00M
11
+ used_memory_rss:16777216
12
+ used_memory_rss_human:16.00M
13
+ mem_fragmentation_ratio:1.28
14
+ maxmemory:0
15
+ maxmemory_human:0B
16
+ maxmemory_policy:noeviction`;
17
+
18
+ describe('parseMemoryUsage', () => {
19
+ it('parses memory fields', () => {
20
+ const result = parseMemoryUsage(SAMPLE_OUTPUT);
21
+ expect(result.usedMemory).toBe(13107200);
22
+ expect(result.usedMemoryHuman).toBe('12.50M');
23
+ expect(result.usedMemoryPeak).toBe(15728640);
24
+ expect(result.usedMemoryPeakHuman).toBe('15.00M');
25
+ expect(result.usedMemoryRss).toBe(16777216);
26
+ expect(result.usedMemoryRssHuman).toBe('16.00M');
27
+ expect(result.memFragmentationRatio).toBe(1.28);
28
+ expect(result.maxmemory).toBe(0);
29
+ expect(result.maxmemoryHuman).toBe('0B');
30
+ expect(result.maxmemoryPolicy).toBe('noeviction');
31
+ });
32
+
33
+ it('handles empty output', () => {
34
+ const result = parseMemoryUsage('');
35
+ expect(result.usedMemory).toBe(0);
36
+ expect(result.usedMemoryHuman).toBe('');
37
+ expect(result.memFragmentationRatio).toBe(0);
38
+ expect(result.maxmemoryPolicy).toBe('');
39
+ });
40
+ });
41
+
42
+ describe('memoryUsage handler', () => {
43
+ it('calls redis-cli INFO memory', async () => {
44
+ const mockExec: ExecFn = async (cmd, args) => {
45
+ expect(cmd).toBe('redis-cli');
46
+ expect(args).toContain('INFO');
47
+ expect(args).toContain('memory');
48
+ return SAMPLE_OUTPUT;
49
+ };
50
+
51
+ const result = (await memoryUsage(undefined, mockExec)) as MemoryUsageResult;
52
+ expect(result.usedMemory).toBe(13107200);
53
+ });
54
+ });
@@ -0,0 +1,46 @@
1
+ import type { ProbeHandler } from '../../types.js';
2
+
3
+ export interface MemoryUsageResult {
4
+ usedMemory: number;
5
+ usedMemoryHuman: string;
6
+ usedMemoryPeak: number;
7
+ usedMemoryPeakHuman: string;
8
+ usedMemoryRss: number;
9
+ usedMemoryRssHuman: string;
10
+ memFragmentationRatio: number;
11
+ maxmemory: number;
12
+ maxmemoryHuman: string;
13
+ maxmemoryPolicy: string;
14
+ }
15
+
16
+ export const memoryUsage: ProbeHandler = async (params, exec) => {
17
+ const host = (params?.host as string) ?? '127.0.0.1';
18
+ const port = String((params?.port as number) ?? 6379);
19
+
20
+ const stdout = await exec('redis-cli', ['-h', host, '-p', port, 'INFO', 'memory']);
21
+ return parseMemoryUsage(stdout);
22
+ };
23
+
24
+ export function parseMemoryUsage(stdout: string): MemoryUsageResult {
25
+ const kv: Record<string, string> = {};
26
+ for (const line of stdout.split('\n')) {
27
+ const trimmed = line.trim();
28
+ if (!trimmed || trimmed.startsWith('#')) continue;
29
+ const colonIdx = trimmed.indexOf(':');
30
+ if (colonIdx === -1) continue;
31
+ kv[trimmed.slice(0, colonIdx)] = trimmed.slice(colonIdx + 1);
32
+ }
33
+
34
+ return {
35
+ usedMemory: Number(kv['used_memory']) || 0,
36
+ usedMemoryHuman: kv['used_memory_human'] ?? '',
37
+ usedMemoryPeak: Number(kv['used_memory_peak']) || 0,
38
+ usedMemoryPeakHuman: kv['used_memory_peak_human'] ?? '',
39
+ usedMemoryRss: Number(kv['used_memory_rss']) || 0,
40
+ usedMemoryRssHuman: kv['used_memory_rss_human'] ?? '',
41
+ memFragmentationRatio: Number.parseFloat(kv['mem_fragmentation_ratio'] ?? '0'),
42
+ maxmemory: Number(kv['maxmemory']) || 0,
43
+ maxmemoryHuman: kv['maxmemory_human'] ?? '',
44
+ maxmemoryPolicy: kv['maxmemory_policy'] ?? '',
45
+ };
46
+ }