@nocobase/cli 2.1.0-alpha.32 → 2.1.0-alpha.33

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.
package/bin/run.js CHANGED
@@ -5,6 +5,7 @@ import fs from 'node:fs';
5
5
  import { createRequire } from 'node:module';
6
6
  import path from 'node:path';
7
7
  import { fileURLToPath, pathToFileURL } from 'node:url';
8
+ import { normalizeSessionEnv } from './session-env.js';
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
11
  const requireFromCli = createRequire(import.meta.url);
@@ -16,6 +17,8 @@ if (process.env.NB_CLI_USE_DIST === '1') {
16
17
  isDev = false;
17
18
  }
18
19
 
20
+ normalizeSessionEnv();
21
+
19
22
  /**
20
23
  * In the monorepo, plain `node` cannot load `.ts`. Re-exec once with `--import <tsx>`
21
24
  * (same effect as a dedicated dev entry with `#!/usr/bin/env -S node --import tsx`).
@@ -0,0 +1,27 @@
1
+ const SESSION_ENV_SOURCES = [
2
+ 'CODEX_THREAD_ID',
3
+ 'OPENCODE_RUN_ID',
4
+ 'COPILOT_AGENT_SESSION_ID',
5
+ 'CLAUDE_CODE_SESSION_ID',
6
+ ];
7
+
8
+ export function resolveNormalizedSessionId(env = process.env) {
9
+ for (const key of SESSION_ENV_SOURCES) {
10
+ const value = String(env[key] ?? '').trim();
11
+ if (value) {
12
+ return value;
13
+ }
14
+ }
15
+
16
+ return undefined;
17
+ }
18
+
19
+ export function normalizeSessionEnv(env = process.env) {
20
+ const sessionId = resolveNormalizedSessionId(env);
21
+ if (!sessionId) {
22
+ return undefined;
23
+ }
24
+
25
+ env.NB_SESSION_ID = sessionId;
26
+ return sessionId;
27
+ }
@@ -160,7 +160,7 @@ export default class AppDown extends Command {
160
160
  }),
161
161
  yes: Flags.boolean({
162
162
  char: 'y',
163
- description: 'Skip the interactive cross-env confirmation prompt',
163
+ description: 'Confirm using --env when it targets a different env than the current env',
164
164
  default: false,
165
165
  }),
166
166
  force: Flags.boolean({
@@ -12,9 +12,9 @@ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNo
12
12
  import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
13
13
  import { deriveBuiltinDbConnection } from '../../lib/builtin-db.js';
14
14
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
15
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
15
16
  import { commandSucceeds, run } from '../../lib/run-npm.js';
16
17
  import { announceTargetEnv, failTask, printInfo, startTask, stopTask, succeedTask, updateTask } from '../../lib/ui.js';
17
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
18
18
  const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
19
19
  const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
20
20
  const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
@@ -361,7 +361,10 @@ export default class AppUpgrade extends Command {
361
361
  if (missing.length > 0) {
362
362
  throw new Error(`The saved Docker settings for "${runtime.envName}" are incomplete. Missing: ${missing.join(', ')}. Re-run \`nb init\` or \`nb env add\` to refresh this env config.`);
363
363
  }
364
- const imageRef = `${dockerRegistry}:${downloadVersion}`;
364
+ const imageRef = resolveDockerImageRef(dockerRegistry, downloadVersion, {
365
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
366
+ defaultVersion: DEFAULT_DOCKER_VERSION,
367
+ });
365
368
  const args = [
366
369
  'run',
367
370
  '-d',
@@ -10,10 +10,9 @@ import { Command, Flags } from '@oclif/core';
10
10
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
11
11
  import { resolveBuiltinDbConnection } from '../../lib/builtin-db.js';
12
12
  import { checkExternalDbConnection, formatDbCheckAddress, readExternalDbConnectionConfig, } from "../../lib/db-connection-check.js";
13
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
13
14
  import { commandOutput } from '../../lib/run-npm.js';
14
15
  import { validateTcpPort } from "../../lib/prompt-validators.js";
15
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
16
- const DEFAULT_DOCKER_VERSION = 'alpha';
17
16
  function trimValue(value) {
18
17
  const text = String(value ?? '').trim();
19
18
  return text || undefined;
@@ -133,7 +132,10 @@ async function runExplicitDbCheck(command, dbConfig) {
133
132
  async function runDockerDbCheck(command, runtime, dbConfig) {
134
133
  const connectionConfig = buildConnectionConfigOrThrow(command, dbConfig);
135
134
  const config = runtime.env.config ?? {};
136
- const imageRef = `${trimValue(config.dockerRegistry) || DEFAULT_DOCKER_REGISTRY}:${trimValue(config.downloadVersion) || DEFAULT_DOCKER_VERSION}`;
135
+ const imageRef = resolveDockerImageRef(config.dockerRegistry, config.downloadVersion, {
136
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
137
+ defaultVersion: DEFAULT_DOCKER_VERSION,
138
+ });
137
139
  const args = [
138
140
  'run',
139
141
  '--rm',
@@ -185,7 +187,7 @@ export default class DbCheck extends Command {
185
187
  static flags = {
186
188
  env: Flags.string({
187
189
  char: 'e',
188
- description: 'CLI env name to read saved database settings from. Defaults to the current env when omitted.',
190
+ description: 'CLI env name to read saved database settings from. Defaults to the current env when omitted',
189
191
  }),
190
192
  'db-dialect': Flags.string({
191
193
  description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
@@ -20,7 +20,7 @@ export default class DbPs extends Command {
20
20
  static flags = {
21
21
  env: Flags.string({
22
22
  char: 'e',
23
- description: 'CLI env name to inspect. Omit to show all configured envs',
23
+ description: 'CLI env name to inspect built-in database status for. Omit to show all configured envs',
24
24
  }),
25
25
  };
26
26
  async run() {
@@ -55,7 +55,7 @@ export default class EnvAdd extends Command {
55
55
  ];
56
56
  static args = {
57
57
  name: Args.string({
58
- description: 'Label for this environment (optional first argument; in a TTY, prompted when omitted; required when not using a TTY)',
58
+ description: 'Environment name to save (optional first argument; in a TTY, prompted when omitted; required when not using a TTY)',
59
59
  required: false,
60
60
  }),
61
61
  };
@@ -19,7 +19,7 @@ export default class EnvAuth extends Command {
19
19
  ];
20
20
  static args = {
21
21
  name: Args.string({
22
- description: 'Environment name (omit to use the current env)',
22
+ description: 'Configured environment name to sign in to. Defaults to the current env when omitted',
23
23
  required: false,
24
24
  }),
25
25
  };
@@ -50,18 +50,19 @@ export default class EnvInfo extends Command {
50
50
  '<%= config.bin %> <%= command.id %> app1',
51
51
  '<%= config.bin %> <%= command.id %> app1 --json',
52
52
  '<%= config.bin %> <%= command.id %> app1 --show-secrets',
53
- '<%= config.bin %> <%= command.id %> --env app1',
54
53
  ];
55
54
  static args = {
56
55
  name: Args.string({
57
- description: 'CLI env name to inspect. Defaults to the current env when omitted',
56
+ description: 'Configured environment name to inspect. Defaults to the current env when omitted',
58
57
  required: false,
59
58
  }),
60
59
  };
61
60
  static flags = {
62
61
  env: Flags.string({
63
62
  char: 'e',
64
- description: 'CLI env name to inspect. Defaults to the current env when omitted',
63
+ hidden: true,
64
+ deprecated: true,
65
+ description: 'Environment name (same as the optional positional argument; for compatibility with -e/--env on other commands)',
65
66
  }),
66
67
  json: Flags.boolean({
67
68
  description: 'Output the result as JSON',
@@ -29,7 +29,7 @@ export default class EnvRemove extends Command {
29
29
  };
30
30
  static args = {
31
31
  name: Args.string({
32
- description: 'Configured environment name',
32
+ description: 'Configured environment name to remove',
33
33
  required: true,
34
34
  }),
35
35
  };
@@ -22,7 +22,7 @@ export default class EnvStatus extends Command {
22
22
  ];
23
23
  static args = {
24
24
  name: Args.string({
25
- description: 'Configured environment name. Defaults to the current env when omitted',
25
+ description: 'Configured environment name to inspect. Defaults to the current env when omitted; cannot be used with --all',
26
26
  required: false,
27
27
  }),
28
28
  };
@@ -22,7 +22,7 @@ export default class EnvUpdate extends Command {
22
22
  ];
23
23
  static args = {
24
24
  name: Args.string({
25
- description: 'Environment name (omit to use the current env)',
25
+ description: 'Configured environment name to refresh. Defaults to the current env when omitted',
26
26
  required: false,
27
27
  }),
28
28
  };
@@ -18,7 +18,7 @@ export default class EnvUse extends Command {
18
18
  ];
19
19
  static args = {
20
20
  name: Args.string({
21
- description: 'Configured environment name',
21
+ description: 'Configured environment name to switch to',
22
22
  required: true,
23
23
  }),
24
24
  };
@@ -19,6 +19,7 @@ import { applyCliLocale, localeText, resolveCliLocale, translateCli, } from "../
19
19
  import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
20
20
  import { defaultDockerContainerPrefix, defaultDockerNetworkName, } from '../lib/app-runtime.js';
21
21
  import { resolveDockerContainerPrefix, resolveDockerNetworkName, } from '../lib/cli-config.js';
22
+ import { DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../lib/docker-image.js";
22
23
  import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
23
24
  import { validateExternalDbConfig } from "../lib/db-connection-check.js";
24
25
  import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
@@ -1561,7 +1562,11 @@ export default class Install extends Command {
1561
1562
  static buildDockerAppPlan(params) {
1562
1563
  const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim()
1563
1564
  || defaultDockerRegistryForLang(process.env.NB_LOCALE);
1564
- const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || 'latest';
1565
+ const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
1566
+ const imageRef = resolveDockerImageRef(dockerRegistry, version, {
1567
+ defaultRegistry: defaultDockerRegistryForLang(process.env.NB_LOCALE),
1568
+ defaultVersion: DEFAULT_DOCKER_VERSION,
1569
+ });
1565
1570
  const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
1566
1571
  const storagePath = resolveConfiguredEnvPath(String(params.appResults.storagePath ?? '').trim()
1567
1572
  || defaultInstallStoragePath(params.envName))
@@ -1596,12 +1601,12 @@ export default class Install extends Command {
1596
1601
  for (const [key, value] of Object.entries(initEnvVars)) {
1597
1602
  args.push('-e', `${key}=${value}`);
1598
1603
  }
1599
- args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:/app/nocobase/storage`, `${dockerRegistry}:${version}`);
1604
+ args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:/app/nocobase/storage`, imageRef);
1600
1605
  return {
1601
1606
  source: 'docker',
1602
1607
  networkName: params.networkName,
1603
1608
  containerName,
1604
- imageRef: `${dockerRegistry}:${version}`,
1609
+ imageRef,
1605
1610
  appPort,
1606
1611
  storagePath,
1607
1612
  appKey,
@@ -10,7 +10,7 @@ import * as p from '@clack/prompts';
10
10
  import { Command, Flags } from '@oclif/core';
11
11
  import { readFile } from 'node:fs/promises';
12
12
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
13
- import { ensureInstanceId, licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, redactLicenseKey, requireLicenseRuntime, resolveLicenseKeyFile, resolveLicenseServiceUrl, saveLicenseKey, sanitizeLicenseOutput, validateLicenseKey, } from './shared.js';
13
+ import { createLicenseEnvFlag, ensureInstanceId, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, redactLicenseKey, requireLicenseRuntime, resolveLicenseKeyFile, resolveLicenseServiceUrl, saveLicenseKey, sanitizeLicenseOutput, validateLicenseKey, } from './shared.js';
14
14
  import { announceTargetEnv, isInteractiveTerminal } from '../../lib/ui.js';
15
15
  import { appUrl } from '../env/shared.js';
16
16
  function resolveOnlineInputValue(value) {
@@ -171,7 +171,7 @@ export default class LicenseActivate extends Command {
171
171
  '<%= config.bin %> <%= command.id %> --env app1 --json --key-file ./license.txt',
172
172
  ];
173
173
  static flags = {
174
- env: licenseEnvFlag,
174
+ env: createLicenseEnvFlag('CLI env name to activate a license for. Defaults to the current env when omitted'),
175
175
  json: licenseJsonFlag,
176
176
  key: Flags.string({
177
177
  description: 'Existing license key to activate',
@@ -193,10 +193,7 @@ export default class LicenseActivate extends Command {
193
193
  description: 'Application name for online activation',
194
194
  }),
195
195
  'pkg-url': licensePkgUrlFlag,
196
- yes: Flags.boolean({
197
- description: 'Skip the interactive cross-env confirmation prompt',
198
- default: false,
199
- }),
196
+ yes: licenseYesFlag,
200
197
  };
201
198
  async run() {
202
199
  const { flags } = await this.parse(LicenseActivate);
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { Command, Flags } from '@oclif/core';
10
10
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
11
- import { generateAndSaveInstanceId, licenseEnvFlag, licenseJsonFlag, readSavedInstanceId, requireLicenseRuntime, resolveInstanceIdFile, } from './shared.js';
11
+ import { createLicenseEnvFlag, generateAndSaveInstanceId, licenseJsonFlag, licenseYesFlag, readSavedInstanceId, requireLicenseRuntime, resolveInstanceIdFile, } from './shared.js';
12
12
  import { announceTargetEnv } from '../../lib/ui.js';
13
13
  export default class LicenseId extends Command {
14
14
  static summary = 'Show the instance ID for the selected env';
@@ -20,12 +20,9 @@ export default class LicenseId extends Command {
20
20
  '<%= config.bin %> <%= command.id %> --env app1 --json',
21
21
  ];
22
22
  static flags = {
23
- env: licenseEnvFlag,
23
+ env: createLicenseEnvFlag('CLI env name to inspect. Defaults to the current env when omitted'),
24
24
  json: licenseJsonFlag,
25
- yes: Flags.boolean({
26
- description: 'Skip the interactive cross-env confirmation prompt',
27
- default: false,
28
- }),
25
+ yes: licenseYesFlag,
29
26
  force: Flags.boolean({
30
27
  description: 'Force regenerate the instance ID even if one is already saved',
31
28
  default: false,
@@ -9,7 +9,7 @@
9
9
  import { Command, Flags } from '@oclif/core';
10
10
  import pc from 'picocolors';
11
11
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../../lib/env-guard.js';
12
- import { licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, requireLicenseRuntime } from '../shared.js';
12
+ import { createLicenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, requireLicenseRuntime, } from '../shared.js';
13
13
  import { cleanLicensedPlugins } from './shared.js';
14
14
  import { resolvePluginStoragePath } from '../../../lib/plugin-storage.js';
15
15
  import { announceTargetEnv } from '../../../lib/ui.js';
@@ -32,7 +32,7 @@ export default class LicensePluginsClean extends Command {
32
32
  '<%= config.bin %> <%= command.id %> --env app1 --json',
33
33
  ];
34
34
  static flags = {
35
- env: licenseEnvFlag,
35
+ env: createLicenseEnvFlag('CLI env name to clean licensed plugins for. Defaults to the current env when omitted'),
36
36
  json: licenseJsonFlag,
37
37
  'pkg-url': licensePkgUrlFlag,
38
38
  'dry-run': Flags.boolean({
@@ -43,10 +43,7 @@ export default class LicensePluginsClean extends Command {
43
43
  description: 'Show detailed per-plugin clean logs',
44
44
  default: false,
45
45
  }),
46
- yes: Flags.boolean({
47
- description: 'Skip the interactive cross-env confirmation prompt',
48
- default: false,
49
- }),
46
+ yes: licenseYesFlag,
50
47
  };
51
48
  async run() {
52
49
  const { flags } = await this.parse(LicensePluginsClean);
@@ -6,9 +6,9 @@
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 { Command, Flags } from '@oclif/core';
9
+ import { Command } from '@oclif/core';
10
10
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../../lib/env-guard.js';
11
- import { licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, requireLicenseRuntime } from '../shared.js';
11
+ import { createLicenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, requireLicenseRuntime, } from '../shared.js';
12
12
  import { fetchLicensedPluginPackages } from './shared.js';
13
13
  import { renderTable } from '../../../lib/ui.js';
14
14
  export default class LicensePluginsList extends Command {
@@ -20,13 +20,10 @@ export default class LicensePluginsList extends Command {
20
20
  '<%= config.bin %> <%= command.id %> --env app1 --json',
21
21
  ];
22
22
  static flags = {
23
- env: licenseEnvFlag,
23
+ env: createLicenseEnvFlag('CLI env name to inspect licensed plugins for. Defaults to the current env when omitted'),
24
24
  json: licenseJsonFlag,
25
25
  'pkg-url': licensePkgUrlFlag,
26
- yes: Flags.boolean({
27
- description: 'Skip the interactive cross-env confirmation prompt',
28
- default: false,
29
- }),
26
+ yes: licenseYesFlag,
30
27
  };
31
28
  async run() {
32
29
  const { flags } = await this.parse(LicensePluginsList);
@@ -10,8 +10,9 @@ import { Command, Flags } from '@oclif/core';
10
10
  import pc from 'picocolors';
11
11
  import { readFile } from 'node:fs/promises';
12
12
  import path from 'node:path';
13
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../../lib/docker-image.js";
13
14
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../../lib/env-guard.js';
14
- import { licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, requireLicenseRuntime } from '../shared.js';
15
+ import { createLicenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, licenseYesFlag, requireLicenseRuntime, } from '../shared.js';
15
16
  import { syncLicensedPlugins } from './shared.js';
16
17
  import { resolvePluginStoragePath } from '../../../lib/plugin-storage.js';
17
18
  import { commandOutput } from '../../../lib/run-npm.js';
@@ -19,8 +20,6 @@ import { announceTargetEnv, startTask, stopTask, succeedTask, updateTask } from
19
20
  const SYNC_LOADING_DELAY_MS = 1200;
20
21
  const SYNC_LOADING_UPDATE_MS = 5000;
21
22
  const LOCAL_APP_PACKAGE_JSON_PATH = 'node_modules/@nocobase/app/package.json';
22
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
23
- const DEFAULT_DOCKER_VERSION = 'alpha';
24
23
  function formatActionLabel(action) {
25
24
  switch (action) {
26
25
  case 'installed':
@@ -90,7 +89,10 @@ async function resolveLocalAppVersion(runtime) {
90
89
  }
91
90
  async function resolveDockerAppVersion(runtime) {
92
91
  const config = runtime.env.config ?? {};
93
- const imageRef = `${trimValue(config.dockerRegistry) || DEFAULT_DOCKER_REGISTRY}:${trimValue(config.downloadVersion) || DEFAULT_DOCKER_VERSION}`;
92
+ const imageRef = resolveDockerImageRef(config.dockerRegistry, config.downloadVersion, {
93
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
94
+ defaultVersion: DEFAULT_DOCKER_VERSION,
95
+ });
94
96
  const args = [
95
97
  'run',
96
98
  '--rm',
@@ -138,7 +140,7 @@ export default class LicensePluginsSync extends Command {
138
140
  '<%= config.bin %> <%= command.id %> --env app1 --json',
139
141
  ];
140
142
  static flags = {
141
- env: licenseEnvFlag,
143
+ env: createLicenseEnvFlag('CLI env name to sync licensed plugins for. Defaults to the current env when omitted'),
142
144
  json: licenseJsonFlag,
143
145
  'pkg-url': licensePkgUrlFlag,
144
146
  'dry-run': Flags.boolean({
@@ -152,10 +154,7 @@ export default class LicensePluginsSync extends Command {
152
154
  description: 'Show detailed per-plugin sync logs',
153
155
  default: false,
154
156
  }),
155
- yes: Flags.boolean({
156
- description: 'Skip the interactive cross-env confirmation prompt',
157
- default: false,
158
- }),
157
+ yes: licenseYesFlag,
159
158
  };
160
159
  async run() {
161
160
  const { flags } = await this.parse(LicensePluginsSync);
@@ -12,22 +12,28 @@ import path from 'node:path';
12
12
  import { getEnvAsync, getInstanceIdAsync, keyDecrypt } from '@nocobase/license-kit';
13
13
  import _ from 'lodash';
14
14
  import { checkExternalDbConnection, readExternalDbConnectionConfig, } from "../../lib/db-connection-check.js";
15
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
15
16
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
16
17
  import { buildRuntimeEnvVars } from '../../lib/runtime-env-vars.js';
17
18
  import { resolveLicensePkgUrlFromConfig } from '../../lib/cli-config.js';
18
19
  import { commandOutput } from '../../lib/run-npm.js';
19
20
  import { appUrl } from '../env/shared.js';
20
- export const licenseEnvFlag = Flags.string({
21
- char: 'e',
22
- description: 'CLI env name (from `nb env` / `nb init`). Defaults to the current env when omitted',
21
+ export function createLicenseEnvFlag(description) {
22
+ return Flags.string({
23
+ char: 'e',
24
+ description,
25
+ });
26
+ }
27
+ export const licenseYesFlag = Flags.boolean({
28
+ char: 'y',
29
+ description: 'Confirm using --env when it targets a different env than the current env',
30
+ default: false,
23
31
  });
24
32
  export const licenseJsonFlag = Flags.boolean({
25
33
  description: 'Output the result as JSON',
26
34
  default: false,
27
35
  });
28
36
  const DEFAULT_LICENSE_PKG_URL = 'https://pkg.nocobase.com/';
29
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
30
- const DEFAULT_DOCKER_VERSION = 'alpha';
31
37
  export const licensePkgUrlFlag = Flags.string({
32
38
  description: 'Commercial package service base URL',
33
39
  hidden: true,
@@ -74,7 +80,10 @@ function normalizeDockerPlatform(value) {
74
80
  }
75
81
  function resolveDockerLicenseImageRef(runtime) {
76
82
  const config = runtime.env.config ?? {};
77
- return `${trimValue(config.dockerRegistry) || DEFAULT_DOCKER_REGISTRY}:${trimValue(config.downloadVersion) || DEFAULT_DOCKER_VERSION}`;
83
+ return resolveDockerImageRef(config.dockerRegistry, config.downloadVersion, {
84
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
85
+ defaultVersion: DEFAULT_DOCKER_VERSION,
86
+ });
78
87
  }
79
88
  function buildDockerLicenseDbFlagArgs(envVars) {
80
89
  return [
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { Command, Flags } from '@oclif/core';
10
10
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
11
- import { ensureInstanceId, licenseEnvFlag, licenseJsonFlag, requireLicenseRuntime } from './shared.js';
11
+ import { createLicenseEnvFlag, ensureInstanceId, licenseJsonFlag, licenseYesFlag, requireLicenseRuntime } from './shared.js';
12
12
  export default class LicenseStatus extends Command {
13
13
  static summary = 'Show commercial license status for the selected env';
14
14
  static description = 'Inspect the selected env and show the current commercial licensing status. Use `--doctor` for extra diagnostic checks once the license backend wiring is implemented.';
@@ -19,12 +19,9 @@ export default class LicenseStatus extends Command {
19
19
  '<%= config.bin %> <%= command.id %> --env app1 --json',
20
20
  ];
21
21
  static flags = {
22
- env: licenseEnvFlag,
22
+ env: createLicenseEnvFlag('CLI env name to inspect. Defaults to the current env when omitted'),
23
23
  json: licenseJsonFlag,
24
- yes: Flags.boolean({
25
- description: 'Skip the interactive cross-env confirmation prompt',
26
- default: false,
27
- }),
24
+ yes: licenseYesFlag,
28
25
  doctor: Flags.boolean({
29
26
  description: 'Run extra diagnostic checks and suggestions',
30
27
  default: false,
@@ -28,7 +28,7 @@ export default class PluginDisable extends Command {
28
28
  static flags = {
29
29
  env: Flags.string({
30
30
  char: 'e',
31
- description: 'CLI env name (from `nb env` / `nb init`). Defaults to the current env when omitted',
31
+ description: 'CLI env name to disable plugins for. Defaults to the current env when omitted',
32
32
  }),
33
33
  yes: Flags.boolean({
34
34
  char: 'y',
@@ -28,7 +28,7 @@ export default class PluginEnable extends Command {
28
28
  static flags = {
29
29
  env: Flags.string({
30
30
  char: 'e',
31
- description: 'CLI env name (from `nb env` / `nb init`). Defaults to the current env when omitted',
31
+ description: 'CLI env name to enable plugins for. Defaults to the current env when omitted',
32
32
  }),
33
33
  yes: Flags.boolean({
34
34
  char: 'y',
@@ -22,7 +22,7 @@ export default class PluginList extends Command {
22
22
  static flags = {
23
23
  env: Flags.string({
24
24
  char: 'e',
25
- description: 'CLI env name (from `nb env` / `nb init`). Defaults to the current env when omitted',
25
+ description: 'CLI env name to inspect plugins for. Defaults to the current env when omitted',
26
26
  }),
27
27
  yes: Flags.boolean({
28
28
  char: 'y',
@@ -7,6 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Command, Flags } from '@oclif/core';
10
+ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
10
11
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
11
12
  import { announceTargetEnv, printInfo } from '../../lib/ui.js';
12
13
  function formatUnsupportedRuntimeMessage(kind, envName) {
@@ -72,7 +73,13 @@ export default class SourceDev extends Command {
72
73
  static flags = {
73
74
  env: Flags.string({
74
75
  char: 'e',
75
- description: 'CLI env name for dev mode. Defaults to the current env when omitted',
76
+ description: 'CLI env name to run dev mode for. Defaults to the current env when omitted',
77
+ required: false,
78
+ }),
79
+ yes: Flags.boolean({
80
+ char: 'y',
81
+ description: 'Confirm using --env when it targets a different env than the current env',
82
+ default: false,
76
83
  required: false,
77
84
  }),
78
85
  'db-sync': Flags.boolean({
@@ -103,6 +110,17 @@ export default class SourceDev extends Command {
103
110
  async run() {
104
111
  const { flags } = await this.parse(SourceDev);
105
112
  const requestedEnv = flags.env?.trim() || undefined;
113
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
114
+ const confirmed = await ensureCrossEnvConfirmed({
115
+ command: this,
116
+ requestedEnv,
117
+ yes: flags.yes,
118
+ });
119
+ if (!confirmed) {
120
+ this.log('Canceled.');
121
+ return;
122
+ }
123
+ }
106
124
  const runtime = await resolveManagedAppRuntime(requestedEnv);
107
125
  if (!runtime) {
108
126
  this.error(formatMissingManagedAppEnvMessage(requestedEnv));
@@ -13,10 +13,9 @@ import path from 'node:path';
13
13
  import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
14
14
  import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
15
15
  import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, resolveCliLocale, translateCli, } from "../../lib/cli-locale.js";
16
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_REGISTRY_ZH_CN, resolveDockerImageRef, } from "../../lib/docker-image.js";
16
17
  import { run } from "../../lib/run-npm.js";
17
18
  import { printVerbose, setVerboseMode, startTask, stopTask, updateTask } from '../../lib/ui.js';
18
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
19
- const DEFAULT_DOCKER_REGISTRY_ZH_CN = 'registry.cn-shanghai.aliyuncs.com/nocobase/nocobase';
20
19
  const DEFAULT_DOCKER_PLATFORM = 'auto';
21
20
  const DEFAULT_DOWNLOAD_VERSION = 'beta';
22
21
  const downloadText = (key, values) => localeText(`commands.download.${key}`, values);
@@ -422,9 +421,11 @@ export default class SourceDownload extends Command {
422
421
  return outputAbs;
423
422
  }
424
423
  dockerTarPath(flags, outputAbs) {
425
- const image = String(flags['docker-registry'] ?? '').trim() || defaultDockerRegistryForLang(process.env.NB_LOCALE);
426
- const tag = flags.version ?? 'latest';
427
- const safeBase = `${image.replace(/[/:]/g, '-')}-${tag.replace(/[/\\]/g, '-')}`;
424
+ const imageRef = resolveDockerImageRef(flags['docker-registry'], flags.version, {
425
+ defaultRegistry: defaultDockerRegistryForLang(process.env.NB_LOCALE),
426
+ defaultVersion: 'latest',
427
+ });
428
+ const safeBase = imageRef.replace(/[\\/:]/g, '-');
428
429
  return path.join(outputAbs, `${safeBase}.tar`);
429
430
  }
430
431
  /**
@@ -709,9 +710,10 @@ export default class SourceDownload extends Command {
709
710
  return argv;
710
711
  }
711
712
  async downloadFromDocker(flags) {
712
- const image = String(flags['docker-registry'] ?? '').trim() || defaultDockerRegistryForLang(process.env.NB_LOCALE);
713
- const tag = flags.version ?? 'latest';
714
- const imageRef = `${image}:${tag}`;
713
+ const imageRef = resolveDockerImageRef(flags['docker-registry'], flags.version, {
714
+ defaultRegistry: defaultDockerRegistryForLang(process.env.NB_LOCALE),
715
+ defaultVersion: 'latest',
716
+ });
715
717
  const platform = dockerPlatformArg(flags['docker-platform']);
716
718
  const pullArgs = ['pull'];
717
719
  if (platform) {
@@ -10,10 +10,9 @@ import { mkdir, readdir } from 'node:fs/promises';
10
10
  import { dockerContainerExists, startDockerContainer } from './app-runtime.js';
11
11
  import { deriveBuiltinDbConnection, resolveBuiltinDbConnection } from './builtin-db.js';
12
12
  import { resolveConfiguredEnvPath } from './cli-home.js';
13
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "./docker-image.js";
13
14
  import { commandSucceeds, run } from './run-npm.js';
14
15
  import Install from '../commands/install.js';
15
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
16
- const DEFAULT_DOCKER_VERSION = 'alpha';
17
16
  const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
18
17
  function commandStdio(verbose) {
19
18
  return verbose ? 'inherit' : 'ignore';
@@ -102,7 +101,10 @@ export function buildSavedDockerRunArgs(runtime) {
102
101
  const dbPassword = trimValue(config.dbPassword);
103
102
  const dockerRegistry = trimValue(config.dockerRegistry) || DEFAULT_DOCKER_REGISTRY;
104
103
  const version = trimValue(config.downloadVersion) || DEFAULT_DOCKER_VERSION;
105
- const imageRef = `${dockerRegistry}:${version}`;
104
+ const imageRef = resolveDockerImageRef(dockerRegistry, version, {
105
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
106
+ defaultVersion: DEFAULT_DOCKER_VERSION,
107
+ });
106
108
  const missing = [];
107
109
  if (!storagePath) {
108
110
  missing.push('storagePath');
@@ -0,0 +1,37 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ export const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
10
+ export const DEFAULT_DOCKER_REGISTRY_ZH_CN = 'registry.cn-shanghai.aliyuncs.com/nocobase/nocobase';
11
+ export const DEFAULT_DOCKER_VERSION = 'alpha';
12
+ export const DOCKER_IMAGE_FULL_SUFFIX = '-full';
13
+ const OFFICIAL_FULL_IMAGE_REGISTRIES = new Set([
14
+ DEFAULT_DOCKER_REGISTRY,
15
+ DEFAULT_DOCKER_REGISTRY_ZH_CN,
16
+ ]);
17
+ function trimValue(value) {
18
+ return String(value ?? '').trim();
19
+ }
20
+ export function shouldUseFullDockerImageTag(registry) {
21
+ return OFFICIAL_FULL_IMAGE_REGISTRIES.has(trimValue(registry));
22
+ }
23
+ export function normalizeDockerImageTag(registry, version) {
24
+ const tag = trimValue(version) || DEFAULT_DOCKER_VERSION;
25
+ if (!shouldUseFullDockerImageTag(registry)) {
26
+ return tag;
27
+ }
28
+ return tag.endsWith(DOCKER_IMAGE_FULL_SUFFIX)
29
+ ? tag
30
+ : `${tag}${DOCKER_IMAGE_FULL_SUFFIX}`;
31
+ }
32
+ export function resolveDockerImageRef(registry, version, options) {
33
+ const resolvedRegistry = trimValue(registry) || options?.defaultRegistry || DEFAULT_DOCKER_REGISTRY;
34
+ const rawVersion = trimValue(version) || options?.defaultVersion || DEFAULT_DOCKER_VERSION;
35
+ const normalizedTag = normalizeDockerImageTag(resolvedRegistry, rawVersion);
36
+ return `${resolvedRegistry}:${normalizedTag}`;
37
+ }
@@ -409,41 +409,25 @@ function buildManagedFileContent(shell) {
409
409
  case 'zsh':
410
410
  return [
411
411
  '# NocoBase session integration',
412
- 'if [ -n "${CODEX_THREAD_ID:-}" ]; then',
413
- ' export NB_SESSION_ID="${CODEX_THREAD_ID}"',
414
- 'else',
415
- ` export NB_SESSION_ID="nb-$(node -e 'console.log(require("node:crypto").randomUUID())')"`,
416
- 'fi',
412
+ `export NB_SESSION_ID="nb-$(node -e 'console.log(require("node:crypto").randomUUID())')"`,
417
413
  '',
418
414
  ].join('\n');
419
415
  case 'fish':
420
416
  return [
421
417
  '# NocoBase session integration',
422
- 'if set -q CODEX_THREAD_ID',
423
- ' set -gx NB_SESSION_ID "$CODEX_THREAD_ID"',
424
- 'else',
425
- ' set -gx NB_SESSION_ID "nb-"(node -e "console.log(require(\'node:crypto\').randomUUID())")',
426
- 'end',
418
+ 'set -gx NB_SESSION_ID "nb-"(node -e "console.log(require(\'node:crypto\').randomUUID())")',
427
419
  '',
428
420
  ].join('\n');
429
421
  case 'powershell':
430
422
  return [
431
423
  '# NocoBase session integration',
432
- 'if ($env:CODEX_THREAD_ID) {',
433
- ' $env:NB_SESSION_ID = $env:CODEX_THREAD_ID',
434
- '} else {',
435
424
  ' $env:NB_SESSION_ID = "nb-" + [guid]::NewGuid().ToString()',
436
- '}',
437
425
  '',
438
426
  ].join('\n');
439
427
  case 'cmd':
440
428
  return [
441
429
  '@echo off',
442
- 'if defined CODEX_THREAD_ID (',
443
- ' set "NB_SESSION_ID=%CODEX_THREAD_ID%"',
444
- ') else (',
445
- ' set "NB_SESSION_ID=nb-%RANDOM%%RANDOM%%RANDOM%%RANDOM%"',
446
- ')',
430
+ 'set "NB_SESSION_ID=nb-%RANDOM%%RANDOM%%RANDOM%%RANDOM%"',
447
431
  '',
448
432
  ].join('\r\n');
449
433
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.1.0-alpha.32",
3
+ "version": "2.1.0-alpha.33",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",
@@ -103,5 +103,5 @@
103
103
  "type": "git",
104
104
  "url": "git+https://github.com/nocobase/nocobase.git"
105
105
  },
106
- "gitHead": "1ba7d717e156651db17c615f9b9c48edd669d19b"
106
+ "gitHead": "64aaff11b9d9cab6890fb4835d7ae6933f2d3081"
107
107
  }