@nocobase/cli 2.1.0-beta.9 → 2.2.0-beta.1

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 (263) hide show
  1. package/assets/env-proxy/nginx/app.conf.tpl +23 -0
  2. package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
  3. package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
  4. package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
  5. package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
  6. package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
  7. package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
  8. package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
  9. package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
  10. package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
  11. package/bin/run.cmd +3 -0
  12. package/bin/run.js +145 -0
  13. package/bin/session-env.js +39 -0
  14. package/dist/commands/api/resource/create.js +15 -0
  15. package/dist/commands/api/resource/destroy.js +15 -0
  16. package/dist/commands/api/resource/get.js +15 -0
  17. package/dist/commands/api/resource/index.js +20 -0
  18. package/dist/commands/api/resource/list.js +16 -0
  19. package/dist/commands/api/resource/query.js +15 -0
  20. package/dist/commands/api/resource/update.js +15 -0
  21. package/dist/commands/app/autostart/disable.js +55 -0
  22. package/dist/commands/app/autostart/enable.js +55 -0
  23. package/dist/commands/app/autostart/list.js +37 -0
  24. package/dist/commands/app/autostart/run.js +84 -0
  25. package/dist/commands/app/autostart/shared.js +49 -0
  26. package/dist/commands/app/destroy.js +234 -0
  27. package/dist/commands/app/down.js +71 -0
  28. package/dist/commands/app/logs.js +115 -0
  29. package/dist/commands/app/restart.js +229 -0
  30. package/dist/commands/app/shared.js +123 -0
  31. package/dist/commands/app/start.js +416 -0
  32. package/dist/commands/app/stop.js +183 -0
  33. package/dist/commands/app/upgrade.js +523 -0
  34. package/dist/commands/backup/create.js +147 -0
  35. package/dist/commands/backup/index.js +20 -0
  36. package/dist/commands/backup/restore.js +105 -0
  37. package/{src/cli.js → dist/commands/build.js} +4 -11
  38. package/dist/commands/config/delete.js +42 -0
  39. package/dist/commands/config/get.js +39 -0
  40. package/dist/commands/config/index.js +20 -0
  41. package/dist/commands/config/list.js +29 -0
  42. package/dist/commands/config/set.js +49 -0
  43. package/dist/commands/db/check.js +240 -0
  44. package/dist/commands/db/logs.js +85 -0
  45. package/dist/commands/db/ps.js +47 -0
  46. package/dist/commands/db/shared.js +96 -0
  47. package/dist/commands/db/start.js +86 -0
  48. package/dist/commands/db/stop.js +71 -0
  49. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  50. package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
  51. package/dist/commands/download.js +13 -0
  52. package/dist/commands/env/add.js +406 -0
  53. package/dist/commands/env/auth.js +189 -0
  54. package/dist/commands/env/current.js +21 -0
  55. package/dist/commands/env/info.js +202 -0
  56. package/dist/commands/env/list.js +43 -0
  57. package/dist/commands/env/remove.js +174 -0
  58. package/dist/commands/env/shared.js +204 -0
  59. package/dist/commands/env/status.js +93 -0
  60. package/dist/commands/env/update.js +448 -0
  61. package/dist/commands/env/use.js +38 -0
  62. package/dist/commands/examples/prompts-stages.js +150 -0
  63. package/dist/commands/examples/prompts-test.js +181 -0
  64. package/dist/commands/init.js +1390 -0
  65. package/dist/commands/install.js +2609 -0
  66. package/dist/commands/license/activate.js +179 -0
  67. package/dist/commands/license/env.js +94 -0
  68. package/dist/commands/license/generate-id.js +108 -0
  69. package/dist/commands/license/id.js +70 -0
  70. package/dist/commands/license/index.js +20 -0
  71. package/dist/commands/license/plugins/clean.js +115 -0
  72. package/dist/commands/license/plugins/index.js +20 -0
  73. package/dist/commands/license/plugins/list.js +64 -0
  74. package/dist/commands/license/plugins/shared.js +382 -0
  75. package/dist/commands/license/plugins/sync.js +314 -0
  76. package/dist/commands/license/shared.js +423 -0
  77. package/dist/commands/license/status.js +64 -0
  78. package/dist/commands/logs.js +12 -0
  79. package/dist/commands/plugin/disable.js +86 -0
  80. package/dist/commands/plugin/enable.js +86 -0
  81. package/dist/commands/plugin/import.js +108 -0
  82. package/dist/commands/plugin/list.js +82 -0
  83. package/dist/commands/pm/disable.js +12 -0
  84. package/dist/commands/pm/enable.js +12 -0
  85. package/dist/commands/pm/list.js +12 -0
  86. package/dist/commands/proxy/caddy/current.js +17 -0
  87. package/dist/commands/proxy/caddy/generate.js +69 -0
  88. package/dist/commands/proxy/caddy/index.js +28 -0
  89. package/dist/commands/proxy/caddy/info.js +31 -0
  90. package/dist/commands/proxy/caddy/reload.js +30 -0
  91. package/dist/commands/proxy/caddy/restart.js +28 -0
  92. package/dist/commands/proxy/caddy/start.js +30 -0
  93. package/dist/commands/proxy/caddy/status.js +19 -0
  94. package/dist/commands/proxy/caddy/stop.js +30 -0
  95. package/dist/commands/proxy/caddy/use.js +26 -0
  96. package/dist/commands/proxy/index.js +28 -0
  97. package/dist/commands/proxy/nginx/current.js +18 -0
  98. package/dist/commands/proxy/nginx/generate.js +68 -0
  99. package/dist/commands/proxy/nginx/index.js +28 -0
  100. package/dist/commands/proxy/nginx/info.js +34 -0
  101. package/dist/commands/proxy/nginx/reload.js +30 -0
  102. package/dist/commands/proxy/nginx/restart.js +28 -0
  103. package/dist/commands/proxy/nginx/start.js +30 -0
  104. package/dist/commands/proxy/nginx/status.js +19 -0
  105. package/dist/commands/proxy/nginx/stop.js +30 -0
  106. package/dist/commands/proxy/nginx/use.js +31 -0
  107. package/dist/commands/restart.js +12 -0
  108. package/dist/commands/revision/create.js +118 -0
  109. package/dist/commands/scaffold/migration.js +38 -0
  110. package/dist/commands/scaffold/plugin.js +37 -0
  111. package/dist/commands/self/check.js +71 -0
  112. package/dist/commands/self/index.js +20 -0
  113. package/dist/commands/self/update.js +152 -0
  114. package/dist/commands/session/id.js +24 -0
  115. package/dist/commands/session/remove.js +57 -0
  116. package/dist/commands/session/setup.js +62 -0
  117. package/dist/commands/skills/check.js +69 -0
  118. package/dist/commands/skills/index.js +20 -0
  119. package/dist/commands/skills/install.js +80 -0
  120. package/dist/commands/skills/remove.js +80 -0
  121. package/dist/commands/skills/update.js +87 -0
  122. package/dist/commands/source/build.js +58 -0
  123. package/dist/commands/source/dev.js +182 -0
  124. package/dist/commands/source/download.js +884 -0
  125. package/dist/commands/source/publish.js +109 -0
  126. package/dist/commands/source/registry/logs.js +70 -0
  127. package/dist/commands/source/registry/start.js +57 -0
  128. package/dist/commands/source/registry/status.js +33 -0
  129. package/dist/commands/source/registry/stop.js +48 -0
  130. package/dist/commands/source/test.js +476 -0
  131. package/dist/commands/start.js +12 -0
  132. package/dist/commands/stop.js +12 -0
  133. package/dist/commands/test.js +12 -0
  134. package/dist/commands/upgrade.js +12 -0
  135. package/dist/commands/v1.js +210 -0
  136. package/dist/generated/command-registry.js +134 -0
  137. package/dist/help/runtime-help.js +23 -0
  138. package/dist/lib/api-client.js +335 -0
  139. package/dist/lib/api-command-compat.js +641 -0
  140. package/dist/lib/app-health.js +139 -0
  141. package/dist/lib/app-managed-resources.js +337 -0
  142. package/dist/lib/app-public-path.js +80 -0
  143. package/dist/lib/app-runtime.js +189 -0
  144. package/dist/lib/auth-store.js +528 -0
  145. package/dist/lib/backup.js +171 -0
  146. package/dist/lib/bootstrap.js +409 -0
  147. package/dist/lib/build-config.js +18 -0
  148. package/dist/lib/builtin-db.js +86 -0
  149. package/dist/lib/cli-config.js +569 -0
  150. package/dist/lib/cli-entry-error.js +52 -0
  151. package/dist/lib/cli-home.js +47 -0
  152. package/dist/lib/cli-locale.js +141 -0
  153. package/dist/lib/command-discovery.js +39 -0
  154. package/dist/lib/command-log.js +284 -0
  155. package/dist/lib/db-connection-check.js +219 -0
  156. package/dist/lib/docker-env-file.js +60 -0
  157. package/dist/lib/docker-image.js +37 -0
  158. package/dist/lib/docker-log-stream.js +45 -0
  159. package/dist/lib/env-auth.js +963 -0
  160. package/dist/lib/env-command-config.js +45 -0
  161. package/dist/lib/env-config.js +108 -0
  162. package/dist/lib/env-guard.js +61 -0
  163. package/dist/lib/env-paths.js +101 -0
  164. package/dist/lib/env-proxy.js +1325 -0
  165. package/dist/lib/generated-command.js +203 -0
  166. package/dist/lib/http-request.js +49 -0
  167. package/dist/lib/inquirer-theme.js +17 -0
  168. package/dist/lib/inquirer.js +243 -0
  169. package/dist/lib/managed-env-file.js +101 -0
  170. package/dist/lib/managed-init-env.js +32 -0
  171. package/dist/lib/naming.js +70 -0
  172. package/dist/lib/object-utils.js +76 -0
  173. package/dist/lib/openapi.js +62 -0
  174. package/dist/lib/plugin-import.js +279 -0
  175. package/dist/lib/plugin-storage.js +64 -0
  176. package/dist/lib/post-processors.js +23 -0
  177. package/dist/lib/prompt-catalog-core.js +186 -0
  178. package/dist/lib/prompt-catalog-terminal.js +374 -0
  179. package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
  180. package/dist/lib/prompt-validators.js +278 -0
  181. package/dist/lib/prompt-web-ui.js +2234 -0
  182. package/dist/lib/proxy-caddy.js +274 -0
  183. package/dist/lib/proxy-nginx.js +330 -0
  184. package/dist/lib/resource-command.js +357 -0
  185. package/dist/lib/resource-request.js +104 -0
  186. package/dist/lib/run-npm.js +429 -0
  187. package/dist/lib/runtime-env-vars.js +32 -0
  188. package/dist/lib/runtime-generator.js +498 -0
  189. package/dist/lib/runtime-store.js +56 -0
  190. package/dist/lib/self-manager.js +301 -0
  191. package/dist/lib/session-id.js +17 -0
  192. package/dist/lib/session-integration.js +703 -0
  193. package/dist/lib/session-store.js +118 -0
  194. package/dist/lib/skills-manager.js +438 -0
  195. package/dist/lib/source-publish.js +326 -0
  196. package/dist/lib/source-registry.js +188 -0
  197. package/dist/lib/startup-update.js +309 -0
  198. package/dist/lib/ui.js +159 -0
  199. package/dist/locale/en-US.json +526 -0
  200. package/dist/locale/zh-CN.json +526 -0
  201. package/dist/post-processors/data-modeling.js +84 -0
  202. package/dist/post-processors/data-source-manager.js +138 -0
  203. package/dist/post-processors/index.js +19 -0
  204. package/nocobase-ctl.config.json +388 -0
  205. package/package.json +128 -24
  206. package/scripts/build.mjs +34 -0
  207. package/scripts/clean.mjs +9 -0
  208. package/tsconfig.json +19 -0
  209. package/bin/index.js +0 -39
  210. package/nocobase.conf.tpl +0 -95
  211. package/src/commands/benchmark.js +0 -73
  212. package/src/commands/build.js +0 -49
  213. package/src/commands/clean.js +0 -30
  214. package/src/commands/client.js +0 -166
  215. package/src/commands/create-nginx-conf.js +0 -37
  216. package/src/commands/create-plugin.js +0 -33
  217. package/src/commands/dev.js +0 -200
  218. package/src/commands/doc.js +0 -76
  219. package/src/commands/e2e.js +0 -265
  220. package/src/commands/global.js +0 -43
  221. package/src/commands/index.js +0 -45
  222. package/src/commands/instance-id.js +0 -47
  223. package/src/commands/locale/cronstrue.js +0 -122
  224. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  225. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  226. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  227. package/src/commands/locale.js +0 -81
  228. package/src/commands/p-test.js +0 -88
  229. package/src/commands/perf.js +0 -63
  230. package/src/commands/pkg.js +0 -321
  231. package/src/commands/pm2.js +0 -37
  232. package/src/commands/postinstall.js +0 -88
  233. package/src/commands/start.js +0 -148
  234. package/src/commands/tar.js +0 -36
  235. package/src/commands/test-coverage.js +0 -55
  236. package/src/commands/test.js +0 -107
  237. package/src/commands/umi.js +0 -33
  238. package/src/commands/update-deps.js +0 -72
  239. package/src/commands/upgrade.js +0 -47
  240. package/src/commands/view-license-key.js +0 -44
  241. package/src/license.js +0 -76
  242. package/src/logger.js +0 -75
  243. package/src/plugin-generator.js +0 -80
  244. package/src/util.js +0 -517
  245. package/templates/bundle-status.html +0 -338
  246. package/templates/create-app-package.json +0 -39
  247. package/templates/plugin/.npmignore.tpl +0 -2
  248. package/templates/plugin/README.md.tpl +0 -1
  249. package/templates/plugin/client.d.ts +0 -2
  250. package/templates/plugin/client.js +0 -1
  251. package/templates/plugin/package.json.tpl +0 -11
  252. package/templates/plugin/server.d.ts +0 -2
  253. package/templates/plugin/server.js +0 -1
  254. package/templates/plugin/src/client/client.d.ts +0 -249
  255. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  256. package/templates/plugin/src/client/locale.ts +0 -21
  257. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  258. package/templates/plugin/src/index.ts +0 -2
  259. package/templates/plugin/src/locale/en-US.json +0 -1
  260. package/templates/plugin/src/locale/zh-CN.json +0 -1
  261. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  262. package/templates/plugin/src/server/index.ts.tpl +0 -1
  263. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,641 @@
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 { translateCli } from './cli-locale.js';
10
+ import { compareVersions } from './self-manager.js';
11
+ function isRecord(value) {
12
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
13
+ }
14
+ function normalizeText(value) {
15
+ const normalized = String(value ?? '').trim();
16
+ return normalized || undefined;
17
+ }
18
+ function parseVersionCondition(value) {
19
+ if (!isRecord(value)) {
20
+ return undefined;
21
+ }
22
+ const condition = {};
23
+ for (const operator of ['eq', 'gt', 'gte', 'lt', 'lte']) {
24
+ const normalized = normalizeText(value[operator]);
25
+ if (normalized) {
26
+ condition[operator] = normalized;
27
+ }
28
+ }
29
+ return Object.keys(condition).length > 0 ? condition : undefined;
30
+ }
31
+ function parseAppByChannelCondition(value) {
32
+ if (!isRecord(value)) {
33
+ return undefined;
34
+ }
35
+ const stable = parseVersionCondition(value.stable);
36
+ const beta = parseVersionCondition(value.beta);
37
+ const alpha = parseVersionCondition(value.alpha);
38
+ const rc = parseVersionCondition(value.rc);
39
+ const unknownPrerelease = value.unknownPrerelease === 'block' || value.unknownPrerelease === 'skip' ? value.unknownPrerelease : undefined;
40
+ if (!stable && !beta && !alpha && !rc && !unknownPrerelease) {
41
+ return undefined;
42
+ }
43
+ return {
44
+ ...(stable ? { stable } : {}),
45
+ ...(beta ? { beta } : {}),
46
+ ...(alpha ? { alpha } : {}),
47
+ ...(rc ? { rc } : {}),
48
+ ...(unknownPrerelease ? { unknownPrerelease } : {}),
49
+ };
50
+ }
51
+ function parseCompatRule(value, order) {
52
+ if (!isRecord(value)) {
53
+ return undefined;
54
+ }
55
+ const code = normalizeText(value.code);
56
+ const target = isRecord(value.target) ? value.target : undefined;
57
+ const command = normalizeText(target?.command);
58
+ const commandPrefix = normalizeText(target?.commandPrefix);
59
+ if (!code || (!command && !commandPrefix)) {
60
+ return undefined;
61
+ }
62
+ const when = isRecord(value.when) ? value.when : undefined;
63
+ const cli = parseVersionCondition(when?.cli);
64
+ const app = parseVersionCondition(when?.app);
65
+ const appByChannel = parseAppByChannelCondition(when?.appByChannel);
66
+ const skills = parseVersionCondition(when?.skills);
67
+ return {
68
+ code,
69
+ order,
70
+ target: {
71
+ ...(command ? { command } : {}),
72
+ ...(commandPrefix ? { commandPrefix } : {}),
73
+ },
74
+ ...(cli || app || appByChannel || skills
75
+ ? {
76
+ when: {
77
+ ...(cli ? { cli } : {}),
78
+ ...(app ? { app } : {}),
79
+ ...(appByChannel ? { appByChannel } : {}),
80
+ ...(skills ? { skills } : {}),
81
+ },
82
+ }
83
+ : {}),
84
+ };
85
+ }
86
+ export function getApiCommandCompatRules(packageJson) {
87
+ if (!isRecord(packageJson)) {
88
+ return [];
89
+ }
90
+ const nocobase = isRecord(packageJson.nocobase) ? packageJson.nocobase : undefined;
91
+ const compat = isRecord(nocobase?.apiCommandCompat) ? nocobase.apiCommandCompat : undefined;
92
+ const rawRules = Array.isArray(compat?.rules) ? compat.rules : [];
93
+ return rawRules
94
+ .map((rule, order) => parseCompatRule(rule, order))
95
+ .filter((rule) => Boolean(rule))
96
+ .map(({ order: _order, ...rule }) => rule);
97
+ }
98
+ function compareWithOperator(version, operator, expected) {
99
+ const compared = compareVersions(version, expected);
100
+ switch (operator) {
101
+ case 'eq':
102
+ return compared === 0;
103
+ case 'gt':
104
+ return compared > 0;
105
+ case 'gte':
106
+ return compared >= 0;
107
+ case 'lt':
108
+ return compared < 0;
109
+ case 'lte':
110
+ return compared <= 0;
111
+ default:
112
+ return false;
113
+ }
114
+ }
115
+ function normalizeAppByChannelComparableVersion(version) {
116
+ const normalized = String(version ?? '').trim();
117
+ const match = normalized.match(/^(\d+\.\d+\.\d+)-(alpha|beta|rc)(?:\.(\d+))?(?:\..+)?$/);
118
+ if (!match) {
119
+ return normalized;
120
+ }
121
+ const [, base, channel, sequence] = match;
122
+ return sequence ? `${base}-${channel}.${sequence}` : `${base}-${channel}`;
123
+ }
124
+ function normalizeAppByChannelCondition(condition) {
125
+ if (!condition) {
126
+ return undefined;
127
+ }
128
+ const normalized = {};
129
+ for (const operator of ['eq', 'gt', 'gte', 'lt', 'lte']) {
130
+ const version = condition[operator];
131
+ if (version) {
132
+ normalized[operator] = normalizeAppByChannelComparableVersion(version);
133
+ }
134
+ }
135
+ return normalized;
136
+ }
137
+ function matchesVersionCondition(version, condition) {
138
+ if (!condition) {
139
+ return true;
140
+ }
141
+ return ['eq', 'gt', 'gte', 'lt', 'lte'].every((operator) => {
142
+ const expected = condition[operator];
143
+ return expected ? compareWithOperator(version, operator, expected) : true;
144
+ });
145
+ }
146
+ function evaluateVersionCondition(version, condition, options) {
147
+ if (!condition) {
148
+ return 'match';
149
+ }
150
+ if (!version) {
151
+ return options.blockOnMissing ? 'missing' : 'skip';
152
+ }
153
+ return matchesVersionCondition(version, condition) ? 'match' : 'mismatch';
154
+ }
155
+ function resolveAppChannel(version) {
156
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-.]+))?$/);
157
+ if (!match) {
158
+ return 'unknownPrerelease';
159
+ }
160
+ const prerelease = match[4];
161
+ if (!prerelease) {
162
+ return 'stable';
163
+ }
164
+ const channel = prerelease.split('.').find(Boolean);
165
+ if (channel === 'alpha') {
166
+ return 'alpha';
167
+ }
168
+ if (channel === 'beta') {
169
+ return 'beta';
170
+ }
171
+ if (channel === 'rc') {
172
+ return 'rc';
173
+ }
174
+ return 'unknownPrerelease';
175
+ }
176
+ function evaluateAppByChannelCondition(version, condition) {
177
+ if (!condition) {
178
+ return {
179
+ result: 'match',
180
+ };
181
+ }
182
+ if (!version) {
183
+ return {
184
+ result: 'missing',
185
+ };
186
+ }
187
+ const channel = resolveAppChannel(version);
188
+ if (channel === 'unknownPrerelease') {
189
+ if (condition.unknownPrerelease === 'block') {
190
+ return {
191
+ result: 'match',
192
+ channel,
193
+ blockedByUnknownChannel: true,
194
+ };
195
+ }
196
+ return {
197
+ result: 'skip',
198
+ channel,
199
+ };
200
+ }
201
+ const channelCondition = condition[channel];
202
+ if (!channelCondition) {
203
+ return {
204
+ result: 'skip',
205
+ channel,
206
+ };
207
+ }
208
+ const comparableVersion = normalizeAppByChannelComparableVersion(version);
209
+ const comparableCondition = normalizeAppByChannelCondition(channelCondition);
210
+ return {
211
+ result: matchesVersionCondition(comparableVersion, comparableCondition) ? 'match' : 'mismatch',
212
+ channel,
213
+ condition: channelCondition,
214
+ };
215
+ }
216
+ function matchesCommandPrefix(commandId, commandPrefix) {
217
+ if (!commandPrefix) {
218
+ return false;
219
+ }
220
+ return commandId === commandPrefix || commandId.startsWith(`${commandPrefix} `);
221
+ }
222
+ function formatVersionCondition(condition) {
223
+ if (!condition) {
224
+ return undefined;
225
+ }
226
+ const clauses = [];
227
+ if (condition.eq) {
228
+ clauses.push(`= ${condition.eq}`);
229
+ }
230
+ if (condition.gt) {
231
+ clauses.push(`> ${condition.gt}`);
232
+ }
233
+ if (condition.gte) {
234
+ clauses.push(`>= ${condition.gte}`);
235
+ }
236
+ if (condition.lt) {
237
+ clauses.push(`< ${condition.lt}`);
238
+ }
239
+ if (condition.lte) {
240
+ clauses.push(`<= ${condition.lte}`);
241
+ }
242
+ return clauses.length > 0 ? clauses.join(' and ') : undefined;
243
+ }
244
+ function getSuggestedUpgradeTarget(condition) {
245
+ if (condition?.lt) {
246
+ return `>= ${condition.lt}`;
247
+ }
248
+ if (condition?.lte) {
249
+ return `> ${condition.lte}`;
250
+ }
251
+ return undefined;
252
+ }
253
+ function getSuggestedCompatibleTarget(condition) {
254
+ if (condition?.gt) {
255
+ return `<= ${condition.gt}`;
256
+ }
257
+ if (condition?.gte) {
258
+ return `< ${condition.gte}`;
259
+ }
260
+ return undefined;
261
+ }
262
+ function getSuggestedTarget(condition) {
263
+ return getSuggestedUpgradeTarget(condition) ?? getSuggestedCompatibleTarget(condition);
264
+ }
265
+ function getPrioritizedRules(commandId, rules) {
266
+ const exactMatches = [];
267
+ const prefixMatches = [];
268
+ rules.forEach((rule, order) => {
269
+ const parsedRule = { ...rule, order };
270
+ if (rule.target.command === commandId) {
271
+ exactMatches.push(parsedRule);
272
+ return;
273
+ }
274
+ if (matchesCommandPrefix(commandId, rule.target.commandPrefix)) {
275
+ prefixMatches.push(parsedRule);
276
+ }
277
+ });
278
+ prefixMatches.sort((left, right) => {
279
+ const leftLength = left.target.commandPrefix?.length ?? 0;
280
+ const rightLength = right.target.commandPrefix?.length ?? 0;
281
+ return rightLength - leftLength || left.order - right.order;
282
+ });
283
+ return [...exactMatches, ...prefixMatches];
284
+ }
285
+ function pushIfPresent(values, value) {
286
+ if (value) {
287
+ values.push(value);
288
+ }
289
+ }
290
+ function formatJoinedList(values) {
291
+ if (values.length <= 1) {
292
+ return values[0] ?? '';
293
+ }
294
+ if (values.length === 2) {
295
+ return translateCli('apiCommandCompat.join.two', {
296
+ left: values[0],
297
+ right: values[1],
298
+ }, {
299
+ fallback: '{{left}} and {{right}}',
300
+ });
301
+ }
302
+ return translateCli('apiCommandCompat.join.many', {
303
+ head: values.slice(0, -1).join(', '),
304
+ tail: values[values.length - 1],
305
+ }, {
306
+ fallback: '{{head}}, and {{tail}}',
307
+ });
308
+ }
309
+ function getAppChannelLabel(channel) {
310
+ return translateCli(`apiCommandCompat.channel.${channel}`, undefined, {
311
+ fallback: channel === 'stable'
312
+ ? 'stable'
313
+ : channel === 'beta'
314
+ ? 'beta'
315
+ : channel === 'alpha'
316
+ ? 'alpha'
317
+ : channel === 'rc'
318
+ ? 'rc'
319
+ : 'unsupported prerelease',
320
+ });
321
+ }
322
+ function buildAppChannelRequirement(channel, condition) {
323
+ return translateCli('apiCommandCompat.requirement.appChannel', {
324
+ channelLabel: getAppChannelLabel(channel),
325
+ condition: formatVersionCondition(condition),
326
+ }, {
327
+ fallback: '{{channelLabel}} app version {{condition}}',
328
+ });
329
+ }
330
+ function buildAppUnknownPrereleaseRequirement() {
331
+ return translateCli('apiCommandCompat.requirement.appUnknownPrereleaseBlocked', {
332
+ channelLabel: getAppChannelLabel('unknownPrerelease'),
333
+ }, {
334
+ fallback: '{{channelLabel}} app versions are blocked',
335
+ });
336
+ }
337
+ function buildAppByChannelRequirementSummary(condition) {
338
+ const parts = [];
339
+ pushIfPresent(parts, condition.stable ? buildAppChannelRequirement('stable', condition.stable) : undefined);
340
+ pushIfPresent(parts, condition.beta ? buildAppChannelRequirement('beta', condition.beta) : undefined);
341
+ pushIfPresent(parts, condition.alpha ? buildAppChannelRequirement('alpha', condition.alpha) : undefined);
342
+ pushIfPresent(parts, condition.rc ? buildAppChannelRequirement('rc', condition.rc) : undefined);
343
+ pushIfPresent(parts, condition.unknownPrerelease === 'block' ? buildAppUnknownPrereleaseRequirement() : undefined);
344
+ return parts.length > 0 ? formatJoinedList(parts) : undefined;
345
+ }
346
+ function buildRequirementText(rule, violation) {
347
+ const parts = [];
348
+ const cliVersionCondition = formatVersionCondition(rule.when?.cli);
349
+ const appVersionCondition = formatVersionCondition(rule.when?.app);
350
+ const skillsVersionCondition = formatVersionCondition(rule.when?.skills);
351
+ pushIfPresent(parts, cliVersionCondition
352
+ ? translateCli('apiCommandCompat.requirement.cli', {
353
+ condition: cliVersionCondition,
354
+ }, {
355
+ fallback: 'CLI version {{condition}}',
356
+ })
357
+ : undefined);
358
+ if (violation.matchedAppByChannelCondition && violation.appChannel && violation.appChannel !== 'unknownPrerelease') {
359
+ pushIfPresent(parts, buildAppChannelRequirement(violation.appChannel, violation.matchedAppByChannelCondition));
360
+ }
361
+ else if (violation.blockedByUnknownAppChannel) {
362
+ pushIfPresent(parts, buildAppUnknownPrereleaseRequirement());
363
+ }
364
+ else {
365
+ pushIfPresent(parts, rule.when?.appByChannel ? buildAppByChannelRequirementSummary(rule.when.appByChannel) : undefined);
366
+ }
367
+ pushIfPresent(parts, appVersionCondition
368
+ ? translateCli('apiCommandCompat.requirement.app', {
369
+ condition: appVersionCondition,
370
+ }, {
371
+ fallback: 'app version {{condition}}',
372
+ })
373
+ : undefined);
374
+ pushIfPresent(parts, skillsVersionCondition
375
+ ? translateCli('apiCommandCompat.requirement.skills', {
376
+ condition: skillsVersionCondition,
377
+ }, {
378
+ fallback: 'NocoBase AI skills version {{condition}}',
379
+ })
380
+ : undefined);
381
+ return parts.length > 0 ? formatJoinedList(parts) : undefined;
382
+ }
383
+ function buildCurrentVersionsText(violation) {
384
+ const parts = [];
385
+ pushIfPresent(parts, violation.cliVersion
386
+ ? translateCli('apiCommandCompat.currentVersion.cli', {
387
+ version: violation.cliVersion,
388
+ }, {
389
+ fallback: 'CLI {{version}}',
390
+ })
391
+ : undefined);
392
+ pushIfPresent(parts, violation.appVersion
393
+ ? translateCli('apiCommandCompat.currentVersion.app', {
394
+ version: violation.appVersion,
395
+ }, {
396
+ fallback: 'app {{version}}',
397
+ })
398
+ : violation.rule.when?.app || violation.rule.when?.appByChannel
399
+ ? translateCli('apiCommandCompat.currentVersion.appUnavailable', undefined, {
400
+ fallback: 'app unavailable',
401
+ })
402
+ : undefined);
403
+ pushIfPresent(parts, violation.skillsVersion
404
+ ? translateCli('apiCommandCompat.currentVersion.skills', {
405
+ version: violation.skillsVersion,
406
+ }, {
407
+ fallback: 'NocoBase AI skills {{version}}',
408
+ })
409
+ : violation.rule.when?.skills
410
+ ? translateCli('apiCommandCompat.currentVersion.skillsUnavailable', undefined, {
411
+ fallback: 'NocoBase AI skills unavailable',
412
+ })
413
+ : undefined);
414
+ return translateCli('apiCommandCompat.currentVersionsLine', {
415
+ versions: formatJoinedList(parts),
416
+ }, {
417
+ fallback: 'Current versions: {{versions}}.',
418
+ });
419
+ }
420
+ function buildMissingVersionLabel(missingVersions) {
421
+ const labels = missingVersions.map((versionKey) => translateCli(`apiCommandCompat.versionLabel.${versionKey}`, undefined, {
422
+ fallback: versionKey === 'cli'
423
+ ? 'CLI version'
424
+ : versionKey === 'app'
425
+ ? 'target app version'
426
+ : 'installed NocoBase AI skills version',
427
+ }));
428
+ return formatJoinedList(labels);
429
+ }
430
+ function getSuggestedAppByChannelTarget(condition, violation) {
431
+ if (!condition || !violation.appChannel || violation.appChannel === 'unknownPrerelease') {
432
+ return undefined;
433
+ }
434
+ return getSuggestedTarget(violation.matchedAppByChannelCondition ?? condition[violation.appChannel]);
435
+ }
436
+ function getSupportedAppVersionSummary(condition) {
437
+ if (!condition) {
438
+ return undefined;
439
+ }
440
+ const parts = [];
441
+ pushIfPresent(parts, condition.stable
442
+ ? translateCli('apiCommandCompat.supportedAppTarget', {
443
+ channelLabel: getAppChannelLabel('stable'),
444
+ target: getSuggestedTarget(condition.stable),
445
+ }, {
446
+ fallback: '{{channelLabel}} {{target}}',
447
+ })
448
+ : undefined);
449
+ pushIfPresent(parts, condition.beta
450
+ ? translateCli('apiCommandCompat.supportedAppTarget', {
451
+ channelLabel: getAppChannelLabel('beta'),
452
+ target: getSuggestedTarget(condition.beta),
453
+ }, {
454
+ fallback: '{{channelLabel}} {{target}}',
455
+ })
456
+ : undefined);
457
+ pushIfPresent(parts, condition.alpha
458
+ ? translateCli('apiCommandCompat.supportedAppTarget', {
459
+ channelLabel: getAppChannelLabel('alpha'),
460
+ target: getSuggestedTarget(condition.alpha),
461
+ }, {
462
+ fallback: '{{channelLabel}} {{target}}',
463
+ })
464
+ : undefined);
465
+ pushIfPresent(parts, condition.rc
466
+ ? translateCli('apiCommandCompat.supportedAppTarget', {
467
+ channelLabel: getAppChannelLabel('rc'),
468
+ target: getSuggestedTarget(condition.rc),
469
+ }, {
470
+ fallback: '{{channelLabel}} {{target}}',
471
+ })
472
+ : undefined);
473
+ return parts.length > 0 ? formatJoinedList(parts) : undefined;
474
+ }
475
+ export function findApiCommandCompatViolation(options) {
476
+ const commandId = normalizeText(options.commandId);
477
+ const cliVersion = normalizeText(options.cliVersion);
478
+ const appVersion = normalizeText(options.appVersion);
479
+ const skillsVersion = normalizeText(options.skillsVersion);
480
+ if (!commandId) {
481
+ return undefined;
482
+ }
483
+ const rules = getApiCommandCompatRules(options.packageJson);
484
+ for (const rule of getPrioritizedRules(commandId, rules)) {
485
+ const missingVersions = new Set();
486
+ const cliResult = evaluateVersionCondition(cliVersion, rule.when?.cli, {
487
+ blockOnMissing: true,
488
+ });
489
+ const appResult = evaluateVersionCondition(appVersion, rule.when?.app, {
490
+ blockOnMissing: false,
491
+ });
492
+ const appByChannelResult = evaluateAppByChannelCondition(appVersion, rule.when?.appByChannel);
493
+ const skillsResult = evaluateVersionCondition(skillsVersion, rule.when?.skills, {
494
+ blockOnMissing: true,
495
+ });
496
+ if ([cliResult, appResult, appByChannelResult.result, skillsResult].includes('mismatch')) {
497
+ continue;
498
+ }
499
+ if ([cliResult, appResult, appByChannelResult.result, skillsResult].includes('skip')) {
500
+ continue;
501
+ }
502
+ if (cliResult === 'missing') {
503
+ missingVersions.add('cli');
504
+ }
505
+ if (appResult === 'missing' || appByChannelResult.result === 'missing') {
506
+ missingVersions.add('app');
507
+ }
508
+ if (skillsResult === 'missing') {
509
+ missingVersions.add('skills');
510
+ }
511
+ return {
512
+ rule,
513
+ commandId,
514
+ cliVersion,
515
+ appVersion,
516
+ skillsVersion,
517
+ missingVersions: Array.from(missingVersions),
518
+ appChannel: appByChannelResult.channel,
519
+ matchedAppByChannelCondition: appByChannelResult.condition,
520
+ blockedByUnknownAppChannel: appByChannelResult.blockedByUnknownChannel,
521
+ };
522
+ }
523
+ return undefined;
524
+ }
525
+ export function formatApiCommandCompatViolation(violation) {
526
+ const requirementText = buildRequirementText(violation.rule, violation);
527
+ const appUpgradeTarget = getSuggestedAppByChannelTarget(violation.rule.when?.appByChannel, violation) ??
528
+ getSuggestedUpgradeTarget(violation.rule.when?.app);
529
+ const supportedAppVersionSummary = getSupportedAppVersionSummary(violation.rule.when?.appByChannel);
530
+ const cliTarget = getSuggestedCompatibleTarget(violation.rule.when?.cli);
531
+ const skillsUpgradeTarget = getSuggestedUpgradeTarget(violation.rule.when?.skills);
532
+ const skillsCompatibleTarget = getSuggestedCompatibleTarget(violation.rule.when?.skills);
533
+ const lines = [];
534
+ if (violation.missingVersions.length > 0) {
535
+ lines.push(translateCli('apiCommandCompat.refusalTitleMissingVersion', {
536
+ commandId: violation.commandId,
537
+ versionLabel: buildMissingVersionLabel(violation.missingVersions),
538
+ }, {
539
+ fallback: 'Refusing to run `nb api {{commandId}}` because the {{versionLabel}} is unavailable.',
540
+ }));
541
+ }
542
+ else if (violation.appVersion) {
543
+ lines.push(translateCli('apiCommandCompat.refusalTitle', {
544
+ commandId: violation.commandId,
545
+ cliVersion: violation.cliVersion,
546
+ appVersion: violation.appVersion,
547
+ }, {
548
+ fallback: 'Refusing to run `nb api {{commandId}}` because the current CLI version is {{cliVersion}} while the target app version is {{appVersion}}.',
549
+ }));
550
+ }
551
+ else {
552
+ lines.push(translateCli('apiCommandCompat.refusalTitleGeneric', {
553
+ commandId: violation.commandId,
554
+ }, {
555
+ fallback: 'Refusing to run `nb api {{commandId}}` because the current version combination is not compatible.',
556
+ }));
557
+ }
558
+ lines.push('');
559
+ lines.push(buildCurrentVersionsText(violation));
560
+ lines.push('');
561
+ lines.push(requirementText
562
+ ? translateCli('apiCommandCompat.refusalBodyWithRequirements', {
563
+ requirements: requirementText,
564
+ }, {
565
+ fallback: 'This API command is blocked when used with {{requirements}}.',
566
+ })
567
+ : translateCli('apiCommandCompat.refusalBody', undefined, {
568
+ fallback: 'This API command may not be compatible with the current CLI and app version combination.',
569
+ }));
570
+ lines.push('');
571
+ lines.push(translateCli('apiCommandCompat.continueTitle', undefined, {
572
+ fallback: 'To continue:',
573
+ }));
574
+ if (violation.missingVersions.includes('app')) {
575
+ lines.push(translateCli('apiCommandCompat.continueRefreshAppVersion', undefined, {
576
+ fallback: '- refresh the target env runtime so the current app version can be detected, then retry',
577
+ }));
578
+ }
579
+ else if (appUpgradeTarget) {
580
+ lines.push(translateCli('apiCommandCompat.continueUpgradeAppToVersion', {
581
+ suggestedUpgradeTarget: appUpgradeTarget,
582
+ }, {
583
+ fallback: '- upgrade the app to {{suggestedUpgradeTarget}}',
584
+ }));
585
+ }
586
+ else if (supportedAppVersionSummary) {
587
+ lines.push(translateCli('apiCommandCompat.continueUseSupportedAppVersion', {
588
+ supportedTargets: supportedAppVersionSummary,
589
+ }, {
590
+ fallback: '- use a supported app version: {{supportedTargets}}',
591
+ }));
592
+ }
593
+ else if (violation.rule.when?.app) {
594
+ lines.push(translateCli('apiCommandCompat.continueUpgradeApp', undefined, {
595
+ fallback: '- upgrade the app to a compatible version',
596
+ }));
597
+ }
598
+ if (violation.rule.when?.skills) {
599
+ if (violation.missingVersions.includes('skills')) {
600
+ if (skillsUpgradeTarget) {
601
+ lines.push(translateCli('apiCommandCompat.continueInstallOrUpdateSkillsToVersion', {
602
+ suggestedSkillsUpgradeTarget: skillsUpgradeTarget,
603
+ }, {
604
+ fallback: '- run `nb skills install --yes` or `nb skills update --yes`, and make sure the managed skills version is {{suggestedSkillsUpgradeTarget}} before retrying',
605
+ }));
606
+ }
607
+ else {
608
+ lines.push(translateCli('apiCommandCompat.continueInstallOrUpdateSkills', undefined, {
609
+ fallback: '- run `nb skills install --yes` or `nb skills update --yes`, then retry once the managed skills version is available',
610
+ }));
611
+ }
612
+ }
613
+ else if (skillsUpgradeTarget) {
614
+ lines.push(translateCli('apiCommandCompat.continueUpdateSkillsToVersion', {
615
+ suggestedSkillsUpgradeTarget: skillsUpgradeTarget,
616
+ }, {
617
+ fallback: '- install or update the NocoBase AI coding skills to {{suggestedSkillsUpgradeTarget}}. Run `nb skills update --yes` to refresh the managed skills.',
618
+ }));
619
+ }
620
+ else if (skillsCompatibleTarget) {
621
+ lines.push(translateCli('apiCommandCompat.continueUseCompatibleSkillsVersion', {
622
+ suggestedSkillsTarget: skillsCompatibleTarget,
623
+ }, {
624
+ fallback: '- or use a NocoBase AI skills version {{suggestedSkillsTarget}}',
625
+ }));
626
+ }
627
+ }
628
+ if (cliTarget) {
629
+ lines.push(translateCli('apiCommandCompat.continueUseCompatibleCliVersion', {
630
+ suggestedCliTarget: cliTarget,
631
+ }, {
632
+ fallback: '- or use a CLI version {{suggestedCliTarget}}',
633
+ }));
634
+ }
635
+ else if (violation.rule.when?.cli) {
636
+ lines.push(translateCli('apiCommandCompat.continueUseCompatibleCli', undefined, {
637
+ fallback: '- or use a compatible CLI version',
638
+ }));
639
+ }
640
+ return lines.join('\n');
641
+ }