@nocobase/cli 2.1.0-beta.33 → 2.1.0-beta.35

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 (62) hide show
  1. package/bin/run.js +2 -1
  2. package/bin/session-env.js +12 -0
  3. package/dist/commands/app/down.js +10 -13
  4. package/dist/commands/app/logs.js +0 -1
  5. package/dist/commands/app/restart.js +63 -2
  6. package/dist/commands/app/start.js +53 -18
  7. package/dist/commands/app/stop.js +0 -1
  8. package/dist/commands/app/upgrade.js +16 -4
  9. package/dist/commands/env/add.js +3 -4
  10. package/dist/commands/env/auth.js +3 -2
  11. package/dist/commands/env/remove.js +38 -13
  12. package/dist/commands/env/update.js +9 -2
  13. package/dist/commands/examples/prompts-stages.js +4 -4
  14. package/dist/commands/examples/prompts-test.js +4 -4
  15. package/dist/commands/init.js +38 -31
  16. package/dist/commands/install.js +100 -63
  17. package/dist/commands/license/activate.js +66 -64
  18. package/dist/commands/license/id.js +0 -1
  19. package/dist/commands/license/plugins/clean.js +0 -1
  20. package/dist/commands/license/plugins/list.js +0 -1
  21. package/dist/commands/license/plugins/sync.js +0 -1
  22. package/dist/commands/license/shared.js +3 -3
  23. package/dist/commands/license/status.js +0 -1
  24. package/dist/commands/plugin/disable.js +0 -1
  25. package/dist/commands/plugin/enable.js +0 -1
  26. package/dist/commands/plugin/list.js +0 -1
  27. package/dist/commands/self/update.js +12 -3
  28. package/dist/commands/skills/install.js +12 -3
  29. package/dist/commands/skills/remove.js +12 -3
  30. package/dist/commands/skills/update.js +12 -3
  31. package/dist/commands/source/dev.js +8 -2
  32. package/dist/commands/source/download.js +29 -17
  33. package/dist/commands/source/publish.js +92 -0
  34. package/dist/commands/source/registry/logs.js +70 -0
  35. package/dist/commands/source/registry/start.js +57 -0
  36. package/dist/commands/source/registry/status.js +33 -0
  37. package/dist/commands/source/registry/stop.js +48 -0
  38. package/dist/lib/app-managed-resources.js +30 -3
  39. package/dist/lib/bootstrap.js +12 -3
  40. package/dist/lib/db-connection-check.js +3 -23
  41. package/dist/lib/docker-env-file.js +52 -0
  42. package/dist/lib/env-auth.js +4 -3
  43. package/dist/lib/env-config.js +1 -0
  44. package/dist/lib/env-guard.js +8 -7
  45. package/dist/lib/generated-command.js +0 -1
  46. package/dist/lib/inquirer-theme.js +17 -0
  47. package/dist/lib/inquirer.js +244 -0
  48. package/dist/lib/object-utils.js +76 -0
  49. package/dist/lib/prompt-catalog-core.js +185 -0
  50. package/dist/lib/prompt-catalog-terminal.js +375 -0
  51. package/dist/lib/prompt-catalog.js +2 -573
  52. package/dist/lib/prompt-validators.js +56 -1
  53. package/dist/lib/resource-command.js +0 -1
  54. package/dist/lib/run-npm.js +8 -9
  55. package/dist/lib/skills-manager.js +75 -11
  56. package/dist/lib/source-publish.js +287 -0
  57. package/dist/lib/source-registry.js +188 -0
  58. package/dist/lib/startup-update.js +12 -8
  59. package/dist/lib/ui.js +28 -51
  60. package/dist/locale/en-US.json +8 -3
  61. package/dist/locale/zh-CN.json +8 -3
  62. package/package.json +7 -5
