@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
@@ -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 import_utils = require("@midscene/core/utils");
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 import_utils2 = require("@midscene/shared/utils");
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, import_utils2.assert)(page, "page is required");
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, import_utils.uploadTestInfoToServer)({ testUrl: url });
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, import_utils2.assert)(screenshotBase64, "screenshotBase64 is required");
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, import_utils2.uuid)().substring(0, 8);
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, import_utils2.logMsg)(`Midscene - report file updated: ${filepath}`);
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 import_node_fs = require("fs");
216
- var import_node_path = require("path");
217
- var import_utils3 = require("@midscene/shared/utils");
218
- var import_common = require("@midscene/shared/common");
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 (import_utils3.ifInBrowser) {
427
+ if (import_utils5.ifInBrowser) {
231
428
  this.output = void 0;
232
429
  } else if (this.target?.output) {
233
- this.output = (0, import_node_path.resolve)(process.cwd(), this.target.output);
430
+ this.output = (0, import_node_path2.resolve)(process.cwd(), this.target.output);
234
431
  } else {
235
- this.output = (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
432
+ this.output = (0, import_node_path2.join)((0, import_common2.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
236
433
  }
237
- if (import_utils3.ifInBrowser) {
434
+ if (import_utils5.ifInBrowser) {
238
435
  this.unstableLogContent = void 0;
239
436
  } else if (typeof this.target?.unstableLogContent === "string") {
240
- this.unstableLogContent = (0, import_node_path.resolve)(
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, import_node_path.join)(
246
- (0, import_common.getMidsceneRunSubDir)("output"),
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, import_node_path.resolve)(process.cwd(), this.output);
292
- const outputDir = (0, import_node_path.dirname)(output);
293
- if (!(0, import_node_fs.existsSync)(outputDir)) {
294
- (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
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, import_node_fs.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
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, import_node_path.resolve)(process.cwd(), this.unstableLogContent);
303
- const outputDir = (0, import_node_path.dirname)(filePath);
304
- if (!(0, import_node_fs.existsSync)(outputDir)) {
305
- (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
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, import_node_fs.writeFileSync)(filePath, JSON.stringify(content, null, 2));
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, import_utils3.assert)(flow, "missing flow in task");
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, import_utils3.assert)(prompt, "missing prompt for ai (aiAction)");
321
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiAssert");
333
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiQuery");
346
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for number");
360
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for string");
374
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for boolean");
388
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiLocate");
398
- (0, import_utils3.assert)(
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, import_utils3.assert)(prompt, "missing prompt for aiWaitFor");
408
- (0, import_utils3.assert)(
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, import_utils3.assert)(
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 import_js_yaml = __toESM(require("js-yaml"));
744
+ var import_js_yaml2 = __toESM(require("js-yaml"));
541
745
 
542
746
  // src/yaml/utils.ts
543
- var import_utils4 = require("@midscene/shared/utils");
544
- var import_js_yaml2 = __toESM(require("js-yaml"));
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 = import_js_yaml2.default.load(interpolatedContent, {
571
- schema: import_js_yaml2.default.JSON_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, import_utils4.assert)(
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, import_utils4.assert)(
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, import_utils4.assert)(
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, import_utils4.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
594
- (0, import_utils4.assert)(
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 import_utils5 = require("@midscene/core/utils");
815
+ var import_utils7 = require("@midscene/core/utils");
612
816
  var import_constants = require("@midscene/shared/constants");
613
- var import_logger = require("@midscene/shared/logger");
614
- var import_utils6 = require("@midscene/shared/utils");
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 debug = (0, import_logger.getDebug)("page-task-executor");
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
- debug(
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
- debug("getXpathsById error: ", error);
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, import_utils5.sleep)(100);
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, import_utils5.sleep)(200)
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, import_utils6.assert)(
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
- let cacheHitFlag = false;
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
- let elementFromCache = null;
851
- try {
852
- if (xpaths?.length && this.taskCache?.isCacheResultUsed && param?.cacheable !== false) {
853
- for (let i = 0; i < xpaths.length; i++) {
854
- const element2 = await this.page.getElementInfoByXpath(
855
- xpaths[i]
856
- );
857
- if (element2?.id) {
858
- elementFromCache = element2;
859
- debug("cache hit, prompt: %s", cachePrompt);
860
- cacheHitFlag = true;
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
- debug(
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
- cache: {
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, import_utils6.assert)(element, "Element not found, cannot tap");
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, import_utils6.assert)(element, "Element not found, cannot right click");
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, import_utils6.assert)(
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, import_utils6.assert)(element, "Element not found, cannot hover");
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, import_utils5.sleep)(500);
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, import_utils5.sleep)(taskParam?.timeMs || 3e3);
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(
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, import_utils6.assert)(data?.result !== void 0, "No result in query data");
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, import_utils6.assert)(assertion, "No assertion for waitFor");
1692
- (0, import_utils6.assert)(timeoutMs, "No timeoutMs for waitFor");
1693
- (0, import_utils6.assert)(checkIntervalMs, "No checkIntervalMs for waitFor");
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 import_logger2 = require("@midscene/shared/logger");
1748
- var import_utils8 = require("@midscene/shared/utils");
1749
- var debug2 = (0, import_logger2.getDebug)("plan-builder");
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
1760
- (0, import_utils8.assert)(locatePlan, `missing locate info for action "${type}"`);
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
1993
+ (0, import_utils10.assert)(locateParam, `missing locate info for action "${type}"`);
1772
1994
  }
1773
- (0, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(param, `missing param for action "${type}"`);
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, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
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
- debug2("buildPlans", returnPlans);
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.19.1";
2767
+ this.version = "0.20.0";
2716
2768
  this.activeTabId = null;
2717
2769
  this.tabIdOfDebuggerAttached = null;
2718
2770
  this.attachingDebugger = null;