cdd-cli 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +29 -5
- package/README.md +29 -5
- package/dist/components/UsageMenu.js +1 -1
- package/dist/helpers/appState.js +26 -0
- package/dist/helpers/dockerService/serviceComponents/containerExec.js +211 -0
- package/dist/hooks/creation/useContainerCreation.js +25 -6
- package/dist/hooks/useContainerCommandRouter.js +12 -2
- package/dist/hooks/useControls.js +5 -0
- package/dist/hooks/useShellMode.js +101 -0
- package/dist/index.js +3 -1
- package/package.json +4 -1
- package/.atl/skill-registry.md +0 -30
- package/.editorconfig +0 -12
- package/.eslintrc.json +0 -30
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -14
- package/.github/workflows/ci.yml +0 -32
- package/.github/workflows/docs.yml +0 -42
- package/.prettierignore +0 -4
- package/.prettierrc +0 -8
- package/.~lock.ROADMAP.md# +0 -1
- package/CHANGELOG.md +0 -216
- package/CODE_OF_CONDUCT.md +0 -12
- package/CONTRIBUTING.md +0 -35
- package/ROADMAP.md +0 -647
- package/SECURITY.md +0 -5
- package/__mocks__/ink.cjs +0 -17
- package/_config.yml +0 -13
- package/babel.config.cjs +0 -23
- package/coverage/clover.xml +0 -575
- package/coverage/coverage-final.json +0 -17
- package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +0 -400
- package/coverage/lcov-report/ControlsHUD.jsx.html +0 -256
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +0 -394
- package/coverage/lcov-report/components/PromptField.jsx.html +0 -229
- package/coverage/lcov-report/components/SuggestionPanel.jsx.html +0 -268
- package/coverage/lcov-report/components/index.html +0 -146
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/helpers/constants.js.html +0 -352
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +0 -244
- package/coverage/lcov-report/helpers/dockerHubService.js.html +0 -310
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +0 -931
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +0 -307
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +0 -131
- package/coverage/lcov-report/helpers/imageNameUtils.js.html +0 -181
- package/coverage/lcov-report/helpers/index.html +0 -206
- package/coverage/lcov-report/helpers/logger.js.html +0 -424
- package/coverage/lcov-report/helpers/safeCall.js.html +0 -139
- package/coverage/lcov-report/helpers/validationHelpers.js.html +0 -346
- package/coverage/lcov-report/hooks/creation/index.html +0 -146
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +0 -313
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +0 -982
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +0 -238
- package/coverage/lcov-report/hooks/debug/index.html +0 -116
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +0 -187
- package/coverage/lcov-report/hooks/index.html +0 -161
- package/coverage/lcov-report/hooks/navigation/index.html +0 -116
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +0 -169
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +0 -454
- package/coverage/lcov-report/hooks/useControls.js.html +0 -868
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +0 -202
- package/coverage/lcov-report/hooks/useExitHandler.js.html +0 -292
- package/coverage/lcov-report/index.html +0 -176
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -1163
- package/fix-imports.cjs +0 -54
- package/jest.config.cjs +0 -14
- package/src/App.jsx +0 -102
- package/src/components/ContainerCreationPrompt.jsx +0 -107
- package/src/components/ContainerList.jsx +0 -32
- package/src/components/ContainerRow.jsx +0 -84
- package/src/components/ContainerSection.jsx +0 -24
- package/src/components/ControlsHUD.jsx +0 -66
- package/src/components/Footer.jsx +0 -15
- package/src/components/Header.jsx +0 -41
- package/src/components/LogViewer.jsx +0 -36
- package/src/components/MessageFeedback.jsx +0 -25
- package/src/components/PromptField.jsx +0 -48
- package/src/components/StatsBar.jsx +0 -70
- package/src/components/SuggestionPanel.jsx +0 -61
- package/src/components/UsageMenu.jsx +0 -52
- package/src/helpers/appInfo.js +0 -23
- package/src/helpers/constants.js +0 -89
- package/src/helpers/containerOptionsBuilder.js +0 -53
- package/src/helpers/dockerHubService.js +0 -75
- package/src/helpers/dockerService/dockerService.js +0 -28
- package/src/helpers/dockerService/serviceComponents/containerActions.js +0 -282
- package/src/helpers/dockerService/serviceComponents/containerList.js +0 -74
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +0 -60
- package/src/helpers/dockerService/serviceComponents/containerStats.js +0 -69
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +0 -32
- package/src/helpers/exitWithMessage.js +0 -32
- package/src/helpers/imageNameUtils.js +0 -32
- package/src/helpers/logger.js +0 -112
- package/src/helpers/safeCall.js +0 -18
- package/src/helpers/validationHelpers.js +0 -87
- package/src/hooks/creation/useContainerActions.js +0 -89
- package/src/hooks/creation/useContainerCreation.js +0 -377
- package/src/hooks/creation/useLogsViewer.js +0 -51
- package/src/hooks/debug/useDebugLogs.js +0 -34
- package/src/hooks/navigation/useContainerSelection.js +0 -28
- package/src/hooks/useContainerCommandRouter.js +0 -123
- package/src/hooks/useContainerStats.js +0 -44
- package/src/hooks/useContainers.js +0 -26
- package/src/hooks/useControls.js +0 -263
- package/src/hooks/useEraseConfirmation.js +0 -39
- package/src/hooks/useExitHandler.js +0 -69
- package/src/index.js +0 -20
- package/test/ContainerCreationPrompt.dom.test.js +0 -313
- package/test/ControlsHUD.dom.test.js +0 -125
- package/test/SuggestionPanel.dom.test.js +0 -83
- package/test/constants.test.js +0 -41
- package/test/containerActions.test.js +0 -229
- package/test/containerList.test.js +0 -79
- package/test/containerOptionsBuilder.test.js +0 -36
- package/test/dockerHubService.test.js +0 -213
- package/test/jest.setup.js +0 -7
- package/test/logger.test.js +0 -58
- package/test/resolveImageTag.test.js +0 -26
- package/test/safeCall.test.js +0 -25
- package/test/useContainerCreation.dom.test.js +0 -567
- package/test/useContainerCreationHub.dom.test.js +0 -184
- package/test/useControls.dom.test.js +0 -519
- package/test/validationHelpers.test.js +0 -111
package/README.es.md
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
<a href="https://github.com/caertos/cdd/actions/workflows/ci.yml">
|
|
7
7
|
<img src="https://github.com/caertos/cdd/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status" />
|
|
8
8
|
</a>
|
|
9
|
-
<a href="https://caertos.github.io/cdd/">
|
|
10
|
-
<img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Docs" />
|
|
11
|
-
</a>
|
|
12
9
|
<a href="https://deepwiki.com/Caertos/cdd"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
13
10
|
</p>
|
|
14
11
|
|
|
@@ -16,9 +13,21 @@
|
|
|
16
13
|
|
|
17
14
|
---
|
|
18
15
|
|
|
19
|
-
## 🎉 Novedades en
|
|
16
|
+
## 🎉 Novedades en v4.1
|
|
17
|
+
|
|
18
|
+
**Abrí un shell dentro de cualquier contenedor con una sola tecla.**
|
|
19
|
+
|
|
20
|
+
Presiona `S` y CDD te lleva a un shell interactivo completo (`bash` o `sh`) dentro del contenedor seleccionado — sin necesidad de escribir `docker exec`.
|
|
21
|
+
|
|
22
|
+
- **Shell auto-detectado** — CDD sondea el contenedor y elige `bash` o `sh` automáticamente
|
|
23
|
+
- **Soporte de terminal completo** — ejecutá `psql`, `python3`, `node`, `redis-cli`, o cualquier comando dentro del contenedor
|
|
24
|
+
- **Salida limpia** — escribí `exit` o presioná `Ctrl+D` para volver al dashboard
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Versiones anteriores
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
### v3.2 — Asistente de creación interactivo
|
|
22
31
|
|
|
23
32
|
Olvídate de los flags de `docker run`, las variables de entorno olvidadas y los tags `:latest` que fallan en silencio. Presiona `C` y CDD te guía para crear un contenedor en segundos:
|
|
24
33
|
|
|
@@ -84,6 +93,7 @@ Usa `↑` / `↓` para navegar por los contenedores. Atajos de teclado disponibl
|
|
|
84
93
|
| `R` | Reiniciar el contenedor seleccionado |
|
|
85
94
|
| `C` | Abrir el asistente de creación |
|
|
86
95
|
| `L` | Ver logs del contenedor seleccionado en tiempo real |
|
|
96
|
+
| `S` | Abrir shell interactivo dentro del contenedor seleccionado |
|
|
87
97
|
| `E` | Eliminar el contenedor seleccionado — requiere confirmación |
|
|
88
98
|
| `D` | Activar/desactivar panel de debug en vivo |
|
|
89
99
|
| `Q` | Salir |
|
|
@@ -126,6 +136,20 @@ Presiona `Enter` en una línea vacía para terminar y crear el contenedor.
|
|
|
126
136
|
|
|
127
137
|
---
|
|
128
138
|
|
|
139
|
+
## Shell Interactivo
|
|
140
|
+
|
|
141
|
+
Presiona `S` desde el dashboard con un contenedor en ejecución seleccionado. CDD hará lo siguiente:
|
|
142
|
+
|
|
143
|
+
1. Detectará el shell disponible dentro del contenedor (`bash` o `sh`)
|
|
144
|
+
2. Abrirá una sesión de terminal interactiva completa
|
|
145
|
+
3. Te dejará en el shell del contenedor
|
|
146
|
+
|
|
147
|
+
Desde ahí podés ejecutar cualquier comando — `psql` para PostgreSQL, `python3` para Python, `node` para Node.js, `redis-cli` para Redis, etc.
|
|
148
|
+
|
|
149
|
+
Escribí `exit` o presioná `Ctrl+D` para salir del shell y volver al dashboard de CDD.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
129
153
|
## Requisitos
|
|
130
154
|
|
|
131
155
|
- Node.js >= 18
|
package/README.md
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
<a href="https://github.com/caertos/cdd/actions/workflows/ci.yml">
|
|
7
7
|
<img src="https://github.com/caertos/cdd/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status" />
|
|
8
8
|
</a>
|
|
9
|
-
<a href="https://caertos.github.io/cdd/">
|
|
10
|
-
<img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Docs" />
|
|
11
|
-
</a>
|
|
12
9
|
<a href="https://deepwiki.com/Caertos/cdd"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
13
10
|
</p>
|
|
14
11
|
|
|
@@ -16,9 +13,21 @@
|
|
|
16
13
|
|
|
17
14
|
---
|
|
18
15
|
|
|
19
|
-
## 🎉 What's new in
|
|
16
|
+
## 🎉 What's new in v4.1
|
|
17
|
+
|
|
18
|
+
**Open a shell inside any container with a single keystroke.**
|
|
19
|
+
|
|
20
|
+
Press `S` and CDD drops you into a full interactive shell (`bash` or `sh`) inside the selected container — no `docker exec` typing needed.
|
|
21
|
+
|
|
22
|
+
- **Auto-detected shell** — CDD probes the container and picks `bash` or `sh` automatically
|
|
23
|
+
- **Full terminal support** — run `psql`, `python3`, `node`, `redis-cli`, or any command inside the container
|
|
24
|
+
- **Clean exit** — type `exit` or press `Ctrl+D` to return to the dashboard
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Previous releases
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
### v3.2 — Interactive creation wizard
|
|
22
31
|
|
|
23
32
|
Forget `docker run` flags, forgotten env vars, and broken `:latest` tags. Press `C` and CDD guides you through creating a container in seconds:
|
|
24
33
|
|
|
@@ -84,6 +93,7 @@ Use `↑` / `↓` to navigate containers. Available keyboard shortcuts:
|
|
|
84
93
|
| `R` | Restart selected container |
|
|
85
94
|
| `C` | Open creation wizard |
|
|
86
95
|
| `L` | Stream logs for selected container |
|
|
96
|
+
| `S` | Open interactive shell inside selected container |
|
|
87
97
|
| `E` | Erase (remove) selected container — confirmation required |
|
|
88
98
|
| `D` | Toggle live debug panel |
|
|
89
99
|
| `Q` | Quit |
|
|
@@ -126,6 +136,20 @@ Press `Enter` on an empty line to finish and create the container.
|
|
|
126
136
|
|
|
127
137
|
---
|
|
128
138
|
|
|
139
|
+
## Interactive Shell
|
|
140
|
+
|
|
141
|
+
Press `S` from the dashboard while a running container is selected. CDD will:
|
|
142
|
+
|
|
143
|
+
1. Detect the available shell inside the container (`bash` or `sh`)
|
|
144
|
+
2. Open a full interactive terminal session
|
|
145
|
+
3. Drop you into the container's shell
|
|
146
|
+
|
|
147
|
+
From there you can run any command — `psql` for PostgreSQL, `python3` for Python, `node` for Node.js, `redis-cli` for Redis, etc.
|
|
148
|
+
|
|
149
|
+
Type `exit` or press `Ctrl+D` to leave the shell and return to the CDD dashboard.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
129
153
|
## Requirements
|
|
130
154
|
|
|
131
155
|
- Node.js >= 18
|
|
@@ -1,6 +1,6 @@
|
|
|
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'];
|
|
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', 'S to open shell in selected container', 'D to toggle debug log panel', 'E to remove selected container', 'Q to quit'];
|
|
4
4
|
var GRID_COLUMNS = 3;
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared reference to the Ink render instance.
|
|
3
|
+
* Set once in index.js after render(<App />) and read by useShellMode
|
|
4
|
+
* to unmount/remount the UI around interactive shell sessions.
|
|
5
|
+
*
|
|
6
|
+
* @module appState
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {import('ink').RenderReturnType | null} */
|
|
10
|
+
var inkApp = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Store the Ink render return value.
|
|
14
|
+
* @param {import('ink').RenderReturnType} app
|
|
15
|
+
*/
|
|
16
|
+
export function setInkApp(app) {
|
|
17
|
+
inkApp = app;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the Ink render return value.
|
|
22
|
+
* @returns {import('ink').RenderReturnType | null}
|
|
23
|
+
*/
|
|
24
|
+
export function getInkApp() {
|
|
25
|
+
return inkApp;
|
|
26
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { docker } from '../dockerService.js';
|
|
7
|
+
import { logger } from '../../logger.js';
|
|
8
|
+
import { TIMEOUTS } from '../../constants.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect the default shell available inside a running container.
|
|
12
|
+
* Tries `bash` first, falls back to `sh`.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} containerId - Docker container id
|
|
15
|
+
* @returns {Promise<string>} The shell command (e.g. 'bash' or 'sh')
|
|
16
|
+
*/
|
|
17
|
+
export function detectShell(_x) {
|
|
18
|
+
return _detectShell.apply(this, arguments);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Spawn an interactive shell inside a running container using `docker exec -it`.
|
|
23
|
+
* Returns a promise that resolves when the user exits the shell.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} containerId - Docker container id
|
|
26
|
+
* @param {Object} [options]
|
|
27
|
+
* @param {string} [options.shell] - Shell to use (auto-detected if omitted)
|
|
28
|
+
* @param {string} [options.workingDir] - Working directory inside the container
|
|
29
|
+
* @returns {Promise<{code: number|null, signal: string|null}>} Exit info
|
|
30
|
+
*/
|
|
31
|
+
function _detectShell() {
|
|
32
|
+
_detectShell = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(containerId) {
|
|
33
|
+
var container, tryShell, hasBash, _t;
|
|
34
|
+
return _regenerator().w(function (_context) {
|
|
35
|
+
while (1) switch (_context.p = _context.n) {
|
|
36
|
+
case 0:
|
|
37
|
+
container = docker.getContainer(containerId);
|
|
38
|
+
tryShell = function tryShell(shell) {
|
|
39
|
+
return new Promise(function (resolve) {
|
|
40
|
+
container.exec({
|
|
41
|
+
Cmd: ['which', shell],
|
|
42
|
+
AttachStdout: true,
|
|
43
|
+
AttachStderr: false,
|
|
44
|
+
Tty: false
|
|
45
|
+
}, function (err, exec) {
|
|
46
|
+
if (err) {
|
|
47
|
+
resolve(false);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
exec.start({
|
|
51
|
+
Tty: false
|
|
52
|
+
}, function (startErr, stream) {
|
|
53
|
+
if (startErr) {
|
|
54
|
+
resolve(false);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
var output = '';
|
|
58
|
+
stream.on('data', function (chunk) {
|
|
59
|
+
output += chunk.toString();
|
|
60
|
+
});
|
|
61
|
+
stream.on('end', function () {
|
|
62
|
+
resolve(output.trim().length > 0);
|
|
63
|
+
});
|
|
64
|
+
stream.on('error', function () {
|
|
65
|
+
return resolve(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
_context.p = 1;
|
|
72
|
+
_context.n = 2;
|
|
73
|
+
return Promise.race([tryShell('bash'), new Promise(function (r) {
|
|
74
|
+
return setTimeout(function () {
|
|
75
|
+
return r(false);
|
|
76
|
+
}, TIMEOUTS.CONTAINER_OP);
|
|
77
|
+
})]);
|
|
78
|
+
case 2:
|
|
79
|
+
hasBash = _context.v;
|
|
80
|
+
if (!hasBash) {
|
|
81
|
+
_context.n = 3;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
logger.debug('Detected bash in container %s', containerId);
|
|
85
|
+
return _context.a(2, 'bash');
|
|
86
|
+
case 3:
|
|
87
|
+
_context.n = 5;
|
|
88
|
+
break;
|
|
89
|
+
case 4:
|
|
90
|
+
_context.p = 4;
|
|
91
|
+
_t = _context.v;
|
|
92
|
+
case 5:
|
|
93
|
+
logger.debug('Falling back to sh in container %s', containerId);
|
|
94
|
+
return _context.a(2, 'sh');
|
|
95
|
+
}
|
|
96
|
+
}, _callee, null, [[1, 4]]);
|
|
97
|
+
}));
|
|
98
|
+
return _detectShell.apply(this, arguments);
|
|
99
|
+
}
|
|
100
|
+
export function execInteractive(_x2) {
|
|
101
|
+
return _execInteractive.apply(this, arguments);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Execute a non-interactive command inside a running container.
|
|
106
|
+
* Returns stdout and stderr as strings.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} containerId - Docker container id
|
|
109
|
+
* @param {string[]} cmd - Command to execute (e.g. ['ls', '-la'])
|
|
110
|
+
* @param {Object} [options]
|
|
111
|
+
* @param {number} [options.timeout] - Timeout in ms
|
|
112
|
+
* @returns {Promise<{stdout: string, stderr: string, exitCode: number}>}
|
|
113
|
+
*/
|
|
114
|
+
function _execInteractive() {
|
|
115
|
+
_execInteractive = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(containerId) {
|
|
116
|
+
var options,
|
|
117
|
+
shell,
|
|
118
|
+
args,
|
|
119
|
+
_args2 = arguments,
|
|
120
|
+
_t2;
|
|
121
|
+
return _regenerator().w(function (_context2) {
|
|
122
|
+
while (1) switch (_context2.n) {
|
|
123
|
+
case 0:
|
|
124
|
+
options = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
|
|
125
|
+
_t2 = options.shell;
|
|
126
|
+
if (_t2) {
|
|
127
|
+
_context2.n = 2;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
_context2.n = 1;
|
|
131
|
+
return detectShell(containerId);
|
|
132
|
+
case 1:
|
|
133
|
+
_t2 = _context2.v;
|
|
134
|
+
case 2:
|
|
135
|
+
shell = _t2;
|
|
136
|
+
args = ['exec', '-it', containerId, shell];
|
|
137
|
+
if (options.workingDir) {
|
|
138
|
+
args.splice(3, 0, '-w', options.workingDir);
|
|
139
|
+
}
|
|
140
|
+
logger.info('Opening interactive shell in container %s (shell=%s)', containerId, shell);
|
|
141
|
+
return _context2.a(2, new Promise(function (resolve, reject) {
|
|
142
|
+
var child = spawn('docker', args, {
|
|
143
|
+
stdio: 'inherit',
|
|
144
|
+
shell: false
|
|
145
|
+
});
|
|
146
|
+
child.on('error', function (err) {
|
|
147
|
+
logger.error('Failed to spawn docker exec', err);
|
|
148
|
+
reject(new Error("Failed to open shell: ".concat(err.message)));
|
|
149
|
+
});
|
|
150
|
+
child.on('close', function (code, signal) {
|
|
151
|
+
logger.info('Interactive shell exited (code=%s, signal=%s)', code, signal);
|
|
152
|
+
resolve({
|
|
153
|
+
code: code,
|
|
154
|
+
signal: signal
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
}, _callee2);
|
|
160
|
+
}));
|
|
161
|
+
return _execInteractive.apply(this, arguments);
|
|
162
|
+
}
|
|
163
|
+
export function execCommand(containerId, cmd) {
|
|
164
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
165
|
+
return new Promise(function (resolve, reject) {
|
|
166
|
+
var container = docker.getContainer(containerId);
|
|
167
|
+
container.exec({
|
|
168
|
+
Cmd: cmd,
|
|
169
|
+
AttachStdout: true,
|
|
170
|
+
AttachStderr: true,
|
|
171
|
+
Tty: false
|
|
172
|
+
}, function (err, exec) {
|
|
173
|
+
if (err) {
|
|
174
|
+
return reject(new Error("Exec create failed: ".concat(err.message)));
|
|
175
|
+
}
|
|
176
|
+
exec.start({
|
|
177
|
+
Tty: false
|
|
178
|
+
}, function (startErr, stream) {
|
|
179
|
+
if (startErr) {
|
|
180
|
+
return reject(new Error("Exec start failed: ".concat(startErr.message)));
|
|
181
|
+
}
|
|
182
|
+
var stdout = '';
|
|
183
|
+
var stderr = '';
|
|
184
|
+
stream.on('data', function (chunk) {
|
|
185
|
+
var str = chunk.toString();
|
|
186
|
+
// Docker multiplexed streams use 8-byte headers.
|
|
187
|
+
// For Tty=false, output is multiplexed; for simplicity we accumulate.
|
|
188
|
+
stdout += str;
|
|
189
|
+
});
|
|
190
|
+
stream.on('end', function () {
|
|
191
|
+
exec.inspect(function (inspectErr, info) {
|
|
192
|
+
var _info$ExitCode;
|
|
193
|
+
resolve({
|
|
194
|
+
stdout: stdout.trim(),
|
|
195
|
+
stderr: stderr.trim(),
|
|
196
|
+
exitCode: (_info$ExitCode = info === null || info === void 0 ? void 0 : info.ExitCode) !== null && _info$ExitCode !== void 0 ? _info$ExitCode : -1
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
stream.on('error', function (streamErr) {
|
|
201
|
+
reject(new Error("Stream error: ".concat(streamErr.message)));
|
|
202
|
+
});
|
|
203
|
+
if (options.timeout) {
|
|
204
|
+
setTimeout(function () {
|
|
205
|
+
reject(new Error('Command timed out'));
|
|
206
|
+
}, options.timeout);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -200,6 +200,25 @@ export function useContainerCreation(_ref) {
|
|
|
200
200
|
|
|
201
201
|
// Timer for auto-clearing messages
|
|
202
202
|
var messageTimerRef = useRef(null);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Sets a permanent message (e.g. step instructions). No auto-clear.
|
|
206
|
+
*/
|
|
207
|
+
function setStepMessage(msg) {
|
|
208
|
+
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'yellow';
|
|
209
|
+
clearTimeout(messageTimerRef.current);
|
|
210
|
+
dispatch({
|
|
211
|
+
type: 'SET',
|
|
212
|
+
payload: {
|
|
213
|
+
message: msg,
|
|
214
|
+
messageColor: color
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sets a transient feedback message (e.g. validation errors). Auto-clears after `ms`.
|
|
221
|
+
*/
|
|
203
222
|
function setTimedMessage(msg) {
|
|
204
223
|
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'yellow';
|
|
205
224
|
var ms = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4000;
|
|
@@ -395,7 +414,7 @@ export function useContainerCreation(_ref) {
|
|
|
395
414
|
step: 1
|
|
396
415
|
}
|
|
397
416
|
});
|
|
398
|
-
|
|
417
|
+
setStepMessage('Optional: Enter container name or leave empty and press Enter', 'yellow');
|
|
399
418
|
return;
|
|
400
419
|
}
|
|
401
420
|
if (step === 1) {
|
|
@@ -405,7 +424,7 @@ export function useContainerCreation(_ref) {
|
|
|
405
424
|
step: 2
|
|
406
425
|
}
|
|
407
426
|
});
|
|
408
|
-
|
|
427
|
+
setStepMessage('Optional: Enter ports (format 8080:80,443:443) or leave empty and press Enter', 'yellow');
|
|
409
428
|
return;
|
|
410
429
|
}
|
|
411
430
|
if (step === 2) {
|
|
@@ -428,13 +447,13 @@ export function useContainerCreation(_ref) {
|
|
|
428
447
|
var profile = imageProfiles[_baseName];
|
|
429
448
|
if (profile && profile.requiredEnv && profile.requiredEnv.length) {
|
|
430
449
|
var suggestedPart = profile.suggestedEnv && profile.suggestedEnv.length ? " | Suggested: ".concat(profile.suggestedEnv.join(', ')) : '';
|
|
431
|
-
|
|
450
|
+
setStepMessage("Required env vars for ".concat(_baseName, ": ").concat(profile.requiredEnv.join(', '), ". Enter as VAR=val,VAR2=val2").concat(suggestedPart), 'yellow');
|
|
432
451
|
} else if (profile && profile.suggestedEnv && profile.suggestedEnv.length) {
|
|
433
|
-
|
|
452
|
+
setStepMessage("Suggested env vars for ".concat(_baseName, ": ").concat(profile.suggestedEnv.join(', '), ". Enter as VAR=val,VAR2=val2 or leave empty."), 'yellow');
|
|
434
453
|
} else if (isDb) {
|
|
435
|
-
|
|
454
|
+
setStepMessage('Warning: This image usually requires environment variables (e.g. MYSQL_ROOT_PASSWORD=my-secret-pw for MySQL, POSTGRES_PASSWORD=yourpassword for Postgres). Enter them as VAR=val,VAR2=val2 or leave empty and press Enter.', 'yellow');
|
|
436
455
|
} else {
|
|
437
|
-
|
|
456
|
+
setStepMessage('Optional: Enter environment variables (format VAR1=val1,VAR2=val2) or leave empty and press Enter', 'yellow');
|
|
438
457
|
}
|
|
439
458
|
return;
|
|
440
459
|
}
|
|
@@ -16,6 +16,7 @@ import { useCallback } from 'react';
|
|
|
16
16
|
* l — open logs viewer and start log stream
|
|
17
17
|
* c — open container creation wizard (reset + activate)
|
|
18
18
|
* d — toggle debug log panel
|
|
19
|
+
* s — open interactive shell in container
|
|
19
20
|
*
|
|
20
21
|
* @param {Object} params
|
|
21
22
|
* @param {Object} params.actions - API from useContainerActions
|
|
@@ -27,6 +28,7 @@ import { useCallback } from 'react';
|
|
|
27
28
|
* @param {Function} params.onStartErase - Callback: activates confirmErase state
|
|
28
29
|
* @param {Function} params.onToggleDebug - Callback: toggles showDebugLogs state
|
|
29
30
|
* @param {Function} params.onStartCreate - Callback: sets creatingContainer = true
|
|
31
|
+
* @param {Function} params.onOpenShell - Callback: opens interactive shell
|
|
30
32
|
* @returns {{ handleDockerCommands: Function }}
|
|
31
33
|
*/
|
|
32
34
|
export function useContainerCommandRouter(_ref) {
|
|
@@ -38,7 +40,8 @@ export function useContainerCommandRouter(_ref) {
|
|
|
38
40
|
startLogsStream = _ref.startLogsStream,
|
|
39
41
|
onStartErase = _ref.onStartErase,
|
|
40
42
|
onToggleDebug = _ref.onToggleDebug,
|
|
41
|
-
onStartCreate = _ref.onStartCreate
|
|
43
|
+
onStartCreate = _ref.onStartCreate,
|
|
44
|
+
onOpenShell = _ref.onOpenShell;
|
|
42
45
|
var handleDockerCommands = useCallback(function (input) {
|
|
43
46
|
var container = containers[selected];
|
|
44
47
|
if (input === 'i') {
|
|
@@ -143,8 +146,15 @@ export function useContainerCommandRouter(_ref) {
|
|
|
143
146
|
onToggleDebug();
|
|
144
147
|
return true;
|
|
145
148
|
}
|
|
149
|
+
if (input === 's') {
|
|
150
|
+
if (!container) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
onOpenShell(container);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
146
156
|
return false;
|
|
147
|
-
}, [actions, containers, creation, logsViewer, onStartCreate, onStartErase, onToggleDebug, selected, startLogsStream]);
|
|
157
|
+
}, [actions, containers, creation, logsViewer, onStartCreate, onStartErase, onToggleDebug, onOpenShell, selected, startLogsStream]);
|
|
148
158
|
return {
|
|
149
159
|
handleDockerCommands: handleDockerCommands
|
|
150
160
|
};
|
|
@@ -22,6 +22,7 @@ import { useDebugLogs } from './debug/useDebugLogs.js';
|
|
|
22
22
|
import { useEraseConfirmation } from './useEraseConfirmation.js';
|
|
23
23
|
import { useExitHandler } from './useExitHandler.js';
|
|
24
24
|
import { useContainerCommandRouter } from './useContainerCommandRouter.js';
|
|
25
|
+
import { useShellMode } from './useShellMode.js';
|
|
25
26
|
import { getLogsStream } from '../helpers/dockerService/serviceComponents/containerLogs.js';
|
|
26
27
|
import { createContainer as svcCreateContainer } from '../helpers/dockerService/serviceComponents/containerActions.js';
|
|
27
28
|
import { buildContainerOptions } from '../helpers/containerOptionsBuilder.js';
|
|
@@ -48,6 +49,7 @@ export function useControls() {
|
|
|
48
49
|
var actions = useContainerActions({
|
|
49
50
|
containers: containers
|
|
50
51
|
});
|
|
52
|
+
var shellMode = useShellMode();
|
|
51
53
|
var creation = useContainerCreation({
|
|
52
54
|
onCreate: function () {
|
|
53
55
|
var _onCreate = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(_ref) {
|
|
@@ -176,6 +178,9 @@ export function useControls() {
|
|
|
176
178
|
},
|
|
177
179
|
onStartCreate: function onStartCreate() {
|
|
178
180
|
return setCreatingContainer(true);
|
|
181
|
+
},
|
|
182
|
+
onOpenShell: function onOpenShell(container) {
|
|
183
|
+
return shellMode.openShell(container);
|
|
179
184
|
}
|
|
180
185
|
});
|
|
181
186
|
var exitHandler = useExitHandler({
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render } from 'ink';
|
|
7
|
+
import { execInteractive, detectShell } from '../helpers/dockerService/serviceComponents/containerExec.js';
|
|
8
|
+
import { getInkApp, setInkApp } from '../helpers/appState.js';
|
|
9
|
+
import App from '../App.js';
|
|
10
|
+
import { logger } from '../helpers/logger.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Open an interactive shell inside a Docker container.
|
|
14
|
+
* Unmounts Ink, spawns `docker exec -it`, and re-renders the app on exit.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} container - Container object from Docker
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
function openShell(_x) {
|
|
20
|
+
return _openShell.apply(this, arguments);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Hook that provides the shell mode API.
|
|
24
|
+
* In practice, the heavy lifting (unmount/spawn/remount) is handled by
|
|
25
|
+
* `openShell` outside the React tree. This hook just exposes a stable
|
|
26
|
+
* reference for useControls.
|
|
27
|
+
*
|
|
28
|
+
* @returns {{ openShell: Function }}
|
|
29
|
+
*/
|
|
30
|
+
function _openShell() {
|
|
31
|
+
_openShell = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(container) {
|
|
32
|
+
var isRunning, containerId, containerName, inkApp, shell, newApp, _t;
|
|
33
|
+
return _regenerator().w(function (_context) {
|
|
34
|
+
while (1) switch (_context.p = _context.n) {
|
|
35
|
+
case 0:
|
|
36
|
+
if (container) {
|
|
37
|
+
_context.n = 1;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
return _context.a(2);
|
|
41
|
+
case 1:
|
|
42
|
+
isRunning = container.state === 'running' || container.status === 'running';
|
|
43
|
+
if (isRunning) {
|
|
44
|
+
_context.n = 2;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
logger.warn('Cannot open shell: container %s is not running', container.name);
|
|
48
|
+
return _context.a(2);
|
|
49
|
+
case 2:
|
|
50
|
+
containerId = container.id;
|
|
51
|
+
containerName = container.name || containerId.slice(0, 12); // Unmount Ink to release the terminal
|
|
52
|
+
inkApp = getInkApp();
|
|
53
|
+
if (inkApp && typeof inkApp.unmount === 'function') {
|
|
54
|
+
inkApp.unmount();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Clear the screen for a clean shell experience
|
|
58
|
+
if (process.stdout && process.stdout.isTTY) {
|
|
59
|
+
process.stdout.write("\x1Bc");
|
|
60
|
+
}
|
|
61
|
+
_context.p = 3;
|
|
62
|
+
_context.n = 4;
|
|
63
|
+
return detectShell(containerId);
|
|
64
|
+
case 4:
|
|
65
|
+
shell = _context.v;
|
|
66
|
+
process.stdout.write("\x1B[36m[shell]\x1B[0m Opening ".concat(shell, " in ").concat(containerName, " (").concat(containerId.slice(0, 12), ")...\r\n"));
|
|
67
|
+
process.stdout.write('\x1b[33mType "exit" or press Ctrl+D to return to CDD.\x1b[0m\r\n\r\n');
|
|
68
|
+
_context.n = 5;
|
|
69
|
+
return execInteractive(containerId, {
|
|
70
|
+
shell: shell
|
|
71
|
+
});
|
|
72
|
+
case 5:
|
|
73
|
+
_context.n = 7;
|
|
74
|
+
break;
|
|
75
|
+
case 6:
|
|
76
|
+
_context.p = 6;
|
|
77
|
+
_t = _context.v;
|
|
78
|
+
process.stdout.write("\x1B[31m[shell] Error: ".concat(_t.message, "\x1B[0m\r\n"));
|
|
79
|
+
logger.error('Shell mode error', _t);
|
|
80
|
+
case 7:
|
|
81
|
+
_context.n = 8;
|
|
82
|
+
return new Promise(function (r) {
|
|
83
|
+
return setTimeout(r, 100);
|
|
84
|
+
});
|
|
85
|
+
case 8:
|
|
86
|
+
// Re-render the app
|
|
87
|
+
console.clear();
|
|
88
|
+
newApp = render(/*#__PURE__*/React.createElement(App, null));
|
|
89
|
+
setInkApp(newApp);
|
|
90
|
+
case 9:
|
|
91
|
+
return _context.a(2);
|
|
92
|
+
}
|
|
93
|
+
}, _callee, null, [[3, 6]]);
|
|
94
|
+
}));
|
|
95
|
+
return _openShell.apply(this, arguments);
|
|
96
|
+
}
|
|
97
|
+
export function useShellMode() {
|
|
98
|
+
return {
|
|
99
|
+
openShell: openShell
|
|
100
|
+
};
|
|
101
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import React from 'react';
|
|
|
11
11
|
import { render } from 'ink';
|
|
12
12
|
import App from './App.js';
|
|
13
13
|
import { logger } from './helpers/logger.js';
|
|
14
|
+
import { setInkApp } from './helpers/appState.js';
|
|
14
15
|
process.on('uncaughtException', function (err) {
|
|
15
16
|
return logger.error('Uncaught exception', err);
|
|
16
17
|
});
|
|
@@ -18,4 +19,5 @@ process.on('unhandledRejection', function (reason) {
|
|
|
18
19
|
return logger.error('Unhandled rejection', reason);
|
|
19
20
|
});
|
|
20
21
|
console.clear();
|
|
21
|
-
render(/*#__PURE__*/React.createElement(App, null));
|
|
22
|
+
var inkApp = render(/*#__PURE__*/React.createElement(App, null));
|
|
23
|
+
setInkApp(inkApp);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdd-cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "CLI Docker Dashboard",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"author": "",
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"type": "module",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
19
22
|
"dependencies": {
|
|
20
23
|
"chalk": "^5.6.2",
|
|
21
24
|
"dockerode": "^4.0.9",
|