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.
Files changed (138) hide show
  1. package/.eslintrc.json +30 -0
  2. package/.github/workflows/docs.yml +35 -0
  3. package/CHANGELOG.md +44 -4
  4. package/FIXES_APPLIED.md +145 -147
  5. package/README.md +26 -0
  6. package/dist/App.js +20 -3
  7. package/dist/components/ContainerCreationPrompt.js +27 -1
  8. package/dist/components/ContainerList.js +11 -15
  9. package/dist/components/ContainerRow.js +73 -14
  10. package/dist/components/ContainerSection.js +16 -2
  11. package/dist/components/Footer.js +7 -1
  12. package/dist/components/Header.js +17 -1
  13. package/dist/components/LogViewer.js +21 -3
  14. package/dist/components/MessageFeedback.js +15 -1
  15. package/dist/components/PromptField.js +31 -1
  16. package/dist/components/StatsBar.js +46 -8
  17. package/dist/components/UsageMenu.js +30 -1
  18. package/dist/helpers/actionHelpers.js +17 -6
  19. package/dist/helpers/constants.js +30 -0
  20. package/dist/helpers/dockerService/dockerService.js +19 -0
  21. package/dist/helpers/dockerService/serviceComponents/containerActions.js +194 -35
  22. package/dist/helpers/dockerService/serviceComponents/containerList.js +45 -8
  23. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +16 -6
  24. package/dist/helpers/dockerService/serviceComponents/containerStats.js +28 -9
  25. package/dist/helpers/exitWithMessage.js +2 -1
  26. package/dist/helpers/logger.js +112 -0
  27. package/dist/helpers/safeCall.js +21 -0
  28. package/dist/helpers/validationHelpers.js +20 -1
  29. package/dist/hooks/creation/useContainerActions.js +2 -1
  30. package/dist/hooks/creation/useContainerCreation.js +5 -4
  31. package/dist/hooks/creation/useLogsViewer.js +3 -2
  32. package/dist/hooks/useContainers.js +6 -3
  33. package/dist/hooks/useControls.js +253 -122
  34. package/dist/hooks/useLogsStream.js +3 -2
  35. package/dist/index.js +1 -0
  36. package/docs/components_ContainerCreationPrompt.jsx.html +114 -0
  37. package/docs/components_ContainerRow.jsx.html +151 -0
  38. package/docs/components_ContainerSection.jsx.html +75 -0
  39. package/docs/components_Footer.jsx.html +66 -0
  40. package/docs/components_Header.jsx.html +84 -0
  41. package/docs/components_LogViewer.jsx.html +91 -0
  42. package/docs/components_MessageFeedback.jsx.html +76 -0
  43. package/docs/components_PromptField.jsx.html +99 -0
  44. package/docs/components_StatsBar.jsx.html +105 -0
  45. package/docs/components_UsageMenu.jsx.html +73 -0
  46. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  47. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  48. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  49. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  50. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  51. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  52. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  53. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  54. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  55. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  56. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  57. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  58. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  59. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  60. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  61. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  62. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  63. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  64. package/docs/global.html +6145 -0
  65. package/docs/helpers_actionHelpers.js.html +94 -0
  66. package/docs/helpers_constants.js.html +81 -0
  67. package/docs/helpers_dockerService_dockerService.js.html +79 -0
  68. package/docs/helpers_dockerService_serviceComponents_containerActions.js.html +153 -0
  69. package/docs/helpers_dockerService_serviceComponents_containerList.js.html +81 -0
  70. package/docs/helpers_dockerService_serviceComponents_containerLogs.js.html +85 -0
  71. package/docs/helpers_dockerService_serviceComponents_containerStats.js.html +107 -0
  72. package/docs/helpers_dockerService_serviceComponents_imageUtils.js.html +82 -0
  73. package/docs/helpers_exitWithMessage.js.html +77 -0
  74. package/docs/helpers_safeCall.js.html +68 -0
  75. package/docs/helpers_validationHelpers.js.html +96 -0
  76. package/docs/hooks_creation_useContainerActions.js.html +120 -0
  77. package/docs/hooks_creation_useContainerCreation.js.html +152 -0
  78. package/docs/hooks_creation_useLogsViewer.js.html +102 -0
  79. package/docs/hooks_useControls.js.html +298 -0
  80. package/docs/hooks_useLogsStream.js.html +82 -0
  81. package/docs/index.html +65 -0
  82. package/docs/index.js.html +65 -0
  83. package/docs/module-index.html +171 -0
  84. package/docs/scripts/linenumber.js +25 -0
  85. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  86. package/docs/scripts/prettify/lang-css.js +2 -0
  87. package/docs/scripts/prettify/prettify.js +28 -0
  88. package/docs/styles/jsdoc-default.css +358 -0
  89. package/docs/styles/prettify-jsdoc.css +111 -0
  90. package/docs/styles/prettify-tomorrow.css +132 -0
  91. package/fix-imports.cjs +33 -6
  92. package/jest.config.cjs +2 -1
  93. package/package.json +19 -9
  94. package/src/App.jsx +16 -2
  95. package/src/components/ContainerCreationPrompt.jsx +26 -0
  96. package/src/components/ContainerList.jsx +12 -13
  97. package/src/components/ContainerRow.jsx +55 -20
  98. package/src/components/ContainerSection.jsx +15 -1
  99. package/src/components/Footer.jsx +6 -1
  100. package/src/components/Header.jsx +16 -0
  101. package/src/components/LogViewer.jsx +19 -2
  102. package/src/components/MessageFeedback.jsx +14 -0
  103. package/src/components/PromptField.jsx +30 -0
  104. package/src/components/StatsBar.jsx +47 -8
  105. package/src/components/UsageMenu.jsx +38 -8
  106. package/src/helpers/actionHelpers.js +16 -7
  107. package/src/helpers/constants.js +30 -0
  108. package/src/helpers/dockerService/dockerService.js +19 -0
  109. package/src/helpers/dockerService/serviceComponents/containerActions.js +123 -12
  110. package/src/helpers/dockerService/serviceComponents/containerList.js +61 -23
  111. package/src/helpers/dockerService/serviceComponents/containerLogs.js +20 -6
  112. package/src/helpers/dockerService/serviceComponents/containerStats.js +24 -5
  113. package/src/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
  114. package/src/helpers/exitWithMessage.js +2 -1
  115. package/src/helpers/logger.js +113 -0
  116. package/src/helpers/safeCall.js +18 -0
  117. package/src/helpers/validationHelpers.js +21 -3
  118. package/src/hooks/creation/useContainerActions.js +4 -3
  119. package/src/hooks/creation/useContainerCreation.js +5 -4
  120. package/src/hooks/creation/useLogsViewer.js +2 -1
  121. package/src/hooks/useContainers.js +6 -3
  122. package/src/hooks/useControls.js +244 -91
  123. package/src/hooks/useLogsStream.js +2 -1
  124. package/src/index.js +1 -0
  125. package/test/actionHelpers.test.js +106 -0
  126. package/test/containerActions.test.js +138 -0
  127. package/test/containerList.test.js +79 -0
  128. package/test/jest.setup.js +7 -0
  129. package/test/safeCall.test.js +25 -0
  130. package/test/useContainerCreation.dom.test.js +97 -0
  131. package/test/validationHelpers.test.js +2 -2
  132. package/AUDIT_REPORT.md +0 -1004
  133. package/dist/cdd.bundle.js +0 -8
  134. package/dist/components/ContainerCreator.js +0 -81
  135. package/dist/components/StatsViewer.js +0 -0
  136. package/dist/helpers/dockerActions.js +0 -64
  137. package/dist/helpers/dockerService.js +0 -284
  138. package/dist/hooks/useStatsPolling.js +0 -54
