@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
|
@@ -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({
|
|
@@ -684,23 +704,20 @@ var require_main = __commonJS({
|
|
|
684
704
|
|
|
685
705
|
// src/playground/server.ts
|
|
686
706
|
import assert3 from "assert";
|
|
687
|
-
import { randomUUID
|
|
707
|
+
import { randomUUID } from "crypto";
|
|
688
708
|
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
689
709
|
import { join as join2 } from "path";
|
|
690
710
|
|
|
691
711
|
// src/common/utils.ts
|
|
692
712
|
var import_dayjs = __toESM(require_dayjs_min());
|
|
693
713
|
import assert from "assert";
|
|
694
|
-
import { randomUUID } from "crypto";
|
|
695
714
|
import { readFileSync } from "fs";
|
|
696
715
|
import path from "path";
|
|
697
716
|
import { NodeType } from "@midscene/shared/constants";
|
|
698
717
|
import { findNearestPackageJson } from "@midscene/shared/fs";
|
|
699
|
-
import {
|
|
700
|
-
base64Encoded,
|
|
701
|
-
imageInfoOfBase64
|
|
702
|
-
} from "@midscene/shared/img";
|
|
718
|
+
import { imageInfoOfBase64 } from "@midscene/shared/img";
|
|
703
719
|
import { compositeElementInfoImg } from "@midscene/shared/img";
|
|
720
|
+
import { uuid } from "@midscene/shared/utils";
|
|
704
721
|
|
|
705
722
|
// src/web-element.ts
|
|
706
723
|
var WebElementInfo = class {
|
|
@@ -728,56 +745,59 @@ var WebElementInfo = class {
|
|
|
728
745
|
};
|
|
729
746
|
|
|
730
747
|
// src/common/utils.ts
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const url = page.url();
|
|
737
|
-
const file = await page.screenshot();
|
|
738
|
-
const screenshotBase64 = base64Encoded(file);
|
|
739
|
-
const captureElementSnapshot = await page.getElementInfos();
|
|
740
|
-
const elementsInfo = await alignElements(captureElementSnapshot, page);
|
|
741
|
-
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
742
|
-
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
743
|
-
return false;
|
|
748
|
+
function parseContextFromWebPage(page, _opt) {
|
|
749
|
+
return __async(this, null, function* () {
|
|
750
|
+
assert(page, "page is required");
|
|
751
|
+
if (page._forceUsePageContext) {
|
|
752
|
+
return yield page._forceUsePageContext();
|
|
744
753
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
754
|
+
const url = page.url();
|
|
755
|
+
const screenshotBase64 = yield page.screenshotBase64();
|
|
756
|
+
const captureElementSnapshot = yield page.getElementInfos();
|
|
757
|
+
const elementsInfo = yield alignElements(captureElementSnapshot, page);
|
|
758
|
+
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
759
|
+
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
return true;
|
|
763
|
+
});
|
|
764
|
+
const size = yield imageInfoOfBase64(screenshotBase64);
|
|
765
|
+
const screenshotBase64WithElementMarker = yield compositeElementInfoImg({
|
|
766
|
+
inputImgBase64: screenshotBase64,
|
|
767
|
+
elementsPositionInfo: elementsPositionInfoWithoutText
|
|
768
|
+
});
|
|
769
|
+
return {
|
|
770
|
+
content: elementsInfo,
|
|
771
|
+
size,
|
|
772
|
+
screenshotBase64,
|
|
773
|
+
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
774
|
+
url
|
|
775
|
+
};
|
|
751
776
|
});
|
|
752
|
-
return {
|
|
753
|
-
content: elementsInfo,
|
|
754
|
-
size,
|
|
755
|
-
screenshotBase64,
|
|
756
|
-
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
757
|
-
url
|
|
758
|
-
};
|
|
759
777
|
}
|
|
760
778
|
var sizeThreshold = 3;
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
779
|
+
function alignElements(elements, page) {
|
|
780
|
+
return __async(this, null, function* () {
|
|
781
|
+
const validElements = elements.filter((item) => {
|
|
782
|
+
return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
|
|
783
|
+
});
|
|
784
|
+
const textsAligned = [];
|
|
785
|
+
for (const item of validElements) {
|
|
786
|
+
const { rect, id, content, attributes, locator, indexId } = item;
|
|
787
|
+
textsAligned.push(
|
|
788
|
+
new WebElementInfo({
|
|
789
|
+
rect,
|
|
790
|
+
locator,
|
|
791
|
+
id,
|
|
792
|
+
content,
|
|
793
|
+
attributes,
|
|
794
|
+
page,
|
|
795
|
+
indexId
|
|
796
|
+
})
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
return textsAligned;
|
|
764
800
|
});
|
|
765
|
-
const textsAligned = [];
|
|
766
|
-
for (const item of validElements) {
|
|
767
|
-
const { rect, id, content, attributes, locator, indexId } = item;
|
|
768
|
-
textsAligned.push(
|
|
769
|
-
new WebElementInfo({
|
|
770
|
-
rect,
|
|
771
|
-
locator,
|
|
772
|
-
id,
|
|
773
|
-
content,
|
|
774
|
-
attributes,
|
|
775
|
-
page,
|
|
776
|
-
indexId
|
|
777
|
-
})
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
return textsAligned;
|
|
781
801
|
}
|
|
782
802
|
function reportFileName(tag = "web") {
|
|
783
803
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss-SSS");
|
|
@@ -808,7 +828,7 @@ var testFileIndex = /* @__PURE__ */ new Map();
|
|
|
808
828
|
function generateCacheId(fileName) {
|
|
809
829
|
let taskFile = fileName || getCurrentExecutionFile();
|
|
810
830
|
if (!taskFile) {
|
|
811
|
-
taskFile =
|
|
831
|
+
taskFile = uuid();
|
|
812
832
|
console.warn(
|
|
813
833
|
"Midscene - using random UUID for cache id. Cache may be invalid."
|
|
814
834
|
);
|
|
@@ -826,8 +846,8 @@ function generateCacheId(fileName) {
|
|
|
826
846
|
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
|
827
847
|
|
|
828
848
|
// src/playground/server.ts
|
|
829
|
-
var import_dotenv = __toESM(require_main());
|
|
830
849
|
import { getTmpDir } from "@midscene/core/utils";
|
|
850
|
+
import { ifInBrowser as ifInBrowser2 } from "@midscene/shared/utils";
|
|
831
851
|
import cors from "cors";
|
|
832
852
|
import express from "express";
|
|
833
853
|
|
|
@@ -846,7 +866,6 @@ import {
|
|
|
846
866
|
plan
|
|
847
867
|
} from "@midscene/core";
|
|
848
868
|
import { sleep } from "@midscene/core/utils";
|
|
849
|
-
import { base64Encoded as base64Encoded2 } from "@midscene/shared/img";
|
|
850
869
|
|
|
851
870
|
// src/common/task-cache.ts
|
|
852
871
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
@@ -856,10 +875,11 @@ import {
|
|
|
856
875
|
stringifyDumpData,
|
|
857
876
|
writeLogFile
|
|
858
877
|
} from "@midscene/core/utils";
|
|
859
|
-
import {
|
|
878
|
+
import { getRunningPkgInfo } from "@midscene/shared/fs";
|
|
879
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
860
880
|
var TaskCache = class {
|
|
861
881
|
constructor(opts) {
|
|
862
|
-
this.midscenePkgInfo =
|
|
882
|
+
this.midscenePkgInfo = getRunningPkgInfo();
|
|
863
883
|
this.cacheId = generateCacheId(opts == null ? void 0 : opts.fileName);
|
|
864
884
|
this.cache = this.readCacheFromFile() || {
|
|
865
885
|
aiTasks: []
|
|
@@ -941,11 +961,17 @@ var TaskCache = class {
|
|
|
941
961
|
return this.newCache;
|
|
942
962
|
}
|
|
943
963
|
readCacheFromFile() {
|
|
964
|
+
if (ifInBrowser) {
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
944
967
|
const cacheFile = join(getLogDirByType("cache"), `${this.cacheId}.json`);
|
|
945
968
|
if (process.env.MIDSCENE_CACHE === "true" && existsSync(cacheFile)) {
|
|
946
969
|
try {
|
|
947
970
|
const data = readFileSync2(cacheFile, "utf8");
|
|
948
971
|
const jsonData = JSON.parse(data);
|
|
972
|
+
if (!this.midscenePkgInfo) {
|
|
973
|
+
return void 0;
|
|
974
|
+
}
|
|
949
975
|
if (jsonData.pkgName !== this.midscenePkgInfo.name || jsonData.pkgVersion !== this.midscenePkgInfo.version) {
|
|
950
976
|
return void 0;
|
|
951
977
|
}
|
|
@@ -957,7 +983,10 @@ var TaskCache = class {
|
|
|
957
983
|
return void 0;
|
|
958
984
|
}
|
|
959
985
|
writeCacheToFile() {
|
|
960
|
-
const midscenePkgInfo =
|
|
986
|
+
const midscenePkgInfo = getRunningPkgInfo();
|
|
987
|
+
if (!midscenePkgInfo) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
961
990
|
writeLogFile({
|
|
962
991
|
fileName: `${this.cacheId}`,
|
|
963
992
|
fileExt: "json",
|
|
@@ -978,423 +1007,435 @@ var TaskCache = class {
|
|
|
978
1007
|
var PageTaskExecutor = class {
|
|
979
1008
|
constructor(page, opts) {
|
|
980
1009
|
this.page = page;
|
|
981
|
-
this.insight = new Insight(
|
|
982
|
-
return
|
|
983
|
-
});
|
|
1010
|
+
this.insight = new Insight(() => __async(this, null, function* () {
|
|
1011
|
+
return yield parseContextFromWebPage(page);
|
|
1012
|
+
}));
|
|
984
1013
|
this.taskCache = new TaskCache({
|
|
985
1014
|
fileName: opts == null ? void 0 : opts.cacheId
|
|
986
1015
|
});
|
|
987
1016
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1017
|
+
recordScreenshot(timing) {
|
|
1018
|
+
return __async(this, null, function* () {
|
|
1019
|
+
const base64 = yield this.page.screenshotBase64();
|
|
1020
|
+
const item = {
|
|
1021
|
+
type: "screenshot",
|
|
1022
|
+
ts: Date.now(),
|
|
1023
|
+
screenshot: base64,
|
|
1024
|
+
timing
|
|
1025
|
+
};
|
|
1026
|
+
return item;
|
|
1027
|
+
});
|
|
997
1028
|
}
|
|
998
1029
|
wrapExecutorWithScreenshot(taskApply) {
|
|
999
1030
|
const taskWithScreenshot = __spreadProps(__spreadValues({}, taskApply), {
|
|
1000
|
-
executor:
|
|
1031
|
+
executor: (param, context, ...args) => __async(this, null, function* () {
|
|
1001
1032
|
const recorder = [];
|
|
1002
1033
|
const { task } = context;
|
|
1003
1034
|
task.recorder = recorder;
|
|
1004
|
-
const shot =
|
|
1035
|
+
const shot = yield this.recordScreenshot(`before ${task.type}`);
|
|
1005
1036
|
recorder.push(shot);
|
|
1006
|
-
const result =
|
|
1037
|
+
const result = yield taskApply.executor(param, context, ...args);
|
|
1007
1038
|
if (taskApply.type === "Action") {
|
|
1008
|
-
|
|
1009
|
-
const shot2 =
|
|
1039
|
+
yield sleep(1e3);
|
|
1040
|
+
const shot2 = yield this.recordScreenshot("after Action");
|
|
1010
1041
|
recorder.push(shot2);
|
|
1011
1042
|
}
|
|
1012
1043
|
return result;
|
|
1013
|
-
}
|
|
1044
|
+
})
|
|
1014
1045
|
});
|
|
1015
1046
|
return taskWithScreenshot;
|
|
1016
1047
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1031
|
-
const pageContext = await this.insight.contextRetrieverFn();
|
|
1032
|
-
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1033
|
-
pageContext,
|
|
1034
|
-
"locate",
|
|
1035
|
-
param.prompt
|
|
1036
|
-
);
|
|
1037
|
-
let locateResult;
|
|
1038
|
-
const callAI = this.insight.aiVendorFn;
|
|
1039
|
-
const element = await this.insight.locate(param.prompt, {
|
|
1040
|
-
quickAnswer: plan2.quickAnswer,
|
|
1041
|
-
callAI: async (...message) => {
|
|
1042
|
-
if (locateCache) {
|
|
1043
|
-
locateResult = locateCache;
|
|
1044
|
-
return Promise.resolve(locateCache);
|
|
1045
|
-
}
|
|
1046
|
-
locateResult = await callAI(...message);
|
|
1047
|
-
assert2(locateResult);
|
|
1048
|
-
return locateResult;
|
|
1049
|
-
}
|
|
1050
|
-
});
|
|
1051
|
-
if (locateResult) {
|
|
1052
|
-
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1053
|
-
type: "locate",
|
|
1054
|
-
pageContext: {
|
|
1055
|
-
url: pageContext.url,
|
|
1056
|
-
size: pageContext.size
|
|
1057
|
-
},
|
|
1058
|
-
prompt: param.prompt,
|
|
1059
|
-
response: locateResult
|
|
1060
|
-
});
|
|
1061
|
-
}
|
|
1062
|
-
if (!element) {
|
|
1063
|
-
task.log = {
|
|
1064
|
-
dump: insightDump
|
|
1048
|
+
convertPlanToExecutable(plans, cacheGroup) {
|
|
1049
|
+
return __async(this, null, function* () {
|
|
1050
|
+
const tasks = plans.map((plan2) => {
|
|
1051
|
+
if (plan2.type === "Locate") {
|
|
1052
|
+
const taskFind = {
|
|
1053
|
+
type: "Insight",
|
|
1054
|
+
subType: "Locate",
|
|
1055
|
+
param: plan2.param,
|
|
1056
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1057
|
+
const { task } = taskContext;
|
|
1058
|
+
let insightDump;
|
|
1059
|
+
const dumpCollector = (dump) => {
|
|
1060
|
+
insightDump = dump;
|
|
1065
1061
|
};
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1062
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1063
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1064
|
+
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1065
|
+
pageContext,
|
|
1066
|
+
"locate",
|
|
1067
|
+
param.prompt
|
|
1068
|
+
);
|
|
1069
|
+
let locateResult;
|
|
1070
|
+
const callAI = this.insight.aiVendorFn;
|
|
1071
|
+
const element = yield this.insight.locate(param.prompt, {
|
|
1072
|
+
quickAnswer: plan2.quickAnswer,
|
|
1073
|
+
callAI: (...message) => __async(this, null, function* () {
|
|
1074
|
+
if (locateCache) {
|
|
1075
|
+
locateResult = locateCache;
|
|
1076
|
+
return Promise.resolve(locateCache);
|
|
1077
|
+
}
|
|
1078
|
+
locateResult = yield callAI(...message);
|
|
1079
|
+
assert2(locateResult);
|
|
1080
|
+
return locateResult;
|
|
1081
|
+
})
|
|
1082
|
+
});
|
|
1083
|
+
if (locateResult) {
|
|
1084
|
+
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1085
|
+
type: "locate",
|
|
1086
|
+
pageContext: {
|
|
1087
|
+
url: pageContext.url,
|
|
1088
|
+
size: pageContext.size
|
|
1089
|
+
},
|
|
1090
|
+
prompt: param.prompt,
|
|
1091
|
+
response: locateResult
|
|
1092
|
+
});
|
|
1077
1093
|
}
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1080
|
-
};
|
|
1081
|
-
return taskFind;
|
|
1082
|
-
}
|
|
1083
|
-
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1084
|
-
const assertPlan = plan2;
|
|
1085
|
-
const taskAssert = {
|
|
1086
|
-
type: "Insight",
|
|
1087
|
-
subType: "Assert",
|
|
1088
|
-
param: assertPlan.param,
|
|
1089
|
-
executor: async (param, taskContext) => {
|
|
1090
|
-
const { task } = taskContext;
|
|
1091
|
-
let insightDump;
|
|
1092
|
-
const dumpCollector = (dump) => {
|
|
1093
|
-
insightDump = dump;
|
|
1094
|
-
};
|
|
1095
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1096
|
-
const assertion = await this.insight.assert(
|
|
1097
|
-
assertPlan.param.assertion
|
|
1098
|
-
);
|
|
1099
|
-
if (!assertion.pass) {
|
|
1100
|
-
if (plan2.type === "Assert") {
|
|
1101
|
-
task.output = assertion;
|
|
1094
|
+
if (!element) {
|
|
1102
1095
|
task.log = {
|
|
1103
1096
|
dump: insightDump
|
|
1104
1097
|
};
|
|
1105
|
-
throw new Error(
|
|
1106
|
-
assertion.thought || "Assertion failed without reason"
|
|
1107
|
-
);
|
|
1098
|
+
throw new Error(`Element not found: ${param.prompt}`);
|
|
1108
1099
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1100
|
+
return {
|
|
1101
|
+
output: {
|
|
1102
|
+
element
|
|
1103
|
+
},
|
|
1104
|
+
log: {
|
|
1105
|
+
dump: insightDump
|
|
1106
|
+
},
|
|
1107
|
+
cache: {
|
|
1108
|
+
hit: Boolean(locateCache)
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
})
|
|
1112
|
+
};
|
|
1113
|
+
return taskFind;
|
|
1114
|
+
}
|
|
1115
|
+
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1116
|
+
const assertPlan = plan2;
|
|
1117
|
+
const taskAssert = {
|
|
1118
|
+
type: "Insight",
|
|
1119
|
+
subType: "Assert",
|
|
1120
|
+
param: assertPlan.param,
|
|
1121
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1122
|
+
const { task } = taskContext;
|
|
1123
|
+
let insightDump;
|
|
1124
|
+
const dumpCollector = (dump) => {
|
|
1125
|
+
insightDump = dump;
|
|
1126
|
+
};
|
|
1127
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1128
|
+
const assertion = yield this.insight.assert(
|
|
1129
|
+
assertPlan.param.assertion
|
|
1130
|
+
);
|
|
1131
|
+
if (!assertion.pass) {
|
|
1132
|
+
if (plan2.type === "Assert") {
|
|
1133
|
+
task.output = assertion;
|
|
1134
|
+
task.log = {
|
|
1135
|
+
dump: insightDump
|
|
1136
|
+
};
|
|
1137
|
+
throw new Error(
|
|
1138
|
+
assertion.thought || "Assertion failed without reason"
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
task.error = assertion.thought;
|
|
1115
1142
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1143
|
+
return {
|
|
1144
|
+
output: assertion,
|
|
1145
|
+
log: {
|
|
1146
|
+
dump: insightDump
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
})
|
|
1150
|
+
};
|
|
1151
|
+
return taskAssert;
|
|
1152
|
+
}
|
|
1153
|
+
if (plan2.type === "Input") {
|
|
1154
|
+
const taskActionInput = {
|
|
1155
|
+
type: "Action",
|
|
1156
|
+
subType: "Input",
|
|
1157
|
+
param: plan2.param,
|
|
1158
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (taskParam, { element }) {
|
|
1159
|
+
if (element) {
|
|
1160
|
+
yield this.page.clearInput(element);
|
|
1161
|
+
if (taskParam.value === "") {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
yield this.page.keyboard.type(taskParam.value);
|
|
1131
1165
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
)
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
return
|
|
1166
|
+
})
|
|
1167
|
+
};
|
|
1168
|
+
return taskActionInput;
|
|
1169
|
+
}
|
|
1170
|
+
if (plan2.type === "KeyboardPress") {
|
|
1171
|
+
const taskActionKeyboardPress = {
|
|
1172
|
+
type: "Action",
|
|
1173
|
+
subType: "KeyboardPress",
|
|
1174
|
+
param: plan2.param,
|
|
1175
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1176
|
+
assert2(taskParam.value, "No key to press");
|
|
1177
|
+
yield this.page.keyboard.press(taskParam.value);
|
|
1178
|
+
})
|
|
1179
|
+
};
|
|
1180
|
+
return taskActionKeyboardPress;
|
|
1181
|
+
}
|
|
1182
|
+
if (plan2.type === "Tap") {
|
|
1183
|
+
const taskActionTap = {
|
|
1184
|
+
type: "Action",
|
|
1185
|
+
subType: "Tap",
|
|
1186
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1187
|
+
assert2(element, "Element not found, cannot tap");
|
|
1188
|
+
yield this.page.mouse.click(
|
|
1189
|
+
element.center[0],
|
|
1190
|
+
element.center[1]
|
|
1191
|
+
);
|
|
1192
|
+
})
|
|
1193
|
+
};
|
|
1194
|
+
return taskActionTap;
|
|
1195
|
+
}
|
|
1196
|
+
if (plan2.type === "Hover") {
|
|
1197
|
+
const taskActionHover = {
|
|
1198
|
+
type: "Action",
|
|
1199
|
+
subType: "Hover",
|
|
1200
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1201
|
+
assert2(element, "Element not found, cannot hover");
|
|
1202
|
+
yield this.page.mouse.move(
|
|
1203
|
+
element.center[0],
|
|
1204
|
+
element.center[1]
|
|
1205
|
+
);
|
|
1206
|
+
})
|
|
1207
|
+
};
|
|
1208
|
+
return taskActionHover;
|
|
1209
|
+
}
|
|
1210
|
+
if (plan2.type === "Scroll") {
|
|
1211
|
+
const taskActionScroll = {
|
|
1212
|
+
type: "Action",
|
|
1213
|
+
subType: "Scroll",
|
|
1214
|
+
param: plan2.param,
|
|
1215
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1216
|
+
const scrollToEventName = taskParam.scrollType;
|
|
1217
|
+
switch (scrollToEventName) {
|
|
1218
|
+
case "scrollUntilTop":
|
|
1219
|
+
yield this.page.scrollUntilTop();
|
|
1220
|
+
break;
|
|
1221
|
+
case "scrollUntilBottom":
|
|
1222
|
+
yield this.page.scrollUntilBottom();
|
|
1223
|
+
break;
|
|
1224
|
+
case "scrollUpOneScreen":
|
|
1225
|
+
yield this.page.scrollUpOneScreen();
|
|
1226
|
+
break;
|
|
1227
|
+
case "scrollDownOneScreen":
|
|
1228
|
+
yield this.page.scrollDownOneScreen();
|
|
1229
|
+
break;
|
|
1230
|
+
default:
|
|
1231
|
+
console.error(
|
|
1232
|
+
"Unknown scroll event type:",
|
|
1233
|
+
scrollToEventName
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
})
|
|
1237
|
+
};
|
|
1238
|
+
return taskActionScroll;
|
|
1239
|
+
}
|
|
1240
|
+
if (plan2.type === "Sleep") {
|
|
1241
|
+
const taskActionSleep = {
|
|
1242
|
+
type: "Action",
|
|
1243
|
+
subType: "Sleep",
|
|
1244
|
+
param: plan2.param,
|
|
1245
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1246
|
+
yield sleep(taskParam.timeMs || 3e3);
|
|
1247
|
+
})
|
|
1248
|
+
};
|
|
1249
|
+
return taskActionSleep;
|
|
1250
|
+
}
|
|
1251
|
+
if (plan2.type === "Error") {
|
|
1252
|
+
const taskActionError = {
|
|
1253
|
+
type: "Action",
|
|
1254
|
+
subType: "Error",
|
|
1255
|
+
param: plan2.param,
|
|
1256
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1257
|
+
assert2(
|
|
1258
|
+
taskParam.thought,
|
|
1259
|
+
"An error occurred, but no thought provided"
|
|
1260
|
+
);
|
|
1261
|
+
throw new Error(taskParam.thought);
|
|
1262
|
+
})
|
|
1263
|
+
};
|
|
1264
|
+
return taskActionError;
|
|
1265
|
+
}
|
|
1266
|
+
throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
|
|
1267
|
+
}).map((task) => {
|
|
1268
|
+
return this.wrapExecutorWithScreenshot(task);
|
|
1269
|
+
});
|
|
1270
|
+
return tasks;
|
|
1237
1271
|
});
|
|
1238
|
-
return tasks;
|
|
1239
1272
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1273
|
+
action(userPrompt) {
|
|
1274
|
+
return __async(this, null, function* () {
|
|
1275
|
+
const taskExecutor = new Executor(userPrompt);
|
|
1276
|
+
const cacheGroup = this.taskCache.getCacheGroupByPrompt(userPrompt);
|
|
1277
|
+
let plans = [];
|
|
1278
|
+
const planningTask = {
|
|
1279
|
+
type: "Planning",
|
|
1280
|
+
param: {
|
|
1281
|
+
userPrompt
|
|
1282
|
+
},
|
|
1283
|
+
executor: (param) => __async(this, null, function* () {
|
|
1284
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1285
|
+
let planResult;
|
|
1286
|
+
const planCache = cacheGroup.readCache(pageContext, "plan", userPrompt);
|
|
1287
|
+
if (planCache) {
|
|
1288
|
+
planResult = planCache;
|
|
1289
|
+
} else {
|
|
1290
|
+
planResult = yield plan(param.userPrompt, {
|
|
1291
|
+
context: pageContext
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
assert2(planResult.plans.length > 0, "No plans found");
|
|
1295
|
+
plans = planResult.plans;
|
|
1296
|
+
cacheGroup.saveCache({
|
|
1297
|
+
type: "plan",
|
|
1298
|
+
pageContext: {
|
|
1299
|
+
url: pageContext.url,
|
|
1300
|
+
size: pageContext.size
|
|
1301
|
+
},
|
|
1302
|
+
prompt: userPrompt,
|
|
1303
|
+
response: planResult
|
|
1258
1304
|
});
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
});
|
|
1305
|
+
return {
|
|
1306
|
+
output: planResult,
|
|
1307
|
+
cache: {
|
|
1308
|
+
hit: Boolean(planCache)
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
})
|
|
1312
|
+
};
|
|
1313
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
|
|
1314
|
+
let output = yield taskExecutor.flush();
|
|
1315
|
+
if (taskExecutor.isInErrorState()) {
|
|
1271
1316
|
return {
|
|
1272
|
-
output
|
|
1273
|
-
|
|
1274
|
-
hit: Boolean(planCache)
|
|
1275
|
-
}
|
|
1317
|
+
output,
|
|
1318
|
+
executor: taskExecutor
|
|
1276
1319
|
};
|
|
1277
1320
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (taskExecutor.isInErrorState()) {
|
|
1321
|
+
const executables = yield this.convertPlanToExecutable(plans, cacheGroup);
|
|
1322
|
+
yield taskExecutor.append(executables);
|
|
1323
|
+
output = yield taskExecutor.flush();
|
|
1282
1324
|
return {
|
|
1283
1325
|
output,
|
|
1284
1326
|
executor: taskExecutor
|
|
1285
1327
|
};
|
|
1286
|
-
}
|
|
1287
|
-
const executables = await this.convertPlanToExecutable(plans, cacheGroup);
|
|
1288
|
-
await taskExecutor.append(executables);
|
|
1289
|
-
output = await taskExecutor.flush();
|
|
1290
|
-
return {
|
|
1291
|
-
output,
|
|
1292
|
-
executor: taskExecutor
|
|
1293
|
-
};
|
|
1294
|
-
}
|
|
1295
|
-
async query(demand) {
|
|
1296
|
-
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1297
|
-
const taskExecutor = new Executor(description);
|
|
1298
|
-
const queryTask = {
|
|
1299
|
-
type: "Insight",
|
|
1300
|
-
subType: "Query",
|
|
1301
|
-
param: {
|
|
1302
|
-
dataDemand: demand
|
|
1303
|
-
},
|
|
1304
|
-
executor: async (param) => {
|
|
1305
|
-
let insightDump;
|
|
1306
|
-
const dumpCollector = (dump) => {
|
|
1307
|
-
insightDump = dump;
|
|
1308
|
-
};
|
|
1309
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1310
|
-
const data = await this.insight.extract(param.dataDemand);
|
|
1311
|
-
return {
|
|
1312
|
-
output: data,
|
|
1313
|
-
log: { dump: insightDump }
|
|
1314
|
-
};
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1318
|
-
const output = await taskExecutor.flush();
|
|
1319
|
-
return {
|
|
1320
|
-
output,
|
|
1321
|
-
executor: taskExecutor
|
|
1322
|
-
};
|
|
1328
|
+
});
|
|
1323
1329
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1330
|
+
query(demand) {
|
|
1331
|
+
return __async(this, null, function* () {
|
|
1332
|
+
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1333
|
+
const taskExecutor = new Executor(description);
|
|
1334
|
+
const queryTask = {
|
|
1335
|
+
type: "Insight",
|
|
1336
|
+
subType: "Query",
|
|
1337
|
+
param: {
|
|
1338
|
+
dataDemand: demand
|
|
1339
|
+
},
|
|
1340
|
+
executor: (param) => __async(this, null, function* () {
|
|
1341
|
+
let insightDump;
|
|
1342
|
+
const dumpCollector = (dump) => {
|
|
1343
|
+
insightDump = dump;
|
|
1344
|
+
};
|
|
1345
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1346
|
+
const data = yield this.insight.extract(param.dataDemand);
|
|
1347
|
+
return {
|
|
1348
|
+
output: data,
|
|
1349
|
+
log: { dump: insightDump }
|
|
1350
|
+
};
|
|
1351
|
+
})
|
|
1352
|
+
};
|
|
1353
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1354
|
+
const output = yield taskExecutor.flush();
|
|
1355
|
+
return {
|
|
1356
|
+
output,
|
|
1357
|
+
executor: taskExecutor
|
|
1358
|
+
};
|
|
1359
|
+
});
|
|
1340
1360
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1348
|
-
const overallStartTime = Date.now();
|
|
1349
|
-
let startTime = Date.now();
|
|
1350
|
-
let errorThought = "";
|
|
1351
|
-
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1352
|
-
startTime = Date.now();
|
|
1353
|
-
const assertPlan = {
|
|
1354
|
-
type: "AssertWithoutThrow",
|
|
1361
|
+
assert(assertion) {
|
|
1362
|
+
return __async(this, null, function* () {
|
|
1363
|
+
const description = `assert: ${assertion}`;
|
|
1364
|
+
const taskExecutor = new Executor(description);
|
|
1365
|
+
const assertionPlan = {
|
|
1366
|
+
type: "Assert",
|
|
1355
1367
|
param: {
|
|
1356
1368
|
assertion
|
|
1357
1369
|
}
|
|
1358
1370
|
};
|
|
1359
|
-
const assertTask =
|
|
1360
|
-
|
|
1361
|
-
const output =
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1371
|
+
const assertTask = yield this.convertPlanToExecutable([assertionPlan]);
|
|
1372
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1373
|
+
const output = yield taskExecutor.flush();
|
|
1374
|
+
return {
|
|
1375
|
+
output,
|
|
1376
|
+
executor: taskExecutor
|
|
1377
|
+
};
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
waitFor(assertion, opt) {
|
|
1381
|
+
return __async(this, null, function* () {
|
|
1382
|
+
const description = `waitFor: ${assertion}`;
|
|
1383
|
+
const taskExecutor = new Executor(description);
|
|
1384
|
+
const { timeoutMs, checkIntervalMs } = opt;
|
|
1385
|
+
assert2(assertion, "No assertion for waitFor");
|
|
1386
|
+
assert2(timeoutMs, "No timeoutMs for waitFor");
|
|
1387
|
+
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1388
|
+
const overallStartTime = Date.now();
|
|
1389
|
+
let startTime = Date.now();
|
|
1390
|
+
let errorThought = "";
|
|
1391
|
+
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1392
|
+
startTime = Date.now();
|
|
1393
|
+
const assertPlan = {
|
|
1394
|
+
type: "AssertWithoutThrow",
|
|
1374
1395
|
param: {
|
|
1375
|
-
|
|
1396
|
+
assertion
|
|
1376
1397
|
}
|
|
1377
1398
|
};
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
)
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1399
|
+
const assertTask = yield this.convertPlanToExecutable([assertPlan]);
|
|
1400
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1401
|
+
const output = yield taskExecutor.flush();
|
|
1402
|
+
if (output == null ? void 0 : output.pass) {
|
|
1403
|
+
return {
|
|
1404
|
+
output: void 0,
|
|
1405
|
+
executor: taskExecutor
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
errorThought = (output == null ? void 0 : output.thought) || "unknown error";
|
|
1409
|
+
const now = Date.now();
|
|
1410
|
+
if (now - startTime < checkIntervalMs) {
|
|
1411
|
+
const timeRemaining = checkIntervalMs - (now - startTime);
|
|
1412
|
+
const sleepPlan = {
|
|
1413
|
+
type: "Sleep",
|
|
1414
|
+
param: {
|
|
1415
|
+
timeMs: timeRemaining
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
const sleepTask = yield this.convertPlanToExecutable([sleepPlan]);
|
|
1419
|
+
yield taskExecutor.append(
|
|
1420
|
+
this.wrapExecutorWithScreenshot(sleepTask[0])
|
|
1421
|
+
);
|
|
1422
|
+
yield taskExecutor.flush();
|
|
1423
|
+
}
|
|
1389
1424
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1425
|
+
const errorPlan = {
|
|
1426
|
+
type: "Error",
|
|
1427
|
+
param: {
|
|
1428
|
+
thought: `waitFor timeout: ${errorThought}`
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
const errorTask = yield this.convertPlanToExecutable([errorPlan]);
|
|
1432
|
+
yield taskExecutor.append(errorTask[0]);
|
|
1433
|
+
yield taskExecutor.flush();
|
|
1434
|
+
return {
|
|
1435
|
+
output: void 0,
|
|
1436
|
+
executor: taskExecutor
|
|
1437
|
+
};
|
|
1438
|
+
});
|
|
1398
1439
|
}
|
|
1399
1440
|
};
|
|
1400
1441
|
|
|
@@ -1439,72 +1480,83 @@ var PageAgent = class {
|
|
|
1439
1480
|
type: "dump",
|
|
1440
1481
|
generateReport
|
|
1441
1482
|
});
|
|
1442
|
-
if (generateReport && autoPrintReportMsg) {
|
|
1483
|
+
if (generateReport && autoPrintReportMsg && this.reportFile) {
|
|
1443
1484
|
printReportMsg(this.reportFile);
|
|
1444
1485
|
}
|
|
1445
1486
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1487
|
+
aiAction(taskPrompt) {
|
|
1488
|
+
return __async(this, null, function* () {
|
|
1489
|
+
const { executor } = yield this.taskExecutor.action(taskPrompt);
|
|
1490
|
+
this.appendExecutionDump(executor.dump());
|
|
1491
|
+
this.writeOutActionDumps();
|
|
1492
|
+
if (executor.isInErrorState()) {
|
|
1493
|
+
const errorTask = executor.latestErrorTask();
|
|
1494
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1453
1495
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1454
|
-
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1455
1498
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1499
|
+
aiQuery(demand) {
|
|
1500
|
+
return __async(this, null, function* () {
|
|
1501
|
+
const { output, executor } = yield this.taskExecutor.query(demand);
|
|
1502
|
+
this.appendExecutionDump(executor.dump());
|
|
1503
|
+
this.writeOutActionDumps();
|
|
1504
|
+
if (executor.isInErrorState()) {
|
|
1505
|
+
const errorTask = executor.latestErrorTask();
|
|
1506
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1463
1507
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1464
|
-
|
|
1465
|
-
return output;
|
|
1466
|
-
}
|
|
1467
|
-
async aiAssert(assertion, msg, opt) {
|
|
1468
|
-
const { output, executor } = await this.taskExecutor.assert(assertion);
|
|
1469
|
-
this.appendExecutionDump(executor.dump());
|
|
1470
|
-
this.writeOutActionDumps();
|
|
1471
|
-
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1508
|
+
}
|
|
1472
1509
|
return output;
|
|
1473
|
-
}
|
|
1474
|
-
if (!(output == null ? void 0 : output.pass)) {
|
|
1475
|
-
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1476
|
-
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || "(no_reason)"}`;
|
|
1477
|
-
throw new Error(`${errMsg}
|
|
1478
|
-
${reasonMsg}`);
|
|
1479
|
-
}
|
|
1510
|
+
});
|
|
1480
1511
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1512
|
+
aiAssert(assertion, msg, opt) {
|
|
1513
|
+
return __async(this, null, function* () {
|
|
1514
|
+
var _a;
|
|
1515
|
+
const { output, executor } = yield this.taskExecutor.assert(assertion);
|
|
1516
|
+
this.appendExecutionDump(executor.dump());
|
|
1517
|
+
this.writeOutActionDumps();
|
|
1518
|
+
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1519
|
+
return output;
|
|
1520
|
+
}
|
|
1521
|
+
if (!(output == null ? void 0 : output.pass)) {
|
|
1522
|
+
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1523
|
+
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || ((_a = executor.latestErrorTask()) == null ? void 0 : _a.error) || "(no_reason)"}`;
|
|
1524
|
+
throw new Error(`${errMsg}
|
|
1525
|
+
${reasonMsg}`);
|
|
1526
|
+
}
|
|
1486
1527
|
});
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1528
|
+
}
|
|
1529
|
+
aiWaitFor(assertion, opt) {
|
|
1530
|
+
return __async(this, null, function* () {
|
|
1531
|
+
const { executor } = yield this.taskExecutor.waitFor(assertion, {
|
|
1532
|
+
timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
|
|
1533
|
+
checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
|
|
1534
|
+
assertion
|
|
1535
|
+
});
|
|
1536
|
+
this.appendExecutionDump(executor.dump());
|
|
1537
|
+
this.writeOutActionDumps();
|
|
1538
|
+
if (executor.isInErrorState()) {
|
|
1539
|
+
const errorTask = executor.latestErrorTask();
|
|
1540
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1492
1541
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1493
|
-
|
|
1542
|
+
}
|
|
1543
|
+
});
|
|
1494
1544
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1545
|
+
ai(taskPrompt, type = "action") {
|
|
1546
|
+
return __async(this, null, function* () {
|
|
1547
|
+
if (type === "action") {
|
|
1548
|
+
return this.aiAction(taskPrompt);
|
|
1549
|
+
}
|
|
1550
|
+
if (type === "query") {
|
|
1551
|
+
return this.aiQuery(taskPrompt);
|
|
1552
|
+
}
|
|
1553
|
+
if (type === "assert") {
|
|
1554
|
+
return this.aiAssert(taskPrompt);
|
|
1555
|
+
}
|
|
1556
|
+
throw new Error(
|
|
1557
|
+
`Unknown type: ${type}, only support 'action', 'query', 'assert'`
|
|
1558
|
+
);
|
|
1559
|
+
});
|
|
1508
1560
|
}
|
|
1509
1561
|
};
|
|
1510
1562
|
|
|
@@ -1516,8 +1568,6 @@ var StaticPageAgent = class extends PageAgent {
|
|
|
1516
1568
|
};
|
|
1517
1569
|
|
|
1518
1570
|
// src/playground/static-page.ts
|
|
1519
|
-
import { getTmpFile } from "@midscene/core/utils";
|
|
1520
|
-
import { saveBase64Image } from "@midscene/shared/img";
|
|
1521
1571
|
var ThrowNotImplemented = (methodName) => {
|
|
1522
1572
|
throw new Error(
|
|
1523
1573
|
`The method "${methodName}" is not implemented as designed since this is a static UI context. (${ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED})`
|
|
@@ -1537,38 +1587,52 @@ var StaticPage = class {
|
|
|
1537
1587
|
};
|
|
1538
1588
|
this.uiContext = uiContext;
|
|
1539
1589
|
}
|
|
1540
|
-
|
|
1541
|
-
return
|
|
1590
|
+
getElementInfos() {
|
|
1591
|
+
return __async(this, null, function* () {
|
|
1592
|
+
return ThrowNotImplemented("getElementInfos");
|
|
1593
|
+
});
|
|
1542
1594
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1595
|
+
screenshotBase64() {
|
|
1596
|
+
return __async(this, null, function* () {
|
|
1597
|
+
const base64 = this.uiContext.screenshotBase64;
|
|
1598
|
+
if (!base64) {
|
|
1599
|
+
throw new Error("screenshot base64 is empty");
|
|
1600
|
+
}
|
|
1601
|
+
return base64;
|
|
1602
|
+
});
|
|
1551
1603
|
}
|
|
1552
1604
|
url() {
|
|
1553
1605
|
return this.uiContext.url;
|
|
1554
1606
|
}
|
|
1555
|
-
|
|
1556
|
-
return
|
|
1607
|
+
scrollUntilTop() {
|
|
1608
|
+
return __async(this, null, function* () {
|
|
1609
|
+
return ThrowNotImplemented("scrollUntilTop");
|
|
1610
|
+
});
|
|
1557
1611
|
}
|
|
1558
|
-
|
|
1559
|
-
return
|
|
1612
|
+
scrollUntilBottom() {
|
|
1613
|
+
return __async(this, null, function* () {
|
|
1614
|
+
return ThrowNotImplemented("scrollUntilBottom");
|
|
1615
|
+
});
|
|
1560
1616
|
}
|
|
1561
|
-
|
|
1562
|
-
return
|
|
1617
|
+
scrollUpOneScreen() {
|
|
1618
|
+
return __async(this, null, function* () {
|
|
1619
|
+
return ThrowNotImplemented("scrollUpOneScreen");
|
|
1620
|
+
});
|
|
1563
1621
|
}
|
|
1564
|
-
|
|
1565
|
-
return
|
|
1622
|
+
scrollDownOneScreen() {
|
|
1623
|
+
return __async(this, null, function* () {
|
|
1624
|
+
return ThrowNotImplemented("scrollDownOneScreen");
|
|
1625
|
+
});
|
|
1566
1626
|
}
|
|
1567
|
-
|
|
1568
|
-
return
|
|
1627
|
+
clearInput() {
|
|
1628
|
+
return __async(this, null, function* () {
|
|
1629
|
+
return ThrowNotImplemented("clearInput");
|
|
1630
|
+
});
|
|
1569
1631
|
}
|
|
1570
|
-
|
|
1571
|
-
return this
|
|
1632
|
+
_forceUsePageContext() {
|
|
1633
|
+
return __async(this, null, function* () {
|
|
1634
|
+
return this.uiContext;
|
|
1635
|
+
});
|
|
1572
1636
|
}
|
|
1573
1637
|
};
|
|
1574
1638
|
|
|
@@ -1582,123 +1646,128 @@ var errorHandler = (err, req, res, next) => {
|
|
|
1582
1646
|
error: err.message
|
|
1583
1647
|
});
|
|
1584
1648
|
};
|
|
1585
|
-
var setup = () => {
|
|
1586
|
-
|
|
1587
|
-
|
|
1649
|
+
var setup = () => __async(void 0, null, function* () {
|
|
1650
|
+
if (!ifInBrowser2) {
|
|
1651
|
+
const dotenv = yield Promise.resolve().then(() => __toESM(require_main()));
|
|
1652
|
+
dotenv.config();
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1588
1655
|
var PlaygroundServer = class {
|
|
1589
1656
|
constructor() {
|
|
1590
1657
|
this.app = express();
|
|
1591
1658
|
this.tmpDir = getTmpDir();
|
|
1592
1659
|
setup();
|
|
1593
1660
|
}
|
|
1594
|
-
filePathForUuid(
|
|
1595
|
-
return join2(this.tmpDir, `${
|
|
1661
|
+
filePathForUuid(uuid2) {
|
|
1662
|
+
return join2(this.tmpDir, `${uuid2}.json`);
|
|
1596
1663
|
}
|
|
1597
|
-
saveContextFile(
|
|
1598
|
-
const tmpFile = this.filePathForUuid(
|
|
1664
|
+
saveContextFile(uuid2, context) {
|
|
1665
|
+
const tmpFile = this.filePathForUuid(uuid2);
|
|
1599
1666
|
console.log(`save context file: ${tmpFile}`);
|
|
1600
1667
|
writeFileSync(tmpFile, context);
|
|
1601
1668
|
return tmpFile;
|
|
1602
1669
|
}
|
|
1603
|
-
|
|
1604
|
-
this
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
this.app.get("/playground/:uuid", async (req, res) => {
|
|
1620
|
-
res.sendFile(join2(staticPath, "index.html"));
|
|
1621
|
-
});
|
|
1622
|
-
this.app.get("/context/:uuid", async (req, res) => {
|
|
1623
|
-
const { uuid } = req.params;
|
|
1624
|
-
const contextFile = this.filePathForUuid(uuid);
|
|
1625
|
-
assert3(existsSync2(contextFile), "Context not found");
|
|
1626
|
-
const context = readFileSync3(contextFile, "utf8");
|
|
1627
|
-
res.json({
|
|
1628
|
-
context
|
|
1670
|
+
launch() {
|
|
1671
|
+
return __async(this, null, function* () {
|
|
1672
|
+
this.app.use(errorHandler);
|
|
1673
|
+
this.app.use(
|
|
1674
|
+
cors({
|
|
1675
|
+
origin: "*",
|
|
1676
|
+
credentials: true
|
|
1677
|
+
})
|
|
1678
|
+
);
|
|
1679
|
+
this.app.get("/status", cors(), (req, res) => __async(this, null, function* () {
|
|
1680
|
+
res.send({
|
|
1681
|
+
status: "ok"
|
|
1682
|
+
});
|
|
1683
|
+
}));
|
|
1684
|
+
this.app.get("/", (req, res) => {
|
|
1685
|
+
res.sendFile(join2(staticPath, "index.html"));
|
|
1629
1686
|
});
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
const
|
|
1636
|
-
assert3(
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
location: `/playground/${uuid}`,
|
|
1641
|
-
uuid
|
|
1687
|
+
this.app.get("/playground/:uuid", (req, res) => __async(this, null, function* () {
|
|
1688
|
+
res.sendFile(join2(staticPath, "index.html"));
|
|
1689
|
+
}));
|
|
1690
|
+
this.app.get("/context/:uuid", (req, res) => __async(this, null, function* () {
|
|
1691
|
+
const { uuid: uuid2 } = req.params;
|
|
1692
|
+
const contextFile = this.filePathForUuid(uuid2);
|
|
1693
|
+
assert3(existsSync2(contextFile), "Context not found");
|
|
1694
|
+
const context = readFileSync3(contextFile, "utf8");
|
|
1695
|
+
res.json({
|
|
1696
|
+
context
|
|
1642
1697
|
});
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1698
|
+
}));
|
|
1699
|
+
this.app.post(
|
|
1700
|
+
"/playground-with-context",
|
|
1701
|
+
express.json({ limit: "50mb" }),
|
|
1702
|
+
(req, res) => __async(this, null, function* () {
|
|
1703
|
+
const context = req.body.context;
|
|
1704
|
+
assert3(context, "context is required");
|
|
1705
|
+
const uuid2 = randomUUID();
|
|
1706
|
+
this.saveContextFile(uuid2, context);
|
|
1707
|
+
return res.json({
|
|
1708
|
+
location: `/playground/${uuid2}`,
|
|
1709
|
+
uuid: uuid2
|
|
1710
|
+
});
|
|
1711
|
+
})
|
|
1712
|
+
);
|
|
1713
|
+
this.app.post(
|
|
1714
|
+
"/execute",
|
|
1715
|
+
express.json({ limit: "30mb" }),
|
|
1716
|
+
(req, res) => __async(this, null, function* () {
|
|
1717
|
+
const { context, type, prompt } = req.body;
|
|
1718
|
+
assert3(context, "context is required");
|
|
1719
|
+
assert3(type, "type is required");
|
|
1720
|
+
assert3(prompt, "prompt is required");
|
|
1721
|
+
const requestId = agentRequestCount++;
|
|
1722
|
+
console.log(`handle request: #${requestId}, ${type}, ${prompt}`);
|
|
1723
|
+
const page = new StaticPage(context);
|
|
1724
|
+
const agent = new StaticPageAgent(page);
|
|
1725
|
+
const response = {
|
|
1726
|
+
result: null,
|
|
1727
|
+
dump: null,
|
|
1728
|
+
error: null
|
|
1729
|
+
};
|
|
1730
|
+
try {
|
|
1731
|
+
if (type === "aiQuery") {
|
|
1732
|
+
response.result = yield agent.aiQuery(prompt);
|
|
1733
|
+
} else if (type === "aiAction") {
|
|
1734
|
+
response.result = yield agent.aiAction(prompt);
|
|
1735
|
+
} else if (type === "aiAssert") {
|
|
1736
|
+
response.result = yield agent.aiAssert(prompt, void 0, {
|
|
1737
|
+
keepRawResponse: true
|
|
1738
|
+
});
|
|
1739
|
+
} else {
|
|
1740
|
+
response.error = `Unknown type: ${type}`;
|
|
1741
|
+
}
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {
|
|
1744
|
+
response.error = error.message;
|
|
1745
|
+
}
|
|
1673
1746
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1747
|
+
try {
|
|
1748
|
+
response.dump = JSON.parse(agent.dumpDataString());
|
|
1749
|
+
agent.writeOutActionDumps();
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
console.error(
|
|
1752
|
+
`write out dump failed: #${requestId}, ${error.message}`
|
|
1753
|
+
);
|
|
1677
1754
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
`
|
|
1685
|
-
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
);
|
|
1697
|
-
return new Promise((resolve, reject) => {
|
|
1698
|
-
const port = this.port || defaultPort;
|
|
1699
|
-
this.server = this.app.listen(port, () => {
|
|
1700
|
-
this.port = port;
|
|
1701
|
-
resolve(this);
|
|
1755
|
+
res.send(response);
|
|
1756
|
+
if (response.error) {
|
|
1757
|
+
console.error(
|
|
1758
|
+
`handle request failed: #${requestId}, ${response.error}`
|
|
1759
|
+
);
|
|
1760
|
+
} else {
|
|
1761
|
+
console.log(`handle request done: #${requestId}`);
|
|
1762
|
+
}
|
|
1763
|
+
})
|
|
1764
|
+
);
|
|
1765
|
+
return new Promise((resolve, reject) => {
|
|
1766
|
+
const port = this.port || defaultPort;
|
|
1767
|
+
this.server = this.app.listen(port, () => {
|
|
1768
|
+
this.port = port;
|
|
1769
|
+
resolve(this);
|
|
1770
|
+
});
|
|
1702
1771
|
});
|
|
1703
1772
|
});
|
|
1704
1773
|
}
|