@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,375 @@
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 pc from 'picocolors';
10
+ import { exit, stdin as stdinStream, stdout as stdoutStream } from 'node:process';
11
+ import { createCliTranslate } from "./cli-locale.js";
12
+ import { confirm, select, input, password } from "./inquirer.js";
13
+ import { createPromptCatalogHooks, hasIvKey, isBlankText, isPromptBlockSkipped, mergedBoolean, mergedInteger, mergedPassword, mergedSelect, mergedText, resolvePromptCatalogLocale, resolvePromptText, runPromptFieldValidate, selectOptionValues, tryApplyPreset, } from "./prompt-catalog-core.js";
14
+ function adaptInquirerValidate(validate) {
15
+ if (!validate) {
16
+ return undefined;
17
+ }
18
+ return async (value) => {
19
+ const result = await validate(value);
20
+ return result === undefined ? true : result;
21
+ };
22
+ }
23
+ function inquirerSelectOptions(options, locale) {
24
+ return options.map((o) => typeof o === 'string'
25
+ ? { value: o, label: o }
26
+ : {
27
+ value: o.value,
28
+ label: resolvePromptText(o.label, locale, o.value),
29
+ ...(o.hint !== undefined ? { hint: resolvePromptText(o.hint, locale) } : {}),
30
+ ...(o.disabled !== undefined ? { disabled: o.disabled } : {}),
31
+ });
32
+ }
33
+ function createInquirerRenderer() {
34
+ return {
35
+ intro(message) {
36
+ console.log(message);
37
+ },
38
+ outro(message) {
39
+ console.log(message);
40
+ },
41
+ error(message) {
42
+ console.error(pc.red(`✖ ${message}`));
43
+ },
44
+ text(options) {
45
+ return input({
46
+ message: options.message,
47
+ default: options.initialValue,
48
+ placeholder: options.placeholder,
49
+ validate: adaptInquirerValidate(options.validate),
50
+ });
51
+ },
52
+ confirm(options) {
53
+ return confirm({
54
+ message: options.message,
55
+ default: options.initialValue,
56
+ });
57
+ },
58
+ select(options) {
59
+ return select({
60
+ message: options.message,
61
+ choices: options.options.map((option) => ({
62
+ value: option.value,
63
+ name: option.label,
64
+ ...(option.hint !== undefined ? { description: option.hint } : {}),
65
+ ...(option.disabled !== undefined ? { disabled: option.disabled } : {}),
66
+ })),
67
+ default: options.initialValue,
68
+ });
69
+ },
70
+ password(options) {
71
+ return password({
72
+ message: options.message,
73
+ mask: options.mask ?? '•',
74
+ validate: adaptInquirerValidate(options.validate),
75
+ });
76
+ },
77
+ isCancel(error) {
78
+ return error instanceof Error && error.name === 'ExitPromptError';
79
+ },
80
+ };
81
+ }
82
+ function defaultOnCancel(locale) {
83
+ void locale;
84
+ exit(0);
85
+ }
86
+ function defaultOnMissingNonInteractive(message) {
87
+ console.error(message);
88
+ exit(1);
89
+ }
90
+ function createTerminalHooks(locale, overrides) {
91
+ return createPromptCatalogHooks(locale, overrides, {
92
+ onCancel: () => defaultOnCancel(locale),
93
+ onMissingNonInteractive: defaultOnMissingNonInteractive,
94
+ });
95
+ }
96
+ async function callPrompt(run, renderer, hooks) {
97
+ try {
98
+ return await run();
99
+ }
100
+ catch (error) {
101
+ if (renderer.isCancel(error)) {
102
+ hooks.onCancel();
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ export async function runPromptCatalog(catalog, options = {}) {
108
+ const locale = resolvePromptCatalogLocale(options.locale);
109
+ const t = createCliTranslate(locale);
110
+ const promptIv = options.initialValues ?? {};
111
+ const yesIv = options.yesInitialValues ?? {};
112
+ const resolveIv = options.yes ? { ...promptIv, ...yesIv } : promptIv;
113
+ const useYesInitial = Boolean(options.yes);
114
+ const hooks = createTerminalHooks(locale, options.hooks);
115
+ const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY && !options.yes);
116
+ const preset = options.values ?? {};
117
+ const out = {};
118
+ const renderer = createInquirerRenderer();
119
+ for (const [key, def] of Object.entries(catalog)) {
120
+ if (isPromptBlockSkipped(def, out)) {
121
+ continue;
122
+ }
123
+ if (tryApplyPreset(key, def, preset, out, hooks, locale)) {
124
+ const errV = await runPromptFieldValidate(def, out[key], out);
125
+ if (errV) {
126
+ hooks.onMissingNonInteractive(errV);
127
+ }
128
+ continue;
129
+ }
130
+ if (def.type === 'intro') {
131
+ renderer.intro(resolvePromptText(def.title, locale));
132
+ continue;
133
+ }
134
+ if (def.type === 'outro') {
135
+ renderer.outro(resolvePromptText(def.message, locale));
136
+ continue;
137
+ }
138
+ if (def.type === 'run') {
139
+ await def.run(out, options.command);
140
+ continue;
141
+ }
142
+ if (def.type === 'text') {
143
+ const message = resolvePromptText(def.message, locale, key);
144
+ const placeholder = def.placeholder !== undefined
145
+ ? resolvePromptText(def.placeholder, locale)
146
+ : undefined;
147
+ if (!interactive) {
148
+ const merged = mergedText(key, def, resolveIv, useYesInitial, out);
149
+ if (def.required && isBlankText(merged)) {
150
+ hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.textRequired', { key }));
151
+ }
152
+ out[key] = merged;
153
+ const errT = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
154
+ if (errT) {
155
+ hooks.onMissingNonInteractive(errT);
156
+ }
157
+ continue;
158
+ }
159
+ const merged = mergedText(key, def, promptIv, false, out);
160
+ const raw = await callPrompt(() => renderer.text({
161
+ message,
162
+ initialValue: merged,
163
+ ...(placeholder !== undefined ? { placeholder } : {}),
164
+ validate: (value) => {
165
+ if (def.required && isBlankText(value)) {
166
+ return t('promptCatalog.common.required');
167
+ }
168
+ if (!def.validate) {
169
+ return undefined;
170
+ }
171
+ const currentValue = typeof value === 'string' ? value : String(value ?? '');
172
+ const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
173
+ return result;
174
+ },
175
+ }), renderer, hooks);
176
+ out[key] = typeof raw === 'string' ? raw : merged;
177
+ continue;
178
+ }
179
+ if (def.type === 'boolean') {
180
+ const message = resolvePromptText(def.message, locale, key);
181
+ if (!interactive) {
182
+ const b = mergedBoolean(key, def, resolveIv, useYesInitial);
183
+ out[key] = b;
184
+ const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
185
+ if (errB) {
186
+ hooks.onMissingNonInteractive(errB);
187
+ }
188
+ continue;
189
+ }
190
+ const merged = mergedBoolean(key, def, promptIv, false);
191
+ if (def.validate) {
192
+ for (;;) {
193
+ const raw = await callPrompt(() => renderer.confirm({ message, initialValue: merged }), renderer, hooks);
194
+ const b = Boolean(raw);
195
+ const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
196
+ if (errB) {
197
+ renderer.error(errB);
198
+ continue;
199
+ }
200
+ out[key] = b;
201
+ break;
202
+ }
203
+ continue;
204
+ }
205
+ const raw = await callPrompt(() => renderer.confirm({ message, initialValue: merged }), renderer, hooks);
206
+ out[key] = Boolean(raw);
207
+ continue;
208
+ }
209
+ if (def.type === 'select') {
210
+ const message = resolvePromptText(def.message, locale, key);
211
+ const valueList = selectOptionValues(def.options);
212
+ if (def.required && def.options.length === 0) {
213
+ hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.selectRequiredNoOptions', { key }));
214
+ }
215
+ if (!interactive) {
216
+ const merged = mergedSelect(key, def, resolveIv, useYesInitial);
217
+ if (merged === undefined || !valueList.includes(merged)) {
218
+ const bad = hasIvKey(resolveIv, key) && !valueList.includes(String(resolveIv[key]))
219
+ ? String(resolveIv[key])
220
+ : hasIvKey(promptIv, key) && !valueList.includes(String(promptIv[key]))
221
+ ? String(promptIv[key])
222
+ : undefined;
223
+ hooks.onMissingNonInteractive(bad !== undefined
224
+ ? t('promptCatalog.nonInteractive.selectInvalidValue', { key, value: bad, options: valueList.join(', ') })
225
+ : t('promptCatalog.nonInteractive.selectMissingDefault', { key }));
226
+ }
227
+ out[key] = merged;
228
+ const errS = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
229
+ if (errS) {
230
+ hooks.onMissingNonInteractive(errS);
231
+ }
232
+ continue;
233
+ }
234
+ const merged = mergedSelect(key, def, promptIv, false);
235
+ const uiInitial = merged ??
236
+ (def.initialValue && valueList.includes(def.initialValue) ? def.initialValue : undefined) ??
237
+ valueList[0];
238
+ if (uiInitial === undefined || !valueList.includes(uiInitial)) {
239
+ const hint = def.required
240
+ ? t('promptCatalog.nonInteractive.selectRequiredInteractive', { key })
241
+ : t('promptCatalog.nonInteractive.selectMissingInteractiveDefault', { key });
242
+ hooks.onMissingNonInteractive(hint);
243
+ }
244
+ if (def.validate) {
245
+ for (;;) {
246
+ const raw = await callPrompt(() => renderer.select({
247
+ message,
248
+ options: inquirerSelectOptions(def.options, locale),
249
+ initialValue: uiInitial,
250
+ }), renderer, hooks);
251
+ const picked = raw;
252
+ const errS = await runPromptFieldValidate(def, picked, { ...out, [key]: picked });
253
+ if (errS) {
254
+ renderer.error(errS);
255
+ continue;
256
+ }
257
+ out[key] = picked;
258
+ break;
259
+ }
260
+ continue;
261
+ }
262
+ const raw = await callPrompt(() => renderer.select({
263
+ message,
264
+ options: inquirerSelectOptions(def.options, locale),
265
+ initialValue: uiInitial,
266
+ }), renderer, hooks);
267
+ out[key] = raw;
268
+ continue;
269
+ }
270
+ if (def.type === 'password') {
271
+ const message = resolvePromptText(def.message, locale, key);
272
+ if (!interactive) {
273
+ const merged = mergedPassword(key, def, resolveIv, useYesInitial);
274
+ if (merged === undefined) {
275
+ if (def.required) {
276
+ hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequired', { key }));
277
+ }
278
+ out[key] = '';
279
+ const errPE = await runPromptFieldValidate(def, '', { ...out, [key]: '' });
280
+ if (errPE) {
281
+ hooks.onMissingNonInteractive(errPE);
282
+ }
283
+ continue;
284
+ }
285
+ if (def.required && isBlankText(merged)) {
286
+ hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequiredNonEmpty', { key }));
287
+ }
288
+ out[key] = merged;
289
+ const errP = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
290
+ if (errP) {
291
+ hooks.onMissingNonInteractive(errP);
292
+ }
293
+ continue;
294
+ }
295
+ const raw = await callPrompt(() => renderer.password({
296
+ message,
297
+ mask: def.mask,
298
+ validate: (value) => {
299
+ if (def.required && isBlankText(value)) {
300
+ return t('promptCatalog.common.required');
301
+ }
302
+ if (!def.validate) {
303
+ return undefined;
304
+ }
305
+ const currentValue = typeof value === 'string' ? value : String(value ?? '');
306
+ const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
307
+ return result;
308
+ },
309
+ }), renderer, hooks);
310
+ out[key] = typeof raw === 'string' ? raw : '';
311
+ continue;
312
+ }
313
+ if (def.type === 'integer') {
314
+ const message = resolvePromptText(def.message, locale, key);
315
+ const placeholder = def.placeholder !== undefined
316
+ ? resolvePromptText(def.placeholder, locale)
317
+ : undefined;
318
+ if (!interactive) {
319
+ const merged = mergedInteger(key, def, resolveIv, useYesInitial);
320
+ if (merged === undefined) {
321
+ if (def.required) {
322
+ hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.integerRequired', { key }));
323
+ }
324
+ const z = def.initialValue ?? 0;
325
+ out[key] = z;
326
+ const errI = await runPromptFieldValidate(def, z, { ...out, [key]: z });
327
+ if (errI) {
328
+ hooks.onMissingNonInteractive(errI);
329
+ }
330
+ continue;
331
+ }
332
+ out[key] = merged;
333
+ const errI2 = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
334
+ if (errI2) {
335
+ hooks.onMissingNonInteractive(errI2);
336
+ }
337
+ continue;
338
+ }
339
+ const merged = mergedInteger(key, def, promptIv, false);
340
+ const lineDefault = merged !== undefined ? String(merged) : String(def.initialValue ?? 0);
341
+ const raw = await callPrompt(() => renderer.text({
342
+ message,
343
+ initialValue: lineDefault,
344
+ ...(placeholder !== undefined ? { placeholder } : {}),
345
+ validate: async (value) => {
346
+ const trimmed = value.trim();
347
+ if (trimmed === '') {
348
+ if (def.required) {
349
+ return t('promptCatalog.common.required');
350
+ }
351
+ if (def.validate) {
352
+ const z = def.initialValue ?? 0;
353
+ return runPromptFieldValidate(def, z, { ...out, [key]: z });
354
+ }
355
+ return undefined;
356
+ }
357
+ if (!/^-?\\d+$/.test(trimmed)) {
358
+ return t('promptCatalog.common.mustBeInteger');
359
+ }
360
+ if (!def.validate) {
361
+ return undefined;
362
+ }
363
+ const n = Number.parseInt(trimmed, 10);
364
+ return runPromptFieldValidate(def, n, { ...out, [key]: n });
365
+ },
366
+ }), renderer, hooks);
367
+ if (typeof raw === 'string' && raw.trim() === '' && !def.required) {
368
+ out[key] = def.initialValue ?? 0;
369
+ continue;
370
+ }
371
+ out[key] = Number.parseInt(String(raw).trim(), 10);
372
+ }
373
+ }
374
+ return out;
375
+ }
@@ -6,9 +6,5 @@
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
-
10
- const util = require('./util');
11
-
12
- module.exports = {
13
- ...util,
14
- };
9
+ export * from "./prompt-catalog-core.js";
10
+ export { runPromptCatalog } from "./prompt-catalog-terminal.js";
@@ -0,0 +1,240 @@
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 { spawn } from 'node:child_process';
10
+ import net from 'node:net';
11
+ import { translateCli } from "./cli-locale.js";
12
+ const API_BASE_URL_EXAMPLE = 'http://localhost:13000/api';
13
+ const ENV_KEY_PATTERN = /^[A-Za-z0-9]+$/;
14
+ const TCP_PORT_EXAMPLE = '13000';
15
+ const API_BASE_URL_REQUEST_TIMEOUT_MS = 5_000;
16
+ function buildHealthCheckUrl(apiBaseUrl) {
17
+ return `${apiBaseUrl.replace(/\/+$/, '')}/__health_check`;
18
+ }
19
+ function isMaintainingHealthCheckResponse(status, body) {
20
+ if (status !== 503 || !body || typeof body !== 'object') {
21
+ return false;
22
+ }
23
+ const error = body.error;
24
+ if (!error || typeof error !== 'object') {
25
+ return false;
26
+ }
27
+ const record = error;
28
+ return record.status === 503 && record.maintaining === true && record.code === 'APP_COMMANDING';
29
+ }
30
+ export async function validateApiBaseUrl(value) {
31
+ const raw = String(value ?? '').trim();
32
+ if (raw === '') {
33
+ return undefined;
34
+ }
35
+ let url;
36
+ try {
37
+ url = new URL(raw);
38
+ }
39
+ catch {
40
+ return translateCli('validators.apiBaseUrl.invalid', { example: API_BASE_URL_EXAMPLE });
41
+ }
42
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
43
+ return translateCli('validators.apiBaseUrl.invalidProtocol', { example: API_BASE_URL_EXAMPLE });
44
+ }
45
+ if (/\/__health_check\/?$/i.test(url.pathname)) {
46
+ return translateCli('validators.apiBaseUrl.healthCheckPathNotAllowed');
47
+ }
48
+ const controller = new AbortController();
49
+ const timeout = setTimeout(() => {
50
+ controller.abort();
51
+ }, API_BASE_URL_REQUEST_TIMEOUT_MS);
52
+ try {
53
+ const response = await fetch(buildHealthCheckUrl(url.toString()), {
54
+ method: 'GET',
55
+ signal: controller.signal,
56
+ });
57
+ if (response.status === 200) {
58
+ return undefined;
59
+ }
60
+ let body;
61
+ try {
62
+ body = await response.json();
63
+ }
64
+ catch {
65
+ body = undefined;
66
+ }
67
+ if (isMaintainingHealthCheckResponse(response.status, body)) {
68
+ return translateCli('validators.apiBaseUrl.maintaining');
69
+ }
70
+ return translateCli('validators.apiBaseUrl.healthCheckFailed', { status: response.status });
71
+ }
72
+ catch (error) {
73
+ if (error instanceof Error && error.name === 'AbortError') {
74
+ return translateCli('validators.apiBaseUrl.timeout', {
75
+ seconds: Math.ceil(API_BASE_URL_REQUEST_TIMEOUT_MS / 1000),
76
+ });
77
+ }
78
+ return translateCli('validators.apiBaseUrl.unreachable', {
79
+ details: error instanceof Error ? error.message : String(error),
80
+ });
81
+ }
82
+ finally {
83
+ clearTimeout(timeout);
84
+ }
85
+ return undefined;
86
+ }
87
+ export function validateEnvKey(value) {
88
+ const raw = String(value ?? '').trim();
89
+ if (raw === '') {
90
+ return undefined;
91
+ }
92
+ if (!ENV_KEY_PATTERN.test(raw)) {
93
+ return translateCli('validators.envKey.invalid');
94
+ }
95
+ return undefined;
96
+ }
97
+ function parseTcpPort(value) {
98
+ const raw = String(value ?? '').trim();
99
+ if (raw === '') {
100
+ return undefined;
101
+ }
102
+ if (!/^\d+$/.test(raw)) {
103
+ return undefined;
104
+ }
105
+ const port = Number(raw);
106
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
107
+ return undefined;
108
+ }
109
+ return port;
110
+ }
111
+ export function validateTcpPort(value) {
112
+ const raw = String(value ?? '').trim();
113
+ if (raw === '') {
114
+ return undefined;
115
+ }
116
+ const port = parseTcpPort(raw);
117
+ if (port === undefined) {
118
+ return translateCli('validators.tcpPort.invalid', { example: TCP_PORT_EXAMPLE });
119
+ }
120
+ return undefined;
121
+ }
122
+ async function canListenOnTcpPort(port) {
123
+ return await new Promise((resolve) => {
124
+ const server = net.createServer();
125
+ const resolveAndCleanup = (result) => {
126
+ server.removeAllListeners();
127
+ if (server.listening) {
128
+ server.close(() => resolve(result));
129
+ }
130
+ else {
131
+ resolve(result);
132
+ }
133
+ };
134
+ server.once('error', () => {
135
+ resolveAndCleanup(false);
136
+ });
137
+ server.once('listening', () => {
138
+ resolveAndCleanup(true);
139
+ });
140
+ server.listen(port, '127.0.0.1');
141
+ });
142
+ }
143
+ function parseDockerPublishedTcpPorts(output) {
144
+ const ports = new Set();
145
+ for (const line of output.split(/\r?\n/)) {
146
+ for (const segment of line.split(',')) {
147
+ const match = segment.trim().match(/^(?:.+:)?(\d+)->\d+(?:-\d+)?\/tcp$/i);
148
+ if (!match) {
149
+ continue;
150
+ }
151
+ const port = Number.parseInt(match[1], 10);
152
+ if (Number.isInteger(port) && port >= 1 && port <= 65535) {
153
+ ports.add(port);
154
+ }
155
+ }
156
+ }
157
+ return ports;
158
+ }
159
+ async function getDockerPublishedTcpPorts() {
160
+ return await new Promise((resolve) => {
161
+ let settled = false;
162
+ const stdoutChunks = [];
163
+ const finish = (ports) => {
164
+ if (settled) {
165
+ return;
166
+ }
167
+ settled = true;
168
+ resolve(ports);
169
+ };
170
+ const child = spawn('docker', ['ps', '--format', '{{.Ports}}']);
171
+ child.stdout?.on('data', (chunk) => {
172
+ stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
173
+ });
174
+ child.once('error', () => {
175
+ finish(new Set());
176
+ });
177
+ child.once('close', (code) => {
178
+ if (code !== 0) {
179
+ finish(new Set());
180
+ return;
181
+ }
182
+ finish(parseDockerPublishedTcpPorts(Buffer.concat(stdoutChunks).toString('utf8')));
183
+ });
184
+ });
185
+ }
186
+ async function allocateAvailableTcpPort() {
187
+ return await new Promise((resolve, reject) => {
188
+ const server = net.createServer();
189
+ server.once('error', reject);
190
+ server.listen(0, '127.0.0.1', () => {
191
+ const address = server.address();
192
+ const port = address && typeof address === 'object'
193
+ ? address.port
194
+ : undefined;
195
+ if (!port) {
196
+ server.close(() => {
197
+ reject(new Error(translateCli('validators.tcpPort.allocateFailed')));
198
+ });
199
+ return;
200
+ }
201
+ server.close((error) => {
202
+ if (error) {
203
+ reject(error);
204
+ return;
205
+ }
206
+ resolve(String(port));
207
+ });
208
+ });
209
+ });
210
+ }
211
+ export async function findAvailableTcpPort() {
212
+ const dockerPorts = await getDockerPublishedTcpPorts();
213
+ for (let attempt = 0; attempt < 10; attempt += 1) {
214
+ const candidate = await allocateAvailableTcpPort();
215
+ if (!dockerPorts.has(Number.parseInt(candidate, 10))) {
216
+ return candidate;
217
+ }
218
+ }
219
+ throw new Error(translateCli('validators.tcpPort.allocateNotDockerPublished'));
220
+ }
221
+ export async function validateAvailableTcpPort(value) {
222
+ const raw = String(value ?? '').trim();
223
+ if (raw === '') {
224
+ return undefined;
225
+ }
226
+ const formatError = validateTcpPort(raw);
227
+ if (formatError) {
228
+ return formatError;
229
+ }
230
+ const port = parseTcpPort(raw);
231
+ const dockerPorts = await getDockerPublishedTcpPorts();
232
+ if (dockerPorts.has(port)) {
233
+ return translateCli('validators.tcpPort.alreadyInUseByDocker', { port });
234
+ }
235
+ const available = await canListenOnTcpPort(port);
236
+ if (!available) {
237
+ return translateCli('validators.tcpPort.alreadyInUse', { port });
238
+ }
239
+ return undefined;
240
+ }