@@ -10,15 +10,20 @@ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.
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
11
  import { docker } from "../dockerService.js";
12
12
  import { imageExists, pullImage } from "./imageUtils.js";
13
+ import { TIMEOUTS } from "../../constants.js";
14
+ import { logger } from "../../logger.js";
13
15
 
14
16
  /**
15
- * Helper to add timeout to promises
16
- * @param {Promise} promise - Promise to wrap
17
- * @param {number} ms - Timeout in milliseconds
18
- * @returns {Promise}
17
+ * Helper to add timeout to promises. If the provided promise does not settle
18
+ * within `ms` milliseconds it will reject with an Error.
19
+ *
20
+ * @template T
21
+ * @param {Promise<T>} promise - Promise to wrap
22
+ * @param {number} [ms=TIMEOUTS.CONTAINER_OP] - Timeout in milliseconds
23
+ * @returns {Promise<T>} The original promise result or a rejection on timeout
19
24
  */
20
25
  function withTimeout(promise) {
21
- var ms = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 30000;
26
+ var ms = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TIMEOUTS.CONTAINER_OP;
22
27
  return Promise.race([promise, new Promise(function (_, reject) {
23
28
  return setTimeout(function () {
24
29
  return reject(new Error('Operation timed out'));
@@ -51,18 +56,21 @@ function _removeContainer() {
51
56
  return _regenerator().w(function (_context) {
52
57
  while (1) switch (_context.p = _context.n) {
53
58
  case 0:
59
+ logger.debug("Removing container %s", containerId);
54
60
  container = docker.getContainer(containerId);
55
61
  _context.p = 1;
56
62
  _context.n = 2;
57
63
  return withTimeout(container.remove({
58
64
  force: true
59
- }), 30000);
65
+ }), TIMEOUTS.CONTAINER_OP);
60
66
  case 2:
67
+ logger.info("Removed container %s", containerId);
61
68
  _context.n = 4;
62
69
  break;
63
70
  case 3:
64
71
  _context.p = 3;
65
72
  _t = _context.v;
73
+ logger.error("Failed to remove container %s", containerId, _t);
66
74
  throw new Error('Error removing container: ' + _t.message);
67
75
  case 4:
68
76
  return _context.a(2);
@@ -85,15 +93,30 @@ function _createContainer() {
85
93
  var options,
86
94
  exists,
87
95
  createOpts,
96
+ hasUserDefinedPorts,
97
+ image,
98
+ _inspectData$Config,
99
+ _inspectData$Containe,
100
+ usedHostPorts,
101
+ containers,
102
+ reservePort,
103
+ pickNextAvailablePort,
104
+ inspectData,
105
+ exposed,
106
+ exposedKeys,
88
107
  container,
108
+ containerId,
89
109
  _args2 = arguments,
90
110
  _t2,
91
111
  _t3,
92
- _t4;
112
+ _t4,
113
+ _t5,
114
+ _t6;
93
115
  return _regenerator().w(function (_context2) {
94
116
  while (1) switch (_context2.p = _context2.n) {
95
117
  case 0:
96
118
  options = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
119
+ logger.info("Creating container from image %s", imageName);
97
120
  _context2.p = 1;
98
121
  _context2.n = 2;
99
122
  return withTimeout(imageExists(imageName), 10000);
@@ -104,41 +127,144 @@ function _createContainer() {
104
127
  case 3:
105
128
  _context2.p = 3;
106
129
  _t2 = _context2.v;
130
+ logger.error("Could not check local images for %s", imageName, _t2);
107
131
  throw new Error('Error listing local images: ' + _t2.message);
108
132
  case 4:
109
133
  if (exists) {
110
134
  _context2.n = 8;
111
135
  break;
112
136
  }
137
+ logger.info("Image %s not found locally, pulling", imageName);
113
138
  _context2.p = 5;
114
139
  _context2.n = 6;
115
- return withTimeout(pullImage(imageName), 300000);
140
+ return withTimeout(pullImage(imageName), TIMEOUTS.PULL_IMAGE);
116
141
  case 6:
142
+ logger.info("Pulled image %s", imageName);
117
143
  _context2.n = 8;
118
144
  break;
119
145
  case 7:
120
146
  _context2.p = 7;
121
147
  _t3 = _context2.v;
148
+ logger.error("Failed to pull image %s", imageName, _t3);
122
149
  throw new Error('Could not pull image: ' + _t3.message);
123
150
  case 8:
124
151
  createOpts = _objectSpread({
125
152
  Image: imageName,
126
153
  Tty: true
127
154
  }, options);
155
+ hasUserDefinedPorts = Boolean(createOpts.ExposedPorts && Object.keys(createOpts.ExposedPorts).length);
156
+ if (hasUserDefinedPorts) {
157
+ _context2.n = 17;
158
+ break;
159
+ }
128
160
  _context2.p = 9;
129
- _context2.n = 10;
130
- return withTimeout(docker.createContainer(createOpts), 30000);
131
- case 10:
132
- container = _context2.v;
133
- return _context2.a(2, container.id || container.Id);
161
+ image = docker.getImage(imageName);
162
+ if (!(image && typeof image.inspect === "function")) {
163
+ _context2.n = 15;
164
+ break;
165
+ }
166
+ usedHostPorts = new Set();
167
+ if (!(typeof docker.listContainers === "function")) {
168
+ _context2.n = 13;
169
+ break;
170
+ }
171
+ _context2.p = 10;
172
+ _context2.n = 11;
173
+ return withTimeout(docker.listContainers({
174
+ all: true
175
+ }), TIMEOUTS.CONTAINER_OP);
134
176
  case 11:
135
- _context2.p = 11;
136
- _t4 = _context2.v;
137
- throw new Error('Error creating container: ' + _t4.message);
177
+ containers = _context2.v;
178
+ containers.forEach(function (container) {
179
+ (container.Ports || []).forEach(function (portInfo) {
180
+ if (portInfo && portInfo.PublicPort) {
181
+ usedHostPorts.add(String(portInfo.PublicPort));
182
+ }
183
+ });
184
+ });
185
+ _context2.n = 13;
186
+ break;
138
187
  case 12:
188
+ _context2.p = 12;
189
+ _t4 = _context2.v;
190
+ logger.warn("Could not inspect existing containers to auto-map ports", _t4);
191
+ case 13:
192
+ reservePort = function reservePort(port) {
193
+ var portStr = String(port);
194
+ usedHostPorts.add(portStr);
195
+ };
196
+ pickNextAvailablePort = function pickNextAvailablePort(base) {
197
+ var numericBase = Number.parseInt(base, 10);
198
+ if (Number.isNaN(numericBase)) {
199
+ // If the base port is non-numeric, just return the base.
200
+ if (!usedHostPorts.has(base)) {
201
+ usedHostPorts.add(base);
202
+ return base;
203
+ }
204
+ var counter = 1;
205
+ var _candidate = "".concat(base, "-").concat(counter);
206
+ while (usedHostPorts.has(_candidate)) {
207
+ counter += 1;
208
+ _candidate = "".concat(base, "-").concat(counter);
209
+ }
210
+ usedHostPorts.add(_candidate);
211
+ return _candidate;
212
+ }
213
+ var candidate = numericBase;
214
+ while (usedHostPorts.has(String(candidate))) {
215
+ candidate += 1;
216
+ }
217
+ reservePort(candidate);
218
+ return String(candidate);
219
+ };
220
+ _context2.n = 14;
221
+ return withTimeout(image.inspect(), TIMEOUTS.CONTAINER_OP);
222
+ case 14:
223
+ inspectData = _context2.v;
224
+ 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 || {});
226
+ if (exposedKeys.length) {
227
+ createOpts.ExposedPorts = createOpts.ExposedPorts || {};
228
+ createOpts.HostConfig = _objectSpread({}, createOpts.HostConfig || {});
229
+ createOpts.HostConfig.PortBindings = _objectSpread({}, createOpts.HostConfig.PortBindings || {});
230
+ exposedKeys.forEach(function (portKey) {
231
+ if (!createOpts.ExposedPorts[portKey]) {
232
+ createOpts.ExposedPorts[portKey] = exposed[portKey] || {};
233
+ }
234
+ if (!createOpts.HostConfig.PortBindings[portKey] || !createOpts.HostConfig.PortBindings[portKey].length) {
235
+ var containerPort = portKey.split("/")[0];
236
+ var hostPort = pickNextAvailablePort(containerPort);
237
+ createOpts.HostConfig.PortBindings[portKey] = [{
238
+ HostPort: hostPort
239
+ }];
240
+ }
241
+ });
242
+ }
243
+ case 15:
244
+ _context2.n = 17;
245
+ break;
246
+ case 16:
247
+ _context2.p = 16;
248
+ _t5 = _context2.v;
249
+ logger.warn("Could not inspect image %s for default ports", imageName, _t5);
250
+ case 17:
251
+ _context2.p = 17;
252
+ _context2.n = 18;
253
+ return withTimeout(docker.createContainer(createOpts), TIMEOUTS.CONTAINER_OP);
254
+ 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);
259
+ case 19:
260
+ _context2.p = 19;
261
+ _t6 = _context2.v;
262
+ logger.error("Failed to create container from image %s", imageName, _t6);
263
+ throw new Error('Error creating container: ' + _t6.message);
264
+ case 20:
139
265
  return _context2.a(2);
140
266
  }
141
- }, _callee2, null, [[9, 11], [5, 7], [1, 3]]);
267
+ }, _callee2, null, [[17, 19], [10, 12], [9, 16], [5, 7], [1, 3]]);
142
268
  }));
143
269
  return _createContainer.apply(this, arguments);
144
270
  }
@@ -153,17 +279,28 @@ export function startContainer(_x3) {
153
279
  */
154
280
  function _startContainer() {
155
281
  _startContainer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(containerId) {
156
- var container;
282
+ var container, _t7;
157
283
  return _regenerator().w(function (_context3) {
158
- while (1) switch (_context3.n) {
284
+ while (1) switch (_context3.p = _context3.n) {
159
285
  case 0:
160
286
  container = docker.getContainer(containerId);
161
- _context3.n = 1;
162
- return withTimeout(container.start(), 30000);
163
- case 1:
287
+ logger.debug("Starting container %s", containerId);
288
+ _context3.p = 1;
289
+ _context3.n = 2;
290
+ return withTimeout(container.start(), TIMEOUTS.CONTAINER_OP);
291
+ case 2:
292
+ logger.info("Started container %s", containerId);
293
+ _context3.n = 4;
294
+ break;
295
+ case 3:
296
+ _context3.p = 3;
297
+ _t7 = _context3.v;
298
+ logger.error("Failed to start container %s", containerId, _t7);
299
+ throw _t7;
300
+ case 4:
164
301
  return _context3.a(2);
165
302
  }
166
- }, _callee3);
303
+ }, _callee3, null, [[1, 3]]);
167
304
  }));
168
305
  return _startContainer.apply(this, arguments);
169
306
  }
