@nocobase/cli 2.1.0-alpha.24 → 2.1.0-alpha.25
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/README.md +8 -4
- package/README.zh-CN.md +6 -2
- package/bin/run.js +15 -0
- package/dist/commands/db/shared.js +19 -5
- package/dist/commands/dev.js +8 -1
- package/dist/commands/down.js +10 -6
- package/dist/commands/env/add.js +14 -34
- package/dist/commands/env/auth.js +6 -13
- package/dist/commands/env/list.js +10 -15
- package/dist/commands/env/remove.js +4 -10
- package/dist/commands/env/update.js +7 -13
- package/dist/commands/env/use.js +5 -13
- package/dist/commands/init.js +190 -62
- package/dist/commands/install.js +65 -26
- package/dist/commands/logs.js +8 -1
- package/dist/commands/pm/list.js +8 -1
- package/dist/commands/ps.js +18 -15
- package/dist/commands/restart.js +74 -0
- package/dist/commands/self/check.js +1 -1
- package/dist/commands/self/update.js +13 -3
- package/dist/commands/skills/check.js +11 -5
- package/dist/commands/skills/index.js +1 -1
- package/dist/commands/skills/install.js +20 -7
- package/dist/commands/skills/update.js +20 -7
- package/dist/commands/start.js +8 -1
- package/dist/commands/stop.js +8 -1
- package/dist/commands/upgrade.js +12 -1
- package/dist/lib/api-client.js +3 -2
- package/dist/lib/app-runtime.js +16 -5
- package/dist/lib/auth-store.js +159 -43
- package/dist/lib/bootstrap.js +13 -12
- package/dist/lib/cli-home.js +33 -2
- package/dist/lib/env-auth.js +3 -3
- package/dist/lib/generated-command.js +10 -2
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/resource-command.js +10 -2
- package/dist/lib/runtime-generator.js +1 -1
- package/dist/lib/self-manager.js +1 -1
- package/dist/lib/skills-manager.js +140 -73
- package/dist/lib/startup-update.js +203 -0
- package/dist/locale/en-US.json +4 -1
- package/dist/locale/zh-CN.json +4 -1
- package/package.json +2 -2
package/dist/lib/cli-home.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import fs from 'node:fs';
|
|
2
10
|
import os from 'node:os';
|
|
3
11
|
import path from 'node:path';
|
|
4
12
|
export const CLI_HOME_DIRNAME = '.nocobase';
|
|
13
|
+
export const NB_CONFIG_SCOPE_ENV = 'NB_CONFIG_SCOPE';
|
|
14
|
+
export const NB_ENV_ROOT_ENV = 'NB_ENV_ROOT';
|
|
15
|
+
export function resolveDefaultConfigScope() {
|
|
16
|
+
const raw = String(process.env[NB_CONFIG_SCOPE_ENV] ?? '').trim().toLowerCase();
|
|
17
|
+
return raw === 'project' ? 'project' : 'global';
|
|
18
|
+
}
|
|
5
19
|
function resolveGlobalCliHomeRoot() {
|
|
6
20
|
if (process.env.NOCOBASE_CTL_HOME) {
|
|
7
21
|
return process.env.NOCOBASE_CTL_HOME;
|
|
8
22
|
}
|
|
9
23
|
return os.homedir();
|
|
10
24
|
}
|
|
11
|
-
export function resolveCliHomeRoot(scope =
|
|
25
|
+
export function resolveCliHomeRoot(scope = resolveDefaultConfigScope()) {
|
|
12
26
|
const cwdRoot = process.cwd();
|
|
13
27
|
if (scope === 'project') {
|
|
14
28
|
return cwdRoot;
|
|
@@ -22,9 +36,26 @@ export function resolveCliHomeRoot(scope = 'auto') {
|
|
|
22
36
|
}
|
|
23
37
|
return resolveGlobalCliHomeRoot();
|
|
24
38
|
}
|
|
25
|
-
export function resolveCliHomeDir(scope =
|
|
39
|
+
export function resolveCliHomeDir(scope = resolveDefaultConfigScope()) {
|
|
26
40
|
return path.join(resolveCliHomeRoot(scope), CLI_HOME_DIRNAME);
|
|
27
41
|
}
|
|
42
|
+
export function resolveEnvRoot(scope = resolveDefaultConfigScope()) {
|
|
43
|
+
const envRoot = String(process.env[NB_ENV_ROOT_ENV] ?? '').trim();
|
|
44
|
+
if (envRoot) {
|
|
45
|
+
return path.resolve(envRoot);
|
|
46
|
+
}
|
|
47
|
+
return resolveCliHomeRoot(scope);
|
|
48
|
+
}
|
|
49
|
+
export function resolveEnvRelativePath(relativePath, scope = resolveDefaultConfigScope()) {
|
|
50
|
+
return path.resolve(resolveEnvRoot(scope), relativePath);
|
|
51
|
+
}
|
|
52
|
+
export function resolveConfiguredEnvPath(value, scope = resolveDefaultConfigScope()) {
|
|
53
|
+
const text = String(value ?? '').trim();
|
|
54
|
+
if (!text) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return path.isAbsolute(text) ? text : resolveEnvRelativePath(text, scope);
|
|
58
|
+
}
|
|
28
59
|
export function formatCliHomeScope(scope) {
|
|
29
60
|
return scope === 'project' ? 'project' : 'global';
|
|
30
61
|
}
|
package/dist/lib/env-auth.js
CHANGED
|
@@ -750,7 +750,7 @@ export async function resolveAccessToken(options) {
|
|
|
750
750
|
}
|
|
751
751
|
const baseUrl = options.baseUrl ?? env.baseUrl;
|
|
752
752
|
if (!baseUrl) {
|
|
753
|
-
throw new Error(`Env "${envName}" is missing a base URL. Run \`nb env add ${envName} --base-url <url>\`.`);
|
|
753
|
+
throw new Error(`Env "${envName}" is missing a base URL. Run \`nb env add ${envName} --api-base-url <url>\`.`);
|
|
754
754
|
}
|
|
755
755
|
printVerbose(`Refreshing OAuth session for env "${envName}"`);
|
|
756
756
|
return refreshOauthAccessToken({
|
|
@@ -771,7 +771,7 @@ export async function resolveServerRequestTarget(options) {
|
|
|
771
771
|
scope: options.scope,
|
|
772
772
|
});
|
|
773
773
|
if (!baseUrl) {
|
|
774
|
-
throw new Error('Missing base URL. Use --base-url or configure one with `nb env add`.');
|
|
774
|
+
throw new Error('Missing base URL. Use --api-base-url or configure one with `nb env add`.');
|
|
775
775
|
}
|
|
776
776
|
return { baseUrl, token };
|
|
777
777
|
}
|
|
@@ -785,7 +785,7 @@ export async function authenticateEnvWithOauth(options) {
|
|
|
785
785
|
? `Environment "${envName}" does not have an API base URL yet.`
|
|
786
786
|
: `Environment "${envName}" has not been set up yet.`,
|
|
787
787
|
env
|
|
788
|
-
? `Run \`nb env add ${envName} --base-url <url>\` to finish setting it up.`
|
|
788
|
+
? `Run \`nb env add ${envName} --api-base-url <url>\` to finish setting it up.`
|
|
789
789
|
: `Run \`nb env add ${envName}\` first.`,
|
|
790
790
|
]
|
|
791
791
|
.filter(Boolean)
|
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Command, Flags } from '@oclif/core';
|
|
2
10
|
import { executeApiRequest } from './api-client.js';
|
|
3
11
|
import { applyPostProcessor } from './post-processors.js';
|
|
@@ -74,7 +82,7 @@ export function createGeneratedFlags(operation) {
|
|
|
74
82
|
exclusive: ['body'],
|
|
75
83
|
});
|
|
76
84
|
}
|
|
77
|
-
flags['base-url'] = Flags.string({
|
|
85
|
+
flags['api-base-url'] = Flags.string({
|
|
78
86
|
description: 'NocoBase API base URL, for example http://localhost:13000/api',
|
|
79
87
|
helpGroup: 'Global',
|
|
80
88
|
});
|
|
@@ -114,7 +122,7 @@ export class GeneratedApiCommand extends Command {
|
|
|
114
122
|
const { flags } = await this.parse(ctor);
|
|
115
123
|
const response = await executeApiRequest({
|
|
116
124
|
envName: flags.env,
|
|
117
|
-
baseUrl: flags['base-url'],
|
|
125
|
+
baseUrl: flags['api-base-url'],
|
|
118
126
|
role: flags.role,
|
|
119
127
|
token: flags.token,
|
|
120
128
|
flags,
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
function normalizeLocationUrl(location, currentUrl) {
|
|
10
|
+
try {
|
|
11
|
+
return new URL(location, currentUrl).toString();
|
|
12
|
+
}
|
|
13
|
+
catch (_error) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function shouldPreserveAuthorizationRedirect(fromUrl, toUrl) {
|
|
18
|
+
try {
|
|
19
|
+
const from = new URL(fromUrl);
|
|
20
|
+
const to = new URL(toUrl);
|
|
21
|
+
return (from.hostname === to.hostname &&
|
|
22
|
+
from.port === to.port &&
|
|
23
|
+
from.pathname === to.pathname &&
|
|
24
|
+
from.search === to.search &&
|
|
25
|
+
from.protocol === 'http:' &&
|
|
26
|
+
to.protocol === 'https:');
|
|
27
|
+
}
|
|
28
|
+
catch (_error) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function fetchWithPreservedAuthRedirect(url, init = {}) {
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
...init,
|
|
35
|
+
redirect: 'manual',
|
|
36
|
+
});
|
|
37
|
+
const location = response.headers.get('location');
|
|
38
|
+
if (!location || ![301, 302, 307, 308].includes(response.status)) {
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
const nextUrl = normalizeLocationUrl(location, url);
|
|
42
|
+
if (!nextUrl || !shouldPreserveAuthorizationRedirect(url, nextUrl)) {
|
|
43
|
+
return response;
|
|
44
|
+
}
|
|
45
|
+
return fetch(nextUrl, {
|
|
46
|
+
...init,
|
|
47
|
+
redirect: 'manual',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Flags } from '@oclif/core';
|
|
2
10
|
import { executeResourceRequest } from './resource-request.js';
|
|
3
11
|
import { setVerboseMode } from './ui.js';
|
|
@@ -80,7 +88,7 @@ function printResponse(command, response, jsonOutput) {
|
|
|
80
88
|
command.log(`HTTP ${response.status}`);
|
|
81
89
|
}
|
|
82
90
|
export const resourceBaseFlags = {
|
|
83
|
-
'base-url': Flags.string({
|
|
91
|
+
'api-base-url': Flags.string({
|
|
84
92
|
description: 'NocoBase API base URL, for example http://localhost:13000/api',
|
|
85
93
|
}),
|
|
86
94
|
verbose: Flags.boolean({
|
|
@@ -325,7 +333,7 @@ export async function runResourceCommand(command, action, flags, args) {
|
|
|
325
333
|
setVerboseMode(Boolean(flags.verbose));
|
|
326
334
|
const response = await executeResourceRequest({
|
|
327
335
|
envName: flags.env,
|
|
328
|
-
baseUrl: flags['base-url'],
|
|
336
|
+
baseUrl: flags['api-base-url'],
|
|
329
337
|
role: flags.role,
|
|
330
338
|
token: flags.token,
|
|
331
339
|
action,
|
|
@@ -10,7 +10,7 @@ import { createHash } from 'node:crypto';
|
|
|
10
10
|
import { loadBuildConfig } from './build-config.js';
|
|
11
11
|
import { toKebabCase, toLogicalActionName, toLogicalResourceName, toResourceSegments } from './naming.js';
|
|
12
12
|
import { collectOperations } from './openapi.js';
|
|
13
|
-
const RESERVED_FLAG_NAMES = new Set(['base-url', 'env', 'token', 'json-output', 'body', 'body-file']);
|
|
13
|
+
const RESERVED_FLAG_NAMES = new Set(['api-base-url', 'base-url', 'env', 'token', 'json-output', 'body', 'body-file']);
|
|
14
14
|
function matchesPattern(value, pattern) {
|
|
15
15
|
if (!value) {
|
|
16
16
|
return false;
|
package/dist/lib/self-manager.js
CHANGED
|
@@ -234,7 +234,7 @@ export async function updateSelf(options = {}) {
|
|
|
234
234
|
}
|
|
235
235
|
const packageSpec = getSelfUpdatePackageSpec(status);
|
|
236
236
|
await (options.runFn ?? run)('npm', ['install', '-g', packageSpec], {
|
|
237
|
-
stdio: 'inherit',
|
|
237
|
+
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
238
238
|
errorName: 'npm install',
|
|
239
239
|
});
|
|
240
240
|
return {
|
|
@@ -6,31 +6,38 @@
|
|
|
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 fs from 'node:fs';
|
|
10
9
|
import fsp from 'node:fs/promises';
|
|
11
10
|
import path from 'node:path';
|
|
11
|
+
import { resolveCliHomeDir } from './cli-home.js';
|
|
12
|
+
import { compareVersions } from './self-manager.js';
|
|
12
13
|
import { commandOutput, run } from './run-npm.js';
|
|
13
|
-
export const
|
|
14
|
-
export const
|
|
14
|
+
export const NOCOBASE_SKILLS_SOURCE = 'nocobase/skills';
|
|
15
|
+
export const NOCOBASE_SKILLS_PACKAGE_NAME = '@nocobase/skills';
|
|
15
16
|
const NOCOBASE_SKILLS_NAME_PREFIX = 'nocobase-';
|
|
16
17
|
function normalizePath(value) {
|
|
17
18
|
return path.resolve(value);
|
|
18
19
|
}
|
|
20
|
+
export function resolveGlobalSkillsRoot(_startCwd = process.cwd()) {
|
|
21
|
+
return normalizePath(resolveCliHomeDir('global'));
|
|
22
|
+
}
|
|
19
23
|
export function resolveSkillsWorkspaceRoot(startCwd = process.cwd()) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
return resolveGlobalSkillsRoot(startCwd);
|
|
25
|
+
}
|
|
26
|
+
function resolveSkillsRoot(options = {}) {
|
|
27
|
+
return options.globalRoot
|
|
28
|
+
? normalizePath(options.globalRoot)
|
|
29
|
+
: options.workspaceRoot
|
|
30
|
+
? normalizePath(options.workspaceRoot)
|
|
31
|
+
: resolveGlobalSkillsRoot();
|
|
32
|
+
}
|
|
33
|
+
function getSkillsCacheRoot(globalRoot) {
|
|
34
|
+
return path.join(globalRoot, 'cache', 'skills');
|
|
31
35
|
}
|
|
32
36
|
export function getManagedSkillsStateFile(workspaceRoot) {
|
|
33
|
-
return path.join(workspaceRoot, '
|
|
37
|
+
return path.join(workspaceRoot, 'skills.json');
|
|
38
|
+
}
|
|
39
|
+
async function ensureSkillsWorkspaceRoot(workspaceRoot) {
|
|
40
|
+
await fsp.mkdir(workspaceRoot, { recursive: true });
|
|
34
41
|
}
|
|
35
42
|
async function readManagedSkillsState(workspaceRoot) {
|
|
36
43
|
const filePath = getManagedSkillsStateFile(workspaceRoot);
|
|
@@ -47,15 +54,19 @@ async function writeManagedSkillsState(workspaceRoot, state) {
|
|
|
47
54
|
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
48
55
|
await fsp.writeFile(filePath, JSON.stringify(state, null, 2));
|
|
49
56
|
}
|
|
50
|
-
export async function
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
export async function listGlobalSkills(options = {}) {
|
|
58
|
+
const globalRoot = resolveSkillsRoot(options);
|
|
59
|
+
await ensureSkillsWorkspaceRoot(globalRoot);
|
|
60
|
+
const output = await (options.commandOutputFn ?? commandOutput)('npx', ['-y', 'skills', 'list', '-g', '--json'], {
|
|
61
|
+
cwd: globalRoot,
|
|
54
62
|
errorName: 'skills list',
|
|
55
63
|
});
|
|
56
64
|
const parsed = JSON.parse(output);
|
|
57
65
|
return Array.isArray(parsed) ? parsed : [];
|
|
58
66
|
}
|
|
67
|
+
export async function listProjectSkills(options = {}) {
|
|
68
|
+
return await listGlobalSkills(options);
|
|
69
|
+
}
|
|
59
70
|
function pickInstalledNocoBaseSkillNames(installedSkills, state) {
|
|
60
71
|
const installedNames = new Set(installedSkills.map((skill) => String(skill.name ?? '').trim()).filter(Boolean));
|
|
61
72
|
if (state?.skillNames?.length) {
|
|
@@ -65,14 +76,17 @@ function pickInstalledNocoBaseSkillNames(installedSkills, state) {
|
|
|
65
76
|
.filter((name) => name.startsWith(NOCOBASE_SKILLS_NAME_PREFIX))
|
|
66
77
|
.sort();
|
|
67
78
|
}
|
|
68
|
-
|
|
79
|
+
async function readPublishedSkillsVersion(options = {}) {
|
|
80
|
+
const globalRoot = resolveSkillsRoot(options);
|
|
81
|
+
await ensureSkillsWorkspaceRoot(globalRoot);
|
|
69
82
|
try {
|
|
70
|
-
const output = await (options.commandOutputFn ?? commandOutput)('
|
|
71
|
-
cwd:
|
|
72
|
-
errorName: '
|
|
83
|
+
const output = await (options.commandOutputFn ?? commandOutput)('npm', ['view', NOCOBASE_SKILLS_PACKAGE_NAME, 'version', '--json'], {
|
|
84
|
+
cwd: globalRoot,
|
|
85
|
+
errorName: 'npm view',
|
|
73
86
|
});
|
|
74
|
-
const
|
|
75
|
-
|
|
87
|
+
const parsed = JSON.parse(output);
|
|
88
|
+
const version = String(parsed ?? '').trim();
|
|
89
|
+
return { version: version || undefined };
|
|
76
90
|
}
|
|
77
91
|
catch (error) {
|
|
78
92
|
return {
|
|
@@ -80,80 +94,140 @@ export async function readNocoBaseSkillsHeadRef(options = {}) {
|
|
|
80
94
|
};
|
|
81
95
|
}
|
|
82
96
|
}
|
|
97
|
+
async function readCachedSkillsVersion(cacheRoot) {
|
|
98
|
+
const packageJsonPath = path.join(cacheRoot, 'node_modules', '@nocobase', 'skills', 'package.json');
|
|
99
|
+
try {
|
|
100
|
+
const content = await fsp.readFile(packageJsonPath, 'utf8');
|
|
101
|
+
const parsed = JSON.parse(content);
|
|
102
|
+
const version = String(parsed.version ?? '').trim();
|
|
103
|
+
return version || undefined;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function prepareLocalSkillsPackage(globalRoot, options = {}, targetVersion) {
|
|
110
|
+
const cacheRoot = getSkillsCacheRoot(globalRoot);
|
|
111
|
+
const packageDir = path.join(cacheRoot, 'node_modules', '@nocobase', 'skills');
|
|
112
|
+
const packageSpec = targetVersion ? `${NOCOBASE_SKILLS_PACKAGE_NAME}@${targetVersion}` : NOCOBASE_SKILLS_PACKAGE_NAME;
|
|
113
|
+
const cachedVersion = await readCachedSkillsVersion(cacheRoot);
|
|
114
|
+
await fsp.mkdir(cacheRoot, { recursive: true });
|
|
115
|
+
if (targetVersion && cachedVersion && compareVersions(cachedVersion, targetVersion) === 0) {
|
|
116
|
+
return {
|
|
117
|
+
packageDir,
|
|
118
|
+
cleanup: async () => undefined,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
await fsp.rm(path.join(cacheRoot, 'node_modules'), { recursive: true, force: true });
|
|
122
|
+
await (options.runFn ?? run)('npm', ['install', '--no-save', '--ignore-scripts', '--no-package-lock', packageSpec], {
|
|
123
|
+
cwd: cacheRoot,
|
|
124
|
+
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
125
|
+
errorName: 'npm install',
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
await fsp.access(packageDir);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
throw new Error(`npm install did not produce a local ${NOCOBASE_SKILLS_PACKAGE_NAME} package.`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
packageDir,
|
|
135
|
+
cleanup: async () => undefined,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
83
138
|
export async function inspectSkillsStatus(options = {}) {
|
|
84
|
-
const
|
|
85
|
-
const stateFile = getManagedSkillsStateFile(
|
|
139
|
+
const globalRoot = resolveSkillsRoot(options);
|
|
140
|
+
const stateFile = getManagedSkillsStateFile(globalRoot);
|
|
86
141
|
const [installedSkills, managedState] = await Promise.all([
|
|
87
|
-
|
|
88
|
-
|
|
142
|
+
listGlobalSkills({
|
|
143
|
+
globalRoot,
|
|
89
144
|
commandOutputFn: options.commandOutputFn,
|
|
90
145
|
}),
|
|
91
|
-
readManagedSkillsState(
|
|
146
|
+
readManagedSkillsState(globalRoot),
|
|
92
147
|
]);
|
|
93
148
|
const installedSkillNames = pickInstalledNocoBaseSkillNames(installedSkills, managedState);
|
|
94
|
-
const managedByNb = managedState?.packageName ===
|
|
95
|
-
let
|
|
149
|
+
const managedByNb = managedState?.packageName === NOCOBASE_SKILLS_PACKAGE_NAME;
|
|
150
|
+
let latestVersion;
|
|
96
151
|
let registryError;
|
|
97
152
|
let updateAvailable = installedSkillNames.length > 0 ? null : false;
|
|
98
153
|
if (installedSkillNames.length > 0 || managedByNb) {
|
|
99
|
-
const
|
|
100
|
-
|
|
154
|
+
const published = await readPublishedSkillsVersion({
|
|
155
|
+
globalRoot,
|
|
101
156
|
commandOutputFn: options.commandOutputFn,
|
|
102
157
|
});
|
|
103
|
-
|
|
104
|
-
registryError =
|
|
105
|
-
|
|
106
|
-
|
|
158
|
+
latestVersion = published.version;
|
|
159
|
+
registryError = published.error;
|
|
160
|
+
const installedVersion = managedState?.installedVersion ?? managedState?.installedRef;
|
|
161
|
+
if (installedVersion && latestVersion) {
|
|
162
|
+
updateAvailable = compareVersions(latestVersion, installedVersion) > 0;
|
|
107
163
|
}
|
|
108
164
|
}
|
|
165
|
+
const installedVersion = managedState?.installedVersion ?? managedState?.installedRef;
|
|
109
166
|
return {
|
|
110
|
-
|
|
167
|
+
globalRoot,
|
|
168
|
+
workspaceRoot: globalRoot,
|
|
111
169
|
stateFile,
|
|
112
170
|
installed: installedSkillNames.length > 0,
|
|
113
171
|
managedByNb,
|
|
114
|
-
sourcePackage:
|
|
172
|
+
sourcePackage: managedState?.sourcePackage ?? NOCOBASE_SKILLS_SOURCE,
|
|
173
|
+
npmPackageName: managedState?.packageName ?? NOCOBASE_SKILLS_PACKAGE_NAME,
|
|
115
174
|
installedSkillNames,
|
|
116
|
-
|
|
117
|
-
|
|
175
|
+
latestVersion,
|
|
176
|
+
installedVersion,
|
|
177
|
+
latestRef: latestVersion,
|
|
178
|
+
installedRef: installedVersion,
|
|
118
179
|
updateAvailable,
|
|
119
180
|
registryError,
|
|
120
181
|
};
|
|
121
182
|
}
|
|
122
183
|
function formatSkillsNotInstalledMessage() {
|
|
123
184
|
return [
|
|
124
|
-
'NocoBase AI coding skills are not installed
|
|
185
|
+
'NocoBase AI coding skills are not installed globally.',
|
|
125
186
|
'Run `nb skills install` first.',
|
|
126
187
|
].join('\n');
|
|
127
188
|
}
|
|
128
|
-
async function persistManagedSkillsState(
|
|
129
|
-
const installedSkills = await
|
|
130
|
-
|
|
189
|
+
async function persistManagedSkillsState(globalRoot, options = {}) {
|
|
190
|
+
const installedSkills = await listGlobalSkills({
|
|
191
|
+
globalRoot,
|
|
131
192
|
commandOutputFn: options.commandOutputFn,
|
|
132
193
|
});
|
|
133
|
-
const managedState = await readManagedSkillsState(
|
|
194
|
+
const managedState = await readManagedSkillsState(globalRoot);
|
|
134
195
|
const installedSkillNames = pickInstalledNocoBaseSkillNames(installedSkills, managedState);
|
|
135
|
-
const
|
|
136
|
-
|
|
196
|
+
const published = await readPublishedSkillsVersion({
|
|
197
|
+
globalRoot,
|
|
137
198
|
commandOutputFn: options.commandOutputFn,
|
|
138
199
|
});
|
|
139
200
|
const now = new Date().toISOString();
|
|
140
|
-
await writeManagedSkillsState(
|
|
141
|
-
packageName:
|
|
142
|
-
|
|
201
|
+
await writeManagedSkillsState(globalRoot, {
|
|
202
|
+
packageName: NOCOBASE_SKILLS_PACKAGE_NAME,
|
|
203
|
+
sourcePackage: NOCOBASE_SKILLS_SOURCE,
|
|
143
204
|
installedAt: managedState?.installedAt ?? now,
|
|
144
205
|
updatedAt: now,
|
|
145
|
-
|
|
206
|
+
installedVersion: published.version,
|
|
146
207
|
skillNames: installedSkillNames,
|
|
147
208
|
});
|
|
148
209
|
return await inspectSkillsStatus({
|
|
149
|
-
|
|
210
|
+
globalRoot,
|
|
150
211
|
commandOutputFn: options.commandOutputFn,
|
|
151
212
|
});
|
|
152
213
|
}
|
|
214
|
+
async function reinstallManagedSkills(globalRoot, options = {}, targetVersion) {
|
|
215
|
+
const prepared = await prepareLocalSkillsPackage(globalRoot, options, targetVersion);
|
|
216
|
+
try {
|
|
217
|
+
await (options.runFn ?? run)('npx', ['-y', 'skills', 'add', prepared.packageDir, '-g', '-y'], {
|
|
218
|
+
cwd: globalRoot,
|
|
219
|
+
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
220
|
+
errorName: 'skills add',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
await prepared.cleanup();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
153
227
|
export async function installNocoBaseSkills(options = {}) {
|
|
154
|
-
const
|
|
228
|
+
const globalRoot = resolveSkillsRoot(options);
|
|
155
229
|
const status = await inspectSkillsStatus({
|
|
156
|
-
|
|
230
|
+
globalRoot,
|
|
157
231
|
commandOutputFn: options.commandOutputFn,
|
|
158
232
|
});
|
|
159
233
|
if (status.installed) {
|
|
@@ -162,41 +236,34 @@ export async function installNocoBaseSkills(options = {}) {
|
|
|
162
236
|
status,
|
|
163
237
|
};
|
|
164
238
|
}
|
|
165
|
-
await (
|
|
166
|
-
|
|
167
|
-
stdio: 'inherit',
|
|
168
|
-
errorName: 'skills add',
|
|
169
|
-
});
|
|
239
|
+
await ensureSkillsWorkspaceRoot(globalRoot);
|
|
240
|
+
await reinstallManagedSkills(globalRoot, options, status.latestVersion);
|
|
170
241
|
return {
|
|
171
242
|
action: 'installed',
|
|
172
|
-
status: await persistManagedSkillsState(
|
|
243
|
+
status: await persistManagedSkillsState(globalRoot, options),
|
|
173
244
|
};
|
|
174
245
|
}
|
|
175
246
|
export async function updateNocoBaseSkills(options = {}) {
|
|
176
|
-
const
|
|
247
|
+
const globalRoot = resolveSkillsRoot(options);
|
|
177
248
|
const status = await inspectSkillsStatus({
|
|
178
|
-
|
|
249
|
+
globalRoot,
|
|
179
250
|
commandOutputFn: options.commandOutputFn,
|
|
180
251
|
});
|
|
181
252
|
if (!status.installed) {
|
|
182
253
|
throw new Error(formatSkillsNotInstalledMessage());
|
|
183
254
|
}
|
|
184
255
|
if (status.managedByNb
|
|
185
|
-
&& status.
|
|
186
|
-
&& status.
|
|
187
|
-
&& status.
|
|
256
|
+
&& status.latestVersion
|
|
257
|
+
&& status.installedVersion
|
|
258
|
+
&& compareVersions(status.latestVersion, status.installedVersion) <= 0) {
|
|
188
259
|
return {
|
|
189
260
|
action: 'noop',
|
|
190
261
|
status,
|
|
191
262
|
};
|
|
192
263
|
}
|
|
193
|
-
await (
|
|
194
|
-
cwd: workspaceRoot,
|
|
195
|
-
stdio: 'inherit',
|
|
196
|
-
errorName: 'skills update',
|
|
197
|
-
});
|
|
264
|
+
await reinstallManagedSkills(globalRoot, options, status.latestVersion);
|
|
198
265
|
return {
|
|
199
266
|
action: 'updated',
|
|
200
|
-
status: await persistManagedSkillsState(
|
|
267
|
+
status: await persistManagedSkillsState(globalRoot, options),
|
|
201
268
|
};
|
|
202
269
|
}
|