@nocobase/cli 2.1.0-alpha.24 → 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 (74) hide show
  1. package/README.md +41 -49
  2. package/README.zh-CN.md +38 -45
  3. package/bin/run.js +15 -0
  4. package/dist/commands/app/down.js +260 -0
  5. package/dist/commands/app/logs.js +98 -0
  6. package/dist/commands/app/restart.js +75 -0
  7. package/dist/commands/app/start.js +252 -0
  8. package/dist/commands/app/stop.js +98 -0
  9. package/dist/commands/app/upgrade.js +595 -0
  10. package/dist/commands/build.js +3 -48
  11. package/dist/commands/db/shared.js +19 -5
  12. package/dist/commands/dev.js +3 -140
  13. package/dist/commands/down.js +3 -184
  14. package/dist/commands/download.js +4 -856
  15. package/dist/commands/env/add.js +33 -48
  16. package/dist/commands/env/auth.js +6 -13
  17. package/dist/commands/env/info.js +152 -0
  18. package/dist/commands/env/list.js +27 -18
  19. package/dist/commands/env/remove.js +4 -10
  20. package/dist/commands/env/shared.js +158 -0
  21. package/dist/commands/env/update.js +7 -13
  22. package/dist/commands/env/use.js +5 -13
  23. package/dist/commands/{prompts-stages.js → examples/prompts-stages.js} +3 -3
  24. package/dist/commands/{prompts-test.js → examples/prompts-test.js} +3 -3
  25. package/dist/commands/init.js +270 -64
  26. package/dist/commands/install.js +352 -86
  27. package/dist/commands/logs.js +3 -81
  28. package/dist/commands/plugin/disable.js +64 -0
  29. package/dist/commands/plugin/enable.js +64 -0
  30. package/dist/commands/plugin/list.js +62 -0
  31. package/dist/commands/pm/disable.js +3 -54
  32. package/dist/commands/pm/enable.js +3 -54
  33. package/dist/commands/pm/list.js +3 -45
  34. package/dist/commands/restart.js +12 -0
  35. package/dist/commands/scaffold/migration.js +1 -1
  36. package/dist/commands/scaffold/plugin.js +1 -1
  37. package/dist/commands/self/check.js +1 -1
  38. package/dist/commands/self/update.js +13 -3
  39. package/dist/commands/skills/check.js +11 -5
  40. package/dist/commands/skills/index.js +1 -1
  41. package/dist/commands/skills/install.js +20 -7
  42. package/dist/commands/skills/remove.js +71 -0
  43. package/dist/commands/skills/update.js +27 -7
  44. package/dist/commands/source/build.js +58 -0
  45. package/dist/commands/source/dev.js +157 -0
  46. package/dist/commands/source/download.js +866 -0
  47. package/dist/commands/source/test.js +467 -0
  48. package/dist/commands/start.js +3 -202
  49. package/dist/commands/stop.js +3 -81
  50. package/dist/commands/test.js +3 -457
  51. package/dist/commands/upgrade.js +3 -574
  52. package/dist/help/runtime-help.js +3 -0
  53. package/dist/lib/api-client.js +22 -7
  54. package/dist/lib/app-health.js +126 -0
  55. package/dist/lib/app-managed-resources.js +264 -0
  56. package/dist/lib/app-runtime.js +16 -5
  57. package/dist/lib/auth-store.js +162 -43
  58. package/dist/lib/bootstrap.js +13 -12
  59. package/dist/lib/cli-home.js +38 -6
  60. package/dist/lib/cli-locale.js +15 -1
  61. package/dist/lib/env-auth.js +3 -3
  62. package/dist/lib/env-config.js +80 -0
  63. package/dist/lib/generated-command.js +10 -2
  64. package/dist/lib/http-request.js +49 -0
  65. package/dist/lib/prompt-web-ui.js +13 -6
  66. package/dist/lib/resource-command.js +10 -2
  67. package/dist/lib/runtime-generator.js +1 -1
  68. package/dist/lib/self-manager.js +1 -1
  69. package/dist/lib/skills-manager.js +173 -79
  70. package/dist/lib/startup-update.js +203 -0
  71. package/dist/locale/en-US.json +4 -1
  72. package/dist/locale/zh-CN.json +4 -1
  73. package/package.json +27 -4
  74. package/dist/commands/ps.js +0 -116
