update SOPs (web_setup, tmwebdriver, autonomous_op) + whitelist adb_ui.py
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -58,6 +58,9 @@ memory/*
|
|||||||
# TMWebDriver SOP
|
# TMWebDriver SOP
|
||||||
!memory/tmwebdriver_sop.md
|
!memory/tmwebdriver_sop.md
|
||||||
|
|
||||||
|
# ADB UI tool
|
||||||
|
!memory/adb_ui.py
|
||||||
|
|
||||||
# Visual Studio
|
# Visual Studio
|
||||||
.vs/
|
.vs/
|
||||||
restore_commit.txt
|
restore_commit.txt
|
||||||
|
|||||||
60
memory/adb_ui.py
Normal file
60
memory/adb_ui.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# adb_ui.py - 一键dump+解析Android UI
|
||||||
|
# PITFALLS: dump已内置--compressed; 美团等动画app需先禁动画(adb shell settings put global animator_duration_scale 0 ...共3条);
|
||||||
|
# 弹窗检测: ui(clickable_only=True, raw=True) 找全屏FrameLayout+底部小ImageView(关闭X)
|
||||||
|
# 已知包名: 美团外卖=com.sankuai.meituan.takeoutnew 淘宝=com.taobao.taobao
|
||||||
|
import subprocess, xml.etree.ElementTree as ET, os, re, shutil
|
||||||
|
|
||||||
|
ADB = shutil.which("adb") or "adb"
|
||||||
|
LOCAL_XML = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui_mt.xml")
|
||||||
|
|
||||||
|
def ui(keyword=None, clickable_only=False, raw=False):
|
||||||
|
"""一键dump+解析Android UI
|
||||||
|
keyword: 过滤含关键词的节点
|
||||||
|
clickable_only: 只显示可点击节点
|
||||||
|
raw: 返回原始节点列表而非打印
|
||||||
|
"""
|
||||||
|
subprocess.run([ADB, "shell", "rm", "-f", "/sdcard/ui.xml"], capture_output=True)
|
||||||
|
r = subprocess.run([ADB, "shell", "uiautomator", "dump", "--compressed", "/sdcard/ui.xml"],
|
||||||
|
capture_output=True, text=True, timeout=15)
|
||||||
|
if "dumped" not in r.stdout.lower() and "dumped" not in r.stderr.lower():
|
||||||
|
print(f"dump failed: {r.stdout}{r.stderr}")
|
||||||
|
return []
|
||||||
|
subprocess.run([ADB, "pull", "/sdcard/ui.xml", LOCAL_XML], capture_output=True, timeout=10)
|
||||||
|
|
||||||
|
tree = ET.parse(LOCAL_XML)
|
||||||
|
nodes = []
|
||||||
|
for n in tree.getroot().iter("node"):
|
||||||
|
text = n.get("text", "")
|
||||||
|
desc = n.get("content-desc", "")
|
||||||
|
bounds = n.get("bounds", "")
|
||||||
|
click = n.get("clickable") == "true"
|
||||||
|
cls = n.get("class", "").split(".")[-1]
|
||||||
|
label = text or desc
|
||||||
|
if not label and not raw:
|
||||||
|
continue
|
||||||
|
if clickable_only and not click:
|
||||||
|
continue
|
||||||
|
if keyword and keyword.lower() not in (label or "").lower():
|
||||||
|
continue
|
||||||
|
cx, cy = 0, 0
|
||||||
|
if bounds:
|
||||||
|
m = re.findall(r'\[(\d+),(\d+)\]', bounds)
|
||||||
|
if len(m) == 2:
|
||||||
|
cx = (int(m[0][0]) + int(m[1][0])) // 2
|
||||||
|
cy = (int(m[0][1]) + int(m[1][1])) // 2
|
||||||
|
nodes.append({"label": label, "click": click, "bounds": bounds, "cx": cx, "cy": cy, "class": cls})
|
||||||
|
|
||||||
|
if not raw:
|
||||||
|
for n in nodes:
|
||||||
|
flag = "Y" if n["click"] else " "
|
||||||
|
coord = f"({n['cx']},{n['cy']})" if n['cx'] else ""
|
||||||
|
print(f"[{flag}] {n['label']} {coord} {n['bounds']}")
|
||||||
|
print(f"\ntotal: {len(nodes)} nodes")
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def tap(x, y):
|
||||||
|
subprocess.run([ADB, "shell", "input", "tap", str(x), str(y)], capture_output=True)
|
||||||
|
print(f"tap({x},{y}) ok")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ui()
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
### 选择原则
|
### 选择原则
|
||||||
- **增量价值优先**:必须产生新知识/新能力,优先选择能扩展自身操作能力的任务
|
- **增量价值优先**:必须产生新知识/新能力,优先选择能扩展自身操作能力的任务
|
||||||
|
- **盲区优先**:探索产出须为自身参数无法复现的知识(小众库发现与用法、环境特有的坑),学已熟知库的基础用法无价值
|
||||||
- **自主发现**:主动扫描用户环境(pip list、项目目录、配置文件)发现未知工具,而非等用户告知
|
- **自主发现**:主动扫描用户环境(pip list、项目目录、配置文件)发现未知工具,而非等用户告知
|
||||||
- **假设驱动**:明确"要验证什么假设",实验必须有动手验证环节
|
- **假设驱动**:明确"要验证什么假设",实验必须有动手验证环节
|
||||||
- **禁止低价值验证**:不验证 global_mem 中的静态配置,不做无假设的巡检
|
- **禁止低价值验证**:不验证 global_mem 中的静态配置,不做无假设的巡检
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
- 受浏览器 **InTrusted / 权限策略** 限制,有些操作不能单靠 TMWebDriver 完成:
|
- 受浏览器 **InTrusted / 权限策略** 限制,有些操作不能单靠 TMWebDriver 完成:
|
||||||
1. **打开新窗口 / 新标签**
|
1. **打开新窗口 / 新标签**
|
||||||
- 部分浏览器/脚本设置中,需要用户显式允许脚本“打开新标签/窗口”;否则相关命令会被拦截。
|
- 已通过 GM_openInTab 替换 window.open 解决。潜在问题:部分浏览器可能仍需用户显式允许脚本打开新标签。
|
||||||
2. **上传文件等受信任交互**
|
2. **上传文件等受信任交互**
|
||||||
- 通常无法单纯用 JS 填充 `<input type="file">` 等敏感控件。
|
- 通常无法单纯用 JS 填充 `<input type="file">` 等敏感控件。
|
||||||
- 需要配合键鼠控制工具(`ljqCtrl.py` / 控制 SOP)在前台模拟真实点击和文件选择。
|
- 需要配合键鼠控制工具(`ljqCtrl.py` / 控制 SOP)在前台模拟真实点击和文件选择。
|
||||||
|
|||||||
@@ -1,18 +1,43 @@
|
|||||||
# Web 工具链初始化执行 SOP (精简版)
|
# Web 工具链初始化执行 SOP
|
||||||
|
|
||||||
若web_scan和web_execute_js已测试可用,无需进行此sop。
|
若 web_scan 和 web_execute_js 已测试可用,无需执行此 SOP。
|
||||||
此sop仅供初始安装时,code_run可用但web工具第一次配置使用。
|
仅供初始安装时,code_run 可用但 web 工具尚未配置的场景。
|
||||||
|
|
||||||
## 目标
|
## 目标
|
||||||
在仅具备系统级权限时,引导用户建立 Web 交互能力。
|
在仅具备系统级权限(code_run)时,建立 Web 交互能力(web_scan / web_execute_js)。
|
||||||
|
|
||||||
## 阶段一:引导安装 (Action: code_run)
|
## 前置:检测浏览器
|
||||||
1. **启动导航**:使用 PowerShell 启动 Chrome 并直达测试版页面:
|
```python
|
||||||
`start chrome "https://chromewebstore.google.com/detail/%E7%AF%A1%E6%94%B9%E7%8C%B4%E6%B5%8B%E8%AF%95%E7%89%88/gcalenpjmijncebpfijmoaglllgpjagf"`
|
import shutil, subprocess
|
||||||
|
browser = "chrome" if shutil.which("chrome") else "msedge" # Edge内置必存在,Chrome可选
|
||||||
|
```
|
||||||
|
|
||||||
## 阶段二:脚本分发 (Action: file_read + code_run)
|
## 阶段一:安装 Tampermonkey (手动)
|
||||||
1. **资源提取与写入**:使用 Python 读取 `../assets/ljq_web_driver.user.js` 并通过 `pyperclip` 写入系统剪贴板。
|
**状态**: 尚未实现自动化,需用户手动操作。
|
||||||
2. **用户交互指令**:通知用户“脚本已入剪贴板”,请求用户在插件中完成【新建-全选-粘贴-保存】操作。
|
1. 用 `start` 打开扩展商店页面(自动适配浏览器):
|
||||||
|
- Chrome: `start "" "https://chromewebstore.google.com/detail/篡改猴测试版/gcalenpjmijncebpfijmoaglllgpjagf"`
|
||||||
|
- Edge: `start "" "https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd"`
|
||||||
|
2. 提示用户点击"安装"并确认。
|
||||||
|
|
||||||
## 阶段三:环境确认 (Action: code_run)
|
## 阶段二:安装 ljq_web_driver.user.js
|
||||||
1. **切换驱动**:尝试调用 `web_scan` 或注入 JS 进行心跳检测,确认 `ljq_web_driver.user.js` 是否已在目标域生效。
|
**脚本路径**: `../assets/ljq_web_driver.user.js`
|
||||||
|
|
||||||
|
### 方案A(自动化,优先)
|
||||||
|
本地 HTTP 服务器 + TM 中间页,用 `start` 命令打开:
|
||||||
|
1. Python 启动 `http.server` 托管脚本(Content-Type: text/javascript)
|
||||||
|
2. `start "" "https://www.tampermonkey.net/script_installation.php#url=http://127.0.0.1:{port}/ljq_web_driver.user.js"`
|
||||||
|
- ⚠️ 以上步骤均须用 `Popen` 非阻塞执行,禁止 `subprocess.run`,否则阻塞 agent
|
||||||
|
3. TM 秒弹安装确认,用户点"安装"即可
|
||||||
|
|
||||||
|
### 方案B(手动 fallback)
|
||||||
|
若方案A失败,用剪贴板:
|
||||||
|
1. 读取脚本内容 → `pyperclip.copy()`
|
||||||
|
2. 通知用户在 TM 中【新建脚本 → 全选 → 粘贴 → 保存】
|
||||||
|
|
||||||
|
## 阶段三:验证
|
||||||
|
调用 `web_scan` 或注入 JS 心跳检测,确认脚本已生效。
|
||||||
|
|
||||||
|
## 避坑 (Chromium untrusted 拦截)
|
||||||
|
- ❌ 直接导航到 `localhost/.user.js` → Chromium 弹 untrusted 拦截 + "另存为",延迟约1分钟
|
||||||
|
- ✅ 必须用 `start` 命令(系统级)打开 TM 中间页 URL → 秒弹安装,无拦截
|
||||||
|
- 此问题 Chrome 和 Edge 均存在(Chromium 内核通病)
|
||||||
Reference in New Issue
Block a user