@midscene/web 0.3.2 → 0.3.3

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.
@@ -0,0 +1,1245 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __commonJS = (cb, mod) => function __require() {
25
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
26
+ };
27
+ var __copyProps = (to, from, except, desc) => {
28
+ if (from && typeof from === "object" || typeof from === "function") {
29
+ for (let key of __getOwnPropNames(from))
30
+ if (!__hasOwnProp.call(to, key) && key !== except)
31
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
32
+ }
33
+ return to;
34
+ };
35
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
36
+ // If the importer is in node compatibility mode or this is not an ESM
37
+ // file that has been converted to a CommonJS file using a Babel-
38
+ // compatible transform (i.e. "__esModule" has not been set), then set
39
+ // "default" to the CommonJS "module.exports" for node compatibility.
40
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
41
+ mod
42
+ ));
43
+
44
+ // ../../node_modules/.pnpm/dayjs@1.11.11/node_modules/dayjs/dayjs.min.js
45
+ var require_dayjs_min = __commonJS({
46
+ "../../node_modules/.pnpm/dayjs@1.11.11/node_modules/dayjs/dayjs.min.js"(exports, module) {
47
+ "use strict";
48
+ !function(t, e) {
49
+ "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
50
+ }(exports, function() {
51
+ "use strict";
52
+ var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) {
53
+ var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100;
54
+ return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]";
55
+ } }, m = function(t2, e2, n2) {
56
+ var r2 = String(t2);
57
+ return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
58
+ }, v = { s: m, z: function(t2) {
59
+ var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
60
+ return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0");
61
+ }, m: function t2(e2, n2) {
62
+ if (e2.date() < n2.date())
63
+ return -t2(n2, e2);
64
+ var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, c), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), c);
65
+ return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0);
66
+ }, a: function(t2) {
67
+ return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2);
68
+ }, p: function(t2) {
69
+ return { M: c, y: h, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: f }[t2] || String(t2 || "").toLowerCase().replace(/s$/, "");
70
+ }, u: function(t2) {
71
+ return void 0 === t2;
72
+ } }, g = "en", D = {};
73
+ D[g] = M;
74
+ var p = "$isDayjsObject", S = function(t2) {
75
+ return t2 instanceof _ || !(!t2 || !t2[p]);
76
+ }, w = function t2(e2, n2, r2) {
77
+ var i2;
78
+ if (!e2)
79
+ return g;
80
+ if ("string" == typeof e2) {
81
+ var s2 = e2.toLowerCase();
82
+ D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2);
83
+ var u2 = e2.split("-");
84
+ if (!i2 && u2.length > 1)
85
+ return t2(u2[0]);
86
+ } else {
87
+ var a2 = e2.name;
88
+ D[a2] = e2, i2 = a2;
89
+ }
90
+ return !r2 && i2 && (g = i2), i2 || !r2 && g;
91
+ }, O = function(t2, e2) {
92
+ if (S(t2))
93
+ return t2.clone();
94
+ var n2 = "object" == typeof e2 ? e2 : {};
95
+ return n2.date = t2, n2.args = arguments, new _(n2);
96
+ }, b = v;
97
+ b.l = w, b.i = S, b.w = function(t2, e2) {
98
+ return O(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset });
99
+ };
100
+ var _ = function() {
101
+ function M2(t2) {
102
+ this.$L = w(t2.locale, null, true), this.parse(t2), this.$x = this.$x || t2.x || {}, this[p] = true;
103
+ }
104
+ var m2 = M2.prototype;
105
+ return m2.parse = function(t2) {
106
+ this.$d = function(t3) {
107
+ var e2 = t3.date, n2 = t3.utc;
108
+ if (null === e2)
109
+ return /* @__PURE__ */ new Date(NaN);
110
+ if (b.u(e2))
111
+ return /* @__PURE__ */ new Date();
112
+ if (e2 instanceof Date)
113
+ return new Date(e2);
114
+ if ("string" == typeof e2 && !/Z$/i.test(e2)) {
115
+ var r2 = e2.match($);
116
+ if (r2) {
117
+ var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3);
118
+ return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2);
119
+ }
120
+ }
121
+ return new Date(e2);
122
+ }(t2), this.init();
123
+ }, m2.init = function() {
124
+ var t2 = this.$d;
125
+ this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds();
126
+ }, m2.$utils = function() {
127
+ return b;
128
+ }, m2.isValid = function() {
129
+ return !(this.$d.toString() === l);
130
+ }, m2.isSame = function(t2, e2) {
131
+ var n2 = O(t2);
132
+ return this.startOf(e2) <= n2 && n2 <= this.endOf(e2);
133
+ }, m2.isAfter = function(t2, e2) {
134
+ return O(t2) < this.startOf(e2);
135
+ }, m2.isBefore = function(t2, e2) {
136
+ return this.endOf(e2) < O(t2);
137
+ }, m2.$g = function(t2, e2, n2) {
138
+ return b.u(t2) ? this[e2] : this.set(n2, t2);
139
+ }, m2.unix = function() {
140
+ return Math.floor(this.valueOf() / 1e3);
141
+ }, m2.valueOf = function() {
142
+ return this.$d.getTime();
143
+ }, m2.startOf = function(t2, e2) {
144
+ var n2 = this, r2 = !!b.u(e2) || e2, f2 = b.p(t2), l2 = function(t3, e3) {
145
+ var i2 = b.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2);
146
+ return r2 ? i2 : i2.endOf(a);
147
+ }, $2 = function(t3, e3) {
148
+ return b.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2);
149
+ }, y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : "");
150
+ switch (f2) {
151
+ case h:
152
+ return r2 ? l2(1, 0) : l2(31, 11);
153
+ case c:
154
+ return r2 ? l2(1, M3) : l2(0, M3 + 1);
155
+ case o:
156
+ var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2;
157
+ return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3);
158
+ case a:
159
+ case d:
160
+ return $2(v2 + "Hours", 0);
161
+ case u:
162
+ return $2(v2 + "Minutes", 1);
163
+ case s:
164
+ return $2(v2 + "Seconds", 2);
165
+ case i:
166
+ return $2(v2 + "Milliseconds", 3);
167
+ default:
168
+ return this.clone();
169
+ }
170
+ }, m2.endOf = function(t2) {
171
+ return this.startOf(t2, false);
172
+ }, m2.$set = function(t2, e2) {
173
+ var n2, o2 = b.p(t2), f2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = f2 + "Date", n2[d] = f2 + "Date", n2[c] = f2 + "Month", n2[h] = f2 + "FullYear", n2[u] = f2 + "Hours", n2[s] = f2 + "Minutes", n2[i] = f2 + "Seconds", n2[r] = f2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2;
174
+ if (o2 === c || o2 === h) {
175
+ var y2 = this.clone().set(d, 1);
176
+ y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d;
177
+ } else
178
+ l2 && this.$d[l2]($2);
179
+ return this.init(), this;
180
+ }, m2.set = function(t2, e2) {
181
+ return this.clone().$set(t2, e2);
182
+ }, m2.get = function(t2) {
183
+ return this[b.p(t2)]();
184
+ }, m2.add = function(r2, f2) {
185
+ var d2, l2 = this;
186
+ r2 = Number(r2);
187
+ var $2 = b.p(f2), y2 = function(t2) {
188
+ var e2 = O(l2);
189
+ return b.w(e2.date(e2.date() + Math.round(t2 * r2)), l2);
190
+ };
191
+ if ($2 === c)
192
+ return this.set(c, this.$M + r2);
193
+ if ($2 === h)
194
+ return this.set(h, this.$y + r2);
195
+ if ($2 === a)
196
+ return y2(1);
197
+ if ($2 === o)
198
+ return y2(7);
199
+ var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3;
200
+ return b.w(m3, this);
201
+ }, m2.subtract = function(t2, e2) {
202
+ return this.add(-1 * t2, e2);
203
+ }, m2.format = function(t2) {
204
+ var e2 = this, n2 = this.$locale();
205
+ if (!this.isValid())
206
+ return n2.invalidDate || l;
207
+ var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = b.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, c2 = n2.months, f2 = n2.meridiem, h2 = function(t3, n3, i3, s3) {
208
+ return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3);
209
+ }, d2 = function(t3) {
210
+ return b.s(s2 % 12 || 12, t3, "0");
211
+ }, $2 = f2 || function(t3, e3, n3) {
212
+ var r3 = t3 < 12 ? "AM" : "PM";
213
+ return n3 ? r3.toLowerCase() : r3;
214
+ };
215
+ return r2.replace(y, function(t3, r3) {
216
+ return r3 || function(t4) {
217
+ switch (t4) {
218
+ case "YY":
219
+ return String(e2.$y).slice(-2);
220
+ case "YYYY":
221
+ return b.s(e2.$y, 4, "0");
222
+ case "M":
223
+ return a2 + 1;
224
+ case "MM":
225
+ return b.s(a2 + 1, 2, "0");
226
+ case "MMM":
227
+ return h2(n2.monthsShort, a2, c2, 3);
228
+ case "MMMM":
229
+ return h2(c2, a2);
230
+ case "D":
231
+ return e2.$D;
232
+ case "DD":
233
+ return b.s(e2.$D, 2, "0");
234
+ case "d":
235
+ return String(e2.$W);
236
+ case "dd":
237
+ return h2(n2.weekdaysMin, e2.$W, o2, 2);
238
+ case "ddd":
239
+ return h2(n2.weekdaysShort, e2.$W, o2, 3);
240
+ case "dddd":
241
+ return o2[e2.$W];
242
+ case "H":
243
+ return String(s2);
244
+ case "HH":
245
+ return b.s(s2, 2, "0");
246
+ case "h":
247
+ return d2(1);
248
+ case "hh":
249
+ return d2(2);
250
+ case "a":
251
+ return $2(s2, u2, true);
252
+ case "A":
253
+ return $2(s2, u2, false);
254
+ case "m":
255
+ return String(u2);
256
+ case "mm":
257
+ return b.s(u2, 2, "0");
258
+ case "s":
259
+ return String(e2.$s);
260
+ case "ss":
261
+ return b.s(e2.$s, 2, "0");
262
+ case "SSS":
263
+ return b.s(e2.$ms, 3, "0");
264
+ case "Z":
265
+ return i2;
266
+ }
267
+ return null;
268
+ }(t3) || i2.replace(":", "");
269
+ });
270
+ }, m2.utcOffset = function() {
271
+ return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
272
+ }, m2.diff = function(r2, d2, l2) {
273
+ var $2, y2 = this, M3 = b.p(d2), m3 = O(r2), v2 = (m3.utcOffset() - this.utcOffset()) * e, g2 = this - m3, D2 = function() {
274
+ return b.m(y2, m3);
275
+ };
276
+ switch (M3) {
277
+ case h:
278
+ $2 = D2() / 12;
279
+ break;
280
+ case c:
281
+ $2 = D2();
282
+ break;
283
+ case f:
284
+ $2 = D2() / 3;
285
+ break;
286
+ case o:
287
+ $2 = (g2 - v2) / 6048e5;
288
+ break;
289
+ case a:
290
+ $2 = (g2 - v2) / 864e5;
291
+ break;
292
+ case u:
293
+ $2 = g2 / n;
294
+ break;
295
+ case s:
296
+ $2 = g2 / e;
297
+ break;
298
+ case i:
299
+ $2 = g2 / t;
300
+ break;
301
+ default:
302
+ $2 = g2;
303
+ }
304
+ return l2 ? $2 : b.a($2);
305
+ }, m2.daysInMonth = function() {
306
+ return this.endOf(c).$D;
307
+ }, m2.$locale = function() {
308
+ return D[this.$L];
309
+ }, m2.locale = function(t2, e2) {
310
+ if (!t2)
311
+ return this.$L;
312
+ var n2 = this.clone(), r2 = w(t2, e2, true);
313
+ return r2 && (n2.$L = r2), n2;
314
+ }, m2.clone = function() {
315
+ return b.w(this.$d, this);
316
+ }, m2.toDate = function() {
317
+ return new Date(this.valueOf());
318
+ }, m2.toJSON = function() {
319
+ return this.isValid() ? this.toISOString() : null;
320
+ }, m2.toISOString = function() {
321
+ return this.$d.toISOString();
322
+ }, m2.toString = function() {
323
+ return this.$d.toUTCString();
324
+ }, M2;
325
+ }(), k = _.prototype;
326
+ return O.prototype = k, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", c], ["$y", h], ["$D", d]].forEach(function(t2) {
327
+ k[t2[1]] = function(e2) {
328
+ return this.$g(e2, t2[0], t2[1]);
329
+ };
330
+ }), O.extend = function(t2, e2) {
331
+ return t2.$i || (t2(e2, _, O), t2.$i = true), O;
332
+ }, O.locale = w, O.isDayjs = S, O.unix = function(t2) {
333
+ return O(1e3 * t2);
334
+ }, O.en = D[g], O.Ls = D, O.p = {}, O;
335
+ });
336
+ }
337
+ });
338
+
339
+ // src/playwright/ai-fixture.ts
340
+ import { randomUUID } from "crypto";
341
+
342
+ // src/common/agent.ts
343
+ import {
344
+ groupedActionDumpFileExt,
345
+ stringifyDumpData,
346
+ writeLogFile
347
+ } from "@midscene/core/utils";
348
+
349
+ // src/common/tasks.ts
350
+ import assert2 from "assert";
351
+ import Insight, {
352
+ Executor,
353
+ plan
354
+ } from "@midscene/core";
355
+ import { base64Encoded as base64Encoded2 } from "@midscene/core/image";
356
+ import { commonScreenshotParam, getTmpFile as getTmpFile2, sleep } from "@midscene/core/utils";
357
+
358
+ // src/common/task-cache.ts
359
+ var TaskCache = class {
360
+ constructor(opts) {
361
+ this.cache = opts == null ? void 0 : opts.cache;
362
+ this.newCache = {
363
+ aiTasks: []
364
+ };
365
+ }
366
+ readCache(pageContext, type, userPrompt) {
367
+ var _a;
368
+ if (this.cache) {
369
+ const { aiTasks } = this.cache;
370
+ const index = aiTasks.findIndex((item) => item.prompt === userPrompt);
371
+ if (index === -1) {
372
+ return false;
373
+ }
374
+ const taskRes = aiTasks.splice(index, 1)[0];
375
+ if ((taskRes == null ? void 0 : taskRes.type) === "locate" && !((_a = taskRes.response) == null ? void 0 : _a.elements.every((element) => {
376
+ const findIndex = pageContext.content.findIndex(
377
+ (contentElement) => contentElement.id === element.id
378
+ );
379
+ if (findIndex === -1) {
380
+ return false;
381
+ }
382
+ return true;
383
+ }))) {
384
+ return false;
385
+ }
386
+ if (taskRes && taskRes.type === type && taskRes.prompt === userPrompt && this.pageContextEqual(taskRes.pageContext, pageContext)) {
387
+ return taskRes.response;
388
+ }
389
+ }
390
+ return false;
391
+ }
392
+ saveCache(cache) {
393
+ var _a;
394
+ if (cache) {
395
+ (_a = this.newCache) == null ? void 0 : _a.aiTasks.push(cache);
396
+ }
397
+ }
398
+ pageContextEqual(taskPageContext, pageContext) {
399
+ return taskPageContext.size.width === pageContext.size.width && taskPageContext.size.height === pageContext.size.height;
400
+ }
401
+ /**
402
+ * Generate task cache data.
403
+ * This method is mainly used to create or obtain some cached data for tasks, and it returns a new cache object.
404
+ * In the cache object, it may contain task-related information, states, or other necessary data.
405
+ * It is assumed that the `newCache` property already exists in the current class or object and is a data structure used to store task cache.
406
+ * @returns {Object} Returns a new cache object, which may include task cache data.
407
+ */
408
+ generateTaskCache() {
409
+ return this.newCache;
410
+ }
411
+ };
412
+
413
+ // src/common/utils.ts
414
+ var import_dayjs = __toESM(require_dayjs_min());
415
+ import assert from "assert";
416
+ import fs, { readFileSync } from "fs";
417
+ import path from "path";
418
+ import {
419
+ base64Encoded,
420
+ imageInfoOfBase64
421
+ } from "@midscene/core/image";
422
+ import { getTmpFile } from "@midscene/core/utils";
423
+
424
+ // src/web-element.ts
425
+ var WebElementInfo = class {
426
+ constructor({
427
+ content,
428
+ rect,
429
+ page,
430
+ locator,
431
+ id,
432
+ attributes
433
+ }) {
434
+ this.content = content;
435
+ this.rect = rect;
436
+ this.center = [
437
+ Math.floor(rect.left + rect.width / 2),
438
+ Math.floor(rect.top + rect.height / 2)
439
+ ];
440
+ this.page = page;
441
+ this.locator = locator;
442
+ this.id = id;
443
+ this.attributes = attributes;
444
+ }
445
+ };
446
+
447
+ // src/common/utils.ts
448
+ async function parseContextFromWebPage(page, _opt) {
449
+ assert(page, "page is required");
450
+ const url = page.url();
451
+ const file = getTmpFile("jpeg");
452
+ await page.screenshot({ path: file, type: "jpeg", quality: 75 });
453
+ const screenshotBuffer = readFileSync(file);
454
+ const screenshotBase64 = base64Encoded(file);
455
+ const captureElementSnapshot = await getElementInfosFromPage(page);
456
+ const elementsInfo = await alignElements(
457
+ screenshotBuffer,
458
+ captureElementSnapshot,
459
+ page
460
+ );
461
+ const size = await imageInfoOfBase64(screenshotBase64);
462
+ return {
463
+ content: elementsInfo,
464
+ size,
465
+ screenshotBase64,
466
+ url
467
+ };
468
+ }
469
+ async function getElementInfosFromPage(page) {
470
+ const pathDir = findNearestPackageJson(__dirname);
471
+ assert(pathDir, `can't find pathDir, with ${__dirname}`);
472
+ const scriptPath = path.join(pathDir, "./dist/script/htmlElement.js");
473
+ const elementInfosScriptContent = readFileSync(scriptPath, "utf-8");
474
+ const extraReturnLogic = `${elementInfosScriptContent}midscene_element_inspector.extractTextWithPosition()`;
475
+ const captureElementSnapshot = await page.evaluate(extraReturnLogic);
476
+ return captureElementSnapshot;
477
+ }
478
+ var sizeThreshold = 3;
479
+ async function alignElements(screenshotBuffer, elements, page) {
480
+ const textsAligned = [];
481
+ const validElements = elements.filter((item) => {
482
+ return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
483
+ });
484
+ for (const item of validElements) {
485
+ const { rect, id, content, attributes, locator } = item;
486
+ textsAligned.push(
487
+ new WebElementInfo({
488
+ rect,
489
+ locator,
490
+ id,
491
+ content,
492
+ attributes,
493
+ page
494
+ })
495
+ );
496
+ }
497
+ return textsAligned;
498
+ }
499
+ function findNearestPackageJson(dir) {
500
+ const packageJsonPath = path.join(dir, "package.json");
501
+ if (fs.existsSync(packageJsonPath)) {
502
+ return dir;
503
+ }
504
+ const parentDir = path.dirname(dir);
505
+ if (parentDir === dir) {
506
+ return null;
507
+ }
508
+ return findNearestPackageJson(parentDir);
509
+ }
510
+ function reportFileName(tag = "web") {
511
+ const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss-SSS");
512
+ return `${tag}-${dateTimeInFileName}`;
513
+ }
514
+ function printReportMsg(filepath) {
515
+ console.log("Midscene - report file updated:", filepath);
516
+ }
517
+
518
+ // src/common/tasks.ts
519
+ var PageTaskExecutor = class {
520
+ constructor(page, opts) {
521
+ this.page = page;
522
+ this.insight = new Insight(async () => {
523
+ return await parseContextFromWebPage(page);
524
+ });
525
+ this.taskCache = new TaskCache(opts);
526
+ }
527
+ async recordScreenshot(timing) {
528
+ const file = getTmpFile2("jpeg");
529
+ await this.page.screenshot(__spreadProps(__spreadValues({}, commonScreenshotParam), {
530
+ path: file
531
+ }));
532
+ const item = {
533
+ type: "screenshot",
534
+ ts: Date.now(),
535
+ screenshot: base64Encoded2(file),
536
+ timing
537
+ };
538
+ return item;
539
+ }
540
+ wrapExecutorWithScreenshot(taskApply) {
541
+ const taskWithScreenshot = __spreadProps(__spreadValues({}, taskApply), {
542
+ executor: async (param, context, ...args) => {
543
+ const recorder = [];
544
+ const { task } = context;
545
+ task.recorder = recorder;
546
+ const shot = await this.recordScreenshot(`before ${task.type}`);
547
+ recorder.push(shot);
548
+ const result = await taskApply.executor(param, context, ...args);
549
+ if (taskApply.type === "Action") {
550
+ await sleep(1e3);
551
+ const shot2 = await this.recordScreenshot("after Action");
552
+ recorder.push(shot2);
553
+ }
554
+ return result;
555
+ }
556
+ });
557
+ return taskWithScreenshot;
558
+ }
559
+ async convertPlanToExecutable(plans) {
560
+ const tasks = plans.map((plan2) => {
561
+ if (plan2.type === "Locate") {
562
+ const taskFind = {
563
+ type: "Insight",
564
+ subType: "Locate",
565
+ param: plan2.param,
566
+ executor: async (param) => {
567
+ let insightDump;
568
+ const dumpCollector = (dump) => {
569
+ insightDump = dump;
570
+ };
571
+ this.insight.onceDumpUpdatedFn = dumpCollector;
572
+ const pageContext = await this.insight.contextRetrieverFn();
573
+ const locateCache = this.taskCache.readCache(
574
+ pageContext,
575
+ "locate",
576
+ param.prompt
577
+ );
578
+ let locateResult;
579
+ const callAI = this.insight.aiVendorFn;
580
+ const element = await this.insight.locate(param.prompt, {
581
+ callAI: async (...message) => {
582
+ if (locateCache) {
583
+ locateResult = locateCache;
584
+ return Promise.resolve(locateCache);
585
+ }
586
+ locateResult = await callAI(...message);
587
+ assert2(locateResult);
588
+ return locateResult;
589
+ }
590
+ });
591
+ assert2(element, `Element not found: ${param.prompt}`);
592
+ if (locateResult) {
593
+ this.taskCache.saveCache({
594
+ type: "locate",
595
+ pageContext: {
596
+ url: pageContext.url,
597
+ size: pageContext.size
598
+ },
599
+ prompt: param.prompt,
600
+ response: locateResult
601
+ });
602
+ }
603
+ return {
604
+ output: {
605
+ element
606
+ },
607
+ log: {
608
+ dump: insightDump
609
+ },
610
+ cache: {
611
+ hit: Boolean(locateResult)
612
+ }
613
+ };
614
+ }
615
+ };
616
+ return taskFind;
617
+ }
618
+ if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
619
+ const assertPlan = plan2;
620
+ const taskAssert = {
621
+ type: "Insight",
622
+ subType: "Assert",
623
+ param: assertPlan.param,
624
+ executor: async (param, taskContext) => {
625
+ const { task } = taskContext;
626
+ let insightDump;
627
+ const dumpCollector = (dump) => {
628
+ insightDump = dump;
629
+ };
630
+ this.insight.onceDumpUpdatedFn = dumpCollector;
631
+ const assertion = await this.insight.assert(
632
+ assertPlan.param.assertion
633
+ );
634
+ if (!assertion.pass) {
635
+ if (plan2.type === "Assert") {
636
+ task.output = assertion;
637
+ task.log = {
638
+ dump: insightDump
639
+ };
640
+ throw new Error(
641
+ assertion.thought || "Assertion failed without reason"
642
+ );
643
+ }
644
+ task.error = assertion.thought;
645
+ }
646
+ return {
647
+ output: assertion,
648
+ log: {
649
+ dump: insightDump
650
+ }
651
+ };
652
+ }
653
+ };
654
+ return taskAssert;
655
+ }
656
+ if (plan2.type === "Input") {
657
+ const taskActionInput = {
658
+ type: "Action",
659
+ subType: "Input",
660
+ param: plan2.param,
661
+ executor: async (taskParam, { element }) => {
662
+ if (element) {
663
+ await this.page.mouse.click(
664
+ element.center[0],
665
+ element.center[1]
666
+ );
667
+ }
668
+ assert2(taskParam.value, "No value to input");
669
+ await this.page.keyboard.type(taskParam.value);
670
+ }
671
+ };
672
+ return taskActionInput;
673
+ }
674
+ if (plan2.type === "KeyboardPress") {
675
+ const taskActionKeyboardPress = {
676
+ type: "Action",
677
+ subType: "KeyboardPress",
678
+ param: plan2.param,
679
+ executor: async (taskParam) => {
680
+ assert2(taskParam.value, "No key to press");
681
+ await this.page.keyboard.press(taskParam.value);
682
+ }
683
+ };
684
+ return taskActionKeyboardPress;
685
+ }
686
+ if (plan2.type === "Tap") {
687
+ const taskActionTap = {
688
+ type: "Action",
689
+ subType: "Tap",
690
+ executor: async (param, { element }) => {
691
+ assert2(element, "Element not found, cannot tap");
692
+ await this.page.mouse.click(
693
+ element.center[0],
694
+ element.center[1]
695
+ );
696
+ }
697
+ };
698
+ return taskActionTap;
699
+ }
700
+ if (plan2.type === "Hover") {
701
+ const taskActionHover = {
702
+ type: "Action",
703
+ subType: "Hover",
704
+ executor: async (param, { element }) => {
705
+ assert2(element, "Element not found, cannot hover");
706
+ await this.page.mouse.move(
707
+ element.center[0],
708
+ element.center[1]
709
+ );
710
+ }
711
+ };
712
+ return taskActionHover;
713
+ }
714
+ if (plan2.type === "Scroll") {
715
+ const taskActionScroll = {
716
+ type: "Action",
717
+ subType: "Scroll",
718
+ param: plan2.param,
719
+ executor: async (taskParam) => {
720
+ const scrollToEventName = taskParam.scrollType;
721
+ const innerHeight = await this.page.evaluate(
722
+ () => window.innerHeight
723
+ );
724
+ switch (scrollToEventName) {
725
+ case "ScrollUntilTop":
726
+ await this.page.mouse.wheel(0, -9999999);
727
+ break;
728
+ case "ScrollUntilBottom":
729
+ await this.page.mouse.wheel(0, 9999999);
730
+ break;
731
+ case "ScrollUp":
732
+ await this.page.mouse.wheel(0, -innerHeight);
733
+ break;
734
+ case "ScrollDown":
735
+ await this.page.mouse.wheel(0, innerHeight);
736
+ break;
737
+ default:
738
+ console.error(
739
+ "Unknown scroll event type:",
740
+ scrollToEventName
741
+ );
742
+ }
743
+ }
744
+ };
745
+ return taskActionScroll;
746
+ }
747
+ if (plan2.type === "Sleep") {
748
+ const taskActionSleep = {
749
+ type: "Action",
750
+ subType: "Sleep",
751
+ param: plan2.param,
752
+ executor: async (taskParam) => {
753
+ assert2(taskParam.timeMs, "No time to sleep");
754
+ await sleep(taskParam.timeMs);
755
+ }
756
+ };
757
+ return taskActionSleep;
758
+ }
759
+ if (plan2.type === "Error") {
760
+ const taskActionError = {
761
+ type: "Action",
762
+ subType: "Error",
763
+ param: plan2.param,
764
+ executor: async (taskParam) => {
765
+ assert2(
766
+ taskParam.thought,
767
+ "An error occurred, but no thought provided"
768
+ );
769
+ throw new Error(taskParam.thought);
770
+ }
771
+ };
772
+ return taskActionError;
773
+ }
774
+ throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
775
+ }).map((task) => {
776
+ return this.wrapExecutorWithScreenshot(task);
777
+ });
778
+ return tasks;
779
+ }
780
+ async action(userPrompt) {
781
+ const taskExecutor = new Executor(userPrompt);
782
+ let plans = [];
783
+ const planningTask = {
784
+ type: "Planning",
785
+ param: {
786
+ userPrompt
787
+ },
788
+ executor: async (param) => {
789
+ const pageContext = await this.insight.contextRetrieverFn();
790
+ let planResult;
791
+ const planCache = this.taskCache.readCache(
792
+ pageContext,
793
+ "plan",
794
+ userPrompt
795
+ );
796
+ if (planCache) {
797
+ planResult = planCache;
798
+ } else {
799
+ planResult = await plan(param.userPrompt, {
800
+ context: pageContext
801
+ });
802
+ }
803
+ assert2(planResult.plans.length > 0, "No plans found");
804
+ plans = planResult.plans;
805
+ this.taskCache.saveCache({
806
+ type: "plan",
807
+ pageContext: {
808
+ url: pageContext.url,
809
+ size: pageContext.size
810
+ },
811
+ prompt: userPrompt,
812
+ response: planResult
813
+ });
814
+ return {
815
+ output: planResult,
816
+ cache: {
817
+ hit: Boolean(planCache)
818
+ }
819
+ };
820
+ }
821
+ };
822
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
823
+ let output = await taskExecutor.flush();
824
+ if (taskExecutor.isInErrorState()) {
825
+ return {
826
+ output,
827
+ executor: taskExecutor
828
+ };
829
+ }
830
+ const executables = await this.convertPlanToExecutable(plans);
831
+ await taskExecutor.append(executables);
832
+ output = await taskExecutor.flush();
833
+ return {
834
+ output,
835
+ executor: taskExecutor
836
+ };
837
+ }
838
+ async query(demand) {
839
+ const description = typeof demand === "string" ? demand : JSON.stringify(demand);
840
+ const taskExecutor = new Executor(description);
841
+ const queryTask = {
842
+ type: "Insight",
843
+ subType: "Query",
844
+ param: {
845
+ dataDemand: demand
846
+ },
847
+ executor: async (param) => {
848
+ let insightDump;
849
+ const dumpCollector = (dump) => {
850
+ insightDump = dump;
851
+ };
852
+ this.insight.onceDumpUpdatedFn = dumpCollector;
853
+ const data = await this.insight.extract(param.dataDemand);
854
+ return {
855
+ output: data,
856
+ log: { dump: insightDump }
857
+ };
858
+ }
859
+ };
860
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
861
+ const output = await taskExecutor.flush();
862
+ return {
863
+ output,
864
+ executor: taskExecutor
865
+ };
866
+ }
867
+ async assert(assertion) {
868
+ const description = `assert: ${assertion}`;
869
+ const taskExecutor = new Executor(description);
870
+ const assertionPlan = {
871
+ type: "Assert",
872
+ param: {
873
+ assertion
874
+ }
875
+ };
876
+ const assertTask = await this.convertPlanToExecutable([assertionPlan]);
877
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
878
+ const output = await taskExecutor.flush();
879
+ return {
880
+ output,
881
+ executor: taskExecutor
882
+ };
883
+ }
884
+ async waitFor(assertion, opt) {
885
+ const description = `waitFor: ${assertion}`;
886
+ const taskExecutor = new Executor(description);
887
+ const { timeoutMs, checkIntervalMs } = opt;
888
+ assert2(assertion, "No assertion for waitFor");
889
+ assert2(timeoutMs, "No timeoutMs for waitFor");
890
+ assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
891
+ const overallStartTime = Date.now();
892
+ let startTime = Date.now();
893
+ let errorThought = "";
894
+ while (Date.now() - overallStartTime < timeoutMs) {
895
+ startTime = Date.now();
896
+ const assertPlan = {
897
+ type: "AssertWithoutThrow",
898
+ param: {
899
+ assertion
900
+ }
901
+ };
902
+ const assertTask = await this.convertPlanToExecutable([assertPlan]);
903
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
904
+ const output = await taskExecutor.flush();
905
+ if (output.pass) {
906
+ return {
907
+ output: void 0,
908
+ executor: taskExecutor
909
+ };
910
+ }
911
+ errorThought = output.thought;
912
+ const now = Date.now();
913
+ if (now - startTime < checkIntervalMs) {
914
+ const timeRemaining = checkIntervalMs - (now - startTime);
915
+ const sleepPlan = {
916
+ type: "Sleep",
917
+ param: {
918
+ timeMs: timeRemaining
919
+ }
920
+ };
921
+ const sleepTask = await this.convertPlanToExecutable([sleepPlan]);
922
+ await taskExecutor.append(
923
+ this.wrapExecutorWithScreenshot(sleepTask[0])
924
+ );
925
+ await taskExecutor.flush();
926
+ }
927
+ }
928
+ const errorPlan = {
929
+ type: "Error",
930
+ param: {
931
+ thought: `waitFor timeout: ${errorThought}`
932
+ }
933
+ };
934
+ const errorTask = await this.convertPlanToExecutable([errorPlan]);
935
+ await taskExecutor.append(errorTask[0]);
936
+ await taskExecutor.flush();
937
+ return {
938
+ output: void 0,
939
+ executor: taskExecutor
940
+ };
941
+ }
942
+ };
943
+
944
+ // src/common/agent.ts
945
+ var PageAgent = class {
946
+ constructor(page, opts) {
947
+ this.page = page;
948
+ this.opts = Object.assign(
949
+ {
950
+ generateReport: true,
951
+ autoPrintReportMsg: true,
952
+ groupName: "Midscene Report",
953
+ groupDescription: ""
954
+ },
955
+ opts || {}
956
+ );
957
+ this.dump = {
958
+ groupName: this.opts.groupName,
959
+ groupDescription: this.opts.groupDescription,
960
+ executions: []
961
+ };
962
+ this.taskExecutor = new PageTaskExecutor(this.page, {
963
+ cache: (opts == null ? void 0 : opts.cache) || { aiTasks: [] }
964
+ });
965
+ this.reportFileName = reportFileName((opts == null ? void 0 : opts.testId) || "web");
966
+ }
967
+ appendExecutionDump(execution) {
968
+ const currentDump = this.dump;
969
+ currentDump.executions.push(execution);
970
+ }
971
+ dumpDataString() {
972
+ this.dump.groupName = this.opts.groupName;
973
+ this.dump.groupDescription = this.opts.groupDescription;
974
+ return stringifyDumpData(this.dump);
975
+ }
976
+ writeOutActionDumps() {
977
+ const { generateReport, autoPrintReportMsg } = this.opts;
978
+ this.reportFile = writeLogFile({
979
+ fileName: this.reportFileName,
980
+ fileExt: groupedActionDumpFileExt,
981
+ fileContent: this.dumpDataString(),
982
+ type: "dump",
983
+ generateReport
984
+ });
985
+ if (generateReport && autoPrintReportMsg) {
986
+ printReportMsg(this.reportFile);
987
+ }
988
+ }
989
+ async aiAction(taskPrompt) {
990
+ const { executor } = await this.taskExecutor.action(taskPrompt);
991
+ this.appendExecutionDump(executor.dump());
992
+ this.writeOutActionDumps();
993
+ if (executor.isInErrorState()) {
994
+ const errorTask = executor.latestErrorTask();
995
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
996
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
997
+ }
998
+ }
999
+ async aiQuery(demand) {
1000
+ const { output, executor } = await this.taskExecutor.query(demand);
1001
+ this.appendExecutionDump(executor.dump());
1002
+ this.writeOutActionDumps();
1003
+ if (executor.isInErrorState()) {
1004
+ const errorTask = executor.latestErrorTask();
1005
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
1006
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
1007
+ }
1008
+ return output;
1009
+ }
1010
+ async aiAssert(assertion, msg) {
1011
+ const { output, executor } = await this.taskExecutor.assert(assertion);
1012
+ this.appendExecutionDump(executor.dump());
1013
+ this.writeOutActionDumps();
1014
+ if (!(output == null ? void 0 : output.pass)) {
1015
+ const errMsg = msg || `Assertion failed: ${assertion}`;
1016
+ const reasonMsg = `Reason: ${output == null ? void 0 : output.thought} || (no_reason)`;
1017
+ throw new Error(`${errMsg}
1018
+ ${reasonMsg}`);
1019
+ }
1020
+ }
1021
+ async aiWaitFor(assertion, opt) {
1022
+ const { executor } = await this.taskExecutor.waitFor(assertion, {
1023
+ timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
1024
+ checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
1025
+ assertion
1026
+ });
1027
+ this.appendExecutionDump(executor.dump());
1028
+ this.writeOutActionDumps();
1029
+ if (executor.isInErrorState()) {
1030
+ const errorTask = executor.latestErrorTask();
1031
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
1032
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
1033
+ }
1034
+ }
1035
+ async ai(taskPrompt, type = "action") {
1036
+ if (type === "action") {
1037
+ return this.aiAction(taskPrompt);
1038
+ }
1039
+ if (type === "query") {
1040
+ return this.aiQuery(taskPrompt);
1041
+ }
1042
+ if (type === "assert") {
1043
+ return this.aiAssert(taskPrompt);
1044
+ }
1045
+ throw new Error(
1046
+ `Unknown type: ${type}, only support 'action', 'query', 'assert'`
1047
+ );
1048
+ }
1049
+ };
1050
+
1051
+ // src/playwright/ai-fixture.ts
1052
+ import { test } from "@playwright/test";
1053
+
1054
+ // src/playwright/cache.ts
1055
+ import fs2 from "fs";
1056
+ import path2, { join } from "path";
1057
+ import {
1058
+ getLogDirByType,
1059
+ stringifyDumpData as stringifyDumpData2,
1060
+ writeLogFile as writeLogFile2
1061
+ } from "@midscene/core/utils";
1062
+ function writeTestCache(taskFile, taskTitle, taskCacheJson) {
1063
+ const packageJson = getPkgInfo();
1064
+ writeLogFile2({
1065
+ fileName: `${taskFile}(${taskTitle})`,
1066
+ fileExt: "json",
1067
+ fileContent: stringifyDumpData2(
1068
+ __spreadValues({
1069
+ pkgName: packageJson.name,
1070
+ pkgVersion: packageJson.version,
1071
+ taskFile,
1072
+ taskTitle
1073
+ }, taskCacheJson),
1074
+ 2
1075
+ ),
1076
+ type: "cache"
1077
+ });
1078
+ }
1079
+ function readTestCache(taskFile, taskTitle) {
1080
+ const cacheFile = join(
1081
+ getLogDirByType("cache"),
1082
+ `${taskFile}(${taskTitle}).json`
1083
+ );
1084
+ const pkgInfo = getPkgInfo();
1085
+ if (process.env.MIDSCENE_CACHE === "true" && fs2.existsSync(cacheFile)) {
1086
+ try {
1087
+ const data = fs2.readFileSync(cacheFile, "utf8");
1088
+ const jsonData = JSON.parse(data);
1089
+ if (jsonData.pkgName !== pkgInfo.name || jsonData.pkgVersion !== pkgInfo.version) {
1090
+ return void 0;
1091
+ }
1092
+ return jsonData;
1093
+ } catch (err) {
1094
+ return void 0;
1095
+ }
1096
+ }
1097
+ return void 0;
1098
+ }
1099
+ function getPkgInfo() {
1100
+ const packageJsonDir = findNearestPackageJson(__dirname);
1101
+ if (!packageJsonDir) {
1102
+ console.error("Cannot find package.json directory: ", __dirname);
1103
+ return {
1104
+ name: "@midscene/web",
1105
+ version: "0.0.0"
1106
+ };
1107
+ }
1108
+ const packageJsonPath = path2.join(packageJsonDir, "package.json");
1109
+ const data = fs2.readFileSync(packageJsonPath, "utf8");
1110
+ const packageJson = JSON.parse(data);
1111
+ return {
1112
+ name: packageJson.name,
1113
+ version: packageJson.version
1114
+ };
1115
+ }
1116
+
1117
+ // src/playwright/ai-fixture.ts
1118
+ var groupAndCaseForTest = (testInfo) => {
1119
+ let taskFile;
1120
+ let taskTitle;
1121
+ const titlePath = [...testInfo.titlePath];
1122
+ if (titlePath.length > 1) {
1123
+ taskTitle = titlePath.pop() || "unnamed";
1124
+ taskFile = `${titlePath.join(" > ")}`;
1125
+ } else if (titlePath.length === 1) {
1126
+ taskTitle = titlePath[0];
1127
+ taskFile = `${taskTitle}`;
1128
+ } else {
1129
+ taskTitle = "unnamed";
1130
+ taskFile = "unnamed";
1131
+ }
1132
+ return { taskFile, taskTitle };
1133
+ };
1134
+ var midsceneAgentKeyId = "_midsceneAgentId";
1135
+ var midsceneDumpAnnotationId = "MIDSCENE_DUMP_ANNOTATION";
1136
+ var PlaywrightAiFixture = () => {
1137
+ const pageAgentMap = {};
1138
+ const agentForPage = (page, testInfo) => {
1139
+ let idForPage = page[midsceneAgentKeyId];
1140
+ if (!idForPage) {
1141
+ idForPage = randomUUID();
1142
+ page[midsceneAgentKeyId] = idForPage;
1143
+ const { testId } = testInfo;
1144
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
1145
+ const testCase = readTestCache(taskFile, taskTitle) || {
1146
+ aiTasks: []
1147
+ };
1148
+ pageAgentMap[idForPage] = new PageAgent(page, {
1149
+ testId: `playwright-${testId}-${idForPage}`,
1150
+ groupName: taskTitle,
1151
+ groupDescription: taskFile,
1152
+ cache: testCase,
1153
+ generateReport: false
1154
+ // we will generate it in the reporter
1155
+ });
1156
+ }
1157
+ return pageAgentMap[idForPage];
1158
+ };
1159
+ const updateDumpAnnotation = (test2, dump) => {
1160
+ const currentAnnotation = test2.annotations.find((item) => {
1161
+ return item.type === midsceneDumpAnnotationId;
1162
+ });
1163
+ if (currentAnnotation) {
1164
+ currentAnnotation.description = dump;
1165
+ } else {
1166
+ test2.annotations.push({
1167
+ type: midsceneDumpAnnotationId,
1168
+ description: dump
1169
+ });
1170
+ }
1171
+ };
1172
+ return {
1173
+ ai: async ({ page }, use, testInfo) => {
1174
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
1175
+ const agent = agentForPage(page, testInfo);
1176
+ await use(
1177
+ async (taskPrompt, opts) => {
1178
+ return new Promise((resolve, reject) => {
1179
+ test.step(`ai - ${taskPrompt}`, async () => {
1180
+ await page.waitForLoadState("networkidle");
1181
+ const actionType = (opts == null ? void 0 : opts.type) || "action";
1182
+ const result = await agent.ai(taskPrompt, actionType);
1183
+ resolve(result);
1184
+ });
1185
+ });
1186
+ }
1187
+ );
1188
+ const taskCacheJson = agent.taskExecutor.taskCache.generateTaskCache();
1189
+ writeTestCache(taskFile, taskTitle, taskCacheJson);
1190
+ updateDumpAnnotation(testInfo, agent.dumpDataString());
1191
+ },
1192
+ aiAction: async ({ page }, use, testInfo) => {
1193
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
1194
+ const agent = agentForPage(page, testInfo);
1195
+ await use(async (taskPrompt) => {
1196
+ test.step(`aiAction - ${taskPrompt}`, async () => {
1197
+ await page.waitForLoadState("networkidle");
1198
+ await agent.aiAction(taskPrompt);
1199
+ });
1200
+ });
1201
+ updateDumpAnnotation(testInfo, agent.dumpDataString());
1202
+ },
1203
+ aiQuery: async ({ page }, use, testInfo) => {
1204
+ const agent = agentForPage(page, testInfo);
1205
+ await use(async (demand) => {
1206
+ return new Promise((resolve, reject) => {
1207
+ test.step(`aiQuery - ${JSON.stringify(demand)}`, async () => {
1208
+ await page.waitForLoadState("networkidle");
1209
+ const result = await agent.aiQuery(demand);
1210
+ resolve(result);
1211
+ });
1212
+ });
1213
+ });
1214
+ updateDumpAnnotation(testInfo, agent.dumpDataString());
1215
+ },
1216
+ aiAssert: async ({ page }, use, testInfo) => {
1217
+ const agent = agentForPage(page, testInfo);
1218
+ await use(async (assertion, errorMsg) => {
1219
+ return new Promise((resolve, reject) => {
1220
+ test.step(`aiAssert - ${assertion}`, async () => {
1221
+ await page.waitForLoadState("networkidle");
1222
+ await agent.aiAssert(assertion, errorMsg);
1223
+ resolve(null);
1224
+ });
1225
+ });
1226
+ });
1227
+ updateDumpAnnotation(testInfo, agent.dumpDataString());
1228
+ },
1229
+ aiWaitFor: async ({ page }, use, testInfo) => {
1230
+ const agent = agentForPage(page, testInfo);
1231
+ await use(async (assertion, opt) => {
1232
+ return new Promise((resolve, reject) => {
1233
+ test.step(`aiWaitFor - ${assertion}`, async () => {
1234
+ await agent.aiWaitFor(assertion, opt);
1235
+ resolve(null);
1236
+ });
1237
+ });
1238
+ });
1239
+ updateDumpAnnotation(testInfo, agent.dumpDataString());
1240
+ }
1241
+ };
1242
+ };
1243
+ export {
1244
+ PlaywrightAiFixture
1245
+ };