@@ -178,17 +315,28 @@ export function stopContainer(_x4) {
178
315
  */
179
316
  function _stopContainer() {
180
317
  _stopContainer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(containerId) {
181
- var container;
318
+ var container, _t8;
182
319
  return _regenerator().w(function (_context4) {
183
- while (1) switch (_context4.n) {
320
+ while (1) switch (_context4.p = _context4.n) {
184
321
  case 0:
185
322
  container = docker.getContainer(containerId);
186
- _context4.n = 1;
187
- return withTimeout(container.stop(), 30000);
188
- case 1:
323
+ logger.debug("Stopping container %s", containerId);
324
+ _context4.p = 1;
325
+ _context4.n = 2;
326
+ return withTimeout(container.stop(), TIMEOUTS.CONTAINER_OP);
327
+ case 2:
328
+ logger.info("Stopped container %s", containerId);
329
+ _context4.n = 4;
330
+ break;
331
+ case 3:
332
+ _context4.p = 3;
333
+ _t8 = _context4.v;
334
+ logger.error("Failed to stop container %s", containerId, _t8);
335
+ throw _t8;
336
+ case 4:
189
337
  return _context4.a(2);
190
338
  }
191
- }, _callee4);
339
+ }, _callee4, null, [[1, 3]]);
192
340
  }));
