@nocobase/cli 2.1.0-alpha.35 → 2.1.0-alpha.36

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/dist/commands/app/down.js +10 -13
  2. package/dist/commands/app/logs.js +0 -1
  3. package/dist/commands/app/restart.js +0 -1
  4. package/dist/commands/app/start.js +0 -1
  5. package/dist/commands/app/stop.js +0 -1
  6. package/dist/commands/app/upgrade.js +0 -1
  7. package/dist/commands/env/add.js +3 -4
  8. package/dist/commands/env/auth.js +3 -2
  9. package/dist/commands/env/remove.js +38 -13
  10. package/dist/commands/env/update.js +9 -2
  11. package/dist/commands/examples/prompts-stages.js +4 -4
  12. package/dist/commands/examples/prompts-test.js +4 -4
  13. package/dist/commands/init.js +38 -31
  14. package/dist/commands/install.js +89 -61
  15. package/dist/commands/license/activate.js +66 -64
  16. package/dist/commands/license/id.js +0 -1
  17. package/dist/commands/license/plugins/clean.js +0 -1
  18. package/dist/commands/license/plugins/list.js +0 -1
  19. package/dist/commands/license/plugins/sync.js +0 -1
  20. package/dist/commands/license/shared.js +3 -3
  21. package/dist/commands/license/status.js +0 -1
  22. package/dist/commands/plugin/disable.js +0 -1
  23. package/dist/commands/plugin/enable.js +0 -1
  24. package/dist/commands/plugin/list.js +0 -1
  25. package/dist/commands/self/update.js +12 -3
  26. package/dist/commands/skills/install.js +12 -3
  27. package/dist/commands/skills/remove.js +12 -3
  28. package/dist/commands/skills/update.js +12 -3
  29. package/dist/commands/source/dev.js +0 -1
  30. package/dist/commands/source/download.js +29 -17
  31. package/dist/lib/bootstrap.js +12 -3
  32. package/dist/lib/db-connection-check.js +3 -23
  33. package/dist/lib/env-auth.js +4 -3
  34. package/dist/lib/env-guard.js +8 -7
  35. package/dist/lib/generated-command.js +0 -1
  36. package/dist/lib/inquirer-theme.js +17 -0
  37. package/dist/lib/inquirer.js +244 -0
  38. package/dist/lib/object-utils.js +76 -0
  39. package/dist/lib/prompt-catalog-core.js +185 -0
  40. package/dist/lib/prompt-catalog-terminal.js +375 -0
  41. package/dist/lib/prompt-catalog.js +2 -573
  42. package/dist/lib/prompt-validators.js +56 -1
  43. package/dist/lib/resource-command.js +0 -1
  44. package/dist/lib/skills-manager.js +75 -11
  45. package/dist/lib/startup-update.js +12 -8
  46. package/dist/lib/ui.js +28 -51
  47. package/dist/locale/en-US.json +8 -3
  48. package/dist/locale/zh-CN.json +8 -3
  49. package/dist/post-processors/data-modeling.js +25 -7
  50. package/dist/post-processors/data-source-manager.js +24 -0
  51. package/nocobase-ctl.config.json +20 -1
  52. package/package.json +7 -5
@@ -7,8 +7,6 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Command, Flags } from '@oclif/core';
10
- import * as p from '@clack/prompts';
11
- import _ from 'lodash';
12
10
  import { spawn } from 'node:child_process';
13
11
  import crypto from 'node:crypto';
14
12
  import { mkdir } from 'node:fs/promises';
@@ -24,7 +22,8 @@ import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, valida
24
22
  import { validateExternalDbConfig } from "../lib/db-connection-check.js";
25
23
  import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
26
24
  import { run, runNocoBaseCommand } from '../lib/run-npm.js';
27
- import { startTask, stopTask, updateTask } from '../lib/ui.js';
25
+ import { printInfo, printStage, printVerbose, printWarning, setVerboseMode, } from '../lib/ui.js';
26
+ import { omitKeys, upperFirst } from "../lib/object-utils.js";
28
27
  import { getEnv, setCurrentEnv, upsertEnv } from '../lib/auth-store.js';
29
28
  import { buildStoredEnvConfig } from '../lib/env-config.js';
30
29
  import Download, { defaultDockerRegistryForLang, } from './download.js';
