playwright-core 1.58.0-alpha-2025-12-10 → 1.58.0-alpha-2025-12-12
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/ThirdPartyNotices.txt +3 -3
- package/browsers.json +2 -2
- package/lib/client/browser.js +3 -5
- package/lib/client/browserType.js +2 -2
- package/lib/client/fetch.js +2 -4
- package/lib/client/page.js +4 -3
- package/lib/mcpBundleImpl/index.js +29 -29
- package/lib/protocol/serializers.js +5 -0
- package/lib/protocol/validator.js +26 -8
- package/lib/server/agent/actionRunner.js +33 -2
- package/lib/server/agent/agent.js +28 -12
- package/lib/server/agent/backend.js +2 -5
- package/lib/server/agent/codegen.js +83 -0
- package/lib/server/agent/context.js +43 -11
- package/lib/server/agent/tools.js +67 -5
- package/lib/server/artifact.js +1 -1
- package/lib/server/chromium/crPage.js +5 -5
- package/lib/server/codegen/javascript.js +6 -29
- package/lib/server/dispatchers/dispatcher.js +5 -8
- package/lib/server/dispatchers/pageDispatcher.js +3 -2
- package/lib/server/firefox/ffBrowser.js +1 -1
- package/lib/server/firefox/ffPage.js +1 -1
- package/lib/server/instrumentation.js +3 -0
- package/lib/server/page.js +2 -2
- package/lib/server/progress.js +2 -0
- package/lib/server/screencast.js +24 -25
- package/lib/server/videoRecorder.js +20 -11
- package/lib/server/webkit/wkBrowser.js +1 -1
- package/lib/server/webkit/wkPage.js +7 -7
- package/lib/utils/isomorphic/stringUtils.js +29 -0
- package/lib/vite/htmlReport/index.html +1 -1
- package/lib/vite/traceViewer/index.BVu7tZDe.css +1 -0
- package/lib/vite/traceViewer/index.html +2 -2
- package/lib/vite/traceViewer/index.zFV_GQE-.js +2 -0
- package/lib/vite/traceViewer/sw.bundle.js +3 -1
- package/package.json +1 -1
- package/types/types.d.ts +27 -2
- package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
- package/lib/vite/traceViewer/index.YskCIlQ-.js +0 -2
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var serializers_exports = {};
|
|
20
20
|
__export(serializers_exports, {
|
|
21
21
|
parseSerializedValue: () => parseSerializedValue,
|
|
22
|
+
serializePlainValue: () => serializePlainValue,
|
|
22
23
|
serializeValue: () => serializeValue
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(serializers_exports);
|
|
@@ -90,6 +91,9 @@ function innerParseSerializedValue(value, handles, refs, accessChain) {
|
|
|
90
91
|
function serializeValue(value, handleSerializer) {
|
|
91
92
|
return innerSerializeValue(value, handleSerializer, { lastId: 0, visited: /* @__PURE__ */ new Map() }, []);
|
|
92
93
|
}
|
|
94
|
+
function serializePlainValue(arg) {
|
|
95
|
+
return serializeValue(arg, (value) => ({ fallThrough: value }));
|
|
96
|
+
}
|
|
93
97
|
function innerSerializeValue(value, handleSerializer, visitorInfo, accessChain) {
|
|
94
98
|
const handle = handleSerializer(value);
|
|
95
99
|
if ("fallThrough" in handle)
|
|
@@ -188,5 +192,6 @@ const constructorToTypedArrayKind = new Map(Object.entries(typedArrayKindToConst
|
|
|
188
192
|
// Annotate the CommonJS export names for ESM import in node:
|
|
189
193
|
0 && (module.exports = {
|
|
190
194
|
parseSerializedValue,
|
|
195
|
+
serializePlainValue,
|
|
191
196
|
serializeValue
|
|
192
197
|
});
|
|
@@ -613,7 +613,9 @@ import_validatorPrimitives.scheme.BrowserTypeLaunchPersistentContextParams = (0,
|
|
|
613
613
|
model: import_validatorPrimitives.tString,
|
|
614
614
|
cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
615
615
|
cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
|
|
616
|
-
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
|
|
616
|
+
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
|
|
617
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
618
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
617
619
|
})),
|
|
618
620
|
userDataDir: import_validatorPrimitives.tString,
|
|
619
621
|
slowMo: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tFloat)
|
|
@@ -712,7 +714,9 @@ import_validatorPrimitives.scheme.BrowserNewContextParams = (0, import_validator
|
|
|
712
714
|
model: import_validatorPrimitives.tString,
|
|
713
715
|
cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
714
716
|
cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
|
|
715
|
-
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
|
|
717
|
+
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
|
|
718
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
719
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
716
720
|
})),
|
|
717
721
|
proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
|
|
718
722
|
server: import_validatorPrimitives.tString,
|
|
@@ -790,7 +794,9 @@ import_validatorPrimitives.scheme.BrowserNewContextForReuseParams = (0, import_v
|
|
|
790
794
|
model: import_validatorPrimitives.tString,
|
|
791
795
|
cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
792
796
|
cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
|
|
793
|
-
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
|
|
797
|
+
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
|
|
798
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
799
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
794
800
|
})),
|
|
795
801
|
proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
|
|
796
802
|
server: import_validatorPrimitives.tString,
|
|
@@ -913,7 +919,9 @@ import_validatorPrimitives.scheme.BrowserContextInitializer = (0, import_validat
|
|
|
913
919
|
model: import_validatorPrimitives.tString,
|
|
914
920
|
cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
915
921
|
cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
|
|
916
|
-
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
|
|
922
|
+
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
|
|
923
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
924
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
917
925
|
}))
|
|
918
926
|
})
|
|
919
927
|
});
|
|
@@ -1519,16 +1527,24 @@ import_validatorPrimitives.scheme.PageUpdateSubscriptionResult = (0, import_vali
|
|
|
1519
1527
|
import_validatorPrimitives.scheme.PagePerformParams = (0, import_validatorPrimitives.tObject)({
|
|
1520
1528
|
task: import_validatorPrimitives.tString,
|
|
1521
1529
|
key: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
1522
|
-
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
1530
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
1531
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
1532
|
+
});
|
|
1533
|
+
import_validatorPrimitives.scheme.PagePerformResult = (0, import_validatorPrimitives.tObject)({
|
|
1534
|
+
turns: import_validatorPrimitives.tInt,
|
|
1535
|
+
inputTokens: import_validatorPrimitives.tInt,
|
|
1536
|
+
outputTokens: import_validatorPrimitives.tInt
|
|
1523
1537
|
});
|
|
1524
|
-
import_validatorPrimitives.scheme.PagePerformResult = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
|
|
1525
1538
|
import_validatorPrimitives.scheme.PageExtractParams = (0, import_validatorPrimitives.tObject)({
|
|
1526
1539
|
query: import_validatorPrimitives.tString,
|
|
1527
1540
|
schema: import_validatorPrimitives.tAny,
|
|
1528
1541
|
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
1529
1542
|
});
|
|
1530
1543
|
import_validatorPrimitives.scheme.PageExtractResult = (0, import_validatorPrimitives.tObject)({
|
|
1531
|
-
result: import_validatorPrimitives.tAny
|
|
1544
|
+
result: import_validatorPrimitives.tAny,
|
|
1545
|
+
turns: import_validatorPrimitives.tInt,
|
|
1546
|
+
inputTokens: import_validatorPrimitives.tInt,
|
|
1547
|
+
outputTokens: import_validatorPrimitives.tInt
|
|
1532
1548
|
});
|
|
1533
1549
|
import_validatorPrimitives.scheme.FrameInitializer = (0, import_validatorPrimitives.tObject)({
|
|
1534
1550
|
url: import_validatorPrimitives.tString,
|
|
@@ -2823,7 +2839,9 @@ import_validatorPrimitives.scheme.AndroidDeviceLaunchBrowserParams = (0, import_
|
|
|
2823
2839
|
model: import_validatorPrimitives.tString,
|
|
2824
2840
|
cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
2825
2841
|
cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
|
|
2826
|
-
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
|
|
2842
|
+
secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
|
|
2843
|
+
maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
|
|
2844
|
+
maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
|
|
2827
2845
|
})),
|
|
2828
2846
|
pkg: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
|
|
2829
2847
|
args: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)(import_validatorPrimitives.tString)),
|
|
@@ -18,9 +18,12 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var actionRunner_exports = {};
|
|
20
20
|
__export(actionRunner_exports, {
|
|
21
|
-
runAction: () => runAction
|
|
21
|
+
runAction: () => runAction,
|
|
22
|
+
serializeArgument: () => serializeArgument
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(actionRunner_exports);
|
|
25
|
+
var import_expectUtils = require("../utils/expectUtils");
|
|
26
|
+
var import_serializers = require("../../protocol/serializers");
|
|
24
27
|
async function runAction(progress, page, action, secrets) {
|
|
25
28
|
const frame = page.mainFrame();
|
|
26
29
|
switch (action.method) {
|
|
@@ -59,10 +62,38 @@ async function runAction(progress, page, action, secrets) {
|
|
|
59
62
|
else
|
|
60
63
|
await frame.uncheck(progress, action.selector, { ...strictTrue });
|
|
61
64
|
break;
|
|
65
|
+
case "expectVisible": {
|
|
66
|
+
const result = await frame.expect(progress, action.selector, { expression: "to.be.visible", isNot: false }, 5e3);
|
|
67
|
+
if (result.errorMessage)
|
|
68
|
+
throw new Error(result.errorMessage);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "expectValue": {
|
|
72
|
+
let result;
|
|
73
|
+
if (action.type === "textbox" || action.type === "combobox" || action.type === "slider") {
|
|
74
|
+
const expectedText = (0, import_expectUtils.serializeExpectedTextValues)([action.value]);
|
|
75
|
+
result = await frame.expect(progress, action.selector, { expression: "to.have.value", expectedText, isNot: false }, 5e3);
|
|
76
|
+
} else if (action.type === "checkbox" || action.type === "radio") {
|
|
77
|
+
const expectedValue = serializeArgument({ checked: true });
|
|
78
|
+
result = await frame.expect(progress, action.selector, { expression: "to.be.checked", expectedValue, isNot: false }, 5e3);
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error(`Unsupported element type: ${action.type}`);
|
|
81
|
+
}
|
|
82
|
+
if (result.errorMessage)
|
|
83
|
+
throw new Error(result.errorMessage);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
62
86
|
}
|
|
63
87
|
}
|
|
88
|
+
function serializeArgument(arg) {
|
|
89
|
+
return {
|
|
90
|
+
value: (0, import_serializers.serializePlainValue)(arg),
|
|
91
|
+
handles: []
|
|
92
|
+
};
|
|
93
|
+
}
|
|
64
94
|
const strictTrue = { strict: true };
|
|
65
95
|
// Annotate the CommonJS export names for ESM import in node:
|
|
66
96
|
0 && (module.exports = {
|
|
67
|
-
runAction
|
|
97
|
+
runAction,
|
|
98
|
+
serializeArgument
|
|
68
99
|
});
|
|
@@ -41,11 +41,10 @@ var import_context = require("./context");
|
|
|
41
41
|
async function pagePerform(progress, page, options) {
|
|
42
42
|
const context = new import_context.Context(progress, page);
|
|
43
43
|
if (await cachedPerform(context, options))
|
|
44
|
-
return;
|
|
45
|
-
await perform(context, options.task,
|
|
46
|
-
error: import_mcpBundle.z.string().optional().describe("An error message if the task could not be completed successfully")
|
|
47
|
-
})), options);
|
|
44
|
+
return { turns: 0, inputTokens: 0, outputTokens: 0 };
|
|
45
|
+
const { usage } = await perform(context, options.task, void 0, options);
|
|
48
46
|
await updateCache(context, options);
|
|
47
|
+
return usage;
|
|
49
48
|
}
|
|
50
49
|
async function pageExtract(progress, page, options) {
|
|
51
50
|
const context = new import_context.Context(progress, page);
|
|
@@ -55,7 +54,8 @@ Extract the following information from the page. Do not perform any actions, jus
|
|
|
55
54
|
|
|
56
55
|
### Query
|
|
57
56
|
${options.query}`;
|
|
58
|
-
|
|
57
|
+
const { result, usage } = await perform(context, task, options.schema, options);
|
|
58
|
+
return { result, usage };
|
|
59
59
|
}
|
|
60
60
|
async function perform(context, userTask, resultSchema, options = {}) {
|
|
61
61
|
const { progress, page } = context;
|
|
@@ -64,12 +64,23 @@ async function perform(context, userTask, resultSchema, options = {}) {
|
|
|
64
64
|
throw new Error(`page.perform() and page.extract() require the agent to be set on the browser context`);
|
|
65
65
|
const { full } = await page.snapshotForAI(progress);
|
|
66
66
|
const { tools, callTool } = (0, import_backend.toolsForLoop)(context);
|
|
67
|
+
const limits = context.limits(options);
|
|
68
|
+
let turns = 0;
|
|
67
69
|
const loop = new import_mcpBundle.Loop(browserContext._options.agent.provider, {
|
|
68
70
|
model: browserContext._options.agent.model,
|
|
69
71
|
summarize: true,
|
|
70
72
|
debug: import_utilsBundle.debug,
|
|
71
73
|
callTool,
|
|
72
74
|
tools,
|
|
75
|
+
...limits,
|
|
76
|
+
beforeTurn: (params) => {
|
|
77
|
+
++turns;
|
|
78
|
+
const lastReply = params.conversation.messages.findLast((m) => m.role === "assistant");
|
|
79
|
+
const toolCall = lastReply?.content.find((c) => c.type === "tool_call");
|
|
80
|
+
if (!resultSchema && toolCall && toolCall.arguments.thatShouldBeIt)
|
|
81
|
+
return "break";
|
|
82
|
+
return "continue";
|
|
83
|
+
},
|
|
73
84
|
...options
|
|
74
85
|
});
|
|
75
86
|
const task = `${userTask}
|
|
@@ -77,16 +88,22 @@ async function perform(context, userTask, resultSchema, options = {}) {
|
|
|
77
88
|
### Page snapshot
|
|
78
89
|
${full}
|
|
79
90
|
`;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
const { result, usage } = await loop.run(task, { resultSchema });
|
|
92
|
+
return {
|
|
93
|
+
result,
|
|
94
|
+
usage: {
|
|
95
|
+
turns,
|
|
96
|
+
inputTokens: usage.input,
|
|
97
|
+
outputTokens: usage.output
|
|
98
|
+
}
|
|
99
|
+
};
|
|
83
100
|
}
|
|
84
101
|
const allCaches = /* @__PURE__ */ new Map();
|
|
85
102
|
async function cachedPerform(context, options) {
|
|
86
103
|
if (!context.options?.cacheFile || context.options.cacheMode === "ignore")
|
|
87
104
|
return false;
|
|
88
105
|
const cache = await cachedActions(context.options.cacheFile);
|
|
89
|
-
const cacheKey = options.key ?? options.task;
|
|
106
|
+
const cacheKey = (options.key ?? options.task).trim();
|
|
90
107
|
const entry = cache[cacheKey];
|
|
91
108
|
if (!entry) {
|
|
92
109
|
if (context.options.cacheMode === "force")
|
|
@@ -102,7 +119,7 @@ async function updateCache(context, options) {
|
|
|
102
119
|
if (!cacheFile)
|
|
103
120
|
return;
|
|
104
121
|
const cache = await cachedActions(cacheFile);
|
|
105
|
-
const cacheKey = options.key ?? options.task;
|
|
122
|
+
const cacheKey = (options.key ?? options.task).trim();
|
|
106
123
|
cache[cacheKey] = {
|
|
107
124
|
timestamp: Date.now(),
|
|
108
125
|
actions: context.actions
|
|
@@ -112,8 +129,7 @@ async function updateCache(context, options) {
|
|
|
112
129
|
async function cachedActions(cacheFile) {
|
|
113
130
|
let cache = allCaches.get(cacheFile);
|
|
114
131
|
if (!cache) {
|
|
115
|
-
|
|
116
|
-
cache = JSON.parse(text);
|
|
132
|
+
cache = await import_fs.default.promises.readFile(cacheFile, "utf-8").then((text) => JSON.parse(text)).catch(() => ({}));
|
|
117
133
|
allCaches.set(cacheFile, cache);
|
|
118
134
|
}
|
|
119
135
|
return cache;
|
|
@@ -32,7 +32,6 @@ __export(backend_exports, {
|
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(backend_exports);
|
|
34
34
|
var import_tools = __toESM(require("./tools"));
|
|
35
|
-
var import_progress = require("../progress");
|
|
36
35
|
var import_mcpBundle = require("../../mcpBundle");
|
|
37
36
|
function toolsForLoop(context) {
|
|
38
37
|
const tools = import_tools.default.map((tool) => {
|
|
@@ -44,6 +43,7 @@ function toolsForLoop(context) {
|
|
|
44
43
|
return result;
|
|
45
44
|
});
|
|
46
45
|
const callTool = async (params) => {
|
|
46
|
+
const intent = params.arguments._meta?.["dev.lowire/intent"];
|
|
47
47
|
const tool = import_tools.default.find((t) => t.schema.name === params.name);
|
|
48
48
|
if (!tool) {
|
|
49
49
|
return {
|
|
@@ -54,11 +54,8 @@ function toolsForLoop(context) {
|
|
|
54
54
|
isError: true
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
const progressController = new import_progress.ProgressController();
|
|
58
57
|
try {
|
|
59
|
-
return await
|
|
60
|
-
return await tool.handle(context, params.arguments);
|
|
61
|
-
});
|
|
58
|
+
return await context.callTool(tool, params.arguments, { intent });
|
|
62
59
|
} catch (error) {
|
|
63
60
|
return {
|
|
64
61
|
content: [{ type: "text", text: error.message }],
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var codegen_exports = {};
|
|
20
|
+
__export(codegen_exports, {
|
|
21
|
+
generateCode: () => generateCode
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(codegen_exports);
|
|
24
|
+
var import_locatorGenerators = require("../../utils/isomorphic/locatorGenerators");
|
|
25
|
+
var import_stringUtils = require("../../utils/isomorphic/stringUtils");
|
|
26
|
+
async function generateCode(sdkLanguage, action) {
|
|
27
|
+
switch (action.method) {
|
|
28
|
+
case "click": {
|
|
29
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
30
|
+
return `await page.${locator}.click(${(0, import_stringUtils.formatObjectOrVoid)(action.options)});`;
|
|
31
|
+
}
|
|
32
|
+
case "drag": {
|
|
33
|
+
const sourceLocator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.sourceSelector);
|
|
34
|
+
const targetLocator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.targetSelector);
|
|
35
|
+
return `await page.${sourceLocator}.dragAndDrop(${targetLocator});`;
|
|
36
|
+
}
|
|
37
|
+
case "hover": {
|
|
38
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
39
|
+
return `await page.${locator}.hover(${(0, import_stringUtils.formatObjectOrVoid)(action.options)});`;
|
|
40
|
+
}
|
|
41
|
+
case "pressKey": {
|
|
42
|
+
return `await page.keyboard.press(${(0, import_stringUtils.escapeWithQuotes)(action.key, "'")});`;
|
|
43
|
+
}
|
|
44
|
+
case "selectOption": {
|
|
45
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
46
|
+
return `await page.${locator}.selectOption(${action.labels.length === 1 ? (0, import_stringUtils.escapeWithQuotes)(action.labels[0]) : "[" + action.labels.map((label) => (0, import_stringUtils.escapeWithQuotes)(label)).join(", ") + "]"});`;
|
|
47
|
+
}
|
|
48
|
+
case "pressSequentially": {
|
|
49
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
50
|
+
const code = [`await page.${locator}.pressSequentially(${(0, import_stringUtils.escapeWithQuotes)(action.text)});`];
|
|
51
|
+
if (action.submit)
|
|
52
|
+
code.push(`await page.keyboard.press('Enter');`);
|
|
53
|
+
return code.join("\n");
|
|
54
|
+
}
|
|
55
|
+
case "fill": {
|
|
56
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
57
|
+
const code = [`await page.${locator}.fill(${(0, import_stringUtils.escapeWithQuotes)(action.text)});`];
|
|
58
|
+
if (action.submit)
|
|
59
|
+
code.push(`await page.keyboard.press('Enter');`);
|
|
60
|
+
return code.join("\n");
|
|
61
|
+
}
|
|
62
|
+
case "setChecked": {
|
|
63
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
64
|
+
if (action.checked)
|
|
65
|
+
return `await page.${locator}.check();`;
|
|
66
|
+
else
|
|
67
|
+
return `await page.${locator}.uncheck();`;
|
|
68
|
+
}
|
|
69
|
+
case "expectVisible": {
|
|
70
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
71
|
+
return `await expect(page.${locator}).toBeVisible();`;
|
|
72
|
+
}
|
|
73
|
+
case "expectValue": {
|
|
74
|
+
const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
|
|
75
|
+
return `await expect(page.${locator}).toHaveValue(${(0, import_stringUtils.escapeWithQuotes)(action.value)});`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error("Unknown action " + action.method);
|
|
79
|
+
}
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
generateCode
|
|
83
|
+
});
|
|
@@ -23,24 +23,39 @@ __export(context_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(context_exports);
|
|
24
24
|
var import_browserContext = require("../browserContext");
|
|
25
25
|
var import_actionRunner = require("./actionRunner");
|
|
26
|
+
var import_codegen = require("./codegen");
|
|
26
27
|
class Context {
|
|
27
28
|
constructor(progress, page) {
|
|
28
29
|
this.actions = [];
|
|
29
30
|
this.progress = progress;
|
|
30
31
|
this.page = page;
|
|
31
32
|
this.options = page.browserContext._options.agent;
|
|
33
|
+
this.sdkLanguage = page.browserContext._browser.sdkLanguage();
|
|
34
|
+
}
|
|
35
|
+
async callTool(tool, params, options) {
|
|
36
|
+
this._callIntent = options.intent;
|
|
37
|
+
try {
|
|
38
|
+
return await tool.handle(this, params);
|
|
39
|
+
} finally {
|
|
40
|
+
this._callIntent = void 0;
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
async runActionAndWait(action) {
|
|
34
44
|
return await this.runActionsAndWait([action]);
|
|
35
45
|
}
|
|
36
46
|
async runActionsAndWait(action) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
try {
|
|
48
|
+
await this.waitForCompletion(async () => {
|
|
49
|
+
for (const a of action) {
|
|
50
|
+
await (0, import_actionRunner.runAction)(this.progress, this.page, a, this.options?.secrets ?? []);
|
|
51
|
+
const code = await (0, import_codegen.generateCode)(this.sdkLanguage, a);
|
|
52
|
+
this.actions.push({ ...a, code, intent: this._callIntent });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return await this.snapshotResult();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return await this.snapshotResult(e);
|
|
58
|
+
}
|
|
44
59
|
}
|
|
45
60
|
async waitForCompletion(callback) {
|
|
46
61
|
const requests = [];
|
|
@@ -73,17 +88,28 @@ class Context {
|
|
|
73
88
|
await this.progress.wait(500);
|
|
74
89
|
return result;
|
|
75
90
|
}
|
|
76
|
-
async snapshotResult() {
|
|
91
|
+
async snapshotResult(error) {
|
|
77
92
|
let { full } = await this.page.snapshotForAI(this.progress);
|
|
78
93
|
full = this._redactText(full);
|
|
79
|
-
const text = [
|
|
80
|
-
|
|
94
|
+
const text = [];
|
|
95
|
+
if (error)
|
|
96
|
+
text.push(`# Error
|
|
97
|
+
${error.message}`);
|
|
98
|
+
else
|
|
99
|
+
text.push(`# Success`);
|
|
100
|
+
text.push(`# Page snapshot
|
|
101
|
+
${full}`);
|
|
81
102
|
return {
|
|
82
103
|
_meta: {
|
|
83
104
|
"dev.lowire/state": {
|
|
84
105
|
"Page snapshot": full
|
|
85
|
-
}
|
|
106
|
+
},
|
|
107
|
+
"dev.lowire/history": error ? [{
|
|
108
|
+
category: "error",
|
|
109
|
+
content: error.message
|
|
110
|
+
}] : []
|
|
86
111
|
},
|
|
112
|
+
isError: !!error,
|
|
87
113
|
content: [{ type: "text", text: text.join("\n\n") }]
|
|
88
114
|
};
|
|
89
115
|
}
|
|
@@ -97,6 +123,12 @@ ${full}`];
|
|
|
97
123
|
}
|
|
98
124
|
}));
|
|
99
125
|
}
|
|
126
|
+
limits(options = {}) {
|
|
127
|
+
return {
|
|
128
|
+
maxTurns: options.maxTurns ?? this.options?.maxTurns ?? 10,
|
|
129
|
+
maxTokens: options.maxTokens ?? this.options?.maxTokens ?? void 0
|
|
130
|
+
};
|
|
131
|
+
}
|
|
100
132
|
_redactText(text) {
|
|
101
133
|
const secrets = this.options?.secrets;
|
|
102
134
|
if (!secrets)
|
|
@@ -22,21 +22,25 @@ __export(tools_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(tools_exports);
|
|
24
24
|
var import_mcpBundle = require("../../mcpBundle");
|
|
25
|
+
var import_locatorUtils = require("../../utils/isomorphic/locatorUtils");
|
|
25
26
|
function defineTool(tool) {
|
|
26
27
|
return tool;
|
|
27
28
|
}
|
|
29
|
+
const baseSchema = import_mcpBundle.z.object({
|
|
30
|
+
thatShouldBeIt: import_mcpBundle.z.boolean().describe("Indicates that this tool call is sufficient to complete the task. If false, the task will continue with the next tool call")
|
|
31
|
+
});
|
|
28
32
|
const snapshot = defineTool({
|
|
29
33
|
schema: {
|
|
30
34
|
name: "browser_snapshot",
|
|
31
35
|
title: "Page snapshot",
|
|
32
36
|
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
|
33
|
-
inputSchema:
|
|
37
|
+
inputSchema: baseSchema
|
|
34
38
|
},
|
|
35
39
|
handle: async (context, params) => {
|
|
36
40
|
return await context.snapshotResult();
|
|
37
41
|
}
|
|
38
42
|
});
|
|
39
|
-
const elementSchema =
|
|
43
|
+
const elementSchema = baseSchema.extend({
|
|
40
44
|
element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),
|
|
41
45
|
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
42
46
|
});
|
|
@@ -70,7 +74,7 @@ const drag = defineTool({
|
|
|
70
74
|
name: "browser_drag",
|
|
71
75
|
title: "Drag mouse",
|
|
72
76
|
description: "Perform drag and drop between two elements",
|
|
73
|
-
inputSchema:
|
|
77
|
+
inputSchema: baseSchema.extend({
|
|
74
78
|
startElement: import_mcpBundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
|
75
79
|
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
|
76
80
|
endElement: import_mcpBundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
|
@@ -181,7 +185,7 @@ const fillForm = defineTool({
|
|
|
181
185
|
name: "browser_fill_form",
|
|
182
186
|
title: "Fill form",
|
|
183
187
|
description: "Fill multiple form fields",
|
|
184
|
-
inputSchema:
|
|
188
|
+
inputSchema: baseSchema.extend({
|
|
185
189
|
fields: import_mcpBundle.z.array(import_mcpBundle.z.object({
|
|
186
190
|
name: import_mcpBundle.z.string().describe("Human-readable field name"),
|
|
187
191
|
type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
|
|
@@ -217,6 +221,61 @@ const fillForm = defineTool({
|
|
|
217
221
|
return await context.runActionsAndWait(actions);
|
|
218
222
|
}
|
|
219
223
|
});
|
|
224
|
+
const expectVisible = defineTool({
|
|
225
|
+
schema: {
|
|
226
|
+
name: "browser_expect_visible",
|
|
227
|
+
title: "Expect element visible",
|
|
228
|
+
description: "Expect element is visible on the page",
|
|
229
|
+
inputSchema: baseSchema.extend({
|
|
230
|
+
role: import_mcpBundle.z.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
|
|
231
|
+
accessibleName: import_mcpBundle.z.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
|
|
232
|
+
})
|
|
233
|
+
},
|
|
234
|
+
handle: async (context, params) => {
|
|
235
|
+
return await context.runActionAndWait({
|
|
236
|
+
method: "expectVisible",
|
|
237
|
+
selector: (0, import_locatorUtils.getByRoleSelector)(params.role, { name: params.accessibleName })
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const expectVisibleText = defineTool({
|
|
242
|
+
schema: {
|
|
243
|
+
name: "browser_expect_visible_text",
|
|
244
|
+
title: "Expect text visible",
|
|
245
|
+
description: `Expect text is visible on the page. Prefer ${expectVisible.schema.name} if possible.`,
|
|
246
|
+
inputSchema: baseSchema.extend({
|
|
247
|
+
text: import_mcpBundle.z.string().describe('TEXT to expect. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
|
|
248
|
+
})
|
|
249
|
+
},
|
|
250
|
+
handle: async (context, params) => {
|
|
251
|
+
return await context.runActionAndWait({
|
|
252
|
+
method: "expectVisible",
|
|
253
|
+
selector: (0, import_locatorUtils.getByTextSelector)(params.text)
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
const expectValue = defineTool({
|
|
258
|
+
schema: {
|
|
259
|
+
name: "browser_expect_value",
|
|
260
|
+
title: "Expect value",
|
|
261
|
+
description: "Expect element value",
|
|
262
|
+
inputSchema: baseSchema.extend({
|
|
263
|
+
type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
|
|
264
|
+
element: import_mcpBundle.z.string().describe("Human-readable element description"),
|
|
265
|
+
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot"),
|
|
266
|
+
value: import_mcpBundle.z.string().describe('Value to expect. For checkbox, use "true" or "false".')
|
|
267
|
+
})
|
|
268
|
+
},
|
|
269
|
+
handle: async (context, params) => {
|
|
270
|
+
const [selector] = await context.refSelectors([{ ref: params.ref, element: params.element }]);
|
|
271
|
+
return await context.runActionAndWait({
|
|
272
|
+
method: "expectValue",
|
|
273
|
+
selector,
|
|
274
|
+
type: params.type,
|
|
275
|
+
value: params.value
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
});
|
|
220
279
|
var tools_default = [
|
|
221
280
|
snapshot,
|
|
222
281
|
click,
|
|
@@ -225,5 +284,8 @@ var tools_default = [
|
|
|
225
284
|
selectOption,
|
|
226
285
|
pressKey,
|
|
227
286
|
type,
|
|
228
|
-
fillForm
|
|
287
|
+
fillForm,
|
|
288
|
+
expectVisible,
|
|
289
|
+
expectVisibleText,
|
|
290
|
+
expectValue
|
|
229
291
|
];
|
package/lib/server/artifact.js
CHANGED
|
@@ -103,7 +103,7 @@ class Artifact extends import_instrumentation.SdkObject {
|
|
|
103
103
|
if (!this._unaccessibleErrorMessage)
|
|
104
104
|
await import_fs.default.promises.unlink(this._localPath).catch((e) => {
|
|
105
105
|
});
|
|
106
|
-
await this.reportFinished(new import_errors.TargetClosedError());
|
|
106
|
+
await this.reportFinished(new import_errors.TargetClosedError(this.closeReason()));
|
|
107
107
|
}
|
|
108
108
|
async reportFinished(error) {
|
|
109
109
|
if (this._finished)
|
|
@@ -357,9 +357,9 @@ class FrameSession {
|
|
|
357
357
|
const { windowId } = await this._client.send("Browser.getWindowForTarget");
|
|
358
358
|
this._windowId = windowId;
|
|
359
359
|
}
|
|
360
|
-
let
|
|
360
|
+
let videoOptions;
|
|
361
361
|
if (!this._page.isStorageStatePage && this._isMainFrame() && hasUIWindow)
|
|
362
|
-
|
|
362
|
+
videoOptions = this._crPage._page.screencast.launchVideoRecorder();
|
|
363
363
|
let lifecycleEventsEnabled;
|
|
364
364
|
if (!this._isMainFrame())
|
|
365
365
|
this._addRendererListeners();
|
|
@@ -439,15 +439,15 @@ class FrameSession {
|
|
|
439
439
|
true
|
|
440
440
|
/* runImmediately */
|
|
441
441
|
));
|
|
442
|
-
if (
|
|
443
|
-
promises.push(this._crPage._page.screencast.startVideoRecording(
|
|
442
|
+
if (videoOptions)
|
|
443
|
+
promises.push(this._crPage._page.screencast.startVideoRecording(videoOptions));
|
|
444
444
|
}
|
|
445
445
|
promises.push(this._client.send("Runtime.runIfWaitingForDebugger"));
|
|
446
446
|
promises.push(this._firstNonInitialNavigationCommittedPromise);
|
|
447
447
|
await Promise.all(promises);
|
|
448
448
|
}
|
|
449
449
|
dispose() {
|
|
450
|
-
this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError());
|
|
450
|
+
this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError(this._page.closeReason()));
|
|
451
451
|
for (const childSession of this._childSessions)
|
|
452
452
|
childSession.dispose();
|
|
453
453
|
if (this._parentSession)
|