@nocobase/cli 2.1.0-beta.42 → 2.1.0-beta.42-test.2
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 +6 -35
- package/dist/commands/app/restart.js +12 -5
- package/dist/commands/app/start.js +1 -0
- package/dist/commands/app/upgrade.js +2 -2
- package/dist/commands/config/delete.js +4 -1
- package/dist/commands/config/get.js +1 -0
- package/dist/commands/config/set.js +6 -1
- package/dist/commands/db/ps.js +5 -18
- package/dist/commands/env/auth.js +1 -1
- package/dist/commands/env/list.js +4 -5
- package/dist/commands/env/status.js +10 -7
- package/dist/commands/init.js +18 -0
- package/dist/commands/install.js +53 -29
- package/dist/commands/self/update.js +58 -1
- package/dist/commands/source/dev.js +5 -5
- package/dist/lib/app-health.js +34 -27
- package/dist/lib/app-managed-resources.js +1 -1
- package/dist/lib/app-runtime.js +2 -2
- package/dist/lib/auth-store.js +9 -0
- package/dist/lib/backup.js +3 -3
- package/dist/lib/bootstrap.js +14 -8
- package/dist/lib/cli-config.js +38 -0
- package/dist/lib/cli-entry-error.js +44 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +21 -13
- package/dist/lib/source-publish.js +4 -1
- package/dist/lib/startup-update.js +74 -50
- package/dist/locale/en-US.json +6 -0
- package/dist/locale/zh-CN.json +6 -0
- package/package.json +2 -3
- package/LICENSE.txt +0 -107
|
@@ -16,20 +16,20 @@ function formatUnsupportedRuntimeMessage(kind, envName) {
|
|
|
16
16
|
return [
|
|
17
17
|
`Can't run dev mode for "${envName}".`,
|
|
18
18
|
'This env is managed by Docker, but `nb source dev` requires a local npm or Git source directory.',
|
|
19
|
-
`Use \`nb app logs --env ${envName}\` to inspect the Docker app, or create a source-based env with \`nb init --env ${envName} --source git\`.`,
|
|
19
|
+
`Use \`nb app logs --env ${envName}\` to inspect the Docker app, or create a source-based env with \`nb init --ui --env ${envName} --source git\`.`,
|
|
20
20
|
].join('\n');
|
|
21
21
|
}
|
|
22
22
|
if (kind === 'ssh') {
|
|
23
23
|
return [
|
|
24
24
|
`Can't run dev mode for "${envName}" yet.`,
|
|
25
25
|
'SSH env support is reserved but not implemented yet.',
|
|
26
|
-
`Create a source-based env with \`nb init --env ${envName} --source git\` if you want local development mode right now.`,
|
|
26
|
+
`Create a source-based env with \`nb init --ui --env ${envName} --source git\` if you want local development mode right now.`,
|
|
27
27
|
].join('\n');
|
|
28
28
|
}
|
|
29
29
|
return [
|
|
30
30
|
`Can't run dev mode for "${envName}".`,
|
|
31
31
|
'This env only has an API connection, but `nb source dev` requires a local npm or Git source directory.',
|
|
32
|
-
`Create a source-based env with \`nb init --env ${envName} --source git\` if you want local development mode.`,
|
|
32
|
+
`Create a source-based env with \`nb init --ui --env ${envName} --source git\` if you want local development mode.`,
|
|
33
33
|
].join('\n');
|
|
34
34
|
}
|
|
35
35
|
function appUrlForPort(port) {
|
|
@@ -129,8 +129,8 @@ export default class SourceDev extends Command {
|
|
|
129
129
|
this.error(formatUnsupportedRuntimeMessage(runtime.kind, runtime.envName));
|
|
130
130
|
}
|
|
131
131
|
announceTargetEnv(runtime.envName);
|
|
132
|
-
const devPort = flags.port
|
|
133
|
-
|
|
132
|
+
const devPort = flags.port ||
|
|
133
|
+
(runtime.env.appPort !== undefined && runtime.env.appPort !== null
|
|
134
134
|
? String(runtime.env.appPort).trim()
|
|
135
135
|
: undefined);
|
|
136
136
|
const appUrl = appUrlForPort(devPort);
|
package/dist/lib/app-health.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
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 { startDockerLogFollower } from './docker-log-stream.js';
|
|
9
10
|
import { printInfo } from './ui.js';
|
|
10
11
|
const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
|
|
11
12
|
const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
|
|
@@ -99,34 +100,40 @@ export async function waitForAppReady(params) {
|
|
|
99
100
|
let lastMessage = 'No response yet';
|
|
100
101
|
let nextProgressLogAt = startedAt + progressLogIntervalMs;
|
|
101
102
|
printInfo(`Waiting for NocoBase to become ready for "${params.envName}"...`);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const now = Date.now();
|
|
113
|
-
if (now >= nextProgressLogAt) {
|
|
114
|
-
const elapsedSeconds = Math.max(1, Math.floor((now - startedAt) / 1000));
|
|
115
|
-
printInfo(`Still waiting for "${params.envName}"... (${elapsedSeconds}s elapsed)`);
|
|
116
|
-
while (nextProgressLogAt <= now) {
|
|
117
|
-
nextProgressLogAt += progressLogIntervalMs;
|
|
103
|
+
const dockerLogFollower = params.verbose && params.containerName ? startDockerLogFollower(params.containerName) : undefined;
|
|
104
|
+
try {
|
|
105
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
106
|
+
const result = await requestAppHealthCheck({
|
|
107
|
+
healthCheckUrl,
|
|
108
|
+
fetchImpl: params.fetchImpl,
|
|
109
|
+
requestTimeoutMs: params.requestTimeoutMs,
|
|
110
|
+
});
|
|
111
|
+
if (result.ok) {
|
|
112
|
+
return;
|
|
118
113
|
}
|
|
114
|
+
lastMessage = result.message;
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
if (now >= nextProgressLogAt) {
|
|
117
|
+
const elapsedSeconds = Math.max(1, Math.floor((now - startedAt) / 1000));
|
|
118
|
+
printInfo(`Still waiting for "${params.envName}"... (${elapsedSeconds}s elapsed)`);
|
|
119
|
+
while (nextProgressLogAt <= now) {
|
|
120
|
+
nextProgressLogAt += progressLogIntervalMs;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const remainingMs = timeoutMs - (Date.now() - startedAt);
|
|
124
|
+
if (remainingMs <= 0) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
await sleep(Math.min(intervalMs, remainingMs));
|
|
119
128
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
const hints = [
|
|
130
|
+
params.logHint,
|
|
131
|
+
params.containerName ? `docker logs ${params.containerName}` : undefined,
|
|
132
|
+
].filter(Boolean);
|
|
133
|
+
const hintText = hints.length > 0 ? ` ${hints.join(' ')}` : '';
|
|
134
|
+
throw new AppHealthCheckError(`NocoBase did not become ready in time for "${params.envName}". Expected \`${healthCheckUrl}\` to respond with \`ok\`, but the last status was: ${lastMessage}.${hintText}`);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await dockerLogFollower?.stop();
|
|
125
138
|
}
|
|
126
|
-
const hints = [
|
|
127
|
-
params.logHint,
|
|
128
|
-
params.containerName ? `docker logs ${params.containerName}` : undefined,
|
|
129
|
-
].filter(Boolean);
|
|
130
|
-
const hintText = hints.length > 0 ? ` ${hints.join(' ')}` : '';
|
|
131
|
-
throw new AppHealthCheckError(`NocoBase did not become ready in time for "${params.envName}". Expected \`${healthCheckUrl}\` to respond with \`ok\`, but the last status was: ${lastMessage}.${hintText}`);
|
|
132
139
|
}
|
|
@@ -82,7 +82,7 @@ function formatSavedDockerSettingsIncomplete(envName, missing) {
|
|
|
82
82
|
return [
|
|
83
83
|
`Can't start NocoBase for "${envName}" yet.`,
|
|
84
84
|
`The saved Docker settings for this env are incomplete. Missing: ${missing.join(', ')}.`,
|
|
85
|
-
|
|
85
|
+
`Re-run \`nb init --ui --env ${envName}\` to refresh this env config, then try again.`,
|
|
86
86
|
].join('\n');
|
|
87
87
|
}
|
|
88
88
|
function formatDockerAppRecreateFailure(envName, message) {
|
package/dist/lib/app-runtime.js
CHANGED
|
@@ -110,10 +110,10 @@ export function formatMissingManagedAppEnvMessage(envName) {
|
|
|
110
110
|
if (requested) {
|
|
111
111
|
return [
|
|
112
112
|
`Env "${requested}" is not configured in this workspace.`,
|
|
113
|
-
`If you want to create a new NocoBase AI environment, run \`nb init --env ${requested}\` first.`,
|
|
113
|
+
`If you want to create a new NocoBase AI environment, run \`nb init --ui --env ${requested}\` first.`,
|
|
114
114
|
].join('\n');
|
|
115
115
|
}
|
|
116
|
-
return 'No NocoBase env is configured yet. Run `nb init` to create one first.';
|
|
116
|
+
return 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.';
|
|
117
117
|
}
|
|
118
118
|
export async function runLocalNocoBaseCommand(runtime, args, options) {
|
|
119
119
|
const envVars = await buildRuntimeEnvVars(runtime);
|
package/dist/lib/auth-store.js
CHANGED
|
@@ -32,6 +32,13 @@ function normalizeOptionalCliLocale(value) {
|
|
|
32
32
|
}
|
|
33
33
|
return normalizeCliLocale(normalized);
|
|
34
34
|
}
|
|
35
|
+
function normalizeOptionalCliUpdatePolicy(value) {
|
|
36
|
+
const normalized = normalizeOptionalString(value);
|
|
37
|
+
if (normalized === 'prompt' || normalized === 'auto' || normalized === 'off') {
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
35
42
|
export function readEnvApiBaseUrl(config) {
|
|
36
43
|
if (!config) {
|
|
37
44
|
return undefined;
|
|
@@ -79,10 +86,12 @@ function normalizeEnvConfigEntry(entry) {
|
|
|
79
86
|
function normalizeAuthConfig(config) {
|
|
80
87
|
const settings = config.settings ?? {};
|
|
81
88
|
const locale = normalizeOptionalCliLocale(settings.locale);
|
|
89
|
+
const updatePolicy = normalizeOptionalCliUpdatePolicy(settings.update?.policy);
|
|
82
90
|
return {
|
|
83
91
|
name: config.name || config.dockerResourcePrefix,
|
|
84
92
|
settings: {
|
|
85
93
|
...(locale ? { locale } : {}),
|
|
94
|
+
...(updatePolicy ? { update: { policy: updatePolicy } } : {}),
|
|
86
95
|
...(settings.license?.pkgUrl ? { license: { pkgUrl: normalizeOptionalString(settings.license.pkgUrl) } } : {}),
|
|
87
96
|
...(settings.docker?.network || settings.docker?.containerPrefix
|
|
88
97
|
? {
|
package/dist/lib/backup.js
CHANGED
|
@@ -62,14 +62,14 @@ export async function resolveBackupTargetEnv(requestedEnv) {
|
|
|
62
62
|
const { envs } = await listEnvs({ scope });
|
|
63
63
|
const configuredEnvNames = Object.keys(envs);
|
|
64
64
|
if (!configuredEnvNames.length) {
|
|
65
|
-
throw new Error('No env is configured. Run `nb
|
|
65
|
+
throw new Error('No env is configured. Run `nb init --ui` first.');
|
|
66
66
|
}
|
|
67
67
|
if (requestedEnv?.trim()) {
|
|
68
|
-
throw new Error(`Env "${envName}" is not configured. Run \`nb env
|
|
68
|
+
throw new Error(`Env "${envName}" is not configured. Run \`nb init --ui --env ${envName}\` first.`);
|
|
69
69
|
}
|
|
70
70
|
throw new Error([
|
|
71
71
|
`Current env "${envName}" is not configured.`,
|
|
72
|
-
|
|
72
|
+
`Switch to an existing env with \`nb env use <name>\`, or run \`nb init --ui --env ${envName}\` to create or connect it.`,
|
|
73
73
|
].join('\n'));
|
|
74
74
|
}
|
|
75
75
|
export async function ensureBackupRuntimeCommands(params) {
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -254,25 +254,31 @@ export function formatSwaggerSchemaError(response, context) {
|
|
|
254
254
|
})
|
|
255
255
|
.join('\n');
|
|
256
256
|
const envLabel = context.envName ? ` for env "${context.envName}"` : '';
|
|
257
|
+
const tokenHint = context.envName
|
|
258
|
+
? `Update the API key with \`nb env update ${context.envName} --token <api-key>\`, log in with \`nb env auth ${context.envName}\`, or rerun the command with \`--access-token <api-key>\`.`
|
|
259
|
+
: 'Update the API key with `nb env update <name> --token <api-key>`, log in with `nb env auth <name>`, or rerun the command with `--access-token <api-key>`.';
|
|
257
260
|
const commandHint = context.commandToken
|
|
258
|
-
? `If \`${context.commandToken}\` is a runtime command,
|
|
259
|
-
: 'Run `nb --help` to inspect built-in commands, then
|
|
261
|
+
? `If \`${context.commandToken}\` is a runtime command, retry it after updating the token. If it is a typo, run \`nb --help\` to inspect available commands.`
|
|
262
|
+
: 'Run `nb --help` to inspect built-in commands, then retry the original command after updating the token.';
|
|
260
263
|
return [
|
|
261
264
|
`Authentication failed while loading the command runtime from \`swagger:get\`${envLabel}.`,
|
|
262
265
|
`Base URL: ${context.baseUrl}`,
|
|
263
266
|
details,
|
|
264
|
-
|
|
267
|
+
tokenHint,
|
|
265
268
|
commandHint,
|
|
266
269
|
].join('\n');
|
|
267
270
|
}
|
|
268
271
|
if (isNetworkFetchFailure(response)) {
|
|
269
272
|
const rawMessage = response.data?.error?.message || 'fetch failed';
|
|
273
|
+
const updateConnectionHint = context.envName
|
|
274
|
+
? `If you recently changed the server address, update env "${context.envName}" with \`nb env update ${context.envName} --api-base-url <url>\` and retry the original command.`
|
|
275
|
+
: 'If you recently changed the server address, update the saved env connection with `nb env update <name> --api-base-url <url>` and retry the original command.';
|
|
270
276
|
return [
|
|
271
277
|
'Failed to reach the NocoBase server while loading the command runtime from `swagger:get`.',
|
|
272
278
|
`Base URL: ${context.baseUrl}`,
|
|
273
279
|
`Network error: ${rawMessage}`,
|
|
274
280
|
'Check that the NocoBase app is running, the base URL is correct, and the server is reachable from this machine.',
|
|
275
|
-
|
|
281
|
+
updateConnectionHint,
|
|
276
282
|
'Use `nb env list` to inspect the current env configuration.',
|
|
277
283
|
].join('\n');
|
|
278
284
|
}
|
|
@@ -282,7 +288,7 @@ export function formatMissingRuntimeEnvError(commandToken) {
|
|
|
282
288
|
if (!commandToken) {
|
|
283
289
|
return [
|
|
284
290
|
'No env is configured for runtime commands.',
|
|
285
|
-
'Run `nb
|
|
291
|
+
'Run `nb init --ui` first.',
|
|
286
292
|
'If you configure multiple environments later, switch with `nb env use <name>`.',
|
|
287
293
|
].join('\n');
|
|
288
294
|
}
|
|
@@ -290,7 +296,7 @@ export function formatMissingRuntimeEnvError(commandToken) {
|
|
|
290
296
|
`Unable to resolve runtime command \`${commandToken}\`.`,
|
|
291
297
|
'No env is configured, so the CLI cannot load runtime commands from `swagger:get`.',
|
|
292
298
|
'If this is a built-in command or a typo, run `nb --help` to inspect available commands.',
|
|
293
|
-
'If this should be an application runtime command, run `nb
|
|
299
|
+
'If this should be an application runtime command, run `nb init --ui` to create or connect a NocoBase app first.',
|
|
294
300
|
].join('\n');
|
|
295
301
|
}
|
|
296
302
|
export async function ensureRuntimeFromArgv(argv, options) {
|
|
@@ -366,8 +372,8 @@ export async function updateEnvRuntime(options) {
|
|
|
366
372
|
throw new Error([
|
|
367
373
|
env
|
|
368
374
|
? `Env "${envName}" is missing a base URL.`
|
|
369
|
-
: `Env "${envName}" is not configured. Run \`nb env
|
|
370
|
-
env ?
|
|
375
|
+
: `Env "${envName}" is not configured. Run \`nb init --ui --env ${envName}\` first.`,
|
|
376
|
+
env ? `Update env "${envName}" with \`nb env update ${envName} --api-base-url <url>\` first.` : '',
|
|
371
377
|
]
|
|
372
378
|
.filter(Boolean)
|
|
373
379
|
.join('\n'));
|
package/dist/lib/cli-config.js
CHANGED
|
@@ -15,8 +15,11 @@ export const DEFAULT_DOCKER_CONTAINER_PREFIX = 'nb';
|
|
|
15
15
|
export const DEFAULT_DOCKER_BIN = 'docker';
|
|
16
16
|
export const DEFAULT_GIT_BIN = 'git';
|
|
17
17
|
export const DEFAULT_YARN_BIN = 'yarn';
|
|
18
|
+
export const CLI_UPDATE_POLICY_OPTIONS = ['prompt', 'auto', 'off'];
|
|
19
|
+
export const DEFAULT_UPDATE_POLICY = 'prompt';
|
|
18
20
|
export const SUPPORTED_CLI_CONFIG_KEYS = [
|
|
19
21
|
'locale',
|
|
22
|
+
'update.policy',
|
|
20
23
|
'license.pkg-url',
|
|
21
24
|
'docker.network',
|
|
22
25
|
'docker.container-prefix',
|
|
@@ -42,9 +45,17 @@ export function assertSupportedCliConfigKey(value) {
|
|
|
42
45
|
}
|
|
43
46
|
return value;
|
|
44
47
|
}
|
|
48
|
+
export function normalizeCliUpdatePolicy(value) {
|
|
49
|
+
const normalized = trimValue(value);
|
|
50
|
+
if (!normalized) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return CLI_UPDATE_POLICY_OPTIONS.includes(normalized) ? normalized : undefined;
|
|
54
|
+
}
|
|
45
55
|
function cloneSettings(config) {
|
|
46
56
|
return {
|
|
47
57
|
...(config.settings?.locale ? { locale: trimValue(config.settings.locale) } : {}),
|
|
58
|
+
update: config.settings?.update ? { ...config.settings.update } : undefined,
|
|
48
59
|
license: config.settings?.license ? { ...config.settings.license } : undefined,
|
|
49
60
|
docker: config.settings?.docker ? { ...config.settings.docker } : undefined,
|
|
50
61
|
bin: config.settings?.bin ? { ...config.settings.bin } : undefined,
|
|
@@ -54,6 +65,10 @@ function pruneSettings(config) {
|
|
|
54
65
|
if (config.settings && !trimValue(config.settings.locale)) {
|
|
55
66
|
delete config.settings.locale;
|
|
56
67
|
}
|
|
68
|
+
const update = config.settings?.update;
|
|
69
|
+
if (update && !normalizeCliUpdatePolicy(update.policy)) {
|
|
70
|
+
delete config.settings?.update;
|
|
71
|
+
}
|
|
57
72
|
const license = config.settings?.license;
|
|
58
73
|
if (license && !trimValue(license.pkgUrl)) {
|
|
59
74
|
delete config.settings?.license;
|
|
@@ -68,6 +83,7 @@ function pruneSettings(config) {
|
|
|
68
83
|
}
|
|
69
84
|
if (config.settings &&
|
|
70
85
|
!config.settings.locale &&
|
|
86
|
+
!config.settings.update &&
|
|
71
87
|
!config.settings.license &&
|
|
72
88
|
!config.settings.docker &&
|
|
73
89
|
!config.settings.bin) {
|
|
@@ -78,6 +94,8 @@ export function getExplicitCliConfigValue(config, key) {
|
|
|
78
94
|
switch (key) {
|
|
79
95
|
case 'locale':
|
|
80
96
|
return trimValue(config.settings?.locale);
|
|
97
|
+
case 'update.policy':
|
|
98
|
+
return normalizeCliUpdatePolicy(config.settings?.update?.policy);
|
|
81
99
|
case 'license.pkg-url':
|
|
82
100
|
return trimValue(config.settings?.license?.pkgUrl);
|
|
83
101
|
case 'docker.network':
|
|
@@ -100,6 +118,8 @@ export function getEffectiveCliConfigValue(config, key) {
|
|
|
100
118
|
switch (key) {
|
|
101
119
|
case 'locale':
|
|
102
120
|
return resolveCliLocale(undefined, { configuredLocale: trimValue(config.settings?.locale) });
|
|
121
|
+
case 'update.policy':
|
|
122
|
+
return explicit ?? DEFAULT_UPDATE_POLICY;
|
|
103
123
|
case 'license.pkg-url':
|
|
104
124
|
return DEFAULT_LICENSE_PKG_URL;
|
|
105
125
|
case 'docker.network':
|
|
@@ -129,6 +149,13 @@ export function normalizeCliConfigValue(key, value) {
|
|
|
129
149
|
}
|
|
130
150
|
return locale;
|
|
131
151
|
}
|
|
152
|
+
if (key === 'update.policy') {
|
|
153
|
+
const policy = normalizeCliUpdatePolicy(normalized);
|
|
154
|
+
if (!policy) {
|
|
155
|
+
throw new Error(`Config key "${key}" must be one of: ${CLI_UPDATE_POLICY_OPTIONS.join(', ')}`);
|
|
156
|
+
}
|
|
157
|
+
return policy;
|
|
158
|
+
}
|
|
132
159
|
return normalized;
|
|
133
160
|
}
|
|
134
161
|
export async function loadCliConfig(options = {}) {
|
|
@@ -158,6 +185,12 @@ export async function setCliConfigValue(key, value, options = {}) {
|
|
|
158
185
|
case 'locale':
|
|
159
186
|
config.settings.locale = normalized;
|
|
160
187
|
break;
|
|
188
|
+
case 'update.policy':
|
|
189
|
+
config.settings.update = {
|
|
190
|
+
...(config.settings.update ?? {}),
|
|
191
|
+
policy: normalized,
|
|
192
|
+
};
|
|
193
|
+
break;
|
|
161
194
|
case 'license.pkg-url':
|
|
162
195
|
config.settings.license = {
|
|
163
196
|
...(config.settings.license ?? {}),
|
|
@@ -211,6 +244,11 @@ export async function deleteCliConfigValue(key, options = {}) {
|
|
|
211
244
|
case 'locale':
|
|
212
245
|
delete config.settings.locale;
|
|
213
246
|
break;
|
|
247
|
+
case 'update.policy':
|
|
248
|
+
if (config.settings.update) {
|
|
249
|
+
delete config.settings.update.policy;
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
214
252
|
case 'license.pkg-url':
|
|
215
253
|
if (config.settings.license) {
|
|
216
254
|
delete config.settings.license.pkgUrl;
|
|
@@ -0,0 +1,44 @@
|
|
|
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 function getCommandPathTokens(argv) {
|
|
10
|
+
const tokens = [];
|
|
11
|
+
for (const token of argv) {
|
|
12
|
+
if (!token) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (token.startsWith('-')) {
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
tokens.push(token);
|
|
19
|
+
}
|
|
20
|
+
return tokens;
|
|
21
|
+
}
|
|
22
|
+
export function formatCliEntryError(error, argv) {
|
|
23
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24
|
+
const missingCommandMatch = message.match(/^Command (.+) not found\.$/);
|
|
25
|
+
if (!missingCommandMatch) {
|
|
26
|
+
return message;
|
|
27
|
+
}
|
|
28
|
+
const commandPathTokens = getCommandPathTokens(argv);
|
|
29
|
+
const attemptedCommand = commandPathTokens.join(' ') || missingCommandMatch[1];
|
|
30
|
+
const isApiCommand = commandPathTokens[0] === 'api';
|
|
31
|
+
if (isApiCommand) {
|
|
32
|
+
const helpCommandTokens = commandPathTokens.length > 2 ? commandPathTokens.slice(0, -1) : ['api'];
|
|
33
|
+
const helpCommand = `nb ${helpCommandTokens.join(' ')} --help`;
|
|
34
|
+
return [
|
|
35
|
+
`Unknown command: \`${attemptedCommand}\`.`,
|
|
36
|
+
`If this is a built-in command or a typo, run \`${helpCommand}\` to inspect the commands available under that API group.`,
|
|
37
|
+
].join('\n');
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
`Unknown command: \`${attemptedCommand}\`.`,
|
|
41
|
+
'If this is a built-in command or a typo, run `nb --help` to inspect available commands.',
|
|
42
|
+
`If \`${attemptedCommand}\` should be a runtime command from your NocoBase app, check whether the connected app exposes it, then retry the command.`,
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { spawn } from 'node:child_process';
|
|
10
|
+
const DEFAULT_DOCKER_LOG_TAIL = 50;
|
|
11
|
+
export function startDockerLogFollower(containerName, options) {
|
|
12
|
+
const tail = Math.max(0, options?.tail ?? DEFAULT_DOCKER_LOG_TAIL);
|
|
13
|
+
const child = spawn('docker', ['logs', '--tail', String(tail), '--follow', containerName], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
});
|
|
16
|
+
let settled = false;
|
|
17
|
+
let resolveClosed;
|
|
18
|
+
const closed = new Promise((resolve) => {
|
|
19
|
+
resolveClosed = resolve;
|
|
20
|
+
});
|
|
21
|
+
const settle = () => {
|
|
22
|
+
if (!settled) {
|
|
23
|
+
settled = true;
|
|
24
|
+
resolveClosed();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
child.once('error', settle);
|
|
28
|
+
child.once('close', settle);
|
|
29
|
+
return {
|
|
30
|
+
stop: async () => {
|
|
31
|
+
if (settled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
if (!child.kill()) {
|
|
36
|
+
settle();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
settle();
|
|
41
|
+
}
|
|
42
|
+
await closed;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
package/dist/lib/env-auth.js
CHANGED
|
@@ -629,19 +629,19 @@ async function createLoopbackServer(state) {
|
|
|
629
629
|
});
|
|
630
630
|
let resolveWaiter;
|
|
631
631
|
let rejectWaiter;
|
|
632
|
-
const waitForCode = () => new Promise((
|
|
632
|
+
const waitForCode = () => new Promise((resolve, reject) => {
|
|
633
633
|
resolveWaiter = (code) => {
|
|
634
634
|
void close();
|
|
635
|
-
|
|
635
|
+
resolve(code);
|
|
636
636
|
};
|
|
637
637
|
rejectWaiter = (error) => {
|
|
638
638
|
void close();
|
|
639
|
-
|
|
639
|
+
reject(error);
|
|
640
640
|
};
|
|
641
641
|
});
|
|
642
642
|
const close = async () => {
|
|
643
|
-
await new Promise((
|
|
644
|
-
server.close(() =>
|
|
643
|
+
await new Promise((resolve) => {
|
|
644
|
+
server.close(() => resolve());
|
|
645
645
|
});
|
|
646
646
|
};
|
|
647
647
|
server.on('error', (error) => {
|
|
@@ -772,7 +772,7 @@ export async function resolveAccessToken(options) {
|
|
|
772
772
|
}
|
|
773
773
|
const baseUrl = options.baseUrl ?? env.baseUrl;
|
|
774
774
|
if (!baseUrl) {
|
|
775
|
-
throw new Error(`Env "${envName}" is missing a base URL.
|
|
775
|
+
throw new Error(`Env "${envName}" is missing a base URL. Update it with \`nb env update ${envName} --api-base-url <url>\` first.`);
|
|
776
776
|
}
|
|
777
777
|
printVerbose(`Refreshing OAuth session for env "${envName}"`);
|
|
778
778
|
return refreshOauthAccessToken({
|
|
@@ -793,7 +793,12 @@ export async function resolveServerRequestTarget(options) {
|
|
|
793
793
|
scope: options.scope,
|
|
794
794
|
});
|
|
795
795
|
if (!baseUrl) {
|
|
796
|
-
throw new Error(
|
|
796
|
+
throw new Error([
|
|
797
|
+
env ? `Env "${envName}" is missing a base URL.` : `Env "${envName}" is not configured.`,
|
|
798
|
+
env
|
|
799
|
+
? `Use --api-base-url or update env "${envName}" with \`nb env update ${envName} --api-base-url <url>\`.`
|
|
800
|
+
: `Use --api-base-url or run \`nb init --ui --env ${envName}\` first.`,
|
|
801
|
+
].join('\n'));
|
|
797
802
|
}
|
|
798
803
|
return { baseUrl, token };
|
|
799
804
|
}
|
|
@@ -807,8 +812,8 @@ export async function authenticateEnvWithBasic(options) {
|
|
|
807
812
|
? `Environment "${envName}" does not have an API base URL yet.`
|
|
808
813
|
: `Environment "${envName}" has not been set up yet.`,
|
|
809
814
|
env
|
|
810
|
-
? `Run \`nb env
|
|
811
|
-
: `Run \`nb env
|
|
815
|
+
? `Run \`nb env update ${envName} --api-base-url <url>\` to finish setting it up.`
|
|
816
|
+
: `Run \`nb init --ui --env ${envName}\` first.`,
|
|
812
817
|
]
|
|
813
818
|
.filter(Boolean)
|
|
814
819
|
.join('\n'));
|
|
@@ -864,8 +869,8 @@ export async function authenticateEnvWithOauth(options) {
|
|
|
864
869
|
? `Environment "${envName}" does not have an API base URL yet.`
|
|
865
870
|
: `Environment "${envName}" has not been set up yet.`,
|
|
866
871
|
env
|
|
867
|
-
? `Run \`nb env
|
|
868
|
-
: `Run \`nb env
|
|
872
|
+
? `Run \`nb env update ${envName} --api-base-url <url>\` to finish setting it up.`
|
|
873
|
+
: `Run \`nb init --ui --env ${envName}\` first.`,
|
|
869
874
|
]
|
|
870
875
|
.filter(Boolean)
|
|
871
876
|
.join('\n'));
|
|
@@ -912,10 +917,13 @@ export async function authenticateEnvWithOauth(options) {
|
|
|
912
917
|
const code = await new Promise((resolve, reject) => {
|
|
913
918
|
const timeout = setTimeout(() => reject(new Error(`OAuth sign-in timed out after 5 minutes. Run \`nb env auth ${envName}\` to try again.`)), OAUTH_LOGIN_TIMEOUT_MS);
|
|
914
919
|
timeout.unref?.();
|
|
915
|
-
callback
|
|
920
|
+
callback
|
|
921
|
+
.waitForCode()
|
|
922
|
+
.then((value) => {
|
|
916
923
|
clearTimeout(timeout);
|
|
917
924
|
resolve(value);
|
|
918
|
-
}
|
|
925
|
+
})
|
|
926
|
+
.catch((error) => {
|
|
919
927
|
clearTimeout(timeout);
|
|
920
928
|
reject(error);
|
|
921
929
|
});
|
|
@@ -292,6 +292,9 @@ export async function publishSourceSnapshot(params) {
|
|
|
292
292
|
if (publishError) {
|
|
293
293
|
throw publishError;
|
|
294
294
|
}
|
|
295
|
+
if (!result) {
|
|
296
|
+
throw new Error('Source snapshot publishing finished without a result.');
|
|
297
|
+
}
|
|
295
298
|
return result;
|
|
296
299
|
}
|
|
297
300
|
export function buildSuggestedInitCommand(result) {
|
|
@@ -299,7 +302,7 @@ export function buildSuggestedInitCommand(result) {
|
|
|
299
302
|
const normalizedRegistry = result.npmRegistry || `http://${host}:${port || DEFAULT_SOURCE_REGISTRY_PORT}`;
|
|
300
303
|
const suggestedEnv = ['snapshot', sanitizeEnvSegment(result.gitSha)].filter(Boolean).join('');
|
|
301
304
|
return [
|
|
302
|
-
`nb init --env ${suggestedEnv} --yes --source npm`,
|
|
305
|
+
`nb init --ui --env ${suggestedEnv} --yes --source npm`,
|
|
303
306
|
`--version ${result.version}`,
|
|
304
307
|
`--npm-registry=${normalizedRegistry}`,
|
|
305
308
|
].join(' ');
|