@nocobase/cli 2.2.0-test.16 → 3.0.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 (37) hide show
  1. package/dist/commands/app/start.js +1 -21
  2. package/dist/commands/config/set.js +1 -1
  3. package/dist/commands/init.js +12 -121
  4. package/dist/commands/install.js +39 -55
  5. package/dist/commands/portal/config.js +88 -0
  6. package/dist/commands/portal/create.js +7 -6
  7. package/dist/commands/portal/deploy.js +2 -2
  8. package/dist/commands/portal/destroy.js +6 -6
  9. package/dist/commands/portal/dev.js +3 -3
  10. package/dist/commands/portal/index.js +1 -1
  11. package/dist/commands/portal/info.js +3 -3
  12. package/dist/commands/portal/list.js +5 -5
  13. package/dist/commands/portal/pull.js +11 -4
  14. package/dist/commands/portal/push.js +4 -4
  15. package/dist/lib/api-client.js +7 -0
  16. package/dist/lib/env-auth.js +2 -2
  17. package/dist/lib/env-config.js +1 -1
  18. package/dist/lib/env-proxy.js +68 -6
  19. package/dist/lib/managed-env-file.js +58 -2
  20. package/dist/lib/managed-init-env.js +1 -1
  21. package/dist/lib/naming.js +9 -0
  22. package/dist/lib/portal-config.js +133 -0
  23. package/dist/lib/portal-configure.js +117 -0
  24. package/dist/lib/portal-create.js +24 -79
  25. package/dist/lib/portal-deploy.js +23 -15
  26. package/dist/lib/portal-destroy.js +3 -3
  27. package/dist/lib/portal-dev.js +3 -3
  28. package/dist/lib/portal-info.js +2 -2
  29. package/dist/lib/portal-list.js +32 -18
  30. package/dist/lib/portal-source.js +150 -43
  31. package/dist/lib/proxy-caddy.js +2 -0
  32. package/dist/lib/proxy-nginx.js +1 -0
  33. package/dist/locale/en-US.json +67 -43
  34. package/dist/locale/zh-CN.json +59 -35
  35. package/nocobase-ctl.config.json +111 -0
  36. package/package.json +4 -3
  37. package/dist/lib/portal-template.js +0 -190
@@ -14,7 +14,6 @@ import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBase
14
14
  import { ensureBuiltinDbReady, ensureLocalPostinstall, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
15
15
  import { clearEnvRootSetup, getEnv, upsertEnv } from '../../lib/auth-store.js';
16
16
  import { buildInitAppEnvVarsFromConfig, isPreparedSetupState } from '../../lib/managed-init-env.js';
17
- import { prepareInitialPortalTemplate } from '../../lib/portal-template.js';
18
17
  import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
19
18
  import { readManagedRuntimeEnvValues } from '../../lib/managed-env-file.js';
20
19
  import { run } from '../../lib/run-npm.js';
@@ -182,19 +181,6 @@ async function finalizePreparedEnv(envName) {
182
181
  setupState: 'installed',
183
182
  });
184
183
  }
185
- async function prepareInitialPortalForRuntime(runtime, verbose) {
186
- await prepareInitialPortalTemplate({
187
- developmentMode: runtime.env.config?.developmentMode,
188
- portalName: runtime.env.config?.portalName,
189
- portalTemplate: runtime.env.config?.portalTemplate,
190
- npmRegistry: runtime.env.config?.npmRegistry,
191
- storagePath: runtime.env.storagePath || runtime.env.config?.storagePath,
192
- verbose,
193
- onStartTask: startTask,
194
- onSucceedTask: succeedTask,
195
- onFailTask: failTask,
196
- });
197
- }
198
184
  export default class AppStart extends Command {
199
185
  static hidden = false;
200
186
  static description = 'Start NocoBase for the selected env. When applicable, the CLI synchronizes licensed commercial plugins first, then prepares and starts the app or recreates the saved Docker container.';
@@ -263,7 +249,7 @@ export default class AppStart extends Command {
263
249
  }
264
250
  const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
265
251
  const runtime = await resolveManagedAppRuntime(requestedEnv);
266
- const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config, { includePortal: false });
252
+ const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config);
267
253
  const isPreparedEnv = isPreparedSetupState(runtime?.env.config?.setupState);
268
254
  const hookCommand = resolveHookCommand(flags['hook-command']);