193
341
  return _stopContainer.apply(this, arguments);
194
342
  }
@@ -197,17 +345,28 @@ export function restartContainer(_x5) {
197
345
  }
198
346
  function _restartContainer() {
199
347
  _restartContainer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(containerId) {
200
- var container;
348
+ var container, _t9;
201
349
  return _regenerator().w(function (_context5) {
202
- while (1) switch (_context5.n) {
350
+ while (1) switch (_context5.p = _context5.n) {
203
351
  case 0:
204
352
  container = docker.getContainer(containerId);
205
- _context5.n = 1;
206
- return withTimeout(container.restart(), 30000);
207
- case 1:
353
+ logger.debug("Restarting container %s", containerId);
354
+ _context5.p = 1;
355
+ _context5.n = 2;
356
+ return withTimeout(container.restart(), TIMEOUTS.CONTAINER_OP);
357
+ case 2:
358
+ logger.info("Restarted container %s", containerId);
359
+ _context5.n = 4;
360
+ break;
361
+ case 3:
362
+ _context5.p = 3;
363
+ _t9 = _context5.v;
364
+ logger.error("Failed to restart container %s", containerId, _t9);
365
+ throw _t9;
366
+ case 4:
208
367
  return _context5.a(2);
209
368
  }
210
- }, _callee5);
369
+ }, _callee5, null, [[1, 3]]);
211
370
  }));
