cdd-cli 3.0.1 → 3.1.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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +14 -0
- package/.github/workflows/ci.yml +29 -0
- package/CHANGELOG.md +34 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +22 -0
- package/README.md +83 -166
- package/SECURITY.md +5 -0
- package/babel.config.cjs +6 -0
- package/dist/App.js +38 -24
- package/dist/cdd.bundle.js +8 -0
- package/dist/components/ContainerCreationPrompt.js +48 -0
- package/dist/components/ContainerCreator.js +81 -0
- package/dist/components/ContainerList.js +24 -4
- package/dist/components/ContainerRow.js +66 -13
- package/dist/components/ContainerSection.js +14 -0
- package/dist/components/Footer.js +10 -0
- package/dist/components/PromptField.js +20 -0
- package/dist/components/UsageMenu.js +8 -0
- package/dist/helpers/actionHelpers.js +0 -1
- package/dist/helpers/dockerActions.js +13 -0
- package/dist/helpers/dockerService/dockerService.js +5 -0
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +163 -0
- package/dist/helpers/dockerService/serviceComponents/containerList.js +57 -0
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +24 -0
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +50 -0
- package/dist/helpers/dockerService/serviceComponents/imageUtils.js +52 -0
- package/dist/helpers/dockerService.js +111 -0
- package/dist/helpers/validationHelpers.js +26 -0
- package/dist/hooks/creation/useContainerActions.js +171 -0
- package/dist/hooks/creation/useContainerCreation.js +141 -0
- package/dist/hooks/creation/useLogsViewer.js +62 -0
- package/dist/hooks/useContainers.js +11 -1
- package/dist/hooks/useControls.js +298 -78
- package/dist/hooks/useLogsStream.js +1 -1
- package/dist/index.js +11 -0
- package/esbuild.config.cjs +11 -0
- package/jest.config.cjs +6 -0
- package/package.json +10 -6
- package/src/App.jsx +32 -26
- package/src/components/ContainerCreationPrompt.jsx +37 -0
- package/src/components/ContainerList.jsx +10 -6
- package/src/components/ContainerRow.jsx +36 -19
- package/src/components/ContainerSection.jsx +10 -0
- package/src/components/Footer.jsx +10 -0
- package/src/components/PromptField.jsx +18 -0
- package/src/components/UsageMenu.jsx +17 -0
- package/src/helpers/actionHelpers.js +0 -1
- package/src/helpers/dockerService/dockerService.js +3 -0
- package/src/helpers/dockerService/serviceComponents/containerActions.js +52 -0
- package/src/helpers/dockerService/serviceComponents/containerList.js +26 -0
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +19 -0
- package/src/helpers/dockerService/serviceComponents/containerStats.js +34 -0
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +21 -0
- package/src/helpers/validationHelpers.js +16 -0
- package/src/hooks/creation/useContainerActions.js +68 -0
- package/src/hooks/creation/useContainerCreation.js +104 -0
- package/src/hooks/creation/useLogsViewer.js +50 -0
- package/src/hooks/useContainers.js +1 -1
- package/src/hooks/useControls.js +176 -55
- package/src/hooks/useLogsStream.js +1 -1
- package/test/validationHelpers.test.js +28 -0
- package/PLAN_KEYBINDINGS_Y_REALTIME.md +0 -260
- package/docs/en/README.md +0 -167
- package/src/components/StatsViewer.jsx +0 -0
- package/src/helpers/dockerActions.js +0 -52
- package/src/helpers/dockerService.js +0 -100
|
@@ -2,78 +2,231 @@ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArra
|
|
|
2
2
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
3
|
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
4
4
|
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
5
|
+
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 }; })(); }
|
|
6
|
+
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); }
|
|
7
|
+
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); }
|
|
8
|
+
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
9
|
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
6
10
|
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
11
|
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
12
|
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
13
|
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
14
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
11
|
-
import React
|
|
15
|
+
import React from "react";
|
|
16
|
+
import { useContainerActions } from "./creation/useContainerActions.js";
|
|
17
|
+
import { useContainerCreation } from "./creation/useContainerCreation.js";
|
|
18
|
+
import { useLogsViewer } from "./creation/useLogsViewer.js";
|
|
12
19
|
import { useInput } from "ink";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
20
|
+
import { getLogsStream } from "../helpers/dockerService/serviceComponents/containerLogs.js";
|
|
21
|
+
import { createContainer as svcCreateContainer } from "../helpers/dockerService/serviceComponents/containerActions.js";
|
|
22
|
+
|
|
23
|
+
// Principal hook to manage user inputs and control the app state
|
|
16
24
|
export function useControls() {
|
|
17
25
|
var containers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
selected =
|
|
21
|
-
setSelected =
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
var _useState7 = useState(false),
|
|
31
|
-
_useState8 = _slicedToArray(_useState7, 2),
|
|
32
|
-
showLogs = _useState8[0],
|
|
33
|
-
setShowLogs = _useState8[1];
|
|
34
|
-
var _useState9 = useState([]),
|
|
35
|
-
_useState0 = _slicedToArray(_useState9, 2),
|
|
36
|
-
logs = _useState0[0],
|
|
37
|
-
setLogs = _useState0[1];
|
|
38
|
-
var logsStreamRef = useRef(null);
|
|
26
|
+
var _React$useState = React.useState(0),
|
|
27
|
+
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
28
|
+
selected = _React$useState2[0],
|
|
29
|
+
setSelected = _React$useState2[1];
|
|
30
|
+
var _React$useState3 = React.useState(false),
|
|
31
|
+
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
32
|
+
creatingContainer = _React$useState4[0],
|
|
33
|
+
setCreatingContainer = _React$useState4[1];
|
|
34
|
+
var _React$useState5 = React.useState(false),
|
|
35
|
+
_React$useState6 = _slicedToArray(_React$useState5, 2),
|
|
36
|
+
confirmErase = _React$useState6[0],
|
|
37
|
+
setConfirmErase = _React$useState6[1];
|
|
39
38
|
var total = containers.length;
|
|
40
39
|
|
|
41
|
-
//
|
|
42
|
-
var
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
// Modular hooks
|
|
41
|
+
var actions = useContainerActions({
|
|
42
|
+
containers: containers
|
|
43
|
+
});
|
|
44
|
+
var creation = useContainerCreation({
|
|
45
|
+
onCreate: function () {
|
|
46
|
+
var _onCreate = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(_ref) {
|
|
47
|
+
var imageName, containerName, portInput, envInput, env, ports, ExposedPorts, PortBindings, options, id, _t;
|
|
48
|
+
return _regenerator().w(function (_context) {
|
|
49
|
+
while (1) switch (_context.p = _context.n) {
|
|
50
|
+
case 0:
|
|
51
|
+
imageName = _ref.imageName, containerName = _ref.containerName, portInput = _ref.portInput, envInput = _ref.envInput;
|
|
52
|
+
// Build Docker options
|
|
53
|
+
env = (envInput || "").split(",").map(function (s) {
|
|
54
|
+
return s.trim();
|
|
55
|
+
}).filter(Boolean);
|
|
56
|
+
ports = (portInput || "").split(",").map(function (s) {
|
|
57
|
+
return s.trim();
|
|
58
|
+
}).filter(Boolean);
|
|
59
|
+
ExposedPorts = {};
|
|
60
|
+
PortBindings = {};
|
|
61
|
+
ports.forEach(function (pair) {
|
|
62
|
+
var _pair$split = pair.split(":"),
|
|
63
|
+
_pair$split2 = _slicedToArray(_pair$split, 2),
|
|
64
|
+
host = _pair$split2[0],
|
|
65
|
+
cont = _pair$split2[1];
|
|
66
|
+
if (!host || !cont) return;
|
|
67
|
+
var key = "".concat(cont, "/tcp");
|
|
68
|
+
ExposedPorts[key] = {};
|
|
69
|
+
PortBindings[key] = PortBindings[key] || [];
|
|
70
|
+
PortBindings[key].push({
|
|
71
|
+
HostPort: "".concat(host)
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
options = {
|
|
75
|
+
Tty: true
|
|
76
|
+
};
|
|
77
|
+
if (Object.keys(ExposedPorts).length) options.ExposedPorts = ExposedPorts;
|
|
78
|
+
if (Object.keys(PortBindings).length) options.HostConfig = {
|
|
79
|
+
PortBindings: PortBindings
|
|
80
|
+
};
|
|
81
|
+
if (env.length) options.Env = env;
|
|
82
|
+
if (containerName) options.name = containerName;
|
|
83
|
+
actions.setMessage("Creating container ".concat(imageName, "..."));
|
|
84
|
+
actions.setMessageColor("yellow");
|
|
85
|
+
_context.p = 1;
|
|
86
|
+
_context.n = 2;
|
|
87
|
+
return svcCreateContainer(imageName, options);
|
|
88
|
+
case 2:
|
|
89
|
+
id = _context.v;
|
|
90
|
+
actions.setMessage("Created container ".concat(id));
|
|
91
|
+
actions.setMessageColor("green");
|
|
92
|
+
_context.n = 4;
|
|
93
|
+
break;
|
|
94
|
+
case 3:
|
|
95
|
+
_context.p = 3;
|
|
96
|
+
_t = _context.v;
|
|
97
|
+
actions.setMessage("Error creating container: ".concat(_t.message));
|
|
98
|
+
actions.setMessageColor("red");
|
|
99
|
+
case 4:
|
|
100
|
+
_context.p = 4;
|
|
101
|
+
setCreatingContainer(false);
|
|
102
|
+
return _context.f(4);
|
|
103
|
+
case 5:
|
|
104
|
+
return _context.a(2);
|
|
105
|
+
}
|
|
106
|
+
}, _callee, null, [[1, 3, 4, 5]]);
|
|
107
|
+
}));
|
|
108
|
+
function onCreate(_x) {
|
|
109
|
+
return _onCreate.apply(this, arguments);
|
|
110
|
+
}
|
|
111
|
+
return onCreate;
|
|
112
|
+
}(),
|
|
113
|
+
onCancel: function onCancel() {
|
|
114
|
+
return setCreatingContainer(false);
|
|
115
|
+
},
|
|
116
|
+
dbImages: ["mysql", "mariadb", "postgres", "mongo", "mssql", "redis"]
|
|
117
|
+
});
|
|
118
|
+
var logsViewer = useLogsViewer();
|
|
119
|
+
|
|
120
|
+
// Handler to exit logs (delegated to logsViewer)
|
|
121
|
+
var exitLogs = logsViewer.closeLogs;
|
|
51
122
|
useInput(function (input, key) {
|
|
52
|
-
|
|
123
|
+
// Confirmación de borrado
|
|
124
|
+
if (confirmErase) {
|
|
125
|
+
if (input === "y" || input === "Y") {
|
|
126
|
+
actions.handleAction({
|
|
127
|
+
actionFn: function () {
|
|
128
|
+
var _actionFn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(id) {
|
|
129
|
+
return _regenerator().w(function (_context2) {
|
|
130
|
+
while (1) switch (_context2.n) {
|
|
131
|
+
case 0:
|
|
132
|
+
_context2.n = 1;
|
|
133
|
+
return actions.removeContainer(id);
|
|
134
|
+
case 1:
|
|
135
|
+
return _context2.a(2, _context2.v);
|
|
136
|
+
}
|
|
137
|
+
}, _callee2);
|
|
138
|
+
}));
|
|
139
|
+
function actionFn(_x2) {
|
|
140
|
+
return _actionFn.apply(this, arguments);
|
|
141
|
+
}
|
|
142
|
+
return actionFn;
|
|
143
|
+
}(),
|
|
144
|
+
actionLabel: "Erasing",
|
|
145
|
+
selected: selected
|
|
146
|
+
});
|
|
147
|
+
setConfirmErase(false);
|
|
148
|
+
actions.setMessageColor("yellow");
|
|
149
|
+
return;
|
|
150
|
+
} else if (input === "n" || input === "N" || key.escape) {
|
|
151
|
+
setConfirmErase(false);
|
|
152
|
+
actions.setMessage("");
|
|
153
|
+
actions.setMessageColor("");
|
|
154
|
+
return;
|
|
155
|
+
} else {
|
|
156
|
+
actions.setMessage("Are you sure you want to erase this container? (y/n)");
|
|
157
|
+
actions.setMessageColor("yellow");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Logs viewer
|
|
163
|
+
if (logsViewer.showLogs) {
|
|
53
164
|
if (input === "q" || key.escape) {
|
|
54
|
-
|
|
165
|
+
logsViewer.closeLogs();
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Container creation flow: delegate input to creation hook
|
|
171
|
+
if (creatingContainer) {
|
|
172
|
+
var step = creation.step;
|
|
173
|
+
var appendCharToField = function appendCharToField(setter, value, ch) {
|
|
174
|
+
setter((value || "") + ch);
|
|
175
|
+
};
|
|
176
|
+
// Escape -> cancel
|
|
177
|
+
if (key.escape) {
|
|
178
|
+
creation.cancelCreation();
|
|
179
|
+
setCreatingContainer(false);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Enter -> next step
|
|
183
|
+
if (input === "\r" || input === "\n") {
|
|
184
|
+
creation.nextStep();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Backspace/Delete support
|
|
188
|
+
if (key.backspace || key["delete"]) {
|
|
189
|
+
if (step === 0) creation.setImageName(function (v) {
|
|
190
|
+
return (v || "").slice(0, -1);
|
|
191
|
+
});
|
|
192
|
+
if (step === 1) creation.setContainerName(function (v) {
|
|
193
|
+
return (v || "").slice(0, -1);
|
|
194
|
+
});
|
|
195
|
+
if (step === 2) creation.setPortInput(function (v) {
|
|
196
|
+
return (v || "").slice(0, -1);
|
|
197
|
+
});
|
|
198
|
+
if (step === 3) creation.setEnvInput(function (v) {
|
|
199
|
+
return (v || "").slice(0, -1);
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
55
202
|
}
|
|
203
|
+
// Printable character input (append)
|
|
204
|
+
if (input && input.length === 1 && !key.ctrl && !key.meta) {
|
|
205
|
+
if (step === 0) appendCharToField(creation.setImageName, creation.imageName, input);
|
|
206
|
+
if (step === 1) appendCharToField(creation.setContainerName, creation.containerName, input);
|
|
207
|
+
if (step === 2) appendCharToField(creation.setPortInput, creation.portInput, input);
|
|
208
|
+
if (step === 3) appendCharToField(creation.setEnvInput, creation.envInput, input);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Otherwise ignore
|
|
56
212
|
return;
|
|
57
213
|
}
|
|
58
214
|
|
|
59
215
|
//==========================================================
|
|
60
216
|
// Menu Navigation
|
|
61
217
|
//==========================================================
|
|
62
|
-
if (key.upArrow && total > 0) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
setSelected(function (i) {
|
|
69
|
-
return i === total - 1 ? 0 : i + 1;
|
|
70
|
-
});
|
|
71
|
-
}
|
|
218
|
+
if (key.upArrow && total > 0) setSelected(function (i) {
|
|
219
|
+
return i === 0 ? total - 1 : i - 1;
|
|
220
|
+
});
|
|
221
|
+
if (key.downArrow && total > 0) setSelected(function (i) {
|
|
222
|
+
return i === total - 1 ? 0 : i + 1;
|
|
223
|
+
});
|
|
72
224
|
if (input === "q") {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
225
|
+
actions.setMessage("Exiting...");
|
|
226
|
+
actions.setMessageColor("yellow");
|
|
227
|
+
setTimeout(function () {
|
|
228
|
+
return process.exit(0);
|
|
229
|
+
}, 500);
|
|
77
230
|
return;
|
|
78
231
|
}
|
|
79
232
|
|
|
@@ -81,62 +234,129 @@ export function useControls() {
|
|
|
81
234
|
// Docker commands
|
|
82
235
|
//==========================================================
|
|
83
236
|
if (input === "i") {
|
|
84
|
-
handleAction({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
237
|
+
actions.handleAction({
|
|
238
|
+
actionFn: function () {
|
|
239
|
+
var _actionFn2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(id) {
|
|
240
|
+
return _regenerator().w(function (_context3) {
|
|
241
|
+
while (1) switch (_context3.n) {
|
|
242
|
+
case 0:
|
|
243
|
+
_context3.n = 1;
|
|
244
|
+
return actions.startContainer(id);
|
|
245
|
+
case 1:
|
|
246
|
+
return _context3.a(2, _context3.v);
|
|
247
|
+
}
|
|
248
|
+
}, _callee3);
|
|
249
|
+
}));
|
|
250
|
+
function actionFn(_x3) {
|
|
251
|
+
return _actionFn2.apply(this, arguments);
|
|
252
|
+
}
|
|
253
|
+
return actionFn;
|
|
254
|
+
}(),
|
|
88
255
|
actionLabel: "Starting",
|
|
89
|
-
|
|
90
|
-
setMessageColor: setMessageColor,
|
|
256
|
+
selected: selected,
|
|
91
257
|
stateCheck: function stateCheck(c) {
|
|
92
258
|
return (c.state === "running" || c.status === "running") && "Container is already running.";
|
|
93
259
|
}
|
|
94
260
|
});
|
|
95
261
|
}
|
|
96
262
|
if (input === "p") {
|
|
97
|
-
handleAction({
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
263
|
+
actions.handleAction({
|
|
264
|
+
actionFn: function () {
|
|
265
|
+
var _actionFn3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(id) {
|
|
266
|
+
return _regenerator().w(function (_context4) {
|
|
267
|
+
while (1) switch (_context4.n) {
|
|
268
|
+
case 0:
|
|
269
|
+
_context4.n = 1;
|
|
270
|
+
return actions.stopContainer(id);
|
|
271
|
+
case 1:
|
|
272
|
+
return _context4.a(2, _context4.v);
|
|
273
|
+
}
|
|
274
|
+
}, _callee4);
|
|
275
|
+
}));
|
|
276
|
+
function actionFn(_x4) {
|
|
277
|
+
return _actionFn3.apply(this, arguments);
|
|
278
|
+
}
|
|
279
|
+
return actionFn;
|
|
280
|
+
}(),
|
|
101
281
|
actionLabel: "Stopping",
|
|
102
|
-
|
|
103
|
-
setMessageColor: setMessageColor,
|
|
282
|
+
selected: selected,
|
|
104
283
|
stateCheck: function stateCheck(c) {
|
|
105
284
|
return (c.state === "exited" || c.status === "exited" || c.state === "stopped" || c.status === "stopped") && "Container is already stopped.";
|
|
106
285
|
}
|
|
107
286
|
});
|
|
108
287
|
}
|
|
109
288
|
if (input === "r") {
|
|
110
|
-
handleAction({
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
289
|
+
actions.handleAction({
|
|
290
|
+
actionFn: function () {
|
|
291
|
+
var _actionFn4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(id) {
|
|
292
|
+
return _regenerator().w(function (_context5) {
|
|
293
|
+
while (1) switch (_context5.n) {
|
|
294
|
+
case 0:
|
|
295
|
+
_context5.n = 1;
|
|
296
|
+
return actions.restartContainer(id);
|
|
297
|
+
case 1:
|
|
298
|
+
return _context5.a(2, _context5.v);
|
|
299
|
+
}
|
|
300
|
+
}, _callee5);
|
|
301
|
+
}));
|
|
302
|
+
function actionFn(_x5) {
|
|
303
|
+
return _actionFn4.apply(this, arguments);
|
|
304
|
+
}
|
|
305
|
+
return actionFn;
|
|
306
|
+
}(),
|
|
114
307
|
actionLabel: "Restarting",
|
|
115
|
-
|
|
116
|
-
setMessageColor: setMessageColor
|
|
308
|
+
selected: selected
|
|
117
309
|
});
|
|
118
310
|
}
|
|
311
|
+
if (input === "e" && containers[selected]) {
|
|
312
|
+
setConfirmErase(true);
|
|
313
|
+
actions.setMessage("Are you sure you want to erase this container? (y/n)");
|
|
314
|
+
actions.setMessageColor("yellow");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
119
317
|
if (input === "l" && containers[selected]) {
|
|
120
|
-
|
|
121
|
-
setLogs([]);
|
|
318
|
+
logsViewer.openLogs();
|
|
122
319
|
getLogsStream(containers[selected].id, function (data) {
|
|
123
|
-
return setLogs(function (prev) {
|
|
320
|
+
return logsViewer.setLogs(function (prev) {
|
|
124
321
|
return [].concat(_toConsumableArray(prev), _toConsumableArray(data.split("\n").filter(Boolean)));
|
|
125
322
|
});
|
|
126
323
|
}, function () {}, function (err) {
|
|
127
|
-
return setLogs(function (prev) {
|
|
324
|
+
return logsViewer.setLogs(function (prev) {
|
|
128
325
|
return [].concat(_toConsumableArray(prev), ["Error: ".concat(err.message)]);
|
|
129
326
|
});
|
|
130
327
|
});
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (input === "c") {
|
|
331
|
+
setCreatingContainer(true);
|
|
332
|
+
creation.setStep(0);
|
|
333
|
+
creation.setImageName("");
|
|
334
|
+
creation.setContainerName("");
|
|
335
|
+
creation.setPortInput("");
|
|
336
|
+
creation.setEnvInput("");
|
|
337
|
+
creation.setMessage("Insert the name of the image to create: ");
|
|
338
|
+
creation.setMessageColor("yellow");
|
|
131
339
|
}
|
|
132
340
|
});
|
|
133
341
|
return {
|
|
134
342
|
selected: selected,
|
|
135
343
|
setSelected: setSelected,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
344
|
+
// Map messaging to creation when creating, otherwise to actions
|
|
345
|
+
message: creatingContainer ? creation.message : actions.message,
|
|
346
|
+
messageColor: creatingContainer ? creation.messageColor : actions.messageColor,
|
|
347
|
+
showLogs: logsViewer.showLogs,
|
|
348
|
+
logs: logsViewer.logs,
|
|
349
|
+
exitLogs: exitLogs,
|
|
350
|
+
creatingContainer: creatingContainer,
|
|
351
|
+
// Expose creation fields in the shape App expects
|
|
352
|
+
creationStep: creation.step,
|
|
353
|
+
imageNameInput: creation.imageName,
|
|
354
|
+
containerNameInput: creation.containerName,
|
|
355
|
+
portInput: creation.portInput,
|
|
356
|
+
envInput: creation.envInput,
|
|
357
|
+
creation: creation,
|
|
358
|
+
actions: actions,
|
|
359
|
+
logsViewer: logsViewer,
|
|
360
|
+
confirmErase: confirmErase
|
|
141
361
|
};
|
|
142
362
|
}
|
|
@@ -5,7 +5,7 @@ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Sym
|
|
|
5
5
|
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
6
6
|
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; }
|
|
7
7
|
import React, { useRef, useCallback } from "react";
|
|
8
|
-
import { getLogsStream } from "../helpers/dockerService.js";
|
|
8
|
+
import { getLogsStream } from "../helpers/dockerService/serviceComponents/containerLogs.js";
|
|
9
9
|
export function useLogsStream() {
|
|
10
10
|
var logsStreamRef = useRef(null);
|
|
11
11
|
var openLogs = useCallback(function (containerId, setLogs) {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Entry point for the CDD CLI application.
|
|
5
|
+
* Punto de entrada para la aplicación CLI de CDD.
|
|
6
|
+
*
|
|
7
|
+
* @module index
|
|
8
|
+
* @example
|
|
9
|
+
* // EN: Run the CLI
|
|
10
|
+
* // ES: Ejecutar el CLI
|
|
11
|
+
* node index.js
|
|
12
|
+
*/
|
|
2
13
|
import React from "react";
|
|
3
14
|
import { render } from "ink";
|
|
4
15
|
import App from './App.js';
|
package/jest.config.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdd-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "CLI Docker Dashboard",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
8
|
+
"build": "babel src --out-dir dist --extensions .js,.jsx && node fix-imports.cjs",
|
|
9
|
+
"changes": "npm run build && node dist/index.js"
|
|
9
10
|
},
|
|
10
11
|
"keywords": [],
|
|
11
12
|
"author": "",
|
|
@@ -23,8 +24,11 @@
|
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@babel/cli": "^7.28.3",
|
|
25
26
|
"@babel/core": "^7.28.4",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"babel-plugin-module-resolver": "^5.0.2",
|
|
28
|
+
"esbuild": "^0.25.11",
|
|
29
|
+
"jest": "^29.6.1",
|
|
30
|
+
"@babel/preset-env": "^7.22.20",
|
|
31
|
+
"@babel/preset-react": "^7.22.5",
|
|
32
|
+
"babel-jest": "^29.6.1"
|
|
29
33
|
}
|
|
30
34
|
}
|
package/src/App.jsx
CHANGED
|
@@ -11,17 +11,33 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import React from "react";
|
|
13
13
|
import { Box, Text, Spacer } from "ink";
|
|
14
|
-
import { useContainers } from "./hooks/useContainers";
|
|
15
|
-
import { useControls } from "./hooks/useControls";
|
|
16
|
-
import
|
|
17
|
-
import MessageFeedback from "./components/MessageFeedback";
|
|
18
|
-
import Header from "./components/Header";
|
|
19
|
-
import LogViewer from "./components/LogViewer";
|
|
14
|
+
import { useContainers } from "./hooks/useContainers.js";
|
|
15
|
+
import { useControls } from "./hooks/useControls.js";
|
|
16
|
+
import ContainerSection from "./components/ContainerSection.jsx";
|
|
17
|
+
import MessageFeedback from "./components/MessageFeedback.jsx";
|
|
18
|
+
import Header from "./components/Header.jsx";
|
|
19
|
+
import LogViewer from "./components/LogViewer.jsx";
|
|
20
|
+
import ContainerCreationPrompt from "./components/ContainerCreationPrompt.jsx";
|
|
21
|
+
import UsageMenu from "./components/UsageMenu.jsx";
|
|
22
|
+
import Footer from "./components/Footer.jsx";
|
|
20
23
|
|
|
21
24
|
export default function App() {
|
|
22
25
|
const { containers } = useContainers();
|
|
23
|
-
const
|
|
24
|
-
|
|
26
|
+
const controls = useControls(containers);
|
|
27
|
+
|
|
28
|
+
if (controls.creatingContainer) {
|
|
29
|
+
return (
|
|
30
|
+
<ContainerCreationPrompt
|
|
31
|
+
step={controls.creationStep}
|
|
32
|
+
imageName={controls.imageNameInput}
|
|
33
|
+
containerName={controls.containerNameInput}
|
|
34
|
+
portInput={controls.portInput}
|
|
35
|
+
envInput={controls.envInput}
|
|
36
|
+
message={controls.message}
|
|
37
|
+
messageColor={controls.messageColor}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
25
41
|
|
|
26
42
|
return (
|
|
27
43
|
<>
|
|
@@ -33,27 +49,17 @@ export default function App() {
|
|
|
33
49
|
>
|
|
34
50
|
<Header count={containers.length} />
|
|
35
51
|
<Text> </Text>
|
|
36
|
-
{containers.
|
|
37
|
-
<Text>No containers found</Text>
|
|
38
|
-
) : (
|
|
39
|
-
<ContainerList containers={containers} selected={selected} />
|
|
40
|
-
)}
|
|
52
|
+
<ContainerSection containers={containers} selected={controls.selected} />
|
|
41
53
|
<Spacer />
|
|
42
|
-
<MessageFeedback message={message} color={messageColor} />
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
<Text>•P to stop selected container</Text>
|
|
46
|
-
<Text>•L to view logs of selected container</Text>
|
|
47
|
-
<Text>•Q to quit</Text>
|
|
48
|
-
<Box justifyContent="flex-end" width="100%">
|
|
49
|
-
<Text dimColor>Crafted by Carlos Cochero • 2025</Text>
|
|
50
|
-
</Box>
|
|
54
|
+
<MessageFeedback message={controls.message} color={controls.messageColor} />
|
|
55
|
+
<UsageMenu />
|
|
56
|
+
<Footer />
|
|
51
57
|
</Box>
|
|
52
|
-
{showLogs && (
|
|
58
|
+
{controls.showLogs && (
|
|
53
59
|
<LogViewer
|
|
54
|
-
logs={logs}
|
|
55
|
-
onExit={exitLogs}
|
|
56
|
-
container={containers[selected]}
|
|
60
|
+
logs={controls.logs}
|
|
61
|
+
onExit={controls.exitLogs}
|
|
62
|
+
container={containers[controls.selected]}
|
|
57
63
|
/>
|
|
58
64
|
)}
|
|
59
65
|
</>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { PromptField, PromptMessage } from "./PromptField.jsx";
|
|
4
|
+
|
|
5
|
+
export default function ContainerCreationPrompt(props) {
|
|
6
|
+
const { step, imageName, containerName, portInput, envInput, message, messageColor } = props;
|
|
7
|
+
const prompts = [
|
|
8
|
+
{
|
|
9
|
+
label: "Name of the image to create (e.g., nginx:latest):",
|
|
10
|
+
value: imageName,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Name of the container (optional):",
|
|
15
|
+
value: containerName,
|
|
16
|
+
required: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "Ports (optional, format 8080:80,443:443):",
|
|
20
|
+
value: portInput,
|
|
21
|
+
required: false,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: "Environment variables (optional, format VAR1=val1,VAR2=val2):",
|
|
25
|
+
value: envInput,
|
|
26
|
+
required: false,
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
const { label, value, required } = prompts[step] || {};
|
|
30
|
+
return (
|
|
31
|
+
<Box flexDirection="column" borderStyle="round" borderColor="yellow" padding={1}>
|
|
32
|
+
<PromptField label={label} value={value} required={required} />
|
|
33
|
+
<PromptMessage message={message} color={messageColor} />
|
|
34
|
+
<Text dimColor>Press Enter to continue, Escape to cancel</Text>
|
|
35
|
+
</Box>
|
|
36
|
+
);
|
|
37
|
+
}
|