aistvagent 1.0.0__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.
- aistvagent-1.0.0/MANIFEST.in +1 -0
- aistvagent-1.0.0/PKG-INFO +40 -0
- aistvagent-1.0.0/README.md +16 -0
- aistvagent-1.0.0/aistvagent.egg-info/PKG-INFO +40 -0
- aistvagent-1.0.0/aistvagent.egg-info/SOURCES.txt +28 -0
- aistvagent-1.0.0/aistvagent.egg-info/dependency_links.txt +1 -0
- aistvagent-1.0.0/aistvagent.egg-info/entry_points.txt +2 -0
- aistvagent-1.0.0/aistvagent.egg-info/requires.txt +3 -0
- aistvagent-1.0.0/aistvagent.egg-info/top_level.txt +1 -0
- aistvagent-1.0.0/openclaw/__init__.py +1 -0
- aistvagent-1.0.0/openclaw/__main__.py +2 -0
- aistvagent-1.0.0/openclaw/core/__init__.py +0 -0
- aistvagent-1.0.0/openclaw/core/agent.py +226 -0
- aistvagent-1.0.0/openclaw/core/function_registry.py +84 -0
- aistvagent-1.0.0/openclaw/main.py +174 -0
- aistvagent-1.0.0/openclaw/skills/__init__.py +0 -0
- aistvagent-1.0.0/openclaw/skills/debugger.py +70 -0
- aistvagent-1.0.0/openclaw/skills/file_manager.py +77 -0
- aistvagent-1.0.0/openclaw/skills/os_control.py +49 -0
- aistvagent-1.0.0/openclaw/skills/software.py +48 -0
- aistvagent-1.0.0/openclaw/skills/system.py +94 -0
- aistvagent-1.0.0/openclaw/skills/web.py +139 -0
- aistvagent-1.0.0/openclaw/ui/__init__.py +0 -0
- aistvagent-1.0.0/openclaw/ui/server.py +124 -0
- aistvagent-1.0.0/openclaw/ui/static/index.html +51 -0
- aistvagent-1.0.0/openclaw/ui/static/logo.jpg +0 -0
- aistvagent-1.0.0/openclaw/ui/static/script.js +96 -0
- aistvagent-1.0.0/openclaw/ui/static/style.css +469 -0
- aistvagent-1.0.0/pyproject.toml +45 -0
- aistvagent-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include openclaw/ui/static *
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aistvagent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AISTV - AI IT Agent cho Windows
|
|
5
|
+
Author: AISTV
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aistv/agent
|
|
8
|
+
Project-URL: Source, https://github.com/aistv/agent
|
|
9
|
+
Keywords: ai,agent,windows,it-support,aistv
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Desktop Environment
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: playwright>=1.40.0
|
|
22
|
+
Requires-Dist: pyautogui>=0.9.54
|
|
23
|
+
Requires-Dist: Pillow>=10.0.0
|
|
24
|
+
|
|
25
|
+
# AISTV - AI IT Agent cho Windows
|
|
26
|
+
|
|
27
|
+
## Cai dat
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install .
|
|
31
|
+
playwright install chromium
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Su dung
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
openaistv
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Mo trinh duyet: http://127.0.0.1:8765
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aistvagent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AISTV - AI IT Agent cho Windows
|
|
5
|
+
Author: AISTV
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aistv/agent
|
|
8
|
+
Project-URL: Source, https://github.com/aistv/agent
|
|
9
|
+
Keywords: ai,agent,windows,it-support,aistv
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Desktop Environment
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: playwright>=1.40.0
|
|
22
|
+
Requires-Dist: pyautogui>=0.9.54
|
|
23
|
+
Requires-Dist: Pillow>=10.0.0
|
|
24
|
+
|
|
25
|
+
# AISTV - AI IT Agent cho Windows
|
|
26
|
+
|
|
27
|
+
## Cai dat
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install .
|
|
31
|
+
playwright install chromium
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Su dung
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
openaistv
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Mo trinh duyet: http://127.0.0.1:8765
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
aistvagent.egg-info/PKG-INFO
|
|
5
|
+
aistvagent.egg-info/SOURCES.txt
|
|
6
|
+
aistvagent.egg-info/dependency_links.txt
|
|
7
|
+
aistvagent.egg-info/entry_points.txt
|
|
8
|
+
aistvagent.egg-info/requires.txt
|
|
9
|
+
aistvagent.egg-info/top_level.txt
|
|
10
|
+
openclaw/__init__.py
|
|
11
|
+
openclaw/__main__.py
|
|
12
|
+
openclaw/main.py
|
|
13
|
+
openclaw/core/__init__.py
|
|
14
|
+
openclaw/core/agent.py
|
|
15
|
+
openclaw/core/function_registry.py
|
|
16
|
+
openclaw/skills/__init__.py
|
|
17
|
+
openclaw/skills/debugger.py
|
|
18
|
+
openclaw/skills/file_manager.py
|
|
19
|
+
openclaw/skills/os_control.py
|
|
20
|
+
openclaw/skills/software.py
|
|
21
|
+
openclaw/skills/system.py
|
|
22
|
+
openclaw/skills/web.py
|
|
23
|
+
openclaw/ui/__init__.py
|
|
24
|
+
openclaw/ui/server.py
|
|
25
|
+
openclaw/ui/static/index.html
|
|
26
|
+
openclaw/ui/static/logo.jpg
|
|
27
|
+
openclaw/ui/static/script.js
|
|
28
|
+
openclaw/ui/static/style.css
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
openclaw
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import re
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from .function_registry import FunctionRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_AI_EXE = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _find_engine() -> str:
|
|
13
|
+
global _AI_EXE
|
|
14
|
+
if _AI_EXE:
|
|
15
|
+
return _AI_EXE
|
|
16
|
+
candidates = [
|
|
17
|
+
r"C:\npm\prefix\node_modules\opencode-ai\bin\opencode.exe",
|
|
18
|
+
os.path.expanduser(r"~\AppData\Roaming\npm\node_modules\opencode-ai\bin\opencode.exe"),
|
|
19
|
+
os.path.expanduser(r"~\AppData\Local\opencode\bin\opencode.exe"),
|
|
20
|
+
]
|
|
21
|
+
for p in candidates:
|
|
22
|
+
if os.path.isfile(p):
|
|
23
|
+
_AI_EXE = p
|
|
24
|
+
return p
|
|
25
|
+
try:
|
|
26
|
+
r = subprocess.run("where opencode", capture_output=True, text=True, timeout=5, shell=True)
|
|
27
|
+
if r.returncode == 0:
|
|
28
|
+
path = r.stdout.strip().split("\n")[0].strip()
|
|
29
|
+
if path:
|
|
30
|
+
_AI_EXE = path
|
|
31
|
+
return path
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
_AI_EXE = "opencode"
|
|
35
|
+
return "opencode"
|
|
36
|
+
|
|
37
|
+
TOOL_LIST = """
|
|
38
|
+
1. get_disk_space - Kiem tra dung luong o dia
|
|
39
|
+
2. clean_temp_files - Don rac he thong
|
|
40
|
+
3. get_system_info - Thong tin he thong
|
|
41
|
+
4. install_software(app_name) - Cai dat phan mem (VD: Discord.Discord)
|
|
42
|
+
5. list_installed_software(filter_keyword) - Liet ke phan mem
|
|
43
|
+
6. uninstall_software(app_name) - Go cai dat
|
|
44
|
+
7. browse_and_search(url, search_query) - Mo trinh duyet + tim kiem
|
|
45
|
+
8. capture_web_screenshot(url) - Chup anh web
|
|
46
|
+
9. manage_file(action, file_path, content) - Doc/ghi/xoa file, liet ke thu muc
|
|
47
|
+
10. find_large_files(directory, min_size_mb) - Tim file lon
|
|
48
|
+
11. run_and_debug_script(file_path) - Chay script Python
|
|
49
|
+
12. build_project(directory, build_tool) - Build project
|
|
50
|
+
13. capture_screen(filename) - Chup man hinh
|
|
51
|
+
14. mouse_click(x, y, button) - Click chuot
|
|
52
|
+
15. keyboard_type(text) - Go chu
|
|
53
|
+
16. press_key(key) - Nhan phim
|
|
54
|
+
17. hotkey_combination(keys) - To hop phim
|
|
55
|
+
18. execute_cmd(command) - Chay lenh CMD
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
SYSTEM_PROMPT = f"""Ban la AISTV, tro ly AI IT cho Windows.
|
|
59
|
+
{TOOL_LIST}
|
|
60
|
+
QUY TAC:
|
|
61
|
+
- Chon cong cu phu hop nhat.
|
|
62
|
+
- KHONG dung execute_cmd neu co cong cu chuyen dung.
|
|
63
|
+
- Tra ve JSON: {{"tool": "ten_cong_cu", "args": {{...}}}} neu can dung cong cu.
|
|
64
|
+
- Tra ve {{"text": "cau tra loi cua ban"}} neu khong can dung cong cu nao.
|
|
65
|
+
- KHONG dung "text" lam ten cong cu. "text" chi la truong trong JSON khi tra loi.
|
|
66
|
+
- Viet tieng Viet co dau."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
_AI_MODEL = "opencode/deepseek-v4-flash-free"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Agent:
|
|
73
|
+
def __init__(self, api_key: str = ""):
|
|
74
|
+
self.api_key = api_key
|
|
75
|
+
self.registry = FunctionRegistry()
|
|
76
|
+
self.messages = []
|
|
77
|
+
|
|
78
|
+
def register_skill(self, fn=None, *, name: str = None, description: str = None):
|
|
79
|
+
return self.registry.register(fn, name=name, description=description)
|
|
80
|
+
|
|
81
|
+
def reset_conversation(self):
|
|
82
|
+
self.messages = []
|
|
83
|
+
|
|
84
|
+
def _build_prompt(self, user_input: str) -> str:
|
|
85
|
+
history = ""
|
|
86
|
+
for m in self.messages[-4:]:
|
|
87
|
+
role = "Nguoi dung" if m["role"] == "user" else "Tro ly"
|
|
88
|
+
history += f"{role}: {m['content']}\n"
|
|
89
|
+
return f"{SYSTEM_PROMPT}\n\nLICH SU:\n{history}\nNguoi dung: {user_input}\n\nTro ly (JSON):"
|
|
90
|
+
|
|
91
|
+
def _call_engine(self, prompt: str):
|
|
92
|
+
proc = subprocess.Popen(
|
|
93
|
+
[_find_engine(), "run", "--model", _AI_MODEL, "--format", "json",
|
|
94
|
+
"--dangerously-skip-permissions", prompt],
|
|
95
|
+
stdout=subprocess.PIPE,
|
|
96
|
+
stderr=subprocess.PIPE,
|
|
97
|
+
text=False,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
for raw_line in iter(proc.stdout.readline, b''):
|
|
101
|
+
line = raw_line.decode("utf-8", errors="replace").strip()
|
|
102
|
+
if not line:
|
|
103
|
+
continue
|
|
104
|
+
try:
|
|
105
|
+
data = json.loads(line)
|
|
106
|
+
yield data
|
|
107
|
+
except json.JSONDecodeError:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
proc.wait()
|
|
111
|
+
if proc.returncode != 0:
|
|
112
|
+
err = proc.stderr.read().decode("utf-8", errors="replace")[:500]
|
|
113
|
+
yield {"type": "error", "content": err or f"Loi AI engine (exit {proc.returncode})"}
|
|
114
|
+
|
|
115
|
+
def _parse_json_response(self, text: str) -> dict:
|
|
116
|
+
text = text.strip()
|
|
117
|
+
match = re.search(r"\{.*\}", text, re.DOTALL)
|
|
118
|
+
if match:
|
|
119
|
+
try:
|
|
120
|
+
return json.loads(match.group())
|
|
121
|
+
except json.JSONDecodeError:
|
|
122
|
+
pass
|
|
123
|
+
try:
|
|
124
|
+
return json.loads(text)
|
|
125
|
+
except json.JSONDecodeError:
|
|
126
|
+
return {"text": text}
|
|
127
|
+
|
|
128
|
+
_TOOL_NAMES = {
|
|
129
|
+
"get_disk_space": "Kiem tra o dia",
|
|
130
|
+
"clean_temp_files": "Don rac he thong",
|
|
131
|
+
"get_system_info": "Xem thong tin he thong",
|
|
132
|
+
"install_software": "Cai dat phan mem",
|
|
133
|
+
"list_installed_software": "Liet ke phan mem",
|
|
134
|
+
"uninstall_software": "Go cai dat",
|
|
135
|
+
"browse_and_search": "Mo trinh duyet",
|
|
136
|
+
"capture_web_screenshot": "Chup anh web",
|
|
137
|
+
"manage_file": "Quan ly file",
|
|
138
|
+
"find_large_files": "Tim file lon",
|
|
139
|
+
"run_and_debug_script": "Chay script",
|
|
140
|
+
"build_project": "Build project",
|
|
141
|
+
"capture_screen": "Chup man hinh",
|
|
142
|
+
"mouse_click": "Click chuot",
|
|
143
|
+
"keyboard_type": "Go phim",
|
|
144
|
+
"press_key": "Nhan phim",
|
|
145
|
+
"hotkey_combination": "To hop phim",
|
|
146
|
+
"execute_cmd": "Chay lenh CMD",
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def process_message_stream(self, user_input: str):
|
|
150
|
+
"""Generator: yields dicts with step info for streaming display."""
|
|
151
|
+
yield {"type": "think", "content": "Dang phan tich yeu cau..."}
|
|
152
|
+
prompt = self._build_prompt(user_input)
|
|
153
|
+
raw_parts = []
|
|
154
|
+
|
|
155
|
+
for data in self._call_engine(prompt):
|
|
156
|
+
event = data.get("type", "")
|
|
157
|
+
if event == "text":
|
|
158
|
+
part = data.get("part", {})
|
|
159
|
+
text = part.get("text", "")
|
|
160
|
+
if text:
|
|
161
|
+
raw_parts.append(text)
|
|
162
|
+
elif event == "error":
|
|
163
|
+
yield {"type": "error", "content": f"Loi: {data.get('content', '')}"}
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
raw = "".join(raw_parts)
|
|
167
|
+
parsed = self._parse_json_response(raw)
|
|
168
|
+
|
|
169
|
+
if "tool" not in parsed:
|
|
170
|
+
text = parsed.get("text", raw)
|
|
171
|
+
yield {"type": "final_answer", "content": text}
|
|
172
|
+
self.messages.append({"role": "user", "content": user_input})
|
|
173
|
+
self.messages.append({"role": "assistant", "content": text})
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
fn_name = parsed["tool"]
|
|
177
|
+
fn_args = parsed.get("args", {})
|
|
178
|
+
|
|
179
|
+
# Neu AI tra ve {"tool": "text", "args": {"content/text": "..."}} -> xu ly nhu text response
|
|
180
|
+
if fn_name == "text":
|
|
181
|
+
text = fn_args.get("content") or fn_args.get("text") or ""
|
|
182
|
+
yield {"type": "final_answer", "content": text}
|
|
183
|
+
self.messages.append({"role": "user", "content": user_input})
|
|
184
|
+
self.messages.append({"role": "assistant", "content": text})
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
label = self._TOOL_NAMES.get(fn_name, fn_name)
|
|
188
|
+
yield {"type": "think", "content": f"Dang thuc thi: {label}..."}
|
|
189
|
+
yield {"type": "tool_call", "tool": fn_name, "arguments": fn_args}
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
result = self.registry.execute(fn_name, fn_args)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
result = f"Loi khi thuc thi {fn_name}: {str(e)}"
|
|
195
|
+
|
|
196
|
+
yield {"type": "think", "content": f"Da thuc thi xong: {label}"}
|
|
197
|
+
yield {"type": "tool_result", "tool": fn_name, "result": result}
|
|
198
|
+
|
|
199
|
+
final = self._format_result(fn_name, fn_args, result)
|
|
200
|
+
yield {"type": "final_answer", "content": final}
|
|
201
|
+
self.messages.append({"role": "user", "content": user_input})
|
|
202
|
+
self.messages.append({"role": "assistant", "content": final})
|
|
203
|
+
|
|
204
|
+
def _format_result(self, tool: str, args: dict, result: str) -> str:
|
|
205
|
+
labels = {
|
|
206
|
+
"get_disk_space": "Ket qua kiem tra o dia",
|
|
207
|
+
"clean_temp_files": "Ket qua don rac he thong",
|
|
208
|
+
"get_system_info": "Thong tin he thong",
|
|
209
|
+
"install_software": "Cai dat phan mem",
|
|
210
|
+
"list_installed_software": "Danh sach phan mem",
|
|
211
|
+
"uninstall_software": "Go cai dat phan mem",
|
|
212
|
+
"browse_and_search": "Trinh duyet web",
|
|
213
|
+
"capture_web_screenshot": "Chup anh web",
|
|
214
|
+
"manage_file": "Quan ly file",
|
|
215
|
+
"find_large_files": "Tim file lon",
|
|
216
|
+
"run_and_debug_script": "Chay script",
|
|
217
|
+
"build_project": "Build project",
|
|
218
|
+
"capture_screen": "Chup man hinh",
|
|
219
|
+
"mouse_click": "Click chuot",
|
|
220
|
+
"keyboard_type": "Go phim",
|
|
221
|
+
"press_key": "Nhan phim",
|
|
222
|
+
"hotkey_combination": "To hop phim",
|
|
223
|
+
"execute_cmd": "Ket qua lenh",
|
|
224
|
+
}
|
|
225
|
+
label = labels.get(tool, "Ket qua")
|
|
226
|
+
return f"{label}:\n{result}"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Tool:
|
|
7
|
+
def __init__(self, fn: Callable, name: str = None, description: str = None):
|
|
8
|
+
self.fn = fn
|
|
9
|
+
self.name = name or fn.__name__
|
|
10
|
+
self.description = description or (fn.__doc__ or "").strip()
|
|
11
|
+
|
|
12
|
+
sig = inspect.signature(fn)
|
|
13
|
+
self.parameters = {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {},
|
|
16
|
+
"required": [],
|
|
17
|
+
}
|
|
18
|
+
for param_name, param in sig.parameters.items():
|
|
19
|
+
annotation = param.annotation
|
|
20
|
+
if annotation is inspect.Parameter.empty:
|
|
21
|
+
param_type = "string"
|
|
22
|
+
elif annotation is str:
|
|
23
|
+
param_type = "string"
|
|
24
|
+
elif annotation is int:
|
|
25
|
+
param_type = "integer"
|
|
26
|
+
elif annotation is bool:
|
|
27
|
+
param_type = "boolean"
|
|
28
|
+
elif annotation is float:
|
|
29
|
+
param_type = "number"
|
|
30
|
+
else:
|
|
31
|
+
param_type = "string"
|
|
32
|
+
|
|
33
|
+
self.parameters["properties"][param_name] = {
|
|
34
|
+
"type": param_type,
|
|
35
|
+
"description": f"Parameter {param_name}",
|
|
36
|
+
}
|
|
37
|
+
if param.default is inspect.Parameter.empty:
|
|
38
|
+
self.parameters["required"].append(param_name)
|
|
39
|
+
|
|
40
|
+
def to_openai_tool(self) -> dict:
|
|
41
|
+
return {
|
|
42
|
+
"type": "function",
|
|
43
|
+
"function": {
|
|
44
|
+
"name": self.name,
|
|
45
|
+
"description": self.description,
|
|
46
|
+
"parameters": self.parameters,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def run(self, **kwargs) -> Any:
|
|
51
|
+
return self.fn(**kwargs)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class FunctionRegistry:
|
|
55
|
+
def __init__(self):
|
|
56
|
+
self._tools: dict[str, Tool] = {}
|
|
57
|
+
|
|
58
|
+
def register(self, fn: Callable = None, *, name: str = None, description: str = None):
|
|
59
|
+
def wrapper(f: Callable):
|
|
60
|
+
tool = Tool(f, name=name, description=description)
|
|
61
|
+
self._tools[tool.name] = tool
|
|
62
|
+
return f
|
|
63
|
+
if fn is not None:
|
|
64
|
+
return wrapper(fn)
|
|
65
|
+
return wrapper
|
|
66
|
+
|
|
67
|
+
def get_tool(self, name: str) -> Tool | None:
|
|
68
|
+
return self._tools.get(name)
|
|
69
|
+
|
|
70
|
+
def list_tools(self) -> list[Tool]:
|
|
71
|
+
return list(self._tools.values())
|
|
72
|
+
|
|
73
|
+
def to_openai_tools(self) -> list[dict]:
|
|
74
|
+
return [t.to_openai_tool() for t in self._tools.values()]
|
|
75
|
+
|
|
76
|
+
def execute(self, name: str, arguments: dict) -> Any:
|
|
77
|
+
tool = self.get_tool(name)
|
|
78
|
+
if not tool:
|
|
79
|
+
return json.dumps({"error": f"Tool '{name}' not found"})
|
|
80
|
+
try:
|
|
81
|
+
result = tool.run(**arguments)
|
|
82
|
+
return str(result)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return json.dumps({"error": str(e)})
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import webbrowser
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from openclaw.core.agent import Agent
|
|
9
|
+
from openclaw.ui.server import run_server
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
13
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
14
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
15
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
SPINNER = r"-\|/|"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _spinner(stop, text):
|
|
22
|
+
i = 0
|
|
23
|
+
while not stop.is_set():
|
|
24
|
+
sym = SPINNER[i % len(SPINNER)]
|
|
25
|
+
print(f"\r{sym} {text}", end="", flush=True)
|
|
26
|
+
i += 1
|
|
27
|
+
time.sleep(0.1)
|
|
28
|
+
print(f"\r=> {text}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _run(cmd, timeout=120, shell=False):
|
|
32
|
+
if not shell and os.name == "nt":
|
|
33
|
+
shell = True
|
|
34
|
+
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, shell=shell)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _check_node() -> bool:
|
|
38
|
+
try:
|
|
39
|
+
r = _run("node --version", timeout=5)
|
|
40
|
+
return r.returncode == 0
|
|
41
|
+
except Exception:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _check_npm() -> bool:
|
|
46
|
+
try:
|
|
47
|
+
r = _run("npm --version", timeout=5)
|
|
48
|
+
return r.returncode == 0
|
|
49
|
+
except Exception:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _setup() -> bool:
|
|
54
|
+
# ---------- Node.js ----------
|
|
55
|
+
stop = threading.Event()
|
|
56
|
+
t = threading.Thread(target=_spinner, args=(stop, "Kiem tra Node.js..."), daemon=True)
|
|
57
|
+
t.start()
|
|
58
|
+
|
|
59
|
+
has_node = _check_node()
|
|
60
|
+
has_npm = _check_npm()
|
|
61
|
+
stop.set(); t.join()
|
|
62
|
+
|
|
63
|
+
if not has_node:
|
|
64
|
+
print("\r[!] Can Node.js: https://nodejs.org")
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
if not has_npm:
|
|
68
|
+
print("\r[!] Can npm (Node.js khong day du)")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
# ---------- AI Engine ----------
|
|
72
|
+
stop = threading.Event()
|
|
73
|
+
t = threading.Thread(target=_spinner, args=(stop, "Kiem tra AI engine..."), daemon=True)
|
|
74
|
+
t.start()
|
|
75
|
+
|
|
76
|
+
found = False
|
|
77
|
+
try:
|
|
78
|
+
r = _run("where opencode", timeout=5)
|
|
79
|
+
if r.returncode == 0 and r.stdout.strip():
|
|
80
|
+
found = True
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
if not found:
|
|
84
|
+
for p in [
|
|
85
|
+
r"C:\npm\prefix\node_modules\opencode-ai\bin\opencode.exe",
|
|
86
|
+
os.path.expanduser(r"~\AppData\Roaming\npm\node_modules\opencode-ai\bin\opencode.exe"),
|
|
87
|
+
]:
|
|
88
|
+
if os.path.isfile(p):
|
|
89
|
+
found = True
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
if not found:
|
|
93
|
+
stop.set(); t.join()
|
|
94
|
+
t = threading.Thread(target=_spinner, args=(threading.Event(), "Dang cai AI engine..."), daemon=True)
|
|
95
|
+
t.start()
|
|
96
|
+
try:
|
|
97
|
+
r = _run("npm i -g opencode-ai", timeout=180)
|
|
98
|
+
found = r.returncode == 0
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"\r[!] Loi cai dat AI engine: {e}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
stop.set(); t.join()
|
|
104
|
+
|
|
105
|
+
if not found:
|
|
106
|
+
print("\r[!] Khong tim thay AI engine")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
# ---------- Playwright browser ----------
|
|
110
|
+
stop = threading.Event()
|
|
111
|
+
t = threading.Thread(target=_spinner, args=(stop, "Kiem tra trinh duyet..."), daemon=True)
|
|
112
|
+
t.start()
|
|
113
|
+
|
|
114
|
+
need_install = False
|
|
115
|
+
try:
|
|
116
|
+
from playwright.sync_api import sync_playwright
|
|
117
|
+
with sync_playwright() as p:
|
|
118
|
+
b = p.chromium.launch(headless=True)
|
|
119
|
+
b.close()
|
|
120
|
+
except Exception:
|
|
121
|
+
need_install = True
|
|
122
|
+
|
|
123
|
+
if need_install:
|
|
124
|
+
stop.set(); t.join()
|
|
125
|
+
t = threading.Thread(target=_spinner, args=(threading.Event(), "Dang tai trinh duyet..."), daemon=True)
|
|
126
|
+
t.start()
|
|
127
|
+
try:
|
|
128
|
+
_run("playwright install chromium", timeout=300)
|
|
129
|
+
except Exception:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
stop.set(); t.join()
|
|
133
|
+
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def register_all_skills(agent: Agent):
|
|
138
|
+
from openclaw.skills.system import execute_cmd, get_disk_space, clean_temp_files, get_system_info
|
|
139
|
+
from openclaw.skills.software import install_software, list_installed_software, uninstall_software
|
|
140
|
+
from openclaw.skills.web import browse_and_search, capture_web_screenshot
|
|
141
|
+
from openclaw.skills.file_manager import manage_file, find_large_files
|
|
142
|
+
from openclaw.skills.debugger import run_and_debug_script, build_project
|
|
143
|
+
from openclaw.skills.os_control import capture_screen, mouse_click, keyboard_type, press_key, hotkey_combination
|
|
144
|
+
|
|
145
|
+
for fn in [
|
|
146
|
+
execute_cmd, get_disk_space, clean_temp_files, get_system_info,
|
|
147
|
+
install_software, list_installed_software, uninstall_software,
|
|
148
|
+
browse_and_search, capture_web_screenshot,
|
|
149
|
+
manage_file, find_large_files,
|
|
150
|
+
run_and_debug_script, build_project,
|
|
151
|
+
capture_screen, mouse_click, keyboard_type, press_key, hotkey_combination,
|
|
152
|
+
]:
|
|
153
|
+
agent.register_skill(fn)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def main():
|
|
157
|
+
print("AISTV - AI IT Agent cho Windows\n")
|
|
158
|
+
|
|
159
|
+
if not _setup():
|
|
160
|
+
sys.exit(1)
|
|
161
|
+
|
|
162
|
+
print("Khoi dong AI...")
|
|
163
|
+
agent = Agent()
|
|
164
|
+
register_all_skills(agent)
|
|
165
|
+
|
|
166
|
+
url = "http://127.0.0.1:8765"
|
|
167
|
+
print(f"San sang: {len(agent.registry.list_tools())} skills")
|
|
168
|
+
print(f"\nMo trinh duyet: {url}")
|
|
169
|
+
webbrowser.open(url)
|
|
170
|
+
run_server(agent, host="127.0.0.1", port=8765)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
main()
|
|
File without changes
|