Jarvis-Brain 0.1.13.5__tar.gz → 0.1.13.7__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Jarvis_Brain
3
- Version: 0.1.13.5
3
+ Version: 0.1.13.7
4
4
  Summary: Jarvis brain mcp
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: beautifulsoup4
@@ -357,7 +357,7 @@ def register_get_ele_info(mcp: FastMCP, browser_manager):
357
357
  "返回值说明:"
358
358
  "element_tag:此属性返回元素的标签名。"
359
359
  "element_attrs_key:此属性以list的形式返回元素所有属性的key。"
360
- "element_rect_size:此属性以元组形式返回元素的大小。"
360
+ "element_rect_size:此属性以元组形式返回元素的大小【如果元素没有位置及大小,则返回空元组】。"
361
361
  "is_in_viewport:此属性以布尔值方式返回元素是否在视口中,以元素可以接受点击的点为判断。"
362
362
  "is_whole_in_viewport:此属性以布尔值方式返回元素是否整个在视口中。"
363
363
  "is_alive:此属性以布尔值形式返回当前元素是否仍可用。用于判断是否因页面刷新而导致元素失效。"
@@ -386,6 +386,14 @@ def register_get_ele_info(mcp: FastMCP, browser_manager):
386
386
  css_selector=css_selector,
387
387
  element_index=element_index,
388
388
  )
389
+ has_rect = target_element.states.has_rect,
390
+ element_rect_size = tuple()
391
+ if not has_rect:
392
+ element_rect_size = target_element.rect.size,
393
+ try:
394
+ child_count=target_element.child_count
395
+ except Exception:
396
+ child_count=0
389
397
  return dp_mcp_message_pack(
390
398
  message="元素可以被正常的选择到,以下是元素相关的一系列信息",
391
399
  browser_port=browser_port,
@@ -394,8 +402,8 @@ def register_get_ele_info(mcp: FastMCP, browser_manager):
394
402
  element_index=element_index,
395
403
  element_tag=target_element.tag,
396
404
  element_attrs_key=list(target_element.attrs.keys()),
397
- # element_child_count=target_element.child_count,
398
- element_rect_size=target_element.rect.size,
405
+ element_child_count=child_count,
406
+ element_rect_size=element_rect_size,
399
407
  is_in_viewport=target_element.states.is_in_viewport,
400
408
  is_whole_in_viewport=target_element.states.is_whole_in_viewport,
401
409
  is_alive=target_element.states.is_alive,
@@ -405,7 +413,7 @@ def register_get_ele_info(mcp: FastMCP, browser_manager):
405
413
  is_displayed=target_element.states.is_displayed,
406
414
  is_covered=target_element.states.is_covered,
407
415
  is_clickable=target_element.states.is_clickable,
408
- has_rect=target_element.states.has_rect,
416
+ has_rect=has_rect
409
417
  )
410
418
 
411
419
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Jarvis_Brain" # 别人下载时用的名字,必须在 PyPI 上唯一
3
- version = "0.1.13.5"
3
+ version = "0.1.13.7"
4
4
  description = "Jarvis brain mcp"
