@midscene/web 0.19.1 → 0.20.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/agent.js +299 -247
- package/dist/es/agent.js.map +1 -1
- package/dist/es/bridge-mode-browser.js +3 -3
- package/dist/es/bridge-mode.js +301 -249
- package/dist/es/bridge-mode.js.map +1 -1
- package/dist/es/chrome-extension.js +342 -290
- package/dist/es/chrome-extension.js.map +1 -1
- package/dist/es/index.js +307 -247
- package/dist/es/index.js.map +1 -1
- package/dist/es/midscene-playground.js +341 -289
- package/dist/es/midscene-playground.js.map +1 -1
- package/dist/es/midscene-server.js +25 -12
- package/dist/es/midscene-server.js.map +1 -1
- package/dist/es/playground.js +341 -289
- package/dist/es/playground.js.map +1 -1
- package/dist/es/playwright-report.js +14 -1
- package/dist/es/playwright-report.js.map +1 -1
- package/dist/es/playwright-reporter.js +14 -1
- package/dist/es/playwright-reporter.js.map +1 -1
- package/dist/es/playwright.js +307 -247
- package/dist/es/playwright.js.map +1 -1
- package/dist/es/puppeteer-agent-launcher.js +299 -247
- package/dist/es/puppeteer-agent-launcher.js.map +1 -1
- package/dist/es/puppeteer.js +299 -247
- package/dist/es/puppeteer.js.map +1 -1
- package/dist/es/utils.js +42 -8
- package/dist/es/utils.js.map +1 -1
- package/dist/es/yaml.js +11 -4
- package/dist/es/yaml.js.map +1 -1
- package/dist/lib/agent.js +308 -256
- package/dist/lib/agent.js.map +1 -1
- package/dist/lib/bridge-mode-browser.js +3 -3
- package/dist/lib/bridge-mode.js +310 -258
- package/dist/lib/bridge-mode.js.map +1 -1
- package/dist/lib/chrome-extension.js +355 -303
- package/dist/lib/chrome-extension.js.map +1 -1
- package/dist/lib/index.js +316 -256
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/midscene-playground.js +354 -302
- package/dist/lib/midscene-playground.js.map +1 -1
- package/dist/lib/midscene-server.js +28 -15
- package/dist/lib/midscene-server.js.map +1 -1
- package/dist/lib/playground.js +354 -302
- package/dist/lib/playground.js.map +1 -1
- package/dist/lib/playwright-report.js +20 -7
- package/dist/lib/playwright-report.js.map +1 -1
- package/dist/lib/playwright-reporter.js +20 -7
- package/dist/lib/playwright-reporter.js.map +1 -1
- package/dist/lib/playwright.js +316 -256
- package/dist/lib/playwright.js.map +1 -1
- package/dist/lib/puppeteer-agent-launcher.js +308 -256
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
- package/dist/lib/puppeteer.js +308 -256
- package/dist/lib/puppeteer.js.map +1 -1
- package/dist/lib/utils.js +48 -13
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/yaml.js +11 -4
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent.d.ts +6 -102
- package/dist/types/bridge-mode-browser.d.ts +3 -2
- package/dist/types/bridge-mode.d.ts +4 -4
- package/dist/types/{browser-5dbb4bfb.d.ts → browser-118d886d.d.ts} +1 -1
- package/dist/types/chrome-extension.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/midscene-server.d.ts +2 -2
- package/dist/types/{page-90e9f9a7.d.ts → page-471361cd.d.ts} +102 -3
- package/dist/types/playground.d.ts +2 -2
- package/dist/types/playwright.d.ts +6 -2
- package/dist/types/puppeteer-agent-launcher.d.ts +1 -1
- package/dist/types/puppeteer.d.ts +3 -3
- package/dist/types/utils.d.ts +2 -1
- package/dist/types/yaml.d.ts +1 -1
- package/package.json +3 -3
package/dist/lib/playground.js
CHANGED
|
@@ -38,11 +38,11 @@ module.exports = __toCommonJS(playground_exports);
|
|
|
38
38
|
|
|
39
39
|
// src/common/utils.ts
|
|
40
40
|
var import_ai_model = require("@midscene/core/ai-model");
|
|
41
|
-
var
|
|
41
|
+
var import_utils3 = require("@midscene/core/utils");
|
|
42
42
|
var import_env = require("@midscene/shared/env");
|
|
43
43
|
var import_extractor = require("@midscene/shared/extractor");
|
|
44
44
|
var import_img = require("@midscene/shared/img");
|
|
45
|
-
var
|
|
45
|
+
var import_utils4 = require("@midscene/shared/utils");
|
|
46
46
|
var import_dayjs = __toESM(require("dayjs"));
|
|
47
47
|
|
|
48
48
|
// src/web-element.ts
|
|
@@ -70,14 +70,189 @@ var WebElementInfo = class {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
// src/common/task-cache.ts
|
|
74
|
+
var import_node_assert = __toESM(require("assert"));
|
|
75
|
+
var import_node_fs = require("fs");
|
|
76
|
+
var import_node_path = require("path");
|
|
77
|
+
var import_common = require("@midscene/shared/common");
|
|
78
|
+
var import_logger = require("@midscene/shared/logger");
|
|
79
|
+
var import_utils = require("@midscene/shared/utils");
|
|
80
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
81
|
+
var import_semver = __toESM(require("semver"));
|
|
82
|
+
|
|
83
|
+
// package.json
|
|
84
|
+
var version = "0.20.0";
|
|
85
|
+
|
|
86
|
+
// src/common/task-cache.ts
|
|
87
|
+
var debug = (0, import_logger.getDebug)("cache");
|
|
88
|
+
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
89
|
+
var cacheFileExt = ".cache.yaml";
|
|
90
|
+
var TaskCache = class {
|
|
91
|
+
// Track matched records
|
|
92
|
+
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
93
|
+
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
94
|
+
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
95
|
+
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
96
|
+
this.cacheFilePath = import_utils.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
97
|
+
this.isCacheResultUsed = isCacheResultUsed;
|
|
98
|
+
let cacheContent;
|
|
99
|
+
if (this.cacheFilePath) {
|
|
100
|
+
cacheContent = this.loadCacheFromFile();
|
|
101
|
+
}
|
|
102
|
+
if (!cacheContent) {
|
|
103
|
+
cacheContent = {
|
|
104
|
+
midsceneVersion: version,
|
|
105
|
+
cacheId: this.cacheId,
|
|
106
|
+
caches: []
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
this.cache = cacheContent;
|
|
110
|
+
this.cacheOriginalLength = this.cache.caches.length;
|
|
111
|
+
}
|
|
112
|
+
matchCache(prompt, type) {
|
|
113
|
+
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
114
|
+
const item = this.cache.caches[i];
|
|
115
|
+
const key = `${type}:${prompt}:${i}`;
|
|
116
|
+
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
117
|
+
this.matchedCacheIndices.add(key);
|
|
118
|
+
debug(
|
|
119
|
+
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
120
|
+
type,
|
|
121
|
+
prompt,
|
|
122
|
+
i
|
|
123
|
+
);
|
|
124
|
+
return {
|
|
125
|
+
cacheContent: item,
|
|
126
|
+
updateFn: (cb) => {
|
|
127
|
+
debug(
|
|
128
|
+
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
129
|
+
type,
|
|
130
|
+
prompt,
|
|
131
|
+
i
|
|
132
|
+
);
|
|
133
|
+
cb(item);
|
|
134
|
+
debug(
|
|
135
|
+
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
136
|
+
type,
|
|
137
|
+
prompt,
|
|
138
|
+
i
|
|
139
|
+
);
|
|
140
|
+
this.flushCacheToFile();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
debug("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
matchPlanCache(prompt) {
|
|
149
|
+
return this.matchCache(prompt, "plan");
|
|
150
|
+
}
|
|
151
|
+
matchLocateCache(prompt) {
|
|
152
|
+
return this.matchCache(prompt, "locate");
|
|
153
|
+
}
|
|
154
|
+
appendCache(cache) {
|
|
155
|
+
debug("will append cache", cache);
|
|
156
|
+
this.cache.caches.push(cache);
|
|
157
|
+
this.flushCacheToFile();
|
|
158
|
+
}
|
|
159
|
+
loadCacheFromFile() {
|
|
160
|
+
const cacheFile = this.cacheFilePath;
|
|
161
|
+
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
162
|
+
if (!(0, import_node_fs.existsSync)(cacheFile)) {
|
|
163
|
+
debug("no cache file found, path: %s", cacheFile);
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
167
|
+
if ((0, import_node_fs.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
168
|
+
console.warn(
|
|
169
|
+
`An outdated cache file from an earlier version of Midscene has been detected. Since version 0.17, we have implemented an improved caching strategy. Please delete the old file located at: ${jsonTypeCacheFile}.`
|
|
170
|
+
);
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const data = (0, import_node_fs.readFileSync)(cacheFile, "utf8");
|
|
175
|
+
const jsonData = import_js_yaml.default.load(data);
|
|
176
|
+
if (!version) {
|
|
177
|
+
debug("no midscene version info, will not read cache from file");
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
181
|
+
console.warn(
|
|
182
|
+
`You are using an old version of Midscene cache file, and we cannot match any info from it. Starting from Midscene v0.17, we changed our strategy to use xpath for cache info, providing better performance.
|
|
183
|
+
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
184
|
+
cache file: ${cacheFile}`
|
|
185
|
+
);
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
debug(
|
|
189
|
+
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
190
|
+
cacheFile,
|
|
191
|
+
jsonData.midsceneVersion,
|
|
192
|
+
jsonData.caches.length
|
|
193
|
+
);
|
|
194
|
+
jsonData.midsceneVersion = version;
|
|
195
|
+
return jsonData;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
debug(
|
|
198
|
+
"cache file exists but load failed, path: %s, error: %s",
|
|
199
|
+
cacheFile,
|
|
200
|
+
err
|
|
201
|
+
);
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
flushCacheToFile() {
|
|
206
|
+
if (!version) {
|
|
207
|
+
debug("no midscene version info, will not write cache to file");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (!this.cacheFilePath) {
|
|
211
|
+
debug("no cache file path, will not write cache to file");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const dir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
216
|
+
if (!(0, import_node_fs.existsSync)(dir)) {
|
|
217
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
218
|
+
debug("created cache directory: %s", dir);
|
|
219
|
+
}
|
|
220
|
+
const yamlData = import_js_yaml.default.dump(this.cache);
|
|
221
|
+
(0, import_node_fs.writeFileSync)(this.cacheFilePath, yamlData);
|
|
222
|
+
debug("cache flushed to file: %s", this.cacheFilePath);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
debug(
|
|
225
|
+
"write cache to file failed, path: %s, error: %s",
|
|
226
|
+
this.cacheFilePath,
|
|
227
|
+
err
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
232
|
+
if (cachedRecord) {
|
|
233
|
+
if (newRecord.type === "plan") {
|
|
234
|
+
cachedRecord.updateFn((cache) => {
|
|
235
|
+
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
236
|
+
});
|
|
237
|
+
} else {
|
|
238
|
+
cachedRecord.updateFn((cache) => {
|
|
239
|
+
cache.xpaths = newRecord.xpaths;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
this.appendCache(newRecord);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
73
248
|
// src/common/utils.ts
|
|
74
249
|
async function parseContextFromWebPage(page, _opt) {
|
|
75
|
-
(0,
|
|
250
|
+
(0, import_utils4.assert)(page, "page is required");
|
|
76
251
|
if (page._forceUsePageContext) {
|
|
77
252
|
return await page._forceUsePageContext();
|
|
78
253
|
}
|
|
79
254
|
const url = await page.url();
|
|
80
|
-
(0,
|
|
255
|
+
(0, import_utils3.uploadTestInfoToServer)({ testUrl: url });
|
|
81
256
|
let screenshotBase64;
|
|
82
257
|
let tree;
|
|
83
258
|
await Promise.all([
|
|
@@ -99,7 +274,7 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
99
274
|
isVisible
|
|
100
275
|
});
|
|
101
276
|
});
|
|
102
|
-
(0,
|
|
277
|
+
(0, import_utils4.assert)(screenshotBase64, "screenshotBase64 is required");
|
|
103
278
|
const size = await page.size();
|
|
104
279
|
if (size.dpr && size.dpr > 1) {
|
|
105
280
|
screenshotBase64 = await (0, import_img.resizeImgBase64)(screenshotBase64, {
|
|
@@ -117,11 +292,11 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
117
292
|
function reportFileName(tag = "web") {
|
|
118
293
|
const reportTagName = (0, import_env.getAIConfig)(import_env.MIDSCENE_REPORT_TAG_NAME);
|
|
119
294
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss");
|
|
120
|
-
const uniqueId = (0,
|
|
295
|
+
const uniqueId = (0, import_utils4.uuid)().substring(0, 8);
|
|
121
296
|
return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;
|
|
122
297
|
}
|
|
123
298
|
function printReportMsg(filepath) {
|
|
124
|
-
(0,
|
|
299
|
+
(0, import_utils4.logMsg)(`Midscene - report file updated: ${filepath}`);
|
|
125
300
|
}
|
|
126
301
|
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
|
127
302
|
function replaceIllegalPathCharsAndSpace(str) {
|
|
@@ -147,6 +322,28 @@ function matchElementFromPlan(planLocateParam, tree) {
|
|
|
147
322
|
}
|
|
148
323
|
return void 0;
|
|
149
324
|
}
|
|
325
|
+
async function matchElementFromCache(taskExecutor, xpaths, cachePrompt, cacheable) {
|
|
326
|
+
try {
|
|
327
|
+
if (xpaths?.length && taskExecutor.taskCache?.isCacheResultUsed && cacheable !== false) {
|
|
328
|
+
for (let i = 0; i < xpaths.length; i++) {
|
|
329
|
+
const element = await taskExecutor.page.getElementInfoByXpath(
|
|
330
|
+
xpaths[i]
|
|
331
|
+
);
|
|
332
|
+
if (element?.id) {
|
|
333
|
+
debug("cache hit, prompt: %s", cachePrompt);
|
|
334
|
+
debug(
|
|
335
|
+
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
336
|
+
xpaths[i],
|
|
337
|
+
element?.id
|
|
338
|
+
);
|
|
339
|
+
return element;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
debug("get element info by xpath error: ", error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
150
347
|
function trimContextByViewport(execution) {
|
|
151
348
|
function filterVisibleTree(node) {
|
|
152
349
|
if (!node)
|
|
@@ -189,10 +386,10 @@ var import_core2 = require("@midscene/core");
|
|
|
189
386
|
var import_js_yaml4 = __toESM(require("js-yaml"));
|
|
190
387
|
|
|
191
388
|
// src/yaml/player.ts
|
|
192
|
-
var
|
|
193
|
-
var
|
|
194
|
-
var
|
|
195
|
-
var
|
|
389
|
+
var import_node_fs2 = require("fs");
|
|
390
|
+
var import_node_path2 = require("path");
|
|
391
|
+
var import_utils5 = require("@midscene/shared/utils");
|
|
392
|
+
var import_common2 = require("@midscene/shared/common");
|
|
196
393
|
var ScriptPlayer = class {
|
|
197
394
|
constructor(script, setupAgent, onTaskStatusChange) {
|
|
198
395
|
this.script = script;
|
|
@@ -204,23 +401,23 @@ var ScriptPlayer = class {
|
|
|
204
401
|
this.pageAgent = null;
|
|
205
402
|
this.result = {};
|
|
206
403
|
this.target = script.target || script.web || script.android;
|
|
207
|
-
if (
|
|
404
|
+
if (import_utils5.ifInBrowser) {
|
|
208
405
|
this.output = void 0;
|
|
209
406
|
} else if (this.target?.output) {
|
|
210
|
-
this.output = (0,
|
|
407
|
+
this.output = (0, import_node_path2.resolve)(process.cwd(), this.target.output);
|
|
211
408
|
} else {
|
|
212
|
-
this.output = (0,
|
|
409
|
+
this.output = (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
|
|
213
410
|
}
|
|
214
|
-
if (
|
|
411
|
+
if (import_utils5.ifInBrowser) {
|
|
215
412
|
this.unstableLogContent = void 0;
|
|
216
413
|
} else if (typeof this.target?.unstableLogContent === "string") {
|
|
217
|
-
this.unstableLogContent = (0,
|
|
414
|
+
this.unstableLogContent = (0, import_node_path2.resolve)(
|
|
218
415
|
process.cwd(),
|
|
219
416
|
this.target.unstableLogContent
|
|
220
417
|
);
|
|
221
418
|
} else if (this.target?.unstableLogContent === true) {
|
|
222
|
-
this.unstableLogContent = (0,
|
|
223
|
-
(0,
|
|
419
|
+
this.unstableLogContent = (0, import_node_path2.join)(
|
|
420
|
+
(0, import_common2.getMidsceneRunSubDir)("output"),
|
|
224
421
|
"unstableLogContent.json"
|
|
225
422
|
);
|
|
226
423
|
}
|
|
@@ -265,28 +462,28 @@ var ScriptPlayer = class {
|
|
|
265
462
|
}
|
|
266
463
|
flushResult() {
|
|
267
464
|
if (Object.keys(this.result).length && this.output) {
|
|
268
|
-
const output = (0,
|
|
269
|
-
const outputDir = (0,
|
|
270
|
-
if (!(0,
|
|
271
|
-
(0,
|
|
465
|
+
const output = (0, import_node_path2.resolve)(process.cwd(), this.output);
|
|
466
|
+
const outputDir = (0, import_node_path2.dirname)(output);
|
|
467
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
468
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
272
469
|
}
|
|
273
|
-
(0,
|
|
470
|
+
(0, import_node_fs2.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
|
|
274
471
|
}
|
|
275
472
|
}
|
|
276
473
|
flushUnstableLogContent() {
|
|
277
474
|
if (this.unstableLogContent) {
|
|
278
475
|
const content = this.pageAgent?._unstableLogContent();
|
|
279
|
-
const filePath = (0,
|
|
280
|
-
const outputDir = (0,
|
|
281
|
-
if (!(0,
|
|
282
|
-
(0,
|
|
476
|
+
const filePath = (0, import_node_path2.resolve)(process.cwd(), this.unstableLogContent);
|
|
477
|
+
const outputDir = (0, import_node_path2.dirname)(filePath);
|
|
478
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
479
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
283
480
|
}
|
|
284
|
-
(0,
|
|
481
|
+
(0, import_node_fs2.writeFileSync)(filePath, JSON.stringify(content, null, 2));
|
|
285
482
|
}
|
|
286
483
|
}
|
|
287
484
|
async playTask(taskStatus, agent) {
|
|
288
485
|
const { flow } = taskStatus;
|
|
289
|
-
(0,
|
|
486
|
+
(0, import_utils5.assert)(flow, "missing flow in task");
|
|
290
487
|
for (const flowItemIndex in flow) {
|
|
291
488
|
const currentStep = Number.parseInt(flowItemIndex, 10);
|
|
292
489
|
taskStatus.currentStep = currentStep;
|
|
@@ -294,8 +491,8 @@ var ScriptPlayer = class {
|
|
|
294
491
|
if ("aiAction" in flowItem || "ai" in flowItem) {
|
|
295
492
|
const actionTask = flowItem;
|
|
296
493
|
const prompt = actionTask.aiAction || actionTask.ai;
|
|
297
|
-
(0,
|
|
298
|
-
(0,
|
|
494
|
+
(0, import_utils5.assert)(prompt, "missing prompt for ai (aiAction)");
|
|
495
|
+
(0, import_utils5.assert)(
|
|
299
496
|
typeof prompt === "string",
|
|
300
497
|
"prompt for aiAction must be a string"
|
|
301
498
|
);
|
|
@@ -306,8 +503,8 @@ var ScriptPlayer = class {
|
|
|
306
503
|
const assertTask = flowItem;
|
|
307
504
|
const prompt = assertTask.aiAssert;
|
|
308
505
|
const msg = assertTask.errorMessage;
|
|
309
|
-
(0,
|
|
310
|
-
(0,
|
|
506
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAssert");
|
|
507
|
+
(0, import_utils5.assert)(
|
|
311
508
|
typeof prompt === "string",
|
|
312
509
|
"prompt for aiAssert must be a string"
|
|
313
510
|
);
|
|
@@ -319,8 +516,8 @@ var ScriptPlayer = class {
|
|
|
319
516
|
domIncluded: queryTask.domIncluded,
|
|
320
517
|
screenshotIncluded: queryTask.screenshotIncluded
|
|
321
518
|
};
|
|
322
|
-
(0,
|
|
323
|
-
(0,
|
|
519
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiQuery");
|
|
520
|
+
(0, import_utils5.assert)(
|
|
324
521
|
typeof prompt === "string",
|
|
325
522
|
"prompt for aiQuery must be a string"
|
|
326
523
|
);
|
|
@@ -333,8 +530,8 @@ var ScriptPlayer = class {
|
|
|
333
530
|
domIncluded: numberTask.domIncluded,
|
|
334
531
|
screenshotIncluded: numberTask.screenshotIncluded
|
|
335
532
|
};
|
|
336
|
-
(0,
|
|
337
|
-
(0,
|
|
533
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiNumber");
|
|
534
|
+
(0, import_utils5.assert)(
|
|
338
535
|
typeof prompt === "string",
|
|
339
536
|
"prompt for number must be a string"
|
|
340
537
|
);
|
|
@@ -347,8 +544,8 @@ var ScriptPlayer = class {
|
|
|
347
544
|
domIncluded: stringTask.domIncluded,
|
|
348
545
|
screenshotIncluded: stringTask.screenshotIncluded
|
|
349
546
|
};
|
|
350
|
-
(0,
|
|
351
|
-
(0,
|
|
547
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiNumber");
|
|
548
|
+
(0, import_utils5.assert)(
|
|
352
549
|
typeof prompt === "string",
|
|
353
550
|
"prompt for string must be a string"
|
|
354
551
|
);
|
|
@@ -361,28 +558,35 @@ var ScriptPlayer = class {
|
|
|
361
558
|
domIncluded: booleanTask.domIncluded,
|
|
362
559
|
screenshotIncluded: booleanTask.screenshotIncluded
|
|
363
560
|
};
|
|
364
|
-
(0,
|
|
365
|
-
(0,
|
|
561
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiBoolean");
|
|
562
|
+
(0, import_utils5.assert)(
|
|
366
563
|
typeof prompt === "string",
|
|
367
564
|
"prompt for boolean must be a string"
|
|
368
565
|
);
|
|
369
566
|
const booleanResult = await agent.aiBoolean(prompt, options);
|
|
370
567
|
this.setResult(booleanTask.name, booleanResult);
|
|
568
|
+
} else if ("aiAsk" in flowItem) {
|
|
569
|
+
const askTask = flowItem;
|
|
570
|
+
const prompt = askTask.aiAsk;
|
|
571
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAsk");
|
|
572
|
+
(0, import_utils5.assert)(typeof prompt === "string", "prompt for aiAsk must be a string");
|
|
573
|
+
const askResult = await agent.aiAsk(prompt);
|
|
574
|
+
this.setResult(askTask.name, askResult);
|
|
371
575
|
} else if ("aiLocate" in flowItem) {
|
|
372
576
|
const locateTask = flowItem;
|
|
373
577
|
const prompt = locateTask.aiLocate;
|
|
374
|
-
(0,
|
|
375
|
-
(0,
|
|
578
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiLocate");
|
|
579
|
+
(0, import_utils5.assert)(
|
|
376
580
|
typeof prompt === "string",
|
|
377
581
|
"prompt for aiLocate must be a string"
|
|
378
582
|
);
|
|
379
|
-
const locateResult = await agent.aiLocate(prompt);
|
|
583
|
+
const locateResult = await agent.aiLocate(prompt, locateTask);
|
|
380
584
|
this.setResult(locateTask.name, locateResult);
|
|
381
585
|
} else if ("aiWaitFor" in flowItem) {
|
|
382
586
|
const waitForTask = flowItem;
|
|
383
587
|
const prompt = waitForTask.aiWaitFor;
|
|
384
|
-
(0,
|
|
385
|
-
(0,
|
|
588
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiWaitFor");
|
|
589
|
+
(0, import_utils5.assert)(
|
|
386
590
|
typeof prompt === "string",
|
|
387
591
|
"prompt for aiWaitFor must be a string"
|
|
388
592
|
);
|
|
@@ -395,7 +599,7 @@ var ScriptPlayer = class {
|
|
|
395
599
|
if (typeof ms === "string") {
|
|
396
600
|
msNumber = Number.parseInt(ms, 10);
|
|
397
601
|
}
|
|
398
|
-
(0,
|
|
602
|
+
(0, import_utils5.assert)(
|
|
399
603
|
msNumber && msNumber > 0,
|
|
400
604
|
`ms for sleep must be greater than 0, but got ${ms}`
|
|
401
605
|
);
|
|
@@ -514,11 +718,11 @@ var ScriptPlayer = class {
|
|
|
514
718
|
};
|
|
515
719
|
|
|
516
720
|
// src/yaml/builder.ts
|
|
517
|
-
var
|
|
721
|
+
var import_js_yaml2 = __toESM(require("js-yaml"));
|
|
518
722
|
|
|
519
723
|
// src/yaml/utils.ts
|
|
520
|
-
var
|
|
521
|
-
var
|
|
724
|
+
var import_utils6 = require("@midscene/shared/utils");
|
|
725
|
+
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
522
726
|
function interpolateEnvVars(content) {
|
|
523
727
|
return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
524
728
|
const value = process.env[envVar.trim()];
|
|
@@ -544,31 +748,31 @@ function parseYamlScript(content, filePath, ignoreCheckingTarget) {
|
|
|
544
748
|
);
|
|
545
749
|
}
|
|
546
750
|
const interpolatedContent = interpolateEnvVars(processedContent);
|
|
547
|
-
const obj =
|
|
548
|
-
schema:
|
|
751
|
+
const obj = import_js_yaml3.default.load(interpolatedContent, {
|
|
752
|
+
schema: import_js_yaml3.default.JSON_SCHEMA
|
|
549
753
|
});
|
|
550
754
|
const pathTip = filePath ? `, failed to load ${filePath}` : "";
|
|
551
755
|
const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
|
|
552
756
|
const webConfig = obj.web || obj.target;
|
|
553
757
|
const web = typeof webConfig !== "undefined" ? Object.assign({}, webConfig || {}) : void 0;
|
|
554
758
|
if (!ignoreCheckingTarget) {
|
|
555
|
-
(0,
|
|
759
|
+
(0, import_utils6.assert)(
|
|
556
760
|
web || android,
|
|
557
761
|
`at least one of "target", "web", or "android" properties is required in yaml script${pathTip}`
|
|
558
762
|
);
|
|
559
|
-
(0,
|
|
763
|
+
(0, import_utils6.assert)(
|
|
560
764
|
web && !android || !web && android,
|
|
561
765
|
`only one of "target", "web", or "android" properties is allowed in yaml script${pathTip}`
|
|
562
766
|
);
|
|
563
767
|
if (web || android) {
|
|
564
|
-
(0,
|
|
768
|
+
(0, import_utils6.assert)(
|
|
565
769
|
typeof web === "object" || typeof android === "object",
|
|
566
770
|
`property "target/web/android" must be an object${pathTip}`
|
|
567
771
|
);
|
|
568
772
|
}
|
|
569
773
|
}
|
|
570
|
-
(0,
|
|
571
|
-
(0,
|
|
774
|
+
(0, import_utils6.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
|
|
775
|
+
(0, import_utils6.assert)(
|
|
572
776
|
Array.isArray(obj.tasks),
|
|
573
777
|
`property "tasks" must be an array in yaml script, but got ${obj.tasks}`
|
|
574
778
|
);
|
|
@@ -585,10 +789,10 @@ var import_utils12 = require("@midscene/shared/utils");
|
|
|
585
789
|
// src/common/tasks.ts
|
|
586
790
|
var import_core = require("@midscene/core");
|
|
587
791
|
var import_ai_model2 = require("@midscene/core/ai-model");
|
|
588
|
-
var
|
|
792
|
+
var import_utils7 = require("@midscene/core/utils");
|
|
589
793
|
var import_constants = require("@midscene/shared/constants");
|
|
590
|
-
var
|
|
591
|
-
var
|
|
794
|
+
var import_logger2 = require("@midscene/shared/logger");
|
|
795
|
+
var import_utils8 = require("@midscene/shared/utils");
|
|
592
796
|
|
|
593
797
|
// src/common/ui-utils.ts
|
|
594
798
|
function typeStr(task) {
|
|
@@ -664,7 +868,7 @@ function paramStr(task) {
|
|
|
664
868
|
}
|
|
665
869
|
|
|
666
870
|
// src/common/tasks.ts
|
|
667
|
-
var
|
|
871
|
+
var debug2 = (0, import_logger2.getDebug)("page-task-executor");
|
|
668
872
|
var replanningCountLimit = 10;
|
|
669
873
|
var isAndroidPage = (page) => {
|
|
670
874
|
return page.pageType === "android";
|
|
@@ -705,7 +909,7 @@ var PageTaskExecutor = class {
|
|
|
705
909
|
if (info?.id) {
|
|
706
910
|
elementId = info.id;
|
|
707
911
|
} else {
|
|
708
|
-
|
|
912
|
+
debug2(
|
|
709
913
|
"no element id found for position node, will not update cache",
|
|
710
914
|
element
|
|
711
915
|
);
|
|
@@ -718,7 +922,7 @@ var PageTaskExecutor = class {
|
|
|
718
922
|
const result = await this.page.getXpathsById(elementId);
|
|
719
923
|
return result;
|
|
720
924
|
} catch (error) {
|
|
721
|
-
|
|
925
|
+
debug2("getXpathsById error: ", error);
|
|
722
926
|
}
|
|
723
927
|
}
|
|
724
928
|
prependExecutorWithScreenshot(taskApply, appendAfterExecution = false) {
|
|
@@ -734,7 +938,7 @@ var PageTaskExecutor = class {
|
|
|
734
938
|
if (taskApply.type === "Action") {
|
|
735
939
|
await Promise.all([
|
|
736
940
|
(async () => {
|
|
737
|
-
await (0,
|
|
941
|
+
await (0, import_utils7.sleep)(100);
|
|
738
942
|
if (this.page.waitUntilNetworkIdle) {
|
|
739
943
|
try {
|
|
740
944
|
await this.page.waitUntilNetworkIdle();
|
|
@@ -742,7 +946,7 @@ var PageTaskExecutor = class {
|
|
|
742
946
|
}
|
|
743
947
|
}
|
|
744
948
|
})(),
|
|
745
|
-
(0,
|
|
949
|
+
(0, import_utils7.sleep)(200)
|
|
746
950
|
]);
|
|
747
951
|
}
|
|
748
952
|
if (appendAfterExecution) {
|
|
@@ -772,7 +976,7 @@ var PageTaskExecutor = class {
|
|
|
772
976
|
locate: plan2.locate,
|
|
773
977
|
executor: async (param, taskContext) => {
|
|
774
978
|
const { task } = taskContext;
|
|
775
|
-
(0,
|
|
979
|
+
(0, import_utils8.assert)(
|
|
776
980
|
param?.prompt || param?.id || param?.bbox,
|
|
777
981
|
"No prompt or id or position or bbox to locate"
|
|
778
982
|
);
|
|
@@ -797,39 +1001,29 @@ var PageTaskExecutor = class {
|
|
|
797
1001
|
timing: "before Insight"
|
|
798
1002
|
};
|
|
799
1003
|
task.recorder = [recordItem];
|
|
800
|
-
|
|
1004
|
+
const elementFromXpath = param.xpath ? await this.page.getElementInfoByXpath(param.xpath) : void 0;
|
|
1005
|
+
const userExpectedPathHitFlag = !!elementFromXpath;
|
|
801
1006
|
const cachePrompt = param.prompt;
|
|
802
1007
|
const locateCacheRecord = this.taskCache?.matchLocateCache(cachePrompt);
|
|
803
1008
|
const xpaths = locateCacheRecord?.cacheContent?.xpaths;
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
debug(
|
|
816
|
-
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
817
|
-
xpaths[i],
|
|
818
|
-
element2?.id
|
|
819
|
-
);
|
|
820
|
-
break;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
} catch (error) {
|
|
825
|
-
debug("get element info by xpath error: ", error);
|
|
826
|
-
}
|
|
827
|
-
const startTime = Date.now();
|
|
828
|
-
const element = elementFromCache || // try to match element from cache
|
|
829
|
-
matchElementFromPlan(param, pageContext.tree) || // try to match element from plan
|
|
830
|
-
(await this.insight.locate(param, {
|
|
1009
|
+
const elementFromCache = userExpectedPathHitFlag ? null : await matchElementFromCache(
|
|
1010
|
+
this,
|
|
1011
|
+
xpaths,
|
|
1012
|
+
cachePrompt,
|
|
1013
|
+
param.cacheable
|
|
1014
|
+
);
|
|
1015
|
+
const cacheHitFlag = !!elementFromCache;
|
|
1016
|
+
const elementFromPlan = !userExpectedPathHitFlag && !cacheHitFlag ? matchElementFromPlan(param, pageContext.tree) : void 0;
|
|
1017
|
+
const planHitFlag = !!elementFromPlan;
|
|
1018
|
+
const elementFromAiLocate = !userExpectedPathHitFlag && !cacheHitFlag && !planHitFlag ? (await this.insight.locate(param, {
|
|
1019
|
+
// fallback to ai locate
|
|
831
1020
|
context: pageContext
|
|
832
|
-
})).element;
|
|
1021
|
+
})).element : void 0;
|
|
1022
|
+
const aiLocateHitFlag = !!elementFromAiLocate;
|
|
1023
|
+
const element = elementFromXpath || // highest priority
|
|
1024
|
+
elementFromCache || // second priority
|
|
1025
|
+
elementFromPlan || // third priority
|
|
1026
|
+
elementFromAiLocate;
|
|
833
1027
|
let currentXpaths;
|
|
834
1028
|
if (element && this.taskCache && !cacheHitFlag && param?.cacheable !== false) {
|
|
835
1029
|
const elementXpaths = await this.getElementXpath(
|
|
@@ -847,7 +1041,7 @@ var PageTaskExecutor = class {
|
|
|
847
1041
|
locateCacheRecord
|
|
848
1042
|
);
|
|
849
1043
|
} else {
|
|
850
|
-
|
|
1044
|
+
debug2(
|
|
851
1045
|
"no xpaths found, will not update cache",
|
|
852
1046
|
cachePrompt,
|
|
853
1047
|
elementXpaths
|
|
@@ -857,16 +1051,44 @@ var PageTaskExecutor = class {
|
|
|
857
1051
|
if (!element) {
|
|
858
1052
|
throw new Error(`Element not found: ${param.prompt}`);
|
|
859
1053
|
}
|
|
1054
|
+
let hitBy;
|
|
1055
|
+
if (userExpectedPathHitFlag) {
|
|
1056
|
+
hitBy = {
|
|
1057
|
+
from: "User expected path",
|
|
1058
|
+
context: {
|
|
1059
|
+
xpath: param.xpath
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
} else if (cacheHitFlag) {
|
|
1063
|
+
hitBy = {
|
|
1064
|
+
from: "Cache",
|
|
1065
|
+
context: {
|
|
1066
|
+
xpathsFromCache: xpaths,
|
|
1067
|
+
xpathsToSave: currentXpaths
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
} else if (planHitFlag) {
|
|
1071
|
+
hitBy = {
|
|
1072
|
+
from: "Planning",
|
|
1073
|
+
context: {
|
|
1074
|
+
id: elementFromPlan?.id,
|
|
1075
|
+
bbox: elementFromPlan?.bbox
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
} else if (aiLocateHitFlag) {
|
|
1079
|
+
hitBy = {
|
|
1080
|
+
from: "AI model",
|
|
1081
|
+
context: {
|
|
1082
|
+
prompt: param.prompt
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
860
1086
|
return {
|
|
861
1087
|
output: {
|
|
862
1088
|
element
|
|
863
1089
|
},
|
|
864
1090
|
pageContext,
|
|
865
|
-
|
|
866
|
-
hit: cacheHitFlag,
|
|
867
|
-
originalXpaths: xpaths,
|
|
868
|
-
currentXpaths
|
|
869
|
-
}
|
|
1091
|
+
hitBy
|
|
870
1092
|
};
|
|
871
1093
|
}
|
|
872
1094
|
};
|
|
@@ -962,7 +1184,7 @@ var PageTaskExecutor = class {
|
|
|
962
1184
|
thought: plan2.thought,
|
|
963
1185
|
locate: plan2.locate,
|
|
964
1186
|
executor: async (param, { element }) => {
|
|
965
|
-
(0,
|
|
1187
|
+
(0, import_utils8.assert)(element, "Element not found, cannot tap");
|
|
966
1188
|
await this.page.mouse.click(element.center[0], element.center[1]);
|
|
967
1189
|
}
|
|
968
1190
|
};
|
|
@@ -974,7 +1196,7 @@ var PageTaskExecutor = class {
|
|
|
974
1196
|
thought: plan2.thought,
|
|
975
1197
|
locate: plan2.locate,
|
|
976
1198
|
executor: async (param, { element }) => {
|
|
977
|
-
(0,
|
|
1199
|
+
(0, import_utils8.assert)(element, "Element not found, cannot right click");
|
|
978
1200
|
await this.page.mouse.click(
|
|
979
1201
|
element.center[0],
|
|
980
1202
|
element.center[1],
|
|
@@ -991,7 +1213,7 @@ var PageTaskExecutor = class {
|
|
|
991
1213
|
thought: plan2.thought,
|
|
992
1214
|
locate: plan2.locate,
|
|
993
1215
|
executor: async (taskParam) => {
|
|
994
|
-
(0,
|
|
1216
|
+
(0, import_utils8.assert)(
|
|
995
1217
|
taskParam?.start_box && taskParam?.end_box,
|
|
996
1218
|
"No start_box or end_box to drag"
|
|
997
1219
|
);
|
|
@@ -1006,7 +1228,7 @@ var PageTaskExecutor = class {
|
|
|
1006
1228
|
thought: plan2.thought,
|
|
1007
1229
|
locate: plan2.locate,
|
|
1008
1230
|
executor: async (param, { element }) => {
|
|
1009
|
-
(0,
|
|
1231
|
+
(0, import_utils8.assert)(element, "Element not found, cannot hover");
|
|
1010
1232
|
await this.page.mouse.move(element.center[0], element.center[1]);
|
|
1011
1233
|
}
|
|
1012
1234
|
};
|
|
@@ -1058,7 +1280,7 @@ var PageTaskExecutor = class {
|
|
|
1058
1280
|
`Unknown scroll direction: ${taskParam.direction}`
|
|
1059
1281
|
);
|
|
1060
1282
|
}
|
|
1061
|
-
await (0,
|
|
1283
|
+
await (0, import_utils7.sleep)(500);
|
|
1062
1284
|
} else {
|
|
1063
1285
|
throw new Error(
|
|
1064
1286
|
`Unknown scroll event type: ${scrollToEventName}, taskParam: ${JSON.stringify(
|
|
@@ -1077,7 +1299,7 @@ var PageTaskExecutor = class {
|
|
|
1077
1299
|
thought: plan2.thought,
|
|
1078
1300
|
locate: plan2.locate,
|
|
1079
1301
|
executor: async (taskParam) => {
|
|
1080
|
-
await (0,
|
|
1302
|
+
await (0, import_utils7.sleep)(taskParam?.timeMs || 3e3);
|
|
1081
1303
|
}
|
|
1082
1304
|
};
|
|
1083
1305
|
tasks.push(taskActionSleep);
|
|
@@ -1125,7 +1347,7 @@ var PageTaskExecutor = class {
|
|
|
1125
1347
|
thought: plan2.thought,
|
|
1126
1348
|
locate: plan2.locate,
|
|
1127
1349
|
executor: async (param) => {
|
|
1128
|
-
(0,
|
|
1350
|
+
(0, import_utils8.assert)(
|
|
1129
1351
|
isAndroidPage(this.page),
|
|
1130
1352
|
"Cannot use home button on non-Android devices"
|
|
1131
1353
|
);
|
|
@@ -1141,7 +1363,7 @@ var PageTaskExecutor = class {
|
|
|
1141
1363
|
thought: plan2.thought,
|
|
1142
1364
|
locate: plan2.locate,
|
|
1143
1365
|
executor: async (param) => {
|
|
1144
|
-
(0,
|
|
1366
|
+
(0, import_utils8.assert)(
|
|
1145
1367
|
isAndroidPage(this.page),
|
|
1146
1368
|
"Cannot use back button on non-Android devices"
|
|
1147
1369
|
);
|
|
@@ -1157,7 +1379,7 @@ var PageTaskExecutor = class {
|
|
|
1157
1379
|
thought: plan2.thought,
|
|
1158
1380
|
locate: plan2.locate,
|
|
1159
1381
|
executor: async (param) => {
|
|
1160
|
-
(0,
|
|
1382
|
+
(0, import_utils8.assert)(
|
|
1161
1383
|
isAndroidPage(this.page),
|
|
1162
1384
|
"Cannot use recent apps button on non-Android devices"
|
|
1163
1385
|
);
|
|
@@ -1308,7 +1530,7 @@ var PageTaskExecutor = class {
|
|
|
1308
1530
|
}
|
|
1309
1531
|
}
|
|
1310
1532
|
if (finalActions.length === 0) {
|
|
1311
|
-
(0,
|
|
1533
|
+
(0, import_utils8.assert)(
|
|
1312
1534
|
!more_actions_needed_by_instruction || sleep2,
|
|
1313
1535
|
error ? `Failed to plan: ${error}` : planParsingError || "No plan found"
|
|
1314
1536
|
);
|
|
@@ -1546,7 +1768,7 @@ var PageTaskExecutor = class {
|
|
|
1546
1768
|
);
|
|
1547
1769
|
let outputResult = data;
|
|
1548
1770
|
if (ifTypeRestricted) {
|
|
1549
|
-
(0,
|
|
1771
|
+
(0, import_utils8.assert)(data?.result !== void 0, "No result in query data");
|
|
1550
1772
|
outputResult = data.result;
|
|
1551
1773
|
}
|
|
1552
1774
|
return {
|
|
@@ -1642,9 +1864,9 @@ var PageTaskExecutor = class {
|
|
|
1642
1864
|
onTaskStart: this.onTaskStartCallback
|
|
1643
1865
|
});
|
|
1644
1866
|
const { timeoutMs, checkIntervalMs } = opt;
|
|
1645
|
-
(0,
|
|
1646
|
-
(0,
|
|
1647
|
-
(0,
|
|
1867
|
+
(0, import_utils8.assert)(assertion, "No assertion for waitFor");
|
|
1868
|
+
(0, import_utils8.assert)(timeoutMs, "No timeoutMs for waitFor");
|
|
1869
|
+
(0, import_utils8.assert)(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1648
1870
|
const overallStartTime = Date.now();
|
|
1649
1871
|
let startTime = Date.now();
|
|
1650
1872
|
let errorThought = "";
|
|
@@ -1698,9 +1920,9 @@ var PageTaskExecutor = class {
|
|
|
1698
1920
|
};
|
|
1699
1921
|
|
|
1700
1922
|
// src/common/plan-builder.ts
|
|
1701
|
-
var
|
|
1702
|
-
var
|
|
1703
|
-
var
|
|
1923
|
+
var import_logger3 = require("@midscene/shared/logger");
|
|
1924
|
+
var import_utils10 = require("@midscene/shared/utils");
|
|
1925
|
+
var debug3 = (0, import_logger3.getDebug)("plan-builder");
|
|
1704
1926
|
function buildPlans(type, locateParam, param) {
|
|
1705
1927
|
let returnPlans = [];
|
|
1706
1928
|
const locatePlan = locateParam ? {
|
|
@@ -1710,8 +1932,8 @@ function buildPlans(type, locateParam, param) {
|
|
|
1710
1932
|
thought: ""
|
|
1711
1933
|
} : null;
|
|
1712
1934
|
if (type === "Tap" || type === "Hover" || type === "RightClick") {
|
|
1713
|
-
(0,
|
|
1714
|
-
(0,
|
|
1935
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1936
|
+
(0, import_utils10.assert)(locatePlan, `missing locate info for action "${type}"`);
|
|
1715
1937
|
const tapPlan = {
|
|
1716
1938
|
type,
|
|
1717
1939
|
param: null,
|
|
@@ -1722,9 +1944,9 @@ function buildPlans(type, locateParam, param) {
|
|
|
1722
1944
|
}
|
|
1723
1945
|
if (type === "Input" || type === "KeyboardPress") {
|
|
1724
1946
|
if (type === "Input") {
|
|
1725
|
-
(0,
|
|
1947
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1726
1948
|
}
|
|
1727
|
-
(0,
|
|
1949
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1728
1950
|
const inputPlan = {
|
|
1729
1951
|
type,
|
|
1730
1952
|
param,
|
|
@@ -1738,7 +1960,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1738
1960
|
}
|
|
1739
1961
|
}
|
|
1740
1962
|
if (type === "Scroll") {
|
|
1741
|
-
(0,
|
|
1963
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1742
1964
|
const scrollPlan = {
|
|
1743
1965
|
type,
|
|
1744
1966
|
param,
|
|
@@ -1752,7 +1974,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1752
1974
|
}
|
|
1753
1975
|
}
|
|
1754
1976
|
if (type === "Sleep") {
|
|
1755
|
-
(0,
|
|
1977
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1756
1978
|
const sleepPlan = {
|
|
1757
1979
|
type,
|
|
1758
1980
|
param,
|
|
@@ -1762,7 +1984,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1762
1984
|
returnPlans = [sleepPlan];
|
|
1763
1985
|
}
|
|
1764
1986
|
if (type === "Locate") {
|
|
1765
|
-
(0,
|
|
1987
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1766
1988
|
const locatePlan2 = {
|
|
1767
1989
|
type,
|
|
1768
1990
|
param: locateParam,
|
|
@@ -1772,187 +1994,12 @@ function buildPlans(type, locateParam, param) {
|
|
|
1772
1994
|
returnPlans = [locatePlan2];
|
|
1773
1995
|
}
|
|
1774
1996
|
if (returnPlans) {
|
|
1775
|
-
|
|
1997
|
+
debug3("buildPlans", returnPlans);
|
|
1776
1998
|
return returnPlans;
|
|
1777
1999
|
}
|
|
1778
2000
|
throw new Error(`Not supported type: ${type}`);
|
|
1779
2001
|
}
|
|
1780
2002
|
|
|
1781
|
-
// src/common/task-cache.ts
|
|
1782
|
-
var import_node_assert = __toESM(require("assert"));
|
|
1783
|
-
var import_node_fs2 = require("fs");
|
|
1784
|
-
var import_node_path2 = require("path");
|
|
1785
|
-
var import_common2 = require("@midscene/shared/common");
|
|
1786
|
-
var import_logger3 = require("@midscene/shared/logger");
|
|
1787
|
-
var import_utils9 = require("@midscene/shared/utils");
|
|
1788
|
-
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
1789
|
-
var import_semver = __toESM(require("semver"));
|
|
1790
|
-
|
|
1791
|
-
// package.json
|
|
1792
|
-
var version = "0.19.1";
|
|
1793
|
-
|
|
1794
|
-
// src/common/task-cache.ts
|
|
1795
|
-
var debug3 = (0, import_logger3.getDebug)("cache");
|
|
1796
|
-
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
1797
|
-
var cacheFileExt = ".cache.yaml";
|
|
1798
|
-
var TaskCache = class {
|
|
1799
|
-
// Track matched records
|
|
1800
|
-
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
1801
|
-
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
1802
|
-
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
1803
|
-
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
1804
|
-
this.cacheFilePath = import_utils9.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
1805
|
-
this.isCacheResultUsed = isCacheResultUsed;
|
|
1806
|
-
let cacheContent;
|
|
1807
|
-
if (this.cacheFilePath) {
|
|
1808
|
-
cacheContent = this.loadCacheFromFile();
|
|
1809
|
-
}
|
|
1810
|
-
if (!cacheContent) {
|
|
1811
|
-
cacheContent = {
|
|
1812
|
-
midsceneVersion: version,
|
|
1813
|
-
cacheId: this.cacheId,
|
|
1814
|
-
caches: []
|
|
1815
|
-
};
|
|
1816
|
-
}
|
|
1817
|
-
this.cache = cacheContent;
|
|
1818
|
-
this.cacheOriginalLength = this.cache.caches.length;
|
|
1819
|
-
}
|
|
1820
|
-
matchCache(prompt, type) {
|
|
1821
|
-
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
1822
|
-
const item = this.cache.caches[i];
|
|
1823
|
-
const key = `${type}:${prompt}:${i}`;
|
|
1824
|
-
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
1825
|
-
this.matchedCacheIndices.add(key);
|
|
1826
|
-
debug3(
|
|
1827
|
-
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
1828
|
-
type,
|
|
1829
|
-
prompt,
|
|
1830
|
-
i
|
|
1831
|
-
);
|
|
1832
|
-
return {
|
|
1833
|
-
cacheContent: item,
|
|
1834
|
-
updateFn: (cb) => {
|
|
1835
|
-
debug3(
|
|
1836
|
-
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
1837
|
-
type,
|
|
1838
|
-
prompt,
|
|
1839
|
-
i
|
|
1840
|
-
);
|
|
1841
|
-
cb(item);
|
|
1842
|
-
debug3(
|
|
1843
|
-
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
1844
|
-
type,
|
|
1845
|
-
prompt,
|
|
1846
|
-
i
|
|
1847
|
-
);
|
|
1848
|
-
this.flushCacheToFile();
|
|
1849
|
-
}
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
debug3("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
1854
|
-
return void 0;
|
|
1855
|
-
}
|
|
1856
|
-
matchPlanCache(prompt) {
|
|
1857
|
-
return this.matchCache(prompt, "plan");
|
|
1858
|
-
}
|
|
1859
|
-
matchLocateCache(prompt) {
|
|
1860
|
-
return this.matchCache(prompt, "locate");
|
|
1861
|
-
}
|
|
1862
|
-
appendCache(cache) {
|
|
1863
|
-
debug3("will append cache", cache);
|
|
1864
|
-
this.cache.caches.push(cache);
|
|
1865
|
-
this.flushCacheToFile();
|
|
1866
|
-
}
|
|
1867
|
-
loadCacheFromFile() {
|
|
1868
|
-
const cacheFile = this.cacheFilePath;
|
|
1869
|
-
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
1870
|
-
if (!(0, import_node_fs2.existsSync)(cacheFile)) {
|
|
1871
|
-
debug3("no cache file found, path: %s", cacheFile);
|
|
1872
|
-
return void 0;
|
|
1873
|
-
}
|
|
1874
|
-
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
1875
|
-
if ((0, import_node_fs2.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
1876
|
-
console.warn(
|
|
1877
|
-
`An outdated cache file from an earlier version of Midscene has been detected. Since version 0.17, we have implemented an improved caching strategy. Please delete the old file located at: ${jsonTypeCacheFile}.`
|
|
1878
|
-
);
|
|
1879
|
-
return void 0;
|
|
1880
|
-
}
|
|
1881
|
-
try {
|
|
1882
|
-
const data = (0, import_node_fs2.readFileSync)(cacheFile, "utf8");
|
|
1883
|
-
const jsonData = import_js_yaml3.default.load(data);
|
|
1884
|
-
if (!version) {
|
|
1885
|
-
debug3("no midscene version info, will not read cache from file");
|
|
1886
|
-
return void 0;
|
|
1887
|
-
}
|
|
1888
|
-
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
1889
|
-
console.warn(
|
|
1890
|
-
`You are using an old version of Midscene cache file, and we cannot match any info from it. Starting from Midscene v0.17, we changed our strategy to use xpath for cache info, providing better performance.
|
|
1891
|
-
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
1892
|
-
cache file: ${cacheFile}`
|
|
1893
|
-
);
|
|
1894
|
-
return void 0;
|
|
1895
|
-
}
|
|
1896
|
-
debug3(
|
|
1897
|
-
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
1898
|
-
cacheFile,
|
|
1899
|
-
jsonData.midsceneVersion,
|
|
1900
|
-
jsonData.caches.length
|
|
1901
|
-
);
|
|
1902
|
-
jsonData.midsceneVersion = version;
|
|
1903
|
-
return jsonData;
|
|
1904
|
-
} catch (err) {
|
|
1905
|
-
debug3(
|
|
1906
|
-
"cache file exists but load failed, path: %s, error: %s",
|
|
1907
|
-
cacheFile,
|
|
1908
|
-
err
|
|
1909
|
-
);
|
|
1910
|
-
return void 0;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
flushCacheToFile() {
|
|
1914
|
-
if (!version) {
|
|
1915
|
-
debug3("no midscene version info, will not write cache to file");
|
|
1916
|
-
return;
|
|
1917
|
-
}
|
|
1918
|
-
if (!this.cacheFilePath) {
|
|
1919
|
-
debug3("no cache file path, will not write cache to file");
|
|
1920
|
-
return;
|
|
1921
|
-
}
|
|
1922
|
-
try {
|
|
1923
|
-
const dir = (0, import_node_path2.dirname)(this.cacheFilePath);
|
|
1924
|
-
if (!(0, import_node_fs2.existsSync)(dir)) {
|
|
1925
|
-
(0, import_node_fs2.mkdirSync)(dir, { recursive: true });
|
|
1926
|
-
debug3("created cache directory: %s", dir);
|
|
1927
|
-
}
|
|
1928
|
-
const yamlData = import_js_yaml3.default.dump(this.cache);
|
|
1929
|
-
(0, import_node_fs2.writeFileSync)(this.cacheFilePath, yamlData);
|
|
1930
|
-
debug3("cache flushed to file: %s", this.cacheFilePath);
|
|
1931
|
-
} catch (err) {
|
|
1932
|
-
debug3(
|
|
1933
|
-
"write cache to file failed, path: %s, error: %s",
|
|
1934
|
-
this.cacheFilePath,
|
|
1935
|
-
err
|
|
1936
|
-
);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
1940
|
-
if (cachedRecord) {
|
|
1941
|
-
if (newRecord.type === "plan") {
|
|
1942
|
-
cachedRecord.updateFn((cache) => {
|
|
1943
|
-
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
1944
|
-
});
|
|
1945
|
-
} else {
|
|
1946
|
-
cachedRecord.updateFn((cache) => {
|
|
1947
|
-
cache.xpaths = newRecord.xpaths;
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
} else {
|
|
1951
|
-
this.appendCache(newRecord);
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
};
|
|
1955
|
-
|
|
1956
2003
|
// src/common/agent.ts
|
|
1957
2004
|
var debug4 = (0, import_logger4.getDebug)("web-integration");
|
|
1958
2005
|
var distanceOfTwoPoints = (p1, p2) => {
|
|
@@ -2081,10 +2128,12 @@ ${errorTask?.errorStack}`);
|
|
|
2081
2128
|
const prompt = opt.prompt ?? locatePrompt;
|
|
2082
2129
|
const deepThink = opt.deepThink ?? false;
|
|
2083
2130
|
const cacheable = opt.cacheable ?? true;
|
|
2131
|
+
const xpath = opt.xpath;
|
|
2084
2132
|
return {
|
|
2085
2133
|
prompt,
|
|
2086
2134
|
deepThink,
|
|
2087
|
-
cacheable
|
|
2135
|
+
cacheable,
|
|
2136
|
+
xpath
|
|
2088
2137
|
};
|
|
2089
2138
|
}
|
|
2090
2139
|
return {
|
|
@@ -2242,6 +2291,9 @@ ${errorTask?.errorStack}`);
|
|
|
2242
2291
|
this.afterTaskRunning(executor);
|
|
2243
2292
|
return output;
|
|
2244
2293
|
}
|
|
2294
|
+
async aiAsk(prompt, opt = defaultInsightExtractOption) {
|
|
2295
|
+
return this.aiString(prompt, opt);
|
|
2296
|
+
}
|
|
2245
2297
|
async describeElementAtPoint(center, opt) {
|
|
2246
2298
|
const { verifyPrompt = true, retryLimit = 3 } = opt || {};
|
|
2247
2299
|
let success = false;
|