@nocobase/cli 2.1.0-beta.29 → 2.1.0-beta.30

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 (50) hide show
  1. package/README.md +14 -0
  2. package/README.zh-CN.md +14 -0
  3. package/bin/run.js +3 -0
  4. package/bin/session-env.js +27 -0
  5. package/dist/commands/app/down.js +47 -9
  6. package/dist/commands/app/logs.js +17 -0
  7. package/dist/commands/app/restart.js +23 -1
  8. package/dist/commands/app/start.js +17 -0
  9. package/dist/commands/app/stop.js +17 -0
  10. package/dist/commands/app/upgrade.js +22 -2
  11. package/dist/commands/db/check.js +6 -4
  12. package/dist/commands/db/ps.js +1 -1
  13. package/dist/commands/env/add.js +3 -2
  14. package/dist/commands/env/auth.js +1 -1
  15. package/dist/commands/env/current.js +21 -0
  16. package/dist/commands/env/info.js +4 -3
  17. package/dist/commands/env/list.js +8 -14
  18. package/dist/commands/env/remove.js +2 -2
  19. package/dist/commands/env/status.js +90 -0
  20. package/dist/commands/env/update.js +1 -1
  21. package/dist/commands/env/use.js +11 -1
  22. package/dist/commands/install.js +10 -4
  23. package/dist/commands/license/activate.js +20 -24
  24. package/dist/commands/license/id.js +17 -2
  25. package/dist/commands/license/plugins/clean.js +17 -2
  26. package/dist/commands/license/plugins/list.js +17 -2
  27. package/dist/commands/license/plugins/sync.js +22 -5
  28. package/dist/commands/license/shared.js +15 -6
  29. package/dist/commands/license/status.js +17 -2
  30. package/dist/commands/plugin/disable.js +25 -4
  31. package/dist/commands/plugin/enable.js +25 -4
  32. package/dist/commands/plugin/list.js +25 -4
  33. package/dist/commands/session/id.js +24 -0
  34. package/dist/commands/session/remove.js +57 -0
  35. package/dist/commands/session/setup.js +62 -0
  36. package/dist/commands/source/dev.js +19 -1
  37. package/dist/commands/source/download.js +10 -8
  38. package/dist/lib/app-managed-resources.js +5 -3
  39. package/dist/lib/app-runtime.js +1 -1
  40. package/dist/lib/auth-store.js +28 -11
  41. package/dist/lib/docker-image.js +37 -0
  42. package/dist/lib/env-guard.js +61 -0
  43. package/dist/lib/generated-command.js +16 -0
  44. package/dist/lib/plugin-storage.js +1 -64
  45. package/dist/lib/resource-command.js +15 -0
  46. package/dist/lib/runtime-generator.js +1 -1
  47. package/dist/lib/session-id.js +17 -0
  48. package/dist/lib/session-integration.js +703 -0
  49. package/dist/lib/session-store.js +118 -0
  50. package/package.json +3 -3
package/README.md CHANGED
@@ -296,6 +296,20 @@ Show the current env:
296
296
  nb env
297
297
  ```
298
298
 
299
+ Show only the current env name:
300
+
301
+ ```bash
302
+ nb env current
303
+ ```
304
+
305
+ Set up shell session integration for `NB_SESSION_ID`:
306
+
307
+ ```bash
308
+ nb session setup
309
+ nb session id
310
+ nb session remove
311
+ ```
312
+
299
313
  List configured envs with token-verified API status:
300
314
 
301
315
  ```bash
package/README.zh-CN.md CHANGED
@@ -256,6 +256,20 @@ nb app down --env app1 --all --yes
256
256
  nb env
