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,5 @@
1
+ """
2
+ ClawBot - AI Browser Automation CLI
3
+ Install: pip install .
4
+ Run: clawbot
5
+ """
@@ -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
+