@nocobase/cli 2.2.0-beta.1 → 2.2.0-beta.10
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/assets/env-proxy/nginx/snippets/proxy-location.conf +1 -0
- package/bin/early-locale.js +89 -0
- package/bin/node-version.js +35 -0
- package/bin/run.js +9 -0
- package/bin/windows-admin.js +60 -0
- package/dist/commands/app/destroy.js +4 -3
- package/dist/commands/app/restart.js +38 -0
- package/dist/commands/app/shared.js +49 -3
- package/dist/commands/app/start.js +95 -0
- package/dist/commands/app/upgrade.js +11 -0
- package/dist/commands/env/info.js +11 -1
- package/dist/commands/examples/prompts-stages.js +2 -2
- package/dist/commands/examples/prompts-test.js +2 -2
- package/dist/commands/init.js +33 -11
- package/dist/commands/install.js +159 -107
- package/dist/commands/license/activate.js +4 -1
- package/dist/commands/license/shared.js +24 -15
- package/dist/commands/proxy/caddy/generate.js +93 -7
- package/dist/commands/proxy/nginx/generate.js +98 -7
- package/dist/commands/revision/create.js +1 -1
- package/dist/commands/self/check.js +1 -1
- package/dist/commands/self/update.js +4 -4
- package/dist/commands/skills/check.js +4 -5
- package/dist/commands/skills/install.js +18 -1
- package/dist/commands/skills/update.js +19 -4
- package/dist/commands/source/dev.js +9 -5
- package/dist/commands/source/download.js +85 -16
- package/dist/lib/api-command-compat.js +51 -8
- package/dist/lib/app-managed-resources.js +104 -5
- package/dist/lib/auth-store.js +102 -12
- package/dist/lib/cli-config.js +73 -1
- package/dist/lib/docker-image.js +94 -6
- package/dist/lib/env-auth.js +291 -45
- package/dist/lib/env-config.js +11 -0
- package/dist/lib/env-proxy-config.js +48 -0
- package/dist/lib/env-proxy.js +164 -58
- package/dist/lib/hook-script.js +160 -0
- package/dist/lib/prompt-catalog-terminal.js +32 -19
- package/dist/lib/prompt-validators.js +1 -1
- package/dist/lib/prompt-web-ui.js +20 -13
- package/dist/lib/proxy-caddy.js +77 -9
- package/dist/lib/proxy-nginx.js +71 -11
- package/dist/lib/run-npm.js +4 -0
- package/dist/lib/self-manager.js +254 -46
- package/dist/lib/skills-manager.js +116 -23
- package/dist/lib/source-publish.js +2 -2
- package/dist/lib/startup-update.js +1 -1
- package/dist/locale/en-US.json +49 -43
- package/dist/locale/zh-CN.json +49 -43
- package/package.json +8 -2
- package/scripts/build.mjs +0 -34
- package/scripts/clean.mjs +0 -9
- package/tsconfig.json +0 -19
|
@@ -10,8 +10,8 @@ import { Flags } from '@oclif/core';
|
|
|
10
10
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { getEnvAsync, getInstanceIdAsync, keyDecrypt } from '@nocobase/license-kit';
|
|
13
|
-
import { checkExternalDbConnection, readExternalDbConnectionConfig
|
|
14
|
-
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef
|
|
13
|
+
import { checkExternalDbConnection, readExternalDbConnectionConfig } from "../../lib/db-connection-check.js";
|
|
14
|
+
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef } from "../../lib/docker-image.js";
|
|
15
15
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
|
|
16
16
|
import { buildRuntimeEnvVars } from '../../lib/runtime-env-vars.js';
|
|
17
17
|
import { resolveLicensePkgUrlFromConfig } from '../../lib/cli-config.js';
|
|
@@ -102,12 +102,7 @@ function buildDockerLicenseDbFlagArgs(envVars) {
|
|
|
102
102
|
];
|
|
103
103
|
}
|
|
104
104
|
async function runDockerLicenseJsonCommand(runtime, commandArgs) {
|
|
105
|
-
const args = [
|
|
106
|
-
'run',
|
|
107
|
-
'--rm',
|
|
108
|
-
'--network',
|
|
109
|
-
runtime.dockerNetworkName || runtime.workspaceName,
|
|
110
|
-
];
|
|
105
|
+
const args = ['run', '--rm', '--network', runtime.dockerNetworkName || runtime.workspaceName];
|
|
111
106
|
const dockerPlatform = normalizeDockerPlatform(runtime.env.config?.dockerPlatform);
|
|
112
107
|
if (dockerPlatform) {
|
|
113
108
|
args.push('--platform', dockerPlatform);
|
|
@@ -193,11 +188,11 @@ export async function generateValidatedInstanceIdFromEnvVars(envVars) {
|
|
|
193
188
|
}
|
|
194
189
|
async function generateInstanceIdForDockerRuntime(runtime) {
|
|
195
190
|
const envVars = await buildRuntimeEnvVars(runtime);
|
|
196
|
-
const payload = await runDockerLicenseJsonCommand(runtime, [
|
|
191
|
+
const payload = (await runDockerLicenseJsonCommand(runtime, [
|
|
197
192
|
'license',
|
|
198
193
|
'generate-id',
|
|
199
194
|
...buildDockerLicenseDbFlagArgs(envVars),
|
|
200
|
-
]);
|
|
195
|
+
]));
|
|
201
196
|
const instanceId = trimValue(payload.instanceId);
|
|
202
197
|
if (!instanceId) {
|
|
203
198
|
throw new Error('Docker instance ID generation did not return an instance ID.');
|
|
@@ -337,7 +332,7 @@ export async function getLicenseStatus(keyData) {
|
|
|
337
332
|
}),
|
|
338
333
|
signal: controller.signal,
|
|
339
334
|
});
|
|
340
|
-
const payload = await response.json();
|
|
335
|
+
const payload = (await response.json());
|
|
341
336
|
return payload?.data?.status === 'active' ? 'active' : 'invalid';
|
|
342
337
|
}
|
|
343
338
|
catch {
|
|
@@ -356,6 +351,22 @@ export async function validateLicenseKey(runtime, key) {
|
|
|
356
351
|
catch {
|
|
357
352
|
keyStatus = 'invalid';
|
|
358
353
|
}
|
|
354
|
+
if (keyStatus) {
|
|
355
|
+
const currentDomain = appUrl(runtime);
|
|
356
|
+
return {
|
|
357
|
+
current: {
|
|
358
|
+
env: undefined,
|
|
359
|
+
domain: currentDomain ? new URL(currentDomain).host : '',
|
|
360
|
+
},
|
|
361
|
+
keyData,
|
|
362
|
+
keyStatus,
|
|
363
|
+
dbMatch: false,
|
|
364
|
+
sysMatch: false,
|
|
365
|
+
envMatch: false,
|
|
366
|
+
domainMatch: false,
|
|
367
|
+
licenseStatus: 'invalid',
|
|
368
|
+
};
|
|
369
|
+
}
|
|
359
370
|
const currentEnv = await getCurrentLicenseEnv(runtime);
|
|
360
371
|
const currentDomain = appUrl(runtime);
|
|
361
372
|
const dbMatch = isDbMatch(currentEnv, keyData);
|
|
@@ -391,7 +402,7 @@ export async function resolveLicenseServiceUrl(value) {
|
|
|
391
402
|
return (await resolveLicensePkgUrl(value)).replace(/\/+$/, '');
|
|
392
403
|
}
|
|
393
404
|
export async function resolveLicensePkgUrl(value) {
|
|
394
|
-
const normalized = String(value ?? '').trim() || await resolveLicensePkgUrlFromConfig();
|
|
405
|
+
const normalized = String(value ?? '').trim() || (await resolveLicensePkgUrlFromConfig());
|
|
395
406
|
return normalized.replace(/\/+$/, '') + '/';
|
|
396
407
|
}
|
|
397
408
|
function shouldRedactOutputKey(key) {
|
|
@@ -414,9 +425,7 @@ export function sanitizeLicenseOutput(value) {
|
|
|
414
425
|
if (value && typeof value === 'object') {
|
|
415
426
|
return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
|
|
416
427
|
key,
|
|
417
|
-
shouldRedactOutputKey(key)
|
|
418
|
-
? redactOutputValue(String(nestedValue ?? ''))
|
|
419
|
-
: sanitizeLicenseOutput(nestedValue),
|
|
428
|
+
shouldRedactOutputKey(key) ? redactOutputValue(String(nestedValue ?? '')) : sanitizeLicenseOutput(nestedValue),
|
|
420
429
|
]));
|
|
421
430
|
}
|
|
422
431
|
return value;
|
|
@@ -8,20 +8,51 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveEnvProxyEntry, setEnvProxyEntry } from '../../../lib/auth-store.js';
|
|
12
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
13
|
+
import { getCaddyProxyDriver, writeManualCaddyProxyBundle, writeCaddyProxyBundle, resolveCaddyProxyRuntimeContext, } from '../../../lib/proxy-caddy.js';
|
|
12
14
|
import { normalizeProxyListenPort } from '../../../lib/proxy-nginx.js';
|
|
13
15
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../../lib/ui.js';
|
|
14
16
|
export default class ProxyCaddyGenerate extends Command {
|
|
15
17
|
static summary = 'Generate caddy proxy files for one managed env';
|
|
16
18
|
static examples = [
|
|
19
|
+
'<%= config.bin %> proxy caddy generate --host app1.example.com',
|
|
17
20
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com',
|
|
18
21
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com --port 8080',
|
|
22
|
+
'<%= config.bin %> proxy caddy generate --manual --name default --storage-path /path/to/storage --dist-root-path /path/to/dist-client --runtime-version 2.1.0 --upstream-port 13000',
|
|
19
23
|
];
|
|
20
24
|
static flags = {
|
|
21
25
|
env: Flags.string({
|
|
22
26
|
char: 'e',
|
|
23
|
-
description: 'CLI env name to generate proxy files for',
|
|
24
|
-
|
|
27
|
+
description: 'CLI env name to generate proxy files for. Defaults to the current env when omitted',
|
|
28
|
+
}),
|
|
29
|
+
manual: Flags.boolean({
|
|
30
|
+
description: 'Generate proxy files from explicit runtime flags instead of a saved env',
|
|
31
|
+
default: false,
|
|
32
|
+
}),
|
|
33
|
+
name: Flags.string({
|
|
34
|
+
description: 'Output bundle name used under .nocobase/proxy/caddy in manual mode',
|
|
35
|
+
}),
|
|
36
|
+
'storage-path': Flags.string({
|
|
37
|
+
description: 'Path to the NocoBase storage directory in manual mode',
|
|
38
|
+
}),
|
|
39
|
+
'dist-root-path': Flags.string({
|
|
40
|
+
description: 'Path to the dist-client root directory used to generate index-v1.html and index-v2.html in manual mode',
|
|
41
|
+
}),
|
|
42
|
+
'runtime-version': Flags.string({
|
|
43
|
+
description: 'Frontend runtime version under dist-root-path in manual mode',
|
|
44
|
+
}),
|
|
45
|
+
'app-public-path': Flags.string({
|
|
46
|
+
description: 'Public base path served by the proxied app in manual mode. Defaults to /',
|
|
47
|
+
}),
|
|
48
|
+
'upstream-host': Flags.string({
|
|
49
|
+
description: 'Upstream host used by caddy reverse_proxy in manual mode',
|
|
50
|
+
}),
|
|
51
|
+
'upstream-port': Flags.string({
|
|
52
|
+
description: 'Upstream port used by caddy reverse_proxy in manual mode',
|
|
53
|
+
}),
|
|
54
|
+
'cdn-base-url': Flags.string({
|
|
55
|
+
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
25
56
|
}),
|
|
26
57
|
host: Flags.string({
|
|
27
58
|
description: 'Host exposed by the caddy site block, such as example.com or localhost',
|
|
@@ -35,9 +66,56 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
35
66
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
36
67
|
const requestedPort = flags.port?.trim() || undefined;
|
|
37
68
|
const normalizedPort = normalizeProxyListenPort(requestedPort);
|
|
69
|
+
const manual = Boolean(flags.manual);
|
|
38
70
|
if (requestedPort && !normalizedPort) {
|
|
39
71
|
this.error(`Invalid proxy entry port "${requestedPort}". Use an integer between 1 and 65535.`);
|
|
40
72
|
}
|
|
73
|
+
if (manual && requestedEnv) {
|
|
74
|
+
this.error('`--manual` cannot be combined with `--env`.');
|
|
75
|
+
}
|
|
76
|
+
if (manual) {
|
|
77
|
+
const name = flags.name?.trim() || undefined;
|
|
78
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
79
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
80
|
+
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
81
|
+
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
82
|
+
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
83
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
84
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
85
|
+
}
|
|
86
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
87
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
88
|
+
}
|
|
89
|
+
const driver = await getCaddyProxyDriver();
|
|
90
|
+
const runtimeContext = await resolveCaddyProxyRuntimeContext();
|
|
91
|
+
announceTargetEnv(name);
|
|
92
|
+
startTask(`Generating caddy proxy config for env "${name}" with the ${driver} driver...`);
|
|
93
|
+
try {
|
|
94
|
+
const { bundle, status } = await writeManualCaddyProxyBundle({
|
|
95
|
+
name,
|
|
96
|
+
storagePath,
|
|
97
|
+
distRootPath,
|
|
98
|
+
runtimeVersion,
|
|
99
|
+
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
100
|
+
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
101
|
+
upstreamPort,
|
|
102
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
103
|
+
}, {
|
|
104
|
+
host: flags.host?.trim() || undefined,
|
|
105
|
+
port: normalizedPort,
|
|
106
|
+
}, runtimeContext, {
|
|
107
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
108
|
+
});
|
|
109
|
+
succeedTask(status === 'created'
|
|
110
|
+
? `Saved caddy proxy files for env "${name}" under ${bundle.entryDir}, and created app.caddy at ${bundle.appConfigPath}.`
|
|
111
|
+
: `Saved caddy proxy files for env "${name}" under ${bundle.entryDir}, and refreshed app.caddy at ${bundle.appConfigPath}.`);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
failTask(`Failed to generate caddy proxy config for env "${name}".`);
|
|
115
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
41
119
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
42
120
|
if (!runtime) {
|
|
43
121
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -53,10 +131,18 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
53
131
|
announceTargetEnv(runtime.envName);
|
|
54
132
|
startTask(`Generating caddy proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
55
133
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
134
|
+
const savedAppEntryOptions = resolveEnvProxyEntry(runtime.env.config, 'caddy');
|
|
135
|
+
const appEntryOptions = {
|
|
136
|
+
host: flags.host?.trim() || savedAppEntryOptions?.host,
|
|
137
|
+
port: normalizedPort ?? (savedAppEntryOptions?.port !== undefined ? String(savedAppEntryOptions.port) : undefined),
|
|
138
|
+
};
|
|
139
|
+
const { bundle, status } = await writeCaddyProxyBundle(runtime, appEntryOptions, runtimeContext, {
|
|
140
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
141
|
+
});
|
|
142
|
+
await setEnvProxyEntry(runtime.envName, 'caddy', {
|
|
143
|
+
host: appEntryOptions.host,
|
|
144
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
145
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
60
146
|
succeedTask(status === 'created'
|
|
61
147
|
? `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created app.caddy at ${bundle.appConfigPath}.`
|
|
62
148
|
: `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed app.caddy at ${bundle.appConfigPath}.`);
|
|
@@ -8,19 +8,54 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
12
|
+
import { getNginxProxyDriver, normalizeProxyListenPort, resolveNginxProxyRuntimeContext, writeManualNginxProxyBundle, writeNginxProxyBundle, } from '../../../lib/proxy-nginx.js';
|
|
13
|
+
import { resolveEnvProxyEntry, setEnvProxyEntry } from '../../../lib/auth-store.js';
|
|
12
14
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../../lib/ui.js';
|
|
13
15
|
export default class ProxyNginxGenerate extends Command {
|
|
14
16
|
static summary = 'Generate nginx proxy files for one managed env';
|
|
15
17
|
static examples = [
|
|
18
|
+
'<%= config.bin %> proxy nginx generate --host app1.example.com',
|
|
16
19
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com',
|
|
17
20
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com --port 8080',
|
|
21
|
+
'<%= config.bin %> proxy nginx generate --manual --name default --storage-path /path/to/storage --dist-root-path /path/to/dist-client --runtime-version 2.1.0 --upstream-port 13000',
|
|
18
22
|
];
|
|
19
23
|
static flags = {
|
|
20
24
|
env: Flags.string({
|
|
21
25
|
char: 'e',
|
|
22
|
-
description: 'CLI env name to generate proxy files for',
|
|
23
|
-
|
|
26
|
+
description: 'CLI env name to generate proxy files for. Defaults to the current env when omitted',
|
|
27
|
+
}),
|
|
28
|
+
manual: Flags.boolean({
|
|
29
|
+
description: 'Generate proxy files from explicit runtime flags instead of a saved env',
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
name: Flags.string({
|
|
33
|
+
description: 'Output bundle name used under .nocobase/proxy/nginx in manual mode',
|
|
34
|
+
}),
|
|
35
|
+
'storage-path': Flags.string({
|
|
36
|
+
description: 'Path to the NocoBase storage directory in manual mode',
|
|
37
|
+
}),
|
|
38
|
+
'dist-root-path': Flags.string({
|
|
39
|
+
description: 'Path to the dist-client root directory used to generate index-v1.html and index-v2.html in manual mode',
|
|
40
|
+
}),
|
|
41
|
+
'runtime-version': Flags.string({
|
|
42
|
+
description: 'Frontend runtime version under dist-root-path in manual mode',
|
|
43
|
+
}),
|
|
44
|
+
'app-public-path': Flags.string({
|
|
45
|
+
description: 'Public base path served by the proxied app in manual mode. Defaults to /',
|
|
46
|
+
}),
|
|
47
|
+
'upstream-host': Flags.string({
|
|
48
|
+
description: 'Upstream host used by nginx proxy_pass in manual mode',
|
|
49
|
+
}),
|
|
50
|
+
'upstream-port': Flags.string({
|
|
51
|
+
description: 'Upstream port used by nginx proxy_pass in manual mode',
|
|
52
|
+
}),
|
|
53
|
+
'cdn-base-url': Flags.string({
|
|
54
|
+
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
55
|
+
}),
|
|
56
|
+
force: Flags.boolean({
|
|
57
|
+
description: 'Overwrite existing app.conf even when the managed block is missing',
|
|
58
|
+
default: false,
|
|
24
59
|
}),
|
|
25
60
|
host: Flags.string({
|
|
26
61
|
description: 'Host exposed by the nginx entry config, such as example.com or localhost',
|
|
@@ -34,9 +69,56 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
34
69
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
35
70
|
const requestedPort = flags.port?.trim() || undefined;
|
|
36
71
|
const normalizedPort = normalizeProxyListenPort(requestedPort);
|
|
72
|
+
const manual = Boolean(flags.manual);
|
|
37
73
|
if (requestedPort && !normalizedPort) {
|
|
38
74
|
this.error(`Invalid proxy entry port "${requestedPort}". Use an integer between 1 and 65535.`);
|
|
39
75
|
}
|
|
76
|
+
if (manual && requestedEnv) {
|
|
77
|
+
this.error('`--manual` cannot be combined with `--env`.');
|
|
78
|
+
}
|
|
79
|
+
if (manual) {
|
|
80
|
+
const name = flags.name?.trim() || undefined;
|
|
81
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
82
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
83
|
+
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
84
|
+
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
85
|
+
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
86
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
87
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
88
|
+
}
|
|
89
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
90
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
91
|
+
}
|
|
92
|
+
const driver = await getNginxProxyDriver();
|
|
93
|
+
const runtimeContext = await resolveNginxProxyRuntimeContext();
|
|
94
|
+
announceTargetEnv(name);
|
|
95
|
+
startTask(`Generating nginx proxy config for env "${name}" with the ${driver} driver...`);
|
|
96
|
+
try {
|
|
97
|
+
const { bundle, status } = await writeManualNginxProxyBundle({
|
|
98
|
+
name,
|
|
99
|
+
storagePath,
|
|
100
|
+
distRootPath,
|
|
101
|
+
runtimeVersion,
|
|
102
|
+
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
103
|
+
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
104
|
+
upstreamPort,
|
|
105
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
106
|
+
}, {
|
|
107
|
+
host: flags.host?.trim() || undefined,
|
|
108
|
+
port: normalizedPort,
|
|
109
|
+
}, runtimeContext, {
|
|
110
|
+
force: flags.force,
|
|
111
|
+
});
|
|
112
|
+
succeedTask(status === 'created'
|
|
113
|
+
? `Saved nginx proxy files for env "${name}" under ${bundle.entryDir}, and created editable app entry config at ${bundle.appConfigPath}.`
|
|
114
|
+
: `Saved nginx proxy files for env "${name}" under ${bundle.entryDir}, and refreshed editable app entry config at ${bundle.appConfigPath}.`);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
failTask(`Failed to generate nginx proxy config for env "${name}".`);
|
|
118
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
40
122
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
41
123
|
if (!runtime) {
|
|
42
124
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -52,10 +134,19 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
52
134
|
announceTargetEnv(runtime.envName);
|
|
53
135
|
startTask(`Generating nginx proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
54
136
|
try {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
137
|
+
const savedAppEntryOptions = resolveEnvProxyEntry(runtime.env.config, 'nginx');
|
|
138
|
+
const appEntryOptions = {
|
|
139
|
+
host: flags.host?.trim() || savedAppEntryOptions?.host,
|
|
140
|
+
port: normalizedPort ?? (savedAppEntryOptions?.port !== undefined ? String(savedAppEntryOptions.port) : undefined),
|
|
141
|
+
};
|
|
142
|
+
const { bundle, status } = await writeNginxProxyBundle(runtime, appEntryOptions, runtimeContext, {
|
|
143
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
144
|
+
force: flags.force,
|
|
145
|
+
});
|
|
146
|
+
await setEnvProxyEntry(runtime.envName, 'nginx', {
|
|
147
|
+
host: appEntryOptions.host,
|
|
148
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
149
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
59
150
|
succeedTask(status === 'created'
|
|
60
151
|
? `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created editable app entry config at ${bundle.appConfigPath}.`
|
|
61
152
|
: `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed editable app entry config at ${bundle.appConfigPath}.`);
|
|
@@ -20,7 +20,7 @@ export default class SelfCheck extends Command {
|
|
|
20
20
|
static flags = {
|
|
21
21
|
channel: Flags.string({
|
|
22
22
|
description: 'Release channel to compare against. Defaults to the current CLI channel.',
|
|
23
|
-
options: ['auto', 'latest', 'beta', 'alpha'],
|
|
23
|
+
options: ['auto', 'latest', 'test', 'beta', 'alpha'],
|
|
24
24
|
default: 'auto',
|
|
25
25
|
}),
|
|
26
26
|
json: Flags.boolean({
|
|
@@ -26,17 +26,17 @@ function formatSkillsUpdateMessage(result, verbose) {
|
|
|
26
26
|
}
|
|
27
27
|
export default class SelfUpdate extends Command {
|
|
28
28
|
static summary = 'Update the globally installed NocoBase CLI';
|
|
29
|
-
static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm install.';
|
|
29
|
+
static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm, pnpm, or yarn install.';
|
|
30
30
|
static examples = [
|
|
31
31
|
'<%= config.bin %> <%= command.id %>',
|
|
32
32
|
'<%= config.bin %> <%= command.id %> --yes',
|
|
33
33
|
'<%= config.bin %> <%= command.id %> --skills',
|
|
34
|
-
'<%= config.bin %> <%= command.id %> --channel
|
|
34
|
+
'<%= config.bin %> <%= command.id %> --channel test --json',
|
|
35
35
|
];
|
|
36
36
|
static flags = {
|
|
37
37
|
channel: Flags.string({
|
|
38
38
|
description: 'Release channel to update to. Defaults to the current CLI channel.',
|
|
39
|
-
options: ['auto', 'latest', 'beta', 'alpha'],
|
|
39
|
+
options: ['auto', 'latest', 'test', 'beta', 'alpha'],
|
|
40
40
|
default: 'auto',
|
|
41
41
|
}),
|
|
42
42
|
yes: Flags.boolean({
|
|
@@ -77,7 +77,7 @@ export default class SelfUpdate extends Command {
|
|
|
77
77
|
message: flags.skills
|
|
78
78
|
? `Update ${status.packageName} from ${status.currentVersion} to ${status.latestVersion} and refresh the globally installed NocoBase AI coding skills?`
|
|
79
79
|
: `Update ${status.packageName} from ${status.currentVersion} to ${status.latestVersion}?`,
|
|
80
|
-
default:
|
|
80
|
+
default: true,
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
catch {
|
|
@@ -12,10 +12,7 @@ import { printInfo, renderTable } from '../../lib/ui.js';
|
|
|
12
12
|
export default class SkillsCheck extends Command {
|
|
13
13
|
static summary = 'Check the globally installed NocoBase AI coding skills';
|
|
14
14
|
static description = 'Inspect the global NocoBase AI coding skills and report whether they are managed by the CLI and whether an update is available.';
|
|
15
|
-
static examples = [
|
|
16
|
-
'<%= config.bin %> <%= command.id %>',
|
|
17
|
-
'<%= config.bin %> <%= command.id %> --json',
|
|
18
|
-
];
|
|
15
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
|
|
19
16
|
static flags = {
|
|
20
17
|
json: Flags.boolean({
|
|
21
18
|
description: 'Output the result as JSON',
|
|
@@ -25,6 +22,7 @@ export default class SkillsCheck extends Command {
|
|
|
25
22
|
async run() {
|
|
26
23
|
const { flags } = await this.parse(SkillsCheck);
|
|
27
24
|
const status = await inspectSkillsStatus();
|
|
25
|
+
const displaySkillNames = status.packageSkillNames.length ? status.packageSkillNames : status.installedSkillNames;
|
|
28
26
|
if (flags.json) {
|
|
29
27
|
this.log(JSON.stringify({
|
|
30
28
|
ok: true,
|
|
@@ -35,6 +33,7 @@ export default class SkillsCheck extends Command {
|
|
|
35
33
|
managedByNb: status.managedByNb,
|
|
36
34
|
sourcePackage: status.sourcePackage,
|
|
37
35
|
npmPackageName: status.npmPackageName,
|
|
36
|
+
packageSkillNames: status.packageSkillNames,
|
|
38
37
|
installedSkillNames: status.installedSkillNames,
|
|
39
38
|
installedVersion: status.installedVersion,
|
|
40
39
|
latestVersion: status.latestVersion,
|
|
@@ -50,7 +49,7 @@ export default class SkillsCheck extends Command {
|
|
|
50
49
|
['Skills home', status.globalRoot],
|
|
51
50
|
['Installed', status.installed ? 'yes' : 'no'],
|
|
52
51
|
['Managed by nb', status.managedByNb ? 'yes' : 'no'],
|
|
53
|
-
['Installed skills',
|
|
52
|
+
['Installed skills', displaySkillNames.length ? displaySkillNames.join(', ') : '(none)'],
|
|
54
53
|
['Installed version', status.installedVersion ?? '(unknown)'],
|
|
55
54
|
['Latest version', status.latestVersion ?? '(unknown)'],
|
|
56
55
|
['Update available', status.updateAvailable === null ? 'unknown' : status.updateAvailable ? 'yes' : 'no'],
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { confirm } from "../../lib/inquirer.js";
|
|
11
|
-
import { setVerboseMode } from '../../lib/ui.js';
|
|
11
|
+
import { setVerboseMode, startTask, stopTask, updateTask } from '../../lib/ui.js';
|
|
12
12
|
import { installNocoBaseSkills } from '../../lib/skills-manager.js';
|
|
13
13
|
export default class SkillsInstall extends Command {
|
|
14
14
|
static summary = 'Install the NocoBase AI coding skills globally';
|
|
@@ -16,6 +16,8 @@ export default class SkillsInstall extends Command {
|
|
|
16
16
|
static examples = [
|
|
17
17
|
'<%= config.bin %> <%= command.id %>',
|
|
18
18
|
'<%= config.bin %> <%= command.id %> --yes',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --version 1.0.4',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> --verbose',
|
|
19
21
|
'<%= config.bin %> <%= command.id %> --json',
|
|
20
22
|
];
|
|
21
23
|
static flags = {
|
|
@@ -32,6 +34,9 @@ export default class SkillsInstall extends Command {
|
|
|
32
34
|
description: 'Show detailed install output',
|
|
33
35
|
default: false,
|
|
34
36
|
}),
|
|
37
|
+
version: Flags.string({
|
|
38
|
+
description: 'Install a specific @nocobase/skills version',
|
|
39
|
+
}),
|
|
35
40
|
};
|
|
36
41
|
async run() {
|
|
37
42
|
const { flags } = await this.parse(SkillsInstall);
|
|
@@ -51,8 +56,20 @@ export default class SkillsInstall extends Command {
|
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
}
|
|
59
|
+
const shouldShowLoading = !flags.json && !flags.verbose;
|
|
60
|
+
if (shouldShowLoading) {
|
|
61
|
+
startTask(flags.version
|
|
62
|
+
? `Installing NocoBase AI coding skills ${flags.version}...`
|
|
63
|
+
: 'Installing NocoBase AI coding skills...');
|
|
64
|
+
}
|
|
54
65
|
const result = await installNocoBaseSkills({
|
|
66
|
+
targetVersion: flags.version,
|
|
55
67
|
verbose: flags.verbose,
|
|
68
|
+
onProgress: shouldShowLoading ? updateTask : undefined,
|
|
69
|
+
}).finally(() => {
|
|
70
|
+
if (shouldShowLoading) {
|
|
71
|
+
stopTask();
|
|
72
|
+
}
|
|
56
73
|
});
|
|
57
74
|
if (flags.json) {
|
|
58
75
|
this.log(JSON.stringify({
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { confirm } from "../../lib/inquirer.js";
|
|
11
|
-
import { setVerboseMode } from '../../lib/ui.js';
|
|
11
|
+
import { setVerboseMode, startTask, stopTask, updateTask } from '../../lib/ui.js';
|
|
12
12
|
import { updateNocoBaseSkills } from '../../lib/skills-manager.js';
|
|
13
13
|
export default class SkillsUpdate extends Command {
|
|
14
14
|
static summary = 'Update the globally installed NocoBase AI coding skills';
|
|
@@ -16,6 +16,8 @@ export default class SkillsUpdate extends Command {
|
|
|
16
16
|
static examples = [
|
|
17
17
|
'<%= config.bin %> <%= command.id %>',
|
|
18
18
|
'<%= config.bin %> <%= command.id %> --yes',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --version 1.0.4',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> --verbose',
|
|
19
21
|
'<%= config.bin %> <%= command.id %> --json',
|
|
20
22
|
];
|
|
21
23
|
static flags = {
|
|
@@ -32,6 +34,9 @@ export default class SkillsUpdate extends Command {
|
|
|
32
34
|
description: 'Show detailed update output',
|
|
33
35
|
default: false,
|
|
34
36
|
}),
|
|
37
|
+
version: Flags.string({
|
|
38
|
+
description: 'Sync to a specific @nocobase/skills version',
|
|
39
|
+
}),
|
|
35
40
|
};
|
|
36
41
|
async run() {
|
|
37
42
|
const { flags } = await this.parse(SkillsUpdate);
|
|
@@ -51,8 +56,20 @@ export default class SkillsUpdate extends Command {
|
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
}
|
|
59
|
+
const shouldShowLoading = !flags.json && !flags.verbose;
|
|
60
|
+
if (shouldShowLoading) {
|
|
61
|
+
startTask(flags.version
|
|
62
|
+
? `Syncing NocoBase AI coding skills to ${flags.version}...`
|
|
63
|
+
: 'Updating NocoBase AI coding skills...');
|
|
64
|
+
}
|
|
54
65
|
const result = await updateNocoBaseSkills({
|
|
66
|
+
targetVersion: flags.version,
|
|
55
67
|
verbose: flags.verbose,
|
|
68
|
+
onProgress: shouldShowLoading ? updateTask : undefined,
|
|
69
|
+
}).finally(() => {
|
|
70
|
+
if (shouldShowLoading) {
|
|
71
|
+
stopTask();
|
|
72
|
+
}
|
|
56
73
|
});
|
|
57
74
|
if (flags.json) {
|
|
58
75
|
this.log(JSON.stringify({
|
|
@@ -80,8 +97,6 @@ export default class SkillsUpdate extends Command {
|
|
|
80
97
|
: 'NocoBase AI coding skills are up to date.');
|
|
81
98
|
return;
|
|
82
99
|
}
|
|
83
|
-
this.log(flags.verbose
|
|
84
|
-
? 'Updated the global NocoBase AI coding skills.'
|
|
85
|
-
: 'Updated NocoBase AI coding skills globally.');
|
|
100
|
+
this.log(flags.verbose ? 'Updated the global NocoBase AI coding skills.' : 'Updated NocoBase AI coding skills globally.');
|
|
86
101
|
}
|
|
87
102
|
}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
11
|
-
import { ensureLocalPostinstall } from '../../lib/app-managed-resources.js';
|
|
11
|
+
import { ensureLocalPostinstall, ensureNpmSourceDevDependencies } from '../../lib/app-managed-resources.js';
|
|
12
12
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
13
|
+
import { findAvailableTcpPort } from '../../lib/prompt-validators.js';
|
|
13
14
|
import { announceTargetEnv, failTask, printInfo, startTask, succeedTask } from '../../lib/ui.js';
|
|
14
15
|
function formatUnsupportedRuntimeMessage(kind, envName) {
|
|
15
16
|
if (kind === 'docker') {
|
|
@@ -129,10 +130,7 @@ export default class SourceDev extends Command {
|
|
|
129
130
|
this.error(formatUnsupportedRuntimeMessage(runtime.kind, runtime.envName));
|
|
130
131
|
}
|
|
131
132
|
announceTargetEnv(runtime.envName);
|
|
132
|
-
const devPort = flags.port ||
|
|
133
|
-
(runtime.env.appPort !== undefined && runtime.env.appPort !== null
|
|
134
|
-
? String(runtime.env.appPort).trim()
|
|
135
|
-
: undefined);
|
|
133
|
+
const devPort = flags.port?.trim() || (await findAvailableTcpPort());
|
|
136
134
|
const appUrl = appUrlForPort(devPort);
|
|
137
135
|
if (await isAppAlreadyRunning(appUrl)) {
|
|
138
136
|
this.error([
|
|
@@ -160,6 +158,12 @@ export default class SourceDev extends Command {
|
|
|
160
158
|
}
|
|
161
159
|
printInfo(`Starting NocoBase dev mode for "${runtime.envName}" from ${runtime.projectRoot}. Press Ctrl+C to stop.`);
|
|
162
160
|
try {
|
|
161
|
+
await ensureNpmSourceDevDependencies(runtime, {
|
|
162
|
+
onStartTask: startTask,
|
|
163
|
+
onSucceedTask: succeedTask,
|
|
164
|
+
onFailTask: failTask,
|
|
165
|
+
verbose: true,
|
|
166
|
+
});
|
|
163
167
|
await ensureLocalPostinstall(runtime, {
|
|
164
168
|
onStartTask: startTask,
|
|
165
169
|
onSucceedTask: succeedTask,
|