@nocobase/cli 2.1.0-alpha.25 → 2.1.0-alpha.27

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 (84) hide show
  1. package/README.md +61 -49
  2. package/README.zh-CN.md +40 -47
  3. package/dist/commands/app/down.js +259 -0
  4. package/dist/commands/app/logs.js +98 -0
  5. package/dist/commands/app/restart.js +75 -0
  6. package/dist/commands/app/start.js +252 -0
  7. package/dist/commands/app/stop.js +98 -0
  8. package/dist/commands/app/upgrade.js +579 -0
  9. package/dist/commands/build.js +3 -48
  10. package/dist/commands/config/delete.js +30 -0
  11. package/dist/commands/config/get.js +29 -0
  12. package/dist/commands/config/index.js +20 -0
  13. package/dist/commands/config/list.js +29 -0
  14. package/dist/commands/config/set.js +35 -0
  15. package/dist/commands/db/check.js +230 -0
  16. package/dist/commands/db/shared.js +1 -1
  17. package/dist/commands/dev.js +3 -147
  18. package/dist/commands/down.js +3 -188
  19. package/dist/commands/download.js +4 -856
  20. package/dist/commands/env/add.js +28 -23
  21. package/dist/commands/env/info.js +152 -0
  22. package/dist/commands/env/list.js +23 -9
  23. package/dist/commands/env/shared.js +158 -0
  24. package/dist/commands/{prompts-stages.js → examples/prompts-stages.js} +3 -3
  25. package/dist/commands/{prompts-test.js → examples/prompts-test.js} +3 -3
  26. package/dist/commands/init.js +83 -6
  27. package/dist/commands/install.js +361 -82
  28. package/dist/commands/license/activate.js +357 -0
  29. package/dist/commands/license/env.js +94 -0
  30. package/dist/commands/license/generate-id.js +107 -0
  31. package/dist/commands/license/id.js +52 -0
  32. package/dist/commands/license/index.js +20 -0
  33. package/dist/commands/license/plugins/clean.js +98 -0
  34. package/dist/commands/license/plugins/index.js +20 -0
  35. package/dist/commands/license/plugins/list.js +50 -0
  36. package/dist/commands/license/plugins/shared.js +325 -0
  37. package/dist/commands/license/plugins/sync.js +267 -0
  38. package/dist/commands/license/shared.js +411 -0
  39. package/dist/commands/license/status.js +50 -0
  40. package/dist/commands/logs.js +3 -88
  41. package/dist/commands/plugin/disable.js +64 -0
  42. package/dist/commands/plugin/enable.js +64 -0
  43. package/dist/commands/plugin/list.js +62 -0
  44. package/dist/commands/pm/disable.js +3 -54
  45. package/dist/commands/pm/enable.js +3 -54
  46. package/dist/commands/pm/list.js +3 -52
  47. package/dist/commands/restart.js +3 -65
  48. package/dist/commands/scaffold/migration.js +1 -1
  49. package/dist/commands/scaffold/plugin.js +1 -1
  50. package/dist/commands/skills/remove.js +71 -0
  51. package/dist/commands/skills/update.js +7 -0
  52. package/dist/commands/source/build.js +58 -0
  53. package/dist/commands/source/dev.js +157 -0
  54. package/dist/commands/source/download.js +866 -0
  55. package/dist/commands/source/test.js +467 -0
  56. package/dist/commands/start.js +3 -209
  57. package/dist/commands/stop.js +3 -88
  58. package/dist/commands/test.js +3 -457
  59. package/dist/commands/upgrade.js +3 -585
  60. package/dist/help/runtime-help.js +3 -0
  61. package/dist/lib/api-client.js +94 -9
  62. package/dist/lib/app-health.js +126 -0
  63. package/dist/lib/app-managed-resources.js +264 -0
  64. package/dist/lib/app-runtime.js +26 -10
  65. package/dist/lib/auth-store.js +29 -63
  66. package/dist/lib/build-config.js +8 -0
  67. package/dist/lib/cli-config.js +176 -0
  68. package/dist/lib/cli-home.js +12 -26
  69. package/dist/lib/cli-locale.js +15 -1
  70. package/dist/lib/db-connection-check.js +178 -0
  71. package/dist/lib/env-config.js +80 -0
  72. package/dist/lib/generated-command.js +23 -3
  73. package/dist/lib/plugin-storage.js +127 -0
  74. package/dist/lib/prompt-validators.js +4 -4
  75. package/dist/lib/prompt-web-ui.js +13 -6
  76. package/dist/lib/runtime-generator.js +89 -10
  77. package/dist/lib/self-manager.js +57 -2
  78. package/dist/lib/skills-manager.js +34 -7
  79. package/dist/lib/startup-update.js +85 -7
  80. package/dist/locale/en-US.json +16 -13
  81. package/dist/locale/zh-CN.json +16 -13
  82. package/nocobase-ctl.config.json +82 -0
  83. package/package.json +41 -6
  84. package/dist/commands/ps.js +0 -119
