@nocobase/cli 2.1.0-alpha.34 → 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
@@ -6,7 +6,6 @@
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 fsp from 'node:fs/promises';
12
11
  import os from 'node:os';
@@ -15,6 +14,7 @@ import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveM
15
14
  import { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
16
15
  import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
17
16
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
17
+ import { confirm } from "../../lib/inquirer.js";
18
18
  import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
19
19
  import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
20
20
  function resolveConfiguredPath(value) {
@@ -109,19 +109,17 @@ async function confirmDownAll(envName, force, options) {
109
109
  }
110
110
  throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --force to delete everything for "${envName}" in non-interactive mode.`);
111
111
  }
112
- const answer = await p.confirm({
113
- message: usedCurrentEnv
114
- ? `Delete everything for current env "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`
115
- : `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
116
- active: 'yes',
117
- inactive: 'no',
118
- initialValue: false,
119
- });
120
- if (p.isCancel(answer)) {
121
- p.cancel('Down cancelled.');
112
+ try {
113
+ return await confirm({
114
+ message: usedCurrentEnv
115
+ ? `Delete everything for current env "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`
116
+ : `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
117
+ default: false,
118
+ });
119
+ }
120
+ catch {
122
121
  return false;
123
122
  }
124
- return answer;
125
123
  }
126
124
  function formatDownCrossEnvForceRequiredMessage(currentEnv, requestedEnv) {
127
125
  return [
@@ -191,7 +189,6 @@ export default class AppDown extends Command {
191
189
  yes: flags.yes,
192
190
  });
193
191
  if (!confirmed) {
194
- this.log('Canceled.');
195
192
  return;
196
193
  }
197
194
  }
@@ -59,7 +59,6 @@ export default class AppLogs extends Command {
59
59
  yes: flags.yes,
60
60
  });
61
61
  if (!confirmed) {
62
- this.log('Canceled.');
63
62
  return;
64
63
  }
65
64
  }
@@ -68,7 +68,6 @@ export default class AppRestart extends Command {
68
68
  yes: flags.yes,
69
69
  });
70
70
  if (!confirmed) {
71
- this.log('Canceled.');
72
71
  return;
73
72
  }
74
73
  }
@@ -100,7 +100,6 @@ export default class AppStart extends Command {
100
100
  yes: flags.yes,
101
101
  });
102
102
  if (!confirmed) {
103
- this.log('Canceled.');
104
103
  return;
105
104
  }
106
105
  }
@@ -59,7 +59,6 @@ export default class AppStop extends Command {
59
59
  yes: flags.yes,
60
60
  });
61
61
  if (!confirmed) {
62
- this.log('Canceled.');
63
62
  return;
64
63
  }
65
64
  }
@@ -560,7 +560,6 @@ export default class AppUpgrade extends Command {
560
560
  yes: parsed.yes,
561
561
  });
562
562
  if (!confirmed) {
563
- this.log('Canceled.');
564
563
  return;
565
564
  }
566
565
  }
@@ -13,8 +13,7 @@ import { buildStoredEnvConfig, } from '../../lib/env-config.js';
13
13
  import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
14
14
  import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, } from '../../lib/cli-locale.js';
15
15
  import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
16
- import { printVerbose, setVerboseMode } from '../../lib/ui.js';
17
- import * as p from '@clack/prompts';
16
+ import { printStage, printSuccess, printVerbose, setVerboseMode } from '../../lib/ui.js';
18
17
  const ENV_RUNTIME_FLAG_MAP = {
19
18
  source: 'source',
20
19
  'download-version': 'downloadVersion',
@@ -292,7 +291,7 @@ export default class EnvAdd extends Command {
292
291
  applyCliLocale(parsedFlags.locale);
293
292
  setVerboseMode(parsedFlags.verbose);
294
293
  if (!parsedFlags['no-intro']) {
295
- p.intro('Connect a NocoBase Environment');
294
+ printStage('Connect to NocoBase');
296
295
  }
297
296
  const results = await runPromptCatalog(EnvAdd.prompts, {
298
297
  values: this.buildPromptValues(args.name, parsedFlags),
@@ -308,6 +307,6 @@ export default class EnvAdd extends Command {
308
307
  await this.config.runCommand('env:auth', [envName]);
309
308
  }
310
309
  await this.config.runCommand('env:update', [envName]);
311
- p.outro(`Env "${envName}" added successfully.`);
310
+ printSuccess(`✔ Env "${envName}" is ready.`);
312
311
  }
313
312
  }
@@ -10,7 +10,7 @@ import { Args, Command, Flags } from '@oclif/core';
10
10
  import { getCurrentEnvName } from '../../lib/auth-store.js';
11
11
  import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
12
12
  import { authenticateEnvWithOauth } from '../../lib/env-auth.js';
13
- import { failTask, startTask, succeedTask } from '../../lib/ui.js';
13
+ import { failTask, printStage, startTask, succeedTask } from '../../lib/ui.js';
14
14
  export default class EnvAuth extends Command {
15
15
  static summary = 'Sign in to a saved NocoBase environment with OAuth';
16
16
  static examples = [
@@ -39,13 +39,14 @@ export default class EnvAuth extends Command {
39
39
  this.error(`Environment name was provided both as the argument ("${nameArg}") and as --env ("${nameFlag}"). Please use only one.`);
40
40
  }
41
41
  const envName = nameArg || nameFlag || (await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
42
+ printStage('Signing in');
42
43
  startTask(`Starting browser sign-in for "${envName}"...`);
43
44
  try {
44
45
  await authenticateEnvWithOauth({
45
46
  envName,
46
47
  scope: resolveDefaultConfigScope(),
47
48
  });
48
- succeedTask(`Signed in to "${envName}".`);
49
+ succeedTask(`✔ Signed in to "${envName}".`);
49
50
  }
50
51
  catch (error) {
51
52
  failTask(`Sign-in failed for "${envName}".`);
@@ -7,19 +7,26 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Args, Command, Flags } from '@oclif/core';
10
- import { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
10
+ import { getCurrentEnvName, loadAuthConfig, removeEnv } from '../../lib/auth-store.js';
11
11
  import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
12
- import { confirmAction, isInteractiveTerminal, printVerbose, setVerboseMode } from '../../lib/ui.js';
12
+ import { confirm } from "../../lib/inquirer.js";
13
+ import { isInteractiveTerminal, printVerbose, setVerboseMode } from '../../lib/ui.js';
13
14
  export default class EnvRemove extends Command {
14
15
  static summary = 'Remove a configured environment';
16
+ static description = 'Remove the saved CLI env config for an environment. This command does not clean local app files, containers, or storage data.';
15
17
  static examples = [
16
18
  '<%= config.bin %> <%= command.id %> staging',
17
- '<%= config.bin %> <%= command.id %> staging -f',
19
+ '<%= config.bin %> <%= command.id %> staging --yes',
18
20
  ];
19
21
  static flags = {
22
+ yes: Flags.boolean({
23
+ char: 'y',
24
+ description: 'Skip confirmation and remove the saved CLI env config',
25
+ default: false,
26
+ }),
20
27
  force: Flags.boolean({
21
28
  char: 'f',
22
- description: 'Remove without confirmation',
29
+ hidden: true,
23
30
  default: false,
24
31
  }),
25
32
  verbose: Flags.boolean({
@@ -36,24 +43,42 @@ export default class EnvRemove extends Command {
36
43
  async run() {
37
44
  const { args, flags } = await this.parse(EnvRemove);
38
45
  setVerboseMode(flags.verbose);
39
- const currentEnv = await getCurrentEnvName({ scope: resolveDefaultConfigScope() });
40
- if (args.name === currentEnv && !flags.force) {
46
+ const scope = resolveDefaultConfigScope();
47
+ const config = await loadAuthConfig({ scope });
48
+ if (!config.envs[args.name]) {
49
+ this.error(`Env "${args.name}" is not configured`);
50
+ }
51
+ const currentEnv = await getCurrentEnvName({ scope });
52
+ const skipConfirmation = flags.yes || flags.force;
53
+ if (!skipConfirmation) {
41
54
  if (!isInteractiveTerminal()) {
42
- this.error('Refusing to remove the current env without confirmation. Re-run with `--force`.');
55
+ this.error(`Refusing to remove env "${args.name}" without confirmation in non-interactive mode. Re-run with \`--yes\` to remove only the saved CLI env config.`);
56
+ }
57
+ const subject = args.name === currentEnv ? `current env "${args.name}"` : `env "${args.name}"`;
58
+ let confirmed = false;
59
+ try {
60
+ confirmed = await confirm({
61
+ message: `Remove ${subject}? Only the saved CLI env config will be removed.`,
62
+ default: false,
63
+ });
64
+ }
65
+ catch {
66
+ return;
43
67
  }
