@aiscene/shared 1.6.0 → 1.7.2
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 +13 -1
- package/dist/es/constants/example-code.mjs +25 -21
- package/dist/es/env/parse-model-config.mjs +11 -1
- package/dist/es/env/types.mjs +1 -0
- package/dist/es/extractor/dom-util.mjs +9 -5
- package/dist/es/extractor/locator.mjs +6 -6
- package/dist/es/extractor/util.mjs +6 -6
- package/dist/es/img/canvas-fallback.mjs +1 -1
- package/dist/es/index.mjs +1 -3
- package/dist/es/logger.mjs +1 -7
- package/dist/es/mcp/base-server.mjs +1 -1
- package/dist/es/mcp/base-tools.mjs +2 -2
- package/dist/es/mcp/tool-generator.mjs +131 -57
- package/dist/es/node/fs.mjs +1 -1
- package/dist/es/us-keyboard-layout.mjs +1 -1
- package/dist/es/utils.mjs +4 -9
- package/dist/lib/baseDB.js +1 -1
- package/dist/lib/cli/cli-runner.js +14 -2
- package/dist/lib/cli/index.js +1 -1
- package/dist/lib/common.js +1 -1
- package/dist/lib/constants/example-code.js +26 -22
- package/dist/lib/constants/index.js +1 -1
- package/dist/lib/env/basic.js +1 -1
- package/dist/lib/env/constants.js +1 -1
- package/dist/lib/env/global-config-manager.js +1 -1
- package/dist/lib/env/helper.js +1 -1
- package/dist/lib/env/index.js +1 -1
- package/dist/lib/env/init-debug.js +1 -1
- package/dist/lib/env/model-config-manager.js +1 -1
- package/dist/lib/env/parse-model-config.js +12 -2
- package/dist/lib/env/types.js +2 -1
- package/dist/lib/env/utils.js +1 -1
- package/dist/lib/extractor/constants.js +1 -1
- package/dist/lib/extractor/dom-util.js +10 -6
- package/dist/lib/extractor/index.js +1 -1
- package/dist/lib/extractor/locator.js +7 -7
- package/dist/lib/extractor/tree.js +1 -1
- package/dist/lib/extractor/util.js +7 -7
- package/dist/lib/extractor/web-extractor.js +1 -1
- package/dist/lib/img/box-select.js +1 -1
- package/dist/lib/img/canvas-fallback.js +2 -2
- package/dist/lib/img/get-photon.js +1 -1
- package/dist/lib/img/get-sharp.js +1 -1
- package/dist/lib/img/index.js +1 -1
- package/dist/lib/img/info.js +1 -1
- package/dist/lib/img/transform.js +1 -1
- package/dist/lib/index.js +3 -14
- package/dist/lib/logger.js +1 -10
- package/dist/lib/mcp/base-server.js +3 -3
- package/dist/lib/mcp/base-tools.js +5 -5
- package/dist/lib/mcp/chrome-path.js +1 -1
- package/dist/lib/mcp/index.js +1 -1
- package/dist/lib/mcp/inject-report-html-plugin.js +1 -1
- package/dist/lib/mcp/launcher-helper.js +1 -1
- package/dist/lib/mcp/tool-generator.js +134 -60
- package/dist/lib/mcp/types.js +1 -1
- package/dist/lib/node/fs.js +2 -2
- package/dist/lib/node/index.js +1 -1
- package/dist/lib/node/port.js +1 -1
- package/dist/lib/polyfills/async-hooks.js +1 -1
- package/dist/lib/polyfills/index.js +1 -1
- package/dist/lib/types/index.js +1 -1
- package/dist/lib/us-keyboard-layout.js +2 -2
- package/dist/lib/utils.js +4 -12
- package/dist/lib/zod-schema-utils.js +1 -1
- package/dist/types/cli/cli-runner.d.ts +8 -0
- package/dist/types/cli/index.d.ts +1 -1
- package/dist/types/constants/example-code.d.ts +1 -1
- package/dist/types/env/types.d.ts +1 -1
- package/dist/types/extractor/dom-util.d.ts +5 -4
- package/dist/types/index.d.ts +0 -2
- package/dist/types/logger.d.ts +0 -1
- package/dist/types/mcp/types.d.ts +1 -0
- package/dist/types/utils.d.ts +0 -3
- package/package.json +2 -2
- package/src/cli/cli-runner.ts +26 -1
- package/src/cli/index.ts +1 -1
- package/src/constants/example-code.ts +25 -21
- package/src/env/parse-model-config.ts +21 -1
- package/src/env/types.ts +2 -0
- package/src/extractor/dom-util.ts +10 -5
- package/src/img/canvas-fallback.ts +1 -1
- package/src/index.ts +0 -7
- package/src/logger.ts +0 -11
- package/src/mcp/base-server.ts +1 -1
- package/src/mcp/base-tools.ts +2 -2
- package/src/mcp/tool-generator.ts +218 -67
- package/src/mcp/types.ts +4 -0
- package/src/utils.ts +0 -17
- package/dist/es/build/copy-static.mjs +0 -31
- package/dist/es/build/rspack-config.mjs +0 -4
- package/dist/lib/build/copy-static.js +0 -79
- package/dist/lib/build/rspack-config.js +0 -38
- package/dist/types/build/copy-static.d.ts +0 -31
- package/dist/types/build/rspack-config.d.ts +0 -8
- package/src/build/copy-static.ts +0 -68
- package/src/build/rspack-config.ts +0 -12
|
@@ -89,7 +89,7 @@ function printHelp(scriptName, commands, version) {
|
|
|
89
89
|
}
|
|
90
90
|
console.log(`\nUsage: ${scriptName} <command> [options]\n`);
|
|
91
91
|
console.log('Commands:');
|
|
92
|
-
for (const { name, def } of commands)console.log(` ${name.padEnd(30)} ${def.description}`);
|
|
92
|
+
for (const { name, def } of commands.filter((command)=>!command.hidden))console.log(` ${name.padEnd(30)} ${def.description}`);
|
|
93
93
|
console.log(` ${'version'.padEnd(30)} Show CLI version`);
|
|
94
94
|
console.log(`\nRun "${scriptName} <command> --help" for more info.`);
|
|
95
95
|
}
|
|
@@ -105,6 +105,18 @@ async function runToolsCLI(tools, scriptName, options) {
|
|
|
105
105
|
name: removePrefix(def.name, options?.stripPrefix).toLowerCase(),
|
|
106
106
|
def
|
|
107
107
|
}));
|
|
108
|
+
if (options?.extraCommands?.length) commands.push(...options.extraCommands.flatMap((cmd)=>[
|
|
109
|
+
{
|
|
110
|
+
name: cmd.name.toLowerCase(),
|
|
111
|
+
def: cmd.def,
|
|
112
|
+
hidden: cmd.hidden
|
|
113
|
+
},
|
|
114
|
+
...(cmd.aliases ?? []).map((alias)=>({
|
|
115
|
+
name: alias.toLowerCase(),
|
|
116
|
+
def: cmd.def,
|
|
117
|
+
hidden: true
|
|
118
|
+
}))
|
|
119
|
+
]));
|
|
108
120
|
const cliVersion = options?.version;
|
|
109
121
|
const [commandName, ...restArgs] = rawArgs;
|
|
110
122
|
if (!commandName || '--help' === commandName || '-h' === commandName) {
|
|
@@ -80,6 +80,23 @@ test('ai shop', async ({
|
|
|
80
80
|
});
|
|
81
81
|
`;
|
|
82
82
|
const YAML_EXAMPLE_CODE = `
|
|
83
|
+
CRITICAL - YAML Indentation Rules:
|
|
84
|
+
For actions with additional parameters (aiScroll, aiInput, aiKeyboardPress), the parameters must be SIBLING keys at the SAME indentation level as the action key, NOT nested children indented further.
|
|
85
|
+
CORRECT (parameters align with the action key):
|
|
86
|
+
- aiScroll:
|
|
87
|
+
direction: 'down'
|
|
88
|
+
scrollType: 'singleAction'
|
|
89
|
+
distance: 500
|
|
90
|
+
locate: "main content area"
|
|
91
|
+
- aiInput: 'text value'
|
|
92
|
+
locate: 'input field description'
|
|
93
|
+
WRONG (parameters are indented further than the action key, DO NOT do this):
|
|
94
|
+
- aiScroll:
|
|
95
|
+
direction: 'down'
|
|
96
|
+
scrollType: 'singleAction'
|
|
97
|
+
- aiInput: 'text value'
|
|
98
|
+
locate: 'input field description'
|
|
99
|
+
|
|
83
100
|
1. Format:
|
|
84
101
|
|
|
85
102
|
web:
|
|
@@ -91,13 +108,18 @@ tasks:
|
|
|
91
108
|
- name: "descriptive task name"
|
|
92
109
|
flow:
|
|
93
110
|
- aiTap: "element description"
|
|
111
|
+
xpath: '/html/body/div[1]/button[1]'
|
|
94
112
|
- aiInput: 'text value'
|
|
95
113
|
locate: 'input field description'
|
|
114
|
+
xpath: '/html/body/div[1]/input[1]'
|
|
96
115
|
- aiScroll:
|
|
97
|
-
direction: down
|
|
98
|
-
scrollType:
|
|
116
|
+
direction: 'down'
|
|
117
|
+
scrollType: 'singleAction'
|
|
118
|
+
distance: 500
|
|
119
|
+
locate: "scrollable area description"
|
|
120
|
+
xpath: '/html/body/div[1]/main[1]'
|
|
99
121
|
- aiAssert: "expected state"
|
|
100
|
-
- sleep:
|
|
122
|
+
- sleep: 1000
|
|
101
123
|
|
|
102
124
|
2. Action Types:
|
|
103
125
|
- aiTap: for clicks (natural language targeting)
|
|
@@ -106,24 +128,6 @@ tasks:
|
|
|
106
128
|
- aiAssert: for validations
|
|
107
129
|
- sleep: for delays (milliseconds)
|
|
108
130
|
|
|
109
|
-
3. Best Practices:
|
|
110
|
-
- Group related actions into logical tasks
|
|
111
|
-
- Use natural language descriptions
|
|
112
|
-
- Add deepLocate: true for complex interactions
|
|
113
|
-
- Keep task names concise but descriptive
|
|
114
|
-
|
|
115
|
-
4. CRITICAL - YAML Indentation Rules:
|
|
116
|
-
- For actions with additional parameters (aiScroll, aiInput, aiKeyboardPress), the parameters must be SIBLING keys, NOT nested children
|
|
117
|
-
- Parameters like direction, scrollType, locate must align with the action key, not indented further
|
|
118
|
-
- CORRECT indentation example:
|
|
119
|
-
- aiScroll:
|
|
120
|
-
direction: down
|
|
121
|
-
scrollType: singleAction
|
|
122
|
-
- WRONG indentation (DO NOT do this):
|
|
123
|
-
- aiScroll:
|
|
124
|
-
direction: down
|
|
125
|
-
scrollType: singleAction
|
|
126
|
-
|
|
127
131
|
|
|
128
132
|
|
|
129
133
|
YAML type
|
|
@@ -43,6 +43,16 @@ const getModelDescription = (modelFamily, uiTarsModelVersion)=>{
|
|
|
43
43
|
if (modelFamily) return `${modelFamily} mode`;
|
|
44
44
|
return '';
|
|
45
45
|
};
|
|
46
|
+
const normalizeOpenaiExtraConfig = (config)=>{
|
|
47
|
+
if (!config || 'object' != typeof config || Array.isArray(config)) return;
|
|
48
|
+
const { defaultHeaders, extra_headers, extraHeaders, ...rest } = config;
|
|
49
|
+
const headers = defaultHeaders ?? extra_headers ?? extraHeaders;
|
|
50
|
+
if (void 0 !== headers) return {
|
|
51
|
+
...rest,
|
|
52
|
+
defaultHeaders: headers
|
|
53
|
+
};
|
|
54
|
+
return rest;
|
|
55
|
+
};
|
|
46
56
|
const parseOpenaiSdkConfig = ({ keys, provider, useLegacyLogic = false })=>{
|
|
47
57
|
initDebugConfig();
|
|
48
58
|
const debugLog = getDebug('ai:config');
|
|
@@ -73,7 +83,7 @@ const parseOpenaiSdkConfig = ({ keys, provider, useLegacyLogic = false })=>{
|
|
|
73
83
|
httpProxy,
|
|
74
84
|
openaiBaseURL,
|
|
75
85
|
openaiApiKey,
|
|
76
|
-
openaiExtraConfig,
|
|
86
|
+
openaiExtraConfig: normalizeOpenaiExtraConfig(openaiExtraConfig),
|
|
77
87
|
extraBody,
|
|
78
88
|
modelFamily,
|
|
79
89
|
uiTarsModelVersion,
|
package/dist/es/env/types.mjs
CHANGED
|
@@ -81,12 +81,16 @@ function generateElementByPoint(center, description, edgeSize = 8) {
|
|
|
81
81
|
description: description || ''
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
function generateElementByRect(sourceRect, description,
|
|
84
|
+
function generateElementByRect(sourceRect, description, _edgeSize = 8) {
|
|
85
85
|
const centerX = sourceRect.left + Math.floor((sourceRect.width - 1) / 2);
|
|
86
86
|
const centerY = sourceRect.top + Math.floor((sourceRect.height - 1) / 2);
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
return {
|
|
88
|
+
rect: sourceRect,
|
|
89
|
+
center: [
|
|
90
|
+
centerX,
|
|
91
|
+
centerY
|
|
92
|
+
],
|
|
93
|
+
description: description || ''
|
|
94
|
+
};
|
|
91
95
|
}
|
|
92
96
|
export { generateElementByPoint, generateElementByRect, isAElement, isButtonElement, isContainerElement, isFormElement, isImgElement, isNotContainerElement, isSvgElement, isTextElement };
|
|
@@ -143,8 +143,8 @@ function getXpathsById(id) {
|
|
|
143
143
|
];
|
|
144
144
|
}
|
|
145
145
|
function getXpathsByPoint(point, isOrderSensitive) {
|
|
146
|
-
let currentWindow =
|
|
147
|
-
let currentDocument =
|
|
146
|
+
let currentWindow = "u" > typeof window ? window : void 0;
|
|
147
|
+
let currentDocument = "u" > typeof document ? document : void 0;
|
|
148
148
|
let { left, top } = point;
|
|
149
149
|
let depth = 0;
|
|
150
150
|
const MAX_DEPTH = 10;
|
|
@@ -199,7 +199,7 @@ function getXpathsByPoint(point, isOrderSensitive) {
|
|
|
199
199
|
function getNodeInfoByXpath(xpath) {
|
|
200
200
|
const parts = xpath.split(SUB_XPATH_SEPARATOR).map((p)=>p.trim()).filter(Boolean);
|
|
201
201
|
if (0 === parts.length) return null;
|
|
202
|
-
let currentDocument =
|
|
202
|
+
let currentDocument = "u" > typeof document ? document : void 0;
|
|
203
203
|
let node = null;
|
|
204
204
|
for(let i = 0; i < parts.length; i++){
|
|
205
205
|
const currentXpath = parts[i];
|
|
@@ -227,13 +227,13 @@ function getNodeInfoByXpath(xpath) {
|
|
|
227
227
|
function getElementInfoByXpath(xpath) {
|
|
228
228
|
const node = getNodeInfoByXpath(xpath);
|
|
229
229
|
if (!node) return null;
|
|
230
|
-
let targetWindow =
|
|
231
|
-
let targetDocument =
|
|
230
|
+
let targetWindow = "u" > typeof window ? window : void 0;
|
|
231
|
+
let targetDocument = "u" > typeof document ? document : void 0;
|
|
232
232
|
if (node.ownerDocument?.defaultView) {
|
|
233
233
|
targetWindow = node.ownerDocument.defaultView;
|
|
234
234
|
targetDocument = node.ownerDocument;
|
|
235
235
|
}
|
|
236
|
-
const rootDoc =
|
|
236
|
+
const rootDoc = "u" > typeof document ? document : null;
|
|
237
237
|
const iframeOffset = calculateIframeOffset(node.ownerDocument ?? null, rootDoc);
|
|
238
238
|
const targetWin = targetWindow;
|
|
239
239
|
const targetDoc = targetDocument;
|
|
@@ -199,10 +199,10 @@ function getNodeAttributes(node, currentWindow) {
|
|
|
199
199
|
}
|
|
200
200
|
const NODE_CACHE_MAX_SIZE = 2000;
|
|
201
201
|
function setNodeHashCacheListOnWindow() {
|
|
202
|
-
if (
|
|
202
|
+
if ("u" > typeof window) window.midsceneNodeHashCache = new Map();
|
|
203
203
|
}
|
|
204
204
|
function getNodeCacheMap() {
|
|
205
|
-
if (
|
|
205
|
+
if ("u" < typeof window) return;
|
|
206
206
|
return window.midsceneNodeHashCache;
|
|
207
207
|
}
|
|
208
208
|
function setNodeToCacheList(node, id) {
|
|
@@ -221,7 +221,7 @@ function getNodeFromCacheList(id) {
|
|
|
221
221
|
function midsceneGenerateHash(node, content, rect) {
|
|
222
222
|
const slicedHash = generateHashId(rect, content);
|
|
223
223
|
if (node) {
|
|
224
|
-
if (
|
|
224
|
+
if ("u" > typeof window && !getNodeCacheMap()) setNodeHashCacheListOnWindow();
|
|
225
225
|
setNodeToCacheList(node, slicedHash);
|
|
226
226
|
}
|
|
227
227
|
return slicedHash;
|
|
@@ -230,13 +230,13 @@ function generateId(numberId) {
|
|
|
230
230
|
return `${numberId}`;
|
|
231
231
|
}
|
|
232
232
|
function setGenerateHashOnWindow() {
|
|
233
|
-
if (
|
|
233
|
+
if ("u" > typeof window) window.midsceneGenerateHash = midsceneGenerateHash;
|
|
234
234
|
}
|
|
235
235
|
function setMidsceneVisibleRectOnWindow() {
|
|
236
|
-
if (
|
|
236
|
+
if ("u" > typeof window) window.midsceneVisibleRect = elementRect;
|
|
237
237
|
}
|
|
238
238
|
function setExtractTextWithPositionOnWindow() {
|
|
239
|
-
if (
|
|
239
|
+
if ("u" > typeof window) window.extractTextWithPosition = extractTextWithPosition;
|
|
240
240
|
}
|
|
241
241
|
function getTopDocument() {
|
|
242
242
|
const container = document.body || document;
|
package/dist/es/index.mjs
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
import { createCopyStaticPlugin, createPlaygroundCopyPlugin } from "./build/copy-static.mjs";
|
|
2
|
-
import { commonIgnoreWarnings } from "./build/rspack-config.mjs";
|
|
3
1
|
const src = {};
|
|
4
|
-
export {
|
|
2
|
+
export { src as default };
|
package/dist/es/logger.mjs
CHANGED
|
@@ -61,10 +61,4 @@ function enableDebug(topic) {
|
|
|
61
61
|
if (ifInNode) return;
|
|
62
62
|
debug.enable(`${topicPrefix}:${topic}`);
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
if (!ifInNode) return;
|
|
66
|
-
for (const stream of logStreams.values())stream.end();
|
|
67
|
-
logStreams.clear();
|
|
68
|
-
debugInstances.clear();
|
|
69
|
-
}
|
|
70
|
-
export { cleanupLogStreams, enableDebug, getDebug };
|
|
64
|
+
export { enableDebug, getDebug };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { setIsMcp } from "
|
|
2
|
+
import { setIsMcp } from "@aiscene/shared/utils";
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { parseBase64 } from "
|
|
2
|
-
import { getDebug } from "
|
|
1
|
+
import { parseBase64 } from "@aiscene/shared/img";
|
|
2
|
+
import { getDebug } from "@aiscene/shared/logger";
|
|
3
3
|
import { generateCommonTools, generateToolsFromActionSpace } from "./tool-generator.mjs";
|
|
4
4
|
function _define_property(obj, key, value) {
|
|
5
5
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { parseBase64 } from "
|
|
1
|
+
import { parseBase64 } from "@aiscene/shared/img";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { getZodDescription, getZodTypeName } from "../zod-schema-utils.mjs";
|
|
3
|
+
import { getZodDescription, getZodTypeName, isMidsceneLocatorField, unwrapZodField } from "../zod-schema-utils.mjs";
|
|
4
4
|
function getErrorMessage(error) {
|
|
5
5
|
return error instanceof Error ? error.message : String(error);
|
|
6
6
|
}
|
|
7
7
|
function describeActionForMCP(action) {
|
|
8
8
|
const actionDesc = action.description || `Execute ${action.name} action`;
|
|
9
9
|
if (!action.paramSchema) return `${action.name} action, ${actionDesc}`;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const description = getZodDescription(schema);
|
|
10
|
+
const shape = getZodObjectShape(action.paramSchema);
|
|
11
|
+
if (!shape) {
|
|
12
|
+
const typeName = getZodTypeName(action.paramSchema);
|
|
13
|
+
const description = getZodDescription(action.paramSchema);
|
|
15
14
|
const paramDesc = description ? `${typeName} - ${description}` : typeName;
|
|
16
15
|
return `${action.name} action, ${actionDesc}. Parameter: ${paramDesc}`;
|
|
17
16
|
}
|
|
18
17
|
const paramDescriptions = [];
|
|
19
|
-
for (const [key, field] of Object.entries(
|
|
18
|
+
for (const [key, field] of Object.entries(shape))if (field && 'object' == typeof field) {
|
|
20
19
|
const isFieldOptional = 'function' == typeof field.isOptional && field.isOptional();
|
|
21
20
|
const typeName = getZodTypeName(field);
|
|
22
21
|
const description = getZodDescription(field);
|
|
@@ -30,9 +29,6 @@ function describeActionForMCP(action) {
|
|
|
30
29
|
function isZodOptional(value) {
|
|
31
30
|
return '_def' in value && value._def?.typeName === 'ZodOptional';
|
|
32
31
|
}
|
|
33
|
-
function isZodObject(value) {
|
|
34
|
-
return '_def' in value && value._def?.typeName === 'ZodObject' && 'shape' in value;
|
|
35
|
-
}
|
|
36
32
|
function unwrapOptional(value) {
|
|
37
33
|
if (isZodOptional(value)) return {
|
|
38
34
|
innerValue: value._def.innerType,
|
|
@@ -43,24 +39,31 @@ function unwrapOptional(value) {
|
|
|
43
39
|
isOptional: false
|
|
44
40
|
};
|
|
45
41
|
}
|
|
46
|
-
function
|
|
47
|
-
if (!
|
|
48
|
-
|
|
42
|
+
function getZodObjectShape(value) {
|
|
43
|
+
if (!value) return;
|
|
44
|
+
const actualValue = unwrapZodField(value);
|
|
45
|
+
if (actualValue._def?.typeName !== 'ZodObject') return;
|
|
46
|
+
if ('function' == typeof actualValue._def.shape) return actualValue._def.shape();
|
|
47
|
+
return actualValue.shape;
|
|
48
|
+
}
|
|
49
|
+
function isRecord(value) {
|
|
50
|
+
return 'object' == typeof value && null !== value && !Array.isArray(value);
|
|
49
51
|
}
|
|
50
|
-
function makePromptOptional(
|
|
52
|
+
function makePromptOptional(shape, wrapInOptional) {
|
|
51
53
|
const newShape = {
|
|
52
|
-
...
|
|
54
|
+
...shape
|
|
53
55
|
};
|
|
54
|
-
newShape.prompt =
|
|
56
|
+
newShape.prompt = shape.prompt.optional();
|
|
55
57
|
let newSchema = z.object(newShape).passthrough();
|
|
56
58
|
if (wrapInOptional) newSchema = newSchema.optional();
|
|
57
59
|
return newSchema;
|
|
58
60
|
}
|
|
59
61
|
function transformSchemaField(key, value) {
|
|
60
62
|
const { innerValue, isOptional } = unwrapOptional(value);
|
|
61
|
-
|
|
63
|
+
const shape = getZodObjectShape(innerValue);
|
|
64
|
+
if (shape && isMidsceneLocatorField(innerValue)) return [
|
|
62
65
|
key,
|
|
63
|
-
makePromptOptional(
|
|
66
|
+
makePromptOptional(shape, isOptional)
|
|
64
67
|
];
|
|
65
68
|
return [
|
|
66
69
|
key,
|
|
@@ -69,9 +72,66 @@ function transformSchemaField(key, value) {
|
|
|
69
72
|
}
|
|
70
73
|
function extractActionSchema(paramSchema) {
|
|
71
74
|
if (!paramSchema) return {};
|
|
72
|
-
const
|
|
73
|
-
if (!
|
|
74
|
-
return Object.fromEntries(Object.entries(
|
|
75
|
+
const shape = getZodObjectShape(paramSchema);
|
|
76
|
+
if (!shape) return paramSchema;
|
|
77
|
+
return Object.fromEntries(Object.entries(shape).map(([key, value])=>transformSchemaField(key, value)));
|
|
78
|
+
}
|
|
79
|
+
function getPromptText(prompt) {
|
|
80
|
+
if ('string' == typeof prompt) return prompt;
|
|
81
|
+
if (isRecord(prompt) && 'string' == typeof prompt.prompt) return prompt.prompt;
|
|
82
|
+
}
|
|
83
|
+
function moveLocateExtrasIntoPrompt(value, locateFieldKeys) {
|
|
84
|
+
const promptText = getPromptText(value.prompt);
|
|
85
|
+
if (!promptText) return value;
|
|
86
|
+
const normalizedPrompt = isRecord(value.prompt) ? {
|
|
87
|
+
...value.prompt
|
|
88
|
+
} : {
|
|
89
|
+
prompt: promptText
|
|
90
|
+
};
|
|
91
|
+
const normalizedLocate = {};
|
|
92
|
+
let movedExtraField = false;
|
|
93
|
+
for (const [key, fieldValue] of Object.entries(value))if ('prompt' !== key) {
|
|
94
|
+
if (locateFieldKeys.has(key)) {
|
|
95
|
+
normalizedLocate[key] = fieldValue;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
movedExtraField = true;
|
|
99
|
+
if (!(key in normalizedPrompt)) normalizedPrompt[key] = fieldValue;
|
|
100
|
+
}
|
|
101
|
+
if (!movedExtraField) return value;
|
|
102
|
+
return {
|
|
103
|
+
...normalizedLocate,
|
|
104
|
+
prompt: normalizedPrompt
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function normalizeLocateLikeArg(value, fieldSchema) {
|
|
108
|
+
if ('string' == typeof value) return {
|
|
109
|
+
prompt: value
|
|
110
|
+
};
|
|
111
|
+
if (!isRecord(value)) return value;
|
|
112
|
+
const shape = getZodObjectShape(fieldSchema);
|
|
113
|
+
if (!shape) return value;
|
|
114
|
+
return moveLocateExtrasIntoPrompt(value, new Set(Object.keys(shape)));
|
|
115
|
+
}
|
|
116
|
+
function normalizeActionArgs(args, paramSchema) {
|
|
117
|
+
if (!paramSchema) return args;
|
|
118
|
+
const shape = getZodObjectShape(paramSchema);
|
|
119
|
+
if (!shape) return args;
|
|
120
|
+
return Object.fromEntries(Object.entries(args).map(([key, value])=>{
|
|
121
|
+
const fieldSchema = shape[key];
|
|
122
|
+
if (!fieldSchema) return [
|
|
123
|
+
key,
|
|
124
|
+
value
|
|
125
|
+
];
|
|
126
|
+
if (isMidsceneLocatorField(fieldSchema)) return [
|
|
127
|
+
key,
|
|
128
|
+
normalizeLocateLikeArg(value, fieldSchema)
|
|
129
|
+
];
|
|
130
|
+
return [
|
|
131
|
+
key,
|
|
132
|
+
value
|
|
133
|
+
];
|
|
134
|
+
}));
|
|
75
135
|
}
|
|
76
136
|
function serializeArgsToDescription(args) {
|
|
77
137
|
try {
|
|
@@ -90,7 +150,7 @@ function serializeArgsToDescription(args) {
|
|
|
90
150
|
}
|
|
91
151
|
}
|
|
92
152
|
function buildActionInstruction(actionName, args) {
|
|
93
|
-
const locatePrompt = args.locate
|
|
153
|
+
const locatePrompt = isRecord(args.locate) ? getPromptText(args.locate.prompt) : void 0;
|
|
94
154
|
switch(actionName){
|
|
95
155
|
case 'Tap':
|
|
96
156
|
return locatePrompt ? `Tap on "${locatePrompt}"` : 'Tap';
|
|
@@ -118,44 +178,59 @@ function buildActionInstruction(actionName, args) {
|
|
|
118
178
|
}
|
|
119
179
|
}
|
|
120
180
|
}
|
|
121
|
-
async function
|
|
181
|
+
async function executeAction(agent, actionName, args) {
|
|
182
|
+
if (agent.callActionInActionSpace) return agent.callActionInActionSpace(actionName, args);
|
|
183
|
+
if (agent.aiAction) {
|
|
184
|
+
const instruction = buildActionInstruction(actionName, args);
|
|
185
|
+
return agent.aiAction(instruction);
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Action "${actionName}" is not supported by this agent`);
|
|
188
|
+
}
|
|
189
|
+
async function captureScreenshotResult(agent, actionName, actionResult) {
|
|
190
|
+
const content = [
|
|
191
|
+
{
|
|
192
|
+
type: 'text',
|
|
193
|
+
text: `Action "${actionName}" completed.`
|
|
194
|
+
}
|
|
195
|
+
];
|
|
196
|
+
if (void 0 !== actionResult) content.push({
|
|
197
|
+
type: 'text',
|
|
198
|
+
text: `Result: ${serializeActionResult(actionResult)}`
|
|
199
|
+
});
|
|
122
200
|
try {
|
|
123
201
|
const screenshot = await agent.page?.screenshotBase64();
|
|
124
202
|
if (!screenshot) return {
|
|
125
|
-
content
|
|
126
|
-
{
|
|
127
|
-
type: 'text',
|
|
128
|
-
text: `Action "${actionName}" completed.`
|
|
129
|
-
}
|
|
130
|
-
]
|
|
203
|
+
content
|
|
131
204
|
};
|
|
132
205
|
const { mimeType, body } = parseBase64(screenshot);
|
|
206
|
+
content.push({
|
|
207
|
+
type: 'image',
|
|
208
|
+
data: body,
|
|
209
|
+
mimeType
|
|
210
|
+
});
|
|
133
211
|
return {
|
|
134
|
-
content
|
|
135
|
-
{
|
|
136
|
-
type: 'text',
|
|
137
|
-
text: `Action "${actionName}" completed.`
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
type: 'image',
|
|
141
|
-
data: body,
|
|
142
|
-
mimeType
|
|
143
|
-
}
|
|
144
|
-
]
|
|
212
|
+
content
|
|
145
213
|
};
|
|
146
214
|
} catch (error) {
|
|
147
215
|
const errorMessage = getErrorMessage(error);
|
|
148
216
|
console.error('Error capturing screenshot:', errorMessage);
|
|
217
|
+
content[0] = {
|
|
218
|
+
type: 'text',
|
|
219
|
+
text: `Action "${actionName}" completed (screenshot unavailable: ${errorMessage})`
|
|
220
|
+
};
|
|
149
221
|
return {
|
|
150
|
-
content
|
|
151
|
-
{
|
|
152
|
-
type: 'text',
|
|
153
|
-
text: `Action "${actionName}" completed (screenshot unavailable: ${errorMessage})`
|
|
154
|
-
}
|
|
155
|
-
]
|
|
222
|
+
content
|
|
156
223
|
};
|
|
157
224
|
}
|
|
158
225
|
}
|
|
226
|
+
function serializeActionResult(actionResult) {
|
|
227
|
+
if ('string' == typeof actionResult) return actionResult;
|
|
228
|
+
try {
|
|
229
|
+
return JSON.stringify(actionResult);
|
|
230
|
+
} catch {
|
|
231
|
+
return String(actionResult);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
159
234
|
function createErrorResult(message) {
|
|
160
235
|
return {
|
|
161
236
|
content: [
|
|
@@ -214,17 +289,16 @@ function generateToolsFromActionSpace(actionSpace, getAgent) {
|
|
|
214
289
|
handler: async (args)=>{
|
|
215
290
|
try {
|
|
216
291
|
const agent = await getAgent();
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
292
|
+
const normalizedArgs = normalizeActionArgs(args, action.paramSchema);
|
|
293
|
+
let actionResult;
|
|
294
|
+
try {
|
|
295
|
+
actionResult = await executeAction(agent, action.name, normalizedArgs);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
const errorMessage = getErrorMessage(error);
|
|
298
|
+
console.error(`Error executing action "${action.name}":`, errorMessage);
|
|
299
|
+
return await captureFailureResult(agent, action.name, errorMessage);
|
|
226
300
|
}
|
|
227
|
-
return await captureScreenshotResult(agent, action.name);
|
|
301
|
+
return await captureScreenshotResult(agent, action.name, actionResult);
|
|
228
302
|
} catch (error) {
|
|
229
303
|
const errorMessage = getErrorMessage(error);
|
|
230
304
|
console.error(`Error in handler for "${action.name}":`, errorMessage);
|