clawbot-plus 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.
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clawbot-plus
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: đ¤ AI-Powered Browser Automation CLI â Automate your browser with AI
|
|
5
|
+
Project-URL: Homepage, https://github.com/clawbot/clawbot
|
|
6
|
+
Author: ClawBot
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: browser-use>=0.12.0
|
|
15
|
+
Requires-Dist: httpx>=0.27.0
|
|
16
|
+
Requires-Dist: inquirerpy>=0.3.4
|
|
17
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
18
|
+
Requires-Dist: rich>=13.0.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# đ¤ ClawBot â AI Browser Automation CLI
|
|
22
|
+
|
|
23
|
+
Apne browser ko AI se automate karo! Just one command:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install clawbot
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
clawbot
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- đ§ **6 AI Providers** â Gemini, OpenAI, Claude, Groq, DeepSeek, Ollama
|
|
38
|
+
- đ **Your Browser** â Use your own Chrome with logged-in accounts
|
|
39
|
+
- đ **API Key Management** â Enter once, saved permanently
|
|
40
|
+
- đ **Interactive Loop** â Keep giving tasks, switch AI anytime
|
|
41
|
+
- đģ **Cross Platform** â Windows, Mac, Linux
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Install
|
|
47
|
+
pip install clawbot
|
|
48
|
+
|
|
49
|
+
# Run
|
|
50
|
+
clawbot
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That's it! ClawBot will:
|
|
54
|
+
1. Ask which AI to use (Gemini, ChatGPT, Claude, etc.)
|
|
55
|
+
2. Ask which model
|
|
56
|
+
3. Ask for API key (saved permanently)
|
|
57
|
+
4. Auto-launch Chrome
|
|
58
|
+
5. Wait for your tasks!
|
|
59
|
+
|
|
60
|
+
## Example Tasks
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
đ¤ Task > open youtube and search for latest tech news
|
|
64
|
+
đ¤ Task > go to amazon and find best laptop under 50k
|
|
65
|
+
đ¤ Task > open gmail and check my latest emails
|
|
66
|
+
đ¤ Task > search google for weather in Delhi
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
| Command | Description |
|
|
72
|
+
|---------|-------------|
|
|
73
|
+
| `quit` | Exit ClawBot |
|
|
74
|
+
| `switch` | Change AI provider/model |
|
|
75
|
+
| `chrome` | Restart Chrome browser |
|
|
76
|
+
|
|
77
|
+
## Requirements
|
|
78
|
+
|
|
79
|
+
- Python 3.11+
|
|
80
|
+
- Google Chrome browser
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# đ¤ ClawBot â AI Browser Automation CLI
|
|
2
|
+
|
|
3
|
+
Apne browser ko AI se automate karo! Just one command:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install clawbot
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Then run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
clawbot
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- đ§ **6 AI Providers** â Gemini, OpenAI, Claude, Groq, DeepSeek, Ollama
|
|
18
|
+
- đ **Your Browser** â Use your own Chrome with logged-in accounts
|
|
19
|
+
- đ **API Key Management** â Enter once, saved permanently
|
|
20
|
+
- đ **Interactive Loop** â Keep giving tasks, switch AI anytime
|
|
21
|
+
- đģ **Cross Platform** â Windows, Mac, Linux
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install
|
|
27
|
+
pip install clawbot
|
|
28
|
+
|
|
29
|
+
# Run
|
|
30
|
+
clawbot
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That's it! ClawBot will:
|
|
34
|
+
1. Ask which AI to use (Gemini, ChatGPT, Claude, etc.)
|
|
35
|
+
2. Ask which model
|
|
36
|
+
3. Ask for API key (saved permanently)
|
|
37
|
+
4. Auto-launch Chrome
|
|
38
|
+
5. Wait for your tasks!
|
|
39
|
+
|
|
40
|
+
## Example Tasks
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
đ¤ Task > open youtube and search for latest tech news
|
|
44
|
+
đ¤ Task > go to amazon and find best laptop under 50k
|
|
45
|
+
đ¤ Task > open gmail and check my latest emails
|
|
46
|
+
đ¤ Task > search google for weather in Delhi
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Commands
|
|
50
|
+
|
|
51
|
+
| Command | Description |
|
|
52
|
+
|---------|-------------|
|
|
53
|
+
| `quit` | Exit ClawBot |
|
|
54
|
+
| `switch` | Change AI provider/model |
|
|
55
|
+
| `chrome` | Restart Chrome browser |
|
|
56
|
+
|
|
57
|
+
## Requirements
|
|
58
|
+
|
|
59
|
+
- Python 3.11+
|
|
60
|
+
- Google Chrome browser
|
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
"""
|
|
2
|
+
đ¤ ClawBot - Interactive Browser Automation CLI
|
|
3
|
+
Powered by browser-use + Your AI of Choice
|
|
4
|
+
|
|
5
|
+
Install: pip install clawbot
|
|
6
|
+
Run: clawbot
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import logging
|
|
16
|
+
from rich.live import Live
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
|
|
19
|
+
# Hide background timeout errors from browser-use/bubus frameworks
|
|
20
|
+
logging.getLogger('bubus').setLevel(logging.FATAL)
|
|
21
|
+
logging.getLogger('bubus.service').setLevel(logging.FATAL)
|
|
22
|
+
logging.getLogger('browser_use.browser').setLevel(logging.FATAL)
|
|
23
|
+
logging.getLogger('browser_use.browser.session').setLevel(logging.FATAL)
|
|
24
|
+
logging.getLogger('BrowserSession').setLevel(logging.FATAL)
|
|
25
|
+
|
|
26
|
+
class PanelLogHandler(logging.Handler):
|
|
27
|
+
def __init__(self, max_lines=10):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self.max_lines = max_lines
|
|
30
|
+
self.logs = [""] * max_lines # Pre-fill to keep height fixed
|
|
31
|
+
self.live = None
|
|
32
|
+
self.setFormatter(logging.Formatter('%(message)s'))
|
|
33
|
+
|
|
34
|
+
def emit(self, record):
|
|
35
|
+
try:
|
|
36
|
+
msg = self.format(record)
|
|
37
|
+
if not msg.strip(): return
|
|
38
|
+
|
|
39
|
+
# Clean up the output to look prettier
|
|
40
|
+
if "EVAL:" in msg.upper() or "EVALUATE:" in msg.upper():
|
|
41
|
+
msg = f"[bold green]{msg}[/bold green]"
|
|
42
|
+
elif "MEMORY:" in msg.upper():
|
|
43
|
+
msg = f"[bold magenta]{msg}[/bold magenta]"
|
|
44
|
+
elif "NEXT GOAL:" in msg.upper():
|
|
45
|
+
msg = f"[bold yellow]{msg}[/bold yellow]"
|
|
46
|
+
elif "ACTION:" in msg.upper() or "CLICK:" in msg.upper() or "NAVIGATE:" in msg.upper():
|
|
47
|
+
msg = f"[bold cyan]{msg}[/bold cyan]"
|
|
48
|
+
|
|
49
|
+
# Split multi-line messages so they don't break the height limit
|
|
50
|
+
for line in msg.split('\n'):
|
|
51
|
+
# Trim overly long lines to prevent text wrapping from expanding the box height
|
|
52
|
+
if len(line) > 110:
|
|
53
|
+
line = line[:107] + "..."
|
|
54
|
+
self.logs.append(line)
|
|
55
|
+
|
|
56
|
+
# Keep exactly max_lines
|
|
57
|
+
self.logs = self.logs[-self.max_lines:]
|
|
58
|
+
|
|
59
|
+
if self.live:
|
|
60
|
+
content = "\n".join(self.logs)
|
|
61
|
+
self.live.update(Panel(content, title="đ§ Agent", border_style="cyan", subtitle="[dim]Live Logs[/dim]", width=115))
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _get_env_file() -> Path:
|
|
67
|
+
"""Get the .env file path in user's home directory."""
|
|
68
|
+
env_dir = Path.home() / '.clawbot'
|
|
69
|
+
env_dir.mkdir(exist_ok=True)
|
|
70
|
+
return env_dir / '.env'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _load_env():
|
|
74
|
+
"""Load environment variables from .env file."""
|
|
75
|
+
from dotenv import load_dotenv
|
|
76
|
+
env_file = _get_env_file()
|
|
77
|
+
if env_file.exists():
|
|
78
|
+
load_dotenv(env_file)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _get_style():
|
|
82
|
+
"""Get InquirerPy style."""
|
|
83
|
+
from InquirerPy.utils import InquirerPyStyle
|
|
84
|
+
return InquirerPyStyle({
|
|
85
|
+
'pointer': '#00d4ff bold',
|
|
86
|
+
'highlighted': '#00d4ff bold',
|
|
87
|
+
'question': 'bold',
|
|
88
|
+
'answer': '#00d4ff bold',
|
|
89
|
+
'questionmark': '#ff6b35 bold',
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# âââ AI Providers & Models ââââââââââââââââââââââââââââââââââââââââââââ
|
|
94
|
+
PROVIDERS = {
|
|
95
|
+
'đĸ Google Gemini': {
|
|
96
|
+
'class': 'ChatGoogle',
|
|
97
|
+
'env_key': 'GOOGLE_API_KEY',
|
|
98
|
+
'models': [
|
|
99
|
+
'gemini-2.5-flash',
|
|
100
|
+
'gemini-2.5-pro',
|
|
101
|
+
'gemini-2.0-flash',
|
|
102
|
+
'gemini-flash-lite-latest',
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
'đĩ OpenAI (ChatGPT)': {
|
|
106
|
+
'class': 'ChatOpenAI',
|
|
107
|
+
'env_key': 'OPENAI_API_KEY',
|
|
108
|
+
'models': [
|
|
109
|
+
'gpt-4o',
|
|
110
|
+
'gpt-4o-mini',
|
|
111
|
+
'o3-mini',
|
|
112
|
+
'o4-mini',
|
|
113
|
+
'gpt-4.1-mini',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
'đ Kimi (Moonshot AI)': {
|
|
117
|
+
'class': 'ChatOpenAI',
|
|
118
|
+
'env_key': 'MOONSHOT_API_KEY',
|
|
119
|
+
'base_url': 'https://api.moonshot.cn/v1',
|
|
120
|
+
'models': [
|
|
121
|
+
'moonshot-v1-8k',
|
|
122
|
+
'moonshot-v1-32k',
|
|
123
|
+
'moonshot-v1-128k',
|
|
124
|
+
'moonshot-v1-auto',
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
'đ Anthropic (Claude)': {
|
|
128
|
+
'class': 'ChatAnthropic',
|
|
129
|
+
'env_key': 'ANTHROPIC_API_KEY',
|
|
130
|
+
'models': [
|
|
131
|
+
'claude-sonnet-4-20250514',
|
|
132
|
+
'claude-3-5-haiku-latest',
|
|
133
|
+
'claude-3-5-sonnet-latest',
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
'⥠Groq (Fast)': {
|
|
137
|
+
'class': 'ChatGroq',
|
|
138
|
+
'env_key': 'GROQ_API_KEY',
|
|
139
|
+
'models': [
|
|
140
|
+
'llama-3.3-70b-versatile',
|
|
141
|
+
'mixtral-8x7b-32768',
|
|
142
|
+
'gemma2-9b-it',
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
'đ DeepSeek': {
|
|
146
|
+
'class': 'ChatDeepSeek',
|
|
147
|
+
'env_key': 'DEEPSEEK_API_KEY',
|
|
148
|
+
'models': [
|
|
149
|
+
'deepseek-chat',
|
|
150
|
+
'deepseek-reasoner',
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
'đŠ NVIDIA NIM': {
|
|
154
|
+
'class': 'ChatOpenAI',
|
|
155
|
+
'env_key': 'NVIDIA_API_KEY',
|
|
156
|
+
'base_url': 'https://integrate.api.nvidia.com/v1',
|
|
157
|
+
'models': [
|
|
158
|
+
'meta/llama-3.2-90b-vision-instruct',
|
|
159
|
+
'meta/llama-3.2-11b-vision-instruct',
|
|
160
|
+
'meta/llama-3.1-405b-instruct',
|
|
161
|
+
'meta/llama-3.1-70b-instruct',
|
|
162
|
+
'meta/llama-3.3-70b-instruct',
|
|
163
|
+
'nvidia/llama-3.1-nemotron-70b-instruct',
|
|
164
|
+
'mistralai/mistral-large-2-instruct',
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
'đĻ Ollama (Local - Free)': {
|
|
168
|
+
'class': 'ChatOllama',
|
|
169
|
+
'env_key': None,
|
|
170
|
+
'models': [
|
|
171
|
+
'qwen3.5:cloud',
|
|
172
|
+
'qwen3.5:397b-cloud',
|
|
173
|
+
'llama3.3',
|
|
174
|
+
'llama3.2',
|
|
175
|
+
'llama3.1',
|
|
176
|
+
'llama3',
|
|
177
|
+
'deepseek-r1',
|
|
178
|
+
'kimi-k2.5:cloud',
|
|
179
|
+
'qwen3-coder:480b-cloud',
|
|
180
|
+
'qwen2.5',
|
|
181
|
+
'mistral',
|
|
182
|
+
'mixtral',
|
|
183
|
+
'gemma2',
|
|
184
|
+
'phi3',
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
'âī¸ Ollama (Cloud / Custom Host)': {
|
|
188
|
+
'class': 'ChatOllama',
|
|
189
|
+
'env_key': 'OLLAMA_BASE_URL',
|
|
190
|
+
'is_base_url_only': True,
|
|
191
|
+
'models': [
|
|
192
|
+
'qwen3.5:cloud',
|
|
193
|
+
'qwen3.5:397b-cloud',
|
|
194
|
+
'llama3.3',
|
|
195
|
+
'llama3.2',
|
|
196
|
+
'llama3.1',
|
|
197
|
+
'llama3',
|
|
198
|
+
'deepseek-r1',
|
|
199
|
+
'kimi-k2.5:cloud',
|
|
200
|
+
'qwen3-coder:480b-cloud',
|
|
201
|
+
'qwen2.5',
|
|
202
|
+
'mistral',
|
|
203
|
+
'mixtral',
|
|
204
|
+
'gemma2',
|
|
205
|
+
'phi3',
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def show_banner():
|
|
212
|
+
"""Show the ClawBot banner with ASCII art logo."""
|
|
213
|
+
import io
|
|
214
|
+
from rich.console import Console
|
|
215
|
+
from rich.panel import Panel
|
|
216
|
+
|
|
217
|
+
# Fix Windows terminal encoding for Unicode block characters
|
|
218
|
+
if os.name == 'nt':
|
|
219
|
+
os.system('chcp 65001 > nul 2>&1') # Switch console to UTF-8
|
|
220
|
+
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
|
221
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
|
222
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
|
223
|
+
|
|
224
|
+
console = Console(force_terminal=True)
|
|
225
|
+
|
|
226
|
+
logo = (
|
|
227
|
+
"[bold cyan] [/]\n"
|
|
228
|
+
"[bold cyan] .:--=--:....................:---::. [/]\n"
|
|
229
|
+
"[bold cyan] :=*#%%%%#+:.................:=*#%%##*-. [/]\n"
|
|
230
|
+
"[bold cyan] :*%%@@%*=:.....................:-+#@@%%+. [/]\n"
|
|
231
|
+
"[bold cyan] -%%@%+:...........................:=#%%#: [/]\n"
|
|
232
|
+
"[bold cyan] .+%+:..:::::...::::::::::::::::::::.:=#=. [/]\n"
|
|
233
|
+
"[bold cyan] .-:::::::::----:::::::::--=--:::::::::. [/]\n"
|
|
234
|
+
"[bold cyan] .::::::::-+*####*=:::::-*#####*+-:::::::. [/]\n"
|
|
235
|
+
"[bold cyan] .:::::::-+*####%###-----+%%%%#####+-::::::. [/]\n"
|
|
236
|
+
"[bold cyan] .::::::-*#####@@%%+-----=#%%@%%%%##+------: [/]\n"
|
|
237
|
+
"[bold cyan] .::::::-*###%%%%*=--+**+-=*%%%%%%%%*------- [/]\n"
|
|
238
|
+
"[bold cyan] .:::::::-+##%##+---=+%#+===+*%%%%%*=====--: [/]\n"
|
|
239
|
+
"[bold cyan] .:::::::::-===----=++++++====++*++=======-. [/]\n"
|
|
240
|
+
"[bold cyan] :-::::----------=======+++++++++++++====+. [/]\n"
|
|
241
|
+
"[bold cyan] :*#=:----------=======+++++++****++++==+#%=. [/]\n"
|
|
242
|
+
"[bold cyan] =%%%+--------=======+++++********+++++*%@%#. [/]\n"
|
|
243
|
+
"[bold cyan] :#%@@*-----======+++++*************++*@@@%+. [/]\n"
|
|
244
|
+
"[bold cyan] .:-:...:-=====+++++*****####****+=:..:--: [/]\n"
|
|
245
|
+
"[bold cyan] ..:+*+++***###########=:. [/]\n"
|
|
246
|
+
"[bold cyan] #@%%%%+-====-%@@@@@: [/]\n"
|
|
247
|
+
"[bold cyan] =%@@@@: *@@@@#. [/]\n"
|
|
248
|
+
"[bold cyan] .:---: .:--:. [/]\n"
|
|
249
|
+
"\n"
|
|
250
|
+
"[bold #00d4ff] ââââââââââ ââââââ âââ ââââââââââ âââââââ âââââââââ[/]\n"
|
|
251
|
+
"[bold #00d4ff] âââââââââââ âââââââââââ âââââââââââââââââââââââââââââ[/]\n"
|
|
252
|
+
"[bold #00d4ff] âââ âââ âââââââââââ ââ ââââââââââââââ âââ âââ [/]\n"
|
|
253
|
+
"[bold #00d4ff] âââ âââ âââââââââââââââââââââââââââââ âââ âââ [/]\n"
|
|
254
|
+
"[bold #00d4ff] âââââââââââââââââââ ââââââââââââââââââââââââââââââ âââ [/]\n"
|
|
255
|
+
"[bold #00d4ff] ââââââââââââââââââ âââ ââââââââ âââââââ âââââââ âââ [/]\n"
|
|
256
|
+
"\n"
|
|
257
|
+
"[dim italic] đ¤ AI-Powered Browser & Computer Automation[/]\n"
|
|
258
|
+
"[dim] v1.0 âĸ pip install clawbot[/]"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
console.print(Panel(
|
|
262
|
+
logo,
|
|
263
|
+
border_style='#00d4ff',
|
|
264
|
+
padding=(1, 2),
|
|
265
|
+
))
|
|
266
|
+
console.print()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def select_provider() -> tuple[str, dict]:
|
|
270
|
+
"""Let user pick an AI provider."""
|
|
271
|
+
from InquirerPy import inquirer
|
|
272
|
+
from InquirerPy.base.control import Choice
|
|
273
|
+
|
|
274
|
+
choices = [Choice(name=name, value=name) for name in PROVIDERS]
|
|
275
|
+
|
|
276
|
+
provider_name = inquirer.select(
|
|
277
|
+
message='đ§ AI Provider choose karo:',
|
|
278
|
+
choices=choices,
|
|
279
|
+
style=_get_style(),
|
|
280
|
+
).execute()
|
|
281
|
+
|
|
282
|
+
if provider_name is None:
|
|
283
|
+
sys.exit(0)
|
|
284
|
+
|
|
285
|
+
return provider_name, PROVIDERS[provider_name]
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def select_model(provider_info: dict) -> str:
|
|
289
|
+
"""Let user pick a model from the selected provider."""
|
|
290
|
+
from InquirerPy import inquirer
|
|
291
|
+
from InquirerPy.base.control import Choice
|
|
292
|
+
|
|
293
|
+
choices = [Choice(name=m, value=m) for m in provider_info['models']]
|
|
294
|
+
|
|
295
|
+
model = inquirer.select(
|
|
296
|
+
message='đĻ Model choose karo:',
|
|
297
|
+
choices=choices,
|
|
298
|
+
style=_get_style(),
|
|
299
|
+
).execute()
|
|
300
|
+
|
|
301
|
+
if model is None:
|
|
302
|
+
sys.exit(0)
|
|
303
|
+
|
|
304
|
+
return model
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def get_api_key(provider_info: dict) -> str | None:
|
|
308
|
+
"""Get API key â load from saved config or ask user."""
|
|
309
|
+
from InquirerPy import inquirer
|
|
310
|
+
from rich.console import Console
|
|
311
|
+
|
|
312
|
+
console = Console()
|
|
313
|
+
env_key = provider_info.get('env_key')
|
|
314
|
+
|
|
315
|
+
if env_key is None:
|
|
316
|
+
console.print('[green]â
Ollama local hai â API key ki zaroorat nahi![/green]')
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
# Check if already set
|
|
320
|
+
existing_key = os.getenv(env_key)
|
|
321
|
+
if existing_key and existing_key != 'your-api-key-here':
|
|
322
|
+
masked = existing_key[:8] + '...' + existing_key[-4:]
|
|
323
|
+
console.print(f'[green]â
{env_key} found:[/green] {masked}')
|
|
324
|
+
|
|
325
|
+
use_existing = inquirer.confirm(
|
|
326
|
+
message='Yahi key use karni hai?',
|
|
327
|
+
default=True,
|
|
328
|
+
style=_get_style(),
|
|
329
|
+
).execute()
|
|
330
|
+
|
|
331
|
+
if use_existing:
|
|
332
|
+
return existing_key
|
|
333
|
+
|
|
334
|
+
# Ask for new key
|
|
335
|
+
is_base_url = provider_info.get('is_base_url_only', False)
|
|
336
|
+
prompt_msg = f'đ {env_key} enter karo (e.g. http://192.168.1.100:11434):' if is_base_url else f'đ {env_key} enter karo:'
|
|
337
|
+
|
|
338
|
+
api_key_input = inquirer.secret(
|
|
339
|
+
message=prompt_msg,
|
|
340
|
+
style=_get_style(),
|
|
341
|
+
).execute() if not is_base_url else inquirer.text(
|
|
342
|
+
message=prompt_msg,
|
|
343
|
+
style=_get_style(),
|
|
344
|
+
).execute()
|
|
345
|
+
|
|
346
|
+
if not api_key_input:
|
|
347
|
+
console.print('[red]â Required hai![/red]')
|
|
348
|
+
sys.exit(1)
|
|
349
|
+
|
|
350
|
+
# Save to config file (~/.clawbot/.env)
|
|
351
|
+
from dotenv import set_key
|
|
352
|
+
env_file = _get_env_file()
|
|
353
|
+
env_file.touch(exist_ok=True)
|
|
354
|
+
set_key(str(env_file), env_key, api_key_input)
|
|
355
|
+
os.environ[env_key] = api_key_input
|
|
356
|
+
console.print(f'[green]â
{env_key} saved to ~/.clawbot/.env[/green]')
|
|
357
|
+
|
|
358
|
+
return api_key_input
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def create_llm(provider_info: dict, model: str, api_key: str | None):
|
|
362
|
+
"""Create the LLM instance."""
|
|
363
|
+
class_name = provider_info['class']
|
|
364
|
+
from browser_use import llm as llm_module
|
|
365
|
+
chat_class = getattr(llm_module, class_name)
|
|
366
|
+
|
|
367
|
+
kwargs = {'model': model}
|
|
368
|
+
|
|
369
|
+
url_param = 'host' if class_name == 'ChatOllama' else 'base_url'
|
|
370
|
+
|
|
371
|
+
if api_key:
|
|
372
|
+
if provider_info.get('is_base_url_only'):
|
|
373
|
+
kwargs[url_param] = api_key
|
|
374
|
+
else:
|
|
375
|
+
kwargs['api_key'] = api_key
|
|
376
|
+
|
|
377
|
+
if 'base_url' in provider_info and not provider_info.get('is_base_url_only'):
|
|
378
|
+
kwargs[url_param] = provider_info['base_url']
|
|
379
|
+
|
|
380
|
+
return chat_class(**kwargs)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def ensure_chrome_running() -> bool:
|
|
384
|
+
"""Make sure Chrome is running with debugging port."""
|
|
385
|
+
import httpx
|
|
386
|
+
from InquirerPy import inquirer
|
|
387
|
+
from rich.console import Console
|
|
388
|
+
|
|
389
|
+
console = Console()
|
|
390
|
+
|
|
391
|
+
is_running = False
|
|
392
|
+
try:
|
|
393
|
+
resp = httpx.get('http://127.0.0.1:9222/json/version', timeout=1.0)
|
|
394
|
+
if resp.status_code == 200:
|
|
395
|
+
is_running = True
|
|
396
|
+
console.print('[yellow]â ī¸ Ek purani Chrome session abhi bhi chal rahi hai.[/yellow]')
|
|
397
|
+
except Exception:
|
|
398
|
+
console.print('[yellow]â ī¸ Chrome debugging mode mein nahi chal raha[/yellow]')
|
|
399
|
+
|
|
400
|
+
prompt_msg = 'Chrome ko refresh/restart karein (recommended)?' if is_running else 'Chrome auto-launch karein port 9222 par?'
|
|
401
|
+
launch = inquirer.confirm(
|
|
402
|
+
message=prompt_msg,
|
|
403
|
+
default=True,
|
|
404
|
+
style=_get_style(),
|
|
405
|
+
).execute()
|
|
406
|
+
|
|
407
|
+
if not launch:
|
|
408
|
+
console.print('[red]â Chrome ke bina agent nahi chalega![/red]')
|
|
409
|
+
console.print('[dim]Manual: chrome --remote-debugging-port=9222[/dim]')
|
|
410
|
+
return False
|
|
411
|
+
|
|
412
|
+
console.print('[dim]Chrome restart ho raha hai...[/dim]')
|
|
413
|
+
|
|
414
|
+
if sys.platform == 'win32':
|
|
415
|
+
subprocess.run(['taskkill', '/F', '/IM', 'chrome.exe'],
|
|
416
|
+
capture_output=True, text=True)
|
|
417
|
+
time.sleep(2)
|
|
418
|
+
chrome_paths = [
|
|
419
|
+
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
|
420
|
+
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
|
|
421
|
+
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe'),
|
|
422
|
+
]
|
|
423
|
+
chrome_exe = next((p for p in chrome_paths if os.path.exists(p)), None)
|
|
424
|
+
if not chrome_exe:
|
|
425
|
+
console.print('[red]â Chrome nahi mila![/red]')
|
|
426
|
+
return False
|
|
427
|
+
temp_profile = os.path.join(os.environ.get('TEMP', '/tmp'), 'clawbot-chrome')
|
|
428
|
+
subprocess.Popen([chrome_exe, '--remote-debugging-port=9222',
|
|
429
|
+
f'--user-data-dir={temp_profile}'])
|
|
430
|
+
elif sys.platform == 'darwin':
|
|
431
|
+
subprocess.run(['pkill', '-f', 'Google Chrome'], capture_output=True)
|
|
432
|
+
time.sleep(2)
|
|
433
|
+
temp_profile = '/tmp/clawbot-chrome'
|
|
434
|
+
subprocess.Popen(['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
435
|
+
'--remote-debugging-port=9222', f'--user-data-dir={temp_profile}'])
|
|
436
|
+
else:
|
|
437
|
+
subprocess.run(['pkill', '-f', 'chrome'], capture_output=True)
|
|
438
|
+
time.sleep(2)
|
|
439
|
+
temp_profile = '/tmp/clawbot-chrome'
|
|
440
|
+
subprocess.Popen(['google-chrome', '--remote-debugging-port=9222',
|
|
441
|
+
f'--user-data-dir={temp_profile}'])
|
|
442
|
+
|
|
443
|
+
console.print('[dim]Chrome start ho raha hai...[/dim]')
|
|
444
|
+
for _ in range(10):
|
|
445
|
+
time.sleep(1)
|
|
446
|
+
try:
|
|
447
|
+
resp = httpx.get('http://127.0.0.1:9222/json/version', timeout=2.0)
|
|
448
|
+
if resp.status_code == 200:
|
|
449
|
+
data = resp.json()
|
|
450
|
+
browser = data.get('Browser', 'Unknown')
|
|
451
|
+
console.print(f'[green]â
Chrome launched:[/green] {browser}')
|
|
452
|
+
return True
|
|
453
|
+
except Exception:
|
|
454
|
+
pass
|
|
455
|
+
|
|
456
|
+
console.print('[red]â Chrome start nahi hua 10 seconds mein[/red]')
|
|
457
|
+
return False
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
async def run_task(llm, task: str):
|
|
461
|
+
"""Run a single browser task."""
|
|
462
|
+
from browser_use import Agent, BrowserSession
|
|
463
|
+
from rich.console import Console
|
|
464
|
+
from rich.panel import Panel
|
|
465
|
+
|
|
466
|
+
console = Console()
|
|
467
|
+
session = BrowserSession.from_existing_browser(port=9222)
|
|
468
|
+
|
|
469
|
+
# Determine Vision compatibility
|
|
470
|
+
# Vision-capable models get live screen watching (like Google Assistant)
|
|
471
|
+
# for faster, more accurate task completion
|
|
472
|
+
use_vision = True
|
|
473
|
+
provider_name = getattr(llm, '__module__', '')
|
|
474
|
+
model_name = getattr(llm, 'model_name', '') or str(getattr(llm, 'model', ''))
|
|
475
|
+
|
|
476
|
+
# Vision-first models: always keep vision ON for best accuracy
|
|
477
|
+
# qwen3.5, gemini, gpt-4o, claude etc. are all vision-capable
|
|
478
|
+
vision_first_models = ['qwen3.5', 'qwen3', 'gemini', 'gpt-4o', 'claude', 'kimi']
|
|
479
|
+
is_vision_model = any(vm in model_name.lower() for vm in vision_first_models)
|
|
480
|
+
|
|
481
|
+
if not is_vision_model:
|
|
482
|
+
# Only forcefully disable for known text-only models
|
|
483
|
+
if 'vision' not in model_name.lower():
|
|
484
|
+
if 'nvidia' in provider_name.lower() or ('llama' in model_name.lower() and 'ollama' not in provider_name.lower()):
|
|
485
|
+
use_vision = False
|
|
486
|
+
|
|
487
|
+
agent = Agent(task=task, llm=llm, browser_session=session, use_vision=use_vision)
|
|
488
|
+
|
|
489
|
+
vision_msg = "" if use_vision else " [dim](vision disabled)[/dim]"
|
|
490
|
+
console.print(f'\n[bold #00d4ff]đ Running:[/bold #00d4ff] {task}{vision_msg}\n')
|
|
491
|
+
|
|
492
|
+
# Intercept agent logs for the UI Panel
|
|
493
|
+
agent_logger = logging.getLogger('browser_use')
|
|
494
|
+
handler = PanelLogHandler(max_lines=12)
|
|
495
|
+
old_handlers = agent_logger.handlers.copy()
|
|
496
|
+
old_propagate = agent_logger.propagate
|
|
497
|
+
|
|
498
|
+
for h in old_handlers:
|
|
499
|
+
agent_logger.removeHandler(h)
|
|
500
|
+
agent_logger.addHandler(handler)
|
|
501
|
+
agent_logger.propagate = False
|
|
502
|
+
|
|
503
|
+
try:
|
|
504
|
+
with Live(Panel("Initializing...", title="đ§ Agent Thinking...", border_style="cyan", subtitle="[dim]Live Logs[/dim]"), refresh_per_second=4) as live:
|
|
505
|
+
handler.live = live
|
|
506
|
+
result = await agent.run()
|
|
507
|
+
|
|
508
|
+
console.print(f'\n[green]â
Done![/green]')
|
|
509
|
+
if result and hasattr(result, 'all_results'):
|
|
510
|
+
for r in result.all_results:
|
|
511
|
+
if r.extracted_content:
|
|
512
|
+
console.print(Panel(str(r.extracted_content),
|
|
513
|
+
title='đ Result', border_style='green'))
|
|
514
|
+
except KeyboardInterrupt:
|
|
515
|
+
console.print('\n[yellow]âšī¸ Task cancelled[/yellow]')
|
|
516
|
+
except Exception as e:
|
|
517
|
+
error_msg = str(e)
|
|
518
|
+
console.print(f'\n[bold red]â Task Error:[/bold red] [red]{error_msg}[/red]\n')
|
|
519
|
+
|
|
520
|
+
# Smart Error Interpreter for User-Friendly Tips
|
|
521
|
+
if '429' in error_msg or 'RESOURCE_EXHAUSTED' in error_msg.upper() or 'rate limit' in error_msg.lower():
|
|
522
|
+
console.print(Panel(
|
|
523
|
+
"[bold yellow]TIPS TO FIX:[/bold yellow]\n\n"
|
|
524
|
+
"âĸ Aapka free API quota (limit) khatam ho gaya hai ya aapne bohut saari requests ek sath bhej di hain.\n"
|
|
525
|
+
"âĸ **Solution:** 5-10 minute wait karein aur wapas try karein, YA naya free provider select karein (jaise NVIDIA NIM ya Kimi).",
|
|
526
|
+
title="â ī¸ Rate Limit Exceeded (429)", border_style="yellow"
|
|
527
|
+
))
|
|
528
|
+
elif '401' in error_msg or 'unauthorized' in error_msg.lower() or 'invalid_api_key' in error_msg.lower():
|
|
529
|
+
console.print(Panel(
|
|
530
|
+
"[bold yellow]TIPS TO FIX:[/bold yellow]\n\n"
|
|
531
|
+
"âĸ Aapki API key galat hai ya expire ho chuki hai.\n"
|
|
532
|
+
"âĸ **Solution:** Aapki key `~/.clawbot/.env` me save stithi me hai. Usko manual delete karke naya setup karein ya dusra API key daalein.",
|
|
533
|
+
title="đ Authentication Error (401)", border_style="yellow"
|
|
534
|
+
))
|
|
535
|
+
elif '404' in error_msg or 'not found' in error_msg.lower():
|
|
536
|
+
console.print(Panel(
|
|
537
|
+
"[bold yellow]TIPS TO FIX:[/bold yellow]\n\n"
|
|
538
|
+
"âĸ Jo Model aapne select kiya hai, wo provider ke paas available nahi hai ya uska server down hai.\n"
|
|
539
|
+
"âĸ **Solution:** List me se koi dusra Model select karein.",
|
|
540
|
+
title="đ Model/Endpoint Not Found (404)", border_style="yellow"
|
|
541
|
+
))
|
|
542
|
+
finally:
|
|
543
|
+
# Restore original loggers
|
|
544
|
+
agent_logger.removeHandler(handler)
|
|
545
|
+
for h in old_handlers:
|
|
546
|
+
agent_logger.addHandler(h)
|
|
547
|
+
agent_logger.propagate = old_propagate
|
|
548
|
+
await session.stop()
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def main():
|
|
552
|
+
"""Main CLI entry point â this is what runs when you type 'clawbot'."""
|
|
553
|
+
import atexit
|
|
554
|
+
import logging as _logging
|
|
555
|
+
atexit.register(_logging.shutdown)
|
|
556
|
+
|
|
557
|
+
from rich.console import Console
|
|
558
|
+
from rich.panel import Panel
|
|
559
|
+
|
|
560
|
+
console = Console()
|
|
561
|
+
|
|
562
|
+
# Load saved config
|
|
563
|
+
_load_env()
|
|
564
|
+
|
|
565
|
+
show_banner()
|
|
566
|
+
|
|
567
|
+
telegram_bot_running = False
|
|
568
|
+
|
|
569
|
+
try:
|
|
570
|
+
from InquirerPy import inquirer
|
|
571
|
+
from InquirerPy.base.control import Choice
|
|
572
|
+
|
|
573
|
+
# ââ Step 0: Connect Menu ââ
|
|
574
|
+
env_file = _get_env_file()
|
|
575
|
+
has_token = bool(os.environ.get('TELEGRAM_BOT_TOKEN', ''))
|
|
576
|
+
|
|
577
|
+
# First ask: Telegram ya Continue?
|
|
578
|
+
first_choice = inquirer.select(
|
|
579
|
+
message='Kya karna chahte hain?',
|
|
580
|
+
choices=[
|
|
581
|
+
Choice(value='telegram', name='đą Telegram Connect'),
|
|
582
|
+
Choice(value='continue', name='âī¸ Continue (skip)'),
|
|
583
|
+
],
|
|
584
|
+
style=_get_style(),
|
|
585
|
+
).execute()
|
|
586
|
+
|
|
587
|
+
if first_choice == 'telegram':
|
|
588
|
+
# Show Telegram sub-menu with connection status
|
|
589
|
+
if has_token:
|
|
590
|
+
console.print(f'\n[green]â
Telegram Bot connected[/green] (Token saved in .env)\n')
|
|
591
|
+
|
|
592
|
+
connect_choice = inquirer.select(
|
|
593
|
+
message='Telegram Bot connect karna chahte hain?',
|
|
594
|
+
choices=[
|
|
595
|
+
Choice(value='skip', name='âī¸ Continue (skip)' + (' â already connected' if has_token else '')),
|
|
596
|
+
Choice(value='connect', name='đą Connect Telegram Bot' + (' â update credentials' if has_token else '')),
|
|
597
|
+
],
|
|
598
|
+
style=_get_style(),
|
|
599
|
+
).execute()
|
|
600
|
+
|
|
601
|
+
if connect_choice == 'connect':
|
|
602
|
+
console.print('\n[bold cyan]đą Telegram Bot Setup[/bold cyan]')
|
|
603
|
+
console.print('[dim]Step 1: @BotFather ko /newbot bhejo â Token milega[/dim]')
|
|
604
|
+
console.print('[dim]Step 2: @userinfobot ko message bhejo â Chat ID milega[/dim]\n')
|
|
605
|
+
|
|
606
|
+
bot_token = inquirer.text(
|
|
607
|
+
message='Telegram Bot Token paste karo:',
|
|
608
|
+
style=_get_style(),
|
|
609
|
+
).execute().strip()
|
|
610
|
+
|
|
611
|
+
chat_id = inquirer.text(
|
|
612
|
+
message='Telegram Chat ID paste karo:',
|
|
613
|
+
style=_get_style(),
|
|
614
|
+
).execute().strip()
|
|
615
|
+
|
|
616
|
+
if bot_token:
|
|
617
|
+
# Save to .env
|
|
618
|
+
existing = ''
|
|
619
|
+
if env_file.exists():
|
|
620
|
+
existing = env_file.read_text()
|
|
621
|
+
|
|
622
|
+
# Remove old telegram keys if present
|
|
623
|
+
lines = existing.splitlines()
|
|
624
|
+
lines = [l for l in lines if not l.startswith('TELEGRAM_BOT_TOKEN') and not l.startswith('TELEGRAM_CHAT_ID')]
|
|
625
|
+
lines.append(f"TELEGRAM_BOT_TOKEN='{bot_token}'")
|
|
626
|
+
if chat_id:
|
|
627
|
+
lines.append(f"TELEGRAM_CHAT_ID='{chat_id}'")
|
|
628
|
+
|
|
629
|
+
env_file.write_text('\n'.join(lines) + '\n')
|
|
630
|
+
os.environ['TELEGRAM_BOT_TOKEN'] = bot_token
|
|
631
|
+
if chat_id:
|
|
632
|
+
os.environ['TELEGRAM_CHAT_ID'] = chat_id
|
|
633
|
+
has_token = True
|
|
634
|
+
console.print(f'[green]â
Telegram credentials saved to {env_file}[/green]\n')
|
|
635
|
+
else:
|
|
636
|
+
console.print('[yellow]â ī¸ Token empty tha, skip kar rahe hain[/yellow]\n')
|
|
637
|
+
|
|
638
|
+
# ââ Step 1: Scope Selection ââ
|
|
639
|
+
scope = inquirer.select(
|
|
640
|
+
message='Kisko control karna hai aaj?',
|
|
641
|
+
choices=[
|
|
642
|
+
Choice(value='browser', name='đ Browser (Web Automation)'),
|
|
643
|
+
Choice(value='computer', name='đģ Computer (Windows OS Control)'),
|
|
644
|
+
Choice(value='both', name='đ Both Control (Computer + Telegram Bot)'),
|
|
645
|
+
],
|
|
646
|
+
style=_get_style(),
|
|
647
|
+
).execute()
|
|
648
|
+
|
|
649
|
+
is_computer = scope in ('computer', 'both')
|
|
650
|
+
|
|
651
|
+
# If 'Both Control', start Telegram bot in background
|
|
652
|
+
if scope == 'both':
|
|
653
|
+
if not has_token:
|
|
654
|
+
console.print('[bold red]â Pehle Telegram Bot connect karo! Token set nahi hai.[/bold red]')
|
|
655
|
+
console.print('[dim]ClawBot restart karo aur "Connect Telegram Bot" select karo.[/dim]')
|
|
656
|
+
sys.exit(1)
|
|
657
|
+
|
|
658
|
+
bot_path = Path(__file__).parent.parent.parent / "computer" / "telegram_bot.py"
|
|
659
|
+
if not bot_path.exists():
|
|
660
|
+
console.print(f'[bold red]â telegram_bot.py not found at: {bot_path}[/bold red]')
|
|
661
|
+
sys.exit(1)
|
|
662
|
+
|
|
663
|
+
subprocess.Popen(
|
|
664
|
+
[sys.executable, str(bot_path)],
|
|
665
|
+
cwd=str(bot_path.parent),
|
|
666
|
+
creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == 'nt' else 0
|
|
667
|
+
)
|
|
668
|
+
telegram_bot_running = True
|
|
669
|
+
console.print('[bold green]â
Telegram Bot background mein chalu ho gaya![/bold green]')
|
|
670
|
+
console.print('[dim]Ab phone se bhi task de sakte hain, aur yahan CLI se bhi.[/dim]\n')
|
|
671
|
+
|
|
672
|
+
mode_label = 'Both (Computer + browser)' if scope == 'both' else ('Computer' if is_computer else 'Browser')
|
|
673
|
+
console.print(f'[#00d4ff]Mode:[/#00d4ff] {mode_label}\n')
|
|
674
|
+
|
|
675
|
+
# ââ Step 2: Select AI Provider ââ
|
|
676
|
+
provider_name, provider_info = select_provider()
|
|
677
|
+
console.print(f'[#00d4ff]Selected:[/#00d4ff] {provider_name}\n')
|
|
678
|
+
|
|
679
|
+
# ââ Step 3: Select Model ââ
|
|
680
|
+
model = select_model(provider_info)
|
|
681
|
+
console.print(f'[#00d4ff]Model:[/#00d4ff] {model}\n')
|
|
682
|
+
|
|
683
|
+
# ââ Step 4: API Key ââ
|
|
684
|
+
api_key = get_api_key(provider_info)
|
|
685
|
+
console.print()
|
|
686
|
+
|
|
687
|
+
# ââ Step 4.5: Ollama Web Search API Key (for Computer mode) ââ
|
|
688
|
+
if is_computer and 'Ollama' in provider_name:
|
|
689
|
+
ollama_api_key = os.getenv('OLLAMA_API_KEY', '')
|
|
690
|
+
if ollama_api_key:
|
|
691
|
+
console.print(f'[green]â
Ollama Web Search API Key found[/green] (web_search enabled)\n')
|
|
692
|
+
else:
|
|
693
|
+
setup_web = inquirer.confirm(
|
|
694
|
+
message='đ Ollama Web Search enable karna hai? (free account se key milegi)',
|
|
695
|
+
default=True,
|
|
696
|
+
style=_get_style(),
|
|
697
|
+
).execute()
|
|
698
|
+
if setup_web:
|
|
699
|
+
ollama_key_input = inquirer.text(
|
|
700
|
+
message='đ Ollama API Key paste karo (ollama.com se):',
|
|
701
|
+
style=_get_style(),
|
|
702
|
+
).execute().strip()
|
|
703
|
+
if ollama_key_input:
|
|
704
|
+
from dotenv import set_key
|
|
705
|
+
set_key(str(env_file), 'OLLAMA_API_KEY', ollama_key_input)
|
|
706
|
+
os.environ['OLLAMA_API_KEY'] = ollama_key_input
|
|
707
|
+
console.print(f'[green]â
OLLAMA_API_KEY saved! Web search enabled.[/green]\n')
|
|
708
|
+
else:
|
|
709
|
+
console.print('[yellow]â ī¸ Key empty â web search disabled[/yellow]\n')
|
|
710
|
+
else:
|
|
711
|
+
console.print('[dim]Web search skipped. Agent browser se search karega.[/dim]\n')
|
|
712
|
+
|
|
713
|
+
# ââ Step 5: Create LLM ââ
|
|
714
|
+
try:
|
|
715
|
+
llm = create_llm(provider_info, model, api_key)
|
|
716
|
+
console.print(f'[green]â
AI ready![/green] {provider_info["class"]}({model})\n')
|
|
717
|
+
except Exception as e:
|
|
718
|
+
console.print(f'[red]â LLM create nahi hua: {e}[/red]')
|
|
719
|
+
sys.exit(1)
|
|
720
|
+
|
|
721
|
+
# ââ Step 6: Ensure Chrome is running (Only for Browser) ââ
|
|
722
|
+
if not is_computer:
|
|
723
|
+
if not ensure_chrome_running():
|
|
724
|
+
sys.exit(1)
|
|
725
|
+
|
|
726
|
+
except KeyboardInterrupt:
|
|
727
|
+
console.print('\n[yellow]âšī¸ ClawBot setup cancelled[/yellow]')
|
|
728
|
+
sys.exit(0)
|
|
729
|
+
|
|
730
|
+
console.print()
|
|
731
|
+
|
|
732
|
+
# Step 7: Interactive Task Loop
|
|
733
|
+
tg_status = ' | [bold green]đą Telegram Bot Active[/bold green]' if telegram_bot_running else ''
|
|
734
|
+
console.print(Panel(
|
|
735
|
+
'[bold]Commands:[/bold]\n'
|
|
736
|
+
' âĸ Task type karo aur Enter dabaao\n'
|
|
737
|
+
' âĸ [bold yellow]Ctrl+C[/bold yellow] â sirf current task rok do (ClawBot chal ta rahega!)\n'
|
|
738
|
+
' âĸ [dim]quit[/dim] / [dim]exit[/dim] â ClawBot poora band karo\n'
|
|
739
|
+
' âĸ [dim]switch[/dim] â AI provider/model badlo\n'
|
|
740
|
+
' âĸ [dim]chrome[/dim] â Chrome restart karo',
|
|
741
|
+
title=f'[bold #00d4ff]đ¤ ClawBot ({mode_label}) Ready!{tg_status}[/bold #00d4ff]',
|
|
742
|
+
border_style='#00d4ff',
|
|
743
|
+
padding=(1, 2),
|
|
744
|
+
))
|
|
745
|
+
console.print()
|
|
746
|
+
|
|
747
|
+
while True:
|
|
748
|
+
try:
|
|
749
|
+
task = console.input('[bold #ff6b35]đ¤ Task > [/bold #ff6b35]').strip()
|
|
750
|
+
|
|
751
|
+
if not task:
|
|
752
|
+
continue
|
|
753
|
+
|
|
754
|
+
if task.lower() in ('quit', 'exit', 'q', 'band karo'):
|
|
755
|
+
console.print('\n[dim]đ Bye! Browser open rahega.[/dim]')
|
|
756
|
+
break
|
|
757
|
+
|
|
758
|
+
if task.lower() == 'switch':
|
|
759
|
+
provider_name, provider_info = select_provider()
|
|
760
|
+
model = select_model(provider_info)
|
|
761
|
+
api_key = get_api_key(provider_info)
|
|
762
|
+
llm = create_llm(provider_info, model, api_key)
|
|
763
|
+
console.print(f'[green]â
Switched to {model}[/green]\n')
|
|
764
|
+
continue
|
|
765
|
+
|
|
766
|
+
if task.lower() == 'chrome':
|
|
767
|
+
if not is_computer:
|
|
768
|
+
ensure_chrome_running()
|
|
769
|
+
else:
|
|
770
|
+
console.print('\n[yellow]â ī¸ Aap Computer OS mode me hain, Chrome ki zarurat nahi![/yellow]\n')
|
|
771
|
+
continue
|
|
772
|
+
|
|
773
|
+
try:
|
|
774
|
+
if is_computer:
|
|
775
|
+
computer_dir = r"c:\Users\thaku\OneDrive\Desktop\my project\browser\computer"
|
|
776
|
+
if computer_dir not in sys.path:
|
|
777
|
+
sys.path.insert(0, computer_dir)
|
|
778
|
+
from agent import run_computer_task
|
|
779
|
+
asyncio.run(run_computer_task(llm, task))
|
|
780
|
+
else:
|
|
781
|
+
asyncio.run(run_task(llm, task))
|
|
782
|
+
except KeyboardInterrupt:
|
|
783
|
+
# Stop ONLY the current task â keep ClawBot running!
|
|
784
|
+
console.print('\n\n[bold yellow]âšī¸ Task roka gaya! ClawBot ab bhi chal raha hai.[/bold yellow]')
|
|
785
|
+
console.print('[dim]Naya task likhein, ya quit/exit karein band karne ke liye.[/dim]\n')
|
|
786
|
+
console.print()
|
|
787
|
+
|
|
788
|
+
except KeyboardInterrupt:
|
|
789
|
+
# Ctrl+C at the input prompt â ask before exiting
|
|
790
|
+
console.print('\n[yellow]Kya aap ClawBot band karna chahte hain? (quit/exit type karein)[/yellow]')
|
|
791
|
+
except EOFError:
|
|
792
|
+
break
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
if __name__ == '__main__':
|
|
796
|
+
try:
|
|
797
|
+
main()
|
|
798
|
+
except KeyboardInterrupt:
|
|
799
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "clawbot-plus"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "đ¤ AI-Powered Browser Automation CLI â Automate your browser with AI"
|
|
5
|
+
authors = [{ name = "ClawBot" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
license = { text = "MIT" }
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Topic :: Internet :: WWW/HTTP :: Browsers",
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"browser-use>=0.12.0",
|
|
18
|
+
"python-dotenv>=1.0.0",
|
|
19
|
+
"InquirerPy>=0.3.4",
|
|
20
|
+
"rich>=13.0.0",
|
|
21
|
+
"httpx>=0.27.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
clawbot = "clawbot.cli:main"
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/clawbot/clawbot"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["hatchling"]
|
|
32
|
+
build-backend = "hatchling.build"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["clawbot"]
|
|
36
|
+
|