-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
executable file
·180 lines (153 loc) · 7.37 KB
/
server.py
File metadata and controls
executable file
·180 lines (153 loc) · 7.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env python3
"""
Standalone Flask server for Browser Automation.
This script is spawned by the Tauri app.
"""
import sys
import os
import signal
import subprocess
import argparse
import logging
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from auto_browser.web_ui import app, automation_server
from auto_browser.config_manager import ConfigManager
def cleanup_port(port=5555):
"""Kill any processes using the specified port (only if it's a Python/Flask process)"""
try:
# Use lsof to find process details using the port
result = subprocess.run(
['lsof', '-i', f':{port}', '-sTCP:LISTEN'],
capture_output=True,
text=True
)
if result.returncode == 0 and result.stdout.strip():
lines = result.stdout.strip().split('\n')[1:] # Skip header
for line in lines:
parts = line.split()
if len(parts) >= 2:
command = parts[0]
pid = parts[1]
# Only kill if it's a Python process (likely our server)
if command.lower().startswith('python'):
try:
pid = int(pid)
print(f"[CLEANUP] Found hanging Python process on port {port} (PID: {pid}), killing it...")
os.kill(pid, signal.SIGTERM)
print(f"[CLEANUP] Successfully killed process {pid}")
except (ValueError, ProcessLookupError) as e:
print(f"[CLEANUP] Could not kill process {pid}: {e}")
else:
print(f"[CLEANUP] WARNING: Port {port} is in use by '{command}' (PID: {pid})")
print(f"[CLEANUP] This doesn't appear to be our server. Skipping cleanup.")
print(f"[CLEANUP] You may need to:")
print(f" - Stop that process manually")
print(f" - Use a different port")
print(f" - Disable AirPlay Receiver in System Settings if it's the culprit")
except FileNotFoundError:
# lsof not available (unlikely on macOS, but handle gracefully)
pass
except Exception as e:
print(f"[CLEANUP] Error during port cleanup: {e}")
def main():
"""Start Flask server"""
# Parse command line arguments
parser = argparse.ArgumentParser(description='Browser Automation Flask Server')
parser.add_argument('--port', '-p', type=int, default=5555,
help='Port to run the server on (default: 5555)')
parser.add_argument('--verbose', '-v', action='store_true',
help='Enable verbose logging for debugging Nova Act connection')
parser.add_argument('--debug', action='store_true',
help='Enable Flask debug mode')
args = parser.parse_args()
# Setup logging based on verbose flag
if args.verbose:
logging.basicConfig(
level=logging.DEBUG,
format='[%(levelname)s] %(name)s: %(message)s'
)
# Enable Nova Act SDK logging if it has a logger
logging.getLogger('nova_act').setLevel(logging.DEBUG)
logging.getLogger('selenium').setLevel(logging.DEBUG)
print("\n" + "="*80)
print("VERBOSE MODE ENABLED - Detailed Nova Act debugging")
print("="*80 + "\n")
print("\n[DEBUG] Starting server initialization...")
print(f"[DEBUG] Config file exists: {ConfigManager.config_exists()}")
print(f"[DEBUG] Config file path: {ConfigManager.CONFIG_FILE}")
# Get API key from config or environment
api_key = os.getenv('NOVA_ACT_API_KEY') # Internal env var name
print(f"[DEBUG] API key from environment: {('***' + api_key[-4:]) if api_key else 'None'}")
if not api_key:
api_key = ConfigManager.get_api_key()
print(f"[DEBUG] API key from config: {('***' + api_key[-4:]) if api_key else 'None'}")
if args.verbose and api_key:
# Only show last 4 chars for security (don't expose first 8 chars)
print(f"[VERBOSE] API key present: ***{api_key[-4:]}")
print(f"[VERBOSE] API key length: {len(api_key)} characters")
# Get agent ID
agent_id = os.getenv('ELEVENLABS_AGENT_ID')
if not agent_id:
agent_id = ConfigManager.get_agent_id()
if agent_id:
os.environ['ELEVENLABS_AGENT_ID'] = agent_id
print(f"[DEBUG] automation_server.is_configured BEFORE configure: {automation_server.is_configured}")
# Clean up any hanging processes on the port we're going to use
cleanup_port(args.port)
# Set verbose mode in environment for web_ui to pick up
if args.verbose:
os.environ['VERBOSE'] = 'true'
# Validate and configure automation
if api_key:
print("[DEBUG] Validating API key...")
if args.verbose:
print("[VERBOSE] Attempting to import nova_act module...")
try:
import nova_act
print(f"[VERBOSE] nova_act module imported successfully")
print(f"[VERBOSE] nova_act version: {getattr(nova_act, '__version__', 'unknown')}")
print(f"[VERBOSE] nova_act location: {nova_act.__file__}")
except ImportError as e:
print(f"[VERBOSE] ERROR: Failed to import nova_act: {e}")
is_valid, error_msg = ConfigManager.validate_api_key(api_key)
if is_valid:
if args.verbose:
print("[VERBOSE] API key validation successful")
print("[VERBOSE] Configuring automation server...")
automation_server.configure(
api_key=api_key,
starting_page="https://google.com",
headless=False
)
print(f"[DEBUG] automation_server.is_configured AFTER configure: {automation_server.is_configured}")
print("✓ Automation enabled")
if args.verbose:
print("[VERBOSE] Automation server configured successfully")
print("[VERBOSE] Browser will initialize on first automation command (lazy init)")
else:
print(f"[DEBUG] API key validation failed: {error_msg}")
print(f"⚠️ Invalid API key: {error_msg}")
print(" Please update your configuration with a valid API key")
if args.verbose:
print(f"[VERBOSE] Full validation error: {error_msg}")
# Leave automation_server.is_configured as False
else:
print(f"[DEBUG] No API key found, automation_server.is_configured: {automation_server.is_configured}")
print("⚠️ No API key found - automation disabled")
print(" Configure via setup or set API key in configuration")
# Start Flask server
print(f"\nStarting Flask server on http://127.0.0.1:{args.port}")
if args.verbose:
print("[VERBOSE] Flask debug mode:", "ENABLED" if args.debug else "DISABLED")
print("[VERBOSE] Flask threading mode: DISABLED (single-threaded for Selenium compatibility)")
print("="*80 + "\n")
app.run(
host='127.0.0.1',
port=args.port,
debug=args.debug,
threaded=False, # CRITICAL: Selenium WebDriver is NOT thread-safe - must use single thread
use_reloader=False # Important: disable reloader for subprocess
)
if __name__ == '__main__':
main()