@nocobase/cli 2.2.0-beta.8 → 2.2.0-test.15

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 (56) hide show
  1. package/assets/env-proxy/nginx/app.conf.tpl +23 -0
  2. package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
  3. package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
  4. package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
  5. package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
  6. package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
  7. package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
  8. package/assets/env-proxy/nginx/snippets/proxy-location.conf +18 -0
  9. package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
  10. package/assets/env-proxy/nginx/snippets/uploads-location.conf +24 -0
  11. package/dist/commands/app/start.js +25 -2
  12. package/dist/commands/config/set.js +1 -0
  13. package/dist/commands/env/info.js +11 -1
  14. package/dist/commands/init.js +131 -4
  15. package/dist/commands/install.js +179 -131
  16. package/dist/commands/portal/create.js +105 -0
  17. package/dist/commands/portal/deploy.js +81 -0
  18. package/dist/commands/portal/destroy.js +104 -0
  19. package/dist/commands/portal/dev.js +71 -0
  20. package/dist/commands/portal/index.js +20 -0
  21. package/dist/commands/portal/info.js +82 -0
  22. package/dist/commands/portal/list.js +98 -0
  23. package/dist/commands/portal/pull.js +77 -0
  24. package/dist/commands/portal/push.js +79 -0
  25. package/dist/commands/proxy/caddy/generate.js +93 -7
  26. package/dist/commands/proxy/nginx/generate.js +98 -7
  27. package/dist/commands/revision/create.js +1 -1
  28. package/dist/commands/source/dev.js +1 -1
  29. package/dist/commands/source/download.js +18 -14
  30. package/dist/lib/app-managed-resources.js +3 -2
  31. package/dist/lib/auth-store.js +71 -1
  32. package/dist/lib/cli-config.js +74 -2
  33. package/dist/lib/docker-image.js +94 -6
  34. package/dist/lib/env-config.js +8 -0
  35. package/dist/lib/env-proxy-config.js +48 -0
  36. package/dist/lib/env-proxy.js +266 -61
  37. package/dist/lib/managed-init-env.js +6 -1
  38. package/dist/lib/portal-command-env.js +31 -0
  39. package/dist/lib/portal-create.js +488 -0
  40. package/dist/lib/portal-deploy.js +275 -0
  41. package/dist/lib/portal-destroy.js +100 -0
  42. package/dist/lib/portal-dev.js +79 -0
  43. package/dist/lib/portal-env-files.js +53 -0
  44. package/dist/lib/portal-info.js +31 -0
  45. package/dist/lib/portal-list.js +197 -0
  46. package/dist/lib/portal-source.js +416 -0
  47. package/dist/lib/portal-template.js +190 -0
  48. package/dist/lib/prompt-catalog-terminal.js +32 -19
  49. package/dist/lib/prompt-web-ui.js +13 -2
  50. package/dist/lib/proxy-caddy.js +77 -9
  51. package/dist/lib/proxy-nginx.js +71 -11
  52. package/dist/lib/run-npm.js +17 -16
  53. package/dist/lib/ui.js +28 -1
  54. package/dist/locale/en-US.json +192 -38
  55. package/dist/locale/zh-CN.json +192 -38
  56. package/package.json +6 -3
