@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
|
@@ -24,11 +24,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
|
|
25
25
|
// src/common/utils.ts
|
|
26
26
|
var import_ai_model = require("@midscene/core/ai-model");
|
|
27
|
-
var
|
|
27
|
+
var import_utils3 = require("@midscene/core/utils");
|
|
28
28
|
var import_env = require("@midscene/shared/env");
|
|
29
29
|
var import_extractor = require("@midscene/shared/extractor");
|
|
30
30
|
var import_img = require("@midscene/shared/img");
|
|
31
|
-
var
|
|
31
|
+
var import_utils4 = require("@midscene/shared/utils");
|
|
32
32
|
var import_dayjs = __toESM(require("dayjs"));
|
|
33
33
|
|
|
34
34
|
// src/web-element.ts
|
|
@@ -56,14 +56,189 @@ var WebElementInfo = class {
|
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
// src/common/task-cache.ts
|
|
60
|
+
var import_node_assert = __toESM(require("assert"));
|
|
61
|
+
var import_node_fs = require("fs");
|
|
62
|
+
var import_node_path = require("path");
|
|
63
|
+
var import_common = require("@midscene/shared/common");
|
|
64
|
+
var import_logger = require("@midscene/shared/logger");
|
|
65
|
+
var import_utils = require("@midscene/shared/utils");
|
|
66
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
67
|
+
var import_semver = __toESM(require("semver"));
|
|
68
|
+
|
|
69
|
+
// package.json
|
|
70
|
+
var version = "0.20.0";
|
|
71
|
+
|
|
72
|
+
// src/common/task-cache.ts
|
|
73
|
+
var debug = (0, import_logger.getDebug)("cache");
|
|
74
|
+
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
75
|
+
var cacheFileExt = ".cache.yaml";
|
|
76
|
+
var TaskCache = class {
|
|
77
|
+
// Track matched records
|
|
78
|
+
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
79
|
+
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
80
|
+
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
81
|
+
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
82
|
+
this.cacheFilePath = import_utils.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
83
|
+
this.isCacheResultUsed = isCacheResultUsed;
|
|
84
|
+
let cacheContent;
|
|
85
|
+
if (this.cacheFilePath) {
|
|
86
|
+
cacheContent = this.loadCacheFromFile();
|
|
87
|
+
}
|
|
88
|
+
if (!cacheContent) {
|
|
89
|
+
cacheContent = {
|
|
90
|
+
midsceneVersion: version,
|
|
91
|
+
cacheId: this.cacheId,
|
|
92
|
+
caches: []
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
this.cache = cacheContent;
|
|
96
|
+
this.cacheOriginalLength = this.cache.caches.length;
|
|
97
|
+
}
|
|
98
|
+
matchCache(prompt, type) {
|
|
99
|
+
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
100
|
+
const item = this.cache.caches[i];
|
|
101
|
+
const key = `${type}:${prompt}:${i}`;
|
|
102
|
+
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
103
|
+
this.matchedCacheIndices.add(key);
|
|
104
|
+
debug(
|
|
105
|
+
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
106
|
+
type,
|
|
107
|
+
prompt,
|
|
108
|
+
i
|
|
109
|
+
);
|
|
110
|
+
return {
|
|
111
|
+
cacheContent: item,
|
|
112
|
+
updateFn: (cb) => {
|
|
113
|
+
debug(
|
|
114
|
+
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
115
|
+
type,
|
|
116
|
+
prompt,
|
|
117
|
+
i
|
|
118
|
+
);
|
|
119
|
+
cb(item);
|
|
120
|
+
debug(
|
|
121
|
+
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
122
|
+
type,
|
|
123
|
+
prompt,
|
|
124
|
+
i
|
|
125
|
+
);
|
|
126
|
+
this.flushCacheToFile();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
debug("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
matchPlanCache(prompt) {
|
|
135
|
+
return this.matchCache(prompt, "plan");
|
|
136
|
+
}
|
|
137
|
+
matchLocateCache(prompt) {
|
|
138
|
+
return this.matchCache(prompt, "locate");
|
|
139
|
+
}
|
|
140
|
+
appendCache(cache) {
|
|
141
|
+
debug("will append cache", cache);
|
|
142
|
+
this.cache.caches.push(cache);
|
|
143
|
+
this.flushCacheToFile();
|
|
144
|
+
}
|
|
145
|
+
loadCacheFromFile() {
|
|
146
|
+
const cacheFile = this.cacheFilePath;
|
|
147
|
+
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
148
|
+
if (!(0, import_node_fs.existsSync)(cacheFile)) {
|
|
149
|
+
debug("no cache file found, path: %s", cacheFile);
|
|
150
|
+
return void 0;
|
|
151
|
+
}
|
|
152
|
+
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
153
|
+
if ((0, import_node_fs.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
154
|
+
console.warn(
|
|
155
|
+
`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}.`
|
|
156
|
+
);
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const data = (0, import_node_fs.readFileSync)(cacheFile, "utf8");
|
|
161
|
+
const jsonData = import_js_yaml.default.load(data);
|
|
162
|
+
if (!version) {
|
|
163
|
+
debug("no midscene version info, will not read cache from file");
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
167
|
+
console.warn(
|
|
168
|
+
`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.
|
|
169
|
+
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
170
|
+
cache file: ${cacheFile}`
|
|
171
|
+
);
|
|
172
|
+
return void 0;
|
|
173
|
+
}
|
|
174
|
+
debug(
|
|
175
|
+
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
176
|
+
cacheFile,
|
|
177
|
+
jsonData.midsceneVersion,
|
|
178
|
+
jsonData.caches.length
|
|
179
|
+
);
|
|
180
|
+
jsonData.midsceneVersion = version;
|
|
181
|
+
return jsonData;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
debug(
|
|
184
|
+
"cache file exists but load failed, path: %s, error: %s",
|
|
185
|
+
cacheFile,
|
|
186
|
+
err
|
|
187
|
+
);
|
|
188
|
+
return void 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
flushCacheToFile() {
|
|
192
|
+
if (!version) {
|
|
193
|
+
debug("no midscene version info, will not write cache to file");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!this.cacheFilePath) {
|
|
197
|
+
debug("no cache file path, will not write cache to file");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const dir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
202
|
+
if (!(0, import_node_fs.existsSync)(dir)) {
|
|
203
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
204
|
+
debug("created cache directory: %s", dir);
|
|
205
|
+
}
|
|
206
|
+
const yamlData = import_js_yaml.default.dump(this.cache);
|
|
207
|
+
(0, import_node_fs.writeFileSync)(this.cacheFilePath, yamlData);
|
|
208
|
+
debug("cache flushed to file: %s", this.cacheFilePath);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
debug(
|
|
211
|
+
"write cache to file failed, path: %s, error: %s",
|
|
212
|
+
this.cacheFilePath,
|
|
213
|
+
err
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
218
|
+
if (cachedRecord) {
|
|
219
|
+
if (newRecord.type === "plan") {
|
|
220
|
+
cachedRecord.updateFn((cache) => {
|
|
221
|
+
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
cachedRecord.updateFn((cache) => {
|
|
225
|
+
cache.xpaths = newRecord.xpaths;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
this.appendCache(newRecord);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
59
234
|
// src/common/utils.ts
|
|
60
235
|
async function parseContextFromWebPage(page, _opt) {
|
|
61
|
-
(0,
|
|
236
|
+
(0, import_utils4.assert)(page, "page is required");
|
|
62
237
|
if (page._forceUsePageContext) {
|
|
63
238
|
return await page._forceUsePageContext();
|
|
64
239
|
}
|
|
65
240
|
const url = await page.url();
|
|
66
|
-
(0,
|
|
241
|
+
(0, import_utils3.uploadTestInfoToServer)({ testUrl: url });
|
|
67
242
|
let screenshotBase64;
|
|
68
243
|
let tree;
|
|
69
244
|
await Promise.all([
|
|
@@ -85,7 +260,7 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
85
260
|
isVisible
|
|
86
261
|
});
|
|
87
262
|
});
|
|
88
|
-
(0,
|
|
263
|
+
(0, import_utils4.assert)(screenshotBase64, "screenshotBase64 is required");
|
|
89
264
|
const size = await page.size();
|
|
90
265
|
if (size.dpr && size.dpr > 1) {
|
|
91
266
|
screenshotBase64 = await (0, import_img.resizeImgBase64)(screenshotBase64, {
|
|
@@ -103,11 +278,11 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
103
278
|
function reportFileName(tag = "web") {
|
|
104
279
|
const reportTagName = (0, import_env.getAIConfig)(import_env.MIDSCENE_REPORT_TAG_NAME);
|
|
105
280
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss");
|
|
106
|
-
const uniqueId = (0,
|
|
281
|
+
const uniqueId = (0, import_utils4.uuid)().substring(0, 8);
|
|
107
282
|
return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;
|
|
108
283
|
}
|
|
109
284
|
function printReportMsg(filepath) {
|
|
110
|
-
(0,
|
|
285
|
+
(0, import_utils4.logMsg)(`Midscene - report file updated: ${filepath}`);
|
|
111
286
|
}
|
|
112
287
|
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
|
113
288
|
function replaceIllegalPathCharsAndSpace(str) {
|
|
@@ -133,6 +308,28 @@ function matchElementFromPlan(planLocateParam, tree) {
|
|
|
133
308
|
}
|
|
134
309
|
return void 0;
|
|
135
310
|
}
|
|
311
|
+
async function matchElementFromCache(taskExecutor, xpaths, cachePrompt, cacheable) {
|
|
312
|
+
try {
|
|
313
|
+
if (xpaths?.length && taskExecutor.taskCache?.isCacheResultUsed && cacheable !== false) {
|
|
314
|
+
for (let i = 0; i < xpaths.length; i++) {
|
|
315
|
+
const element = await taskExecutor.page.getElementInfoByXpath(
|
|
316
|
+
xpaths[i]
|
|
317
|
+
);
|
|
318
|
+
if (element?.id) {
|
|
319
|
+
debug("cache hit, prompt: %s", cachePrompt);
|
|
320
|
+
debug(
|
|
321
|
+
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
322
|
+
xpaths[i],
|
|
323
|
+
element?.id
|
|
324
|
+
);
|
|
325
|
+
return element;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} catch (error) {
|
|
330
|
+
debug("get element info by xpath error: ", error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
136
333
|
function trimContextByViewport(execution) {
|
|
137
334
|
function filterVisibleTree(node) {
|
|
138
335
|
if (!node)
|
|
@@ -175,10 +372,10 @@ var import_core2 = require("@midscene/core");
|
|
|
175
372
|
var import_js_yaml4 = __toESM(require("js-yaml"));
|
|
176
373
|
|
|
177
374
|
// src/yaml/player.ts
|
|
178
|
-
var
|
|
179
|
-
var
|
|
180
|
-
var
|
|
181
|
-
var
|
|
375
|
+
var import_node_fs2 = require("fs");
|
|
376
|
+
var import_node_path2 = require("path");
|
|
377
|
+
var import_utils5 = require("@midscene/shared/utils");
|
|
378
|
+
var import_common2 = require("@midscene/shared/common");
|
|
182
379
|
var ScriptPlayer = class {
|
|
183
380
|
constructor(script, setupAgent, onTaskStatusChange) {
|
|
184
381
|
this.script = script;
|
|
@@ -190,23 +387,23 @@ var ScriptPlayer = class {
|
|
|
190
387
|
this.pageAgent = null;
|
|
191
388
|
this.result = {};
|
|
192
389
|
this.target = script.target || script.web || script.android;
|
|
193
|
-
if (
|
|
390
|
+
if (import_utils5.ifInBrowser) {
|
|
194
391
|
this.output = void 0;
|
|
195
392
|
} else if (this.target?.output) {
|
|
196
|
-
this.output = (0,
|
|
393
|
+
this.output = (0, import_node_path2.resolve)(process.cwd(), this.target.output);
|
|
197
394
|
} else {
|
|
198
|
-
this.output = (0,
|
|
395
|
+
this.output = (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
|
|
199
396
|
}
|
|
200
|
-
if (
|
|
397
|
+
if (import_utils5.ifInBrowser) {
|
|
201
398
|
this.unstableLogContent = void 0;
|
|
202
399
|
} else if (typeof this.target?.unstableLogContent === "string") {
|
|
203
|
-
this.unstableLogContent = (0,
|
|
400
|
+
this.unstableLogContent = (0, import_node_path2.resolve)(
|
|
204
401
|
process.cwd(),
|
|
205
402
|
this.target.unstableLogContent
|
|
206
403
|
);
|
|
207
404
|
} else if (this.target?.unstableLogContent === true) {
|
|
208
|
-
this.unstableLogContent = (0,
|
|
209
|
-
(0,
|
|
405
|
+
this.unstableLogContent = (0, import_node_path2.join)(
|
|
406
|
+
(0, import_common2.getMidsceneRunSubDir)("output"),
|
|
210
407
|
"unstableLogContent.json"
|
|
211
408
|
);
|
|
212
409
|
}
|
|
@@ -251,28 +448,28 @@ var ScriptPlayer = class {
|
|
|
251
448
|
}
|
|
252
449
|
flushResult() {
|
|
253
450
|
if (Object.keys(this.result).length && this.output) {
|
|
254
|
-
const output = (0,
|
|
255
|
-
const outputDir = (0,
|
|
256
|
-
if (!(0,
|
|
257
|
-
(0,
|
|
451
|
+
const output = (0, import_node_path2.resolve)(process.cwd(), this.output);
|
|
452
|
+
const outputDir = (0, import_node_path2.dirname)(output);
|
|
453
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
454
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
258
455
|
}
|
|
259
|
-
(0,
|
|
456
|
+
(0, import_node_fs2.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
|
|
260
457
|
}
|
|
261
458
|
}
|
|
262
459
|
flushUnstableLogContent() {
|
|
263
460
|
if (this.unstableLogContent) {
|
|
264
461
|
const content = this.pageAgent?._unstableLogContent();
|
|
265
|
-
const filePath = (0,
|
|
266
|
-
const outputDir = (0,
|
|
267
|
-
if (!(0,
|
|
268
|
-
(0,
|
|
462
|
+
const filePath = (0, import_node_path2.resolve)(process.cwd(), this.unstableLogContent);
|
|
463
|
+
const outputDir = (0, import_node_path2.dirname)(filePath);
|
|
464
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
465
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
269
466
|
}
|
|
270
|
-
(0,
|
|
467
|
+
(0, import_node_fs2.writeFileSync)(filePath, JSON.stringify(content, null, 2));
|
|
271
468
|
}
|
|
272
469
|
}
|
|
273
470
|
async playTask(taskStatus, agent) {
|
|
274
471
|
const { flow } = taskStatus;
|
|
275
|
-
(0,
|
|
472
|
+
(0, import_utils5.assert)(flow, "missing flow in task");
|
|
276
473
|
for (const flowItemIndex in flow) {
|
|
277
474
|
const currentStep = Number.parseInt(flowItemIndex, 10);
|
|
278
475
|
taskStatus.currentStep = currentStep;
|
|
@@ -280,8 +477,8 @@ var ScriptPlayer = class {
|
|
|
280
477
|
if ("aiAction" in flowItem || "ai" in flowItem) {
|
|
281
478
|
const actionTask = flowItem;
|
|
282
479
|
const prompt = actionTask.aiAction || actionTask.ai;
|
|
283
|
-
(0,
|
|
284
|
-
(0,
|
|
480
|
+
(0, import_utils5.assert)(prompt, "missing prompt for ai (aiAction)");
|
|
481
|
+
(0, import_utils5.assert)(
|
|
285
482
|
typeof prompt === "string",
|
|
286
483
|
"prompt for aiAction must be a string"
|
|
287
484
|
);
|
|
@@ -292,8 +489,8 @@ var ScriptPlayer = class {
|
|
|
292
489
|
const assertTask = flowItem;
|
|
293
490
|
const prompt = assertTask.aiAssert;
|
|
294
491
|
const msg = assertTask.errorMessage;
|
|
295
|
-
(0,
|
|
296
|
-
(0,
|
|
492
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAssert");
|
|
493
|
+
(0, import_utils5.assert)(
|
|
297
494
|
typeof prompt === "string",
|
|
298
495
|
"prompt for aiAssert must be a string"
|
|
299
496
|
);
|
|
@@ -305,8 +502,8 @@ var ScriptPlayer = class {
|
|
|
305
502
|
domIncluded: queryTask.domIncluded,
|
|
306
503
|
screenshotIncluded: queryTask.screenshotIncluded
|
|
307
504
|
};
|
|
308
|
-
(0,
|
|
309
|
-
(0,
|
|
505
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiQuery");
|
|
506
|
+
(0, import_utils5.assert)(
|
|
310
507
|
typeof prompt === "string",
|
|
311
508
|
"prompt for aiQuery must be a string"
|
|
312
509
|
);
|
|
@@ -319,8 +516,8 @@ var ScriptPlayer = class {
|
|
|
319
516
|
domIncluded: numberTask.domIncluded,
|
|
320
517
|
screenshotIncluded: numberTask.screenshotIncluded
|
|
321
518
|
};
|
|
322
|
-
(0,
|
|
323
|
-
(0,
|
|
519
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiNumber");
|
|
520
|
+
(0, import_utils5.assert)(
|
|
324
521
|
typeof prompt === "string",
|
|
325
522
|
"prompt for number must be a string"
|
|
326
523
|
);
|
|
@@ -333,8 +530,8 @@ var ScriptPlayer = class {
|
|
|
333
530
|
domIncluded: stringTask.domIncluded,
|
|
334
531
|
screenshotIncluded: stringTask.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 string must be a string"
|
|
340
537
|
);
|
|
@@ -347,28 +544,35 @@ var ScriptPlayer = class {
|
|
|
347
544
|
domIncluded: booleanTask.domIncluded,
|
|
348
545
|
screenshotIncluded: booleanTask.screenshotIncluded
|
|
349
546
|
};
|
|
350
|
-
(0,
|
|
351
|
-
(0,
|
|
547
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiBoolean");
|
|
548
|
+
(0, import_utils5.assert)(
|
|
352
549
|
typeof prompt === "string",
|
|
353
550
|
"prompt for boolean must be a string"
|
|
354
551
|
);
|
|
355
552
|
const booleanResult = await agent.aiBoolean(prompt, options);
|
|
356
553
|
this.setResult(booleanTask.name, booleanResult);
|
|
554
|
+
} else if ("aiAsk" in flowItem) {
|
|
555
|
+
const askTask = flowItem;
|
|
556
|
+
const prompt = askTask.aiAsk;
|
|
557
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAsk");
|
|
558
|
+
(0, import_utils5.assert)(typeof prompt === "string", "prompt for aiAsk must be a string");
|
|
559
|
+
const askResult = await agent.aiAsk(prompt);
|
|
560
|
+
this.setResult(askTask.name, askResult);
|
|
357
561
|
} else if ("aiLocate" in flowItem) {
|
|
358
562
|
const locateTask = flowItem;
|
|
359
563
|
const prompt = locateTask.aiLocate;
|
|
360
|
-
(0,
|
|
361
|
-
(0,
|
|
564
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiLocate");
|
|
565
|
+
(0, import_utils5.assert)(
|
|
362
566
|
typeof prompt === "string",
|
|
363
567
|
"prompt for aiLocate must be a string"
|
|
364
568
|
);
|
|
365
|
-
const locateResult = await agent.aiLocate(prompt);
|
|
569
|
+
const locateResult = await agent.aiLocate(prompt, locateTask);
|
|
366
570
|
this.setResult(locateTask.name, locateResult);
|
|
367
571
|
} else if ("aiWaitFor" in flowItem) {
|
|
368
572
|
const waitForTask = flowItem;
|
|
369
573
|
const prompt = waitForTask.aiWaitFor;
|
|
370
|
-
(0,
|
|
371
|
-
(0,
|
|
574
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiWaitFor");
|
|
575
|
+
(0, import_utils5.assert)(
|
|
372
576
|
typeof prompt === "string",
|
|
373
577
|
"prompt for aiWaitFor must be a string"
|
|
374
578
|
);
|
|
@@ -381,7 +585,7 @@ var ScriptPlayer = class {
|
|
|
381
585
|
if (typeof ms === "string") {
|
|
382
586
|
msNumber = Number.parseInt(ms, 10);
|
|
383
587
|
}
|
|
384
|
-
(0,
|
|
588
|
+
(0, import_utils5.assert)(
|
|
385
589
|
msNumber && msNumber > 0,
|
|
386
590
|
`ms for sleep must be greater than 0, but got ${ms}`
|
|
387
591
|
);
|
|
@@ -500,11 +704,11 @@ var ScriptPlayer = class {
|
|
|
500
704
|
};
|
|
501
705
|
|
|
502
706
|
// src/yaml/builder.ts
|
|
503
|
-
var
|
|
707
|
+
var import_js_yaml2 = __toESM(require("js-yaml"));
|
|
504
708
|
|
|
505
709
|
// src/yaml/utils.ts
|
|
506
|
-
var
|
|
507
|
-
var
|
|
710
|
+
var import_utils6 = require("@midscene/shared/utils");
|
|
711
|
+
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
508
712
|
function interpolateEnvVars(content) {
|
|
509
713
|
return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
510
714
|
const value = process.env[envVar.trim()];
|
|
@@ -530,31 +734,31 @@ function parseYamlScript(content, filePath, ignoreCheckingTarget) {
|
|
|
530
734
|
);
|
|
531
735
|
}
|
|
532
736
|
const interpolatedContent = interpolateEnvVars(processedContent);
|
|
533
|
-
const obj =
|
|
534
|
-
schema:
|
|
737
|
+
const obj = import_js_yaml3.default.load(interpolatedContent, {
|
|
738
|
+
schema: import_js_yaml3.default.JSON_SCHEMA
|
|
535
739
|
});
|
|
536
740
|
const pathTip = filePath ? `, failed to load ${filePath}` : "";
|
|
537
741
|
const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
|
|
538
742
|
const webConfig = obj.web || obj.target;
|
|
539
743
|
const web = typeof webConfig !== "undefined" ? Object.assign({}, webConfig || {}) : void 0;
|
|
540
744
|
if (!ignoreCheckingTarget) {
|
|
541
|
-
(0,
|
|
745
|
+
(0, import_utils6.assert)(
|
|
542
746
|
web || android,
|
|
543
747
|
`at least one of "target", "web", or "android" properties is required in yaml script${pathTip}`
|
|
544
748
|
);
|
|
545
|
-
(0,
|
|
749
|
+
(0, import_utils6.assert)(
|
|
546
750
|
web && !android || !web && android,
|
|
547
751
|
`only one of "target", "web", or "android" properties is allowed in yaml script${pathTip}`
|
|
548
752
|
);
|
|
549
753
|
if (web || android) {
|
|
550
|
-
(0,
|
|
754
|
+
(0, import_utils6.assert)(
|
|
551
755
|
typeof web === "object" || typeof android === "object",
|
|
552
756
|
`property "target/web/android" must be an object${pathTip}`
|
|
553
757
|
);
|
|
554
758
|
}
|
|
555
759
|
}
|
|
556
|
-
(0,
|
|
557
|
-
(0,
|
|
760
|
+
(0, import_utils6.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
|
|
761
|
+
(0, import_utils6.assert)(
|
|
558
762
|
Array.isArray(obj.tasks),
|
|
559
763
|
`property "tasks" must be an array in yaml script, but got ${obj.tasks}`
|
|
560
764
|
);
|
|
@@ -571,10 +775,10 @@ var import_utils12 = require("@midscene/shared/utils");
|
|
|
571
775
|
// src/common/tasks.ts
|
|
572
776
|
var import_core = require("@midscene/core");
|
|
573
777
|
var import_ai_model2 = require("@midscene/core/ai-model");
|
|
574
|
-
var
|
|
778
|
+
var import_utils7 = require("@midscene/core/utils");
|
|
575
779
|
var import_constants = require("@midscene/shared/constants");
|
|
576
|
-
var
|
|
577
|
-
var
|
|
780
|
+
var import_logger2 = require("@midscene/shared/logger");
|
|
781
|
+
var import_utils8 = require("@midscene/shared/utils");
|
|
578
782
|
|
|
579
783
|
// src/common/ui-utils.ts
|
|
580
784
|
function typeStr(task) {
|
|
@@ -650,7 +854,7 @@ function paramStr(task) {
|
|
|
650
854
|
}
|
|
651
855
|
|
|
652
856
|
// src/common/tasks.ts
|
|
653
|
-
var
|
|
857
|
+
var debug2 = (0, import_logger2.getDebug)("page-task-executor");
|
|
654
858
|
var replanningCountLimit = 10;
|
|
655
859
|
var isAndroidPage = (page) => {
|
|
656
860
|
return page.pageType === "android";
|
|
@@ -691,7 +895,7 @@ var PageTaskExecutor = class {
|
|
|
691
895
|
if (info?.id) {
|
|
692
896
|
elementId = info.id;
|
|
693
897
|
} else {
|
|
694
|
-
|
|
898
|
+
debug2(
|
|
695
899
|
"no element id found for position node, will not update cache",
|
|
696
900
|
element
|
|
697
901
|
);
|
|
@@ -704,7 +908,7 @@ var PageTaskExecutor = class {
|
|
|
704
908
|
const result = await this.page.getXpathsById(elementId);
|
|
705
909
|
return result;
|
|
706
910
|
} catch (error) {
|
|
707
|
-
|
|
911
|
+
debug2("getXpathsById error: ", error);
|
|
708
912
|
}
|
|
709
913
|
}
|
|
710
914
|
prependExecutorWithScreenshot(taskApply, appendAfterExecution = false) {
|
|
@@ -720,7 +924,7 @@ var PageTaskExecutor = class {
|
|
|
720
924
|
if (taskApply.type === "Action") {
|
|
721
925
|
await Promise.all([
|
|
722
926
|
(async () => {
|
|
723
|
-
await (0,
|
|
927
|
+
await (0, import_utils7.sleep)(100);
|
|
724
928
|
if (this.page.waitUntilNetworkIdle) {
|
|
725
929
|
try {
|
|
726
930
|
await this.page.waitUntilNetworkIdle();
|
|
@@ -728,7 +932,7 @@ var PageTaskExecutor = class {
|
|
|
728
932
|
}
|
|
729
933
|
}
|
|
730
934
|
})(),
|
|
731
|
-
(0,
|
|
935
|
+
(0, import_utils7.sleep)(200)
|
|
732
936
|
]);
|
|
733
937
|
}
|
|
734
938
|
if (appendAfterExecution) {
|
|
@@ -758,7 +962,7 @@ var PageTaskExecutor = class {
|
|
|
758
962
|
locate: plan2.locate,
|
|
759
963
|
executor: async (param, taskContext) => {
|
|
760
964
|
const { task } = taskContext;
|
|
761
|
-
(0,
|
|
965
|
+
(0, import_utils8.assert)(
|
|
762
966
|
param?.prompt || param?.id || param?.bbox,
|
|
763
967
|
"No prompt or id or position or bbox to locate"
|
|
764
968
|
);
|
|
@@ -783,39 +987,29 @@ var PageTaskExecutor = class {
|
|
|
783
987
|
timing: "before Insight"
|
|
784
988
|
};
|
|
785
989
|
task.recorder = [recordItem];
|
|
786
|
-
|
|
990
|
+
const elementFromXpath = param.xpath ? await this.page.getElementInfoByXpath(param.xpath) : void 0;
|
|
991
|
+
const userExpectedPathHitFlag = !!elementFromXpath;
|
|
787
992
|
const cachePrompt = param.prompt;
|
|
788
993
|
const locateCacheRecord = this.taskCache?.matchLocateCache(cachePrompt);
|
|
789
994
|
const xpaths = locateCacheRecord?.cacheContent?.xpaths;
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
debug(
|
|
802
|
-
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
803
|
-
xpaths[i],
|
|
804
|
-
element2?.id
|
|
805
|
-
);
|
|
806
|
-
break;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
} catch (error) {
|
|
811
|
-
debug("get element info by xpath error: ", error);
|
|
812
|
-
}
|
|
813
|
-
const startTime = Date.now();
|
|
814
|
-
const element = elementFromCache || // try to match element from cache
|
|
815
|
-
matchElementFromPlan(param, pageContext.tree) || // try to match element from plan
|
|
816
|
-
(await this.insight.locate(param, {
|
|
995
|
+
const elementFromCache = userExpectedPathHitFlag ? null : await matchElementFromCache(
|
|
996
|
+
this,
|
|
997
|
+
xpaths,
|
|
998
|
+
cachePrompt,
|
|
999
|
+
param.cacheable
|
|
1000
|
+
);
|
|
1001
|
+
const cacheHitFlag = !!elementFromCache;
|
|
1002
|
+
const elementFromPlan = !userExpectedPathHitFlag && !cacheHitFlag ? matchElementFromPlan(param, pageContext.tree) : void 0;
|
|
1003
|
+
const planHitFlag = !!elementFromPlan;
|
|
1004
|
+
const elementFromAiLocate = !userExpectedPathHitFlag && !cacheHitFlag && !planHitFlag ? (await this.insight.locate(param, {
|
|
1005
|
+
// fallback to ai locate
|
|
817
1006
|
context: pageContext
|
|
818
|
-
})).element;
|
|
1007
|
+
})).element : void 0;
|
|
1008
|
+
const aiLocateHitFlag = !!elementFromAiLocate;
|
|
1009
|
+
const element = elementFromXpath || // highest priority
|
|
1010
|
+
elementFromCache || // second priority
|
|
1011
|
+
elementFromPlan || // third priority
|
|
1012
|
+
elementFromAiLocate;
|
|
819
1013
|
let currentXpaths;
|
|
820
1014
|
if (element && this.taskCache && !cacheHitFlag && param?.cacheable !== false) {
|
|
821
1015
|
const elementXpaths = await this.getElementXpath(
|
|
@@ -833,7 +1027,7 @@ var PageTaskExecutor = class {
|
|
|
833
1027
|
locateCacheRecord
|
|
834
1028
|
);
|
|
835
1029
|
} else {
|
|
836
|
-
|
|
1030
|
+
debug2(
|
|
837
1031
|
"no xpaths found, will not update cache",
|
|
838
1032
|
cachePrompt,
|
|
839
1033
|
elementXpaths
|
|
@@ -843,16 +1037,44 @@ var PageTaskExecutor = class {
|
|
|
843
1037
|
if (!element) {
|
|
844
1038
|
throw new Error(`Element not found: ${param.prompt}`);
|
|
845
1039
|
}
|
|
1040
|
+
let hitBy;
|
|
1041
|
+
if (userExpectedPathHitFlag) {
|
|
1042
|
+
hitBy = {
|
|
1043
|
+
from: "User expected path",
|
|
1044
|
+
context: {
|
|
1045
|
+
xpath: param.xpath
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
} else if (cacheHitFlag) {
|
|
1049
|
+
hitBy = {
|
|
1050
|
+
from: "Cache",
|
|
1051
|
+
context: {
|
|
1052
|
+
xpathsFromCache: xpaths,
|
|
1053
|
+
xpathsToSave: currentXpaths
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
} else if (planHitFlag) {
|
|
1057
|
+
hitBy = {
|
|
1058
|
+
from: "Planning",
|
|
1059
|
+
context: {
|
|
1060
|
+
id: elementFromPlan?.id,
|
|
1061
|
+
bbox: elementFromPlan?.bbox
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
} else if (aiLocateHitFlag) {
|
|
1065
|
+
hitBy = {
|
|
1066
|
+
from: "AI model",
|
|
1067
|
+
context: {
|
|
1068
|
+
prompt: param.prompt
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
846
1072
|
return {
|
|
847
1073
|
output: {
|
|
848
1074
|
element
|
|
849
1075
|
},
|
|
850
1076
|
pageContext,
|
|
851
|
-
|
|
852
|
-
hit: cacheHitFlag,
|
|
853
|
-
originalXpaths: xpaths,
|
|
854
|
-
currentXpaths
|
|
855
|
-
}
|
|
1077
|
+
hitBy
|
|
856
1078
|
};
|
|
857
1079
|
}
|
|
858
1080
|
};
|
|
@@ -948,7 +1170,7 @@ var PageTaskExecutor = class {
|
|
|
948
1170
|
thought: plan2.thought,
|
|
949
1171
|
locate: plan2.locate,
|
|
950
1172
|
executor: async (param, { element }) => {
|
|
951
|
-
(0,
|
|
1173
|
+
(0, import_utils8.assert)(element, "Element not found, cannot tap");
|
|
952
1174
|
await this.page.mouse.click(element.center[0], element.center[1]);
|
|
953
1175
|
}
|
|
954
1176
|
};
|
|
@@ -960,7 +1182,7 @@ var PageTaskExecutor = class {
|
|
|
960
1182
|
thought: plan2.thought,
|
|
961
1183
|
locate: plan2.locate,
|
|
962
1184
|
executor: async (param, { element }) => {
|
|
963
|
-
(0,
|
|
1185
|
+
(0, import_utils8.assert)(element, "Element not found, cannot right click");
|
|
964
1186
|
await this.page.mouse.click(
|
|
965
1187
|
element.center[0],
|
|
966
1188
|
element.center[1],
|
|
@@ -977,7 +1199,7 @@ var PageTaskExecutor = class {
|
|
|
977
1199
|
thought: plan2.thought,
|
|
978
1200
|
locate: plan2.locate,
|
|
979
1201
|
executor: async (taskParam) => {
|
|
980
|
-
(0,
|
|
1202
|
+
(0, import_utils8.assert)(
|
|
981
1203
|
taskParam?.start_box && taskParam?.end_box,
|
|
982
1204
|
"No start_box or end_box to drag"
|
|
983
1205
|
);
|
|
@@ -992,7 +1214,7 @@ var PageTaskExecutor = class {
|
|
|
992
1214
|
thought: plan2.thought,
|
|
993
1215
|
locate: plan2.locate,
|
|
994
1216
|
executor: async (param, { element }) => {
|
|
995
|
-
(0,
|
|
1217
|
+
(0, import_utils8.assert)(element, "Element not found, cannot hover");
|
|
996
1218
|
await this.page.mouse.move(element.center[0], element.center[1]);
|
|
997
1219
|
}
|
|
998
1220
|
};
|
|
@@ -1044,7 +1266,7 @@ var PageTaskExecutor = class {
|
|
|
1044
1266
|
`Unknown scroll direction: ${taskParam.direction}`
|
|
1045
1267
|
);
|
|
1046
1268
|
}
|
|
1047
|
-
await (0,
|
|
1269
|
+
await (0, import_utils7.sleep)(500);
|
|
1048
1270
|
} else {
|
|
1049
1271
|
throw new Error(
|
|
1050
1272
|
`Unknown scroll event type: ${scrollToEventName}, taskParam: ${JSON.stringify(
|
|
@@ -1063,7 +1285,7 @@ var PageTaskExecutor = class {
|
|
|
1063
1285
|
thought: plan2.thought,
|
|
1064
1286
|
locate: plan2.locate,
|
|
1065
1287
|
executor: async (taskParam) => {
|
|
1066
|
-
await (0,
|
|
1288
|
+
await (0, import_utils7.sleep)(taskParam?.timeMs || 3e3);
|
|
1067
1289
|
}
|
|
1068
1290
|
};
|
|
1069
1291
|
tasks.push(taskActionSleep);
|
|
@@ -1111,7 +1333,7 @@ var PageTaskExecutor = class {
|
|
|
1111
1333
|
thought: plan2.thought,
|
|
1112
1334
|
locate: plan2.locate,
|
|
1113
1335
|
executor: async (param) => {
|
|
1114
|
-
(0,
|
|
1336
|
+
(0, import_utils8.assert)(
|
|
1115
1337
|
isAndroidPage(this.page),
|
|
1116
1338
|
"Cannot use home button on non-Android devices"
|
|
1117
1339
|
);
|
|
@@ -1127,7 +1349,7 @@ var PageTaskExecutor = class {
|
|
|
1127
1349
|
thought: plan2.thought,
|
|
1128
1350
|
locate: plan2.locate,
|
|
1129
1351
|
executor: async (param) => {
|
|
1130
|
-
(0,
|
|
1352
|
+
(0, import_utils8.assert)(
|
|
1131
1353
|
isAndroidPage(this.page),
|
|
1132
1354
|
"Cannot use back button on non-Android devices"
|
|
1133
1355
|
);
|
|
@@ -1143,7 +1365,7 @@ var PageTaskExecutor = class {
|
|
|
1143
1365
|
thought: plan2.thought,
|
|
1144
1366
|
locate: plan2.locate,
|
|
1145
1367
|
executor: async (param) => {
|
|
1146
|
-
(0,
|
|
1368
|
+
(0, import_utils8.assert)(
|
|
1147
1369
|
isAndroidPage(this.page),
|
|
1148
1370
|
"Cannot use recent apps button on non-Android devices"
|
|
1149
1371
|
);
|
|
@@ -1294,7 +1516,7 @@ var PageTaskExecutor = class {
|
|
|
1294
1516
|
}
|
|
1295
1517
|
}
|
|
1296
1518
|
if (finalActions.length === 0) {
|
|
1297
|
-
(0,
|
|
1519
|
+
(0, import_utils8.assert)(
|
|
1298
1520
|
!more_actions_needed_by_instruction || sleep2,
|
|
1299
1521
|
error ? `Failed to plan: ${error}` : planParsingError || "No plan found"
|
|
1300
1522
|
);
|
|
@@ -1532,7 +1754,7 @@ var PageTaskExecutor = class {
|
|
|
1532
1754
|
);
|
|
1533
1755
|
let outputResult = data;
|
|
1534
1756
|
if (ifTypeRestricted) {
|
|
1535
|
-
(0,
|
|
1757
|
+
(0, import_utils8.assert)(data?.result !== void 0, "No result in query data");
|
|
1536
1758
|
outputResult = data.result;
|
|
1537
1759
|
}
|
|
1538
1760
|
return {
|
|
@@ -1628,9 +1850,9 @@ var PageTaskExecutor = class {
|
|
|
1628
1850
|
onTaskStart: this.onTaskStartCallback
|
|
1629
1851
|
});
|
|
1630
1852
|
const { timeoutMs, checkIntervalMs } = opt;
|
|
1631
|
-
(0,
|
|
1632
|
-
(0,
|
|
1633
|
-
(0,
|
|
1853
|
+
(0, import_utils8.assert)(assertion, "No assertion for waitFor");
|
|
1854
|
+
(0, import_utils8.assert)(timeoutMs, "No timeoutMs for waitFor");
|
|
1855
|
+
(0, import_utils8.assert)(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1634
1856
|
const overallStartTime = Date.now();
|
|
1635
1857
|
let startTime = Date.now();
|
|
1636
1858
|
let errorThought = "";
|
|
@@ -1684,9 +1906,9 @@ var PageTaskExecutor = class {
|
|
|
1684
1906
|
};
|
|
1685
1907
|
|
|
1686
1908
|
// src/common/plan-builder.ts
|
|
1687
|
-
var
|
|
1688
|
-
var
|
|
1689
|
-
var
|
|
1909
|
+
var import_logger3 = require("@midscene/shared/logger");
|
|
1910
|
+
var import_utils10 = require("@midscene/shared/utils");
|
|
1911
|
+
var debug3 = (0, import_logger3.getDebug)("plan-builder");
|
|
1690
1912
|
function buildPlans(type, locateParam, param) {
|
|
1691
1913
|
let returnPlans = [];
|
|
1692
1914
|
const locatePlan = locateParam ? {
|
|
@@ -1696,8 +1918,8 @@ function buildPlans(type, locateParam, param) {
|
|
|
1696
1918
|
thought: ""
|
|
1697
1919
|
} : null;
|
|
1698
1920
|
if (type === "Tap" || type === "Hover" || type === "RightClick") {
|
|
1699
|
-
(0,
|
|
1700
|
-
(0,
|
|
1921
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1922
|
+
(0, import_utils10.assert)(locatePlan, `missing locate info for action "${type}"`);
|
|
1701
1923
|
const tapPlan = {
|
|
1702
1924
|
type,
|
|
1703
1925
|
param: null,
|
|
@@ -1708,9 +1930,9 @@ function buildPlans(type, locateParam, param) {
|
|
|
1708
1930
|
}
|
|
1709
1931
|
if (type === "Input" || type === "KeyboardPress") {
|
|
1710
1932
|
if (type === "Input") {
|
|
1711
|
-
(0,
|
|
1933
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1712
1934
|
}
|
|
1713
|
-
(0,
|
|
1935
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1714
1936
|
const inputPlan = {
|
|
1715
1937
|
type,
|
|
1716
1938
|
param,
|
|
@@ -1724,7 +1946,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1724
1946
|
}
|
|
1725
1947
|
}
|
|
1726
1948
|
if (type === "Scroll") {
|
|
1727
|
-
(0,
|
|
1949
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1728
1950
|
const scrollPlan = {
|
|
1729
1951
|
type,
|
|
1730
1952
|
param,
|
|
@@ -1738,7 +1960,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1738
1960
|
}
|
|
1739
1961
|
}
|
|
1740
1962
|
if (type === "Sleep") {
|
|
1741
|
-
(0,
|
|
1963
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1742
1964
|
const sleepPlan = {
|
|
1743
1965
|
type,
|
|
1744
1966
|
param,
|
|
@@ -1748,7 +1970,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1748
1970
|
returnPlans = [sleepPlan];
|
|
1749
1971
|
}
|
|
1750
1972
|
if (type === "Locate") {
|
|
1751
|
-
(0,
|
|
1973
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1752
1974
|
const locatePlan2 = {
|
|
1753
1975
|
type,
|
|
1754
1976
|
param: locateParam,
|
|
@@ -1758,187 +1980,12 @@ function buildPlans(type, locateParam, param) {
|
|
|
1758
1980
|
returnPlans = [locatePlan2];
|
|
1759
1981
|
}
|
|
1760
1982
|
if (returnPlans) {
|
|
1761
|
-
|
|
1983
|
+
debug3("buildPlans", returnPlans);
|
|
1762
1984
|
return returnPlans;
|
|
1763
1985
|
}
|
|
1764
1986
|
throw new Error(`Not supported type: ${type}`);
|
|
1765
1987
|
}
|
|
1766
1988
|
|
|
1767
|
-
// src/common/task-cache.ts
|
|
1768
|
-
var import_node_assert = __toESM(require("assert"));
|
|
1769
|
-
var import_node_fs2 = require("fs");
|
|
1770
|
-
var import_node_path2 = require("path");
|
|
1771
|
-
var import_common2 = require("@midscene/shared/common");
|
|
1772
|
-
var import_logger3 = require("@midscene/shared/logger");
|
|
1773
|
-
var import_utils9 = require("@midscene/shared/utils");
|
|
1774
|
-
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
1775
|
-
var import_semver = __toESM(require("semver"));
|
|
1776
|
-
|
|
1777
|
-
// package.json
|
|
1778
|
-
var version = "0.19.1";
|
|
1779
|
-
|
|
1780
|
-
// src/common/task-cache.ts
|
|
1781
|
-
var debug3 = (0, import_logger3.getDebug)("cache");
|
|
1782
|
-
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
1783
|
-
var cacheFileExt = ".cache.yaml";
|
|
1784
|
-
var TaskCache = class {
|
|
1785
|
-
// Track matched records
|
|
1786
|
-
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
1787
|
-
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
1788
|
-
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
1789
|
-
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
1790
|
-
this.cacheFilePath = import_utils9.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
1791
|
-
this.isCacheResultUsed = isCacheResultUsed;
|
|
1792
|
-
let cacheContent;
|
|
1793
|
-
if (this.cacheFilePath) {
|
|
1794
|
-
cacheContent = this.loadCacheFromFile();
|
|
1795
|
-
}
|
|
1796
|
-
if (!cacheContent) {
|
|
1797
|
-
cacheContent = {
|
|
1798
|
-
midsceneVersion: version,
|
|
1799
|
-
cacheId: this.cacheId,
|
|
1800
|
-
caches: []
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
|
-
this.cache = cacheContent;
|
|
1804
|
-
this.cacheOriginalLength = this.cache.caches.length;
|
|
1805
|
-
}
|
|
1806
|
-
matchCache(prompt, type) {
|
|
1807
|
-
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
1808
|
-
const item = this.cache.caches[i];
|
|
1809
|
-
const key = `${type}:${prompt}:${i}`;
|
|
1810
|
-
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
1811
|
-
this.matchedCacheIndices.add(key);
|
|
1812
|
-
debug3(
|
|
1813
|
-
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
1814
|
-
type,
|
|
1815
|
-
prompt,
|
|
1816
|
-
i
|
|
1817
|
-
);
|
|
1818
|
-
return {
|
|
1819
|
-
cacheContent: item,
|
|
1820
|
-
updateFn: (cb) => {
|
|
1821
|
-
debug3(
|
|
1822
|
-
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
1823
|
-
type,
|
|
1824
|
-
prompt,
|
|
1825
|
-
i
|
|
1826
|
-
);
|
|
1827
|
-
cb(item);
|
|
1828
|
-
debug3(
|
|
1829
|
-
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
1830
|
-
type,
|
|
1831
|
-
prompt,
|
|
1832
|
-
i
|
|
1833
|
-
);
|
|
1834
|
-
this.flushCacheToFile();
|
|
1835
|
-
}
|
|
1836
|
-
};
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
debug3("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
1840
|
-
return void 0;
|
|
1841
|
-
}
|
|
1842
|
-
matchPlanCache(prompt) {
|
|
1843
|
-
return this.matchCache(prompt, "plan");
|
|
1844
|
-
}
|
|
1845
|
-
matchLocateCache(prompt) {
|
|
1846
|
-
return this.matchCache(prompt, "locate");
|
|
1847
|
-
}
|
|
1848
|
-
appendCache(cache) {
|
|
1849
|
-
debug3("will append cache", cache);
|
|
1850
|
-
this.cache.caches.push(cache);
|
|
1851
|
-
this.flushCacheToFile();
|
|
1852
|
-
}
|
|
1853
|
-
loadCacheFromFile() {
|
|
1854
|
-
const cacheFile = this.cacheFilePath;
|
|
1855
|
-
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
1856
|
-
if (!(0, import_node_fs2.existsSync)(cacheFile)) {
|
|
1857
|
-
debug3("no cache file found, path: %s", cacheFile);
|
|
1858
|
-
return void 0;
|
|
1859
|
-
}
|
|
1860
|
-
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
1861
|
-
if ((0, import_node_fs2.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
1862
|
-
console.warn(
|
|
1863
|
-
`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}.`
|
|
1864
|
-
);
|
|
1865
|
-
return void 0;
|
|
1866
|
-
}
|
|
1867
|
-
try {
|
|
1868
|
-
const data = (0, import_node_fs2.readFileSync)(cacheFile, "utf8");
|
|
1869
|
-
const jsonData = import_js_yaml3.default.load(data);
|
|
1870
|
-
if (!version) {
|
|
1871
|
-
debug3("no midscene version info, will not read cache from file");
|
|
1872
|
-
return void 0;
|
|
1873
|
-
}
|
|
1874
|
-
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
1875
|
-
console.warn(
|
|
1876
|
-
`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.
|
|
1877
|
-
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
1878
|
-
cache file: ${cacheFile}`
|
|
1879
|
-
);
|
|
1880
|
-
return void 0;
|
|
1881
|
-
}
|
|
1882
|
-
debug3(
|
|
1883
|
-
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
1884
|
-
cacheFile,
|
|
1885
|
-
jsonData.midsceneVersion,
|
|
1886
|
-
jsonData.caches.length
|
|
1887
|
-
);
|
|
1888
|
-
jsonData.midsceneVersion = version;
|
|
1889
|
-
return jsonData;
|
|
1890
|
-
} catch (err) {
|
|
1891
|
-
debug3(
|
|
1892
|
-
"cache file exists but load failed, path: %s, error: %s",
|
|
1893
|
-
cacheFile,
|
|
1894
|
-
err
|
|
1895
|
-
);
|
|
1896
|
-
return void 0;
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
flushCacheToFile() {
|
|
1900
|
-
if (!version) {
|
|
1901
|
-
debug3("no midscene version info, will not write cache to file");
|
|
1902
|
-
return;
|
|
1903
|
-
}
|
|
1904
|
-
if (!this.cacheFilePath) {
|
|
1905
|
-
debug3("no cache file path, will not write cache to file");
|
|
1906
|
-
return;
|
|
1907
|
-
}
|
|
1908
|
-
try {
|
|
1909
|
-
const dir = (0, import_node_path2.dirname)(this.cacheFilePath);
|
|
1910
|
-
if (!(0, import_node_fs2.existsSync)(dir)) {
|
|
1911
|
-
(0, import_node_fs2.mkdirSync)(dir, { recursive: true });
|
|
1912
|
-
debug3("created cache directory: %s", dir);
|
|
1913
|
-
}
|
|
1914
|
-
const yamlData = import_js_yaml3.default.dump(this.cache);
|
|
1915
|
-
(0, import_node_fs2.writeFileSync)(this.cacheFilePath, yamlData);
|
|
1916
|
-
debug3("cache flushed to file: %s", this.cacheFilePath);
|
|
1917
|
-
} catch (err) {
|
|
1918
|
-
debug3(
|
|
1919
|
-
"write cache to file failed, path: %s, error: %s",
|
|
1920
|
-
this.cacheFilePath,
|
|
1921
|
-
err
|
|
1922
|
-
);
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
1926
|
-
if (cachedRecord) {
|
|
1927
|
-
if (newRecord.type === "plan") {
|
|
1928
|
-
cachedRecord.updateFn((cache) => {
|
|
1929
|
-
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
1930
|
-
});
|
|
1931
|
-
} else {
|
|
1932
|
-
cachedRecord.updateFn((cache) => {
|
|
1933
|
-
cache.xpaths = newRecord.xpaths;
|
|
1934
|
-
});
|
|
1935
|
-
}
|
|
1936
|
-
} else {
|
|
1937
|
-
this.appendCache(newRecord);
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
};
|
|
1941
|
-
|
|
1942
1989
|
// src/common/agent.ts
|
|
1943
1990
|
var debug4 = (0, import_logger4.getDebug)("web-integration");
|
|
1944
1991
|
var distanceOfTwoPoints = (p1, p2) => {
|
|
@@ -2067,10 +2114,12 @@ ${errorTask?.errorStack}`);
|
|
|
2067
2114
|
const prompt = opt.prompt ?? locatePrompt;
|
|
2068
2115
|
const deepThink = opt.deepThink ?? false;
|
|
2069
2116
|
const cacheable = opt.cacheable ?? true;
|
|
2117
|
+
const xpath = opt.xpath;
|
|
2070
2118
|
return {
|
|
2071
2119
|
prompt,
|
|
2072
2120
|
deepThink,
|
|
2073
|
-
cacheable
|
|
2121
|
+
cacheable,
|
|
2122
|
+
xpath
|
|
2074
2123
|
};
|
|
2075
2124
|
}
|
|
2076
2125
|
return {
|
|
@@ -2228,6 +2277,9 @@ ${errorTask?.errorStack}`);
|
|
|
2228
2277
|
this.afterTaskRunning(executor);
|
|
2229
2278
|
return output;
|
|
2230
2279
|
}
|
|
2280
|
+
async aiAsk(prompt, opt = defaultInsightExtractOption) {
|
|
2281
|
+
return this.aiString(prompt, opt);
|
|
2282
|
+
}
|
|
2231
2283
|
async describeElementAtPoint(center, opt) {
|
|
2232
2284
|
const { verifyPrompt = true, retryLimit = 3 } = opt || {};
|
|
2233
2285
|
let success = false;
|