ljsysfurry commited on
Commit
fc12f23
·
verified ·
1 Parent(s): 80e1d19

Upload agentframe_hands.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. agentframe_hands.py +224 -0
agentframe_hands.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ AgentFrame 任务3: OpenClaw 工具接入 (browser + computer-use)
3
+ ===========================================================
4
+ 扩展 Hands 层: 在 exec/read_file 基础上加入 browser 操控
5
+
6
+ browser CLI 命令 (OpenClaw):
7
+ openclaw browser --browser-profile openclaw <cmd>
8
+ - open <url>: 打开网页
9
+ - snapshot: 页面快照 (AI 可读)
10
+ - click <ref>: 点击元素
11
+ - fill <form>: 填表
12
+ - navigate <url>: 导航
13
+ - screenshot: 截图 (MEDIA:<path>)
14
+ - evaluate <js>: 执行 JS
15
+ - pdf: 存 PDF
16
+ """
17
+ import subprocess
18
+ import time
19
+ import re
20
+ from typing import Dict, List, Optional
21
+
22
+ BROWSER_CMD = "openclaw browser --browser-profile openclaw"
23
+
24
+
25
+ class BrowserHands:
26
+ """OpenClaw 浏览器操控 (Agent 的"眼睛和手")"""
27
+
28
+ def __init__(self, timeout=30):
29
+ self.timeout = timeout
30
+
31
+ def _run(self, args: str) -> str:
32
+ """执行 openclaw browser 命令"""
33
+ cmd = f"{BROWSER_CMD} {args}"
34
+ try:
35
+ r = subprocess.run(cmd, shell=True, capture_output=True,
36
+ text=True, timeout=self.timeout)
37
+ # 过滤 gateway 噪音
38
+ out = r.stdout
39
+ for line in out.split('\n'):
40
+ if 'Config warning' in line or 'gateway connect' in line:
41
+ continue
42
+ return out.strip()
43
+ except subprocess.TimeoutExpired:
44
+ return "⚠️ 浏览器命令超时"
45
+ except Exception as e:
46
+ return f"❌ {e}"
47
+
48
+ def open(self, url: str) -> str:
49
+ """打开网页"""
50
+ return self._run(f'open "{url}"')
51
+
52
+ def navigate(self, url: str) -> str:
53
+ """导航当前标签页"""
54
+ return self._run(f'navigate "{url}"')
55
+
56
+ def snapshot(self) -> str:
57
+ """页面快照 (AI 可读的 DOM 描述)"""
58
+ return self._run("snapshot")
59
+
60
+ def click(self, ref: str) -> str:
61
+ """点击元素"""
62
+ return self._run(f'click "{ref}"')
63
+
64
+ def fill(self, form_json: str) -> str:
65
+ """填表 (JSON 格式)"""
66
+ return self._run(f'fill \'{form_json}\'')
67
+
68
+ def press(self, key: str) -> str:
69
+ """按键"""
70
+ return self._run(f'press "{key}"')
71
+
72
+ def screenshot(self) -> str:
73
+ """截图 (返回 MEDIA 路径)"""
74
+ return self._run("screenshot")
75
+
76
+ def evaluate(self, js: str) -> str:
77
+ """执行 JS"""
78
+ return self._run(f'evaluate "{js}"')
79
+
80
+ def pdf(self, path: str) -> str:
81
+ """存 PDF"""
82
+ return self._run(f'pdf "{path}"')
83
+
84
+ def focus(self, target: str) -> str:
85
+ """聚焦标签页"""
86
+ return self._run(f'focus "{target}"')
87
+
88
+
89
+ class ComputerUseHands:
90
+ """电脑操控 (Codex Computer Use / cua-driver)"""
91
+ # 需要安装 cua-driver MCP 后启用
92
+ def __init__(self):
93
+ self.available = False
94
+
95
+ def check(self) -> bool:
96
+ """检查是否可用"""
97
+ try:
98
+ r = subprocess.run("cua-driver --version", shell=True,
99
+ capture_output=True, text=True, timeout=5)
100
+ self.available = r.returncode == 0
101
+ except Exception:
102
+ self.available = False
103
+ return self.available
104
+
105
+
106
+ # ============================================================
107
+ # 集成到 AgentFrame Hands
108
+ # ============================================================
109
+ class AgentFrameHands:
110
+ """完整 Hands: exec + 文件 + 浏览器 + (可选)电脑操控"""
111
+
112
+ def __init__(self, workspace="/root/.openclaw/workspace"):
113
+ self.workspace = workspace
114
+ self.browser = BrowserHands()
115
+ self.computer = ComputerUseHands()
116
+
117
+ def tool_schemas(self) -> List[Dict]:
118
+ """喂给 DeepSeek 的工具定义"""
119
+ return [
120
+ {
121
+ "type": "function",
122
+ "function": {
123
+ "name": "exec",
124
+ "description": "执行 shell 命令",
125
+ "parameters": {"type": "object",
126
+ "properties": {"command": {"type": "string"}},
127
+ "required": ["command"]},
128
+ },
129
+ },
130
+ {
131
+ "type": "function",
132
+ "function": {
133
+ "name": "browser_open",
134
+ "description": "打开网页 (如 https://example.com)",
135
+ "parameters": {"type": "object",
136
+ "properties": {"url": {"type": "string"}},
137
+ "required": ["url"]},
138
+ },
139
+ },
140
+ {
141
+ "type": "function",
142
+ "function": {
143
+ "name": "browser_snapshot",
144
+ "description": "获取当前网页内容快照 (AI 可读)",
145
+ "parameters": {"type": "object", "properties": {}},
146
+ },
147
+ },
148
+ {
149
+ "type": "function",
150
+ "function": {
151
+ "name": "browser_click",
152
+ "description": "点击网页元素 (ref 来自 snapshot)",
153
+ "parameters": {"type": "object",
154
+ "properties": {"ref": {"type": "string"}},
155
+ "required": ["ref"]},
156
+ },
157
+ },
158
+ {
159
+ "type": "function",
160
+ "function": {
161
+ "name": "read_file",
162
+ "description": "读取文件内容",
163
+ "parameters": {"type": "object",
164
+ "properties": {"path": {"type": "string"}},
165
+ "required": ["path"]},
166
+ },
167
+ },
168
+ ]
169
+
170
+ def execute(self, name: str, args: Dict) -> str:
171
+ """执行工具"""
172
+ if name == "exec":
173
+ r = subprocess.run(args.get("command", ""), shell=True,
174
+ capture_output=True, text=True, timeout=15,
175
+ cwd=self.workspace)
176
+ return (r.stdout or r.stderr)[:600]
177
+
178
+ elif name == "browser_open":
179
+ return self.browser.open(args.get("url", ""))
180
+
181
+ elif name == "browser_snapshot":
182
+ return self.browser.snapshot()
183
+
184
+ elif name == "browser_click":
185
+ return self.browser.click(args.get("ref", ""))
186
+
187
+ elif name == "read_file":
188
+ try:
189
+ with open(args.get("path", "")) as f:
190
+ return f.read()[:600]
191
+ except Exception as e:
192
+ return f"错误: {e}"
193
+
194
+ return f"未知工具: {name}"
195
+
196
+
197
+ # ============================================================
198
+ # 演示
199
+ # ============================================================
200
+ if __name__ == "__main__":
201
+ print("=" * 60)
202
+ print("AgentFrame 任务3: OpenClaw 工具接入 演示")
203
+ print("=" * 60)
204
+
205
+ hands = AgentFrameHands()
206
+ print("\n📋 工具清单:")
207
+ for t in hands.tool_schemas():
208
+ print(f" 🛠 {t['function']['name']}: {t['function']['description']}")
209
+
210
+ # 测试浏览器
211
+ print("\n🌐 测试浏览器 (无头模式验证):")
212
+ try:
213
+ r = hands.browser.open("https://example.com")
214
+ print(f" open: {'✅' if '成功' in r or r else '⚠️ 需 gateway 授权: ' + r[:50]}")
215
+ except Exception as e:
216
+ print(f" ⚠️ {e}")
217
+
218
+ # 测试 exec (无需浏览器授权)
219
+ print("\n💻 测试 exec (本地):")
220
+ r = hands.execute("exec", {"command": "echo 'AgentFrame Hands 就绪!'"})
221
+ print(f" → {r.strip()}")
222
+
223
+ print("\n✅ 任务3完成: OpenClaw 工具集已接入 Hands 层")
224
+ print(" (browser 需 gateway scope 授权后完整可用)")