44
- const confirmed = await confirmAction(`Remove current env "${args.name}"?`, { defaultValue: false });
45
68
  if (!confirmed) {
46
- this.log('Canceled.');
47
69
  return;
48
70
  }
49
71
  }
50
72
  printVerbose(`Removing env "${args.name}"`);
51
- const result = await removeEnv(args.name, { scope: resolveDefaultConfigScope() });
52
- this.log(`Removed env "${result.removed}".`);
73
+ const result = await removeEnv(args.name, { scope });
53
74
  if (result.hasEnvs) {
54
- this.log(`Current env: ${await getCurrentEnvName({ scope: resolveDefaultConfigScope() })}`);
75
+ if (args.name === currentEnv) {
76
+ this.log(`Removed env "${result.removed}". Switched current env to "${await getCurrentEnvName({ scope })}".`);
77
+ return;
78
+ }
79
+ this.log(`Removed env "${result.removed}".`);
55
80
  return;
56
81
  }
57
- this.log('No envs configured.');
82
+ this.log(`Removed env "${result.removed}". No envs configured.`);
58
83
  }
59
84
  }
@@ -12,7 +12,7 @@ import { Args, Command, Flags } from '@oclif/core';
12
12
  import { getCurrentEnvName } from '../../lib/auth-store.js';
