cdd-cli 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/.atl/skill-registry.md +30 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintrc.json +1 -1
  4. package/.prettierignore +4 -0
  5. package/.prettierrc +8 -0
  6. package/CHANGELOG.md +29 -0
  7. package/SECURITY.md +5 -0
  8. package/__mocks__/ink.cjs +17 -0
  9. package/coverage/clover.xml +526 -436
  10. package/coverage/coverage-final.json +19 -15
  11. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +376 -0
  12. package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
  13. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +244 -0
  14. package/coverage/lcov-report/components/index.html +146 -0
  15. package/coverage/lcov-report/helpers/constants.js.html +80 -17
  16. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +41 -8
  17. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
  18. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
  19. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
  20. package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
  21. package/coverage/lcov-report/helpers/index.html +27 -12
  22. package/coverage/lcov-report/helpers/logger.js.html +19 -19
  23. package/coverage/lcov-report/helpers/safeCall.js.html +3 -3
  24. package/coverage/lcov-report/helpers/validationHelpers.js.html +28 -76
  25. package/coverage/lcov-report/hooks/creation/index.html +20 -20
  26. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
  27. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +416 -89
  28. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
  29. package/coverage/lcov-report/hooks/debug/index.html +5 -5
  30. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
  31. package/coverage/lcov-report/hooks/index.html +40 -40
  32. package/coverage/lcov-report/hooks/navigation/index.html +19 -19
  33. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
  34. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
  35. package/coverage/lcov-report/hooks/useControls.js.html +263 -122
  36. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
  37. package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
  38. package/coverage/lcov-report/index.html +67 -52
  39. package/coverage/lcov.info +991 -805
  40. package/dist/App.js +11 -11
  41. package/dist/components/ContainerCreationPrompt.js +31 -10
  42. package/dist/components/ContainerList.js +3 -3
  43. package/dist/components/ContainerRow.js +20 -20
  44. package/dist/components/ContainerSection.js +3 -3
  45. package/dist/components/Footer.js +2 -2
  46. package/dist/components/Header.js +7 -7
  47. package/dist/components/LogViewer.js +3 -3
  48. package/dist/components/MessageFeedback.js +2 -2
  49. package/dist/components/PromptField.js +4 -4
  50. package/dist/components/StatsBar.js +5 -5
  51. package/dist/components/SuggestionPanel.js +44 -0
  52. package/dist/components/UsageMenu.js +3 -3
  53. package/dist/helpers/appInfo.js +7 -7
  54. package/dist/helpers/constants.js +114 -0
  55. package/dist/helpers/containerOptionsBuilder.js +3 -3
  56. package/dist/helpers/dockerService/dockerService.js +1 -1
  57. package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
  58. package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
  59. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
  60. package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
  61. package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
  62. package/dist/helpers/exitWithMessage.js +10 -10
  63. package/dist/helpers/imageNameUtils.js +32 -0
  64. package/dist/helpers/logger.js +13 -13
  65. package/dist/helpers/safeCall.js +2 -2
  66. package/dist/helpers/validationHelpers.js +76 -21
  67. package/dist/hooks/creation/useContainerActions.js +9 -9
  68. package/dist/hooks/creation/useContainerCreation.js +138 -36
  69. package/dist/hooks/creation/useLogsViewer.js +2 -2
  70. package/dist/hooks/debug/useDebugLogs.js +4 -4
  71. package/dist/hooks/useContainerCommandRouter.js +13 -13
  72. package/dist/hooks/useContainers.js +3 -3
  73. package/dist/hooks/useControls.js +66 -35
  74. package/dist/hooks/useEraseConfirmation.js +3 -3
  75. package/dist/index.js +2 -2
  76. package/jest.config.cjs +7 -0
  77. package/package.json +7 -3
  78. package/src/App.jsx +33 -16
  79. package/src/components/ContainerCreationPrompt.jsx +43 -9
  80. package/src/components/ContainerList.jsx +3 -3
  81. package/src/components/ContainerRow.jsx +19 -16
  82. package/src/components/ContainerSection.jsx +3 -3
  83. package/src/components/Footer.jsx +2 -2
  84. package/src/components/Header.jsx +7 -7
  85. package/src/components/LogViewer.jsx +5 -3
  86. package/src/components/MessageFeedback.jsx +2 -2
  87. package/src/components/PromptField.jsx +4 -4
  88. package/src/components/StatsBar.jsx +11 -7
  89. package/src/components/SuggestionPanel.jsx +53 -0
  90. package/src/components/UsageMenu.jsx +18 -13
  91. package/src/helpers/appInfo.js +7 -7
  92. package/src/helpers/constants.js +30 -9
  93. package/src/helpers/containerOptionsBuilder.js +16 -5
  94. package/src/helpers/dockerService/dockerService.js +1 -1
  95. package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
  96. package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
  97. package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
  98. package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
  99. package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
  100. package/src/helpers/exitWithMessage.js +13 -7
  101. package/src/helpers/imageNameUtils.js +32 -0
  102. package/src/helpers/logger.js +15 -15
  103. package/src/helpers/safeCall.js +2 -2
  104. package/src/helpers/validationHelpers.js +13 -29
  105. package/src/hooks/creation/useContainerActions.js +19 -12
  106. package/src/hooks/creation/useContainerCreation.js +150 -41
  107. package/src/hooks/creation/useLogsViewer.js +2 -2
  108. package/src/hooks/debug/useDebugLogs.js +15 -10
  109. package/src/hooks/navigation/useContainerSelection.js +17 -14
  110. package/src/hooks/useContainerCommandRouter.js +71 -58
  111. package/src/hooks/useContainers.js +4 -4
  112. package/src/hooks/useControls.js +115 -68
  113. package/src/hooks/useEraseConfirmation.js +3 -3
  114. package/src/hooks/useExitHandler.js +29 -26
  115. package/src/index.js +4 -4
  116. package/test/ContainerCreationPrompt.dom.test.js +68 -0
  117. package/test/SuggestionPanel.dom.test.js +53 -0
  118. package/test/constants.test.js +34 -0
  119. package/test/useContainerCreation.dom.test.js +123 -0
  120. package/test/useControls.dom.test.js +198 -2
