@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,1049 @@
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/common/agent.ts
340
+ import {
341
+ groupedActionDumpFileExt,
342
+ stringifyDumpData,
343
+ writeLogFile
344
+ } from "@midscene/core/utils";
345
+
346
+ // src/common/tasks.ts
347
+ import assert2 from "assert";
348
+ import Insight, {
349
+ Executor,
350
+ plan
351
+ } from "@midscene/core";
352
+ import { base64Encoded as base64Encoded2 } from "@midscene/core/image";
353
+ import { commonScreenshotParam, getTmpFile as getTmpFile2, sleep } from "@midscene/core/utils";
354
+
355
+ // src/common/task-cache.ts
356
+ var TaskCache = class {
357
+ constructor(opts) {
358
+ this.cache = opts == null ? void 0 : opts.cache;
359
+ this.newCache = {
360
+ aiTasks: []
361
+ };
362
+ }
363
+ readCache(pageContext, type, userPrompt) {
364
+ var _a;
365
+ if (this.cache) {
366
+ const { aiTasks } = this.cache;
367
+ const index = aiTasks.findIndex((item) => item.prompt === userPrompt);
368
+ if (index === -1) {
369
+ return false;
370
+ }
371
+ const taskRes = aiTasks.splice(index, 1)[0];
372
+ if ((taskRes == null ? void 0 : taskRes.type) === "locate" && !((_a = taskRes.response) == null ? void 0 : _a.elements.every((element) => {
373
+ const findIndex = pageContext.content.findIndex(
374
+ (contentElement) => contentElement.id === element.id
375
+ );
376
+ if (findIndex === -1) {
377
+ return false;
378
+ }
379
+ return true;
380
+ }))) {
381
+ return false;
382
+ }
383
+ if (taskRes && taskRes.type === type && taskRes.prompt === userPrompt && this.pageContextEqual(taskRes.pageContext, pageContext)) {
384
+ return taskRes.response;
385
+ }
386
+ }
387
+ return false;
388
+ }
389
+ saveCache(cache) {
390
+ var _a;
391
+ if (cache) {
392
+ (_a = this.newCache) == null ? void 0 : _a.aiTasks.push(cache);
393
+ }
394
+ }
395
+ pageContextEqual(taskPageContext, pageContext) {
396
+ return taskPageContext.size.width === pageContext.size.width && taskPageContext.size.height === pageContext.size.height;
397
+ }
398
+ /**
399
+ * Generate task cache data.
400
+ * This method is mainly used to create or obtain some cached data for tasks, and it returns a new cache object.
401
+ * In the cache object, it may contain task-related information, states, or other necessary data.
402
+ * 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.
403
+ * @returns {Object} Returns a new cache object, which may include task cache data.
404
+ */
405
+ generateTaskCache() {
406
+ return this.newCache;
407
+ }
408
+ };
409
+
410
+ // src/common/utils.ts
411
+ var import_dayjs = __toESM(require_dayjs_min());
412
+ import assert from "assert";
413
+ import fs, { readFileSync } from "fs";
414
+ import path from "path";
415
+ import {
416
+ base64Encoded,
417
+ imageInfoOfBase64
418
+ } from "@midscene/core/image";
419
+ import { getTmpFile } from "@midscene/core/utils";
420
+
421
+ // src/web-element.ts
422
+ var WebElementInfo = class {
423
+ constructor({
424
+ content,
425
+ rect,
426
+ page,
427
+ locator,
428
+ id,
429
+ attributes
430
+ }) {
431
+ this.content = content;
432
+ this.rect = rect;
433
+ this.center = [
434
+ Math.floor(rect.left + rect.width / 2),
435
+ Math.floor(rect.top + rect.height / 2)
436
+ ];
437
+ this.page = page;
438
+ this.locator = locator;
439
+ this.id = id;
440
+ this.attributes = attributes;
441
+ }
442
+ };
443
+
444
+ // src/common/utils.ts
445
+ async function parseContextFromWebPage(page, _opt) {
446
+ assert(page, "page is required");
447
+ const url = page.url();
448
+ const file = getTmpFile("jpeg");
449
+ await page.screenshot({ path: file, type: "jpeg", quality: 75 });
450
+ const screenshotBuffer = readFileSync(file);
451
+ const screenshotBase64 = base64Encoded(file);
452
+ const captureElementSnapshot = await getElementInfosFromPage(page);
453
+ const elementsInfo = await alignElements(
454
+ screenshotBuffer,
455
+ captureElementSnapshot,
456
+ page
457
+ );
458
+ const size = await imageInfoOfBase64(screenshotBase64);
459
+ return {
460
+ content: elementsInfo,
461
+ size,
462
+ screenshotBase64,
463
+ url
464
+ };
465
+ }
466
+ async function getElementInfosFromPage(page) {
467
+ const pathDir = findNearestPackageJson(__dirname);
468
+ assert(pathDir, `can't find pathDir, with ${__dirname}`);
469
+ const scriptPath = path.join(pathDir, "./dist/script/htmlElement.js");
470
+ const elementInfosScriptContent = readFileSync(scriptPath, "utf-8");
471
+ const extraReturnLogic = `${elementInfosScriptContent}midscene_element_inspector.extractTextWithPosition()`;
472
+ const captureElementSnapshot = await page.evaluate(extraReturnLogic);
473
+ return captureElementSnapshot;
474
+ }
475
+ var sizeThreshold = 3;
476
+ async function alignElements(screenshotBuffer, elements, page) {
477
+ const textsAligned = [];
478
+ const validElements = elements.filter((item) => {
479
+ return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
480
+ });
481
+ for (const item of validElements) {
482
+ const { rect, id, content, attributes, locator } = item;
483
+ textsAligned.push(
484
+ new WebElementInfo({
485
+ rect,
486
+ locator,
487
+ id,
488
+ content,
489
+ attributes,
490
+ page
491
+ })
492
+ );
493
+ }
494
+ return textsAligned;
495
+ }
496
+ function findNearestPackageJson(dir) {
497
+ const packageJsonPath = path.join(dir, "package.json");
498
+ if (fs.existsSync(packageJsonPath)) {
499
+ return dir;
500
+ }
501
+ const parentDir = path.dirname(dir);
502
+ if (parentDir === dir) {
503
+ return null;
504
+ }
505
+ return findNearestPackageJson(parentDir);
506
+ }
507
+ function reportFileName(tag = "web") {
508
+ const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss-SSS");
509
+ return `${tag}-${dateTimeInFileName}`;
510
+ }
511
+ function printReportMsg(filepath) {
512
+ console.log("Midscene - report file updated:", filepath);
513
+ }
514
+
515
+ // src/common/tasks.ts
516
+ var PageTaskExecutor = class {
517
+ constructor(page, opts) {
518
+ this.page = page;
519
+ this.insight = new Insight(async () => {
520
+ return await parseContextFromWebPage(page);
521
+ });
522
+ this.taskCache = new TaskCache(opts);
523
+ }
524
+ async recordScreenshot(timing) {
525
+ const file = getTmpFile2("jpeg");
526
+ await this.page.screenshot(__spreadProps(__spreadValues({}, commonScreenshotParam), {
527
+ path: file
528
+ }));
529
+ const item = {
530
+ type: "screenshot",
531
+ ts: Date.now(),
532
+ screenshot: base64Encoded2(file),
533
+ timing
534
+ };
535
+ return item;
536
+ }
537
+ wrapExecutorWithScreenshot(taskApply) {
538
+ const taskWithScreenshot = __spreadProps(__spreadValues({}, taskApply), {
539
+ executor: async (param, context, ...args) => {
540
+ const recorder = [];
541
+ const { task } = context;
542
+ task.recorder = recorder;
543
+ const shot = await this.recordScreenshot(`before ${task.type}`);
544
+ recorder.push(shot);
545
+ const result = await taskApply.executor(param, context, ...args);
546
+ if (taskApply.type === "Action") {
547
+ await sleep(1e3);
548
+ const shot2 = await this.recordScreenshot("after Action");
549
+ recorder.push(shot2);
550
+ }
551
+ return result;
552
+ }
553
+ });
554
+ return taskWithScreenshot;
555
+ }
556
+ async convertPlanToExecutable(plans) {
557
+ const tasks = plans.map((plan2) => {
558
+ if (plan2.type === "Locate") {
559
+ const taskFind = {
560
+ type: "Insight",
561
+ subType: "Locate",
562
+ param: plan2.param,
563
+ executor: async (param) => {
564
+ let insightDump;
565
+ const dumpCollector = (dump) => {
566
+ insightDump = dump;
567
+ };
568
+ this.insight.onceDumpUpdatedFn = dumpCollector;
569
+ const pageContext = await this.insight.contextRetrieverFn();
570
+ const locateCache = this.taskCache.readCache(
571
+ pageContext,
572
+ "locate",
573
+ param.prompt
574
+ );
575
+ let locateResult;
576
+ const callAI = this.insight.aiVendorFn;
577
+ const element = await this.insight.locate(param.prompt, {
578
+ callAI: async (...message) => {
579
+ if (locateCache) {
580
+ locateResult = locateCache;
581
+ return Promise.resolve(locateCache);
582
+ }
583
+ locateResult = await callAI(...message);
584
+ assert2(locateResult);
585
+ return locateResult;
586
+ }
587
+ });
588
+ assert2(element, `Element not found: ${param.prompt}`);
589
+ if (locateResult) {
590
+ this.taskCache.saveCache({
591
+ type: "locate",
592
+ pageContext: {
593
+ url: pageContext.url,
594
+ size: pageContext.size
595
+ },
596
+ prompt: param.prompt,
597
+ response: locateResult
598
+ });
599
+ }
600
+ return {
601
+ output: {
602
+ element
603
+ },
604
+ log: {
605
+ dump: insightDump
606
+ },
607
+ cache: {
608
+ hit: Boolean(locateResult)
609
+ }
610
+ };
611
+ }
612
+ };
613
+ return taskFind;
614
+ }
615
+ if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
616
+ const assertPlan = plan2;
617
+ const taskAssert = {
618
+ type: "Insight",
619
+ subType: "Assert",
620
+ param: assertPlan.param,
621
+ executor: async (param, taskContext) => {
622
+ const { task } = taskContext;
623
+ let insightDump;
624
+ const dumpCollector = (dump) => {
625
+ insightDump = dump;
626
+ };
627
+ this.insight.onceDumpUpdatedFn = dumpCollector;
628
+ const assertion = await this.insight.assert(
629
+ assertPlan.param.assertion
630
+ );
631
+ if (!assertion.pass) {
632
+ if (plan2.type === "Assert") {
633
+ task.output = assertion;
634
+ task.log = {
635
+ dump: insightDump
636
+ };
637
+ throw new Error(
638
+ assertion.thought || "Assertion failed without reason"
639
+ );
640
+ }
641
+ task.error = assertion.thought;
642
+ }
643
+ return {
644
+ output: assertion,
645
+ log: {
646
+ dump: insightDump
647
+ }
648
+ };
649
+ }
650
+ };
651
+ return taskAssert;
652
+ }
653
+ if (plan2.type === "Input") {
654
+ const taskActionInput = {
655
+ type: "Action",
656
+ subType: "Input",
657
+ param: plan2.param,
658
+ executor: async (taskParam, { element }) => {
659
+ if (element) {
660
+ await this.page.mouse.click(
661
+ element.center[0],
662
+ element.center[1]
663
+ );
664
+ }
665
+ assert2(taskParam.value, "No value to input");
666
+ await this.page.keyboard.type(taskParam.value);
667
+ }
668
+ };
669
+ return taskActionInput;
670
+ }
671
+ if (plan2.type === "KeyboardPress") {
672
+ const taskActionKeyboardPress = {
673
+ type: "Action",
674
+ subType: "KeyboardPress",
675
+ param: plan2.param,
676
+ executor: async (taskParam) => {
677
+ assert2(taskParam.value, "No key to press");
678
+ await this.page.keyboard.press(taskParam.value);
679
+ }
680
+ };
681
+ return taskActionKeyboardPress;
682
+ }
683
+ if (plan2.type === "Tap") {
684
+ const taskActionTap = {
685
+ type: "Action",
686
+ subType: "Tap",
687
+ executor: async (param, { element }) => {
688
+ assert2(element, "Element not found, cannot tap");
689
+ await this.page.mouse.click(
690
+ element.center[0],
691
+ element.center[1]
692
+ );
693
+ }
694
+ };
695
+ return taskActionTap;
696
+ }
697
+ if (plan2.type === "Hover") {
698
+ const taskActionHover = {
699
+ type: "Action",
700
+ subType: "Hover",
701
+ executor: async (param, { element }) => {
702
+ assert2(element, "Element not found, cannot hover");
703
+ await this.page.mouse.move(
704
+ element.center[0],
705
+ element.center[1]
706
+ );
707
+ }
708
+ };
709
+ return taskActionHover;
710
+ }
711
+ if (plan2.type === "Scroll") {
712
+ const taskActionScroll = {
713
+ type: "Action",
714
+ subType: "Scroll",
715
+ param: plan2.param,
716
+ executor: async (taskParam) => {
717
+ const scrollToEventName = taskParam.scrollType;
718
+ const innerHeight = await this.page.evaluate(
719
+ () => window.innerHeight
720
+ );
721
+ switch (scrollToEventName) {
722
+ case "ScrollUntilTop":
723
+ await this.page.mouse.wheel(0, -9999999);
724
+ break;
725
+ case "ScrollUntilBottom":
726
+ await this.page.mouse.wheel(0, 9999999);
727
+ break;
728
+ case "ScrollUp":
729
+ await this.page.mouse.wheel(0, -innerHeight);
730
+ break;
731
+ case "ScrollDown":
732
+ await this.page.mouse.wheel(0, innerHeight);
733
+ break;
734
+ default:
735
+ console.error(
736
+ "Unknown scroll event type:",
737
+ scrollToEventName
738
+ );
739
+ }
740
+ }
741
+ };
742
+ return taskActionScroll;
743
+ }
744
+ if (plan2.type === "Sleep") {
745
+ const taskActionSleep = {
746
+ type: "Action",
747
+ subType: "Sleep",
748
+ param: plan2.param,
749
+ executor: async (taskParam) => {
750
+ assert2(taskParam.timeMs, "No time to sleep");
751
+ await sleep(taskParam.timeMs);
752
+ }
753
+ };
754
+ return taskActionSleep;
755
+ }
756
+ if (plan2.type === "Error") {
757
+ const taskActionError = {
758
+ type: "Action",
759
+ subType: "Error",
760
+ param: plan2.param,
761
+ executor: async (taskParam) => {
762
+ assert2(
763
+ taskParam.thought,
764
+ "An error occurred, but no thought provided"
765
+ );
766
+ throw new Error(taskParam.thought);
767
+ }
768
+ };
769
+ return taskActionError;
770
+ }
771
+ throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
772
+ }).map((task) => {
773
+ return this.wrapExecutorWithScreenshot(task);
774
+ });
775
+ return tasks;
776
+ }
777
+ async action(userPrompt) {
778
+ const taskExecutor = new Executor(userPrompt);
779
+ let plans = [];
780
+ const planningTask = {
781
+ type: "Planning",
782
+ param: {
783
+ userPrompt
784
+ },
785
+ executor: async (param) => {
786
+ const pageContext = await this.insight.contextRetrieverFn();
787
+ let planResult;
788
+ const planCache = this.taskCache.readCache(
789
+ pageContext,
790
+ "plan",
791
+ userPrompt
792
+ );
793
+ if (planCache) {
794
+ planResult = planCache;
795
+ } else {
796
+ planResult = await plan(param.userPrompt, {
797
+ context: pageContext
798
+ });
799
+ }
800
+ assert2(planResult.plans.length > 0, "No plans found");
801
+ plans = planResult.plans;
802
+ this.taskCache.saveCache({
803
+ type: "plan",
804
+ pageContext: {
805
+ url: pageContext.url,
806
+ size: pageContext.size
807
+ },
808
+ prompt: userPrompt,
809
+ response: planResult
810
+ });
811
+ return {
812
+ output: planResult,
813
+ cache: {
814
+ hit: Boolean(planCache)
815
+ }
816
+ };
817
+ }
818
+ };
819
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
820
+ let output = await taskExecutor.flush();
821
+ if (taskExecutor.isInErrorState()) {
822
+ return {
823
+ output,
824
+ executor: taskExecutor
825
+ };
826
+ }
827
+ const executables = await this.convertPlanToExecutable(plans);
828
+ await taskExecutor.append(executables);
829
+ output = await taskExecutor.flush();
830
+ return {
831
+ output,
832
+ executor: taskExecutor
833
+ };
834
+ }
835
+ async query(demand) {
836
+ const description = typeof demand === "string" ? demand : JSON.stringify(demand);
837
+ const taskExecutor = new Executor(description);
838
+ const queryTask = {
839
+ type: "Insight",
840
+ subType: "Query",
841
+ param: {
842
+ dataDemand: demand
843
+ },
844
+ executor: async (param) => {
845
+ let insightDump;
846
+ const dumpCollector = (dump) => {
847
+ insightDump = dump;
848
+ };
849
+ this.insight.onceDumpUpdatedFn = dumpCollector;
850
+ const data = await this.insight.extract(param.dataDemand);
851
+ return {
852
+ output: data,
853
+ log: { dump: insightDump }
854
+ };
855
+ }
856
+ };
857
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
858
+ const output = await taskExecutor.flush();
859
+ return {
860
+ output,
861
+ executor: taskExecutor
862
+ };
863
+ }
864
+ async assert(assertion) {
865
+ const description = `assert: ${assertion}`;
866
+ const taskExecutor = new Executor(description);
867
+ const assertionPlan = {
868
+ type: "Assert",
869
+ param: {
870
+ assertion
871
+ }
872
+ };
873
+ const assertTask = await this.convertPlanToExecutable([assertionPlan]);
874
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
875
+ const output = await taskExecutor.flush();
876
+ return {
877
+ output,
878
+ executor: taskExecutor
879
+ };
880
+ }
881
+ async waitFor(assertion, opt) {
882
+ const description = `waitFor: ${assertion}`;
883
+ const taskExecutor = new Executor(description);
884
+ const { timeoutMs, checkIntervalMs } = opt;
885
+ assert2(assertion, "No assertion for waitFor");
886
+ assert2(timeoutMs, "No timeoutMs for waitFor");
887
+ assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
888
+ const overallStartTime = Date.now();
889
+ let startTime = Date.now();
890
+ let errorThought = "";
891
+ while (Date.now() - overallStartTime < timeoutMs) {
892
+ startTime = Date.now();
893
+ const assertPlan = {
894
+ type: "AssertWithoutThrow",
895
+ param: {
896
+ assertion
897
+ }
898
+ };
899
+ const assertTask = await this.convertPlanToExecutable([assertPlan]);
900
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
901
+ const output = await taskExecutor.flush();
902
+ if (output.pass) {
903
+ return {
904
+ output: void 0,
905
+ executor: taskExecutor
906
+ };
907
+ }
908
+ errorThought = output.thought;
909
+ const now = Date.now();
910
+ if (now - startTime < checkIntervalMs) {
911
+ const timeRemaining = checkIntervalMs - (now - startTime);
912
+ const sleepPlan = {
913
+ type: "Sleep",
914
+ param: {
915
+ timeMs: timeRemaining
916
+ }
917
+ };
918
+ const sleepTask = await this.convertPlanToExecutable([sleepPlan]);
919
+ await taskExecutor.append(
920
+ this.wrapExecutorWithScreenshot(sleepTask[0])
921
+ );
922
+ await taskExecutor.flush();
923
+ }
924
+ }
925
+ const errorPlan = {
926
+ type: "Error",
927
+ param: {
928
+ thought: `waitFor timeout: ${errorThought}`
929
+ }
930
+ };
931
+ const errorTask = await this.convertPlanToExecutable([errorPlan]);
932
+ await taskExecutor.append(errorTask[0]);
933
+ await taskExecutor.flush();
934
+ return {
935
+ output: void 0,
936
+ executor: taskExecutor
937
+ };
938
+ }
939
+ };
940
+
941
+ // src/common/agent.ts
942
+ var PageAgent = class {
943
+ constructor(page, opts) {
944
+ this.page = page;
945
+ this.opts = Object.assign(
946
+ {
947
+ generateReport: true,
948
+ autoPrintReportMsg: true,
949
+ groupName: "Midscene Report",
950
+ groupDescription: ""
951
+ },
952
+ opts || {}
953
+ );
954
+ this.dump = {
955
+ groupName: this.opts.groupName,
956
+ groupDescription: this.opts.groupDescription,
957
+ executions: []
958
+ };
959
+ this.taskExecutor = new PageTaskExecutor(this.page, {
960
+ cache: (opts == null ? void 0 : opts.cache) || { aiTasks: [] }
961
+ });
962
+ this.reportFileName = reportFileName((opts == null ? void 0 : opts.testId) || "web");
963
+ }
964
+ appendExecutionDump(execution) {
965
+ const currentDump = this.dump;
966
+ currentDump.executions.push(execution);
967
+ }
968
+ dumpDataString() {
969
+ this.dump.groupName = this.opts.groupName;
970
+ this.dump.groupDescription = this.opts.groupDescription;
971
+ return stringifyDumpData(this.dump);
972
+ }
973
+ writeOutActionDumps() {
974
+ const { generateReport, autoPrintReportMsg } = this.opts;
975
+ this.reportFile = writeLogFile({
976
+ fileName: this.reportFileName,
977
+ fileExt: groupedActionDumpFileExt,
978
+ fileContent: this.dumpDataString(),
979
+ type: "dump",
980
+ generateReport
981
+ });
982
+ if (generateReport && autoPrintReportMsg) {
983
+ printReportMsg(this.reportFile);
984
+ }
985
+ }
986
+ async aiAction(taskPrompt) {
987
+ const { executor } = await this.taskExecutor.action(taskPrompt);
988
+ this.appendExecutionDump(executor.dump());
989
+ this.writeOutActionDumps();
990
+ if (executor.isInErrorState()) {
991
+ const errorTask = executor.latestErrorTask();
992
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
993
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
994
+ }
995
+ }
996
+ async aiQuery(demand) {
997
+ const { output, executor } = await this.taskExecutor.query(demand);
998
+ this.appendExecutionDump(executor.dump());
999
+ this.writeOutActionDumps();
1000
+ if (executor.isInErrorState()) {
1001
+ const errorTask = executor.latestErrorTask();
1002
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
1003
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
1004
+ }
1005
+ return output;
1006
+ }
1007
+ async aiAssert(assertion, msg) {
1008
+ const { output, executor } = await this.taskExecutor.assert(assertion);
1009
+ this.appendExecutionDump(executor.dump());
1010
+ this.writeOutActionDumps();
1011
+ if (!(output == null ? void 0 : output.pass)) {
1012
+ const errMsg = msg || `Assertion failed: ${assertion}`;
1013
+ const reasonMsg = `Reason: ${output == null ? void 0 : output.thought} || (no_reason)`;
1014
+ throw new Error(`${errMsg}
1015
+ ${reasonMsg}`);
1016
+ }
1017
+ }
1018
+ async aiWaitFor(assertion, opt) {
1019
+ const { executor } = await this.taskExecutor.waitFor(assertion, {
1020
+ timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
1021
+ checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
1022
+ assertion
1023
+ });
1024
+ this.appendExecutionDump(executor.dump());
1025
+ this.writeOutActionDumps();
1026
+ if (executor.isInErrorState()) {
1027
+ const errorTask = executor.latestErrorTask();
1028
+ throw new Error(`${errorTask == null ? void 0 : errorTask.error}
1029
+ ${errorTask == null ? void 0 : errorTask.errorStack}`);
1030
+ }
1031
+ }
1032
+ async ai(taskPrompt, type = "action") {
1033
+ if (type === "action") {
1034
+ return this.aiAction(taskPrompt);
1035
+ }
1036
+ if (type === "query") {
1037
+ return this.aiQuery(taskPrompt);
1038
+ }
1039
+ if (type === "assert") {
1040
+ return this.aiAssert(taskPrompt);
1041
+ }
1042
+ throw new Error(
1043
+ `Unknown type: ${type}, only support 'action', 'query', 'assert'`
1044
+ );
1045
+ }
1046
+ };
1047
+ export {
1048
+ PageAgent as PuppeteerAgent
1049
+ };