@nocobase/cli 2.2.0-beta.9 → 2.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -170,8 +170,13 @@ export default class EnvInfo extends Command {
170
170
  'auth.accessToken': authGroup.accessToken,
171
171
  'auth.refreshToken': authGroup.refreshToken,
172
172
  };
173
+ const envGroup = {
174
+ name: runtime.envName,
175
+ kind: runtime.kind,
176
+ };
173
177
  const output = {
174
178
  ok: true,
179
+ name: runtime.envName,
175
180
  env: runtime.envName,
176
181
  kind: runtime.kind,
177
182
  app: serializeGroup(appGroup),
@@ -197,6 +202,11 @@ export default class EnvInfo extends Command {
197
202
  this.log(JSON.stringify(output, null, 2));
198
203
  return;
199
204
  }
200
- this.log([createGroupTable('App', appGroup), createGroupTable('DB', dbGroup), createGroupTable('API', apiGroup)].join('\n\n'));
205
+ this.log([
206
+ createGroupTable('Env', envGroup),
207
+ createGroupTable('App', appGroup),
208
+ createGroupTable('DB', dbGroup),
209
+ createGroupTable('API', apiGroup),
210
+ ].join('\n\n'));
201
211
  }
202
212
  }
@@ -16,7 +16,8 @@ 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
18
  import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
