@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.
- package/dist/es/cli/cli-runner.mjs +113 -9
- package/dist/es/key-alias-utils.mjs +19 -0
- package/dist/es/mcp/base-tools.mjs +45 -2
- package/dist/es/mcp/index.mjs +1 -0
- package/dist/es/mcp/init-arg-utils.mjs +38 -0
- package/dist/es/mcp/tool-generator.mjs +29 -11
- package/dist/lib/cli/cli-runner.js +113 -9
- package/dist/lib/key-alias-utils.js +62 -0
- package/dist/lib/mcp/base-tools.js +45 -2
- package/dist/lib/mcp/index.js +19 -12
- package/dist/lib/mcp/init-arg-utils.js +78 -0
- package/dist/lib/mcp/tool-generator.js +29 -11
- package/dist/types/cli/cli-runner.d.ts +3 -1
- package/dist/types/key-alias-utils.d.ts +9 -0
- package/dist/types/mcp/base-tools.d.ts +65 -5
- package/dist/types/mcp/index.d.ts +1 -0
- package/dist/types/mcp/init-arg-utils.d.ts +13 -0
- package/dist/types/mcp/tool-generator.d.ts +3 -3
- package/dist/types/mcp/types.d.ts +8 -0
- package/package.json +1 -1
- package/src/cli/cli-runner.ts +214 -12
- package/src/key-alias-utils.ts +23 -0
- package/src/mcp/base-tools.ts +164 -9
- package/src/mcp/index.ts +1 -0
- package/src/mcp/init-arg-utils.ts +105 -0
- package/src/mcp/tool-generator.ts +47 -10
- package/src/mcp/types.ts +10 -0
|
@@ -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
|
|
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)
|
|
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
|
-
|
|
40
|
-
} else
|
|
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
|
-
|
|
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
|
|
140
|
-
|
|
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
|
}
|
package/dist/es/mcp/index.mjs
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
82
|
-
} else
|
|
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
|
-
|
|
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
|
|
182
|
-
|
|
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
|
+
});
|