@nocobase/cli 2.1.0-beta.9 → 2.2.0-alpha.1

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 (263) hide show
  1. package/assets/env-proxy/nginx/app.conf.tpl +23 -0
  2. package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
  3. package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
  4. package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
  5. package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
  6. package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
  7. package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
  8. package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
  9. package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
  10. package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
  11. package/bin/run.cmd +3 -0
  12. package/bin/run.js +145 -0
  13. package/bin/session-env.js +39 -0
  14. package/dist/commands/api/resource/create.js +15 -0
  15. package/dist/commands/api/resource/destroy.js +15 -0
  16. package/dist/commands/api/resource/get.js +15 -0
  17. package/dist/commands/api/resource/index.js +20 -0
  18. package/dist/commands/api/resource/list.js +16 -0
  19. package/dist/commands/api/resource/query.js +15 -0
  20. package/dist/commands/api/resource/update.js +15 -0
  21. package/dist/commands/app/autostart/disable.js +55 -0
  22. package/dist/commands/app/autostart/enable.js +55 -0
  23. package/dist/commands/app/autostart/list.js +37 -0
  24. package/dist/commands/app/autostart/run.js +84 -0
  25. package/dist/commands/app/autostart/shared.js +49 -0
  26. package/dist/commands/app/destroy.js +234 -0
  27. package/dist/commands/app/down.js +71 -0
  28. package/dist/commands/app/logs.js +115 -0
  29. package/dist/commands/app/restart.js +229 -0
  30. package/dist/commands/app/shared.js +123 -0
  31. package/dist/commands/app/start.js +416 -0
  32. package/dist/commands/app/stop.js +183 -0
  33. package/dist/commands/app/upgrade.js +523 -0
  34. package/dist/commands/backup/create.js +147 -0
  35. package/dist/commands/backup/index.js +20 -0
  36. package/dist/commands/backup/restore.js +105 -0
  37. package/{src/cli.js → dist/commands/build.js} +4 -11
  38. package/dist/commands/config/delete.js +42 -0
  39. package/dist/commands/config/get.js +39 -0
  40. package/dist/commands/config/index.js +20 -0
  41. package/dist/commands/config/list.js +29 -0
  42. package/dist/commands/config/set.js +49 -0
  43. package/dist/commands/db/check.js +240 -0
  44. package/dist/commands/db/logs.js +85 -0
  45. package/dist/commands/db/ps.js +47 -0
  46. package/dist/commands/db/shared.js +96 -0
  47. package/dist/commands/db/start.js +86 -0
  48. package/dist/commands/db/stop.js +71 -0
  49. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  50. package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
  51. package/dist/commands/download.js +13 -0
  52. package/dist/commands/env/add.js +406 -0
  53. package/dist/commands/env/auth.js +189 -0
  54. package/dist/commands/env/current.js +21 -0
  55. package/dist/commands/env/info.js +202 -0
  56. package/dist/commands/env/list.js +43 -0
  57. package/dist/commands/env/remove.js +174 -0
  58. package/dist/commands/env/shared.js +204 -0
  59. package/dist/commands/env/status.js +93 -0
  60. package/dist/commands/env/update.js +448 -0
  61. package/dist/commands/env/use.js +38 -0
  62. package/dist/commands/examples/prompts-stages.js +150 -0
  63. package/dist/commands/examples/prompts-test.js +181 -0
  64. package/dist/commands/init.js +1390 -0
  65. package/dist/commands/install.js +2609 -0
  66. package/dist/commands/license/activate.js +179 -0
  67. package/dist/commands/license/env.js +94 -0
  68. package/dist/commands/license/generate-id.js +108 -0
  69. package/dist/commands/license/id.js +70 -0
  70. package/dist/commands/license/index.js +20 -0
  71. package/dist/commands/license/plugins/clean.js +115 -0
  72. package/dist/commands/license/plugins/index.js +20 -0
  73. package/dist/commands/license/plugins/list.js +64 -0
  74. package/dist/commands/license/plugins/shared.js +382 -0
  75. package/dist/commands/license/plugins/sync.js +314 -0
  76. package/dist/commands/license/shared.js +423 -0
  77. package/dist/commands/license/status.js +64 -0
  78. package/dist/commands/logs.js +12 -0
  79. package/dist/commands/plugin/disable.js +86 -0
  80. package/dist/commands/plugin/enable.js +86 -0
  81. package/dist/commands/plugin/import.js +108 -0
  82. package/dist/commands/plugin/list.js +82 -0
  83. package/dist/commands/pm/disable.js +12 -0
  84. package/dist/commands/pm/enable.js +12 -0
  85. package/dist/commands/pm/list.js +12 -0
  86. package/dist/commands/proxy/caddy/current.js +17 -0
  87. package/dist/commands/proxy/caddy/generate.js +69 -0
  88. package/dist/commands/proxy/caddy/index.js +28 -0
  89. package/dist/commands/proxy/caddy/info.js +31 -0
  90. package/dist/commands/proxy/caddy/reload.js +30 -0
  91. package/dist/commands/proxy/caddy/restart.js +28 -0
  92. package/dist/commands/proxy/caddy/start.js +30 -0
  93. package/dist/commands/proxy/caddy/status.js +19 -0
  94. package/dist/commands/proxy/caddy/stop.js +30 -0
  95. package/dist/commands/proxy/caddy/use.js +26 -0
  96. package/dist/commands/proxy/index.js +28 -0
  97. package/dist/commands/proxy/nginx/current.js +18 -0
  98. package/dist/commands/proxy/nginx/generate.js +68 -0
  99. package/dist/commands/proxy/nginx/index.js +28 -0
  100. package/dist/commands/proxy/nginx/info.js +34 -0
  101. package/dist/commands/proxy/nginx/reload.js +30 -0
  102. package/dist/commands/proxy/nginx/restart.js +28 -0
  103. package/dist/commands/proxy/nginx/start.js +30 -0
  104. package/dist/commands/proxy/nginx/status.js +19 -0
  105. package/dist/commands/proxy/nginx/stop.js +30 -0
  106. package/dist/commands/proxy/nginx/use.js +31 -0
  107. package/dist/commands/restart.js +12 -0
  108. package/dist/commands/revision/create.js +118 -0
  109. package/dist/commands/scaffold/migration.js +38 -0
  110. package/dist/commands/scaffold/plugin.js +37 -0
  111. package/dist/commands/self/check.js +71 -0
  112. package/dist/commands/self/index.js +20 -0
  113. package/dist/commands/self/update.js +152 -0
  114. package/dist/commands/session/id.js +24 -0
  115. package/dist/commands/session/remove.js +57 -0
  116. package/dist/commands/session/setup.js +62 -0
  117. package/dist/commands/skills/check.js +69 -0
  118. package/dist/commands/skills/index.js +20 -0
  119. package/dist/commands/skills/install.js +80 -0
  120. package/dist/commands/skills/remove.js +80 -0
  121. package/dist/commands/skills/update.js +87 -0
  122. package/dist/commands/source/build.js +58 -0
  123. package/dist/commands/source/dev.js +182 -0
  124. package/dist/commands/source/download.js +884 -0
  125. package/dist/commands/source/publish.js +109 -0
  126. package/dist/commands/source/registry/logs.js +70 -0
  127. package/dist/commands/source/registry/start.js +57 -0
  128. package/dist/commands/source/registry/status.js +33 -0
  129. package/dist/commands/source/registry/stop.js +48 -0
  130. package/dist/commands/source/test.js +476 -0
  131. package/dist/commands/start.js +12 -0
  132. package/dist/commands/stop.js +12 -0
  133. package/dist/commands/test.js +12 -0
  134. package/dist/commands/upgrade.js +12 -0
  135. package/dist/commands/v1.js +210 -0
  136. package/dist/generated/command-registry.js +134 -0
  137. package/dist/help/runtime-help.js +23 -0
  138. package/dist/lib/api-client.js +335 -0
  139. package/dist/lib/api-command-compat.js +641 -0
  140. package/dist/lib/app-health.js +139 -0
  141. package/dist/lib/app-managed-resources.js +337 -0
  142. package/dist/lib/app-public-path.js +80 -0
  143. package/dist/lib/app-runtime.js +189 -0
  144. package/dist/lib/auth-store.js +528 -0
  145. package/dist/lib/backup.js +171 -0
  146. package/dist/lib/bootstrap.js +409 -0
  147. package/dist/lib/build-config.js +18 -0
  148. package/dist/lib/builtin-db.js +86 -0
  149. package/dist/lib/cli-config.js +569 -0
  150. package/dist/lib/cli-entry-error.js +52 -0
  151. package/dist/lib/cli-home.js +47 -0
  152. package/dist/lib/cli-locale.js +141 -0
  153. package/dist/lib/command-discovery.js +39 -0
  154. package/dist/lib/command-log.js +284 -0
  155. package/dist/lib/db-connection-check.js +219 -0
  156. package/dist/lib/docker-env-file.js +60 -0
  157. package/dist/lib/docker-image.js +37 -0
  158. package/dist/lib/docker-log-stream.js +45 -0
  159. package/dist/lib/env-auth.js +963 -0
  160. package/dist/lib/env-command-config.js +45 -0
  161. package/dist/lib/env-config.js +108 -0
  162. package/dist/lib/env-guard.js +61 -0
  163. package/dist/lib/env-paths.js +101 -0
  164. package/dist/lib/env-proxy.js +1325 -0
  165. package/dist/lib/generated-command.js +203 -0
  166. package/dist/lib/http-request.js +49 -0
  167. package/dist/lib/inquirer-theme.js +17 -0
  168. package/dist/lib/inquirer.js +243 -0
  169. package/dist/lib/managed-env-file.js +101 -0
  170. package/dist/lib/managed-init-env.js +32 -0
  171. package/dist/lib/naming.js +70 -0
  172. package/dist/lib/object-utils.js +76 -0
  173. package/dist/lib/openapi.js +62 -0
  174. package/dist/lib/plugin-import.js +279 -0
  175. package/dist/lib/plugin-storage.js +64 -0
  176. package/dist/lib/post-processors.js +23 -0
  177. package/dist/lib/prompt-catalog-core.js +186 -0
  178. package/dist/lib/prompt-catalog-terminal.js +374 -0
  179. package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
  180. package/dist/lib/prompt-validators.js +278 -0
  181. package/dist/lib/prompt-web-ui.js +2234 -0
  182. package/dist/lib/proxy-caddy.js +274 -0
  183. package/dist/lib/proxy-nginx.js +330 -0
  184. package/dist/lib/resource-command.js +357 -0
  185. package/dist/lib/resource-request.js +104 -0
  186. package/dist/lib/run-npm.js +429 -0
  187. package/dist/lib/runtime-env-vars.js +32 -0
  188. package/dist/lib/runtime-generator.js +498 -0
  189. package/dist/lib/runtime-store.js +56 -0
  190. package/dist/lib/self-manager.js +301 -0
  191. package/dist/lib/session-id.js +17 -0
  192. package/dist/lib/session-integration.js +703 -0
  193. package/dist/lib/session-store.js +118 -0
  194. package/dist/lib/skills-manager.js +438 -0
  195. package/dist/lib/source-publish.js +326 -0
  196. package/dist/lib/source-registry.js +188 -0
  197. package/dist/lib/startup-update.js +309 -0
  198. package/dist/lib/ui.js +159 -0
  199. package/dist/locale/en-US.json +526 -0
  200. package/dist/locale/zh-CN.json +526 -0
  201. package/dist/post-processors/data-modeling.js +84 -0
  202. package/dist/post-processors/data-source-manager.js +138 -0
  203. package/dist/post-processors/index.js +19 -0
  204. package/nocobase-ctl.config.json +388 -0
  205. package/package.json +128 -24
  206. package/scripts/build.mjs +34 -0
  207. package/scripts/clean.mjs +9 -0
  208. package/tsconfig.json +19 -0
  209. package/bin/index.js +0 -39
  210. package/nocobase.conf.tpl +0 -95
  211. package/src/commands/benchmark.js +0 -73
  212. package/src/commands/build.js +0 -49
  213. package/src/commands/clean.js +0 -30
  214. package/src/commands/client.js +0 -166
  215. package/src/commands/create-nginx-conf.js +0 -37
  216. package/src/commands/create-plugin.js +0 -33
  217. package/src/commands/dev.js +0 -200
  218. package/src/commands/doc.js +0 -76
  219. package/src/commands/e2e.js +0 -265
  220. package/src/commands/global.js +0 -43
  221. package/src/commands/index.js +0 -45
  222. package/src/commands/instance-id.js +0 -47
  223. package/src/commands/locale/cronstrue.js +0 -122
  224. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  225. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  226. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  227. package/src/commands/locale.js +0 -81
  228. package/src/commands/p-test.js +0 -88
  229. package/src/commands/perf.js +0 -63
  230. package/src/commands/pkg.js +0 -321
  231. package/src/commands/pm2.js +0 -37
  232. package/src/commands/postinstall.js +0 -88
  233. package/src/commands/start.js +0 -148
  234. package/src/commands/tar.js +0 -36
  235. package/src/commands/test-coverage.js +0 -55
  236. package/src/commands/test.js +0 -107
  237. package/src/commands/umi.js +0 -33
  238. package/src/commands/update-deps.js +0 -72
  239. package/src/commands/upgrade.js +0 -47
  240. package/src/commands/view-license-key.js +0 -44
  241. package/src/license.js +0 -76
  242. package/src/logger.js +0 -75
  243. package/src/plugin-generator.js +0 -80
  244. package/src/util.js +0 -517
  245. package/templates/bundle-status.html +0 -338
  246. package/templates/create-app-package.json +0 -39
  247. package/templates/plugin/.npmignore.tpl +0 -2
  248. package/templates/plugin/README.md.tpl +0 -1
  249. package/templates/plugin/client.d.ts +0 -2
  250. package/templates/plugin/client.js +0 -1
  251. package/templates/plugin/package.json.tpl +0 -11
  252. package/templates/plugin/server.d.ts +0 -2
  253. package/templates/plugin/server.js +0 -1
  254. package/templates/plugin/src/client/client.d.ts +0 -249
  255. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  256. package/templates/plugin/src/client/locale.ts +0 -21
  257. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  258. package/templates/plugin/src/index.ts +0 -2
  259. package/templates/plugin/src/locale/en-US.json +0 -1
  260. package/templates/plugin/src/locale/zh-CN.json +0 -1
  261. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  262. package/templates/plugin/src/server/index.ts.tpl +0 -1
  263. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,1390 @@
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 { Command, Flags } from '@oclif/core';
10
+ import pc from 'picocolors';
11
+ import crypto from 'node:crypto';
12
+ import { existsSync } from 'node:fs';
13
+ import path from 'node:path';
14
+ import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
15
+ import { getEnv, upsertEnv } from "../lib/auth-store.js";
16
+ import { runPromptCatalog, } from "../lib/prompt-catalog.js";
17
+ import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
18
+ import { resolveDefaultConfigScope } from '../lib/cli-home.js';
19
+ import { resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
20
+ import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
21
+ import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
22
+ import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
23
+ import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
24
+ import { installNocoBaseSkills, isNpmRegistryUnavailable } from '../lib/skills-manager.js';
25
+ import { omitKeys, pickKeys } from "../lib/object-utils.js";
26
+ import { printInfo, printStage, printVerbose, printWarning } from '../lib/ui.js';
27
+ import Download from "./download.js";
28
+ import EnvAdd from "./env/add.js";
29
+ import Install, { defaultDbPortForDialect } from "./install.js";
30
+ const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
31
+ const DEFAULT_INIT_APP_NAME = 'local';
32
+ const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
33
+ const INIT_SETUP_MODES = ['install-new', 'manage-local', 'connect-remote'];
34
+ const INIT_ENV_ADD_FLAG_NAMES = [
35
+ 'locale',
36
+ 'default-api-base-url',
37
+ 'api-base-url',
38
+ 'auth-type',
39
+ 'access-token',
40
+ 'token',
41
+ 'username',
42
+ 'password',
43
+ 'skip-auth',
44
+ ];
45
+ const initText = (key, values) => localeText(`commands.init.${key}`, values);
46
+ function withExtraHidden(def, extraHidden) {
47
+ if (def.type === 'run') {
48
+ return def;
49
+ }
50
+ return {
51
+ ...def,
52
+ hidden: (values) => extraHidden(values) || (def.hidden?.(values) ?? false),
53
+ };
54
+ }
55
+ function normalizeInitSetupMode(value) {
56
+ const mode = String(value ?? '').trim();
57
+ if (mode === 'manage-local' || mode === 'connect-remote' || mode === 'install-new') {
58
+ return mode;
59
+ }
60
+ if (mode === 'yes') {
61
+ return 'connect-remote';
62
+ }
63
+ if (mode === 'no') {
64
+ return 'install-new';
65
+ }
66
+ return 'install-new';
67
+ }
68
+ function resolveInitSetupMode(values) {
69
+ return normalizeInitSetupMode(values.setupMode ?? values.hasNocobase);
70
+ }
71
+ function isRemoteSetupMode(values) {
72
+ return resolveInitSetupMode(values) === 'connect-remote';
73
+ }
74
+ function isInstallNewSetupMode(values) {
75
+ return resolveInitSetupMode(values) === 'install-new';
76
+ }
77
+ function isInstallLikeSetupMode(values) {
78
+ return !isRemoteSetupMode(values);
79
+ }
80
+ function remoteConnectionOnly(def) {
81
+ return withExtraHidden(def, (values) => !isRemoteSetupMode(values));
82
+ }
83
+ function installLikeOnly(def) {
84
+ return withExtraHidden(def, (values) => !isInstallLikeSetupMode(values));
85
+ }
86
+ function installNewOnly(def) {
87
+ return withExtraHidden(def, (values) => !isInstallNewSetupMode(values));
88
+ }
89
+ function installConnectionOnly(def) {
90
+ return withExtraHidden(def, (values) => !isInstallNewSetupMode(values));
91
+ }
92
+ function installLikeDownloadExecutionOnly(def) {
93
+ return withExtraHidden(def, (values) => !isInstallLikeSetupMode(values) || values.skipDownload === true);
94
+ }
95
+ function argvHasToken(argv, tokens) {
96
+ return tokens.some((token) => argv.includes(token));
97
+ }
98
+ function resolveInitDownloadVersion(results) {
99
+ const preset = String(results.version ?? '').trim();
100
+ if (preset === 'other') {
101
+ return String(results.otherVersion ?? '').trim();
102
+ }
103
+ return preset;
104
+ }
105
+ function initVersionPromptValue(version) {
106
+ return version === 'latest' || version === 'beta' || version === 'alpha' ? version : 'other';
107
+ }
108
+ function yesInitialValue(def, fallback) {
109
+ if ('yesInitialValue' in def && def.yesInitialValue !== undefined) {
110
+ return String(def.yesInitialValue);
111
+ }
112
+ return fallback;
113
+ }
114
+ function hasDownloadOverride(flags) {
115
+ return Boolean(String(flags.source ?? '').trim() || String(flags.version ?? '').trim());
116
+ }
117
+ function explicitApiBaseUrlFlag(flags) {
118
+ return String(flags['api-base-url'] ?? '').trim();
119
+ }
120
+ function explicitDbHostFlag(flags) {
121
+ return String(flags['db-host'] ?? '').trim();
122
+ }
123
+ function explicitSetupModeFlag(flags) {
124
+ const mode = String(flags['setup-mode'] ?? '').trim();
125
+ return mode ? normalizeInitSetupMode(mode) : undefined;
126
+ }
127
+ function applyLegacyHasNocobaseAlias(values) {
128
+ if (!Object.prototype.hasOwnProperty.call(values, 'setupMode') &&
129
+ !Object.prototype.hasOwnProperty.call(values, 'hasNocobase')) {
130
+ return;
131
+ }
132
+ const setupMode = resolveInitSetupMode(values);
133
+ if (setupMode === 'connect-remote') {
134
+ values.hasNocobase = 'yes';
135
+ return;
136
+ }
137
+ if (setupMode === 'install-new') {
138
+ values.hasNocobase = 'no';
139
+ return;
140
+ }
141
+ delete values.hasNocobase;
142
+ }
143
+ function optionalInitString(value) {
144
+ const text = String(value ?? '').trim();
145
+ return text || undefined;
146
+ }
147
+ function resolveManagedAppKey(value) {
148
+ return optionalInitString(value) ?? crypto.randomBytes(32).toString('hex');
149
+ }
150
+ function resolveManagedTimeZone(value) {
151
+ return optionalInitString(value) ?? (Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC');
152
+ }
153
+ function normalizeConnectionString(value) {
154
+ return String(value ?? '').trim();
155
+ }
156
+ function deriveInstallConnectionApiBaseUrl(values, defaultApiHost = '127.0.0.1') {
157
+ const appPort = normalizeConnectionString(values.appPort);
158
+ return appPort ? `http://${defaultApiHost}:${appPort}/api` : DEFAULT_INIT_API_BASE_URL;
159
+ }
160
+ function createInstallConnectionApiBaseUrlPrompt(defaultApiHost) {
161
+ return installConnectionOnly({
162
+ type: 'text',
163
+ message: initText('prompts.apiBaseUrl.message'),
164
+ placeholder: initText('prompts.apiBaseUrl.placeholder'),
165
+ required: true,
166
+ initialValue: (values) => deriveInstallConnectionApiBaseUrl(values, defaultApiHost),
167
+ });
168
+ }
169
+ const installConnectionAuthTypePrompt = installConnectionOnly({
170
+ ...EnvAdd.prompts.authType,
171
+ });
172
+ const installConnectionUsernamePrompt = installConnectionOnly({
173
+ ...Install.rootUserPrompts.rootUsername,
174
+ hidden: () => true,
175
+ initialValue: (values) => normalizeConnectionString(values.rootUsername),
176
+ });
177
+ const installConnectionPasswordPrompt = installConnectionOnly({
178
+ ...Install.rootUserPrompts.rootPassword,
179
+ hidden: () => true,
180
+ initialValue: (values) => String(values.rootPassword ?? ''),
181
+ });
182
+ const installConnectionAccessTokenPrompt = installConnectionOnly({
183
+ ...EnvAdd.prompts.accessToken,
184
+ hidden: (values) => values.installAuthType !== 'token' || values.skipAuth === true,
185
+ });
186
+ function shouldAllowExistingInitEnv() {
187
+ return argvHasToken(process.argv.slice(2), ['--force', '-f']);
188
+ }
189
+ async function validateInitAppName(value) {
190
+ const formatError = validateEnvKey(value);
191
+ if (formatError) {
192
+ return formatError;
193
+ }
194
+ const envName = String(value ?? '').trim();
195
+ if (!envName) {
196
+ return undefined;
197
+ }
198
+ const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
199
+ if (existingEnv) {
200
+ if (shouldAllowExistingInitEnv()) {
201
+ return undefined;
202
+ }
203
+ return translateCli('commands.init.validation.envExists', { envName });
204
+ }
205
+ return undefined;
206
+ }
207
+ function highlightInitValidationMessage(message) {
208
+ return message.replace(/Env "([^"]+)"/, (_match, envName) => `Env ${pc.cyan(pc.bold(`"${envName}"`))}`);
209
+ }
210
+ function formatInitValidationMessage(message) {
211
+ return message;
212
+ }
213
+ function formatResumeEnvRequiredMessage() {
214
+ return [
215
+ translateCli('commands.init.messages.resumeEnvRequired'),
216
+ translateCli('commands.init.messages.resumeEnvHelp'),
217
+ ].join('\n');
218
+ }
219
+ function formatSkippedAppNameRequiredMessage() {
220
+ return [
221
+ translateCli('commands.init.messages.appNameRequiredWhenSkipped'),
222
+ translateCli('commands.init.messages.appNameEnvHelp'),
223
+ ].join('\n');
224
+ }
225
+ function shellQuoteArg(value) {
226
+ return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
227
+ }
228
+ function initTitle() {
229
+ return 'Set up NocoBase';
230
+ }
231
+ function logInitStage(title) {
232
+ printStage(title);
233
+ }
234
+ function logInitUiReady(command, url) {
235
+ command.log(translateCli('commands.init.messages.uiReady'));
236
+ command.log(translateCli('commands.init.messages.uiReadyHelp'));
237
+ command.log(`URL: ${url}`);
238
+ }
239
+ function logInitUiBrowserOpenFallback() {
240
+ printWarning(translateCli('commands.init.messages.uiOpenBrowserFallback'));
241
+ }
242
+ function formatBrowserOpenError(error) {
243
+ if (error instanceof Error) {
244
+ return error.message;
245
+ }
246
+ return String(error);
247
+ }
248
+ export default class Init extends Command {
249
+ static summary = 'Set up NocoBase so coding agents can connect and work with it';
250
+ static description = `Set up NocoBase for coding agents in the current workspace.
251
+
252
+ \`nb init\` prepares a NocoBase environment that coding agents can use. It supports three setup paths:
253
+
254
+ - Install a new NocoBase app, then save it as a CLI env.
255
+ - Take over managing an app that already exists on this machine by reusing its database.
256
+ - Connect a remote NocoBase app and save it as a CLI env.
257
+
258
+ It can also install NocoBase AI coding skills (\`nocobase/skills\`) so agents get the project-specific workflow guidance.
259
+
260
+ If setup was interrupted earlier, use \`--resume\` with an existing env name to continue from the saved workspace config.
261
+
262
+ Prompt modes:
263
+ - Default: guided prompts in the terminal.
264
+ - \`--ui\`: open the same setup flow in a local browser form.
265
+ - \`-y\`, \`--yes\`: skip prompts. In this mode \`--env <envName>\` is required, and init uses flags plus safe defaults for the selected setup mode.
266
+
267
+ \`--ui\` cannot be combined with \`--yes\`.`;
268
+ static examples = [
269
+ '<%= config.bin %> <%= command.id %>',
270
+ '<%= config.bin %> <%= command.id %> --env app1',
271
+ '<%= config.bin %> <%= command.id %> --env app1 --ui',
272
+ '<%= config.bin %> <%= command.id %> --ui',
273
+ '<%= config.bin %> <%= command.id %> --env app1 --yes',
274
+ '<%= config.bin %> <%= command.id %> --env app1 --resume',
275
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source docker --version alpha',
276
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --setup-mode manage-local --source npm --version beta',
277
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source npm --version alpha --app-port 13080',
278
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source git --version fix/cli-v2',
279
+ '<%= config.bin %> <%= command.id %> --env staging --yes --setup-mode connect-remote --api-base-url https://demo.example.com/api',
280
+ '<%= config.bin %> <%= command.id %> --ui --ui-port 3000',
281
+ ];
282
+ static prompts = {
283
+ seedResume: {
284
+ type: 'run',
285
+ run: (values, command) => {
286
+ const record = values;
287
+ if (record.resume === undefined) {
288
+ const flags = command?.parsedFlagsForPromptSeeds;
289
+ record.resume = Boolean(flags?.resume);
290
+ }
291
+ },
292
+ },
293
+ seedEnvName: {
294
+ type: 'run',
295
+ run: (values) => {
296
+ const record = values;
297
+ const appName = String(record.appName ?? '').trim();
298
+ if (appName && record.env === undefined) {
299
+ record.env = appName;
300
+ }
301
+ },
302
+ },
303
+ appName: {
304
+ type: 'text',
305
+ message: initText('prompts.appName.message'),
306
+ placeholder: initText('prompts.appName.placeholder'),
307
+ required: true,
308
+ validate: validateInitAppName,
309
+ },
310
+ setupMode: {
311
+ type: 'select',
312
+ variant: 'radio',
313
+ message: initText('prompts.setupMode.message'),
314
+ options: [
315
+ {
316
+ value: 'install-new',
317
+ label: initText('prompts.setupMode.installNewLabel'),
318
+ hint: initText('prompts.setupMode.installNewHint'),
319
+ },
320
+ {
321
+ value: 'connect-remote',
322
+ label: initText('prompts.setupMode.connectRemoteLabel'),
323
+ hint: initText('prompts.setupMode.connectRemoteHint'),
324
+ },
325
+ {
326
+ value: 'manage-local',
327
+ label: initText('prompts.setupMode.manageLocalLabel'),
328
+ hint: initText('prompts.setupMode.manageLocalHint'),
329
+ disabled: true,
330
+ },
331
+ ],
332
+ initialValue: 'install-new',
333
+ yesInitialValue: 'install-new',
334
+ required: true,
335
+ },
336
+ apiBaseUrl: remoteConnectionOnly({
337
+ type: 'text',
338
+ message: initText('prompts.apiBaseUrl.message'),
339
+ placeholder: initText('prompts.apiBaseUrl.placeholder'),
340
+ required: true,
341
+ validate: validateApiBaseUrl,
342
+ }),
343
+ authType: remoteConnectionOnly(EnvAdd.prompts.authType),
344
+ username: remoteConnectionOnly(EnvAdd.prompts.username),
345
+ password: remoteConnectionOnly(EnvAdd.prompts.password),
346
+ accessToken: remoteConnectionOnly(EnvAdd.prompts.accessToken),
347
+ lang: installLikeOnly(Install.appPrompts.lang),
348
+ appPath: installLikeOnly(Install.appPrompts.appPath),
349
+ appPort: installLikeOnly(Install.appPrompts.appPort),
350
+ appPublicPath: installLikeOnly(Install.appPrompts.appPublicPath),
351
+ skipDownload: installNewOnly({
352
+ type: 'boolean',
353
+ message: initText('prompts.skipDownload.message'),
354
+ initialValue: false,
355
+ yesInitialValue: false,
356
+ }),
357
+ source: installLikeOnly(Download.prompts.source),
358
+ version: installLikeOnly(Download.prompts.version),
359
+ otherVersion: installLikeOnly(Download.prompts.otherVersion),
360
+ dockerRegistry: installLikeOnly(Download.prompts.dockerRegistry),
361
+ dockerPlatform: installLikeOnly(Download.prompts.dockerPlatform),
362
+ dockerSave: installLikeDownloadExecutionOnly(Download.prompts.dockerSave),
363
+ gitUrl: installLikeOnly(Download.prompts.gitUrl),
364
+ outputDir: installLikeDownloadExecutionOnly({
365
+ ...DOWNLOAD_OUTPUT_DIR_PROMPT,
366
+ hidden: (values) => {
367
+ const source = String(values.source ?? '').trim();
368
+ if (source === 'npm' || source === 'git') {
369
+ return true;
370
+ }
371
+ return DOWNLOAD_OUTPUT_DIR_PROMPT.hidden?.(values) ?? false;
372
+ },
373
+ initialValue: (values) => {
374
+ const source = String(values.source ?? '').trim();
375
+ if (source === 'npm' || source === 'git') {
376
+ const appPath = String(values.appPath ?? inferConfiguredAppPathFromLegacyConfig(values) ?? '').trim();
377
+ const appRootPath = String(values.appRootPath ?? '').trim() || (appPath ? deriveConfiguredSourcePath(appPath) : '');
378
+ if (appRootPath) {
379
+ return appRootPath;
380
+ }
381
+ }
382
+ const initialValue = DOWNLOAD_OUTPUT_DIR_PROMPT.initialValue;
383
+ return typeof initialValue === 'function' ? initialValue(values) : String(initialValue ?? '');
384
+ },
385
+ }),
386
+ npmRegistry: installLikeOnly(Download.prompts.npmRegistry),
387
+ replace: installLikeDownloadExecutionOnly(Download.prompts.replace),
388
+ devDependencies: installLikeDownloadExecutionOnly(Download.prompts.devDependencies),
389
+ build: installLikeDownloadExecutionOnly(Download.prompts.build),
390
+ buildDts: installLikeDownloadExecutionOnly(Download.prompts.buildDts),
391
+ dbDialect: installLikeOnly(Install.dbPrompts.dbDialect),
392
+ builtinDb: installLikeOnly(Install.dbPrompts.builtinDb),
393
+ builtinDbImage: installLikeOnly(Install.dbPrompts.builtinDbImage),
394
+ dbHost: installLikeOnly(Install.dbPrompts.dbHost),
395
+ dbPort: installLikeOnly(Install.dbPrompts.dbPort),
396
+ dbDatabase: installLikeOnly(Install.dbPrompts.dbDatabase),
397
+ dbUser: installLikeOnly(Install.dbPrompts.dbUser),
398
+ dbPassword: installLikeOnly(Install.dbPrompts.dbPassword),
399
+ dbSchema: installLikeOnly(Install.dbPrompts.dbSchema),
400
+ dbTablePrefix: installLikeOnly(Install.dbPrompts.dbTablePrefix),
401
+ dbUnderscored: installLikeOnly(Install.dbPrompts.dbUnderscored),
402
+ rootUsername: installNewOnly(Install.rootUserPrompts.rootUsername),
403
+ rootEmail: installNewOnly(Install.rootUserPrompts.rootEmail),
404
+ rootPassword: installNewOnly(Install.rootUserPrompts.rootPassword),
405
+ rootNickname: installNewOnly(Install.rootUserPrompts.rootNickname),
406
+ installAuthType: installConnectionAuthTypePrompt,
407
+ installUsername: installConnectionUsernamePrompt,
408
+ installPassword: installConnectionPasswordPrompt,
409
+ installAccessToken: installConnectionAccessTokenPrompt,
410
+ };
411
+ buildPromptCatalog(flags, options) {
412
+ const prompts = {
413
+ ...Init.prompts,
414
+ installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
415
+ };
416
+ if (flags['skip-auth']) {
417
+ const accessTokenPrompt = {
418
+ ...EnvAdd.prompts.accessToken,
419
+ hidden: () => true,
420
+ };
421
+ const usernamePrompt = {
422
+ ...EnvAdd.prompts.username,
423
+ hidden: () => true,
424
+ };
425
+ const passwordPrompt = {
426
+ ...EnvAdd.prompts.password,
427
+ hidden: () => true,
428
+ };
429
+ const installAccessTokenPrompt = {
430
+ ...installConnectionAccessTokenPrompt,
431
+ hidden: () => true,
432
+ };
433
+ const installUsernamePrompt = {
434
+ ...installConnectionUsernamePrompt,
435
+ hidden: () => true,
436
+ };
437
+ const installPasswordPrompt = {
438
+ ...installConnectionPasswordPrompt,
439
+ hidden: () => true,
440
+ };
441
+ prompts.username = remoteConnectionOnly(usernamePrompt);
442
+ prompts.password = remoteConnectionOnly(passwordPrompt);
443
+ prompts.accessToken = remoteConnectionOnly(accessTokenPrompt);
444
+ prompts.installUsername = installUsernamePrompt;
445
+ prompts.installPassword = installPasswordPrompt;
446
+ prompts.installAccessToken = installAccessTokenPrompt;
447
+ }
448
+ return prompts;
449
+ }
450
+ parsedFlagsForPromptSeeds;
451
+ static flags = {
452
+ yes: Flags.boolean({
453
+ char: 'y',
454
+ description: 'Skip prompts and use flags plus defaults for the selected setup mode. Requires an env name.',
455
+ default: false,
456
+ }),
457
+ env: Flags.string({
458
+ char: 'e',
459
+ description: 'Env name for this setup. Required with --yes and --resume',
460
+ }),
461
+ 'setup-mode': Flags.string({
462
+ description: 'Setup mode: install a new app, manage a local app by reusing its database, or connect a remote app',
463
+ options: [...INIT_SETUP_MODES],
464
+ }),
465
+ ui: Flags.boolean({
466
+ description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
467
+ default: false,
468
+ }),
469
+ verbose: Flags.boolean({
470
+ description: 'Show detailed command output',
471
+ default: true,
472
+ }),
473
+ 'skip-skills': Flags.boolean({
474
+ description: 'Skip installing NocoBase AI coding skills during init',
475
+ default: false,
476
+ }),
477
+ 'ui-host': Flags.string({
478
+ description: 'Host for the local --ui setup server (default: 127.0.0.1)',
479
+ }),
480
+ 'ui-port': Flags.integer({
481
+ description: 'Port for the local --ui setup server; 0 lets the OS choose an available port',
482
+ min: 0,
483
+ max: 65535,
484
+ }),
485
+ ...pickKeys(EnvAdd.flags, INIT_ENV_ADD_FLAG_NAMES),
486
+ ...omitKeys(Install.flags, ['yes', 'env', 'verbose']),
487
+ };
488
+ async run() {
489
+ const parsedResult = await this.parse(Init);
490
+ applyCliLocale(parsedResult.flags.locale);
491
+ const flags = parsedResult.flags;
492
+ const normalizedFlags = { ...flags };
493
+ this.parsedFlagsForPromptSeeds = {
494
+ resume: Boolean(normalizedFlags.resume),
495
+ };
496
+ if (normalizedFlags.ui && normalizedFlags.yes) {
497
+ this.error('--ui cannot be used with --yes.');
498
+ }
499
+ if (normalizedFlags['skip-auth'] &&
500
+ (normalizedFlags['access-token'] !== undefined || normalizedFlags.token !== undefined)) {
501
+ this.error('--skip-auth cannot be used with --access-token or --token.');
502
+ }
503
+ if (normalizedFlags['prepare-only'] && explicitSetupModeFlag(normalizedFlags) === 'connect-remote') {
504
+ this.error('--prepare-only is only available for local or install-new setup flows.');
505
+ }
506
+ if (!normalizedFlags.ui && (normalizedFlags['ui-host'] !== undefined || normalizedFlags['ui-port'] !== undefined)) {
507
+ this.error('--ui-host and --ui-port require --ui.');
508
+ }
509
+ if (normalizedFlags.resume && !normalizedFlags.ui) {
510
+ const envName = String(normalizedFlags.env ?? '').trim();
511
+ if (!envName) {
512
+ this.error(formatResumeEnvRequiredMessage());
513
+ this.exit(1);
514
+ }
515
+ logInitStage(initTitle());
516
+ await this.syncNocoBaseSkills({
517
+ skip: Boolean(normalizedFlags['skip-skills']),
518
+ });
519
+ try {
520
+ await this.config.runCommand('install', this.buildResumeInstallArgv(normalizedFlags));
521
+ }
522
+ catch (error) {
523
+ const message = error instanceof Error ? error.message : String(error);
524
+ this.error(message);
525
+ }
526
+ return;
527
+ }
528
+ const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY);
529
+ const useBrowserUi = Boolean(normalizedFlags.ui);
530
+ let presetValues = this.buildPresetValuesFromFlags(normalizedFlags);
531
+ if (normalizedFlags.resume) {
532
+ const resumeEnvName = String(normalizedFlags.env ?? '').trim();
533
+ if (resumeEnvName) {
534
+ const resumeEnv = await getEnv(resumeEnvName, {
535
+ scope: resolveDefaultConfigScope(),
536
+ });
537
+ if (!resumeEnv) {
538
+ this.error(formatMissingManagedAppEnvMessage(resumeEnvName));
539
+ }
540
+ const resumePresetValues = Install.buildResumePresetValues(resumeEnv);
541
+ presetValues = {
542
+ ...resumePresetValues.envPreset,
543
+ ...resumePresetValues.appPreset,
544
+ ...resumePresetValues.downloadPreset,
545
+ ...resumePresetValues.dbPreset,
546
+ ...resumePresetValues.rootPreset,
547
+ ...resumePresetValues.envAddPreset,
548
+ ...presetValues,
549
+ };
550
+ const savedAppPort = String(resumeEnv.config.appPort ?? '').trim();
551
+ const savedDbPort = String(resumeEnv.config.dbPort ?? '').trim();
552
+ if (savedAppPort) {
553
+ presetValues.resumeSavedAppPort = savedAppPort;
554
+ }
555
+ if (savedDbPort) {
556
+ presetValues.resumeSavedDbPort = savedDbPort;
557
+ }
558
+ }
559
+ }
560
+ if (normalizedFlags.yes && !String(presetValues.appName ?? '').trim()) {
561
+ const formatted = formatSkippedAppNameRequiredMessage();
562
+ this.error(highlightInitValidationMessage(formatted));
563
+ this.exit(1);
564
+ }
565
+ const appName = String(presetValues.appName ?? '').trim();
566
+ if (useBrowserUi) {
567
+ logInitStage(initTitle());
568
+ this.log(translateCli('commands.init.messages.uiOpening'));
569
+ }
570
+ else {
571
+ logInitStage(initTitle());
572
+ if (normalizedFlags.yes) {
573
+ printInfo(`Non-interactive setup for env "${appName}" (--yes).`);
574
+ }
575
+ else if (!interactive) {
576
+ printWarning('No interactive terminal detected. NocoBase will be installed using the provided flags and safe defaults.');
577
+ }
578
+ }
579
+ const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
580
+ const defaultUiHost = await resolveDefaultUiHost();
581
+ const defaultApiHost = await resolveDefaultApiHost();
582
+ const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost });
583
+ if (useBrowserUi) {
584
+ presetValues = await runPromptCatalogWebUI({
585
+ stages: Init.buildWebUiStages(promptCatalog),
586
+ values: {
587
+ ...dynamicInitialValues,
588
+ ...presetValues,
589
+ },
590
+ host: normalizedFlags['ui-host']?.trim() || defaultUiHost,
591
+ port: normalizedFlags['ui-port'] ?? 0,
592
+ pageTitle: initText('webUi.pageTitle'),
593
+ documentHeading: initText('webUi.documentHeading'),
594
+ documentHint: initText('webUi.documentHint'),
595
+ onServerStart: ({ url }) => {
596
+ logInitUiReady(this, url);
597
+ },
598
+ onOpenBrowserError: (_url, err) => {
599
+ logInitUiBrowserOpenFallback();
600
+ this.log(`Browser open error: ${formatBrowserOpenError(err)}`);
601
+ },
602
+ });
603
+ }
604
+ const results = await runPromptCatalog(promptCatalog, {
605
+ initialValues: {
606
+ ...dynamicInitialValues,
607
+ ...(presetValues.setupMode === undefined && presetValues.hasNocobase !== undefined
608
+ ? { setupMode: normalizeInitSetupMode(presetValues.hasNocobase) }
609
+ : {}),
610
+ },
611
+ values: presetValues,
612
+ yes: normalizedFlags.yes || useBrowserUi || !interactive,
613
+ hooks: {
614
+ onCancel: () => {
615
+ this.exit(0);
616
+ },
617
+ onMissingNonInteractive: (message) => {
618
+ const formatted = formatInitValidationMessage(message);
619
+ this.error(highlightInitValidationMessage(formatted));
620
+ this.exit(1);
621
+ },
622
+ },
623
+ command: this,
624
+ });
625
+ const normalizedResults = this.normalizeInitResults({
626
+ ...pickKeys(presetValues, [
627
+ 'defaultApiHost',
628
+ 'authType',
629
+ 'accessToken',
630
+ 'dbSchema',
631
+ 'dbTablePrefix',
632
+ 'dbUnderscored',
633
+ 'installApiBaseUrl',
634
+ 'installAuthType',
635
+ 'installAccessToken',
636
+ 'installUsername',
637
+ 'installPassword',
638
+ 'skipAuth',
639
+ 'username',
640
+ 'password',
641
+ ]),
642
+ defaultApiHost,
643
+ ...results,
644
+ });
645
+ const setupMode = resolveInitSetupMode(normalizedResults);
646
+ const existingEnv = isInstallLikeSetupMode(normalizedResults)
647
+ ? await getEnv(String(normalizedResults.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
648
+ : undefined;
649
+ if (existingEnv && Boolean(normalizedFlags.force)) {
650
+ printWarning(`Reconfiguring existing env ${pc.cyan(pc.bold(`"${existingEnv.name}"`))} from the global config because ${pc.bold('--force')} was set. The env config will be updated before install starts, then refreshed again after install succeeds.`);
651
+ }
652
+ await this.syncNocoBaseSkills({
653
+ skip: Boolean(normalizedFlags['skip-skills']),
654
+ });
655
+ let managedInstallResults;
656
+ try {
657
+ // oclif explicit registry keys use `:` (e.g. `env:add`); users still type `nb env add`.
658
+ if (setupMode === 'connect-remote') {
659
+ logInitStage('Connecting to the env');
660
+ printVerbose('Running nb env add');
661
+ await this.config.runCommand('env:add', this.buildEnvAddArgv(normalizedResults));
662
+ }
663
+ else {
664
+ logInitStage('Saving env config');
665
+ await this.persistManagedEnvConfig(normalizedResults, normalizedFlags);
666
+ managedInstallResults = normalizedResults;
667
+ printInfo(`Saved env config for "${String(normalizedResults.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME}".`);
668
+ printVerbose('Running nb install');
669
+ await this.config.runCommand('install', this.buildInstallArgv(normalizedResults, normalizedFlags));
670
+ }
671
+ }
672
+ catch (error) {
673
+ const message = error instanceof Error ? error.message : String(error);
674
+ const formatted = managedInstallResults
675
+ ? await this.formatManagedInstallFailureMessage(message, managedInstallResults, normalizedFlags)
676
+ : message;
677
+ this.error(pc.red(formatted));
678
+ this.exit(1);
679
+ }
680
+ }
681
+ static async buildDynamicInitialValuesForInstall(flags, presetValues) {
682
+ const out = {};
683
+ if (!Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
684
+ const appInitialValues = await Install.buildAppPromptInitialValues({
685
+ envName: String(presetValues.appName ?? '').trim(),
686
+ flags: {
687
+ ...flags,
688
+ 'app-path': flags['app-path'] ?? '',
689
+ 'app-root-path': flags['app-root-path'] ?? '',
690
+ 'storage-path': flags['storage-path'] ?? '',
691
+ },
692
+ warnOnPortFallback: false,
693
+ });
694
+ if (appInitialValues.appPort !== undefined) {
695
+ out.appPort = appInitialValues.appPort;
696
+ }
697
+ }
698
+ const downloadSeed = { ...presetValues };
699
+ if (flags.yes && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')) {
700
+ downloadSeed.source = 'docker';
701
+ }
702
+ const dbInitial = await Install.buildDbPromptInitialValues({
703
+ flags,
704
+ downloadResults: downloadSeed,
705
+ dbPreset: presetValues,
706
+ warnOnPortFallback: false,
707
+ });
708
+ for (const [key, value] of Object.entries(dbInitial)) {
709
+ if (!Object.prototype.hasOwnProperty.call(presetValues, key)) {
710
+ out[key] = value;
711
+ }
712
+ }
713
+ return out;
714
+ }
715
+ static buildWebUiStages(c = Init.prompts) {
716
+ return [
717
+ {
718
+ sectionTitle: initText('webUi.gettingStarted.title'),
719
+ sectionDescription: initText('webUi.gettingStarted.description'),
720
+ catalog: {
721
+ appName: c.appName,
722
+ setupMode: c.setupMode,
723
+ },
724
+ },
725
+ {
726
+ sectionTitle: initText('webUi.connectExistingApp.title'),
727
+ sectionDescription: initText('webUi.connectExistingApp.description'),
728
+ catalog: {
729
+ apiBaseUrl: c.apiBaseUrl,
730
+ authType: c.authType,
731
+ username: c.username,
732
+ password: c.password,
733
+ accessToken: c.accessToken,
734
+ },
735
+ },
736
+ {
737
+ sectionTitle: initText('webUi.createNewApp.title'),
738
+ sectionDescription: initText('webUi.createNewApp.description'),
739
+ catalog: {
740
+ lang: c.lang,
741
+ appPath: c.appPath,
742
+ appPort: c.appPort,
743
+ appPublicPath: c.appPublicPath,
744
+ skipDownload: c.skipDownload,
745
+ },
746
+ },
747
+ {
748
+ sectionTitle: initText('webUi.downloadAppFiles.title'),
749
+ sectionDescription: initText('webUi.downloadAppFiles.description'),
750
+ catalog: {
751
+ source: c.source,
752
+ version: c.version,
753
+ otherVersion: c.otherVersion,
754
+ dockerRegistry: c.dockerRegistry,
755
+ dockerPlatform: c.dockerPlatform,
756
+ dockerSave: c.dockerSave,
757
+ gitUrl: c.gitUrl,
758
+ outputDir: c.outputDir,
759
+ npmRegistry: c.npmRegistry,
760
+ replace: c.replace,
761
+ devDependencies: c.devDependencies,
762
+ build: c.build,
763
+ buildDts: c.buildDts,
764
+ },
765
+ },
766
+ {
767
+ sectionTitle: initText('webUi.configureDatabase.title'),
768
+ sectionDescription: initText('webUi.configureDatabase.description'),
769
+ catalog: {
770
+ dbDialect: c.dbDialect,
771
+ builtinDb: c.builtinDb,
772
+ builtinDbImage: c.builtinDbImage,
773
+ dbHost: c.dbHost,
774
+ dbPort: c.dbPort,
775
+ dbDatabase: c.dbDatabase,
776
+ dbUser: c.dbUser,
777
+ dbPassword: c.dbPassword,
778
+ dbSchema: c.dbSchema,
779
+ dbTablePrefix: c.dbTablePrefix,
780
+ dbUnderscored: c.dbUnderscored,
781
+ },
782
+ },
783
+ {
784
+ sectionTitle: initText('webUi.createAdminAccount.title'),
785
+ sectionDescription: initText('webUi.createAdminAccount.description'),
786
+ catalog: {
787
+ rootUsername: c.rootUsername,
788
+ rootEmail: c.rootEmail,
789
+ rootPassword: c.rootPassword,
790
+ rootNickname: c.rootNickname,
791
+ },
792
+ },
793
+ {
794
+ sectionTitle: initText('webUi.connectExistingApp.title'),
795
+ sectionDescription: initText('webUi.connectExistingApp.description'),
796
+ catalog: {
797
+ installApiBaseUrl: c.installApiBaseUrl,
798
+ installAuthType: c.installAuthType,
799
+ installAccessToken: c.installAccessToken,
800
+ },
801
+ },
802
+ ];
803
+ }
804
+ buildPresetValuesFromFlags(flags) {
805
+ const preset = {};
806
+ const argv = process.argv.slice(2);
807
+ const setupMode = explicitSetupModeFlag(flags);
808
+ if (flags.env !== undefined && String(flags.env).trim() !== '') {
809
+ preset.appName = String(flags.env).trim();
810
+ }
811
+ if (setupMode) {
812
+ preset.setupMode = setupMode;
813
+ }
814
+ const apiBaseUrl = explicitApiBaseUrlFlag(flags);
815
+ if (apiBaseUrl) {
816
+ preset.setupMode ??= 'connect-remote';
817
+ preset.apiBaseUrl = apiBaseUrl;
818
+ preset.installApiBaseUrl = apiBaseUrl;
819
+ }
820
+ else if (flags['default-api-base-url'] !== undefined && String(flags['default-api-base-url']).trim() !== '') {
821
+ const defaultApiBaseUrl = String(flags['default-api-base-url']).trim();
822
+ preset.apiBaseUrl = defaultApiBaseUrl;
823
+ preset.installApiBaseUrl = defaultApiBaseUrl;
824
+ }
825
+ if (flags['auth-type'] !== undefined && String(flags['auth-type']).trim() !== '') {
826
+ const authType = String(flags['auth-type']).trim();
827
+ preset.authType = authType;
828
+ preset.installAuthType = authType;
829
+ }
830
+ if (flags['skip-auth']) {
831
+ preset.skipAuth = true;
832
+ }
833
+ const accessToken = String(flags['access-token'] ?? flags.token ?? '');
834
+ if (flags['access-token'] !== undefined || flags.token !== undefined) {
835
+ preset.accessToken = accessToken;
836
+ preset.installAccessToken = accessToken;
837
+ }
838
+ if (flags.username !== undefined) {
839
+ const username = String(flags.username ?? '').trim();
840
+ preset.username = username;
841
+ preset.installUsername = username;
842
+ }
843
+ if (flags.password !== undefined) {
844
+ const password = String(flags.password ?? '');
845
+ preset.password = password;
846
+ preset.installPassword = password;
847
+ }
848
+ if (flags.lang !== undefined && String(flags.lang).trim() !== '') {
849
+ preset.lang = String(flags.lang).trim();
850
+ }
851
+ if (flags['app-path'] !== undefined && String(flags['app-path']).trim() !== '') {
852
+ preset.appPath = String(flags['app-path']).trim();
853
+ }
854
+ if (flags['app-root-path'] !== undefined && String(flags['app-root-path']).trim() !== '') {
855
+ preset.appRootPath = String(flags['app-root-path']).trim();
856
+ }
857
+ if (flags['app-port'] !== undefined && String(flags['app-port']).trim() !== '') {
858
+ preset.appPort = String(flags['app-port']).trim();
859
+ }
860
+ if (flags['storage-path'] !== undefined && String(flags['storage-path']).trim() !== '') {
861
+ preset.storagePath = String(flags['storage-path']).trim();
862
+ }
863
+ if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
864
+ preset.appPublicPath = String(flags['app-public-path']).trim();
865
+ }
866
+ if (flags['root-username'] !== undefined) {
867
+ preset.rootUsername = String(flags['root-username'] ?? '').trim();
868
+ }
869
+ if (flags['root-email'] !== undefined) {
870
+ preset.rootEmail = String(flags['root-email'] ?? '').trim();
871
+ }
872
+ if (flags['root-password'] !== undefined) {
873
+ preset.rootPassword = String(flags['root-password'] ?? '');
874
+ }
875
+ if (flags['root-nickname'] !== undefined) {
876
+ preset.rootNickname = String(flags['root-nickname'] ?? '').trim();
877
+ }
878
+ if (flags['db-dialect'] !== undefined && String(flags['db-dialect']).trim() !== '') {
879
+ preset.dbDialect = String(flags['db-dialect']).trim();
880
+ }
881
+ if (flags['builtin-db-image'] !== undefined && String(flags['builtin-db-image']).trim() !== '') {
882
+ preset.builtinDbImage = String(flags['builtin-db-image']).trim();
883
+ }
884
+ if (flags['db-host'] !== undefined && String(flags['db-host']).trim() !== '') {
885
+ preset.dbHost = String(flags['db-host']).trim();
886
+ preset.builtinDb = false;
887
+ }
888
+ if (flags['db-port'] !== undefined && String(flags['db-port']).trim() !== '') {
889
+ preset.dbPort = String(flags['db-port']).trim();
890
+ }
891
+ if (flags['db-database'] !== undefined && String(flags['db-database']).trim() !== '') {
892
+ preset.dbDatabase = String(flags['db-database']).trim();
893
+ }
894
+ if (flags['db-user'] !== undefined && String(flags['db-user']).trim() !== '') {
895
+ preset.dbUser = String(flags['db-user']).trim();
896
+ }
897
+ if (flags['db-password'] !== undefined) {
898
+ preset.dbPassword = String(flags['db-password'] ?? '');
899
+ }
900
+ if (flags['db-schema'] !== undefined && String(flags['db-schema']).trim() !== '') {
901
+ preset.dbSchema = String(flags['db-schema']).trim();
902
+ }
903
+ if (flags['db-table-prefix'] !== undefined && String(flags['db-table-prefix']).trim() !== '') {
904
+ preset.dbTablePrefix = String(flags['db-table-prefix']).trim();
905
+ }
906
+ if (argvHasToken(argv, ['--db-underscored', '--no-db-underscored'])) {
907
+ preset.dbUnderscored = Boolean(flags['db-underscored']);
908
+ }
909
+ if (argvHasToken(argv, ['--skip-download'])) {
910
+ preset.skipDownload = Boolean(flags['skip-download']);
911
+ if (preset.skipDownload) {
912
+ preset.dockerSave = false;
913
+ preset.replace = false;
914
+ preset.devDependencies = false;
915
+ preset.build = false;
916
+ preset.buildDts = false;
917
+ }
918
+ }
919
+ if (flags.source !== undefined && String(flags.source).trim() !== '') {
920
+ preset.source = String(flags.source).trim();
921
+ }
922
+ if (flags.version !== undefined) {
923
+ const version = String(flags.version).trim() || 'latest';
924
+ preset.version = initVersionPromptValue(version);
925
+ if (preset.version === 'other') {
926
+ preset.otherVersion = version;
927
+ }
928
+ }
929
+ if (flags['docker-registry'] !== undefined && String(flags['docker-registry']).trim() !== '') {
930
+ preset.dockerRegistry = String(flags['docker-registry']).trim();
931
+ }
932
+ if (flags['docker-platform'] !== undefined && String(flags['docker-platform']).trim() !== '') {
933
+ preset.dockerPlatform = String(flags['docker-platform']).trim();
934
+ }
935
+ if (flags['output-dir'] !== undefined && String(flags['output-dir']).trim() !== '') {
936
+ preset.outputDir = String(flags['output-dir']).trim();
937
+ }
938
+ if (flags['git-url'] !== undefined && String(flags['git-url']).trim() !== '') {
939
+ preset.gitUrl = String(flags['git-url']).trim();
940
+ }
941
+ if (flags['npm-registry'] !== undefined) {
942
+ preset.npmRegistry = String(flags['npm-registry'] ?? '');
943
+ }
944
+ if (argvHasToken(argv, ['--replace', '-r'])) {
945
+ preset.replace = Boolean(flags.replace);
946
+ }
947
+ if (argvHasToken(argv, ['--dev-dependencies', '--no-dev-dependencies', '-D'])) {
948
+ preset.devDependencies = Boolean(flags['dev-dependencies']);
949
+ }
950
+ if (argvHasToken(argv, ['--docker-save', '--no-docker-save'])) {
951
+ preset.dockerSave = Boolean(flags['docker-save']);
952
+ }
953
+ if (argvHasToken(argv, ['--build', '--no-build'])) {
954
+ preset.build = Boolean(flags.build);
955
+ }
956
+ if (argvHasToken(argv, ['--build-dts', '--no-build-dts'])) {
957
+ preset.buildDts = Boolean(flags['build-dts']);
958
+ }
959
+ if (argvHasToken(argv, ['--builtin-db', '--no-builtin-db'])) {
960
+ preset.builtinDb = Boolean(flags['builtin-db']);
961
+ }
962
+ if (explicitDbHostFlag(flags)) {
963
+ preset.builtinDb = false;
964
+ }
965
+ if (!preset.setupMode && hasDownloadOverride(flags)) {
966
+ preset.setupMode = 'install-new';
967
+ }
968
+ if (preset.setupMode === 'manage-local') {
969
+ preset.skipDownload = false;
970
+ }
971
+ applyLegacyHasNocobaseAlias(preset);
972
+ return preset;
973
+ }
974
+ hasAgentsDirInCwd() {
975
+ return existsSync(path.resolve(process.cwd(), '.agents'));
976
+ }
977
+ async syncNocoBaseSkills(options) {
978
+ if (options?.skip) {
979
+ printVerbose('Skipped agent skills sync.');
980
+ return;
981
+ }
982
+ try {
983
+ logInitStage('Syncing agent skills');
984
+ await installNocoBaseSkills();
985
+ printInfo('Agent skills ready.');
986
+ }
987
+ catch (error) {
988
+ const message = error instanceof Error ? error.message : String(error);
989
+ if (isNpmRegistryUnavailable(error)) {
990
+ printWarning(translateCli('commands.init.messages.skillsSyncRegistryUnavailable'));
991
+ printVerbose(`Skipped agent skills sync because the npm registry was unavailable: ${message}`);
992
+ return;
993
+ }
994
+ this.error(pc.red(`Skills sync failed: ${message}`));
995
+ this.exit(1);
996
+ }
997
+ }
998
+ async persistManagedEnvConfig(results, flags = {}) {
999
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
1000
+ const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
1001
+ const appPort = String(results.appPort ?? '').trim();
1002
+ const appPublicPath = String(results.appPublicPath ?? '').trim();
1003
+ const source = String(results.source ?? '').trim();
1004
+ const version = resolveInitDownloadVersion(results);
1005
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
1006
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
1007
+ const gitUrl = String(results.gitUrl ?? '').trim();
1008
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
1009
+ const appPath = String(results.appPath ?? '').trim() ||
1010
+ inferConfiguredAppPathFromLegacyConfig({
1011
+ appRootPath: results.appRootPath,
1012
+ storagePath: results.storagePath,
1013
+ }) ||
1014
+ '';
1015
+ const appRootPath = String(results.appRootPath ?? '').trim();
1016
+ const storagePath = String(results.storagePath ?? '').trim();
1017
+ const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : '';
1018
+ const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : '';
1019
+ const dbDialect = String(results.dbDialect ?? '').trim();
1020
+ const builtinDbImage = String(results.builtinDbImage ?? '').trim();
1021
+ const dbHost = String(results.dbHost ?? '').trim();
1022
+ const dbPort = String(results.dbPort ?? '').trim();
1023
+ const dbDatabase = String(results.dbDatabase ?? '').trim();
1024
+ const dbUser = String(results.dbUser ?? '').trim();
1025
+ const dbPassword = String(results.dbPassword ?? '');
1026
+ const dbSchema = String(results.dbSchema ?? '').trim();
1027
+ const dbTablePrefix = String(results.dbTablePrefix ?? '').trim();
1028
+ const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
1029
+ const authType = String(results.authType ?? '').trim() || 'oauth';
1030
+ const authUsername = authType === 'basic' ? String(results.username ?? results.rootUsername ?? '').trim() : '';
1031
+ const accessToken = String(results.accessToken ?? '');
1032
+ const skipDownload = results.skipDownload === true;
1033
+ const appKey = resolveManagedAppKey(results.appKey ?? existingEnv?.config.appKey);
1034
+ const timeZone = resolveManagedTimeZone(results.timeZone ?? existingEnv?.config.timezone);
1035
+ const builtinDb = explicitDbHostFlag(flags)
1036
+ ? false
1037
+ : results.builtinDb === undefined
1038
+ ? undefined
1039
+ : Boolean(results.builtinDb);
1040
+ results.appKey = appKey;
1041
+ results.timeZone = timeZone;
1042
+ await upsertEnv(envName, {
1043
+ ...(source === 'docker'
1044
+ ? { kind: 'docker' }
1045
+ : source || appPath || appRootPath
1046
+ ? { kind: 'local' }
1047
+ : appPort
1048
+ ? { kind: 'http' }
1049
+ : {}),
1050
+ ...(apiBaseUrl ? { apiBaseUrl } : appPort ? { apiBaseUrl: `http://127.0.0.1:${appPort}/api` } : {}),
1051
+ ...(authType ? { authType } : {}),
1052
+ ...(authUsername ? { authUsername } : {}),
1053
+ ...((authType === 'token' || authType === 'basic') && accessToken ? { accessToken } : {}),
1054
+ ...(source ? { source } : {}),
1055
+ ...(version ? { downloadVersion: version } : {}),
1056
+ ...(dockerRegistry ? { dockerRegistry } : {}),
1057
+ ...(dockerPlatform ? { dockerPlatform } : {}),
1058
+ ...(gitUrl ? { gitUrl } : {}),
1059
+ ...(npmRegistry ? { npmRegistry } : {}),
1060
+ ...(appPath ? { appPath } : {}),
1061
+ ...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
1062
+ ...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
1063
+ ...(appPort ? { appPort } : {}),
1064
+ ...(appPublicPath ? { appPublicPath } : {}),
1065
+ ...(appKey ? { appKey } : {}),
1066
+ ...(timeZone ? { timezone: timeZone } : {}),
1067
+ ...(!skipDownload && results.devDependencies !== undefined
1068
+ ? { devDependencies: Boolean(results.devDependencies) }
1069
+ : {}),
1070
+ ...(!skipDownload && results.build !== undefined ? { build: Boolean(results.build) } : {}),
1071
+ ...(!skipDownload && results.buildDts !== undefined ? { buildDts: Boolean(results.buildDts) } : {}),
1072
+ ...(builtinDb !== undefined ? { builtinDb } : {}),
1073
+ ...(dbDialect ? { dbDialect } : {}),
1074
+ ...(builtinDbImage || builtinDb === false ? { builtinDbImage: builtinDbImage || undefined } : {}),
1075
+ ...(dbHost ? { dbHost } : {}),
1076
+ ...(dbPort ? { dbPort } : {}),
1077
+ ...(dbDatabase ? { dbDatabase } : {}),
1078
+ ...(dbUser ? { dbUser } : {}),
1079
+ ...(dbPassword ? { dbPassword } : {}),
1080
+ ...(dbSchema ? { dbSchema } : {}),
1081
+ ...(dbTablePrefix ? { dbTablePrefix } : {}),
1082
+ ...(results.dbUnderscored !== undefined ? { dbUnderscored: Boolean(results.dbUnderscored) } : {}),
1083
+ setupState: 'prepared',
1084
+ ...(String(results.lang ?? '').trim() ? { lang: String(results.lang ?? '').trim() } : {}),
1085
+ }, { scope: resolveDefaultConfigScope() });
1086
+ }
1087
+ buildEnvAddArgv(results) {
1088
+ const argv = [String(results.appName ?? DEFAULT_INIT_APP_NAME)];
1089
+ argv.push('--no-intro');
1090
+ argv.push('--api-base-url', String(results.apiBaseUrl ?? DEFAULT_INIT_API_BASE_URL));
1091
+ argv.push('--auth-type', String(results.authType ?? 'oauth'));
1092
+ const accessToken = String(results.accessToken ?? '');
1093
+ const username = String(results.username ?? '').trim();
1094
+ const password = String(results.password ?? '');
1095
+ if (results.skipAuth === true) {
1096
+ argv.push('--skip-auth');
1097
+ }
1098
+ else if (results.authType === 'token' && accessToken) {
1099
+ argv.push('--access-token', accessToken);
1100
+ }
1101
+ if (results.authType === 'basic' && username) {
1102
+ argv.push('--username', username);
1103
+ }
1104
+ if (results.authType === 'basic' && results.skipAuth !== true && password) {
1105
+ argv.push('--password', password);
1106
+ }
1107
+ return argv;
1108
+ }
1109
+ buildInstallArgv(results, flags, options) {
1110
+ const argv = ['--no-intro'];
1111
+ if (options?.nonInteractive ?? true) {
1112
+ argv.unshift('-y');
1113
+ }
1114
+ argv.push('--skip-save-env-log');
1115
+ const processArgv = process.argv.slice(2);
1116
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
1117
+ const source = String(results.source ?? '').trim();
1118
+ const setupMode = resolveInitSetupMode({
1119
+ setupMode: results.setupMode ?? flags['setup-mode'],
1120
+ hasNocobase: results.hasNocobase,
1121
+ });
1122
+ const skipDownload = setupMode === 'manage-local' ? false : Boolean(flags['skip-download']) || results.skipDownload === true;
1123
+ const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
1124
+ const authType = String(results.authType ?? '').trim();
1125
+ const accessToken = String(results.accessToken ?? '');
1126
+ const username = String(results.username ?? flags.username ?? '').trim();
1127
+ const password = String(results.password ?? flags.password ?? '');
1128
+ argv.push('--env', envName);
1129
+ if (options?.resume) {
1130
+ argv.push('--resume');
1131
+ }
1132
+ if (flags['prepare-only']) {
1133
+ argv.push('--prepare-only');
1134
+ }
1135
+ if (flags.verbose) {
1136
+ argv.push('--verbose');
1137
+ }
1138
+ if (setupMode === 'connect-remote' && apiBaseUrl) {
1139
+ argv.push('--api-base-url', apiBaseUrl);
1140
+ }
1141
+ if (authType) {
1142
+ argv.push('--auth-type', authType);
1143
+ }
1144
+ if (Boolean(flags['skip-auth']) || results.skipAuth === true) {
1145
+ argv.push('--skip-auth');
1146
+ }
1147
+ if (authType === 'token' && accessToken) {
1148
+ argv.push('--access-token', accessToken);
1149
+ }
1150
+ if (authType === 'basic' && username) {
1151
+ argv.push('--username', username);
1152
+ }
1153
+ if (authType === 'basic' && password) {
1154
+ argv.push('--password', password);
1155
+ }
1156
+ const lang = String(results.lang ?? '').trim();
1157
+ if (lang) {
1158
+ argv.push('--lang', lang);
1159
+ }
1160
+ const appPath = String(results.appPath ?? '').trim() ||
1161
+ inferConfiguredAppPathFromLegacyConfig({
1162
+ appRootPath: results.appRootPath,
1163
+ storagePath: results.storagePath,
1164
+ }) ||
1165
+ '';
1166
+ if (appPath) {
1167
+ argv.push('--app-path', appPath);
1168
+ }
1169
+ const appRootPath = String(results.appRootPath ?? '').trim();
1170
+ if (appRootPath && (!appPath || !areConfiguredPathsEquivalent(appRootPath, deriveConfiguredSourcePath(appPath)))) {
1171
+ argv.push('--app-root-path', appRootPath);
1172
+ }
1173
+ const appPort = String(results.appPort ?? '').trim();
1174
+ if (appPort && (!flags.yes || argvHasToken(processArgv, ['--app-port']) || appPort !== '13000')) {
1175
+ argv.push('--app-port', appPort);
1176
+ }
1177
+ const storagePath = String(results.storagePath ?? '').trim();
1178
+ if (storagePath && (!appPath || !areConfiguredPathsEquivalent(storagePath, deriveConfiguredStoragePath(appPath)))) {
1179
+ argv.push('--storage-path', storagePath);
1180
+ }
1181
+ const appPublicPath = String(results.appPublicPath ?? '').trim();
1182
+ if (appPublicPath) {
1183
+ argv.push('--app-public-path', appPublicPath);
1184
+ }
1185
+ if (flags.force) {
1186
+ argv.push('--force');
1187
+ }
1188
+ if (source) {
1189
+ argv.push('--source', source);
1190
+ }
1191
+ const version = resolveInitDownloadVersion(results);
1192
+ if (version) {
1193
+ argv.push('--version', version);
1194
+ }
1195
+ const gitUrl = String(results.gitUrl ?? '').trim();
1196
+ if (gitUrl) {
1197
+ argv.push('--git-url', gitUrl);
1198
+ }
1199
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
1200
+ if (dockerRegistry) {
1201
+ argv.push('--docker-registry', dockerRegistry);
1202
+ }
1203
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
1204
+ if (dockerPlatform) {
1205
+ argv.push('--docker-platform', dockerPlatform);
1206
+ }
1207
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
1208
+ if (npmRegistry) {
1209
+ argv.push('--npm-registry', npmRegistry);
1210
+ }
1211
+ if (skipDownload) {
1212
+ argv.push('--skip-download');
1213
+ }
1214
+ else {
1215
+ const outputDir = String(results.outputDir ?? '').trim();
1216
+ if (outputDir) {
1217
+ argv.push('--output-dir', outputDir);
1218
+ }
1219
+ if (results.replace) {
1220
+ argv.push('--replace');
1221
+ }
1222
+ if (results.devDependencies) {
1223
+ argv.push('--dev-dependencies');
1224
+ }
1225
+ if (results.dockerSave) {
1226
+ argv.push('--docker-save');
1227
+ }
1228
+ if (results.build !== undefined && !results.build) {
1229
+ argv.push('--no-build');
1230
+ }
1231
+ else if (argvHasToken(processArgv, ['--build', '--no-build']) && flags.build === false) {
1232
+ argv.push('--no-build');
1233
+ }
1234
+ if (results.buildDts) {
1235
+ argv.push('--build-dts');
1236
+ }
1237
+ }
1238
+ const explicitDbHost = explicitDbHostFlag(flags);
1239
+ const dbHost = explicitDbHost || String(results.dbHost ?? '').trim();
1240
+ const builtinDb = explicitDbHost ? false : Boolean(results.builtinDb);
1241
+ if (builtinDb) {
1242
+ argv.push('--builtin-db');
1243
+ }
1244
+ else if (explicitDbHost || results.builtinDb !== undefined) {
1245
+ argv.push('--no-builtin-db');
1246
+ }
1247
+ const dbDialect = String(results.dbDialect ?? '').trim();
1248
+ if (dbDialect) {
1249
+ argv.push('--db-dialect', dbDialect);
1250
+ }
1251
+ const builtinDbImage = String(results.builtinDbImage ?? '').trim();
1252
+ if (builtinDb && builtinDbImage) {
1253
+ argv.push('--builtin-db-image', builtinDbImage);
1254
+ }
1255
+ if (dbHost) {
1256
+ argv.push('--db-host', dbHost);
1257
+ }
1258
+ const dbPort = String(results.dbPort ?? '').trim();
1259
+ const dbPortWasProvided = argvHasToken(processArgv, ['--db-port']);
1260
+ const dockerBuiltinDbPortIsHidden = builtinDb && source === 'docker';
1261
+ const dbDefaultPort = defaultDbPortForDialect(dbDialect);
1262
+ if (dbPort &&
1263
+ (!dockerBuiltinDbPortIsHidden || dbPortWasProvided) &&
1264
+ (!flags.yes || dbPortWasProvided || dbPort !== dbDefaultPort)) {
1265
+ argv.push('--db-port', dbPort);
1266
+ }
1267
+ const dbDatabase = String(results.dbDatabase ?? '').trim();
1268
+ if (dbDatabase) {
1269
+ argv.push('--db-database', dbDatabase);
1270
+ }
1271
+ const dbUser = String(results.dbUser ?? '').trim();
1272
+ if (dbUser) {
1273
+ argv.push('--db-user', dbUser);
1274
+ }
1275
+ const dbPassword = String(results.dbPassword ?? '');
1276
+ if (dbPassword) {
1277
+ argv.push('--db-password', dbPassword);
1278
+ }
1279
+ const dbSchema = String(results.dbSchema ?? '').trim();
1280
+ if (dbSchema) {
1281
+ argv.push('--db-schema', dbSchema);
1282
+ }
1283
+ const dbTablePrefix = String(results.dbTablePrefix ?? '').trim();
1284
+ if (dbTablePrefix) {
1285
+ argv.push('--db-table-prefix', dbTablePrefix);
1286
+ }
1287
+ if (results.dbUnderscored !== undefined) {
1288
+ argv.push(results.dbUnderscored ? '--db-underscored' : '--no-db-underscored');
1289
+ }
1290
+ const includeRootUser = setupMode === 'install-new';
1291
+ const rootUsername = String(results.rootUsername ?? '').trim();
1292
+ if (includeRootUser && rootUsername) {
1293
+ argv.push('--root-username', rootUsername);
1294
+ }
1295
+ const rootEmail = String(results.rootEmail ?? '').trim();
1296
+ if (includeRootUser && rootEmail) {
1297
+ argv.push('--root-email', rootEmail);
1298
+ }
1299
+ const rootPassword = String(results.rootPassword ?? '');
1300
+ if (includeRootUser && rootPassword) {
1301
+ argv.push('--root-password', rootPassword);
1302
+ }
1303
+ const rootNickname = String(results.rootNickname ?? '').trim();
1304
+ if (includeRootUser && rootNickname) {
1305
+ argv.push('--root-nickname', rootNickname);
1306
+ }
1307
+ return argv;
1308
+ }
1309
+ buildManagedInstallResumeCommand(results, flags) {
1310
+ const argv = ['nb', 'init'];
1311
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
1312
+ argv.push('--env', envName);
1313
+ if (flags.yes) {
1314
+ argv.push('--yes');
1315
+ }
1316
+ if (flags.ui) {
1317
+ argv.push('--ui');
1318
+ }
1319
+ argv.push('--resume');
1320
+ return argv.map(shellQuoteArg).join(' ');
1321
+ }
1322
+ async formatManagedInstallFailureMessage(message, results, flags) {
1323
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
1324
+ const savedEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
1325
+ if (savedEnv?.config.setupState === 'installed') {
1326
+ return message;
1327
+ }
1328
+ const command = this.buildManagedInstallResumeCommand(results, flags);
1329
+ return [message, '', translateCli('commands.init.messages.resumeAfterInstallFailure', { command })].join('\n');
1330
+ }
1331
+ buildResumeInstallArgv(flags) {
1332
+ const preset = this.buildPresetValuesFromFlags(flags);
1333
+ const setupMode = resolveInitSetupMode(preset);
1334
+ if (flags.yes) {
1335
+ preset.lang ??= yesInitialValue(Install.appPrompts.lang, 'en-US');
1336
+ if (setupMode === 'install-new') {
1337
+ preset.rootUsername ??= yesInitialValue(Install.rootUserPrompts.rootUsername, 'nocobase');
1338
+ preset.rootEmail ??= yesInitialValue(Install.rootUserPrompts.rootEmail, 'admin@nocobase.com');
1339
+ preset.rootPassword ??= yesInitialValue(Install.rootUserPrompts.rootPassword, 'admin123');
1340
+ preset.rootNickname ??= yesInitialValue(Install.rootUserPrompts.rootNickname, 'Super Admin');
1341
+ }
1342
+ }
1343
+ if (setupMode === 'manage-local') {
1344
+ preset.skipDownload = false;
1345
+ }
1346
+ else if (!flags['skip-download']) {
1347
+ preset.replace ??= true;
1348
+ }
1349
+ return this.buildInstallArgv(preset, flags, {
1350
+ nonInteractive: Boolean(flags.yes),
1351
+ resume: true,
1352
+ });
1353
+ }
1354
+ normalizeInitResults(results) {
1355
+ const normalized = { ...results };
1356
+ const setupMode = resolveInitSetupMode(normalized);
1357
+ const defaultApiHost = normalizeConnectionString(normalized.defaultApiHost) || '127.0.0.1';
1358
+ if (setupMode === 'install-new') {
1359
+ normalized.apiBaseUrl =
1360
+ normalizeConnectionString(normalized.installApiBaseUrl) ||
1361
+ deriveInstallConnectionApiBaseUrl(normalized, defaultApiHost);
1362
+ normalized.authType = normalizeConnectionString(normalized.installAuthType) || 'oauth';
1363
+ normalized.username =
1364
+ normalized.authType === 'basic'
1365
+ ? normalizeConnectionString(normalized.installUsername) || normalizeConnectionString(normalized.rootUsername)
1366
+ : normalizeConnectionString(normalized.installUsername);
1367
+ normalized.password =
1368
+ normalized.authType === 'basic'
1369
+ ? String(normalized.installPassword ?? '') || String(normalized.rootPassword ?? '')
1370
+ : String(normalized.installPassword ?? '');
1371
+ normalized.accessToken = String(normalized.installAccessToken ?? '');
1372
+ }
1373
+ normalized.setupMode = setupMode;
1374
+ applyLegacyHasNocobaseAlias(normalized);
1375
+ if (setupMode === 'manage-local') {
1376
+ normalized.skipDownload = false;
1377
+ delete normalized.rootUsername;
1378
+ delete normalized.rootEmail;
1379
+ delete normalized.rootPassword;
1380
+ delete normalized.rootNickname;
1381
+ }
1382
+ delete normalized.installApiBaseUrl;
1383
+ delete normalized.installAuthType;
1384
+ delete normalized.installUsername;
1385
+ delete normalized.installPassword;
1386
+ delete normalized.installAccessToken;
1387
+ delete normalized.defaultApiHost;
1388
+ return normalized;
1389
+ }
1390
+ }