@@ -6,21 +6,14 @@
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 { Args, Command, Flags } from '@oclif/core';
9
+ import { Args, Command } from '@oclif/core';
10
10
  import { setCurrentEnv } from '../../lib/auth-store.js';
11
- import { formatCliHomeScope } from '../../lib/cli-home.js';
11
+ import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
12
12
  export default class EnvUse extends Command {
13
13
  static summary = 'Switch the current environment';
14
14
  static examples = [
15
15
  '<%= config.bin %> <%= command.id %> local',
16
16
  ];
17
- static flags = {
18
- scope: Flags.string({
19
- char: 's',
20
- description: 'Config scope',
21
- options: ['project', 'global'],
22
- }),
23
- };
24
17
  static args = {
25
18
  name: Args.string({
26
19
  description: 'Configured environment name',
@@ -28,9 +21,8 @@ export default class EnvUse extends Command {
28
21
  }),
29
22
  };
30
23
  async run() {
31
- const { args, flags } = await this.parse(EnvUse);
32
- const scope = flags.scope;
33
- await setCurrentEnv(args.name, { scope });
34
- this.log(`Current env: ${args.name}${scope ? ` (${formatCliHomeScope(scope)} scope)` : ''}`);
24
+ const { args } = await this.parse(EnvUse);
25
+ await setCurrentEnv(args.name, { scope: resolveDefaultConfigScope() });
26
+ this.log(`Current env: ${args.name}`);
35
27
  }
36
28
  }
@@ -7,9 +7,9 @@
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 { runPromptCatalog, } from "../lib/prompt-catalog.js";
11
- import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../lib/cli-locale.js";
12
- import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
10
+ import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
11
+ import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../../lib/cli-locale.js";
12
+ import { runPromptCatalogWebUI } from "../../lib/prompt-web-ui.js";
13
13
  import PromptsTest from "./prompts-test.js";
14
14
  function buildWebUiStagesFromTestPrompts(c) {
15
15
  return [
@@ -8,9 +8,9 @@
8
8
  */
9
9
  import * as p from '@clack/prompts';
10
10
  import { Args, Command, Flags } from '@oclif/core';
11
- import { runPromptCatalog, } from "../lib/prompt-catalog.js";
12
- import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../lib/cli-locale.js";
13
- import { runPromptCatalogWebUI, } from "../lib/prompt-web-ui.js";
11
+ import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
12
+ import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../../lib/cli-locale.js";
13
+ import { runPromptCatalogWebUI, } from "../../lib/prompt-web-ui.js";
14
14
  export default class PromptsTest extends Command {
15
15
  static hidden = true;
16
16
  static args = {
@@ -15,9 +15,10 @@ import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
15
15
  import { getEnv, upsertEnv } from "../lib/auth-store.js";
16
16
  import { runPromptCatalog, } from "../lib/prompt-catalog.js";
17
17
  import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
18
+ import { resolveDefaultConfigScope } from '../lib/cli-home.js';
18
19
  import { runPromptCatalogWebUI, } from "../lib/prompt-web-ui.js";
19
20
  import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
20
- import { installNocoBaseSkills } from '../lib/skills-manager.js';
21
+ import { inspectSkillsStatus, installNocoBaseSkills, updateNocoBaseSkills, } from '../lib/skills-manager.js';
21
22
  import Download from "./download.js";
22
23
  import EnvAdd from "./env/add.js";
23
24
  import Install, { defaultDbPortForDialect } from "./install.js";
@@ -25,7 +26,14 @@ import _ from 'lodash';
25
26
  const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
26
27
  const DEFAULT_INIT_APP_NAME = 'local';
27
28
  const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
28
- const CONFIG_SCOPE = 'project';
29
+ const INIT_ENV_ADD_FLAG_NAMES = [
30
+ 'locale',
31
+ 'default-api-base-url',
32
+ 'api-base-url',
33
+ 'auth-type',
34
+ 'access-token',
35
+ 'token',
36
+ ];
29
37
  const initText = (key, values) => localeText(`commands.init.${key}`, values);
30
38
  function withExtraHidden(def, extraHidden) {
31
39
  if (def.type === 'run') {
@@ -55,6 +63,27 @@ function resolveInitDownloadVersion(results) {
55
63
  }
56
64
  return preset;
57
65
  }
66
+ function initVersionPromptValue(version) {
67
+ return version === 'latest' || version === 'beta' || version === 'alpha'
68
+ ? version
69
+ : 'other';
70
+ }
71
+ function yesInitialValue(def, fallback) {
72
+ if ('yesInitialValue' in def && def.yesInitialValue !== undefined) {
73
+ return String(def.yesInitialValue);
74
+ }
75
+ return fallback;
76
+ }
77
+ function hasDownloadOverride(flags) {
78
+ return Boolean(String(flags.source ?? '').trim()
79
+ || String(flags.version ?? '').trim());
80
+ }
81
+ function explicitApiBaseUrlFlag(flags) {
82
+ return String(flags['api-base-url'] ?? '').trim();
83
+ }
84
+ function explicitDbHostFlag(flags) {
85
+ return String(flags['db-host'] ?? '').trim();
86
+ }
58
87
  function shouldAllowExistingInitEnv() {
59
88
  return argvHasToken(process.argv.slice(2), ['--force', '-f']);
60
89
  }
@@ -67,7 +96,7 @@ async function validateInitAppName(value) {
67
96
  if (!envName) {
68
97
  return undefined;
69
98
  }
70
- const existingEnv = await getEnv(envName, { scope: 'project' });
99
+ const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
71
100
  if (existingEnv) {
72
101
  if (shouldAllowExistingInitEnv()) {
73
102
  return undefined;
@@ -94,6 +123,11 @@ function formatSkippedAppNameRequiredMessage() {
94
123
  translateCli('commands.init.messages.appNameEnvHelp'),
95
124
  ].join('\n');
96
125
  }
126
+ function shellQuoteArg(value) {
127
+ return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value)
128
+ ? value
129
+ : `'${value.replace(/'/g, `'\\''`)}'`;
130
+ }
97
131
  function initTitle() {
98
132
  return translateCli('commands.init.messages.title');
99
133
  }
@@ -105,6 +139,12 @@ function logInitUiReady(command, url) {
105
139
  function logInitUiBrowserOpenFallback() {
106
140
  p.log.warn(translateCli('commands.init.messages.uiOpenBrowserFallback'));
107
141
  }
142
+ function formatBrowserOpenError(error) {
143
+ if (error instanceof Error) {
144
+ return error.message;
145
+ }
146
+ return String(error);
147
+ }
108
148
  export default class Init extends Command {
109
149
  static summary = 'Set up NocoBase so coding agents can connect and work with it';
110
150
  static description = `Set up NocoBase for coding agents in the current workspace.
@@ -137,6 +177,26 @@ Prompt modes:
137
177
  '<%= config.bin %> <%= command.id %> --ui --ui-port 3000',
138
178
  ];
139
179
  static prompts = {
180
+ seedResume: {
181
+ type: 'run',
182
+ run: (values, command) => {
183
+ const record = values;
184
+ if (record.resume === undefined) {
185
+ const flags = command?.parsedFlagsForPromptSeeds;
186
+ record.resume = Boolean(flags?.resume);
187
+ }
188
+ },
189
+ },
190
+ seedEnvName: {
191
+ type: 'run',
192
+ run: (values) => {
193
+ const record = values;
194
+ const appName = String(record.appName ?? '').trim();
195
+ if (appName && record.env === undefined) {
196
+ record.env = appName;
197
+ }
198
+ },
199
+ },
140
200
  appName: {
141
201
  type: 'text',
142
202
  message: initText('prompts.appName.message'),
@@ -162,12 +222,6 @@ Prompt modes:
162
222
  yesInitialValue: 'no',
163
223
  required: true,
164
224
  },
165
- installSkills: {
166
- type: 'boolean',
167
- message: initText('prompts.installSkills.message'),
168
- initialValue: true,
169
- yesInitialValue: true,
170
- },
171
225
  apiBaseUrl: existingAppOnly({
172
226
  type: 'text',
173
227
  message: initText('prompts.apiBaseUrl.message'),
@@ -228,6 +282,7 @@ Prompt modes:
228
282
  rootPassword: newInstallOnly(Install.rootUserPrompts.rootPassword),
229
283
  rootNickname: newInstallOnly(Install.rootUserPrompts.rootNickname),
230
284
  };
285
+ parsedFlagsForPromptSeeds;
231
286
  static flags = {
232
287
  yes: Flags.boolean({
233
288
  char: 'y',
@@ -238,10 +293,6 @@ Prompt modes:
238
293
  char: 'e',
239
294
  description: 'Env name for this setup. Required with --yes and --resume',
240
295
  }),
241
- 'install-skills': Flags.boolean({
242
- description: 'Install NocoBase AI coding skills (`nocobase/skills`) for this workspace',
243
- default: false,
244
- }),
245
296
  ui: Flags.boolean({
246
297
  description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
247
298
  default: false,
@@ -250,6 +301,11 @@ Prompt modes:
250
301
  description: 'Show detailed command output',
251
302
  default: false,
252
303
  }),
304
+ 'skip-skills': Flags.boolean({
305
+ description: 'Skip installing or updating NocoBase AI coding skills during init',
306
+ hidden: true,
307
+ default: false,
308
+ }),
253
309
  'ui-host': Flags.string({
254
310
  description: 'Host for the local --ui setup server (default: 127.0.0.1)',
255
311
  }),
@@ -258,6 +314,7 @@ Prompt modes:
258
314
  min: 0,
259
315
  max: 65535,
260
316
  }),
317
+ ..._.pick(EnvAdd.flags, INIT_ENV_ADD_FLAG_NAMES),
261
318
  ..._.omit(Install.flags, ['yes', 'env']),
262
319
  };
263
320
  async run() {
@@ -265,6 +322,9 @@ Prompt modes:
265
322
  applyCliLocale(parsedResult.flags.locale);
266
323
  const flags = parsedResult.flags;
267
324
  const normalizedFlags = { ...flags };
325
+ this.parsedFlagsForPromptSeeds = {
326
+ resume: Boolean(normalizedFlags.resume),
327
+ };
268
328
  if (normalizedFlags.ui && normalizedFlags.yes) {
269
329
  this.error('--ui cannot be used with --yes.');
270
330
  }
@@ -282,24 +342,14 @@ Prompt modes:
282
342
  this.exit(1);
283
343
  }
284
344
  p.intro(initTitle());
285
- if (Boolean(normalizedFlags['install-skills'])) {
286
- try {
287
- p.log.step('Installing NocoBase agent skills (nb skills install)');
288
- await installNocoBaseSkills();
289
- }
290
- catch (error) {
291
- const message = error instanceof Error ? error.message : String(error);
292
- p.outro(pc.red(`Skills install failed: ${message}`));
293
- this.error(message);
294
- }
295
- }
345
+ await this.syncNocoBaseSkills({
346
+ skip: Boolean(normalizedFlags['skip-skills']),
347
+ });
296
348
  try {
297
- p.log.step(`Resuming setup for env "${envName}" from the saved workspace config`);
298
349
  await this.config.runCommand('install', this.buildResumeInstallArgv(normalizedFlags));
299
350
  }
300
351
  catch (error) {
301
352
  const message = error instanceof Error ? error.message : String(error);
302
- p.outro(pc.red(message));
303
353
  this.error(message);
304
354
  }
305
355
  p.outro('Workspace init finished.');
@@ -308,6 +358,24 @@ Prompt modes:
308
358
  const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY);
309
359
  const useBrowserUi = Boolean(normalizedFlags.ui);
310
360
  let presetValues = this.buildPresetValuesFromFlags(normalizedFlags);
361
+ if (normalizedFlags.resume) {
362
+ const resumeEnvName = String(normalizedFlags.env ?? '').trim();
363
+ if (resumeEnvName) {
364
+ const resumeEnv = await getEnv(resumeEnvName, {
365
+ scope: resolveDefaultConfigScope(),
366
+ });
367
+ if (resumeEnv) {
368
+ const savedAppPort = String(resumeEnv.config.appPort ?? '').trim();
369
+ const savedDbPort = String(resumeEnv.config.dbPort ?? '').trim();
370
+ if (savedAppPort) {
371
+ presetValues.resumeSavedAppPort = savedAppPort;
372
+ }
373
+ if (savedDbPort) {
374
+ presetValues.resumeSavedDbPort = savedDbPort;
375
+ }
376
+ }
377
+ }
378
+ }
311
379
  if (normalizedFlags.yes && !String(presetValues.appName ?? '').trim()) {
312
380
  const formatted = formatSkippedAppNameRequiredMessage();
313
381
  p.log.error(highlightInitValidationMessage(formatted));
@@ -343,8 +411,9 @@ Prompt modes:
343
411
  onServerStart: ({ url }) => {
344
412
  logInitUiReady(this, url);
345
413
  },
346
- onOpenBrowserError: (_url, _err) => {
414
+ onOpenBrowserError: (_url, err) => {
347
415
  logInitUiBrowserOpenFallback();
416
+ p.log.info(`Browser open error: ${formatBrowserOpenError(err)}`);
348
417
  },
349
418
  });
350
419
  }
@@ -365,28 +434,17 @@ Prompt modes:
365
434
  },
366
435
  command: this,
367
436
  });
368
- const installSkills = Boolean(results.installSkills);
369
437
  const hasNocobase = results.hasNocobase === 'yes';
370
438
  const existingEnv = !hasNocobase
371
- ? await getEnv(String(results.appName ?? '').trim(), { scope: 'project' })
439
+ ? await getEnv(String(results.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
372
440
  : undefined;
373
441
  if (existingEnv && Boolean(normalizedFlags.force)) {
374
- p.log.warn(`Reconfiguring existing env ${pc.cyan(pc.bold(`"${existingEnv.name}"`))} in this workspace because ${pc.bold('--force')} was set. The env config will be updated before install starts, then refreshed again after install succeeds.`);
375
- }
376
- if (installSkills) {
377
- try {
378
- p.log.step('Installing NocoBase agent skills (nb skills install)');
379
- await installNocoBaseSkills();
380
- }
381
- catch (error) {
382
- const message = error instanceof Error ? error.message : String(error);
383
- p.outro(pc.red(`Skills install failed: ${message}`));
384
- this.error(message);
385
- }
386
- }
387
- else {
388
- p.log.info('Skipped NocoBase agent skills install.');
442
+ 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.`);
389
443
  }
444
+ await this.syncNocoBaseSkills({
445
+ skip: Boolean(normalizedFlags['skip-skills']),
446
+ });
447
+ let managedInstallResults;
390
448
  try {
391
449
  // oclif explicit registry keys use `:` (e.g. `env:add`); users still type `nb env add`.
392
450
  if (hasNocobase) {
@@ -395,15 +453,19 @@ Prompt modes:
395
453
  }
396
454
  else {
397
455
  p.log.step('Saving the local env config');
398
- await this.persistManagedEnvConfig(results);
399
- p.log.step('Running nb install');
456
+ await this.persistManagedEnvConfig(results, normalizedFlags);
457
+ managedInstallResults = results;
458
+ p.log.step('Running nb init');
400
459
  await this.config.runCommand('install', this.buildInstallArgv(results, normalizedFlags));
401
460
  }
402
461
  }
403
462
  catch (error) {
404
463
  const message = error instanceof Error ? error.message : String(error);
405
- p.outro(pc.red(message));
406
- this.error(message);
464
+ const formatted = managedInstallResults
465
+ ? this.formatManagedInstallFailureMessage(message, managedInstallResults, normalizedFlags)
466
+ : message;
467
+ p.outro(pc.red(formatted));
468
+ this.exit(1);
407
469
  }
408
470
  p.outro('Workspace init finished.');
409
471
  }
@@ -451,7 +513,6 @@ Prompt modes:
451
513
  catalog: {
452
514
  appName: c.appName,
453
515
  hasNocobase: c.hasNocobase,
454
- installSkills: c.installSkills,
455
516
  },
456
517
  },
457
518
  {
@@ -525,6 +586,22 @@ Prompt modes:
525
586
  if (flags.env !== undefined && String(flags.env).trim() !== '') {
526
587
  preset.appName = String(flags.env).trim();
527
588
  }
589
+ const apiBaseUrl = explicitApiBaseUrlFlag(flags);
590
+ if (apiBaseUrl) {
591
+ preset.hasNocobase = 'yes';
592
+ preset.apiBaseUrl = apiBaseUrl;
593
+ }
594
+ else if (flags['default-api-base-url'] !== undefined
595
+ && String(flags['default-api-base-url']).trim() !== '') {
596
+ preset.apiBaseUrl = String(flags['default-api-base-url']).trim();
597
+ }
598
+ if (flags['auth-type'] !== undefined && String(flags['auth-type']).trim() !== '') {
599
+ preset.authType = String(flags['auth-type']).trim();
600
+ }
601
+ const accessToken = String(flags['access-token'] ?? flags.token ?? '');
602
+ if (flags['access-token'] !== undefined || flags.token !== undefined) {
603
+ preset.accessToken = accessToken;
604
+ }
528
605
  if (flags.lang !== undefined && String(flags.lang).trim() !== '') {
529
606
  preset.lang = String(flags.lang).trim();
530
607
  }
@@ -557,6 +634,7 @@ Prompt modes:
557
634
  }
558
635
  if (flags['db-host'] !== undefined && String(flags['db-host']).trim() !== '') {
559
636
  preset.dbHost = String(flags['db-host']).trim();
637
+ preset.builtinDb = false;
560
638
  }
561
639
  if (flags['db-port'] !== undefined && String(flags['db-port']).trim() !== '') {
562
640
  preset.dbPort = String(flags['db-port']).trim();
@@ -577,7 +655,11 @@ Prompt modes:
577
655
  preset.source = String(flags.source).trim();
578
656
  }
579
657
  if (flags.version !== undefined) {
580
- preset.version = String(flags.version).trim() || 'latest';
658
+ const version = String(flags.version).trim() || 'latest';
659
+ preset.version = initVersionPromptValue(version);
660
+ if (preset.version === 'other') {
661
+ preset.otherVersion = version;
662
+ }
581
663
  }
582
664
  if (flags['docker-registry'] !== undefined && String(flags['docker-registry']).trim() !== '') {
583
665
  preset.dockerRegistry = String(flags['docker-registry']).trim();
@@ -609,21 +691,42 @@ Prompt modes:
609
691
  if (argvHasToken(argv, ['--build-dts', '--no-build-dts'])) {
610
692
  preset.buildDts = Boolean(flags['build-dts']);
611
693
  }
612
- if (argvHasToken(argv, ['--builtin-db'])) {
694
+ if (argvHasToken(argv, ['--builtin-db', '--no-builtin-db'])) {
613
695
  preset.builtinDb = Boolean(flags['builtin-db']);
614
696
  }
615
- if (argvHasToken(argv, ['--install-skills'])) {
616
- preset.installSkills = Boolean(flags['install-skills']);
697
+ if (explicitDbHostFlag(flags)) {
698
+ preset.builtinDb = false;
617
699
  }
618
- else if (this.hasAgentsDirInCwd()) {
619
- preset.installSkills = false;
700
+ if (!preset.hasNocobase && hasDownloadOverride(flags)) {
701
+ preset.hasNocobase = 'no';
620
702
  }
621
703
  return preset;
622
704
  }
623
705
  hasAgentsDirInCwd() {
624
706
  return existsSync(path.resolve(process.cwd(), '.agents'));
625
707
  }
626
- async persistManagedEnvConfig(results) {
708
+ async syncNocoBaseSkills(options) {
709
+ if (options?.skip) {
710
+ p.log.step('Skipped NocoBase agent skills sync.');
711
+ return;
712
+ }
713
+ try {
714
+ const status = await inspectSkillsStatus();
715
+ if (!status.installed) {
716
+ p.log.step('Installing NocoBase agent skills (nb skills install)');
717
+ await installNocoBaseSkills();
718
+ return;
719
+ }
720
+ p.log.step('Updating NocoBase agent skills (nb skills update)');
721
+ await updateNocoBaseSkills();
722
+ }
723
+ catch (error) {
724
+ const message = error instanceof Error ? error.message : String(error);
725
+ p.outro(pc.red(`Skills sync failed: ${message}`));
726
+ this.exit(1);
727
+ }
728
+ }
729
+ async persistManagedEnvConfig(results, flags = {}) {
627
730
  const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
628
731
  const appPort = String(results.appPort ?? '').trim();
629
732
  const source = String(results.source ?? '').trim();
@@ -641,8 +744,24 @@ Prompt modes:
641
744
  const dbDatabase = String(results.dbDatabase ?? '').trim();
642
745
  const dbUser = String(results.dbUser ?? '').trim();
643
746
  const dbPassword = String(results.dbPassword ?? '');
747
+ const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
748
+ const authType = String(results.authType ?? '').trim() || 'oauth';
749
+ const accessToken = String(results.accessToken ?? '');
750
+ const builtinDb = explicitDbHostFlag(flags)
751
+ ? false
752
+ : results.builtinDb === undefined
753
+ ? undefined
754
+ : Boolean(results.builtinDb);
644
755
  await upsertEnv(envName, {
645
- ...(appPort ? { baseUrl: `http://127.0.0.1:${appPort}/api` } : {}),
756
+ ...(source === 'docker'
757
+ ? { kind: 'docker' }
758
+ : source || appRootPath
759
+ ? { kind: 'local' }
760
+ : appPort
761
+ ? { kind: 'http' }
762
+ : {}),
763
+ ...(apiBaseUrl ? { apiBaseUrl } : appPort ? { apiBaseUrl: `http://127.0.0.1:${appPort}/api` } : {}),
764
+ ...(authType === 'token' && accessToken ? { accessToken } : {}),
646
765
  ...(source ? { source } : {}),
647
766
  ...(version ? { downloadVersion: version } : {}),
648
767
  ...(dockerRegistry ? { dockerRegistry } : {}),
@@ -655,20 +774,19 @@ Prompt modes:
655
774
  ...(results.devDependencies !== undefined ? { devDependencies: Boolean(results.devDependencies) } : {}),
656
775
  ...(results.build !== undefined ? { build: Boolean(results.build) } : {}),
657
776
  ...(results.buildDts !== undefined ? { buildDts: Boolean(results.buildDts) } : {}),
658
- ...(results.builtinDb !== undefined ? { builtinDb: Boolean(results.builtinDb) } : {}),
777
+ ...(builtinDb !== undefined ? { builtinDb } : {}),
659
778
  ...(dbDialect ? { dbDialect } : {}),
660
- ...(builtinDbImage || results.builtinDb === false ? { builtinDbImage: builtinDbImage || undefined } : {}),
779
+ ...(builtinDbImage || builtinDb === false ? { builtinDbImage: builtinDbImage || undefined } : {}),
661
780
  ...(dbHost ? { dbHost } : {}),
662
781
  ...(dbPort ? { dbPort } : {}),
663
782
  ...(dbDatabase ? { dbDatabase } : {}),
664
783
  ...(dbUser ? { dbUser } : {}),
665
784
  ...(dbPassword ? { dbPassword } : {}),
666
- }, { scope: CONFIG_SCOPE });
785
+ }, { scope: resolveDefaultConfigScope() });
667
786
  }
