11import asyncio
22import json
33import os
4+ from pathlib import Path
45import subprocess
56import shutil
67import time
78from collections import deque
89
910from aiohttp import web
1011
11- SCRIPTS_DIR = None
12- PROFILE_DIR = None
13- STATE_FILE = None
1412AGENT_BROWSER_USER_AGENT = os .getenv (
1513 "AGENT_BROWSER_USER_AGENT" ,
1614 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36" ,
2018
2119
2220def install (ctx ):
23- global SCRIPTS_DIR , PROFILE_DIR , STATE_FILE
2421
2522 # Check for agent-browser binary
2623 if not shutil .which ("agent-browser" ):
2724 ctx .log ("agent-browser not found. See https://agent-browser.dev/installation to use the browser extension." )
2825 ctx .disabled = True
2926 return
3027
31- user_path = ctx .get_user_path ()
32- SCRIPTS_DIR = os .path .join (user_path , "browser" , "scripts" )
33- PROFILE_DIR = os .path .join (user_path , "browser" , "profile" )
34- STATE_FILE = os .path .join (user_path , "browser" , "state.json" )
28+ def ensure_dir (path ):
29+ os .makedirs (path , exist_ok = True )
30+ return path
3531
36- os . makedirs ( SCRIPTS_DIR , exist_ok = True )
37- os .makedirs ( PROFILE_DIR , exist_ok = True )
32+ def get_script_dir ( req ):
33+ return ensure_dir ( os .path . join ( ctx . get_user_path ( user = ctx . get_username ( req )), "browser" , "scripts" ) )
3834
39- _browser_env = {"AGENT_BROWSER_PROFILE" : PROFILE_DIR , "AGENT_BROWSER_USER_AGENT" : AGENT_BROWSER_USER_AGENT }
35+ def get_profile_dir (req ):
36+ return ensure_dir (os .path .join (ctx .get_user_path (user = ctx .get_username (req )), "browser" , "profile" ))
37+
38+ def get_state_file (req ):
39+ return os .path .join (ctx .get_user_path (user = ctx .get_username (req )), "browser" , "state.json" )
4040
4141 def _add_debug_log (cmd_str , result , duration ):
4242 global DEBUG_LOG_COUNTER
@@ -54,33 +54,6 @@ def _add_debug_log(cmd_str, result, duration):
5454 }
5555 )
5656
57- def run_browser_cmd (* args , timeout = 30 , env = None ):
58- """Run agent-browser command and return output."""
59- cmd = ["agent-browser" ] + list (args )
60- cmd_str = " " .join (cmd )
61- t0 = time .monotonic ()
62- try :
63- ctx .dbg (f"Running: { cmd_str } " )
64- result = subprocess .run (
65- cmd ,
66- capture_output = True ,
67- text = True ,
68- timeout = timeout ,
69- env = {** os .environ , ** env } if env else None ,
70- )
71- ret = {
72- "success" : result .returncode == 0 ,
73- "stdout" : result .stdout ,
74- "stderr" : result .stderr ,
75- "returncode" : result .returncode ,
76- }
77- except subprocess .TimeoutExpired :
78- ret = {"success" : False , "error" : "Command timed out" }
79- except Exception as e :
80- ret = {"success" : False , "error" : str (e )}
81- _add_debug_log (cmd_str , ret , time .monotonic () - t0 )
82- return ret
83-
8457 async def run_browser_cmd_async (* args , timeout = 30 , env = None ):
8558 """Run agent-browser command asynchronously."""
8659 cmd = ["agent-browser" ] + list (args )
@@ -108,7 +81,7 @@ async def run_browser_cmd_async(*args, timeout=30, env=None):
10881 ret = {"success" : False , "error" : str (e )}
10982 _add_debug_log (cmd_str , ret , time .monotonic () - t0 )
11083 return ret
111-
84+
11285 # =========================================================================
11386 # Status & Screenshot Endpoints
11487 # =========================================================================
@@ -206,6 +179,7 @@ async def browser_open(req):
206179 if not url :
207180 return web .json_response ({"error" : "URL required" }, status = 400 )
208181
182+ _browser_env = {"AGENT_BROWSER_PROFILE" : get_profile_dir (req ), "AGENT_BROWSER_USER_AGENT" : AGENT_BROWSER_USER_AGENT }
209183 result = await run_browser_cmd_async ("open" , url , timeout = 60 , env = _browser_env )
210184 ctx .log (
211185 f"browser_open: Open result: success={ result ['success' ]} , stdout={ result .get ('stdout' , '' )[:100 ]} , stderr={ result .get ('stderr' , '' )[:100 ]} "
@@ -229,7 +203,7 @@ async def browser_open(req):
229203 async def browser_close (req ):
230204 """Close browser session and save state."""
231205 # Save state before closing
232- await run_browser_cmd_async ("state" , "save" , STATE_FILE )
206+ await run_browser_cmd_async ("state" , "save" , get_state_file ( req ) )
233207 result = await run_browser_cmd_async ("close" )
234208 return web .json_response ({"success" : result ["success" ]})
235209
@@ -326,17 +300,19 @@ async def browser_scroll(req):
326300
327301 async def save_state (req ):
328302 """Save browser session state."""
329- result = await run_browser_cmd_async ("state" , "save" , STATE_FILE )
330- return web .json_response ({"success" : result ["success" ], "path" : STATE_FILE if result ["success" ] else None })
303+ state_file = get_state_file (req )
304+ result = await run_browser_cmd_async ("state" , "save" , state_file )
305+ return web .json_response ({"success" : result ["success" ], "path" : state_file if result ["success" ] else None })
331306
332307 ctx .add_post ("/browser/state/save" , save_state )
333308
334309 async def load_state (req ):
335310 """Load browser session state."""
336- if not os .path .exists (STATE_FILE ):
311+ state_file = get_state_file (req )
312+ if not os .path .exists (state_file ):
337313 return web .json_response ({"error" : "No saved state found" }, status = 404 )
338314
339- result = await run_browser_cmd_async ("state" , "load" , STATE_FILE )
315+ result = await run_browser_cmd_async ("state" , "load" , state_file )
340316 return web .json_response ({"success" : result ["success" ]})
341317
342318 ctx .add_post ("/browser/state/load" , load_state )
@@ -356,10 +332,11 @@ async def list_sessions(req):
356332 async def list_scripts (req ):
357333 """List automation scripts."""
358334 scripts = []
359- if os .path .exists (SCRIPTS_DIR ):
360- for name in os .listdir (SCRIPTS_DIR ):
335+ scripts_dir = get_script_dir (req )
336+ if os .path .exists (scripts_dir ):
337+ for name in os .listdir (scripts_dir ):
361338 if name .endswith (".sh" ):
362- path = os .path .join (SCRIPTS_DIR , name )
339+ path = os .path .join (scripts_dir , name )
363340 scripts .append (
364341 {"name" : name , "path" : path , "size" : os .path .getsize (path ), "modified" : os .path .getmtime (path )}
365342 )
@@ -372,7 +349,7 @@ async def get_script(req):
372349 name = req .match_info ["name" ]
373350 if not name .endswith (".sh" ):
374351 name += ".sh"
375- path = os .path .join (SCRIPTS_DIR , name )
352+ path = os .path .join (get_script_dir ( req ) , name )
376353
377354 if not os .path .exists (path ):
378355 return web .json_response ({"error" : "Script not found" }, status = 404 )
@@ -398,7 +375,7 @@ async def save_script(req):
398375
399376 # Sanitize name
400377 name = os .path .basename (name )
401- path = os .path .join (SCRIPTS_DIR , name )
378+ path = os .path .join (get_script_dir ( req ) , name )
402379
403380 with open (path , "w" ) as f :
404381 f .write (content )
@@ -414,7 +391,7 @@ async def delete_script(req):
414391 name = req .match_info ["name" ]
415392 if not name .endswith (".sh" ):
416393 name += ".sh"
417- path = os .path .join (SCRIPTS_DIR , os .path .basename (name ))
394+ path = os .path .join (get_script_dir ( req ) , os .path .basename (name ))
418395
419396 if not os .path .exists (path ):
420397 return web .json_response ({"error" : "Script not found" }, status = 404 )
@@ -429,7 +406,7 @@ async def run_script(req):
429406 name = req .match_info ["name" ]
430407 if not name .endswith (".sh" ):
431408 name += ".sh"
432- path = os .path .join (SCRIPTS_DIR , os .path .basename (name ))
409+ path = os .path .join (get_script_dir ( req ) , os .path .basename (name ))
433410
434411 if not os .path .exists (path ):
435412 return web .json_response ({"error" : "Script not found" }, status = 404 )
0 commit comments