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

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 (52) hide show
  1. package/README.md +37 -49
  2. package/README.zh-CN.md +36 -47
  3. package/dist/commands/app/down.js +260 -0
  4. package/dist/commands/app/logs.js +98 -0
  5. package/dist/commands/app/restart.js +75 -0
  6. package/dist/commands/app/start.js +252 -0
  7. package/dist/commands/app/stop.js +98 -0
  8. package/dist/commands/app/upgrade.js +595 -0
  9. package/dist/commands/build.js +3 -48
  10. package/dist/commands/dev.js +3 -147
  11. package/dist/commands/down.js +3 -188
  12. package/dist/commands/download.js +4 -856
  13. package/dist/commands/env/add.js +28 -23
  14. package/dist/commands/env/info.js +152 -0
  15. package/dist/commands/env/list.js +23 -9
  16. package/dist/commands/env/shared.js +158 -0
  17. package/dist/commands/{prompts-stages.js → examples/prompts-stages.js} +3 -3
  18. package/dist/commands/{prompts-test.js → examples/prompts-test.js} +3 -3
  19. package/dist/commands/init.js +84 -6
  20. package/dist/commands/install.js +288 -61
  21. package/dist/commands/logs.js +3 -88
  22. package/dist/commands/plugin/disable.js +64 -0
  23. package/dist/commands/plugin/enable.js +64 -0
  24. package/dist/commands/plugin/list.js +62 -0
  25. package/dist/commands/pm/disable.js +3 -54
  26. package/dist/commands/pm/enable.js +3 -54
  27. package/dist/commands/pm/list.js +3 -52
  28. package/dist/commands/restart.js +3 -65
  29. package/dist/commands/scaffold/migration.js +1 -1
  30. package/dist/commands/scaffold/plugin.js +1 -1
  31. package/dist/commands/skills/remove.js +71 -0
  32. package/dist/commands/skills/update.js +7 -0
  33. package/dist/commands/source/build.js +58 -0
  34. package/dist/commands/source/dev.js +157 -0
  35. package/dist/commands/source/download.js +866 -0
  36. package/dist/commands/source/test.js +467 -0
  37. package/dist/commands/start.js +3 -209
  38. package/dist/commands/stop.js +3 -88
  39. package/dist/commands/test.js +3 -457
  40. package/dist/commands/upgrade.js +3 -585
  41. package/dist/help/runtime-help.js +3 -0
  42. package/dist/lib/api-client.js +20 -6
  43. package/dist/lib/app-health.js +126 -0
  44. package/dist/lib/app-managed-resources.js +264 -0
  45. package/dist/lib/auth-store.js +5 -2
  46. package/dist/lib/cli-home.js +7 -6
  47. package/dist/lib/cli-locale.js +15 -1
  48. package/dist/lib/env-config.js +80 -0
  49. package/dist/lib/prompt-web-ui.js +13 -6
  50. package/dist/lib/skills-manager.js +34 -7
  51. package/package.json +27 -4
  52. package/dist/commands/ps.js +0 -119
@@ -16,12 +16,13 @@ import path from 'node:path';
16
16
  import { exit } from 'node:process';
17
17
  import { runPromptCatalog, } from "../lib/prompt-catalog.js";
18
18
  import { applyCliLocale, localeText, resolveCliLocale, translateCli, } from "../lib/cli-locale.js";
19
- import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath, } from '../lib/cli-home.js';
19
+ import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
20
20
  import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
21
21
  import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
22
22
  import { run, runNocoBaseCommand } from '../lib/run-npm.js';
23
23
  import { startTask, stopTask, updateTask } from '../lib/ui.js';
24
- import { ensureWorkspaceName, getEnv } from '../lib/auth-store.js';
24
+ import { ensureWorkspaceName, getEnv, loadAuthConfig, upsertEnv } from '../lib/auth-store.js';
25
+ import { buildStoredEnvConfig } from '../lib/env-config.js';
25
26
  import Download, { defaultDockerRegistryForLang, } from './download.js';
26
27
  import EnvAdd from "./env/add.js";
27
28
  const DEFAULT_INSTALL_ENV_NAME = 'local';
@@ -245,7 +246,8 @@ async function commandOutput(command, args, options) {
245
246
  });
246
247
  }
