@nocobase/cli 2.1.0-alpha.4 → 2.1.0-alpha.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +393 -19
  3. package/README.zh-CN.md +343 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +135 -0
  6. package/bin/session-env.js +39 -0
  7. package/dist/commands/api/resource/create.js +15 -0
  8. package/dist/commands/api/resource/destroy.js +15 -0
  9. package/dist/commands/api/resource/get.js +15 -0
  10. package/dist/commands/api/resource/index.js +20 -0
  11. package/dist/commands/api/resource/list.js +16 -0
  12. package/dist/commands/api/resource/query.js +15 -0
  13. package/dist/commands/api/resource/update.js +15 -0
  14. package/dist/commands/app/down.js +301 -0
  15. package/dist/commands/app/logs.js +114 -0
  16. package/dist/commands/app/restart.js +158 -0
  17. package/dist/commands/app/start.js +305 -0
  18. package/dist/commands/app/stop.js +115 -0
  19. package/dist/commands/app/upgrade.js +636 -0
  20. package/dist/commands/backup/create.js +147 -0
  21. package/dist/commands/backup/index.js +20 -0
  22. package/dist/commands/backup/restore.js +105 -0
  23. package/{src/cli.js → dist/commands/build.js} +4 -11
  24. package/dist/commands/config/delete.js +30 -0
  25. package/dist/commands/config/get.js +29 -0
  26. package/dist/commands/config/index.js +20 -0
  27. package/dist/commands/config/list.js +29 -0
  28. package/dist/commands/config/set.js +35 -0
  29. package/dist/commands/db/check.js +240 -0
  30. package/dist/commands/db/logs.js +85 -0
  31. package/dist/commands/db/ps.js +60 -0
  32. package/dist/commands/db/shared.js +96 -0
  33. package/dist/commands/db/start.js +71 -0
  34. package/dist/commands/db/stop.js +71 -0
  35. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  36. package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
  37. package/dist/commands/download.js +13 -0
  38. package/dist/commands/env/add.js +366 -0
  39. package/dist/commands/env/auth.js +130 -0
  40. package/dist/commands/env/current.js +21 -0
  41. package/dist/commands/env/info.js +157 -0
  42. package/dist/commands/env/list.js +44 -0
  43. package/dist/commands/env/remove.js +84 -0
  44. package/dist/commands/env/shared.js +158 -0
  45. package/dist/commands/env/status.js +90 -0
  46. package/dist/commands/env/update.js +74 -0
  47. package/dist/commands/env/use.js +38 -0
  48. package/dist/commands/examples/prompts-stages.js +150 -0
  49. package/dist/commands/examples/prompts-test.js +181 -0
  50. package/dist/commands/init.js +1092 -0
  51. package/dist/commands/install.js +2378 -0
  52. package/dist/commands/license/activate.js +360 -0
  53. package/dist/commands/license/env.js +94 -0
  54. package/dist/commands/license/generate-id.js +108 -0
  55. package/dist/commands/license/id.js +70 -0
  56. package/dist/commands/license/index.js +20 -0
  57. package/dist/commands/license/plugins/clean.js +115 -0
  58. package/dist/commands/license/plugins/index.js +20 -0
  59. package/dist/commands/license/plugins/list.js +64 -0
  60. package/dist/commands/license/plugins/shared.js +325 -0
  61. package/dist/commands/license/plugins/sync.js +285 -0
  62. package/dist/commands/license/shared.js +423 -0
  63. package/dist/commands/license/status.js +64 -0
  64. package/dist/commands/logs.js +12 -0
  65. package/dist/commands/plugin/disable.js +86 -0
  66. package/dist/commands/plugin/enable.js +86 -0
  67. package/dist/commands/plugin/list.js +82 -0
  68. package/dist/commands/pm/disable.js +12 -0
  69. package/dist/commands/pm/enable.js +12 -0
  70. package/dist/commands/pm/list.js +12 -0
  71. package/dist/commands/restart.js +12 -0
  72. package/dist/commands/scaffold/migration.js +38 -0
  73. package/dist/commands/scaffold/plugin.js +37 -0
  74. package/dist/commands/self/check.js +71 -0
  75. package/dist/commands/self/index.js +20 -0
  76. package/dist/commands/self/update.js +95 -0
  77. package/dist/commands/session/id.js +24 -0
  78. package/dist/commands/session/remove.js +57 -0
  79. package/dist/commands/session/setup.js +62 -0
  80. package/dist/commands/skills/check.js +69 -0
  81. package/dist/commands/skills/index.js +20 -0
  82. package/dist/commands/skills/install.js +80 -0
  83. package/dist/commands/skills/remove.js +80 -0
  84. package/dist/commands/skills/update.js +87 -0
  85. package/dist/commands/source/build.js +58 -0
  86. package/dist/commands/source/dev.js +182 -0
  87. package/dist/commands/source/download.js +880 -0
  88. package/dist/commands/source/publish.js +109 -0
  89. package/dist/commands/source/registry/logs.js +70 -0
  90. package/dist/commands/source/registry/start.js +57 -0
  91. package/dist/commands/source/registry/status.js +33 -0
  92. package/dist/commands/source/registry/stop.js +48 -0
  93. package/dist/commands/source/test.js +477 -0
  94. package/dist/commands/start.js +12 -0
  95. package/dist/commands/stop.js +12 -0
  96. package/dist/commands/test.js +12 -0
  97. package/dist/commands/upgrade.js +12 -0
  98. package/dist/commands/v1.js +210 -0
  99. package/dist/generated/command-registry.js +133 -0
  100. package/dist/help/runtime-help.js +23 -0
  101. package/dist/lib/api-client.js +329 -0
  102. package/dist/lib/app-health.js +126 -0
  103. package/dist/lib/app-managed-resources.js +316 -0
  104. package/dist/lib/app-runtime.js +180 -0
  105. package/dist/lib/auth-store.js +368 -0
  106. package/dist/lib/backup.js +171 -0
  107. package/dist/lib/bootstrap.js +403 -0
  108. package/dist/lib/build-config.js +18 -0
  109. package/dist/lib/builtin-db.js +86 -0
  110. package/dist/lib/cli-config.js +176 -0
  111. package/dist/lib/cli-home.js +47 -0
  112. package/dist/lib/cli-locale.js +129 -0
  113. package/dist/lib/command-discovery.js +39 -0
  114. package/dist/lib/db-connection-check.js +158 -0
  115. package/dist/lib/docker-env-file.js +52 -0
  116. package/dist/lib/docker-image.js +37 -0
  117. package/dist/lib/env-auth.js +873 -0
  118. package/dist/lib/env-config.js +94 -0
  119. package/dist/lib/env-guard.js +62 -0
  120. package/dist/lib/generated-command.js +186 -0
  121. package/dist/lib/http-request.js +49 -0
  122. package/dist/lib/inquirer-theme.js +17 -0
  123. package/dist/lib/inquirer.js +244 -0
  124. package/dist/lib/naming.js +70 -0
  125. package/dist/lib/object-utils.js +76 -0
  126. package/dist/lib/openapi.js +62 -0
  127. package/dist/lib/plugin-storage.js +64 -0
  128. package/dist/lib/post-processors.js +23 -0
  129. package/dist/lib/prompt-catalog-core.js +185 -0
  130. package/dist/lib/prompt-catalog-terminal.js +375 -0
  131. package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
  132. package/dist/lib/prompt-validators.js +240 -0
  133. package/dist/lib/prompt-web-ui.js +2103 -0
  134. package/dist/lib/resource-command.js +357 -0
  135. package/dist/lib/resource-request.js +104 -0
  136. package/dist/lib/run-npm.js +275 -0
  137. package/dist/lib/runtime-env-vars.js +32 -0
  138. package/dist/lib/runtime-generator.js +498 -0
  139. package/dist/lib/runtime-store.js +56 -0
  140. package/dist/lib/self-manager.js +301 -0
  141. package/dist/lib/session-id.js +17 -0
  142. package/dist/lib/session-integration.js +703 -0
  143. package/dist/lib/session-store.js +118 -0
  144. package/dist/lib/skills-manager.js +360 -0
  145. package/dist/lib/source-publish.js +306 -0
  146. package/dist/lib/source-registry.js +188 -0
  147. package/dist/lib/startup-update.js +285 -0
  148. package/dist/lib/ui.js +155 -0
  149. package/dist/locale/en-US.json +344 -0
  150. package/dist/locale/zh-CN.json +344 -0
  151. package/dist/post-processors/data-modeling.js +84 -0
  152. package/dist/post-processors/data-source-manager.js +138 -0
  153. package/dist/post-processors/index.js +19 -0
  154. package/nocobase-ctl.config.json +388 -0
  155. package/package.json +100 -26
  156. package/LICENSE +0 -661
  157. package/bin/index.js +0 -39
  158. package/nocobase.conf.tpl +0 -95
  159. package/src/commands/benchmark.js +0 -73
  160. package/src/commands/build.js +0 -49
  161. package/src/commands/clean.js +0 -30
  162. package/src/commands/client.js +0 -166
  163. package/src/commands/create-nginx-conf.js +0 -37
  164. package/src/commands/create-plugin.js +0 -33
  165. package/src/commands/dev.js +0 -200
  166. package/src/commands/doc.js +0 -76
  167. package/src/commands/e2e.js +0 -265
  168. package/src/commands/global.js +0 -43
  169. package/src/commands/index.js +0 -45
  170. package/src/commands/instance-id.js +0 -47
  171. package/src/commands/locale/cronstrue.js +0 -122
  172. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  173. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  174. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  175. package/src/commands/locale.js +0 -81
  176. package/src/commands/p-test.js +0 -88
  177. package/src/commands/perf.js +0 -63
  178. package/src/commands/pkg.js +0 -321
  179. package/src/commands/pm2.js +0 -37
  180. package/src/commands/postinstall.js +0 -88
  181. package/src/commands/start.js +0 -148
  182. package/src/commands/tar.js +0 -36
  183. package/src/commands/test-coverage.js +0 -55
  184. package/src/commands/test.js +0 -107
  185. package/src/commands/umi.js +0 -33
  186. package/src/commands/update-deps.js +0 -72
  187. package/src/commands/upgrade.js +0 -47
  188. package/src/commands/view-license-key.js +0 -44
  189. package/src/license.js +0 -76
  190. package/src/logger.js +0 -75
  191. package/src/plugin-generator.js +0 -80
  192. package/src/util.js +0 -517
  193. package/templates/bundle-status.html +0 -338
  194. package/templates/create-app-package.json +0 -39
  195. package/templates/plugin/.npmignore.tpl +0 -2
  196. package/templates/plugin/README.md.tpl +0 -1
  197. package/templates/plugin/client.d.ts +0 -2
  198. package/templates/plugin/client.js +0 -1
  199. package/templates/plugin/package.json.tpl +0 -11
  200. package/templates/plugin/server.d.ts +0 -2
  201. package/templates/plugin/server.js +0 -1
  202. package/templates/plugin/src/client/client.d.ts +0 -249
  203. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  204. package/templates/plugin/src/client/locale.ts +0 -21
  205. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  206. package/templates/plugin/src/index.ts +0 -2
  207. package/templates/plugin/src/locale/en-US.json +0 -1
  208. package/templates/plugin/src/locale/zh-CN.json +0 -1
  209. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  210. package/templates/plugin/src/server/index.ts.tpl +0 -1
  211. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,118 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { promises as fs } from 'node:fs';