package/README.md CHANGED
@@ -60,12 +60,16 @@ nb init --ui
60
60
  When creating a new app, it can also install NocoBase AI coding skills
61
61
  (`nocobase/skills`) globally.
62
62
 
63
+ Use `--skip-skills` if the skills are managed separately, or when running in CI
64
+ or offline environments where `nb init` should not install or update them.
65
+
63
66
  ### Non-Interactive Setup
64
67
 
65
68
  When prompts are skipped, an app/env name is required:
66
69
 
67
70
  ```bash
68
71
  nb init --env app1 --yes
72
+ nb init --env app1 --yes --skip-skills
69
73
  ```
70
74
 
71
75
  Install with Docker:
@@ -111,17 +115,12 @@ If `nb init` was interrupted after the env config had already been saved, you ca
111
115
 
112
116
  ```bash
113
117
  nb init --env app1 --resume
114
- ```
115
-
116
- The advanced low-level equivalent is:
117
-
118
- ```bash
119
- nb install --env app1 --resume
118
+ nb init --env app1 --resume --skip-skills
120
119
  ```
121
120
 
122
121
  `--resume` reuses the saved workspace env config for app, source, database, and env connection settings. In interactive mode, it only asks for any missing setup-only values.
123
122
 
124
- In non-interactive mode, pass these setup-only flags again because they are not saved in env config:
123
+ In non-interactive resume mode, `nb init --resume --yes` uses default initialization values unless these flags are passed explicitly:
125
124
 
126
125
  - `--lang`
127
126
  - `--root-username`
@@ -134,40 +133,32 @@ In non-interactive mode, pass these setup-only flags again because they are not
134
133
  | Command | Description |
135
134
  | --- | --- |
136
135
  | `nb init` | Set up NocoBase and connect it as a CLI env for coding agents. |
137
- | `nb install` | Advanced command used by `nb init` to install a local NocoBase app and save env config. In most cases, use `nb init` instead. |
138
- | `nb download` | Advanced command used by `nb init` or `nb upgrade` to fetch NocoBase from Docker, npm, or Git. It is rarely used directly. |
139
- | `nb start` | Start the selected local app or Docker container. |
140
- | `nb stop` | Stop the selected local app or Docker container. |
141
- | `nb restart` | Stop, then start the selected local app or Docker container. |
142
- | `nb dev` | Run development mode for npm/Git source envs. |
143
- | `nb logs` | Show app logs for npm/Git or Docker envs. |
144
- | `nb ps` | Show runtime status for configured envs. |
136
+ | `nb app` | Manage app runtimes: start, stop, restart, logs, cleanup, and upgrades. |
137
+ | `nb source` | Manage the local source project: download, develop, build, and test. |
145
138
  | `nb db` | Inspect or manage built-in database runtime status for local envs. |
146
- | `nb upgrade` | Refresh code/image and restart the selected app. |
147
- | `nb down` | Stop and remove local runtime containers for an env. |
148
139
  | `nb env` | Manage saved CLI env connections. |
149
140
  | `nb api` | Call NocoBase API resources from the CLI. |
150
- | `nb pm` | Manage plugins for the selected NocoBase env. |
141
+ | `nb plugin` | Manage plugins for the selected NocoBase env. |
151
142
  | `nb self` | Check or update the installed NocoBase CLI. |
152
143
  | `nb skills` | Check, install, or update global NocoBase AI coding skills. |
153
144
 
154
- Recommended style: use `--env` explicitly for app/runtime commands. `-e` is the short form:
145
+ Recommended style: pass the env name explicitly when operating on a specific env. Runtime commands accept `--env`, and `nb env info` also accepts a positional env name:
155
146
 
