@nocobase/cli 2.1.0-beta.2 → 2.1.0-beta.21

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 (145) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +367 -19
  3. package/README.zh-CN.md +336 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +131 -0
  6. package/dist/commands/api/resource/create.js +15 -0
  7. package/dist/commands/api/resource/destroy.js +15 -0
  8. package/dist/commands/api/resource/get.js +15 -0
  9. package/dist/commands/api/resource/index.js +20 -0
  10. package/dist/commands/api/resource/list.js +16 -0
  11. package/dist/commands/api/resource/query.js +15 -0
  12. package/dist/commands/api/resource/update.js +15 -0
  13. package/dist/commands/build.js +57 -0
  14. package/dist/commands/db/logs.js +85 -0
  15. package/dist/commands/db/ps.js +60 -0
  16. package/dist/commands/db/shared.js +95 -0
  17. package/dist/commands/db/start.js +70 -0
  18. package/dist/commands/db/stop.js +70 -0
  19. package/dist/commands/dev.js +156 -0
  20. package/dist/commands/down.js +197 -0
  21. package/dist/commands/download.js +865 -0
  22. package/dist/commands/env/add.js +307 -0
  23. package/dist/commands/env/auth.js +55 -0
  24. package/dist/commands/env/list.js +36 -0
  25. package/dist/commands/env/remove.js +59 -0
  26. package/dist/commands/env/update.js +67 -0
  27. package/dist/commands/env/use.js +28 -0
  28. package/dist/commands/init.js +950 -0
  29. package/dist/commands/install.js +1927 -0
  30. package/dist/commands/logs.js +97 -0
  31. package/dist/commands/pm/disable.js +63 -0
  32. package/dist/commands/pm/enable.js +63 -0
  33. package/dist/commands/pm/list.js +61 -0
  34. package/dist/commands/prompts-stages.js +150 -0
  35. package/dist/commands/prompts-test.js +181 -0
  36. package/dist/commands/ps.js +119 -0
  37. package/dist/commands/restart.js +74 -0
  38. package/dist/commands/scaffold/migration.js +38 -0
  39. package/dist/commands/scaffold/plugin.js +37 -0
  40. package/dist/commands/self/check.js +71 -0
  41. package/dist/commands/self/index.js +20 -0
  42. package/dist/commands/self/update.js +86 -0
  43. package/dist/commands/skills/check.js +69 -0
  44. package/dist/commands/skills/index.js +20 -0
  45. package/dist/commands/skills/install.js +71 -0
  46. package/dist/commands/skills/update.js +71 -0
  47. package/dist/commands/start.js +218 -0
  48. package/dist/commands/stop.js +97 -0
  49. package/dist/commands/test.js +466 -0
  50. package/dist/commands/upgrade.js +594 -0
  51. package/dist/generated/command-registry.js +133 -0
  52. package/dist/help/runtime-help.js +20 -0
  53. package/dist/lib/api-client.js +244 -0
  54. package/dist/lib/app-runtime.js +153 -0
  55. package/dist/lib/auth-store.js +357 -0
  56. package/dist/lib/bootstrap.js +388 -0
  57. package/dist/lib/build-config.js +10 -0
  58. package/dist/lib/cli-home.js +61 -0
  59. package/dist/lib/cli-locale.js +115 -0
  60. package/dist/lib/command-discovery.js +39 -0
  61. package/dist/lib/env-auth.js +872 -0
  62. package/dist/lib/generated-command.js +150 -0
  63. package/dist/lib/http-request.js +49 -0
  64. package/dist/lib/naming.js +70 -0
  65. package/dist/lib/openapi.js +62 -0
  66. package/dist/lib/post-processors.js +23 -0
  67. package/dist/lib/prompt-catalog.js +581 -0
  68. package/dist/lib/prompt-validators.js +185 -0
  69. package/dist/lib/prompt-web-ui.js +2096 -0
  70. package/dist/lib/resource-command.js +343 -0
  71. package/dist/lib/resource-request.js +104 -0
  72. package/dist/lib/run-npm.js +197 -0
  73. package/dist/lib/runtime-generator.js +419 -0
  74. package/dist/lib/runtime-store.js +56 -0
  75. package/dist/lib/self-manager.js +246 -0
  76. package/dist/lib/skills-manager.js +269 -0
  77. package/dist/lib/startup-update.js +203 -0
  78. package/dist/lib/ui.js +175 -0
  79. package/dist/locale/en-US.json +336 -0
  80. package/dist/locale/zh-CN.json +336 -0
  81. package/dist/post-processors/data-modeling.js +66 -0
  82. package/dist/post-processors/data-source-manager.js +114 -0
  83. package/dist/post-processors/index.js +19 -0
  84. package/nocobase-ctl.config.json +287 -0
  85. package/package.json +60 -26
  86. package/LICENSE +0 -661
  87. package/bin/index.js +0 -39
  88. package/nocobase.conf.tpl +0 -95
  89. package/src/cli.js +0 -19
  90. package/src/commands/benchmark.js +0 -73
  91. package/src/commands/build.js +0 -49
  92. package/src/commands/clean.js +0 -30
  93. package/src/commands/client.js +0 -166
  94. package/src/commands/create-nginx-conf.js +0 -37
  95. package/src/commands/create-plugin.js +0 -33
  96. package/src/commands/dev.js +0 -200
  97. package/src/commands/doc.js +0 -76
  98. package/src/commands/e2e.js +0 -265
  99. package/src/commands/global.js +0 -43
  100. package/src/commands/index.js +0 -45
  101. package/src/commands/instance-id.js +0 -47
  102. package/src/commands/locale/cronstrue.js +0 -122
  103. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  104. package/src/commands/locale/react-js-cron/index.js +0 -17
  105. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  106. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  107. package/src/commands/locale.js +0 -81
  108. package/src/commands/p-test.js +0 -88
  109. package/src/commands/perf.js +0 -63
  110. package/src/commands/pkg.js +0 -321
  111. package/src/commands/pm2.js +0 -37
  112. package/src/commands/postinstall.js +0 -88
  113. package/src/commands/start.js +0 -148
  114. package/src/commands/tar.js +0 -36
  115. package/src/commands/test-coverage.js +0 -55
  116. package/src/commands/test.js +0 -107
  117. package/src/commands/umi.js +0 -33
  118. package/src/commands/update-deps.js +0 -72
  119. package/src/commands/upgrade.js +0 -47
  120. package/src/commands/view-license-key.js +0 -44
  121. package/src/index.js +0 -14
  122. package/src/license.js +0 -76
  123. package/src/logger.js +0 -75
  124. package/src/plugin-generator.js +0 -80
  125. package/src/util.js +0 -517
  126. package/templates/bundle-status.html +0 -338
  127. package/templates/create-app-package.json +0 -39
  128. package/templates/plugin/.npmignore.tpl +0 -2
  129. package/templates/plugin/README.md.tpl +0 -1
  130. package/templates/plugin/client.d.ts +0 -2
  131. package/templates/plugin/client.js +0 -1
  132. package/templates/plugin/package.json.tpl +0 -11
  133. package/templates/plugin/server.d.ts +0 -2
  134. package/templates/plugin/server.js +0 -1
  135. package/templates/plugin/src/client/client.d.ts +0 -249
  136. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  137. package/templates/plugin/src/client/locale.ts +0 -21
  138. package/templates/plugin/src/client/models/index.ts +0 -12
  139. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  140. package/templates/plugin/src/index.ts +0 -2
  141. package/templates/plugin/src/locale/en-US.json +0 -1
  142. package/templates/plugin/src/locale/zh-CN.json +0 -1
  143. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  144. package/templates/plugin/src/server/index.ts.tpl +0 -1
  145. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,950 @@
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 * as p from '@clack/prompts';
11
+ import pc from 'picocolors';
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 { runPromptCatalogWebUI, } from "../lib/prompt-web-ui.js";
20
+ import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
21
+ import { inspectSkillsStatus, installNocoBaseSkills, updateNocoBaseSkills, } from '../lib/skills-manager.js';
22
+ import Download from "./download.js";
23
+ import EnvAdd from "./env/add.js";
24
+ import Install, { defaultDbPortForDialect } from "./install.js";
25
+ import _ from 'lodash';
26
+ const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
27
+ const DEFAULT_INIT_APP_NAME = 'local';
28
+ const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
29
+ const INIT_ENV_ADD_FLAG_NAMES = [
30
+ 'locale',
31
+ 'default-api-base-url',
32
+ 'api-base-url',
33
+ 'auth-type',
34
+ 'access-token',
35
+ 'token',
36
+ ];
37
+ const initText = (key, values) => localeText(`commands.init.${key}`, values);
38
+ function withExtraHidden(def, extraHidden) {
39
+ if (def.type === 'run') {
40
+ return def;
41
+ }
42
+ return {
43
+ ...def,
44
+ hidden: (values) => extraHidden(values) || (def.hidden?.(values) ?? false),
45
+ };
46
+ }
47
+ function existingAppOnly(def) {
48
+ return withExtraHidden(def, (values) => values.hasNocobase !== 'yes');
49
+ }
50
+ function newInstallOnly(def) {
51
+ return withExtraHidden(def, (values) => values.hasNocobase !== 'no');
52
+ }
53
+ function downloadInNewInstallOnly(def) {
54
+ return withExtraHidden(def, (values) => values.hasNocobase !== 'no' || values.fetchSource !== true);
55
+ }
56
+ function argvHasToken(argv, tokens) {
57
+ return tokens.some((token) => argv.includes(token));
58
+ }
59
+ function resolveInitDownloadVersion(results) {
60
+ const preset = String(results.version ?? '').trim();
61
+ if (preset === 'other') {
62
+ return String(results.otherVersion ?? '').trim();
63
+ }
64
+ return preset;
65
+ }
66
+ function initVersionPromptValue(version) {
67
+ return version === 'latest' || version === 'beta' || version === 'alpha'
68
+ ? version
69
+ : 'other';
70
+ }
71
+ function yesInitialValue(def, fallback) {
72
+ if ('yesInitialValue' in def && def.yesInitialValue !== undefined) {
73
+ return String(def.yesInitialValue);
74
+ }
75
+ return fallback;
76
+ }
77
+ function hasDownloadOverride(flags) {
78
+ return Boolean(String(flags.source ?? '').trim()
79
+ || String(flags.version ?? '').trim());
80
+ }
81
+ function explicitApiBaseUrlFlag(flags) {
82
+ return String(flags['api-base-url'] ?? '').trim();
83
+ }
84
+ function explicitDbHostFlag(flags) {
85
+ return String(flags['db-host'] ?? '').trim();
86
+ }
87
+ function shouldAllowExistingInitEnv() {
88
+ return argvHasToken(process.argv.slice(2), ['--force', '-f']);
89
+ }
90
+ async function validateInitAppName(value) {
91
+ const formatError = validateEnvKey(value);
92
+ if (formatError) {
93
+ return formatError;
94
+ }
95
+ const envName = String(value ?? '').trim();
96
+ if (!envName) {
97
+ return undefined;
98
+ }
99
+ const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
100
+ if (existingEnv) {
101
+ if (shouldAllowExistingInitEnv()) {
102
+ return undefined;
103
+ }
104
+ return translateCli('commands.init.validation.envExists', { envName });
105
+ }
106
+ return undefined;
107
+ }
108
+ function highlightInitValidationMessage(message) {
109
+ return message.replace(/Env "([^"]+)"/, (_match, envName) => `Env ${pc.cyan(pc.bold(`"${envName}"`))}`);
110
+ }
111
+ function formatInitValidationMessage(message) {
112
+ return message;
113
+ }
114
+ function formatResumeEnvRequiredMessage() {
115
+ return [
116
+ translateCli('commands.init.messages.resumeEnvRequired'),
117
+ translateCli('commands.init.messages.resumeEnvHelp'),
118
+ ].join('\n');
119
+ }
120
+ function formatSkippedAppNameRequiredMessage() {
121
+ return [
122
+ translateCli('commands.init.messages.appNameRequiredWhenSkipped'),
123
+ translateCli('commands.init.messages.appNameEnvHelp'),
124
+ ].join('\n');
125
+ }
126
+ function shellQuoteArg(value) {
127
+ return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value)
128
+ ? value
129
+ : `'${value.replace(/'/g, `'\\''`)}'`;
130
+ }
131
+ function initTitle() {
132
+ return translateCli('commands.init.messages.title');
133
+ }
134
+ function logInitUiReady(command, url) {
135
+ p.log.step(translateCli('commands.init.messages.uiReady'));
136
+ p.log.info(translateCli('commands.init.messages.uiReadyHelp'));
137
+ command.log(`URL: ${url}`);
138
+ }
139
+ function logInitUiBrowserOpenFallback() {
140
+ p.log.warn(translateCli('commands.init.messages.uiOpenBrowserFallback'));
141
+ }
142
+ export default class Init extends Command {
143
+ static summary = 'Set up NocoBase so coding agents can connect and work with it';
144
+ static description = `Set up NocoBase for coding agents in the current workspace.
145
+
146
+ \`nb init\` prepares a NocoBase environment that coding agents can use. It supports two setup paths:
147
+
148
+ - Connect an existing NocoBase app and save it as a CLI env.
149
+ - Install a new NocoBase app, then save it as a CLI env.
150
+
151
+ It can also install NocoBase AI coding skills (\`nocobase/skills\`) so agents get the project-specific workflow guidance.
152
+
153
+ If setup was interrupted earlier, use \`--resume\` with an existing env name to continue from the saved workspace config.
154
+
155
+ Prompt modes:
156
+ - Default: guided prompts in the terminal.
157
+ - \`--ui\`: open the same setup flow in a local browser form.
158
+ - \`-y\`, \`--yes\`: skip prompts. In this mode \`--env <envName>\` is required, and init creates a new local NocoBase app with safe defaults.
159
+
160
+ \`--ui\` cannot be combined with \`--yes\`.`;
161
+ static examples = [
162
+ '<%= config.bin %> <%= command.id %>',
163
+ '<%= config.bin %> <%= command.id %> --env app1',
164
+ '<%= config.bin %> <%= command.id %> --env app1 --ui',
165
+ '<%= config.bin %> <%= command.id %> --ui',
166
+ '<%= config.bin %> <%= command.id %> --env app1 --yes',
167
+ '<%= config.bin %> <%= command.id %> --env app1 --resume',
168
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source docker --version alpha',
169
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source npm --version alpha --app-port 13080',
170
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --source git --version fix/cli-v2',
171
+ '<%= config.bin %> <%= command.id %> --ui --ui-port 3000',
172
+ ];
173
+ static prompts = {
174
+ appName: {
175
+ type: 'text',
176
+ message: initText('prompts.appName.message'),
177
+ placeholder: initText('prompts.appName.placeholder'),
178
+ required: true,
179
+ validate: validateInitAppName,
180
+ },
181
+ hasNocobase: {
182
+ type: 'select',
183
+ variant: 'radio',
184
+ message: initText('prompts.hasNocobase.message'),
185
+ options: [
186
+ {
187
+ value: 'no',
188
+ label: initText('prompts.hasNocobase.noLabel'),
189
+ },
190
+ {
191
+ value: 'yes',
192
+ label: initText('prompts.hasNocobase.yesLabel'),
193
+ },
194
+ ],
195
+ initialValue: 'no',
196
+ yesInitialValue: 'no',
197
+ required: true,
198
+ },
199
+ apiBaseUrl: existingAppOnly({
200
+ type: 'text',
201
+ message: initText('prompts.apiBaseUrl.message'),
202
+ placeholder: initText('prompts.apiBaseUrl.placeholder'),
203
+ required: true,
204
+ validate: validateApiBaseUrl,
205
+ }),
206
+ authType: existingAppOnly(EnvAdd.prompts.authType),
207
+ accessToken: existingAppOnly(EnvAdd.prompts.accessToken),
208
+ lang: newInstallOnly(Install.appPrompts.lang),
209
+ appRootPath: newInstallOnly(Install.appPrompts.appRootPath),
210
+ appPort: newInstallOnly(Install.appPrompts.appPort),
211
+ storagePath: newInstallOnly(Install.appPrompts.storagePath),
212
+ fetchSource: newInstallOnly(Install.appPrompts.fetchSource),
213
+ source: downloadInNewInstallOnly(Download.prompts.source),
214
+ version: downloadInNewInstallOnly(Download.prompts.version),
215
+ otherVersion: downloadInNewInstallOnly(Download.prompts.otherVersion),
216
+ dockerRegistry: downloadInNewInstallOnly(Download.prompts.dockerRegistry),
217
+ dockerPlatform: downloadInNewInstallOnly(Download.prompts.dockerPlatform),
218
+ dockerSave: downloadInNewInstallOnly(Download.prompts.dockerSave),
219
+ gitUrl: downloadInNewInstallOnly(Download.prompts.gitUrl),
220
+ outputDir: downloadInNewInstallOnly({
221
+ ...DOWNLOAD_OUTPUT_DIR_PROMPT,
222
+ hidden: (values) => {
223
+ const source = String(values.source ?? '').trim();
224
+ if (source === 'npm' || source === 'git') {
225
+ return true;
226
+ }
227
+ return DOWNLOAD_OUTPUT_DIR_PROMPT.hidden?.(values) ?? false;
228
+ },
229
+ initialValue: (values) => {
230
+ const source = String(values.source ?? '').trim();
231
+ if (source === 'npm' || source === 'git') {
232
+ const appRootPath = String(values.appRootPath ?? '').trim();
233
+ if (appRootPath) {
234
+ return appRootPath;
235
+ }
236
+ }
237
+ const initialValue = DOWNLOAD_OUTPUT_DIR_PROMPT.initialValue;
238
+ return typeof initialValue === 'function' ? initialValue(values) : String(initialValue ?? '');
239
+ },
240
+ }),
241
+ npmRegistry: downloadInNewInstallOnly(Download.prompts.npmRegistry),
242
+ replace: downloadInNewInstallOnly(Download.prompts.replace),
243
+ devDependencies: downloadInNewInstallOnly(Download.prompts.devDependencies),
244
+ build: downloadInNewInstallOnly(Download.prompts.build),
245
+ buildDts: downloadInNewInstallOnly(Download.prompts.buildDts),
246
+ dbDialect: newInstallOnly(Install.dbPrompts.dbDialect),
247
+ builtinDb: newInstallOnly(Install.dbPrompts.builtinDb),
248
+ builtinDbImage: newInstallOnly(Install.dbPrompts.builtinDbImage),
249
+ dbHost: newInstallOnly(Install.dbPrompts.dbHost),
250
+ dbPort: newInstallOnly(Install.dbPrompts.dbPort),
251
+ dbDatabase: newInstallOnly(Install.dbPrompts.dbDatabase),
252
+ dbUser: newInstallOnly(Install.dbPrompts.dbUser),
253
+ dbPassword: newInstallOnly(Install.dbPrompts.dbPassword),
254
+ rootUsername: newInstallOnly(Install.rootUserPrompts.rootUsername),
255
+ rootEmail: newInstallOnly(Install.rootUserPrompts.rootEmail),
256
+ rootPassword: newInstallOnly(Install.rootUserPrompts.rootPassword),
257
+ rootNickname: newInstallOnly(Install.rootUserPrompts.rootNickname),
258
+ };
259
+ static flags = {
260
+ yes: Flags.boolean({
261
+ char: 'y',
262
+ description: 'Skip prompts and create a new local NocoBase app. Requires an env name.',
263
+ default: false,
264
+ }),
265
+ env: Flags.string({
266
+ char: 'e',
267
+ description: 'Env name for this setup. Required with --yes and --resume',
268
+ }),
269
+ ui: Flags.boolean({
270
+ description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
271
+ default: false,
272
+ }),
273
+ verbose: Flags.boolean({
274
+ description: 'Show detailed command output',
275
+ default: false,
276
+ }),
277
+ 'ui-host': Flags.string({
278
+ description: 'Host for the local --ui setup server (default: 127.0.0.1)',
279
+ }),
280
+ 'ui-port': Flags.integer({
281
+ description: 'Port for the local --ui setup server; 0 lets the OS choose an available port',
282
+ min: 0,
283
+ max: 65535,
284
+ }),
285
+ ..._.pick(EnvAdd.flags, INIT_ENV_ADD_FLAG_NAMES),
286
+ ..._.omit(Install.flags, ['yes', 'env']),
287
+ };
288
+ async run() {
289
+ const parsedResult = await this.parse(Init);
290
+ applyCliLocale(parsedResult.flags.locale);
291
+ const flags = parsedResult.flags;
292
+ const normalizedFlags = { ...flags };
293
+ if (normalizedFlags.ui && normalizedFlags.yes) {
294
+ this.error('--ui cannot be used with --yes.');
295
+ }
296
+ if (normalizedFlags.ui && normalizedFlags.resume) {
297
+ this.error('--ui cannot be used with --resume.');
298
+ }
299
+ if (!normalizedFlags.ui &&
300
+ (normalizedFlags['ui-host'] !== undefined || normalizedFlags['ui-port'] !== undefined)) {
301
+ this.error('--ui-host and --ui-port require --ui.');
302
+ }
303
+ if (normalizedFlags.resume) {
304
+ const envName = String(normalizedFlags.env ?? '').trim();
305
+ if (!envName) {
306
+ p.log.error(formatResumeEnvRequiredMessage());
307
+ this.exit(1);
308
+ }
309
+ p.intro(initTitle());
310
+ await this.syncNocoBaseSkills();
311
+ try {
312
+ await this.config.runCommand('install', this.buildResumeInstallArgv(normalizedFlags));
313
+ }
314
+ catch (error) {
315
+ const message = error instanceof Error ? error.message : String(error);
316
+ this.error(message);
317
+ }
318
+ p.outro('Workspace init finished.');
319
+ return;
320
+ }
321
+ const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY);
322
+ const useBrowserUi = Boolean(normalizedFlags.ui);
323
+ let presetValues = this.buildPresetValuesFromFlags(normalizedFlags);
324
+ if (normalizedFlags.yes && !String(presetValues.appName ?? '').trim()) {
325
+ const formatted = formatSkippedAppNameRequiredMessage();
326
+ p.log.error(highlightInitValidationMessage(formatted));
327
+ this.exit(1);
328
+ }
329
+ const appName = String(presetValues.appName ?? '').trim();
330
+ if (useBrowserUi) {
331
+ p.intro(initTitle());
332
+ p.log.info(translateCli('commands.init.messages.uiOpening'));
333
+ }
334
+ else {
335
+ p.intro(initTitle());
336
+ if (normalizedFlags.yes) {
337
+ p.log.info(`Prompts skipped (--yes). NocoBase will be installed for env "${appName}" using the provided flags and safe defaults.`);
338
+ }
339
+ else if (!interactive) {
340
+ p.log.warn('No interactive terminal detected. NocoBase will be installed using the provided flags and safe defaults.');
341
+ }
342
+ }
343
+ const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
344
+ if (useBrowserUi) {
345
+ presetValues = await runPromptCatalogWebUI({
346
+ stages: Init.buildWebUiStages(),
347
+ values: {
348
+ ...dynamicInitialValues,
349
+ ...presetValues,
350
+ },
351
+ host: normalizedFlags['ui-host']?.trim() || '127.0.0.1',
352
+ port: normalizedFlags['ui-port'] ?? 0,
353
+ pageTitle: initText('webUi.pageTitle'),
354
+ documentHeading: initText('webUi.documentHeading'),
355
+ documentHint: initText('webUi.documentHint'),
356
+ onServerStart: ({ url }) => {
357
+ logInitUiReady(this, url);
358
+ },
359
+ onOpenBrowserError: (_url, _err) => {
360
+ logInitUiBrowserOpenFallback();
361
+ },
362
+ });
363
+ }
364
+ const results = await runPromptCatalog(Init.prompts, {
365
+ initialValues: dynamicInitialValues,
366
+ values: presetValues,
367
+ yes: normalizedFlags.yes || useBrowserUi || !interactive,
368
+ hooks: {
369
+ onCancel: () => {
370
+ p.cancel('Init cancelled.');
371
+ this.exit(0);
372
+ },
373
+ onMissingNonInteractive: (message) => {
374
+ const formatted = formatInitValidationMessage(message);
375
+ p.log.error(highlightInitValidationMessage(formatted));
376
+ this.exit(1);
377
+ },
378
+ },
379
+ command: this,
380
+ });
381
+ const hasNocobase = results.hasNocobase === 'yes';
382
+ const existingEnv = !hasNocobase
383
+ ? await getEnv(String(results.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
384
+ : undefined;
385
+ if (existingEnv && Boolean(normalizedFlags.force)) {
386
+ p.log.warn(`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.`);
387
+ }
388
+ await this.syncNocoBaseSkills();
389
+ let managedInstallResults;
390
+ try {
391
+ // oclif explicit registry keys use `:` (e.g. `env:add`); users still type `nb env add`.
392
+ if (hasNocobase) {
393
+ p.log.step('Running nb env add');
394
+ await this.config.runCommand('env:add', this.buildEnvAddArgv(results));
395
+ }
396
+ else {
397
+ p.log.step('Saving the local env config');
398
+ await this.persistManagedEnvConfig(results, normalizedFlags);
399
+ managedInstallResults = results;
400
+ p.log.step('Running nb install');
401
+ await this.config.runCommand('install', this.buildInstallArgv(results, normalizedFlags));
402
+ }
403
+ }
404
+ catch (error) {
405
+ const message = error instanceof Error ? error.message : String(error);
406
+ const formatted = managedInstallResults
407
+ ? this.formatManagedInstallFailureMessage(message, managedInstallResults, normalizedFlags)
408
+ : message;
409
+ p.outro(pc.red(formatted));
410
+ this.exit(1);
411
+ }
412
+ p.outro('Workspace init finished.');
413
+ }
414
+ static async buildDynamicInitialValuesForInstall(flags, presetValues) {
415
+ const out = {};
416
+ if (!Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
417
+ const appInitialValues = await Install.buildAppPromptInitialValues({
418
+ envName: String(presetValues.appName ?? '').trim(),
419
+ flags: {
420
+ ...flags,
421
+ 'app-root-path': flags['app-root-path'] ?? '',
422
+ 'storage-path': flags['storage-path'] ?? '',
423
+ },
424
+ warnOnPortFallback: false,
425
+ });
426
+ if (appInitialValues.appPort !== undefined) {
427
+ out.appPort = appInitialValues.appPort;
428
+ }
429
+ }
430
+ const downloadSeed = { ...presetValues };
431
+ if (flags.yes
432
+ && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')
433
+ && downloadSeed.fetchSource !== false) {
434
+ downloadSeed.source = 'docker';
435
+ }
436
+ const dbInitial = await Install.buildDbPromptInitialValues({
437
+ flags,
438
+ downloadResults: downloadSeed,
439
+ dbPreset: presetValues,
440
+ warnOnPortFallback: false,
441
+ });
442
+ for (const [key, value] of Object.entries(dbInitial)) {
443
+ if (!Object.prototype.hasOwnProperty.call(presetValues, key)) {
444
+ out[key] = value;
445
+ }
446
+ }
447
+ return out;
448
+ }
449
+ static buildWebUiStages() {
450
+ const c = Init.prompts;
451
+ return [
452
+ {
453
+ sectionTitle: initText('webUi.gettingStarted.title'),
454
+ sectionDescription: initText('webUi.gettingStarted.description'),
455
+ catalog: {
456
+ appName: c.appName,
457
+ hasNocobase: c.hasNocobase,
458
+ },
459
+ },
460
+ {
461
+ sectionTitle: initText('webUi.connectExistingApp.title'),
462
+ sectionDescription: initText('webUi.connectExistingApp.description'),
463
+ catalog: {
464
+ apiBaseUrl: c.apiBaseUrl,
465
+ authType: c.authType,
466
+ accessToken: c.accessToken,
467
+ },
468
+ },
469
+ {
470
+ sectionTitle: initText('webUi.createNewApp.title'),
471
+ sectionDescription: initText('webUi.createNewApp.description'),
472
+ catalog: {
473
+ lang: c.lang,
474
+ appRootPath: c.appRootPath,
475
+ appPort: c.appPort,
476
+ storagePath: c.storagePath,
477
+ fetchSource: c.fetchSource,
478
+ },
479
+ },
480
+ {
481
+ sectionTitle: initText('webUi.downloadAppFiles.title'),
482
+ sectionDescription: initText('webUi.downloadAppFiles.description'),
483
+ catalog: {
484
+ source: c.source,
485
+ version: c.version,
486
+ otherVersion: c.otherVersion,
487
+ dockerRegistry: c.dockerRegistry,
488
+ dockerPlatform: c.dockerPlatform,
489
+ dockerSave: c.dockerSave,
490
+ gitUrl: c.gitUrl,
491
+ outputDir: c.outputDir,
492
+ npmRegistry: c.npmRegistry,
493
+ replace: c.replace,
494
+ devDependencies: c.devDependencies,
495
+ build: c.build,
496
+ buildDts: c.buildDts,
497
+ },
498
+ },
499
+ {
500
+ sectionTitle: initText('webUi.configureDatabase.title'),
501
+ sectionDescription: initText('webUi.configureDatabase.description'),
502
+ catalog: {
503
+ dbDialect: c.dbDialect,
504
+ builtinDb: c.builtinDb,
505
+ builtinDbImage: c.builtinDbImage,
506
+ dbHost: c.dbHost,
507
+ dbPort: c.dbPort,
508
+ dbDatabase: c.dbDatabase,
509
+ dbUser: c.dbUser,
510
+ dbPassword: c.dbPassword,
511
+ },
512
+ },
513
+ {
514
+ sectionTitle: initText('webUi.createAdminAccount.title'),
515
+ sectionDescription: initText('webUi.createAdminAccount.description'),
516
+ catalog: {
517
+ rootUsername: c.rootUsername,
518
+ rootEmail: c.rootEmail,
519
+ rootPassword: c.rootPassword,
520
+ rootNickname: c.rootNickname,
521
+ },
522
+ },
523
+ ];
524
+ }
525
+ buildPresetValuesFromFlags(flags) {
526
+ const preset = {};
527
+ const argv = process.argv.slice(2);
528
+ if (flags.env !== undefined && String(flags.env).trim() !== '') {
529
+ preset.appName = String(flags.env).trim();
530
+ }
531
+ const apiBaseUrl = explicitApiBaseUrlFlag(flags);
532
+ if (apiBaseUrl) {
533
+ preset.hasNocobase = 'yes';
534
+ preset.apiBaseUrl = apiBaseUrl;
535
+ }
536
+ else if (flags['default-api-base-url'] !== undefined
537
+ && String(flags['default-api-base-url']).trim() !== '') {
538
+ preset.apiBaseUrl = String(flags['default-api-base-url']).trim();
539
+ }
540
+ if (flags['auth-type'] !== undefined && String(flags['auth-type']).trim() !== '') {
541
+ preset.authType = String(flags['auth-type']).trim();
542
+ }
543
+ const accessToken = String(flags['access-token'] ?? flags.token ?? '');
544
+ if (flags['access-token'] !== undefined || flags.token !== undefined) {
545
+ preset.accessToken = accessToken;
546
+ }
547
+ if (flags.lang !== undefined && String(flags.lang).trim() !== '') {
548
+ preset.lang = String(flags.lang).trim();
549
+ }
550
+ if (flags['app-root-path'] !== undefined && String(flags['app-root-path']).trim() !== '') {
551
+ preset.appRootPath = String(flags['app-root-path']).trim();
552
+ }
553
+ if (flags['app-port'] !== undefined && String(flags['app-port']).trim() !== '') {
554
+ preset.appPort = String(flags['app-port']).trim();
555
+ }
556
+ if (flags['storage-path'] !== undefined && String(flags['storage-path']).trim() !== '') {
557
+ preset.storagePath = String(flags['storage-path']).trim();
558
+ }
559
+ if (flags['root-username'] !== undefined) {
560
+ preset.rootUsername = String(flags['root-username'] ?? '').trim();
561
+ }
562
+ if (flags['root-email'] !== undefined) {
563
+ preset.rootEmail = String(flags['root-email'] ?? '').trim();
564
+ }
565
+ if (flags['root-password'] !== undefined) {
566
+ preset.rootPassword = String(flags['root-password'] ?? '');
567
+ }
568
+ if (flags['root-nickname'] !== undefined) {
569
+ preset.rootNickname = String(flags['root-nickname'] ?? '').trim();
570
+ }
571
+ if (flags['db-dialect'] !== undefined && String(flags['db-dialect']).trim() !== '') {
572
+ preset.dbDialect = String(flags['db-dialect']).trim();
573
+ }
574
+ if (flags['builtin-db-image'] !== undefined && String(flags['builtin-db-image']).trim() !== '') {
575
+ preset.builtinDbImage = String(flags['builtin-db-image']).trim();
576
+ }
577
+ if (flags['db-host'] !== undefined && String(flags['db-host']).trim() !== '') {
578
+ preset.dbHost = String(flags['db-host']).trim();
579
+ preset.builtinDb = false;
580
+ }
581
+ if (flags['db-port'] !== undefined && String(flags['db-port']).trim() !== '') {
582
+ preset.dbPort = String(flags['db-port']).trim();
583
+ }
584
+ if (flags['db-database'] !== undefined && String(flags['db-database']).trim() !== '') {
585
+ preset.dbDatabase = String(flags['db-database']).trim();
586
+ }
587
+ if (flags['db-user'] !== undefined && String(flags['db-user']).trim() !== '') {
588
+ preset.dbUser = String(flags['db-user']).trim();
589
+ }
590
+ if (flags['db-password'] !== undefined) {
591
+ preset.dbPassword = String(flags['db-password'] ?? '');
592
+ }
593
+ if (argvHasToken(argv, ['--fetch-source'])) {
594
+ preset.fetchSource = Boolean(flags['fetch-source']);
595
+ }
596
+ if (flags.source !== undefined && String(flags.source).trim() !== '') {
597
+ preset.source = String(flags.source).trim();
598
+ }
599
+ if (flags.version !== undefined) {
600
+ const version = String(flags.version).trim() || 'latest';
601
+ preset.version = initVersionPromptValue(version);
602
+ if (preset.version === 'other') {
603
+ preset.otherVersion = version;
604
+ }
605
+ }
606
+ if (flags['docker-registry'] !== undefined && String(flags['docker-registry']).trim() !== '') {
607
+ preset.dockerRegistry = String(flags['docker-registry']).trim();
608
+ }
609
+ if (flags['docker-platform'] !== undefined && String(flags['docker-platform']).trim() !== '') {
610
+ preset.dockerPlatform = String(flags['docker-platform']).trim();
611
+ }
612
+ if (flags['output-dir'] !== undefined && String(flags['output-dir']).trim() !== '') {
613
+ preset.outputDir = String(flags['output-dir']).trim();
614
+ }
615
+ if (flags['git-url'] !== undefined && String(flags['git-url']).trim() !== '') {
616
+ preset.gitUrl = String(flags['git-url']).trim();
617
+ }
618
+ if (flags['npm-registry'] !== undefined) {
619
+ preset.npmRegistry = String(flags['npm-registry'] ?? '');
620
+ }
621
+ if (argvHasToken(argv, ['--replace', '-r'])) {
622
+ preset.replace = Boolean(flags.replace);
623
+ }
624
+ if (argvHasToken(argv, ['--dev-dependencies', '--no-dev-dependencies', '-D'])) {
625
+ preset.devDependencies = Boolean(flags['dev-dependencies']);
626
+ }
627
+ if (argvHasToken(argv, ['--docker-save', '--no-docker-save'])) {
628
+ preset.dockerSave = Boolean(flags['docker-save']);
629
+ }
630
+ if (argvHasToken(argv, ['--build', '--no-build'])) {
631
+ preset.build = Boolean(flags.build);
632
+ }
633
+ if (argvHasToken(argv, ['--build-dts', '--no-build-dts'])) {
634
+ preset.buildDts = Boolean(flags['build-dts']);
635
+ }
636
+ if (argvHasToken(argv, ['--builtin-db', '--no-builtin-db'])) {
637
+ preset.builtinDb = Boolean(flags['builtin-db']);
638
+ }
639
+ if (explicitDbHostFlag(flags)) {
640
+ preset.builtinDb = false;
641
+ }
642
+ if (!preset.hasNocobase && hasDownloadOverride(flags)) {
643
+ preset.hasNocobase = 'no';
644
+ }
645
+ return preset;
646
+ }
647
+ hasAgentsDirInCwd() {
648
+ return existsSync(path.resolve(process.cwd(), '.agents'));
649
+ }
650
+ async syncNocoBaseSkills() {
651
+ try {
652
+ const status = await inspectSkillsStatus();
653
+ if (!status.installed) {
654
+ p.log.step('Installing NocoBase agent skills (nb skills install)');
655
+ await installNocoBaseSkills();
656
+ return;
657
+ }
658
+ p.log.step('Updating NocoBase agent skills (nb skills update)');
659
+ await updateNocoBaseSkills();
660
+ }
661
+ catch (error) {
662
+ const message = error instanceof Error ? error.message : String(error);
663
+ p.outro(pc.red(`Skills sync failed: ${message}`));
664
+ this.exit(1);
665
+ }
666
+ }
667
+ async persistManagedEnvConfig(results, flags = {}) {
668
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
669
+ const appPort = String(results.appPort ?? '').trim();
670
+ const source = String(results.source ?? '').trim();
671
+ const version = resolveInitDownloadVersion(results);
672
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
673
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
674
+ const gitUrl = String(results.gitUrl ?? '').trim();
675
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
676
+ const appRootPath = String(results.appRootPath ?? '').trim();
677
+ const storagePath = String(results.storagePath ?? '').trim();
678
+ const dbDialect = String(results.dbDialect ?? '').trim();
679
+ const builtinDbImage = String(results.builtinDbImage ?? '').trim();
680
+ const dbHost = String(results.dbHost ?? '').trim();
681
+ const dbPort = String(results.dbPort ?? '').trim();
682
+ const dbDatabase = String(results.dbDatabase ?? '').trim();
683
+ const dbUser = String(results.dbUser ?? '').trim();
684
+ const dbPassword = String(results.dbPassword ?? '');
685
+ const builtinDb = explicitDbHostFlag(flags)
686
+ ? false
687
+ : results.builtinDb === undefined
688
+ ? undefined
689
+ : Boolean(results.builtinDb);
690
+ await upsertEnv(envName, {
691
+ ...(source === 'docker'
692
+ ? { kind: 'docker' }
693
+ : source || appRootPath
694
+ ? { kind: 'local' }
695
+ : appPort
696
+ ? { kind: 'http' }
697
+ : {}),
698
+ ...(appPort ? { apiBaseUrl: `http://127.0.0.1:${appPort}/api` } : {}),
699
+ ...(source ? { source } : {}),
700
+ ...(version ? { downloadVersion: version } : {}),
701
+ ...(dockerRegistry ? { dockerRegistry } : {}),
702
+ ...(dockerPlatform ? { dockerPlatform } : {}),
703
+ ...(gitUrl ? { gitUrl } : {}),
704
+ ...(npmRegistry ? { npmRegistry } : {}),
705
+ ...(appRootPath ? { appRootPath } : {}),
706
+ ...(storagePath ? { storagePath } : {}),
707
+ ...(appPort ? { appPort } : {}),
708
+ ...(results.devDependencies !== undefined ? { devDependencies: Boolean(results.devDependencies) } : {}),
709
+ ...(results.build !== undefined ? { build: Boolean(results.build) } : {}),
710
+ ...(results.buildDts !== undefined ? { buildDts: Boolean(results.buildDts) } : {}),
711
+ ...(builtinDb !== undefined ? { builtinDb } : {}),
712
+ ...(dbDialect ? { dbDialect } : {}),
713
+ ...(builtinDbImage || builtinDb === false ? { builtinDbImage: builtinDbImage || undefined } : {}),
714
+ ...(dbHost ? { dbHost } : {}),
715
+ ...(dbPort ? { dbPort } : {}),
716
+ ...(dbDatabase ? { dbDatabase } : {}),
717
+ ...(dbUser ? { dbUser } : {}),
718
+ ...(dbPassword ? { dbPassword } : {}),
719
+ }, { scope: resolveDefaultConfigScope() });
720
+ }
721
+ buildEnvAddArgv(results) {
722
+ const argv = [String(results.appName ?? DEFAULT_INIT_APP_NAME)];
723
+ argv.push('--no-intro');
724
+ argv.push('--api-base-url', String(results.apiBaseUrl ?? DEFAULT_INIT_API_BASE_URL));
725
+ argv.push('--auth-type', String(results.authType ?? 'oauth'));
726
+ if (results.authType === 'token') {
727
+ argv.push('--access-token', String(results.accessToken ?? ''));
728
+ }
729
+ return argv;
730
+ }
731
+ buildInstallArgv(results, flags, options) {
732
+ const argv = ['--no-intro'];
733
+ if (options?.nonInteractive ?? true) {
734
+ argv.unshift('-y');
735
+ }
736
+ const processArgv = process.argv.slice(2);
737
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
738
+ const source = String(results.source ?? '').trim();
739
+ argv.push('--env', envName);
740
+ if (options?.resume) {
741
+ argv.push('--resume');
742
+ }
743
+ if (Boolean(flags.verbose)) {
744
+ argv.push('--verbose');
745
+ }
746
+ const lang = String(results.lang ?? '').trim();
747
+ if (lang) {
748
+ argv.push('--lang', lang);
749
+ }
750
+ const appRootPath = String(results.appRootPath ?? '').trim();
751
+ if (appRootPath) {
752
+ argv.push('--app-root-path', appRootPath);
753
+ }
754
+ const appPort = String(results.appPort ?? '').trim();
755
+ if (appPort && (!flags.yes || argvHasToken(processArgv, ['--app-port']) || appPort !== '13000')) {
756
+ argv.push('--app-port', appPort);
757
+ }
758
+ const storagePath = String(results.storagePath ?? '').trim();
759
+ if (storagePath) {
760
+ argv.push('--storage-path', storagePath);
761
+ }
762
+ if (Boolean(flags.force)) {
763
+ argv.push('--force');
764
+ }
765
+ if (Boolean(results.fetchSource)) {
766
+ argv.push('--fetch-source');
767
+ if (source) {
768
+ argv.push('--source', source);
769
+ }
770
+ const version = resolveInitDownloadVersion(results);
771
+ if (version) {
772
+ argv.push('--version', version);
773
+ }
774
+ const outputDir = String(results.outputDir ?? '').trim();
775
+ if (outputDir) {
776
+ argv.push('--output-dir', outputDir);
777
+ }
778
+ const gitUrl = String(results.gitUrl ?? '').trim();
779
+ if (gitUrl) {
780
+ argv.push('--git-url', gitUrl);
781
+ }
782
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
783
+ if (dockerRegistry) {
784
+ argv.push('--docker-registry', dockerRegistry);
785
+ }
786
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
787
+ if (dockerPlatform) {
788
+ argv.push('--docker-platform', dockerPlatform);
789
+ }
790
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
791
+ if (npmRegistry) {
792
+ argv.push('--npm-registry', npmRegistry);
793
+ }
794
+ if (Boolean(results.replace)) {
795
+ argv.push('--replace');
796
+ }
797
+ if (Boolean(results.devDependencies)) {
798
+ argv.push('--dev-dependencies');
799
+ }
800
+ if (Boolean(results.dockerSave)) {
801
+ argv.push('--docker-save');
802
+ }
803
+ if (results.build !== undefined && !Boolean(results.build)) {
804
+ argv.push('--no-build');
805
+ }
806
+ else if (argvHasToken(processArgv, ['--build', '--no-build']) && flags.build === false) {
807
+ argv.push('--no-build');
808
+ }
809
+ if (Boolean(results.buildDts)) {
810
+ argv.push('--build-dts');
811
+ }
812
+ }
813
+ const explicitDbHost = explicitDbHostFlag(flags);
814
+ const dbHost = explicitDbHost || String(results.dbHost ?? '').trim();
815
+ const builtinDb = explicitDbHost ? false : Boolean(results.builtinDb);
816
+ if (builtinDb) {
817
+ argv.push('--builtin-db');
818
+ }
819
+ else if (explicitDbHost || results.builtinDb !== undefined) {
820
+ argv.push('--no-builtin-db');
821
+ }
822
+ const dbDialect = String(results.dbDialect ?? '').trim();
823
+ if (dbDialect) {
824
+ argv.push('--db-dialect', dbDialect);
825
+ }
826
+ const builtinDbImage = String(results.builtinDbImage ?? '').trim();
827
+ if (builtinDb && builtinDbImage) {
828
+ argv.push('--builtin-db-image', builtinDbImage);
829
+ }
830
+ if (dbHost) {
831
+ argv.push('--db-host', dbHost);
832
+ }
833
+ const dbPort = String(results.dbPort ?? '').trim();
834
+ const dbPortWasProvided = argvHasToken(processArgv, ['--db-port']);
835
+ const dockerBuiltinDbPortIsHidden = builtinDb && source === 'docker';
836
+ const dbDefaultPort = defaultDbPortForDialect(dbDialect);
837
+ if (dbPort
838
+ && (!dockerBuiltinDbPortIsHidden || dbPortWasProvided)
839
+ && (!flags.yes || dbPortWasProvided || dbPort !== dbDefaultPort)) {
840
+ argv.push('--db-port', dbPort);
841
+ }
842
+ const dbDatabase = String(results.dbDatabase ?? '').trim();
843
+ if (dbDatabase) {
844
+ argv.push('--db-database', dbDatabase);
845
+ }
846
+ const dbUser = String(results.dbUser ?? '').trim();
847
+ if (dbUser) {
848
+ argv.push('--db-user', dbUser);
849
+ }
850
+ const dbPassword = String(results.dbPassword ?? '');
851
+ if (dbPassword) {
852
+ argv.push('--db-password', dbPassword);
853
+ }
854
+ const rootUsername = String(results.rootUsername ?? '').trim();
855
+ if (rootUsername) {
856
+ argv.push('--root-username', rootUsername);
857
+ }
858
+ const rootEmail = String(results.rootEmail ?? '').trim();
859
+ if (rootEmail) {
860
+ argv.push('--root-email', rootEmail);
861
+ }
862
+ const rootPassword = String(results.rootPassword ?? '');
863
+ if (rootPassword) {
864
+ argv.push('--root-password', rootPassword);
865
+ }
866
+ const rootNickname = String(results.rootNickname ?? '').trim();
867
+ if (rootNickname) {
868
+ argv.push('--root-nickname', rootNickname);
869
+ }
870
+ return argv;
871
+ }
872
+ buildManagedInstallResumeCommand(results, flags) {
873
+ const argv = ['nb', 'init'];
874
+ if (Boolean(flags.yes)) {
875
+ argv.push('--yes');
876
+ }
877
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
878
+ argv.push('--env', envName);
879
+ if (Boolean(results.fetchSource)) {
880
+ const source = String(results.source ?? '').trim();
881
+ if (source) {
882
+ argv.push('--source', source);
883
+ }
884
+ const version = resolveInitDownloadVersion(results);
885
+ if (version) {
886
+ argv.push('--version', version);
887
+ }
888
+ const outputDir = String(results.outputDir ?? '').trim();
889
+ if (outputDir && outputDir !== String(results.appRootPath ?? '').trim()) {
890
+ argv.push('--output-dir', outputDir);
891
+ }
892
+ const gitUrl = String(results.gitUrl ?? '').trim();
893
+ if (gitUrl) {
894
+ argv.push('--git-url', gitUrl);
895
+ }
896
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
897
+ if (dockerRegistry) {
898
+ argv.push('--docker-registry', dockerRegistry);
899
+ }
900
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
901
+ if (dockerPlatform) {
902
+ argv.push('--docker-platform', dockerPlatform);
903
+ }
904
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
905
+ if (npmRegistry) {
906
+ argv.push('--npm-registry', npmRegistry);
907
+ }
908
+ if (Boolean(results.devDependencies)) {
909
+ argv.push('--dev-dependencies');
910
+ }
911
+ if (Boolean(results.dockerSave)) {
912
+ argv.push('--docker-save');
913
+ }
914
+ if (results.build !== undefined && !Boolean(results.build)) {
915
+ argv.push('--no-build');
916
+ }
917
+ if (Boolean(results.buildDts)) {
918
+ argv.push('--build-dts');
919
+ }
920
+ }
921
+ argv.push('--resume', '--verbose');
922
+ return argv.map(shellQuoteArg).join(' ');
923
+ }
924
+ formatManagedInstallFailureMessage(message, results, flags) {
925
+ const command = this.buildManagedInstallResumeCommand(results, flags);
926
+ return [
927
+ message,
928
+ '',
929
+ translateCli('commands.init.messages.resumeAfterInstallFailure', { command }),
930
+ ].join('\n');
931
+ }
932
+ buildResumeInstallArgv(flags) {
933
+ const preset = this.buildPresetValuesFromFlags(flags);
934
+ if (flags.yes) {
935
+ preset.lang ??= yesInitialValue(Install.appPrompts.lang, 'en-US');
936
+ preset.rootUsername ??= yesInitialValue(Install.rootUserPrompts.rootUsername, 'nocobase');
937
+ preset.rootEmail ??= yesInitialValue(Install.rootUserPrompts.rootEmail, 'admin@nocobase.com');
938
+ preset.rootPassword ??= yesInitialValue(Install.rootUserPrompts.rootPassword, 'admin123');
939
+ preset.rootNickname ??= yesInitialValue(Install.rootUserPrompts.rootNickname, 'Super Admin');
940
+ }
941
+ if (hasDownloadOverride(flags)) {
942
+ preset.fetchSource ??= true;
943
+ }
944
+ preset.replace ??= true;
945
+ return this.buildInstallArgv(preset, flags, {
946
+ nonInteractive: Boolean(flags.yes),
947
+ resume: true,
948
+ });
949
+ }
950
+ }