@midscene/web 0.7.1 → 0.7.2-beta-20241024094141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/playground.js +8438 -0
- package/dist/browser/types/playground.d.ts +313 -0
- package/dist/es/appium.js +680 -604
- package/dist/es/debug.js +95 -73
- package/dist/es/index.js +939 -797
- package/dist/es/midscene-playground.js +678 -609
- package/dist/es/playground.js +593 -1024
- package/dist/es/playwright-report.js +29 -11
- package/dist/es/playwright.js +705 -597
- package/dist/es/puppeteer.js +636 -552
- package/dist/lib/appium.js +688 -609
- package/dist/lib/debug.js +95 -73
- package/dist/lib/index.js +950 -804
- package/dist/lib/midscene-playground.js +687 -615
- package/dist/lib/playground.js +586 -1007
- package/dist/lib/playwright-report.js +30 -9
- package/dist/lib/playwright.js +713 -602
- package/dist/lib/puppeteer.js +644 -557
- package/dist/script/htmlElement.js +11 -10
- package/dist/script/htmlElementDebug.js +11 -10
- package/dist/types/appium.d.ts +2 -3
- package/dist/types/debug.d.ts +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/{page-ad820b3c.d.ts → page-8117b0ad.d.ts} +8 -7
- package/dist/types/playground.d.ts +6 -21
- package/dist/types/playwright.d.ts +3 -4
- package/dist/types/puppeteer.d.ts +2 -3
- package/dist/types/{tasks-82c1054b.d.ts → tasks-cb6bf758.d.ts} +6 -6
- package/package.json +11 -5
- package/static/index.html +1 -1
package/dist/es/appium.js
CHANGED
|
@@ -47,6 +47,26 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
47
47
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
48
48
|
mod
|
|
49
49
|
));
|
|
50
|
+
var __async = (__this, __arguments, generator) => {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
var fulfilled = (value) => {
|
|
53
|
+
try {
|
|
54
|
+
step(generator.next(value));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var rejected = (value) => {
|
|
60
|
+
try {
|
|
61
|
+
step(generator.throw(value));
|
|
62
|
+
} catch (e) {
|
|
63
|
+
reject(e);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
67
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
68
|
+
});
|
|
69
|
+
};
|
|
50
70
|
|
|
51
71
|
// ../../node_modules/.pnpm/dayjs@1.11.11/node_modules/dayjs/dayjs.min.js
|
|
52
72
|
var require_dayjs_min = __commonJS({
|
|
@@ -877,7 +897,6 @@ import {
|
|
|
877
897
|
plan
|
|
878
898
|
} from "@midscene/core";
|
|
879
899
|
import { sleep } from "@midscene/core/utils";
|
|
880
|
-
import { base64Encoded as base64Encoded2 } from "@midscene/shared/img";
|
|
881
900
|
|
|
882
901
|
// src/common/task-cache.ts
|
|
883
902
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
@@ -887,21 +906,19 @@ import {
|
|
|
887
906
|
stringifyDumpData,
|
|
888
907
|
writeLogFile
|
|
889
908
|
} from "@midscene/core/utils";
|
|
890
|
-
import {
|
|
909
|
+
import { getRunningPkgInfo } from "@midscene/shared/fs";
|
|
910
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
891
911
|
|
|
892
912
|
// src/common/utils.ts
|
|
893
913
|
var import_dayjs = __toESM(require_dayjs_min());
|
|
894
914
|
import assert from "assert";
|
|
895
|
-
import { randomUUID } from "crypto";
|
|
896
915
|
import { readFileSync } from "fs";
|
|
897
916
|
import path from "path";
|
|
898
917
|
import { NodeType } from "@midscene/shared/constants";
|
|
899
918
|
import { findNearestPackageJson } from "@midscene/shared/fs";
|
|
900
|
-
import {
|
|
901
|
-
base64Encoded,
|
|
902
|
-
imageInfoOfBase64
|
|
903
|
-
} from "@midscene/shared/img";
|
|
919
|
+
import { imageInfoOfBase64 } from "@midscene/shared/img";
|
|
904
920
|
import { compositeElementInfoImg } from "@midscene/shared/img";
|
|
921
|
+
import { uuid } from "@midscene/shared/utils";
|
|
905
922
|
|
|
906
923
|
// src/web-element.ts
|
|
907
924
|
var WebElementInfo = class {
|
|
@@ -929,56 +946,59 @@ var WebElementInfo = class {
|
|
|
929
946
|
};
|
|
930
947
|
|
|
931
948
|
// src/common/utils.ts
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
const url = page.url();
|
|
938
|
-
const file = await page.screenshot();
|
|
939
|
-
const screenshotBase64 = base64Encoded(file);
|
|
940
|
-
const captureElementSnapshot = await page.getElementInfos();
|
|
941
|
-
const elementsInfo = await alignElements(captureElementSnapshot, page);
|
|
942
|
-
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
943
|
-
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
944
|
-
return false;
|
|
949
|
+
function parseContextFromWebPage(page, _opt) {
|
|
950
|
+
return __async(this, null, function* () {
|
|
951
|
+
assert(page, "page is required");
|
|
952
|
+
if (page._forceUsePageContext) {
|
|
953
|
+
return yield page._forceUsePageContext();
|
|
945
954
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
955
|
+
const url = page.url();
|
|
956
|
+
const screenshotBase64 = yield page.screenshotBase64();
|
|
957
|
+
const captureElementSnapshot = yield page.getElementInfos();
|
|
958
|
+
const elementsInfo = yield alignElements(captureElementSnapshot, page);
|
|
959
|
+
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
960
|
+
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
return true;
|
|
964
|
+
});
|
|
965
|
+
const size = yield imageInfoOfBase64(screenshotBase64);
|
|
966
|
+
const screenshotBase64WithElementMarker = yield compositeElementInfoImg({
|
|
967
|
+
inputImgBase64: screenshotBase64,
|
|
968
|
+
elementsPositionInfo: elementsPositionInfoWithoutText
|
|
969
|
+
});
|
|
970
|
+
return {
|
|
971
|
+
content: elementsInfo,
|
|
972
|
+
size,
|
|
973
|
+
screenshotBase64,
|
|
974
|
+
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
975
|
+
url
|
|
976
|
+
};
|
|
952
977
|
});
|
|
953
|
-
return {
|
|
954
|
-
content: elementsInfo,
|
|
955
|
-
size,
|
|
956
|
-
screenshotBase64,
|
|
957
|
-
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
958
|
-
url
|
|
959
|
-
};
|
|
960
978
|
}
|
|
961
979
|
var sizeThreshold = 3;
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
980
|
+
function alignElements(elements, page) {
|
|
981
|
+
return __async(this, null, function* () {
|
|
982
|
+
const validElements = elements.filter((item) => {
|
|
983
|
+
return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
|
|
984
|
+
});
|
|
985
|
+
const textsAligned = [];
|
|
986
|
+
for (const item of validElements) {
|
|
987
|
+
const { rect, id, content, attributes, locator, indexId } = item;
|
|
988
|
+
textsAligned.push(
|
|
989
|
+
new WebElementInfo({
|
|
990
|
+
rect,
|
|
991
|
+
locator,
|
|
992
|
+
id,
|
|
993
|
+
content,
|
|
994
|
+
attributes,
|
|
995
|
+
page,
|
|
996
|
+
indexId
|
|
997
|
+
})
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return textsAligned;
|
|
965
1001
|
});
|
|
966
|
-
const textsAligned = [];
|
|
967
|
-
for (const item of validElements) {
|
|
968
|
-
const { rect, id, content, attributes, locator, indexId } = item;
|
|
969
|
-
textsAligned.push(
|
|
970
|
-
new WebElementInfo({
|
|
971
|
-
rect,
|
|
972
|
-
locator,
|
|
973
|
-
id,
|
|
974
|
-
content,
|
|
975
|
-
attributes,
|
|
976
|
-
page,
|
|
977
|
-
indexId
|
|
978
|
-
})
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
return textsAligned;
|
|
982
1002
|
}
|
|
983
1003
|
function reportFileName(tag = "web") {
|
|
984
1004
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss-SSS");
|
|
@@ -1009,7 +1029,7 @@ var testFileIndex = /* @__PURE__ */ new Map();
|
|
|
1009
1029
|
function generateCacheId(fileName) {
|
|
1010
1030
|
let taskFile = fileName || getCurrentExecutionFile();
|
|
1011
1031
|
if (!taskFile) {
|
|
1012
|
-
taskFile =
|
|
1032
|
+
taskFile = uuid();
|
|
1013
1033
|
console.warn(
|
|
1014
1034
|
"Midscene - using random UUID for cache id. Cache may be invalid."
|
|
1015
1035
|
);
|
|
@@ -1028,7 +1048,7 @@ function generateCacheId(fileName) {
|
|
|
1028
1048
|
// src/common/task-cache.ts
|
|
1029
1049
|
var TaskCache = class {
|
|
1030
1050
|
constructor(opts) {
|
|
1031
|
-
this.midscenePkgInfo =
|
|
1051
|
+
this.midscenePkgInfo = getRunningPkgInfo();
|
|
1032
1052
|
this.cacheId = generateCacheId(opts == null ? void 0 : opts.fileName);
|
|
1033
1053
|
this.cache = this.readCacheFromFile() || {
|
|
1034
1054
|
aiTasks: []
|
|
@@ -1110,11 +1130,17 @@ var TaskCache = class {
|
|
|
1110
1130
|
return this.newCache;
|
|
1111
1131
|
}
|
|
1112
1132
|
readCacheFromFile() {
|
|
1133
|
+
if (ifInBrowser) {
|
|
1134
|
+
return void 0;
|
|
1135
|
+
}
|
|
1113
1136
|
const cacheFile = join(getLogDirByType("cache"), `${this.cacheId}.json`);
|
|
1114
1137
|
if (process.env.MIDSCENE_CACHE === "true" && existsSync(cacheFile)) {
|
|
1115
1138
|
try {
|
|
1116
1139
|
const data = readFileSync2(cacheFile, "utf8");
|
|
1117
1140
|
const jsonData = JSON.parse(data);
|
|
1141
|
+
if (!this.midscenePkgInfo) {
|
|
1142
|
+
return void 0;
|
|
1143
|
+
}
|
|
1118
1144
|
if (jsonData.pkgName !== this.midscenePkgInfo.name || jsonData.pkgVersion !== this.midscenePkgInfo.version) {
|
|
1119
1145
|
return void 0;
|
|
1120
1146
|
}
|
|
@@ -1126,7 +1152,10 @@ var TaskCache = class {
|
|
|
1126
1152
|
return void 0;
|
|
1127
1153
|
}
|
|
1128
1154
|
writeCacheToFile() {
|
|
1129
|
-
const midscenePkgInfo =
|
|
1155
|
+
const midscenePkgInfo = getRunningPkgInfo();
|
|
1156
|
+
if (!midscenePkgInfo) {
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1130
1159
|
writeLogFile({
|
|
1131
1160
|
fileName: `${this.cacheId}`,
|
|
1132
1161
|
fileExt: "json",
|
|
@@ -1147,423 +1176,435 @@ var TaskCache = class {
|
|
|
1147
1176
|
var PageTaskExecutor = class {
|
|
1148
1177
|
constructor(page, opts) {
|
|
1149
1178
|
this.page = page;
|
|
1150
|
-
this.insight = new Insight(
|
|
1151
|
-
return
|
|
1152
|
-
});
|
|
1179
|
+
this.insight = new Insight(() => __async(this, null, function* () {
|
|
1180
|
+
return yield parseContextFromWebPage(page);
|
|
1181
|
+
}));
|
|
1153
1182
|
this.taskCache = new TaskCache({
|
|
1154
1183
|
fileName: opts == null ? void 0 : opts.cacheId
|
|
1155
1184
|
});
|
|
1156
1185
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1186
|
+
recordScreenshot(timing) {
|
|
1187
|
+
return __async(this, null, function* () {
|
|
1188
|
+
const base64 = yield this.page.screenshotBase64();
|
|
1189
|
+
const item = {
|
|
1190
|
+
type: "screenshot",
|
|
1191
|
+
ts: Date.now(),
|
|
1192
|
+
screenshot: base64,
|
|
1193
|
+
timing
|
|
1194
|
+
};
|
|
1195
|
+
return item;
|
|
1196
|
+
});
|
|
1166
1197
|
}
|
|
1167
1198
|
wrapExecutorWithScreenshot(taskApply) {
|
|
1168
1199
|
const taskWithScreenshot = __spreadProps(__spreadValues({}, taskApply), {
|
|
1169
|
-
executor:
|
|
1200
|
+
executor: (param, context, ...args) => __async(this, null, function* () {
|
|
1170
1201
|
const recorder = [];
|
|
1171
1202
|
const { task } = context;
|
|
1172
1203
|
task.recorder = recorder;
|
|
1173
|
-
const shot =
|
|
1204
|
+
const shot = yield this.recordScreenshot(`before ${task.type}`);
|
|
1174
1205
|
recorder.push(shot);
|
|
1175
|
-
const result =
|
|
1206
|
+
const result = yield taskApply.executor(param, context, ...args);
|
|
1176
1207
|
if (taskApply.type === "Action") {
|
|
1177
|
-
|
|
1178
|
-
const shot2 =
|
|
1208
|
+
yield sleep(1e3);
|
|
1209
|
+
const shot2 = yield this.recordScreenshot("after Action");
|
|
1179
1210
|
recorder.push(shot2);
|
|
1180
1211
|
}
|
|
1181
1212
|
return result;
|
|
1182
|
-
}
|
|
1213
|
+
})
|
|
1183
1214
|
});
|
|
1184
1215
|
return taskWithScreenshot;
|
|
1185
1216
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1200
|
-
const pageContext = await this.insight.contextRetrieverFn();
|
|
1201
|
-
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1202
|
-
pageContext,
|
|
1203
|
-
"locate",
|
|
1204
|
-
param.prompt
|
|
1205
|
-
);
|
|
1206
|
-
let locateResult;
|
|
1207
|
-
const callAI = this.insight.aiVendorFn;
|
|
1208
|
-
const element = await this.insight.locate(param.prompt, {
|
|
1209
|
-
quickAnswer: plan2.quickAnswer,
|
|
1210
|
-
callAI: async (...message) => {
|
|
1211
|
-
if (locateCache) {
|
|
1212
|
-
locateResult = locateCache;
|
|
1213
|
-
return Promise.resolve(locateCache);
|
|
1214
|
-
}
|
|
1215
|
-
locateResult = await callAI(...message);
|
|
1216
|
-
assert2(locateResult);
|
|
1217
|
-
return locateResult;
|
|
1218
|
-
}
|
|
1219
|
-
});
|
|
1220
|
-
if (locateResult) {
|
|
1221
|
-
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1222
|
-
type: "locate",
|
|
1223
|
-
pageContext: {
|
|
1224
|
-
url: pageContext.url,
|
|
1225
|
-
size: pageContext.size
|
|
1226
|
-
},
|
|
1227
|
-
prompt: param.prompt,
|
|
1228
|
-
response: locateResult
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
if (!element) {
|
|
1232
|
-
task.log = {
|
|
1233
|
-
dump: insightDump
|
|
1217
|
+
convertPlanToExecutable(plans, cacheGroup) {
|
|
1218
|
+
return __async(this, null, function* () {
|
|
1219
|
+
const tasks = plans.map((plan2) => {
|
|
1220
|
+
if (plan2.type === "Locate") {
|
|
1221
|
+
const taskFind = {
|
|
1222
|
+
type: "Insight",
|
|
1223
|
+
subType: "Locate",
|
|
1224
|
+
param: plan2.param,
|
|
1225
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1226
|
+
const { task } = taskContext;
|
|
1227
|
+
let insightDump;
|
|
1228
|
+
const dumpCollector = (dump) => {
|
|
1229
|
+
insightDump = dump;
|
|
1234
1230
|
};
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1231
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1232
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1233
|
+
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1234
|
+
pageContext,
|
|
1235
|
+
"locate",
|
|
1236
|
+
param.prompt
|
|
1237
|
+
);
|
|
1238
|
+
let locateResult;
|
|
1239
|
+
const callAI = this.insight.aiVendorFn;
|
|
1240
|
+
const element = yield this.insight.locate(param.prompt, {
|
|
1241
|
+
quickAnswer: plan2.quickAnswer,
|
|
1242
|
+
callAI: (...message) => __async(this, null, function* () {
|
|
1243
|
+
if (locateCache) {
|
|
1244
|
+
locateResult = locateCache;
|
|
1245
|
+
return Promise.resolve(locateCache);
|
|
1246
|
+
}
|
|
1247
|
+
locateResult = yield callAI(...message);
|
|
1248
|
+
assert2(locateResult);
|
|
1249
|
+
return locateResult;
|
|
1250
|
+
})
|
|
1251
|
+
});
|
|
1252
|
+
if (locateResult) {
|
|
1253
|
+
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1254
|
+
type: "locate",
|
|
1255
|
+
pageContext: {
|
|
1256
|
+
url: pageContext.url,
|
|
1257
|
+
size: pageContext.size
|
|
1258
|
+
},
|
|
1259
|
+
prompt: param.prompt,
|
|
1260
|
+
response: locateResult
|
|
1261
|
+
});
|
|
1246
1262
|
}
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1249
|
-
};
|
|
1250
|
-
return taskFind;
|
|
1251
|
-
}
|
|
1252
|
-
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1253
|
-
const assertPlan = plan2;
|
|
1254
|
-
const taskAssert = {
|
|
1255
|
-
type: "Insight",
|
|
1256
|
-
subType: "Assert",
|
|
1257
|
-
param: assertPlan.param,
|
|
1258
|
-
executor: async (param, taskContext) => {
|
|
1259
|
-
const { task } = taskContext;
|
|
1260
|
-
let insightDump;
|
|
1261
|
-
const dumpCollector = (dump) => {
|
|
1262
|
-
insightDump = dump;
|
|
1263
|
-
};
|
|
1264
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1265
|
-
const assertion = await this.insight.assert(
|
|
1266
|
-
assertPlan.param.assertion
|
|
1267
|
-
);
|
|
1268
|
-
if (!assertion.pass) {
|
|
1269
|
-
if (plan2.type === "Assert") {
|
|
1270
|
-
task.output = assertion;
|
|
1263
|
+
if (!element) {
|
|
1271
1264
|
task.log = {
|
|
1272
1265
|
dump: insightDump
|
|
1273
1266
|
};
|
|
1274
|
-
throw new Error(
|
|
1275
|
-
assertion.thought || "Assertion failed without reason"
|
|
1276
|
-
);
|
|
1267
|
+
throw new Error(`Element not found: ${param.prompt}`);
|
|
1277
1268
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1269
|
+
return {
|
|
1270
|
+
output: {
|
|
1271
|
+
element
|
|
1272
|
+
},
|
|
1273
|
+
log: {
|
|
1274
|
+
dump: insightDump
|
|
1275
|
+
},
|
|
1276
|
+
cache: {
|
|
1277
|
+
hit: Boolean(locateCache)
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
})
|
|
1281
|
+
};
|
|
1282
|
+
return taskFind;
|
|
1283
|
+
}
|
|
1284
|
+
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1285
|
+
const assertPlan = plan2;
|
|
1286
|
+
const taskAssert = {
|
|
1287
|
+
type: "Insight",
|
|
1288
|
+
subType: "Assert",
|
|
1289
|
+
param: assertPlan.param,
|
|
1290
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1291
|
+
const { task } = taskContext;
|
|
1292
|
+
let insightDump;
|
|
1293
|
+
const dumpCollector = (dump) => {
|
|
1294
|
+
insightDump = dump;
|
|
1295
|
+
};
|
|
1296
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1297
|
+
const assertion = yield this.insight.assert(
|
|
1298
|
+
assertPlan.param.assertion
|
|
1299
|
+
);
|
|
1300
|
+
if (!assertion.pass) {
|
|
1301
|
+
if (plan2.type === "Assert") {
|
|
1302
|
+
task.output = assertion;
|
|
1303
|
+
task.log = {
|
|
1304
|
+
dump: insightDump
|
|
1305
|
+
};
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
assertion.thought || "Assertion failed without reason"
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
task.error = assertion.thought;
|
|
1284
1311
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1312
|
+
return {
|
|
1313
|
+
output: assertion,
|
|
1314
|
+
log: {
|
|
1315
|
+
dump: insightDump
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
})
|
|
1319
|
+
};
|
|
1320
|
+
return taskAssert;
|
|
1321
|
+
}
|
|
1322
|
+
if (plan2.type === "Input") {
|
|
1323
|
+
const taskActionInput = {
|
|
1324
|
+
type: "Action",
|
|
1325
|
+
subType: "Input",
|
|
1326
|
+
param: plan2.param,
|
|
1327
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (taskParam, { element }) {
|
|
1328
|
+
if (element) {
|
|
1329
|
+
yield this.page.clearInput(element);
|
|
1330
|
+
if (taskParam.value === "") {
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
yield this.page.keyboard.type(taskParam.value);
|
|
1300
1334
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
)
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
return
|
|
1335
|
+
})
|
|
1336
|
+
};
|
|
1337
|
+
return taskActionInput;
|
|
1338
|
+
}
|
|
1339
|
+
if (plan2.type === "KeyboardPress") {
|
|
1340
|
+
const taskActionKeyboardPress = {
|
|
1341
|
+
type: "Action",
|
|
1342
|
+
subType: "KeyboardPress",
|
|
1343
|
+
param: plan2.param,
|
|
1344
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1345
|
+
assert2(taskParam.value, "No key to press");
|
|
1346
|
+
yield this.page.keyboard.press(taskParam.value);
|
|
1347
|
+
})
|
|
1348
|
+
};
|
|
1349
|
+
return taskActionKeyboardPress;
|
|
1350
|
+
}
|
|
1351
|
+
if (plan2.type === "Tap") {
|
|
1352
|
+
const taskActionTap = {
|
|
1353
|
+
type: "Action",
|
|
1354
|
+
subType: "Tap",
|
|
1355
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1356
|
+
assert2(element, "Element not found, cannot tap");
|
|
1357
|
+
yield this.page.mouse.click(
|
|
1358
|
+
element.center[0],
|
|
1359
|
+
element.center[1]
|
|
1360
|
+
);
|
|
1361
|
+
})
|
|
1362
|
+
};
|
|
1363
|
+
return taskActionTap;
|
|
1364
|
+
}
|
|
1365
|
+
if (plan2.type === "Hover") {
|
|
1366
|
+
const taskActionHover = {
|
|
1367
|
+
type: "Action",
|
|
1368
|
+
subType: "Hover",
|
|
1369
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1370
|
+
assert2(element, "Element not found, cannot hover");
|
|
1371
|
+
yield this.page.mouse.move(
|
|
1372
|
+
element.center[0],
|
|
1373
|
+
element.center[1]
|
|
1374
|
+
);
|
|
1375
|
+
})
|
|
1376
|
+
};
|
|
1377
|
+
return taskActionHover;
|
|
1378
|
+
}
|
|
1379
|
+
if (plan2.type === "Scroll") {
|
|
1380
|
+
const taskActionScroll = {
|
|
1381
|
+
type: "Action",
|
|
1382
|
+
subType: "Scroll",
|
|
1383
|
+
param: plan2.param,
|
|
1384
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1385
|
+
const scrollToEventName = taskParam.scrollType;
|
|
1386
|
+
switch (scrollToEventName) {
|
|
1387
|
+
case "scrollUntilTop":
|
|
1388
|
+
yield this.page.scrollUntilTop();
|
|
1389
|
+
break;
|
|
1390
|
+
case "scrollUntilBottom":
|
|
1391
|
+
yield this.page.scrollUntilBottom();
|
|
1392
|
+
break;
|
|
1393
|
+
case "scrollUpOneScreen":
|
|
1394
|
+
yield this.page.scrollUpOneScreen();
|
|
1395
|
+
break;
|
|
1396
|
+
case "scrollDownOneScreen":
|
|
1397
|
+
yield this.page.scrollDownOneScreen();
|
|
1398
|
+
break;
|
|
1399
|
+
default:
|
|
1400
|
+
console.error(
|
|
1401
|
+
"Unknown scroll event type:",
|
|
1402
|
+
scrollToEventName
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
})
|
|
1406
|
+
};
|
|
1407
|
+
return taskActionScroll;
|
|
1408
|
+
}
|
|
1409
|
+
if (plan2.type === "Sleep") {
|
|
1410
|
+
const taskActionSleep = {
|
|
1411
|
+
type: "Action",
|
|
1412
|
+
subType: "Sleep",
|
|
1413
|
+
param: plan2.param,
|
|
1414
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1415
|
+
yield sleep(taskParam.timeMs || 3e3);
|
|
1416
|
+
})
|
|
1417
|
+
};
|
|
1418
|
+
return taskActionSleep;
|
|
1419
|
+
}
|
|
1420
|
+
if (plan2.type === "Error") {
|
|
1421
|
+
const taskActionError = {
|
|
1422
|
+
type: "Action",
|
|
1423
|
+
subType: "Error",
|
|
1424
|
+
param: plan2.param,
|
|
1425
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1426
|
+
assert2(
|
|
1427
|
+
taskParam.thought,
|
|
1428
|
+
"An error occurred, but no thought provided"
|
|
1429
|
+
);
|
|
1430
|
+
throw new Error(taskParam.thought);
|
|
1431
|
+
})
|
|
1432
|
+
};
|
|
1433
|
+
return taskActionError;
|
|
1434
|
+
}
|
|
1435
|
+
throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
|
|
1436
|
+
}).map((task) => {
|
|
1437
|
+
return this.wrapExecutorWithScreenshot(task);
|
|
1438
|
+
});
|
|
1439
|
+
return tasks;
|
|
1406
1440
|
});
|
|
1407
|
-
return tasks;
|
|
1408
1441
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1442
|
+
action(userPrompt) {
|
|
1443
|
+
return __async(this, null, function* () {
|
|
1444
|
+
const taskExecutor = new Executor(userPrompt);
|
|
1445
|
+
const cacheGroup = this.taskCache.getCacheGroupByPrompt(userPrompt);
|
|
1446
|
+
let plans = [];
|
|
1447
|
+
const planningTask = {
|
|
1448
|
+
type: "Planning",
|
|
1449
|
+
param: {
|
|
1450
|
+
userPrompt
|
|
1451
|
+
},
|
|
1452
|
+
executor: (param) => __async(this, null, function* () {
|
|
1453
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1454
|
+
let planResult;
|
|
1455
|
+
const planCache = cacheGroup.readCache(pageContext, "plan", userPrompt);
|
|
1456
|
+
if (planCache) {
|
|
1457
|
+
planResult = planCache;
|
|
1458
|
+
} else {
|
|
1459
|
+
planResult = yield plan(param.userPrompt, {
|
|
1460
|
+
context: pageContext
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
assert2(planResult.plans.length > 0, "No plans found");
|
|
1464
|
+
plans = planResult.plans;
|
|
1465
|
+
cacheGroup.saveCache({
|
|
1466
|
+
type: "plan",
|
|
1467
|
+
pageContext: {
|
|
1468
|
+
url: pageContext.url,
|
|
1469
|
+
size: pageContext.size
|
|
1470
|
+
},
|
|
1471
|
+
prompt: userPrompt,
|
|
1472
|
+
response: planResult
|
|
1427
1473
|
});
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
});
|
|
1474
|
+
return {
|
|
1475
|
+
output: planResult,
|
|
1476
|
+
cache: {
|
|
1477
|
+
hit: Boolean(planCache)
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
})
|
|
1481
|
+
};
|
|
1482
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
|
|
1483
|
+
let output = yield taskExecutor.flush();
|
|
1484
|
+
if (taskExecutor.isInErrorState()) {
|
|
1440
1485
|
return {
|
|
1441
|
-
output
|
|
1442
|
-
|
|
1443
|
-
hit: Boolean(planCache)
|
|
1444
|
-
}
|
|
1486
|
+
output,
|
|
1487
|
+
executor: taskExecutor
|
|
1445
1488
|
};
|
|
1446
1489
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
if (taskExecutor.isInErrorState()) {
|
|
1490
|
+
const executables = yield this.convertPlanToExecutable(plans, cacheGroup);
|
|
1491
|
+
yield taskExecutor.append(executables);
|
|
1492
|
+
output = yield taskExecutor.flush();
|
|
1451
1493
|
return {
|
|
1452
1494
|
output,
|
|
1453
1495
|
executor: taskExecutor
|
|
1454
1496
|
};
|
|
1455
|
-
}
|
|
1456
|
-
const executables = await this.convertPlanToExecutable(plans, cacheGroup);
|
|
1457
|
-
await taskExecutor.append(executables);
|
|
1458
|
-
output = await taskExecutor.flush();
|
|
1459
|
-
return {
|
|
1460
|
-
output,
|
|
1461
|
-
executor: taskExecutor
|
|
1462
|
-
};
|
|
1463
|
-
}
|
|
1464
|
-
async query(demand) {
|
|
1465
|
-
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1466
|
-
const taskExecutor = new Executor(description);
|
|
1467
|
-
const queryTask = {
|
|
1468
|
-
type: "Insight",
|
|
1469
|
-
subType: "Query",
|
|
1470
|
-
param: {
|
|
1471
|
-
dataDemand: demand
|
|
1472
|
-
},
|
|
1473
|
-
executor: async (param) => {
|
|
1474
|
-
let insightDump;
|
|
1475
|
-
const dumpCollector = (dump) => {
|
|
1476
|
-
insightDump = dump;
|
|
1477
|
-
};
|
|
1478
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1479
|
-
const data = await this.insight.extract(param.dataDemand);
|
|
1480
|
-
return {
|
|
1481
|
-
output: data,
|
|
1482
|
-
log: { dump: insightDump }
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1487
|
-
const output = await taskExecutor.flush();
|
|
1488
|
-
return {
|
|
1489
|
-
output,
|
|
1490
|
-
executor: taskExecutor
|
|
1491
|
-
};
|
|
1497
|
+
});
|
|
1492
1498
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1499
|
+
query(demand) {
|
|
1500
|
+
return __async(this, null, function* () {
|
|
1501
|
+
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1502
|
+
const taskExecutor = new Executor(description);
|
|
1503
|
+
const queryTask = {
|
|
1504
|
+
type: "Insight",
|
|
1505
|
+
subType: "Query",
|
|
1506
|
+
param: {
|
|
1507
|
+
dataDemand: demand
|
|
1508
|
+
},
|
|
1509
|
+
executor: (param) => __async(this, null, function* () {
|
|
1510
|
+
let insightDump;
|
|
1511
|
+
const dumpCollector = (dump) => {
|
|
1512
|
+
insightDump = dump;
|
|
1513
|
+
};
|
|
1514
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1515
|
+
const data = yield this.insight.extract(param.dataDemand);
|
|
1516
|
+
return {
|
|
1517
|
+
output: data,
|
|
1518
|
+
log: { dump: insightDump }
|
|
1519
|
+
};
|
|
1520
|
+
})
|
|
1521
|
+
};
|
|
1522
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1523
|
+
const output = yield taskExecutor.flush();
|
|
1524
|
+
return {
|
|
1525
|
+
output,
|
|
1526
|
+
executor: taskExecutor
|
|
1527
|
+
};
|
|
1528
|
+
});
|
|
1509
1529
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1517
|
-
const overallStartTime = Date.now();
|
|
1518
|
-
let startTime = Date.now();
|
|
1519
|
-
let errorThought = "";
|
|
1520
|
-
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1521
|
-
startTime = Date.now();
|
|
1522
|
-
const assertPlan = {
|
|
1523
|
-
type: "AssertWithoutThrow",
|
|
1530
|
+
assert(assertion) {
|
|
1531
|
+
return __async(this, null, function* () {
|
|
1532
|
+
const description = `assert: ${assertion}`;
|
|
1533
|
+
const taskExecutor = new Executor(description);
|
|
1534
|
+
const assertionPlan = {
|
|
1535
|
+
type: "Assert",
|
|
1524
1536
|
param: {
|
|
1525
1537
|
assertion
|
|
1526
1538
|
}
|
|
1527
1539
|
};
|
|
1528
|
-
const assertTask =
|
|
1529
|
-
|
|
1530
|
-
const output =
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1540
|
+
const assertTask = yield this.convertPlanToExecutable([assertionPlan]);
|
|
1541
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1542
|
+
const output = yield taskExecutor.flush();
|
|
1543
|
+
return {
|
|
1544
|
+
output,
|
|
1545
|
+
executor: taskExecutor
|
|
1546
|
+
};
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
waitFor(assertion, opt) {
|
|
1550
|
+
return __async(this, null, function* () {
|
|
1551
|
+
const description = `waitFor: ${assertion}`;
|
|
1552
|
+
const taskExecutor = new Executor(description);
|
|
1553
|
+
const { timeoutMs, checkIntervalMs } = opt;
|
|
1554
|
+
assert2(assertion, "No assertion for waitFor");
|
|
1555
|
+
assert2(timeoutMs, "No timeoutMs for waitFor");
|
|
1556
|
+
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1557
|
+
const overallStartTime = Date.now();
|
|
1558
|
+
let startTime = Date.now();
|
|
1559
|
+
let errorThought = "";
|
|
1560
|
+
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1561
|
+
startTime = Date.now();
|
|
1562
|
+
const assertPlan = {
|
|
1563
|
+
type: "AssertWithoutThrow",
|
|
1543
1564
|
param: {
|
|
1544
|
-
|
|
1565
|
+
assertion
|
|
1545
1566
|
}
|
|
1546
1567
|
};
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
)
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1568
|
+
const assertTask = yield this.convertPlanToExecutable([assertPlan]);
|
|
1569
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1570
|
+
const output = yield taskExecutor.flush();
|
|
1571
|
+
if (output == null ? void 0 : output.pass) {
|
|
1572
|
+
return {
|
|
1573
|
+
output: void 0,
|
|
1574
|
+
executor: taskExecutor
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
errorThought = (output == null ? void 0 : output.thought) || "unknown error";
|
|
1578
|
+
const now = Date.now();
|
|
1579
|
+
if (now - startTime < checkIntervalMs) {
|
|
1580
|
+
const timeRemaining = checkIntervalMs - (now - startTime);
|
|
1581
|
+
const sleepPlan = {
|
|
1582
|
+
type: "Sleep",
|
|
1583
|
+
param: {
|
|
1584
|
+
timeMs: timeRemaining
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
const sleepTask = yield this.convertPlanToExecutable([sleepPlan]);
|
|
1588
|
+
yield taskExecutor.append(
|
|
1589
|
+
this.wrapExecutorWithScreenshot(sleepTask[0])
|
|
1590
|
+
);
|
|
1591
|
+
yield taskExecutor.flush();
|
|
1592
|
+
}
|
|
1558
1593
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1594
|
+
const errorPlan = {
|
|
1595
|
+
type: "Error",
|
|
1596
|
+
param: {
|
|
1597
|
+
thought: `waitFor timeout: ${errorThought}`
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
const errorTask = yield this.convertPlanToExecutable([errorPlan]);
|
|
1601
|
+
yield taskExecutor.append(errorTask[0]);
|
|
1602
|
+
yield taskExecutor.flush();
|
|
1603
|
+
return {
|
|
1604
|
+
output: void 0,
|
|
1605
|
+
executor: taskExecutor
|
|
1606
|
+
};
|
|
1607
|
+
});
|
|
1567
1608
|
}
|
|
1568
1609
|
};
|
|
1569
1610
|
|
|
@@ -1608,79 +1649,90 @@ var PageAgent = class {
|
|
|
1608
1649
|
type: "dump",
|
|
1609
1650
|
generateReport
|
|
1610
1651
|
});
|
|
1611
|
-
if (generateReport && autoPrintReportMsg) {
|
|
1652
|
+
if (generateReport && autoPrintReportMsg && this.reportFile) {
|
|
1612
1653
|
printReportMsg(this.reportFile);
|
|
1613
1654
|
}
|
|
1614
1655
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1656
|
+
aiAction(taskPrompt) {
|
|
1657
|
+
return __async(this, null, function* () {
|
|
1658
|
+
const { executor } = yield this.taskExecutor.action(taskPrompt);
|
|
1659
|
+
this.appendExecutionDump(executor.dump());
|
|
1660
|
+
this.writeOutActionDumps();
|
|
1661
|
+
if (executor.isInErrorState()) {
|
|
1662
|
+
const errorTask = executor.latestErrorTask();
|
|
1663
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1622
1664
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1623
|
-
|
|
1665
|
+
}
|
|
1666
|
+
});
|
|
1624
1667
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1668
|
+
aiQuery(demand) {
|
|
1669
|
+
return __async(this, null, function* () {
|
|
1670
|
+
const { output, executor } = yield this.taskExecutor.query(demand);
|
|
1671
|
+
this.appendExecutionDump(executor.dump());
|
|
1672
|
+
this.writeOutActionDumps();
|
|
1673
|
+
if (executor.isInErrorState()) {
|
|
1674
|
+
const errorTask = executor.latestErrorTask();
|
|
1675
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1632
1676
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1633
|
-
|
|
1634
|
-
return output;
|
|
1635
|
-
}
|
|
1636
|
-
async aiAssert(assertion, msg, opt) {
|
|
1637
|
-
const { output, executor } = await this.taskExecutor.assert(assertion);
|
|
1638
|
-
this.appendExecutionDump(executor.dump());
|
|
1639
|
-
this.writeOutActionDumps();
|
|
1640
|
-
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1677
|
+
}
|
|
1641
1678
|
return output;
|
|
1642
|
-
}
|
|
1643
|
-
if (!(output == null ? void 0 : output.pass)) {
|
|
1644
|
-
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1645
|
-
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || "(no_reason)"}`;
|
|
1646
|
-
throw new Error(`${errMsg}
|
|
1647
|
-
${reasonMsg}`);
|
|
1648
|
-
}
|
|
1679
|
+
});
|
|
1649
1680
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1681
|
+
aiAssert(assertion, msg, opt) {
|
|
1682
|
+
return __async(this, null, function* () {
|
|
1683
|
+
var _a;
|
|
1684
|
+
const { output, executor } = yield this.taskExecutor.assert(assertion);
|
|
1685
|
+
this.appendExecutionDump(executor.dump());
|
|
1686
|
+
this.writeOutActionDumps();
|
|
1687
|
+
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1688
|
+
return output;
|
|
1689
|
+
}
|
|
1690
|
+
if (!(output == null ? void 0 : output.pass)) {
|
|
1691
|
+
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1692
|
+
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || ((_a = executor.latestErrorTask()) == null ? void 0 : _a.error) || "(no_reason)"}`;
|
|
1693
|
+
throw new Error(`${errMsg}
|
|
1694
|
+
${reasonMsg}`);
|
|
1695
|
+
}
|
|
1655
1696
|
});
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1697
|
+
}
|
|
1698
|
+
aiWaitFor(assertion, opt) {
|
|
1699
|
+
return __async(this, null, function* () {
|
|
1700
|
+
const { executor } = yield this.taskExecutor.waitFor(assertion, {
|
|
1701
|
+
timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
|
|
1702
|
+
checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
|
|
1703
|
+
assertion
|
|
1704
|
+
});
|
|
1705
|
+
this.appendExecutionDump(executor.dump());
|
|
1706
|
+
this.writeOutActionDumps();
|
|
1707
|
+
if (executor.isInErrorState()) {
|
|
1708
|
+
const errorTask = executor.latestErrorTask();
|
|
1709
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1661
1710
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1662
|
-
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1663
1713
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1714
|
+
ai(taskPrompt, type = "action") {
|
|
1715
|
+
return __async(this, null, function* () {
|
|
1716
|
+
if (type === "action") {
|
|
1717
|
+
return this.aiAction(taskPrompt);
|
|
1718
|
+
}
|
|
1719
|
+
if (type === "query") {
|
|
1720
|
+
return this.aiQuery(taskPrompt);
|
|
1721
|
+
}
|
|
1722
|
+
if (type === "assert") {
|
|
1723
|
+
return this.aiAssert(taskPrompt);
|
|
1724
|
+
}
|
|
1725
|
+
throw new Error(
|
|
1726
|
+
`Unknown type: ${type}, only support 'action', 'query', 'assert'`
|
|
1727
|
+
);
|
|
1728
|
+
});
|
|
1677
1729
|
}
|
|
1678
1730
|
};
|
|
1679
1731
|
|
|
1680
1732
|
// src/appium/page.ts
|
|
1681
1733
|
import fs from "fs";
|
|
1682
1734
|
import { getTmpFile } from "@midscene/core/utils";
|
|
1683
|
-
import { resizeImg } from "@midscene/shared/img";
|
|
1735
|
+
import { base64Encoded, resizeImg } from "@midscene/shared/img";
|
|
1684
1736
|
import { DOMParser } from "@xmldom/xmldom";
|
|
1685
1737
|
|
|
1686
1738
|
// src/extractor/web-extractor.ts
|
|
@@ -1883,26 +1935,30 @@ var Page = class {
|
|
|
1883
1935
|
this.pageType = "appium";
|
|
1884
1936
|
this.browser = browser;
|
|
1885
1937
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1938
|
+
getElementInfos() {
|
|
1939
|
+
return __async(this, null, function* () {
|
|
1940
|
+
const pageSource = yield this.browser.getPageSource();
|
|
1941
|
+
const { width, height } = yield this.browser.getWindowSize();
|
|
1942
|
+
const parser = new DOMParser();
|
|
1943
|
+
const doc = parser.parseFromString(pageSource, "text/xml");
|
|
1944
|
+
const infos = extractTextWithPosition2(doc).filter(
|
|
1945
|
+
(element) => element.rect.height !== height && element.rect.width !== width && element.rect.left !== 0 && element.rect.top !== 0 && element.attributes.visible === "true"
|
|
1946
|
+
);
|
|
1947
|
+
return infos;
|
|
1948
|
+
});
|
|
1895
1949
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1950
|
+
screenshotBase64() {
|
|
1951
|
+
return __async(this, null, function* () {
|
|
1952
|
+
const { width, height } = yield this.browser.getWindowSize();
|
|
1953
|
+
const path2 = getTmpFile("png");
|
|
1954
|
+
const screenshotBuffer = yield this.browser.saveScreenshot(path2);
|
|
1955
|
+
const resizedScreenshotBuffer = yield resizeImg(screenshotBuffer, {
|
|
1956
|
+
width,
|
|
1957
|
+
height
|
|
1958
|
+
});
|
|
1959
|
+
fs.writeFileSync(path2, resizedScreenshotBuffer);
|
|
1960
|
+
return base64Encoded(path2);
|
|
1903
1961
|
});
|
|
1904
|
-
fs.writeFileSync(path2, resizedScreenshotBuffer);
|
|
1905
|
-
return path2;
|
|
1906
1962
|
}
|
|
1907
1963
|
get mouse() {
|
|
1908
1964
|
return {
|
|
@@ -1918,14 +1974,16 @@ var Page = class {
|
|
|
1918
1974
|
press: (key) => this.keyboardPress(key)
|
|
1919
1975
|
};
|
|
1920
1976
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1977
|
+
clearInput(element) {
|
|
1978
|
+
return __async(this, null, function* () {
|
|
1979
|
+
if (!element) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
const ele = yield this.browser.$(element.locator);
|
|
1983
|
+
const blank = " ";
|
|
1984
|
+
yield this.keyboardType(blank);
|
|
1985
|
+
yield ele.clearValue();
|
|
1986
|
+
});
|
|
1929
1987
|
}
|
|
1930
1988
|
url() {
|
|
1931
1989
|
var _a;
|
|
@@ -1941,114 +1999,132 @@ var Page = class {
|
|
|
1941
1999
|
return "";
|
|
1942
2000
|
}
|
|
1943
2001
|
// Scroll to top element
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2002
|
+
scrollUntilTop() {
|
|
2003
|
+
return __async(this, null, function* () {
|
|
2004
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2005
|
+
yield this.mouseWheel(0, height, 100);
|
|
2006
|
+
});
|
|
1947
2007
|
}
|
|
1948
2008
|
// Scroll to bottom element
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2009
|
+
scrollUntilBottom() {
|
|
2010
|
+
return __async(this, null, function* () {
|
|
2011
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2012
|
+
yield this.mouseWheel(0, -height, 100);
|
|
2013
|
+
});
|
|
1952
2014
|
}
|
|
1953
2015
|
// Scroll up one screen
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2016
|
+
scrollUpOneScreen() {
|
|
2017
|
+
return __async(this, null, function* () {
|
|
2018
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2019
|
+
yield this.mouseWheel(0, height, 1e3);
|
|
2020
|
+
});
|
|
1957
2021
|
}
|
|
1958
2022
|
// Scroll down one screen
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2023
|
+
scrollDownOneScreen() {
|
|
2024
|
+
return __async(this, null, function* () {
|
|
2025
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2026
|
+
yield this.mouseWheel(0, -height, 1e3);
|
|
2027
|
+
});
|
|
1962
2028
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
if (!actions.length) {
|
|
1970
|
-
return;
|
|
1971
|
-
}
|
|
1972
|
-
await this.browser.performActions([
|
|
1973
|
-
{
|
|
1974
|
-
type: "key",
|
|
1975
|
-
id: "keyboard",
|
|
1976
|
-
actions
|
|
2029
|
+
keyboardType(text) {
|
|
2030
|
+
return __async(this, null, function* () {
|
|
2031
|
+
const actions = [];
|
|
2032
|
+
for (const char of text) {
|
|
2033
|
+
actions.push({ type: "keyDown", value: char });
|
|
2034
|
+
actions.push({ type: "keyUp", value: char });
|
|
1977
2035
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
async keyboardPress(key) {
|
|
1981
|
-
await this.browser.performActions([
|
|
1982
|
-
{
|
|
1983
|
-
type: "key",
|
|
1984
|
-
id: "keyboard",
|
|
1985
|
-
actions: [
|
|
1986
|
-
{ type: "keyDown", value: key },
|
|
1987
|
-
{ type: "keyUp", value: key }
|
|
1988
|
-
]
|
|
2036
|
+
if (!actions.length) {
|
|
2037
|
+
return;
|
|
1989
2038
|
}
|
|
1990
|
-
|
|
2039
|
+
yield this.browser.performActions([
|
|
2040
|
+
{
|
|
2041
|
+
type: "key",
|
|
2042
|
+
id: "keyboard",
|
|
2043
|
+
actions
|
|
2044
|
+
}
|
|
2045
|
+
]);
|
|
2046
|
+
});
|
|
1991
2047
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
}
|
|
2006
|
-
]);
|
|
2048
|
+
keyboardPress(key) {
|
|
2049
|
+
return __async(this, null, function* () {
|
|
2050
|
+
yield this.browser.performActions([
|
|
2051
|
+
{
|
|
2052
|
+
type: "key",
|
|
2053
|
+
id: "keyboard",
|
|
2054
|
+
actions: [
|
|
2055
|
+
{ type: "keyDown", value: key },
|
|
2056
|
+
{ type: "keyUp", value: key }
|
|
2057
|
+
]
|
|
2058
|
+
}
|
|
2059
|
+
]);
|
|
2060
|
+
});
|
|
2007
2061
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2062
|
+
mouseClick(x, y, button = "left") {
|
|
2063
|
+
return __async(this, null, function* () {
|
|
2064
|
+
yield this.mouseMove(x, y);
|
|
2065
|
+
yield this.browser.performActions([
|
|
2066
|
+
{
|
|
2067
|
+
type: "pointer",
|
|
2068
|
+
id: "mouse",
|
|
2069
|
+
parameters: { pointerType: "mouse" },
|
|
2070
|
+
actions: [
|
|
2071
|
+
{ type: "pointerMove", duration: 0, x, y },
|
|
2072
|
+
{ type: "pointerDown", button: buttonToNumber(button) },
|
|
2073
|
+
{ type: "pause", duration: 100 },
|
|
2074
|
+
{ type: "pointerUp", button: buttonToNumber(button) }
|
|
2075
|
+
]
|
|
2076
|
+
}
|
|
2077
|
+
]);
|
|
2078
|
+
});
|
|
2017
2079
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2080
|
+
mouseMove(x, y) {
|
|
2081
|
+
return __async(this, null, function* () {
|
|
2082
|
+
yield this.browser.performActions([
|
|
2083
|
+
{
|
|
2084
|
+
type: "pointer",
|
|
2085
|
+
id: "mouse",
|
|
2086
|
+
parameters: { pointerType: "mouse" },
|
|
2087
|
+
actions: [{ type: "pointerMove", duration: 0, x, y }]
|
|
2088
|
+
}
|
|
2089
|
+
]);
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
mouseWheel(deltaX, deltaY, duration = 1e3) {
|
|
2093
|
+
return __async(this, null, function* () {
|
|
2094
|
+
const n = 4;
|
|
2095
|
+
const windowSize = yield this.browser.getWindowSize();
|
|
2096
|
+
const startX = deltaX < 0 ? (n - 1) * (windowSize.width / n) : windowSize.width / n;
|
|
2097
|
+
const startY = deltaY < 0 ? (n - 1) * (windowSize.height / n) : windowSize.height / n;
|
|
2098
|
+
const maxNegativeDeltaX = startX;
|
|
2099
|
+
const maxPositiveDeltaX = (n - 1) * (windowSize.width / n);
|
|
2100
|
+
const maxNegativeDeltaY = startY;
|
|
2101
|
+
const maxPositiveDeltaY = (n - 1) * (windowSize.height / n);
|
|
2102
|
+
deltaX = Math.max(-maxNegativeDeltaX, Math.min(deltaX, maxPositiveDeltaX));
|
|
2103
|
+
deltaY = Math.max(-maxNegativeDeltaY, Math.min(deltaY, maxPositiveDeltaY));
|
|
2104
|
+
yield this.browser.performActions([
|
|
2105
|
+
{
|
|
2106
|
+
type: "pointer",
|
|
2107
|
+
id: "finger1",
|
|
2108
|
+
parameters: { pointerType: "touch" },
|
|
2109
|
+
actions: [
|
|
2110
|
+
{ type: "pointerMove", duration: 0, x: startX, y: startY },
|
|
2111
|
+
{ type: "pointerDown", button: 0 },
|
|
2112
|
+
{ type: "pause", duration },
|
|
2113
|
+
{
|
|
2114
|
+
type: "pointerMove",
|
|
2115
|
+
duration,
|
|
2116
|
+
origin: "pointer",
|
|
2117
|
+
// Use 'pointer' as the starting point
|
|
2118
|
+
x: deltaX,
|
|
2119
|
+
// X offset relative to the starting point
|
|
2120
|
+
y: deltaY
|
|
2121
|
+
// Y offset relative to the starting point
|
|
2122
|
+
},
|
|
2123
|
+
{ type: "pointerUp", button: 0 }
|
|
2124
|
+
]
|
|
2125
|
+
}
|
|
2126
|
+
]);
|
|
2127
|
+
});
|
|
2052
2128
|
}
|
|
2053
2129
|
};
|
|
2054
2130
|
export {
|