269
255
  const hookPhase = resolveAppStartHookPhase({
@@ -336,9 +322,6 @@ export default class AppStart extends Command {
336
322
  command: hookCommand,
337
323
  });
338
324
  }
339
- if (isPreparedEnv) {
340
- await prepareInitialPortalForRuntime(runtime, flags.verbose);
341
- }
342
325
  await recreateSavedDockerApp(runtime, {
343
326
  initEnvVars: isPreparedEnv ? preparedInitEnvVars : undefined,
344
327
  verbose: flags.verbose,
@@ -487,9 +470,6 @@ export default class AppStart extends Command {
487
470
  command: hookCommand,
488
471
  });
489
472
  }
490
- if (isPreparedEnv) {
491
- await prepareInitialPortalForRuntime(runtime, flags.verbose);
492
- }
493
473
  await runLocalNocoBaseCommand(runtime, npmArgs, {
494
474
  env: startEnv,
495
475
  stdio: commandStdio,
@@ -14,7 +14,7 @@ export default class ConfigSet extends Command {
14
14
  static description = 'Set a CLI configuration value for a supported configuration key.';
15
15
  static examples = [
16
16
  '<%= config.bin %> <%= command.id %> locale zh-CN',
17
- '<%= config.bin %> <%= command.id %> default-portal-template git@github.com:nocobase/admin-starter.git',
17
+ '<%= config.bin %> <%= command.id %> default-portal-template @nocobase/portal-template-default',
18
18
  '<%= config.bin %> <%= command.id %> update.policy prompt',
19
19
  '<%= config.bin %> <%= command.id %> docker.network nocobase',
20
20
  '<%= config.bin %> <%= command.id %> docker.container-prefix nb',
@@ -27,17 +27,14 @@ import { omitKeys, pickKeys } from "../lib/object-utils.js";
27
27
  import { ENV_CONFIG_SCHEMA_VERSION } from '../lib/env-config.js';
28
28
  import { printInfo, printStage, printVerbose, printWarning } from '../lib/ui.js';
29
29
  import { persistHookScript } from '../lib/hook-script.js';
30
+ import { ensureManagedEnvFileDefaults } from '../lib/managed-env-file.js';
30
31
  import Download from "./download.js";
31
32
  import EnvAdd from "./env/add.js";
32
33
  import Install, { defaultDbPortForDialect } from "./install.js";
33
34
  const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
34
35
  const DEFAULT_INIT_APP_NAME = 'local';
35
- const DEFAULT_INIT_DEVELOPMENT_MODE = 'no-code';
36
- const DEFAULT_INIT_PORTAL_NAME = 'admin';
37
- const DEFAULT_INIT_PORTAL_TEMPLATE = 'git@github.com:nocobase/admin-starter.git';
38
36
  const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
39
37
  const INIT_SETUP_MODES = ['install-new', 'manage-local', 'connect-remote'];
40
- const INIT_DEVELOPMENT_MODES = ['no-code', 'vibe-coding'];
41
38
  const INIT_ENV_ADD_FLAG_NAMES = [
42
39
  'locale',
43
40
  'default-api-base-url',
@@ -84,9 +81,6 @@ function isInstallNewSetupMode(values) {
84
81
  function isInstallLikeSetupMode(values) {
85
82
  return !isRemoteSetupMode(values);
86
83
  }
87
- function isVibeCodingMode(values) {
88
- return String(values.developmentMode ?? DEFAULT_INIT_DEVELOPMENT_MODE).trim() === 'vibe-coding';
89
- }
90
84
  function remoteConnectionOnly(def) {
91
85
  return withExtraHidden(def, (values) => !isRemoteSetupMode(values));
92
86
  }
@@ -394,43 +388,6 @@ Prompt modes:
394
388
  devDependencies: installLikeDownloadExecutionOnly(Download.prompts.devDependencies),
395
389
  build: installLikeDownloadExecutionOnly(Download.prompts.build),
396
390
  buildDts: installLikeDownloadExecutionOnly(Download.prompts.buildDts),
397
- developmentMode: installNewOnly({
398
- type: 'select',
399
- variant: 'radio',
400
- message: initText('prompts.developmentMode.message'),
401
- options: [
402
- {
403
- value: 'no-code',
404
- label: initText('prompts.developmentMode.noCodeLabel'),
405
- hint: initText('prompts.developmentMode.noCodeHint'),
406
- },
407
- {
408
- value: 'vibe-coding',
409
- label: initText('prompts.developmentMode.vibeCodingLabel'),
410
- hint: initText('prompts.developmentMode.vibeCodingHint'),
411
- },
412
- ],
413
- initialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
414
- yesInitialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
415
- required: true,
416
- }),
417
- portalName: installNewOnly({
418
- type: 'text',
419
- message: initText('prompts.portalName.message'),
420
- placeholder: DEFAULT_INIT_PORTAL_NAME,
421
- initialValue: DEFAULT_INIT_PORTAL_NAME,
422
- yesInitialValue: DEFAULT_INIT_PORTAL_NAME,
423
- hidden: (values) => !isVibeCodingMode(values),
424
- required: true,
425
- }),
426
- portalTemplate: installNewOnly({
427
- type: 'text',
428
- message: initText('prompts.portalTemplate.message'),
429
- placeholder: DEFAULT_INIT_PORTAL_TEMPLATE,
430
- yesInitialValue: DEFAULT_INIT_PORTAL_TEMPLATE,
431
- hidden: (values) => !isVibeCodingMode(values),
432
- required: true,
433
- }),
434
391
  dbDialect: installLikeOnly(Install.dbPrompts.dbDialect),
435
392
  builtinDb: installLikeOnly(Install.dbPrompts.builtinDb),
436
393
  builtinDbImage: installLikeOnly(Install.dbPrompts.builtinDbImage),
@@ -456,13 +413,6 @@ Prompt modes:
456
413
  ...Init.prompts,
457
414
  installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
458
415
  };
459
- const defaultPortalTemplate = String(options.defaultPortalTemplate ?? '').trim();
460
- if (defaultPortalTemplate) {
461
- prompts.portalTemplate = {
462
- ...prompts.portalTemplate,
463
- yesInitialValue: defaultPortalTemplate,
464
- };
465
- }
466
416
  if (flags['skip-auth']) {
467
417
  const accessTokenPrompt = {
468
418
  ...EnvAdd.prompts.accessToken,
@@ -512,16 +462,6 @@ Prompt modes:
512
462
  description: 'Setup mode: install a new app, manage a local app by reusing its database, or connect a remote app',
513
463
  options: [...INIT_SETUP_MODES],
514
464
  }),
515
- 'development-mode': Flags.string({
516
- description: 'Initial development mode: no-code or vibe-coding',
517
- options: [...INIT_DEVELOPMENT_MODES],
518
- }),
519
- 'portal-name': Flags.string({
520
- description: 'Initial portal name when --development-mode vibe-coding is used',
521
- }),
522
- 'portal-template': Flags.string({
523
- description: 'Initial portal template Git URL or local path when --development-mode vibe-coding is used',
524
- }),
525
465
  ui: Flags.boolean({
526
466
  description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
527
467
  default: false,
@@ -639,8 +579,7 @@ Prompt modes:
639
579
  const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
640
580
  const defaultUiHost = await resolveDefaultUiHost();
641
581
  const defaultApiHost = await resolveDefaultApiHost();
642
- const defaultPortalTemplate = String(dynamicInitialValues.portalTemplate ?? '').trim();
643
- const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost, defaultPortalTemplate });
582
+ const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost });
644
583
  if (useBrowserUi) {
645
584
  presetValues = await runPromptCatalogWebUI({
646
585
  stages: Init.buildWebUiStages(promptCatalog),
@@ -669,7 +608,7 @@ Prompt modes:
669
608
  ? { setupMode: normalizeInitSetupMode(presetValues.hasNocobase) }
670
609
  : {}),
671
610
  },
672
- yesInitialValues: pickKeys(dynamicInitialValues, ['portalTemplate']),
611
+ yesInitialValues: {},
673
612
  values: presetValues,
674
613
  yes: normalizedFlags.yes || useBrowserUi || !interactive,
675
614
  hooks: {
@@ -742,8 +681,7 @@ Prompt modes:
742
681
  }
743
682
  static async buildDynamicInitialValuesForInstall(flags, presetValues) {
744
683
  const out = {};
745
- const shouldResolveAppInitialValues = !Object.prototype.hasOwnProperty.call(presetValues, 'appPort') ||
746
- !Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate');
684
+ const shouldResolveAppInitialValues = !Object.prototype.hasOwnProperty.call(presetValues, 'appPort');
747
685
  if (shouldResolveAppInitialValues) {
748
686
  const appInitialValues = await Install.buildAppPromptInitialValues({
749
687
  envName: String(presetValues.appName ?? '').trim(),
@@ -752,20 +690,12 @@ Prompt modes:
752
690
  'app-path': flags['app-path'] ?? '',
753
691
  'app-root-path': flags['app-root-path'] ?? '',
754
692
  'storage-path': flags['storage-path'] ?? '',
755
- 'portal-template': flags['portal-template'] ??
756
- (Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')
757
- ? String(presetValues.portalTemplate ?? '')
758
- : undefined),
759
693
  },
760
694
  warnOnPortFallback: false,
761
695
  });
762
696
  if (appInitialValues.appPort !== undefined && !Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
763
697
  out.appPort = appInitialValues.appPort;
764
698
  }
765
- if (appInitialValues.portalTemplate !== undefined &&
766
- !Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')) {
767
- out.portalTemplate = appInitialValues.portalTemplate;
768
- }
769
699
  }
770
700
  const downloadSeed = { ...presetValues };
771
701
  if (flags.yes && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')) {
@@ -844,13 +774,6 @@ Prompt modes:
844
774
  buildDts: c.buildDts,
845
775
  },
846
776
  },
847
- {
848
- sectionTitle: initText('webUi.developmentMode.title'),
849
- sectionDescription: initText('webUi.developmentMode.description'),
850
- catalog: {
851
- developmentMode: c.developmentMode,
852
- },
853
- },
854
777
  {
855
778
  sectionTitle: initText('webUi.configureDatabase.title'),
856
779
  sectionDescription: initText('webUi.configureDatabase.description'),
@@ -951,15 +874,6 @@ Prompt modes:
951
874
  if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
952
875
  preset.appPublicPath = String(flags['app-public-path']).trim();
953
876
  }
954
- if (flags['development-mode'] !== undefined && String(flags['development-mode']).trim() !== '') {
955
- preset.developmentMode = String(flags['development-mode']).trim();
956
- }
957
- if (flags['portal-name'] !== undefined && String(flags['portal-name']).trim() !== '') {
958
- preset.portalName = String(flags['portal-name']).trim();
959
- }
960
- if (flags['portal-template'] !== undefined && String(flags['portal-template']).trim() !== '') {
961
- preset.portalTemplate = String(flags['portal-template']).trim();
962
- }
963
877
  if (flags['root-username'] !== undefined) {
964
878
  preset.rootUsername = String(flags['root-username'] ?? '').trim();
965
879
  }
@@ -1097,9 +1011,6 @@ Prompt modes:
1097
1011
  const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
1098
1012
  const appPort = String(results.appPort ?? '').trim();
1099
1013
  const appPublicPath = String(results.appPublicPath ?? '').trim();
1100
- const developmentMode = String(results.developmentMode ?? '').trim();
1101
- const portalName = String(results.portalName ?? '').trim();
1102
- const portalTemplate = String(results.portalTemplate ?? '').trim();
1103
1014
  const source = String(results.source ?? '').trim();
1104
1015
  const version = resolveInitDownloadVersion(results);
1105
1016
  const dockerRegistry = String(results.dockerRegistry ?? '').trim();
@@ -1126,7 +1037,8 @@ Prompt modes:
1126
1037
  const dbSchema = String(results.dbSchema ?? '').trim();
1127
1038
  const dbTablePrefix = String(results.dbTablePrefix ?? '').trim();
1128
1039
  const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
1129
- const authType = String(results.authType ?? '').trim() || 'oauth';
1040
+ const authTypeInput = String(results.authType ?? '').trim();
1041
+ const authType = authTypeInput === 'basic' || authTypeInput === 'token' || authTypeInput === 'oauth' ? authTypeInput : 'oauth';
1130
1042
  const authUsername = authType === 'basic' ? String(results.username ?? results.rootUsername ?? '').trim() : '';
1131
1043
  const accessToken = String(results.accessToken ?? '');
1132
1044
  const skipDownload = results.skipDownload === true;
@@ -1147,7 +1059,7 @@ Prompt modes:
1147
1059
  : Boolean(results.builtinDb);
1148
1060
  results.appKey = appKey;
1149
1061
  results.timeZone = timeZone;
1150
- await upsertEnv(envName, {
1062
+ const savedEnvConfig = {
1151
1063
  schemaVersion: ENV_CONFIG_SCHEMA_VERSION,
1152
1064
  ...(source === 'docker'
1153
1065
  ? { kind: 'docker' }
@@ -1172,9 +1084,6 @@ Prompt modes:
1172
1084
  ...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
1173
1085
  ...(appPort ? { appPort } : {}),
1174
1086
  ...(appPublicPath ? { appPublicPath } : {}),
1175
- ...(developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE ? { developmentMode } : {}),
1176
- ...(portalName ? { portalName } : {}),
1177
- ...(portalTemplate ? { portalTemplate } : {}),
1178
1087
  ...(appKey ? { appKey } : {}),
1179
1088
  ...(timeZone ? { timezone: timeZone } : {}),
1180
1089
  ...(!skipDownload && results.devDependencies !== undefined
@@ -1195,7 +1104,11 @@ Prompt modes:
1195
1104
  ...(results.dbUnderscored !== undefined ? { dbUnderscored: Boolean(results.dbUnderscored) } : {}),
1196
1105
  setupState: 'prepared',
1197
1106
  ...(String(results.lang ?? '').trim() ? { lang: String(results.lang ?? '').trim() } : {}),
1198
- }, { scope: resolveDefaultConfigScope() });
1107
+ };
1108
+ await upsertEnv(envName, savedEnvConfig, { scope: resolveDefaultConfigScope() });
1109
+ if (source === 'docker' || appPath) {
1110
+ await ensureManagedEnvFileDefaults(envName, savedEnvConfig);
1111
+ }
1199
1112
  }
1200
1113
  buildEnvAddArgv(results) {
1201
1114
  const argv = [String(results.appName ?? DEFAULT_INIT_APP_NAME)];
@@ -1299,18 +1212,6 @@ Prompt modes:
1299
1212
  if (appPublicPath) {
1300
1213
  argv.push('--app-public-path', appPublicPath);
1301
1214
  }
1302
- const developmentMode = String(results.developmentMode ?? '').trim();
1303
- if (developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE) {
1304
- argv.push('--development-mode', developmentMode);
1305
- }
1306
- const portalName = String(results.portalName ?? '').trim();
1307
- if (portalName) {
1308
- argv.push('--portal-name', portalName);
1309
- }
1310
- const portalTemplate = String(results.portalTemplate ?? '').trim();
1311
- if (portalTemplate) {
1312
- argv.push('--portal-template', portalTemplate);
1313
- }
1314
1215
  if (flags.force) {
1315
1216
  argv.push('--force');
1316
1217
  }
@@ -1508,16 +1409,6 @@ Prompt modes:
1508
1409
  delete normalized.rootPassword;
1509
1410
  delete normalized.rootNickname;
1510
1411
  }
1511
- const developmentMode = normalizeConnectionString(normalized.developmentMode) || DEFAULT_INIT_DEVELOPMENT_MODE;
1512
- normalized.developmentMode = developmentMode;
1513
- if (developmentMode === 'vibe-coding') {
1514
- normalized.portalName = normalizeConnectionString(normalized.portalName) || DEFAULT_INIT_PORTAL_NAME;
1515
- normalized.portalTemplate = normalizeConnectionString(normalized.portalTemplate);
1516
- }
1517
- else {
1518
- delete normalized.portalName;
1519
- delete normalized.portalTemplate;
1520
- }
1521
1412
  delete normalized.installApiBaseUrl;
1522
1413
  delete normalized.installAuthType;
1523
1414
  delete normalized.installUsername;
@@ -29,7 +29,7 @@ import { buildStoredEnvConfig } from '../lib/env-config.js';
29
29
  import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
30
30
  import { startDockerLogFollower } from '../lib/docker-log-stream.js';
31
31
  import { buildInitAppEnvVarsFromConfig } from '../lib/managed-init-env.js';
32
- import { prepareInitialPortalTemplate } from '../lib/portal-template.js';
32
+ import { ensureManagedEnvFileDefaults } from '../lib/managed-env-file.js';
33
33
  import { buildHookContext, persistHookScript, resolveHookScriptPath, runHookScriptHook, } from '../lib/hook-script.js';
34
34
  import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
35
35
  import Download from './download.js';
@@ -54,10 +54,10 @@ const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
54
54
  const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
55
55
  const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
56
56
  const DEFAULT_INSTALL_API_HOST = '127.0.0.1';
57
- const DEFAULT_INSTALL_DEVELOPMENT_MODE = 'no-code';
58
- const DEFAULT_INSTALL_PORTAL_NAME = 'admin';
59
- const DEFAULT_INSTALL_PORTAL_TEMPLATE = 'git@github.com:nocobase/admin-starter.git';
60
- const INSTALL_DEVELOPMENT_MODES = ['no-code', 'vibe-coding'];
57
+ const DEFAULT_INSTALL_PORTAL_TYPE = 'ai';
58
+ const DEFAULT_INSTALL_PORTAL_NAME = 'main';
59
+ const DEFAULT_INSTALL_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
60
+ const INSTALL_PORTAL_TYPES = ['no-code', 'ai'];
61
61
  function toOptionalPromptString(value) {
62
62
  const text = String(value ?? '').trim();
63
63
  return text || undefined;
@@ -185,8 +185,8 @@ function defaultBuiltinDbImageForDialect(value, options) {
185
185
  function defaultDbDatabaseForDialect(value) {
186
186
  return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
187
187
  }
188
- function isVibeCodingMode(values) {
189
- return String(values.developmentMode ?? DEFAULT_INSTALL_DEVELOPMENT_MODE).trim() === 'vibe-coding';
188
+ function isAiMode(values) {
189
+ return String(values.portalType ?? DEFAULT_INSTALL_PORTAL_TYPE).trim() === 'ai';
190
190
  }
191
191
  function supportsDbSchemaPrompt(value) {
192
192
  const dialect = String(value ?? '').trim();
@@ -412,15 +412,15 @@ export default class Install extends Command {
412
412
  'app-public-path': Flags.string({
413
413
  description: 'Public path for the local app, for example / or /console/',
414
414
  }),
415
- 'development-mode': Flags.string({
416
- description: 'Initial development mode for the installed app',
417
- options: [...INSTALL_DEVELOPMENT_MODES],
415
+ 'portal-type': Flags.string({
416
+ description: 'Initial portal type for the installed app',
417
+ options: [...INSTALL_PORTAL_TYPES],
418
418
  }),
419
419
  'portal-name': Flags.string({
420
- description: 'Initial portal name when --development-mode vibe-coding is used',
420
+ description: 'Initial portal name',
421
421
  }),
422
422
  'portal-template': Flags.string({
423
- description: 'Initial portal template Git URL or local path when --development-mode vibe-coding is used',
423
+ description: 'Initial portal template npm package or local path when --portal-type ai is used',
424
424
  }),
425
425
  'root-username': Flags.string({
426
426
  description: 'Initial admin username for the installed app',
@@ -531,23 +531,23 @@ export default class Install extends Command {
531
531
  yesInitialValue: '/',
532
532
  validate: validateAppPublicPath,
533
533
  },
534
- developmentMode: {
534
+ portalType: {
535
535
  type: 'select',
536
- message: installText('prompts.developmentMode.message'),
536
+ message: installText('prompts.portalType.message'),
537
537
  options: [
538
538
  {
539
539
  value: 'no-code',
540
- label: installText('prompts.developmentMode.noCodeLabel'),
541
- hint: installText('prompts.developmentMode.noCodeHint'),
540
+ label: installText('prompts.portalType.noCodeLabel'),
541
+ hint: installText('prompts.portalType.noCodeHint'),
542
542
  },
543
543
  {
544
- value: 'vibe-coding',
545
- label: installText('prompts.developmentMode.vibeCodingLabel'),
546
- hint: installText('prompts.developmentMode.vibeCodingHint'),
544
+ value: 'ai',
545
+ label: installText('prompts.portalType.aiLabel'),
546
+ hint: installText('prompts.portalType.aiHint'),
547
547
  },
548
548
  ],
549
- initialValue: DEFAULT_INSTALL_DEVELOPMENT_MODE,
550
- yesInitialValue: DEFAULT_INSTALL_DEVELOPMENT_MODE,
549
+ initialValue: DEFAULT_INSTALL_PORTAL_TYPE,
550
+ yesInitialValue: DEFAULT_INSTALL_PORTAL_TYPE,
551
551
  required: true,
552
552
  },
553
553
  portalName: {
@@ -556,7 +556,6 @@ export default class Install extends Command {
556
556
  placeholder: DEFAULT_INSTALL_PORTAL_NAME,
557
557
  initialValue: DEFAULT_INSTALL_PORTAL_NAME,
558
558
  yesInitialValue: DEFAULT_INSTALL_PORTAL_NAME,
559
- hidden: (values) => !isVibeCodingMode(values),
560
559
  required: true,
561
560
  },
562
561
  portalTemplate: {
@@ -564,7 +563,7 @@ export default class Install extends Command {
564
563
  message: installText('prompts.portalTemplate.message'),
565
564
  placeholder: DEFAULT_INSTALL_PORTAL_TEMPLATE,
566
565
  yesInitialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
567
- hidden: (values) => !isVibeCodingMode(values),
566
+ hidden: (values) => !isAiMode(values),
568
567
  required: true,
569
568
  },
570
569
  };
@@ -826,10 +825,10 @@ export default class Install extends Command {
826
825
  preset.appPublicPath = v;
827
826
  }
828
827
  }
829
- if (flags['development-mode'] !== undefined) {
830
- const v = String(flags['development-mode'] ?? '').trim();
828
+ if (flags['portal-type'] !== undefined) {
829
+ const v = String(flags['portal-type'] ?? '').trim();
831
830
  if (v) {
832
- preset.developmentMode = v;
831
+ preset.portalType = v;
833
832
  }
834
833
  }
835
834
  if (flags['portal-name'] !== undefined) {
@@ -935,7 +934,7 @@ export default class Install extends Command {
935
934
  'appPort',
936
935
  'storagePath',
937
936
  'appPublicPath',
938
- 'developmentMode',
937
+ 'portalType',
939
938
  'portalName',
940
939
  'portalTemplate',
941
940
  ]);
@@ -1187,7 +1186,7 @@ export default class Install extends Command {
1187
1186
  const rootPassword = Install.toOptionalPromptString(config.rootPassword);
1188
1187
  const rootNickname = Install.toOptionalPromptString(config.rootNickname);
1189
1188
  const lang = Install.toOptionalPromptString(config.lang);
1190
- const developmentMode = Install.toOptionalPromptString(config.developmentMode);
1189
+ const portalType = Install.toOptionalPromptString(config.portalType);
1191
1190
  const portalName = Install.toOptionalPromptString(config.portalName);
1192
1191
  const portalTemplate = Install.toOptionalPromptString(config.portalTemplate);
1193
1192
  const auth = config.auth;
@@ -1199,7 +1198,7 @@ export default class Install extends Command {
1199
1198
  ...(appPort ? { appPort } : {}),
1200
1199
  ...(storagePath ? { storagePath } : {}),
1201
1200
  ...(appPublicPath ? { appPublicPath } : {}),
1202
- ...(developmentMode ? { developmentMode } : {}),
1201
+ ...(portalType ? { portalType } : {}),
1203
1202
  ...(portalName ? { portalName } : {}),
1204
1203
  ...(portalTemplate ? { portalTemplate } : {}),
1205
1204
  ...(hookScript ? { hookScript } : {}),
@@ -1524,7 +1523,7 @@ export default class Install extends Command {
1524
1523
  rootEmail: String(params.rootResults.rootEmail ?? ''),
1525
1524
  rootPassword: String(params.rootResults.rootPassword ?? ''),
1526
1525
  rootNickname: String(params.rootResults.rootNickname ?? ''),
1527
- developmentMode: String(params.appResults.developmentMode ?? ''),
1526
+ portalType: String(params.appResults.portalType ?? ''),
1528
1527
  portalName: String(params.appResults.portalName ?? ''),
1529
1528
  portalTemplate: String(params.appResults.portalTemplate ?? ''),
1530
1529
  }, options);
@@ -1882,7 +1881,7 @@ export default class Install extends Command {
1882
1881
  const initEnvVars = Install.buildInitAppEnvVars({
1883
1882
  appResults: params.appResults,
1884
1883
  rootResults: params.rootResults,
1885
- }, { includePortal: false });
1884
+ });
1886
1885
  const containerPort = resolveDockerImageContainerPort(imageRef);
1887
1886
  const args = [
1888
1887
  'run',
@@ -2159,7 +2158,7 @@ export default class Install extends Command {
2159
2158
  ...Install.buildInitAppEnvVars({
2160
2159
  appResults: params.appResults,
2161
2160
  rootResults: params.rootResults,
2162
- }, { includePortal: false }),
2161
+ }),
2163
2162
  };
2164
2163
  setOptionalEnvVar(env, 'APP_PUBLIC_PATH', Install.toOptionalPromptString(params.appResults.appPublicPath));
2165
2164
  setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
@@ -2318,9 +2317,13 @@ export default class Install extends Command {
2318
2317
  }
2319
2318
  async saveInstalledEnv(params) {
2320
2319
  const defaultApiHost = await resolveDefaultApiHost();
2321
- await upsertEnv(params.envName, Install.buildSavedEnvConfig(params, { defaultApiHost }), {
2320
+ const savedEnvConfig = Install.buildSavedEnvConfig(params, { defaultApiHost });
2321
+ await upsertEnv(params.envName, savedEnvConfig, {
2322
2322
  scope: resolveDefaultConfigScope(),
2323
2323
  });
2324
+ if (params.ensureEnvFileDefaults !== false) {
2325
+ await ensureManagedEnvFileDefaults(params.envName, savedEnvConfig);
2326
+ }
2324
2327
  await setCurrentEnv(params.envName, { scope: resolveDefaultConfigScope() });
2325
2328
  }
2326
2329
  async syncInstalledEnvConnection(params) {
@@ -2356,19 +2359,6 @@ export default class Install extends Command {
2356
2359
  }
2357
2360
  return argv;
2358
2361
  }
2359
- async prepareInitialPortalIfNeeded(params) {
2360
- await prepareInitialPortalTemplate({
2361
- developmentMode: Install.toOptionalPromptString(params.appResults.developmentMode),
2362
- portalName: Install.toOptionalPromptString(params.appResults.portalName),
2363
- portalTemplate: Install.toOptionalPromptString(params.appResults.portalTemplate),
2364
- npmRegistry: Install.toOptionalPromptString(params.appResults.npmRegistry),
2365
- storagePath: Install.resolveAbsoluteStoragePath(params.envName, params.appResults),
2366
- verbose: params.verbose,
2367
- onStartTask: (message) => this.logStage(message),
2368
- onSucceedTask: (message) => printInfo(message),
2369
- onFailTask: (message) => printWarning(message),
2370
- });
2371
- }
2372
2362
  async runInstallHookIfNeeded(params) {
2373
2363
  const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
2374
2364
  const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
@@ -2412,7 +2402,7 @@ export default class Install extends Command {
2412
2402
  const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
2413
2403
  const storagePath = Install.toOptionalPromptString(params.appResults.storagePath);
2414
2404
  const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
2415
- const developmentMode = Install.toOptionalPromptString(params.appResults.developmentMode);
2405
+ const portalType = Install.toOptionalPromptString(params.appResults.portalType);
2416
2406
  const portalName = Install.toOptionalPromptString(params.appResults.portalName);
2417
2407
  const portalTemplate = Install.toOptionalPromptString(params.appResults.portalTemplate);
2418
2408
  const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : undefined;
@@ -2449,7 +2439,7 @@ export default class Install extends Command {
2449
2439
  ...(appPublicPath ? { appPublicPath } : {}),
2450
2440
  ...(envFile ? { envFile } : {}),
2451
2441
  lang: params.appResults.lang,
2452
- developmentMode,
2442
+ portalType,
2453
2443
  portalName,
2454
2444
  portalTemplate,
2455
2445
  appKey: params.appResults.appKey,
@@ -2675,6 +2665,7 @@ export default class Install extends Command {
2675
2665
  dbResults,
2676
2666
  rootResults,
2677
2667
  envAddResults,
2668
+ ensureEnvFileDefaults: false,
2678
2669
  });
2679
2670
  if (!parsed['skip-save-env-log']) {
2680
2671
  printInfo(`Saved env config for "${envName}".`);
@@ -2742,13 +2733,6 @@ export default class Install extends Command {
2742
2733
  envAddResults,
2743
2734
  });
2744
2735
  }
2745
- if (shouldStartApp) {
2746
- await this.prepareInitialPortalIfNeeded({
2747
- envName,
2748
- appResults,
2749
- verbose: parsed.verbose,
2750
- });
2751
- }
2752
2736
  if (shouldStartApp) {
2753
2737
  this.logStage('Starting NocoBase');
2754
2738
  await this.config.runCommand('app:start', this.buildAppStartArgv({ envName, verbose: parsed.verbose }));