toolcraft 0.0.232 → 0.0.233
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/composition.json +3 -3
- package/dist/applied-default.d.ts +3 -0
- package/dist/applied-default.js +16 -0
- package/dist/cli.js +508 -204
- package/dist/composition.json +3 -3
- package/dist/discriminator.d.ts +9 -0
- package/dist/discriminator.js +32 -0
- package/dist/error-report.d.ts +2 -18
- package/dist/error-report.js +12 -14
- package/dist/http.js +8 -1
- package/dist/human-in-loop/gate.js +6 -0
- package/dist/index.compile-check.js +22 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +50 -0
- package/dist/json-schema-converter.d.ts +14 -6
- package/dist/json-schema-converter.js +150 -20
- package/dist/mcp-proxy-metadata.d.ts +2 -0
- package/dist/mcp-proxy-metadata.js +13 -0
- package/dist/mcp-proxy.d.ts +1 -0
- package/dist/mcp-proxy.js +65 -47
- package/dist/mcp-result.d.ts +3 -0
- package/dist/mcp-result.js +10 -0
- package/dist/mcp.js +211 -153
- package/dist/redaction.js +26 -17
- package/dist/renderer.js +40 -29
- package/dist/schema-member-names.d.ts +2 -0
- package/dist/schema-member-names.js +39 -0
- package/dist/schema-scope.d.ts +1 -0
- package/dist/schema-scope.js +29 -16
- package/dist/schema.d.ts +1 -1
- package/dist/sdk.d.ts +2 -1
- package/dist/sdk.js +164 -50
- package/dist/stream.js +24 -23
- package/dist/testing/diagnostic-value.d.ts +1 -0
- package/dist/testing/diagnostic-value.js +57 -0
- package/dist/testing/fakes.js +2 -1
- package/dist/testing/harness.js +1 -0
- package/dist/testing/memory-fs.js +42 -1
- package/dist/testing/parity.js +50 -6
- package/dist/union-validation.d.ts +5 -0
- package/dist/union-validation.js +36 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +2 -0
- package/node_modules/mcp-oauth-server/dist/index.js +47 -6
- package/node_modules/tiny-http-mcp-server/dist/cli.js +14 -2
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/dist/http-server.d.ts +7 -5
- package/node_modules/tiny-http-mcp-server/dist/http-server.js +3 -11
- package/node_modules/tiny-http-mcp-server/dist/http-transport.d.ts +6 -1
- package/node_modules/tiny-http-mcp-server/dist/http-transport.js +196 -15
- package/node_modules/tiny-http-mcp-server/dist/index.d.ts +2 -2
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.d.ts +3 -0
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.js +35 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.js +9 -5
- package/node_modules/tiny-http-mcp-server/dist/test-support.js +17 -0
- package/node_modules/tiny-http-mcp-server/dist/testing.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/dist/testing.js +3 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +21 -4
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +14 -6
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.js +8 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.js +50 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +21 -3
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +1 -0
- package/node_modules/tiny-mcp-client/dist/index.d.ts +178 -101
- package/node_modules/tiny-mcp-client/dist/index.js +4696 -291
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/base64.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/base64.js +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/audio.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.d.ts +5 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.js +41 -6
- package/node_modules/tiny-stdio-mcp-server/dist/content/file.d.ts +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/image.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.d.ts +10 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.js +180 -0
- package/node_modules/tiny-stdio-mcp-server/dist/index.d.ts +5 -5
- package/node_modules/tiny-stdio-mcp-server/dist/index.js +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.d.ts +220 -0
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.js +1995 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.js +38 -25
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.d.ts +2 -0
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.js +37 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.d.ts +4 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.js +53 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.d.ts +17 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.js +228 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.js +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.d.ts +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.js +34 -0
- package/node_modules/tiny-stdio-mcp-server/dist/schema.d.ts +4 -1
- package/node_modules/tiny-stdio-mcp-server/dist/server.d.ts +9 -4
- package/node_modules/tiny-stdio-mcp-server/dist/server.js +334 -135
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.js +50 -28
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.d.ts +11 -0
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.js +128 -0
- package/node_modules/tiny-stdio-mcp-server/dist/types.d.ts +58 -18
- package/node_modules/tiny-stdio-mcp-server/dist/types.js +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/uri.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/uri.js +41 -0
- package/node_modules/tiny-stdio-mcp-server/package.json +8 -0
- package/node_modules/toolcraft-design/dist/command-registry.d.ts +11 -0
- package/node_modules/toolcraft-design/dist/command-registry.js +24 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.js +81 -27
- package/node_modules/toolcraft-design/dist/dashboard/buffer.js +39 -25
- package/node_modules/toolcraft-design/dist/dashboard/components/border.js +6 -5
- package/node_modules/toolcraft-design/dist/dashboard/components/footer.js +26 -21
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.js +12 -6
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.d.ts +2 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.js +51 -10
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.d.ts +4 -0
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.js +77 -4
- package/node_modules/toolcraft-design/dist/dashboard/index.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/index.js +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/keymap.js +26 -6
- package/node_modules/toolcraft-design/dist/dashboard/layout.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/layout.js +19 -3
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.js +63 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.js +70 -0
- package/node_modules/toolcraft-design/dist/dashboard/snapshot.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/store.js +9 -3
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.js +108 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/terminal.js +11 -1
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.js +126 -0
- package/node_modules/toolcraft-design/dist/dashboard/types.d.ts +3 -1
- package/node_modules/toolcraft-design/dist/event-groups.d.ts +19 -0
- package/node_modules/toolcraft-design/dist/event-groups.js +49 -0
- package/node_modules/toolcraft-design/dist/index.d.ts +6 -1
- package/node_modules/toolcraft-design/dist/index.js +5 -1
- package/node_modules/toolcraft-design/dist/inline-notice.d.ts +14 -0
- package/node_modules/toolcraft-design/dist/inline-notice.js +28 -0
- package/node_modules/toolcraft-design/dist/metric.d.ts +8 -0
- package/node_modules/toolcraft-design/dist/metric.js +21 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.js +10 -0
- package/node_modules/toolcraft-design/dist/primitives.d.ts +12 -0
- package/node_modules/toolcraft-design/dist/primitives.js +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.d.ts +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.js +10 -0
- package/node_modules/toolcraft-design/dist/render-performance.d.ts +33 -0
- package/node_modules/toolcraft-design/dist/render-performance.js +71 -0
- package/node_modules/toolcraft-design/dist/task-tree.d.ts +22 -0
- package/node_modules/toolcraft-design/dist/task-tree.js +79 -0
- package/node_modules/toolcraft-design/dist/viewport.d.ts +18 -0
- package/node_modules/toolcraft-design/dist/viewport.js +56 -0
- package/node_modules/toolcraft-schema/dist/clone-default.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/clone-default.js +26 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.js +1 -0
- package/node_modules/toolcraft-schema/dist/index.d.ts +18 -9
- package/node_modules/toolcraft-schema/dist/index.js +46 -8
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +6 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +6 -3
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.js +69 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json.d.ts +4 -1
- package/node_modules/toolcraft-schema/dist/json.js +57 -1
- package/node_modules/toolcraft-schema/dist/native-json-schema.d.ts +10 -0
- package/node_modules/toolcraft-schema/dist/native-json-schema.js +11 -0
- package/node_modules/toolcraft-schema/dist/validate.d.ts +5 -1
- package/node_modules/toolcraft-schema/dist/validate.js +61 -47
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +6 -3
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@ import "./node-require-shim.js";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Command as CommanderCommand, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
5
|
-
import { validate as validateSchema } from "toolcraft-schema";
|
|
6
|
-
import {
|
|
5
|
+
import { cloneDefaultValue, nativeJsonSchema, unicodeLength, validate as validateSchema } from "toolcraft-schema";
|
|
6
|
+
import { configureTheme, confirm, createLogger, formatCommandList, formatOptionList, getTheme, helpFormatterPlain, isCancel, note, promptText, renderHelpTokens, renderTable, select, text, withOutputFormat } from "toolcraft-design";
|
|
7
7
|
import { ApprovalDeclinedError, ToolcraftBugError, UserError, assertCommandRequirements, getCommandSourcePath, hasMcpProxyConfig, isUserError, resolveCommandSecrets } from "./index.js";
|
|
8
8
|
import { hasOwnErrorCode } from "./error-codes.js";
|
|
9
9
|
import { writeErrorReport } from "./error-report.js";
|
|
@@ -203,10 +203,20 @@ function collectFields(schema, casing, globalLongOptionFlags, path = [], inherit
|
|
|
203
203
|
optional: runtimeOptional,
|
|
204
204
|
hasDefault: false,
|
|
205
205
|
defaultValue: undefined,
|
|
206
|
-
requiredWhenActive
|
|
206
|
+
requiredWhenActive,
|
|
207
|
+
variantId: variantContext?.id,
|
|
208
|
+
variantBranchId: variantContext?.branchId
|
|
207
209
|
};
|
|
208
210
|
collected.fields.push(controlField);
|
|
209
211
|
const branches = [];
|
|
212
|
+
collected.variants.push({
|
|
213
|
+
id: variantId,
|
|
214
|
+
controlDisplayPath: controlField.displayPath,
|
|
215
|
+
controlFieldId: controlField.id,
|
|
216
|
+
optional: variantContext === undefined ? runtimeOptional : !requiredWhenActive,
|
|
217
|
+
parent: variantContext,
|
|
218
|
+
branches
|
|
219
|
+
});
|
|
210
220
|
for (const [branchId, branchSchema] of Object.entries(childSchema.branches)) {
|
|
211
221
|
const branch = collectFields(branchSchema, casing, globalLongOptionFlags, nextPath, true, {
|
|
212
222
|
id: variantId,
|
|
@@ -227,20 +237,21 @@ function collectFields(schema, casing, globalLongOptionFlags, path = [], inherit
|
|
|
227
237
|
.map((field) => field.id)
|
|
228
238
|
});
|
|
229
239
|
}
|
|
230
|
-
collected.variants.push({
|
|
231
|
-
id: variantId,
|
|
232
|
-
controlDisplayPath: controlField.displayPath,
|
|
233
|
-
controlFieldId: controlField.id,
|
|
234
|
-
optional: runtimeOptional,
|
|
235
|
-
branches
|
|
236
|
-
});
|
|
237
240
|
continue;
|
|
238
241
|
}
|
|
239
242
|
if (childSchema.kind === "union") {
|
|
240
243
|
const variantId = `${toDisplayPath(nextPath)}:union`;
|
|
241
244
|
const controlPath = toUnionKindControlPath(nextPath);
|
|
242
245
|
const controlDisplayPath = toUnionKindDisplayPath(nextPath);
|
|
243
|
-
const
|
|
246
|
+
const seenFingerprints = new Set();
|
|
247
|
+
const branchIds = childSchema.branches.map((branch, index) => {
|
|
248
|
+
const fingerprint = getRequiredBranchFingerprint(branch, casing);
|
|
249
|
+
const branchId = seenFingerprints.has(fingerprint)
|
|
250
|
+
? `${fingerprint} (branch ${index + 1})`
|
|
251
|
+
: fingerprint;
|
|
252
|
+
seenFingerprints.add(fingerprint);
|
|
253
|
+
return branchId;
|
|
254
|
+
});
|
|
244
255
|
const controlField = {
|
|
245
256
|
id: controlDisplayPath,
|
|
246
257
|
path: controlPath,
|
|
@@ -256,10 +267,20 @@ function collectFields(schema, casing, globalLongOptionFlags, path = [], inherit
|
|
|
256
267
|
hasDefault: false,
|
|
257
268
|
defaultValue: undefined,
|
|
258
269
|
requiredWhenActive,
|
|
259
|
-
synthetic: true
|
|
270
|
+
synthetic: true,
|
|
271
|
+
variantId: variantContext?.id,
|
|
272
|
+
variantBranchId: variantContext?.branchId
|
|
260
273
|
};
|
|
261
274
|
collected.fields.push(controlField);
|
|
262
275
|
const branches = [];
|
|
276
|
+
collected.variants.push({
|
|
277
|
+
id: variantId,
|
|
278
|
+
controlDisplayPath,
|
|
279
|
+
controlFieldId: controlField.id,
|
|
280
|
+
optional: variantContext === undefined ? runtimeOptional : !requiredWhenActive,
|
|
281
|
+
parent: variantContext,
|
|
282
|
+
branches
|
|
283
|
+
});
|
|
263
284
|
childSchema.branches.forEach((branchSchema, index) => {
|
|
264
285
|
const branchId = branchIds[index] ?? "";
|
|
265
286
|
const branch = collectFields(branchSchema, casing, globalLongOptionFlags, nextPath, true, {
|
|
@@ -281,13 +302,6 @@ function collectFields(schema, casing, globalLongOptionFlags, path = [], inherit
|
|
|
281
302
|
.map((field) => field.id)
|
|
282
303
|
});
|
|
283
304
|
});
|
|
284
|
-
collected.variants.push({
|
|
285
|
-
id: variantId,
|
|
286
|
-
controlDisplayPath,
|
|
287
|
-
controlFieldId: controlField.id,
|
|
288
|
-
optional: runtimeOptional,
|
|
289
|
-
branches
|
|
290
|
-
});
|
|
291
305
|
continue;
|
|
292
306
|
}
|
|
293
307
|
if (childSchema.kind === "record") {
|
|
@@ -418,23 +432,24 @@ function parseBooleanText(value, label) {
|
|
|
418
432
|
if (normalized === "false") {
|
|
419
433
|
return false;
|
|
420
434
|
}
|
|
421
|
-
throw new
|
|
435
|
+
throw new UserError(`Invalid value for "${label}". Expected true or false, got ${describeReceived(value)}.`);
|
|
422
436
|
}
|
|
423
437
|
function parseEnumValue(value, values, label) {
|
|
424
438
|
const match = values.find((candidate) => String(candidate) === value);
|
|
425
439
|
if (match === undefined) {
|
|
426
440
|
const suggestions = suggest(value, values.map((candidate) => String(candidate)));
|
|
427
441
|
const suggestionLine = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?\n` : " ";
|
|
428
|
-
throw new
|
|
442
|
+
throw new UserError(`Invalid value for "${label}".${suggestionLine}Expected one of: ${values.map((candidate) => String(candidate)).join(", ")}, got ${describeReceived(value)}.`);
|
|
429
443
|
}
|
|
430
444
|
return match;
|
|
431
445
|
}
|
|
432
446
|
function validateStringPattern(value, schema, label) {
|
|
433
|
-
|
|
434
|
-
|
|
447
|
+
const length = unicodeLength(value);
|
|
448
|
+
if (schema.minLength !== undefined && length < schema.minLength) {
|
|
449
|
+
throw new UserError(`Invalid value for "${label}". Expected a string with length at least ${schema.minLength}, got string with length ${length}.`);
|
|
435
450
|
}
|
|
436
|
-
if (schema.maxLength !== undefined &&
|
|
437
|
-
throw new UserError(`Invalid value for "${label}". Expected a string with length at most ${schema.maxLength}, got string with length ${
|
|
451
|
+
if (schema.maxLength !== undefined && length > schema.maxLength) {
|
|
452
|
+
throw new UserError(`Invalid value for "${label}". Expected a string with length at most ${schema.maxLength}, got string with length ${length}.`);
|
|
438
453
|
}
|
|
439
454
|
if (schema.pattern !== undefined && !matchesStringPattern(value, schema.pattern)) {
|
|
440
455
|
throw new UserError(`Invalid value for "${label}": "${value}" does not match pattern "${schema.pattern}".`);
|
|
@@ -449,7 +464,7 @@ function parseJsonText(value, label) {
|
|
|
449
464
|
return JSON.parse(value);
|
|
450
465
|
}
|
|
451
466
|
catch (error) {
|
|
452
|
-
throw new
|
|
467
|
+
throw new UserError(`Invalid value for "${label}". Expected valid JSON, got ${describeReceived(value)} (parser: ${getErrorMessage(error)}).`);
|
|
453
468
|
}
|
|
454
469
|
}
|
|
455
470
|
function describeReceived(value) {
|
|
@@ -573,8 +588,8 @@ function parseScalarValue(value, schema, label) {
|
|
|
573
588
|
return validateStringPattern(value, schema, label);
|
|
574
589
|
case "number": {
|
|
575
590
|
const parsed = Number(value);
|
|
576
|
-
if (!isValidNumberSchemaValue(parsed, schema)) {
|
|
577
|
-
throw new
|
|
591
|
+
if (value.trim().length === 0 || !isValidNumberSchemaValue(parsed, schema)) {
|
|
592
|
+
throw new UserError(`Invalid value for "${label}". Expected ${getExpectedNumberDescription(schema)}, got ${describeReceived(value)}.`);
|
|
578
593
|
}
|
|
579
594
|
return parsed;
|
|
580
595
|
}
|
|
@@ -616,22 +631,66 @@ function parseArrayValue(value, schema, label) {
|
|
|
616
631
|
}
|
|
617
632
|
return splitArrayInput(value).map((item) => parseScalarValue(item, itemSchema, label));
|
|
618
633
|
}
|
|
619
|
-
function
|
|
634
|
+
function isNegativeNumericToken(token) {
|
|
620
635
|
if (!token.startsWith("-") || token.startsWith("--")) {
|
|
621
636
|
return false;
|
|
622
637
|
}
|
|
623
|
-
const itemSchema = unwrapOptional(schema.item);
|
|
624
|
-
if (itemSchema.kind !== "number") {
|
|
625
|
-
return false;
|
|
626
|
-
}
|
|
627
638
|
const items = splitArrayInput(token);
|
|
628
|
-
return (items.length > 0 && items.every((item) =>
|
|
639
|
+
return (items.length > 0 && items.every((item) => Number.isFinite(Number(item))));
|
|
629
640
|
}
|
|
630
641
|
function isNextArrayOptionToken(token, schema) {
|
|
631
642
|
if (token === "-" && unwrapOptional(schema.item).kind === "string") {
|
|
632
643
|
return false;
|
|
633
644
|
}
|
|
634
|
-
return token.startsWith("-") &&
|
|
645
|
+
return token.startsWith("-") &&
|
|
646
|
+
!(unwrapOptional(schema.item).kind === "number" && isNegativeNumericToken(token));
|
|
647
|
+
}
|
|
648
|
+
function normalizeNumericArrayOptions(argv, options, numericArrayOptions) {
|
|
649
|
+
if (numericArrayOptions.size === 0) {
|
|
650
|
+
return argv;
|
|
651
|
+
}
|
|
652
|
+
const normalized = [];
|
|
653
|
+
let activeFlag;
|
|
654
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
655
|
+
const token = argv[index];
|
|
656
|
+
if (token === "--") {
|
|
657
|
+
normalized.push(...argv.slice(index));
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
const option = options.find((candidate) => candidate.short === token || candidate.long === token);
|
|
661
|
+
const attachedOption = token.startsWith("-") && !token.startsWith("--") && token.length > 2
|
|
662
|
+
? options.find((candidate) => candidate.short === token.slice(0, 2))
|
|
663
|
+
: undefined;
|
|
664
|
+
if (activeFlag !== undefined && option === undefined && attachedOption === undefined) {
|
|
665
|
+
if (token.startsWith("-") && !token.startsWith("--")) {
|
|
666
|
+
if (isNegativeNumericToken(token)) {
|
|
667
|
+
normalized.push(activeFlag);
|
|
668
|
+
}
|
|
669
|
+
else if (token !== "-") {
|
|
670
|
+
activeFlag = undefined;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
else if (token.startsWith("--")) {
|
|
674
|
+
activeFlag = undefined;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
activeFlag = undefined;
|
|
679
|
+
}
|
|
680
|
+
normalized.push(token);
|
|
681
|
+
if (option !== undefined) {
|
|
682
|
+
const nextToken = argv[index + 1];
|
|
683
|
+
if (nextToken !== undefined && (option.required ||
|
|
684
|
+
(option.optional && !(nextToken.length > 1 && nextToken.startsWith("-"))))) {
|
|
685
|
+
normalized.push(nextToken);
|
|
686
|
+
index += 1;
|
|
687
|
+
}
|
|
688
|
+
if (numericArrayOptions.has(option)) {
|
|
689
|
+
activeFlag = token;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return normalized;
|
|
635
694
|
}
|
|
636
695
|
function validateArrayBounds(value, schema, label) {
|
|
637
696
|
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
@@ -642,31 +701,37 @@ function validateArrayBounds(value, schema, label) {
|
|
|
642
701
|
}
|
|
643
702
|
}
|
|
644
703
|
function createOption(field, globalLongOptionFlags) {
|
|
645
|
-
const flags = formatOptionFlags(field, globalLongOptionFlags);
|
|
646
704
|
const collidesWithGlobalFlag = globalLongOptionFlags.has(field.optionFlag);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
mainOption.preset(true);
|
|
653
|
-
// Commander v14 passes the preset value through argParser too, so guard with typeof check
|
|
654
|
-
mainOption.argParser((value) => (typeof value === "boolean" ? value : value));
|
|
655
|
-
return [
|
|
656
|
-
mainOption,
|
|
657
|
-
createCommanderOption(`--no-${field.optionFlag.slice(2)}`, field.description, field)
|
|
705
|
+
const flagGroups = collidesWithGlobalFlag
|
|
706
|
+
? [formatOptionFlags(field, globalLongOptionFlags)]
|
|
707
|
+
: [
|
|
708
|
+
field.shortFlag === undefined ? field.optionFlag : `-${field.shortFlag}, ${field.optionFlag}`,
|
|
709
|
+
...field.longAliases
|
|
658
710
|
];
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
711
|
+
return flagGroups.flatMap((flags, index) => {
|
|
712
|
+
if (field.schema.kind === "boolean") {
|
|
713
|
+
if (collidesWithGlobalFlag) {
|
|
714
|
+
return [createCommanderOption(flags, field.description, field)];
|
|
715
|
+
}
|
|
716
|
+
const mainOption = createCommanderOption(`${flags} [value]`, field.description, field);
|
|
717
|
+
mainOption.preset(true);
|
|
718
|
+
// Commander v14 passes the preset value through argParser too, so guard with typeof check
|
|
719
|
+
mainOption.argParser((value) => (typeof value === "boolean" ? value : value));
|
|
720
|
+
return index === 0
|
|
721
|
+
? [mainOption, createCommanderOption(`--no-${field.optionFlag.slice(2)}`, field.description, field)]
|
|
722
|
+
: [mainOption];
|
|
723
|
+
}
|
|
724
|
+
if (field.schema.kind === "array") {
|
|
725
|
+
return [
|
|
726
|
+
createCommanderOption(`${flags} <value...>`, field.description, field).argParser((value, previous = []) => [...previous, value])
|
|
727
|
+
];
|
|
728
|
+
}
|
|
729
|
+
if (field.schema.kind === "json") {
|
|
730
|
+
return [createCommanderOption(`${flags} <json>`, field.description, field)];
|
|
731
|
+
}
|
|
732
|
+
const option = createCommanderOption(`${flags} <value>`, field.description, field);
|
|
733
|
+
return [option];
|
|
734
|
+
});
|
|
670
735
|
}
|
|
671
736
|
function resolveCLIControls(controls) {
|
|
672
737
|
const outputFormats = typeof controls?.output === "object" ? (controls.output.formats ?? {}) : {};
|
|
@@ -752,11 +817,101 @@ function createCommanderOption(flags, description, field) {
|
|
|
752
817
|
}
|
|
753
818
|
return option;
|
|
754
819
|
}
|
|
755
|
-
function
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
820
|
+
function prepareCliArguments(program, argv, fieldLoaders, casing, controls) {
|
|
821
|
+
const normalized = argv.slice(0, 2);
|
|
822
|
+
const path = [];
|
|
823
|
+
let current = program;
|
|
824
|
+
let helpPath;
|
|
825
|
+
let output;
|
|
826
|
+
for (let index = 2; index < argv.length; index += 1) {
|
|
827
|
+
let token = argv[index];
|
|
828
|
+
if (token === "--") {
|
|
829
|
+
normalized.push(...argv.slice(index));
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
if (HELP_FLAGS.has(token)) {
|
|
833
|
+
helpPath ??= [...path];
|
|
834
|
+
normalized.push(token);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (controls.verbose && token === "-v") {
|
|
838
|
+
token = "--verbose";
|
|
839
|
+
}
|
|
840
|
+
const dynamicFields = fieldLoaders.get(current)?.() ?? [];
|
|
841
|
+
const equalsIndex = token.startsWith("--") ? token.indexOf("=") : -1;
|
|
842
|
+
const flag = equalsIndex < 0 ? token : token.slice(0, equalsIndex);
|
|
843
|
+
let option = current.options.find((candidate) => candidate.long === flag || candidate.short === flag);
|
|
844
|
+
let attached = equalsIndex >= 0;
|
|
845
|
+
if (option === undefined && token.startsWith("-") && !token.startsWith("--") && token.length > 2) {
|
|
846
|
+
for (let offset = 1; offset < token.length; offset += 1) {
|
|
847
|
+
const candidate = current.options.find((entry) => entry.short === `-${token[offset]}`);
|
|
848
|
+
if (candidate === undefined) {
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
option = candidate;
|
|
852
|
+
if (candidate.required || candidate.optional) {
|
|
853
|
+
attached = offset < token.length - 1;
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (option !== undefined) {
|
|
859
|
+
normalized.push(token);
|
|
860
|
+
const next = argv[index + 1];
|
|
861
|
+
if (flag === "--output" && controls.output) {
|
|
862
|
+
output = attached ? token.slice(equalsIndex + 1) : next;
|
|
863
|
+
}
|
|
864
|
+
if (!attached && next !== undefined && (option.required ||
|
|
865
|
+
(option.optional && !(next.length > 1 && next.startsWith("-"))))) {
|
|
866
|
+
normalized.push(next);
|
|
867
|
+
index += 1;
|
|
868
|
+
}
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
const child = current.commands.find((command) => command.name() === token || command.aliases().includes(token));
|
|
872
|
+
if (child !== undefined) {
|
|
873
|
+
current = child;
|
|
874
|
+
path.push(token);
|
|
875
|
+
normalized.push(token);
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
const defaultName = getDefaultCommanderCommandName(current);
|
|
879
|
+
const defaultCommand = current.commands.find((command) => command.name() === defaultName);
|
|
880
|
+
if (defaultCommand !== undefined) {
|
|
881
|
+
current = defaultCommand;
|
|
882
|
+
index -= 1;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (token.startsWith("--") && equalsIndex < 0) {
|
|
886
|
+
const normalizedFlag = token.startsWith("--no-") ? token.slice(5) : token.slice(2);
|
|
887
|
+
let dynamic;
|
|
888
|
+
try {
|
|
889
|
+
dynamic = resolveDynamicOption(dynamicFields, normalizedFlag, casing);
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
if (!isUserError(error)) {
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const next = argv[index + 1];
|
|
897
|
+
if (dynamic !== undefined && next !== undefined &&
|
|
898
|
+
dynamic.leaf.schema.kind !== "boolean" && dynamic.leaf.schema.kind !== "array") {
|
|
899
|
+
normalized.push(`${token}=${next}`);
|
|
900
|
+
index += 1;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (!token.startsWith("-") && current.commands.length > 0) {
|
|
905
|
+
path.push(token);
|
|
906
|
+
}
|
|
907
|
+
normalized.push(token);
|
|
908
|
+
}
|
|
909
|
+
return {
|
|
910
|
+
argv: normalized,
|
|
911
|
+
...(helpPath === undefined ? {} : {
|
|
912
|
+
helpArgv: [...argv.slice(0, 2), ...helpPath, ...(output === undefined ? [] : ["--output", output])]
|
|
913
|
+
})
|
|
914
|
+
};
|
|
760
915
|
}
|
|
761
916
|
function resolveHelpOutput(argv) {
|
|
762
917
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -1538,12 +1693,20 @@ function formatJsonHelpOption(field, globalLongOptionFlags) {
|
|
|
1538
1693
|
name: field.displayPath,
|
|
1539
1694
|
flags: formatHelpFieldFlags(field, globalLongOptionFlags).split(", "),
|
|
1540
1695
|
type: formatJsonHelpSchemaType(field.schema),
|
|
1696
|
+
...(field.schema.kind === "enum" ? { choices: formatCLIEnumChoices(field.schema) } : {}),
|
|
1541
1697
|
...(field.description === undefined ? {} : { description: field.description }),
|
|
1542
1698
|
required: field.requiredWhenActive,
|
|
1543
1699
|
...(field.hasDefault ? { default: field.defaultValue } : {}),
|
|
1544
1700
|
...(field.positionalIndex === undefined ? {} : { positional: true })
|
|
1545
1701
|
};
|
|
1546
1702
|
}
|
|
1703
|
+
function formatCLIEnumChoices(schema) {
|
|
1704
|
+
const choices = schema.values.map((value) => String(value));
|
|
1705
|
+
if (schema.nullable === true && !choices.includes("null")) {
|
|
1706
|
+
choices.push("null");
|
|
1707
|
+
}
|
|
1708
|
+
return choices;
|
|
1709
|
+
}
|
|
1547
1710
|
function formatJsonHelpSchemaType(schema) {
|
|
1548
1711
|
if (schema.kind === "enum") {
|
|
1549
1712
|
return "enum";
|
|
@@ -1591,7 +1754,7 @@ async function renderGeneratedHelp(root, argv, options) {
|
|
|
1591
1754
|
}, rootUsageName));
|
|
1592
1755
|
return;
|
|
1593
1756
|
}
|
|
1594
|
-
await withOutputFormat(output, async () => {
|
|
1757
|
+
await withOutputFormat(toDesignSystemOutput(output), async () => {
|
|
1595
1758
|
const rendered = target.node.kind === "group"
|
|
1596
1759
|
? renderGroupHelp(target.node, target.breadcrumb, "cli", casing, {
|
|
1597
1760
|
controls,
|
|
@@ -1606,18 +1769,17 @@ async function renderGeneratedHelp(root, argv, options) {
|
|
|
1606
1769
|
process.stdout.write(rendered);
|
|
1607
1770
|
});
|
|
1608
1771
|
}
|
|
1609
|
-
function createNodeCommand(node, casing, globalLongOptionFlags, execute, presetsEnabled, controls, pathSegments = []) {
|
|
1772
|
+
function createNodeCommand(node, casing, globalLongOptionFlags, execute, presetsEnabled, controls, fieldLoaders, pathSegments = []) {
|
|
1610
1773
|
const nextPathSegments = [...pathSegments, node.name];
|
|
1611
1774
|
if (node.kind === "command") {
|
|
1612
1775
|
if (!node.scope.includes("cli")) {
|
|
1613
1776
|
return null;
|
|
1614
1777
|
}
|
|
1615
1778
|
const command = new CommanderCommand(node.name);
|
|
1779
|
+
let unknownArgv = [];
|
|
1780
|
+
let loadedFields;
|
|
1616
1781
|
Reflect.set(command, "_toolcraftHidden", node.hidden);
|
|
1617
1782
|
Reflect.set(command, "_toolcraftOriginalName", node.name);
|
|
1618
|
-
const collected = collectFields(node.params, casing, globalLongOptionFlags);
|
|
1619
|
-
const fields = assignPositionals(collected.fields, node.positional);
|
|
1620
|
-
validateUniqueOptionFlags(fields, globalLongOptionFlags);
|
|
1621
1783
|
if (node.description !== undefined) {
|
|
1622
1784
|
command.description(node.description);
|
|
1623
1785
|
}
|
|
@@ -1625,36 +1787,63 @@ function createNodeCommand(node, casing, globalLongOptionFlags, execute, presets
|
|
|
1625
1787
|
command.addHelpCommand(false);
|
|
1626
1788
|
addGlobalOptions(command, presetsEnabled, controls);
|
|
1627
1789
|
command.allowExcessArguments(true);
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
for (const field of fields) {
|
|
1632
|
-
if (field.positionalIndex !== undefined) {
|
|
1633
|
-
// Positionals are declared optional so resolveParams stays the single owner of argument
|
|
1634
|
-
// validation: Commander would otherwise report a missing positional in its own words
|
|
1635
|
-
// before prompts, defaults, and presets have had a chance to supply the value.
|
|
1636
|
-
command.argument(field.variadicPosition === true ? `[${field.displayPath}...]` : `[${field.displayPath}]`);
|
|
1637
|
-
continue;
|
|
1790
|
+
fieldLoaders.set(command, () => {
|
|
1791
|
+
if (loadedFields !== undefined) {
|
|
1792
|
+
return loadedFields;
|
|
1638
1793
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
const
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1794
|
+
const collected = collectFields(node.params, casing, globalLongOptionFlags);
|
|
1795
|
+
const fields = assignPositionals(collected.fields, node.positional);
|
|
1796
|
+
validateUniqueOptionFlags(fields, globalLongOptionFlags);
|
|
1797
|
+
if (collected.dynamicFields.length > 0) {
|
|
1798
|
+
command.allowUnknownOption(true);
|
|
1799
|
+
}
|
|
1800
|
+
const numericArrayOptions = new Set();
|
|
1801
|
+
for (const field of fields) {
|
|
1802
|
+
if (field.positionalIndex !== undefined) {
|
|
1803
|
+
// Positionals are declared optional so resolveParams stays the single owner of argument
|
|
1804
|
+
// validation: Commander would otherwise report a missing positional in its own words
|
|
1805
|
+
// before prompts, defaults, and presets have had a chance to supply the value.
|
|
1806
|
+
command.argument(field.variadicPosition === true ? `[${field.displayPath}...]` : `[${field.displayPath}]`);
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
for (const option of createOption(field, globalLongOptionFlags)) {
|
|
1810
|
+
command.addOption(option);
|
|
1811
|
+
if (field.schema.kind === "array" && unwrapOptional(field.schema.item).kind === "number") {
|
|
1812
|
+
numericArrayOptions.add(option);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
const parseOptions = command.parseOptions.bind(command);
|
|
1817
|
+
command.parseOptions = (argv) => {
|
|
1818
|
+
const parsed = parseOptions(normalizeNumericArrayOptions(argv, command.options, numericArrayOptions));
|
|
1819
|
+
const firstUnknownOption = parsed.unknown.findIndex((token) => token.length > 1 && token.startsWith("-") && !isNegativeNumericToken(token));
|
|
1820
|
+
const operandCount = firstUnknownOption < 0 ? parsed.unknown.length : firstUnknownOption;
|
|
1821
|
+
parsed.operands.push(...parsed.unknown.splice(0, operandCount));
|
|
1822
|
+
if (parsed.unknown[0] === "--") {
|
|
1823
|
+
parsed.operands.push(...parsed.unknown.slice(1));
|
|
1824
|
+
parsed.unknown.length = 0;
|
|
1825
|
+
}
|
|
1826
|
+
unknownArgv = parsed.unknown;
|
|
1827
|
+
return parsed;
|
|
1828
|
+
};
|
|
1829
|
+
command.action(async (...args) => {
|
|
1830
|
+
const actionCommand = args[args.length - 1];
|
|
1831
|
+
const positionalValues = actionCommand.args.slice(0, actionCommand.args.length - unknownArgv.length);
|
|
1832
|
+
await execute({
|
|
1833
|
+
command: node,
|
|
1834
|
+
commandPath: nextPathSegments.join("."),
|
|
1835
|
+
casing,
|
|
1836
|
+
dynamicFields: collected.dynamicFields,
|
|
1837
|
+
fields,
|
|
1838
|
+
positionalValues,
|
|
1839
|
+
presetsEnabled,
|
|
1840
|
+
rawArgv: unknownArgv,
|
|
1841
|
+
actionCommand,
|
|
1842
|
+
variants: collected.variants
|
|
1843
|
+
});
|
|
1657
1844
|
});
|
|
1845
|
+
loadedFields = collected.dynamicFields;
|
|
1846
|
+
return loadedFields;
|
|
1658
1847
|
});
|
|
1659
1848
|
return command;
|
|
1660
1849
|
}
|
|
@@ -1665,7 +1854,7 @@ function createNodeCommand(node, casing, globalLongOptionFlags, execute, presets
|
|
|
1665
1854
|
.filter((child) => !isNodeVisibleInScope(child, "cli"))
|
|
1666
1855
|
.flatMap((child) => getNodeCommandNames(child));
|
|
1667
1856
|
const visibleChildren = node.children
|
|
1668
|
-
.map((child) => createNodeCommand(child, casing, globalLongOptionFlags, execute, presetsEnabled, controls, nextPathSegments))
|
|
1857
|
+
.map((child) => createNodeCommand(child, casing, globalLongOptionFlags, execute, presetsEnabled, controls, fieldLoaders, nextPathSegments))
|
|
1669
1858
|
.filter((child) => child !== null);
|
|
1670
1859
|
const group = new CommanderCommand(node.name);
|
|
1671
1860
|
Reflect.set(group, "_toolcraftReservedChildNames", reservedChildNames);
|
|
@@ -1773,7 +1962,8 @@ function createGlobalSnapshotOptions(presetsEnabled, versionEnabled, controls) {
|
|
|
1773
1962
|
type: "enum",
|
|
1774
1963
|
required: false,
|
|
1775
1964
|
hidden: true,
|
|
1776
|
-
description: "Print stack traces for unexpected errors."
|
|
1965
|
+
description: "Print stack traces for unexpected errors.",
|
|
1966
|
+
choices: ["trim", "raw"]
|
|
1777
1967
|
});
|
|
1778
1968
|
}
|
|
1779
1969
|
if (controls.logLevel) {
|
|
@@ -1783,7 +1973,8 @@ function createGlobalSnapshotOptions(presetsEnabled, versionEnabled, controls) {
|
|
|
1783
1973
|
type: "enum",
|
|
1784
1974
|
required: false,
|
|
1785
1975
|
hidden: true,
|
|
1786
|
-
description: "Set runtime diagnostic log level."
|
|
1976
|
+
description: "Set runtime diagnostic log level.",
|
|
1977
|
+
choices: [...LOG_LEVELS]
|
|
1787
1978
|
});
|
|
1788
1979
|
}
|
|
1789
1980
|
if (controls.verbose) {
|
|
@@ -1849,6 +2040,7 @@ function createFieldSnapshotOption(field, globalLongOptionFlags) {
|
|
|
1849
2040
|
name: field.displayPath,
|
|
1850
2041
|
flags: formatHelpFieldFlags(field, globalLongOptionFlags).split(", "),
|
|
1851
2042
|
type: formatJsonHelpSchemaType(field.schema),
|
|
2043
|
+
...(field.schema.kind === "enum" ? { choices: formatCLIEnumChoices(field.schema) } : {}),
|
|
1852
2044
|
required: field.requiredWhenActive,
|
|
1853
2045
|
hidden: false,
|
|
1854
2046
|
...(field.description === undefined ? {} : { description: field.description }),
|
|
@@ -1999,7 +2191,6 @@ function withPromptStreams(options, streams) {
|
|
|
1999
2191
|
};
|
|
2000
2192
|
}
|
|
2001
2193
|
function throwPromptCancellation() {
|
|
2002
|
-
cancel("Operation cancelled.");
|
|
2003
2194
|
throw new UserError("Operation cancelled.");
|
|
2004
2195
|
}
|
|
2005
2196
|
async function promptForField(field, streams = {}) {
|
|
@@ -2044,7 +2235,7 @@ async function promptForField(field, streams = {}) {
|
|
|
2044
2235
|
throw new UserError(`Missing required parameter "${field.displayPath}".`);
|
|
2045
2236
|
}
|
|
2046
2237
|
if (entered.trim().length === 0 && field.hasDefault) {
|
|
2047
|
-
return field.defaultValue;
|
|
2238
|
+
return cloneDefaultValue(field.defaultValue);
|
|
2048
2239
|
}
|
|
2049
2240
|
if (field.schema.kind === "array") {
|
|
2050
2241
|
return parseArrayValue(entered, field.schema, field.displayPath);
|
|
@@ -2112,23 +2303,6 @@ function toDesignSystemOutput(output) {
|
|
|
2112
2303
|
}
|
|
2113
2304
|
return "terminal";
|
|
2114
2305
|
}
|
|
2115
|
-
async function withOutputFormat(output, fn) {
|
|
2116
|
-
const previous = process.env.OUTPUT_FORMAT;
|
|
2117
|
-
process.env.OUTPUT_FORMAT = toDesignSystemOutput(output);
|
|
2118
|
-
resetOutputFormatCache();
|
|
2119
|
-
try {
|
|
2120
|
-
return await fn();
|
|
2121
|
-
}
|
|
2122
|
-
finally {
|
|
2123
|
-
if (previous === undefined) {
|
|
2124
|
-
delete process.env.OUTPUT_FORMAT;
|
|
2125
|
-
}
|
|
2126
|
-
else {
|
|
2127
|
-
process.env.OUTPUT_FORMAT = previous;
|
|
2128
|
-
}
|
|
2129
|
-
resetOutputFormatCache();
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
2306
|
function isPlainObject(value) {
|
|
2133
2307
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2134
2308
|
}
|
|
@@ -2159,20 +2333,22 @@ function validatePresetScalarValue(value, schema, fieldPath, presetPath) {
|
|
|
2159
2333
|
return null;
|
|
2160
2334
|
}
|
|
2161
2335
|
switch (schema.kind) {
|
|
2162
|
-
case "string":
|
|
2336
|
+
case "string": {
|
|
2163
2337
|
if (typeof value !== "string") {
|
|
2164
2338
|
break;
|
|
2165
2339
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2340
|
+
const length = unicodeLength(value);
|
|
2341
|
+
if (schema.minLength !== undefined && length < schema.minLength) {
|
|
2342
|
+
throw new UserError(`Preset file "${presetPath}" has an invalid value for "${fieldPath}". Expected a string with length at least ${schema.minLength}, got string with length ${length}.`);
|
|
2168
2343
|
}
|
|
2169
|
-
if (schema.maxLength !== undefined &&
|
|
2170
|
-
throw new UserError(`Preset file "${presetPath}" has an invalid value for "${fieldPath}". Expected a string with length at most ${schema.maxLength}, got string with length ${
|
|
2344
|
+
if (schema.maxLength !== undefined && length > schema.maxLength) {
|
|
2345
|
+
throw new UserError(`Preset file "${presetPath}" has an invalid value for "${fieldPath}". Expected a string with length at most ${schema.maxLength}, got string with length ${length}.`);
|
|
2171
2346
|
}
|
|
2172
2347
|
if (schema.pattern !== undefined && !matchesStringPattern(value, schema.pattern)) {
|
|
2173
2348
|
throw new UserError(`Preset file "${presetPath}" has an invalid value for "${fieldPath}": "${value}" does not match pattern "${schema.pattern}".`);
|
|
2174
2349
|
}
|
|
2175
2350
|
return value;
|
|
2351
|
+
}
|
|
2176
2352
|
case "number":
|
|
2177
2353
|
if (!isValidNumberSchemaValue(value, schema)) {
|
|
2178
2354
|
break;
|
|
@@ -2200,6 +2376,9 @@ function validatePresetFieldValue(value, field, presetPath) {
|
|
|
2200
2376
|
if (field.schema.kind !== "array") {
|
|
2201
2377
|
return validatePresetScalarValue(value, field.schema, field.displayPath, presetPath);
|
|
2202
2378
|
}
|
|
2379
|
+
if (value === null && field.schema.nullable === true) {
|
|
2380
|
+
return null;
|
|
2381
|
+
}
|
|
2203
2382
|
const itemSchema = unwrapOptional(field.schema.item);
|
|
2204
2383
|
if (itemSchema.kind === "array" || itemSchema.kind === "object") {
|
|
2205
2384
|
throw new UserError(`Array parameter "${field.displayPath}" must use scalar items.`);
|
|
@@ -2215,7 +2394,7 @@ function validatePresetFieldValue(value, field, presetPath) {
|
|
|
2215
2394
|
}
|
|
2216
2395
|
return value.map((item) => validatePresetScalarValue(item, itemSchema, field.displayPath, presetPath));
|
|
2217
2396
|
}
|
|
2218
|
-
async function loadPresetValues(fields, presetPath) {
|
|
2397
|
+
async function loadPresetValues(fields, dynamicFields, presetPath) {
|
|
2219
2398
|
let rawPreset;
|
|
2220
2399
|
try {
|
|
2221
2400
|
rawPreset = await readFile(presetPath, {
|
|
@@ -2242,7 +2421,10 @@ async function loadPresetValues(fields, presetPath) {
|
|
|
2242
2421
|
throw new UserError(`Preset file "${presetPath}" must contain a JSON object.`);
|
|
2243
2422
|
}
|
|
2244
2423
|
const fieldByPath = new Map(fields.map((field) => [field.displayPath, field]));
|
|
2424
|
+
const dynamicFieldByPath = new Map(dynamicFields.map((field) => [field.displayPath, field]));
|
|
2425
|
+
const allFields = [...fields, ...dynamicFields];
|
|
2245
2426
|
const presetValues = {};
|
|
2427
|
+
const dynamicValues = new Map();
|
|
2246
2428
|
function visitObject(current, path) {
|
|
2247
2429
|
for (const [key, value] of Object.entries(current)) {
|
|
2248
2430
|
const nextPath = [...path, key];
|
|
@@ -2252,7 +2434,18 @@ async function loadPresetValues(fields, presetPath) {
|
|
|
2252
2434
|
presetValues[field.optionAttribute] = validatePresetFieldValue(value, field, presetPath);
|
|
2253
2435
|
continue;
|
|
2254
2436
|
}
|
|
2255
|
-
|
|
2437
|
+
const dynamicField = dynamicFieldByPath.get(displayPath);
|
|
2438
|
+
if (dynamicField !== undefined) {
|
|
2439
|
+
const validation = validateSchema(dynamicField.schema, value, { defaults: "all" });
|
|
2440
|
+
if (!validation.ok) {
|
|
2441
|
+
const issue = validation.issues[0];
|
|
2442
|
+
const issuePath = [displayPath, ...issue.path].join(".");
|
|
2443
|
+
throw new UserError(`Preset file "${presetPath}" has an invalid value for "${issuePath}". ${issue.message}`);
|
|
2444
|
+
}
|
|
2445
|
+
dynamicValues.set(dynamicField.id, validation.value);
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2448
|
+
if (!hasNestedField(allFields, nextPath)) {
|
|
2256
2449
|
throw new UserError(`Preset file "${presetPath}" contains unknown parameter "${displayPath}".`);
|
|
2257
2450
|
}
|
|
2258
2451
|
if (!isPlainObject(value)) {
|
|
@@ -2262,7 +2455,7 @@ async function loadPresetValues(fields, presetPath) {
|
|
|
2262
2455
|
}
|
|
2263
2456
|
}
|
|
2264
2457
|
visitObject(parsedPreset, []);
|
|
2265
|
-
return presetValues;
|
|
2458
|
+
return { fields: presetValues, dynamic: dynamicValues };
|
|
2266
2459
|
}
|
|
2267
2460
|
function isNumericFixtureSelector(value) {
|
|
2268
2461
|
if (value.length === 0) {
|
|
@@ -2576,15 +2769,16 @@ function isHumanInLoopPending(result) {
|
|
|
2576
2769
|
typeof result.message === "string" &&
|
|
2577
2770
|
typeof result.enqueuedAt === "string");
|
|
2578
2771
|
}
|
|
2579
|
-
function renderHumanInLoopPending(pending, rootUsageName) {
|
|
2580
|
-
|
|
2772
|
+
function renderHumanInLoopPending(pending, rootUsageName, outputEmitter) {
|
|
2773
|
+
const message = `✓ Queued for human approval (id: ${pending.approvalId})\n` +
|
|
2581
2774
|
` Message: ${pending.message}\n` +
|
|
2582
|
-
` Track: ${rootUsageName} approvals show --approval-id ${pending.approvalId}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2775
|
+
` Track: ${rootUsageName} approvals show --approval-id ${pending.approvalId}`;
|
|
2776
|
+
if (outputEmitter === undefined) {
|
|
2777
|
+
process.stdout.write(`${message}\n`);
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
outputEmitter(message);
|
|
2781
|
+
}
|
|
2588
2782
|
}
|
|
2589
2783
|
function renderCliErrorPattern(pattern, outputEmitter) {
|
|
2590
2784
|
const logger = createLogger(outputEmitter);
|
|
@@ -2633,7 +2827,7 @@ function renderCliErrorPattern(pattern, outputEmitter) {
|
|
|
2633
2827
|
process.exitCode = 1;
|
|
2634
2828
|
}
|
|
2635
2829
|
function getNestedValue(target, path) {
|
|
2636
|
-
return path.reduce((current, segment) => current !== null && typeof current === "object"
|
|
2830
|
+
return path.reduce((current, segment) => current !== null && typeof current === "object" && Object.prototype.hasOwnProperty.call(current, segment)
|
|
2637
2831
|
? current[segment]
|
|
2638
2832
|
: undefined, target);
|
|
2639
2833
|
}
|
|
@@ -2800,6 +2994,10 @@ function resolveDynamicLeaf(schema, rawSegments, casing, outputPath = [], displa
|
|
|
2800
2994
|
: [qualifyDisplayPath(displayPathPrefix, toDisplayPath(displayPath))])}`);
|
|
2801
2995
|
}
|
|
2802
2996
|
}
|
|
2997
|
+
function formatFieldValidationIssue(issue, displayPath) {
|
|
2998
|
+
const path = [displayPath, ...issue.path].filter((part) => part !== "").join(".") || "parameters";
|
|
2999
|
+
return { path, message: `Invalid value for "${path}". ${issue.message}` };
|
|
3000
|
+
}
|
|
2803
3001
|
function finalizeDynamicValue(schema, value, displayPath, errors) {
|
|
2804
3002
|
const unwrappedSchema = unwrapOptional(schema);
|
|
2805
3003
|
if (value === undefined) {
|
|
@@ -2813,8 +3011,14 @@ function finalizeDynamicValue(schema, value, displayPath, errors) {
|
|
|
2813
3011
|
case "number":
|
|
2814
3012
|
case "boolean":
|
|
2815
3013
|
case "enum":
|
|
2816
|
-
case "json":
|
|
2817
3014
|
return value;
|
|
3015
|
+
case "json": {
|
|
3016
|
+
const validation = validateSchema(unwrappedSchema, value);
|
|
3017
|
+
if (!validation.ok) {
|
|
3018
|
+
errors.push(...validation.issues.map((issue) => formatFieldValidationIssue(issue, displayPath)));
|
|
3019
|
+
}
|
|
3020
|
+
return value;
|
|
3021
|
+
}
|
|
2818
3022
|
case "array": {
|
|
2819
3023
|
const itemSchema = unwrapOptional(unwrappedSchema.item);
|
|
2820
3024
|
if (itemSchema.kind !== "object") {
|
|
@@ -2862,7 +3066,7 @@ function finalizeDynamicValue(schema, value, displayPath, errors) {
|
|
|
2862
3066
|
const childDisplayPath = displayPath.length === 0 ? key : `${displayPath}.${key}`;
|
|
2863
3067
|
if (childValue === undefined) {
|
|
2864
3068
|
if (childSchema.default !== undefined) {
|
|
2865
|
-
result[key] = childSchema.default;
|
|
3069
|
+
result[key] = cloneDefaultValue(childSchema.default);
|
|
2866
3070
|
continue;
|
|
2867
3071
|
}
|
|
2868
3072
|
if (rawChildSchema.kind === "optional") {
|
|
@@ -2903,7 +3107,7 @@ function formatCliSchemaKind(kind) {
|
|
|
2903
3107
|
return kind === "oneOf" ? "oneof" : kind;
|
|
2904
3108
|
}
|
|
2905
3109
|
function formatUnsupportedDynamicSchemaMessage(kind, displayPath) {
|
|
2906
|
-
return `Unsupported
|
|
3110
|
+
return `Unsupported CLI argument shape for "${displayPath}" (type "${kind}").`;
|
|
2907
3111
|
}
|
|
2908
3112
|
function qualifyDisplayPath(prefix, displayPath) {
|
|
2909
3113
|
if (prefix.length === 0) {
|
|
@@ -2914,13 +3118,38 @@ function qualifyDisplayPath(prefix, displayPath) {
|
|
|
2914
3118
|
}
|
|
2915
3119
|
return `${prefix}.${displayPath}`;
|
|
2916
3120
|
}
|
|
3121
|
+
function resolveDynamicOption(dynamicFields, flagName, casing) {
|
|
3122
|
+
const flagPath = flagName.split(".");
|
|
3123
|
+
const match = [...dynamicFields]
|
|
3124
|
+
.sort((left, right) => right.optionPath.length - left.optionPath.length)
|
|
3125
|
+
.find((field) => {
|
|
3126
|
+
const optionPath = field.optionPath.map((segment) => formatSegment(segment, casing));
|
|
3127
|
+
return flagPath.length > optionPath.length &&
|
|
3128
|
+
optionPath.every((segment, index) => flagPath[index] === segment);
|
|
3129
|
+
});
|
|
3130
|
+
if (match === undefined) {
|
|
3131
|
+
return undefined;
|
|
3132
|
+
}
|
|
3133
|
+
return {
|
|
3134
|
+
match,
|
|
3135
|
+
leaf: resolveDynamicLeaf(match.schema, flagPath.slice(match.optionPath.length), casing, [], [], match.displayPath)
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
2917
3138
|
function parseDynamicValues(dynamicFields, rawArgv, casing, errors) {
|
|
2918
3139
|
const rawValues = new Map();
|
|
2919
3140
|
const providedFieldIds = new Set();
|
|
2920
|
-
const
|
|
3141
|
+
const positionals = [];
|
|
2921
3142
|
for (let index = 0; index < rawArgv.length; index += 1) {
|
|
2922
3143
|
const token = rawArgv[index] ?? "";
|
|
3144
|
+
if (token === "--") {
|
|
3145
|
+
positionals.push(...rawArgv.slice(index + 1));
|
|
3146
|
+
break;
|
|
3147
|
+
}
|
|
2923
3148
|
if (!token.startsWith("--")) {
|
|
3149
|
+
if (token.length > 1 && token.startsWith("-") && !isNegativeNumericToken(token)) {
|
|
3150
|
+
throw new UserError(`Unknown option "${token}".`);
|
|
3151
|
+
}
|
|
3152
|
+
positionals.push(token);
|
|
2924
3153
|
continue;
|
|
2925
3154
|
}
|
|
2926
3155
|
const negated = token.startsWith("--no-");
|
|
@@ -2928,18 +3157,11 @@ function parseDynamicValues(dynamicFields, rawArgv, casing, errors) {
|
|
|
2928
3157
|
const equalsIndex = normalized.indexOf("=");
|
|
2929
3158
|
const flagName = equalsIndex >= 0 ? normalized.slice(2, equalsIndex) : normalized.slice(2);
|
|
2930
3159
|
const inlineValue = equalsIndex >= 0 ? normalized.slice(equalsIndex + 1) : undefined;
|
|
2931
|
-
const
|
|
2932
|
-
|
|
2933
|
-
const optionPath = field.optionPath.map((segment) => formatSegment(segment, casing));
|
|
2934
|
-
return (flagPath.length > optionPath.length &&
|
|
2935
|
-
optionPath.every((segment, segmentIndex) => flagPath[segmentIndex] === segment));
|
|
2936
|
-
});
|
|
2937
|
-
if (match === undefined) {
|
|
3160
|
+
const dynamic = resolveDynamicOption(dynamicFields, flagName, casing);
|
|
3161
|
+
if (dynamic === undefined) {
|
|
2938
3162
|
throw new UserError(`Unknown parameter "${flagName}". ${formatAvailableList(dynamicFields.map((field) => field.optionPathDisplay))}`);
|
|
2939
3163
|
}
|
|
2940
|
-
const
|
|
2941
|
-
const remainder = flagPath.slice(optionPath.length);
|
|
2942
|
-
const leaf = resolveDynamicLeaf(match.schema, remainder, casing, [], [], match.displayPath);
|
|
3164
|
+
const { match, leaf } = dynamic;
|
|
2943
3165
|
const rawStore = rawValues.get(match.id) ?? {};
|
|
2944
3166
|
const label = `${match.displayPath}.${leaf.displayPath}`.replace(/^\./u, "");
|
|
2945
3167
|
const parsed = negated && leaf.schema.kind === "boolean"
|
|
@@ -2955,6 +3177,7 @@ function parseDynamicValues(dynamicFields, rawArgv, casing, errors) {
|
|
|
2955
3177
|
}
|
|
2956
3178
|
return {
|
|
2957
3179
|
providedFieldIds,
|
|
3180
|
+
positionals,
|
|
2958
3181
|
values: new Map(dynamicFields
|
|
2959
3182
|
.filter((field) => rawValues.has(field.id))
|
|
2960
3183
|
.map((field) => [
|
|
@@ -2963,9 +3186,10 @@ function parseDynamicValues(dynamicFields, rawArgv, casing, errors) {
|
|
|
2963
3186
|
]))
|
|
2964
3187
|
};
|
|
2965
3188
|
}
|
|
2966
|
-
async function enforceVariantConstraints(params, fields, dynamicFields, variants, resolvedFieldValues, providedDynamicFieldIds, providedFieldIds, shouldPrompt, errors) {
|
|
3189
|
+
async function enforceVariantConstraints(params, fields, dynamicFields, variants, resolvedFieldValues, providedDynamicFieldIds, providedFieldIds, shouldPrompt, errors, promptStreams) {
|
|
2967
3190
|
const fieldById = new Map(fields.map((field) => [field.id, field]));
|
|
2968
3191
|
const dynamicFieldById = new Map(dynamicFields.map((field) => [field.id, field]));
|
|
3192
|
+
const activeBranches = new Map();
|
|
2969
3193
|
const getAvailableBranchParameters = (branch) => [
|
|
2970
3194
|
...branch.fieldIds
|
|
2971
3195
|
.map((fieldId) => fieldById.get(fieldId))
|
|
@@ -2977,12 +3201,16 @@ async function enforceVariantConstraints(params, fields, dynamicFields, variants
|
|
|
2977
3201
|
.map((field) => field.optionPathDisplay)
|
|
2978
3202
|
];
|
|
2979
3203
|
for (const variant of variants) {
|
|
3204
|
+
if (variant.parent !== undefined && activeBranches.get(variant.parent.id) !== variant.parent.branchId) {
|
|
3205
|
+
continue;
|
|
3206
|
+
}
|
|
2980
3207
|
let selectedBranchId = resolvedFieldValues.get(variant.controlFieldId);
|
|
2981
3208
|
if (selectedBranchId === undefined && shouldPrompt) {
|
|
2982
3209
|
const controlField = fieldById.get(variant.controlFieldId);
|
|
2983
3210
|
if (controlField !== undefined) {
|
|
2984
|
-
selectedBranchId = await promptForField(controlField);
|
|
3211
|
+
selectedBranchId = await promptForField(controlField, promptStreams);
|
|
2985
3212
|
resolvedFieldValues.set(controlField.id, selectedBranchId);
|
|
3213
|
+
providedFieldIds.add(controlField.id);
|
|
2986
3214
|
if (!controlField.synthetic) {
|
|
2987
3215
|
setNestedValue(params, controlField.path, selectedBranchId);
|
|
2988
3216
|
}
|
|
@@ -3037,16 +3265,33 @@ async function enforceVariantConstraints(params, fields, dynamicFields, variants
|
|
|
3037
3265
|
if (hasInvalidBranchParameter) {
|
|
3038
3266
|
continue;
|
|
3039
3267
|
}
|
|
3268
|
+
activeBranches.set(variant.id, selectedBranch.branchId);
|
|
3269
|
+
for (const fieldId of selectedBranch.fieldIds) {
|
|
3270
|
+
const field = fieldById.get(fieldId);
|
|
3271
|
+
if (field?.variantId !== variant.id || !field.hasDefault || getNestedValue(params, field.path) !== undefined) {
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
const value = cloneDefaultValue(field.defaultValue);
|
|
3275
|
+
resolvedFieldValues.set(field.id, value);
|
|
3276
|
+
setNestedValue(params, field.path, value);
|
|
3277
|
+
}
|
|
3278
|
+
for (const fieldId of selectedBranch.dynamicFieldIds) {
|
|
3279
|
+
const field = dynamicFieldById.get(fieldId);
|
|
3280
|
+
if (field?.variantId !== variant.id || !field.hasDefault || getNestedValue(params, field.path) !== undefined) {
|
|
3281
|
+
continue;
|
|
3282
|
+
}
|
|
3283
|
+
setNestedValue(params, field.path, cloneDefaultValue(field.defaultValue));
|
|
3284
|
+
}
|
|
3040
3285
|
for (const fieldId of selectedBranch.requiredFieldIds) {
|
|
3041
3286
|
const field = fieldById.get(fieldId);
|
|
3042
|
-
if (field
|
|
3287
|
+
if (field?.variantId !== variant.id || field.synthetic) {
|
|
3043
3288
|
continue;
|
|
3044
3289
|
}
|
|
3045
3290
|
if (getNestedValue(params, field.path) !== undefined) {
|
|
3046
3291
|
continue;
|
|
3047
3292
|
}
|
|
3048
3293
|
if (shouldPrompt) {
|
|
3049
|
-
const promptedValue = await promptForField(field);
|
|
3294
|
+
const promptedValue = await promptForField(field, promptStreams);
|
|
3050
3295
|
resolvedFieldValues.set(field.id, promptedValue);
|
|
3051
3296
|
setNestedValue(params, field.path, promptedValue);
|
|
3052
3297
|
providedFieldIds.add(field.id);
|
|
@@ -3059,7 +3304,7 @@ async function enforceVariantConstraints(params, fields, dynamicFields, variants
|
|
|
3059
3304
|
}
|
|
3060
3305
|
for (const fieldId of selectedBranch.requiredDynamicFieldIds) {
|
|
3061
3306
|
const field = dynamicFieldById.get(fieldId);
|
|
3062
|
-
if (field
|
|
3307
|
+
if (field?.variantId !== variant.id) {
|
|
3063
3308
|
continue;
|
|
3064
3309
|
}
|
|
3065
3310
|
if (getNestedValue(params, field.path) !== undefined) {
|
|
@@ -3074,18 +3319,27 @@ async function enforceVariantConstraints(params, fields, dynamicFields, variants
|
|
|
3074
3319
|
}
|
|
3075
3320
|
async function resolveParams(fields, dynamicFields, variants, positionalValues, optionValues, rawArgv, casing, presetPath, shouldPrompt, missingParameterContext, promptStreams) {
|
|
3076
3321
|
const params = {};
|
|
3322
|
+
const errors = [];
|
|
3323
|
+
const dynamicResults = parseDynamicValues(dynamicFields, rawArgv, casing, errors);
|
|
3324
|
+
const positionalTokens = [...positionalValues, ...dynamicResults.positionals];
|
|
3325
|
+
const positionalFields = fields.filter((field) => field.positionalIndex !== undefined);
|
|
3326
|
+
if (!positionalFields.some((field) => field.variadicPosition === true) &&
|
|
3327
|
+
positionalTokens.length > positionalFields.length) {
|
|
3328
|
+
throw new UserError(`Unexpected arguments: ${positionalTokens.slice(positionalFields.length).map((token) => JSON.stringify(token)).join(", ")}.`);
|
|
3329
|
+
}
|
|
3077
3330
|
const presetValues = typeof presetPath === "string" && presetPath.length > 0
|
|
3078
|
-
? await loadPresetValues(fields, presetPath)
|
|
3079
|
-
: {};
|
|
3331
|
+
? await loadPresetValues(fields, dynamicFields, presetPath)
|
|
3332
|
+
: { fields: {}, dynamic: new Map() };
|
|
3080
3333
|
const providedFieldIds = new Set();
|
|
3081
3334
|
const resolvedFieldValues = new Map();
|
|
3082
|
-
const errors = [];
|
|
3083
3335
|
for (const field of fields) {
|
|
3084
3336
|
let value;
|
|
3085
3337
|
let resolvedMissing = false;
|
|
3086
3338
|
let source;
|
|
3087
3339
|
if (field.positionalIndex !== undefined) {
|
|
3088
|
-
const positionalValue =
|
|
3340
|
+
const positionalValue = field.variadicPosition === true
|
|
3341
|
+
? positionalTokens.slice(field.positionalIndex)
|
|
3342
|
+
: positionalTokens[field.positionalIndex];
|
|
3089
3343
|
if (field.schema.kind === "array") {
|
|
3090
3344
|
if (Array.isArray(positionalValue) && positionalValue.length > 0) {
|
|
3091
3345
|
const itemSchema = unwrapOptional(field.schema.item);
|
|
@@ -3096,7 +3350,7 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3096
3350
|
source = "positional";
|
|
3097
3351
|
}
|
|
3098
3352
|
}
|
|
3099
|
-
else if (typeof positionalValue === "string"
|
|
3353
|
+
else if (typeof positionalValue === "string") {
|
|
3100
3354
|
value = parseFieldInputValue(positionalValue, field.schema, field.displayPath);
|
|
3101
3355
|
source = "positional";
|
|
3102
3356
|
}
|
|
@@ -3122,8 +3376,8 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3122
3376
|
source = "option";
|
|
3123
3377
|
}
|
|
3124
3378
|
if (value === undefined &&
|
|
3125
|
-
Object.prototype.hasOwnProperty.call(presetValues, field.optionAttribute)) {
|
|
3126
|
-
value = presetValues[field.optionAttribute];
|
|
3379
|
+
Object.prototype.hasOwnProperty.call(presetValues.fields, field.optionAttribute)) {
|
|
3380
|
+
value = presetValues.fields[field.optionAttribute];
|
|
3127
3381
|
source = "preset";
|
|
3128
3382
|
}
|
|
3129
3383
|
if (source === "option") {
|
|
@@ -3178,8 +3432,8 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3178
3432
|
value = await promptForField(field, promptStreams);
|
|
3179
3433
|
source = "prompt";
|
|
3180
3434
|
}
|
|
3181
|
-
if (value === undefined && field.hasDefault) {
|
|
3182
|
-
value = field.defaultValue;
|
|
3435
|
+
if (value === undefined && field.hasDefault && field.variantId === undefined) {
|
|
3436
|
+
value = cloneDefaultValue(field.defaultValue);
|
|
3183
3437
|
source = "default";
|
|
3184
3438
|
}
|
|
3185
3439
|
if (value === undefined) {
|
|
@@ -3192,6 +3446,14 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3192
3446
|
});
|
|
3193
3447
|
continue;
|
|
3194
3448
|
}
|
|
3449
|
+
if (field.schema.kind === "json") {
|
|
3450
|
+
const validation = validateSchema(field.schema, value);
|
|
3451
|
+
if (!validation.ok) {
|
|
3452
|
+
errors.push(...validation.issues.map((issue) => formatFieldValidationIssue(issue, field.displayPath)));
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
value = validation.value;
|
|
3456
|
+
}
|
|
3195
3457
|
resolvedFieldValues.set(field.id, value);
|
|
3196
3458
|
if (source !== undefined && source !== "default") {
|
|
3197
3459
|
providedFieldIds.add(field.id);
|
|
@@ -3200,16 +3462,15 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3200
3462
|
setNestedValue(params, field.path, value);
|
|
3201
3463
|
}
|
|
3202
3464
|
}
|
|
3203
|
-
const dynamicResults = dynamicFields.length > 0
|
|
3204
|
-
? parseDynamicValues(dynamicFields, rawArgv, casing, errors)
|
|
3205
|
-
: {
|
|
3206
|
-
providedFieldIds: new Set(),
|
|
3207
|
-
values: new Map()
|
|
3208
|
-
};
|
|
3209
3465
|
for (const field of dynamicFields) {
|
|
3210
3466
|
let value = dynamicResults.values.get(field.id);
|
|
3211
|
-
if (
|
|
3212
|
-
value
|
|
3467
|
+
if (presetValues.dynamic.has(field.id)) {
|
|
3468
|
+
if (value === undefined)
|
|
3469
|
+
value = presetValues.dynamic.get(field.id);
|
|
3470
|
+
dynamicResults.providedFieldIds.add(field.id);
|
|
3471
|
+
}
|
|
3472
|
+
if (value === undefined && field.hasDefault && field.variantId === undefined) {
|
|
3473
|
+
value = cloneDefaultValue(field.defaultValue);
|
|
3213
3474
|
}
|
|
3214
3475
|
if (value === undefined) {
|
|
3215
3476
|
if (field.optional || field.variantId !== undefined) {
|
|
@@ -3223,17 +3484,24 @@ async function resolveParams(fields, dynamicFields, variants, positionalValues,
|
|
|
3223
3484
|
}
|
|
3224
3485
|
setNestedValue(params, field.path, value);
|
|
3225
3486
|
}
|
|
3226
|
-
await enforceVariantConstraints(params, fields, dynamicFields, variants, resolvedFieldValues, dynamicResults.providedFieldIds, providedFieldIds, shouldPrompt, errors);
|
|
3487
|
+
await enforceVariantConstraints(params, fields, dynamicFields, variants, resolvedFieldValues, dynamicResults.providedFieldIds, providedFieldIds, shouldPrompt, errors, promptStreams);
|
|
3227
3488
|
throwValidationErrors(errors);
|
|
3228
3489
|
return params;
|
|
3229
3490
|
}
|
|
3230
3491
|
function getResolvedFlags(command) {
|
|
3231
|
-
const flags =
|
|
3492
|
+
const flags = {};
|
|
3493
|
+
const commands = [];
|
|
3494
|
+
for (let current = command; current !== null; current = current.parent) {
|
|
3495
|
+
commands.unshift(current);
|
|
3496
|
+
}
|
|
3497
|
+
for (const current of commands) {
|
|
3498
|
+
Object.assign(flags, current.opts());
|
|
3499
|
+
}
|
|
3232
3500
|
return flags;
|
|
3233
3501
|
}
|
|
3234
3502
|
async function executeCommand(state, rootUsageName, services, requirementOptions, runtimeFetch, humanInLoop, runtimeEnv, runtimeFs, outputEmitter, outputFormats, promptStreams, diagnosticsOptions, onErrorReportContext) {
|
|
3235
3503
|
const logger = createLogger(outputEmitter);
|
|
3236
|
-
const optionValues = state.actionCommand
|
|
3504
|
+
const optionValues = getResolvedFlags(state.actionCommand);
|
|
3237
3505
|
const resolvedFlags = optionValues;
|
|
3238
3506
|
const output = resolveOutput(resolvedFlags);
|
|
3239
3507
|
const primitives = {
|
|
@@ -3280,9 +3548,15 @@ async function executeCommand(state, rootUsageName, services, requirementOptions
|
|
|
3280
3548
|
let runtimeSecrets;
|
|
3281
3549
|
let resolvedParams;
|
|
3282
3550
|
try {
|
|
3283
|
-
await withOutputFormat(output, async () => {
|
|
3551
|
+
await withOutputFormat(toDesignSystemOutput(output), async () => {
|
|
3284
3552
|
await assertCommandRequirements(state.command, preflightContext, runtime.requirementOptions);
|
|
3285
3553
|
const params = await resolveParams(state.fields, state.dynamicFields, state.variants, state.positionalValues, optionValues, state.rawArgv, state.casing, state.presetsEnabled ? resolvedFlags.preset : undefined, shouldPrompt, missingParameterContext, promptStreams);
|
|
3554
|
+
const paramsSchema = state.command.params;
|
|
3555
|
+
if (paramsSchema !== undefined && paramsSchema[nativeJsonSchema] !== undefined) {
|
|
3556
|
+
const validation = validateSchema(paramsSchema, params);
|
|
3557
|
+
if (!validation.ok)
|
|
3558
|
+
throwValidationErrors(validation.issues.map((issue) => formatFieldValidationIssue(issue, "")));
|
|
3559
|
+
}
|
|
3286
3560
|
resolvedParams = params;
|
|
3287
3561
|
runtimeSecrets = runtime.secrets;
|
|
3288
3562
|
const context = {
|
|
@@ -3314,7 +3588,7 @@ async function executeCommand(state, rootUsageName, services, requirementOptions
|
|
|
3314
3588
|
}
|
|
3315
3589
|
});
|
|
3316
3590
|
const interrupt = () => {
|
|
3317
|
-
void stream.cancel(new UserError("Operation cancelled."));
|
|
3591
|
+
void stream.cancel(new UserError("Operation cancelled.")).catch(() => undefined);
|
|
3318
3592
|
};
|
|
3319
3593
|
process.once("SIGINT", interrupt);
|
|
3320
3594
|
try {
|
|
@@ -3343,8 +3617,7 @@ async function executeCommand(state, rootUsageName, services, requirementOptions
|
|
|
3343
3617
|
}
|
|
3344
3618
|
if (state.command.confirm &&
|
|
3345
3619
|
!state.command.humanInLoop &&
|
|
3346
|
-
|
|
3347
|
-
process.stdin.isTTY) {
|
|
3620
|
+
shouldPrompt) {
|
|
3348
3621
|
for (const field of state.fields) {
|
|
3349
3622
|
const value = field.path.reduce((current, segment) => current && typeof current === "object"
|
|
3350
3623
|
? current[segment]
|
|
@@ -3355,10 +3628,10 @@ async function executeCommand(state, rootUsageName, services, requirementOptions
|
|
|
3355
3628
|
}
|
|
3356
3629
|
const proceed = await confirm({
|
|
3357
3630
|
message: "Proceed?",
|
|
3358
|
-
initialValue: true
|
|
3631
|
+
initialValue: true,
|
|
3632
|
+
...promptStreams
|
|
3359
3633
|
});
|
|
3360
3634
|
if (isCancel(proceed)) {
|
|
3361
|
-
cancel("Operation cancelled.");
|
|
3362
3635
|
throw new UserError("Operation cancelled.");
|
|
3363
3636
|
}
|
|
3364
3637
|
if (proceed !== true) {
|
|
@@ -3371,11 +3644,12 @@ async function executeCommand(state, rootUsageName, services, requirementOptions
|
|
|
3371
3644
|
if (output === "rich" && runtime.isFixture) {
|
|
3372
3645
|
writeRichHeader(`${state.command.name} (fixture)`);
|
|
3373
3646
|
}
|
|
3374
|
-
|
|
3375
|
-
|
|
3647
|
+
const pendingApproval = isHumanInLoopPending(result);
|
|
3648
|
+
if (pendingApproval && output === "rich") {
|
|
3649
|
+
renderHumanInLoopPending(result, rootUsageName, outputEmitter);
|
|
3376
3650
|
return;
|
|
3377
3651
|
}
|
|
3378
|
-
const renderStatus = renderCLIResult(state.command, state.commandPath, result, output, primitives, outputFormats, outputEmitter === undefined
|
|
3652
|
+
const renderStatus = renderCLIResult(pendingApproval ? { ...state.command, render: undefined } : state.command, state.commandPath, result, output, primitives, outputFormats, outputEmitter === undefined
|
|
3379
3653
|
? undefined
|
|
3380
3654
|
: (chunk) => outputEmitter(chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk), outputEmitter);
|
|
3381
3655
|
if (renderStatus.mcpError) {
|
|
@@ -3610,7 +3884,7 @@ function renderHttpError(error, options) {
|
|
|
3610
3884
|
}
|
|
3611
3885
|
async function handleRunError(error, options) {
|
|
3612
3886
|
const logger = createLogger(options.outputEmitter);
|
|
3613
|
-
await withOutputFormat(options.output, async () => {
|
|
3887
|
+
await withOutputFormat(toDesignSystemOutput(options.output), async () => {
|
|
3614
3888
|
if (isUserError(error)) {
|
|
3615
3889
|
renderCliErrorPattern(options.userErrorPattern === "definition"
|
|
3616
3890
|
? {
|
|
@@ -3910,26 +4184,30 @@ function getDefaultCommanderCommandName(command) {
|
|
|
3910
4184
|
? candidate._defaultCommandName
|
|
3911
4185
|
: undefined;
|
|
3912
4186
|
}
|
|
3913
|
-
function configureCommanderSuggestionOutput(command) {
|
|
4187
|
+
function configureCommanderSuggestionOutput(command, version) {
|
|
3914
4188
|
command.exitOverride();
|
|
4189
|
+
command.enablePositionalOptions();
|
|
4190
|
+
command.helpOption(false);
|
|
4191
|
+
if (version !== undefined && !command.options.some((option) => option.long === "--version")) {
|
|
4192
|
+
command.version(version, "--version");
|
|
4193
|
+
}
|
|
3915
4194
|
command.configureOutput({
|
|
3916
4195
|
// Every Commander error throws through exitOverride and is rendered once by handleRunError
|
|
3917
4196
|
// in the design-system error pattern; writing it here as well double-reports one mistake.
|
|
3918
4197
|
outputError: () => { }
|
|
3919
4198
|
});
|
|
3920
|
-
command.commands.forEach((child) => configureCommanderSuggestionOutput(child));
|
|
4199
|
+
command.commands.forEach((child) => configureCommanderSuggestionOutput(child, version));
|
|
3921
4200
|
}
|
|
3922
4201
|
export async function runCLI(roots, options = {}) {
|
|
3923
4202
|
enableSourceMaps();
|
|
3924
4203
|
const controls = resolveCLIControls(options.controls);
|
|
3925
|
-
|
|
3926
|
-
? normalizeVerboseAlias([...(options.argv ?? process.argv)])
|
|
3927
|
-
: [...(options.argv ?? process.argv)];
|
|
4204
|
+
let argv = [...(options.argv ?? process.argv)];
|
|
3928
4205
|
const rootUsageName = options.rootUsageName ?? inferProgramName(argv);
|
|
3929
4206
|
let lastActionCommand;
|
|
3930
4207
|
let resolvedCommandPath = "";
|
|
3931
4208
|
let program;
|
|
3932
4209
|
let version;
|
|
4210
|
+
let proxyCleanup;
|
|
3933
4211
|
let userErrorPattern = "definition";
|
|
3934
4212
|
let errorReportContext;
|
|
3935
4213
|
try {
|
|
@@ -3937,7 +4215,9 @@ export async function runCLI(roots, options = {}) {
|
|
|
3937
4215
|
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
3938
4216
|
assertHumanInLoopWired(root, options.humanInLoop);
|
|
3939
4217
|
if (hasMcpProxyConfig(root)) {
|
|
3940
|
-
|
|
4218
|
+
const proxyRuntime = await importOptionalModule(optionalModulePaths.mcpProxy);
|
|
4219
|
+
await proxyRuntime.resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
4220
|
+
proxyCleanup = { dispose: proxyRuntime.disposeMcpProxies, root };
|
|
3941
4221
|
}
|
|
3942
4222
|
const casing = options.casing ?? "kebab";
|
|
3943
4223
|
const services = (options.services ?? {});
|
|
@@ -3954,11 +4234,6 @@ export async function runCLI(roots, options = {}) {
|
|
|
3954
4234
|
env: options.env
|
|
3955
4235
|
};
|
|
3956
4236
|
validateServices(services);
|
|
3957
|
-
if (hasHelpFlag(argv)) {
|
|
3958
|
-
userErrorPattern = "usage";
|
|
3959
|
-
await renderGeneratedHelp(root, argv, { ...options, version });
|
|
3960
|
-
return;
|
|
3961
|
-
}
|
|
3962
4237
|
if (argv.length <= 2 && root.default?.scope.includes("cli") !== true) {
|
|
3963
4238
|
userErrorPattern = "usage";
|
|
3964
4239
|
await renderGeneratedHelp(root, argv, { ...options, version });
|
|
@@ -3971,6 +4246,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
3971
4246
|
program.addHelpCommand(false);
|
|
3972
4247
|
const presetsEnabled = options.presets === true;
|
|
3973
4248
|
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled, version !== undefined, controls);
|
|
4249
|
+
const fieldLoaders = new Map();
|
|
3974
4250
|
addGlobalOptions(program, presetsEnabled, controls);
|
|
3975
4251
|
if (version !== undefined) {
|
|
3976
4252
|
program.version(version, "--version");
|
|
@@ -3996,7 +4272,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
3996
4272
|
.filter((candidate) => isNodeVisibleInScope(candidate, "cli"))
|
|
3997
4273
|
.map((candidate) => candidate.name));
|
|
3998
4274
|
for (const child of root.children) {
|
|
3999
|
-
const command = createNodeCommand(child, casing, globalLongOptionFlags, execute, presetsEnabled, controls);
|
|
4275
|
+
const command = createNodeCommand(child, casing, globalLongOptionFlags, execute, presetsEnabled, controls, fieldLoaders);
|
|
4000
4276
|
if (command === null) {
|
|
4001
4277
|
continue;
|
|
4002
4278
|
}
|
|
@@ -4005,25 +4281,42 @@ export async function runCLI(roots, options = {}) {
|
|
|
4005
4281
|
(command.name() === root.default.name || command.aliases().includes(root.default.name));
|
|
4006
4282
|
addCommanderChild(program, command, isDefaultChild, rootChildNames);
|
|
4007
4283
|
}
|
|
4008
|
-
configureCommanderSuggestionOutput(program);
|
|
4284
|
+
configureCommanderSuggestionOutput(program, version);
|
|
4285
|
+
const prepared = prepareCliArguments(program, argv, fieldLoaders, casing, controls);
|
|
4286
|
+
argv = prepared.argv;
|
|
4287
|
+
if (prepared.helpArgv !== undefined) {
|
|
4288
|
+
userErrorPattern = "usage";
|
|
4289
|
+
await renderGeneratedHelp(root, prepared.helpArgv, { ...options, version });
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
for (const loadFields of fieldLoaders.values()) {
|
|
4293
|
+
loadFields();
|
|
4294
|
+
}
|
|
4009
4295
|
const unknownCommand = findUnknownCommanderCommand(program, argv);
|
|
4010
4296
|
if (unknownCommand !== undefined) {
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4297
|
+
await withOutputFormat(toDesignSystemOutput(resolveOutputFromArgv(argv, controls.outputFormats)), async () => {
|
|
4298
|
+
renderCliErrorPattern({
|
|
4299
|
+
kind: "usage",
|
|
4300
|
+
message: formatUnknownCommandMessage(unknownCommand.input, unknownCommand.currentCommand),
|
|
4301
|
+
rootUsageName,
|
|
4302
|
+
commandPath: unknownCommand.commandPath
|
|
4303
|
+
}, options.outputEmitter);
|
|
4304
|
+
});
|
|
4016
4305
|
return;
|
|
4017
4306
|
}
|
|
4018
4307
|
userErrorPattern = "usage";
|
|
4019
4308
|
await program.parseAsync(argv);
|
|
4020
4309
|
}
|
|
4021
4310
|
catch (error) {
|
|
4311
|
+
const resolvedFlags = lastActionCommand ? getResolvedFlags(lastActionCommand) : undefined;
|
|
4022
4312
|
if (error instanceof ApprovalDeclinedError) {
|
|
4023
|
-
|
|
4313
|
+
await withOutputFormat(toDesignSystemOutput(resolvedFlags !== undefined
|
|
4314
|
+
? resolveOutput(resolvedFlags)
|
|
4315
|
+
: resolveOutputFromArgv(argv, controls.outputFormats)), async () => {
|
|
4316
|
+
renderCliErrorPattern({ kind: "runtime-user", message: error.message }, options.outputEmitter);
|
|
4317
|
+
});
|
|
4024
4318
|
return;
|
|
4025
4319
|
}
|
|
4026
|
-
const resolvedFlags = lastActionCommand ? getResolvedFlags(lastActionCommand) : undefined;
|
|
4027
4320
|
const report = await writeErrorReport({
|
|
4028
4321
|
argv,
|
|
4029
4322
|
command: errorReportContext?.command,
|
|
@@ -4059,4 +4352,15 @@ export async function runCLI(roots, options = {}) {
|
|
|
4059
4352
|
userErrorPattern: errorReportContext?.params === undefined ? userErrorPattern : "runtime-user"
|
|
4060
4353
|
});
|
|
4061
4354
|
}
|
|
4355
|
+
finally {
|
|
4356
|
+
if (proxyCleanup !== undefined) {
|
|
4357
|
+
try {
|
|
4358
|
+
await proxyCleanup.dispose(proxyCleanup.root);
|
|
4359
|
+
}
|
|
4360
|
+
catch (error) {
|
|
4361
|
+
process.exitCode = 1;
|
|
4362
|
+
process.stderr.write(`Failed to close MCP proxy connections: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4062
4366
|
}
|