cdd-cli 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +11 -0
- package/README.md +52 -0
- package/dist/App.js +66 -0
- package/dist/components/ContainerRow.js +74 -0
- package/dist/components/Header.js +15 -0
- package/dist/components/StatsBar.js +22 -0
- package/dist/dockerService.js +85 -0
- package/dist/index.js +5 -0
- package/fix-imports.cjs +24 -0
- package/package.json +30 -0
- package/src/App.jsx +41 -0
- package/src/components/ContainerRow.jsx +49 -0
- package/src/components/Header.jsx +17 -0
- package/src/components/StatsBar.jsx +27 -0
- package/src/dockerService.js +49 -0
- package/src/index.js +6 -0
package/.babelrc
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# CDD - CLI Docker Dashboard
|
|
2
|
+
|
|
3
|
+
CDD es una herramienta de línea de comandos (CLI) construida con React e Ink para monitorear contenedores Docker de forma interactiva y visual en la terminal.
|
|
4
|
+
|
|
5
|
+
## Características
|
|
6
|
+
- Visualización en tiempo real de los contenedores Docker activos y detenidos.
|
|
7
|
+
- Actualización automática del estado de los contenedores cada 2 segundos.
|
|
8
|
+
- Interfaz amigable y colorida usando Ink y React.
|
|
9
|
+
- Información mostrada: nombre, imagen, estado, puertos y estado textual de cada contenedor.
|
|
10
|
+
- Mensaje de ayuda para salir fácilmente (`Press Ctrl+C to exit`).
|
|
11
|
+
|
|
12
|
+
## Instalación
|
|
13
|
+
1. Clona el repositorio y entra en la carpeta del proyecto.
|
|
14
|
+
2. Instala las dependencias:
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
3. Transpila el código fuente y corrige los imports:
|
|
19
|
+
```bash
|
|
20
|
+
npm run build
|
|
21
|
+
```
|
|
22
|
+
4. Ejecuta el CLI:
|
|
23
|
+
```bash
|
|
24
|
+
node dist/index.js
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Estructura del proyecto
|
|
28
|
+
- `src/` - Código fuente en JSX/ESM.
|
|
29
|
+
- `App.jsx` - Componente principal.
|
|
30
|
+
- `dockerService.js` - Lógica para obtener los contenedores Docker.
|
|
31
|
+
- `components/` - Componentes visuales (`Header.jsx`, `ContainerRow.jsx`).
|
|
32
|
+
- `dist/` - Código transpilado listo para Node.js.
|
|
33
|
+
- `.babelrc` - Configuración de Babel para JSX y módulos ES.
|
|
34
|
+
- `fix-imports.cjs` - Script post-build para corregir imports en dist/.
|
|
35
|
+
- `package.json` - Scripts y dependencias.
|
|
36
|
+
- `ERRORS.md` - Documentación de errores y soluciones.
|
|
37
|
+
|
|
38
|
+
## Scripts útiles
|
|
39
|
+
- `npm run build` - Transpila el código fuente y corrige los imports automáticamente.
|
|
40
|
+
|
|
41
|
+
## Requisitos
|
|
42
|
+
- Node.js 18+
|
|
43
|
+
- Docker instalado y corriendo (el CLI se conecta al socket local de Docker)
|
|
44
|
+
|
|
45
|
+
## Notas de desarrollo
|
|
46
|
+
- Puedes escribir imports sin extensión en el código fuente (`import X from './X'`).
|
|
47
|
+
- El flujo de build se encarga de que los imports sean válidos en Node.js.
|
|
48
|
+
- Consulta `ERRORS.md` para ver el historial de problemas y soluciones.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
CDD es ideal para desarrolladores que desean monitorear sus contenedores Docker de forma rápida y visual desde la terminal, aprovechando la potencia de React en el CLI.
|
package/dist/App.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
6
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
7
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
8
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
9
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
10
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
11
|
+
import React from "react";
|
|
12
|
+
import { useState, useEffect } from "react";
|
|
13
|
+
import { Box, Text, Spacer } from "ink";
|
|
14
|
+
import { getContainers } from "./dockerService.js";
|
|
15
|
+
import ContainerRow from "./components/ContainerRow.js";
|
|
16
|
+
import Header from "./components/Header.js";
|
|
17
|
+
export default function App() {
|
|
18
|
+
var _useState = useState([]),
|
|
19
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
20
|
+
containers = _useState2[0],
|
|
21
|
+
setContainers = _useState2[1];
|
|
22
|
+
useEffect(function () {
|
|
23
|
+
var fetch = /*#__PURE__*/function () {
|
|
24
|
+
var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
25
|
+
var _t;
|
|
26
|
+
return _regenerator().w(function (_context) {
|
|
27
|
+
while (1) switch (_context.n) {
|
|
28
|
+
case 0:
|
|
29
|
+
_t = setContainers;
|
|
30
|
+
_context.n = 1;
|
|
31
|
+
return getContainers();
|
|
32
|
+
case 1:
|
|
33
|
+
return _context.a(2, _t(_context.v));
|
|
34
|
+
}
|
|
35
|
+
}, _callee);
|
|
36
|
+
}));
|
|
37
|
+
return function fetch() {
|
|
38
|
+
return _ref.apply(this, arguments);
|
|
39
|
+
};
|
|
40
|
+
}();
|
|
41
|
+
fetch();
|
|
42
|
+
var timer = setInterval(fetch, 3000);
|
|
43
|
+
return function () {
|
|
44
|
+
return clearInterval(timer);
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
48
|
+
flexDirection: "column",
|
|
49
|
+
borderStyle: "round",
|
|
50
|
+
borderColor: "cyan",
|
|
51
|
+
padding: 1
|
|
52
|
+
}, /*#__PURE__*/React.createElement(Header, {
|
|
53
|
+
count: containers.length
|
|
54
|
+
}), /*#__PURE__*/React.createElement(Text, null, " "), containers.length === 0 ? /*#__PURE__*/React.createElement(Text, null, "No containers found") : containers.map(function (container) {
|
|
55
|
+
return /*#__PURE__*/React.createElement(ContainerRow, {
|
|
56
|
+
key: container.id,
|
|
57
|
+
container: container
|
|
58
|
+
});
|
|
59
|
+
}), /*#__PURE__*/React.createElement(Spacer, null), /*#__PURE__*/React.createElement(Box, {
|
|
60
|
+
marginTop: 1
|
|
61
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
62
|
+
dimColor: true
|
|
63
|
+
}, "Press Ctrl+C to exit")), /*#__PURE__*/React.createElement(Text, {
|
|
64
|
+
dimColor: true
|
|
65
|
+
}, "Crafted by Carlos Cochero \u2022 2025"));
|
|
66
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
6
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
7
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
8
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
9
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
10
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
11
|
+
import React, { useState, useEffect } from "react";
|
|
12
|
+
import { Box, Text } from "ink";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
import { getStats } from "../dockerService.js";
|
|
15
|
+
import StatsBar from "./StatsBar.js";
|
|
16
|
+
var colorByState = function colorByState(state) {
|
|
17
|
+
if (state === "running") return chalk.greenBright("🟢 RUNNING");
|
|
18
|
+
if (state === "exited") return chalk.redBright("🔴 EXITED");
|
|
19
|
+
if (state === "paused") return chalk.yellowBright("🟠 PAUSED");
|
|
20
|
+
return chalk.gray(state.toUpperCase());
|
|
21
|
+
};
|
|
22
|
+
export default function ContainerRow(_ref) {
|
|
23
|
+
var container = _ref.container;
|
|
24
|
+
var id = container.id,
|
|
25
|
+
name = container.name,
|
|
26
|
+
image = container.image,
|
|
27
|
+
state = container.state;
|
|
28
|
+
var _useState = useState({
|
|
29
|
+
cpuPercent: 0,
|
|
30
|
+
memPercent: 0,
|
|
31
|
+
netIO: {
|
|
32
|
+
rx: 0,
|
|
33
|
+
tx: 0
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
37
|
+
stats = _useState2[0],
|
|
38
|
+
setStats = _useState2[1];
|
|
39
|
+
useEffect(function () {
|
|
40
|
+
if (state !== "running") return;
|
|
41
|
+
var fetchStats = /*#__PURE__*/function () {
|
|
42
|
+
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
43
|
+
var s;
|
|
44
|
+
return _regenerator().w(function (_context) {
|
|
45
|
+
while (1) switch (_context.n) {
|
|
46
|
+
case 0:
|
|
47
|
+
_context.n = 1;
|
|
48
|
+
return getStats(id);
|
|
49
|
+
case 1:
|
|
50
|
+
s = _context.v;
|
|
51
|
+
setStats(s);
|
|
52
|
+
case 2:
|
|
53
|
+
return _context.a(2);
|
|
54
|
+
}
|
|
55
|
+
}, _callee);
|
|
56
|
+
}));
|
|
57
|
+
return function fetchStats() {
|
|
58
|
+
return _ref2.apply(this, arguments);
|
|
59
|
+
};
|
|
60
|
+
}();
|
|
61
|
+
fetchStats();
|
|
62
|
+
var timer = setInterval(fetchStats, 1500);
|
|
63
|
+
return function () {
|
|
64
|
+
return clearInterval(timer);
|
|
65
|
+
};
|
|
66
|
+
}, [id, state]);
|
|
67
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
68
|
+
flexDirection: "column",
|
|
69
|
+
marginBottom: 1
|
|
70
|
+
}, /*#__PURE__*/React.createElement(Text, null, chalk.cyan(name.padEnd(20)), " ", chalk.gray(image.padEnd(20)), " ", state === "running" ? chalk.greenBright("🟢 RUNNING") : chalk.redBright("\uD83D\uDD34 ".concat(state.toUpperCase()))), state === "running" && /*#__PURE__*/React.createElement(StatsBar, {
|
|
71
|
+
cpu: parseFloat(stats.cpuPercent),
|
|
72
|
+
mem: parseFloat(stats.memPercent)
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
export default function Header(_ref) {
|
|
5
|
+
var count = _ref.count;
|
|
6
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
7
|
+
justifyContent: "space-between"
|
|
8
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
9
|
+
color: "cyanBright"
|
|
10
|
+
}, "\uD83D\uDC33 ", chalk.bold("CDD"), /*#__PURE__*/React.createElement(Text, {
|
|
11
|
+
color: "gray"
|
|
12
|
+
}, " \u2014 CLI Docker Dashboard")), /*#__PURE__*/React.createElement(Text, {
|
|
13
|
+
color: "gray"
|
|
14
|
+
}, count, " container", count === 1 ? "" : "s", " found"));
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
export default function StatsBar(_ref) {
|
|
5
|
+
var cpu = _ref.cpu,
|
|
6
|
+
mem = _ref.mem;
|
|
7
|
+
var cpuBar = makeBar(cpu);
|
|
8
|
+
var memBar = makeBar(mem);
|
|
9
|
+
return /*#__PURE__*/React.createElement(Text, null, "CPU: ", cpuBar, " ", colorize(cpu), "% MEM: ", memBar, " ", colorize(mem), "%");
|
|
10
|
+
}
|
|
11
|
+
function makeBar(value) {
|
|
12
|
+
var filled = Math.round(value / 10);
|
|
13
|
+
var empty = 10 - filled;
|
|
14
|
+
var bar = "▓".repeat(filled) + "░".repeat(empty);
|
|
15
|
+
return colorize(value, bar);
|
|
16
|
+
}
|
|
17
|
+
function colorize(value) {
|
|
18
|
+
var text = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : value.toString();
|
|
19
|
+
if (value < 50) return chalk.greenBright(text);
|
|
20
|
+
if (value < 80) return chalk.yellowBright(text);
|
|
21
|
+
return chalk.redBright(text);
|
|
22
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
import Docker from 'dockerode';
|
|
6
|
+
var docker = new Docker({
|
|
7
|
+
socketPath: '/var/run/docker.sock'
|
|
8
|
+
});
|
|
9
|
+
export function getContainers() {
|
|
10
|
+
return _getContainers.apply(this, arguments);
|
|
11
|
+
}
|
|
12
|
+
function _getContainers() {
|
|
13
|
+
_getContainers = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
14
|
+
var containers;
|
|
15
|
+
return _regenerator().w(function (_context) {
|
|
16
|
+
while (1) switch (_context.n) {
|
|
17
|
+
case 0:
|
|
18
|
+
_context.n = 1;
|
|
19
|
+
return docker.listContainers({
|
|
20
|
+
all: true
|
|
21
|
+
});
|
|
22
|
+
case 1:
|
|
23
|
+
containers = _context.v;
|
|
24
|
+
return _context.a(2, containers.map(function (container) {
|
|
25
|
+
return {
|
|
26
|
+
id: container.Id,
|
|
27
|
+
name: container.Names[0].replace('/', ''),
|
|
28
|
+
image: container.Image,
|
|
29
|
+
state: container.State,
|
|
30
|
+
status: container.Status,
|
|
31
|
+
ports: container.Ports.map(function (port) {
|
|
32
|
+
return "".concat(port.PublicPort || '', ":").concat(port.PrivatePort);
|
|
33
|
+
}).join(', ') || '-'
|
|
34
|
+
};
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
}, _callee);
|
|
38
|
+
}));
|
|
39
|
+
return _getContainers.apply(this, arguments);
|
|
40
|
+
}
|
|
41
|
+
export function getStats(_x) {
|
|
42
|
+
return _getStats.apply(this, arguments);
|
|
43
|
+
}
|
|
44
|
+
function _getStats() {
|
|
45
|
+
_getStats = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(containerId) {
|
|
46
|
+
var container, stream, cpuDelta, systemDelta, cpuPercent, memUsage, memLimit, memPercent, rx, tx;
|
|
47
|
+
return _regenerator().w(function (_context2) {
|
|
48
|
+
while (1) switch (_context2.n) {
|
|
49
|
+
case 0:
|
|
50
|
+
container = docker.getContainer(containerId);
|
|
51
|
+
_context2.n = 1;
|
|
52
|
+
return container.stats({
|
|
53
|
+
stream: false
|
|
54
|
+
});
|
|
55
|
+
case 1:
|
|
56
|
+
stream = _context2.v;
|
|
57
|
+
cpuDelta = stream.cpu_stats.cpu_usage.total_usage - stream.precpu_stats.cpu_usage.total_usage;
|
|
58
|
+
systemDelta = stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
|
|
59
|
+
cpuPercent = systemDelta > 0 ? cpuDelta / systemDelta * 100 : 0;
|
|
60
|
+
memUsage = stream.memory_stats.usage || 0;
|
|
61
|
+
memLimit = stream.memory_stats.limit || 1;
|
|
62
|
+
memPercent = memUsage / memLimit * 100;
|
|
63
|
+
rx = stream.networks ? Object.values(stream.networks).map(function (n) {
|
|
64
|
+
return n.rx_bytes;
|
|
65
|
+
}).reduce(function (a, b) {
|
|
66
|
+
return a + b;
|
|
67
|
+
}, 0) : 0;
|
|
68
|
+
tx = stream.networks ? Object.values(stream.networks).map(function (n) {
|
|
69
|
+
return n.tx_bytes;
|
|
70
|
+
}).reduce(function (a, b) {
|
|
71
|
+
return a + b;
|
|
72
|
+
}, 0) : 0;
|
|
73
|
+
return _context2.a(2, {
|
|
74
|
+
cpuPercent: cpuPercent.toFixed(1),
|
|
75
|
+
memPercent: memPercent.toFixed(1),
|
|
76
|
+
netIO: {
|
|
77
|
+
rx: rx,
|
|
78
|
+
tx: tx
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}, _callee2);
|
|
83
|
+
}));
|
|
84
|
+
return _getStats.apply(this, arguments);
|
|
85
|
+
}
|
package/dist/index.js
ADDED
package/fix-imports.cjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Script para agregar automáticamente la extensión .js a los imports relativos en dist/
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function addJsExtensionToImports(filePath) {
|
|
6
|
+
let code = fs.readFileSync(filePath, 'utf8');
|
|
7
|
+
// Reemplaza imports relativos sin extensión por .js
|
|
8
|
+
code = code.replace(/(import\s+[^'";]+['"])(\.\/[^'".]+)(['"])/g, '$1$2.js$3');
|
|
9
|
+
code = code.replace(/(from\s+['"])(\.\/[^'".]+)(['"])/g, '$1$2.js$3');
|
|
10
|
+
fs.writeFileSync(filePath, code, 'utf8');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function processDir(dir) {
|
|
14
|
+
fs.readdirSync(dir).forEach(file => {
|
|
15
|
+
const fullPath = path.join(dir, file);
|
|
16
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
17
|
+
processDir(fullPath);
|
|
18
|
+
} else if (file.endsWith('.js')) {
|
|
19
|
+
addJsExtensionToImports(fullPath);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
processDir(path.join(__dirname, 'dist'));
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cdd-cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "CLI Docker Dashboard",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"build": "babel src --out-dir dist --extensions .js,.jsx && node fix-imports.cjs"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"chalk": "^5.6.2",
|
|
16
|
+
"dockerode": "^4.0.9",
|
|
17
|
+
"ink": "^6.3.1",
|
|
18
|
+
"react": "^19.2.0"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"cdd": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@babel/cli": "^7.28.3",
|
|
25
|
+
"@babel/core": "^7.28.4",
|
|
26
|
+
"@babel/preset-env": "^7.28.3",
|
|
27
|
+
"@babel/preset-react": "^7.27.1",
|
|
28
|
+
"babel-plugin-module-resolver": "^5.0.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/App.jsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Box, Text, Spacer } from "ink";
|
|
4
|
+
import { getContainers } from "./dockerService";
|
|
5
|
+
import ContainerRow from "./components/ContainerRow";
|
|
6
|
+
import Header from "./components/Header";
|
|
7
|
+
|
|
8
|
+
export default function App() {
|
|
9
|
+
const [containers, setContainers] = useState([]);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const fetch = async () => setContainers(await getContainers());
|
|
13
|
+
fetch();
|
|
14
|
+
const timer = setInterval(fetch, 3000);
|
|
15
|
+
return () => clearInterval(timer);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Box
|
|
20
|
+
flexDirection="column"
|
|
21
|
+
borderStyle="round"
|
|
22
|
+
borderColor="cyan"
|
|
23
|
+
padding={1}
|
|
24
|
+
>
|
|
25
|
+
<Header count={containers.length} />
|
|
26
|
+
<Text> </Text>
|
|
27
|
+
{containers.length === 0 ? (
|
|
28
|
+
<Text>No containers found</Text>
|
|
29
|
+
) : (
|
|
30
|
+
containers.map((container) => (
|
|
31
|
+
<ContainerRow key={container.id} container={container} />
|
|
32
|
+
))
|
|
33
|
+
)}
|
|
34
|
+
<Spacer />
|
|
35
|
+
<Box marginTop={1}>
|
|
36
|
+
<Text dimColor>Press Ctrl+C to exit</Text>
|
|
37
|
+
</Box>
|
|
38
|
+
<Text dimColor>Crafted by Carlos Cochero • 2025</Text>
|
|
39
|
+
</Box>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { getStats } from "../dockerService.js";
|
|
5
|
+
import StatsBar from "./StatsBar";
|
|
6
|
+
|
|
7
|
+
const colorByState = (state) => {
|
|
8
|
+
if (state === "running") return chalk.greenBright("🟢 RUNNING");
|
|
9
|
+
if (state === "exited") return chalk.redBright("🔴 EXITED");
|
|
10
|
+
if (state === "paused") return chalk.yellowBright("🟠 PAUSED");
|
|
11
|
+
return chalk.gray(state.toUpperCase());
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default function ContainerRow({ container }) {
|
|
15
|
+
const { id, name, image, state } = container;
|
|
16
|
+
const [stats, setStats] = useState({
|
|
17
|
+
cpuPercent: 0,
|
|
18
|
+
memPercent: 0,
|
|
19
|
+
netIO: { rx: 0, tx: 0 },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (state !== "running") return;
|
|
24
|
+
const fetchStats = async () => {
|
|
25
|
+
const s = await getStats(id);
|
|
26
|
+
setStats(s);
|
|
27
|
+
};
|
|
28
|
+
fetchStats();
|
|
29
|
+
const timer = setInterval(fetchStats, 1500);
|
|
30
|
+
return () => clearInterval(timer);
|
|
31
|
+
}, [id, state]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
35
|
+
<Text>
|
|
36
|
+
{chalk.cyan(name.padEnd(20))} {chalk.gray(image.padEnd(20))}{" "}
|
|
37
|
+
{state === "running"
|
|
38
|
+
? chalk.greenBright("🟢 RUNNING")
|
|
39
|
+
: chalk.redBright(`🔴 ${state.toUpperCase()}`)}
|
|
40
|
+
</Text>
|
|
41
|
+
{state === "running" && (
|
|
42
|
+
<StatsBar
|
|
43
|
+
cpu={parseFloat(stats.cpuPercent)}
|
|
44
|
+
mem={parseFloat(stats.memPercent)}
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
</Box>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
export default function Header({ count }) {
|
|
6
|
+
return (
|
|
7
|
+
<Box justifyContent="space-between">
|
|
8
|
+
<Text color="cyanBright">
|
|
9
|
+
🐳 {chalk.bold("CDD")}
|
|
10
|
+
<Text color="gray"> — CLI Docker Dashboard</Text>
|
|
11
|
+
</Text>
|
|
12
|
+
<Text color="gray">
|
|
13
|
+
{count} container{count === 1 ? "" : "s"} found
|
|
14
|
+
</Text>
|
|
15
|
+
</Box>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
export default function StatsBar({ cpu, mem }) {
|
|
6
|
+
const cpuBar = makeBar(cpu);
|
|
7
|
+
const memBar = makeBar(mem);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Text>
|
|
11
|
+
CPU: {cpuBar} {colorize(cpu)}% MEM: {memBar} {colorize(mem)}%
|
|
12
|
+
</Text>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function makeBar(value) {
|
|
17
|
+
const filled = Math.round(value / 10);
|
|
18
|
+
const empty = 10 - filled;
|
|
19
|
+
const bar = "▓".repeat(filled) + "░".repeat(empty);
|
|
20
|
+
return colorize(value, bar);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function colorize(value, text = value.toString()) {
|
|
24
|
+
if (value < 50) return chalk.greenBright(text);
|
|
25
|
+
if (value < 80) return chalk.yellowBright(text);
|
|
26
|
+
return chalk.redBright(text);
|
|
27
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Docker from 'dockerode';
|
|
2
|
+
const docker = new Docker({ socketPath: '/var/run/docker.sock'});
|
|
3
|
+
|
|
4
|
+
export async function getContainers() {
|
|
5
|
+
const containers = await
|
|
6
|
+
docker.listContainers({ all: true });
|
|
7
|
+
return containers.map(container => ({
|
|
8
|
+
id: container.Id,
|
|
9
|
+
name: container.Names[0].replace('/', ''),
|
|
10
|
+
image: container.Image,
|
|
11
|
+
state: container.State,
|
|
12
|
+
status: container.Status,
|
|
13
|
+
ports: container.Ports.map(port => `${port.PublicPort
|
|
14
|
+
|| '' }:${port.PrivatePort}`).join(', ') || '-'
|
|
15
|
+
}))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getStats(containerId) {
|
|
19
|
+
const container = docker.getContainer(containerId);
|
|
20
|
+
const stream = await container.stats({ stream: false });
|
|
21
|
+
|
|
22
|
+
const cpuDelta =
|
|
23
|
+
stream.cpu_stats.cpu_usage.total_usage -
|
|
24
|
+
stream.precpu_stats.cpu_usage.total_usage;
|
|
25
|
+
const systemDelta =
|
|
26
|
+
stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
|
|
27
|
+
const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * 100 : 0;
|
|
28
|
+
|
|
29
|
+
const memUsage = stream.memory_stats.usage || 0;
|
|
30
|
+
const memLimit = stream.memory_stats.limit || 1;
|
|
31
|
+
const memPercent = (memUsage / memLimit) * 100;
|
|
32
|
+
|
|
33
|
+
const rx = stream.networks
|
|
34
|
+
? Object.values(stream.networks)
|
|
35
|
+
.map(n => n.rx_bytes)
|
|
36
|
+
.reduce((a, b) => a + b, 0)
|
|
37
|
+
: 0;
|
|
38
|
+
const tx = stream.networks
|
|
39
|
+
? Object.values(stream.networks)
|
|
40
|
+
.map(n => n.tx_bytes)
|
|
41
|
+
.reduce((a, b) => a + b, 0)
|
|
42
|
+
: 0;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
cpuPercent: cpuPercent.toFixed(1),
|
|
46
|
+
memPercent: memPercent.toFixed(1),
|
|
47
|
+
netIO: { rx, tx },
|
|
48
|
+
};
|
|
49
|
+
}
|