@@ -0,0 +1,244 @@
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 { cursorTo } from '@inquirer/ansi';
10
+ import pc from 'picocolors';
11
+ import { createPrompt, isBackspaceKey, isEnterKey, makeTheme, useEffect, useKeypress, usePrefix, useState, } from '@inquirer/core';
12
+ let confirmModulePromise;
13
+ let selectModulePromise;
14
+ function loadConfirmModule() {
15
+ confirmModulePromise ??= import('@inquirer/confirm');
16
+ return confirmModulePromise;
17
+ }
18
+ function loadSelectModule() {
19
+ selectModulePromise ??= import('@inquirer/select');
20
+ return selectModulePromise;
21
+ }
22
+ export function buildPlainMessageTheme(extra) {
23
+ return {
24
+ ...extra,
25
+ style: {
26
+ ...extra?.style,
27
+ message: (text) => text,
28
+ },
29
+ };
30
+ }
31
+ function buildSelectAnswer(text) {
32
+ return `\n${pc.cyan('❯')} ${pc.cyan(text)}`;
33
+ }
34
+ function buildConfirmAnswer(text) {
35
+ return `\n${pc.cyan('❯')} ${pc.cyan(text)}`;
36
+ }
37
+ function isFullWidthCodePoint(codePoint) {
38
+ return (codePoint >= 0x1100 &&
39
+ (codePoint <= 0x115f ||
40
+ codePoint === 0x2329 ||
41
+ codePoint === 0x232a ||
42
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
43
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
44
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
45
+ (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
46
+ (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
47
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
48
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
49
+ (codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
50
+ (codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
51
+ (codePoint >= 0x20000 && codePoint <= 0x3fffd)));
52
+ }
53
+ function stringWidth(value) {
54
+ let width = 0;
55
+ for (const char of value) {
56
+ const codePoint = char.codePointAt(0);
57
+ if (codePoint == null)
58
+ continue;
59
+ if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f))
60
+ continue;
61
+ width += isFullWidthCodePoint(codePoint) ? 2 : 1;
62
+ }
63
+ return width;
64
+ }
65
+ function stripAnsi(value) {
66
+ return value.replace(new RegExp(String.raw `\\u001B\\[[0-9;]*m`, 'g'), '');
67
+ }
68
+ function lastLineWidth(value) {
69
+ const lines = stripAnsi(value).split('\n');
70
+ return stringWidth(lines[lines.length - 1] ?? '');
71
+ }
72
+ function resolveRequiredError(required) {
73
+ if (typeof required === 'string') {
74
+ return required;
75
+ }
76
+ return '此项为必填';
77
+ }
78
+ function hasUsableDefault(value) {
79
+ return value != null && value !== '';
80
+ }
81
+ function buildIdleHint(config) {
82
+ const placeholder = config.placeholder?.trim();
83
+ if (placeholder) {
84
+ return placeholder;
85
+ }
86
+ return '';
87
+ }
88
+ function buildInputLine(value) {
89
+ return `${pc.cyan('❯')} ${value}`;
90
+ }
91
+ function buildErrorLine(error) {
92
+ return pc.red(`✖ ${error}`);
93
+ }
94
+ export const input = createPrompt((config, done) => {
95
+ const theme = makeTheme(config.theme);
96
+ const [status, setStatus] = useState('idle');
97
+ const [defaultValue, setDefaultValue] = useState(config.default ?? '');
98
+ const [value, setValue] = useState('');
99
+ const [error, setError] = useState();
100
+ const prefix = usePrefix({ status, theme });
101
+ useEffect((rl) => {
102
+ if (config.default) {
103
+ rl.write(config.default);
104
+ setValue(config.default);
105
+ }
106
+ }, []);
107
+ useKeypress(async (key, rl) => {
108
+ if (status !== 'idle')
109
+ return;
110
+ if (isBackspaceKey(key) && !value) {
111
+ setDefaultValue('');
112
+ return;
113
+ }
114
+ if (isBackspaceKey(key)) {
115
+ setValue(rl.line.slice(0, -1));
116
+ setError(undefined);
117
+ return;
118
+ }
119
+ if (!isEnterKey(key)) {
120
+ setValue(rl.line);
121
+ setError(undefined);
122
+ return;
123
+ }
124
+ const answer = value || defaultValue;
125
+ setStatus('loading');
126
+ if (!answer && config.required) {
127
+ rl.write(value);
128
+ setError(resolveRequiredError(config.required));
129
+ setStatus('idle');
130
+ return;
131
+ }
132
+ if (config.validate) {
133
+ const result = await config.validate(answer);
134
+ if (result !== true) {
135
+ rl.write(value);
136
+ setError(typeof result === 'string' ? result : 'Invalid input');
137
+ setStatus('idle');
138
+ return;
139
+ }
140
+ }
141
+ setValue(answer);
142
+ setStatus('done');
143
+ done(answer);
144
+ });
145
+ const message = theme.style.message(config.message, status);
146
+ const isTyping = value.length > 0;
147
+ const hasDefault = hasUsableDefault(defaultValue);
148
+ const idleHint = buildIdleHint(config);
149
+ const showIdleHint = !isTyping && status === 'idle' && idleHint !== '';
150
+ const showDefaultValue = !isTyping && status === 'idle' && hasDefault;
151
+ const displayValue = status === 'done'
152
+ ? theme.style.answer(value)
153
+ : isTyping
154
+ ? value
155
+ : showDefaultValue
156
+ ? defaultValue
157
+ : showIdleHint
158
+ ? pc.dim(idleHint)
159
+ : '';
160
+ const headerLine = [prefix, message].filter(Boolean).join(' ');
161
+ const inputLine = buildInputLine(displayValue);
162
+ const prompt = headerLine.endsWith('\n') ? `${headerLine}${inputLine}` : `${headerLine}\n${inputLine}`;
163
+ const promptWithCursorFix = showIdleHint
164
+ ? prompt + cursorTo(lastLineWidth(buildInputLine('')))
165
+ : showDefaultValue
166
+ ? prompt + cursorTo(lastLineWidth(buildInputLine(defaultValue)))
167
+ : prompt;
168
+ return [promptWithCursorFix, error ? buildErrorLine(error) : ''];
169
+ });
170
+ export async function confirm(options) {
171
+ const module = await loadConfirmModule();
172
+ return module.default({
173
+ ...options,
174
+ theme: buildPlainMessageTheme({
175
+ style: {
176
+ answer: buildConfirmAnswer,
177
+ },
178
+ ...options.theme,
179
+ }),
180
+ transformer: options.transformer ?? ((value) => (value ? 'Yes' : 'No')),
181
+ });
182
+ }
183
+ export async function select(options) {
184
+ const module = await loadSelectModule();
185
+ return module.default({
186
+ ...options,
187
+ theme: buildPlainMessageTheme({
188
+ style: {
189
+ answer: buildSelectAnswer,
190
+ },
191
+ ...options.theme,
192
+ }),
193
+ });
194
+ }
195
+ function resolveMaskChar(mask) {
196
+ if (typeof mask === 'string') {
197
+ return mask;
198
+ }
199
+ if (mask === false) {
200
+ return '';
201
+ }
202
+ return '•';
203
+ }
204
+ export const password = createPrompt((config, done) => {
205
+ const theme = makeTheme(config.theme);
206
+ const [status, setStatus] = useState('idle');
207
+ const [value, setValue] = useState('');
208
+ const [error, setError] = useState();
209
+ const prefix = usePrefix({ status, theme });
210
+ useKeypress(async (key, rl) => {
211
+ if (status !== 'idle')
212
+ return;
213
+ if (!isEnterKey(key)) {
214
+ setValue(rl.line);
215
+ setError(undefined);
216
+ return;
217
+ }
218
+ const answer = value;
219
+ setStatus('loading');
220
+ if (config.validate) {
221
+ const result = await config.validate(answer);
222
+ if (result !== true) {
223
+ rl.write(value);
224
+ setError(typeof result === 'string' ? result : 'Invalid input');
225
+ setStatus('idle');
226
+ return;
227
+ }
228
+ }
229
+ setValue(answer);
230
+ setStatus('done');
231
+ done(answer);
232
+ });
233
+ const message = theme.style.message(config.message, status);
234
+ const maskChar = resolveMaskChar(config.mask);
235
+ const displayValue = status === 'done'
236
+ ? theme.style.answer(maskChar.repeat(value.length))
237
+ : maskChar
238
+ ? maskChar.repeat(value.length)
239
+ : '';
240
+ const headerLine = [prefix, message].filter(Boolean).join(' ');
241
+ const inputLine = buildInputLine(displayValue);
242
+ const prompt = headerLine.endsWith('\n') ? `${headerLine}${inputLine}` : `${headerLine}\n${inputLine}`;
243
+ return [prompt, error ? buildErrorLine(error) : ''];
244
+ });
@@ -0,0 +1,76 @@
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
+ export function pickKeys(object, keys) {
10
+ const picked = {};
11
+ for (const key of keys) {
12
+ if (Object.prototype.hasOwnProperty.call(object, key)) {
13
+ const typedKey = key;
14
+ picked[typedKey] = object[typedKey];
15
+ }
16
+ }
17
+ return picked;
18
+ }
19
+ export function omitKeys(object, keys) {
20
+ const omittedKeys = new Set(keys);
21
+ const result = {};
22
+ for (const key of Object.keys(object)) {
23
+ if (!omittedKeys.has(key)) {
24
+ const typedKey = key;
25
+ result[typedKey] = object[typedKey];
26
+ }
27
+ }
28
+ return result;
29
+ }
30
+ export function upperFirst(value) {
31
+ if (!value) {
32
+ return value;
33
+ }
34
+ return value[0].toUpperCase() + value.slice(1);
35
+ }
36
+ export function deepEqual(left, right) {
37
+ if (Object.is(left, right)) {
38
+ return true;
39
+ }
40
+ if (left == null || right == null) {
41
+ return false;
42
+ }
43
+ if (typeof left !== typeof right) {
44
+ return false;
45
+ }
46
+ if (typeof left !== 'object') {
47
+ return false;
48
+ }
49
+ if (Array.isArray(left) || Array.isArray(right)) {
50
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
51
+ return false;
52
+ }
53
+ for (let index = 0; index < left.length; index += 1) {
54
+ if (!deepEqual(left[index], right[index])) {
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
60
+ const leftObject = left;
61
+ const rightObject = right;
62
+ const leftKeys = Object.keys(leftObject);
63
+ const rightKeys = Object.keys(rightObject);
64
+ if (leftKeys.length !== rightKeys.length) {
65
+ return false;
66
+ }
67
+ for (const key of leftKeys) {
68
+ if (!Object.prototype.hasOwnProperty.call(rightObject, key)) {
69
+ return false;
70
+ }
71
+ if (!deepEqual(leftObject[key], rightObject[key])) {
72
+ return false;
73
+ }
74
+ }
75
+ return true;
76
+ }
@@ -0,0 +1,185 @@
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 { createCliTranslate, resolveCliLocale, resolveLocalizedText, } from "./cli-locale.js";
10
+ export function selectOptionValues(options) {
11
+ return options.map((o) => (typeof o === 'string' ? o : o.value));
12
+ }
13
+ export function resolvePromptText(text, locale, fallback = '') {
14
+ return resolveLocalizedText(text, { locale, fallback });
15
+ }
16
+ export function enabledSelectOptionValues(options) {
17
+ return options
18
+ .filter((o) => typeof o === 'string' || o.disabled !== true)
19
+ .map((o) => (typeof o === 'string' ? o : o.value));
20
+ }
21
+ export function createPromptCatalogHooks(locale, overrides, defaults) {
22
+ return {
23
+ onCancel: overrides?.onCancel
24
+ ?? defaults?.onCancel
25
+ ?? (() => {
26
+ throw new Error(createCliTranslate(locale)('promptCatalog.common.cancelled'));
27
+ }),
28
+ onMissingNonInteractive: overrides?.onMissingNonInteractive
29
+ ?? defaults?.onMissingNonInteractive
30
+ ?? ((message) => {
31
+ throw new Error(message);
32
+ }),
33
+ };
34
+ }
35
+ export function hasIvKey(iv, key) {
36
+ return (Object.prototype.hasOwnProperty.call(iv, key) && iv[key] !== undefined && iv[key] !== null);
37
+ }
38
+ export function resolveTextInitial(def, valuesSoFar) {
39
+ const iv = def.initialValue;
40
+ if (typeof iv === 'function') {
41
+ return iv(valuesSoFar);
42
+ }
43
+ return iv;
44
+ }
45
+ export function mergedText(key, def, iv, useYesInitial, valuesSoFar = {}) {
46
+ if (hasIvKey(iv, key)) {
47
+ return String(iv[key]);
48
+ }
49
+ if (useYesInitial && def.yesInitialValue !== undefined) {
50
+ return def.yesInitialValue;
51
+ }
52
+ return resolveTextInitial(def, valuesSoFar) ?? '';
53
+ }
54
+ export function mergedBoolean(key, def, iv, useYesInitial) {
55
+ if (hasIvKey(iv, key)) {
56
+ return Boolean(iv[key]);
57
+ }
58
+ if (useYesInitial && def.yesInitialValue !== undefined) {
59
+ return def.yesInitialValue;
60
+ }
61
+ return def.initialValue ?? true;
62
+ }
63
+ export function mergedSelect(key, def, iv, useYesInitial) {
64
+ const enabledValueList = enabledSelectOptionValues(def.options);
65
+ if (hasIvKey(iv, key)) {
66
+ const s = String(iv[key]);
67
+ if (enabledValueList.includes(s)) {
68
+ return s;
69
+ }
70
+ return undefined;
71
+ }
72
+ if (useYesInitial && def.yesInitialValue !== undefined && enabledValueList.includes(def.yesInitialValue)) {
73
+ return def.yesInitialValue;
74
+ }
75
+ const d = def.initialValue;
76
+ if (d !== undefined && enabledValueList.includes(d)) {
77
+ return d;
78
+ }
79
+ return enabledValueList[0];
80
+ }
81
+ export function mergedInteger(key, def, iv, useYesInitial) {
82
+ if (hasIvKey(iv, key)) {
83
+ const v = iv[key];
84
+ return typeof v === 'number' ? v : Number.parseInt(String(v), 10);
85
+ }
86
+ if (useYesInitial && def.yesInitialValue !== undefined) {
87
+ return def.yesInitialValue;
88
+ }
89
+ return def.initialValue;
90
+ }
91
+ export function mergedPassword(key, def, iv, useYesInitial) {
92
+ if (hasIvKey(iv, key)) {
93
+ return String(iv[key] ?? '');
94
+ }
95
+ if (useYesInitial && def.yesInitialValue !== undefined) {
96
+ return def.yesInitialValue;
97
+ }
98
+ return def.initialValue;
99
+ }
100
+ export function isBlankText(value) {
101
+ return value.trim() === '';
102
+ }
103
+ export async function runPromptFieldValidate(def, value, values) {
104
+ if (def.type === 'intro' || def.type === 'outro' || def.type === 'run') {
105
+ return undefined;
106
+ }
107
+ if (!('validate' in def) || def.validate === undefined) {
108
+ return undefined;
109
+ }
110
+ const r = await def.validate(value, values);
111
+ if (r == null || r === '') {
112
+ return undefined;
113
+ }
114
+ return String(r);
115
+ }
116
+ export function isPromptBlockSkipped(def, values) {
117
+ if (def.type === 'run') {
118
+ return def.when !== undefined && !def.when(values);
119
+ }
120
+ return def.hidden !== undefined && def.hidden(values);
121
+ }
122
+ export function tryApplyPreset(key, def, preset, out, hooks, locale) {
123
+ const t = createCliTranslate(locale);
124
+ if (!hasIvKey(preset, key)) {
125
+ return false;
126
+ }
127
+ const raw = preset[key];
128
+ switch (def.type) {
129
+ case 'intro':
130
+ case 'outro':
131
+ case 'run':
132
+ return false;
133
+ case 'text': {
134
+ const s = String(raw ?? '');
135
+ if (def.required && isBlankText(s)) {
136
+ hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
137
+ }
138
+ out[key] = s;
139
+ return true;
140
+ }
141
+ case 'boolean': {
142
+ out[key] = Boolean(raw);
143
+ return true;
144
+ }
145
+ case 'select': {
146
+ const valueList = selectOptionValues(def.options);
147
+ const s = String(raw ?? '');
148
+ if (!valueList.includes(s)) {
149
+ hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidSelect', { key, value: s, options: valueList.join(', ') }));
150
+ }
151
+ out[key] = s;
152
+ return true;
153
+ }
154
+ case 'password': {
155
+ const s = String(raw ?? '');
156
+ if (def.required && isBlankText(s)) {
157
+ hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
158
+ }
159
+ out[key] = s;
160
+ return true;
161
+ }
162
+ case 'integer': {
163
+ if (typeof raw === 'number' && Number.isFinite(raw)) {
164
+ out[key] = raw;
165
+ return true;
166
+ }
167
+ const s = String(raw ?? '').trim();
168
+ if (s === '') {
169
+ if (def.required) {
170
+ hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
171
+ }
172
+ out[key] = def.initialValue ?? 0;
173
+ return true;
174
+ }
175
+ if (!/^-?\\d+$/.test(s)) {
176
+ hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidInteger', { key }));
177
+ }
178
+ out[key] = Number.parseInt(s, 10);
179
+ return true;
180
+ }
181
+ }
182
+ }
183
+ export function resolvePromptCatalogLocale(locale) {
184
+ return resolveCliLocale(locale);
185
+ }