@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
|
@@ -61,11 +61,11 @@ module.exports = __toCommonJS(chrome_extension_exports);
|
|
|
61
61
|
|
|
62
62
|
// src/common/utils.ts
|
|
63
63
|
var import_ai_model = require("@midscene/core/ai-model");
|
|
64
|
-
var
|
|
64
|
+
var import_utils3 = require("@midscene/core/utils");
|
|
65
65
|
var import_env = require("@midscene/shared/env");
|
|
66
66
|
var import_extractor = require("@midscene/shared/extractor");
|
|
67
67
|
var import_img = require("@midscene/shared/img");
|
|
68
|
-
var
|
|
68
|
+
var import_utils4 = require("@midscene/shared/utils");
|
|
69
69
|
var import_dayjs = __toESM(require("dayjs"));
|
|
70
70
|
|
|
71
71
|
// src/web-element.ts
|
|
@@ -93,14 +93,189 @@ var WebElementInfo = class {
|
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
+
// src/common/task-cache.ts
|
|
97
|
+
var import_node_assert = __toESM(require("assert"));
|
|
98
|
+
var import_node_fs = require("fs");
|
|
99
|
+
var import_node_path = require("path");
|
|
100
|
+
var import_common = require("@midscene/shared/common");
|
|
101
|
+
var import_logger = require("@midscene/shared/logger");
|
|
102
|
+
var import_utils = require("@midscene/shared/utils");
|
|
103
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
104
|
+
var import_semver = __toESM(require("semver"));
|
|
105
|
+
|
|
106
|
+
// package.json
|
|
107
|
+
var version = "0.20.0";
|
|
108
|
+
|
|
109
|
+
// src/common/task-cache.ts
|
|
110
|
+
var debug = (0, import_logger.getDebug)("cache");
|
|
111
|
+
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
112
|
+
var cacheFileExt = ".cache.yaml";
|
|
113
|
+
var TaskCache = class {
|
|
114
|
+
// Track matched records
|
|
115
|
+
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
116
|
+
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
117
|
+
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
118
|
+
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
119
|
+
this.cacheFilePath = import_utils.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
120
|
+
this.isCacheResultUsed = isCacheResultUsed;
|
|
121
|
+
let cacheContent;
|
|
122
|
+
if (this.cacheFilePath) {
|
|
123
|
+
cacheContent = this.loadCacheFromFile();
|
|
124
|
+
}
|
|
125
|
+
if (!cacheContent) {
|
|
126
|
+
cacheContent = {
|
|
127
|
+
midsceneVersion: version,
|
|
128
|
+
cacheId: this.cacheId,
|
|
129
|
+
caches: []
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
this.cache = cacheContent;
|
|
133
|
+
this.cacheOriginalLength = this.cache.caches.length;
|
|
134
|
+
}
|
|
135
|
+
matchCache(prompt, type) {
|
|
136
|
+
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
137
|
+
const item = this.cache.caches[i];
|
|
138
|
+
const key = `${type}:${prompt}:${i}`;
|
|
139
|
+
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
140
|
+
this.matchedCacheIndices.add(key);
|
|
141
|
+
debug(
|
|
142
|
+
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
143
|
+
type,
|
|
144
|
+
prompt,
|
|
145
|
+
i
|
|
146
|
+
);
|
|
147
|
+
return {
|
|
148
|
+
cacheContent: item,
|
|
149
|
+
updateFn: (cb) => {
|
|
150
|
+
debug(
|
|
151
|
+
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
152
|
+
type,
|
|
153
|
+
prompt,
|
|
154
|
+
i
|
|
155
|
+
);
|
|
156
|
+
cb(item);
|
|
157
|
+
debug(
|
|
158
|
+
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
159
|
+
type,
|
|
160
|
+
prompt,
|
|
161
|
+
i
|
|
162
|
+
);
|
|
163
|
+
this.flushCacheToFile();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
debug("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
matchPlanCache(prompt) {
|
|
172
|
+
return this.matchCache(prompt, "plan");
|
|
173
|
+
}
|
|
174
|
+
matchLocateCache(prompt) {
|
|
175
|
+
return this.matchCache(prompt, "locate");
|
|
176
|
+
}
|
|
177
|
+
appendCache(cache) {
|
|
178
|
+
debug("will append cache", cache);
|
|
179
|
+
this.cache.caches.push(cache);
|
|
180
|
+
this.flushCacheToFile();
|
|
181
|
+
}
|
|
182
|
+
loadCacheFromFile() {
|
|
183
|
+
const cacheFile = this.cacheFilePath;
|
|
184
|
+
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
185
|
+
if (!(0, import_node_fs.existsSync)(cacheFile)) {
|
|
186
|
+
debug("no cache file found, path: %s", cacheFile);
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
190
|
+
if ((0, import_node_fs.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
191
|
+
console.warn(
|
|
192
|
+
`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}.`
|
|
193
|
+
);
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const data = (0, import_node_fs.readFileSync)(cacheFile, "utf8");
|
|
198
|
+
const jsonData = import_js_yaml.default.load(data);
|
|
199
|
+
if (!version) {
|
|
200
|
+
debug("no midscene version info, will not read cache from file");
|
|
201
|
+
return void 0;
|
|
202
|
+
}
|
|
203
|
+
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
204
|
+
console.warn(
|
|
205
|
+
`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.
|
|
206
|
+
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
207
|
+
cache file: ${cacheFile}`
|
|
208
|
+
);
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
debug(
|
|
212
|
+
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
213
|
+
cacheFile,
|
|
214
|
+
jsonData.midsceneVersion,
|
|
215
|
+
jsonData.caches.length
|
|
216
|
+
);
|
|
217
|
+
jsonData.midsceneVersion = version;
|
|
218
|
+
return jsonData;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
debug(
|
|
221
|
+
"cache file exists but load failed, path: %s, error: %s",
|
|
222
|
+
cacheFile,
|
|
223
|
+
err
|
|
224
|
+
);
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
flushCacheToFile() {
|
|
229
|
+
if (!version) {
|
|
230
|
+
debug("no midscene version info, will not write cache to file");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!this.cacheFilePath) {
|
|
234
|
+
debug("no cache file path, will not write cache to file");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const dir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
239
|
+
if (!(0, import_node_fs.existsSync)(dir)) {
|
|
240
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
241
|
+
debug("created cache directory: %s", dir);
|
|
242
|
+
}
|
|
243
|
+
const yamlData = import_js_yaml.default.dump(this.cache);
|
|
244
|
+
(0, import_node_fs.writeFileSync)(this.cacheFilePath, yamlData);
|
|
245
|
+
debug("cache flushed to file: %s", this.cacheFilePath);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
debug(
|
|
248
|
+
"write cache to file failed, path: %s, error: %s",
|
|
249
|
+
this.cacheFilePath,
|
|
250
|
+
err
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
255
|
+
if (cachedRecord) {
|
|
256
|
+
if (newRecord.type === "plan") {
|
|
257
|
+
cachedRecord.updateFn((cache) => {
|
|
258
|
+
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
cachedRecord.updateFn((cache) => {
|
|
262
|
+
cache.xpaths = newRecord.xpaths;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
this.appendCache(newRecord);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
96
271
|
// src/common/utils.ts
|
|
97
272
|
async function parseContextFromWebPage(page, _opt) {
|
|
98
|
-
(0,
|
|
273
|
+
(0, import_utils4.assert)(page, "page is required");
|
|
99
274
|
if (page._forceUsePageContext) {
|
|
100
275
|
return await page._forceUsePageContext();
|
|
101
276
|
}
|
|
102
277
|
const url = await page.url();
|
|
103
|
-
(0,
|
|
278
|
+
(0, import_utils3.uploadTestInfoToServer)({ testUrl: url });
|
|
104
279
|
let screenshotBase64;
|
|
105
280
|
let tree;
|
|
106
281
|
await Promise.all([
|
|
@@ -122,7 +297,7 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
122
297
|
isVisible
|
|
123
298
|
});
|
|
124
299
|
});
|
|
125
|
-
(0,
|
|
300
|
+
(0, import_utils4.assert)(screenshotBase64, "screenshotBase64 is required");
|
|
126
301
|
const size = await page.size();
|
|
127
302
|
if (size.dpr && size.dpr > 1) {
|
|
128
303
|
screenshotBase64 = await (0, import_img.resizeImgBase64)(screenshotBase64, {
|
|
@@ -140,11 +315,11 @@ async function parseContextFromWebPage(page, _opt) {
|
|
|
140
315
|
function reportFileName(tag = "web") {
|
|
141
316
|
const reportTagName = (0, import_env.getAIConfig)(import_env.MIDSCENE_REPORT_TAG_NAME);
|
|
142
317
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss");
|
|
143
|
-
const uniqueId = (0,
|
|
318
|
+
const uniqueId = (0, import_utils4.uuid)().substring(0, 8);
|
|
144
319
|
return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;
|
|
145
320
|
}
|
|
146
321
|
function printReportMsg(filepath) {
|
|
147
|
-
(0,
|
|
322
|
+
(0, import_utils4.logMsg)(`Midscene - report file updated: ${filepath}`);
|
|
148
323
|
}
|
|
149
324
|
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
|
150
325
|
function replaceIllegalPathCharsAndSpace(str) {
|
|
@@ -170,6 +345,28 @@ function matchElementFromPlan(planLocateParam, tree) {
|
|
|
170
345
|
}
|
|
171
346
|
return void 0;
|
|
172
347
|
}
|
|
348
|
+
async function matchElementFromCache(taskExecutor, xpaths, cachePrompt, cacheable) {
|
|
349
|
+
try {
|
|
350
|
+
if (xpaths?.length && taskExecutor.taskCache?.isCacheResultUsed && cacheable !== false) {
|
|
351
|
+
for (let i = 0; i < xpaths.length; i++) {
|
|
352
|
+
const element = await taskExecutor.page.getElementInfoByXpath(
|
|
353
|
+
xpaths[i]
|
|
354
|
+
);
|
|
355
|
+
if (element?.id) {
|
|
356
|
+
debug("cache hit, prompt: %s", cachePrompt);
|
|
357
|
+
debug(
|
|
358
|
+
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
359
|
+
xpaths[i],
|
|
360
|
+
element?.id
|
|
361
|
+
);
|
|
362
|
+
return element;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
debug("get element info by xpath error: ", error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
173
370
|
function trimContextByViewport(execution) {
|
|
174
371
|
function filterVisibleTree(node) {
|
|
175
372
|
if (!node)
|
|
@@ -212,10 +409,10 @@ var import_core2 = require("@midscene/core");
|
|
|
212
409
|
var import_js_yaml4 = __toESM(require("js-yaml"));
|
|
213
410
|
|
|
214
411
|
// src/yaml/player.ts
|
|
215
|
-
var
|
|
216
|
-
var
|
|
217
|
-
var
|
|
218
|
-
var
|
|
412
|
+
var import_node_fs2 = require("fs");
|
|
413
|
+
var import_node_path2 = require("path");
|
|
414
|
+
var import_utils5 = require("@midscene/shared/utils");
|
|
415
|
+
var import_common2 = require("@midscene/shared/common");
|
|
219
416
|
var ScriptPlayer = class {
|
|
220
417
|
constructor(script, setupAgent, onTaskStatusChange) {
|
|
221
418
|
this.script = script;
|
|
@@ -227,23 +424,23 @@ var ScriptPlayer = class {
|
|
|
227
424
|
this.pageAgent = null;
|
|
228
425
|
this.result = {};
|
|
229
426
|
this.target = script.target || script.web || script.android;
|
|
230
|
-
if (
|
|
427
|
+
if (import_utils5.ifInBrowser) {
|
|
231
428
|
this.output = void 0;
|
|
232
429
|
} else if (this.target?.output) {
|
|
233
|
-
this.output = (0,
|
|
430
|
+
this.output = (0, import_node_path2.resolve)(process.cwd(), this.target.output);
|
|
234
431
|
} else {
|
|
235
|
-
this.output = (0,
|
|
432
|
+
this.output = (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
|
|
236
433
|
}
|
|
237
|
-
if (
|
|
434
|
+
if (import_utils5.ifInBrowser) {
|
|
238
435
|
this.unstableLogContent = void 0;
|
|
239
436
|
} else if (typeof this.target?.unstableLogContent === "string") {
|
|
240
|
-
this.unstableLogContent = (0,
|
|
437
|
+
this.unstableLogContent = (0, import_node_path2.resolve)(
|
|
241
438
|
process.cwd(),
|
|
242
439
|
this.target.unstableLogContent
|
|
243
440
|
);
|
|
244
441
|
} else if (this.target?.unstableLogContent === true) {
|
|
245
|
-
this.unstableLogContent = (0,
|
|
246
|
-
(0,
|
|
442
|
+
this.unstableLogContent = (0, import_node_path2.join)(
|
|
443
|
+
(0, import_common2.getMidsceneRunSubDir)("output"),
|
|
247
444
|
"unstableLogContent.json"
|
|
248
445
|
);
|
|
249
446
|
}
|
|
@@ -288,28 +485,28 @@ var ScriptPlayer = class {
|
|
|
288
485
|
}
|
|
289
486
|
flushResult() {
|
|
290
487
|
if (Object.keys(this.result).length && this.output) {
|
|
291
|
-
const output = (0,
|
|
292
|
-
const outputDir = (0,
|
|
293
|
-
if (!(0,
|
|
294
|
-
(0,
|
|
488
|
+
const output = (0, import_node_path2.resolve)(process.cwd(), this.output);
|
|
489
|
+
const outputDir = (0, import_node_path2.dirname)(output);
|
|
490
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
491
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
295
492
|
}
|
|
296
|
-
(0,
|
|
493
|
+
(0, import_node_fs2.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
|
|
297
494
|
}
|
|
298
495
|
}
|
|
299
496
|
flushUnstableLogContent() {
|
|
300
497
|
if (this.unstableLogContent) {
|
|
301
498
|
const content = this.pageAgent?._unstableLogContent();
|
|
302
|
-
const filePath = (0,
|
|
303
|
-
const outputDir = (0,
|
|
304
|
-
if (!(0,
|
|
305
|
-
(0,
|
|
499
|
+
const filePath = (0, import_node_path2.resolve)(process.cwd(), this.unstableLogContent);
|
|
500
|
+
const outputDir = (0, import_node_path2.dirname)(filePath);
|
|
501
|
+
if (!(0, import_node_fs2.existsSync)(outputDir)) {
|
|
502
|
+
(0, import_node_fs2.mkdirSync)(outputDir, { recursive: true });
|
|
306
503
|
}
|
|
307
|
-
(0,
|
|
504
|
+
(0, import_node_fs2.writeFileSync)(filePath, JSON.stringify(content, null, 2));
|
|
308
505
|
}
|
|
309
506
|
}
|
|
310
507
|
async playTask(taskStatus, agent) {
|
|
311
508
|
const { flow } = taskStatus;
|
|
312
|
-
(0,
|
|
509
|
+
(0, import_utils5.assert)(flow, "missing flow in task");
|
|
313
510
|
for (const flowItemIndex in flow) {
|
|
314
511
|
const currentStep = Number.parseInt(flowItemIndex, 10);
|
|
315
512
|
taskStatus.currentStep = currentStep;
|
|
@@ -317,8 +514,8 @@ var ScriptPlayer = class {
|
|
|
317
514
|
if ("aiAction" in flowItem || "ai" in flowItem) {
|
|
318
515
|
const actionTask = flowItem;
|
|
319
516
|
const prompt = actionTask.aiAction || actionTask.ai;
|
|
320
|
-
(0,
|
|
321
|
-
(0,
|
|
517
|
+
(0, import_utils5.assert)(prompt, "missing prompt for ai (aiAction)");
|
|
518
|
+
(0, import_utils5.assert)(
|
|
322
519
|
typeof prompt === "string",
|
|
323
520
|
"prompt for aiAction must be a string"
|
|
324
521
|
);
|
|
@@ -329,8 +526,8 @@ var ScriptPlayer = class {
|
|
|
329
526
|
const assertTask = flowItem;
|
|
330
527
|
const prompt = assertTask.aiAssert;
|
|
331
528
|
const msg = assertTask.errorMessage;
|
|
332
|
-
(0,
|
|
333
|
-
(0,
|
|
529
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAssert");
|
|
530
|
+
(0, import_utils5.assert)(
|
|
334
531
|
typeof prompt === "string",
|
|
335
532
|
"prompt for aiAssert must be a string"
|
|
336
533
|
);
|
|
@@ -342,8 +539,8 @@ var ScriptPlayer = class {
|
|
|
342
539
|
domIncluded: queryTask.domIncluded,
|
|
343
540
|
screenshotIncluded: queryTask.screenshotIncluded
|
|
344
541
|
};
|
|
345
|
-
(0,
|
|
346
|
-
(0,
|
|
542
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiQuery");
|
|
543
|
+
(0, import_utils5.assert)(
|
|
347
544
|
typeof prompt === "string",
|
|
348
545
|
"prompt for aiQuery must be a string"
|
|
349
546
|
);
|
|
@@ -356,8 +553,8 @@ var ScriptPlayer = class {
|
|
|
356
553
|
domIncluded: numberTask.domIncluded,
|
|
357
554
|
screenshotIncluded: numberTask.screenshotIncluded
|
|
358
555
|
};
|
|
359
|
-
(0,
|
|
360
|
-
(0,
|
|
556
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiNumber");
|
|
557
|
+
(0, import_utils5.assert)(
|
|
361
558
|
typeof prompt === "string",
|
|
362
559
|
"prompt for number must be a string"
|
|
363
560
|
);
|
|
@@ -370,8 +567,8 @@ var ScriptPlayer = class {
|
|
|
370
567
|
domIncluded: stringTask.domIncluded,
|
|
371
568
|
screenshotIncluded: stringTask.screenshotIncluded
|
|
372
569
|
};
|
|
373
|
-
(0,
|
|
374
|
-
(0,
|
|
570
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiNumber");
|
|
571
|
+
(0, import_utils5.assert)(
|
|
375
572
|
typeof prompt === "string",
|
|
376
573
|
"prompt for string must be a string"
|
|
377
574
|
);
|
|
@@ -384,28 +581,35 @@ var ScriptPlayer = class {
|
|
|
384
581
|
domIncluded: booleanTask.domIncluded,
|
|
385
582
|
screenshotIncluded: booleanTask.screenshotIncluded
|
|
386
583
|
};
|
|
387
|
-
(0,
|
|
388
|
-
(0,
|
|
584
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiBoolean");
|
|
585
|
+
(0, import_utils5.assert)(
|
|
389
586
|
typeof prompt === "string",
|
|
390
587
|
"prompt for boolean must be a string"
|
|
391
588
|
);
|
|
392
589
|
const booleanResult = await agent.aiBoolean(prompt, options);
|
|
393
590
|
this.setResult(booleanTask.name, booleanResult);
|
|
591
|
+
} else if ("aiAsk" in flowItem) {
|
|
592
|
+
const askTask = flowItem;
|
|
593
|
+
const prompt = askTask.aiAsk;
|
|
594
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiAsk");
|
|
595
|
+
(0, import_utils5.assert)(typeof prompt === "string", "prompt for aiAsk must be a string");
|
|
596
|
+
const askResult = await agent.aiAsk(prompt);
|
|
597
|
+
this.setResult(askTask.name, askResult);
|
|
394
598
|
} else if ("aiLocate" in flowItem) {
|
|
395
599
|
const locateTask = flowItem;
|
|
396
600
|
const prompt = locateTask.aiLocate;
|
|
397
|
-
(0,
|
|
398
|
-
(0,
|
|
601
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiLocate");
|
|
602
|
+
(0, import_utils5.assert)(
|
|
399
603
|
typeof prompt === "string",
|
|
400
604
|
"prompt for aiLocate must be a string"
|
|
401
605
|
);
|
|
402
|
-
const locateResult = await agent.aiLocate(prompt);
|
|
606
|
+
const locateResult = await agent.aiLocate(prompt, locateTask);
|
|
403
607
|
this.setResult(locateTask.name, locateResult);
|
|
404
608
|
} else if ("aiWaitFor" in flowItem) {
|
|
405
609
|
const waitForTask = flowItem;
|
|
406
610
|
const prompt = waitForTask.aiWaitFor;
|
|
407
|
-
(0,
|
|
408
|
-
(0,
|
|
611
|
+
(0, import_utils5.assert)(prompt, "missing prompt for aiWaitFor");
|
|
612
|
+
(0, import_utils5.assert)(
|
|
409
613
|
typeof prompt === "string",
|
|
410
614
|
"prompt for aiWaitFor must be a string"
|
|
411
615
|
);
|
|
@@ -418,7 +622,7 @@ var ScriptPlayer = class {
|
|
|
418
622
|
if (typeof ms === "string") {
|
|
419
623
|
msNumber = Number.parseInt(ms, 10);
|
|
420
624
|
}
|
|
421
|
-
(0,
|
|
625
|
+
(0, import_utils5.assert)(
|
|
422
626
|
msNumber && msNumber > 0,
|
|
423
627
|
`ms for sleep must be greater than 0, but got ${ms}`
|
|
424
628
|
);
|
|
@@ -537,11 +741,11 @@ var ScriptPlayer = class {
|
|
|
537
741
|
};
|
|
538
742
|
|
|
539
743
|
// src/yaml/builder.ts
|
|
540
|
-
var
|
|
744
|
+
var import_js_yaml2 = __toESM(require("js-yaml"));
|
|
541
745
|
|
|
542
746
|
// src/yaml/utils.ts
|
|
543
|
-
var
|
|
544
|
-
var
|
|
747
|
+
var import_utils6 = require("@midscene/shared/utils");
|
|
748
|
+
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
545
749
|
function interpolateEnvVars(content) {
|
|
546
750
|
return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
547
751
|
const value = process.env[envVar.trim()];
|
|
@@ -567,31 +771,31 @@ function parseYamlScript(content, filePath, ignoreCheckingTarget) {
|
|
|
567
771
|
);
|
|
568
772
|
}
|
|
569
773
|
const interpolatedContent = interpolateEnvVars(processedContent);
|
|
570
|
-
const obj =
|
|
571
|
-
schema:
|
|
774
|
+
const obj = import_js_yaml3.default.load(interpolatedContent, {
|
|
775
|
+
schema: import_js_yaml3.default.JSON_SCHEMA
|
|
572
776
|
});
|
|
573
777
|
const pathTip = filePath ? `, failed to load ${filePath}` : "";
|
|
574
778
|
const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
|
|
575
779
|
const webConfig = obj.web || obj.target;
|
|
576
780
|
const web = typeof webConfig !== "undefined" ? Object.assign({}, webConfig || {}) : void 0;
|
|
577
781
|
if (!ignoreCheckingTarget) {
|
|
578
|
-
(0,
|
|
782
|
+
(0, import_utils6.assert)(
|
|
579
783
|
web || android,
|
|
580
784
|
`at least one of "target", "web", or "android" properties is required in yaml script${pathTip}`
|
|
581
785
|
);
|
|
582
|
-
(0,
|
|
786
|
+
(0, import_utils6.assert)(
|
|
583
787
|
web && !android || !web && android,
|
|
584
788
|
`only one of "target", "web", or "android" properties is allowed in yaml script${pathTip}`
|
|
585
789
|
);
|
|
586
790
|
if (web || android) {
|
|
587
|
-
(0,
|
|
791
|
+
(0, import_utils6.assert)(
|
|
588
792
|
typeof web === "object" || typeof android === "object",
|
|
589
793
|
`property "target/web/android" must be an object${pathTip}`
|
|
590
794
|
);
|
|
591
795
|
}
|
|
592
796
|
}
|
|
593
|
-
(0,
|
|
594
|
-
(0,
|
|
797
|
+
(0, import_utils6.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
|
|
798
|
+
(0, import_utils6.assert)(
|
|
595
799
|
Array.isArray(obj.tasks),
|
|
596
800
|
`property "tasks" must be an array in yaml script, but got ${obj.tasks}`
|
|
597
801
|
);
|
|
@@ -608,10 +812,10 @@ var import_utils12 = require("@midscene/shared/utils");
|
|
|
608
812
|
// src/common/tasks.ts
|
|
609
813
|
var import_core = require("@midscene/core");
|
|
610
814
|
var import_ai_model2 = require("@midscene/core/ai-model");
|
|
611
|
-
var
|
|
815
|
+
var import_utils7 = require("@midscene/core/utils");
|
|
612
816
|
var import_constants = require("@midscene/shared/constants");
|
|
613
|
-
var
|
|
614
|
-
var
|
|
817
|
+
var import_logger2 = require("@midscene/shared/logger");
|
|
818
|
+
var import_utils8 = require("@midscene/shared/utils");
|
|
615
819
|
|
|
616
820
|
// src/common/ui-utils.ts
|
|
617
821
|
function typeStr(task) {
|
|
@@ -710,7 +914,7 @@ if (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {
|
|
|
710
914
|
`;
|
|
711
915
|
|
|
712
916
|
// src/common/tasks.ts
|
|
713
|
-
var
|
|
917
|
+
var debug2 = (0, import_logger2.getDebug)("page-task-executor");
|
|
714
918
|
var replanningCountLimit = 10;
|
|
715
919
|
var isAndroidPage = (page) => {
|
|
716
920
|
return page.pageType === "android";
|
|
@@ -751,7 +955,7 @@ var PageTaskExecutor = class {
|
|
|
751
955
|
if (info?.id) {
|
|
752
956
|
elementId = info.id;
|
|
753
957
|
} else {
|
|
754
|
-
|
|
958
|
+
debug2(
|
|
755
959
|
"no element id found for position node, will not update cache",
|
|
756
960
|
element
|
|
757
961
|
);
|
|
@@ -764,7 +968,7 @@ var PageTaskExecutor = class {
|
|
|
764
968
|
const result = await this.page.getXpathsById(elementId);
|
|
765
969
|
return result;
|
|
766
970
|
} catch (error) {
|
|
767
|
-
|
|
971
|
+
debug2("getXpathsById error: ", error);
|
|
768
972
|
}
|
|
769
973
|
}
|
|
770
974
|
prependExecutorWithScreenshot(taskApply, appendAfterExecution = false) {
|
|
@@ -780,7 +984,7 @@ var PageTaskExecutor = class {
|
|
|
780
984
|
if (taskApply.type === "Action") {
|
|
781
985
|
await Promise.all([
|
|
782
986
|
(async () => {
|
|
783
|
-
await (0,
|
|
987
|
+
await (0, import_utils7.sleep)(100);
|
|
784
988
|
if (this.page.waitUntilNetworkIdle) {
|
|
785
989
|
try {
|
|
786
990
|
await this.page.waitUntilNetworkIdle();
|
|
@@ -788,7 +992,7 @@ var PageTaskExecutor = class {
|
|
|
788
992
|
}
|
|
789
993
|
}
|
|
790
994
|
})(),
|
|
791
|
-
(0,
|
|
995
|
+
(0, import_utils7.sleep)(200)
|
|
792
996
|
]);
|
|
793
997
|
}
|
|
794
998
|
if (appendAfterExecution) {
|
|
@@ -818,7 +1022,7 @@ var PageTaskExecutor = class {
|
|
|
818
1022
|
locate: plan2.locate,
|
|
819
1023
|
executor: async (param, taskContext) => {
|
|
820
1024
|
const { task } = taskContext;
|
|
821
|
-
(0,
|
|
1025
|
+
(0, import_utils8.assert)(
|
|
822
1026
|
param?.prompt || param?.id || param?.bbox,
|
|
823
1027
|
"No prompt or id or position or bbox to locate"
|
|
824
1028
|
);
|
|
@@ -843,39 +1047,29 @@ var PageTaskExecutor = class {
|
|
|
843
1047
|
timing: "before Insight"
|
|
844
1048
|
};
|
|
845
1049
|
task.recorder = [recordItem];
|
|
846
|
-
|
|
1050
|
+
const elementFromXpath = param.xpath ? await this.page.getElementInfoByXpath(param.xpath) : void 0;
|
|
1051
|
+
const userExpectedPathHitFlag = !!elementFromXpath;
|
|
847
1052
|
const cachePrompt = param.prompt;
|
|
848
1053
|
const locateCacheRecord = this.taskCache?.matchLocateCache(cachePrompt);
|
|
849
1054
|
const xpaths = locateCacheRecord?.cacheContent?.xpaths;
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
debug(
|
|
862
|
-
"found a new new element with same xpath, xpath: %s, id: %s",
|
|
863
|
-
xpaths[i],
|
|
864
|
-
element2?.id
|
|
865
|
-
);
|
|
866
|
-
break;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
} catch (error) {
|
|
871
|
-
debug("get element info by xpath error: ", error);
|
|
872
|
-
}
|
|
873
|
-
const startTime = Date.now();
|
|
874
|
-
const element = elementFromCache || // try to match element from cache
|
|
875
|
-
matchElementFromPlan(param, pageContext.tree) || // try to match element from plan
|
|
876
|
-
(await this.insight.locate(param, {
|
|
1055
|
+
const elementFromCache = userExpectedPathHitFlag ? null : await matchElementFromCache(
|
|
1056
|
+
this,
|
|
1057
|
+
xpaths,
|
|
1058
|
+
cachePrompt,
|
|
1059
|
+
param.cacheable
|
|
1060
|
+
);
|
|
1061
|
+
const cacheHitFlag = !!elementFromCache;
|
|
1062
|
+
const elementFromPlan = !userExpectedPathHitFlag && !cacheHitFlag ? matchElementFromPlan(param, pageContext.tree) : void 0;
|
|
1063
|
+
const planHitFlag = !!elementFromPlan;
|
|
1064
|
+
const elementFromAiLocate = !userExpectedPathHitFlag && !cacheHitFlag && !planHitFlag ? (await this.insight.locate(param, {
|
|
1065
|
+
// fallback to ai locate
|
|
877
1066
|
context: pageContext
|
|
878
|
-
})).element;
|
|
1067
|
+
})).element : void 0;
|
|
1068
|
+
const aiLocateHitFlag = !!elementFromAiLocate;
|
|
1069
|
+
const element = elementFromXpath || // highest priority
|
|
1070
|
+
elementFromCache || // second priority
|
|
1071
|
+
elementFromPlan || // third priority
|
|
1072
|
+
elementFromAiLocate;
|
|
879
1073
|
let currentXpaths;
|
|
880
1074
|
if (element && this.taskCache && !cacheHitFlag && param?.cacheable !== false) {
|
|
881
1075
|
const elementXpaths = await this.getElementXpath(
|
|
@@ -893,7 +1087,7 @@ var PageTaskExecutor = class {
|
|
|
893
1087
|
locateCacheRecord
|
|
894
1088
|
);
|
|
895
1089
|
} else {
|
|
896
|
-
|
|
1090
|
+
debug2(
|
|
897
1091
|
"no xpaths found, will not update cache",
|
|
898
1092
|
cachePrompt,
|
|
899
1093
|
elementXpaths
|
|
@@ -903,16 +1097,44 @@ var PageTaskExecutor = class {
|
|
|
903
1097
|
if (!element) {
|
|
904
1098
|
throw new Error(`Element not found: ${param.prompt}`);
|
|
905
1099
|
}
|
|
1100
|
+
let hitBy;
|
|
1101
|
+
if (userExpectedPathHitFlag) {
|
|
1102
|
+
hitBy = {
|
|
1103
|
+
from: "User expected path",
|
|
1104
|
+
context: {
|
|
1105
|
+
xpath: param.xpath
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
} else if (cacheHitFlag) {
|
|
1109
|
+
hitBy = {
|
|
1110
|
+
from: "Cache",
|
|
1111
|
+
context: {
|
|
1112
|
+
xpathsFromCache: xpaths,
|
|
1113
|
+
xpathsToSave: currentXpaths
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
} else if (planHitFlag) {
|
|
1117
|
+
hitBy = {
|
|
1118
|
+
from: "Planning",
|
|
1119
|
+
context: {
|
|
1120
|
+
id: elementFromPlan?.id,
|
|
1121
|
+
bbox: elementFromPlan?.bbox
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
} else if (aiLocateHitFlag) {
|
|
1125
|
+
hitBy = {
|
|
1126
|
+
from: "AI model",
|
|
1127
|
+
context: {
|
|
1128
|
+
prompt: param.prompt
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
906
1132
|
return {
|
|
907
1133
|
output: {
|
|
908
1134
|
element
|
|
909
1135
|
},
|
|
910
1136
|
pageContext,
|
|
911
|
-
|
|
912
|
-
hit: cacheHitFlag,
|
|
913
|
-
originalXpaths: xpaths,
|
|
914
|
-
currentXpaths
|
|
915
|
-
}
|
|
1137
|
+
hitBy
|
|
916
1138
|
};
|
|
917
1139
|
}
|
|
918
1140
|
};
|
|
@@ -1008,7 +1230,7 @@ var PageTaskExecutor = class {
|
|
|
1008
1230
|
thought: plan2.thought,
|
|
1009
1231
|
locate: plan2.locate,
|
|
1010
1232
|
executor: async (param, { element }) => {
|
|
1011
|
-
(0,
|
|
1233
|
+
(0, import_utils8.assert)(element, "Element not found, cannot tap");
|
|
1012
1234
|
await this.page.mouse.click(element.center[0], element.center[1]);
|
|
1013
1235
|
}
|
|
1014
1236
|
};
|
|
@@ -1020,7 +1242,7 @@ var PageTaskExecutor = class {
|
|
|
1020
1242
|
thought: plan2.thought,
|
|
1021
1243
|
locate: plan2.locate,
|
|
1022
1244
|
executor: async (param, { element }) => {
|
|
1023
|
-
(0,
|
|
1245
|
+
(0, import_utils8.assert)(element, "Element not found, cannot right click");
|
|
1024
1246
|
await this.page.mouse.click(
|
|
1025
1247
|
element.center[0],
|
|
1026
1248
|
element.center[1],
|
|
@@ -1037,7 +1259,7 @@ var PageTaskExecutor = class {
|
|
|
1037
1259
|
thought: plan2.thought,
|
|
1038
1260
|
locate: plan2.locate,
|
|
1039
1261
|
executor: async (taskParam) => {
|
|
1040
|
-
(0,
|
|
1262
|
+
(0, import_utils8.assert)(
|
|
1041
1263
|
taskParam?.start_box && taskParam?.end_box,
|
|
1042
1264
|
"No start_box or end_box to drag"
|
|
1043
1265
|
);
|
|
@@ -1052,7 +1274,7 @@ var PageTaskExecutor = class {
|
|
|
1052
1274
|
thought: plan2.thought,
|
|
1053
1275
|
locate: plan2.locate,
|
|
1054
1276
|
executor: async (param, { element }) => {
|
|
1055
|
-
(0,
|
|
1277
|
+
(0, import_utils8.assert)(element, "Element not found, cannot hover");
|
|
1056
1278
|
await this.page.mouse.move(element.center[0], element.center[1]);
|
|
1057
1279
|
}
|
|
1058
1280
|
};
|
|
@@ -1104,7 +1326,7 @@ var PageTaskExecutor = class {
|
|
|
1104
1326
|
`Unknown scroll direction: ${taskParam.direction}`
|
|
1105
1327
|
);
|
|
1106
1328
|
}
|
|
1107
|
-
await (0,
|
|
1329
|
+
await (0, import_utils7.sleep)(500);
|
|
1108
1330
|
} else {
|
|
1109
1331
|
throw new Error(
|
|
1110
1332
|
`Unknown scroll event type: ${scrollToEventName}, taskParam: ${JSON.stringify(
|
|
@@ -1123,7 +1345,7 @@ var PageTaskExecutor = class {
|
|
|
1123
1345
|
thought: plan2.thought,
|
|
1124
1346
|
locate: plan2.locate,
|
|
1125
1347
|
executor: async (taskParam) => {
|
|
1126
|
-
await (0,
|
|
1348
|
+
await (0, import_utils7.sleep)(taskParam?.timeMs || 3e3);
|
|
1127
1349
|
}
|
|
1128
1350
|
};
|
|
1129
1351
|
tasks.push(taskActionSleep);
|
|
@@ -1171,7 +1393,7 @@ var PageTaskExecutor = class {
|
|
|
1171
1393
|
thought: plan2.thought,
|
|
1172
1394
|
locate: plan2.locate,
|
|
1173
1395
|
executor: async (param) => {
|
|
1174
|
-
(0,
|
|
1396
|
+
(0, import_utils8.assert)(
|
|
1175
1397
|
isAndroidPage(this.page),
|
|
1176
1398
|
"Cannot use home button on non-Android devices"
|
|
1177
1399
|
);
|
|
@@ -1187,7 +1409,7 @@ var PageTaskExecutor = class {
|
|
|
1187
1409
|
thought: plan2.thought,
|
|
1188
1410
|
locate: plan2.locate,
|
|
1189
1411
|
executor: async (param) => {
|
|
1190
|
-
(0,
|
|
1412
|
+
(0, import_utils8.assert)(
|
|
1191
1413
|
isAndroidPage(this.page),
|
|
1192
1414
|
"Cannot use back button on non-Android devices"
|
|
1193
1415
|
);
|
|
@@ -1203,7 +1425,7 @@ var PageTaskExecutor = class {
|
|
|
1203
1425
|
thought: plan2.thought,
|
|
1204
1426
|
locate: plan2.locate,
|
|
1205
1427
|
executor: async (param) => {
|
|
1206
|
-
(0,
|
|
1428
|
+
(0, import_utils8.assert)(
|
|
1207
1429
|
isAndroidPage(this.page),
|
|
1208
1430
|
"Cannot use recent apps button on non-Android devices"
|
|
1209
1431
|
);
|
|
@@ -1354,7 +1576,7 @@ var PageTaskExecutor = class {
|
|
|
1354
1576
|
}
|
|
1355
1577
|
}
|
|
1356
1578
|
if (finalActions.length === 0) {
|
|
1357
|
-
(0,
|
|
1579
|
+
(0, import_utils8.assert)(
|
|
1358
1580
|
!more_actions_needed_by_instruction || sleep3,
|
|
1359
1581
|
error ? `Failed to plan: ${error}` : planParsingError || "No plan found"
|
|
1360
1582
|
);
|
|
@@ -1592,7 +1814,7 @@ var PageTaskExecutor = class {
|
|
|
1592
1814
|
);
|
|
1593
1815
|
let outputResult = data;
|
|
1594
1816
|
if (ifTypeRestricted) {
|
|
1595
|
-
(0,
|
|
1817
|
+
(0, import_utils8.assert)(data?.result !== void 0, "No result in query data");
|
|
1596
1818
|
outputResult = data.result;
|
|
1597
1819
|
}
|
|
1598
1820
|
return {
|
|
@@ -1688,9 +1910,9 @@ var PageTaskExecutor = class {
|
|
|
1688
1910
|
onTaskStart: this.onTaskStartCallback
|
|
1689
1911
|
});
|
|
1690
1912
|
const { timeoutMs, checkIntervalMs } = opt;
|
|
1691
|
-
(0,
|
|
1692
|
-
(0,
|
|
1693
|
-
(0,
|
|
1913
|
+
(0, import_utils8.assert)(assertion, "No assertion for waitFor");
|
|
1914
|
+
(0, import_utils8.assert)(timeoutMs, "No timeoutMs for waitFor");
|
|
1915
|
+
(0, import_utils8.assert)(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1694
1916
|
const overallStartTime = Date.now();
|
|
1695
1917
|
let startTime = Date.now();
|
|
1696
1918
|
let errorThought = "";
|
|
@@ -1744,9 +1966,9 @@ var PageTaskExecutor = class {
|
|
|
1744
1966
|
};
|
|
1745
1967
|
|
|
1746
1968
|
// src/common/plan-builder.ts
|
|
1747
|
-
var
|
|
1748
|
-
var
|
|
1749
|
-
var
|
|
1969
|
+
var import_logger3 = require("@midscene/shared/logger");
|
|
1970
|
+
var import_utils10 = require("@midscene/shared/utils");
|
|
1971
|
+
var debug3 = (0, import_logger3.getDebug)("plan-builder");
|
|
1750
1972
|
function buildPlans(type, locateParam, param) {
|
|
1751
1973
|
let returnPlans = [];
|
|
1752
1974
|
const locatePlan = locateParam ? {
|
|
@@ -1756,8 +1978,8 @@ function buildPlans(type, locateParam, param) {
|
|
|
1756
1978
|
thought: ""
|
|
1757
1979
|
} : null;
|
|
1758
1980
|
if (type === "Tap" || type === "Hover" || type === "RightClick") {
|
|
1759
|
-
(0,
|
|
1760
|
-
(0,
|
|
1981
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1982
|
+
(0, import_utils10.assert)(locatePlan, `missing locate info for action "${type}"`);
|
|
1761
1983
|
const tapPlan = {
|
|
1762
1984
|
type,
|
|
1763
1985
|
param: null,
|
|
@@ -1768,9 +1990,9 @@ function buildPlans(type, locateParam, param) {
|
|
|
1768
1990
|
}
|
|
1769
1991
|
if (type === "Input" || type === "KeyboardPress") {
|
|
1770
1992
|
if (type === "Input") {
|
|
1771
|
-
(0,
|
|
1993
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1772
1994
|
}
|
|
1773
|
-
(0,
|
|
1995
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1774
1996
|
const inputPlan = {
|
|
1775
1997
|
type,
|
|
1776
1998
|
param,
|
|
@@ -1784,7 +2006,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1784
2006
|
}
|
|
1785
2007
|
}
|
|
1786
2008
|
if (type === "Scroll") {
|
|
1787
|
-
(0,
|
|
2009
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1788
2010
|
const scrollPlan = {
|
|
1789
2011
|
type,
|
|
1790
2012
|
param,
|
|
@@ -1798,7 +2020,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1798
2020
|
}
|
|
1799
2021
|
}
|
|
1800
2022
|
if (type === "Sleep") {
|
|
1801
|
-
(0,
|
|
2023
|
+
(0, import_utils10.assert)(param, `missing param for action "${type}"`);
|
|
1802
2024
|
const sleepPlan = {
|
|
1803
2025
|
type,
|
|
1804
2026
|
param,
|
|
@@ -1808,7 +2030,7 @@ function buildPlans(type, locateParam, param) {
|
|
|
1808
2030
|
returnPlans = [sleepPlan];
|
|
1809
2031
|
}
|
|
1810
2032
|
if (type === "Locate") {
|
|
1811
|
-
(0,
|
|
2033
|
+
(0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
|
|
1812
2034
|
const locatePlan2 = {
|
|
1813
2035
|
type,
|
|
1814
2036
|
param: locateParam,
|
|
@@ -1818,187 +2040,12 @@ function buildPlans(type, locateParam, param) {
|
|
|
1818
2040
|
returnPlans = [locatePlan2];
|
|
1819
2041
|
}
|
|
1820
2042
|
if (returnPlans) {
|
|
1821
|
-
|
|
2043
|
+
debug3("buildPlans", returnPlans);
|
|
1822
2044
|
return returnPlans;
|
|
1823
2045
|
}
|
|
1824
2046
|
throw new Error(`Not supported type: ${type}`);
|
|
1825
2047
|
}
|
|
1826
2048
|
|
|
1827
|
-
// src/common/task-cache.ts
|
|
1828
|
-
var import_node_assert = __toESM(require("assert"));
|
|
1829
|
-
var import_node_fs2 = require("fs");
|
|
1830
|
-
var import_node_path2 = require("path");
|
|
1831
|
-
var import_common2 = require("@midscene/shared/common");
|
|
1832
|
-
var import_logger3 = require("@midscene/shared/logger");
|
|
1833
|
-
var import_utils9 = require("@midscene/shared/utils");
|
|
1834
|
-
var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
1835
|
-
var import_semver = __toESM(require("semver"));
|
|
1836
|
-
|
|
1837
|
-
// package.json
|
|
1838
|
-
var version = "0.19.1";
|
|
1839
|
-
|
|
1840
|
-
// src/common/task-cache.ts
|
|
1841
|
-
var debug3 = (0, import_logger3.getDebug)("cache");
|
|
1842
|
-
var lowestSupportedMidsceneVersion = "0.16.10";
|
|
1843
|
-
var cacheFileExt = ".cache.yaml";
|
|
1844
|
-
var TaskCache = class {
|
|
1845
|
-
// Track matched records
|
|
1846
|
-
constructor(cacheId, isCacheResultUsed, cacheFilePath) {
|
|
1847
|
-
this.matchedCacheIndices = /* @__PURE__ */ new Set();
|
|
1848
|
-
(0, import_node_assert.default)(cacheId, "cacheId is required");
|
|
1849
|
-
this.cacheId = replaceIllegalPathCharsAndSpace(cacheId);
|
|
1850
|
-
this.cacheFilePath = import_utils9.ifInBrowser ? void 0 : cacheFilePath || (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("cache"), `${this.cacheId}${cacheFileExt}`);
|
|
1851
|
-
this.isCacheResultUsed = isCacheResultUsed;
|
|
1852
|
-
let cacheContent;
|
|
1853
|
-
if (this.cacheFilePath) {
|
|
1854
|
-
cacheContent = this.loadCacheFromFile();
|
|
1855
|
-
}
|
|
1856
|
-
if (!cacheContent) {
|
|
1857
|
-
cacheContent = {
|
|
1858
|
-
midsceneVersion: version,
|
|
1859
|
-
cacheId: this.cacheId,
|
|
1860
|
-
caches: []
|
|
1861
|
-
};
|
|
1862
|
-
}
|
|
1863
|
-
this.cache = cacheContent;
|
|
1864
|
-
this.cacheOriginalLength = this.cache.caches.length;
|
|
1865
|
-
}
|
|
1866
|
-
matchCache(prompt, type) {
|
|
1867
|
-
for (let i = 0; i < this.cacheOriginalLength; i++) {
|
|
1868
|
-
const item = this.cache.caches[i];
|
|
1869
|
-
const key = `${type}:${prompt}:${i}`;
|
|
1870
|
-
if (item.type === type && item.prompt === prompt && !this.matchedCacheIndices.has(key)) {
|
|
1871
|
-
this.matchedCacheIndices.add(key);
|
|
1872
|
-
debug3(
|
|
1873
|
-
"cache found and marked as used, type: %s, prompt: %s, index: %d",
|
|
1874
|
-
type,
|
|
1875
|
-
prompt,
|
|
1876
|
-
i
|
|
1877
|
-
);
|
|
1878
|
-
return {
|
|
1879
|
-
cacheContent: item,
|
|
1880
|
-
updateFn: (cb) => {
|
|
1881
|
-
debug3(
|
|
1882
|
-
"will call updateFn to update cache, type: %s, prompt: %s, index: %d",
|
|
1883
|
-
type,
|
|
1884
|
-
prompt,
|
|
1885
|
-
i
|
|
1886
|
-
);
|
|
1887
|
-
cb(item);
|
|
1888
|
-
debug3(
|
|
1889
|
-
"cache updated, will flush to file, type: %s, prompt: %s, index: %d",
|
|
1890
|
-
type,
|
|
1891
|
-
prompt,
|
|
1892
|
-
i
|
|
1893
|
-
);
|
|
1894
|
-
this.flushCacheToFile();
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
debug3("no unused cache found, type: %s, prompt: %s", type, prompt);
|
|
1900
|
-
return void 0;
|
|
1901
|
-
}
|
|
1902
|
-
matchPlanCache(prompt) {
|
|
1903
|
-
return this.matchCache(prompt, "plan");
|
|
1904
|
-
}
|
|
1905
|
-
matchLocateCache(prompt) {
|
|
1906
|
-
return this.matchCache(prompt, "locate");
|
|
1907
|
-
}
|
|
1908
|
-
appendCache(cache) {
|
|
1909
|
-
debug3("will append cache", cache);
|
|
1910
|
-
this.cache.caches.push(cache);
|
|
1911
|
-
this.flushCacheToFile();
|
|
1912
|
-
}
|
|
1913
|
-
loadCacheFromFile() {
|
|
1914
|
-
const cacheFile = this.cacheFilePath;
|
|
1915
|
-
(0, import_node_assert.default)(cacheFile, "cache file path is required");
|
|
1916
|
-
if (!(0, import_node_fs2.existsSync)(cacheFile)) {
|
|
1917
|
-
debug3("no cache file found, path: %s", cacheFile);
|
|
1918
|
-
return void 0;
|
|
1919
|
-
}
|
|
1920
|
-
const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, ".json");
|
|
1921
|
-
if ((0, import_node_fs2.existsSync)(jsonTypeCacheFile) && this.isCacheResultUsed) {
|
|
1922
|
-
console.warn(
|
|
1923
|
-
`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}.`
|
|
1924
|
-
);
|
|
1925
|
-
return void 0;
|
|
1926
|
-
}
|
|
1927
|
-
try {
|
|
1928
|
-
const data = (0, import_node_fs2.readFileSync)(cacheFile, "utf8");
|
|
1929
|
-
const jsonData = import_js_yaml3.default.load(data);
|
|
1930
|
-
if (!version) {
|
|
1931
|
-
debug3("no midscene version info, will not read cache from file");
|
|
1932
|
-
return void 0;
|
|
1933
|
-
}
|
|
1934
|
-
if (import_semver.default.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) && !jsonData.midsceneVersion.includes("beta")) {
|
|
1935
|
-
console.warn(
|
|
1936
|
-
`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.
|
|
1937
|
-
Please delete the existing cache and rebuild it. Sorry for the inconvenience.
|
|
1938
|
-
cache file: ${cacheFile}`
|
|
1939
|
-
);
|
|
1940
|
-
return void 0;
|
|
1941
|
-
}
|
|
1942
|
-
debug3(
|
|
1943
|
-
"cache loaded from file, path: %s, cache version: %s, record length: %s",
|
|
1944
|
-
cacheFile,
|
|
1945
|
-
jsonData.midsceneVersion,
|
|
1946
|
-
jsonData.caches.length
|
|
1947
|
-
);
|
|
1948
|
-
jsonData.midsceneVersion = version;
|
|
1949
|
-
return jsonData;
|
|
1950
|
-
} catch (err) {
|
|
1951
|
-
debug3(
|
|
1952
|
-
"cache file exists but load failed, path: %s, error: %s",
|
|
1953
|
-
cacheFile,
|
|
1954
|
-
err
|
|
1955
|
-
);
|
|
1956
|
-
return void 0;
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
flushCacheToFile() {
|
|
1960
|
-
if (!version) {
|
|
1961
|
-
debug3("no midscene version info, will not write cache to file");
|
|
1962
|
-
return;
|
|
1963
|
-
}
|
|
1964
|
-
if (!this.cacheFilePath) {
|
|
1965
|
-
debug3("no cache file path, will not write cache to file");
|
|
1966
|
-
return;
|
|
1967
|
-
}
|
|
1968
|
-
try {
|
|
1969
|
-
const dir = (0, import_node_path2.dirname)(this.cacheFilePath);
|
|
1970
|
-
if (!(0, import_node_fs2.existsSync)(dir)) {
|
|
1971
|
-
(0, import_node_fs2.mkdirSync)(dir, { recursive: true });
|
|
1972
|
-
debug3("created cache directory: %s", dir);
|
|
1973
|
-
}
|
|
1974
|
-
const yamlData = import_js_yaml3.default.dump(this.cache);
|
|
1975
|
-
(0, import_node_fs2.writeFileSync)(this.cacheFilePath, yamlData);
|
|
1976
|
-
debug3("cache flushed to file: %s", this.cacheFilePath);
|
|
1977
|
-
} catch (err) {
|
|
1978
|
-
debug3(
|
|
1979
|
-
"write cache to file failed, path: %s, error: %s",
|
|
1980
|
-
this.cacheFilePath,
|
|
1981
|
-
err
|
|
1982
|
-
);
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
updateOrAppendCacheRecord(newRecord, cachedRecord) {
|
|
1986
|
-
if (cachedRecord) {
|
|
1987
|
-
if (newRecord.type === "plan") {
|
|
1988
|
-
cachedRecord.updateFn((cache) => {
|
|
1989
|
-
cache.yamlWorkflow = newRecord.yamlWorkflow;
|
|
1990
|
-
});
|
|
1991
|
-
} else {
|
|
1992
|
-
cachedRecord.updateFn((cache) => {
|
|
1993
|
-
cache.xpaths = newRecord.xpaths;
|
|
1994
|
-
});
|
|
1995
|
-
}
|
|
1996
|
-
} else {
|
|
1997
|
-
this.appendCache(newRecord);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
2049
|
// src/common/agent.ts
|
|
2003
2050
|
var debug4 = (0, import_logger4.getDebug)("web-integration");
|
|
2004
2051
|
var distanceOfTwoPoints = (p1, p2) => {
|
|
@@ -2127,10 +2174,12 @@ ${errorTask?.errorStack}`);
|
|
|
2127
2174
|
const prompt = opt.prompt ?? locatePrompt;
|
|
2128
2175
|
const deepThink = opt.deepThink ?? false;
|
|
2129
2176
|
const cacheable = opt.cacheable ?? true;
|
|
2177
|
+
const xpath = opt.xpath;
|
|
2130
2178
|
return {
|
|
2131
2179
|
prompt,
|
|
2132
2180
|
deepThink,
|
|
2133
|
-
cacheable
|
|
2181
|
+
cacheable,
|
|
2182
|
+
xpath
|
|
2134
2183
|
};
|
|
2135
2184
|
}
|
|
2136
2185
|
return {
|
|
@@ -2288,6 +2337,9 @@ ${errorTask?.errorStack}`);
|
|
|
2288
2337
|
this.afterTaskRunning(executor);
|
|
2289
2338
|
return output;
|
|
2290
2339
|
}
|
|
2340
|
+
async aiAsk(prompt, opt = defaultInsightExtractOption) {
|
|
2341
|
+
return this.aiString(prompt, opt);
|
|
2342
|
+
}
|
|
2291
2343
|
async describeElementAtPoint(center, opt) {
|
|
2292
2344
|
const { verifyPrompt = true, retryLimit = 3 } = opt || {};
|
|
2293
2345
|
let success = false;
|
|
@@ -2712,7 +2764,7 @@ function sleep2(ms) {
|
|
|
2712
2764
|
var ChromeExtensionProxyPage = class {
|
|
2713
2765
|
constructor(forceSameTabNavigation) {
|
|
2714
2766
|
this.pageType = "chrome-extension-proxy";
|
|
2715
|
-
this.version = "0.
|
|
2767
|
+
this.version = "0.20.0";
|
|
2716
2768
|
this.activeTabId = null;
|
|
2717
2769
|
this.tabIdOfDebuggerAttached = null;
|
|
2718
2770
|
this.attachingDebugger = null;
|