@chen-rmag/ai-runner 0.1.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.
Files changed (102) hide show
  1. package/README.md +263 -0
  2. package/SUMMARY_USAGE.md +359 -0
  3. package/TOOLS_INTEGRATION_SUMMARY.md +206 -0
  4. package/dist/agents/error-analyzer.d.ts +62 -0
  5. package/dist/agents/error-analyzer.d.ts.map +1 -0
  6. package/dist/agents/error-analyzer.js +168 -0
  7. package/dist/agents/error-analyzer.js.map +1 -0
  8. package/dist/agents/heal-agent.d.ts +30 -0
  9. package/dist/agents/heal-agent.d.ts.map +1 -0
  10. package/dist/agents/heal-agent.js +76 -0
  11. package/dist/agents/heal-agent.js.map +1 -0
  12. package/dist/agents/healer.d.ts +73 -0
  13. package/dist/agents/healer.d.ts.map +1 -0
  14. package/dist/agents/healer.js +538 -0
  15. package/dist/agents/healer.js.map +1 -0
  16. package/dist/agents/langgraph-agent.d.ts +44 -0
  17. package/dist/agents/langgraph-agent.d.ts.map +1 -0
  18. package/dist/agents/langgraph-agent.js +328 -0
  19. package/dist/agents/langgraph-agent.js.map +1 -0
  20. package/dist/agents/react-agent.d.ts +52 -0
  21. package/dist/agents/react-agent.d.ts.map +1 -0
  22. package/dist/agents/react-agent.js +262 -0
  23. package/dist/agents/react-agent.js.map +1 -0
  24. package/dist/agents/tools/form.d.ts +22 -0
  25. package/dist/agents/tools/form.d.ts.map +1 -0
  26. package/dist/agents/tools/form.js +134 -0
  27. package/dist/agents/tools/form.js.map +1 -0
  28. package/dist/agents/tools/index.d.ts +13 -0
  29. package/dist/agents/tools/index.d.ts.map +1 -0
  30. package/dist/agents/tools/index.js +33 -0
  31. package/dist/agents/tools/index.js.map +1 -0
  32. package/dist/agents/tools/navigate.d.ts +22 -0
  33. package/dist/agents/tools/navigate.d.ts.map +1 -0
  34. package/dist/agents/tools/navigate.js +74 -0
  35. package/dist/agents/tools/navigate.js.map +1 -0
  36. package/dist/agents/tools/snapshot.d.ts +22 -0
  37. package/dist/agents/tools/snapshot.d.ts.map +1 -0
  38. package/dist/agents/tools/snapshot.js +110 -0
  39. package/dist/agents/tools/snapshot.js.map +1 -0
  40. package/dist/agents/tools/verify.d.ts +34 -0
  41. package/dist/agents/tools/verify.d.ts.map +1 -0
  42. package/dist/agents/tools/verify.js +169 -0
  43. package/dist/agents/tools/verify.js.map +1 -0
  44. package/dist/agents/tools/wait.d.ts +22 -0
  45. package/dist/agents/tools/wait.d.ts.map +1 -0
  46. package/dist/agents/tools/wait.js +104 -0
  47. package/dist/agents/tools/wait.js.map +1 -0
  48. package/dist/agents/types.d.ts +51 -0
  49. package/dist/agents/types.d.ts.map +1 -0
  50. package/dist/agents/types.js +6 -0
  51. package/dist/agents/types.js.map +1 -0
  52. package/dist/core/ai-heal.d.ts +89 -0
  53. package/dist/core/ai-heal.d.ts.map +1 -0
  54. package/dist/core/ai-heal.js +468 -0
  55. package/dist/core/ai-heal.js.map +1 -0
  56. package/dist/core/execution-engine.d.ts +16 -0
  57. package/dist/core/execution-engine.d.ts.map +1 -0
  58. package/dist/core/execution-engine.js +44 -0
  59. package/dist/core/execution-engine.js.map +1 -0
  60. package/dist/core/runner.d.ts +195 -0
  61. package/dist/core/runner.d.ts.map +1 -0
  62. package/dist/core/runner.js +658 -0
  63. package/dist/core/runner.js.map +1 -0
  64. package/dist/index.d.ts +8 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +11 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/types/external.d.ts +6 -0
  69. package/dist/types/external.d.ts.map +1 -0
  70. package/dist/types/external.js +7 -0
  71. package/dist/types/external.js.map +1 -0
  72. package/dist/types/index.d.ts +153 -0
  73. package/dist/types/index.d.ts.map +1 -0
  74. package/dist/types/index.js +26 -0
  75. package/dist/types/index.js.map +1 -0
  76. package/dist/utils/object-registry.d.ts +48 -0
  77. package/dist/utils/object-registry.d.ts.map +1 -0
  78. package/dist/utils/object-registry.js +133 -0
  79. package/dist/utils/object-registry.js.map +1 -0
  80. package/package.json +37 -0
  81. package/playwright.config.ts +38 -0
  82. package/src/agents/heal-agent.ts +85 -0
  83. package/src/agents/healer.ts +619 -0
  84. package/src/agents/tools/EXAMPLES.md +347 -0
  85. package/src/agents/tools/README.md +207 -0
  86. package/src/agents/tools/form.ts +138 -0
  87. package/src/agents/tools/index.ts +29 -0
  88. package/src/agents/tools/navigate.ts +69 -0
  89. package/src/agents/tools/snapshot.ts +109 -0
  90. package/src/agents/tools/verify.ts +168 -0
  91. package/src/agents/tools/wait.ts +103 -0
  92. package/src/agents/types.ts +79 -0
  93. package/src/core/runner.ts +756 -0
  94. package/src/index.ts +29 -0
  95. package/src/types/external.ts +7 -0
  96. package/src/types/index.ts +200 -0
  97. package/tests/agent/test-heal-agent.spec.ts +81 -0
  98. package/tests/tools/README.md +227 -0
  99. package/tests/tools/TEST_SUMMARY.md +214 -0
  100. package/tests/tools/quick-test.ts +88 -0
  101. package/tests/tools/tools.test.ts +491 -0
  102. package/tsconfig.json +22 -0
