@nocobase/cli 2.1.0-beta.43 → 2.1.0-beta.44.test.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 (101) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +63 -380
  3. package/assets/env-proxy/nginx/app.conf.tpl +23 -0
  4. package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
  5. package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
  6. package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
  7. package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
  8. package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
  9. package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
  10. package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
  11. package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
  12. package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
  13. package/dist/commands/app/autostart/disable.js +55 -0
  14. package/dist/commands/app/autostart/enable.js +55 -0
  15. package/dist/commands/app/autostart/list.js +37 -0
  16. package/dist/commands/app/autostart/run.js +84 -0
  17. package/dist/commands/app/autostart/shared.js +49 -0
  18. package/dist/commands/app/destroy.js +8 -6
  19. package/dist/commands/app/down.js +2 -2
  20. package/dist/commands/app/logs.js +2 -1
  21. package/dist/commands/app/restart.js +79 -23
  22. package/dist/commands/app/shared.js +1 -1
  23. package/dist/commands/app/start.js +134 -38
  24. package/dist/commands/app/stop.js +31 -2
  25. package/dist/commands/app/upgrade.js +3 -1
  26. package/dist/commands/config/delete.js +4 -1
  27. package/dist/commands/config/get.js +4 -1
  28. package/dist/commands/config/set.js +5 -2
  29. package/dist/commands/env/add.js +19 -39
  30. package/dist/commands/env/info.js +3 -2
  31. package/dist/commands/env/proxy/caddy.js +28 -0
  32. package/dist/commands/env/proxy/index.js +353 -0
  33. package/dist/commands/env/proxy/nginx.js +28 -0
  34. package/dist/commands/env/remove.js +112 -22
  35. package/dist/commands/env/shared.js +17 -9
  36. package/dist/commands/env/update.js +385 -21
  37. package/dist/commands/init.js +233 -91
  38. package/dist/commands/install.js +174 -68
  39. package/dist/commands/license/activate.js +63 -244
  40. package/dist/commands/license/plugins/shared.js +64 -13
  41. package/dist/commands/plugin/import.js +108 -0
  42. package/dist/commands/revision/create.js +89 -0
  43. package/dist/locale/en-US.json +105 -19
  44. package/dist/locale/zh-CN.json +102 -16
  45. package/package.json +5 -8
  46. package/scripts/build.mjs +34 -0
  47. package/scripts/clean.mjs +9 -0
  48. package/tsconfig.json +19 -0
  49. package/LICENSE.txt +0 -107
  50. package/README.zh-CN.md +0 -355
  51. package/dist/lib/api-client.js +0 -335
  52. package/dist/lib/api-command-compat.js +0 -641
  53. package/dist/lib/app-health.js +0 -139
  54. package/dist/lib/app-managed-resources.js +0 -316
  55. package/dist/lib/app-runtime.js +0 -180
  56. package/dist/lib/auth-store.js +0 -405
  57. package/dist/lib/backup.js +0 -171
  58. package/dist/lib/bootstrap.js +0 -409
  59. package/dist/lib/build-config.js +0 -18
  60. package/dist/lib/builtin-db.js +0 -86
  61. package/dist/lib/cli-config.js +0 -309
  62. package/dist/lib/cli-entry-error.js +0 -44
  63. package/dist/lib/cli-home.js +0 -47
  64. package/dist/lib/cli-locale.js +0 -141
  65. package/dist/lib/command-discovery.js +0 -39
  66. package/dist/lib/db-connection-check.js +0 -219
  67. package/dist/lib/docker-env-file.js +0 -52
  68. package/dist/lib/docker-image.js +0 -37
  69. package/dist/lib/docker-log-stream.js +0 -45
  70. package/dist/lib/env-auth.js +0 -960
  71. package/dist/lib/env-config.js +0 -95
  72. package/dist/lib/env-guard.js +0 -62
  73. package/dist/lib/generated-command.js +0 -203
  74. package/dist/lib/http-request.js +0 -49
  75. package/dist/lib/inquirer-theme.js +0 -17
  76. package/dist/lib/inquirer.js +0 -244
  77. package/dist/lib/naming.js +0 -70
  78. package/dist/lib/object-utils.js +0 -76
  79. package/dist/lib/openapi.js +0 -62
  80. package/dist/lib/plugin-storage.js +0 -64
  81. package/dist/lib/post-processors.js +0 -23
  82. package/dist/lib/prompt-catalog-core.js +0 -185
  83. package/dist/lib/prompt-catalog-terminal.js +0 -375
  84. package/dist/lib/prompt-catalog.js +0 -10
  85. package/dist/lib/prompt-validators.js +0 -258
  86. package/dist/lib/prompt-web-ui.js +0 -2227
  87. package/dist/lib/resource-command.js +0 -357
  88. package/dist/lib/resource-request.js +0 -104
  89. package/dist/lib/run-npm.js +0 -385
  90. package/dist/lib/runtime-env-vars.js +0 -32
  91. package/dist/lib/runtime-generator.js +0 -498
  92. package/dist/lib/runtime-store.js +0 -56
  93. package/dist/lib/self-manager.js +0 -301
  94. package/dist/lib/session-id.js +0 -17
  95. package/dist/lib/session-integration.js +0 -703
  96. package/dist/lib/session-store.js +0 -118
  97. package/dist/lib/skills-manager.js +0 -436
  98. package/dist/lib/source-publish.js +0 -309
  99. package/dist/lib/source-registry.js +0 -188
  100. package/dist/lib/startup-update.js +0 -309
  101. package/dist/lib/ui.js +0 -158
