@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.
Files changed (73) hide show
  1. package/dist/es/agent.js +299 -247
  2. package/dist/es/agent.js.map +1 -1
  3. package/dist/es/bridge-mode-browser.js +3 -3
  4. package/dist/es/bridge-mode.js +301 -249
  5. package/dist/es/bridge-mode.js.map +1 -1
  6. package/dist/es/chrome-extension.js +342 -290
  7. package/dist/es/chrome-extension.js.map +1 -1
  8. package/dist/es/index.js +307 -247
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/midscene-playground.js +341 -289
  11. package/dist/es/midscene-playground.js.map +1 -1
  12. package/dist/es/midscene-server.js +25 -12
  13. package/dist/es/midscene-server.js.map +1 -1
  14. package/dist/es/playground.js +341 -289
  15. package/dist/es/playground.js.map +1 -1
  16. package/dist/es/playwright-report.js +14 -1
  17. package/dist/es/playwright-report.js.map +1 -1
  18. package/dist/es/playwright-reporter.js +14 -1
  19. package/dist/es/playwright-reporter.js.map +1 -1
  20. package/dist/es/playwright.js +307 -247
  21. package/dist/es/playwright.js.map +1 -1
  22. package/dist/es/puppeteer-agent-launcher.js +299 -247
  23. package/dist/es/puppeteer-agent-launcher.js.map +1 -1
  24. package/dist/es/puppeteer.js +299 -247
  25. package/dist/es/puppeteer.js.map +1 -1
  26. package/dist/es/utils.js +42 -8
  27. package/dist/es/utils.js.map +1 -1
  28. package/dist/es/yaml.js +11 -4
  29. package/dist/es/yaml.js.map +1 -1
  30. package/dist/lib/agent.js +308 -256
  31. package/dist/lib/agent.js.map +1 -1
  32. package/dist/lib/bridge-mode-browser.js +3 -3
  33. package/dist/lib/bridge-mode.js +310 -258
  34. package/dist/lib/bridge-mode.js.map +1 -1
  35. package/dist/lib/chrome-extension.js +355 -303
  36. package/dist/lib/chrome-extension.js.map +1 -1
  37. package/dist/lib/index.js +316 -256
  38. package/dist/lib/index.js.map +1 -1
  39. package/dist/lib/midscene-playground.js +354 -302
  40. package/dist/lib/midscene-playground.js.map +1 -1
  41. package/dist/lib/midscene-server.js +28 -15
  42. package/dist/lib/midscene-server.js.map +1 -1
  43. package/dist/lib/playground.js +354 -302
  44. package/dist/lib/playground.js.map +1 -1
  45. package/dist/lib/playwright-report.js +20 -7
  46. package/dist/lib/playwright-report.js.map +1 -1
  47. package/dist/lib/playwright-reporter.js +20 -7
  48. package/dist/lib/playwright-reporter.js.map +1 -1
  49. package/dist/lib/playwright.js +316 -256
  50. package/dist/lib/playwright.js.map +1 -1
  51. package/dist/lib/puppeteer-agent-launcher.js +308 -256
  52. package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
  53. package/dist/lib/puppeteer.js +308 -256
  54. package/dist/lib/puppeteer.js.map +1 -1
  55. package/dist/lib/utils.js +48 -13
  56. package/dist/lib/utils.js.map +1 -1
  57. package/dist/lib/yaml.js +11 -4
  58. package/dist/lib/yaml.js.map +1 -1
  59. package/dist/types/agent.d.ts +6 -102
  60. package/dist/types/bridge-mode-browser.d.ts +3 -2
  61. package/dist/types/bridge-mode.d.ts +4 -4
  62. package/dist/types/{browser-5dbb4bfb.d.ts → browser-118d886d.d.ts} +1 -1
  63. package/dist/types/chrome-extension.d.ts +2 -2
  64. package/dist/types/index.d.ts +1 -1
  65. package/dist/types/midscene-server.d.ts +2 -2
  66. package/dist/types/{page-90e9f9a7.d.ts → page-471361cd.d.ts} +102 -3
  67. package/dist/types/playground.d.ts +2 -2
  68. package/dist/types/playwright.d.ts +6 -2
  69. package/dist/types/puppeteer-agent-launcher.d.ts +1 -1
  70. package/dist/types/puppeteer.d.ts +3 -3
  71. package/dist/types/utils.d.ts +2 -1
  72. package/dist/types/yaml.d.ts +1 -1
  73. 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 import_utils = require("@midscene/core/utils");
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 import_utils2 = require("@midscene/shared/utils");
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, import_utils2.assert)(page, "page is required");
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, import_utils.uploadTestInfoToServer)({ testUrl: url });
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, import_utils2.assert)(screenshotBase64, "screenshotBase64 is required");
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, import_utils2.uuid)().substring(0, 8);
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, import_utils2.logMsg)(`Midscene - report file updated: ${filepath}`);
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 import_node_fs = require("fs");
179
- var import_node_path = require("path");
180
- var import_utils3 = require("@midscene/shared/utils");
181
- var import_common = require("@midscene/shared/common");
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 (import_utils3.ifInBrowser) {
390
+ if (import_utils5.ifInBrowser) {
194
391
  this.output = void 0;
195
392
  } else if (this.target?.output) {
196
- this.output = (0, import_node_path.resolve)(process.cwd(), this.target.output);
393
+ this.output = (0, import_node_path2.resolve)(process.cwd(), this.target.output);
197
394
  } else {
198
- this.output = (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
395
+ this.output = (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
199
396
  }
200
- if (import_utils3.ifInBrowser) {
397
+ if (import_utils5.ifInBrowser) {
201
398
  this.unstableLogContent = void 0;
202
399
  } else if (typeof this.target?.unstableLogContent === "string") {
203
- this.unstableLogContent = (0, import_node_path.resolve)(
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, import_node_path.join)(
209
- (0, import_common.getMidsceneRunSubDir)("output"),
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, import_node_path.resolve)(process.cwd(), this.output);
255
- const outputDir = (0, import_node_path.dirname)(output);
256
- if (!(0, import_node_fs.existsSync)(outputDir)) {
257
- (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
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, import_node_fs.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
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, import_node_path.resolve)(process.cwd(), this.unstableLogContent);
266
- const outputDir = (0, import_node_path.dirname)(filePath);
267
- if (!(0, import_node_fs.existsSync)(outputDir)) {
268
- (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
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, import_node_fs.writeFileSync)(filePath, JSON.stringify(content, null, 2));
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, import_utils3.assert)(flow, "missing flow in task");
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, import_utils3.assert)(prompt, "missing prompt for ai (aiAction)");
284
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiAssert");
296
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiQuery");
309
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for number");
323
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for string");
337
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for boolean");
351
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiLocate");
361
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiWaitFor");
371
- (0, import_utils3.assert)(
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, import_utils3.assert)(
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 import_js_yaml = __toESM(require("js-yaml"));
707
+ var import_js_yaml2 = __toESM(require("js-yaml"));
504
708
 
505
709
  // src/yaml/utils.ts
506
- var import_utils4 = require("@midscene/shared/utils");
507
- var import_js_yaml2 = __toESM(require("js-yaml"));
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 = import_js_yaml2.default.load(interpolatedContent, {
534
- schema: import_js_yaml2.default.JSON_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, import_utils4.assert)(
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, import_utils4.assert)(
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, import_utils4.assert)(
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, import_utils4.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
557
- (0, import_utils4.assert)(
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 import_utils5 = require("@midscene/core/utils");
778
+ var import_utils7 = require("@midscene/core/utils");
575
779
  var import_constants = require("@midscene/shared/constants");
576
- var import_logger = require("@midscene/shared/logger");
577
- var import_utils6 = require("@midscene/shared/utils");
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 debug = (0, import_logger.getDebug)("page-task-executor");
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
- debug(
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
- debug("getXpathsById error: ", error);
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, import_utils5.sleep)(100);
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, import_utils5.sleep)(200)
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, import_utils6.assert)(
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
- let cacheHitFlag = false;
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
- let elementFromCache = null;
791
- try {
792
- if (xpaths?.length && this.taskCache?.isCacheResultUsed && param?.cacheable !== false) {
793
- for (let i = 0; i < xpaths.length; i++) {
794
- const element2 = await this.page.getElementInfoByXpath(
795
- xpaths[i]
796
- );
797
- if (element2?.id) {
798
- elementFromCache = element2;
799
- debug("cache hit, prompt: %s", cachePrompt);
800
- cacheHitFlag = true;
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
- debug(
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
- cache: {
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, import_utils6.assert)(element, "Element not found, cannot tap");
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, import_utils6.assert)(element, "Element not found, cannot right click");
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, import_utils6.assert)(
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, import_utils6.assert)(element, "Element not found, cannot hover");
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, import_utils5.sleep)(500);
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, import_utils5.sleep)(taskParam?.timeMs || 3e3);
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(data?.result !== void 0, "No result in query data");
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, import_utils6.assert)(assertion, "No assertion for waitFor");
1632
- (0, import_utils6.assert)(timeoutMs, "No timeoutMs for waitFor");
1633
- (0, import_utils6.assert)(checkIntervalMs, "No checkIntervalMs for waitFor");
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 import_logger2 = require("@midscene/shared/logger");
1688
- var import_utils8 = require("@midscene/shared/utils");
1689
- var debug2 = (0, import_logger2.getDebug)("plan-builder");
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
1700
- (0, import_utils8.assert)(locatePlan, `missing locate info for action "${type}"`);
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
1933
+ (0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
1712
1934
  }
1713
- (0, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
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
- debug2("buildPlans", returnPlans);
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;