212
371
  return _restartContainer.apply(this, arguments);
213
372
  }
@@ -9,6 +9,33 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
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
11
  import { docker } from "../dockerService.js";
12
+ import { logger } from "../../logger.js";
13
+ var DEFAULT_CONTAINER_NAME = "Unknown";
14
+
15
+ /**
16
+ * Produce a safe, display-friendly container name from the raw Docker payload.
17
+ *
18
+ * @param {Object} container Docker container summary as returned by dockerode.
19
+ * @returns {string} Normalized name without leading slashes and with a fallback applied.
20
+ */
21
+ function normalizeContainerName(container) {
22
+ var names = container === null || container === void 0 ? void 0 : container.Names;
23
+ var rawName;
24
+ if (Array.isArray(names) && names.length > 0 && typeof names[0] === "string") {
25
+ rawName = names[0];
26
+ } else if (typeof names === "string" && names.trim().length > 0) {
27
+ rawName = names;
28
+ } else if (typeof (container === null || container === void 0 ? void 0 : container.Name) === "string" && container.Name.trim().length > 0) {
29
+ rawName = container.Name;
30
+ } else {
31
+ rawName = DEFAULT_CONTAINER_NAME;
32
+ }
33
+ if (typeof rawName !== "string") {
34
+ return DEFAULT_CONTAINER_NAME;
35
+ }
36
+ var cleanedName = rawName.replace(/^\/+/, "").trim();
37
+ return cleanedName.length > 0 ? cleanedName : DEFAULT_CONTAINER_NAME;
38
+ }
12
39
 