10
+ import path from 'node:path';
11
+ import { resolveCliHomeDir } from './cli-home.js';
12
+ export function getSessionId() {
13
+ const value = String(process.env.NB_SESSION_ID ?? '').trim();
14
+ return value || undefined;
15
+ }
16
+ function sessionsDir(scope) {
17
+ return path.join(resolveCliHomeDir(scope), 'sessions');
18
+ }
19
+ export function getSessionFilePath(sessionId, scope) {
20
+ return path.join(sessionsDir(scope), `${sessionId}.json`);
21
+ }
22
+ export async function loadSessionState(sessionId, scope) {
23
+ try {
24
+ const content = await fs.readFile(getSessionFilePath(sessionId, scope), 'utf8');
25
+ const parsed = JSON.parse(content);
26
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
27
+ }
28
+ catch (_error) {
29
+ return undefined;
30
+ }
31
+ }
32
+ export async function saveSessionState(sessionId, state, scope) {
33
+ const filePath = getSessionFilePath(sessionId, scope);
34
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
35
+ await fs.writeFile(filePath, JSON.stringify(state, null, 2));
36
+ }
37
+ export async function deleteSessionState(sessionId, scope) {
38
+ await fs.rm(getSessionFilePath(sessionId, scope), { force: true });
39
+ }
40
+ export async function getSessionCurrentEnv(scope) {
41
+ const sessionId = getSessionId();
42
+ if (!sessionId) {
43
+ return undefined;
44
+ }
45
+ const state = await loadSessionState(sessionId, scope);
46
+ const currentEnv = String(state?.currentEnv ?? '').trim();
47
+ return currentEnv || undefined;
48
+ }
49
+ export async function setSessionCurrentEnv(envName, scope) {
50
+ const sessionId = getSessionId();
51
+ if (!sessionId) {
52
+ return false;
53
+ }
54
+ await saveSessionState(sessionId, {
55
+ currentEnv: envName,
56
+ updatedAt: new Date().toISOString(),
57
+ }, scope);
58
+ return true;
59
+ }
60
+ export async function clearSessionCurrentEnv(scope) {
61
+ const sessionId = getSessionId();
62
+ if (!sessionId) {
63
+ return false;
64
+ }
65
+ await deleteSessionState(sessionId, scope);
66
+ return true;
67
+ }
68
+ export async function resolveEffectiveCurrentEnv(validEnvNames, options = {}) {
69
+ const sessionId = getSessionId();
70
+ const normalizedValidNames = validEnvNames.filter(Boolean);
71
+ if (sessionId) {
72
+ const state = await loadSessionState(sessionId, options.scope);
73
+ const sessionEnv = String(state?.currentEnv ?? '').trim();
74
+ if (sessionEnv && normalizedValidNames.includes(sessionEnv)) {
75
+ return sessionEnv;
76
+ }
77
+ if (sessionEnv) {
78
+ const lastEnv = String(options.lastEnv ?? '').trim();
79
+ const fallbackEnv = lastEnv && normalizedValidNames.includes(lastEnv)
80
+ ? lastEnv
81
+ : normalizedValidNames[0];
82
+ if (fallbackEnv) {
83
+ await saveSessionState(sessionId, {
84
+ currentEnv: fallbackEnv,
85
+ updatedAt: new Date().toISOString(),
86
+ }, options.scope);
87
+ return fallbackEnv;
88
+ }
89
+ await deleteSessionState(sessionId, options.scope);
90
+ }
91
+ }
92
+ const lastEnv = String(options.lastEnv ?? '').trim();
93
+ if (lastEnv && normalizedValidNames.includes(lastEnv)) {
94
+ return lastEnv;
95
+ }
96
+ return normalizedValidNames[0] || 'default';
97
+ }
98
+ export async function cleanupCurrentSessionAfterEnvRemoval(removedEnv, options = {}) {
99
+ const sessionId = getSessionId();
100
+ if (!sessionId) {
101
+ return false;
102
+ }
103
+ const state = await loadSessionState(sessionId, options.scope);
104
+ const sessionEnv = String(state?.currentEnv ?? '').trim();
105
+ if (sessionEnv !== removedEnv) {
106
+ return false;
107
+ }
108
+ const fallbackEnv = String(options.fallbackEnv ?? '').trim();
109
+ if (fallbackEnv) {
110
+ await saveSessionState(sessionId, {
111
+ currentEnv: fallbackEnv,
112
+ updatedAt: new Date().toISOString(),
113
+ }, options.scope);
114
+ return true;
115
+ }
116
+ await deleteSessionState(sessionId, options.scope);
117
+ return true;
118
+ }
@@ -0,0 +1,360 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import fs from 'node:fs';
10
+ import fsp from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { createGunzip } from 'node:zlib';
13
+ import * as tar from 'tar';
14
+ import { resolveCliHomeDir } from './cli-home.js';
15
+ import { compareVersions } from './self-manager.js';
16
+ import { commandOutput, commandOutputViaFile, run } from './run-npm.js';
17
+ export const NOCOBASE_SKILLS_SOURCE = 'nocobase/skills';
18
+ export const NOCOBASE_SKILLS_PACKAGE_NAME = '@nocobase/skills';
19
+ const NOCOBASE_SKILLS_NAME_PREFIX = 'nocobase-';
20
+ function normalizePath(value) {
21
+ return path.resolve(value);
22
+ }
23
+ export function resolveGlobalSkillsRoot(_startCwd = process.cwd()) {
24
+ return normalizePath(resolveCliHomeDir('global'));
25
+ }
26
+ export function resolveSkillsWorkspaceRoot(startCwd = process.cwd()) {
27
+ return resolveGlobalSkillsRoot(startCwd);
28
+ }
29
+ function resolveSkillsRoot(options = {}) {
30
+ return options.globalRoot
31
+ ? normalizePath(options.globalRoot)
32
+ : options.workspaceRoot
33
+ ? normalizePath(options.workspaceRoot)
34
+ : resolveGlobalSkillsRoot();
35
+ }
36
+ function getSkillsCacheRoot(globalRoot) {
37
+ return path.join(globalRoot, 'cache', 'skills');
38
+ }
39
+ function getCachedSkillsPackageDir(cacheRoot) {
40
+ return path.join(cacheRoot, 'node_modules', '@nocobase', 'skills');
41
+ }
42
+ function getCachedSkillsPackRoot(cacheRoot) {
43
+ return path.join(cacheRoot, 'pack');
44
+ }
45
+ function getCachedSkillsExtractRoot(cacheRoot) {
46
+ return path.join(cacheRoot, 'extract');
47
+ }
48
+ export function getManagedSkillsStateFile(workspaceRoot) {
49
+ return path.join(workspaceRoot, 'skills.json');
50
+ }
51
+ async function ensureSkillsWorkspaceRoot(workspaceRoot) {
52
+ await fsp.mkdir(workspaceRoot, { recursive: true });
53
+ }
54
+ async function readManagedSkillsState(workspaceRoot) {
55
+ const filePath = getManagedSkillsStateFile(workspaceRoot);
56
+ try {
57
+ const content = await fsp.readFile(filePath, 'utf8');
58
+ return JSON.parse(content);
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ }
64
+ async function writeManagedSkillsState(workspaceRoot, state) {
65
+ const filePath = getManagedSkillsStateFile(workspaceRoot);
66
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
67
+ await fsp.writeFile(filePath, JSON.stringify(state, null, 2));
68
+ }
69
+ export async function listGlobalSkills(options = {}) {
70
+ const globalRoot = resolveSkillsRoot(options);
71
+ await ensureSkillsWorkspaceRoot(globalRoot);
72
+ const output = await (options.commandOutputFn ?? commandOutputViaFile)('npx', ['-y', 'skills', 'list', '-g', '--json'], {
73
+ cwd: globalRoot,
74
+ errorName: 'skills list',
75
+ });
76
+ const parsed = JSON.parse(output);
77
+ return Array.isArray(parsed) ? parsed : [];
78
+ }
79
+ export async function listProjectSkills(options = {}) {
80
+ return await listGlobalSkills(options);
81
+ }
82
+ function pickInstalledNocoBaseSkillNames(installedSkills, state) {
83
+ const installedNames = new Set(installedSkills.map((skill) => String(skill.name ?? '').trim()).filter(Boolean));
84
+ if (state?.skillNames?.length) {
85
+ return state.skillNames.filter((name) => installedNames.has(name)).sort();
86
+ }
87
+ return Array.from(installedNames)
88
+ .filter((name) => name.startsWith(NOCOBASE_SKILLS_NAME_PREFIX))
89
+ .sort();
90
+ }
91
+ async function readPublishedSkillsVersion(options = {}) {
92
+ const globalRoot = resolveSkillsRoot(options);
93
+ await ensureSkillsWorkspaceRoot(globalRoot);
94
+ try {
95
+ const output = await (options.commandOutputFn ?? commandOutput)('npm', ['view', NOCOBASE_SKILLS_PACKAGE_NAME, 'version', '--json'], {
96
+ cwd: globalRoot,
97
+ errorName: 'npm view',
98
+ });
99
+ const parsed = JSON.parse(output);
100
+ const version = String(parsed ?? '').trim();
101
+ return { version: version || undefined };
102
+ }
103
+ catch (error) {
104
+ return {
105
+ error: error instanceof Error ? error.message : String(error),
106
+ };
107
+ }
108
+ }
109
+ async function readCachedSkillsVersion(cacheRoot) {
110
+ const packageJsonPath = path.join(getCachedSkillsPackageDir(cacheRoot), 'package.json');
111
+ try {
112
+ const content = await fsp.readFile(packageJsonPath, 'utf8');
113
+ const parsed = JSON.parse(content);
114
+ const version = String(parsed.version ?? '').trim();
115
+ return version || undefined;
116
+ }
117
+ catch {
118
+ return undefined;
119
+ }
120
+ }
121
+ async function resolvePackedSkillsTarball(packRoot) {
122
+ const entries = await fsp.readdir(packRoot, { withFileTypes: true });
123
+ const tarballs = entries
124
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.tgz'))
125
+ .map((entry) => path.join(packRoot, entry.name))
126
+ .sort();
127
+ if (tarballs.length === 1) {
128
+ return tarballs[0];
129
+ }
130
+ if (tarballs.length === 0) {
131
+ throw new Error(`npm pack did not produce a local tarball for ${NOCOBASE_SKILLS_PACKAGE_NAME}.`);
132
+ }
133
+ throw new Error(`npm pack produced multiple tarballs for ${NOCOBASE_SKILLS_PACKAGE_NAME}.`);
134
+ }
135
+ async function extractPackedSkillsTarball(tarballPath, cacheRoot, targetVersion) {
136
+ const packageDir = getCachedSkillsPackageDir(cacheRoot);
137
+ const extractRoot = getCachedSkillsExtractRoot(cacheRoot);
138
+ await fsp.rm(extractRoot, { recursive: true, force: true });
139
+ await fsp.mkdir(extractRoot, { recursive: true });
140
+ try {
141
+ await new Promise((resolve, reject) => {
142
+ fs.createReadStream(tarballPath)
143
+ .pipe(createGunzip())
144
+ .pipe(tar.extract({ cwd: extractRoot, strip: 1 }))
145
+ .on('finish', () => resolve())
146
+ .on('error', reject);
147
+ });
148
+ const packageJsonPath = path.join(extractRoot, 'package.json');
149
+ const packageJsonRaw = await fsp.readFile(packageJsonPath, 'utf8');
150
+ const manifest = JSON.parse(packageJsonRaw);
151
+ const packageName = String(manifest.name ?? '').trim();
152
+ const packageVersion = String(manifest.version ?? '').trim();
153
+ if (packageName !== NOCOBASE_SKILLS_PACKAGE_NAME) {
154
+ throw new Error(`packed tarball resolved to ${packageName || '(missing package name)'} instead of ${NOCOBASE_SKILLS_PACKAGE_NAME}.`);
155
+ }
156
+ if (targetVersion && packageVersion !== targetVersion) {
157
+ throw new Error(`packed tarball resolved to version ${packageVersion || '(missing version)'} instead of ${targetVersion}.`);
158
+ }
159
+ await fsp.rm(packageDir, { recursive: true, force: true });
160
+ await fsp.mkdir(path.dirname(packageDir), { recursive: true });
161
+ await fsp.rename(extractRoot, packageDir);
162
+ return packageDir;
163
+ }
164
+ catch (error) {
165
+ await fsp.rm(extractRoot, { recursive: true, force: true });
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ throw new Error(`failed to extract ${NOCOBASE_SKILLS_PACKAGE_NAME} tarball: ${message}`);
168
+ }
169
+ }
170
+ async function prepareLocalSkillsPackage(globalRoot, options = {}, targetVersion) {
171
+ const cacheRoot = getSkillsCacheRoot(globalRoot);
172
+ const packageDir = getCachedSkillsPackageDir(cacheRoot);
173
+ const packRoot = getCachedSkillsPackRoot(cacheRoot);
174
+ const packageSpec = targetVersion ? `${NOCOBASE_SKILLS_PACKAGE_NAME}@${targetVersion}` : NOCOBASE_SKILLS_PACKAGE_NAME;
175
+ const cachedVersion = await readCachedSkillsVersion(cacheRoot);
176
+ await fsp.mkdir(cacheRoot, { recursive: true });
177
+ if (targetVersion && cachedVersion && compareVersions(cachedVersion, targetVersion) === 0) {
178
+ return {
179
+ packageDir,
180
+ cleanup: async () => undefined,
181
+ };
182
+ }
183
+ await fsp.rm(packRoot, { recursive: true, force: true });
184
+ await fsp.mkdir(packRoot, { recursive: true });
185
+ try {
186
+ await (options.runFn ?? run)('npm', ['pack', '--silent', packageSpec], {
187
+ cwd: packRoot,
188
+ stdio: options.verbose ? 'inherit' : 'ignore',
189
+ errorName: 'npm pack',
190
+ });
191
+ const tarballPath = await resolvePackedSkillsTarball(packRoot);
192
+ await extractPackedSkillsTarball(tarballPath, cacheRoot, targetVersion);
193
+ }
194
+ finally {
195
+ await fsp.rm(packRoot, { recursive: true, force: true });
196
+ }
197
+ return {
198
+ packageDir,
199
+ cleanup: async () => undefined,
200
+ };
201
+ }
202
+ export async function inspectSkillsStatus(options = {}) {
203
+ const globalRoot = resolveSkillsRoot(options);
204
+ const stateFile = getManagedSkillsStateFile(globalRoot);
205
+ const [installedSkills, managedState] = await Promise.all([
206
+ listGlobalSkills({
207
+ globalRoot,
208
+ commandOutputFn: options.commandOutputFn,
209
+ }),
210
+ readManagedSkillsState(globalRoot),
211
+ ]);
212
+ const installedSkillNames = pickInstalledNocoBaseSkillNames(installedSkills, managedState);
213
+ const managedByNb = managedState?.packageName === NOCOBASE_SKILLS_PACKAGE_NAME;
214
+ let latestVersion;
215
+ let registryError;
216
+ let updateAvailable = installedSkillNames.length > 0 ? null : false;
217
+ if (installedSkillNames.length > 0 || managedByNb) {
218
+ const published = await readPublishedSkillsVersion({
219
+ globalRoot,
220
+ commandOutputFn: options.commandOutputFn,
221
+ });
222
+ latestVersion = published.version;
223
+ registryError = published.error;
224
+ const installedVersion = managedState?.installedVersion ?? managedState?.installedRef;
225
+ if (installedVersion && latestVersion) {
226
+ updateAvailable = compareVersions(latestVersion, installedVersion) > 0;
227
+ }
228
+ }
229
+ const installedVersion = managedState?.installedVersion ?? managedState?.installedRef;
230
+ return {
231
+ globalRoot,
232
+ workspaceRoot: globalRoot,
233
+ stateFile,
234
+ installed: installedSkillNames.length > 0,
235
+ managedByNb,
236
+ sourcePackage: managedState?.sourcePackage ?? NOCOBASE_SKILLS_SOURCE,
237
+ npmPackageName: managedState?.packageName ?? NOCOBASE_SKILLS_PACKAGE_NAME,
238
+ installedSkillNames,
239
+ latestVersion,
240
+ installedVersion,
241
+ latestRef: latestVersion,
242
+ installedRef: installedVersion,
243
+ updateAvailable,
244
+ registryError,
245
+ };
246
+ }
247
+ async function persistManagedSkillsState(globalRoot, options = {}) {
248
+ const installedSkills = await listGlobalSkills({
249
+ globalRoot,
250
+ commandOutputFn: options.commandOutputFn,
251
+ });
252
+ const managedState = await readManagedSkillsState(globalRoot);
253
+ const installedSkillNames = pickInstalledNocoBaseSkillNames(installedSkills, managedState);
254
+ const published = await readPublishedSkillsVersion({
255
+ globalRoot,
256
+ commandOutputFn: options.commandOutputFn,
257
+ });
258
+ const now = new Date().toISOString();
259
+ await writeManagedSkillsState(globalRoot, {
260
+ packageName: NOCOBASE_SKILLS_PACKAGE_NAME,
261
+ sourcePackage: NOCOBASE_SKILLS_SOURCE,
262
+ installedAt: managedState?.installedAt ?? now,
263
+ updatedAt: now,
264
+ installedVersion: published.version,
265
+ skillNames: installedSkillNames,
266
+ });
267
+ return await inspectSkillsStatus({
268
+ globalRoot,
269
+ commandOutputFn: options.commandOutputFn,
270
+ });
271
+ }
272
+ async function reinstallManagedSkills(globalRoot, options = {}, targetVersion) {
273
+ const prepared = await prepareLocalSkillsPackage(globalRoot, options, targetVersion);
274
+ try {
275
+ await (options.runFn ?? run)('npx', ['-y', 'skills', 'add', prepared.packageDir, '-g', '-y'], {
276
+ cwd: globalRoot,
277
+ stdio: options.verbose ? 'inherit' : 'ignore',
278
+ errorName: 'skills add',
279
+ });
280
+ }
281
+ finally {
282
+ await prepared.cleanup();
283
+ }
284
+ }
285
+ export async function installNocoBaseSkills(options = {}) {
286
+ const globalRoot = resolveSkillsRoot(options);
287
+ const status = await inspectSkillsStatus({
288
+ globalRoot,
289
+ commandOutputFn: options.commandOutputFn,
290
+ });
291
+ if (status.installed) {
292
+ return {
293
+ action: 'noop',
294
+ status,
295
+ };
296
+ }
297
+ await ensureSkillsWorkspaceRoot(globalRoot);
298
+ await reinstallManagedSkills(globalRoot, options, status.latestVersion);
299
+ return {
300
+ action: 'installed',
301
+ status: await persistManagedSkillsState(globalRoot, options),
302
+ };
303
+ }
304
+ export async function updateNocoBaseSkills(options = {}) {
305
+ const globalRoot = resolveSkillsRoot(options);
306
+ const status = await inspectSkillsStatus({
307
+ globalRoot,
308
+ commandOutputFn: options.commandOutputFn,
309
+ });
310
+ if (!status.installed) {
311
+ return {
312
+ action: 'noop',
313
+ reason: 'not-installed',
314
+ status,
315
+ };
316
+ }
317
+ if (status.managedByNb
318
+ && status.latestVersion
319
+ && status.installedVersion
320
+ && compareVersions(status.latestVersion, status.installedVersion) <= 0) {
321
+ return {
322
+ action: 'noop',
323
+ reason: 'up-to-date',
324
+ status,
325
+ };
326
+ }
327
+ await reinstallManagedSkills(globalRoot, options, status.latestVersion);
328
+ return {
329
+ action: 'updated',
330
+ status: await persistManagedSkillsState(globalRoot, options),
331
+ };
332
+ }
333
+ export async function removeNocoBaseSkills(options = {}) {
334
+ const globalRoot = resolveSkillsRoot(options);
335
+ const status = await inspectSkillsStatus({
336
+ globalRoot,
337
+ commandOutputFn: options.commandOutputFn,
338
+ });
339
+ if (!status.installed || status.installedSkillNames.length === 0) {
340
+ return {
341
+ action: 'noop',
342
+ status,
343
+ };
344
+ }
345
+ for (const skillName of status.installedSkillNames) {
346
+ await (options.runFn ?? run)('npx', ['-y', 'skills', 'remove', skillName, '-g', '-y'], {
347
+ cwd: globalRoot,
348
+ stdio: options.verbose ? 'inherit' : 'ignore',
349
+ errorName: 'skills remove',
350
+ });
351
+ }
352
+ await fsp.rm(getManagedSkillsStateFile(globalRoot), { force: true });
353
+ return {
354
+ action: 'removed',
355
+ status: await inspectSkillsStatus({
356
+ globalRoot,
357
+ commandOutputFn: options.commandOutputFn,
358
+ }),
359
+ };
360
+ }