@@ -16,6 +16,7 @@ import { getEnv, upsertEnv } from "../lib/auth-store.js";
16
16
  import { runPromptCatalog, } from "../lib/prompt-catalog.js";
17
17
  import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
18
18
  import { resolveDefaultConfigScope } from '../lib/cli-home.js';
19
+ import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
19
20
  import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
20
21
  import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
21
22
  import { installNocoBaseSkills, isNpmRegistryUnavailable } from '../lib/skills-manager.js';
@@ -27,6 +28,7 @@ import Install, { defaultDbPortForDialect } from "./install.js";
27
28
  const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
28
29
  const DEFAULT_INIT_APP_NAME = 'local';
29
30
  const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
31
+ const INIT_SETUP_MODES = ['install-new', 'manage-local', 'connect-remote'];
30
32
  const INIT_ENV_ADD_FLAG_NAMES = [
31
33
  'locale',
32
34
  'default-api-base-url',
@@ -48,14 +50,42 @@ function withExtraHidden(def, extraHidden) {
48
50
  hidden: (values) => extraHidden(values) || (def.hidden?.(values) ?? false),
49
51
  };
50
52
  }
51
- function existingAppOnly(def) {
52
- return withExtraHidden(def, (values) => values.hasNocobase !== 'yes');
53
+ function normalizeInitSetupMode(value) {
54
+ const mode = String(value ?? '').trim();
55
+ if (mode === 'manage-local' || mode === 'connect-remote' || mode === 'install-new') {
56
+ return mode;
57
+ }
58
+ if (mode === 'yes') {
59
+ return 'connect-remote';
60
+ }
61
+ if (mode === 'no') {
62
+ return 'install-new';
63
+ }
64
+ return 'install-new';
65
+ }
66
+ function resolveInitSetupMode(values) {
67
+ return normalizeInitSetupMode(values.setupMode ?? values.hasNocobase);
68
+ }
69
+ function isRemoteSetupMode(values) {
70
+ return resolveInitSetupMode(values) === 'connect-remote';
71
+ }
72
+ function isInstallNewSetupMode(values) {
73
+ return resolveInitSetupMode(values) === 'install-new';
74
+ }
75
+ function isInstallLikeSetupMode(values) {
76
+ return !isRemoteSetupMode(values);
77
+ }
78
+ function remoteConnectionOnly(def) {
79
+ return withExtraHidden(def, (values) => !isRemoteSetupMode(values));
53
80
  }
54
- function newInstallOnly(def) {
55
- return withExtraHidden(def, (values) => values.hasNocobase !== 'no');
81
+ function installLikeOnly(def) {
82
+ return withExtraHidden(def, (values) => !isInstallLikeSetupMode(values));
56
83
  }
57
- function newInstallDownloadExecutionOnly(def) {
58
- return withExtraHidden(def, (values) => values.hasNocobase !== 'no' || values.skipDownload === true);
84
+ function installNewOnly(def) {
85
+ return withExtraHidden(def, (values) => !isInstallNewSetupMode(values));
86
+ }
87
+ function installLikeDownloadExecutionOnly(def) {
88
+ return withExtraHidden(def, (values) => !isInstallLikeSetupMode(values) || values.skipDownload === true);
59
89
  }
60
90
  function argvHasToken(argv, tokens) {
61
91
  return tokens.some((token) => argv.includes(token));
@@ -85,6 +115,26 @@ function explicitApiBaseUrlFlag(flags) {
85
115
  function explicitDbHostFlag(flags) {
86
116
  return String(flags['db-host'] ?? '').trim();
87
117
  }
118
+ function explicitSetupModeFlag(flags) {
119
+ const mode = String(flags['setup-mode'] ?? '').trim();
120
+ return mode ? normalizeInitSetupMode(mode) : undefined;
121
+ }
122
+ function applyLegacyHasNocobaseAlias(values) {
123
+ if (!Object.prototype.hasOwnProperty.call(values, 'setupMode') &&
124
+ !Object.prototype.hasOwnProperty.call(values, 'hasNocobase')) {
125
+ return;
126
+ }
127
+ const setupMode = resolveInitSetupMode(values);
128
+ if (setupMode === 'connect-remote') {
129
+ values.hasNocobase = 'yes';
130
+ return;
131
+ }
132
+ if (setupMode === 'install-new') {
133
+ values.hasNocobase = 'no';
134
+ return;
135
+ }
136
+ delete values.hasNocobase;
137
+ }
88
138
  function optionalInitString(value) {
89
139
  const text = String(value ?? '').trim();
90
140
  return text || undefined;
@@ -161,10 +211,11 @@ export default class Init extends Command {
161
211
  static summary = 'Set up NocoBase so coding agents can connect and work with it';
162
212
  static description = `Set up NocoBase for coding agents in the current workspace.
163
213
 
164
- \`nb init\` prepares a NocoBase environment that coding agents can use. It supports two setup paths:
214
+ \`nb init\` prepares a NocoBase environment that coding agents can use. It supports three setup paths:
165
215
 
166
- - Connect an existing NocoBase app and save it as a CLI env.
167
216
  - Install a new NocoBase app, then save it as a CLI env.
217
+ - Take over managing an app that already exists on this machine by reusing its database.
218
+ - Connect a remote NocoBase app and save it as a CLI env.
168
219
 
169
220
  It can also install NocoBase AI coding skills (\`nocobase/skills\`) so agents get the project-specific workflow guidance.
170
221
 
@@ -173,7 +224,7 @@ If setup was interrupted earlier, use \`--resume\` with an existing env name to
173
224
  Prompt modes:
174
225
  - Default: guided prompts in the terminal.
175
226
  - \`--ui\`: open the same setup flow in a local browser form.
176
- - \`-y\`, \`--yes\`: skip prompts. In this mode \`--env <envName>\` is required, and init creates a new local NocoBase app with safe defaults.
227
+ - \`-y\`, \`--yes\`: skip prompts. In this mode \`--env <envName>\` is required, and init uses flags plus safe defaults for the selected setup mode.
177
228
 
178
229
  \`--ui\` cannot be combined with \`--yes\`.`;
179
230
  static examples = [
@@ -184,8 +235,10 @@ Prompt modes:
184
235
  '<%= config.bin %> <%= command.id %> --env app1 --yes',
185
236
  '<%= config.bin %> <%= command.id %> --env app1 --resume',
186
237
  '<%= config.bin %> <%= command.id %> --env app1 --yes --source docker --version alpha',
238
+ '<%= config.bin %> <%= command.id %> --env app1 --yes --setup-mode manage-local --source npm --version beta',
187
239
  '<%= config.bin %> <%= command.id %> --env app1 --yes --source npm --version alpha --app-port 13080',
188
240
  '<%= config.bin %> <%= command.id %> --env app1 --yes --source git --version fix/cli-v2',
241
+ '<%= config.bin %> <%= command.id %> --env staging --yes --setup-mode connect-remote --api-base-url https://demo.example.com/api',
189
242
  '<%= config.bin %> <%= command.id %> --ui --ui-port 3000',
190
243
  ];
191
244
  static prompts = {
@@ -216,53 +269,61 @@ Prompt modes:
216
269
  required: true,
217
270
  validate: validateInitAppName,
218
271
  },
219
- hasNocobase: {
272
+ setupMode: {
220
273
  type: 'select',
221
274
  variant: 'radio',
222
- message: initText('prompts.hasNocobase.message'),
275
+ message: initText('prompts.setupMode.message'),
223
276
  options: [
224
277
  {
225
- value: 'no',
226
- label: initText('prompts.hasNocobase.noLabel'),
278
+ value: 'install-new',
279
+ label: initText('prompts.setupMode.installNewLabel'),
280
+ hint: initText('prompts.setupMode.installNewHint'),
281
+ },
282
+ {
283
+ value: 'connect-remote',
284
+ label: initText('prompts.setupMode.connectRemoteLabel'),
285
+ hint: initText('prompts.setupMode.connectRemoteHint'),
227
286
  },
228
287
  {
229
- value: 'yes',
230
- label: initText('prompts.hasNocobase.yesLabel'),
288
+ value: 'manage-local',
289
+ label: initText('prompts.setupMode.manageLocalLabel'),
290
+ hint: initText('prompts.setupMode.manageLocalHint'),
291
+ disabled: true,
231
292
  },
232
293
  ],
233
- initialValue: 'no',
234
- yesInitialValue: 'no',
294
+ initialValue: 'install-new',
295
+ yesInitialValue: 'install-new',
235
296
  required: true,
236
297
  },
237
- apiBaseUrl: existingAppOnly({
298
+ apiBaseUrl: remoteConnectionOnly({
238
299
  type: 'text',
239
300
  message: initText('prompts.apiBaseUrl.message'),
240
301
  placeholder: initText('prompts.apiBaseUrl.placeholder'),
241
302
  required: true,
242
303
  validate: validateApiBaseUrl,
243
304
  }),
244
- authType: existingAppOnly(EnvAdd.prompts.authType),
245
- username: existingAppOnly(EnvAdd.prompts.username),
246
- password: existingAppOnly(EnvAdd.prompts.password),
247
- accessToken: existingAppOnly(EnvAdd.prompts.accessToken),
248
- lang: newInstallOnly(Install.appPrompts.lang),
249
- appRootPath: newInstallOnly(Install.appPrompts.appRootPath),
250
- appPort: newInstallOnly(Install.appPrompts.appPort),
251
- storagePath: newInstallOnly(Install.appPrompts.storagePath),
252
- skipDownload: newInstallOnly({
305
+ authType: remoteConnectionOnly(EnvAdd.prompts.authType),
306
+ username: remoteConnectionOnly(EnvAdd.prompts.username),
307
+ password: remoteConnectionOnly(EnvAdd.prompts.password),
308
+ accessToken: remoteConnectionOnly(EnvAdd.prompts.accessToken),
309
+ lang: installLikeOnly(Install.appPrompts.lang),
310
+ appPath: installLikeOnly(Install.appPrompts.appPath),
311
+ appPort: installLikeOnly(Install.appPrompts.appPort),
312
+ appPublicPath: installLikeOnly(Install.appPrompts.appPublicPath),
313
+ skipDownload: installNewOnly({
253
314
  type: 'boolean',
254
315
  message: initText('prompts.skipDownload.message'),
255
316
  initialValue: false,
256
317
  yesInitialValue: false,
257
318
  }),
258
- source: newInstallOnly(Download.prompts.source),
259
- version: newInstallOnly(Download.prompts.version),
260
- otherVersion: newInstallOnly(Download.prompts.otherVersion),
261
- dockerRegistry: newInstallOnly(Download.prompts.dockerRegistry),
262
- dockerPlatform: newInstallOnly(Download.prompts.dockerPlatform),
263
- dockerSave: newInstallDownloadExecutionOnly(Download.prompts.dockerSave),
264
- gitUrl: newInstallOnly(Download.prompts.gitUrl),
265
- outputDir: newInstallDownloadExecutionOnly({
319
+ source: installLikeOnly(Download.prompts.source),
320
+ version: installLikeOnly(Download.prompts.version),
321
+ otherVersion: installLikeOnly(Download.prompts.otherVersion),
322
+ dockerRegistry: installLikeOnly(Download.prompts.dockerRegistry),
323
+ dockerPlatform: installLikeOnly(Download.prompts.dockerPlatform),
324
+ dockerSave: installLikeDownloadExecutionOnly(Download.prompts.dockerSave),
325
+ gitUrl: installLikeOnly(Download.prompts.gitUrl),
326
+ outputDir: installLikeDownloadExecutionOnly({
266
327
  ...DOWNLOAD_OUTPUT_DIR_PROMPT,
267
328
  hidden: (values) => {
268
329
  const source = String(values.source ?? '').trim();
@@ -274,7 +335,8 @@ Prompt modes:
274
335
  initialValue: (values) => {
275
336
  const source = String(values.source ?? '').trim();
276
337
  if (source === 'npm' || source === 'git') {
277
- const appRootPath = String(values.appRootPath ?? '').trim();
338
+ const appPath = String(values.appPath ?? inferConfiguredAppPathFromLegacyConfig(values) ?? '').trim();
339
+ const appRootPath = String(values.appRootPath ?? '').trim() || (appPath ? deriveConfiguredSourcePath(appPath) : '');
278
340
  if (appRootPath) {
279
341
  return appRootPath;
280
342
  }
@@ -283,26 +345,26 @@ Prompt modes:
283
345
  return typeof initialValue === 'function' ? initialValue(values) : String(initialValue ?? '');
284
346
  },
285
347
  }),
286
- npmRegistry: newInstallOnly(Download.prompts.npmRegistry),
287
- replace: newInstallDownloadExecutionOnly(Download.prompts.replace),
288
- devDependencies: newInstallDownloadExecutionOnly(Download.prompts.devDependencies),
289
- build: newInstallDownloadExecutionOnly(Download.prompts.build),
290
- buildDts: newInstallDownloadExecutionOnly(Download.prompts.buildDts),
291
- dbDialect: newInstallOnly(Install.dbPrompts.dbDialect),
292
- builtinDb: newInstallOnly(Install.dbPrompts.builtinDb),
293
- builtinDbImage: newInstallOnly(Install.dbPrompts.builtinDbImage),
294
- dbHost: newInstallOnly(Install.dbPrompts.dbHost),
295
- dbPort: newInstallOnly(Install.dbPrompts.dbPort),
296
- dbDatabase: newInstallOnly(Install.dbPrompts.dbDatabase),
297
- dbUser: newInstallOnly(Install.dbPrompts.dbUser),
298
- dbPassword: newInstallOnly(Install.dbPrompts.dbPassword),
299
- dbSchema: newInstallOnly(Install.dbPrompts.dbSchema),
300
- dbTablePrefix: newInstallOnly(Install.dbPrompts.dbTablePrefix),
301
- dbUnderscored: newInstallOnly(Install.dbPrompts.dbUnderscored),
302
- rootUsername: newInstallOnly(Install.rootUserPrompts.rootUsername),
303
- rootEmail: newInstallOnly(Install.rootUserPrompts.rootEmail),
304
- rootPassword: newInstallOnly(Install.rootUserPrompts.rootPassword),
305
- rootNickname: newInstallOnly(Install.rootUserPrompts.rootNickname),
348
+ npmRegistry: installLikeOnly(Download.prompts.npmRegistry),
349
+ replace: installLikeDownloadExecutionOnly(Download.prompts.replace),
350
+ devDependencies: installLikeDownloadExecutionOnly(Download.prompts.devDependencies),
351
+ build: installLikeDownloadExecutionOnly(Download.prompts.build),
352
+ buildDts: installLikeDownloadExecutionOnly(Download.prompts.buildDts),
353
+ dbDialect: installLikeOnly(Install.dbPrompts.dbDialect),
354
+ builtinDb: installLikeOnly(Install.dbPrompts.builtinDb),
355
+ builtinDbImage: installLikeOnly(Install.dbPrompts.builtinDbImage),
356
+ dbHost: installLikeOnly(Install.dbPrompts.dbHost),
357
+ dbPort: installLikeOnly(Install.dbPrompts.dbPort),
358
+ dbDatabase: installLikeOnly(Install.dbPrompts.dbDatabase),
359
+ dbUser: installLikeOnly(Install.dbPrompts.dbUser),
360
+ dbPassword: installLikeOnly(Install.dbPrompts.dbPassword),
361
+ dbSchema: installLikeOnly(Install.dbPrompts.dbSchema),
362
+ dbTablePrefix: installLikeOnly(Install.dbPrompts.dbTablePrefix),
363
+ dbUnderscored: installLikeOnly(Install.dbPrompts.dbUnderscored),
364
+ rootUsername: installNewOnly(Install.rootUserPrompts.rootUsername),
365
+ rootEmail: installNewOnly(Install.rootUserPrompts.rootEmail),
366
+ rootPassword: installNewOnly(Install.rootUserPrompts.rootPassword),
367
+ rootNickname: installNewOnly(Install.rootUserPrompts.rootNickname),
306
368
  };
307
369
  buildPromptCatalog(flags) {
308
370
  const prompts = {
@@ -321,9 +383,9 @@ Prompt modes:
321
383
  ...EnvAdd.prompts.password,
322
384
  hidden: () => true,
323
385
  };
324
- prompts.username = existingAppOnly(usernamePrompt);
325
- prompts.password = existingAppOnly(passwordPrompt);
326
- prompts.accessToken = existingAppOnly(accessTokenPrompt);
386
+ prompts.username = remoteConnectionOnly(usernamePrompt);
387
+ prompts.password = remoteConnectionOnly(passwordPrompt);
388
+ prompts.accessToken = remoteConnectionOnly(accessTokenPrompt);
327
389
  }
328
390
  return prompts;
329
391
  }
@@ -331,13 +393,17 @@ Prompt modes:
331
393
  static flags = {
332
394
  yes: Flags.boolean({
333
395
  char: 'y',
334
- description: 'Skip prompts and create a new local NocoBase app. Requires an env name.',
396
+ description: 'Skip prompts and use flags plus defaults for the selected setup mode. Requires an env name.',
335
397
  default: false,
336
398
  }),
337
399
  env: Flags.string({
338
400
  char: 'e',
339
401
  description: 'Env name for this setup. Required with --yes and --resume',
340
402
  }),
403
+ 'setup-mode': Flags.string({
404
+ description: 'Setup mode: install a new app, manage a local app by reusing its database, or connect a remote app',
405
+ options: [...INIT_SETUP_MODES],
406
+ }),
341
407
  ui: Flags.boolean({
342
408
  description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
343
409
  default: false,
@@ -465,7 +531,12 @@ Prompt modes:
465
531
  });
466
532
  }
467
533
  const results = await runPromptCatalog(promptCatalog, {
468
- initialValues: dynamicInitialValues,
534
+ initialValues: {
535
+ ...dynamicInitialValues,
536
+ ...(presetValues.setupMode === undefined && presetValues.hasNocobase !== undefined
537
+ ? { setupMode: normalizeInitSetupMode(presetValues.hasNocobase) }
538
+ : {}),
539
+ },
469
540
  values: presetValues,
470
541
  yes: normalizedFlags.yes || useBrowserUi || !interactive,
471
542
  hooks: {
@@ -480,7 +551,7 @@ Prompt modes:
480
551
  },
481
552
  command: this,
482
553
  });
483
- const normalizedResults = {
554
+ const normalizedResults = this.normalizeInitResults({
484
555
  ...pickKeys(presetValues, [
485
556
  'authType',
486
557
  'accessToken',
@@ -492,9 +563,9 @@ Prompt modes:
492
563
  'password',
493
564
  ]),
494
565
  ...results,
495
- };
496
- const hasNocobase = normalizedResults.hasNocobase === 'yes';
497
- const existingEnv = !hasNocobase
566
+ });
567
+ const setupMode = resolveInitSetupMode(normalizedResults);
568
+ const existingEnv = isInstallLikeSetupMode(normalizedResults)
498
569
  ? await getEnv(String(normalizedResults.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
499
570
  : undefined;
500
571
  if (existingEnv && Boolean(normalizedFlags.force)) {
@@ -506,7 +577,7 @@ Prompt modes:
506
577
  let managedInstallResults;
507
578
  try {
508
579
  // oclif explicit registry keys use `:` (e.g. `env:add`); users still type `nb env add`.
509
- if (hasNocobase) {
580
+ if (setupMode === 'connect-remote') {
510
581
  logInitStage('Connecting to the env');
511
582
  printVerbose('Running nb env add');
512
583
  await this.config.runCommand('env:add', this.buildEnvAddArgv(normalizedResults));
@@ -536,6 +607,7 @@ Prompt modes:
536
607
  envName: String(presetValues.appName ?? '').trim(),
537
608
  flags: {
538
609
  ...flags,
610
+ 'app-path': flags['app-path'] ?? '',
539
611
  'app-root-path': flags['app-root-path'] ?? '',
540
612
  'storage-path': flags['storage-path'] ?? '',
541
613
  },
@@ -569,7 +641,7 @@ Prompt modes:
569
641
  sectionDescription: initText('webUi.gettingStarted.description'),
570
642
  catalog: {
571
643
  appName: c.appName,
572
- hasNocobase: c.hasNocobase,
644
+ setupMode: c.setupMode,
573
645
  },
574
646
  },
575
647
  {
@@ -588,9 +660,8 @@ Prompt modes:
588
660
  sectionDescription: initText('webUi.createNewApp.description'),
589
661
  catalog: {
590
662
  lang: c.lang,
591
- appRootPath: c.appRootPath,
663
+ appPath: c.appPath,
592
664
  appPort: c.appPort,
593
- storagePath: c.storagePath,
594
665
  skipDownload: c.skipDownload,
595
666
  },
596
667
  },
@@ -645,12 +716,16 @@ Prompt modes:
645
716
  buildPresetValuesFromFlags(flags) {
646
717
  const preset = {};
647
718
  const argv = process.argv.slice(2);
719
+ const setupMode = explicitSetupModeFlag(flags);
648
720
  if (flags.env !== undefined && String(flags.env).trim() !== '') {
649
721
  preset.appName = String(flags.env).trim();
650
722
  }
723
+ if (setupMode) {
724
+ preset.setupMode = setupMode;
725
+ }
651
726
  const apiBaseUrl = explicitApiBaseUrlFlag(flags);
652
727
  if (apiBaseUrl) {
653
- preset.hasNocobase = 'yes';
728
+ preset.setupMode ??= 'connect-remote';
654
729
  preset.apiBaseUrl = apiBaseUrl;
655
730
  }
656
731
  else if (flags['default-api-base-url'] !== undefined && String(flags['default-api-base-url']).trim() !== '') {
@@ -675,6 +750,9 @@ Prompt modes:
675
750
  if (flags.lang !== undefined && String(flags.lang).trim() !== '') {
676
751
  preset.lang = String(flags.lang).trim();
677
752
  }
753
+ if (flags['app-path'] !== undefined && String(flags['app-path']).trim() !== '') {
754
+ preset.appPath = String(flags['app-path']).trim();
755
+ }
678
756
  if (flags['app-root-path'] !== undefined && String(flags['app-root-path']).trim() !== '') {
679
757
  preset.appRootPath = String(flags['app-root-path']).trim();
680
758
  }
@@ -684,6 +762,9 @@ Prompt modes:
684
762
  if (flags['storage-path'] !== undefined && String(flags['storage-path']).trim() !== '') {
685
763
  preset.storagePath = String(flags['storage-path']).trim();
686
764
  }
765
+ if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
766
+ preset.appPublicPath = String(flags['app-public-path']).trim();
767
+ }
687
768
  if (flags['root-username'] !== undefined) {
688
769
  preset.rootUsername = String(flags['root-username'] ?? '').trim();
689
770
  }
@@ -783,9 +864,13 @@ Prompt modes:
783
864
  if (explicitDbHostFlag(flags)) {
784
865
  preset.builtinDb = false;
785
866
  }
786
- if (!preset.hasNocobase && hasDownloadOverride(flags)) {
787
- preset.hasNocobase = 'no';
867
+ if (!preset.setupMode && hasDownloadOverride(flags)) {
868
+ preset.setupMode = 'install-new';
869
+ }
870
+ if (preset.setupMode === 'manage-local') {
871
+ preset.skipDownload = false;
788
872
  }
873
+ applyLegacyHasNocobaseAlias(preset);
789
874
  return preset;
790
875
  }
791
876
  hasAgentsDirInCwd() {
@@ -822,8 +907,16 @@ Prompt modes:
822
907
  const dockerPlatform = String(results.dockerPlatform ?? '').trim();
823
908
  const gitUrl = String(results.gitUrl ?? '').trim();
824
909
  const npmRegistry = String(results.npmRegistry ?? '').trim();
910
+ const appPath = String(results.appPath ?? '').trim() ||
911
+ inferConfiguredAppPathFromLegacyConfig({
912
+ appRootPath: results.appRootPath,
913
+ storagePath: results.storagePath,
914
+ }) ||
915
+ '';
825
916
  const appRootPath = String(results.appRootPath ?? '').trim();
826
917
  const storagePath = String(results.storagePath ?? '').trim();
918
+ const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : '';
919
+ const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : '';
827
920
  const dbDialect = String(results.dbDialect ?? '').trim();
828
921
  const builtinDbImage = String(results.builtinDbImage ?? '').trim();
829
922
  const dbHost = String(results.dbHost ?? '').trim();
@@ -850,7 +943,7 @@ Prompt modes:
850
943
  await upsertEnv(envName, {
851
944
  ...(source === 'docker'
852
945
  ? { kind: 'docker' }
853
- : source || appRootPath
946
+ : source || appPath || appRootPath
854
947
  ? { kind: 'local' }
855
948
  : appPort
856
949
  ? { kind: 'http' }
@@ -865,8 +958,9 @@ Prompt modes:
865
958
  ...(dockerPlatform ? { dockerPlatform } : {}),
866
959
  ...(gitUrl ? { gitUrl } : {}),
867
960
  ...(npmRegistry ? { npmRegistry } : {}),
868
- ...(appRootPath ? { appRootPath } : {}),
869
- ...(storagePath ? { storagePath } : {}),
961
+ ...(appPath ? { appPath } : {}),
962
+ ...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
963
+ ...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
870
964
  ...(appPort ? { appPort } : {}),
871
965
  ...(appKey ? { appKey } : {}),
872
966
  ...(timeZone ? { timezone: timeZone } : {}),
@@ -919,8 +1013,11 @@ Prompt modes:
919
1013
  const processArgv = process.argv.slice(2);
920
1014
  const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
921
1015
  const source = String(results.source ?? '').trim();
922
- const skipDownload = Boolean(flags['skip-download']) || results.skipDownload === true;
923
- const hasNocobase = String(results.hasNocobase ?? '').trim() === 'yes';
1016
+ const setupMode = resolveInitSetupMode({
1017
+ setupMode: results.setupMode ?? flags['setup-mode'],
1018
+ hasNocobase: results.hasNocobase,
1019
+ });
1020
+ const skipDownload = setupMode === 'manage-local' ? false : Boolean(flags['skip-download']) || results.skipDownload === true;
924
1021
  const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
925
1022
  const authType = String(results.authType ?? '').trim();
926
1023
  const accessToken = String(results.accessToken ?? '');
@@ -933,7 +1030,7 @@ Prompt modes:
933
1030
  if (flags.verbose) {
934
1031
  argv.push('--verbose');
935
1032
  }
936
- if (hasNocobase && apiBaseUrl) {
1033
+ if (setupMode === 'connect-remote' && apiBaseUrl) {
937
1034
  argv.push('--api-base-url', apiBaseUrl);
938
1035
  }
939
1036
  if (authType) {
@@ -955,8 +1052,17 @@ Prompt modes:
955
1052
  if (lang) {
956
1053
  argv.push('--lang', lang);
957
1054
  }
1055
+ const appPath = String(results.appPath ?? '').trim() ||
1056
+ inferConfiguredAppPathFromLegacyConfig({
1057
+ appRootPath: results.appRootPath,
1058
+ storagePath: results.storagePath,
1059
+ }) ||
1060
+ '';
1061
+ if (appPath) {
1062
+ argv.push('--app-path', appPath);
1063
+ }
958
1064
  const appRootPath = String(results.appRootPath ?? '').trim();
959
- if (appRootPath) {
1065
+ if (appRootPath && (!appPath || !areConfiguredPathsEquivalent(appRootPath, deriveConfiguredSourcePath(appPath)))) {
960
1066
  argv.push('--app-root-path', appRootPath);
961
1067
  }
962
1068
  const appPort = String(results.appPort ?? '').trim();
@@ -964,9 +1070,13 @@ Prompt modes:
964
1070
  argv.push('--app-port', appPort);
965
1071
  }
966
1072
  const storagePath = String(results.storagePath ?? '').trim();
967
- if (storagePath) {
1073
+ if (storagePath && (!appPath || !areConfiguredPathsEquivalent(storagePath, deriveConfiguredStoragePath(appPath)))) {
968
1074
  argv.push('--storage-path', storagePath);
969
1075
  }
1076
+ const appPublicPath = String(results.appPublicPath ?? '').trim();
1077
+ if (appPublicPath) {
1078
+ argv.push('--app-public-path', appPublicPath);
1079
+ }
970
1080
  if (flags.force) {
971
1081
  argv.push('--force');
972
1082
  }
@@ -1072,20 +1182,21 @@ Prompt modes:
1072
1182
  if (results.dbUnderscored !== undefined) {
1073
1183
  argv.push(results.dbUnderscored ? '--db-underscored' : '--no-db-underscored');
1074
1184
  }
1185
+ const includeRootUser = setupMode === 'install-new';
1075
1186
  const rootUsername = String(results.rootUsername ?? '').trim();
1076
- if (rootUsername) {
1187
+ if (includeRootUser && rootUsername) {
1077
1188
  argv.push('--root-username', rootUsername);
1078
1189
  }
1079
1190
  const rootEmail = String(results.rootEmail ?? '').trim();
1080
- if (rootEmail) {
1191
+ if (includeRootUser && rootEmail) {
1081
1192
  argv.push('--root-email', rootEmail);
1082
1193
  }
1083
1194
  const rootPassword = String(results.rootPassword ?? '');
1084
- if (rootPassword) {
1195
+ if (includeRootUser && rootPassword) {
1085
1196
  argv.push('--root-password', rootPassword);
1086
1197
  }
1087
1198
  const rootNickname = String(results.rootNickname ?? '').trim();
1088
- if (rootNickname) {
1199
+ if (includeRootUser && rootNickname) {
1089
1200
  argv.push('--root-nickname', rootNickname);
1090
1201
  }
1091
1202
  return argv;
@@ -1097,8 +1208,12 @@ Prompt modes:
1097
1208
  }
1098
1209
  const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
1099
1210
  argv.push('--env', envName);
1211
+ const setupMode = resolveInitSetupMode(results);
1212
+ if (setupMode === 'manage-local') {
1213
+ argv.push('--setup-mode', 'manage-local');
1214
+ }
1100
1215
  const source = String(results.source ?? '').trim();
1101
- const skipDownload = Boolean(flags['skip-download']) || results.skipDownload === true;
1216
+ const skipDownload = setupMode === 'manage-local' ? false : Boolean(flags['skip-download']) || results.skipDownload === true;
1102
1217
  if (source) {
1103
1218
  argv.push('--source', source);
1104
1219
  }
@@ -1127,7 +1242,14 @@ Prompt modes:
1127
1242
  }
1128
1243
  else {
1129
1244
  const outputDir = String(results.outputDir ?? '').trim();
1130
- if (outputDir && outputDir !== String(results.appRootPath ?? '').trim()) {
1245
+ const appPath = String(results.appPath ?? '').trim() ||
1246
+ inferConfiguredAppPathFromLegacyConfig({
1247
+ appRootPath: results.appRootPath,
1248
+ storagePath: results.storagePath,
1249
+ }) ||
1250
+ '';
1251
+ const expectedOutputDir = String(results.appRootPath ?? '').trim() || (appPath ? deriveConfiguredSourcePath(appPath) : '');
1252
+ if (outputDir && outputDir !== expectedOutputDir) {
1131
1253
  argv.push('--output-dir', outputDir);
1132
1254
  }
1133
1255
  if (results.devDependencies) {
@@ -1152,14 +1274,20 @@ Prompt modes:
1152
1274
  }
1153
1275
  buildResumeInstallArgv(flags) {
1154
1276
  const preset = this.buildPresetValuesFromFlags(flags);
1277
+ const setupMode = resolveInitSetupMode(preset);
1155
1278
  if (flags.yes) {
1156
1279
  preset.lang ??= yesInitialValue(Install.appPrompts.lang, 'en-US');
1157
- preset.rootUsername ??= yesInitialValue(Install.rootUserPrompts.rootUsername, 'nocobase');
1158
- preset.rootEmail ??= yesInitialValue(Install.rootUserPrompts.rootEmail, 'admin@nocobase.com');
1159
- preset.rootPassword ??= yesInitialValue(Install.rootUserPrompts.rootPassword, 'admin123');
1160
- preset.rootNickname ??= yesInitialValue(Install.rootUserPrompts.rootNickname, 'Super Admin');
1280
+ if (setupMode === 'install-new') {
1281
+ preset.rootUsername ??= yesInitialValue(Install.rootUserPrompts.rootUsername, 'nocobase');
1282
+ preset.rootEmail ??= yesInitialValue(Install.rootUserPrompts.rootEmail, 'admin@nocobase.com');
1283
+ preset.rootPassword ??= yesInitialValue(Install.rootUserPrompts.rootPassword, 'admin123');
1284
+ preset.rootNickname ??= yesInitialValue(Install.rootUserPrompts.rootNickname, 'Super Admin');
1285
+ }
1286
+ }
1287
+ if (setupMode === 'manage-local') {
1288
+ preset.skipDownload = false;
1161
1289
  }
1162
- if (!flags['skip-download']) {
1290
+ else if (!flags['skip-download']) {
1163
1291
  preset.replace ??= true;
1164
1292
  }
1165
1293
  return this.buildInstallArgv(preset, flags, {
@@ -1167,4 +1295,18 @@ Prompt modes:
1167
1295
  resume: true,
1168
1296
  });
1169
1297
  }
1298
+ normalizeInitResults(results) {
1299
+ const normalized = { ...results };
1300
+ const setupMode = resolveInitSetupMode(normalized);
1301
+ normalized.setupMode = setupMode;
1302
+ applyLegacyHasNocobaseAlias(normalized);
1303
+ if (setupMode === 'manage-local') {
1304
+ normalized.skipDownload = false;
1305
+ delete normalized.rootUsername;
1306
+ delete normalized.rootEmail;
1307
+ delete normalized.rootPassword;
1308
+ delete normalized.rootNickname;
1309
+ }
1310
+ return normalized;
1311
+ }
1170
1312
  }