156
147
  ```bash
157
- nb start --env app1
158
- nb restart --env app1
159
- nb logs --env app1
160
- nb ps --env app1
148
+ nb app start --env app1
149
+ nb app restart --env app1
150
+ nb app logs --env app1
151
+ nb env info app1
161
152
  nb db ps --env app1
162
153
  ```
163
154
 
164
155
  Equivalent shorthand examples:
165
156
 
166
157
  ```bash
167
- nb start -e app1
168
- nb restart -e app1
169
- nb logs -e app1
170
- nb upgrade -e app1
158
+ nb app start -e app1
159
+ nb app restart -e app1
160
+ nb app logs -e app1
161
+ nb app upgrade -e app1
171
162
  nb db start -e app1
172
163
  ```
173
164
 
@@ -208,18 +199,18 @@ Docker envs are managed through saved Docker containers and images:
208
199
 
209
200
  ```bash
210
201
  nb init --env app1 --yes --source docker --version alpha
211
- nb start --env app1
212
- nb restart --env app1
213
- nb logs --env app1
214
- nb stop --env app1
202
+ nb app start --env app1
203
+ nb app restart --env app1
204
+ nb app logs --env app1
205
+ nb app stop --env app1
215
206
  ```
216
207
 
217
208
  Docker downloads support platform selection:
218
209
 
219
210
  ```bash
220
- nb download --source docker --version alpha --docker-platform auto
221
- nb download --source docker --version alpha --docker-platform linux/amd64
222
- nb download --source docker --version alpha --docker-platform linux/arm64
211
+ nb source download --source docker --version alpha --docker-platform auto
212
+ nb source download --source docker --version alpha --docker-platform linux/amd64
213
+ nb source download --source docker --version alpha --docker-platform linux/arm64
223
214
  ```
224
215
 
225
216
  ### npm and Git
@@ -228,11 +219,11 @@ npm and Git envs use a local source directory and can run development mode:
228
219
 
229
220
  ```bash
230
221
  nb init --env app1 --yes --source git --version alpha
231
- nb dev --env app1
222
+ nb source dev --env app1
232
223
  ```
233
224
 
234
- `nb dev` only supports npm/Git source envs. Docker envs can be inspected with
235
- `nb logs`, and remote envs only support API/env operations.
225
+ `nb source dev` only supports npm/Git source envs. Docker envs can be inspected with
226
+ `nb app logs`, and remote envs only support API/env operations.
236
227
 
237
228
  ### Existing NocoBase App
238
229
 
@@ -250,14 +241,14 @@ nb env add app1 --api-base-url http://localhost:13000/api
250
241
  Upgrade refreshes the saved source or image, then restarts the app:
251
242
 
252
243
  ```bash
253
- nb upgrade --env app1
244
+ nb app upgrade --env app1
254
245
  ```
255
246
 
256
247
  Use `--skip-code-update` or `-s` to restart with the saved local code or Docker
257
248
  image without downloading updates first:
258
249
 
259
250
  ```bash
260
- nb upgrade --env app1 -s
251
+ nb app upgrade --env app1 -s
261
252
  ```
262
253
 
263
254
  ## Database Commands
@@ -283,23 +274,19 @@ Notes:
283
274
  Bring down a local env:
284
275
 
285
276
  ```bash
286
- nb down --env app1
277
+ nb app down --env app1
287
278
  ```
288
279
 
289
- By default, `nb down` stops the app and removes app/database containers if they
290
- exist. It keeps user data, source files, and CLI env config.
280
+ By default, `nb app down` stops the app and removes app/database containers if they
281
+ exist. For local envs, it also deletes the saved local app files. It keeps storage data and CLI env config.
291
282
 
292
283
  Use explicit flags for destructive cleanup:
293
284
 
294
285
  ```bash
295
- nb down --env app1 --remove-data
296
- nb down --env app1 --remove-source
297
- nb down --env app1 --remove-env
286
+ nb app down --env app1 --all --yes
298
287
  ```
299
288
 
300
- - `--remove-data`: delete storage and managed database data. This requires confirmation unless `--yes` is used.
301
- - `--remove-source`: delete the npm/Git source directory.
302
- - `--remove-env`: remove the saved CLI env config.
289
+ - `--all`: delete everything for the env, including storage data and the saved env config. This requires `--yes`.
303
290
 
304
291
  ## Environment Management
305
292
 
@@ -309,12 +296,18 @@ Show the current env:
309
296
  nb env
310
297
  ```
311
298
 
312
- List configured envs:
299
+ List configured envs with token-verified API status:
313
300
 
314
301
  ```bash