13
40
  /**
14
41
  * Return a list of containers with normalized fields for the UI.
@@ -19,20 +46,23 @@ export function getContainers() {
19
46
  }
20
47
  function _getContainers() {
21
48
  _getContainers = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
22
- var containers;
49
+ var containers, _t;
23
50
  return _regenerator().w(function (_context) {
24
- while (1) switch (_context.n) {
51
+ while (1) switch (_context.p = _context.n) {
25
52
  case 0:
26
- _context.n = 1;
53
+ logger.debug("Listing containers");
54
+ _context.p = 1;
55
+ _context.n = 2;
27
56
  return docker.listContainers({
28
57
  all: true
29
58
  });
30
- case 1:
59
+ case 2:
31
60
  containers = _context.v;
61
+ logger.debug("Retrieved %d containers", containers.length);
32
62
  return _context.a(2, containers.map(function (container) {
33
63
  return {
34
64
  id: container.Id,
35
- name: (container.Names && container.Names[0] || 'Unknown').replace("/", ""),
65
+ name: normalizeContainerName(container),
36
66
  image: container.Image,
37
67
  state: container.State,
38
68
  status: container.Status,
@@ -45,18 +75,25 @@ function _getContainers() {
45
75
  if (publicPorts.length > 0) {
46
76
  return _toConsumableArray(new Set(publicPorts));
47
77
  }
48
- // If there are no public ports, show private exposed ports
78
+ // Fall back to exposed private ports when there are no bindings.
49
79
  var privatePorts = container.Ports.filter(function (port) {
50
80
  return port.PrivatePort;
51
81
  }).map(function (port) {
52
- return ":".concat(port.PrivatePort);
82
+ return "".concat(port.PrivatePort);
53
83
  });
54
84
  return _toConsumableArray(new Set(privatePorts));
55
85
  }()
56
86
  };
57
87
  }));
88
+ case 3:
89
+ _context.p = 3;
90
+ _t = _context.v;
91
+ logger.error("Failed to list containers", _t);
92
+ throw _t;
93
+ case 4:
94
+ return _context.a(2);
58
95
  }
59
- }, _callee);
96
+ }, _callee, null, [[1, 3]]);
60
97
  }));
61
98
  return _getContainers.apply(this, arguments);
62
99
  }
@@ -1,4 +1,6 @@
1
1
  import { docker } from "../dockerService.js";
2
+ import { safeCall } from "../../safeCall.js";
3
+ import { logger } from "../../logger.js";
2
4
 
3
5
  /**
4
6
  * Return a stream of logs from a container and call callbacks for events.
@@ -9,8 +11,11 @@ import { docker } from "../dockerService.js";
9
11
  * @param {Function} onError - Called on error
10
12
  */
