cdd-cli 3.1.3 → 3.1.5
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/.eslintrc.json +30 -0
- package/.github/workflows/docs.yml +35 -0
- package/CHANGELOG.md +44 -4
- package/FIXES_APPLIED.md +145 -147
- package/README.md +26 -0
- package/dist/App.js +20 -3
- package/dist/components/ContainerCreationPrompt.js +27 -1
- package/dist/components/ContainerList.js +11 -15
- package/dist/components/ContainerRow.js +73 -14
- package/dist/components/ContainerSection.js +16 -2
- package/dist/components/Footer.js +7 -1
- package/dist/components/Header.js +17 -1
- package/dist/components/LogViewer.js +21 -3
- package/dist/components/MessageFeedback.js +15 -1
- package/dist/components/PromptField.js +31 -1
- package/dist/components/StatsBar.js +46 -8
- package/dist/components/UsageMenu.js +30 -1
- package/dist/helpers/actionHelpers.js +17 -6
- package/dist/helpers/constants.js +30 -0
- package/dist/helpers/dockerService/dockerService.js +19 -0
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +194 -35
- package/dist/helpers/dockerService/serviceComponents/containerList.js +45 -8
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +16 -6
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +28 -9
- package/dist/helpers/exitWithMessage.js +2 -1
- package/dist/helpers/logger.js +112 -0
- package/dist/helpers/safeCall.js +21 -0
- package/dist/helpers/validationHelpers.js +20 -1
- package/dist/hooks/creation/useContainerActions.js +2 -1
- package/dist/hooks/creation/useContainerCreation.js +5 -4
- package/dist/hooks/creation/useLogsViewer.js +3 -2
- package/dist/hooks/useContainers.js +6 -3
- package/dist/hooks/useControls.js +253 -122
- package/dist/hooks/useLogsStream.js +3 -2
- package/dist/index.js +1 -0
- package/docs/components_ContainerCreationPrompt.jsx.html +114 -0
- package/docs/components_ContainerRow.jsx.html +151 -0
- package/docs/components_ContainerSection.jsx.html +75 -0
- package/docs/components_Footer.jsx.html +66 -0
- package/docs/components_Header.jsx.html +84 -0
- package/docs/components_LogViewer.jsx.html +91 -0
- package/docs/components_MessageFeedback.jsx.html +76 -0
- package/docs/components_PromptField.jsx.html +99 -0
- package/docs/components_StatsBar.jsx.html +105 -0
- package/docs/components_UsageMenu.jsx.html +73 -0
- package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/global.html +6145 -0
- package/docs/helpers_actionHelpers.js.html +94 -0
- package/docs/helpers_constants.js.html +81 -0
- package/docs/helpers_dockerService_dockerService.js.html +79 -0
- package/docs/helpers_dockerService_serviceComponents_containerActions.js.html +153 -0
- package/docs/helpers_dockerService_serviceComponents_containerList.js.html +81 -0
- package/docs/helpers_dockerService_serviceComponents_containerLogs.js.html +85 -0
- package/docs/helpers_dockerService_serviceComponents_containerStats.js.html +107 -0
- package/docs/helpers_dockerService_serviceComponents_imageUtils.js.html +82 -0
- package/docs/helpers_exitWithMessage.js.html +77 -0
- package/docs/helpers_safeCall.js.html +68 -0
- package/docs/helpers_validationHelpers.js.html +96 -0
- package/docs/hooks_creation_useContainerActions.js.html +120 -0
- package/docs/hooks_creation_useContainerCreation.js.html +152 -0
- package/docs/hooks_creation_useLogsViewer.js.html +102 -0
- package/docs/hooks_useControls.js.html +298 -0
- package/docs/hooks_useLogsStream.js.html +82 -0
- package/docs/index.html +65 -0
- package/docs/index.js.html +65 -0
- package/docs/module-index.html +171 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/styles/jsdoc-default.css +358 -0
- package/docs/styles/prettify-jsdoc.css +111 -0
- package/docs/styles/prettify-tomorrow.css +132 -0
- package/fix-imports.cjs +33 -6
- package/jest.config.cjs +2 -1
- package/package.json +19 -9
- package/src/App.jsx +16 -2
- package/src/components/ContainerCreationPrompt.jsx +26 -0
- package/src/components/ContainerList.jsx +12 -13
- package/src/components/ContainerRow.jsx +55 -20
- package/src/components/ContainerSection.jsx +15 -1
- package/src/components/Footer.jsx +6 -1
- package/src/components/Header.jsx +16 -0
- package/src/components/LogViewer.jsx +19 -2
- package/src/components/MessageFeedback.jsx +14 -0
- package/src/components/PromptField.jsx +30 -0
- package/src/components/StatsBar.jsx +47 -8
- package/src/components/UsageMenu.jsx +38 -8
- package/src/helpers/actionHelpers.js +16 -7
- package/src/helpers/constants.js +30 -0
- package/src/helpers/dockerService/dockerService.js +19 -0
- package/src/helpers/dockerService/serviceComponents/containerActions.js +123 -12
- package/src/helpers/dockerService/serviceComponents/containerList.js +61 -23
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +20 -6
- package/src/helpers/dockerService/serviceComponents/containerStats.js +24 -5
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
- package/src/helpers/exitWithMessage.js +2 -1
- package/src/helpers/logger.js +113 -0
- package/src/helpers/safeCall.js +18 -0
- package/src/helpers/validationHelpers.js +21 -3
- package/src/hooks/creation/useContainerActions.js +4 -3
- package/src/hooks/creation/useContainerCreation.js +5 -4
- package/src/hooks/creation/useLogsViewer.js +2 -1
- package/src/hooks/useContainers.js +6 -3
- package/src/hooks/useControls.js +244 -91
- package/src/hooks/useLogsStream.js +2 -1
- package/src/index.js +1 -0
- package/test/actionHelpers.test.js +106 -0
- package/test/containerActions.test.js +138 -0
- package/test/containerList.test.js +79 -0
- package/test/jest.setup.js +7 -0
- package/test/safeCall.test.js +25 -0
- package/test/useContainerCreation.dom.test.js +97 -0
- package/test/validationHelpers.test.js +2 -2
- package/AUDIT_REPORT.md +0 -1004
- package/dist/cdd.bundle.js +0 -8
- package/dist/components/ContainerCreator.js +0 -81
- package/dist/components/StatsViewer.js +0 -0
- package/dist/helpers/dockerActions.js +0 -64
- package/dist/helpers/dockerService.js +0 -284
- package/dist/hooks/useStatsPolling.js +0 -54
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* List component for Docker containers.
|
|
3
3
|
*
|
|
4
4
|
* @component
|
|
5
|
-
* @param {Object} props - Component props
|
|
6
|
-
* @param {Array} props.containers - Containers to display
|
|
7
|
-
* @returns {JSX.Element} Rendered list
|
|
5
|
+
* @param {Object} props - Component props
|
|
6
|
+
* @param {Array} props.containers - Containers to display
|
|
7
|
+
* @returns {JSX.Element} Rendered list
|
|
8
8
|
* @example
|
|
9
9
|
* // Render with containers
|
|
10
10
|
* <ContainerList containers={containers} />
|
|
11
11
|
*/
|
|
12
12
|
import React from "react";
|
|
13
|
-
import
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import { Box } from "ink";
|
|
14
15
|
import ContainerRow from "./ContainerRow.js";
|
|
15
16
|
export default function ContainerList(_ref) {
|
|
16
17
|
var containers = _ref.containers,
|
|
@@ -19,19 +20,14 @@ export default function ContainerList(_ref) {
|
|
|
19
20
|
return /*#__PURE__*/React.createElement(Box, {
|
|
20
21
|
key: container.id,
|
|
21
22
|
flexDirection: "row",
|
|
22
|
-
alignItems: "flex-start"
|
|
23
|
-
}, /*#__PURE__*/React.createElement(Box, {
|
|
24
|
-
width: 2,
|
|
25
|
-
minWidth: 2,
|
|
26
|
-
justifyContent: "flex-end"
|
|
27
|
-
}, /*#__PURE__*/React.createElement(Text, {
|
|
28
|
-
color: i === selected ? "green" : undefined
|
|
29
|
-
}, i === selected ? "➤" : " ")), /*#__PURE__*/React.createElement(Box, {
|
|
30
|
-
flexGrow: 1,
|
|
31
23
|
paddingLeft: 1
|
|
32
24
|
}, /*#__PURE__*/React.createElement(ContainerRow, {
|
|
33
25
|
container: container,
|
|
34
26
|
isSelected: i === selected
|
|
35
|
-
}))
|
|
27
|
+
}));
|
|
36
28
|
}));
|
|
37
|
-
}
|
|
29
|
+
}
|
|
30
|
+
ContainerList.propTypes = {
|
|
31
|
+
containers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
32
|
+
selected: PropTypes.number
|
|
33
|
+
};
|
|
@@ -11,7 +11,9 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
|
11
11
|
import React, { useState, useEffect } from "react";
|
|
12
12
|
import { Box, Text } from "ink";
|
|
13
13
|
import { getStats } from "../helpers/dockerService/serviceComponents/containerStats.js";
|
|
14
|
+
import { REFRESH_INTERVALS } from "../helpers/constants.js";
|
|
14
15
|
import StatsBar from "./StatsBar.js";
|
|
16
|
+
import PropTypes from 'prop-types';
|
|
15
17
|
var stateText = function stateText(state) {
|
|
16
18
|
if (state === "running") return {
|
|
17
19
|
text: "🟢 RUNNING",
|
|
@@ -30,8 +32,18 @@ var stateText = function stateText(state) {
|
|
|
30
32
|
color: "gray"
|
|
31
33
|
};
|
|
32
34
|
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Row component that renders information and live stats for a container.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} props
|
|
40
|
+
* @param {Object} props.container - Container object with id, name, image, state and ports
|
|
41
|
+
* @returns {JSX.Element}
|
|
42
|
+
*/
|
|
33
43
|
export default function ContainerRow(_ref) {
|
|
34
|
-
var container = _ref.container
|
|
44
|
+
var container = _ref.container,
|
|
45
|
+
_ref$isSelected = _ref.isSelected,
|
|
46
|
+
isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected;
|
|
35
47
|
var id = container.id,
|
|
36
48
|
name = container.name,
|
|
37
49
|
image = container.image,
|
|
@@ -48,15 +60,22 @@ export default function ContainerRow(_ref) {
|
|
|
48
60
|
stats = _useState2[0],
|
|
49
61
|
setStats = _useState2[1];
|
|
50
62
|
|
|
51
|
-
// Format ports for display
|
|
63
|
+
// Format ports for display (no leading space to avoid layout shifts)
|
|
52
64
|
var formatPorts = function formatPorts(ports) {
|
|
53
65
|
if (!ports || ports.length === 0) return "";
|
|
54
66
|
if (Array.isArray(ports)) {
|
|
55
|
-
return ports.map(function (p,
|
|
56
|
-
return "
|
|
67
|
+
return ports.map(function (p, _i) {
|
|
68
|
+
return "\uD83D\uDD17 ".concat(p);
|
|
57
69
|
}).join(" ");
|
|
58
70
|
}
|
|
59
|
-
return "
|
|
71
|
+
return "\uD83D\uDD17 ".concat(ports);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Helper to truncate long strings to a max length without adding trailing spaces
|
|
75
|
+
var truncate = function truncate(s) {
|
|
76
|
+
var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
|
|
77
|
+
if (!s) return "";
|
|
78
|
+
return s.length > max ? s.slice(0, max - 1) + "…" : s;
|
|
60
79
|
};
|
|
61
80
|
var _useState3 = useState(""),
|
|
62
81
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
@@ -106,7 +125,7 @@ export default function ContainerRow(_ref) {
|
|
|
106
125
|
};
|
|
107
126
|
}();
|
|
108
127
|
fetchStats();
|
|
109
|
-
var timer = setInterval(fetchStats,
|
|
128
|
+
var timer = setInterval(fetchStats, REFRESH_INTERVALS.CONTAINER_STATS);
|
|
110
129
|
return function () {
|
|
111
130
|
isMounted = false;
|
|
112
131
|
clearInterval(timer);
|
|
@@ -116,18 +135,58 @@ export default function ContainerRow(_ref) {
|
|
|
116
135
|
return /*#__PURE__*/React.createElement(Box, {
|
|
117
136
|
flexDirection: "column",
|
|
118
137
|
marginBottom: 1
|
|
119
|
-
}, /*#__PURE__*/React.createElement(Box,
|
|
138
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
139
|
+
flexDirection: "row",
|
|
140
|
+
alignItems: "center"
|
|
141
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
142
|
+
width: 2,
|
|
143
|
+
minWidth: 2,
|
|
144
|
+
justifyContent: "flex-end",
|
|
145
|
+
paddingRight: 1
|
|
146
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
147
|
+
color: isSelected ? "green" : undefined
|
|
148
|
+
}, isSelected ? "➤" : " ")), /*#__PURE__*/React.createElement(Box, {
|
|
149
|
+
width: 18,
|
|
150
|
+
flexShrink: 1,
|
|
151
|
+
paddingRight: 1
|
|
152
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
120
153
|
color: "cyan"
|
|
121
|
-
}, name
|
|
154
|
+
}, truncate(name, 18))), /*#__PURE__*/React.createElement(Box, {
|
|
155
|
+
width: 18,
|
|
156
|
+
flexShrink: 1,
|
|
157
|
+
paddingRight: 1
|
|
158
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
122
159
|
color: "gray"
|
|
123
|
-
}, image
|
|
160
|
+
}, truncate(image, 18))), /*#__PURE__*/React.createElement(Box, {
|
|
161
|
+
width: 14,
|
|
162
|
+
minWidth: 12,
|
|
163
|
+
paddingRight: 1
|
|
164
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
124
165
|
color: stateInfo.color
|
|
125
|
-
}, stateInfo.text), /*#__PURE__*/React.createElement(
|
|
166
|
+
}, stateInfo.text)), /*#__PURE__*/React.createElement(Box, {
|
|
167
|
+
flexGrow: 1,
|
|
168
|
+
flexShrink: 1,
|
|
169
|
+
paddingLeft: 0,
|
|
170
|
+
paddingRight: 1
|
|
171
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
126
172
|
color: "yellow"
|
|
127
|
-
}, formatPorts(container.ports))
|
|
173
|
+
}, formatPorts(container.ports))), /*#__PURE__*/React.createElement(Box, {
|
|
174
|
+
flexShrink: 0,
|
|
175
|
+
paddingLeft: 1
|
|
176
|
+
}, state === "running" ? /*#__PURE__*/React.createElement(StatsBar, {
|
|
128
177
|
cpu: parseFloat(stats.cpuPercent),
|
|
129
178
|
mem: parseFloat(stats.memPercent)
|
|
130
|
-
})
|
|
179
|
+
}) : null)), statsError && /*#__PURE__*/React.createElement(Text, {
|
|
131
180
|
color: "red"
|
|
132
|
-
}, statsError))
|
|
133
|
-
}
|
|
181
|
+
}, statsError));
|
|
182
|
+
}
|
|
183
|
+
ContainerRow.propTypes = {
|
|
184
|
+
container: PropTypes.shape({
|
|
185
|
+
id: PropTypes.string.isRequired,
|
|
186
|
+
name: PropTypes.string.isRequired,
|
|
187
|
+
image: PropTypes.string,
|
|
188
|
+
state: PropTypes.string,
|
|
189
|
+
ports: PropTypes.oneOfType([PropTypes.array, PropTypes.string])
|
|
190
|
+
}).isRequired,
|
|
191
|
+
isSelected: PropTypes.bool
|
|
192
|
+
};
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import ContainerList from "./ContainerList.js";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Section that displays the list of containers or a message when none exist.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {Array<Object>} props.containers - Array of container objects to display
|
|
11
|
+
* @param {number} [props.selected] - Index of the currently selected container
|
|
12
|
+
* @returns {JSX.Element}
|
|
13
|
+
*/
|
|
4
14
|
export default function ContainerSection(_ref) {
|
|
5
15
|
var containers = _ref.containers,
|
|
6
16
|
selected = _ref.selected;
|
|
7
|
-
if (containers.length === 0) {
|
|
17
|
+
if (!containers || containers.length === 0) {
|
|
8
18
|
return /*#__PURE__*/React.createElement(Text, null, "No containers found");
|
|
9
19
|
}
|
|
10
20
|
return /*#__PURE__*/React.createElement(ContainerList, {
|
|
11
21
|
containers: containers,
|
|
12
22
|
selected: selected
|
|
13
23
|
});
|
|
14
|
-
}
|
|
24
|
+
}
|
|
25
|
+
ContainerSection.propTypes = {
|
|
26
|
+
containers: PropTypes.array.isRequired,
|
|
27
|
+
selected: PropTypes.number
|
|
28
|
+
};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simple footer displayed at the bottom of the interface.
|
|
6
|
+
*
|
|
7
|
+
* @returns {JSX.Element}
|
|
8
|
+
*/
|
|
3
9
|
export default function Footer() {
|
|
4
10
|
return /*#__PURE__*/React.createElement(Box, {
|
|
5
11
|
justifyContent: "flex-end",
|
|
6
12
|
width: "100%"
|
|
7
13
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
8
14
|
dimColor: true
|
|
9
|
-
}, "Crafted by Carlos Cochero
|
|
15
|
+
}, "Crafted by Carlos Cochero 2025"));
|
|
10
16
|
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Header component displayed at the top of the CLI UI.
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {number} props.count - Number of containers currently detected
|
|
12
|
+
* @returns {JSX.Element}
|
|
13
|
+
* @example
|
|
14
|
+
* <Header count={3} />
|
|
15
|
+
*/
|
|
16
|
+
|
|
4
17
|
export default function Header(_ref) {
|
|
5
18
|
var count = _ref.count;
|
|
6
19
|
return /*#__PURE__*/React.createElement(Box, {
|
|
@@ -12,4 +25,7 @@ export default function Header(_ref) {
|
|
|
12
25
|
}, " \u2014 CLI Docker Dashboard")), /*#__PURE__*/React.createElement(Text, {
|
|
13
26
|
color: "gray"
|
|
14
27
|
}, count, " container", count === 1 ? "" : "s", " found"));
|
|
15
|
-
}
|
|
28
|
+
}
|
|
29
|
+
Header.propTypes = {
|
|
30
|
+
count: PropTypes.number.isRequired
|
|
31
|
+
};
|
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text, useInput } from "ink";
|
|
3
|
+
import { safeCall } from "../helpers/safeCall.js";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Log viewer overlay component. Shows the most recent lines and closes on ESC.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {Array<string>} props.logs - Array of log lines
|
|
11
|
+
* @param {Function} [props.onExit] - Optional callback when the viewer closes
|
|
12
|
+
* @param {Object} [props.container] - Optional container metadata (used for title)
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/
|
|
3
15
|
export default function LogViewer(_ref) {
|
|
16
|
+
var _container$name;
|
|
4
17
|
var logs = _ref.logs,
|
|
5
18
|
onExit = _ref.onExit,
|
|
6
19
|
container = _ref.container;
|
|
7
20
|
useInput(function (input, key) {
|
|
8
21
|
if (key.escape) {
|
|
9
|
-
onExit
|
|
22
|
+
safeCall(onExit);
|
|
10
23
|
}
|
|
11
24
|
});
|
|
12
25
|
var visibleLogs = logs.slice(-15);
|
|
13
26
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
|
|
14
27
|
color: "green"
|
|
15
|
-
}, (container === null || container === void 0 ? void 0 : container.name)
|
|
28
|
+
}, (_container$name = container === null || container === void 0 ? void 0 : container.name) !== null && _container$name !== void 0 ? _container$name : "Container", " logs, press ESC to exit"), visibleLogs.length === 0 ? /*#__PURE__*/React.createElement(Text, {
|
|
16
29
|
dimColor: true
|
|
17
30
|
}, "No logs...") : visibleLogs.map(function (line, idx) {
|
|
18
31
|
return /*#__PURE__*/React.createElement(Text, {
|
|
19
32
|
key: idx
|
|
20
33
|
}, line);
|
|
21
34
|
}));
|
|
22
|
-
}
|
|
35
|
+
}
|
|
36
|
+
LogViewer.propTypes = {
|
|
37
|
+
logs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
38
|
+
onExit: PropTypes.func,
|
|
39
|
+
container: PropTypes.object
|
|
40
|
+
};
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Small component that shows a feedback message in the UI.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} props
|
|
9
|
+
* @param {string} props.message - Message to display. If falsy, component returns null.
|
|
10
|
+
* @param {string} [props.color] - Optional color name for the message text.
|
|
11
|
+
* @returns {JSX.Element|null}
|
|
12
|
+
*/
|
|
3
13
|
export default function MessageFeedback(_ref) {
|
|
4
14
|
var message = _ref.message,
|
|
5
15
|
color = _ref.color;
|
|
@@ -9,4 +19,8 @@ export default function MessageFeedback(_ref) {
|
|
|
9
19
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
10
20
|
color: color
|
|
11
21
|
}, message));
|
|
12
|
-
}
|
|
22
|
+
}
|
|
23
|
+
MessageFeedback.propTypes = {
|
|
24
|
+
message: PropTypes.string,
|
|
25
|
+
color: PropTypes.string
|
|
26
|
+
};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* PromptField shows a label and the current typed value for an input field.
|
|
7
|
+
* The cursor is represented with a trailing underscore.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {string} props.label - Label shown above the input value
|
|
11
|
+
* @param {string} props.value - Current value of the input
|
|
12
|
+
* @param {boolean} [props.required=false] - Whether the field is required
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/
|
|
3
15
|
export function PromptField(_ref) {
|
|
4
16
|
var label = _ref.label,
|
|
5
17
|
value = _ref.value,
|
|
@@ -10,6 +22,15 @@ export function PromptField(_ref) {
|
|
|
10
22
|
color: isEmpty ? "red" : "cyan"
|
|
11
23
|
}, value, "_"));
|
|
12
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Small helper to show a message below prompts (errors, hints, etc.).
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} props
|
|
30
|
+
* @param {string} props.message - Message text to display
|
|
31
|
+
* @param {string} [props.color] - Optional color for the message
|
|
32
|
+
* @returns {JSX.Element|null}
|
|
33
|
+
*/
|
|
13
34
|
export function PromptMessage(_ref2) {
|
|
14
35
|
var message = _ref2.message,
|
|
15
36
|
color = _ref2.color;
|
|
@@ -17,4 +38,13 @@ export function PromptMessage(_ref2) {
|
|
|
17
38
|
return /*#__PURE__*/React.createElement(Text, {
|
|
18
39
|
color: color || "yellow"
|
|
19
40
|
}, message);
|
|
20
|
-
}
|
|
41
|
+
}
|
|
42
|
+
PromptField.propTypes = {
|
|
43
|
+
label: PropTypes.string,
|
|
44
|
+
value: PropTypes.string,
|
|
45
|
+
required: PropTypes.bool
|
|
46
|
+
};
|
|
47
|
+
PromptMessage.propTypes = {
|
|
48
|
+
message: PropTypes.string,
|
|
49
|
+
color: PropTypes.string
|
|
50
|
+
};
|
|
@@ -1,19 +1,57 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
var BAR_WIDTH = 8;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Small visual bar that shows CPU and memory usage percentages.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {number} props.cpu - CPU percentage (0-100)
|
|
12
|
+
* @param {number} props.mem - Memory percentage (0-100)
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/
|
|
4
15
|
export default function StatsBar(_ref) {
|
|
5
16
|
var cpu = _ref.cpu,
|
|
6
17
|
mem = _ref.mem;
|
|
7
|
-
var cpuBar = makeBar(cpu);
|
|
8
|
-
var memBar = makeBar(mem);
|
|
9
|
-
return /*#__PURE__*/React.createElement(Text, null, "CPU:
|
|
18
|
+
var cpuBar = makeBar(cpu, BAR_WIDTH);
|
|
19
|
+
var memBar = makeBar(mem, BAR_WIDTH);
|
|
20
|
+
return /*#__PURE__*/React.createElement(Text, null, "CPU:", cpuBar, colorize(cpu, " ".concat(formatPercent(cpu), "%")), " MEM:", memBar, colorize(mem, " ".concat(formatPercent(mem), "%")));
|
|
10
21
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
StatsBar.propTypes = {
|
|
23
|
+
cpu: PropTypes.number.isRequired,
|
|
24
|
+
mem: PropTypes.number.isRequired
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Build a fixed-width bar representation from a numeric percentage.
|
|
29
|
+
*
|
|
30
|
+
* @param {number} value - Percentage value (0-100)
|
|
31
|
+
* @returns {string} Colored bar string
|
|
32
|
+
*/
|
|
33
|
+
function makeBar(value, width) {
|
|
34
|
+
var safeWidth = Math.max(1, Math.round(width));
|
|
35
|
+
var normalized = Number.isFinite(value) ? Math.min(Math.max(value, 0), 100) : 0;
|
|
36
|
+
var filled = Math.round(normalized / 100 * safeWidth);
|
|
37
|
+
var empty = Math.max(0, safeWidth - filled);
|
|
38
|
+
var bar = "█".repeat(filled) + "░".repeat(empty);
|
|
39
|
+
return colorize(normalized, bar);
|
|
16
40
|
}
|
|
41
|
+
function formatPercent(value) {
|
|
42
|
+
if (!Number.isFinite(value)) return 0;
|
|
43
|
+
var normalized = Math.min(Math.max(value, 0), 100);
|
|
44
|
+
if (normalized >= 10) return Math.round(normalized);
|
|
45
|
+
return normalized.toFixed(1).replace(/\.0$/, "");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Colorize a value or text depending on thresholds.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} value - Numeric value used to pick color
|
|
52
|
+
* @param {string} [text] - Text to colorize, defaults to the numeric string
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
17
55
|
function colorize(value) {
|
|
18
56
|
var text = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : value.toString();
|
|
19
57
|
if (value < 50) return chalk.greenBright(text);
|
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
var SHORTCUTS = ["Use ↑/↓ for navigation", "I to initiate selected container", "P to stop selected container", "R to restart selected container", "C to create a container", "L to view logs of selected container", "D to toggle debug log panel", "E to remove selected container", "Q to quit"];
|
|
4
|
+
var GRID_COLUMNS = 3;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Small usage menu that explains available keybindings to the user.
|
|
8
|
+
* Renders entries in a responsive grid to avoid an excessively long column.
|
|
9
|
+
*
|
|
10
|
+
* @returns {JSX.Element}
|
|
11
|
+
*/
|
|
3
12
|
export default function UsageMenu() {
|
|
13
|
+
var rowsPerColumn = Math.ceil(SHORTCUTS.length / GRID_COLUMNS);
|
|
14
|
+
var columns = Array.from({
|
|
15
|
+
length: GRID_COLUMNS
|
|
16
|
+
}, function (_, columnIndex) {
|
|
17
|
+
return SHORTCUTS.slice(columnIndex * rowsPerColumn, (columnIndex + 1) * rowsPerColumn);
|
|
18
|
+
}).filter(function (column) {
|
|
19
|
+
return column.length > 0;
|
|
20
|
+
});
|
|
4
21
|
return /*#__PURE__*/React.createElement(Box, {
|
|
5
22
|
flexDirection: "column",
|
|
6
23
|
marginTop: 1
|
|
7
|
-
}, /*#__PURE__*/React.createElement(
|
|
24
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
25
|
+
flexDirection: "row"
|
|
26
|
+
}, columns.map(function (column, columnIndex) {
|
|
27
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
28
|
+
key: "shortcut-column-".concat(columnIndex),
|
|
29
|
+
flexDirection: "column",
|
|
30
|
+
marginRight: columnIndex === columns.length - 1 ? 0 : 3
|
|
31
|
+
}, column.map(function (shortcut, rowIndex) {
|
|
32
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
33
|
+
key: "shortcut-".concat(columnIndex, "-").concat(rowIndex)
|
|
34
|
+
}, shortcut);
|
|
35
|
+
}));
|
|
36
|
+
})));
|
|
8
37
|
}
|
|
@@ -2,6 +2,8 @@ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present,
|
|
|
2
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
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
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 { MESSAGE_TIMEOUTS } from "../helpers/constants.js";
|
|
6
|
+
import { logger } from "./logger.js";
|
|
5
7
|
/**
|
|
6
8
|
* Generic helper to perform a container action with user feedback.
|
|
7
9
|
*
|
|
@@ -20,7 +22,7 @@ export function handleAction(_x) {
|
|
|
20
22
|
}
|
|
21
23
|
function _handleAction() {
|
|
22
24
|
_handleAction = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(_ref) {
|
|
23
|
-
var containers, selected, actionFn, actionLabel, setMessage, setMessageColor, stateCheck, c, _t;
|
|
25
|
+
var containers, selected, actionFn, actionLabel, setMessage, setMessageColor, stateCheck, c, failureReason, _t;
|
|
24
26
|
return _regenerator().w(function (_context) {
|
|
25
27
|
while (1) switch (_context.p = _context.n) {
|
|
26
28
|
case 0:
|
|
@@ -32,19 +34,26 @@ function _handleAction() {
|
|
|
32
34
|
}
|
|
33
35
|
return _context.a(2);
|
|
34
36
|
case 1:
|
|
35
|
-
if (!
|
|
37
|
+
if (!stateCheck) {
|
|
36
38
|
_context.n = 2;
|
|
37
39
|
break;
|
|
38
40
|
}
|
|
39
|
-
|
|
41
|
+
failureReason = stateCheck(c);
|
|
42
|
+
if (!failureReason) {
|
|
43
|
+
_context.n = 2;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
logger.warn("Action %s blocked for container %s: %s", actionLabel, c.id, failureReason);
|
|
47
|
+
setMessage(failureReason);
|
|
40
48
|
setMessageColor("red");
|
|
41
49
|
setTimeout(function () {
|
|
42
50
|
return setMessage("");
|
|
43
|
-
},
|
|
51
|
+
}, MESSAGE_TIMEOUTS.SHORT);
|
|
44
52
|
return _context.a(2);
|
|
45
53
|
case 2:
|
|
46
54
|
setMessage("".concat(actionLabel, " container..."));
|
|
47
55
|
setMessageColor("green");
|
|
56
|
+
logger.info("Action %s requested for container %s", actionLabel, c.id);
|
|
48
57
|
_context.p = 3;
|
|
49
58
|
_context.n = 4;
|
|
50
59
|
return actionFn(c.id);
|
|
@@ -53,7 +62,8 @@ function _handleAction() {
|
|
|
53
62
|
setMessageColor("green");
|
|
54
63
|
setTimeout(function () {
|
|
55
64
|
return setMessage("");
|
|
56
|
-
},
|
|
65
|
+
}, MESSAGE_TIMEOUTS.DEFAULT);
|
|
66
|
+
logger.info("Action %s completed for container %s", actionLabel, c.id);
|
|
57
67
|
_context.n = 6;
|
|
58
68
|
break;
|
|
59
69
|
case 5:
|
|
@@ -63,7 +73,8 @@ function _handleAction() {
|
|
|
63
73
|
setMessageColor("red");
|
|
64
74
|
setTimeout(function () {
|
|
65
75
|
return setMessage("");
|
|
66
|
-
},
|
|
76
|
+
}, MESSAGE_TIMEOUTS.DEFAULT);
|
|
77
|
+
logger.error("Action %s failed for container %s", actionLabel, c.id, _t);
|
|
67
78
|
case 6:
|
|
68
79
|
return _context.a(2);
|
|
69
80
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time intervals used across the app (milliseconds).
|
|
3
|
+
* - CONTAINER_LIST: how often to refresh the list of containers
|
|
4
|
+
* - CONTAINER_STATS: how often to fetch stats for running containers
|
|
5
|
+
*/
|
|
6
|
+
export var REFRESH_INTERVALS = {
|
|
7
|
+
CONTAINER_LIST: 3000,
|
|
8
|
+
CONTAINER_STATS: 1500
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Timeout values for UI messages before they disappear.
|
|
13
|
+
*/
|
|
14
|
+
export var MESSAGE_TIMEOUTS = {
|
|
15
|
+
SHORT: 2000,
|
|
16
|
+
DEFAULT: 3000
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Delay used before exiting the process when showing an exit message.
|
|
21
|
+
*/
|
|
22
|
+
export var EXIT_DELAY = 500;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Operational timeouts for Docker operations and image pulls.
|
|
26
|
+
*/
|
|
27
|
+
export var TIMEOUTS = {
|
|
28
|
+
CONTAINER_OP: 30000,
|
|
29
|
+
PULL_IMAGE: 300000
|
|
30
|
+
};
|
|
@@ -4,5 +4,24 @@ import Docker from "dockerode";
|
|
|
4
4
|
// - /var/run/docker.sock on Linux/Mac
|
|
5
5
|
// - //./pipe/docker_engine on Windows
|
|
6
6
|
// - Environment variables DOCKER_HOST, DOCKER_CERT_PATH, etc.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Typedef for the docker client used in this project.
|
|
10
|
+
* Use a generic object shape so JSDoc can render a meaningful type.
|
|
11
|
+
*
|
|
12
|
+
* @typedef {Object} DockerClient
|
|
13
|
+
* @property {Function} listContainers
|
|
14
|
+
* @property {Function} getContainer
|
|
15
|
+
* @property {Function} listImages
|
|
16
|
+
* @property {Function} createContainer
|
|
17
|
+
* @property {Function} pull
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shared Docker client instance (dockerode). Consumers import { docker }
|
|
22
|
+
* and use it to list/create/start/stop containers and images.
|
|
23
|
+
*
|
|
24
|
+
* @type {DockerClient}
|
|
25
|
+
*/
|
|
7
26
|
var docker = new Docker();
|
|
8
27
|
export { docker };
|