cdd-cli 4.0.0 → 4.2.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 +60 -26
- package/README.md +60 -26
- package/dist/App.js +1 -0
- package/dist/components/ContainerCreationPrompt.js +14 -0
- package/dist/components/PromptField.js +13 -6
- package/dist/components/TextField.js +54 -0
- 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/helpers/textEditing.js +293 -0
- package/dist/hooks/creation/useContainerCreation.js +132 -15
- package/dist/hooks/useContainerCommandRouter.js +12 -2
- package/dist/hooks/useControls.js +21 -26
- 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
|
@@ -3,12 +3,6 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="https://img.shields.io/npm/v/cdd-cli?color=blue&label=npm%20package" alt="npm version"/>
|
|
5
5
|
<img src="https://img.shields.io/npm/dt/cdd-cli?color=green&label=downloads" alt="npm downloads"/>
|
|
6
|
-
<a href="https://github.com/caertos/cdd/actions/workflows/ci.yml">
|
|
7
|
-
<img src="https://github.com/caertos/cdd/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status" />
|
|
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
6
|
<a href="https://deepwiki.com/Caertos/cdd"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
13
7
|
</p>
|
|
14
8
|
|
|
@@ -16,9 +10,34 @@
|
|
|
16
10
|
|
|
17
11
|
---
|
|
18
12
|
|
|
19
|
-
## 🎉 Novedades en
|
|
13
|
+
## 🎉 Novedades en v4.2
|
|
20
14
|
|
|
21
|
-
**
|
|
15
|
+
**Campos de texto editables con cursor visible en el asistente de creación.**
|
|
16
|
+
|
|
17
|
+
Ahora los campos de texto del asistente se comportan como un campo de texto normal: un cursor que se ve, que se mueve con las flechas, que inserta y borra donde está.
|
|
18
|
+
|
|
19
|
+
- **Navegación con cursor** — `←` / `→` mueven el cursor dentro del campo
|
|
20
|
+
- **Inicio / Fin** — saltan al principio o al final del texto
|
|
21
|
+
- **Edición completa** — `Backspace` y `Delete` funcionan en cualquier posición
|
|
22
|
+
- **Atajos de teclado** — `Ctrl+A/E/W/U/K` para edición rápida
|
|
23
|
+
- **Pegado** — pegar texto lo inserta completo en la posición del cursor
|
|
24
|
+
- **Acentos y emojis** — manejo correcto por punto de código
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Versiones anteriores
|
|
29
|
+
|
|
30
|
+
### v4.1 — Shell interactivo
|
|
31
|
+
|
|
32
|
+
**Abre un shell dentro de cualquier contenedor con una sola tecla.**
|
|
33
|
+
|
|
34
|
+
Presiona `S` y CDD te lleva a un shell interactivo completo (`bash` o `sh`) dentro del contenedor seleccionado — sin necesidad de escribir `docker exec`.
|
|
35
|
+
|
|
36
|
+
- **Shell auto-detectado** — CDD sondea el contenedor y elige `bash` o `sh` automáticamente
|
|
37
|
+
- **Soporte de terminal completo** — ejecuta `psql`, `python3`, `node`, `redis-cli`, o cualquier comando dentro del contenedor
|
|
38
|
+
- **Salida limpia** — escribe `exit` o presiona `Ctrl+D` para volver al dashboard
|
|
39
|
+
|
|
40
|
+
### v3.2 — Asistente de creación interactivo
|
|
22
41
|
|
|
23
42
|
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
43
|
|
|
@@ -76,17 +95,18 @@ cdd
|
|
|
76
95
|
|
|
77
96
|
Usa `↑` / `↓` para navegar por los contenedores. Atajos de teclado disponibles:
|
|
78
97
|
|
|
79
|
-
| Tecla
|
|
80
|
-
|
|
81
|
-
| `↑` / `↓` | Navegar la lista de contenedores
|
|
82
|
-
| `I`
|
|
83
|
-
| `P`
|
|
84
|
-
| `R`
|
|
85
|
-
| `C`
|
|
86
|
-
| `L`
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
98
|
+
| Tecla | Acción |
|
|
99
|
+
| --------- | ----------------------------------------------------------- |
|
|
100
|
+
| `↑` / `↓` | Navegar la lista de contenedores |
|
|
101
|
+
| `I` | Iniciar el contenedor seleccionado |
|
|
102
|
+
| `P` | Detener el contenedor seleccionado |
|
|
103
|
+
| `R` | Reiniciar el contenedor seleccionado |
|
|
104
|
+
| `C` | Abrir el asistente de creación |
|
|
105
|
+
| `L` | Ver logs del contenedor seleccionado en tiempo real |
|
|
106
|
+
| `S` | Abrir shell interactivo dentro del contenedor seleccionado |
|
|
107
|
+
| `E` | Eliminar el contenedor seleccionado — requiere confirmación |
|
|
108
|
+
| `D` | Activar/desactivar panel de debug en vivo |
|
|
109
|
+
| `Q` | Salir |
|
|
90
110
|
|
|
91
111
|
---
|
|
92
112
|
|
|
@@ -114,18 +134,32 @@ Ingresa un mapeo de puertos en formato `HOST:CONTENEDOR`, por ejemplo `8080:80`,
|
|
|
114
134
|
|
|
115
135
|
Ingresa pares `CLAVE=VALOR` de a uno. Las **sugerencias contextuales** muestran variables recomendadas para la imagen seleccionada:
|
|
116
136
|
|
|
117
|
-
| Imagen
|
|
118
|
-
|
|
119
|
-
| postgres
|
|
120
|
-
| mysql
|
|
121
|
-
| redis
|
|
122
|
-
| mongo
|
|
123
|
-
| node / nginx / otros | Variables de runtime comunes según corresponda
|
|
137
|
+
| Imagen | Variables sugeridas |
|
|
138
|
+
| -------------------- | ---------------------------------------------------------- |
|
|
139
|
+
| postgres | `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` |
|
|
140
|
+
| mysql | `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE` |
|
|
141
|
+
| redis | _(sin variables requeridas)_ |
|
|
142
|
+
| mongo | `MONGO_INITDB_ROOT_USERNAME`, `MONGO_INITDB_ROOT_PASSWORD` |
|
|
143
|
+
| node / nginx / otros | Variables de runtime comunes según corresponda |
|
|
124
144
|
|
|
125
145
|
Presiona `Enter` en una línea vacía para terminar y crear el contenedor.
|
|
126
146
|
|
|
127
147
|
---
|
|
128
148
|
|
|
149
|
+
## Shell Interactivo
|
|
150
|
+
|
|
151
|
+
Presiona `S` desde el dashboard con un contenedor en ejecución seleccionado. CDD hará lo siguiente:
|
|
152
|
+
|
|
153
|
+
1. Detectará el shell disponible dentro del contenedor (`bash` o `sh`)
|
|
154
|
+
2. Abrirá una sesión de terminal interactiva completa
|
|
155
|
+
3. Te dejará en el shell del contenedor
|
|
156
|
+
|
|
157
|
+
Desde ahí puedes ejecutar cualquier comando — `psql` para PostgreSQL, `python3` para Python, `node` para Node.js, `redis-cli` para Redis, etc.
|
|
158
|
+
|
|
159
|
+
Escribe `exit` o presiona `Ctrl+D` para salir del shell y volver al dashboard de CDD.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
129
163
|
## Requisitos
|
|
130
164
|
|
|
131
165
|
- Node.js >= 18
|
package/README.md
CHANGED
|
@@ -3,12 +3,6 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="https://img.shields.io/npm/v/cdd-cli?color=blue&label=npm%20package" alt="npm version"/>
|
|
5
5
|
<img src="https://img.shields.io/npm/dt/cdd-cli?color=green&label=downloads" alt="npm downloads"/>
|
|
6
|
-
<a href="https://github.com/caertos/cdd/actions/workflows/ci.yml">
|
|
7
|
-
<img src="https://github.com/caertos/cdd/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status" />
|
|
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
6
|
<a href="https://deepwiki.com/Caertos/cdd"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
13
7
|
</p>
|
|
14
8
|
|
|
@@ -16,9 +10,34 @@
|
|
|
16
10
|
|
|
17
11
|
---
|
|
18
12
|
|
|
19
|
-
## 🎉 What's new in
|
|
13
|
+
## 🎉 What's new in v4.2
|
|
20
14
|
|
|
21
|
-
**
|
|
15
|
+
**Editable text fields with visible cursor in the creation wizard.**
|
|
16
|
+
|
|
17
|
+
Text fields in the wizard now behave like a proper text input: a visible cursor that moves with arrow keys, inserts and deletes where it stands.
|
|
18
|
+
|
|
19
|
+
- **Cursor navigation** — `←` / `→` move the cursor within the field
|
|
20
|
+
- **Home / End** — jump to the start or end of the text
|
|
21
|
+
- **Full editing** — `Backspace` and `Delete` work at any cursor position
|
|
22
|
+
- **Keyboard shortcuts** — `Ctrl+A/E/W/U/K` for fast text editing
|
|
23
|
+
- **Paste support** — pasted text inserts at the cursor position
|
|
24
|
+
- **Accents and emojis** — correct handling via code-point segmentation
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Previous releases
|
|
29
|
+
|
|
30
|
+
### v4.1 — Interactive shell
|
|
31
|
+
|
|
32
|
+
**Open a shell inside any container with a single keystroke.**
|
|
33
|
+
|
|
34
|
+
Press `S` and CDD drops you into a full interactive shell (`bash` or `sh`) inside the selected container — no `docker exec` typing needed.
|
|
35
|
+
|
|
36
|
+
- **Auto-detected shell** — CDD probes the container and picks `bash` or `sh` automatically
|
|
37
|
+
- **Full terminal support** — run `psql`, `python3`, `node`, `redis-cli`, or any command inside the container
|
|
38
|
+
- **Clean exit** — type `exit` or press `Ctrl+D` to return to the dashboard
|
|
39
|
+
|
|
40
|
+
### v3.2 — Interactive creation wizard
|
|
22
41
|
|
|
23
42
|
Forget `docker run` flags, forgotten env vars, and broken `:latest` tags. Press `C` and CDD guides you through creating a container in seconds:
|
|
24
43
|
|
|
@@ -76,17 +95,18 @@ cdd
|
|
|
76
95
|
|
|
77
96
|
Use `↑` / `↓` to navigate containers. Available keyboard shortcuts:
|
|
78
97
|
|
|
79
|
-
| Key
|
|
80
|
-
|
|
81
|
-
| `↑` / `↓` | Navigate container list
|
|
82
|
-
| `I`
|
|
83
|
-
| `P`
|
|
84
|
-
| `R`
|
|
85
|
-
| `C`
|
|
86
|
-
| `L`
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
98
|
+
| Key | Action |
|
|
99
|
+
| --------- | --------------------------------------------------------- |
|
|
100
|
+
| `↑` / `↓` | Navigate container list |
|
|
101
|
+
| `I` | Start selected container |
|
|
102
|
+
| `P` | Stop selected container |
|
|
103
|
+
| `R` | Restart selected container |
|
|
104
|
+
| `C` | Open creation wizard |
|
|
105
|
+
| `L` | Stream logs for selected container |
|
|
106
|
+
| `S` | Open interactive shell inside selected container |
|
|
107
|
+
| `E` | Erase (remove) selected container — confirmation required |
|
|
108
|
+
| `D` | Toggle live debug panel |
|
|
109
|
+
| `Q` | Quit |
|
|
90
110
|
|
|
91
111
|
---
|
|
92
112
|
|
|
@@ -114,18 +134,32 @@ Enter a port mapping in `HOST:CONTAINER` format, e.g. `8080:80`, `5432:5432`. Le
|
|
|
114
134
|
|
|
115
135
|
Enter `KEY=VALUE` pairs one at a time. **Contextual hints** show recommended variables for the selected image:
|
|
116
136
|
|
|
117
|
-
| Image
|
|
118
|
-
|
|
119
|
-
| postgres
|
|
120
|
-
| mysql
|
|
121
|
-
| redis
|
|
122
|
-
| mongo
|
|
123
|
-
| node / nginx / others | Common runtime vars as applicable
|
|
137
|
+
| Image | Suggested vars |
|
|
138
|
+
| --------------------- | ---------------------------------------------------------- |
|
|
139
|
+
| postgres | `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` |
|
|
140
|
+
| mysql | `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE` |
|
|
141
|
+
| redis | _(no required vars)_ |
|
|
142
|
+
| mongo | `MONGO_INITDB_ROOT_USERNAME`, `MONGO_INITDB_ROOT_PASSWORD` |
|
|
143
|
+
| node / nginx / others | Common runtime vars as applicable |
|
|
124
144
|
|
|
125
145
|
Press `Enter` on an empty line to finish and create the container.
|
|
126
146
|
|
|
127
147
|
---
|
|
128
148
|
|
|
149
|
+
## Interactive Shell
|
|
150
|
+
|
|
151
|
+
Press `S` from the dashboard while a running container is selected. CDD will:
|
|
152
|
+
|
|
153
|
+
1. Detect the available shell inside the container (`bash` or `sh`)
|
|
154
|
+
2. Open a full interactive terminal session
|
|
155
|
+
3. Drop you into the container's shell
|
|
156
|
+
|
|
157
|
+
From there you can run any command — `psql` for PostgreSQL, `python3` for Python, `node` for Node.js, `redis-cli` for Redis, etc.
|
|
158
|
+
|
|
159
|
+
Type `exit` or press `Ctrl+D` to leave the shell and return to the CDD dashboard.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
129
163
|
## Requirements
|
|
130
164
|
|
|
131
165
|
- Node.js >= 18
|
package/dist/App.js
CHANGED
|
@@ -31,6 +31,7 @@ export default function App() {
|
|
|
31
31
|
containerName: controls.containerNameInput,
|
|
32
32
|
portInput: controls.portInput,
|
|
33
33
|
envInput: controls.envInput,
|
|
34
|
+
cursors: controls.creation.cursors,
|
|
34
35
|
message: controls.message,
|
|
35
36
|
messageColor: controls.messageColor,
|
|
36
37
|
suggestions: controls.creation.suggestions,
|
|
@@ -29,6 +29,8 @@ export default function ContainerCreationPrompt(props) {
|
|
|
29
29
|
containerName = props.containerName,
|
|
30
30
|
portInput = props.portInput,
|
|
31
31
|
envInput = props.envInput,
|
|
32
|
+
_props$cursors = props.cursors,
|
|
33
|
+
cursors = _props$cursors === void 0 ? {} : _props$cursors,
|
|
32
34
|
message = props.message,
|
|
33
35
|
messageColor = props.messageColor,
|
|
34
36
|
_props$suggestions = props.suggestions,
|
|
@@ -46,23 +48,28 @@ export default function ContainerCreationPrompt(props) {
|
|
|
46
48
|
var prompts = [{
|
|
47
49
|
label: 'Name of the image to create (e.g., nginx:1.27-alpine):',
|
|
48
50
|
value: imageName,
|
|
51
|
+
cursor: cursors.imageName,
|
|
49
52
|
required: true
|
|
50
53
|
}, {
|
|
51
54
|
label: 'Name of the container (optional):',
|
|
52
55
|
value: containerName,
|
|
56
|
+
cursor: cursors.containerName,
|
|
53
57
|
required: false
|
|
54
58
|
}, {
|
|
55
59
|
label: 'Ports (optional, format 8080:80,443:443):',
|
|
56
60
|
value: portInput,
|
|
61
|
+
cursor: cursors.portInput,
|
|
57
62
|
required: false
|
|
58
63
|
}, {
|
|
59
64
|
label: 'Environment variables (optional, format VAR1=val1,VAR2=val2):',
|
|
60
65
|
value: envInput,
|
|
66
|
+
cursor: cursors.envInput,
|
|
61
67
|
required: false
|
|
62
68
|
}];
|
|
63
69
|
var _ref = prompts[step] || {},
|
|
64
70
|
label = _ref.label,
|
|
65
71
|
value = _ref.value,
|
|
72
|
+
cursor = _ref.cursor,
|
|
66
73
|
required = _ref.required;
|
|
67
74
|
var activeItems = hubResults !== null && hubResults !== void 0 ? hubResults : suggestions;
|
|
68
75
|
var showSuggestions = step === 0 && (isSearchingHub || activeItems.length > 0);
|
|
@@ -75,6 +82,7 @@ export default function ContainerCreationPrompt(props) {
|
|
|
75
82
|
}, /*#__PURE__*/React.createElement(PromptField, {
|
|
76
83
|
label: label,
|
|
77
84
|
value: value,
|
|
85
|
+
cursor: cursor,
|
|
78
86
|
required: required
|
|
79
87
|
}), showSuggestions && /*#__PURE__*/React.createElement(SuggestionPanel, {
|
|
80
88
|
items: activeItems,
|
|
@@ -97,6 +105,12 @@ ContainerCreationPrompt.propTypes = {
|
|
|
97
105
|
containerName: PropTypes.string,
|
|
98
106
|
portInput: PropTypes.string,
|
|
99
107
|
envInput: PropTypes.string,
|
|
108
|
+
cursors: PropTypes.shape({
|
|
109
|
+
imageName: PropTypes.number,
|
|
110
|
+
containerName: PropTypes.number,
|
|
111
|
+
portInput: PropTypes.number,
|
|
112
|
+
envInput: PropTypes.number
|
|
113
|
+
}),
|
|
100
114
|
message: PropTypes.string,
|
|
101
115
|
messageColor: PropTypes.string,
|
|
102
116
|
suggestions: PropTypes.arrayOf(PropTypes.string),
|
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { TextField } from './TextField.js';
|
|
2
3
|
import { Text } from 'ink';
|
|
3
4
|
import PropTypes from 'prop-types';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* PromptField shows a label and the current typed value for an input field.
|
|
7
|
-
*
|
|
8
|
+
* Wraps TextField with cursor rendering.
|
|
8
9
|
*
|
|
9
10
|
* @param {Object} props
|
|
10
11
|
* @param {string} props.label - Label shown above the input value
|
|
11
12
|
* @param {string} props.value - Current value of the input
|
|
13
|
+
* @param {number} [props.cursor] - Cursor position (0..value.length). When
|
|
14
|
+
* omitted the cursor defaults to the end of the value.
|
|
12
15
|
* @param {boolean} [props.required=false] - Whether the field is required
|
|
13
16
|
* @returns {JSX.Element}
|
|
14
17
|
*/
|
|
15
18
|
export function PromptField(_ref) {
|
|
16
19
|
var label = _ref.label,
|
|
17
20
|
value = _ref.value,
|
|
21
|
+
cursor = _ref.cursor,
|
|
18
22
|
required = _ref.required;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
var cursorPos = cursor !== undefined ? cursor : (value || '').length;
|
|
24
|
+
return /*#__PURE__*/React.createElement(TextField, {
|
|
25
|
+
label: label,
|
|
26
|
+
value: value || '',
|
|
27
|
+
cursor: cursorPos,
|
|
28
|
+
required: required
|
|
29
|
+
});
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
/**
|
|
@@ -42,6 +48,7 @@ export function PromptMessage(_ref2) {
|
|
|
42
48
|
PromptField.propTypes = {
|
|
43
49
|
label: PropTypes.string,
|
|
44
50
|
value: PropTypes.string,
|
|
51
|
+
cursor: PropTypes.number,
|
|
45
52
|
required: PropTypes.bool
|
|
46
53
|
};
|
|
47
54
|
PromptMessage.propTypes = {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TextField renders a single-line text input with a visible cursor.
|
|
7
|
+
* Purely presentational — does not capture keyboard input.
|
|
8
|
+
*
|
|
9
|
+
* The cursor inverts the character under it. When the cursor is at the end
|
|
10
|
+
* of the text, a block cursor (inverted space) is shown.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} props
|
|
13
|
+
* @param {string} props.label - Label shown above the field
|
|
14
|
+
* @param {string} props.value - Current text content
|
|
15
|
+
* @param {number} props.cursor - Cursor position (0..value.length)
|
|
16
|
+
* @param {boolean} [props.required=false] - Show in red if empty
|
|
17
|
+
* @param {boolean} [props.masked=false] - Reserved for TASK-5 (secrets)
|
|
18
|
+
* @param {string} [props.placeholder] - Dimmed text when empty
|
|
19
|
+
*/
|
|
20
|
+
export function TextField(_ref) {
|
|
21
|
+
var _chars$clampedCursor;
|
|
22
|
+
var label = _ref.label,
|
|
23
|
+
_ref$value = _ref.value,
|
|
24
|
+
value = _ref$value === void 0 ? '' : _ref$value,
|
|
25
|
+
_ref$cursor = _ref.cursor,
|
|
26
|
+
cursor = _ref$cursor === void 0 ? 0 : _ref$cursor,
|
|
27
|
+
_ref$required = _ref.required,
|
|
28
|
+
required = _ref$required === void 0 ? false : _ref$required,
|
|
29
|
+
_ref$masked = _ref.masked,
|
|
30
|
+
masked = _ref$masked === void 0 ? false : _ref$masked;
|
|
31
|
+
var isEmpty = required && !value.trim();
|
|
32
|
+
var displayValue = masked ? '*'.repeat(value.length) : value;
|
|
33
|
+
var chars = Array.from(displayValue);
|
|
34
|
+
var clampedCursor = Math.max(0, Math.min(chars.length, cursor));
|
|
35
|
+
var before = chars.slice(0, clampedCursor).join('');
|
|
36
|
+
var atCursor = (_chars$clampedCursor = chars[clampedCursor]) !== null && _chars$clampedCursor !== void 0 ? _chars$clampedCursor : '';
|
|
37
|
+
var after = chars.slice(clampedCursor + 1).join('');
|
|
38
|
+
var color = isEmpty ? 'red' : 'cyan';
|
|
39
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, label), /*#__PURE__*/React.createElement(Text, {
|
|
40
|
+
color: color
|
|
41
|
+
}, before, atCursor ? /*#__PURE__*/React.createElement(Text, {
|
|
42
|
+
inverse: true
|
|
43
|
+
}, atCursor) : /*#__PURE__*/React.createElement(Text, {
|
|
44
|
+
inverse: true
|
|
45
|
+
}, " "), after));
|
|
46
|
+
}
|
|
47
|
+
TextField.propTypes = {
|
|
48
|
+
label: PropTypes.string.isRequired,
|
|
49
|
+
value: PropTypes.string,
|
|
50
|
+
cursor: PropTypes.number,
|
|
51
|
+
required: PropTypes.bool,
|
|
52
|
+
masked: PropTypes.bool,
|
|
53
|
+
placeholder: PropTypes.string
|
|
54
|
+
};
|
|
@@ -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
|
+
}
|