257
257
  ```
258
258
 
259
+ 只查看当前 env 名称:
260
+
261
+ ```bash
262
+ nb env current
263
+ ```
264
+
265
+ 配置 `NB_SESSION_ID` 的 shell session 集成:
266
+
267
+ ```bash
268
+ nb session setup
269
+ nb session id
270
+ nb session remove
271
+ ```
272
+
259
273
  查看已配置的 env 及 Token 验证后的 API 状态:
260
274
 
261
275
  ```bash
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
+ }
@@ -12,8 +12,9 @@ import fsp from 'node:fs/promises';
12
12
  import os from 'node:os';
13
13
  import path from 'node:path';
14
14
  import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
15
- import { removeEnv } from '../../lib/auth-store.js';
15
+ import { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
16
16
  import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
17
+ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
17
18
  import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
18
19
  import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
19
20
  function resolveConfiguredPath(value) {
@@ -97,16 +98,16 @@ function builtinDbContainerName(runtime) {
97
98
  function managedDockerNetworkName(runtime) {
98
99
  return runtime.dockerNetworkName?.trim() || runtime.workspaceName?.trim() || undefined;
99
100
  }
100
- async function confirmDownAll(envName, yes, options) {
101
- if (yes) {
101
+ async function confirmDownAll(envName, force, options) {
102
+ if (force) {
102
103
  return true;
103
104
  }
104
105
  const usedCurrentEnv = options?.explicitEnv === false;
105
106
  if (!isInteractiveTerminal()) {
106
107
  if (usedCurrentEnv) {
107
- throw new Error(`\`nb app down --all\` is using the current env "${envName}". Re-run with --env ${envName} --yes to delete everything for that env in non-interactive mode.`);
108
+ throw new Error(`\`nb app down --all\` is using the current env "${envName}". Re-run with --env ${envName} --force to delete everything for that env in non-interactive mode.`);
108
109
  }
109
- throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --yes to delete everything for "${envName}" in non-interactive mode.`);
110
+ throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --force to delete everything for "${envName}" in non-interactive mode.`);
110
111
  }
111
112
  const answer = await p.confirm({
112
113
  message: usedCurrentEnv
@@ -122,6 +123,17 @@ async function confirmDownAll(envName, yes, options) {
122
123
  }
123
124
  return answer;
124
125
  }
126
+ function formatDownCrossEnvForceRequiredMessage(currentEnv, requestedEnv) {
127
+ return [
128
+ `Refusing to run against env "${requestedEnv}" because the current env is "${currentEnv}" and interactive confirmation is unavailable in the current agent session.`,
129
+ '',
130
+ 'For safety, the agent will not switch envs automatically and will not add --force on your behalf.',
131
+ '',
132
+ 'To continue:',
133
+ `- run \`nb env use ${requestedEnv}\` yourself and then re-run the command, or`,
134
+ `- re-run the same command with \`--env ${requestedEnv} --force\` to confirm this one-off cross-env operation.`,
135
+ ].join('\n');
136
+ }
125
137
  function formatDownFailure(envName, message) {
126
138
  return [
127
139
  `Couldn't bring down NocoBase for "${envName}".`,
@@ -134,7 +146,8 @@ export default class AppDown extends Command {
134
146
  static description = 'Bring down the selected env by removing runtime containers and the saved local app files. Storage data and env config are kept unless explicitly requested.';
135
147
  static examples = [
136
148
  '<%= config.bin %> <%= command.id %> --env app1',
137
- '<%= config.bin %> <%= command.id %> --env app1 --all --yes',
149
+ '<%= config.bin %> <%= command.id %> --env app1 --all --force',
150
+ '<%= config.bin %> <%= command.id %> --env app1 --force',
138
151
  ];
139
152
  static flags = {
140
153
  env: Flags.string({
@@ -147,7 +160,12 @@ export default class AppDown extends Command {
147
160
  }),
148
161
  yes: Flags.boolean({
149
162
  char: 'y',
150
- description: 'Confirm destructive actions without prompting',
163
+ description: 'Confirm using --env when it targets a different env than the current env',
164
+ default: false,
165
+ }),
166
+ force: Flags.boolean({
167
+ char: 'f',
168
+ description: 'Force a one-off cross-env operation when --env targets a different env in non-interactive mode',
151
169
  default: false,
152
170
  }),
153
171
  verbose: Flags.boolean({
@@ -158,6 +176,26 @@ export default class AppDown extends Command {
158
176
  async run() {
159
177
  const { flags } = await this.parse(AppDown);
160
178
  const requestedEnv = flags.env?.trim() || undefined;
179
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
180
+ if (!isInteractiveTerminal()) {
181
+ const currentEnv = await getCurrentEnvName();
182
+ const normalizedCurrentEnv = String(currentEnv ?? '').trim() || undefined;
183
+ if (normalizedCurrentEnv && normalizedCurrentEnv !== requestedEnv && !flags.force) {
184
+ this.error(formatDownCrossEnvForceRequiredMessage(normalizedCurrentEnv, requestedEnv));
185
+ }
186
+ }
187
+ else {
188
+ const confirmed = await ensureCrossEnvConfirmed({
189
+ command: this,
190
+ requestedEnv,
191
+ yes: flags.yes,
192
+ });
193
+ if (!confirmed) {
194
+ this.log('Canceled.');
195
+ return;
196
+ }
197
+ }
198
+ }
161
199
  const explicitEnv = Boolean(requestedEnv);
162
200
  const removeData = Boolean(flags.all);
163
201
  const removeEnvConfig = Boolean(flags.all);
@@ -182,7 +220,7 @@ export default class AppDown extends Command {
182
220
  if (flags.all) {
183
221
  let confirmed = false;
184
222
  try {
185
- confirmed = await confirmDownAll(runtime.envName, flags.yes, { explicitEnv });
223
+ confirmed = await confirmDownAll(runtime.envName, flags.force, { explicitEnv });
186
224
  }
187
225
  catch (error) {
188
226
  this.error(error instanceof Error ? error.message : String(error));
@@ -255,7 +293,7 @@ export default class AppDown extends Command {
255
293
  if (removeEnvConfig) {
256
294
  startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
257
295
  const result = await removeEnv(runtime.envName);
258
- succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.currentEnv ? ` (current env: ${result.currentEnv})` : ''}.`);
296
+ succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.lastEnv ? ` (last env: ${result.lastEnv})` : ''}.`);
259
297
  }
260
298
  }
261
299
  catch (error) {
@@ -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 { run } from '../../lib/run-npm.js';
12
13
  import { printInfo } from '../../lib/ui.js';
@@ -31,6 +32,11 @@ export default class AppLogs extends Command {
31
32
  char: 'e',
32
33
  description: 'CLI env name to inspect logs for. Defaults to the current env when omitted',
33
34
  }),
35
+ yes: Flags.boolean({
36
+ char: 'y',
37
+ description: 'Confirm using --env when it targets a different env than the current env',
38
+ default: false,
39
+ }),
34
40
  tail: Flags.integer({
35
41
  description: 'Number of recent log lines to show before following',
36
42
  default: 100,
@@ -46,6 +52,17 @@ export default class AppLogs extends Command {
46
52
  async run() {
47
53
  const { flags } = await this.parse(AppLogs);
48
54
  const requestedEnv = flags.env?.trim() || undefined;
55
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
56
+ const confirmed = await ensureCrossEnvConfirmed({
57
+ command: this,
58
+ requestedEnv,
59
+ yes: flags.yes,
60
+ });
61
+ if (!confirmed) {
62
+ this.log('Canceled.');
63
+ return;
64
+ }
65
+ }
49
66
  const runtime = await resolveManagedAppRuntime(requestedEnv);
50
67
  if (!runtime) {
51
68
  this.error(formatMissingManagedAppEnvMessage(requestedEnv));
@@ -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
  function argvHasToken(argv, tokens) {
11
12
  return tokens.some((token) => argv.includes(token));
12
13
  }
@@ -35,6 +36,11 @@ export default class AppRestart extends Command {
35
36
  char: 'e',
36
37
  description: 'CLI env name to restart. Defaults to the current env when omitted',
37
38
  }),
39
+ yes: Flags.boolean({
40
+ char: 'y',
41
+ description: 'Confirm using --env when it targets a different env than the current env',
42
+ default: false,
43
+ }),
38
44
  quickstart: Flags.boolean({ description: 'Quickstart the application after stopping it', required: false }),
39
45
  port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
40
46
  daemon: Flags.boolean({
@@ -53,9 +59,25 @@ export default class AppRestart extends Command {
53
59
  };
54
60
  async run() {
55
61
  const { flags } = await this.parse(AppRestart);
62
+ const requestedEnv = flags.env?.trim() || undefined;
63
+ const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
64
+ if (explicitEnvSelection) {
65
+ const confirmed = await ensureCrossEnvConfirmed({
66
+ command: this,
67
+ requestedEnv,
68
+ yes: flags.yes,
69
+ });
70
+ if (!confirmed) {
71
+ this.log('Canceled.');
72
+ return;
73
+ }
74
+ }
56
75
  const stopArgv = [];
57
76
  const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
58
- pushFlag(stopArgv, '--env', flags.env?.trim() || undefined);
77
+ pushFlag(stopArgv, '--env', requestedEnv);
78
+ if (flags.yes || explicitEnvSelection) {
79
+ stopArgv.push('--yes');
80
+ }
59
81
  if (flags.verbose) {
60
82
  stopArgv.push('--verbose');
61
83
  }
@@ -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, startDockerContainer, } from '../../lib/app-runtime.js';
11
12
  import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBaseUrl, waitForAppReady, } from '../../lib/app-health.js';
12
13
  import { ensureBuiltinDbReady, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
@@ -68,6 +69,11 @@ export default class AppStart extends Command {
68
69
  char: 'e',
69
70
  description: 'CLI env name to start. Defaults to the current env when omitted',
70
71
  }),
72
+ yes: Flags.boolean({
73
+ char: 'y',
74
+ description: 'Confirm using --env when it targets a different env than the current env',
75
+ default: false,
76
+ }),
71
77
  quickstart: Flags.boolean({ description: 'Quickstart the application', required: false }),
72
78
  port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
73
79
  daemon: Flags.boolean({
@@ -87,6 +93,17 @@ export default class AppStart extends Command {
87
93
  async run() {
88
94
  const { flags } = await this.parse(AppStart);
89
95
  const requestedEnv = flags.env?.trim() || undefined;
96
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
97
+ const confirmed = await ensureCrossEnvConfirmed({
98
+ command: this,
99
+ requestedEnv,
100
+ yes: flags.yes,
101
+ });
102
+ if (!confirmed) {
103
+ this.log('Canceled.');
104
+ return;
105
+ }
106
+ }
90
107
  const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
91
108
  const runtime = await resolveManagedAppRuntime(requestedEnv);
92
109
  const commandStdio = flags.verbose ? 'inherit' : 'ignore';
@@ -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, stopDockerContainer, } from '../../lib/app-runtime.js';
11
12
  import { announceTargetEnv, failTask, startTask, succeedTask } from '../../lib/ui.js';
12
13
  function formatStopFailure(envName, message) {
@@ -38,6 +39,11 @@ export default class AppStop extends Command {
38
39
  char: 'e',
39
40
  description: 'CLI env name to stop. Defaults to the current env when omitted',
40
41
  }),
42
+ yes: Flags.boolean({
43
+ char: 'y',
44
+ description: 'Confirm using --env when it targets a different env than the current env',
45
+ default: false,
46
+ }),
41
47
  verbose: Flags.boolean({
42
48
  description: 'Show raw shutdown output from the underlying local or Docker command',
43
49
  default: false,
@@ -46,6 +52,17 @@ export default class AppStop extends Command {
46
52
  async run() {
47
53
  const { flags } = await this.parse(AppStop);
48
54
  const requestedEnv = flags.env?.trim() || undefined;
55
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
56
+ const confirmed = await ensureCrossEnvConfirmed({
57
+ command: this,
58
+ requestedEnv,
59
+ yes: flags.yes,
60
+ });
61
+ if (!confirmed) {
62
+ this.log('Canceled.');
63
+ return;
64
+ }
65
+ }
49
66
  const runtime = await resolveManagedAppRuntime(requestedEnv);
50
67
  const commandStdio = flags.verbose ? 'inherit' : 'ignore';
51
68
  if (!runtime) {
@@ -11,9 +11,10 @@ import { upsertEnv } from '../../lib/auth-store.js';
11
11
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, stopDockerContainer, } from '../../lib/app-runtime.js';
12
12
  import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
13
13
  import { deriveBuiltinDbConnection } from '../../lib/builtin-db.js';
14
+ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
15
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
14
16
  import { commandSucceeds, run } from '../../lib/run-npm.js';
15
17
  import { announceTargetEnv, failTask, printInfo, startTask, stopTask, succeedTask, updateTask } from '../../lib/ui.js';
16
- const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
17
18
  const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
18
19
  const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
19
20
  const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
@@ -211,6 +212,11 @@ export default class AppUpgrade extends Command {
211
212
  char: 'e',
212
213
  description: 'CLI env name to upgrade. Defaults to the current env when omitted',
213
214
  }),
215
+ yes: Flags.boolean({
216
+ char: 'y',
217
+ description: 'Confirm using --env when it targets a different env than the current env',
218
+ default: false,
219
+ }),
214
220
  'skip-code-update': Flags.boolean({
215
221
  char: 's',
216
222
  description: 'Restart with the saved local code or Docker image without downloading updates first',
@@ -355,7 +361,10 @@ export default class AppUpgrade extends Command {
355
361
  if (missing.length > 0) {
356
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.`);
357
363
  }
358
- const imageRef = `${dockerRegistry}:${downloadVersion}`;
364
+ const imageRef = resolveDockerImageRef(dockerRegistry, downloadVersion, {
365
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
366
+ defaultVersion: DEFAULT_DOCKER_VERSION,
367
+ });
359
368
  const args = [
360
369
  'run',
361
370
  '-d',
@@ -544,6 +553,17 @@ export default class AppUpgrade extends Command {
544
553
  const { flags } = await this.parse(AppUpgrade);
545
554
  const parsed = flags;
546
555
  const requestedEnv = parsed.env?.trim() || undefined;
556
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
557
+ const confirmed = await ensureCrossEnvConfirmed({
558
+ command: this,
559
+ requestedEnv,
560
+ yes: parsed.yes,
561
+ });
562
+ if (!confirmed) {
563
+ this.log('Canceled.');
564
+ return;
565
+ }
566
+ }
547
567
  const commandStdio = parsed.verbose ? 'inherit' : 'ignore';
548
568
  const runtime = await resolveManagedAppRuntime(requestedEnv);
549
569
  if (!runtime) {
@@ -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() {
@@ -7,7 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Args, Command, Flags } from '@oclif/core';
10
- import { upsertEnv } from '../../lib/auth-store.js';
10
+ import { setCurrentEnv, upsertEnv } from '../../lib/auth-store.js';
11
11
  import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
12
12
  import { buildStoredEnvConfig, } from '../../lib/env-config.js';
13
13
  import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
@@ -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
  };
@@ -303,6 +303,7 @@ export default class EnvAdd extends Command {
303
303
  const envConfig = this.buildEnvConfig(results, parsedFlags);
304
304
  printVerbose(`Saving env "${envName}" globally.`);
305
305
  await upsertEnv(envName, envConfig, { scope: resolveDefaultConfigScope() });
306
+ await setCurrentEnv(envName, { scope: resolveDefaultConfigScope() });
306
307
  if (results.authType === 'oauth') {
307
308
  await this.config.runCommand('env:auth', [envName]);
308
309
  }
@@ -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
  };
@@ -0,0 +1,21 @@
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
+ import { Command } from '@oclif/core';
10
+ import { getCurrentEnvName } from '../../lib/auth-store.js';
11
+ import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
12
+ export default class EnvCurrent extends Command {
13
+ static summary = 'Show the current environment name';
14
+ static examples = [
15
+ '<%= config.bin %> <%= command.id %>',
16
+ ];
17
+ async run() {
18
+ await this.parse(EnvCurrent);
19
+ this.log(await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
20
+ }
21
+ }
@@ -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',
@@ -7,20 +7,20 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Command } from '@oclif/core';
10
- import { resolveManagedAppRuntime } from '../../lib/app-runtime.js';
11
- import { listEnvs } from '../../lib/auth-store.js';
10
+ import { getCurrentEnvName, listEnvs } from '../../lib/auth-store.js';
12
11
  import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
13
12
  import { renderTable } from '../../lib/ui.js';
14
- import { apiStatus, appUrl, resolveApiBaseUrl } from './shared.js';
13
+ import { resolveApiBaseUrl } from './shared.js';
15
14
  export default class EnvList extends Command {
16
- static summary = 'List configured environments and API auth status';
15
+ static summary = 'List configured environments';
17
16
  static examples = [
18
17
  '<%= config.bin %> <%= command.id %>',
19
18
  ];
20
19
  async run() {
21
20
  await this.parse(EnvList);
22
21
  const scope = resolveDefaultConfigScope();
23
- const { currentEnv, envs } = await listEnvs({ scope });
22
+ const { envs } = await listEnvs({ scope });
23
+ const currentEnv = await getCurrentEnvName({ scope });
24
24
  const names = Object.keys(envs).sort();
25
25
  if (!names.length) {
26
26
  this.log('No envs configured.');
@@ -30,21 +30,15 @@ export default class EnvList extends Command {
30
30
  const rows = [];
31
31
  for (const name of names) {
32
32
  const env = envs[name];
33
- const runtime = await resolveManagedAppRuntime(name);
34
- const statusConfig = {
35
- ...env,
36
- ...(runtime?.env.config ?? {}),
37
- };
38
33
  rows.push([
39
34
  name === currentEnv ? '*' : '',
40
35
  name,
41
- runtime?.kind ?? env.kind ?? '-',
42
- await apiStatus(name, statusConfig, { scope }),
43
- runtime ? appUrl(runtime) : resolveApiBaseUrl(env),
36
+ env.kind ?? '-',
37
+ resolveApiBaseUrl(env),
44
38
  env.auth?.type ?? '',
45
39
  env.runtime?.version ?? '',
46
40
  ]);
47
41
  }
48
- this.log(renderTable(['Current', 'Name', 'Kind', 'App Status', 'URL', 'Auth', 'Runtime'], rows));
42
+ this.log(renderTable(['Current', 'Name', 'Kind', 'API Base URL', 'Auth', 'Runtime'], rows));
49
43
  }
50
44
  }
@@ -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
  };
@@ -51,7 +51,7 @@ export default class EnvRemove extends Command {
51
51
  const result = await removeEnv(args.name, { scope: resolveDefaultConfigScope() });
52
52
  this.log(`Removed env "${result.removed}".`);
53
53
  if (result.hasEnvs) {
54
- this.log(`Current env: ${result.currentEnv}`);
54
+ this.log(`Current env: ${await getCurrentEnvName({ scope: resolveDefaultConfigScope() })}`);
55
55
  return;
56
56
  }
57
57
  this.log('No envs configured.');