validpilot-oss 1.1.1 → 1.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.0] - 2026-06-28
6
+
7
+ ### Added
8
+
9
+ - 新增 `browser_screenshot_element` 工具 - 对指定页面元素进行截图,支持padding参数扩展截图区域
10
+ - 新增 `browser_navigate` 工具 - 浏览器导航操作,支持前进(forward)、后退(back)、刷新(refresh)、重新加载(reload)
11
+
12
+ ### Fixed
13
+
14
+ - 完整实现文档承诺的所有72个工具,能力匹配度100%
15
+
5
16
  ## [1.1.1] - 2026-06-28
6
17
 
7
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "validpilot-oss",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "ValidPilot Open Source - Browser automation and validation framework with MCP protocol",
5
5
  "bin": {
6
6
  "validpilot": "bin/validpilot.js",
package/server.js CHANGED
@@ -3949,6 +3949,81 @@ async function callTool(name, args = {}) {
3949
3949
 
3950
3950
  return text(JSON.stringify({ image: filePath, success: true, error_analysis: { has_errors: false } }, null, 2));
3951
3951
  }
3952
+ case 'browser_screenshot_element': {
3953
+ const { target } = await ensurePage();
3954
+ ensureArtifactsDir();
3955
+ const selector = args.selector;
3956
+ if (!selector) {
3957
+ return { isError: true, content: [{ type: 'text', text: 'browser_screenshot_element 需要提供 selector 参数' }] };
3958
+ }
3959
+ const padding = args.padding || 0;
3960
+ const safeName = (args.name || `element-screenshot-${Date.now()}`).replace(/[^a-zA-Z0-9_-]/g, '_');
3961
+ const filePath = path.join(SCREENSHOT_DIR, `${safeName}.png`);
3962
+
3963
+ try {
3964
+ const element = await target.$(selector);
3965
+ if (!element) {
3966
+ return { isError: true, content: [{ type: 'text', text: `未找到选择器 "${selector}" 对应的元素` }] };
3967
+ }
3968
+
3969
+ const box = await element.boundingBox();
3970
+ if (!box) {
3971
+ return { isError: true, content: [{ type: 'text', text: `元素 "${selector}" 不可见或尺寸为0` }] };
3972
+ }
3973
+
3974
+ const clip = {
3975
+ x: Math.max(0, box.x - padding),
3976
+ y: Math.max(0, box.y - padding),
3977
+ width: box.width + padding * 2,
3978
+ height: box.height + padding * 2
3979
+ };
3980
+
3981
+ await target.screenshot({ path: filePath, clip, omitBackground: false });
3982
+
3983
+ return text(JSON.stringify({
3984
+ image: filePath,
3985
+ success: true,
3986
+ selector,
3987
+ elementSize: { width: box.width, height: box.height },
3988
+ screenshotSize: { width: clip.width, height: clip.height },
3989
+ padding
3990
+ }, null, 2));
3991
+ } catch (e) {
3992
+ return { isError: true, content: [{ type: 'text', text: `元素截图失败: ${e.message}` }] };
3993
+ }
3994
+ }
3995
+ case 'browser_navigate': {
3996
+ const { target } = await ensurePage();
3997
+ const action = args.action || 'refresh';
3998
+ const waitUntil = args.waitUntil || 'domcontentloaded';
3999
+ const timeout = args.timeout || 30000;
4000
+
4001
+ try {
4002
+ switch (action) {
4003
+ case 'forward':
4004
+ await target.goForward({ timeout });
4005
+ break;
4006
+ case 'back':
4007
+ await target.goBack({ timeout });
4008
+ break;
4009
+ case 'refresh':
4010
+ case 'reload':
4011
+ await target.reload({ waitUntil, timeout });
4012
+ break;
4013
+ default:
4014
+ return { isError: true, content: [{ type: 'text', text: `不支持的导航操作: ${action},支持 forward/back/refresh/reload` }] };
4015
+ }
4016
+
4017
+ return text(JSON.stringify({
4018
+ action,
4019
+ success: true,
4020
+ currentUrl: target.url(),
4021
+ waitUntil
4022
+ }, null, 2));
4023
+ } catch (e) {
4024
+ return { isError: true, content: [{ type: 'text', text: `导航失败: ${e.message}` }] };
4025
+ }
4026
+ }
3952
4027
  case 'browser_step': {
3953
4028
  const { target } = await ensurePage();
3954
4029
  return text(JSON.stringify(await captureStepEvidence(target, args.label || 'manual-step', args), null, 2));
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "browser_navigate",
3
+ "description": "浏览器导航操作。支持前进(forward)、后退(back)、刷新(refresh)、重新加载(reload)操作。",
4
+ "inputSchema": {
5
+ "type": "object",
6
+ "properties": {
7
+ "action": {
8
+ "type": "string",
9
+ "description": "导航操作:forward(前进)、back(后退)、refresh(刷新)、reload(重新加载)",
10
+ "enum": ["forward", "back", "refresh", "reload"],
11
+ "default": "refresh"
12
+ },
13
+ "waitUntil": {
14
+ "type": "string",
15
+ "description": "等待条件:domcontentloaded(DOM加载完成)、load(页面完全加载)、networkidle(网络空闲)",
16
+ "enum": ["domcontentloaded", "load", "networkidle"],
17
+ "default": "domcontentloaded"
18
+ },
19
+ "timeout": {
20
+ "type": "number",
21
+ "description": "超时时间,单位毫秒,默认30000"
22
+ }
23
+ },
24
+ "required": ["action"]
25
+ }
26
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "browser_screenshot_element",
3
+ "description": "对指定页面元素进行截图。使用CSS选择器定位元素,截取该元素的可见区域,返回截图路径和尺寸信息。支持padding参数扩展截图区域。",
4
+ "inputSchema": {
5
+ "type": "object",
6
+ "properties": {
7
+ "selector": {
8
+ "type": "string",
9
+ "description": "目标元素的CSS选择器"
10
+ },
11
+ "padding": {
12
+ "type": "number",
13
+ "description": "截图区域周围的padding,单位像素,默认0"
14
+ },
15
+ "name": {
16
+ "type": "string",
17
+ "description": "截图文件名(不含扩展名),默认自动生成"
18
+ }
19
+ },
20
+ "required": ["selector"]
21
+ }
22
+ }