315
302
  nb env list
316
303
  ```
317
304
 
305
+ Show details for one env:
306
+
307
+ ```bash
308
+ nb env info app1
309
+ ```
310
+
318
311
  Switch the current env:
319
312
 
320
313
  ```bash
@@ -343,6 +336,23 @@ nb api resource get --resource users --filter-by-tk 1 -e app1
343
336
  nb api resource create --resource users --values '{"nickname":"Ada"}' -e app1
344
337
  ```
345
338
 
339
+ Create and download a backup:
340
+
341
+ ```bash
342
+ nb api backup create -e app1
343
+ nb api backup status --name backup_20260430_120000_1234.nbdata -e app1
344
+ nb api backup download --name backup_20260430_120000_1234.nbdata --output ./backup.nbdata -e app1
345
+ ```
346
+
347
+ Restore or run a migration package:
348
+
349
+ ```bash
350
+ nb api backup restore-upload --file ./backup.nbdata -e app1
351
+ nb api migration rules create --name default --user-defined-rule schema-only --system-defined-rule overwrite-first -e app1
352
+ nb api migration create --rule-id 1 --title release-20260430 -e app1
353
+ nb api migration execute --file ./migration.nbdata -e app1
354
+ ```
355
+
346
356
  Use `-j, --json-output` to print raw JSON when available:
347
357
 
348
358
  ```bash
@@ -357,9 +367,11 @@ Available API command topics:
357
367
  | `nb api api-keys` | Manage API keys for HTTP API access. |
358
368
  | `nb api app` | Manage application resources. |
359
369
  | `nb api authenticators` | Manage user authentication, including password auth, SMS auth, SSO protocols, and extensible providers. |
370
+ | `nb api backup` | Create, download, remove, and restore backups. |
360
371
  | `nb api data-modeling` | Manage data sources, collections, and database modeling resources. |
361
372
  | `nb api file-manager` | Manage file storage services, file collections, and attachment fields. |
362
373
  | `nb api flow-surfaces` | Compose and mutate page, tab, block, field, and action surfaces. |
374
+ | `nb api migration` | Create, check, execute, and inspect migration packages. |
363
375
  | `nb api pm` | Manage plugins through API commands. |
364
376
  | `nb api resource` | Work with generic collection resources. |
365
377
  | `nb api system-settings` | Adjust system title, logo, language, and other global settings. |
package/README.zh-CN.md CHANGED
@@ -55,12 +55,15 @@ nb init --ui
55
55
 
56
56
  `nb init` 可以连接已有的 NocoBase 应用,也可以安装一个新的 NocoBase 应用。创建新应用时,还可以全局安装 NocoBase AI coding skills (`nocobase/skills`)。
57
57
 
58
+ 如果已经自行管理 skills,或在 CI、离线环境中运行,不希望 `nb init` 安装或更新 skills,可以传入 `--skip-skills`。
59
+
58
60
  ### 非交互式初始化
59
61
 
60
62
  跳过交互提示时,必须提供 app/env name:
61
63
 
62
64
  ```bash
63
65
  nb init --env app1 --yes
66
+ nb init --env app1 --yes --skip-skills
64
67
  ```
65
68
 
66
69
  使用 Docker 安装:
@@ -106,17 +109,12 @@ nb init --env app1 --yes --source git --version fix/cli-v2
106
109
 
107
110
  ```bash
108
111
  nb init --env app1 --resume
109
- ```
110
-
111
- 对应的底层高级命令是:
112
-
113
- ```bash
114
- nb install --env app1 --resume
112
+ nb init --env app1 --resume --skip-skills
115
113
  ```
116
114
 
117
115
  `--resume` 会复用工作区里已保存的 env config,包括应用、source、数据库和 env 连接相关配置。在交互模式下,只会继续补齐缺失的初始化参数。
118
116
 
119
- 在非交互模式下,需要重新传这些只用于初始化、不会保存到 env config 的参数:
117
+ 在非交互恢复模式下,如果没有显式传入这些参数,`nb init --resume --yes` 会使用默认初始化值:
120
118
 
121
119
  - `--lang`
122
120
  - `--root-username`
@@ -129,38 +127,30 @@ nb install --env app1 --resume
129
127
  | 命令 | 说明 |
130
128
  | --- | --- |
