@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.
- package/dist/es/debug.js +559 -0
- package/dist/es/index.js +3 -3
- package/dist/es/playwright.js +1245 -0
- package/dist/es/puppeteer.js +1049 -0
- package/dist/lib/debug.js +563 -0
- package/dist/lib/index.js +3 -3
- package/dist/lib/playwright.js +1245 -0
- package/dist/lib/puppeteer.js +1053 -0
- package/dist/types/debug.d.ts +17 -0
- package/dist/types/index.d.ts +11 -191
- package/dist/types/page.d-70ed000f.d.ts +6 -0
- package/dist/types/playwright.d.ts +36 -0
- package/dist/types/puppeteer.d.ts +33 -0
- package/dist/types/tasks-75eae15e.d.ts +125 -0
- package/package.json +26 -2
|
@@ -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
|
+
};
|