Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 81 additions & 2 deletions TMWebDriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,67 @@ def __init__(self, host: str = '127.0.0.1', port: int = 18765):
else:
self.remote = f'http://{self.host}:{self.port+1}/link'

def _find_chrome_exe(self):
"""Find Chrome/Chromium executable path."""
import os
candidates = [
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe'),
r'C:\Program Files\Chromium\Application\chrome.exe',
]
for p in candidates:
if os.path.exists(p):
return p
# Search PATH
import shutil
found = shutil.which('chrome') or shutil.which('chrome.exe') or shutil.which('chromium')
if found:
return found
return None

def ensure_tab(self, url='https://www.baidu.com', timeout=30):
"""Ensure at least one scriptable tab exists. Opens Chrome with URL if needed.
Returns the session_id of the new/existing tab, or None on timeout."""
import subprocess, os
# Check if we already have active sessions
active = [s for s in self.sessions.values() if s.is_active() and s.url]
if active:
return active[0].id

# Open Chrome with URL
chrome = self._find_chrome_exe()
if not chrome:
print("[ensure_tab] Chrome executable not found, cannot open tab")
return None

print(f"[ensure_tab] No active sessions, opening Chrome with: {url}")
subprocess.Popen([chrome, url],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

# Wait for extension to connect and register the session
start = time.time()
while time.time() - start < timeout:
active = [s for s in self.sessions.values() if s.is_active() and s.url]
if active:
sid = active[0].id
self.default_session_id = sid
print(f"[ensure_tab] Session appeared: {sid} ({active[0].url})")
return sid
time.sleep(0.5)

# Final check after timeout (race condition mitigation)
time.sleep(1)
active = [s for s in self.sessions.values() if s.is_active() and s.url]
if active:
sid = active[0].id
self.default_session_id = sid
print(f"[ensure_tab] Session appeared just after timeout: {sid} ({active[0].url})")
return sid

print(f"[ensure_tab] Timeout after {timeout}s, no session appeared")
return None

def start_http_server(self):
self.app = app = bottle.Bottle()

Expand Down Expand Up @@ -76,6 +137,8 @@ def long_poll():
@app.route('/api/result', method=['GET','POST'])
def result():
data = request.json
if data is None:
return 'ignored'
if data.get('type') == 'result':
self.results[data.get('id')] = {'success': True, 'data': data.get('result'), 'newTabs': data.get('newTabs', [])}
elif data.get('type') == 'error':
Expand Down Expand Up @@ -199,8 +262,16 @@ def execute_js(self, code, timeout=15, session_id=None) -> Any:
session = alive_sessions[0]
print(f"会话 {session_id} 未连接,自动切换到最新活动会话: {session.id}")
session_id = self.default_session_id = session.id
if not session or not session.is_active():
raise ValueError(f"会话ID {session_id} 未连接")
if not session or not session.is_active():
# Fallback: no scriptable tabs (e.g., all tabs are about:blank)
# Try to open a real URL in Chrome
print(f"[execute_js] No active session, attempting ensure_tab fallback...")
new_sid = self.ensure_tab(timeout=30)
if new_sid:
session = self.sessions.get(new_sid)
session_id = new_sid
if not session or not session.is_active():
raise ValueError(f"会话ID {session_id} 未连接,且无法自动打开浏览器标签页。请确保Chrome已打开非空白页面。")

tp = session.type
if tp not in ('ws', 'http', 'ext_ws'):
Expand Down Expand Up @@ -279,6 +350,14 @@ def set_session(self, url_pattern: str) -> bool:
return self.default_session_id

def jump(self, url, timeout=10): self.execute_js(f"window.location.href='{url}'", timeout=timeout)

def navigate(self, url, timeout=15):
"""Navigate to URL. Uses jump() if session exists, otherwise opens Chrome directly."""
active = [s for s in self.sessions.values() if s.is_active() and s.url]
if active:
return self.jump(url, timeout)
else:
return self.ensure_tab(url, timeout)

if __name__ == "__main__":
driver = TMWebDriver(host='127.0.0.1', port=18765)
12 changes: 10 additions & 2 deletions assets/tmwd_cdp_bridge/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,18 @@ async function isServerAlive() {
try {
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 2000);
await fetch('http://127.0.0.1:18765', { signal: ctrl.signal });
await fetch('http://127.0.0.1:18766/api/result', { signal: ctrl.signal, method: 'POST', body: '{}' });
return true; // Got HTTP response → port is listening
} catch (e) {
return false; // Network error (connection refused) or timeout → server not alive
// Also try WS port as fallback (some setups serve HTTP on WS port)
try {
const ctrl2 = new AbortController();
setTimeout(() => ctrl2.abort(), 1500);
await fetch('http://127.0.0.1:18765', { signal: ctrl2.signal });
return true;
} catch (e2) {
return false; // Both failed → server not alive
}
}
}

Expand Down
21 changes: 14 additions & 7 deletions ga.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ def first_init_driver():
global driver
from TMWebDriver import TMWebDriver
driver = TMWebDriver()
for i in range(20):
for i in range(5):
time.sleep(1)
sess = driver.get_all_sessions()
if len(sess) > 0: break
if len(sess) == 0: return
if len(sess) == 1:
#driver.newtab()
time.sleep(3)
if len(sess) == 0:
# Try to recover: open a URL in Chrome
print("[first_init] No sessions, attempting ensure_tab fallback...")
driver.ensure_tab(timeout=30)

def web_scan(tabs_only=False, switch_tab_id=None, text_only=False, maxlen=35000):
"""获取当前页面的简化HTML内容和标签页列表。注意:简化过程会过滤边栏、浮动元素等非主体内容。
Expand All @@ -126,7 +126,10 @@ def web_scan(tabs_only=False, switch_tab_id=None, text_only=False, maxlen=35000)
try:
if driver is None: first_init_driver()
if len(driver.get_all_sessions()) == 0:
return {"status": "error", "msg": "没有可用的浏览器标签页,查L3记忆分析原因。"}
# Try to recover: open a URL in Chrome
sid = driver.ensure_tab(timeout=30)
if not sid and len(driver.get_all_sessions()) == 0:
return {"status": "error", "msg": "没有可用的浏览器标签页,查L3记忆分析原因。"}
tabs = []
for sess in driver.get_all_sessions():
sess.pop('connected_at', None)
Expand Down Expand Up @@ -172,7 +175,11 @@ def web_execute_js(script, switch_tab_id=None, no_monitor=False):
global driver
try:
if driver is None: first_init_driver()
if len(driver.get_all_sessions()) == 0: return {"status": "error", "msg": "没有可用的浏览器标签页,查L3记忆分析原因。"}
if len(driver.get_all_sessions()) == 0:
# Try to recover: open a URL in Chrome
sid = driver.ensure_tab(timeout=30)
if not sid and len(driver.get_all_sessions()) == 0:
return {"status": "error", "msg": "没有可用的浏览器标签页,查L3记忆分析原因。"}
if switch_tab_id: driver.default_session_id = switch_tab_id
result = simphtml.execute_js_rich(script, driver, no_monitor=no_monitor)
return result
Expand Down