131
129
  | `nb init` | 初始化 NocoBase,并连接为 coding agent 可使用的 CLI env。 |
132
- | `nb install` | `nb init` 内部会使用的高级命令,用于安装本地 NocoBase 应用并保存 env 配置。通常建议直接使用 `nb init`。 |
133
- | `nb download` | `nb init` 或 `nb upgrade` 会使用的高级命令,用于从 Docker、npm 或 Git 获取 NocoBase。通常很少直接使用。 |
134
- | `nb start` | 启动选中的本地应用或 Docker 容器。 |
135
- | `nb stop` | 停止选中的本地应用或 Docker 容器。 |
136
- | `nb restart` | 先停止,再启动选中的本地应用或 Docker 容器。 |
137
- | `nb dev` | 为 npm/Git 源码 env 启动开发模式。 |
138
- | `nb logs` | 查看 npm/Git 或 Docker env 的应用日志。 |
139
- | `nb ps` | 查看已配置 env 的运行状态。 |
130
+ | `nb app` | 管理应用运行态:启动、停止、重启、日志、状态、清理和升级。 |
131
+ | `nb source` | 管理本地源码工程:下载、开发、构建和测试。 |
140
132
  | `nb db` | 查看或管理本地 env 的内置数据库运行状态。 |
141
- | `nb upgrade` | 更新源码/镜像并重启选中的应用。 |
142
- | `nb down` | 停止并移除某个 env 的本地运行容器。 |
143
133
  | `nb env` | 管理已保存的 CLI env 连接。 |
144
134
  | `nb api` | 通过 CLI 调用 NocoBase API 资源。 |
145
- | `nb pm` | 管理选中 NocoBase env 的插件。 |
135
+ | `nb plugin` | 管理选中 NocoBase env 的插件。 |
146
136
 
147
- 推荐在应用和运行时相关命令里显式使用 `--env`;`-e` 是它的简写:
137
+ 推荐在操作指定 env 时显式传入 env 名称。运行时命令支持 `--env`,`nb env info` 也支持位置参数:
148
138
 
149
139
  ```bash
150
- nb start --env app1
151
- nb restart --env app1
152
- nb logs --env app1
153
- nb ps --env app1
140
+ nb app start --env app1
141
+ nb app restart --env app1
142
+ nb app logs --env app1
143
+ nb env info app1
154
144
  nb db ps --env app1
155
145
  ```
156
146
 
157
147
  等价的简写示例:
158
148
 
159
149
  ```bash
160
- nb start -e app1
161
- nb restart -e app1
162
- nb logs -e app1
163
- nb upgrade -e app1
150
+ nb app start -e app1
151
+ nb app restart -e app1
152
+ nb app logs -e app1
153
+ nb app upgrade -e app1
164
154
  nb db start -e app1
165
155
  ```
166
156
 
@@ -172,18 +162,18 @@ Docker env 会通过已保存的 Docker 容器和镜像进行管理:
172
162
 
173
163
  ```bash
174
164
  nb init --env app1 --yes --source docker --version alpha
175
- nb start --env app1
176
- nb restart --env app1
177
- nb logs --env app1
178
- nb stop --env app1
165
+ nb app start --env app1
166
+ nb app restart --env app1
167
+ nb app logs --env app1
168
+ nb app stop --env app1
179
169
  ```
180
170
 
181
171
  Docker 下载支持指定平台:
182
172
 
183
173
  ```bash
184
- nb download --source docker --version alpha --docker-platform auto
185
- nb download --source docker --version alpha --docker-platform linux/amd64
186
- nb download --source docker --version alpha --docker-platform linux/arm64
174
+ nb source download --source docker --version alpha --docker-platform auto
175
+ nb source download --source docker --version alpha --docker-platform linux/amd64
176
+ nb source download --source docker --version alpha --docker-platform linux/arm64
187
177
  ```
188
178
 
189
179
  ### npm 和 Git
@@ -192,10 +182,10 @@ npm 和 Git env 会使用本地源码目录,并支持开发模式:
192
182
 
193
183
  ```bash
194
184
  nb init --env app1 --yes --source git --version alpha
195
- nb dev --env app1
185
+ nb source dev --env app1
196
186
  ```
197
187
 
198
- `nb dev` 只支持 npm/Git 源码 env。Docker env 可以通过 `nb logs` 查看日志,remote env 只支持 API 和 env 相关操作。
188
+ `nb source dev` 只支持 npm/Git 源码 env。Docker env 可以通过 `nb app logs` 查看日志,remote env 只支持 API 和 env 相关操作。
199
189
 