@@ -0,0 +1,23 @@
1
+ # Rendered by `nb env proxy`.
2
+ # Context:
3
+ # publicBasePath={{publicBasePath}}
4
+ # apiBasePath={{apiBasePath}}
5
+ # wsPath={{wsPath}}
6
+ # v2PublicPath={{v2PublicPath}}
7
+ # backendUrl={{backendUrl}}
8
+ # snippetsDir={{snippetsDir}}
9
+ # uploadsDir={{uploadsDir}}
10
+ # distRootDir={{distRootDir}}
11
+ # entryDir={{entryDir}}
12
+ # publicDir={{publicDir}}
13
+
14
+ server {
15
+ listen 80;
16
+ server_name _;
17
+
18
+ # Add custom directives or locations above the managed block as needed.
19
+
20
+ {{managedConfigBlock}}
21
+
22
+ # Add custom directives or locations below the managed block as needed.
23
+ }
@@ -0,0 +1,5 @@
1
+ # Managed by `nb env proxy`. Changes will be overwritten.
2
+
3
+ include {{snippetsDir}}/log-format-http.conf;
4
+ include {{snippetsDir}}/maps-http.conf;
5
+ include {{appConfigIncludePath}};
@@ -0,0 +1,5 @@
1
+ expires 365d;
2
+ add_header Cache-Control "public" always;
3
+
4
+ access_log off;
5
+ autoindex off;
@@ -0,0 +1,17 @@
1
+ gzip on;
2
+ gzip_types
3
+ text/plain
4
+ text/css
5
+ text/xml
6
+ text/markdown
7
+ text/javascript
8
+ application/javascript
9
+ application/json
10
+ application/manifest+json
11
+ application/atom+xml
12
+ application/rss+xml
13
+ application/xml
14
+ application/xml+rss
15
+ application/xhtml+xml
16
+ application/wasm
17
+ image/svg+xml;
@@ -0,0 +1,13 @@
1
+ log_format apm '"$time_local" client=$remote_addr '
2
+ 'method=$request_method request="$request" '
3
+ 'request_length=$request_length '
4
+ 'status=$status bytes_sent=$bytes_sent '
5
+ 'body_bytes_sent=$body_bytes_sent '
6
+ 'referer=$http_referer '
7
+ 'user_agent="$http_user_agent" '
8
+ 'upstream_addr=$upstream_addr '
9
+ 'upstream_status=$upstream_status '
10
+ 'request_time=$request_time '
11
+ 'upstream_response_time=$upstream_response_time '
12
+ 'upstream_connect_time=$upstream_connect_time '
13
+ 'upstream_header_time=$upstream_header_time';
@@ -0,0 +1,14 @@
1
+ map $http_upgrade $connection_upgrade {
2
+ default upgrade;
3
+ "" close;
4
+ }
5
+
6
+ map $http_x_forwarded_proto $upstream_x_forwarded_proto {
7
+ default $http_x_forwarded_proto;
8
+ "" $scheme;
9
+ }
10
+
11
+ map $http_host $final_host {
12
+ default $http_host;
13
+ "" $host;
14
+ }
@@ -0,0 +1,98 @@
1
+ types {
2
+ text/html html htm shtml;
3
+ text/css css;
4
+ text/xml xml;
5
+ image/gif gif;
6
+ image/jpeg jpeg jpg;
7
+ application/javascript js mjs;
8
+ application/atom+xml atom;
9
+ application/rss+xml rss;
10
+
11
+ text/mathml mml;
12
+ text/plain txt;
13
+ text/vnd.sun.j2me.app-descriptor jad;
14
+ text/vnd.wap.wml wml;
15
+ text/x-component htc;
16
+
17
+ image/avif avif;
18
+ image/png png;
19
+ image/svg+xml svg svgz;
20
+ image/tiff tif tiff;
21
+ image/vnd.wap.wbmp wbmp;
22
+ image/webp webp;
23
+ image/x-icon ico;
24
+ image/x-jng jng;
25
+ image/x-ms-bmp bmp;
26
+
27
+ font/woff woff;
28
+ font/woff2 woff2;
29
+
30
+ application/java-archive jar war ear;
31
+ application/json json;
32
+ application/mac-binhex40 hqx;
33
+ application/msword doc;
34
+ application/pdf pdf;
35
+ application/postscript ps eps ai;
36
+ application/rtf rtf;
37
+ application/vnd.apple.mpegurl m3u8;
38
+ application/vnd.google-earth.kml+xml kml;
39
+ application/vnd.google-earth.kmz kmz;
40
+ application/vnd.ms-excel xls;
41
+ application/vnd.ms-fontobject eot;
42
+ application/vnd.ms-powerpoint ppt;
43
+ application/vnd.oasis.opendocument.graphics odg;
44
+ application/vnd.oasis.opendocument.presentation odp;
45
+ application/vnd.oasis.opendocument.spreadsheet ods;
46
+ application/vnd.oasis.opendocument.text odt;
47
+ application/vnd.openxmlformats-officedocument.presentationml.presentation
48
+ pptx;
49
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
50
+ xlsx;
51
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
52
+ docx;
53
+ application/vnd.wap.wmlc wmlc;
54
+ application/wasm wasm;
55
+ application/x-7z-compressed 7z;
56
+ application/x-cocoa cco;
57
+ application/x-java-archive-diff jardiff;
58
+ application/x-java-jnlp-file jnlp;
59
+ application/x-makeself run;
60
+ application/x-perl pl pm;
61
+ application/x-pilot prc pdb;
62
+ application/x-rar-compressed rar;
63
+ application/x-redhat-package-manager rpm;
64
+ application/x-sea sea;
65
+ application/x-shockwave-flash swf;
66
+ application/x-stuffit sit;
67
+ application/x-tcl tcl tk;
68
+ application/x-x509-ca-cert der pem crt;
69
+ application/x-xpinstall xpi;
70
+ application/xhtml+xml xhtml;
71
+ application/xspf+xml xspf;
72
+ application/zip zip;
73
+
74
+ application/octet-stream bin exe dll;
75
+ application/octet-stream deb;
76
+ application/octet-stream dmg;
77
+ application/octet-stream iso img;
78
+ application/octet-stream msi msp msm;
79
+
80
+ audio/midi mid midi kar;
81
+ audio/mpeg mp3;
82
+ audio/ogg ogg;
83
+ audio/x-m4a m4a;
84
+ audio/x-realaudio ra;
85
+
86
+ video/3gpp 3gpp 3gp;
87
+ video/mp2t ts;
88
+ video/mp4 mp4;
89
+ video/mpeg mpeg mpg;
90
+ video/quicktime mov;
91
+ video/webm webm;
92
+ video/x-flv flv;
93
+ video/x-m4v m4v;
94
+ video/x-mng mng;
95
+ video/x-ms-asf asx asf;
96
+ video/x-ms-wmv wmv;
97
+ video/x-msvideo avi;
98
+ }
@@ -0,0 +1,18 @@
1
+ proxy_http_version 1.1;
2
+
3
+ proxy_set_header Upgrade $http_upgrade;
4
+ proxy_set_header Connection $connection_upgrade;
5
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
7
+ proxy_set_header Host $final_host;
8
+ proxy_set_header Referer $http_referer;
9
+ proxy_set_header User-Agent $http_user_agent;
10
+
11
+ add_header Cache-Control "no-cache, no-store" always;
12
+ proxy_cache_bypass $http_upgrade;
13
+
14
+ proxy_connect_timeout 600;
15
+ proxy_send_timeout 600;
16
+ proxy_read_timeout 600;
17
+ send_timeout 600;
18
+ proxy_buffering off;
@@ -0,0 +1,6 @@
1
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
2
+ add_header X-Robots-Tag "noindex, nofollow" always;
3
+
4
+ if_modified_since off;
5
+ expires off;
6
+ etag off;
@@ -0,0 +1,24 @@
1
+ add_header Cache-Control "public" always;
2
+ add_header Content-Security-Policy "sandbox" always;
3
+ add_header X-Content-Type-Options "nosniff" always;
4
+
5
+ access_log off;
6
+ autoindex off;
7
+
8
+ # Force potentially renderable uploaded files to download.
9
+ location ~* \.(?:htm|html|pdf|svg|svgz|xht|xhtml|xml|xsl|xslt)$ {
10
+ add_header Cache-Control "public" always;
11
+ add_header Content-Security-Policy "sandbox" always;
12
+ add_header X-Content-Type-Options "nosniff" always;
13
+ add_header Content-Disposition "attachment" always;
14
+ }
15
+
16
+ # Let Markdown files render as plain Markdown text.
17
+ location ~* \.md$ {
18
+ default_type text/markdown;
19
+
20
+ add_header Cache-Control "public" always;
21
+ add_header Content-Security-Policy "sandbox" always;
22
+ add_header X-Content-Type-Options "nosniff" always;
23
+ add_header Content-Disposition "inline" always;
24
+ }
@@ -14,6 +14,7 @@ import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBase
14
14
  import { ensureBuiltinDbReady, ensureLocalPostinstall, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
15
15
  import { clearEnvRootSetup, getEnv, upsertEnv } from '../../lib/auth-store.js';
16
16
  import { buildInitAppEnvVarsFromConfig, isPreparedSetupState } from '../../lib/managed-init-env.js';
17
+ import { prepareInitialPortalTemplate } from '../../lib/portal-template.js';
17
18
  import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
18
19
  import { readManagedRuntimeEnvValues } from '../../lib/managed-env-file.js';
19
20
  import { run } from '../../lib/run-npm.js';
@@ -37,6 +38,9 @@ function buildLicenseSyncArgv(envName, options) {
37
38
  }
38
39
  function resolveHookCommand(value) {
39
40
  const text = String(value ?? '').trim();
41
+ if (text === 'init') {
42
+ return text;
43
+ }
40
44
  if (text === 'app:restart' || text === 'app:upgrade') {
41
45
  return text;
42
46
  }
@@ -178,6 +182,19 @@ async function finalizePreparedEnv(envName) {
178
182
  setupState: 'installed',
179
183
  });
180
184
  }
