From 9968082c1b882e07f73a66e8978c2c94200f3e55 Mon Sep 17 00:00:00 2001 From: Guy Ernest Date: Sat, 31 Jan 2026 07:26:51 -0800 Subject: [PATCH 1/8] feat(browser-agent): Add persistent session test UI and bump to v0.5.1 Add sequential script execution to Test screen for testing persistent browser sessions. Users can now run two scripts back-to-back with the browser kept alive between runs. Co-Authored-By: Claude Opus 4.5 --- .../src-tauri/tauri.conf.json | 2 +- .../ui/src/components/TestScreen.tsx | 187 +++++++++++++++++- 2 files changed, 185 insertions(+), 4 deletions(-) diff --git a/lambda/tools/local-browser-agent/src-tauri/tauri.conf.json b/lambda/tools/local-browser-agent/src-tauri/tauri.conf.json index 9fe04028..9512551a 100644 --- a/lambda/tools/local-browser-agent/src-tauri/tauri.conf.json +++ b/lambda/tools/local-browser-agent/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "Local Browser Agent", - "version": "0.5.0" + "version": "0.5.1" }, "tauri": { "allowlist": { diff --git a/lambda/tools/local-browser-agent/ui/src/components/TestScreen.tsx b/lambda/tools/local-browser-agent/ui/src/components/TestScreen.tsx index ec95121b..e04a0187 100644 --- a/lambda/tools/local-browser-agent/ui/src/components/TestScreen.tsx +++ b/lambda/tools/local-browser-agent/ui/src/components/TestScreen.tsx @@ -33,6 +33,14 @@ interface ProfileListResponse { total_count: number } +interface ConfigData { + aws_profile: string + s3_bucket: string + headless: boolean + browser_channel: string | null + persistent_browser_session: boolean +} + export default function TestScreen() { const [script, setScript] = useState('') const [validationResult, setValidationResult] = useState(null) @@ -44,6 +52,9 @@ export default function TestScreen() { const [showOtherLogs, setShowOtherLogs] = useState(false) const [profiles, setProfiles] = useState([]) const [selectedProfile, setSelectedProfile] = useState('') + const [secondScript, setSecondScript] = useState('') + const [showPersistentTest, setShowPersistentTest] = useState(false) + const [isRunningSequence, setIsRunningSequence] = useState(false) useEffect(() => { // Load list of example scripts and profiles @@ -221,6 +232,118 @@ export default function TestScreen() { } } + const handleLoadSecondExample = async (filename: string) => { + try { + const content = await invoke('load_browser_example', { filename }) + setSecondScript(content) + setExecutionLog(prev => [...prev, `[INFO] Loaded second script: ${filename}`]) + } catch (error) { + setExecutionLog(prev => [...prev, `[ERROR] Failed to load second script: ${error}`]) + } + } + + const handleLoadSecondFile = async () => { + try { + const selected = await open({ + multiple: false, + filters: [{ name: 'JSON', extensions: ['json'] }] + }) + if (selected && typeof selected === 'string') { + const content = await readTextFile(selected) + setSecondScript(content) + setExecutionLog(prev => [...prev, `[INFO] Loaded second script file: ${selected}`]) + } + } catch (error) { + setExecutionLog(prev => [...prev, `[ERROR] Failed to load second script file: ${error}`]) + } + } + + const injectProfile = (scriptStr: string): string => { + if (!selectedProfile) return scriptStr + try { + const parsed = JSON.parse(scriptStr) + parsed.session = { + ...parsed.session, + profile_name: selectedProfile, + clone_for_parallel: false + } + return JSON.stringify(parsed) + } catch { + return scriptStr + } + } + + const handlePersistentSequence = async () => { + if (!script || !secondScript) return + setIsRunningSequence(true) + setExecutionLog([`[START] Persistent sequence started at ${new Date().toLocaleTimeString()}`]) + + try { + // Load config to get AWS settings + let config: ConfigData + try { + config = await invoke('load_config_from_file', { path: 'config.yaml' }) + } catch { + // Fallback defaults if config file doesn't exist + config = { aws_profile: 'default', s3_bucket: '', headless: false, browser_channel: null, persistent_browser_session: false } + } + setExecutionLog(prev => [...prev, `[INFO] Loaded config (AWS profile: ${config.aws_profile})`]) + + const firstScriptWithProfile = injectProfile(script) + const secondScriptWithProfile = injectProfile(secondScript) + + if (selectedProfile) { + setExecutionLog(prev => [...prev, `[INFO] Using browser profile: ${selectedProfile}`]) + } + + // Step 1: Start persistent session with the first script + // Profile resolution happens on the Python side via session.profile_name in the script JSON + setExecutionLog(prev => [...prev, '[STEP 1] Starting persistent browser session with first script...']) + await invoke('start_persistent_session', { + request: { + first_script: firstScriptWithProfile, + aws_profile: config.aws_profile, + s3_bucket: config.s3_bucket || null, + headless: config.headless, + browser_channel: config.browser_channel, + navigation_timeout: 60000, + user_data_dir: null, + } + }) + setExecutionLog(prev => [...prev, '[STEP 1] Persistent session started, first script executing...']) + + // The first script result comes back from start_persistent_session + // but the current API just returns a success string. + // We need to get the result from the persistent session. + setExecutionLog(prev => [...prev, '[STEP 1] First script completed (browser kept alive)']) + + // Step 2: Execute second script on the persistent session + setExecutionLog(prev => [...prev, '[STEP 2] Executing second script on persistent session...']) + const secondResult = await invoke('execute_persistent_script', { + scriptJson: secondScriptWithProfile + }) + setExecutionLog(prev => [...prev, '[STEP 2] Second script completed']) + if (secondResult) { + setExecutionLog(prev => [...prev, secondResult]) + } + + setExecutionLog(prev => [...prev, '[SUCCESS] Persistent sequence completed successfully']) + } catch (error) { + setExecutionLog(prev => [...prev, `[ERROR] Persistent sequence failed: ${error}`]) + } finally { + // Step 3: Always stop persistent session + try { + setExecutionLog(prev => [...prev, '[CLEANUP] Stopping persistent session...']) + await invoke('stop_persistent_session') + setExecutionLog(prev => [...prev, '[CLEANUP] Persistent session stopped']) + } catch (stopErr) { + setExecutionLog(prev => [...prev, `[WARNING] Failed to stop persistent session: ${stopErr}`]) + } + setIsRunningSequence(false) + setExecutionLog(prev => [...prev, `[END] Completed at ${new Date().toLocaleTimeString()}`]) + } + } + return (
@@ -341,7 +464,7 @@ export default function TestScreen() {
+ + {/* Persistent Session Test */} +
+
setShowPersistentTest(!showPersistentTest)} + > +

{showPersistentTest ? 'โ–ผ' : 'โ–ถ'} Persistent Session Test

+
+ + {showPersistentTest && ( +
+

+ Test persistent browser sessions by running two scripts sequentially. + The browser stays open between runs (no re-login needed). +

+ +
+ +
+ + +
+