@@ -260,6 +259,13 @@ async function commandOutput(command, args, options) {
260
259
  });
261
260
  }
262
261
  export default class Install extends Command {
262
+ ensuredDockerNetworks = new Set();
263
+ logStage(title) {
264
+ printStage(title);
265
+ }
266
+ logDetail(message) {
267
+ printVerbose(message);
268
+ }
263
269
  static hidden = true;
264
270
  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.';
265
271
  static examples = [
@@ -288,6 +294,10 @@ export default class Install extends Command {
288
294
  description: 'Show detailed command output',
289
295
  default: false,
290
296
  }),
297
+ 'skip-save-env-log': Flags.boolean({
298
+ hidden: true,
299
+ default: false,
300
+ }),
291
301
  env: Flags.string({
292
302
  char: 'e',
293
303
  description: 'App/env name to create or update. Defaults app paths to ./<envName>/source/ and ./<envName>/storage/.',
@@ -373,7 +383,7 @@ export default class Install extends Command {
373
383
  description: 'Download NocoBase app files or pull a Docker image before installing',
374
384
  default: false,
375
385
  }),
376
- ..._.omit(Download.flags, ['yes']),
386
+ ...omitKeys(Download.flags, ['yes']),
377
387
  };
378
388
  /** Environment name only: run before {@link Install.prompts} (see `run`). */
379
389
  static envPrompts = {
@@ -797,7 +807,7 @@ export default class Install extends Command {
797
807
  const database = String(dbResults.dbDatabase ?? '').trim();
798
808
  const address = host && port ? `${host}:${port}` : host || port || '(unknown address)';
799
809
  const target = database ? `${address}/${database}` : address;
800
- p.log.step(`Checking external ${dialect} database: ${target}`);
810
+ printVerbose(`Checking external ${dialect} database: ${target}`);
801
811
  const validationError = await validateExternalDbConfig(dbResults);
802
812
  if (validationError) {
803
813
  throw new Error(validationError);
@@ -1026,7 +1036,7 @@ export default class Install extends Command {
1026
1036
  }
1027
1037
  const nextPort = await findAvailableTcpPort();
1028
1038
  if (options?.warn) {
1029
- p.log.warn(`${options.label ?? 'Default port'} ${normalized} is already in use. Using available port ${nextPort} for this setup.`);
1039
+ printWarning(`${options.label ?? 'Default port'} ${normalized} is already in use. Using available port ${nextPort} for this setup.`);
1030
1040
  }
1031
1041
  return nextPort;
1032
1042
  }
@@ -1093,7 +1103,6 @@ export default class Install extends Command {
1093
1103
  yes: false,
1094
1104
  hooks: {
1095
1105
  onCancel: () => {
1096
- p.cancel('Download cancelled.');
1097
1106
  exit(0);
1098
1107
  },
1099
1108
  onMissingNonInteractive: (message) => {
@@ -1443,18 +1452,23 @@ export default class Install extends Command {
1443
1452
  throw new Error(`Built-in database does not support "${dbDialect}" yet. Please choose PostgreSQL, MySQL, MariaDB, or KingbaseES.`);
1444
1453
  }
1445
1454
  async ensureDockerNetwork(name) {
1446
- p.log.step(`Checking Docker network: ${name}`);
1455
+ if (this.ensuredDockerNetworks.has(name)) {
1456
+ return;
1457
+ }
1458
+ printVerbose(`Checking Docker network: ${name}`);
1447
1459
  const exists = await commandSucceeds('docker', ['network', 'inspect', name]);
1448
1460
  if (exists) {
1449
- p.log.info(`Docker network already exists: ${name}`);
1461
+ printVerbose(`Docker network already exists: ${name}`);
1462
+ this.ensuredDockerNetworks.add(name);
1450
1463
  return;
1451
1464
  }
1452
- p.log.step(`Creating Docker network: ${name}`);
1465
+ printVerbose(`Creating Docker network: ${name}`);
1453
1466
  try {
1454
1467
  await run('docker', ['network', 'create', name], {
1455
1468
  errorName: 'docker network create',
1456
1469
  });
1457
- p.log.info(`Docker network is ready: ${name}`);
1470
+ printVerbose(`Docker network is ready: ${name}`);
1471
+ this.ensuredDockerNetworks.add(name);
1458
1472
  }
1459
1473
  catch (error) {
1460
1474
  const message = error instanceof Error ? error.message : String(error);
@@ -1489,7 +1503,7 @@ export default class Install extends Command {
1489
1503
  if (!params.force) {
1490
1504
  return true;
1491
1505
  }
1492
- p.log.info(`Removing existing ${params.displayName}: ${params.containerName}`);
1506
+ printVerbose(`Removing existing ${params.displayName}: ${params.containerName}`);
1493
1507
  await this.removeDockerContainer(params.containerName);
1494
1508
  return false;
1495
1509
  }
@@ -1513,7 +1527,7 @@ export default class Install extends Command {
1513
1527
  async ensureBuiltinDbContainer(plan, options) {
1514
1528
  const exists = await this.dockerContainerExists(plan.containerName);
1515
1529
  if (exists) {
1516
- p.log.info(`Built-in ${plan.dbDialect} container already exists: ${plan.containerName}`);
1530
+ printVerbose(`Built-in ${plan.dbDialect} container already exists: ${plan.containerName}`);
1517
1531
  return;
1518
1532
  }
1519
1533
  await mkdir(plan.dataDir, { recursive: true });
@@ -1540,7 +1554,8 @@ export default class Install extends Command {
1540
1554
  dbPassword: params.dbResults.dbPassword,
1541
1555
  builtinDbImage: params.dbResults.builtinDbImage,
1542
1556
  });
1543
- p.log.step(`Preparing built-in ${plan.dbDialect} database`);
1557
+ this.logStage('Preparing database');
1558
+ printInfo(`Using built-in ${plan.dbDialect} database.`);
1544
1559
  await this.ensureDockerNetwork(plan.networkName);
1545
1560
  const existingContainerKept = await this.removeDockerContainerIfForced({
1546
1561
  containerName: plan.containerName,
@@ -1556,7 +1571,8 @@ export default class Install extends Command {
1556
1571
  await this.ensureBuiltinDbContainer(plan, {
1557
1572
  stdio: params.commandStdio ?? 'ignore',
1558
1573
  });
1559
- p.log.info(`Built-in ${plan.dbDialect} database is ready at ${plan.dbHost}:${plan.dbPort}`);
1574
+ printInfo(`${upperFirst(plan.dbDialect)} database ready.`);
1575
+ printVerbose(`Built-in ${plan.dbDialect} database ready at ${plan.dbHost}:${plan.dbPort}`);
1560
1576
  return plan;
1561
1577
  }
1562
1578
  static buildDockerAppPlan(params) {
@@ -1617,7 +1633,7 @@ export default class Install extends Command {
1617
1633
  async ensureDockerAppContainer(plan, options) {
1618
1634
  const exists = await this.dockerContainerExists(plan.containerName);
1619
1635
  if (exists) {
1620
- p.log.info(`App container already exists: ${plan.containerName}`);
1636
+ printVerbose(`App container already exists: ${plan.containerName}`);
1621
1637
  return 'existing';
1622
1638
  }
1623
1639
  await mkdir(plan.storagePath, { recursive: true });
@@ -1641,7 +1657,7 @@ export default class Install extends Command {
1641
1657
  rootResults: params.rootResults,
1642
1658
  networkName,
1643
1659
  });
1644
- p.log.step(`Starting Docker app ${plan.imageRef}`);
1660
+ printVerbose('Starting NocoBase app (Docker)');
1645
1661
  await this.removeDockerContainerIfForced({
1646
1662
  containerName: plan.containerName,
1647
1663
  displayName: 'app container',
@@ -1655,7 +1671,7 @@ export default class Install extends Command {
1655
1671
  plan.appKey = env.APP_KEY || plan.appKey;
1656
1672
  plan.timeZone = env.TZ || plan.timeZone;
1657
1673
  }
1658
- p.log.info(`App container is ready at http://127.0.0.1:${plan.appPort}`);
1674
+ printVerbose(`NocoBase app is starting at http://127.0.0.1:${plan.appPort}`);
1659
1675
  return plan;
1660
1676
  }
1661
1677
  static pushDownloadArgIfValue(argv, flag, value) {
@@ -1666,6 +1682,9 @@ export default class Install extends Command {
1666
1682
  }
1667
1683
  static buildDownloadArgvFromResults(results, options) {
1668
1684
  const argv = ['-y', '--no-intro'];
1685
+ if (options?.compactLog) {
1686
+ argv.push('--compact-log');
1687
+ }
1669
1688
  const source = String(results.source ?? '').trim();
1670
1689
  if (options?.verbose) {
1671
1690
  argv.push('--verbose');
@@ -1718,11 +1737,8 @@ export default class Install extends Command {
1718
1737
  async downloadManagedSource(params) {
1719
1738
  const argv = Install.buildDownloadArgvFromResults(params.downloadResults, {
1720
1739
  verbose: params.verbose,
1740
+ compactLog: true,
1721
1741
  });
1722
- const source = String(params.downloadResults.source ?? '').trim();
1723
- p.log.step(source === 'docker'
1724
- ? 'Downloading Docker image'
1725
- : 'Downloading local NocoBase app files');
1726
1742
  return await this.config.runCommand('source:download', argv);
1727
1743
  }
1728
1744
  async downloadLocalApp(params) {
@@ -1784,7 +1800,7 @@ export default class Install extends Command {
1784
1800
  rootResults: params.rootResults,
1785
1801
  });
1786
1802
  const args = ['start', '--quickstart', '--daemon'];
1787
- p.log.step(`Stopping any existing local NocoBase process in ${params.projectRoot}`);
1803
+ this.logDetail(`Stopping any existing local NocoBase process in ${params.projectRoot}`);
1788
1804
  try {
1789
1805
  await runNocoBaseCommand(['pm2', 'kill'], {
1790
1806
  cwd: params.projectRoot,
@@ -1794,15 +1810,15 @@ export default class Install extends Command {
1794
1810
  }
1795
1811
  catch (error) {
1796
1812
  const message = error instanceof Error ? error.message : String(error);
1797
- p.log.info(`Skipped local process cleanup before start: ${message}`);
1813
+ this.logDetail(`Skipped local process cleanup before start: ${message}`);
1798
1814
  }
1799
- p.log.step(`Starting local NocoBase app from ${params.projectRoot}`);
1815
+ this.logDetail(`Starting local NocoBase app from ${params.projectRoot}`);
1800
1816
  await runNocoBaseCommand(args, {
1801
1817
  cwd: params.projectRoot,
1802
1818
  env,
1803
1819
  stdio: params.commandStdio ?? 'ignore',
1804
1820
  });
1805
- p.log.info(`Local app is starting at http://127.0.0.1:${env.APP_PORT}`);
1821
+ this.logDetail(`Local app is starting at http://127.0.0.1:${env.APP_PORT}`);
1806
1822
  return {
1807
1823
  source: params.source,
1808
1824
  projectRoot: params.projectRoot,
@@ -1876,35 +1892,29 @@ export default class Install extends Command {
1876
1892
  const fetchImpl = options?.fetchImpl ?? fetch;
1877
1893
  const startedAt = Date.now();
1878
1894
  let lastMessage = 'No response yet';
1879
- let taskActive = true;
1880
- startTask(`Waiting for application health check: ${healthCheckUrl}. NocoBase has started and is still booting...`);
1881
- try {
1882
- while (Date.now() - startedAt < timeoutMs) {
1883
- const result = await Install.requestAppHealthCheck({
1884
- healthCheckUrl,
1885
- fetchImpl,
1886
- requestTimeoutMs,
1887
- });
1888
- if (result.ok) {
1889
- stopTask();
1890
- taskActive = false;
1891
- p.log.info(`Application health check passed: ${healthCheckUrl}`);
1892
- return;
1893
- }
1894
- lastMessage = result.message;
1895
- const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1000));
1896
- updateTask(`Waiting for application health check: ${healthCheckUrl}. Still starting... (${elapsedSeconds}s elapsed, last status: ${Install.formatHealthCheckMessage(lastMessage)})`);
1897
- const remainingMs = timeoutMs - (Date.now() - startedAt);
1898
- if (remainingMs <= 0) {
1899
- break;
1900
- }
1901
- await Install.sleep(Math.min(intervalMs, remainingMs));
1895
+ let lastLoggedStatus = '';
1896
+ printInfo('Waiting for NocoBase to become ready...');
1897
+ while (Date.now() - startedAt < timeoutMs) {
1898
+ const result = await Install.requestAppHealthCheck({
1899
+ healthCheckUrl,
1900
+ fetchImpl,
1901
+ requestTimeoutMs,
1902
+ });
1903
+ if (result.ok) {
1904
+ return;
1902
1905
  }
1903
- }
1904
- finally {
1905
- if (taskActive) {
1906
- stopTask();
1906
+ lastMessage = result.message;
1907
+ const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1000));
1908
+ const statusLine = `Waiting for NocoBase to become ready... (${elapsedSeconds}s elapsed, last status: ${Install.formatHealthCheckMessage(lastMessage)})`;
1909
+ if (statusLine !== lastLoggedStatus) {
1910
+ printInfo(statusLine);
1911
+ lastLoggedStatus = statusLine;
1912
+ }
1913
+ const remainingMs = timeoutMs - (Date.now() - startedAt);
1914
+ if (remainingMs <= 0) {
1915
+ break;
1907
1916
  }
1917
+ await Install.sleep(Math.min(intervalMs, remainingMs));
1908
1918
  }
1909
1919
  const logHint = options?.containerName
1910
1920
  ? ` You can inspect startup logs with: docker logs ${options.containerName}`
@@ -2049,7 +2059,14 @@ export default class Install extends Command {
2049
2059
  },
2050
2060
  yes,
2051
2061
  });
2052
- const envAddResults = await runPromptCatalog(EnvAdd.prompts, {
2062
+ const envAddPromptsForInstall = {
2063
+ ...EnvAdd.prompts,
2064
+ apiBaseUrl: {
2065
+ ...EnvAdd.prompts.apiBaseUrl,
2066
+ validate: undefined,
2067
+ },
2068
+ };
2069
+ const envAddResults = await runPromptCatalog(envAddPromptsForInstall, {
2053
2070
  initialValues: {
2054
2071
  apiBaseUrl: `http://127.0.0.1:${appResults.appPort ?? DEFAULT_INSTALL_APP_PORT}/api`,
2055
2072
  },
@@ -2077,13 +2094,14 @@ export default class Install extends Command {
2077
2094
  const parsed = {
2078
2095
  ...flags,
2079
2096
  };
2097
+ setVerboseMode(Boolean(parsed.verbose));
2080
2098
  const commandStdio = this.commandStdio(parsed.verbose);
2081
2099
  if (!parsed['no-intro']) {
2082
- p.intro('Set Up NocoBase');
2100
+ this.logStage('Set up NocoBase');
2083
2101
  }
2084
2102
  if (parsed.resume) {
2085
2103
  const envLabel = Install.toOptionalPromptString(parsed.env);
2086
- p.log.step(envLabel
2104
+ printInfo(envLabel
2087
2105
  ? `Resuming setup for env "${envLabel}" from the saved workspace config`
2088
2106
  : 'Resuming setup from the saved workspace config');
2089
2107
  }
@@ -2100,6 +2118,9 @@ export default class Install extends Command {
2100
2118
  : undefined;
2101
2119
  await Install.ensureExternalDbReadyForInstall(dbResults);
2102
2120
  if (!parsed.resume) {
2121
+ if (!parsed['skip-save-env-log']) {
2122
+ this.logStage('Saving env config');
2123
+ }
2103
2124
  await this.saveInstalledEnv({
2104
2125
  envName,
2105
2126
  appResults,
@@ -2108,7 +2129,9 @@ export default class Install extends Command {
2108
2129
  rootResults,
2109
2130
  envAddResults,
2110
2131
  });
2111
- p.log.info(`Saved install config for env "${envName}"`);
2132
+ if (!parsed['skip-save-env-log']) {
2133
+ printInfo(`Saved env config for "${envName}".`);
2134
+ }
2112
2135
  }
2113
2136
  let builtinDbPlan;
2114
2137
  if (Boolean(dbResults.builtinDb)) {
@@ -2132,11 +2155,13 @@ export default class Install extends Command {
2132
2155
  let dockerAppPlan;
2133
2156
  let localAppPlan;
2134
2157
  if (Boolean(appResults.fetchSource)) {
2158
+ this.logStage('Preparing application');
2135
2159
  if (source === 'docker') {
2136
2160
  await this.downloadManagedSource({
2137
2161
  downloadResults,
2138
2162
  verbose: parsed.verbose,
2139
2163
  });
2164
+ printInfo('Application image ready.');
2140
2165
  dockerAppPlan = await this.installDockerApp({
2141
2166
  envName,
2142
2167
  dockerNetworkName,
@@ -2160,6 +2185,7 @@ export default class Install extends Command {
2160
2185
  downloadResults,
2161
2186
  verbose: parsed.verbose,
2162
2187
  });
2188
+ printInfo('Application files ready.');
2163
2189
  localAppPlan = await this.startLocalApp({
2164
2190
  envName,
2165
2191
  source: localSource,
@@ -2174,15 +2200,17 @@ export default class Install extends Command {
2174
2200
  }
2175
2201
  }
2176
2202
  else {
2177
- p.log.info('Skipped app download and install.');
2203
+ this.logDetail('Skipped app download and install.');
2178
2204
  }
2179
2205
  if (dockerAppPlan || localAppPlan) {
2206
+ this.logStage('Starting NocoBase');
2180
2207
  await this.waitForAppHealthCheck(Install.resolveApiBaseUrl({
2181
2208
  appResults,
2182
2209
  envAddResults,
2183
2210
  }), {
2184
2211
  containerName: dockerAppPlan?.containerName,
2185
2212
  });
2213
+ printInfo(`NocoBase is ready at http://127.0.0.1:${dockerAppPlan?.appPort ?? localAppPlan?.appPort}`);
2186
2214
  }
2187
2215
  if (dockerAppPlan || localAppPlan || builtinDbPlan) {
2188
2216
  await this.saveInstalledEnv({
@@ -2199,9 +2227,9 @@ export default class Install extends Command {
2199
2227
  envAddResults,
2200
2228
  appReady: Boolean(dockerAppPlan || localAppPlan),
2201
2229
  });
2202
- p.outro(dockerAppPlan || localAppPlan
2203
- ? `NocoBase is ready at http://127.0.0.1:${dockerAppPlan?.appPort ?? localAppPlan?.appPort}`
2204
- : `Install config for "${envName}" has been saved.`);
2230
+ if (!dockerAppPlan && !localAppPlan) {
2231
+ printInfo(`Install config for "${envName}" has been saved.`);
2232
+ }
2205
2233
  }
2206
2234
  }
2207
2235
  function downloadResultsValue(downloadResults, key) {
@@ -6,10 +6,10 @@
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 * as p from '@clack/prompts';
10
9
  import { Command, Flags } from '@oclif/core';
11
10
  import { readFile } from 'node:fs/promises';
12
11
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
12
+ import { input, password as promptPassword, select } from "../../lib/inquirer.js";
13
13
  import { createLicenseEnvFlag, ensureInstanceId, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, redactLicenseKey, requireLicenseRuntime, resolveLicenseKeyFile, resolveLicenseServiceUrl, saveLicenseKey, sanitizeLicenseOutput, validateLicenseKey, } from './shared.js';
14
14
  import { announceTargetEnv, isInteractiveTerminal } from '../../lib/ui.js';
15
15
  import { appUrl } from '../env/shared.js';
@@ -17,102 +17,107 @@ function resolveOnlineInputValue(value) {
17
17
  return String(value ?? '').trim();
18
18
  }
19
19
  async function promptActivationMode() {
20
- const answer = await p.select({
21
- message: 'How do you want to activate the license?',
22
- options: [
23
- { value: 'key', label: 'Use an existing license key' },
24
- { value: 'online', label: 'Request and activate a license online' },
25
- { value: 'cancel', label: 'Cancel' },
26
- ],
27
- initialValue: 'key',
28
- });
29
- if (p.isCancel(answer)) {
30
- p.cancel('License activation cancelled.');
20
+ try {
21
+ return await select({
22
+ message: 'How do you want to activate the license?',
23
+ choices: [
24
+ { value: 'key', name: 'Use an existing license key' },
25
+ { value: 'online', name: 'Request and activate a license online' },
26
+ { value: 'cancel', name: 'Cancel' },
27
+ ],
28
+ default: 'key',
29
+ });
30
+ }
31
+ catch {
31
32
  return 'cancel';
32
33
  }
33
- return answer;
34
34
  }
35
35
  async function promptLicenseKeyInput() {
36
- const answer = await p.select({
37
- message: 'How do you want to provide the license key?',
38
- options: [
39
- { value: 'key', label: 'Paste the license key' },
40
- { value: 'file', label: 'Read the key from a file' },
41
- ],
42
- initialValue: 'key',
43
- });
44
- if (p.isCancel(answer)) {
45
- p.cancel('License activation cancelled.');
36
+ let answer;
37
+ try {
38
+ answer = await select({
39
+ message: 'How do you want to provide the license key?',
40
+ choices: [
41
+ { value: 'key', name: 'Paste the license key' },
42
+ { value: 'file', name: 'Read the key from a file' },
43
+ ],
44
+ default: 'key',
45
+ });
46
+ }
47
+ catch {
46
48
  return {};
47
49
  }
48
50
  if (answer === 'key') {
49
- const key = await p.text({
50
- message: 'License key',
51
- validate: (value) => String(value ?? '').trim() ? undefined : 'License key is required.',
52
- });
53
- if (p.isCancel(key)) {
54
- p.cancel('License activation cancelled.');
51
+ try {
52
+ const key = await input({
53
+ message: 'License key',
54
+ validate: (value) => String(value ?? '').trim() ? true : 'License key is required.',
55
+ });
56
+ return { key: String(key ?? '').trim() || undefined };
57
+ }
58
+ catch {
55
59
  return {};
56
60
  }
57
- return { key: String(key ?? '').trim() || undefined };
58
61
  }
59
- const keyFile = await p.text({
60
- message: 'Path to the license key file',
61
- validate: (value) => String(value ?? '').trim() ? undefined : 'License key file path is required.',
62
- });
63
- if (p.isCancel(keyFile)) {
64
- p.cancel('License activation cancelled.');
62
+ try {
63
+ const keyFile = await input({
64
+ message: 'Path to the license key file',
65
+ validate: (value) => String(value ?? '').trim() ? true : 'License key file path is required.',
66
+ });
67
+ return { keyFile: String(keyFile ?? '').trim() || undefined };
68
+ }
69
+ catch {
65
70
  return {};
66
71
  }
67
- return { keyFile: String(keyFile ?? '').trim() || undefined };
68
72
  }
69
73
  async function promptOnlineActivationInput(initial) {
70
74
  let account = String(initial.account ?? '').trim();
71
75
  if (!account) {
72
- const answer = await p.text({
73
- message: 'Service account',
74
- validate: (value) => String(value ?? '').trim() ? undefined : 'Service account is required.',
75
- });
76
- if (p.isCancel(answer)) {
77
- p.cancel('License activation cancelled.');
76
+ try {
77
+ const answer = await input({
78
+ message: 'Service account',
79
+ validate: (value) => String(value ?? '').trim() ? true : 'Service account is required.',
80
+ });
81
+ account = String(answer ?? '').trim();
82
+ }
83
+ catch {
78
84
  return;
79
85
  }
80
- account = String(answer ?? '').trim();
81
86
  }
82
87
  if (!account) {
83
- p.cancel('License activation cancelled.');
84
88
  return;
85
89
  }
86
90
  let password = String(initial.password ?? '').trim();
87
91
  if (!password) {
88
- const answer = await p.password({
89
- message: 'Service password',
90
- validate: (value) => String(value ?? '').trim() ? undefined : 'Service password is required.',
91
- });
92
- if (p.isCancel(answer)) {
93
- p.cancel('License activation cancelled.');
92
+ try {
93
+ const answer = await promptPassword({
94
+ message: 'Service password',
95
+ mask: '•',
96
+ validate: (value) => String(value ?? '').trim() ? true : 'Service password is required.',
97
+ });
98
+ password = String(answer ?? '').trim();
99
+ }
100
+ catch {
94
101
  return;
95
102
  }
96
- password = String(answer ?? '').trim();
97
103
  }
98
104
  if (!password) {
99
- p.cancel('License activation cancelled.');
100
105
  return;
101
106
  }
102
107
  let appName = String(initial.appName ?? '').trim();
103
108
  if (!appName) {
104
- const answer = await p.text({
105
- message: 'Application name',
106
- validate: (value) => String(value ?? '').trim() ? undefined : 'Application name is required.',
107
- });
108
- if (p.isCancel(answer)) {
109
- p.cancel('License activation cancelled.');
109
+ try {
110
+ const answer = await input({
111
+ message: 'Application name',
112
+ validate: (value) => String(value ?? '').trim() ? true : 'Application name is required.',
113
+ });
114
+ appName = String(answer ?? '').trim();
115
+ }
116
+ catch {
110
117
  return;
111
118
  }
112
- appName = String(answer ?? '').trim();
113
119
  }
114
120
  if (!appName) {
115
- p.cancel('License activation cancelled.');
116
121
  return;
117
122
  }
118
123
  return {
@@ -206,7 +211,6 @@ export default class LicenseActivate extends Command {
206
211
  yes: flags.yes,
207
212
  });
208
213
  if (!confirmed) {
209
- this.log('Canceled.');
210
214
  return;
211
215
  }
212
216
  }
@@ -223,7 +227,6 @@ export default class LicenseActivate extends Command {
223
227
  }
224
228
  const mode = await promptActivationMode();
225
229
  if (mode === 'cancel') {
226
- this.log('Cancelled license activation.');
227
230
  return;
228
231
  }
229
232
  if (mode === 'online') {
@@ -258,7 +261,6 @@ export default class LicenseActivate extends Command {
258
261
  }
259
262
  const prompted = await promptOnlineActivationInput(initialOnline);
260
263
  if (!prompted) {
261
- this.log('Cancelled license activation.');
262
264
  return;
263
265
  }
264
266
  onlineInput = prompted;
@@ -39,7 +39,6 @@ export default class LicenseId extends Command {
39
39
  yes: flags.yes,
40
40
  });
41
41
  if (!confirmed) {
42
- this.log('Canceled.');
43
42
  return;
44
43
  }
45
44
  }
@@ -56,7 +56,6 @@ export default class LicensePluginsClean extends Command {
56
56
  yes: flags.yes,
57
57
  });
58
58
  if (!confirmed) {
59
- this.log('Canceled.');
60
59
  return;
61
60
  }
62
61
  }
@@ -36,7 +36,6 @@ export default class LicensePluginsList extends Command {
36
36
  yes: flags.yes,
37
37
  });
38
38
  if (!confirmed) {
39
- this.log('Canceled.');
40
39
  return;
41
40
  }
42
41
  }
@@ -167,7 +167,6 @@ export default class LicensePluginsSync extends Command {
167
167
  yes: flags.yes,
168
168
  });
169
169
  if (!confirmed) {
170
- this.log('Canceled.');
171
170
  return;
172
171
  }
173
172
  }
@@ -10,12 +10,12 @@ import { Flags } from '@oclif/core';
10
10
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
11
11
  import path from 'node:path';
12
12
  import { getEnvAsync, getInstanceIdAsync, keyDecrypt } from '@nocobase/license-kit';
13
- import _ from 'lodash';
14
13
  import { checkExternalDbConnection, readExternalDbConnectionConfig, } from "../../lib/db-connection-check.js";
15
14
  import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
16
15
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
17
16
  import { buildRuntimeEnvVars } from '../../lib/runtime-env-vars.js';
18
17
  import { resolveLicensePkgUrlFromConfig } from '../../lib/cli-config.js';
18
+ import { deepEqual, omitKeys } from "../../lib/object-utils.js";
19
19
  import { commandOutput } from '../../lib/run-npm.js';
20
20
  import { appUrl } from '../env/shared.js';
21
21
  export function createLicenseEnvFlag(description) {
@@ -296,7 +296,7 @@ export function isDbMatch(env, keyData) {
296
296
  if (currentDb?.id && licenseDb?.id) {
297
297
  return currentDb.id === licenseDb.id;
298
298
  }
299
- return _.isEqual(_.omit(currentDb, ['id']), _.omit(licenseDb, ['id']));
299
+ return deepEqual(omitKeys(currentDb, ['id']), omitKeys(licenseDb, ['id']));
300
300
  }
301
301
  export function isSysMatch(env, keyData) {
302
302
  const instance = keyData?.instanceData;
@@ -307,7 +307,7 @@ export function isSysMatch(env, keyData) {
307
307
  sys: item?.sys ?? null,
308
308
  osVer: item?.osVer ?? null,
309
309
  });
310
- return _.isEqual(normalize(env), normalize(instance));
310
+ return deepEqual(normalize(env), normalize(instance));
311
311
  }
312
312
  export async function getLicenseStatus(keyData) {
313
313
  if (!keyData) {
@@ -38,7 +38,6 @@ export default class LicenseStatus extends Command {
38
38
  yes: flags.yes,
39
39
  });
40
40
  if (!confirmed) {
41
- this.log('Canceled.');
42
41
  return;
43
42
  }
44
43
  }