@nocobase/cli 2.2.0-alpha.1 → 2.2.0-alpha.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/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 +92 -0
- package/dist/commands/app/upgrade.js +11 -0
- package/dist/commands/examples/prompts-stages.js +2 -2
- package/dist/commands/examples/prompts-test.js +2 -2
- package/dist/commands/init.js +22 -10
- package/dist/commands/install.js +135 -4
- package/dist/commands/license/activate.js +4 -1
- package/dist/commands/license/shared.js +24 -15
- 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 +67 -2
- package/dist/lib/api-command-compat.js +51 -8
- package/dist/lib/app-managed-resources.js +101 -1
- package/dist/lib/auth-store.js +34 -12
- package/dist/lib/cli-config.js +19 -0
- package/dist/lib/env-config.js +6 -0
- package/dist/lib/hook-script.js +160 -0
- package/dist/lib/prompt-web-ui.js +7 -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 +10 -4
- package/dist/locale/zh-CN.json +10 -4
- package/package.json +7 -2
- package/assets/env-proxy/nginx/app.conf.tpl +0 -23
- package/assets/env-proxy/nginx/nocobase.conf.tpl +0 -5
- package/assets/env-proxy/nginx/snippets/dist-location.conf +0 -5
- package/assets/env-proxy/nginx/snippets/gzip.conf +0 -17
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +0 -13
- package/assets/env-proxy/nginx/snippets/maps-http.conf +0 -14
- package/assets/env-proxy/nginx/snippets/mime-types.conf +0 -98
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +0 -17
- package/assets/env-proxy/nginx/snippets/spa-location.conf +0 -6
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +0 -21
- package/scripts/build.mjs +0 -34
- package/scripts/clean.mjs +0 -9
- package/tsconfig.json +0 -19
package/dist/commands/init.js
CHANGED
|
@@ -15,7 +15,7 @@ import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
|
15
15
|
import { getEnv, upsertEnv } from "../lib/auth-store.js";
|
|
16
16
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
17
17
|
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
18
|
-
import { resolveDefaultConfigScope } from '../lib/cli-home.js';
|
|
18
|
+
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
|
|
19
19
|
import { resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
|
|
20
20
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
21
21
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
@@ -23,7 +23,9 @@ import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
|
|
|
23
23
|
import { validateApiBaseUrl, validateEnvKey } from "../lib/prompt-validators.js";
|
|
24
24
|
import { installNocoBaseSkills, isNpmRegistryUnavailable } from '../lib/skills-manager.js';
|
|
25
25
|
import { omitKeys, pickKeys } from "../lib/object-utils.js";
|
|
26
|
+
import { ENV_CONFIG_SCHEMA_VERSION } from '../lib/env-config.js';
|
|
26
27
|
import { printInfo, printStage, printVerbose, printWarning } from '../lib/ui.js';
|
|
28
|
+
import { persistHookScript } from '../lib/hook-script.js';
|
|
27
29
|
import Download from "./download.js";
|
|
28
30
|
import EnvAdd from "./env/add.js";
|
|
29
31
|
import Install, { defaultDbPortForDialect } from "./install.js";
|
|
@@ -246,16 +248,12 @@ function formatBrowserOpenError(error) {
|
|
|
246
248
|
return String(error);
|
|
247
249
|
}
|
|
248
250
|
export default class Init extends Command {
|
|
249
|
-
static summary = 'Set up
|
|
250
|
-
static description = `Set up NocoBase
|
|
251
|
+
static summary = 'Set up or connect a NocoBase environment in the current workspace';
|
|
252
|
+
static description = `Set up NocoBase in the current workspace.
|
|
251
253
|
|
|
252
|
-
\`nb init\`
|
|
254
|
+
\`nb init\` helps you install a new NocoBase app, take over managing one that already exists on this machine, or connect a remote NocoBase app and save it as a CLI env.
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
- Take over managing an app that already exists on this machine by reusing its database.
|
|
256
|
-
- Connect a remote NocoBase app and save it as a CLI env.
|
|
257
|
-
|
|
258
|
-
It can also install NocoBase AI coding skills (\`nocobase/skills\`) so agents get the project-specific workflow guidance.
|
|
256
|
+
You can use the saved environment directly, or let a coding agent access it later. It can also install NocoBase AI coding skills (\`nocobase/skills\`) when you want agent-specific workflow guidance.
|
|
259
257
|
|
|
260
258
|
If setup was interrupted earlier, use \`--resume\` with an existing env name to continue from the saved workspace config.
|
|
261
259
|
|
|
@@ -475,7 +473,7 @@ Prompt modes:
|
|
|
475
473
|
default: false,
|
|
476
474
|
}),
|
|
477
475
|
'ui-host': Flags.string({
|
|
478
|
-
description: '
|
|
476
|
+
description: 'Browser-accessible host for the --ui setup page URL (default: 127.0.0.1)',
|
|
479
477
|
}),
|
|
480
478
|
'ui-port': Flags.integer({
|
|
481
479
|
description: 'Port for the local --ui setup server; 0 lets the OS choose an available port',
|
|
@@ -1032,6 +1030,14 @@ Prompt modes:
|
|
|
1032
1030
|
const skipDownload = results.skipDownload === true;
|
|
1033
1031
|
const appKey = resolveManagedAppKey(results.appKey ?? existingEnv?.config.appKey);
|
|
1034
1032
|
const timeZone = resolveManagedTimeZone(results.timeZone ?? existingEnv?.config.timezone);
|
|
1033
|
+
const hookScriptInput = String(flags['hook-script'] ?? '').trim();
|
|
1034
|
+
const hookAppPath = appPath || `./${envName}/`;
|
|
1035
|
+
const hookScript = hookScriptInput
|
|
1036
|
+
? await persistHookScript({
|
|
1037
|
+
sourcePath: hookScriptInput,
|
|
1038
|
+
appPath: resolveConfiguredEnvPath(hookAppPath) ?? resolveEnvRelativePath(hookAppPath),
|
|
1039
|
+
})
|
|
1040
|
+
: String(results.hookScript ?? '').trim();
|
|
1035
1041
|
const builtinDb = explicitDbHostFlag(flags)
|
|
1036
1042
|
? false
|
|
1037
1043
|
: results.builtinDb === undefined
|
|
@@ -1040,6 +1046,7 @@ Prompt modes:
|
|
|
1040
1046
|
results.appKey = appKey;
|
|
1041
1047
|
results.timeZone = timeZone;
|
|
1042
1048
|
await upsertEnv(envName, {
|
|
1049
|
+
schemaVersion: ENV_CONFIG_SCHEMA_VERSION,
|
|
1043
1050
|
...(source === 'docker'
|
|
1044
1051
|
? { kind: 'docker' }
|
|
1045
1052
|
: source || appPath || appRootPath
|
|
@@ -1057,6 +1064,7 @@ Prompt modes:
|
|
|
1057
1064
|
...(dockerPlatform ? { dockerPlatform } : {}),
|
|
1058
1065
|
...(gitUrl ? { gitUrl } : {}),
|
|
1059
1066
|
...(npmRegistry ? { npmRegistry } : {}),
|
|
1067
|
+
...(hookScript ? { hookScript } : {}),
|
|
1060
1068
|
...(appPath ? { appPath } : {}),
|
|
1061
1069
|
...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
|
|
1062
1070
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
@@ -1132,6 +1140,10 @@ Prompt modes:
|
|
|
1132
1140
|
if (flags['prepare-only']) {
|
|
1133
1141
|
argv.push('--prepare-only');
|
|
1134
1142
|
}
|
|
1143
|
+
const hookScript = String(results.hookScript ?? flags['hook-script'] ?? '').trim();
|
|
1144
|
+
if (hookScript) {
|
|
1145
|
+
argv.push('--hook-script', hookScript);
|
|
1146
|
+
}
|
|
1135
1147
|
if (flags.verbose) {
|
|
1136
1148
|
argv.push('--verbose');
|
|
1137
1149
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -15,7 +15,7 @@ import { appendAppPublicPath } from '../lib/app-public-path.js';
|
|
|
15
15
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
16
16
|
import { applyCliLocale, localeText, resolveCliLocale, translateCli } from "../lib/cli-locale.js";
|
|
17
17
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
|
|
18
|
-
import { defaultDockerContainerPrefix, defaultDockerNetworkName } from '../lib/app-runtime.js';
|
|
18
|
+
import { defaultDockerContainerPrefix, defaultDockerNetworkName, managedAppLifecycleEnvVars, } from '../lib/app-runtime.js';
|
|
19
19
|
import { resolveDefaultApiHost, resolveDockerContainerPrefix, resolveDockerNetworkName } from '../lib/cli-config.js';
|
|
20
20
|
import { DEFAULT_DOCKER_VERSION, resolveDockerImageRef } from "../lib/docker-image.js";
|
|
21
21
|
import { findAvailableTcpPort, validateAppPublicPath, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
@@ -29,6 +29,7 @@ import { buildStoredEnvConfig } from '../lib/env-config.js';
|
|
|
29
29
|
import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
|
|
30
30
|
import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
31
31
|
import { buildInitAppEnvVarsFromConfig } from '../lib/managed-init-env.js';
|
|
32
|
+
import { buildHookContext, persistHookScript, resolveHookScriptPath, runHookScriptHook, } from '../lib/hook-script.js';
|
|
32
33
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
33
34
|
import Download, { defaultDockerRegistryForLang } from './download.js';
|
|
34
35
|
import EnvAdd from "./env/add.js";
|
|
@@ -472,7 +473,10 @@ export default class Install extends Command {
|
|
|
472
473
|
description: 'Skip the download step and reuse an existing local app directory or Docker image',
|
|
473
474
|
default: false,
|
|
474
475
|
}),
|
|
475
|
-
|
|
476
|
+
'hook-script': Flags.string({
|
|
477
|
+
description: 'Hook module copied to <appPath>/.nb/hooks.mjs and run before npm/git dependency installation.',
|
|
478
|
+
}),
|
|
479
|
+
...omitKeys(Download.flags, ['yes', 'hook-script']),
|
|
476
480
|
};
|
|
477
481
|
/** Environment name only: run before {@link Install.prompts} (see `run`). */
|
|
478
482
|
static envPrompts = {
|
|
@@ -1095,6 +1099,7 @@ export default class Install extends Command {
|
|
|
1095
1099
|
const appPort = Install.toOptionalPromptString(config.appPort);
|
|
1096
1100
|
const storagePath = Install.toOptionalPromptString(config.storagePath);
|
|
1097
1101
|
const appPublicPath = Install.toOptionalPromptString(config.appPublicPath);
|
|
1102
|
+
const hookScript = Install.toOptionalPromptString(config.hookScript);
|
|
1098
1103
|
const apiBaseUrl = Install.toOptionalPromptString(config.apiBaseUrl);
|
|
1099
1104
|
const downloadVersion = Install.toOptionalPromptString(config.downloadVersion);
|
|
1100
1105
|
const dockerRegistry = Install.toOptionalPromptString(config.dockerRegistry);
|
|
@@ -1125,6 +1130,7 @@ export default class Install extends Command {
|
|
|
1125
1130
|
...(appPort ? { appPort } : {}),
|
|
1126
1131
|
...(storagePath ? { storagePath } : {}),
|
|
1127
1132
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1133
|
+
...(hookScript ? { hookScript } : {}),
|
|
1128
1134
|
};
|
|
1129
1135
|
const downloadPreset = {
|
|
1130
1136
|
...(source ? { source } : {}),
|
|
@@ -1916,6 +1922,12 @@ export default class Install extends Command {
|
|
|
1916
1922
|
if (results.buildDts) {
|
|
1917
1923
|
argv.push('--build-dts');
|
|
1918
1924
|
}
|
|
1925
|
+
Install.pushDownloadArgIfValue(argv, '--hook-script', results.hookScript);
|
|
1926
|
+
Install.pushDownloadArgIfValue(argv, '--hook-phase', results.hookPhase);
|
|
1927
|
+
Install.pushDownloadArgIfValue(argv, '--hook-command', results.hookCommand);
|
|
1928
|
+
Install.pushDownloadArgIfValue(argv, '--hook-env-name', results.hookEnvName);
|
|
1929
|
+
Install.pushDownloadArgIfValue(argv, '--hook-app-path', results.hookAppPath);
|
|
1930
|
+
Install.pushDownloadArgIfValue(argv, '--hook-storage-path', results.hookStoragePath);
|
|
1919
1931
|
return argv;
|
|
1920
1932
|
}
|
|
1921
1933
|
static resolveLocalProjectRoot(params) {
|
|
@@ -1942,6 +1954,40 @@ export default class Install extends Command {
|
|
|
1942
1954
|
buildDts: false,
|
|
1943
1955
|
};
|
|
1944
1956
|
}
|
|
1957
|
+
static resolveAbsoluteAppPath(envName, appResults) {
|
|
1958
|
+
const configuredAppPath = resolveConfiguredAppPathValue(appResults) ?? defaultInstallAppPath(envName);
|
|
1959
|
+
return resolveConfiguredEnvPath(configuredAppPath) ?? resolveEnvRelativePath(defaultInstallAppPath(envName));
|
|
1960
|
+
}
|
|
1961
|
+
static resolveAbsoluteStoragePath(envName, appResults) {
|
|
1962
|
+
const configuredStoragePath = resolveConfiguredStoragePathValue(appResults, envName);
|
|
1963
|
+
return (resolveConfiguredEnvPath(configuredStoragePath) ?? resolveEnvRelativePath(defaultInstallStoragePath(envName)));
|
|
1964
|
+
}
|
|
1965
|
+
async prepareHookScriptForInstall(params) {
|
|
1966
|
+
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
1967
|
+
const storagePath = Install.resolveAbsoluteStoragePath(params.envName, params.appResults);
|
|
1968
|
+
const inputHookScript = Install.toOptionalPromptString(params.hookScript);
|
|
1969
|
+
if (inputHookScript) {
|
|
1970
|
+
params.appResults.hookScript = await persistHookScript({
|
|
1971
|
+
sourcePath: inputHookScript,
|
|
1972
|
+
appPath,
|
|
1973
|
+
});
|
|
1974
|
+
printInfo(`Saved hook script to ${path.join(appPath, String(params.appResults.hookScript))}.`);
|
|
1975
|
+
}
|
|
1976
|
+
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
1977
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
1978
|
+
appPath,
|
|
1979
|
+
hookScript: savedHookScript,
|
|
1980
|
+
});
|
|
1981
|
+
if (!hookScriptPath) {
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
params.downloadResults.hookScript = hookScriptPath;
|
|
1985
|
+
params.downloadResults.hookPhase = 'init';
|
|
1986
|
+
params.downloadResults.hookCommand = 'init';
|
|
1987
|
+
params.downloadResults.hookEnvName = params.envName;
|
|
1988
|
+
params.downloadResults.hookAppPath = appPath;
|
|
1989
|
+
params.downloadResults.hookStoragePath = storagePath;
|
|
1990
|
+
}
|
|
1945
1991
|
commandStdio(verbose) {
|
|
1946
1992
|
return verbose ? 'inherit' : 'ignore';
|
|
1947
1993
|
}
|
|
@@ -2011,7 +2057,9 @@ export default class Install extends Command {
|
|
|
2011
2057
|
const dbDialect = String(params.dbResults.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
2012
2058
|
const appKey = Install.resolveManagedAppKey(params.appResults.appKey);
|
|
2013
2059
|
const timeZone = Install.resolveManagedTimeZone(params.appResults.timeZone);
|
|
2060
|
+
const lifecycleEnvVars = managedAppLifecycleEnvVars();
|
|
2014
2061
|
const env = {
|
|
2062
|
+
...lifecycleEnvVars,
|
|
2015
2063
|
STORAGE_PATH: storagePath,
|
|
2016
2064
|
APP_PORT: String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT,
|
|
2017
2065
|
APP_KEY: appKey,
|
|
@@ -2216,6 +2264,44 @@ export default class Install extends Command {
|
|
|
2216
2264
|
}
|
|
2217
2265
|
await this.config.runCommand('env:update', [params.envName]);
|
|
2218
2266
|
}
|
|
2267
|
+
async runInstallHookIfNeeded(params) {
|
|
2268
|
+
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2269
|
+
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
2270
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
2271
|
+
appPath,
|
|
2272
|
+
hookScript: savedHookScript,
|
|
2273
|
+
});
|
|
2274
|
+
if (!hookScriptPath) {
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
const storagePath = Install.resolveAbsoluteStoragePath(params.envName, params.appResults);
|
|
2278
|
+
const sourcePath = params.projectRoot ??
|
|
2279
|
+
resolveConfiguredEnvPath(resolveConfiguredSourcePathValue(params.appResults, params.envName)) ??
|
|
2280
|
+
path.join(appPath, 'source');
|
|
2281
|
+
const context = buildHookContext({
|
|
2282
|
+
phase: 'init',
|
|
2283
|
+
command: 'init',
|
|
2284
|
+
envName: params.envName,
|
|
2285
|
+
source: params.source,
|
|
2286
|
+
version: downloadResultsValue(params.downloadResults, 'version'),
|
|
2287
|
+
appPath,
|
|
2288
|
+
sourcePath,
|
|
2289
|
+
storagePath,
|
|
2290
|
+
hookScript: savedHookScript,
|
|
2291
|
+
envConfig: Install.buildSavedEnvConfig(params, {
|
|
2292
|
+
defaultApiHost: params.defaultApiHost,
|
|
2293
|
+
}),
|
|
2294
|
+
});
|
|
2295
|
+
if (!context) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
this.log(`Running hook ${params.hookName}: ${hookScriptPath}`);
|
|
2299
|
+
await runHookScriptHook({
|
|
2300
|
+
hookScriptPath,
|
|
2301
|
+
hookName: params.hookName,
|
|
2302
|
+
context,
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2219
2305
|
static buildSavedEnvConfig(params, options = {}) {
|
|
2220
2306
|
const appPath = resolveConfiguredAppPathValue(params.appResults);
|
|
2221
2307
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
@@ -2247,6 +2333,7 @@ export default class Install extends Command {
|
|
|
2247
2333
|
devDependencies: downloadResultsValue(params.downloadResults, 'devDependencies'),
|
|
2248
2334
|
build: downloadResultsValue(params.downloadResults, 'build'),
|
|
2249
2335
|
buildDts: downloadResultsValue(params.downloadResults, 'buildDts'),
|
|
2336
|
+
hookScript: params.appResults.hookScript,
|
|
2250
2337
|
...(appPath ? { appPath } : {}),
|
|
2251
2338
|
...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
|
|
2252
2339
|
appPort,
|
|
@@ -2311,6 +2398,9 @@ export default class Install extends Command {
|
|
|
2311
2398
|
yesInitialValues: { resume: parsed.resume },
|
|
2312
2399
|
yes,
|
|
2313
2400
|
});
|
|
2401
|
+
if (resumePreset?.appPreset?.hookScript !== undefined && appResults.hookScript === undefined) {
|
|
2402
|
+
appResults.hookScript = resumePreset.appPreset.hookScript;
|
|
2403
|
+
}
|
|
2314
2404
|
const downloadOpts = Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2315
2405
|
downloadOpts.values = {
|
|
2316
2406
|
...(resumePreset?.downloadPreset ?? {}),
|
|
@@ -2440,6 +2530,12 @@ export default class Install extends Command {
|
|
|
2440
2530
|
appResults,
|
|
2441
2531
|
});
|
|
2442
2532
|
appResults.setupState = 'prepared';
|
|
2533
|
+
await this.prepareHookScriptForInstall({
|
|
2534
|
+
envName,
|
|
2535
|
+
appResults,
|
|
2536
|
+
downloadResults,
|
|
2537
|
+
hookScript: parsed['hook-script'],
|
|
2538
|
+
});
|
|
2443
2539
|
const source = String(downloadResultsValue(downloadResults, 'source') ?? '').trim();
|
|
2444
2540
|
const usesDockerResources = Boolean(dbResults.builtinDb) || source === 'docker';
|
|
2445
2541
|
const dockerNetworkName = usesDockerResources
|
|
@@ -2505,6 +2601,17 @@ export default class Install extends Command {
|
|
|
2505
2601
|
printInfo('Application image ready.');
|
|
2506
2602
|
}
|
|
2507
2603
|
if (!parsed['prepare-only']) {
|
|
2604
|
+
await this.runInstallHookIfNeeded({
|
|
2605
|
+
hookName: 'beforeAppInstall',
|
|
2606
|
+
envName,
|
|
2607
|
+
source,
|
|
2608
|
+
appResults,
|
|
2609
|
+
downloadResults,
|
|
2610
|
+
dbResults,
|
|
2611
|
+
rootResults,
|
|
2612
|
+
envAddResults,
|
|
2613
|
+
defaultApiHost,
|
|
2614
|
+
});
|
|
2508
2615
|
dockerAppPlan = await this.installDockerApp({
|
|
2509
2616
|
envName,
|
|
2510
2617
|
dockerNetworkName,
|
|
@@ -2523,7 +2630,7 @@ export default class Install extends Command {
|
|
|
2523
2630
|
}
|
|
2524
2631
|
else if (source === 'npm' || source === 'git') {
|
|
2525
2632
|
const localSource = source === 'npm' ? 'npm' : 'git';
|
|
2526
|
-
const projectRoot = parsed['skip-download']
|
|
2633
|
+
const projectRoot = parsed['skip-download'] || parsed['prepare-only']
|
|
2527
2634
|
? Install.resolveLocalProjectRoot({
|
|
2528
2635
|
envName,
|
|
2529
2636
|
appResults,
|
|
@@ -2535,10 +2642,22 @@ export default class Install extends Command {
|
|
|
2535
2642
|
downloadResults,
|
|
2536
2643
|
verbose: parsed.verbose,
|
|
2537
2644
|
});
|
|
2538
|
-
if (!parsed['skip-download']) {
|
|
2645
|
+
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2539
2646
|
printInfo('Application files ready.');
|
|
2540
2647
|
}
|
|
2541
2648
|
if (!parsed['prepare-only']) {
|
|
2649
|
+
await this.runInstallHookIfNeeded({
|
|
2650
|
+
hookName: 'beforeAppInstall',
|
|
2651
|
+
envName,
|
|
2652
|
+
source,
|
|
2653
|
+
appResults,
|
|
2654
|
+
downloadResults,
|
|
2655
|
+
dbResults,
|
|
2656
|
+
rootResults,
|
|
2657
|
+
envAddResults,
|
|
2658
|
+
projectRoot,
|
|
2659
|
+
defaultApiHost,
|
|
2660
|
+
});
|
|
2542
2661
|
localAppPlan = await this.startLocalApp({
|
|
2543
2662
|
envName,
|
|
2544
2663
|
source: localSource,
|
|
@@ -2573,6 +2692,18 @@ export default class Install extends Command {
|
|
|
2573
2692
|
});
|
|
2574
2693
|
printInfo(`NocoBase is ready at ${formatInstallDisplayUrl(displayApiBaseUrl)}`);
|
|
2575
2694
|
appResults.setupState = 'installed';
|
|
2695
|
+
await this.runInstallHookIfNeeded({
|
|
2696
|
+
hookName: 'afterAppStart',
|
|
2697
|
+
envName,
|
|
2698
|
+
source,
|
|
2699
|
+
appResults,
|
|
2700
|
+
downloadResults,
|
|
2701
|
+
dbResults,
|
|
2702
|
+
rootResults,
|
|
2703
|
+
envAddResults,
|
|
2704
|
+
projectRoot: localAppPlan?.projectRoot,
|
|
2705
|
+
defaultApiHost,
|
|
2706
|
+
});
|
|
2576
2707
|
}
|
|
2577
2708
|
if (dockerAppPlan || localAppPlan || builtinDbPlan) {
|
|
2578
2709
|
await this.saveInstalledEnv({
|
|
@@ -143,11 +143,14 @@ export default class LicenseActivate extends Command {
|
|
|
143
143
|
const validation = await validateLicenseKey(runtime, resolvedKey);
|
|
144
144
|
const ok = !validation.keyStatus && validation.envMatch && validation.domainMatch && validation.licenseStatus === 'active';
|
|
145
145
|
const licenseKeyPath = ok ? await saveLicenseKey(runtime, resolvedKey) : resolveLicenseKeyFile(runtime);
|
|
146
|
+
const shouldResolveInstanceId = Boolean(interactiveKeyFlowInstanceId || ok || (flags.json && !validation.keyStatus));
|
|
146
147
|
const payload = {
|
|
147
148
|
ok,
|
|
148
149
|
env: runtime.envName,
|
|
149
150
|
kind: runtime.kind,
|
|
150
|
-
instanceId:
|
|
151
|
+
instanceId: shouldResolveInstanceId
|
|
152
|
+
? interactiveKeyFlowInstanceId ?? (await ensureInstanceId(runtime))
|
|
153
|
+
: undefined,
|
|
151
154
|
mode: 'key',
|
|
152
155
|
key: redactLicenseKey(resolvedKey),
|
|
153
156
|
keyFile: keyFile || undefined,
|
|
@@ -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;
|
|
@@ -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
|
}
|