11
13
  export function getLogsStream(containerId, onData, onEnd, onError) {
14
+ // Use shared safeCall util to call optional callbacks safely
15
+
12
16
  try {
13
17
  var container = docker.getContainer(containerId);
18
+ logger.debug("Opening logs stream for container %s", containerId);
14
19
  container.logs({
15
20
  follow: true,
16
21
  stdout: true,
@@ -18,20 +23,25 @@ export function getLogsStream(containerId, onData, onEnd, onError) {
18
23
  tail: 100
19
24
  }, function (err, stream) {
20
25
  if (err) {
21
- onError === null || onError === void 0 || onError(err);
26
+ logger.error("Failed to open logs stream for container %s", containerId, err);
27
+ safeCall(onError, err);
22
28
  return;
23
29
  }
30
+ logger.info("Logs stream established for container %s", containerId);
24
31
  stream.on('data', function (chunk) {
25
- return onData === null || onData === void 0 ? void 0 : onData(chunk.toString());
32
+ return safeCall(onData, chunk.toString());
26
33
  });
27
34
  stream.on('end', function () {
28
- return onEnd === null || onEnd === void 0 ? void 0 : onEnd();
35
+ logger.debug("Logs stream ended for container %s", containerId);
36
+ safeCall(onEnd);
29
37
  });
30
- stream.on('error', function (err) {
31
- return onError === null || onError === void 0 ? void 0 : onError(err);
38
+ stream.on('error', function (streamErr) {
39
+ logger.error("Logs stream error for container %s", containerId, streamErr);
40
+ safeCall(onError, streamErr);
32
41
  });
33
42
  });
34
43
  } catch (err) {
35
- onError === null || onError === void 0 || onError(err);
44
+ logger.error("Unexpected error while opening logs stream for container %s", containerId, err);
45
+ safeCall(onError, err);
36
46
  }
37
47
  }
@@ -3,6 +3,7 @@ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try {
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
5
  import { docker } from "../dockerService.js";
6
+ import { logger } from "../../logger.js";
6
7
 
7
8
  /**
8
9
  * Retrieve a snapshot of container resource usage (CPU, memory, network).
@@ -15,21 +16,37 @@ export function getStats(_x) {
15
16
  }
16
17
  function _getStats() {
17
18
  _getStats = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(containerId) {
18
- var _stream$cpu_stats$cpu;
19
- var container, stream, cpuDelta, systemDelta, numCpus, cpuPercent, memUsage, memLimit, memPercent, rx, tx;
19
+ var container, stream, cpuDelta, systemDelta, numCpus, cpuPercent, memUsage, memLimit, memPercent, rx, tx, snapshot, _t;
20
20
  return _regenerator().w(function (_context) {
21
- while (1) switch (_context.n) {
21
+ while (1) switch (_context.p = _context.n) {
22
22
  case 0:
23
23
  container = docker.getContainer(containerId);
24
- _context.n = 1;
24
+ logger.debug("Fetching stats for container %s", containerId);
25
+ _context.p = 1;
26
+ _context.n = 2;
25
27
  return container.stats({
26
28
  stream: false
27
29
  });
28
- case 1:
30
+ case 2:
29
31
  stream = _context.v;
32
+ _context.n = 4;
33
+ break;
34
+ case 3:
35
+ _context.p = 3;
36
+ _t = _context.v;
37
+ logger.error("Failed to fetch stats for container %s", containerId, _t);
38
+ throw _t;
39
+ case 4:
30
40
  cpuDelta = stream.cpu_stats.cpu_usage.total_usage - stream.precpu_stats.cpu_usage.total_usage;
31
41
  systemDelta = stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage; // Get number of CPUs for proper normalization
32
- numCpus = stream.cpu_stats.online_cpus || ((_stream$cpu_stats$cpu = stream.cpu_stats.cpu_usage.percpu_usage) === null || _stream$cpu_stats$cpu === void 0 ? void 0 : _stream$cpu_stats$cpu.length) || 1; // Calculate normalized CPU percentage
42
+ numCpus = 1;
43
+ if (typeof stream.cpu_stats.online_cpus === 'number' && stream.cpu_stats.online_cpus > 0) {
44
+ numCpus = stream.cpu_stats.online_cpus;
45
+ } else if (stream.cpu_stats.cpu_usage && Array.isArray(stream.cpu_stats.cpu_usage.percpu_usage) && stream.cpu_stats.cpu_usage.percpu_usage.length > 0) {
46
+ numCpus = stream.cpu_stats.cpu_usage.percpu_usage.length;
47
+ }
48
+
49
+ // Calculate normalized CPU percentage
33
50
  cpuPercent = systemDelta > 0 ? cpuDelta / systemDelta * numCpus * 100 : 0;
34
51
  memUsage = stream.memory_stats.usage || 0;
35
52
  memLimit = stream.memory_stats.limit || 1;
@@ -44,16 +61,18 @@ function _getStats() {
44
61
  }).reduce(function (a, b) {
45
62
  return a + b;
46
63
  }, 0) : 0;
47
- return _context.a(2, {
64
+ snapshot = {
48
65
  cpuPercent: cpuPercent.toFixed(1),
49
66
  memPercent: memPercent.toFixed(1),
50
67
  netIO: {
51
68
  rx: rx,
52
69
  tx: tx
53
70
  }
54
- });
71
+ };
72
+ logger.debug("Stats fetched for container %s", containerId, snapshot);
73
+ return _context.a(2, snapshot);
55
74
  }
56
- }, _callee);
75
+ }, _callee, null, [[1, 3]]);
57
76
  }));
58
77
  return _getStats.apply(this, arguments);
59
78
  }
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "child_process";
2
+ import { EXIT_DELAY } from "./constants.js";
2
3
 
3
4
  /**
4
5
  * Show an exit message using provided setters, clear the terminal and exit after a delay.
@@ -18,7 +19,7 @@ export function exitWithMessage(_ref) {
18
19
  _ref$color = _ref.color,
19
20
  color = _ref$color === void 0 ? "yellow" : _ref$color,
20
21
  _ref$delay = _ref.delay,
21
- delay = _ref$delay === void 0 ? 1500 : _ref$delay;
22
+ delay = _ref$delay === void 0 ? EXIT_DELAY : _ref$delay;
22
23
  setMessage(message);
23
24
  setMessageColor(color);
24
25
  setTimeout(function () {