200
190
  ### 已有 NocoBase 应用
201
191
 
@@ -212,13 +202,13 @@ nb env add app1 --api-base-url http://localhost:13000/api
212
202
  升级会更新已保存的源码或镜像,然后重启应用:
213
203
 
214
204
  ```bash
215
- nb upgrade --env app1
205
+ nb app upgrade --env app1
216
206
  ```
217
207
 
218
208
  如果只想使用当前已保存的本地源码或 Docker 镜像重启应用,可以使用 `--skip-code-update` 或 `-s`:
219
209
 
220
210
  ```bash
221
- nb upgrade --env app1 -s
211
+ nb app upgrade --env app1 -s
222
212
  ```
223
213
 
224
214
  ## 数据库命令
@@ -244,22 +234,19 @@ nb db logs --env app1
244
234
  关闭并清理某个本地 env:
245
235
 
246
236
  ```bash
247
- nb down --env app1
237
+ nb app down --env app1
248
238
  ```
249
239
 
250
- 默认情况下,`nb down` 会停止应用,并在存在时移除应用容器和数据库容器。它不会删除用户数据、源码文件和 CLI env 配置。
240
+ 默认情况下,`nb app down` 会停止应用,并在存在时移除应用容器和数据库容器。它不会删除用户数据、源码文件和 CLI env 配置。
241
+ 对于本地 env,它还会删除已保存的本地 app 目录。默认仍会保留 storage 数据和 CLI env 配置。
251
242
 
252
243
  如需执行破坏性清理,需要显式指定参数:
253
244
 
254
245
  ```bash
255
- nb down --env app1 --remove-data
256
- nb down --env app1 --remove-source
257
- nb down --env app1 --remove-env
246
+ nb app down --env app1 --all --yes
258
247
  ```
259
248
 
260
- - `--remove-data`:删除 storage 和托管数据库数据。除非使用 `--yes`,否则需要二次确认。
261
- - `--remove-source`:删除 npm/Git 源码目录。
262
- - `--remove-env`:删除已保存的 CLI env 配置。
249
+ - `--all`:删除该 env 的所有内容,包括 storage 数据和已保存的 CLI env 配置。必须同时传 `--yes`。
263
250
 
264
251
  ## Env 管理
265
252
 
@@ -269,12 +256,18 @@ nb down --env app1 --remove-env
269
256
  nb env
270
257
  ```
271
258
 
272
- 查看已配置的 env
259
+ 查看已配置的 env 及 Token 验证后的 API 状态:
273
260
 
274
261
  ```bash
275
262
  nb env list
276
263
  ```
277
264
 
265
+ 查看某个 env 的详情:
266
+
267
+ ```bash
268
+ nb env info app1
269
+ ```
270
+
278
271
  切换当前 env:
279
272
 
280
273
  ```bash