5
5
  dependencies = [
6
6
  "fastmcp",
@@ -10,7 +10,7 @@ import base64
10
10
  from PIL import Image
11
11
  import io
12
12
 
13
- compress_html_js = """
13
+ compress_html_js1 = """
14
14
  function getSimplifiedDOM(node) {
15
15
  // 1. 处理文本节点
16
16
  if (node.nodeType === Node.TEXT_NODE) {
@@ -102,6 +102,99 @@ function getSimplifiedDOM(node) {
102
102
  return getSimplifiedDOM(document.body);
103
103
  """
104
104
 
105
+ # 我自己优化后的版本,逻辑为:删除不可见元素、标签的任何属性value的长度大于20时直接删除这个属性、id和class采用简写方式:id=>#,class=>.
106
+ compress_html_js="""
107
+ function getSimplifiedDOM(node) {
108
+ // 1. 处理文本节点
109
+ if (node.nodeType === Node.TEXT_NODE) {
110
+ const text = node.textContent.trim();
111
+ return text ? text.slice(0, 100) + (text.length > 100 ? '...' : '') : null;
112
+ }
113
+
114
+ // 2. 过滤无用标签
115
+ // 注意:保留了 SVG 标签以便进入内部处理 path
116
+ const ignoreTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME', 'LINK', 'META', 'AUDIO', 'VIDEO', 'CANVAS'];
117
+ if (ignoreTags.includes(node.tagName)) return null;
118
+ if (node.nodeType !== Node.ELEMENT_NODE) return null;
119
+
120
+ // 3. 过滤不可见元素
121
+ const style = window.getComputedStyle(node);
122
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return null;
123
+
124
+ const rect = node.getBoundingClientRect();
125
+ if ((rect.width === 0 || rect.height === 0) && style.overflow !== 'visible') return null;
126
+
127
+ // --- 开始构建标签字符串 ---
128
+ const tagName = node.tagName.toLowerCase();
129
+ let tagStr = tagName;
130
+
131
+ // 获取 ID 和 Class,用于简写逻辑
132
+ const id = node.id;
133
+ // SVG 元素的 className 是对象,需用 getAttribute 安全获取
134
+ const className = node.getAttribute('class');
135
+
136
+ // A. 处理 ID 简写 (#id)
137
+ // 规则应用:如果 ID 值长度 > 20,也视为过长属性,直接丢弃,不显示在简写中
138
+ if (id && id.length <= 20) {
139
+ tagStr += `#${id}`;
140
+ }
141
+
142
+ // B. 处理 Class 简写 (.class)
143
+ // 规则应用:如果 Class 值长度 > 20,直接丢弃
144
+ if (className && typeof className === 'string' && className.length <= 20) {
145
+ const classes = className.trim().split(/\s+/);
146
+ if (classes.length > 0) {
147
+ tagStr += `.${classes.join('.')}`;
148
+ }
149
+ }
150
+
151
+ let propsStr = '';
152
+
153
+ // C. 处理常规属性
154
+ if (node.hasAttributes()) {
155
+ for (const attr of node.attributes) {
156
+ const name = attr.name;
157
+ const value = attr.value;
158
+
159
+ // 1. 跳过 ID 和 Class(因为已经在上面 tagStr 处理过了)
160
+ // 注意:即使因为过长在上面没显示,这里也不应该重复显示,遵循“过长即删除”原则
161
+ if (name === 'id' || name === 'class') continue;
162
+
163
+ // 2. 黑名单过滤:直接删除 style 和 aria-label
164
+ if (name === 'style' || name === 'aria-label') continue;
165
+
166
+ // 3. 特殊标签处理:如果是 path 标签,删除所有属性
167
+ // (continue 意味着直接跳过该属性的拼接,即 key 和 value 都不显示)
168
+ if (tagName === 'path') continue;
169
+
170
+ // 4. 【核心需求】通用长度限制
171
+ // 如果属性值长度超过 20,直接删除该属性
172
+ if (value.length > 20) continue;
173
+
174
+ // 5. 保留属性
175
+ propsStr += ` ${name}="${value.replace(/"/g, '&quot;')}"`;
176
+ }
177
+ }
178
+
179
+ // 4. 递归子节点
180
+ let childNodes = Array.from(node.childNodes);
181
+ if (node.shadowRoot) {
182
+ childNodes = [...childNodes, ...Array.from(node.shadowRoot.childNodes)];
183
+ }
184
+
185
+ const children = childNodes
186
+ .map(getSimplifiedDOM)
187
+ .filter(n => n !== null);
188
+
189
+ // 5. 组装输出
190
+ if (children.length === 0) {
191
+ return `<${tagStr}${propsStr} />`;
192
+ }
193
+ return `<${tagStr}${propsStr}>${children.join('')}</${tagName}>`;
194
+ }
195
+
196
+ return getSimplifiedDOM(document.body);
197
+ """
105
198
 
106
199
  # 使用requests获取html,用于测试是否使用了瑞数和jsl
107
200
  def requests_html(url):
@@ -268,11 +361,3 @@ def compress_image_bytes(input_bytes, target_size_mb=1):
268
361
 
269
362
  return output_bytes
270
363
 
271
- # todo: 大致盘一下各种判定的逻辑【以下的所有压缩比之间的差距均取“绝对值”】
272
- # 1. 如果requests、无头、有头获取到的压缩比之间从差距都在15%以内,则认定该页面是静态页面,此时优先使用requests请求
273
- # 2. 如果requests的status_code为特定的412,或者521,则判定是瑞数和jsl。[此时还有一个特点:requests的压缩比会与其他两种方式获取到的压缩比差距非常大(一两千的那种)]
274
- # 3. 如果requests、无头、有头获取到的压缩比之间差距都在40%以上,则判定该页面只可以用有头采集
275
- # 4. 如果无头和有头获取到的压缩比之间差距小于15%,但是requests和无头的差距大于40%,则认定该页面可以使用无头浏览器采集
276
- # 5. 如果requests和有头获取到的压缩比之间差距小于15%,但是无头和有头的差距大于40%,则认定该页面优先使用有头浏览器采集
277
- # 【此时可能是:1.使用了别的检测无头的waf。2.网站使用瑞数,但是这次请求没有拦截requests(不知道是不是瑞数那边故意设置的),
278
- # 此时如果想进一步判定是否是瑞数,可以使用有头浏览器取一下cookies,如果cookies里面存在瑞数的cookie,那么就可以断定是瑞数】
File without changes