668
787
  buildEnvAddArgv(results) {
669
788
  const argv = [String(results.appName ?? DEFAULT_INIT_APP_NAME)];
670
789
  argv.push('--no-intro');
671
- argv.push('--scope', CONFIG_SCOPE);
672
790
  argv.push('--api-base-url', String(results.apiBaseUrl ?? DEFAULT_INIT_API_BASE_URL));
673
791
  argv.push('--auth-type', String(results.authType ?? 'oauth'));
674
792
  if (results.authType === 'token') {
@@ -684,6 +802,9 @@ Prompt modes:
684
802
  const processArgv = process.argv.slice(2);
685
803
  const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
686
804
  const source = String(results.source ?? '').trim();
805
+ const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
806
+ const authType = String(results.authType ?? '').trim();
807
+ const accessToken = String(results.accessToken ?? '');
687
808
  argv.push('--env', envName);
688
809
  if (options?.resume) {
689
810
  argv.push('--resume');
@@ -691,6 +812,15 @@ Prompt modes:
691
812
  if (Boolean(flags.verbose)) {
692
813
  argv.push('--verbose');
693
814
  }
815
+ if (apiBaseUrl) {
816
+ argv.push('--api-base-url', apiBaseUrl);
817
+ }
818
+ if (authType) {
819
+ argv.push('--auth-type', authType);
820
+ }
821
+ if (authType === 'token' && accessToken) {
822
+ argv.push('--access-token', accessToken);
823
+ }
694
824
  const lang = String(results.lang ?? '').trim();
695
825
  if (lang) {
696
826
  argv.push('--lang', lang);
@@ -758,10 +888,15 @@ Prompt modes:
758
888
  argv.push('--build-dts');
759
889
  }
760
890
  }
761
- const builtinDb = Boolean(results.builtinDb);
891
+ const explicitDbHost = explicitDbHostFlag(flags);
892
+ const dbHost = explicitDbHost || String(results.dbHost ?? '').trim();
893
+ const builtinDb = explicitDbHost ? false : Boolean(results.builtinDb);
762
894
  if (builtinDb) {
763
895
  argv.push('--builtin-db');
764
896
  }
897
+ else if (explicitDbHost || results.builtinDb !== undefined) {
898
+ argv.push('--no-builtin-db');
899
+ }
765
900
  const dbDialect = String(results.dbDialect ?? '').trim();
766
901
  if (dbDialect) {
767
902
  argv.push('--db-dialect', dbDialect);
@@ -770,7 +905,6 @@ Prompt modes:
770
905
  if (builtinDb && builtinDbImage) {
771
906
  argv.push('--builtin-db-image', builtinDbImage);
772
907
  }
773
- const dbHost = String(results.dbHost ?? '').trim();
774
908
  if (dbHost) {
775
909
  argv.push('--db-host', dbHost);
776
910
  }
@@ -813,8 +947,80 @@ Prompt modes:
813
947
  }
814
948
  return argv;
815
949
  }
950
+ buildManagedInstallResumeCommand(results, flags) {
951
+ const argv = ['nb', 'init'];
952
+ if (Boolean(flags.yes)) {
953
+ argv.push('--yes');
954
+ }
955
+ const envName = String(results.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME;
956
+ argv.push('--env', envName);
957
+ if (Boolean(results.fetchSource)) {
958
+ const source = String(results.source ?? '').trim();
959
+ if (source) {
960
+ argv.push('--source', source);
961
+ }
962
+ const version = resolveInitDownloadVersion(results);
963
+ if (version) {
964
+ argv.push('--version', version);
965
+ }
966
+ const outputDir = String(results.outputDir ?? '').trim();
967
+ if (outputDir && outputDir !== String(results.appRootPath ?? '').trim()) {
968
+ argv.push('--output-dir', outputDir);
969
+ }
970
+ const gitUrl = String(results.gitUrl ?? '').trim();
971
+ if (gitUrl) {
972
+ argv.push('--git-url', gitUrl);
973
+ }
974
+ const dockerRegistry = String(results.dockerRegistry ?? '').trim();
975
+ if (dockerRegistry) {
976
+ argv.push('--docker-registry', dockerRegistry);
977
+ }
978
+ const dockerPlatform = String(results.dockerPlatform ?? '').trim();
979
+ if (dockerPlatform) {
980
+ argv.push('--docker-platform', dockerPlatform);
981
+ }
982
+ const npmRegistry = String(results.npmRegistry ?? '').trim();
983
+ if (npmRegistry) {
984
+ argv.push('--npm-registry', npmRegistry);
985
+ }
986
+ if (Boolean(results.devDependencies)) {
987
+ argv.push('--dev-dependencies');
988
+ }
989
+ if (Boolean(results.dockerSave)) {
990
+ argv.push('--docker-save');
991
+ }
992
+ if (results.build !== undefined && !Boolean(results.build)) {
993
+ argv.push('--no-build');
994
+ }
995
+ if (Boolean(results.buildDts)) {
996
+ argv.push('--build-dts');
997
+ }
998
+ }
999
+ argv.push('--resume', '--verbose');
1000
+ return argv.map(shellQuoteArg).join(' ');
1001
+ }
1002
+ formatManagedInstallFailureMessage(message, results, flags) {
1003
+ const command = this.buildManagedInstallResumeCommand(results, flags);
1004
+ return [
1005
+ message,
1006
+ '',
1007
+ translateCli('commands.init.messages.resumeAfterInstallFailure', { command }),
1008
+ ].join('\n');
1009
+ }
816
1010
  buildResumeInstallArgv(flags) {
817
- return this.buildInstallArgv(this.buildPresetValuesFromFlags(flags), flags, {
1011
+ const preset = this.buildPresetValuesFromFlags(flags);
1012
+ if (flags.yes) {
1013
+ preset.lang ??= yesInitialValue(Install.appPrompts.lang, 'en-US');
1014
+ preset.rootUsername ??= yesInitialValue(Install.rootUserPrompts.rootUsername, 'nocobase');
1015
+ preset.rootEmail ??= yesInitialValue(Install.rootUserPrompts.rootEmail, 'admin@nocobase.com');
1016
+ preset.rootPassword ??= yesInitialValue(Install.rootUserPrompts.rootPassword, 'admin123');
1017
+ preset.rootNickname ??= yesInitialValue(Install.rootUserPrompts.rootNickname, 'Super Admin');
1018
+ }
1019
+ if (hasDownloadOverride(flags)) {
1020
+ preset.fetchSource ??= true;
1021
+ }
1022
+ preset.replace ??= true;
1023
+ return this.buildInstallArgv(preset, flags, {
818
1024
  nonInteractive: Boolean(flags.yes),
819
1025
  resume: true,
820
1026
  });