@@ -0,0 +1,259 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import * as p from '@clack/prompts';
10
+ import { Command, Flags } from '@oclif/core';
11
+ import fsp from 'node:fs/promises';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
15
+ import { removeEnv } from '../../lib/auth-store.js';
16
+ import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
17
+ import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
18
+ import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
19
+ function resolveConfiguredPath(value) {
20
+ return resolveConfiguredEnvPath(value);
21
+ }
22
+ function assertSafeRemovalPath(target, label) {
23
+ const resolved = path.resolve(target);
24
+ const cwd = path.resolve(process.cwd());
25
+ const home = path.resolve(os.homedir());
26
+ const root = path.parse(resolved).root;
27
+ if (resolved === root || resolved === cwd || resolved === home) {
28
+ throw new Error(`Refusing to remove ${label} at "${resolved}" because it is too broad.`);
29
+ }
30
+ }
31
+ async function removePathIfExists(target, label) {
32
+ const resolved = path.resolve(target);
33
+ assertSafeRemovalPath(resolved, label);
34
+ await fsp.rm(resolved, { recursive: true, force: true });
35
+ }
36
+ async function dockerContainerExists(containerName) {
37
+ return await commandSucceeds('docker', ['container', 'inspect', containerName]);
38
+ }
39
+ async function removeDockerContainerIfExists(containerName) {
40
+ if (!(await dockerContainerExists(containerName))) {
41
+ return 'missing';
42
+ }
43
+ await run('docker', ['rm', '-f', containerName], {
44
+ errorName: 'docker rm',
45
+ stdio: 'ignore',
46
+ });
47
+ return 'removed';
48
+ }
49
+ async function dockerNetworkExists(networkName) {
50
+ return await commandSucceeds('docker', ['network', 'inspect', networkName]);
51
+ }
52
+ async function dockerNetworkHasActiveEndpoints(networkName) {
53
+ try {
54
+ const output = await commandOutput('docker', [
55
+ 'network',
56
+ 'inspect',
57
+ networkName,
58
+ '--format',
59
+ '{{json .Containers}}',
60
+ ], {
61
+ errorName: 'docker network inspect',
62
+ });
63
+ const containers = JSON.parse(output || '{}');
64
+ return Boolean(containers && typeof containers === 'object' && Object.keys(containers).length > 0);
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ async function removeDockerNetworkIfUnused(networkName) {
71
+ if (!(await dockerNetworkExists(networkName))) {
72
+ return 'missing';
73
+ }
74
+ try {
75
+ await run('docker', ['network', 'rm', networkName], {
76
+ errorName: 'docker network rm',
77
+ stdio: 'ignore',
78
+ });
79
+ return 'removed';
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ if (/has active endpoints|is in use|active endpoints/i.test(message)
84
+ || (await dockerNetworkExists(networkName) && await dockerNetworkHasActiveEndpoints(networkName))) {
85
+ return 'in-use';
86
+ }
87
+ throw error;
88
+ }
89
+ }
90
+ function builtinDbContainerName(runtime) {
91
+ if (!runtime.env.config.builtinDb) {
92
+ return undefined;
93
+ }
94
+ const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
95
+ return buildDockerDbContainerName(runtime.envName, dbDialect, runtime.dockerContainerPrefix || runtime.workspaceName);
96
+ }
97
+ function managedDockerNetworkName(runtime) {
98
+ return runtime.dockerNetworkName?.trim() || runtime.workspaceName?.trim() || undefined;
99
+ }
100
+ async function confirmDownAll(envName, yes) {
101
+ if (yes) {
102
+ return true;
103
+ }
104
+ if (!isInteractiveTerminal()) {
105
+ throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --yes to delete everything for "${envName}" in non-interactive mode.`);
106
+ }
107
+ const answer = await p.confirm({
108
+ message: `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
109
+ active: 'yes',
110
+ inactive: 'no',
111
+ initialValue: false,
112
+ });
113
+ if (p.isCancel(answer)) {
114
+ p.cancel('Down cancelled.');
115
+ return false;
116
+ }
117
+ return answer;
118
+ }
119
+ function formatDownFailure(envName, message) {
120
+ return [
121
+ `Couldn't bring down NocoBase for "${envName}".`,
122
+ 'Some local runtime resources may still exist. Check Docker or the local app process, then try again.',
123
+ `Details: ${message}`,
124
+ ].join('\n');
125
+ }
126
+ export default class AppDown extends Command {
127
+ static hidden = false;
128
+ static description = 'Bring down the selected env by removing runtime containers and the saved local app files. Storage data and env config are kept unless explicitly requested.';
129
+ static examples = [
130
+ '<%= config.bin %> <%= command.id %> --env app1',
131
+ '<%= config.bin %> <%= command.id %> --env app1 --all --yes',
132
+ ];
133
+ static flags = {
134
+ env: Flags.string({
135
+ char: 'e',
136
+ description: 'CLI env name to bring down. Defaults to the current env when omitted',
137
+ }),
138
+ all: Flags.boolean({
139
+ description: 'Delete everything for this env, including storage data and the saved env config',
140
+ default: false,
141
+ }),
142
+ yes: Flags.boolean({
143
+ char: 'y',
144
+ description: 'Confirm destructive actions without prompting',
145
+ default: false,
146
+ }),
147
+ verbose: Flags.boolean({
148
+ description: 'Show raw output from shutdown commands',
149
+ default: false,
150
+ }),
151
+ };
152
+ async run() {
153
+ const { flags } = await this.parse(AppDown);
154
+ const requestedEnv = flags.env?.trim() || undefined;
155
+ const removeData = Boolean(flags.all);
156
+ const removeEnvConfig = Boolean(flags.all);
157
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
158
+ if (!runtime) {
159
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
160
+ }
161
+ if (runtime.kind === 'http') {
162
+ this.error([
163
+ `Can't bring down "${runtime.envName}" from this machine.`,
164
+ 'This env only has an API connection, so there is no saved local app, Docker app, or managed database to remove here.',
165
+ 'Use `nb env remove` if you only want to remove the CLI connection config.',
166
+ ].join('\n'));
167
+ }
168
+ if (runtime.kind === 'ssh') {
169
+ this.error([
170
+ `Can't bring down "${runtime.envName}" yet.`,
171
+ 'SSH env support is reserved but not implemented yet.',
172
+ 'Use `nb env remove` if you only want to remove the saved CLI config for now.',
173
+ ].join('\n'));
174
+ }
175
+ if (flags.all) {
176
+ let confirmed = false;
177
+ try {
178
+ confirmed = await confirmDownAll(runtime.envName, flags.yes);
179
+ }
180
+ catch (error) {
181
+ this.error(error instanceof Error ? error.message : String(error));
182
+ }
183
+ if (!confirmed) {
184
+ return;
185
+ }
186
+ }
187
+ try {
188
+ if (runtime.kind === 'docker') {
189
+ startTask(`Removing Docker app container for "${runtime.envName}"...`);
190
+ const state = await removeDockerContainerIfExists(runtime.containerName);
191
+ succeedTask(state === 'removed'
192
+ ? `Docker app container removed for "${runtime.envName}".`
193
+ : `No Docker app container found for "${runtime.envName}".`);
194
+ }
195
+ else {
196
+ startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
197
+ await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
198
+ stdio: flags.verbose ? 'inherit' : 'ignore',
199
+ });
200
+ succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
201
+ }
202
+ if (runtime.kind === 'local' || runtime.kind === 'docker') {
203
+ const dbContainerName = builtinDbContainerName(runtime);
204
+ if (dbContainerName) {
205
+ startTask(`Removing built-in database container for "${runtime.envName}"...`);
206
+ const state = await removeDockerContainerIfExists(dbContainerName);
207
+ succeedTask(state === 'removed'
208
+ ? `Built-in database container removed for "${runtime.envName}".`
209
+ : `No built-in database container found for "${runtime.envName}".`);
210
+ }
211
+ const networkName = managedDockerNetworkName(runtime);
212
+ if (networkName) {
213
+ startTask(`Removing Docker network for "${runtime.envName}" if unused...`);
214
+ const state = await removeDockerNetworkIfUnused(networkName);
215
+ if (state === 'removed') {
216
+ succeedTask(`Docker network removed for "${runtime.envName}".`);
217
+ }
218
+ else if (state === 'missing') {
219
+ succeedTask(`No Docker network found for "${runtime.envName}".`);
220
+ }
221
+ else {
222
+ succeedTask(`Docker network is still in use for "${runtime.envName}". Keeping it.`);
223
+ }
224
+ }
225
+ }
226
+ if (runtime.kind === 'local') {
227
+ const localAppPath = resolveConfiguredPath(runtime.env.config.appRootPath) || runtime.projectRoot;
228
+ if (localAppPath) {
229
+ startTask(`Removing local app files for "${runtime.envName}"...`);
230
+ await removePathIfExists(localAppPath, `app files for "${runtime.envName}"`);
231
+ succeedTask(`Local app files removed for "${runtime.envName}".`);
232
+ }
233
+ else {
234
+ printInfo(`No saved local app path found for "${runtime.envName}".`);
235
+ }
236
+ }
237
+ if (removeData) {
238
+ const configuredStoragePath = resolveConfiguredPath(runtime.env.config.storagePath);
239
+ if (configuredStoragePath) {
240
+ startTask(`Removing storage data for "${runtime.envName}"...`);
241
+ await removePathIfExists(configuredStoragePath, `storage data for "${runtime.envName}"`);
242
+ succeedTask(`Storage data removed for "${runtime.envName}".`);
243
+ }
244
+ else {
245
+ printInfo(`No saved storage path found for "${runtime.envName}".`);
246
+ }
247
+ }
248
+ if (removeEnvConfig) {
249
+ startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
250
+ const result = await removeEnv(runtime.envName);
251
+ succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.currentEnv ? ` (current env: ${result.currentEnv})` : ''}.`);
252
+ }
253
+ }
254
+ catch (error) {
255
+ failTask(`Failed to bring down NocoBase for "${runtime.envName}".`);
256
+ this.error(formatDownFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
257
+ }
258
+ }
259
+ }