185
+ async function prepareInitialPortalForRuntime(runtime, verbose) {
186
+ await prepareInitialPortalTemplate({
187
+ developmentMode: runtime.env.config?.developmentMode,
188
+ portalName: runtime.env.config?.portalName,
189
+ portalTemplate: runtime.env.config?.portalTemplate,
190
+ npmRegistry: runtime.env.config?.npmRegistry,
191
+ storagePath: runtime.env.storagePath || runtime.env.config?.storagePath,
192
+ verbose,
193
+ onStartTask: startTask,
194
+ onSucceedTask: succeedTask,
195
+ onFailTask: failTask,
196
+ });
197
+ }
181
198
  export default class AppStart extends Command {
182
199
  static hidden = false;
183
200
  static description = 'Start NocoBase for the selected env. When applicable, the CLI synchronizes licensed commercial plugins first, then prepares and starts the app or recreates the saved Docker container.';
@@ -225,7 +242,7 @@ export default class AppStart extends Command {
225
242
  }),
226
243
  'hook-command': Flags.string({
227
244
  hidden: true,
228
- options: ['app:start', 'app:restart', 'app:upgrade'],
245
+ options: ['init', 'app:start', 'app:restart', 'app:upgrade'],
229
246
  }),
230
247
  };
231
248
  async run() {
@@ -246,7 +263,7 @@ export default class AppStart extends Command {
246
263
  }
247
264
  const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
248
265
  const runtime = await resolveManagedAppRuntime(requestedEnv);
249
- const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config);
266
+ const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config, { includePortal: false });
250
267
  const isPreparedEnv = isPreparedSetupState(runtime?.env.config?.setupState);
