@nocobase/cli 2.1.0-beta.36 → 2.1.0-beta.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/run.js +3 -2
- package/dist/commands/config/delete.js +4 -0
- package/dist/commands/config/get.js +4 -0
- package/dist/commands/config/set.js +5 -1
- package/dist/commands/env/add.js +66 -6
- package/dist/commands/env/auth.js +86 -27
- package/dist/commands/env/info.js +52 -8
- package/dist/commands/env/list.js +2 -2
- package/dist/commands/env/shared.js +41 -3
- package/dist/commands/init.js +196 -136
- package/dist/commands/install.js +311 -265
- package/dist/lib/auth-store.js +47 -19
- package/dist/lib/cli-config.js +99 -4
- package/dist/lib/cli-locale.js +19 -7
- package/dist/lib/db-connection-check.js +61 -0
- package/dist/lib/env-auth.js +79 -0
- package/dist/lib/env-config.js +3 -2
- package/dist/lib/prompt-validators.js +23 -5
- package/dist/lib/prompt-web-ui.js +143 -19
- package/dist/lib/run-npm.js +133 -23
- package/dist/lib/skills-manager.js +74 -4
- package/dist/locale/en-US.json +36 -5
- package/dist/locale/zh-CN.json +36 -5
- package/package.json +2 -2
|
@@ -13,6 +13,7 @@ const API_BASE_URL_EXAMPLE = 'http://localhost:13000/api';
|
|
|
13
13
|
const ENV_KEY_PATTERN = /^[A-Za-z0-9]+$/;
|
|
14
14
|
const TCP_PORT_EXAMPLE = '13000';
|
|
15
15
|
const API_BASE_URL_REQUEST_TIMEOUT_MS = 5_000;
|
|
16
|
+
const TCP_PORT_PROBE_HOSTS = ['127.0.0.1', '0.0.0.0', '::1'];
|
|
16
17
|
function buildHealthCheckUrl(apiBaseUrl) {
|
|
17
18
|
return `${apiBaseUrl.replace(/\/+$/, '')}/__health_check`;
|
|
18
19
|
}
|
|
@@ -119,7 +120,7 @@ export function validateTcpPort(value) {
|
|
|
119
120
|
}
|
|
120
121
|
return undefined;
|
|
121
122
|
}
|
|
122
|
-
async function
|
|
123
|
+
async function canListenOnTcpPortHost(port, host) {
|
|
123
124
|
return await new Promise((resolve) => {
|
|
124
125
|
const server = net.createServer();
|
|
125
126
|
const resolveAndCleanup = (result) => {
|
|
@@ -131,15 +132,28 @@ async function canListenOnTcpPort(port) {
|
|
|
131
132
|
resolve(result);
|
|
132
133
|
}
|
|
133
134
|
};
|
|
134
|
-
server.once('error', () => {
|
|
135
|
+
server.once('error', (error) => {
|
|
136
|
+
if (error.code === 'EADDRNOTAVAIL' || error.code === 'EAFNOSUPPORT') {
|
|
137
|
+
resolveAndCleanup(undefined);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
135
140
|
resolveAndCleanup(false);
|
|
136
141
|
});
|
|
137
142
|
server.once('listening', () => {
|
|
138
143
|
resolveAndCleanup(true);
|
|
139
144
|
});
|
|
140
|
-
server.listen(port,
|
|
145
|
+
server.listen(port, host);
|
|
141
146
|
});
|
|
142
147
|
}
|
|
148
|
+
async function canListenOnTcpPort(port) {
|
|
149
|
+
for (const host of TCP_PORT_PROBE_HOSTS) {
|
|
150
|
+
const result = await canListenOnTcpPortHost(port, host);
|
|
151
|
+
if (result === false) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
143
157
|
function parseDockerPublishedTcpPorts(output) {
|
|
144
158
|
const ports = new Set();
|
|
145
159
|
for (const line of output.split(/\r?\n/)) {
|
|
@@ -210,9 +224,10 @@ async function allocateAvailableTcpPort() {
|
|
|
210
224
|
}
|
|
211
225
|
export async function findAvailableTcpPort() {
|
|
212
226
|
const dockerPorts = await getDockerPublishedTcpPorts();
|
|
213
|
-
for (let attempt = 0; attempt <
|
|
227
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
214
228
|
const candidate = await allocateAvailableTcpPort();
|
|
215
|
-
|
|
229
|
+
const port = Number.parseInt(candidate, 10);
|
|
230
|
+
if (!dockerPorts.has(port) && (await canListenOnTcpPort(port))) {
|
|
216
231
|
return candidate;
|
|
217
232
|
}
|
|
218
233
|
}
|
|
@@ -228,6 +243,9 @@ export async function validateAvailableTcpPort(value) {
|
|
|
228
243
|
return formatError;
|
|
229
244
|
}
|
|
230
245
|
const port = parseTcpPort(raw);
|
|
246
|
+
if (port === undefined) {
|
|
247
|
+
return translateCli('validators.tcpPort.invalid', { example: TCP_PORT_EXAMPLE });
|
|
248
|
+
}
|
|
231
249
|
const dockerPorts = await getDockerPublishedTcpPorts();
|
|
232
250
|
if (dockerPorts.has(port)) {
|
|
233
251
|
return translateCli('validators.tcpPort.alreadyInUseByDocker', { port });
|
|
@@ -295,6 +295,10 @@ function computePwcWizardSteps(options, merged, locale) {
|
|
|
295
295
|
const PWC_FORM_ITEM_EXPLAIN = '<div class="pwc-form-item-explain" data-pwc-explain="1" role="alert" hidden></div>';
|
|
296
296
|
/** Suffix slot for status icon (e.g. fail) — filled by client script. */
|
|
297
297
|
const PWC_FORM_ITEM_SUFFIX = '<span class="pwc-form-item-suffix" data-pwc-suffix="1" aria-hidden="true"></span>';
|
|
298
|
+
/** Password visibility toggle icon (`eye`). */
|
|
299
|
+
const PWC_FORM_EYE_ICON_SVG = `<svg class="pwc-form-toggle__svg" viewBox="0 0 14 14" width="14" height="14" focusable="false" aria-hidden="true"><path fill="currentColor" d="M7 3c-3.14 0-5.67 2.06-6.69 4c1.02 1.94 3.55 4 6.69 4s5.67-2.06 6.69-4C12.67 5.06 10.14 3 7 3zm0 6.8A2.8 2.8 0 1 1 7 4.2a2.8 2.8 0 0 1 0 5.6zm0-1.2A1.6 1.6 0 1 0 7 5.4a1.6 1.6 0 0 0 0 3.2z"/></svg>`;
|
|
300
|
+
/** Password visibility toggle icon (`eye-off`). */
|
|
301
|
+
const PWC_FORM_EYE_OFF_ICON_SVG = `<svg class="pwc-form-toggle__svg" viewBox="0 0 14 14" width="14" height="14" focusable="false" aria-hidden="true"><path fill="currentColor" d="M2.17 1.32l10.51 10.51-.85.85-2.04-2.04A7.37 7.37 0 0 1 7 11C3.86 11 1.33 8.94.31 7c.54-1.02 1.53-2.15 2.84-2.98L1.32 2.17l.85-.85zm1.88 3.58A6.26 6.26 0 0 0 1.69 7c1.02 1.63 3.12 2.8 5.31 2.8c.79 0 1.56-.15 2.25-.43l-1.1-1.1A2.8 2.8 0 0 1 4.88 5l-.83-.1zm5.87 2.48l-.9-.9a2 2 0 0 0-1.68-1.68l-.9-.9a2.8 2.8 0 0 1 3.48 3.48zM7 3c3.14 0 5.67 2.06 6.69 4a8.4 8.4 0 0 1-2.13 2.43l-.84-.84A6.98 6.98 0 0 0 12.31 7C11.29 5.37 9.19 4.2 7 4.2c-.39 0-.77.04-1.14.11l-.98-.98C5.56 3.12 6.27 3 7 3z"/></svg>`;
|
|
298
302
|
function renderPwcRadioOptions(key, def, defaults, hidden, locale) {
|
|
299
303
|
return def.options
|
|
300
304
|
.map((o, index) => {
|
|
@@ -322,7 +326,7 @@ function renderPwcRadioOptions(key, def, defaults, hidden, locale) {
|
|
|
322
326
|
})
|
|
323
327
|
.join('');
|
|
324
328
|
}
|
|
325
|
-
function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
329
|
+
function renderPwcFieldRow(key, def, defaults, show, locale, uiText) {
|
|
326
330
|
if (!isInputBlock(def)) {
|
|
327
331
|
return '';
|
|
328
332
|
}
|
|
@@ -330,13 +334,16 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
330
334
|
const hidden = show[key] === false;
|
|
331
335
|
const display = hidden ? 'none' : 'block';
|
|
332
336
|
const itemOpen = (extraClass) => `<div class="pwc-form-item${extraClass ? ` ${extraClass}` : ''}" data-pwc-wrap="${escapeHtml(key)}" data-pwc-field="${escapeHtml(key)}" style="display:${display}">`;
|
|
337
|
+
const label = `<span class="pwc-form-item-label-text">${'required' in def && def.required
|
|
338
|
+
? '<span class="pwc-form-item-required" aria-hidden="true">*</span>'
|
|
339
|
+
: ''}<span class="pwc-l">${escapeHtml(labelText)}</span></span>`;
|
|
333
340
|
if (def.type === 'text') {
|
|
334
341
|
const req = def.required ? ' required' : '';
|
|
335
342
|
const disabled = hidden ? ' disabled' : '';
|
|
336
343
|
const ph = def.placeholder ? ` placeholder="${escapeHtml(resolveUiText(def.placeholder, locale))}"` : '';
|
|
337
344
|
const v = escapeHtml(String(defaults[key] ?? ''));
|
|
338
345
|
return (itemOpen('') +
|
|
339
|
-
`<div class="pwc-form-item-label"
|
|
346
|
+
`<div class="pwc-form-item-label">${label}</div>` +
|
|
340
347
|
`<div class="pwc-form-item-control">` +
|
|
341
348
|
`<div class="pwc-form-item-control-input">` +
|
|
342
349
|
`<div class="pwc-input-affix-wrapper">` +
|
|
@@ -350,7 +357,7 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
350
357
|
const checked = defaults[key] === true ? ' checked' : '';
|
|
351
358
|
const disabled = hidden ? ' disabled' : '';
|
|
352
359
|
return (itemOpen('pwc-form-item--bool') +
|
|
353
|
-
`<div class="pwc-bool-wrap"><label><input class="pwc-bool-input" name="${escapeHtml(key)}" type="checkbox" value="1"${checked}${disabled} />
|
|
360
|
+
`<div class="pwc-bool-wrap"><label><input class="pwc-bool-input" name="${escapeHtml(key)}" type="checkbox" value="1"${checked}${disabled} /> ${label}</label></div>` +
|
|
354
361
|
PWC_FORM_ITEM_EXPLAIN +
|
|
355
362
|
`</div>`);
|
|
356
363
|
}
|
|
@@ -358,7 +365,7 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
358
365
|
if (def.variant === 'radio') {
|
|
359
366
|
const opts = renderPwcRadioOptions(key, def, defaults, hidden, locale);
|
|
360
367
|
return (itemOpen('') +
|
|
361
|
-
`<div class="pwc-form-item-label"
|
|
368
|
+
`<div class="pwc-form-item-label">${label}</div>` +
|
|
362
369
|
`<div class="pwc-form-item-control">` +
|
|
363
370
|
`<div class="pwc-form-item-control-input">` +
|
|
364
371
|
`<div class="pwc-radio-group" role="radiogroup" aria-label="${escapeHtml(labelText)}">` +
|
|
@@ -379,7 +386,7 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
379
386
|
const req = def.required ? ' required' : '';
|
|
380
387
|
const disabled = hidden ? ' disabled' : '';
|
|
381
388
|
return (itemOpen('') +
|
|
382
|
-
`<div class="pwc-form-item-label"
|
|
389
|
+
`<div class="pwc-form-item-label">${label}</div>` +
|
|
383
390
|
`<div class="pwc-form-item-control">` +
|
|
384
391
|
`<div class="pwc-form-item-control-input">` +
|
|
385
392
|
`<div class="pwc-input-affix-wrapper pwc-input-affix-wrapper--select">` +
|
|
@@ -393,13 +400,15 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
393
400
|
const req = def.required ? ' required' : '';
|
|
394
401
|
const disabled = hidden ? ' disabled' : '';
|
|
395
402
|
const v = escapeHtml(String(defaults[key] ?? ''));
|
|
403
|
+
const showPassword = escapeHtml(uiText?.showPassword ?? 'Show password');
|
|
396
404
|
return (itemOpen('') +
|
|
397
|
-
`<div class="pwc-form-item-label"
|
|
405
|
+
`<div class="pwc-form-item-label">${label}</div>` +
|
|
398
406
|
`<div class="pwc-form-item-control">` +
|
|
399
407
|
`<div class="pwc-form-item-control-input">` +
|
|
400
|
-
`<div class="pwc-input-affix-wrapper">` +
|
|
401
|
-
`<input class="pwc-form-input" name="${escapeHtml(key)}" type="password" value="${v}"${req}${disabled} autocomplete="new-password" />` +
|
|
408
|
+
`<div class="pwc-input-affix-wrapper pwc-input-affix-wrapper--password" data-pwc-password-wrap="1">` +
|
|
409
|
+
`<input class="pwc-form-input" data-pwc-password-input="1" name="${escapeHtml(key)}" type="password" value="${v}"${req}${disabled} autocomplete="new-password" />` +
|
|
402
410
|
PWC_FORM_ITEM_SUFFIX +
|
|
411
|
+
`<button class="pwc-password-toggle" type="button" data-pwc-password-toggle="1" aria-label="${showPassword}" title="${showPassword}" aria-pressed="false"${disabled}>${PWC_FORM_EYE_ICON_SVG}</button>` +
|
|
403
412
|
`</div></div>` +
|
|
404
413
|
PWC_FORM_ITEM_EXPLAIN +
|
|
405
414
|
`</div></div>`);
|
|
@@ -410,7 +419,7 @@ function renderPwcFieldRow(key, def, defaults, show, locale) {
|
|
|
410
419
|
const disabled = hidden ? ' disabled' : '';
|
|
411
420
|
const v = String(defaults[key] ?? 0);
|
|
412
421
|
return (itemOpen('') +
|
|
413
|
-
`<div class="pwc-form-item-label"
|
|
422
|
+
`<div class="pwc-form-item-label">${label}</div>` +
|
|
414
423
|
`<div class="pwc-form-item-control">` +
|
|
415
424
|
`<div class="pwc-form-item-control-input">` +
|
|
416
425
|
`<div class="pwc-input-affix-wrapper">` +
|
|
@@ -517,7 +526,7 @@ function buildPwcFormHtml(catalog, defaults, show, stepDefs, initialStepIndex, t
|
|
|
517
526
|
for (const key of sd.keys) {
|
|
518
527
|
const def = catalog[key];
|
|
519
528
|
if (def) {
|
|
520
|
-
out.push(renderPwcFieldRow(key, def, defaults, show, locale));
|
|
529
|
+
out.push(renderPwcFieldRow(key, def, defaults, show, locale, uiText));
|
|
521
530
|
}
|
|
522
531
|
}
|
|
523
532
|
out.push('</section>');
|
|
@@ -707,6 +716,8 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
707
716
|
back: t('promptCatalog.web.back'),
|
|
708
717
|
next: t('promptCatalog.web.next'),
|
|
709
718
|
submit: t('promptCatalog.web.submit'),
|
|
719
|
+
showPassword: t('promptCatalog.web.showPassword'),
|
|
720
|
+
hidePassword: t('promptCatalog.web.hidePassword'),
|
|
710
721
|
checking: t('promptCatalog.web.checking'),
|
|
711
722
|
sending: t('promptCatalog.web.sending'),
|
|
712
723
|
successTitle: t('promptCatalog.web.successTitle'),
|
|
@@ -1213,6 +1224,8 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1213
1224
|
/* antd Form.Item: label + control + explain — @see https://ant.design/components/form */
|
|
1214
1225
|
.pwc-form-item { margin: 0 0 24px 0; }
|
|
1215
1226
|
.pwc-form-item-label { margin: 0; padding: 0; }
|
|
1227
|
+
.pwc-form-item-label-text { display: inline-flex; align-items: flex-start; gap: 4px; }
|
|
1228
|
+
.pwc-form-item-required { color: var(--pwc-form-color-error); font-weight: 600; line-height: 1; margin-top: 2px; }
|
|
1216
1229
|
.pwc-form-item-control { width: 100%; }
|
|
1217
1230
|
.pwc-form-item--bool { margin: 0 0 24px 0; }
|
|
1218
1231
|
.pwc-form-item--bool .pwc-bool-wrap { margin: 0; }
|
|
@@ -1226,7 +1239,71 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1226
1239
|
pointer-events: none;
|
|
1227
1240
|
}
|
|
1228
1241
|
.pwc-input-affix-wrapper .pwc-form-input { padding-right: 32px; }
|
|
1229
|
-
.pwc-input-affix-wrapper--select
|
|
1242
|
+
.pwc-input-affix-wrapper--select::after {
|
|
1243
|
+
content: '';
|
|
1244
|
+
position: absolute;
|
|
1245
|
+
right: 14px;
|
|
1246
|
+
top: 50%;
|
|
1247
|
+
width: 8px;
|
|
1248
|
+
height: 8px;
|
|
1249
|
+
margin-top: -6px;
|
|
1250
|
+
border-right: 2px solid var(--pwc-ad-color-text-description);
|
|
1251
|
+
border-bottom: 2px solid var(--pwc-ad-color-text-description);
|
|
1252
|
+
transform: rotate(45deg);
|
|
1253
|
+
pointer-events: none;
|
|
1254
|
+
transition: border-color 0.2s;
|
|
1255
|
+
}
|
|
1256
|
+
.pwc-input-affix-wrapper--select:hover::after {
|
|
1257
|
+
border-color: var(--pwc-ad-color-text);
|
|
1258
|
+
}
|
|
1259
|
+
.pwc-input-affix-wrapper--select:focus-within::after {
|
|
1260
|
+
border-color: var(--pwc-ad-color-primary);
|
|
1261
|
+
}
|
|
1262
|
+
.pwc-input-affix-wrapper--select .pwc-form-input {
|
|
1263
|
+
padding-right: 56px;
|
|
1264
|
+
appearance: none;
|
|
1265
|
+
-webkit-appearance: none;
|
|
1266
|
+
-moz-appearance: none;
|
|
1267
|
+
background-image: none;
|
|
1268
|
+
}
|
|
1269
|
+
.pwc-input-affix-wrapper--select .pwc-form-item-suffix { right: 36px; }
|
|
1270
|
+
.pwc-input-affix-wrapper--password .pwc-form-input { padding-right: 68px; }
|
|
1271
|
+
.pwc-input-affix-wrapper--password .pwc-form-item-suffix { right: 39px; }
|
|
1272
|
+
.pwc-password-toggle {
|
|
1273
|
+
position: absolute;
|
|
1274
|
+
z-index: 1;
|
|
1275
|
+
right: 8px;
|
|
1276
|
+
top: 50%;
|
|
1277
|
+
width: 24px;
|
|
1278
|
+
height: 24px;
|
|
1279
|
+
margin: 0;
|
|
1280
|
+
padding: 0;
|
|
1281
|
+
display: inline-flex;
|
|
1282
|
+
align-items: center;
|
|
1283
|
+
justify-content: center;
|
|
1284
|
+
border: none;
|
|
1285
|
+
border-radius: 6px;
|
|
1286
|
+
background: transparent;
|
|
1287
|
+
color: var(--pwc-ad-color-text-description);
|
|
1288
|
+
cursor: pointer;
|
|
1289
|
+
transform: translateY(-50%);
|
|
1290
|
+
transition: color 0.2s, background 0.2s, box-shadow 0.2s;
|
|
1291
|
+
}
|
|
1292
|
+
.pwc-password-toggle:hover {
|
|
1293
|
+
color: var(--pwc-ad-color-text);
|
|
1294
|
+
background: color-mix(in srgb, var(--pwc-ad-color-text) 6%, transparent);
|
|
1295
|
+
}
|
|
1296
|
+
.pwc-password-toggle:focus-visible {
|
|
1297
|
+
outline: none;
|
|
1298
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--pwc-ad-color-primary) 16%, transparent);
|
|
1299
|
+
}
|
|
1300
|
+
.pwc-password-toggle:disabled {
|
|
1301
|
+
color: color-mix(in srgb, var(--pwc-ad-color-text-description) 60%, transparent);
|
|
1302
|
+
cursor: not-allowed;
|
|
1303
|
+
background: transparent;
|
|
1304
|
+
box-shadow: none;
|
|
1305
|
+
}
|
|
1306
|
+
.pwc-form-toggle__svg { display: block; }
|
|
1230
1307
|
.pwc-form-item-explain { font-size: 14px; line-height: 1.5715; margin-top: 4px; clear: both; }
|
|
1231
1308
|
.pwc-form-item-explain[hidden] { display: none !important; }
|
|
1232
1309
|
.pwc-form-item-explain:not([hidden]) { color: var(--pwc-form-color-error); }
|
|
@@ -1257,6 +1334,9 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1257
1334
|
border-radius: 6px;
|
|
1258
1335
|
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
|
|
1259
1336
|
}
|
|
1337
|
+
input.pwc-form-input, select.pwc-form-input {
|
|
1338
|
+
height: 40px;
|
|
1339
|
+
}
|
|
1260
1340
|
input.pwc-form-input:hover, select.pwc-form-input:hover, textarea:hover {
|
|
1261
1341
|
border-color: var(--pwc-ad-color-primary);
|
|
1262
1342
|
}
|
|
@@ -1411,6 +1491,8 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1411
1491
|
var pwcErrIcon = ${JSON.stringify(PWC_AD_ALERT_ERROR_ICON_SVG)};
|
|
1412
1492
|
var pwcOkIcon = ${JSON.stringify(PWC_AD_ALERT_SUCCESS_ICON_SVG)};
|
|
1413
1493
|
var pwcFieldFail = ${JSON.stringify(PWC_FORM_FAIL_ICON_SVG)};
|
|
1494
|
+
var pwcEyeIcon = ${JSON.stringify(PWC_FORM_EYE_ICON_SVG)};
|
|
1495
|
+
var pwcEyeOffIcon = ${JSON.stringify(PWC_FORM_EYE_OFF_ICON_SVG)};
|
|
1414
1496
|
var pwcCloseTimer = null;
|
|
1415
1497
|
var pwcCloseProbeTimer = null;
|
|
1416
1498
|
var pwcFieldReqSeq = {};
|
|
@@ -1468,7 +1550,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1468
1550
|
}
|
|
1469
1551
|
function pwcSetFieldError(key, message) {
|
|
1470
1552
|
if (!form || !key) { return; }
|
|
1471
|
-
var wrap = form.querySelector('[data-pwc-field
|
|
1553
|
+
var wrap = form.querySelector('[data-pwc-field="' + String(key) + '"]');
|
|
1472
1554
|
if (!wrap) { return; }
|
|
1473
1555
|
var ex = wrap.querySelector('[data-pwc-explain]');
|
|
1474
1556
|
var suf = wrap.querySelector('[data-pwc-suffix]');
|
|
@@ -1506,9 +1588,50 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1506
1588
|
} catch (e) {}
|
|
1507
1589
|
return { msg: msg, fk: fk };
|
|
1508
1590
|
}
|
|
1591
|
+
function pwcSyncPasswordToggle(toggle, input) {
|
|
1592
|
+
if (!toggle || !input) { return; }
|
|
1593
|
+
var visible = input.getAttribute('type') === 'text';
|
|
1594
|
+
var label = visible ? uiText.hidePassword : uiText.showPassword;
|
|
1595
|
+
toggle.setAttribute('aria-label', String(label));
|
|
1596
|
+
toggle.setAttribute('title', String(label));
|
|
1597
|
+
toggle.setAttribute('aria-pressed', visible ? 'true' : 'false');
|
|
1598
|
+
toggle.innerHTML = visible ? pwcEyeOffIcon : pwcEyeIcon;
|
|
1599
|
+
}
|
|
1600
|
+
function pwcInitPasswordToggles() {
|
|
1601
|
+
if (!form) { return; }
|
|
1602
|
+
var toggles = form.querySelectorAll('[data-pwc-password-toggle]');
|
|
1603
|
+
for (var i = 0; i < toggles.length; i++) {
|
|
1604
|
+
(function () {
|
|
1605
|
+
var toggle = toggles[i];
|
|
1606
|
+
var wrap = toggle.parentElement;
|
|
1607
|
+
var input = wrap ? wrap.querySelector('[data-pwc-password-input]') : null;
|
|
1608
|
+
pwcSyncPasswordToggle(toggle, input);
|
|
1609
|
+
toggle.addEventListener('click', function () {
|
|
1610
|
+
if (!input || input.disabled) { return; }
|
|
1611
|
+
var nextType = input.getAttribute('type') === 'text' ? 'password' : 'text';
|
|
1612
|
+
var start = typeof input.selectionStart === 'number' ? input.selectionStart : null;
|
|
1613
|
+
var end = typeof input.selectionEnd === 'number' ? input.selectionEnd : null;
|
|
1614
|
+
input.setAttribute('type', nextType);
|
|
1615
|
+
pwcSyncPasswordToggle(toggle, input);
|
|
1616
|
+
if (typeof input.focus === 'function') {
|
|
1617
|
+
input.focus({ preventScroll: true });
|
|
1618
|
+
}
|
|
1619
|
+
if (
|
|
1620
|
+
start !== null &&
|
|
1621
|
+
end !== null &&
|
|
1622
|
+
typeof input.setSelectionRange === 'function'
|
|
1623
|
+
) {
|
|
1624
|
+
try {
|
|
1625
|
+
input.setSelectionRange(start, end);
|
|
1626
|
+
} catch (e) {}
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
})();
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1509
1632
|
function pwcSetFieldDirty(key, dirty) {
|
|
1510
1633
|
if (!form || !key) { return; }
|
|
1511
|
-
var wrap = form.querySelector('[data-pwc-field
|
|
1634
|
+
var wrap = form.querySelector('[data-pwc-field="' + String(key) + '"]');
|
|
1512
1635
|
if (!wrap) { return; }
|
|
1513
1636
|
if (dirty) {
|
|
1514
1637
|
wrap.setAttribute('data-pwc-dirty', '1');
|
|
@@ -1518,7 +1641,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1518
1641
|
}
|
|
1519
1642
|
function pwcIsFieldDirty(key) {
|
|
1520
1643
|
if (!form || !key) { return false; }
|
|
1521
|
-
var wrap = form.querySelector('[data-pwc-field
|
|
1644
|
+
var wrap = form.querySelector('[data-pwc-field="' + String(key) + '"]');
|
|
1522
1645
|
return !!(wrap && wrap.getAttribute('data-pwc-dirty') === '1');
|
|
1523
1646
|
}
|
|
1524
1647
|
function getControl(formEl, k) {
|
|
@@ -1609,7 +1732,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1609
1732
|
var wasHidden = w.style.display === 'none';
|
|
1610
1733
|
var hidden = k != null && sh[k] === false;
|
|
1611
1734
|
w.style.display = hidden ? 'none' : 'block';
|
|
1612
|
-
var ctrls = w.querySelectorAll('input, select, textarea');
|
|
1735
|
+
var ctrls = w.querySelectorAll('input, select, textarea, button[data-pwc-password-toggle]');
|
|
1613
1736
|
for (var i = 0; i < ctrls.length; i++) {
|
|
1614
1737
|
ctrls[i].disabled = hidden || ctrls[i].getAttribute('data-pwc-static-disabled') === '1';
|
|
1615
1738
|
}
|
|
@@ -1633,7 +1756,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1633
1756
|
}
|
|
1634
1757
|
for (var i = 0; i < st.keys.length; i++) {
|
|
1635
1758
|
var key = st.keys[i];
|
|
1636
|
-
var wrap = form.querySelector('[data-pwc-wrap
|
|
1759
|
+
var wrap = form.querySelector('[data-pwc-wrap="' + String(key) + '"]');
|
|
1637
1760
|
if (wrap && wrap.style.display !== 'none') {
|
|
1638
1761
|
return true;
|
|
1639
1762
|
}
|
|
@@ -1771,7 +1894,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1771
1894
|
}
|
|
1772
1895
|
for (var j = 0; j < st.keys.length; j++) {
|
|
1773
1896
|
var k = st.keys[j];
|
|
1774
|
-
var w = form.querySelector('[data-pwc-wrap
|
|
1897
|
+
var w = form.querySelector('[data-pwc-wrap="' + k + '"]');
|
|
1775
1898
|
if (!w || w.style.display === 'none') { continue; }
|
|
1776
1899
|
var inp = getControl(form, k);
|
|
1777
1900
|
if (inp == null) { continue; }
|
|
@@ -1796,7 +1919,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1796
1919
|
}
|
|
1797
1920
|
function pwcValidateField(key) {
|
|
1798
1921
|
if (!form || !key || pwcValField == null) { return Promise.resolve(); }
|
|
1799
|
-
var wrap = form.querySelector('[data-pwc-wrap
|
|
1922
|
+
var wrap = form.querySelector('[data-pwc-wrap="' + String(key) + '"]');
|
|
1800
1923
|
if (!wrap || wrap.style.display === 'none') { return Promise.resolve(); }
|
|
1801
1924
|
var reqSeq = (pwcFieldReqSeq[key] || 0) + 1;
|
|
1802
1925
|
pwcFieldReqSeq[key] = reqSeq;
|
|
@@ -1898,6 +2021,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1898
2021
|
var pwcNext = document.getElementById('pwcNext');
|
|
1899
2022
|
if (pwcNext) { pwcNext.addEventListener('click', function () { void pwcTryAdvanceNext(); }); }
|
|
1900
2023
|
pwcRefreshVisibleSteps();
|
|
2024
|
+
pwcInitPasswordToggles();
|
|
1901
2025
|
if (form) { setTimeout(function () { reflow(); }, 0); }
|
|
1902
2026
|
if (form) {
|
|
1903
2027
|
form.addEventListener('submit', function (e) {
|
|
@@ -1934,7 +2058,7 @@ function runPromptCatalogWebUIImpl(options) {
|
|
|
1934
2058
|
});
|
|
1935
2059
|
}
|
|
1936
2060
|
})();
|
|
1937
|
-
|
|
2061
|
+
</script>
|
|
1938
2062
|
</body>
|
|
1939
2063
|
</html>`;
|
|
1940
2064
|
return page;
|
package/dist/lib/run-npm.js
CHANGED
|
@@ -19,9 +19,39 @@ import fsp from 'node:fs/promises';
|
|
|
19
19
|
import os from 'node:os';
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import spawn from 'cross-spawn';
|
|
22
|
+
import { translateCli } from './cli-locale.js';
|
|
23
|
+
import { resolveConfiguredCommandName } from './cli-config.js';
|
|
22
24
|
const FORWARDED_SIGNALS = ['SIGINT', 'SIGTERM'];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
const PROCESS_TIMEOUT_FORCE_KILL_DELAY_MS = 1000;
|
|
26
|
+
const MISSING_COMMAND_SPECS = {
|
|
27
|
+
docker: {
|
|
28
|
+
displayName: 'Docker',
|
|
29
|
+
configKey: 'bin.docker',
|
|
30
|
+
},
|
|
31
|
+
git: {
|
|
32
|
+
displayName: 'Git',
|
|
33
|
+
configKey: 'bin.git',
|
|
34
|
+
},
|
|
35
|
+
yarn: {
|
|
36
|
+
displayName: 'Yarn',
|
|
37
|
+
configKey: 'bin.yarn',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
async function resolveCommandName(name) {
|
|
41
|
+
return await resolveConfiguredCommandName(name);
|
|
42
|
+
}
|
|
43
|
+
function createMissingCommandError(name, label, error) {
|
|
44
|
+
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined;
|
|
45
|
+
if (code !== 'ENOENT') {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(MISSING_COMMAND_SPECS, name)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const spec = MISSING_COMMAND_SPECS[name];
|
|
52
|
+
return new Error(translateCli('commands.shared.missingCommand', { action: label, displayName: spec.displayName, configKey: spec.configKey }, {
|
|
53
|
+
fallback: `Couldn't run \`${label}\` because the ${spec.displayName} executable could not be found. Install ${spec.displayName} or update \`nb config set ${spec.configKey} <path>\` and try again.`,
|
|
54
|
+
}));
|
|
25
55
|
}
|
|
26
56
|
function pathExists(candidate) {
|
|
27
57
|
try {
|
|
@@ -40,8 +70,8 @@ function isDirectory(candidate) {
|
|
|
40
70
|
}
|
|
41
71
|
}
|
|
42
72
|
function hasLocalNocoBaseBinary(candidate) {
|
|
43
|
-
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1'))
|
|
44
|
-
|
|
73
|
+
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1')) ||
|
|
74
|
+
pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1.cmd')));
|
|
45
75
|
}
|
|
46
76
|
export function resolveCwd(cwd) {
|
|
47
77
|
const next = cwd ?? process.cwd();
|
|
@@ -61,10 +91,7 @@ export function resolveProjectCwd(cwd) {
|
|
|
61
91
|
throw new Error(`The specified --cwd is not a directory: ${fallback}`);
|
|
62
92
|
}
|
|
63
93
|
let current = hasExplicitInput ? fallback : process.cwd();
|
|
64
|
-
while (
|
|
65
|
-
if (hasLocalNocoBaseBinary(current)) {
|
|
66
|
-
return current;
|
|
67
|
-
}
|
|
94
|
+
while (!hasLocalNocoBaseBinary(current)) {
|
|
68
95
|
const parent = path.dirname(current);
|
|
69
96
|
if (parent === current) {
|
|
70
97
|
if (hasExplicitInput) {
|
|
@@ -74,12 +101,13 @@ export function resolveProjectCwd(cwd) {
|
|
|
74
101
|
}
|
|
75
102
|
current = parent;
|
|
76
103
|
}
|
|
104
|
+
return current;
|
|
77
105
|
}
|
|
78
|
-
export function run(name, args, options) {
|
|
106
|
+
export async function run(name, args, options) {
|
|
79
107
|
const cwd = resolveCwd(options?.cwd);
|
|
80
108
|
const label = options?.errorName ?? name;
|
|
81
|
-
const command = resolveCommandName(name);
|
|
82
|
-
return new Promise((resolve, reject) => {
|
|
109
|
+
const command = await resolveCommandName(name);
|
|
110
|
+
return await new Promise((resolve, reject) => {
|
|
83
111
|
const child = spawn(command, [...args], {
|
|
84
112
|
stdio: options?.stdio ?? 'inherit',
|
|
85
113
|
cwd,
|
|
@@ -104,12 +132,19 @@ export function run(name, args, options) {
|
|
|
104
132
|
}
|
|
105
133
|
}
|
|
106
134
|
const cleanupSignalForwarding = forwardSignalsToChild(child);
|
|
135
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
107
136
|
child.once('error', (error) => {
|
|
137
|
+
timeoutController.cleanup();
|
|
108
138
|
cleanupSignalForwarding();
|
|
109
|
-
reject(error);
|
|
139
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
110
140
|
});
|
|
111
141
|
child.once('close', (code, signal) => {
|
|
142
|
+
timeoutController.cleanup();
|
|
112
143
|
cleanupSignalForwarding();
|
|
144
|
+
if (timeoutController.didTimeout()) {
|
|
145
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
113
148
|
if (code === 0) {
|
|
114
149
|
resolve();
|
|
115
150
|
return;
|
|
@@ -122,6 +157,46 @@ export function run(name, args, options) {
|
|
|
122
157
|
});
|
|
123
158
|
});
|
|
124
159
|
}
|
|
160
|
+
function attachProcessTimeout(child, timeoutMs) {
|
|
161
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
162
|
+
return {
|
|
163
|
+
cleanup: () => undefined,
|
|
164
|
+
didTimeout: () => false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
let didTimeout = false;
|
|
168
|
+
let forceKillTimer;
|
|
169
|
+
const isChildRunning = () => child.exitCode === null && child.signalCode === null;
|
|
170
|
+
const terminateChild = (signal) => {
|
|
171
|
+
if (!isChildRunning()) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
child.kill(signal);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Ignore kill errors here and let the child close/error handlers surface the failure.
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const timeoutTimer = setTimeout(() => {
|
|
182
|
+
didTimeout = true;
|
|
183
|
+
terminateChild('SIGTERM');
|
|
184
|
+
forceKillTimer = setTimeout(() => {
|
|
185
|
+
terminateChild('SIGKILL');
|
|
186
|
+
}, PROCESS_TIMEOUT_FORCE_KILL_DELAY_MS);
|
|
187
|
+
forceKillTimer.unref?.();
|
|
188
|
+
}, timeoutMs);
|
|
189
|
+
timeoutTimer.unref?.();
|
|
190
|
+
return {
|
|
191
|
+
cleanup: () => {
|
|
192
|
+
clearTimeout(timeoutTimer);
|
|
193
|
+
if (forceKillTimer) {
|
|
194
|
+
clearTimeout(forceKillTimer);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
didTimeout: () => didTimeout,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
125
200
|
function forwardSignalsToChild(child) {
|
|
126
201
|
let forwardedSignalCount = 0;
|
|
127
202
|
const listeners = new Map();
|
|
@@ -149,10 +224,11 @@ function forwardSignalsToChild(child) {
|
|
|
149
224
|
}
|
|
150
225
|
};
|
|
151
226
|
}
|
|
152
|
-
export function commandSucceeds(name, args, options) {
|
|
227
|
+
export async function commandSucceeds(name, args, options) {
|
|
153
228
|
const cwd = resolveCwd(options?.cwd);
|
|
154
|
-
const
|
|
155
|
-
|
|
229
|
+
const label = options?.errorName ?? name;
|
|
230
|
+
const command = await resolveCommandName(name);
|
|
231
|
+
return await new Promise((resolve, reject) => {
|
|
156
232
|
const child = spawn(command, [...args], {
|
|
157
233
|
cwd,
|
|
158
234
|
env: {
|
|
@@ -162,15 +238,31 @@ export function commandSucceeds(name, args, options) {
|
|
|
162
238
|
stdio: 'ignore',
|
|
163
239
|
windowsHide: process.platform === 'win32',
|
|
164
240
|
});
|
|
165
|
-
|
|
166
|
-
child.once('
|
|
241
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
242
|
+
child.once('error', (error) => {
|
|
243
|
+
timeoutController.cleanup();
|
|
244
|
+
const missingCommandError = createMissingCommandError(name, label, error);
|
|
245
|
+
if (missingCommandError) {
|
|
246
|
+
reject(missingCommandError);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
resolve(false);
|
|
250
|
+
});
|
|
251
|
+
child.once('close', (code) => {
|
|
252
|
+
timeoutController.cleanup();
|
|
253
|
+
if (timeoutController.didTimeout()) {
|
|
254
|
+
resolve(false);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
resolve(code === 0);
|
|
258
|
+
});
|
|
167
259
|
});
|
|
168
260
|
}
|
|
169
|
-
export function commandOutput(name, args, options) {
|
|
261
|
+
export async function commandOutput(name, args, options) {
|
|
170
262
|
const cwd = resolveCwd(options?.cwd);
|
|
171
263
|
const label = options?.errorName ?? name;
|
|
172
|
-
const command = resolveCommandName(name);
|
|
173
|
-
return new Promise((resolve, reject) => {
|
|
264
|
+
const command = await resolveCommandName(name);
|
|
265
|
+
return await new Promise((resolve, reject) => {
|
|
174
266
|
const child = spawn(command, [...args], {
|
|
175
267
|
cwd,
|
|
176
268
|
env: {
|
|
@@ -190,8 +282,17 @@ export function commandOutput(name, args, options) {
|
|
|
190
282
|
child.stderr.on('data', (chunk) => {
|
|
191
283
|
stderr += chunk;
|
|
192
284
|
});
|
|
193
|
-
child
|
|
285
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
286
|
+
child.once('error', (error) => {
|
|
287
|
+
timeoutController.cleanup();
|
|
288
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
289
|
+
});
|
|
194
290
|
child.once('close', (code, signal) => {
|
|
291
|
+
timeoutController.cleanup();
|
|
292
|
+
if (timeoutController.didTimeout()) {
|
|
293
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
195
296
|
if (code === 0) {
|
|
196
297
|
resolve(stdout.trim());
|
|
197
298
|
return;
|
|
@@ -216,7 +317,7 @@ async function readCommandOutputFile(filePath) {
|
|
|
216
317
|
export async function commandOutputViaFile(name, args, options) {
|
|
217
318
|
const cwd = resolveCwd(options?.cwd);
|
|
218
319
|
const label = options?.errorName ?? name;
|
|
219
|
-
const command = resolveCommandName(name);
|
|
320
|
+
const command = await resolveCommandName(name);
|
|
220
321
|
const captureDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'nocobase-cli-output-'));
|
|
221
322
|
const stdoutPath = path.join(captureDir, 'stdout.log');
|
|
222
323
|
const stderrPath = path.join(captureDir, 'stderr.log');
|
|
@@ -233,8 +334,17 @@ export async function commandOutputViaFile(name, args, options) {
|
|
|
233
334
|
stdio: ['ignore', stdoutHandle.fd, stderrHandle.fd],
|
|
234
335
|
windowsHide: process.platform === 'win32',
|
|
235
336
|
});
|
|
236
|
-
child
|
|
337
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
338
|
+
child.once('error', (error) => {
|
|
339
|
+
timeoutController.cleanup();
|
|
340
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
341
|
+
});
|
|
237
342
|
child.once('close', (code, signal) => {
|
|
343
|
+
timeoutController.cleanup();
|
|
344
|
+
if (timeoutController.didTimeout()) {
|
|
345
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
238
348
|
resolve({ code, signal });
|
|
239
349
|
});
|
|
240
350
|
});
|