@@ -0,0 +1,347 @@
1
+ # LangChain Playwright 工具使用示例
2
+
3
+ ## 完整示例:在 Healer 中使用工具
4
+
5
+ ```typescript
6
+ import { Page } from 'playwright';
7
+ import { createAgent } from '@langchain/core';
8
+ import { getAllTools } from './tools';
9
+
10
+ class Healer {
11
+ private llm: BaseChatModel;
12
+ private page: Page;
13
+
14
+ constructor(llm: BaseChatModel, page: Page) {
15
+ this.llm = llm;
16
+ this.page = page;
17
+ }
18
+
19
+ async execute(objective: string) {
20
+ // 获取所有工具
21
+ const tools = getAllTools(this.page);
22
+
23
+ // 创建 Agent
24
+ const agent = createAgent({
25
+ model: this.llm,
26
+ tools: tools,
27
+ systemPrompt: `
28
+ 你是一个浏览器自动化专家。
29
+ 使用提供的工具完成用户的目标。
30
+
31
+ 可用工具包括:
32
+ - 导航工具:browser_navigate, browser_navigate_back
33
+ - 交互工具:browser_click, browser_hover
34
+ - 表单工具:browser_fill, browser_select_option
35
+ - 等待工具:browser_wait_for_selector, browser_wait_for_time
36
+ - 验证工具:browser_get_url, browser_screenshot, browser_verify_text
37
+
38
+ 请根据目标选择合适的工具。
39
+ `
40
+ });
41
+
42
+ // 执行 Agent
43
+ const result = await agent.invoke({
44
+ messages: [{
45
+ role: 'user',
46
+ content: objective
47
+ }]
48
+ });
49
+
50
+ return result;
51
+ }
52
+ }
53
+
54
+ // 使用示例
55
+ const page = await browser.newPage();
56
+ const healer = new Healer(llm, page);
57
+ await healer.execute('打开 example.com 并点击第一个链接');
58
+ ```
59
+
60
+ ## 单独使用工具
61
+
62
+ ### 导航工具
63
+
64
+ ```typescript
65
+ import { createNavigateTool, createGoBackTool } from './tools/navigate';
66
+
67
+ const navigateTool = createNavigateTool(page);
68
+ const result = await navigateTool.invoke('https://example.com');
69
+ console.log(result); // "Navigated to https://example.com"
70
+
71
+ const goBackTool = createGoBackTool(page);
72
+ await goBackTool.invoke('');
73
+ ```
74
+
75
+ ### 交互工具
76
+
77
+ ```typescript
78
+ import { createClickTool, createHoverTool } from './tools/snapshot';
79
+
80
+ const clickTool = createClickTool(page);
81
+
82
+ // 点击按钮
83
+ await clickTool.invoke('button');
84
+
85
+ // 使用多个选择器
86
+ await clickTool.invoke('button[type="submit"], #submit, .submit-btn');
87
+
88
+ const hoverTool = createHoverTool(page);
89
+ await hoverTool.invoke('nav.menu');
90
+ ```
91
+
92
+ ### 表单工具
93
+
94
+ ```typescript
95
+ import {
96
+ createFillFormTool,
97
+ createSelectOptionTool,
98
+ createSetCheckedTool
99
+ } from './tools/form';
100
+
101
+ const fillTool = createFillFormTool(page);
102
+ await fillTool.invoke('{"selector": "#username", "value": "john"}');
103
+
104
+ const selectTool = createSelectOptionTool(page);
105
+ await selectTool.invoke('{"selector": "select#country", "value": "China"}');
106
+
107
+ const checkTool = createSetCheckedTool(page);
108
+ await checkTool.invoke('{"selector": "#agree", "checked": true}');
109
+ ```
110
+
111
+ ### 等待工具
112
+
113
+ ```typescript
114
+ import {
115
+ createWaitForTimeTool,
116
+ createWaitForSelectorTool,
117
+ createWaitForURLTool
118
+ } from './tools/wait';
119
+
120
+ const waitTimeTool = createWaitForTimeTool(page);
121
+ await waitTimeTool.invoke('5000'); // 等待 5 秒
122
+
123
+ const waitSelectorTool = createWaitForSelectorTool(page);
124
+ await waitSelectorTool.invoke('button, timeout=5000'); // 等待按钮出现,超时 5 秒
125
+
126
+ const waitURLTool = createWaitForURLTool(page);
127
+ await waitURLTool.invoke('**/login'); // 等待 URL 匹配
128
+ ```
129
+
130
+ ### 验证工具
131
+
132
+ ```typescript
133
+ import {
134
+ createGetURLTool,
135
+ createGetTextTool,
136
+ createScreenshotTool,
137
+ createVerifyTextVisibleTool
138
+ } from './tools/verify';
139
+
140
+ const getURLTool = createGetURLTool(page);
141
+ const url = await getURLTool.invoke('');
142
+ console.log(url); // "Current URL: https://example.com"
143
+
144
+ const getTextTool = createGetTextTool(page);
145
+ const text = await getTextTool.invoke('h1');
146
+ console.log(text); // "Text from element "h1": Example Domain"
147
+
148
+ const screenshotTool = createScreenshotTool(page);
149
+ await screenshotTool.invoke(''); // 保存截图
150
+
151
+ const verifyTool = createVerifyTextVisibleTool(page);
152
+ await verifyTool.invoke('Welcome'); // 验证文本可见
153
+ ```
154
+
155
+ ## 在现有 Healer 中集成
156
+
157
+ ### 替换现有的 createPlaywrightTools
158
+
159
+ ```typescript
160
+ // healer.ts
161
+ import { getAllTools } from './tools';
162
+
163
+ class Healer {
164
+ private createPlaywrightTools(page: Page): DynamicTool[] {
165
+ // 使用新的工具集
166
+ return getAllTools(page);
167
+ }
168
+
169
+ async execute(state: HealAgentState) {
170
+ const page = state.context.page;
171
+
172
+ // 获取工具
173
+ const tools = this.createPlaywrightTools(page);
174
+
175
+ // 创建 Agent
176
+ const agent = createAgent({
177
+ model: this.llm,
178
+ tools: tools,
179
+ systemPrompt: this.buildSystemPrompt(state)
180
+ });
181
+
182
+ // 执行...
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### 按需选择工具
188
+
189
+ ```typescript
190
+ import { navigateTools, snapshotTools, waitTools } from './tools';
191
+
192
+ class Healer {
193
+ private createMinimalTools(page: Page): DynamicTool[] {
194
+ // 只使用必要的工具
195
+ return [
196
+ ...navigateTools(page),
197
+ ...snapshotTools(page).slice(0, 2), // 只使用 snapshot 和 click
198
+ ...waitTools(page).slice(0, 1), // 只使用 wait_for_time
199
+ ];
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## 高级用法
205
+
206
+ ### 自定义工具组合
207
+
208
+ ```typescript
209
+ import {
210
+ createNavigateTool,
211
+ createClickTool,
212
+ createFillFormTool,
213
+ createWaitForSelectorTool
214
+ } from './tools';
215
+
216
+ const customToolSet = (page: Page) => [
217
+ createNavigateTool(page),
218
+ createClickTool(page),
219
+ createFillFormTool(page),
220
+ createWaitForSelectorTool(page),
221
+ ];
222
+ ```
223
+
224
+ ### 包装工具以添加日志
225
+
226
+ ```typescript
227
+ import { createClickTool } from './tools/snapshot';
228
+
229
+ function createClickToolWithLogging(page: Page) {
230
+ const tool = createClickTool(page);
231
+
232
+ return new DynamicTool({
233
+ name: tool.name,
234
+ description: tool.description,
235
+ func: async (input: string) => {
236
+ console.log(`[Tool] Calling ${tool.name} with input: ${input}`);
237
+ const startTime = Date.now();
238
+
239
+ try {
240
+ const result = await tool.invoke(input);
241
+ const duration = Date.now() - startTime;
242
+ console.log(`[Tool] ${tool.name} completed in ${duration}ms`);
243
+ return result;
244
+ } catch (error) {
245
+ console.error(`[Tool] ${tool.name} failed:`, error);
246
+ throw error;
247
+ }
248
+ }
249
+ });
250
+ }
251
+ ```
252
+
253
+ ### 组合多个工具
254
+
255
+ ```typescript
256
+ import { createClickTool, createFillFormTool } from './tools';
257
+
258
+ async function fillAndSubmitForm(page: Page) {
259
+ const fillTool = createFillFormTool(page);
260
+ const clickTool = createClickTool(page);
261
+
262
+ // 填写表单
263
+ await fillTool.invoke('{"selector": "#email", "value": "test@example.com"}');
264
+ await fillTool.invoke('{"selector": "#password", "value": "secret"}');
265
+
266
+ // 点击提交按钮
267
+ await clickTool.invoke('button[type="submit"]');
268
+ }
269
+ ```
270
+
271
+ ## 错误处理
272
+
273
+ ```typescript
274
+ import { createClickTool } from './tools/snapshot';
275
+
276
+ async function safeClick(page: Page, selector: string) {
277
+ const tool = createClickTool(page);
278
+
279
+ try {
280
+ const result = await tool.invoke(selector);
281
+ console.log('Success:', result);
282
+ } catch (error) {
283
+ console.error('Failed to click:', (error as Error).message);
284
+
285
+ // 尝试备选方案
286
+ console.log('Trying alternative selector...');
287
+ await tool.wait(1000); // 等待一下
288
+ await tool.invoke(selector);
289
+ }
290
+ }
291
+ ```
292
+
293
+ ## 性能优化
294
+
295
+ ```typescript
296
+ import { getAllTools } from './tools';
297
+
298
+ // 缓存工具实例
299
+ class ToolCache {
300
+ private tools: Map<Page, DynamicTool[]> = new Map();
301
+
302
+ get(page: Page): DynamicTool[] {
303
+ if (!this.tools.has(page)) {
304
+ this.tools.set(page, getAllTools(page));
305
+ }
306
+ return this.tools.get(page)!;
307
+ }
308
+ }
309
+
310
+ const cache = new ToolCache();
311
+ const tools = cache.get(page);
312
+ ```
313
+
314
+ ## 调试技巧
315
+
316
+ ```typescript
317
+ import { createClickTool } from './tools';
318
+
319
+ // 添加详细日志
320
+ async function debugClick(page: Page) {
321
+ const tool = createClickTool(page);
322
+
323
+ console.log('Available tools:', [tool.name]);
324
+ console.log('Tool description:', tool.description);
325
+
326
+ const selectors = ['button', 'a', 'input[type="submit"]'];
327
+
328
+ for (const selector of selectors) {
329
+ console.log(`Trying selector: ${selector}`);
330
+ try {
331
+ const result = await tool.invoke(selector);
332
+ console.log(`✓ Success with "${selector}":`, result);
333
+ break;
334
+ } catch (error) {
335
+ console.log(`✗ Failed with "${selector}"`);
336
+ }
337
+ }
338
+ }
339
+ ```
340
+
341
+ ## 注意事项
342
+
343
+ 1. **超时处理**:大多数工具有默认超时,注意处理超时异常
344
+ 2. **选择器优先级**:工具会按优先级尝试不同类型的选择器
345
+ 3. **返回值**:所有工具返回字符串格式的结果
346
+ 4. **JSON 输入**:某些工具需要 JSON 格式的输入,注意格式正确性
347
+ 5. **错误传播**:工具失败会抛出异常,需要在外部捕获处理
@@ -0,0 +1,207 @@
1
+ # LangChain 兼容的 Playwright 工具
2
+
3
+ 这是从 Playwright MCP 工具转换而来的 LangChain 兼容工具集,专门用于自愈 Agent。
4
+
5
+ ## 工具分类
6
+
7
+ ### 1. 导航工具 (`navigate.ts`)
8
+
9
+ - **browser_navigate** - 导航到指定 URL
10
+ - **browser_navigate_back** - 返回上一页
11
+ - **browser_navigate_forward** - 前进到下一页
12
+
13
+ ### 2. 快照和交互工具 (`snapshot.ts`)
14
+
15
+ - **browser_snapshot** - 获取页面的可访问性快照
16
+ - **browser_click** - 点击元素(支持多个选择器)
17
+ - **browser_hover** - 悬停在元素上
18
+
19
+ ### 3. 表单工具 (`form.ts`)
20
+
21
+ - **browser_fill** - 填写表单字段
22
+ - **browser_select_option** - 选择下拉选项
23
+ - **browser_set_checked** - 勾选/取消复选框
24
+
25
+ ### 4. 等待工具 (`wait.ts`)
26
+
27
+ - **browser_wait_for_time** - 等待指定时间
28
+ - **browser_wait_for_selector** - 等待元素出现
29
+ - **browser_wait_for_url** - 等待 URL 匹配
30
+
31
+ ### 5. 验证工具 (`verify.ts`)
32
+
33
+ - **browser_get_url** - 获取当前 URL
34
+ - **browser_get_content** - 获取页面内容
35
+ - **browser_get_text** - 获取元素文本
36
+ - **browser_screenshot** - 截取页面截图
37
+ - **browser_verify_text** - 验证文本可见
38
+ - **browser_verify_element** - 验证元素可见
39
+
40
+ ## 使用方法
41
+
42
+ ### 基本用法
43
+
44
+ ```typescript
45
+ import { Page } from 'playwright';
46
+ import { allTools } from './tools';
47
+
48
+ const page: Page = await browser.newPage();
49
+
50
+ // 获取所有工具
51
+ const tools = allTools(page);
52
+
53
+ // 在 LangChain Agent 中使用
54
+ import { createReactAgent } from 'langchain/agents';
55
+
56
+ const agent = createReactAgent({
57
+ llm: yourLLM,
58
+ tools: tools,
59
+ verbose: true
60
+ });
61
+ ```
62
+
63
+ ### 按需导入
64
+
65
+ ```typescript
66
+ // 只导入导航工具
67
+ import { navigateTools } from './tools/navigate';
68
+
69
+ const tools = navigateTools(page);
70
+
71
+ // 只导入交互工具
72
+ import { snapshotTools } from './tools/snapshot';
73
+
74
+ const tools = snapshotTools(page);
75
+ ```
76
+
77
+ ## 工具特性
78
+
79
+ ### 1. 多选择器支持
80
+
81
+ 大多数交互工具支持多个选择器(用逗号分隔):
82
+
83
+ ```typescript
84
+ // 工具会依次尝试每个选择器,直到成功
85
+ await clickTool.run('button, input[type="submit"], #submit-btn');
86
+ ```
87
+
88
+ ### 2. 智能选择器匹配
89
+
90
+ 工具会自动尝试不同类型的选择器:
91
+
92
+ 1. **文本选择器** - 通过文本内容查找元素
93
+ 2. **标签选择器** - 通过 label 关联查找表单元素
94
+ 3. **CSS 选择器** - 使用标准 CSS 选择器
95
+
96
+ ### 3. 错误处理
97
+
98
+ 所有工具都有完善的错误处理:
99
+
100
+ ```typescript
101
+ try {
102
+ const result = await clickTool.run('button');
103
+ console.log(result);
104
+ } catch (error) {
105
+ console.error('Tool failed:', error.message);
106
+ }
107
+ ```
108
+
109
+ ## 与自愈 Agent 集成
110
+
111
+ 在 Healer 中使用这些工具:
112
+
113
+ ```typescript
114
+ import { Healer } from './healer';
115
+ import { allTools } from './tools';
116
+
117
+ class Healer {
118
+ private createTools(page: Page): DynamicTool[] {
119
+ return allTools(page);
120
+ }
121
+
122
+ async execute(state: HealAgentState) {
123
+ const tools = this.createTools(state.context.page);
124
+
125
+ const agent = createAgent({
126
+ model: this.llm,
127
+ tools: tools,
128
+ systemPrompt: this.buildSystemPrompt(state)
129
+ });
130
+
131
+ // ... 执行 Agent
132
+ }
133
+ }
134
+ ```
135
+
136
+ ## 工具输入格式
137
+
138
+ ### JSON 格式(用于复杂参数)
139
+
140
+ ```typescript
141
+ // 填写表单
142
+ await fillTool.run('{"selector": "#username", "value": "john"}');
143
+
144
+ // 选择选项
145
+ await selectOptionTool.run('{"selector": "select#country", "value": "China"}');
146
+
147
+ // 勾选复选框
148
+ await setCheckedTool.run('{"selector": "#agree", "checked": true}');
149
+ ```
150
+
151
+ ### 带参数的选择器
152
+
153
+ ```typescript
154
+ // 带超时的等待
155
+ await waitForSelectorTool.run('button, timeout=5000');
156
+ ```
157
+
158
+ ### 简单字符串输入
159
+
160
+ ```typescript
161
+ // 导航
162
+ await navigateTool.run('https://example.com');
163
+
164
+ // 点击
165
+ await clickTool.run('button');
166
+
167
+ // 获取文本
168
+ await getTextTool.run('h1');
169
+ ```
170
+
171
+ ## 从 MCP 转换说明
172
+
173
+ 这些工具是从 Playwright MCP 工具转换而来,主要变化:
174
+
175
+ 1. **移除了复杂的上下文系统** - 直接使用 Playwright Page 对象
176
+ 2. **简化了输入格式** - 使用简单的字符串或 JSON,而不是 Zod schema
177
+ 3. **LangChain 集成** - 使用 `DynamicTool` 包装,兼容 LangChain Agent
178
+ 4. **错误处理改进** - 统一的错误处理和消息格式
179
+ 5. **多选择器支持** - 增强的元素查找能力
180
+
181
+ ## 注意事项
182
+
183
+ 1. **超时设置** - 大多数工具有默认的超时时间(通常 30 秒)
184
+ 2. **元素查找** - 工具会按优先级尝试不同类型的选择器
185
+ 3. **返回值** - 所有工具返回字符串格式的结果消息
186
+ 4. **异常处理** - 工具执行失败会抛出异常,需要在外部处理
187
+
188
+ ## 扩展工具
189
+
190
+ 如果需要添加新工具:
191
+
192
+ ```typescript
193
+ import { DynamicTool } from '@langchain/core/tools';
194
+
195
+ export function createYourTool(page: Page): DynamicTool {
196
+ return new DynamicTool({
197
+ name: 'browser_your_tool',
198
+ description: 'Your tool description',
199
+ func: async (input: string) => {
200
+ // 实现你的逻辑
201
+ return 'result message';
202
+ }
203
+ });
204
+ }
205
+ ```
206
+
207
+ 然后在对应的分类文件中导出,并在 `index.ts` 中注册。
@@ -0,0 +1,138 @@
1
+ /**
2
+ * 表单工具 - Form
3
+ */
4
+
5
+ import { DynamicTool } from '@langchain/core/tools';
6
+ import type { Page } from 'playwright';
7
+
8
+ /**
9
+ * 创建填写表单工具
10
+ */
11
+ export function createFillFormTool(page: Page): DynamicTool {
12
+ return new DynamicTool({
13
+ name: 'browser_fill',
14
+ description: 'Fill in a form field. Input should be in JSON format: {"selector": "css-selector-or-text", "value": "value-to-fill"}. For checkboxes, use "true" or "false" as the value.',
15
+ func: async (input: string) => {
16
+ try {
17
+ const params = JSON.parse(input);
18
+ const { selector, value } = params;
19
+
20
+ // 尝试多个选择器
21
+ const selectors = selector.split(',').map((s: string) => s.trim());
22
+
23
+ for (const sel of selectors) {
24
+ try {
25
+ // 尝试作为文本选择器
26
+ const locator = page.getByLabel(sel).or(page.getByPlaceholder(sel));
27
+ const count = await locator.count();
28
+ if (count > 0) {
29
+ await locator.fill(value);
30
+ return `Filled field with label: "${sel}" with value: "${value}"`;
31
+ }
32
+
33
+ // 尝试作为 CSS 选择器
34
+ await page.fill(sel, value);
35
+ return `Filled field: "${sel}" with value: "${value}"`;
36
+ } catch {
37
+ continue;
38
+ }
39
+ }
40
+
41
+ throw new Error(`No field found matching: ${selector}`);
42
+ } catch (error) {
43
+ throw new Error(`Failed to fill form: ${(error as Error).message}`);
44
+ }
45
+ }
46
+ });
47
+ }
48
+
49
+ /**
50
+ * 创建选择选项工具
51
+ */
52
+ export function createSelectOptionTool(page: Page): DynamicTool {
53
+ return new DynamicTool({
54
+ name: 'browser_select_option',
55
+ description: 'Select an option in a dropdown. Input should be in JSON format: {"selector": "css-selector-or-text", "value": "option-value"}.',
56
+ func: async (input: string) => {
57
+ try {
58
+ const params = JSON.parse(input);
59
+ const { selector, value } = params;
60
+
61
+ // 尝试多个选择器
62
+ const selectors = selector.split(',').map((s: string) => s.trim());
63
+
64
+ for (const sel of selectors) {
65
+ try {
66
+ // 尝试作为文本选择器
67
+ const locator = page.getByLabel(sel);
68
+ const count = await locator.count();
69
+ if (count > 0) {
70
+ await locator.selectOption(value);
71
+ return `Selected option "${value}" in dropdown with label: "${sel}"`;
72
+ }
73
+
74
+ // 尝试作为 CSS 选择器
75
+ await page.selectOption(sel, value);
76
+ return `Selected option "${value}" in dropdown: "${sel}"`;
77
+ } catch {
78
+ continue;
79
+ }
80
+ }
81
+
82
+ throw new Error(`No dropdown found matching: ${selector}`);
83
+ } catch (error) {
84
+ throw new Error(`Failed to select option: ${(error as Error).message}`);
85
+ }
86
+ }
87
+ });
88
+ }
89
+
90
+ /**
91
+ * 创建复选框工具
92
+ */
93
+ export function createSetCheckedTool(page: Page): DynamicTool {
94
+ return new DynamicTool({
95
+ name: 'browser_set_checked',
96
+ description: 'Check or uncheck a checkbox. Input should be in JSON format: {"selector": "css-selector-or-text", "checked": true|false}.',
97
+ func: async (input: string) => {
98
+ try {
99
+ const params = JSON.parse(input);
100
+ const { selector, checked } = params;
101
+
102
+ // 尝试多个选择器
103
+ const selectors = selector.split(',').map((s: string) => s.trim());
104
+
105
+ for (const sel of selectors) {
106
+ try {
107
+ // 尝试作为文本选择器
108
+ const locator = page.getByLabel(sel);
109
+ const count = await locator.count();
110
+ if (count > 0) {
111
+ await locator.setChecked(checked);
112
+ return `${checked ? 'Checked' : 'Unchecked'} checkbox with label: "${sel}"`;
113
+ }
114
+
115
+ // 尝试作为 CSS 选择器
116
+ await page.setChecked(sel, checked);
117
+ return `${checked ? 'Checked' : 'Unchecked'} checkbox: "${sel}"`;
118
+ } catch {
119
+ continue;
120
+ }
121
+ }
122
+
123
+ throw new Error(`No checkbox found matching: ${selector}`);
124
+ } catch (error) {
125
+ throw new Error(`Failed to set checked: ${(error as Error).message}`);
126
+ }
127
+ }
128
+ });
129
+ }
130
+
131
+ /**
132
+ * 表单工具集
133
+ */
134
+ export const formTools = (page: Page): DynamicTool[] => [
135
+ createFillFormTool(page),
136
+ createSelectOptionTool(page),
137
+ createSetCheckedTool(page),
138
+ ];
@@ -0,0 +1,29 @@
1
+ /**
2
+ * LangChain 兼容的 Playwright 工具集
3
+ * 从 Playwright MCP 工具转换而来,用于自愈 Agent
4
+ */
5
+
6
+ import { DynamicTool } from '@langchain/core/tools';
7
+ import type { Page } from 'playwright';
8
+ import { navigateTools } from './navigate';
9
+ import { snapshotTools } from './snapshot';
10
+ import { formTools } from './form';
11
+ import { waitTools } from './wait';
12
+ import { verifyTools } from './verify';
13
+
14
+ // 重新导出工具工厂函数
15
+ export { navigateTools } from './navigate';
16
+ export { snapshotTools } from './snapshot';
17
+ export { formTools } from './form';
18
+ export { waitTools } from './wait';
19
+ export { verifyTools } from './verify';
20
+
21
+ // 获取所有工具的工厂函数
22
+ export const getAllTools = (page: Page): DynamicTool[] => [
23
+ ...navigateTools(page),
24
+ ...snapshotTools(page),
25
+ ...formTools(page),
26
+ ...waitTools(page),
27
+ ...verifyTools(page),
28
+ ];
29
+