@@ -1,4 +1,10 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
3
+ 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."); }
4
+ 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; } }
5
+ 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; }
6
+ 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; } }
7
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
2
8
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
9
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
10
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -8,10 +14,11 @@ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present,
8
14
  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); }
9
15
  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); }
10
16
  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); }); }; }
11
- import { docker } from "../dockerService.js";
12
- import { imageExists, pullImage } from "./imageUtils.js";
13
- import { TIMEOUTS } from "../../constants.js";
14
- import { logger } from "../../logger.js";
17
+ import { docker } from '../dockerService.js';
18
+ import { imageExists, pullImage } from './imageUtils.js';
19
+ import { TIMEOUTS, IMAGE_PROFILES } from '../../constants.js';
20
+ import { logger } from '../../logger.js';
21
+ import { normalizeImageName } from '../../imageNameUtils.js';
15
22
 
16
23
  /**
17
24
  * Helper to add timeout to promises. If the provided promise does not settle
@@ -47,7 +54,8 @@ export function removeContainer(_x) {
47
54
  *
48
55
  * @param {string} imageName - Image name (e.g. 'nginx:alpine')
49
56
  * @param {Object} [options] - Docker create options (Env, ExposedPorts, HostConfig, name, etc.)
50
- * @returns {Promise<string>} The created container id
57
+ * @param {Object} [imageProfiles] - Image profiles map for defaultPort fallback
58
+ * @returns {Promise<{id: string, ports: Array<{containerPort: string, hostPort: string, protocol: string, source: string}>}>}
51
59
  * @throws {Error} If image listing/pull or creation fails
52
60
  */
