@midscene/shared 1.7.5-beta-20260420032657.0 → 1.7.5-beta-20260420052829.0

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.
@@ -2,6 +2,8 @@ import { existsSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import dotenv from "dotenv";
5
+ import { z } from "zod";
6
+ import { getKeyAliases, isRecord } from "../key-alias-utils.mjs";
5
7
  import { getDebug } from "../logger.mjs";
6
8
  function _define_property(obj, key, value) {
7
9
  if (key in obj) Object.defineProperty(obj, key, {
@@ -26,19 +28,48 @@ function parseValue(raw) {
26
28
  if (/^-?\d+(\.\d+)?$/.test(raw)) return Number(raw);
27
29
  return raw;
28
30
  }
29
- function parseCliArgs(args) {
30
- const result = {};
31
+ function walkCliArgs(args, setArgValue) {
31
32
  for(let i = 0; i < args.length; i++){
32
33
  const arg = args[i];
33
34
  if (!arg.startsWith('--')) continue;
34
35
  const body = arg.slice(2);
35
36
  const eqIdx = body.indexOf('=');
36
- if (eqIdx >= 0) result[body.slice(0, eqIdx)] = parseValue(body.slice(eqIdx + 1));
37
+ if (eqIdx >= 0) setArgValue(body.slice(0, eqIdx), parseValue(body.slice(eqIdx + 1)));
37
38
  else if (args[i + 1] && !args[i + 1].startsWith('--')) {
38
39
  i++;
39
- result[body] = parseValue(args[i]);
40
- } else result[body] = true;
40
+ setArgValue(body, parseValue(args[i]));
41
+ } else setArgValue(body, true);
41
42
  }
43
+ }
44
+ function parseRawCliArgs(args) {
45
+ const result = {};
46
+ walkCliArgs(args, (key, value)=>{
47
+ result[key] = value;
48
+ });
49
+ return result;
50
+ }
51
+ function parseCliArgs(args) {
52
+ const result = {};
53
+ walkCliArgs(args, (key, value)=>{
54
+ if (!key.includes('.')) {
55
+ result[key] = value;
56
+ return;
57
+ }
58
+ const segments = key.split('.');
59
+ let current = result;
60
+ for (const segment of segments.slice(0, -1)){
61
+ const aliases = getKeyAliases(segment);
62
+ const existing = aliases.map((alias)=>current[alias]);
63
+ const nestedRecord = existing.find(isRecord);
64
+ const conflictingScalar = existing.find((entry)=>void 0 !== entry && !isRecord(entry));
65
+ if (void 0 !== conflictingScalar) throw new CLIError(`Conflicting CLI args: "${segment}" is used both as a value and as a namespace`);
66
+ const target = nestedRecord ?? {};
67
+ for (const alias of aliases)current[alias] = target;
68
+ current = target;
69
+ }
70
+ const leafSegment = segments[segments.length - 1];
71
+ for (const alias of getKeyAliases(leafSegment))current[alias] = value;
72
+ });
42
73
  return result;
43
74
  }
44
75
  function outputContentItem(item, isError) {
@@ -66,16 +97,86 @@ function removePrefix(name, prefix) {
66
97
  if (prefix && name.startsWith(prefix)) return name.slice(prefix.length);
67
98
  return name;
68
99
  }
100
+ function formatCliOptionName(name) {
101
+ return `--${name}`;
102
+ }
103
+ function getCliOptionDisplay(key, cliOption) {
104
+ const label = formatCliOptionName(cliOption?.preferredName ?? key);
105
+ const aliases = [
106
+ ...new Set(cliOption?.aliases ?? [])
107
+ ].map((alias)=>formatCliOptionName(alias)).filter((alias)=>alias !== label);
108
+ return {
109
+ label,
110
+ aliases
111
+ };
112
+ }
113
+ function getAcceptedCliOptionNames(key, cliOption) {
114
+ return [
115
+ ...new Set(cliOption ? [
116
+ cliOption.preferredName ?? key,
117
+ ...cliOption.aliases ?? []
118
+ ] : [
119
+ key,
120
+ ...getKeyAliases(key)
121
+ ])
122
+ ];
123
+ }
124
+ function toOptionalCliSchemaField(field) {
125
+ if ('object' == typeof field && null !== field && 'function' == typeof field.optional) return field.optional();
126
+ const description = 'object' == typeof field && null !== field && "description" in field && 'string' == typeof field.description ? field.description : void 0;
127
+ return description ? z.any().describe(description) : z.any();
128
+ }
129
+ function buildCliArgSchema(def) {
130
+ return Object.fromEntries(Object.entries(def.schema).flatMap(([key, zodType])=>getAcceptedCliOptionNames(key, def.cli?.options?.[key]).map((cliKey)=>[
131
+ cliKey,
132
+ toOptionalCliSchemaField(zodType)
133
+ ])));
134
+ }
135
+ function buildDisallowedCliSpellings(def) {
136
+ const disallowedSpellings = new Map();
137
+ for (const [key] of Object.entries(def.schema)){
138
+ const cliOption = def.cli?.options?.[key];
139
+ const preferredLabel = formatCliOptionName(cliOption?.preferredName ?? key);
140
+ const acceptedNames = new Set(getAcceptedCliOptionNames(key, cliOption));
141
+ const knownSpellings = new Set([
142
+ key,
143
+ ...getKeyAliases(key),
144
+ ...cliOption?.preferredName ? getKeyAliases(cliOption.preferredName) : [],
145
+ ...cliOption?.aliases ?? []
146
+ ]);
147
+ for (const spelling of knownSpellings)if (!acceptedNames.has(spelling)) disallowedSpellings.set(spelling, preferredLabel);
148
+ }
149
+ return disallowedSpellings;
150
+ }
151
+ function formatCliValidationError(scriptName, commandName, def, rawArgs) {
152
+ if (0 === Object.keys(def.schema).length) return;
153
+ const cliSchema = z.object(buildCliArgSchema(def)).strict();
154
+ const parsed = cliSchema.safeParse(rawArgs);
155
+ if (parsed.success) return;
156
+ const disallowedSpellings = buildDisallowedCliSpellings(def);
157
+ const unknownKeys = parsed.error.issues.flatMap((issue)=>'unrecognized_keys' === issue.code ? issue.keys : []);
158
+ if (unknownKeys.length > 0) return unknownKeys.map((key)=>{
159
+ const preferredLabel = disallowedSpellings.get(key);
160
+ if (preferredLabel) return `Unsupported option "--${key}" for ${scriptName} ${commandName}. Use "${preferredLabel}" instead.`;
161
+ return `Unknown option "--${key}" for ${scriptName} ${commandName}.`;
162
+ }).join('\n');
163
+ const [issue] = parsed.error.issues;
164
+ const optionName = 'string' == typeof issue?.path[0] ? `--${issue.path[0]}` : 'CLI arguments';
165
+ return `Invalid value for "${optionName}" in ${scriptName} ${commandName}: ${issue?.message ?? parsed.error.message}`;
166
+ }
69
167
  function printCommandHelp(scriptName, cmd) {
70
168
  const { def } = cmd;
71
169
  console.log(`\nUsage: ${scriptName} ${cmd.name} [options]\n`);
72
170
  console.log(def.description);
73
171
  const schemaEntries = Object.entries(def.schema);
74
172
  if (schemaEntries.length > 0) {
173
+ const optionWidth = Math.max(22, ...schemaEntries.map(([key])=>getCliOptionDisplay(key, def.cli?.options?.[key]).label.length));
75
174
  console.log('\nOptions:');
76
175
  for (const [key, zodType] of schemaEntries){
176
+ const { label, aliases } = getCliOptionDisplay(key, def.cli?.options?.[key]);
77
177
  const desc = zodType.description ?? '';
78
- console.log(` --${key.padEnd(20)} ${desc}`);
178
+ const aliasText = aliases.length > 0 ? ` (aliases: ${aliases.join(', ')})` : '';
179
+ console.log(` ${label.padEnd(optionWidth)} ${desc}${aliasText}`);
79
180
  }
80
181
  }
81
182
  }
@@ -136,13 +237,16 @@ async function runToolsCLI(tools, scriptName, options) {
136
237
  printHelp(scriptName, commands, cliVersion);
137
238
  throw new CLIError(`Unknown command: ${commandName}`);
138
239
  }
139
- const parsedArgs = parseCliArgs(restArgs);
140
- debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
141
- if (true === parsedArgs.help) {
240
+ const rawCliArgs = parseRawCliArgs(restArgs);
241
+ if (true === rawCliArgs.help) {
142
242
  debug('showing command help for: %s', match.name);
143
243
  printCommandHelp(scriptName, match);
144
244
  return;
145
245
  }
246
+ const cliValidationError = formatCliValidationError(scriptName, match.name, match.def, rawCliArgs);
247
+ if (cliValidationError) throw new CLIError(cliValidationError);
248
+ const parsedArgs = parseCliArgs(restArgs);
249
+ debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
146
250
  const result = await match.def.handler(parsedArgs);
147
251
  debug('command %s completed, isError: %s', match.name, result.isError ?? false);
148
252
  outputResult(result);
@@ -0,0 +1,19 @@
1
+ function kebabToCamel(str) {
2
+ return str.replace(/-([a-z])/g, (_, letter)=>letter.toUpperCase());
3
+ }
4
+ function camelToKebab(str) {
5
+ return str.replace(/[A-Z]/g, (letter)=>`-${letter.toLowerCase()}`).replace(/^-/, '');
6
+ }
7
+ function getKeyAliases(key) {
8
+ return [
9
+ ...new Set([
10
+ key,
11
+ kebabToCamel(key),
12
+ camelToKebab(key)
13
+ ])
14
+ ];
15
+ }
16
+ function isRecord(value) {
17
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
18
+ }
19
+ export { camelToKebab, getKeyAliases, isRecord, kebabToCamel };
@@ -1,5 +1,7 @@
1
1
  import { parseBase64 } from "@midscene/shared/img";
2
2
  import { getDebug } from "@midscene/shared/logger";
3
+ import { camelToKebab, getKeyAliases } from "../key-alias-utils.mjs";
4
+ import { createNamespacedInitArgSchema, extractNamespacedArgs, sanitizeNamespacedArgs } from "./init-arg-utils.mjs";
3
5
  import { generateCommonTools, generateToolsFromActionSpace } from "./tool-generator.mjs";
4
6
  function _define_property(obj, key, value) {
5
7
  if (key in obj) Object.defineProperty(obj, key, {
@@ -13,6 +15,47 @@ function _define_property(obj, key, value) {
13
15
  }
14
16
  const debug = getDebug('mcp:base-tools');
15
17
  class BaseMidsceneTools {
18
+ getInitArgKeys() {
19
+ return this.initArgSpec ? Object.keys(this.initArgSpec.shape) : [];
20
+ }
21
+ extractAgentInitParam(args) {
22
+ if (!this.initArgSpec) return;
23
+ const extracted = extractNamespacedArgs(args, this.initArgSpec.namespace, this.getInitArgKeys());
24
+ if (this.initArgSpec.adapt) return this.initArgSpec.adapt(extracted);
25
+ return extracted;
26
+ }
27
+ sanitizeToolArgs(args) {
28
+ if (!this.initArgSpec) return args;
29
+ return sanitizeNamespacedArgs(args, this.initArgSpec.namespace, this.getInitArgKeys());
30
+ }
31
+ getAgentInitArgSchema() {
32
+ if (!this.initArgSpec) return {};
33
+ return createNamespacedInitArgSchema(this.initArgSpec.namespace, this.initArgSpec.shape);
34
+ }
35
+ getAgentInitArgCliMetadata() {
36
+ if (!this.initArgSpec?.cli) return;
37
+ const options = Object.fromEntries(this.getInitArgKeys().map((key)=>{
38
+ const canonicalKey = `${this.initArgSpec.namespace}.${key}`;
39
+ const preferredName = this.initArgSpec.cli?.preferredNames?.[key] ?? (this.initArgSpec.cli?.preferBareKeys ? camelToKebab(key) : canonicalKey);
40
+ const acceptedNames = new Set([
41
+ preferredName,
42
+ ...this.initArgSpec.cli?.preferBareKeys ? getKeyAliases(key) : getKeyAliases(canonicalKey)
43
+ ]);
44
+ acceptedNames.delete(preferredName);
45
+ return [
46
+ canonicalKey,
47
+ {
48
+ preferredName,
49
+ aliases: [
50
+ ...acceptedNames
51
+ ]
52
+ }
53
+ ];
54
+ }));
55
+ return {
56
+ options
57
+ };
58
+ }
16
59
  preparePlatformTools() {
17
60
  return [];
18
61
  }
@@ -30,8 +73,8 @@ class BaseMidsceneTools {
30
73
  await tempDevice.destroy?.();
31
74
  debug('Action space from temporary device:', actionSpace.map((a)=>a.name).join(', '));
32
75
  }
33
- const actionTools = generateToolsFromActionSpace(actionSpace, ()=>this.ensureAgent());
34
- const commonTools = generateCommonTools(()=>this.ensureAgent());
76
+ const actionTools = generateToolsFromActionSpace(actionSpace, (args = {})=>this.ensureAgent(this.extractAgentInitParam(args)), (args = {})=>this.sanitizeToolArgs(args), this.getAgentInitArgSchema(), this.getAgentInitArgCliMetadata());
77
+ const commonTools = generateCommonTools((args = {})=>this.ensureAgent(this.extractAgentInitParam(args)), this.getAgentInitArgSchema(), this.getAgentInitArgCliMetadata());
35
78
  this.toolDefinitions.push(...actionTools, ...commonTools);
36
79
  debug('Total tools prepared:', this.toolDefinitions.length);
37
80
  }
@@ -1,5 +1,6 @@
1
1
  export * from "./base-server.mjs";
2
2
  export * from "./base-tools.mjs";
3
+ export * from "./init-arg-utils.mjs";
3
4
  export * from "./error-formatter.mjs";
4
5
  export * from "./tool-generator.mjs";
5
6
  export * from "./types.mjs";
@@ -0,0 +1,38 @@
1
+ import { getKeyAliases, isRecord } from "../key-alias-utils.mjs";
2
+ function readAliasedValue(args, key) {
3
+ for (const alias of getKeyAliases(key))if (alias in args) return args[alias];
4
+ }
5
+ function readNamespacedArg(args, namespace, key) {
6
+ const namespacedArgs = readAliasedValue(args, namespace);
7
+ if (isRecord(namespacedArgs)) {
8
+ const nestedValue = readAliasedValue(namespacedArgs, key);
9
+ if (void 0 !== nestedValue) return nestedValue;
10
+ }
11
+ const dottedValue = readAliasedValue(args, `${namespace}.${key}`);
12
+ if (void 0 !== dottedValue) return dottedValue;
13
+ const directValue = readAliasedValue(args, key);
14
+ if (void 0 !== directValue) return directValue;
15
+ }
16
+ function extractNamespacedArgs(args, namespace, keys) {
17
+ const extracted = {};
18
+ for (const key of keys){
19
+ const value = readNamespacedArg(args, namespace, key);
20
+ if (void 0 !== value) extracted[key] = value;
21
+ }
22
+ return Object.keys(extracted).length > 0 ? extracted : void 0;
23
+ }
24
+ function sanitizeNamespacedArgs(args, namespace, keys) {
25
+ const excludedKeys = new Set(getKeyAliases(namespace));
26
+ for (const key of keys){
27
+ for (const alias of getKeyAliases(key))excludedKeys.add(alias);
28
+ for (const alias of getKeyAliases(`${namespace}.${key}`))excludedKeys.add(alias);
29
+ }
30
+ return Object.fromEntries(Object.entries(args).filter(([key])=>!excludedKeys.has(key)));
31
+ }
32
+ function createNamespacedInitArgSchema(namespace, shape) {
33
+ return Object.fromEntries(Object.entries(shape).map(([key, value])=>[
34
+ `${namespace}.${key}`,
35
+ value
36
+ ]));
37
+ }
38
+ export { createNamespacedInitArgSchema, extractNamespacedArgs, sanitizeNamespacedArgs };
@@ -277,17 +277,30 @@ async function captureFailureResult(agent, actionName, errorMessage) {
277
277
  };
278
278
  }
279
279
  }
280
- function generateToolsFromActionSpace(actionSpace, getAgent) {
280
+ function mergeToolCliMetadata(base, extra) {
281
+ const options = {
282
+ ...base?.options ?? {},
283
+ ...extra?.options ?? {}
284
+ };
285
+ return Object.keys(options).length > 0 ? {
286
+ options
287
+ } : void 0;
288
+ }
289
+ function generateToolsFromActionSpace(actionSpace, getAgent, sanitizeArgs = (args)=>args, initArgSchema = {}, initArgCliMetadata) {
281
290
  return actionSpace.map((action)=>{
282
- const schema = extractActionSchema(action.paramSchema);
291
+ const schema = {
292
+ ...extractActionSchema(action.paramSchema),
293
+ ...initArgSchema
294
+ };
283
295
  return {
284
296
  name: action.name,
285
297
  description: describeActionForMCP(action),
286
298
  schema,
299
+ cli: initArgCliMetadata,
287
300
  handler: async (args)=>{
288
301
  try {
289
- const agent = await getAgent();
290
- const normalizedArgs = normalizeActionArgs(args, action.paramSchema);
302
+ const agent = await getAgent(args);
303
+ const normalizedArgs = normalizeActionArgs(sanitizeArgs(args), action.paramSchema);
291
304
  let actionResult;
292
305
  try {
293
306
  actionResult = await executeAction(agent, action.name, normalizedArgs);
@@ -306,15 +319,18 @@ function generateToolsFromActionSpace(actionSpace, getAgent) {
306
319
  };
307
320
  });
308
321
  }
309
- function generateCommonTools(getAgent) {
322
+ function generateCommonTools(getAgent, initArgSchema = {}, initArgCliMetadata) {
310
323
  return [
311
324
  {
312
325
  name: 'take_screenshot',
313
326
  description: 'Capture screenshot of current page/screen',
314
- schema: {},
315
- handler: async ()=>{
327
+ schema: {
328
+ ...initArgSchema
329
+ },
330
+ cli: initArgCliMetadata,
331
+ handler: async (args = {})=>{
316
332
  try {
317
- const agent = await getAgent();
333
+ const agent = await getAgent(args);
318
334
  const screenshot = await agent.page?.screenshotBase64();
319
335
  if (!screenshot) return createErrorResult('Screenshot not available');
320
336
  const { mimeType, body } = parseBase64(screenshot);
@@ -338,12 +354,14 @@ function generateCommonTools(getAgent) {
338
354
  name: 'act',
339
355
  description: 'Execute a natural language action. The AI will plan and perform multi-step operations in a single invocation, useful for transient UI interactions (e.g., Spotlight, dropdown menus) that disappear between separate commands.',
340
356
  schema: {
341
- prompt: z.string().describe('Natural language description of the action to perform, e.g. "press Command+Space, type Safari, press Enter"')
357
+ prompt: z.string().describe('Natural language description of the action to perform, e.g. "press Command+Space, type Safari, press Enter"'),
358
+ ...initArgSchema
342
359
  },
343
- handler: async (args)=>{
360
+ cli: mergeToolCliMetadata(void 0, initArgCliMetadata),
361
+ handler: async (args = {})=>{
344
362
  const prompt = args.prompt;
345
363
  try {
346
- const agent = await getAgent();
364
+ const agent = await getAgent(args);
347
365
  if (!agent.aiAction) return createErrorResult('act is not supported by this agent');
348
366
  const result = await agent.aiAction(prompt, {
349
367
  deepThink: false
@@ -44,6 +44,8 @@ const external_node_os_namespaceObject = require("node:os");
44
44
  const external_node_path_namespaceObject = require("node:path");
45
45
  const external_dotenv_namespaceObject = require("dotenv");
46
46
  var external_dotenv_default = /*#__PURE__*/ __webpack_require__.n(external_dotenv_namespaceObject);
47
+ const external_zod_namespaceObject = require("zod");
48
+ const external_key_alias_utils_js_namespaceObject = require("../key-alias-utils.js");
47
49
  const external_logger_js_namespaceObject = require("../logger.js");
48
50
  function _define_property(obj, key, value) {
49
51
  if (key in obj) Object.defineProperty(obj, key, {
@@ -68,19 +70,48 @@ function parseValue(raw) {
68
70
  if (/^-?\d+(\.\d+)?$/.test(raw)) return Number(raw);
69
71
  return raw;
70
72
  }
71
- function parseCliArgs(args) {
72
- const result = {};
73
+ function walkCliArgs(args, setArgValue) {
73
74
  for(let i = 0; i < args.length; i++){
74
75
  const arg = args[i];
75
76
  if (!arg.startsWith('--')) continue;
76
77
  const body = arg.slice(2);
77
78
  const eqIdx = body.indexOf('=');
78
- if (eqIdx >= 0) result[body.slice(0, eqIdx)] = parseValue(body.slice(eqIdx + 1));
79
+ if (eqIdx >= 0) setArgValue(body.slice(0, eqIdx), parseValue(body.slice(eqIdx + 1)));
79
80
  else if (args[i + 1] && !args[i + 1].startsWith('--')) {
80
81
  i++;
81
- result[body] = parseValue(args[i]);
82
- } else result[body] = true;
82
+ setArgValue(body, parseValue(args[i]));
83
+ } else setArgValue(body, true);
83
84
  }
85
+ }
86
+ function parseRawCliArgs(args) {
87
+ const result = {};
88
+ walkCliArgs(args, (key, value)=>{
89
+ result[key] = value;
90
+ });
91
+ return result;
92
+ }
93
+ function parseCliArgs(args) {
94
+ const result = {};
95
+ walkCliArgs(args, (key, value)=>{
96
+ if (!key.includes('.')) {
97
+ result[key] = value;
98
+ return;
99
+ }
100
+ const segments = key.split('.');
101
+ let current = result;
102
+ for (const segment of segments.slice(0, -1)){
103
+ const aliases = (0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(segment);
104
+ const existing = aliases.map((alias)=>current[alias]);
105
+ const nestedRecord = existing.find(external_key_alias_utils_js_namespaceObject.isRecord);
106
+ const conflictingScalar = existing.find((entry)=>void 0 !== entry && !(0, external_key_alias_utils_js_namespaceObject.isRecord)(entry));
107
+ if (void 0 !== conflictingScalar) throw new CLIError(`Conflicting CLI args: "${segment}" is used both as a value and as a namespace`);
108
+ const target = nestedRecord ?? {};
109
+ for (const alias of aliases)current[alias] = target;
110
+ current = target;
111
+ }
112
+ const leafSegment = segments[segments.length - 1];
113
+ for (const alias of (0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(leafSegment))current[alias] = value;
114
+ });
84
115
  return result;
85
116
  }
86
117
  function outputContentItem(item, isError) {
@@ -108,16 +139,86 @@ function removePrefix(name, prefix) {
108
139
  if (prefix && name.startsWith(prefix)) return name.slice(prefix.length);
109
140
  return name;
110
141
  }
142
+ function formatCliOptionName(name) {
143
+ return `--${name}`;
144
+ }
145
+ function getCliOptionDisplay(key, cliOption) {
146
+ const label = formatCliOptionName(cliOption?.preferredName ?? key);
147
+ const aliases = [
148
+ ...new Set(cliOption?.aliases ?? [])
149
+ ].map((alias)=>formatCliOptionName(alias)).filter((alias)=>alias !== label);
150
+ return {
151
+ label,
152
+ aliases
153
+ };
154
+ }
155
+ function getAcceptedCliOptionNames(key, cliOption) {
156
+ return [
157
+ ...new Set(cliOption ? [
158
+ cliOption.preferredName ?? key,
159
+ ...cliOption.aliases ?? []
160
+ ] : [
161
+ key,
162
+ ...(0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(key)
163
+ ])
164
+ ];
165
+ }
166
+ function toOptionalCliSchemaField(field) {
167
+ if ('object' == typeof field && null !== field && 'function' == typeof field.optional) return field.optional();
168
+ const description = 'object' == typeof field && null !== field && "description" in field && 'string' == typeof field.description ? field.description : void 0;
169
+ return description ? external_zod_namespaceObject.z.any().describe(description) : external_zod_namespaceObject.z.any();
170
+ }
171
+ function buildCliArgSchema(def) {
172
+ return Object.fromEntries(Object.entries(def.schema).flatMap(([key, zodType])=>getAcceptedCliOptionNames(key, def.cli?.options?.[key]).map((cliKey)=>[
173
+ cliKey,
174
+ toOptionalCliSchemaField(zodType)
175
+ ])));
176
+ }
177
+ function buildDisallowedCliSpellings(def) {
178
+ const disallowedSpellings = new Map();
179
+ for (const [key] of Object.entries(def.schema)){
180
+ const cliOption = def.cli?.options?.[key];
181
+ const preferredLabel = formatCliOptionName(cliOption?.preferredName ?? key);
182
+ const acceptedNames = new Set(getAcceptedCliOptionNames(key, cliOption));
183
+ const knownSpellings = new Set([
184
+ key,
185
+ ...(0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(key),
186
+ ...cliOption?.preferredName ? (0, external_key_alias_utils_js_namespaceObject.getKeyAliases)(cliOption.preferredName) : [],
187
+ ...cliOption?.aliases ?? []
188
+ ]);
189
+ for (const spelling of knownSpellings)if (!acceptedNames.has(spelling)) disallowedSpellings.set(spelling, preferredLabel);
190
+ }
191
+ return disallowedSpellings;
192
+ }
193
+ function formatCliValidationError(scriptName, commandName, def, rawArgs) {
194
+ if (0 === Object.keys(def.schema).length) return;
195
+ const cliSchema = external_zod_namespaceObject.z.object(buildCliArgSchema(def)).strict();
196
+ const parsed = cliSchema.safeParse(rawArgs);
197
+ if (parsed.success) return;
198
+ const disallowedSpellings = buildDisallowedCliSpellings(def);
199
+ const unknownKeys = parsed.error.issues.flatMap((issue)=>'unrecognized_keys' === issue.code ? issue.keys : []);
200
+ if (unknownKeys.length > 0) return unknownKeys.map((key)=>{
201
+ const preferredLabel = disallowedSpellings.get(key);
202
+ if (preferredLabel) return `Unsupported option "--${key}" for ${scriptName} ${commandName}. Use "${preferredLabel}" instead.`;
203
+ return `Unknown option "--${key}" for ${scriptName} ${commandName}.`;
204
+ }).join('\n');
205
+ const [issue] = parsed.error.issues;
206
+ const optionName = 'string' == typeof issue?.path[0] ? `--${issue.path[0]}` : 'CLI arguments';
207
+ return `Invalid value for "${optionName}" in ${scriptName} ${commandName}: ${issue?.message ?? parsed.error.message}`;
208
+ }
111
209
  function printCommandHelp(scriptName, cmd) {
112
210
  const { def } = cmd;
113
211
  console.log(`\nUsage: ${scriptName} ${cmd.name} [options]\n`);
114
212
  console.log(def.description);
115
213
  const schemaEntries = Object.entries(def.schema);
116
214
  if (schemaEntries.length > 0) {
215
+ const optionWidth = Math.max(22, ...schemaEntries.map(([key])=>getCliOptionDisplay(key, def.cli?.options?.[key]).label.length));
117
216
  console.log('\nOptions:');
118
217
  for (const [key, zodType] of schemaEntries){
218
+ const { label, aliases } = getCliOptionDisplay(key, def.cli?.options?.[key]);
119
219
  const desc = zodType.description ?? '';
120
- console.log(` --${key.padEnd(20)} ${desc}`);
220
+ const aliasText = aliases.length > 0 ? ` (aliases: ${aliases.join(', ')})` : '';
221
+ console.log(` ${label.padEnd(optionWidth)} ${desc}${aliasText}`);
121
222
  }
122
223
  }
123
224
  }
@@ -178,13 +279,16 @@ async function runToolsCLI(tools, scriptName, options) {
178
279
  printHelp(scriptName, commands, cliVersion);
179
280
  throw new CLIError(`Unknown command: ${commandName}`);
180
281
  }
181
- const parsedArgs = parseCliArgs(restArgs);
182
- debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
183
- if (true === parsedArgs.help) {
282
+ const rawCliArgs = parseRawCliArgs(restArgs);
283
+ if (true === rawCliArgs.help) {
184
284
  debug('showing command help for: %s', match.name);
185
285
  printCommandHelp(scriptName, match);
186
286
  return;
187
287
  }
288
+ const cliValidationError = formatCliValidationError(scriptName, match.name, match.def, rawCliArgs);
289
+ if (cliValidationError) throw new CLIError(cliValidationError);
290
+ const parsedArgs = parseCliArgs(restArgs);
291
+ debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
188
292
  const result = await match.def.handler(parsedArgs);
189
293
  debug('command %s completed, isError: %s', match.name, result.isError ?? false);
190
294
  outputResult(result);
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ camelToKebab: ()=>camelToKebab,
28
+ getKeyAliases: ()=>getKeyAliases,
29
+ isRecord: ()=>isRecord,
30
+ kebabToCamel: ()=>kebabToCamel
31
+ });
32
+ function kebabToCamel(str) {
33
+ return str.replace(/-([a-z])/g, (_, letter)=>letter.toUpperCase());
34
+ }
35
+ function camelToKebab(str) {
36
+ return str.replace(/[A-Z]/g, (letter)=>`-${letter.toLowerCase()}`).replace(/^-/, '');
37
+ }
38
+ function getKeyAliases(key) {
39
+ return [
40
+ ...new Set([
41
+ key,
42
+ kebabToCamel(key),
43
+ camelToKebab(key)
44
+ ])
45
+ ];
46
+ }
47
+ function isRecord(value) {
48
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
49
+ }
50
+ exports.camelToKebab = __webpack_exports__.camelToKebab;
51
+ exports.getKeyAliases = __webpack_exports__.getKeyAliases;
52
+ exports.isRecord = __webpack_exports__.isRecord;
53
+ exports.kebabToCamel = __webpack_exports__.kebabToCamel;
54
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
55
+ "camelToKebab",
56
+ "getKeyAliases",
57
+ "isRecord",
58
+ "kebabToCamel"
59
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
60
+ Object.defineProperty(exports, '__esModule', {
61
+ value: true
62
+ });