19
- import { resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
19
+ import { getCliConfigValue, resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
20
+ import { resolveOfficialDockerRegistry } from '../lib/docker-image.js';
20
21
  import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
21
22
  import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
22
23
  import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
@@ -697,6 +698,12 @@ Prompt modes:
697
698
  if (flags.yes && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')) {
698
699
  downloadSeed.source = 'docker';
699
700
  }
701
+ const builtinDbImageRegistry = String(presetValues.dockerRegistry ?? '').trim() ||
702
+ resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
703
+ if (!Object.prototype.hasOwnProperty.call(presetValues, 'dockerRegistry')) {
704
+ out.dockerRegistry = builtinDbImageRegistry;
705
+ }
706
+ out.builtinDbImageRegistry = builtinDbImageRegistry;
700
707
  const dbInitial = await Install.buildDbPromptInitialValues({
701
708
  flags,
702
709
  downloadResults: downloadSeed,
@@ -704,6 +711,9 @@ Prompt modes:
704
711
  warnOnPortFallback: false,
705
712
  });
706
713
  for (const [key, value] of Object.entries(dbInitial)) {
714
+ if (key === 'builtinDbImage') {
715
+ continue;
716
+ }
707
717
  if (!Object.prototype.hasOwnProperty.call(presetValues, key)) {
708
718
  out[key] = value;
709
719
  }
@@ -180,6 +180,10 @@ function defaultBuiltinDbImageForDialect(value, options) {
180
180
  function defaultDbDatabaseForDialect(value) {
181
181
  return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
182
182
  }
183
+ function supportsDbSchemaPrompt(value) {
184
+ const dialect = String(value ?? '').trim();
185
+ return dialect === 'postgres' || dialect === 'kingbase';
186
+ }
183
187
  function defaultDbHostForBuiltinDb(values) {
184
188
  return values.builtinDb ? DEFAULT_INSTALL_BUILTIN_DB_HOST : DEFAULT_INSTALL_DB_HOST;
185
189
  }
@@ -536,7 +540,9 @@ export default class Install extends Command {
536
540
  type: 'text',
537
541
  message: installText('prompts.builtinDbImage.message'),
538
542
  placeholder: installText('prompts.builtinDbImage.placeholder'),
539
- initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect),
543
+ initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect, {
544
+ registry: String(values.builtinDbImageRegistry ?? '').trim() || undefined,
545
+ }),
540
546
  hidden: (values) => !values.builtinDb || !supportsBuiltinDbDialect(values.dbDialect),
541
547
  required: true,
542
548
  },
@@ -586,7 +592,7 @@ export default class Install extends Command {
586
592
  type: 'text',
587
593
  message: installText('prompts.dbSchema.message'),
588
594
  placeholder: installText('prompts.dbSchema.placeholder'),
589
- hidden: (values) => String(values.dbDialect ?? '').trim() !== 'postgres',
595
+ hidden: (values) => !supportsDbSchemaPrompt(values.dbDialect),
590
596
  },
591
597
  dbTablePrefix: {
592
598
  type: 'text',
@@ -345,8 +345,8 @@ export default class SourceDownload extends Command {
345
345
  label: downloadText('prompts.dockerPlatform.autoLabel'),
346
346
  hint: downloadText('prompts.dockerPlatform.autoHint'),
347
347
  },
348
- { value: 'linux/amd64', label: 'linux/amd64' },
349
- { value: 'linux/arm64', label: 'linux/arm64' },
348
+ { value: 'linux/amd64', label: 'amd64' },
349
+ { value: 'linux/arm64', label: 'arm64' },
350
350
  ],
351
351
  initialValue: DEFAULT_DOCKER_PLATFORM,
352
352
  yesInitialValue: DEFAULT_DOCKER_PLATFORM,
@@ -242,7 +242,9 @@ export function getEffectiveCliConfigValue(config, key) {
242
242
  case 'docker.container-prefix':
243
243
  return trimValue(config.name) || DEFAULT_DOCKER_CONTAINER_PREFIX;
244
244
  case 'nb-image-registry':
245
- return explicit ?? DEFAULT_NB_IMAGE_REGISTRY;
245
+ return explicit ?? (resolveCliLocale(undefined, { configuredLocale: trimValue(config.settings?.locale) }) === 'zh-CN'
246
+ ? 'aliyun'
247
+ : DEFAULT_NB_IMAGE_REGISTRY);
246
248
  case 'nb-image-variant':
247
249
  return explicit ?? DEFAULT_NB_IMAGE_VARIANT;
248
250
  case 'bin.docker':
@@ -489,6 +489,7 @@ function buildNginxManagedConfigBlock(context) {
489
489
  const v2PublicPathNoTrailingSlash = trimTrailingSlash(context.v2PublicPath);
490
490
  const apiBasePathNoTrailingSlash = trimTrailingSlash(context.apiBasePath);
491
491
  const appPublicPathNoTrailingSlash = trimTrailingSlash(context.appPublicPath);
492
+ const fileAccessPath = `${context.appPublicPath}files/`;
492
493
  const isRootMounted = context.appPublicPath === '/';
493
494
  const appPublicPathRedirectBlock = isRootMounted
494
495
  ? ''
@@ -526,6 +527,20 @@ function buildNginxManagedConfigBlock(context) {
526
527
  ` include ${context.snippetsDir}/proxy-location.conf;`,
527
528
  ' }',
528
529
  '',
530
+ ` location ^~ ${fileAccessPath} {`,
531
+ ` proxy_pass ${context.backendUrl};`,
532
+ ` include ${context.snippetsDir}/proxy-location.conf;`,
533
+ ' }',
534
+ ...(!isRootMounted
535
+ ? [
536
+ '',
537
+ ' location ^~ /files/ {',
538
+ ` proxy_pass ${context.backendUrl};`,
539
+ ` include ${context.snippetsDir}/proxy-location.conf;`,
540
+ ' }',
541
+ ]
542
+ : []),
543
+ '',
529
544
  ` location = ${apiBasePathNoTrailingSlash} {`,
530
545
  ` return 308 ${context.apiBasePath}$is_args$args;`,
531
546
  ' }',
@@ -1095,6 +1110,7 @@ function buildCaddyContextCommentLines(siteAddress, context, publicDir) {
1095
1110
  }
1096
1111
  function renderCaddyAppTemplate(siteAddress, context, publicDir) {
1097
1112
  const uploadsPath = `${context.appPublicPath}storage/uploads/`;
1113
+ const fileAccessPathMatcher = toCaddyPathMatcher(`${context.appPublicPath}files/`);
1098
1114
  const distPathMatcher = toCaddyPathMatcher(context.distPath);
1099
1115
  const uploadsPathMatcher = toCaddyPathMatcher(uploadsPath);
1100
1116
  const apiPathMatcher = toCaddyPathMatcher(context.apiBasePath);
@@ -1161,7 +1177,19 @@ function renderCaddyAppTemplate(siteAddress, context, publicDir) {
1161
1177
  ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1162
1178
  ' }',
1163
1179
  '',
1164
- ' # Keep API and WS routes above the SPA fallbacks.',
1180
+ ' # Keep file, API and WS routes above the SPA fallbacks.',
1181
+ ` handle ${fileAccessPathMatcher} {`,
1182
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1183
+ ' }',
1184
+ ...(context.appPublicPath === DEFAULT_APP_PUBLIC_PATH
1185
+ ? []
1186
+ : [
1187
+ '',
1188
+ ' handle /files/* {',
1189
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1190
+ ' }',
1191
+ ]),
1192
+ '',
1165
1193
  ` handle ${apiPathMatcher} {`,
1166
1194
  ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1167
1195
  ' }',
@@ -11,6 +11,17 @@ import { exit, stdin as stdinStream, stdout as stdoutStream } from 'node:process
11
11
  import { createCliTranslate } from "./cli-locale.js";
12
12
  import { confirm, select, input, password } from "./inquirer.js";
13
13
  import { createPromptCatalogHooks, hasIvKey, isBlankText, isPromptBlockSkipped, mergedBoolean, mergedInteger, mergedPassword, mergedSelect, mergedText, resolvePromptCatalogLocale, resolvePromptText, runPromptFieldValidate, selectOptionValues, tryApplyPreset, } from "./prompt-catalog-core.js";
14
+ function buildPromptComputationSeed(catalog, initialValues) {
15
+ const catalogKeys = new Set(Object.keys(catalog));
16
+ const seed = {};
17
+ for (const [key, value] of Object.entries(initialValues)) {
18
+ if (catalogKeys.has(key) || value === undefined || value === null) {
19
+ continue;
20
+ }
21
+ seed[key] = value;
22
+ }
23
+ return seed;
24
+ }
14
25
  function adaptInquirerValidate(validate) {
15
26
  if (!validate) {
16
27
  return undefined;
@@ -113,14 +124,16 @@ export async function runPromptCatalog(catalog, options = {}) {
113
124
  const hooks = createTerminalHooks(locale, options.hooks);
114
125
  const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY && !options.yes);
115
126
  const preset = options.values ?? {};
127
+ const computationSeed = buildPromptComputationSeed(catalog, resolveIv);
116
128
  const out = {};
117
129
  const renderer = createInquirerRenderer();
118
130
  for (const [key, def] of Object.entries(catalog)) {
119
- if (isPromptBlockSkipped(def, out)) {
131
+ const valuesSoFar = { ...computationSeed, ...out };
132
+ if (isPromptBlockSkipped(def, valuesSoFar)) {
120
133
  continue;
121
134
  }
122
135
  if (tryApplyPreset(key, def, preset, out, hooks, locale)) {
123
- const errV = await runPromptFieldValidate(def, out[key], out);
136
+ const errV = await runPromptFieldValidate(def, out[key], { ...computationSeed, ...out });
124
137
  if (errV) {
125
138
  hooks.onMissingNonInteractive(errV);
126
139
  }
@@ -135,7 +148,7 @@ export async function runPromptCatalog(catalog, options = {}) {
135
148
  continue;
136
149
  }
137
150
  if (def.type === 'run') {
138
- await def.run(out, options.command);
151
+ await def.run({ ...computationSeed, ...out }, options.command);
139
152
  continue;
140
153
  }
141
154
  if (def.type === 'text') {
@@ -144,18 +157,18 @@ export async function runPromptCatalog(catalog, options = {}) {
144
157
  ? resolvePromptText(def.placeholder, locale)
145
158
  : undefined;
146
159
  if (!interactive) {
147
- const merged = mergedText(key, def, resolveIv, useYesInitial, out);
160
+ const merged = mergedText(key, def, resolveIv, useYesInitial, valuesSoFar);
148
161
  if (def.required && isBlankText(merged)) {
149
162
  hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.textRequired', { key }));
150
163
  }
151
164
  out[key] = merged;
152
- const errT = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
165
+ const errT = await runPromptFieldValidate(def, merged, { ...computationSeed, ...out, [key]: merged });
153
166
  if (errT) {
154
167
  hooks.onMissingNonInteractive(errT);
155
168
  }
156
169
  continue;
157
170
  }
158
- const merged = mergedText(key, def, promptIv, false, out);
171
+ const merged = mergedText(key, def, promptIv, false, valuesSoFar);
159
172
  const raw = await callPrompt(() => renderer.text({
160
173
  message,
161
174
  initialValue: merged,
@@ -168,7 +181,7 @@ export async function runPromptCatalog(catalog, options = {}) {
168
181
  return undefined;
169
182
  }
170
183
  const currentValue = typeof value === 'string' ? value : String(value ?? '');
171
- const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
184
+ const result = runPromptFieldValidate(def, currentValue, { ...computationSeed, ...out, [key]: currentValue });
172
185
  return result;
173
186
  },
174
187
  }), renderer, hooks);
@@ -180,7 +193,7 @@ export async function runPromptCatalog(catalog, options = {}) {
180
193
  if (!interactive) {
181
194
  const b = mergedBoolean(key, def, resolveIv, useYesInitial);
182
195
  out[key] = b;
183
- const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
196
+ const errB = await runPromptFieldValidate(def, b, { ...computationSeed, ...out, [key]: b });
184
197
  if (errB) {
185
198
  hooks.onMissingNonInteractive(errB);
186
199
  }
@@ -191,7 +204,7 @@ export async function runPromptCatalog(catalog, options = {}) {
191
204
  for (;;) {
192
205
  const raw = await callPrompt(() => renderer.confirm({ message, initialValue: merged }), renderer, hooks);
193
206
  const b = Boolean(raw);
194
- const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
207
+ const errB = await runPromptFieldValidate(def, b, { ...computationSeed, ...out, [key]: b });
195
208
  if (errB) {
196
209
  renderer.error(errB);
197
210
  continue;
@@ -224,7 +237,7 @@ export async function runPromptCatalog(catalog, options = {}) {
224
237
  : t('promptCatalog.nonInteractive.selectMissingDefault', { key }));
225
238
  }
226
239
  out[key] = merged;
227
- const errS = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
240
+ const errS = await runPromptFieldValidate(def, merged, { ...computationSeed, ...out, [key]: merged });
228
241
  if (errS) {
229
242
  hooks.onMissingNonInteractive(errS);
230
243
  }
@@ -248,7 +261,7 @@ export async function runPromptCatalog(catalog, options = {}) {
248
261
  initialValue: uiInitial,
249
262
  }), renderer, hooks);
250
263
  const picked = raw;
251
- const errS = await runPromptFieldValidate(def, picked, { ...out, [key]: picked });
264
+ const errS = await runPromptFieldValidate(def, picked, { ...computationSeed, ...out, [key]: picked });
252
265
  if (errS) {
253
266
  renderer.error(errS);
254
267
  continue;
@@ -269,13 +282,13 @@ export async function runPromptCatalog(catalog, options = {}) {
269
282
  if (def.type === 'password') {
270
283
  const message = resolvePromptText(def.message, locale, key);
271
284
  if (!interactive) {
272
- const merged = mergedPassword(key, def, resolveIv, useYesInitial);
285
+ const merged = mergedPassword(key, def, resolveIv, useYesInitial, valuesSoFar);
273
286
  if (merged === undefined) {
274
287
  if (def.required) {
275
288
  hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequired', { key }));
276
289
  }
277
290
  out[key] = '';
278
- const errPE = await runPromptFieldValidate(def, '', { ...out, [key]: '' });
291
+ const errPE = await runPromptFieldValidate(def, '', { ...computationSeed, ...out, [key]: '' });
279
292
  if (errPE) {
280
293
  hooks.onMissingNonInteractive(errPE);
281
294
  }
@@ -285,7 +298,7 @@ export async function runPromptCatalog(catalog, options = {}) {
285
298
  hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequiredNonEmpty', { key }));
286
299
  }
287
300
  out[key] = merged;
288
- const errP = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
301
+ const errP = await runPromptFieldValidate(def, merged, { ...computationSeed, ...out, [key]: merged });
289
302
  if (errP) {
290
303
  hooks.onMissingNonInteractive(errP);
291
304
  }
@@ -302,7 +315,7 @@ export async function runPromptCatalog(catalog, options = {}) {
302
315
  return undefined;
303
316
  }
304
317
  const currentValue = typeof value === 'string' ? value : String(value ?? '');
305
- const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
318
+ const result = runPromptFieldValidate(def, currentValue, { ...computationSeed, ...out, [key]: currentValue });
306
319
  return result;
307
320
  },
308
321
  }), renderer, hooks);
@@ -322,14 +335,14 @@ export async function runPromptCatalog(catalog, options = {}) {
322
335
  }
323
336
  const z = def.initialValue ?? 0;
324
337
  out[key] = z;
325
- const errI = await runPromptFieldValidate(def, z, { ...out, [key]: z });
338
+ const errI = await runPromptFieldValidate(def, z, { ...computationSeed, ...out, [key]: z });
326
339
  if (errI) {
327
340
  hooks.onMissingNonInteractive(errI);
328
341
  }
329
342
  continue;
330
343
  }
331
344
  out[key] = merged;
332
- const errI2 = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
345
+ const errI2 = await runPromptFieldValidate(def, merged, { ...computationSeed, ...out, [key]: merged });
333
346
  if (errI2) {
334
347
  hooks.onMissingNonInteractive(errI2);
335
348
  }
@@ -349,7 +362,7 @@ export async function runPromptCatalog(catalog, options = {}) {
349
362
  }
350
363
  if (def.validate) {
351
364
  const z = def.initialValue ?? 0;
352
- return runPromptFieldValidate(def, z, { ...out, [key]: z });
365
+ return runPromptFieldValidate(def, z, { ...computationSeed, ...out, [key]: z });
353
366
  }
354
367
  return undefined;
355
368
  }
@@ -360,7 +373,7 @@ export async function runPromptCatalog(catalog, options = {}) {
360
373
  return undefined;
361
374
  }
362
375
  const n = Number.parseInt(trimmed, 10);
363
- return runPromptFieldValidate(def, n, { ...out, [key]: n });
376
+ return runPromptFieldValidate(def, n, { ...computationSeed, ...out, [key]: n });
364
377
  },
365
378
  }), renderer, hooks);
366
379
  if (typeof raw === 'string' && raw.trim() === '' && !def.required) {
@@ -48,13 +48,24 @@ function isInputBlock(def) {
48
48
  def.type === 'password' ||
49
49
  def.type === 'integer');
50
50
  }
51
+ function buildPromptComputationSeed(catalog, userPreset) {
52
+ const catalogKeys = new Set(Object.keys(catalog));
53
+ const seed = {};
54
+ for (const [key, value] of Object.entries(userPreset)) {
55
+ if (catalogKeys.has(key) || value === undefined || value === null) {
56
+ continue;
57
+ }
58
+ seed[key] = value;
59
+ }
60
+ return seed;
61
+ }
51
62
  /**
52
63
  * Merges CLI/env **`userPreset`** with catalog block defaults, in the same key order and with the
53
64
  * same `hidden` / `run` semantics as {@link isPromptBlockSkipped}, so the web form can prefill
54
65
  * and reflow `hidden` fields (e.g. `integer` when `select` changes).
55
66
  */
56
67
  export function buildWebFormValuesFromCatalog(catalog, userPreset = {}) {
57
- const out = {};
68
+ const out = buildPromptComputationSeed(catalog, userPreset);
58
69
  for (const [key, def] of Object.entries(catalog)) {
59
70
  if (def.type === 'intro' || def.type === 'outro') {
60
71
  continue;
@@ -109,7 +120,7 @@ function defaultValueForInput(key, def, out) {
109
120
  * from current raw form data (e.g. after changing `select`). Matches how {@link isPromptBlockSkipped} uses `out` while iterating the catalog.
110
121
  */
111
122
  export function reflowWebFormState(catalog, raw, userSeed = {}) {
112
- const out = {};
123
+ const out = buildPromptComputationSeed(catalog, userSeed);
113
124
  const show = {};
114
125
  for (const [key, def] of Object.entries(catalog)) {
115
126
  if (def.type === 'intro' || def.type === 'outro') {
@@ -93,20 +93,20 @@
93
93
  "envAdd": {
94
94
  "prompts": {
95
95
  "name": {
96
- "message": "What would you like to call this environment?",
96
+ "message": "App environment identifier",
97
97
  "placeholder": "default"
98
98
  },
99
99
  "scope": {
100
- "message": "Where should this connection be saved?",
100
+ "message": "Connection save location",
101
101
  "globalLabel": "Global",
102
102
  "globalHint": "user-level config"
103
103
  },
104
104
  "apiBaseUrl": {
105
- "message": "What is the API base URL?",
105
+ "message": "API base URL",
106
106
  "placeholder": "https://demo.example.com/api or https://demo.example.com/api/__app/<subapp>"
107
107
  },
108
108
  "authType": {
109
- "message": "Which authentication method would you like to use?",
109
+ "message": "Authentication method",
110
110
  "basicLabel": "Basic authentication (username + password)",
111
111
  "basicHint": "uses your credentials to fetch a token after save",
112
112
  "oauthLabel": "OAuth (browser authentication)",
@@ -114,14 +114,14 @@
114
114
  "tokenLabel": "API token / API key"
115
115
  },
116
116
  "username": {
117
- "message": "Enter the username for basic login",
117
+ "message": "Basic login username",
118
118
  "placeholder": "admin"
119
119
  },
120
120
  "password": {
121
- "message": "Enter the password for basic login"
121
+ "message": "Basic login password"
122
122
  },
123
123
  "accessToken": {
124
- "message": "Enter an API token or API key",
124
+ "message": "API token or API key",
125
125
  "placeholder": "Enter your API token / API key"
126
126
  }
127
127
  }
@@ -179,7 +179,7 @@
179
179
  },
180
180
  "prompts": {
181
181
  "provideMethod": {
182
- "message": "How do you want to provide the license key?",
182
+ "message": "License key input method",
183
183
  "keyOption": "Paste the license key",
184
184
  "fileOption": "Read the key from a file"
185
185
  },
@@ -252,7 +252,7 @@
252
252
  },
253
253
  "prompts": {
254
254
  "source": {
255
- "message": "How would you like to get NocoBase?",
255
+ "message": "NocoBase install source",
256
256
  "dockerLabel": "Docker install (Recommended)",
257
257
  "dockerHint": "Best for no-code and low-maintenance setups. You can upgrade later by pulling a newer image and restarting the app.",
258
258
  "npmLabel": "create-nocobase-app install",
@@ -261,7 +261,7 @@
261
261
  "gitHint": "Best when you want to try the latest unreleased changes, contribute code, or debug and modify NocoBase source directly. This option is more developer-oriented."
262
262
  },
263
263
  "version": {
264
- "message": "Which version would you like to use?",
264
+ "message": "NocoBase version",
265
265
  "latestLabel": "latest",
266
266
  "latestHint": "Stable release. Best for production use and the most predictable experience.",
267
267
  "betaLabel": "beta",
@@ -272,15 +272,15 @@
272
272
  "otherHint": "Enter another package version, Docker tag, or Git ref manually, such as a branch name."
273
273
  },
274
274
  "otherVersion": {
275
- "message": "Enter the version, Docker tag, or Git ref you want to use.",
275
+ "message": "Custom version, Docker tag, or Git ref",
276
276
  "placeholder": "For example: fix/cli-v2"
277
277
  },
278
278
  "dockerRegistry": {
279
- "message": "Which Docker registry would you like to use? The image tag is set separately in Version.",
279
+ "message": "Docker registry (image tag is set in Version)",
280
280
  "placeholder": "nocobase/nocobase"
281
281
  },
282
282
  "dockerPlatform": {
283
- "message": "Which Docker image platform should be used?",
283
+ "message": "Architecture",
284
284
  "autoLabel": "Auto",
285
285
  "autoHint": "Use Docker default for this machine"
286
286
  },
@@ -323,79 +323,79 @@
323
323
  },
324
324
  "prompts": {
325
325
  "env": {
326
- "message": "What would you like to call this app?",
326
+ "message": "App environment identifier",
327
327
  "placeholder": "local"
328
328
  },
329
329
  "lang": {
330
- "message": "Which language would you like to use for the app?"
330
+ "message": "App language"
331
331
  },
332
332
  "appPath": {
333
- "message": "Where should this app be stored? (relative to {{root}})",
333
+ "message": "App directory (relative to {{root}})",
334
334
  "placeholder": "./<env>/"
335
335
  },
336
336
  "appPort": {
337
- "message": "Which port should this app use?",
337
+ "message": "App port",
338
338
  "placeholder": "13000"
339
339
  },
340
340
  "appPublicPath": {
341
- "message": "What is the app subpath? (for example, /nocobase/)",
341
+ "message": "App subpath (for example, /nocobase/)",
342
342
  "placeholder": "/ or /nocobase/"
343
343
  },
344
344
  "storagePath": {
345
- "message": "Where should uploads and local files be stored?",
345
+ "message": "Uploads and local files directory",
346
346
  "placeholder": "./<env>/storage/"
347
347
  },
348
348
  "dbDialect": {
349
- "message": "Which database would you like to use?"
349
+ "message": "Database type"
350
350
  },
351
351
  "builtinDb": {
352
- "message": "Would you like to use the built-in database?"
352
+ "message": "Use built-in database"
353
353
  },
354
354
  "builtinDbImage": {
355
- "message": "Which Docker image should be used for the built-in database?",
355
+ "message": "Built-in database Docker image",
356
356
  "placeholder": "postgres:16"
357
357
  },
358
358
  "dbHost": {
359
- "message": "What is the database host?",
359
+ "message": "Database host",
360
360
  "placeholder": "127.0.0.1"
361
361
  },
362
362
  "dbPort": {
363
- "message": "What is the database port?",
363
+ "message": "Database port",
364
364
  "placeholder": "5432"
365
365
  },
366
366
  "dbDatabase": {
367
- "message": "What is the database name?"
367
+ "message": "Database name"
368
368
  },
369
369
  "dbUser": {
370
- "message": "What is the database username?"
370
+ "message": "Database username"
371
371
  },
372
372
  "dbPassword": {
373
- "message": "What is the database password?"
373
+ "message": "Database password"
374
374
  },
375
375
  "dbSchema": {
376
- "message": "What is the database schema? (PostgreSQL only, optional)",
376
+ "message": "Database schema (PostgreSQL/KingbaseES only, optional)",
377
377
  "placeholder": "Leave empty to use the default schema"
378
378
  },
379
379
  "dbTablePrefix": {
380
- "message": "What table prefix should be used? (optional)",
380
+ "message": "Database table prefix (optional)",
381
381
  "placeholder": "For example: nb_"
382
382
  },
383
383
  "dbUnderscored": {
384
- "message": "Use underscored names for database tables and columns?"
384
+ "message": "Use underscored names for database tables and columns"
385
385
  },
386
386
  "rootUsername": {
387
- "message": "Choose the initial admin username",
387
+ "message": "Initial admin username",
388
388
  "placeholder": "nocobase"
389
389
  },
390
390
  "rootEmail": {
391
- "message": "What is the initial admin email?",
391
+ "message": "Initial admin email",
392
392
  "placeholder": "admin@nocobase.com"
393
393
  },
394
394
  "rootPassword": {
395
- "message": "Choose the initial admin password"
395
+ "message": "Initial admin password"
396
396
  },
397
397
  "rootNickname": {
398
- "message": "What display name should the initial admin use?",
398
+ "message": "Initial admin display name",
399
399
  "placeholder": "Super Admin"
400
400
  }
401
401
  }
@@ -419,11 +419,11 @@
419
419
  },
420
420
  "prompts": {
421
421
  "appName": {
422
- "message": "What should this environment use for `--env`?",
422
+ "message": "Unique app environment identifier (used by the CLI to locate and manage this environment)",
423
423
  "placeholder": "local"
424
424
  },
425
425
  "setupMode": {
426
- "message": "How would you like to set up this environment?",
426
+ "message": "App environment setup method",
427
427
  "installNewLabel": "Install a new app",
428
428
  "installNewHint": "Install a brand-new app from scratch. Best for first-time setup, evaluation, or a fresh local environment.",
429
429
  "manageLocalLabel": "Manage an app already on this machine",
@@ -432,7 +432,7 @@
432
432
  "connectRemoteHint": "Save only the remote app connection. Nothing will be installed or taken over on this machine."
433
433
  },
434
434
  "installSkills": {
435
- "message": "Install NocoBase AI coding skills (nocobase/skills)?"
435
+ "message": "Install NocoBase AI coding skills (nocobase/skills)"
436
436
  },
437
437
  "apiBaseUrl": {
438
438
  "message": "API base URL",
@@ -93,20 +93,20 @@
93
93
  "envAdd": {
94
94
  "prompts": {
95
95
  "name": {
96
- "message": "你想如何命名这个环境?",
96
+ "message": "应用环境标识",
97
97
  "placeholder": "default"
98
98
  },
99
99
  "scope": {
100
- "message": "这个连接要保存到哪里?",
100
+ "message": "连接保存位置",
101
101
  "globalLabel": "全局",
102
102
  "globalHint": "保存在用户级配置中"
103
103
  },
104
104
  "apiBaseUrl": {
105
- "message": "API 基础地址是什么?",
105
+ "message": "API 基础地址",
106
106
  "placeholder": "https://demo.example.com/api 或 https://demo.example.com/api/__app/<subapp>"
107
107
  },
108
108
  "authType": {
109
- "message": "你想使用哪种认证方式?",
109
+ "message": "认证方式",
110
110
  "basicLabel": "Basic 认证(用户名 + 密码)",
111
111
  "basicHint": "保存后会用用户名和密码换取 Token",
112
112
  "oauthLabel": "OAuth(浏览器认证)",
@@ -114,14 +114,14 @@
114
114
  "tokenLabel": "API Token / API Key"
115
115
  },
116
116
  "username": {
117
- "message": "请输入 Basic 登录用户名",
117
+ "message": "Basic 登录用户名",
118
118
  "placeholder": "admin"
119
119
  },
120
120
  "password": {
121
- "message": "请输入 Basic 登录密码"
121
+ "message": "Basic 登录密码"
122
122
  },
123
123
  "accessToken": {
124
- "message": "请输入 API Token 或 API Key",
124
+ "message": "API Token 或 API Key",
125
125
  "placeholder": "请输入你的 API Token / API Key"
126
126
  }
127
127
  }
@@ -179,7 +179,7 @@
179
179
  },
180
180
  "prompts": {
181
181
  "provideMethod": {
182
- "message": "你想通过哪种方式提供 license key?",
182
+ "message": "License key 提供方式",
183
183
  "keyOption": "直接粘贴 license key",
184
184
  "fileOption": "从文件读取 key"
185
185
  },
@@ -252,7 +252,7 @@
252
252
  },
253
253
  "prompts": {
254
254
  "source": {
255
- "message": "你想通过哪种方式获取 NocoBase?",
255
+ "message": "NocoBase 获取方式",
256
256
  "dockerLabel": "Docker 安装(推荐)",
257
257
  "dockerHint": "适合无代码或低维护成本场景。后续升级时,拉取新镜像并重启应用即可。",
258
258
  "npmLabel": "create-nocobase-app 安装",
@@ -261,7 +261,7 @@
261
261
  "gitHint": "适合体验最新未发布版本、参与贡献,或直接修改和调试 NocoBase 源码。这个方式更偏向开发者使用。"
262
262
  },
263
263
  "version": {
264
- "message": "你想使用哪个版本?",
264
+ "message": "NocoBase 版本",
265
265
  "latestLabel": "latest",
266
266
  "latestHint": "稳定版。适合生产环境和希望获得稳定体验的场景。",
267
267
  "betaLabel": "beta",
@@ -272,15 +272,15 @@
272
272
  "otherHint": "手动填写其他版本号、Docker tag 或 Git ref,例如分支名。"
273
273
  },
274
274
  "otherVersion": {
275
- "message": "请输入你想使用的版本号、Docker tag 或 Git ref",
275
+ "message": "自定义版本号、Docker tag 或 Git ref",
276
276
  "placeholder": "例如:fix/cli-v2"
277
277
  },
278
278
  "dockerRegistry": {
279
- "message": "你想使用哪个 Docker registry?镜像 tag 请单独在 Version 中填写。",
279
+ "message": "Docker registry(镜像 tag 在版本中单独设置)",
280
280
  "placeholder": "registry.cn-shanghai.aliyuncs.com/nocobase/nocobase"
281
281
  },
282
282
  "dockerPlatform": {
283
- "message": "要使用哪个 Docker 镜像平台?",
283
+ "message": "架构",
284
284
  "autoLabel": "自动",
285
285
  "autoHint": "由 Docker 根据当前机器自动选择"
286
286
  },
@@ -323,79 +323,79 @@
323
323
  },
324
324
  "prompts": {
325
325
  "env": {
326
- "message": "你想如何命名这个应用?",
326
+ "message": "应用环境标识",
327
327
  "placeholder": "local"
328
328
  },
329
329
  "lang": {
330
- "message": "你希望应用使用哪种语言?"
330
+ "message": "应用语言"
331
331
  },
332
332
  "appPath": {
333
- "message": "应用要放到哪里?(相对路径基于 {{root}})",
333
+ "message": "应用目录(相对路径基于 {{root}})",
334
334
  "placeholder": "./<env>/"
335
335
  },
336
336
  "appPort": {
337
- "message": "应用要使用哪个端口?",
337
+ "message": "应用端口",
338
338
  "placeholder": "13000"
339
339
  },
340
340
  "appPublicPath": {
341
- "message": "应用的子路径是什么?(例如 /nocobase/)",
341
+ "message": "应用子路径(例如 /nocobase/)",
342
342
  "placeholder": "/ 或 /nocobase/"
343
343
  },
344
344
  "storagePath": {
345
- "message": "上传文件和本地存储目录要放到哪里?",
345
+ "message": "上传文件和本地存储目录",
346
346
  "placeholder": "./<env>/storage/"
347
347
  },
348
348
  "dbDialect": {
349
- "message": "你想使用哪种数据库?"
349
+ "message": "数据库类型"
350
350
  },
351
351
  "builtinDb": {
352
- "message": "是否使用内置数据库?"
352
+ "message": "使用内置数据库"
353
353
  },
354
354
  "builtinDbImage": {
355
- "message": "内置数据库要使用哪个 Docker 镜像?",
355
+ "message": "内置数据库 Docker 镜像",
356
356
  "placeholder": "postgres:16"
357
357
  },
358
358
  "dbHost": {
359
- "message": "数据库主机地址是什么?",
359
+ "message": "数据库主机地址",
360
360
  "placeholder": "127.0.0.1"
361
361
  },
362
362
  "dbPort": {
363
- "message": "数据库端口是什么?",
363
+ "message": "数据库端口",
364
364
  "placeholder": "5432"
365
365
  },
366
366
  "dbDatabase": {
367
- "message": "数据库名称是什么?"
367
+ "message": "数据库名称"
368
368
  },
369
369
  "dbUser": {
370
- "message": "数据库用户名是什么?"
370
+ "message": "数据库用户名"
371
371
  },
372
372
  "dbPassword": {
373
- "message": "数据库密码是什么?"
373
+ "message": "数据库密码"
374
374
  },
375
375
  "dbSchema": {
376
- "message": "数据库 schema 是什么?(仅 PostgreSQL,可选)",
376
+ "message": "数据库 schema(仅 PostgreSQL/KingbaseES,可选)",
377
377
  "placeholder": "留空则使用默认 schema"
378
378
  },
379
379
  "dbTablePrefix": {
380
- "message": "要使用什么数据表前缀?(可选)",
380
+ "message": "数据表前缀(可选)",
381
381
  "placeholder": "例如:nb_"
382
382
  },
383
383
  "dbUnderscored": {
384
- "message": "数据库表名和字段名是否使用下划线风格?"
384
+ "message": "数据库表名和字段名使用下划线风格"
385
385
  },
386
386
  "rootUsername": {
387
- "message": "设置初始管理员用户名",
387
+ "message": "初始管理员用户名",
388
388
  "placeholder": "nocobase"
389
389
  },
390
390
  "rootEmail": {
391
- "message": "初始管理员邮箱是什么?",
391
+ "message": "初始管理员邮箱",
392
392
  "placeholder": "admin@nocobase.com"
393
393
  },
394
394
  "rootPassword": {
395
- "message": "设置初始管理员密码"
395
+ "message": "初始管理员密码"
396
396
  },
397
397
  "rootNickname": {
398
- "message": "初始管理员显示名称是什么?",
398
+ "message": "初始管理员显示名称",
399
399
  "placeholder": "Super Admin"
400
400
  }
401
401
  }
@@ -419,11 +419,11 @@
419
419
  },
420
420
  "prompts": {
421
421
  "appName": {
422
- "message": "这个环境要使用什么 `--env` 标识?",
422
+ "message": "应用环境唯一标识(CLI 将通过该标识定位并操作对应环境)",
423
423
  "placeholder": "local"
424
424
  },
425
425
  "setupMode": {
426
- "message": "你想用哪种方式配置这个环境?",
426
+ "message": "应用环境配置方式",
427
427
  "installNewLabel": "新安装",
428
428
  "installNewHint": "从零安装一个新的应用,适合首次部署、试用或新建本地开发环境。",
429
429
  "manageLocalLabel": "本机接管",
@@ -432,7 +432,7 @@
432
432
  "connectRemoteHint": "只保存远程应用的连接信息,不在本机安装或接管应用。"
433
433
  },
434
434
  "installSkills": {
435
- "message": "是否安装 NocoBase AI coding skills(nocobase/skills)?"
435
+ "message": "安装 NocoBase AI coding skills(nocobase/skills"
436
436
  },
437
437
  "apiBaseUrl": {
438
438
  "message": "API 基础地址",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.2.0-beta.9",
3
+ "version": "2.3.0-alpha.1",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",
@@ -144,5 +144,5 @@
144
144
  "type": "git",
145
145
  "url": "git+https://github.com/nocobase/nocobase.git"
146
146
  },
147
- "gitHead": "60e3d7abbaa0c7cead76f71a4f3d5eedb6b8acdb"
147
+ "gitHead": "2377df8ceb12549149017f7f14a61207bf6e49a2"
148
148
  }