53
61
  function _removeContainer() {
@@ -56,7 +64,7 @@ function _removeContainer() {
56
64
  return _regenerator().w(function (_context) {
57
65
  while (1) switch (_context.p = _context.n) {
58
66
  case 0:
59
- logger.debug("Removing container %s", containerId);
67
+ logger.debug('Removing container %s', containerId);
60
68
  container = docker.getContainer(containerId);
61
69
  _context.p = 1;
62
70
  _context.n = 2;
@@ -64,13 +72,13 @@ function _removeContainer() {
64
72
  force: true
65
73
  }), TIMEOUTS.CONTAINER_OP);
66
74
  case 2:
67
- logger.info("Removed container %s", containerId);
75
+ logger.info('Removed container %s', containerId);
68
76
  _context.n = 4;
69
77
  break;
70
78
  case 3:
71
79
  _context.p = 3;
72
80
  _t = _context.v;
73
- logger.error("Failed to remove container %s", containerId, _t);
81
+ logger.error('Failed to remove container %s', containerId, _t);
74
82
  throw new Error('Error removing container: ' + _t.message);
75
83
  case 4:
76
84
  return _context.a(2);
@@ -91,9 +99,11 @@ export function createContainer(_x2) {
91
99
  function _createContainer() {
92
100
  _createContainer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(imageName) {
93
101
  var options,
102
+ imageProfiles,
94
103
  exists,
95
104
  createOpts,
96
105
  hasUserDefinedPorts,
106
+ assignedPorts,
97
107
  image,
98
108
  _inspectData$Config,
99
109
  _inspectData$Containe,
@@ -104,6 +114,11 @@ function _createContainer() {
104
114
  inspectData,
105
115
  exposed,
106
116
  exposedKeys,
117
+ baseName,
118
+ profile,
119
+ fallbackKey,
120
+ _createOpts$HostConfi,
121
+ portBindings,
107
122
  container,
108
123
  containerId,
109
124
  _args2 = arguments,
@@ -116,7 +131,8 @@ function _createContainer() {
116
131
  while (1) switch (_context2.p = _context2.n) {
117
132
  case 0:
118
133
  options = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
119
- logger.info("Creating container from image %s", imageName);
134
+ imageProfiles = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : IMAGE_PROFILES;
135
+ logger.info('Creating container from image %s', imageName);
120
136
  _context2.p = 1;
121
137
  _context2.n = 2;
122
138
  return withTimeout(imageExists(imageName), 10000);
@@ -127,25 +143,25 @@ function _createContainer() {
127
143
  case 3:
128
144
  _context2.p = 3;
129
145
  _t2 = _context2.v;
130
- logger.error("Could not check local images for %s", imageName, _t2);
146
+ logger.error('Could not check local images for %s', imageName, _t2);
131
147
  throw new Error('Error listing local images: ' + _t2.message);
132
148
  case 4:
133
149
  if (exists) {
134
150
  _context2.n = 8;
135
151
  break;
136
152
  }
137
- logger.info("Image %s not found locally, pulling", imageName);
153
+ logger.info('Image %s not found locally, pulling', imageName);
138
154
  _context2.p = 5;
139
155
  _context2.n = 6;
140
156
  return withTimeout(pullImage(imageName), TIMEOUTS.PULL_IMAGE);
141
157
  case 6:
142
- logger.info("Pulled image %s", imageName);
158
+ logger.info('Pulled image %s', imageName);
143
159
  _context2.n = 8;
144
160
  break;
145
161
  case 7:
146
162
  _context2.p = 7;
147
163
  _t3 = _context2.v;
148
- logger.error("Failed to pull image %s", imageName, _t3);
164
+ logger.error('Failed to pull image %s', imageName, _t3);
149
165
  throw new Error('Could not pull image: ' + _t3.message);
150
166
  case 8:
151
167
  createOpts = _objectSpread({
@@ -153,18 +169,20 @@ function _createContainer() {
153
169
  Tty: true
154
170
  }, options);
155
171
  hasUserDefinedPorts = Boolean(createOpts.ExposedPorts && Object.keys(createOpts.ExposedPorts).length);
172
+ /** @type {Array<{containerPort: string, hostPort: string, protocol: string, source: string}>} */
173
+ assignedPorts = [];
156
174
  if (hasUserDefinedPorts) {
157
- _context2.n = 17;
175
+ _context2.n = 18;
158
176
  break;
159
177
  }
160
178
  _context2.p = 9;
161
179
  image = docker.getImage(imageName);
162
- if (!(image && typeof image.inspect === "function")) {
180
+ if (!(image && typeof image.inspect === 'function')) {
163
181
  _context2.n = 15;
164
182
  break;
165
183
  }
166
184
  usedHostPorts = new Set();
167
- if (!(typeof docker.listContainers === "function")) {
185
+ if (!(typeof docker.listContainers === 'function')) {
168
186
  _context2.n = 13;
169
187
  break;
170
188
  }
@@ -187,7 +205,7 @@ function _createContainer() {
187
205
  case 12:
188
206
  _context2.p = 12;
189
207
  _t4 = _context2.v;
190
- logger.warn("Could not inspect existing containers to auto-map ports", _t4);
208
+ logger.warn('Could not inspect existing containers to auto-map ports', _t4);
191
209
  case 13:
192
210
  reservePort = function reservePort(port) {
193
211
  var portStr = String(port);
@@ -196,7 +214,6 @@ function _createContainer() {
196
214
  pickNextAvailablePort = function pickNextAvailablePort(base) {
197
215
  var numericBase = Number.parseInt(base, 10);
198
216
  if (Number.isNaN(numericBase)) {
199
- // If the base port is non-numeric, just return the base.
200
217
  if (!usedHostPorts.has(base)) {
201
218
  usedHostPorts.add(base);
202
219
  return base;
@@ -222,7 +239,16 @@ function _createContainer() {
222
239
  case 14:
223
240
  inspectData = _context2.v;
224
241
  exposed = (inspectData === null || inspectData === void 0 || (_inspectData$Config = inspectData.Config) === null || _inspectData$Config === void 0 ? void 0 : _inspectData$Config.ExposedPorts) || (inspectData === null || inspectData === void 0 || (_inspectData$Containe = inspectData.ContainerConfig) === null || _inspectData$Containe === void 0 ? void 0 : _inspectData$Containe.ExposedPorts) || {};
225
- exposedKeys = Object.keys(exposed || {});
242
+ exposedKeys = Object.keys(exposed || {}); // If no ExposedPorts in image, fall back to IMAGE_PROFILES defaultPort
243
+ if (!exposedKeys.length) {
244
+ baseName = normalizeImageName(imageName);
245
+ profile = imageProfiles[baseName];
246
+ if (profile && profile.defaultPort) {
247
+ fallbackKey = "".concat(profile.defaultPort, "/tcp");
248
+ exposed = _defineProperty({}, fallbackKey, {});
249
+ exposedKeys = [fallbackKey];
250
+ }
251
+ }
226
252
  if (exposedKeys.length) {
227
253
  createOpts.ExposedPorts = createOpts.ExposedPorts || {};
228
254
  createOpts.HostConfig = _objectSpread({}, createOpts.HostConfig || {});
@@ -232,11 +258,19 @@ function _createContainer() {
232
258
  createOpts.ExposedPorts[portKey] = exposed[portKey] || {};
233
259
  }
234
260
  if (!createOpts.HostConfig.PortBindings[portKey] || !createOpts.HostConfig.PortBindings[portKey].length) {
235
- var containerPort = portKey.split("/")[0];
261
+ var parts = portKey.split('/');
262
+ var containerPort = parts[0];
263
+ var protocol = parts[1] || 'tcp';
236
264
  var hostPort = pickNextAvailablePort(containerPort);
237
265
  createOpts.HostConfig.PortBindings[portKey] = [{
238
266
  HostPort: hostPort
239
267
  }];
268
+ assignedPorts.push({
269
+ containerPort: containerPort,
270
+ hostPort: hostPort,
271
+ protocol: protocol,
272
+ source: 'auto'
273
+ });
240
274
  }
241
275
  });
242
276
  }
@@ -246,25 +280,50 @@ function _createContainer() {
246
280
  case 16:
247
281
  _context2.p = 16;
248
282
  _t5 = _context2.v;
249
- logger.warn("Could not inspect image %s for default ports", imageName, _t5);
283
+ logger.warn('Could not inspect image %s for default ports', imageName, _t5);
250
284
  case 17:
251
- _context2.p = 17;
252
- _context2.n = 18;
253
- return withTimeout(docker.createContainer(createOpts), TIMEOUTS.CONTAINER_OP);
285
+ _context2.n = 19;
286
+ break;
254
287
  case 18:
255
- container = _context2.v;
256
- containerId = container.id || container.Id;
257
- logger.info("Created container %s from image %s", containerId, imageName);
258
- return _context2.a(2, container.id || container.Id);
288
+ // User-defined ports: collect them with source: "user"
289
+ portBindings = ((_createOpts$HostConfi = createOpts.HostConfig) === null || _createOpts$HostConfi === void 0 ? void 0 : _createOpts$HostConfi.PortBindings) || {};
290
+ Object.entries(portBindings).forEach(function (_ref) {
291
+ var _ref2 = _slicedToArray(_ref, 2),
292
+ portKey = _ref2[0],
293
+ bindings = _ref2[1];
294
+ var parts = portKey.split('/');
295
+ var containerPort = parts[0];
296
+ var protocol = parts[1] || 'tcp';
297
+ (bindings || []).forEach(function (b) {
298
+ assignedPorts.push({
299
+ containerPort: containerPort,
300
+ hostPort: b.HostPort,
301
+ protocol: protocol,
302
+ source: 'user'
303
+ });
304
+ });
305
+ });
259
306
  case 19:
260
307
  _context2.p = 19;
308
+ _context2.n = 20;
309
+ return withTimeout(docker.createContainer(createOpts), TIMEOUTS.CONTAINER_OP);
310
+ case 20:
311
+ container = _context2.v;
312
+ containerId = container.id || container.Id;
313
+ logger.info('Created container %s from image %s', containerId, imageName);
314
+ return _context2.a(2, {
315
+ id: containerId,
316
+ ports: assignedPorts
317
+ });
318
+ case 21:
319
+ _context2.p = 21;
261
320
  _t6 = _context2.v;
262
- logger.error("Failed to create container from image %s", imageName, _t6);
321
+ logger.error('Failed to create container from image %s', imageName, _t6);
263
322
  throw new Error('Error creating container: ' + _t6.message);
264
- case 20:
323
+ case 22:
265
324
  return _context2.a(2);
266
325
  }
267
- }, _callee2, null, [[17, 19], [10, 12], [9, 16], [5, 7], [1, 3]]);
326
+ }, _callee2, null, [[19, 21], [10, 12], [9, 16], [5, 7], [1, 3]]);
268
327
  }));
269
328
  return _createContainer.apply(this, arguments);
270
329
  }
@@ -284,18 +343,18 @@ function _startContainer() {
284
343
  while (1) switch (_context3.p = _context3.n) {
285
344
  case 0:
286
345
  container = docker.getContainer(containerId);
287
- logger.debug("Starting container %s", containerId);
346
+ logger.debug('Starting container %s', containerId);
288
347
  _context3.p = 1;
289
348
  _context3.n = 2;
290
349
  return withTimeout(container.start(), TIMEOUTS.CONTAINER_OP);
291
350
  case 2:
292
- logger.info("Started container %s", containerId);
351
+ logger.info('Started container %s', containerId);
293
352
  _context3.n = 4;
294
353
  break;
295
354
  case 3:
296
355
  _context3.p = 3;
297
356
  _t7 = _context3.v;
298
- logger.error("Failed to start container %s", containerId, _t7);
357
+ logger.error('Failed to start container %s', containerId, _t7);
299
358
  throw _t7;
300
359
  case 4:
301
360
  return _context3.a(2);
@@ -320,18 +379,18 @@ function _stopContainer() {
320
379
  while (1) switch (_context4.p = _context4.n) {
321
380
  case 0:
322
381
  container = docker.getContainer(containerId);
323
- logger.debug("Stopping container %s", containerId);
382
+ logger.debug('Stopping container %s', containerId);
324
383
  _context4.p = 1;
325
384
  _context4.n = 2;
326
385
  return withTimeout(container.stop(), TIMEOUTS.CONTAINER_OP);
327
386
  case 2:
328
- logger.info("Stopped container %s", containerId);
387
+ logger.info('Stopped container %s', containerId);
329
388
  _context4.n = 4;
330
389
  break;
331
390
  case 3:
332
391
  _context4.p = 3;
333
392
  _t8 = _context4.v;
334
- logger.error("Failed to stop container %s", containerId, _t8);
393
+ logger.error('Failed to stop container %s', containerId, _t8);
335
394
  throw _t8;
336
395
  case 4:
337
396
  return _context4.a(2);
@@ -350,18 +409,18 @@ function _restartContainer() {
350
409
  while (1) switch (_context5.p = _context5.n) {
351
410
  case 0:
352
411
  container = docker.getContainer(containerId);
353
- logger.debug("Restarting container %s", containerId);
412
+ logger.debug('Restarting container %s', containerId);
354
413
  _context5.p = 1;
355
414
  _context5.n = 2;
356
415
  return withTimeout(container.restart(), TIMEOUTS.CONTAINER_OP);
357
416
  case 2:
358
- logger.info("Restarted container %s", containerId);
417
+ logger.info('Restarted container %s', containerId);
359
418
  _context5.n = 4;
360
419
  break;
361
420
  case 3:
362
421
  _context5.p = 3;
363
422
  _t9 = _context5.v;
364
- logger.error("Failed to restart container %s", containerId, _t9);
423
+ logger.error('Failed to restart container %s', containerId, _t9);
365
424
  throw _t9;
366
425
  case 4:
367
426
  return _context5.a(2);
@@ -8,9 +8,9 @@ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(
8
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
9
  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); }
10
10
  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); }); }; }
11
- import { docker } from "../dockerService.js";
12
- import { logger } from "../../logger.js";
13
- var DEFAULT_CONTAINER_NAME = "Unknown";
11
+ import { docker } from '../dockerService.js';
12
+ import { logger } from '../../logger.js';
13
+ var DEFAULT_CONTAINER_NAME = 'Unknown';
14
14
 
15
15
  /**
16
16
  * Produce a safe, display-friendly container name from the raw Docker payload.
@@ -21,19 +21,19 @@ var DEFAULT_CONTAINER_NAME = "Unknown";
21
21
  function normalizeContainerName(container) {
22
22
  var names = container === null || container === void 0 ? void 0 : container.Names;
23
23
  var rawName;
24
- if (Array.isArray(names) && names.length > 0 && typeof names[0] === "string") {
24
+ if (Array.isArray(names) && names.length > 0 && typeof names[0] === 'string') {
25
25
  rawName = names[0];
26
- } else if (typeof names === "string" && names.trim().length > 0) {
26
+ } else if (typeof names === 'string' && names.trim().length > 0) {
27
27
  rawName = names;
28
- } else if (typeof (container === null || container === void 0 ? void 0 : container.Name) === "string" && container.Name.trim().length > 0) {
28
+ } else if (typeof (container === null || container === void 0 ? void 0 : container.Name) === 'string' && container.Name.trim().length > 0) {
29
29
  rawName = container.Name;
30
30
  } else {
31
31
  rawName = DEFAULT_CONTAINER_NAME;
32
32
  }
33
- if (typeof rawName !== "string") {
33
+ if (typeof rawName !== 'string') {
34
34
  return DEFAULT_CONTAINER_NAME;
35
35
  }
36
- var cleanedName = rawName.replace(/^\/+/, "").trim();
36
+ var cleanedName = rawName.replace(/^\/+/, '').trim();
37
37
  return cleanedName.length > 0 ? cleanedName : DEFAULT_CONTAINER_NAME;
38
38
  }
39
39
 
@@ -50,7 +50,7 @@ function _getContainers() {
50
50
  return _regenerator().w(function (_context) {
51
51
  while (1) switch (_context.p = _context.n) {
52
52
  case 0:
53
- logger.debug("Listing containers");
53
+ logger.debug('Listing containers');
54
54
  _context.p = 1;
55
55
  _context.n = 2;
56
56
  return docker.listContainers({
@@ -58,7 +58,7 @@ function _getContainers() {
58
58
  });
59
59
  case 2:
60
60
  containers = _context.v;
61
- logger.debug("Retrieved %d containers", containers.length);
61
+ logger.debug('Retrieved %d containers', containers.length);
62
62
  return _context.a(2, containers.map(function (container) {
63
63
  return {
64
64
  id: container.Id,
@@ -88,7 +88,7 @@ function _getContainers() {
88
88
  case 3:
89
89
  _context.p = 3;
90
90
  _t = _context.v;
91
- logger.error("Failed to list containers", _t);
91
+ logger.error('Failed to list containers', _t);
92
92
  throw _t;
93
93
  case 4:
94
94
  return _context.a(2);
@@ -1,6 +1,6 @@
1
- import { docker } from "../dockerService.js";
2
- import { safeCall } from "../../safeCall.js";
3
- import { logger } from "../../logger.js";
1
+ import { docker } from '../dockerService.js';
2
+ import { safeCall } from '../../safeCall.js';
3
+ import { logger } from '../../logger.js';
4
4
 
5
5
  /**
6
6
  * Return a stream of logs from a container and call callbacks for events.
@@ -15,7 +15,7 @@ export function getLogsStream(containerId, onData, onEnd, onError) {
15
15
 
16
16
  try {
17
17
  var container = docker.getContainer(containerId);
18
- logger.debug("Opening logs stream for container %s", containerId);
18
+ logger.debug('Opening logs stream for container %s', containerId);
19
19
  container.logs({
20
20
  follow: true,
21
21
  stdout: true,
@@ -23,25 +23,25 @@ export function getLogsStream(containerId, onData, onEnd, onError) {
23
23
  tail: 100
24
24
  }, function (err, stream) {
25
25
  if (err) {
26
- logger.error("Failed to open logs stream for container %s", containerId, err);
26
+ logger.error('Failed to open logs stream for container %s', containerId, err);
27
27
  safeCall(onError, err);
28
28
  return;
29
29
  }
30
- logger.info("Logs stream established for container %s", containerId);
30
+ logger.info('Logs stream established for container %s', containerId);
31
31
  stream.on('data', function (chunk) {
32
32
  return safeCall(onData, chunk.toString());
33
33
  });
34
34
  stream.on('end', function () {
35
- logger.debug("Logs stream ended for container %s", containerId);
35
+ logger.debug('Logs stream ended for container %s', containerId);
36
36
  safeCall(onEnd);
37
37
  });
38
38
  stream.on('error', function (streamErr) {
39
- logger.error("Logs stream error for container %s", containerId, streamErr);
39
+ logger.error('Logs stream error for container %s', containerId, streamErr);
40
40
  safeCall(onError, streamErr);
41
41
  });
42
42
  });
43
43
  } catch (err) {
44
- logger.error("Unexpected error while opening logs stream for container %s", containerId, err);
44
+ logger.error('Unexpected error while opening logs stream for container %s', containerId, err);
45
45
  safeCall(onError, err);
46
46
  }
47
47
  }
@@ -2,8 +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 { docker } from "../dockerService.js";
6
- import { logger } from "../../logger.js";
5
+ import { docker } from '../dockerService.js';
6
+ import { logger } from '../../logger.js';
7
7
 
8
8
  /**
9
9
  * Retrieve a snapshot of container resource usage (CPU, memory, network).
@@ -21,7 +21,7 @@ function _getStats() {
21
21
  while (1) switch (_context.p = _context.n) {
22
22
  case 0:
23
23
  container = docker.getContainer(containerId);
24
- logger.debug("Fetching stats for container %s", containerId);
24
+ logger.debug('Fetching stats for container %s', containerId);
25
25
  _context.p = 1;
26
26
  _context.n = 2;
27
27
  return container.stats({
@@ -34,7 +34,7 @@ function _getStats() {
34
34
  case 3:
35
35
  _context.p = 3;
36
36
  _t = _context.v;
37
- logger.error("Failed to fetch stats for container %s", containerId, _t);
37
+ logger.error('Failed to fetch stats for container %s', containerId, _t);
38
38
  throw _t;
39
39
  case 4:
40
40
  cpuDelta = stream.cpu_stats.cpu_usage.total_usage - stream.precpu_stats.cpu_usage.total_usage;
@@ -69,7 +69,7 @@ function _getStats() {
69
69
  tx: tx
70
70
  }
71
71
  };
72
- logger.debug("Stats fetched for container %s", containerId, snapshot);
72
+ logger.debug('Stats fetched for container %s', containerId, snapshot);
73
73
  return _context.a(2, snapshot);
74
74
  }
75
75
  }, _callee, null, [[1, 3]]);
@@ -2,7 +2,7 @@ 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 { docker } from "../dockerService.js";
5
+ import { docker } from '../dockerService.js';
6
6
 
7
7
  /**
8
8
  * Check whether an image exists locally.
@@ -1,5 +1,5 @@
1
- import { spawn } from "child_process";
2
- import { EXIT_DELAY } from "./constants.js";
1
+ import { spawn } from 'child_process';
2
+ import { EXIT_DELAY } from './constants.js';
3
3
 
4
4
  /**
5
5
  * Show an exit message using provided setters, clear the terminal and exit after a delay.
@@ -15,22 +15,22 @@ export function exitWithMessage(_ref) {
15
15
  var setMessage = _ref.setMessage,
16
16
  setMessageColor = _ref.setMessageColor,
17
17
  _ref$message = _ref.message,
18
- message = _ref$message === void 0 ? "Exiting..." : _ref$message,
18
+ message = _ref$message === void 0 ? 'Exiting...' : _ref$message,
19
19
  _ref$color = _ref.color,
20
- color = _ref$color === void 0 ? "yellow" : _ref$color,
20
+ color = _ref$color === void 0 ? 'yellow' : _ref$color,
21
21
  _ref$delay = _ref.delay,
22
22
  delay = _ref$delay === void 0 ? EXIT_DELAY : _ref$delay;
23
23
  setMessage(message);
24
24
  setMessageColor(color);
25
25
  setTimeout(function () {
26
- setMessage("");
27
- if (process.platform === "win32") {
28
- spawn("cmd", ["/c", "cls"], {
29
- stdio: "inherit"
26
+ setMessage('');
27
+ if (process.platform === 'win32') {
28
+ spawn('cmd', ['/c', 'cls'], {
29
+ stdio: 'inherit'
30
30
  });
31
31
  } else {
32
- spawn("clear", [], {
33
- stdio: "inherit"
32
+ spawn('clear', [], {
33
+ stdio: 'inherit'
34
34
  });
35
35
  }
36
36
  process.exit();
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Utility for normalizing Docker image names.
3
+ * Extracted from validationHelpers.js and containerActions.js — both had
4
+ * identical implementations; consolidated here to avoid duplication.
5
+ */
6
+
7
+ /**
8
+ * Normalize an image name to its base name (strips registry prefix and tag).
9
+ *
10
+ * Examples:
11
+ * "docker.io/library/postgres:16-alpine" → "postgres"
12
+ * "nginx:alpine" → "nginx"
13
+ * "myregistry.io/myorg/myapp:latest" → "myapp"
14
+ *
15
+ * @param {string} imageName - Raw image name (from user input or Docker API)
16
+ * @returns {string} Base image name in lowercase, empty string if input is falsy
17
+ */
18
+ export function normalizeImageName(imageName) {
19
+ if (!imageName) return '';
20
+ // Remove registry prefix (everything up to and including the last '/')
21
+ var name = imageName;
22
+ var slashIdx = name.lastIndexOf('/');
23
+ if (slashIdx !== -1) {
24
+ name = name.slice(slashIdx + 1);
25
+ }
26
+ // Remove tag (everything from the first ':' onward)
27
+ var colonIdx = name.indexOf(':');
28
+ if (colonIdx !== -1) {
29
+ name = name.slice(0, colonIdx);
30
+ }
31
+ return name.toLowerCase();
32
+ }
@@ -25,7 +25,7 @@ var LOG_LEVELS = {
25
25
  */
26
26
 
27
27
  function parseLevel(value) {
28
- if (typeof value !== "string") {
28
+ if (typeof value !== 'string') {
29
29
  return null;
30
30
  }
31
31
  var normalized = value.trim().toLowerCase();
@@ -35,11 +35,11 @@ function parseLevel(value) {
35
35
  return null;
36
36
  }
37
37
  var envLevel = (_ref = (_parseLevel = parseLevel(process.env.CDD_LOG_LEVEL)) !== null && _parseLevel !== void 0 ? _parseLevel : parseLevel(process.env.LOG_LEVEL)) !== null && _ref !== void 0 ? _ref : LOG_LEVELS.info;
38
- var currentLevel = typeof envLevel === "number" ? envLevel : LOG_LEVELS.info;
38
+ var currentLevel = typeof envLevel === 'number' ? envLevel : LOG_LEVELS.info;
39
39
  var listeners = new Set();
40
40
  function shouldLog(level) {
41
41
  var target = LOG_LEVELS[level];
42
- return typeof target === "number" && target <= currentLevel;
42
+ return typeof target === 'number' && target <= currentLevel;
43
43
  }
44
44
  function log(level, message) {
45
45
  if (!shouldLog(level)) {
@@ -58,10 +58,10 @@ function log(level, message) {
58
58
  timestamp: timestamp,
59
59
  formatted: formatted
60
60
  };
61
- var shouldWriteToConsole = listeners.size === 0 || level === "error" || level === "warn";
61
+ var shouldWriteToConsole = listeners.size === 0 || level === 'error' || level === 'warn';
62
62
  if (shouldWriteToConsole) {
63
- var method = level === "debug" ? "log" : level;
64
- if (typeof console[method] === "function") {
63
+ var method = level === 'debug' ? 'log' : level;
64
+ if (typeof console[method] === 'function') {
65
65
  var _console;
66
66
  (_console = console)[method].apply(_console, [formatted].concat(args));
67
67
  } else {
@@ -88,34 +88,34 @@ export var logger = {
88
88
  for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
89
89
  args[_key2 - 1] = arguments[_key2];
90
90
  }
91
- log.apply(void 0, ["debug", message].concat(args));
91
+ log.apply(void 0, ['debug', message].concat(args));
92
92
  },
93
93
  info: function info(message) {
94
94
  for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
95
95
  args[_key3 - 1] = arguments[_key3];
96
96
  }
97
- log.apply(void 0, ["info", message].concat(args));
97
+ log.apply(void 0, ['info', message].concat(args));
98
98
  },
99
99
  warn: function warn(message) {
100
100
  for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
101
101
  args[_key4 - 1] = arguments[_key4];
102
102
  }
103
- log.apply(void 0, ["warn", message].concat(args));
103
+ log.apply(void 0, ['warn', message].concat(args));
104
104
  },
105
105
  error: function error(message) {
106
106
  for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
107
107
  args[_key5 - 1] = arguments[_key5];
108
108
  }
109
- log.apply(void 0, ["error", message].concat(args));
109
+ log.apply(void 0, ['error', message].concat(args));
110
110
  },
111
111
  /**
112
112
  * Subscribe to log events.
113
113
  *
114
- * @param {LogListener} listener Listener invoked for every log entry that passes the current level filter.
115
- * @returns {function(): void} Cleanup function that removes the listener when called.
114
+ * @param {LogListener} listener Listener invoked for every log entry that passes the current level filter.
115
+ * @returns {function(): void} Cleanup function that removes the listener when called.
116
116
  */
117
117
  subscribe: function subscribe(listener) {
118
- if (typeof listener !== "function") {
118
+ if (typeof listener !== 'function') {
119
119
  return function () {};
120
120
  }
121
121
  listeners.add(listener);