251
268
  const hookCommand = resolveHookCommand(flags['hook-command']);
252
269
  const hookPhase = resolveAppStartHookPhase({
@@ -319,6 +336,9 @@ export default class AppStart extends Command {
319
336
  command: hookCommand,
320
337
  });
321
338
  }
339
+ if (isPreparedEnv) {
340
+ await prepareInitialPortalForRuntime(runtime, flags.verbose);
341
+ }
322
342
  await recreateSavedDockerApp(runtime, {
323
343
  initEnvVars: isPreparedEnv ? preparedInitEnvVars : undefined,
324
344
  verbose: flags.verbose,
@@ -467,6 +487,9 @@ export default class AppStart extends Command {
467
487
  command: hookCommand,
468
488
  });
469
489
  }
490
+ if (isPreparedEnv) {
491
+ await prepareInitialPortalForRuntime(runtime, flags.verbose);
492
+ }
470
493
  await runLocalNocoBaseCommand(runtime, npmArgs, {
471
494
  env: startEnv,
472
495
  stdio: commandStdio,
@@ -14,6 +14,7 @@ export default class ConfigSet extends Command {
14
14
  static description = 'Set a CLI configuration value for a supported configuration key.';
15
15
  static examples = [
16
16
  '<%= config.bin %> <%= command.id %> locale zh-CN',
17
+ '<%= config.bin %> <%= command.id %> default-portal-template git@github.com:nocobase/admin-starter.git',
17
18
  '<%= config.bin %> <%= command.id %> update.policy prompt',
18
19
  '<%= config.bin %> <%= command.id %> docker.network nocobase',
19
20
  '<%= config.bin %> <%= command.id %> docker.container-prefix nb',
@@ -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";
@@ -31,8 +32,12 @@ import EnvAdd from "./env/add.js";
31
32
  import Install, { defaultDbPortForDialect } from "./install.js";
32
33
  const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
33
34
  const DEFAULT_INIT_APP_NAME = 'local';
35
+ const DEFAULT_INIT_DEVELOPMENT_MODE = 'no-code';
36
+ const DEFAULT_INIT_PORTAL_NAME = 'admin';
37
+ const DEFAULT_INIT_PORTAL_TEMPLATE = 'git@github.com:nocobase/admin-starter.git';
34
38
  const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
35
39
  const INIT_SETUP_MODES = ['install-new', 'manage-local', 'connect-remote'];
40
+ const INIT_DEVELOPMENT_MODES = ['no-code', 'vibe-coding'];
36
41
  const INIT_ENV_ADD_FLAG_NAMES = [
37
42
  'locale',
38
43
  'default-api-base-url',
@@ -79,6 +84,9 @@ function isInstallNewSetupMode(values) {
79
84
  function isInstallLikeSetupMode(values) {
80
85
  return !isRemoteSetupMode(values);
81
86
  }
87
+ function isVibeCodingMode(values) {
88
+ return String(values.developmentMode ?? DEFAULT_INIT_DEVELOPMENT_MODE).trim() === 'vibe-coding';
89
+ }
82
90
  function remoteConnectionOnly(def) {
83
91
  return withExtraHidden(def, (values) => !isRemoteSetupMode(values));
84
92
  }
@@ -386,6 +394,43 @@ Prompt modes:
386
394
  devDependencies: installLikeDownloadExecutionOnly(Download.prompts.devDependencies),
387
395
  build: installLikeDownloadExecutionOnly(Download.prompts.build),
388
396
  buildDts: installLikeDownloadExecutionOnly(Download.prompts.buildDts),
397
+ developmentMode: installNewOnly({
398
+ type: 'select',
399
+ variant: 'radio',
400
+ message: initText('prompts.developmentMode.message'),
401
+ options: [
402
+ {
403
+ value: 'no-code',
404
+ label: initText('prompts.developmentMode.noCodeLabel'),
405
+ hint: initText('prompts.developmentMode.noCodeHint'),
406
+ },
407
+ {
408
+ value: 'vibe-coding',
409
+ label: initText('prompts.developmentMode.vibeCodingLabel'),
410
+ hint: initText('prompts.developmentMode.vibeCodingHint'),
411
+ },
412
+ ],
413
+ initialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
414
+ yesInitialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
415
+ required: true,
416
+ }),
417
+ portalName: installNewOnly({
418
+ type: 'text',
419
+ message: initText('prompts.portalName.message'),
420
+ placeholder: DEFAULT_INIT_PORTAL_NAME,
421
+ initialValue: DEFAULT_INIT_PORTAL_NAME,
422
+ yesInitialValue: DEFAULT_INIT_PORTAL_NAME,
423
+ hidden: (values) => !isVibeCodingMode(values),
424
+ required: true,
425
+ }),
426
+ portalTemplate: installNewOnly({
427
+ type: 'text',
428
+ message: initText('prompts.portalTemplate.message'),
429
+ placeholder: DEFAULT_INIT_PORTAL_TEMPLATE,
430
+ yesInitialValue: DEFAULT_INIT_PORTAL_TEMPLATE,
431
+ hidden: (values) => !isVibeCodingMode(values),
432
+ required: true,
433
+ }),
389
434
  dbDialect: installLikeOnly(Install.dbPrompts.dbDialect),
390
435
  builtinDb: installLikeOnly(Install.dbPrompts.builtinDb),
391
436
  builtinDbImage: installLikeOnly(Install.dbPrompts.builtinDbImage),
@@ -411,6 +456,13 @@ Prompt modes:
411
456
  ...Init.prompts,
412
457
  installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
413
458
  };
459
+ const defaultPortalTemplate = String(options.defaultPortalTemplate ?? '').trim();
460
+ if (defaultPortalTemplate) {
461
+ prompts.portalTemplate = {
462
+ ...prompts.portalTemplate,
463
+ yesInitialValue: defaultPortalTemplate,
464
+ };
465
+ }
414
466
  if (flags['skip-auth']) {
415
467
  const accessTokenPrompt = {
416
468
  ...EnvAdd.prompts.accessToken,
@@ -460,6 +512,16 @@ Prompt modes:
460
512
  description: 'Setup mode: install a new app, manage a local app by reusing its database, or connect a remote app',
461
513
  options: [...INIT_SETUP_MODES],
462
514
  }),
515
+ 'development-mode': Flags.string({
516
+ description: 'Initial development mode: no-code or vibe-coding',
517
+ options: [...INIT_DEVELOPMENT_MODES],
518
+ }),
519
+ 'portal-name': Flags.string({
520
+ description: 'Initial portal name when --development-mode vibe-coding is used',
521
+ }),
522
+ 'portal-template': Flags.string({
523
+ description: 'Initial portal template Git URL or local path when --development-mode vibe-coding is used',
524
+ }),
463
525
  ui: Flags.boolean({
464
526
  description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
465
527
  default: false,
@@ -577,7 +639,8 @@ Prompt modes:
577
639
  const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
578
640
  const defaultUiHost = await resolveDefaultUiHost();
579
641
  const defaultApiHost = await resolveDefaultApiHost();
580
- const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost });
642
+ const defaultPortalTemplate = String(dynamicInitialValues.portalTemplate ?? '').trim();
643
+ const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost, defaultPortalTemplate });
581
644
  if (useBrowserUi) {
582
645
  presetValues = await runPromptCatalogWebUI({
583
646
  stages: Init.buildWebUiStages(promptCatalog),
@@ -606,6 +669,7 @@ Prompt modes:
606
669
  ? { setupMode: normalizeInitSetupMode(presetValues.hasNocobase) }
607
670
  : {}),
608
671
  },
672
+ yesInitialValues: pickKeys(dynamicInitialValues, ['portalTemplate']),
609
673
  values: presetValues,
610
674
  yes: normalizedFlags.yes || useBrowserUi || !interactive,
611
675
  hooks: {
@@ -678,7 +742,9 @@ Prompt modes:
678
742
  }
679
743
  static async buildDynamicInitialValuesForInstall(flags, presetValues) {
680
744
  const out = {};
681
- if (!Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
745
+ const shouldResolveAppInitialValues = !Object.prototype.hasOwnProperty.call(presetValues, 'appPort') ||
746
+ !Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate');
747
+ if (shouldResolveAppInitialValues) {
682
748
  const appInitialValues = await Install.buildAppPromptInitialValues({
683
749
  envName: String(presetValues.appName ?? '').trim(),
684
750
  flags: {
@@ -686,17 +752,31 @@ Prompt modes:
686
752
  'app-path': flags['app-path'] ?? '',
687
753
  'app-root-path': flags['app-root-path'] ?? '',
688
754
  'storage-path': flags['storage-path'] ?? '',
755
+ 'portal-template': flags['portal-template'] ??
756
+ (Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')
757
+ ? String(presetValues.portalTemplate ?? '')
758
+ : undefined),
689
759
  },
690
760
  warnOnPortFallback: false,
691
761
  });
692
- if (appInitialValues.appPort !== undefined) {
762
+ if (appInitialValues.appPort !== undefined && !Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
693
763
  out.appPort = appInitialValues.appPort;
694
764
  }
765
+ if (appInitialValues.portalTemplate !== undefined &&
766
+ !Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')) {
767
+ out.portalTemplate = appInitialValues.portalTemplate;
768
+ }
695
769
  }
696
770
  const downloadSeed = { ...presetValues };
697
771
  if (flags.yes && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')) {
698
772
  downloadSeed.source = 'docker';
699
773
  }
774
+ const builtinDbImageRegistry = String(presetValues.dockerRegistry ?? '').trim() ||
775
+ resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
776
+ if (!Object.prototype.hasOwnProperty.call(presetValues, 'dockerRegistry')) {
777
+ out.dockerRegistry = builtinDbImageRegistry;
778
+ }
779
+ out.builtinDbImageRegistry = builtinDbImageRegistry;
700
780
  const dbInitial = await Install.buildDbPromptInitialValues({
701
781
  flags,
702
782
  downloadResults: downloadSeed,
@@ -704,6 +784,9 @@ Prompt modes:
704
784
  warnOnPortFallback: false,
705
785
  });
706
786
  for (const [key, value] of Object.entries(dbInitial)) {
787
+ if (key === 'builtinDbImage') {
788
+ continue;
789
+ }
707
790
  if (!Object.prototype.hasOwnProperty.call(presetValues, key)) {
708
791
  out[key] = value;
709
792
  }
@@ -761,6 +844,13 @@ Prompt modes:
761
844
  buildDts: c.buildDts,
762
845
  },
763
846
  },
847
+ {
848
+ sectionTitle: initText('webUi.developmentMode.title'),
849
+ sectionDescription: initText('webUi.developmentMode.description'),
850
+ catalog: {
851
+ developmentMode: c.developmentMode,
852
+ },
853
+ },
764
854
  {
765
855
  sectionTitle: initText('webUi.configureDatabase.title'),
766
856
  sectionDescription: initText('webUi.configureDatabase.description'),
@@ -861,6 +951,15 @@ Prompt modes:
861
951
  if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
862
952
  preset.appPublicPath = String(flags['app-public-path']).trim();
863
953
  }
954
+ if (flags['development-mode'] !== undefined && String(flags['development-mode']).trim() !== '') {
955
+ preset.developmentMode = String(flags['development-mode']).trim();
956
+ }
957
+ if (flags['portal-name'] !== undefined && String(flags['portal-name']).trim() !== '') {
958
+ preset.portalName = String(flags['portal-name']).trim();
959
+ }
960
+ if (flags['portal-template'] !== undefined && String(flags['portal-template']).trim() !== '') {
961
+ preset.portalTemplate = String(flags['portal-template']).trim();
962
+ }
864
963
  if (flags['root-username'] !== undefined) {
865
964
  preset.rootUsername = String(flags['root-username'] ?? '').trim();
866
965
  }
@@ -998,6 +1097,9 @@ Prompt modes:
998
1097
  const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
999
1098
  const appPort = String(results.appPort ?? '').trim();
1000
1099
  const appPublicPath = String(results.appPublicPath ?? '').trim();
1100
+ const developmentMode = String(results.developmentMode ?? '').trim();
1101
+ const portalName = String(results.portalName ?? '').trim();
1102
+ const portalTemplate = String(results.portalTemplate ?? '').trim();
1001
1103
  const source = String(results.source ?? '').trim();
1002
1104
  const version = resolveInitDownloadVersion(results);
1003
1105
  const dockerRegistry = String(results.dockerRegistry ?? '').trim();
@@ -1070,6 +1172,9 @@ Prompt modes:
1070
1172
  ...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
1071
1173
  ...(appPort ? { appPort } : {}),
1072
1174
  ...(appPublicPath ? { appPublicPath } : {}),
1175
+ ...(developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE ? { developmentMode } : {}),
1176
+ ...(portalName ? { portalName } : {}),
1177
+ ...(portalTemplate ? { portalTemplate } : {}),
1073
1178
  ...(appKey ? { appKey } : {}),
1074
1179
  ...(timeZone ? { timezone: timeZone } : {}),
1075
1180
  ...(!skipDownload && results.devDependencies !== undefined
@@ -1194,6 +1299,18 @@ Prompt modes:
1194
1299
  if (appPublicPath) {
1195
1300
  argv.push('--app-public-path', appPublicPath);
1196
1301
  }
1302
+ const developmentMode = String(results.developmentMode ?? '').trim();
1303
+ if (developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE) {
1304
+ argv.push('--development-mode', developmentMode);
1305
+ }
1306
+ const portalName = String(results.portalName ?? '').trim();
1307
+ if (portalName) {
1308
+ argv.push('--portal-name', portalName);
1309
+ }
1310
+ const portalTemplate = String(results.portalTemplate ?? '').trim();
1311
+ if (portalTemplate) {
1312
+ argv.push('--portal-template', portalTemplate);
1313
+ }
1197
1314
  if (flags.force) {
1198
1315
  argv.push('--force');
1199
1316
  }
@@ -1391,6 +1508,16 @@ Prompt modes:
1391
1508
  delete normalized.rootPassword;
1392
1509
  delete normalized.rootNickname;
1393
1510
  }
1511
+ const developmentMode = normalizeConnectionString(normalized.developmentMode) || DEFAULT_INIT_DEVELOPMENT_MODE;
1512
+ normalized.developmentMode = developmentMode;
1513
+ if (developmentMode === 'vibe-coding') {
1514
+ normalized.portalName = normalizeConnectionString(normalized.portalName) || DEFAULT_INIT_PORTAL_NAME;
1515
+ normalized.portalTemplate = normalizeConnectionString(normalized.portalTemplate);
1516
+ }
1517
+ else {
1518
+ delete normalized.portalName;
1519
+ delete normalized.portalTemplate;
1520
+ }
1394
1521
  delete normalized.installApiBaseUrl;
1395
1522
  delete normalized.installAuthType;
1396
1523
  delete normalized.installUsername;