13
13
  import { updateEnvRuntime } from '../../lib/bootstrap.js';
14
14
  import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
15
- import { failTask, startTask, succeedTask } from '../../lib/ui.js';
15
+ import { failTask, printVerbose, setVerboseMode, startTask, stopTask, succeedTask } from '../../lib/ui.js';
16
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
17
  export default class EnvUpdate extends Command {
18
18
  static summary = 'Refresh an environment runtime from swagger:get and persist connection overrides';
@@ -44,6 +44,7 @@ export default class EnvUpdate extends Command {
44
44
  };
45
45
  async run() {
46
46
  const { args, flags } = await this.parse(EnvUpdate);
47
+ setVerboseMode(Boolean(flags.verbose));
47
48
  const envName = args.name;
48
49
  const envLabel = envName ?? (await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
49
50
  startTask(`Updating env runtime: ${envLabel}`);
@@ -57,7 +58,13 @@ export default class EnvUpdate extends Command {
57
58
  configFile: path.join(path.dirname(path.dirname(path.dirname(__dirname))), 'nocobase-ctl.config.json'),
58
59
  verbose: flags.verbose,
59
60
  });
60
- succeedTask(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
61
+ if (flags.verbose) {
62
+ succeedTask(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
63
+ }
64
+ else {
65
+ stopTask();
66
+ printVerbose(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
67
+ }
61
68
  }
62
69
  catch (error) {
63
70
  failTask(`Failed to update env "${envLabel}".`);
@@ -44,8 +44,8 @@ function buildWebUiStagesFromTestPrompts(c) {
44
44
  }
45
45
  /**
46
46
  * With `--ui`: `runPromptCatalogWebUI` using `stages` + `sectionTitle`, then
47
- * `runPromptCatalog` on the same catalog as `prompts-test`. Without `--ui`, same terminal behavior
48
- * as `prompts-test` (handy for comparing).
47
+ * `runPromptCatalog` on the same catalog as `prompts-test`. Without `--ui`, the same terminal prompt
48
+ * behavior as `prompts-test` (handy for comparing).
49
49
  */
50
50
  export default class PromptsStages extends Command {
51
51
  static hidden = true;
@@ -60,12 +60,12 @@ export default class PromptsStages extends Command {
60
60
  ];
61
61
  static flags = {
62
62
  ui: Flags.boolean({
63
- description: 'Open the localhost form: `runPromptCatalogWebUI` with `stages` and `sectionTitle` (vs `prompts-test --ui`, which uses one `catalog`). Without --ui, behavior matches `prompts-test` (Clack in TTY / presets).',
63
+ description: 'Open the localhost form: `runPromptCatalogWebUI` with `stages` and `sectionTitle` (vs `prompts-test --ui`, which uses one `catalog`). Without --ui, behavior matches `prompts-test` (terminal prompt layer in TTY / presets).',
64
64
  default: false,
65
65
  }),
66
66
  yes: Flags.boolean({
67
67
  char: 'y',
68
- description: 'Accept defaults only in the terminal (no Clack after submit); same semantics as `prompts-test` when used with the submitted or default preset.',
68
+ description: 'Accept defaults only in the terminal (no extra interactive prompts after submit); same semantics as `prompts-test` when used with the submitted or default preset.',
69
69
  default: false,
70
70
  }),
71
71
  locale: Flags.string({
@@ -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 { Args, Command, Flags } from '@oclif/core';
11
10
  import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
12
11
  import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../../lib/cli-locale.js";
12
+ import { printInfo } from "../../lib/ui.js";
13
13
  import { runPromptCatalogWebUI, } from "../../lib/prompt-web-ui.js";
14
14
  export default class PromptsTest extends Command {
15
15
  static hidden = true;
@@ -84,7 +84,7 @@ export default class PromptsTest extends Command {
84
84
  initialValue: 'world',
85
85
  placeholder: 'Your name',
86
86
  /**
87
- * Example: async validation (Clack re-prompts; Web UI returns 400 on submit with this message).
87
+ * Example: async validation (the terminal prompt layer re-prompts; Web UI returns 400 on submit with this message).
88
88
  */
89
89
  validate: async (v) => {
90
90
  if (String(v).trim().length < 2) {
@@ -109,7 +109,7 @@ export default class PromptsTest extends Command {
109
109
  type: 'run',
110
110
  when: (values) => values.select === 'option1',
111
111
  run: (values) => {
112
- p.log.info(`run block: select is option1 (current keys: ${Object.keys(values).join(', ')})`);
112
+ printInfo(`run block: select is option1 (current keys: ${Object.keys(values).join(', ')})`);
113
113
  },
114
114
  },
115
115
  password: {
@@ -154,7 +154,7 @@ export default class PromptsTest extends Command {
154
154
  });
155
155
  this.log(JSON.stringify(results, null, 2));
156
156
  }
157
- /** Map oclif parse result into `values` presets: each key here skips that prompt (no Clack UI). */
157
+ /** Map oclif parse result into `values` presets: each key here skips that prompt (no terminal prompt UI). */
158
158
  buildPresetValuesFromParsed(args, flags) {
159
159
  const preset = {};
160
160
  const file = args.file ?? flags.file;
@@ -7,7 +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
10
  import pc from 'picocolors';
12
11
  import { existsSync } from 'node:fs';
13
12
  import path from 'node:path';
@@ -19,10 +18,11 @@ import { resolveDefaultConfigScope } from '../lib/cli-home.js';
19
18
  import { runPromptCatalogWebUI, } from "../lib/prompt-web-ui.js";
20
19
  import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
21
20
  import { inspectSkillsStatus, installNocoBaseSkills, updateNocoBaseSkills, } from '../lib/skills-manager.js';
21
+ import { omitKeys, pickKeys } from "../lib/object-utils.js";
22
+ import { printInfo, printStage, printVerbose, printWarning } from '../lib/ui.js';
22
23
  import Download from "./download.js";
23
24
  import EnvAdd from "./env/add.js";
24
25
  import Install, { defaultDbPortForDialect } from "./install.js";
25
- import _ from 'lodash';
26
26
  const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
27
27
  const DEFAULT_INIT_APP_NAME = 'local';
28
28
  const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
@@ -129,15 +129,18 @@ function shellQuoteArg(value) {
129
129
  : `'${value.replace(/'/g, `'\\''`)}'`;
130
130
  }
131
131
  function initTitle() {
132
- return translateCli('commands.init.messages.title');
132
+ return 'Set up NocoBase';
133
+ }
134
+ function logInitStage(title) {
135
+ printStage(title);
133
136
  }
134
137
  function logInitUiReady(command, url) {
135
- p.log.step(translateCli('commands.init.messages.uiReady'));
136
- p.log.info(translateCli('commands.init.messages.uiReadyHelp'));
138
+ command.log(translateCli('commands.init.messages.uiReady'));
139
+ command.log(translateCli('commands.init.messages.uiReadyHelp'));
137
140
  command.log(`URL: ${url}`);
138
141
  }
139
142
  function logInitUiBrowserOpenFallback() {
140
- p.log.warn(translateCli('commands.init.messages.uiOpenBrowserFallback'));
143
+ printWarning(translateCli('commands.init.messages.uiOpenBrowserFallback'));
141
144
  }
142
145
  function formatBrowserOpenError(error) {
143
146
  if (error instanceof Error) {
@@ -313,8 +316,8 @@ Prompt modes:
313
316
  min: 0,
314
317
  max: 65535,
315
318
  }),
316
- ..._.pick(EnvAdd.flags, INIT_ENV_ADD_FLAG_NAMES),
317
- ..._.omit(Install.flags, ['yes', 'env']),
319
+ ...pickKeys(EnvAdd.flags, INIT_ENV_ADD_FLAG_NAMES),
320
+ ...omitKeys(Install.flags, ['yes', 'env']),
318
321
  };
319
322
  async run() {
320
323
  const parsedResult = await this.parse(Init);
@@ -337,10 +340,10 @@ Prompt modes:
337
340
  if (normalizedFlags.resume) {
338
341
  const envName = String(normalizedFlags.env ?? '').trim();
339
342
  if (!envName) {
340
- p.log.error(formatResumeEnvRequiredMessage());
343
+ this.error(formatResumeEnvRequiredMessage());
341
344
  this.exit(1);
342
345
  }
343
- p.intro(initTitle());
346
+ logInitStage(initTitle());
344
347
  await this.syncNocoBaseSkills({
345
348
  skip: Boolean(normalizedFlags['skip-skills']),
346
349
  });
@@ -351,7 +354,6 @@ Prompt modes:
351
354
  const message = error instanceof Error ? error.message : String(error);
352
355
  this.error(message);
353
356
  }
354
- p.outro('Workspace init finished.');
355
357
  return;
356
358
  }
357
359
  const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY);
@@ -377,21 +379,21 @@ Prompt modes:
377
379
  }
378
380
  if (normalizedFlags.yes && !String(presetValues.appName ?? '').trim()) {
379
381
  const formatted = formatSkippedAppNameRequiredMessage();
380
- p.log.error(highlightInitValidationMessage(formatted));
382
+ this.error(highlightInitValidationMessage(formatted));
381
383
  this.exit(1);
382
384
  }
383
385
  const appName = String(presetValues.appName ?? '').trim();
384
386
  if (useBrowserUi) {
385
- p.intro(initTitle());
386
- p.log.info(translateCli('commands.init.messages.uiOpening'));
387
+ logInitStage(initTitle());
388
+ this.log(translateCli('commands.init.messages.uiOpening'));
387
389
  }
388
390
  else {
389
- p.intro(initTitle());
391
+ logInitStage(initTitle());
390
392
  if (normalizedFlags.yes) {
391
- p.log.info(`Prompts skipped (--yes). NocoBase will be installed for env "${appName}" using the provided flags and safe defaults.`);
393
+ printInfo(`Non-interactive setup for env "${appName}" (--yes).`);
392
394
  }
393
395
  else if (!interactive) {
394
- p.log.warn('No interactive terminal detected. NocoBase will be installed using the provided flags and safe defaults.');
396
+ printWarning('No interactive terminal detected. NocoBase will be installed using the provided flags and safe defaults.');
395
397
  }
396
398
  }
397
399
  const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
@@ -412,7 +414,7 @@ Prompt modes:
412
414
  },
413
415
  onOpenBrowserError: (_url, err) => {
414
416
  logInitUiBrowserOpenFallback();
415
- p.log.info(`Browser open error: ${formatBrowserOpenError(err)}`);
417
+ this.log(`Browser open error: ${formatBrowserOpenError(err)}`);
416
418
  },
417
419
  });
418
420
  }
@@ -422,12 +424,11 @@ Prompt modes:
422
424
  yes: normalizedFlags.yes || useBrowserUi || !interactive,
423
425
  hooks: {
424
426
  onCancel: () => {
425
- p.cancel('Init cancelled.');
426
427
  this.exit(0);
427
428
  },
428
429
  onMissingNonInteractive: (message) => {
429
430
  const formatted = formatInitValidationMessage(message);
430
- p.log.error(highlightInitValidationMessage(formatted));
431
+ this.error(highlightInitValidationMessage(formatted));
431
432
  this.exit(1);
432
433
  },
433
434
  },
@@ -438,7 +439,7 @@ Prompt modes:
438
439
  ? await getEnv(String(results.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
439
440
  : undefined;
440
441
  if (existingEnv && Boolean(normalizedFlags.force)) {
441
- p.log.warn(`Reconfiguring existing env ${pc.cyan(pc.bold(`"${existingEnv.name}"`))} from the global config because ${pc.bold('--force')} was set. The env config will be updated before install starts, then refreshed again after install succeeds.`);
442
+ printWarning(`Reconfiguring existing env ${pc.cyan(pc.bold(`"${existingEnv.name}"`))} from the global config because ${pc.bold('--force')} was set. The env config will be updated before install starts, then refreshed again after install succeeds.`);
442
443
  }
443
444
  await this.syncNocoBaseSkills({
444
445
  skip: Boolean(normalizedFlags['skip-skills']),
@@ -447,14 +448,16 @@ Prompt modes:
447
448
  try {
448
449
  // oclif explicit registry keys use `:` (e.g. `env:add`); users still type `nb env add`.
449
450
  if (hasNocobase) {
450
- p.log.step('Running nb env add');
451
+ logInitStage('Connecting to the env');
452
+ printVerbose('Running nb env add');
451
453
  await this.config.runCommand('env:add', this.buildEnvAddArgv(results));
452
454
  }
453
455
  else {
454
- p.log.step('Saving the local env config');
456
+ logInitStage('Saving env config');
455
457
  await this.persistManagedEnvConfig(results, normalizedFlags);
456
458
  managedInstallResults = results;
457
- p.log.step('Running nb init');
459
+ printInfo(`Saved env config for "${String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME}".`);
460
+ printVerbose('Running nb init');
458
461
  await this.config.runCommand('install', this.buildInstallArgv(results, normalizedFlags));
459
462
  }
460
463
  }
@@ -463,10 +466,9 @@ Prompt modes:
463
466
  const formatted = managedInstallResults
464
467
  ? this.formatManagedInstallFailureMessage(message, managedInstallResults, normalizedFlags)
465
468
  : message;
466
- p.outro(pc.red(formatted));
469
+ this.error(pc.red(formatted));
467
470
  this.exit(1);
468
471
  }
469
- p.outro('Workspace init finished.');
470
472
  }
471
473
  static async buildDynamicInitialValuesForInstall(flags, presetValues) {
472
474
  const out = {};
@@ -706,22 +708,25 @@ Prompt modes:
706
708
  }
707
709
  async syncNocoBaseSkills(options) {
708
710
  if (options?.skip) {
709
- p.log.step('Skipped NocoBase agent skills sync.');
711
+ printVerbose('Skipped agent skills sync.');
710
712
  return;
711
713
  }
712
714
  try {
715
+ logInitStage('Syncing agent skills');
713
716
  const status = await inspectSkillsStatus();
714
717
  if (!status.installed) {
715
- p.log.step('Installing NocoBase agent skills (nb skills install)');
718
+ printVerbose('Installing NocoBase agent skills (nb skills install)');
716
719
  await installNocoBaseSkills();
720
+ printInfo('Agent skills ready.');
717
721
  return;
718
722
  }
719
- p.log.step('Updating NocoBase agent skills (nb skills update)');
723
+ printVerbose('Updating NocoBase agent skills (nb skills update)');
720
724
  await updateNocoBaseSkills();
725
+ printInfo('Agent skills ready.');
721
726
  }
722
727
  catch (error) {
723
728
  const message = error instanceof Error ? error.message : String(error);
724
- p.outro(pc.red(`Skills sync failed: ${message}`));
729
+ this.error(pc.red(`Skills sync failed: ${message}`));
725
730
  this.exit(1);
726
731
  }
727
732
  }
@@ -798,9 +803,11 @@ Prompt modes:
798
803
  if (options?.nonInteractive ?? true) {
799
804
  argv.unshift('-y');
800
805
  }
806
+ argv.push('--skip-save-env-log');
801
807
  const processArgv = process.argv.slice(2);
802
808
  const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
803
809
  const source = String(results.source ?? '').trim();
810
+ const hasNocobase = String(results.hasNocobase ?? '').trim() === 'yes';
804
811
  const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
805
812
  const authType = String(results.authType ?? '').trim();
806
813
  const accessToken = String(results.accessToken ?? '');
@@ -811,7 +818,7 @@ Prompt modes:
811
818
  if (Boolean(flags.verbose)) {
812
819
  argv.push('--verbose');
813
820
  }
814
- if (apiBaseUrl) {
821
+ if (hasNocobase && apiBaseUrl) {
815
822
  argv.push('--api-base-url', apiBaseUrl);
816
823
  }
817
824
  if (authType) {