247
248
  export default class Install extends Command {
248
- static description = 'Install NocoBase: database, storage, admin user, and `nocobase-v1 install`. Optionally run `nb download` first; distribution and image details are configured on `nb download`, not here. Use `--resume` to continue an interrupted setup from the saved workspace env config.';
249
+ static hidden = true;
250
+ static description = 'Install NocoBase: database, storage, admin user, and `nocobase-v1 install`. Optionally run `nb source download` first; distribution and image details are configured on `nb source download`, not here. Use `--resume` to continue an interrupted setup from the saved workspace env config.';
249
251
  static examples = [
250
252
  '<%= config.bin %> <%= command.id %>',
251
253
  '<%= config.bin %> <%= command.id %> --env app1',
@@ -276,6 +278,25 @@ export default class Install extends Command {
276
278
  char: 'e',
277
279
  description: 'App/env name to create or update. Defaults app paths to ./<envName>/source/ and ./<envName>/storage/.',
278
280
  }),
281
+ 'default-api-base-url': Flags.string({
282
+ char: 'd',
283
+ hidden: true,
284
+ description: 'Default API base URL for HTTP API calls, including the /api prefix (e.g. http://localhost:13000/api)',
285
+ }),
286
+ 'api-base-url': Flags.string({
287
+ char: 'u',
288
+ description: 'Root URL for HTTP API calls, including the /api prefix (e.g. http://localhost:13000/api)',
289
+ }),
290
+ 'auth-type': Flags.string({
291
+ char: 'a',
292
+ description: 'Authentication: token (API key) or oauth (browser login via `nb env auth`)',
293
+ options: ['token', 'oauth'],
294
+ }),
295
+ 'access-token': Flags.string({
296
+ char: 't',
297
+ aliases: ['token'],
298
+ description: 'API key or access token when using --auth-type token',
299
+ }),
279
300
  lang: Flags.string({ description: 'Language for the installed NocoBase app', char: 'l', required: false }),
280
301
  force: Flags.boolean({
281
302
  description: 'Reconfigure an existing env and replace conflicting runtime resources when needed',
@@ -374,7 +395,7 @@ export default class Install extends Command {
374
395
  type: 'text',
375
396
  message: installText('prompts.appPort.message'),
376
397
  placeholder: installText('prompts.appPort.placeholder'),
377
- validate: validateAvailableTcpPort,
398
+ validate: Install.validateAppPort,
378
399
  },
379
400
  storagePath: {
380
401
  type: 'text',
@@ -434,7 +455,7 @@ export default class Install extends Command {
434
455
  placeholder: installText('prompts.dbPort.placeholder'),
435
456
  initialValue: (values) => defaultDbPortForDialect(values.dbDialect),
436
457
  required: true,
437
- validate: validateTcpPort,
458
+ validate: Install.validateDbPort,
438
459
  hidden: (values) => Boolean(values.builtinDb)
439
460
  && String(values.source ?? '').trim() === 'docker',
440
461
  },
@@ -492,7 +513,7 @@ export default class Install extends Command {
492
513
  * App catalog with `env` seeded into `out` first so `storagePath`’s `initialValue(values)`
493
514
  * sees `values.env` (same iteration order as {@link runPromptCatalog}).
494
515
  */
495
- static buildAppPromptsCatalog(seedEnv) {
516
+ static buildAppPromptsCatalog(seedEnv, options) {
496
517
  return {
497
518
  seedEnv: {
498
519
  type: 'run',
@@ -500,12 +521,27 @@ export default class Install extends Command {
500
521
  values.env = seedEnv;
501
522
  },
502
523
  },
524
+ seedResume: {
525
+ type: 'run',
526
+ run: (values) => {
527
+ const record = values;
528
+ record.resume = Boolean(options?.resume);
529
+ },
530
+ },
503
531
  ...Install.appPrompts,
504
532
  };
505
533
  }
506
- static buildDbPromptsCatalog(downloadResults) {
534
+ static buildDbPromptsCatalog(envName, downloadResults, options) {
507
535
  const source = String(downloadResults.source ?? '').trim();
508
536
  return {
537
+ seedEnv: {
538
+ type: 'run',
539
+ run: (values) => {
540
+ if (envName) {
541
+ values.env = envName;
542
+ }
543
+ },
544
+ },
509
545
  seedDownloadSource: {
510
546
  type: 'run',
511
547
  run: (values) => {
@@ -514,6 +550,13 @@ export default class Install extends Command {
514
550
  }
515
551
  },
516
552
  },
553
+ seedResume: {
554
+ type: 'run',
555
+ run: (values) => {
556
+ const record = values;
557
+ record.resume = Boolean(options?.resume);
558
+ },
559
+ },
517
560
  ...Install.dbPrompts,
518
561
  };
519
562
  }
@@ -533,6 +576,25 @@ export default class Install extends Command {
533
576
  static buildPresetValuesFromFlags(flags) {
534
577
  const preset = {};
535
578
  const argv = process.argv.slice(2);
579
+ const apiBaseUrl = Install.toOptionalPromptString(flags['api-base-url']);
580
+ if (apiBaseUrl) {
581
+ preset.apiBaseUrl = apiBaseUrl;
582
+ }
583
+ else if (flags['default-api-base-url'] !== undefined) {
584
+ const defaultApiBaseUrl = Install.toOptionalPromptString(flags['default-api-base-url']);
585
+ if (defaultApiBaseUrl) {
586
+ preset.apiBaseUrl = defaultApiBaseUrl;
587
+ }
588
+ }
589
+ if (flags['auth-type'] !== undefined) {
590
+ const authType = Install.toOptionalPromptString(flags['auth-type']);
591
+ if (authType) {
592
+ preset.authType = authType;
593
+ }
594
+ }
595
+ if (flags['access-token'] !== undefined || flags.token !== undefined) {
596
+ preset.accessToken = String(flags['access-token'] ?? flags.token ?? '');
597
+ }
536
598
  if (flags.lang !== undefined) {
537
599
  const v = String(flags.lang).trim();
538
600
  if (v) {
@@ -650,6 +712,13 @@ export default class Install extends Command {
650
712
  'rootNickname',
651
713
  ]);
652
714
  }
715
+ static buildEnvAddPresetValuesFromFlags(flags) {
716
+ return pickPresetKeys(Install.buildPresetValuesFromFlags(flags), [
717
+ 'apiBaseUrl',
718
+ 'authType',
719
+ 'accessToken',
720
+ ]);
721
+ }
653
722
  static toOptionalPromptString(value) {
654
723
  if (value === undefined || value === null) {
655
724
  return undefined;
@@ -657,6 +726,131 @@ export default class Install extends Command {
657
726
  const text = String(value).trim();
658
727
  return text || undefined;
659
728
  }
729
+ static async validateAppPort(value, values) {
730
+ const formatError = validateTcpPort(value);
731
+ if (formatError) {
732
+ return formatError;
733
+ }
734
+ return await Install.validateResumeAwareTcpPort(value, values, 'app');
735
+ }
736
+ static async validateDbPort(value, values) {
737
+ const formatError = validateTcpPort(value);
738
+ if (formatError) {
739
+ return formatError;
740
+ }
741
+ const builtinDb = values.builtinDb === undefined ? true : Boolean(values.builtinDb);
742
+ const source = String(values.source ?? '').trim();
743
+ if (!builtinDb || source === 'docker') {
744
+ return undefined;
745
+ }
746
+ return await Install.validateResumeAwareTcpPort(value, values, 'db');
747
+ }
748
+ static async validateResumeAwareTcpPort(value, values, target) {
749
+ const portError = await validateAvailableTcpPort(value);
750
+ if (!portError) {
751
+ return undefined;
752
+ }
753
+ const context = await Install.readResumePortValidationContext(values);
754
+ if (!context) {
755
+ return portError;
756
+ }
757
+ const port = Install.toOptionalPromptString(value);
758
+ if (!port) {
759
+ return portError;
760
+ }
761
+ const reusesManagedPort = await Install.isResumeManagedPortReuse({
762
+ target,
763
+ port,
764
+ context,
765
+ });
766
+ return reusesManagedPort ? undefined : portError;
767
+ }
768
+ static async readResumePortValidationContext(values) {
769
+ if (!Boolean(values.resume)) {
770
+ return undefined;
771
+ }
772
+ const envName = Install.toOptionalPromptString(values.env);
773
+ if (!envName) {
774
+ return undefined;
775
+ }
776
+ const source = Install.toOptionalPromptString(values.source);
777
+ const builtinDb = values.builtinDb === undefined ? undefined : Boolean(values.builtinDb);
778
+ const dbDialect = Install.toOptionalPromptString(values.dbDialect);
779
+ const appRootPath = Install.toOptionalPromptString(values.appRootPath);
780
+ const workspaceName = Install.toOptionalPromptString(values.workspaceName)
781
+ ?? await Install.resolveResumeWorkspaceName(envName);
782
+ return {
783
+ envName,
784
+ ...(workspaceName ? { workspaceName } : {}),
785
+ ...(source ? { source } : {}),
786
+ ...(builtinDb !== undefined ? { builtinDb } : {}),
787
+ ...(dbDialect ? { dbDialect } : {}),
788
+ ...(appRootPath ? { appRootPath } : {}),
789
+ };
790
+ }
791
+ static async resolveResumeWorkspaceName(envName) {
792
+ if (!envName) {
793
+ return undefined;
794
+ }
795
+ const config = await loadAuthConfig({ scope: resolveDefaultConfigScope() });
796
+ const stored = String(config.name ?? '').trim();
797
+ return stored || Install.defaultWorkspaceName();
798
+ }
799
+ static async isResumeManagedPortReuse(params) {
800
+ if (params.target === 'app') {
801
+ if ((params.context.source === 'npm' || params.context.source === 'git')
802
+ && params.context.appRootPath) {
803
+ return await Install.isLocalPm2ProcessUsingPort(params.context.appRootPath, params.port);
804
+ }
805
+ const containerName = Install.buildDockerAppContainerName(params.context.envName, params.context.workspaceName);
806
+ return await Install.isDockerContainerPublishingPort(containerName, params.port);
807
+ }
808
+ if (!params.context.builtinDb || params.context.source === 'docker') {
809
+ return false;
810
+ }
811
+ const containerName = Install.buildBuiltinDbContainerName(params.context.envName, params.context.dbDialect ?? 'postgres', params.context.workspaceName);
812
+ return await Install.isDockerContainerPublishingPort(containerName, params.port);
813
+ }
814
+ static async isDockerContainerPublishingPort(containerName, port) {
815
+ if (!containerName || !port) {
816
+ return false;
817
+ }
818
+ const exists = await commandSucceeds('docker', [
819
+ 'container',
820
+ 'inspect',
821
+ containerName,
822
+ ]);
823
+ if (!exists) {
824
+ return false;
825
+ }
826
+ try {
827
+ const output = await commandOutput('docker', ['port', containerName]);
828
+ return output
829
+ .split(/\r?\n/)
830
+ .some((line) => line.includes(`:${port}`));
831
+ }
832
+ catch {
833
+ return false;
834
+ }
835
+ }
836
+ static async isLocalPm2ProcessUsingPort(appRootPath, port) {
837
+ const cwd = resolveConfiguredEnvPath(appRootPath);
838
+ if (!cwd) {
839
+ return false;
840
+ }
841
+ try {
842
+ const output = await commandOutput('pm2', ['jlist'], { cwd });
843
+ const rows = JSON.parse(output);
844
+ return rows.some((row) => {
845
+ const pmCwd = Install.toOptionalPromptString(row.pm2_env?.pm_cwd);
846
+ const appPort = Install.toOptionalPromptString(row.pm2_env?.env?.APP_PORT);
847
+ return Boolean(pmCwd && appPort && pmCwd === cwd && appPort === port);
848
+ });
849
+ }
850
+ catch {
851
+ return false;
852
+ }
853
+ }
660
854
  static buildResumePresetValues(env) {
661
855
  const envName = String(env.name ?? '').trim();
662
856
  const config = env.config ?? {};
@@ -676,6 +870,10 @@ export default class Install extends Command {
676
870
  const dbUser = Install.toOptionalPromptString(config.dbUser);
677
871
  const dbPassword = Install.toOptionalPromptString(config.dbPassword);
678
872
  const builtinDbImage = Install.toOptionalPromptString(config.builtinDbImage);
873
+ const rootUsername = Install.toOptionalPromptString(config.rootUsername);
874
+ const rootEmail = Install.toOptionalPromptString(config.rootEmail);
875
+ const rootPassword = Install.toOptionalPromptString(config.rootPassword);
876
+ const rootNickname = Install.toOptionalPromptString(config.rootNickname);
679
877
  const auth = config.auth;
680
878
  const appPreset = {
681
879
  ...(appRootPath ? { appRootPath } : {}),
@@ -717,6 +915,12 @@ export default class Install extends Command {
717
915
  ...(dbUser ? { dbUser } : {}),
718
916
  ...(dbPassword ? { dbPassword } : {}),
719
917
  };
918
+ const rootPreset = {
919
+ ...(rootUsername ? { rootUsername } : {}),
920
+ ...(rootEmail ? { rootEmail } : {}),
921
+ ...(rootPassword ? { rootPassword } : {}),
922
+ ...(rootNickname ? { rootNickname } : {}),
923
+ };
720
924
  const envAddPreset = {};
721
925
  if (auth?.type === 'token') {
722
926
  envAddPreset.authType = 'token';
@@ -734,6 +938,7 @@ export default class Install extends Command {
734
938
  appPreset,
735
939
  downloadPreset,
736
940
  dbPreset,
941
+ rootPreset,
737
942
  envAddPreset,
738
943
  };
739
944
  }
@@ -770,7 +975,7 @@ export default class Install extends Command {
770
975
  throw new Error([
771
976
  `Cannot continue setup for "${env.name}" in non-interactive resume mode yet.`,
772
977
  `These setup-only flags are not saved in the env config: ${missingFlags.join(', ')}`,
773
- `Run \`nb install --env ${env.name} --resume\` without \`--yes\`, or pass those flags again.`,
978
+ `Run \`nb init --env ${env.name} --resume\` without \`--yes\`, or pass those flags again.`,
774
979
  ].join('\n'));
775
980
  }
776
981
  }
@@ -944,7 +1149,7 @@ export default class Install extends Command {
944
1149
  return normalized || 'nocobase';
945
1150
  }
946
1151
  static defaultWorkspaceName() {
947
- return Install.sanitizeDockerResourceName(`nb-${path.basename(process.cwd())}`);
1152
+ return Install.sanitizeDockerResourceName(`nb-${path.basename(resolveEnvRoot(resolveDefaultConfigScope()))}`);
948
1153
  }
949
1154
  static buildBuiltinDbResourcePrefix(envName, workspaceName) {
950
1155
  void envName;
@@ -1468,7 +1673,7 @@ export default class Install extends Command {
1468
1673
  p.log.step(source === 'docker'
1469
1674
  ? 'Downloading Docker image'
1470
1675
  : 'Downloading local NocoBase app files');
1471
- return await this.config.runCommand('download', argv);
1676
+ return await this.config.runCommand('source:download', argv);
1472
1677
  }
1473
1678
  async downloadLocalApp(params) {
1474
1679
  const result = await this.downloadManagedSource({
@@ -1657,21 +1862,20 @@ export default class Install extends Command {
1657
1862
  throw new Error(`The application did not become ready in time. Expected \`${healthCheckUrl}\` to respond with \`ok\`, but the last status was: ${Install.formatHealthCheckMessage(lastMessage)}.${logHint}`);
1658
1863
  }
1659
1864
  async saveInstalledEnv(params) {
1660
- await this.config.runCommand('env:add', Install.buildEnvAddArgv(params));
1865
+ await upsertEnv(params.envName, Install.buildSavedEnvConfig(params), { scope: resolveDefaultConfigScope() });
1661
1866
  }
1662
- static pushArgIfValue(argv, flag, value) {
1663
- const text = String(value ?? '').trim();
1664
- if (text) {
1665
- argv.push(flag, text);
1666
- }
1667
- }
1668
- static pushBooleanArgIfSet(argv, flag, value) {
1669
- if (value === undefined) {
1867
+ async syncInstalledEnvConnection(params) {
1868
+ if (!params.appReady) {
1670
1869
  return;
1671
1870
  }
1672
- argv.push(Boolean(value) ? flag : `--no-${flag.replace(/^--/, '')}`);
1871
+ const authType = String(params.envAddResults.authType ?? 'oauth').trim()
1872
+ || 'oauth';
1873
+ if (authType === 'oauth') {
1874
+ await this.config.runCommand('env:auth', [params.envName]);
1875
+ }
1876
+ await this.config.runCommand('env:update', [params.envName]);
1673
1877
  }
1674
- static buildEnvAddArgv(params) {
1878
+ static buildSavedEnvConfig(params) {
1675
1879
  const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim()
1676
1880
  || DEFAULT_INSTALL_APP_PORT;
1677
1881
  const storagePath = String(params.appResults.storagePath ?? '').trim()
@@ -1682,42 +1886,37 @@ export default class Install extends Command {
1682
1886
  });
1683
1887
  const authType = String(params.envAddResults.authType ?? 'oauth').trim()
1684
1888
  || 'oauth';
1685
- const argv = [
1686
- params.envName,
1687
- '--no-intro',
1688
- '--api-base-url',
1889
+ return buildStoredEnvConfig({
1689
1890
  apiBaseUrl,
1690
- '--auth-type',
1691
1891
  authType,
1692
- '--app-port',
1892
+ accessToken: params.envAddResults.accessToken,
1893
+ source: downloadResultsValue(params.downloadResults, 'source'),
1894
+ downloadVersion: downloadResultsValue(params.downloadResults, 'version'),
1895
+ dockerRegistry: downloadResultsValue(params.downloadResults, 'dockerRegistry'),
1896
+ dockerPlatform: downloadResultsValue(params.downloadResults, 'dockerPlatform'),
1897
+ gitUrl: downloadResultsValue(params.downloadResults, 'gitUrl'),
1898
+ npmRegistry: downloadResultsValue(params.downloadResults, 'npmRegistry'),
1899
+ devDependencies: downloadResultsValue(params.downloadResults, 'devDependencies'),
1900
+ build: downloadResultsValue(params.downloadResults, 'build'),
1901
+ buildDts: downloadResultsValue(params.downloadResults, 'buildDts'),
1902
+ appRootPath: params.appResults.appRootPath,
1693
1903
  appPort,
1694
- '--storage-path',
1695
1904
  storagePath,
1696
- ];
1697
- Install.pushArgIfValue(argv, '--source', downloadResultsValue(params.downloadResults, 'source'));
1698
- Install.pushArgIfValue(argv, '--download-version', downloadResultsValue(params.downloadResults, 'version'));
1699
- Install.pushArgIfValue(argv, '--docker-registry', downloadResultsValue(params.downloadResults, 'dockerRegistry'));
1700
- Install.pushArgIfValue(argv, '--docker-platform', downloadResultsValue(params.downloadResults, 'dockerPlatform'));
1701
- Install.pushArgIfValue(argv, '--git-url', downloadResultsValue(params.downloadResults, 'gitUrl'));
1702
- Install.pushArgIfValue(argv, '--npm-registry', downloadResultsValue(params.downloadResults, 'npmRegistry'));
1703
- Install.pushBooleanArgIfSet(argv, '--dev-dependencies', downloadResultsValue(params.downloadResults, 'devDependencies'));
1704
- Install.pushBooleanArgIfSet(argv, '--build', downloadResultsValue(params.downloadResults, 'build'));
1705
- Install.pushBooleanArgIfSet(argv, '--build-dts', downloadResultsValue(params.downloadResults, 'buildDts'));
1706
- Install.pushArgIfValue(argv, '--app-root-path', params.appResults.appRootPath);
1707
- Install.pushArgIfValue(argv, '--app-key', params.appResults.appKey);
1708
- Install.pushArgIfValue(argv, '--timezone', params.appResults.timeZone);
1709
- Install.pushBooleanArgIfSet(argv, '--builtin-db', params.dbResults.builtinDb);
1710
- Install.pushArgIfValue(argv, '--db-dialect', params.dbResults.dbDialect);
1711
- Install.pushArgIfValue(argv, '--builtin-db-image', params.dbResults.builtinDbImage);
1712
- Install.pushArgIfValue(argv, '--db-host', params.dbResults.dbHost);
1713
- Install.pushArgIfValue(argv, '--db-port', params.dbResults.dbPort);
1714
- Install.pushArgIfValue(argv, '--db-database', params.dbResults.dbDatabase);
1715
- Install.pushArgIfValue(argv, '--db-user', params.dbResults.dbUser);
1716
- Install.pushArgIfValue(argv, '--db-password', params.dbResults.dbPassword);
1717
- if (authType === 'token') {
1718
- argv.push('--access-token', String(params.envAddResults.accessToken ?? ''));
1719
- }
1720
- return argv;
1905
+ appKey: params.appResults.appKey,
1906
+ timezone: params.appResults.timeZone,
1907
+ builtinDb: params.dbResults.builtinDb,
1908
+ dbDialect: params.dbResults.dbDialect,
1909
+ builtinDbImage: params.dbResults.builtinDbImage,
1910
+ dbHost: params.dbResults.dbHost,
1911
+ dbPort: params.dbResults.dbPort,
1912
+ dbDatabase: params.dbResults.dbDatabase,
1913
+ dbUser: params.dbResults.dbUser,
1914
+ dbPassword: params.dbResults.dbPassword,
1915
+ rootUsername: params.rootResults.rootUsername,
1916
+ rootEmail: params.rootResults.rootEmail,
1917
+ rootPassword: params.rootResults.rootPassword,
1918
+ rootNickname: params.rootResults.rootNickname,
1919
+ });
1721
1920
  }
1722
1921
  async collectPromptResults(parsed, yes) {
1723
1922
  const resumePreset = await this.resolveResumePresetValues(parsed, yes);
@@ -1737,7 +1936,9 @@ export default class Install extends Command {
1737
1936
  ...(resumePreset?.appPreset ?? {}),
1738
1937
  ...Install.buildAppPresetValuesFromFlags(parsed),
1739
1938
  };
1740
- const appCatalog = Install.buildAppPromptsCatalog(envName);
1939
+ const appCatalog = Install.buildAppPromptsCatalog(envName, {
1940
+ resume: parsed.resume,
1941
+ });
1741
1942
  const appResults = await runPromptCatalog(appCatalog, {
1742
1943
  initialValues: await Install.buildAppPromptInitialValues({
1743
1944
  envName,
@@ -1752,6 +1953,7 @@ export default class Install extends Command {
1752
1953
  },
1753
1954
  }),
1754
1955
  values: appPreset,
1956
+ yesInitialValues: { resume: parsed.resume },
1755
1957
  yes,
1756
1958
  });
1757
1959
  let downloadResults = {};
@@ -1769,7 +1971,9 @@ export default class Install extends Command {
1769
1971
  ...(resumePreset?.dbPreset ?? {}),
1770
1972
  ...Install.buildDbPresetValuesFromFlags(parsed),
1771
1973
  };
1772
- const dbResults = await runPromptCatalog(Install.buildDbPromptsCatalog(downloadResults), {
1974
+ const dbResults = await runPromptCatalog(Install.buildDbPromptsCatalog(envName, downloadResults, {
1975
+ resume: parsed.resume,
1976
+ }), {
1773
1977
  initialValues: {
1774
1978
  ...downloadResults,
1775
1979
  ...await Install.buildDbPromptInitialValues({
@@ -1788,7 +1992,10 @@ export default class Install extends Command {
1788
1992
  const rootPreset = Install.buildRootPresetValuesFromFlags(parsed);
1789
1993
  const rootResults = await runPromptCatalog(Install.rootUserPrompts, {
1790
1994
  initialValues: {},
1791
- values: rootPreset,
1995
+ values: {
1996
+ ...(resumePreset?.rootPreset ?? {}),
1997
+ ...rootPreset,
1998
+ },
1792
1999
  yes,
1793
2000
  });
1794
2001
  const envAddResults = await runPromptCatalog(EnvAdd.prompts, {
@@ -1798,6 +2005,7 @@ export default class Install extends Command {
1798
2005
  values: {
1799
2006
  name: envName,
1800
2007
  ...(resumePreset?.envAddPreset ?? {}),
2008
+ ...Install.buildEnvAddPresetValuesFromFlags(parsed),
1801
2009
  },
1802
2010
  yes,
1803
2011
  });
@@ -1836,6 +2044,16 @@ export default class Install extends Command {
1836
2044
  const workspaceName = usesDockerResources
1837
2045
  ? await Install.ensureWorkspaceName()
1838
2046
  : undefined;
2047
+ if (!parsed.resume) {
2048
+ await this.saveInstalledEnv({
2049
+ envName,
2050
+ appResults,
2051
+ downloadResults,
2052
+ dbResults,
2053
+ rootResults,
2054
+ envAddResults,
2055
+ });
2056
+ }
1839
2057
  let builtinDbPlan;
1840
2058
  if (Boolean(dbResults.builtinDb)) {
1841
2059
  builtinDbPlan = await this.startBuiltinDb({
@@ -1877,6 +2095,7 @@ export default class Install extends Command {
1877
2095
  appResults.timeZone = dockerAppPlan.timeZone;
1878
2096
  }
1879
2097
  else if (source === 'npm' || source === 'git') {
2098
+ const localSource = source === 'npm' ? 'npm' : 'git';
1880
2099
  const projectRoot = await this.downloadLocalApp({
1881
2100
  envName,
1882
2101
  appResults,
@@ -1885,7 +2104,7 @@ export default class Install extends Command {
1885
2104
  });
1886
2105
  localAppPlan = await this.startLocalApp({
1887
2106
  envName,
1888
- source,
2107
+ source: localSource,
1889
2108
  projectRoot,
1890
2109
  appResults,
1891
2110
  dbResults,
@@ -1907,12 +2126,20 @@ export default class Install extends Command {
1907
2126
  containerName: dockerAppPlan?.containerName,
1908
2127
  });
1909
2128
  }
1910
- await this.saveInstalledEnv({
2129
+ if (dockerAppPlan || localAppPlan || builtinDbPlan) {
2130
+ await this.saveInstalledEnv({
2131
+ envName,
2132
+ appResults,
2133
+ downloadResults,
2134
+ dbResults,
2135
+ rootResults,
2136
+ envAddResults,
2137
+ });
2138
+ }
2139
+ await this.syncInstalledEnvConnection({
1911
2140
  envName,
1912
- appResults,
1913
- downloadResults,
1914
- dbResults,
1915
2141
  envAddResults,
2142
+ appReady: Boolean(dockerAppPlan || localAppPlan),
1916
2143
  });
1917
2144
  p.outro(dockerAppPlan || localAppPlan
1918
2145
  ? `NocoBase is ready at http://127.0.0.1:${dockerAppPlan?.appPort ?? localAppPlan?.appPort}`
@@ -6,92 +6,7 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { Command, Flags } from '@oclif/core';
10
- import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../lib/app-runtime.js';
11
- import { run } from '../lib/run-npm.js';
12
- import { printInfo } from '../lib/ui.js';
13
- function formatLogsFailure(envName, message) {
14
- return [
15
- `Couldn't show logs for "${envName}".`,
16
- 'Check that the saved local app or Docker container still exists on this machine, then try again.',
17
- `Details: ${message}`,
18
- ].join('\n');
19
- }
20
- export default class Logs extends Command {
21
- static description = 'Show NocoBase logs for the selected env. Local npm/git installs use pm2 logs, and Docker installs use docker logs.';
22
- static examples = [
23
- '<%= config.bin %> <%= command.id %>',
24
- '<%= config.bin %> <%= command.id %> --env app1',
25
- '<%= config.bin %> <%= command.id %> --env app1 --tail 200',
26
- '<%= config.bin %> <%= command.id %> --env app1 --no-follow',
27
- ];
28
- static flags = {
29
- env: Flags.string({
30
- char: 'e',
31
- description: 'CLI env name to inspect logs for. Defaults to the current env when omitted',
32
- }),
33
- tail: Flags.integer({
34
- description: 'Number of recent log lines to show before following',
35
- default: 100,
36
- min: 0,
37
- }),
38
- follow: Flags.boolean({
39
- char: 'f',
40
- description: 'Keep streaming new log lines',
41
- default: true,
42
- allowNo: true,
43
- }),
44
- };
45
- async run() {
46
- const { flags } = await this.parse(Logs);
47
- const requestedEnv = flags.env?.trim() || undefined;
48
- const runtime = await resolveManagedAppRuntime(requestedEnv);
49
- if (!runtime) {
50
- this.error(formatMissingManagedAppEnvMessage(requestedEnv));
51
- }
52
- if (runtime.kind === 'http') {
53
- this.error([
54
- `Can't show runtime logs for "${runtime.envName}" from this machine.`,
55
- 'This env only has an API connection, so there is no saved local app or Docker container to read logs from.',
56
- 'Connect it to a local checkout or reinstall it with npm, git, or Docker if you want CLI-managed logs.',
57
- ].join('\n'));
58
- }
59
- if (runtime.kind === 'ssh') {
60
- this.error([
61
- `Can't show runtime logs for "${runtime.envName}" yet.`,
62
- 'SSH env support is reserved but not implemented yet.',
63
- 'Use a local or Docker env for CLI-managed logs for now.',
64
- ].join('\n'));
65
- }
66
- const tail = String(flags.tail ?? 100);
67
- const follow = flags.follow !== false;
68
- printInfo(follow
69
- ? `Showing logs for "${runtime.envName}" (press Ctrl+C to stop).`
70
- : `Showing recent logs for "${runtime.envName}".`);
71
- try {
72
- if (runtime.kind === 'docker') {
73
- const dockerArgs = ['logs', '--tail', tail];
74
- if (follow) {
75
- dockerArgs.push('--follow');
76
- }
77
- dockerArgs.push(runtime.containerName);
78
- await run('docker', dockerArgs, {
79
- errorName: 'docker logs',
80
- stdio: 'inherit',
81
- });
82
- return;
83
- }
84
- const localArgs = ['pm2', 'logs', '--lines', tail];
85
- if (!follow) {
86
- localArgs.push('--nostream');
87
- }
88
- await runLocalNocoBaseCommand(runtime, localArgs, {
89
- stdio: 'inherit',
90
- });
91
- }
92
- catch (error) {
93
- const message = error instanceof Error ? error.message : String(error);
94
- this.error(formatLogsFailure(runtime.envName, message));
95
- }
96
- }
9
+ import AppLogs from './app/logs.js';
10
+ export default class Logs extends AppLogs {
11
+ static hidden = true;
97
12
  }