optexity-browser-use 0.9.5__py3-none-any.whl
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.
- browser_use/__init__.py +157 -0
- browser_use/actor/__init__.py +11 -0
- browser_use/actor/element.py +1175 -0
- browser_use/actor/mouse.py +134 -0
- browser_use/actor/page.py +561 -0
- browser_use/actor/playground/flights.py +41 -0
- browser_use/actor/playground/mixed_automation.py +54 -0
- browser_use/actor/playground/playground.py +236 -0
- browser_use/actor/utils.py +176 -0
- browser_use/agent/cloud_events.py +282 -0
- browser_use/agent/gif.py +424 -0
- browser_use/agent/judge.py +170 -0
- browser_use/agent/message_manager/service.py +473 -0
- browser_use/agent/message_manager/utils.py +52 -0
- browser_use/agent/message_manager/views.py +98 -0
- browser_use/agent/prompts.py +413 -0
- browser_use/agent/service.py +2316 -0
- browser_use/agent/system_prompt.md +185 -0
- browser_use/agent/system_prompt_flash.md +10 -0
- browser_use/agent/system_prompt_no_thinking.md +183 -0
- browser_use/agent/views.py +743 -0
- browser_use/browser/__init__.py +41 -0
- browser_use/browser/cloud/cloud.py +203 -0
- browser_use/browser/cloud/views.py +89 -0
- browser_use/browser/events.py +578 -0
- browser_use/browser/profile.py +1158 -0
- browser_use/browser/python_highlights.py +548 -0
- browser_use/browser/session.py +3225 -0
- browser_use/browser/session_manager.py +399 -0
- browser_use/browser/video_recorder.py +162 -0
- browser_use/browser/views.py +200 -0
- browser_use/browser/watchdog_base.py +260 -0
- browser_use/browser/watchdogs/__init__.py +0 -0
- browser_use/browser/watchdogs/aboutblank_watchdog.py +253 -0
- browser_use/browser/watchdogs/crash_watchdog.py +335 -0
- browser_use/browser/watchdogs/default_action_watchdog.py +2729 -0
- browser_use/browser/watchdogs/dom_watchdog.py +817 -0
- browser_use/browser/watchdogs/downloads_watchdog.py +1277 -0
- browser_use/browser/watchdogs/local_browser_watchdog.py +461 -0
- browser_use/browser/watchdogs/permissions_watchdog.py +43 -0
- browser_use/browser/watchdogs/popups_watchdog.py +143 -0
- browser_use/browser/watchdogs/recording_watchdog.py +126 -0
- browser_use/browser/watchdogs/screenshot_watchdog.py +62 -0
- browser_use/browser/watchdogs/security_watchdog.py +280 -0
- browser_use/browser/watchdogs/storage_state_watchdog.py +335 -0
- browser_use/cli.py +2359 -0
- browser_use/code_use/__init__.py +16 -0
- browser_use/code_use/formatting.py +192 -0
- browser_use/code_use/namespace.py +665 -0
- browser_use/code_use/notebook_export.py +276 -0
- browser_use/code_use/service.py +1340 -0
- browser_use/code_use/system_prompt.md +574 -0
- browser_use/code_use/utils.py +150 -0
- browser_use/code_use/views.py +171 -0
- browser_use/config.py +505 -0
- browser_use/controller/__init__.py +3 -0
- browser_use/dom/enhanced_snapshot.py +161 -0
- browser_use/dom/markdown_extractor.py +169 -0
- browser_use/dom/playground/extraction.py +312 -0
- browser_use/dom/playground/multi_act.py +32 -0
- browser_use/dom/serializer/clickable_elements.py +200 -0
- browser_use/dom/serializer/code_use_serializer.py +287 -0
- browser_use/dom/serializer/eval_serializer.py +478 -0
- browser_use/dom/serializer/html_serializer.py +212 -0
- browser_use/dom/serializer/paint_order.py +197 -0
- browser_use/dom/serializer/serializer.py +1170 -0
- browser_use/dom/service.py +825 -0
- browser_use/dom/utils.py +129 -0
- browser_use/dom/views.py +906 -0
- browser_use/exceptions.py +5 -0
- browser_use/filesystem/__init__.py +0 -0
- browser_use/filesystem/file_system.py +619 -0
- browser_use/init_cmd.py +376 -0
- browser_use/integrations/gmail/__init__.py +24 -0
- browser_use/integrations/gmail/actions.py +115 -0
- browser_use/integrations/gmail/service.py +225 -0
- browser_use/llm/__init__.py +155 -0
- browser_use/llm/anthropic/chat.py +242 -0
- browser_use/llm/anthropic/serializer.py +312 -0
- browser_use/llm/aws/__init__.py +36 -0
- browser_use/llm/aws/chat_anthropic.py +242 -0
- browser_use/llm/aws/chat_bedrock.py +289 -0
- browser_use/llm/aws/serializer.py +257 -0
- browser_use/llm/azure/chat.py +91 -0
- browser_use/llm/base.py +57 -0
- browser_use/llm/browser_use/__init__.py +3 -0
- browser_use/llm/browser_use/chat.py +201 -0
- browser_use/llm/cerebras/chat.py +193 -0
- browser_use/llm/cerebras/serializer.py +109 -0
- browser_use/llm/deepseek/chat.py +212 -0
- browser_use/llm/deepseek/serializer.py +109 -0
- browser_use/llm/exceptions.py +29 -0
- browser_use/llm/google/__init__.py +3 -0
- browser_use/llm/google/chat.py +542 -0
- browser_use/llm/google/serializer.py +120 -0
- browser_use/llm/groq/chat.py +229 -0
- browser_use/llm/groq/parser.py +158 -0
- browser_use/llm/groq/serializer.py +159 -0
- browser_use/llm/messages.py +238 -0
- browser_use/llm/models.py +271 -0
- browser_use/llm/oci_raw/__init__.py +10 -0
- browser_use/llm/oci_raw/chat.py +443 -0
- browser_use/llm/oci_raw/serializer.py +229 -0
- browser_use/llm/ollama/chat.py +97 -0
- browser_use/llm/ollama/serializer.py +143 -0
- browser_use/llm/openai/chat.py +264 -0
- browser_use/llm/openai/like.py +15 -0
- browser_use/llm/openai/serializer.py +165 -0
- browser_use/llm/openrouter/chat.py +211 -0
- browser_use/llm/openrouter/serializer.py +26 -0
- browser_use/llm/schema.py +176 -0
- browser_use/llm/views.py +48 -0
- browser_use/logging_config.py +330 -0
- browser_use/mcp/__init__.py +18 -0
- browser_use/mcp/__main__.py +12 -0
- browser_use/mcp/client.py +544 -0
- browser_use/mcp/controller.py +264 -0
- browser_use/mcp/server.py +1114 -0
- browser_use/observability.py +204 -0
- browser_use/py.typed +0 -0
- browser_use/sandbox/__init__.py +41 -0
- browser_use/sandbox/sandbox.py +637 -0
- browser_use/sandbox/views.py +132 -0
- browser_use/screenshots/__init__.py +1 -0
- browser_use/screenshots/service.py +52 -0
- browser_use/sync/__init__.py +6 -0
- browser_use/sync/auth.py +357 -0
- browser_use/sync/service.py +161 -0
- browser_use/telemetry/__init__.py +51 -0
- browser_use/telemetry/service.py +112 -0
- browser_use/telemetry/views.py +101 -0
- browser_use/tokens/__init__.py +0 -0
- browser_use/tokens/custom_pricing.py +24 -0
- browser_use/tokens/mappings.py +4 -0
- browser_use/tokens/service.py +580 -0
- browser_use/tokens/views.py +108 -0
- browser_use/tools/registry/service.py +572 -0
- browser_use/tools/registry/views.py +174 -0
- browser_use/tools/service.py +1675 -0
- browser_use/tools/utils.py +82 -0
- browser_use/tools/views.py +100 -0
- browser_use/utils.py +670 -0
- optexity_browser_use-0.9.5.dist-info/METADATA +344 -0
- optexity_browser_use-0.9.5.dist-info/RECORD +147 -0
- optexity_browser_use-0.9.5.dist-info/WHEEL +4 -0
- optexity_browser_use-0.9.5.dist-info/entry_points.txt +3 -0
- optexity_browser_use-0.9.5.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""Storage state watchdog for managing browser cookies and storage persistence."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, ClassVar
|
|
8
|
+
|
|
9
|
+
from bubus import BaseEvent
|
|
10
|
+
from cdp_use.cdp.network import Cookie
|
|
11
|
+
from pydantic import Field, PrivateAttr
|
|
12
|
+
|
|
13
|
+
from browser_use.browser.events import (
|
|
14
|
+
BrowserConnectedEvent,
|
|
15
|
+
BrowserStopEvent,
|
|
16
|
+
LoadStorageStateEvent,
|
|
17
|
+
SaveStorageStateEvent,
|
|
18
|
+
StorageStateLoadedEvent,
|
|
19
|
+
StorageStateSavedEvent,
|
|
20
|
+
)
|
|
21
|
+
from browser_use.browser.watchdog_base import BaseWatchdog
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StorageStateWatchdog(BaseWatchdog):
|
|
25
|
+
"""Monitors and persists browser storage state including cookies and localStorage."""
|
|
26
|
+
|
|
27
|
+
# Event contracts
|
|
28
|
+
LISTENS_TO: ClassVar[list[type[BaseEvent]]] = [
|
|
29
|
+
BrowserConnectedEvent,
|
|
30
|
+
BrowserStopEvent,
|
|
31
|
+
SaveStorageStateEvent,
|
|
32
|
+
LoadStorageStateEvent,
|
|
33
|
+
]
|
|
34
|
+
EMITS: ClassVar[list[type[BaseEvent]]] = [
|
|
35
|
+
StorageStateSavedEvent,
|
|
36
|
+
StorageStateLoadedEvent,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
# Configuration
|
|
40
|
+
auto_save_interval: float = Field(default=30.0) # Auto-save every 30 seconds
|
|
41
|
+
save_on_change: bool = Field(default=True) # Save immediately when cookies change
|
|
42
|
+
|
|
43
|
+
# Private state
|
|
44
|
+
_monitoring_task: asyncio.Task | None = PrivateAttr(default=None)
|
|
45
|
+
_last_cookie_state: list[dict] = PrivateAttr(default_factory=list)
|
|
46
|
+
_save_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
|
|
47
|
+
|
|
48
|
+
async def on_BrowserConnectedEvent(self, event: BrowserConnectedEvent) -> None:
|
|
49
|
+
"""Start monitoring when browser starts."""
|
|
50
|
+
self.logger.debug('[StorageStateWatchdog] 🍪 Initializing auth/cookies sync <-> with storage_state.json file')
|
|
51
|
+
|
|
52
|
+
# Start monitoring
|
|
53
|
+
await self._start_monitoring()
|
|
54
|
+
|
|
55
|
+
# Automatically load storage state after browser start
|
|
56
|
+
await self.event_bus.dispatch(LoadStorageStateEvent())
|
|
57
|
+
|
|
58
|
+
async def on_BrowserStopEvent(self, event: BrowserStopEvent) -> None:
|
|
59
|
+
"""Stop monitoring when browser stops."""
|
|
60
|
+
self.logger.debug('[StorageStateWatchdog] Stopping storage_state monitoring')
|
|
61
|
+
await self._stop_monitoring()
|
|
62
|
+
|
|
63
|
+
async def on_SaveStorageStateEvent(self, event: SaveStorageStateEvent) -> None:
|
|
64
|
+
"""Handle storage state save request."""
|
|
65
|
+
# Use provided path or fall back to profile default
|
|
66
|
+
path = event.path
|
|
67
|
+
if path is None:
|
|
68
|
+
# Use profile default path if available
|
|
69
|
+
if self.browser_session.browser_profile.storage_state:
|
|
70
|
+
path = str(self.browser_session.browser_profile.storage_state)
|
|
71
|
+
else:
|
|
72
|
+
path = None # Skip saving if no path available
|
|
73
|
+
await self._save_storage_state(path)
|
|
74
|
+
|
|
75
|
+
async def on_LoadStorageStateEvent(self, event: LoadStorageStateEvent) -> None:
|
|
76
|
+
"""Handle storage state load request."""
|
|
77
|
+
# Use provided path or fall back to profile default
|
|
78
|
+
path = event.path
|
|
79
|
+
if path is None:
|
|
80
|
+
# Use profile default path if available
|
|
81
|
+
if self.browser_session.browser_profile.storage_state:
|
|
82
|
+
path = str(self.browser_session.browser_profile.storage_state)
|
|
83
|
+
else:
|
|
84
|
+
path = None # Skip loading if no path available
|
|
85
|
+
await self._load_storage_state(path)
|
|
86
|
+
|
|
87
|
+
async def _start_monitoring(self) -> None:
|
|
88
|
+
"""Start the monitoring task."""
|
|
89
|
+
if self._monitoring_task and not self._monitoring_task.done():
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
assert self.browser_session.cdp_client is not None
|
|
93
|
+
|
|
94
|
+
self._monitoring_task = asyncio.create_task(self._monitor_storage_changes())
|
|
95
|
+
# self.logger'[StorageStateWatchdog] Started storage monitoring task')
|
|
96
|
+
|
|
97
|
+
async def _stop_monitoring(self) -> None:
|
|
98
|
+
"""Stop the monitoring task."""
|
|
99
|
+
if self._monitoring_task and not self._monitoring_task.done():
|
|
100
|
+
self._monitoring_task.cancel()
|
|
101
|
+
try:
|
|
102
|
+
await self._monitoring_task
|
|
103
|
+
except asyncio.CancelledError:
|
|
104
|
+
pass
|
|
105
|
+
# self.logger.debug('[StorageStateWatchdog] Stopped storage monitoring task')
|
|
106
|
+
|
|
107
|
+
async def _check_for_cookie_changes_cdp(self, event: dict) -> None:
|
|
108
|
+
"""Check if a CDP network event indicates cookie changes.
|
|
109
|
+
|
|
110
|
+
This would be called by Network.responseReceivedExtraInfo events
|
|
111
|
+
if we set up CDP event listeners.
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
# Check for Set-Cookie headers in the response
|
|
115
|
+
headers = event.get('headers', {})
|
|
116
|
+
if 'set-cookie' in headers or 'Set-Cookie' in headers:
|
|
117
|
+
self.logger.debug('[StorageStateWatchdog] Cookie change detected via CDP')
|
|
118
|
+
|
|
119
|
+
# If save on change is enabled, trigger save immediately
|
|
120
|
+
if self.save_on_change:
|
|
121
|
+
await self._save_storage_state()
|
|
122
|
+
except Exception as e:
|
|
123
|
+
self.logger.warning(f'[StorageStateWatchdog] Error checking for cookie changes: {e}')
|
|
124
|
+
|
|
125
|
+
async def _monitor_storage_changes(self) -> None:
|
|
126
|
+
"""Periodically check for storage changes and auto-save."""
|
|
127
|
+
while True:
|
|
128
|
+
try:
|
|
129
|
+
await asyncio.sleep(self.auto_save_interval)
|
|
130
|
+
|
|
131
|
+
# Check if cookies have changed
|
|
132
|
+
if await self._have_cookies_changed():
|
|
133
|
+
self.logger.debug('[StorageStateWatchdog] Detected changes to sync with storage_state.json')
|
|
134
|
+
await self._save_storage_state()
|
|
135
|
+
|
|
136
|
+
except asyncio.CancelledError:
|
|
137
|
+
break
|
|
138
|
+
except Exception as e:
|
|
139
|
+
self.logger.error(f'[StorageStateWatchdog] Error in monitoring loop: {e}')
|
|
140
|
+
|
|
141
|
+
async def _have_cookies_changed(self) -> bool:
|
|
142
|
+
"""Check if cookies have changed since last save."""
|
|
143
|
+
if not self.browser_session.cdp_client:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Get current cookies using CDP
|
|
148
|
+
current_cookies = await self.browser_session._cdp_get_cookies()
|
|
149
|
+
|
|
150
|
+
# Convert to comparable format, using .get() for optional fields
|
|
151
|
+
current_cookie_set = {
|
|
152
|
+
(c.get('name', ''), c.get('domain', ''), c.get('path', '')): c.get('value', '') for c in current_cookies
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
last_cookie_set = {
|
|
156
|
+
(c.get('name', ''), c.get('domain', ''), c.get('path', '')): c.get('value', '') for c in self._last_cookie_state
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return current_cookie_set != last_cookie_set
|
|
160
|
+
except Exception as e:
|
|
161
|
+
self.logger.debug(f'[StorageStateWatchdog] Error comparing cookies: {e}')
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
async def _save_storage_state(self, path: str | None = None) -> None:
|
|
165
|
+
"""Save browser storage state to file."""
|
|
166
|
+
async with self._save_lock:
|
|
167
|
+
# Check if CDP client is available
|
|
168
|
+
assert await self.browser_session.get_or_create_cdp_session(target_id=None)
|
|
169
|
+
|
|
170
|
+
save_path = path or self.browser_session.browser_profile.storage_state
|
|
171
|
+
if not save_path:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Skip saving if the storage state is already a dict (indicates it was loaded from memory)
|
|
175
|
+
# We only save to file if it started as a file path
|
|
176
|
+
if isinstance(save_path, dict):
|
|
177
|
+
self.logger.debug('[StorageStateWatchdog] Storage state is already a dict, skipping file save')
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
# Get current storage state using CDP
|
|
182
|
+
storage_state = await self.browser_session._cdp_get_storage_state()
|
|
183
|
+
|
|
184
|
+
# Update our last known state
|
|
185
|
+
self._last_cookie_state = storage_state.get('cookies', []).copy()
|
|
186
|
+
|
|
187
|
+
# Convert path to Path object
|
|
188
|
+
json_path = Path(save_path).expanduser().resolve()
|
|
189
|
+
json_path.parent.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
|
|
191
|
+
# Merge with existing state if file exists
|
|
192
|
+
merged_state = storage_state
|
|
193
|
+
if json_path.exists():
|
|
194
|
+
try:
|
|
195
|
+
existing_state = json.loads(json_path.read_text())
|
|
196
|
+
merged_state = self._merge_storage_states(existing_state, dict(storage_state))
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self.logger.error(f'[StorageStateWatchdog] Failed to merge with existing state: {e}')
|
|
199
|
+
|
|
200
|
+
# Write atomically
|
|
201
|
+
temp_path = json_path.with_suffix('.json.tmp')
|
|
202
|
+
temp_path.write_text(json.dumps(merged_state, indent=4))
|
|
203
|
+
|
|
204
|
+
# Backup existing file
|
|
205
|
+
if json_path.exists():
|
|
206
|
+
backup_path = json_path.with_suffix('.json.bak')
|
|
207
|
+
json_path.replace(backup_path)
|
|
208
|
+
|
|
209
|
+
# Move temp to final
|
|
210
|
+
temp_path.replace(json_path)
|
|
211
|
+
|
|
212
|
+
# Emit success event
|
|
213
|
+
self.event_bus.dispatch(
|
|
214
|
+
StorageStateSavedEvent(
|
|
215
|
+
path=str(json_path),
|
|
216
|
+
cookies_count=len(merged_state.get('cookies', [])),
|
|
217
|
+
origins_count=len(merged_state.get('origins', [])),
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
self.logger.debug(
|
|
222
|
+
f'[StorageStateWatchdog] Saved storage state to {json_path} '
|
|
223
|
+
f'({len(merged_state.get("cookies", []))} cookies, '
|
|
224
|
+
f'{len(merged_state.get("origins", []))} origins)'
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
self.logger.error(f'[StorageStateWatchdog] Failed to save storage state: {e}')
|
|
229
|
+
|
|
230
|
+
async def _load_storage_state(self, path: str | None = None) -> None:
|
|
231
|
+
"""Load browser storage state from file."""
|
|
232
|
+
if not self.browser_session.cdp_client:
|
|
233
|
+
self.logger.warning('[StorageStateWatchdog] No CDP client available for loading')
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
load_path = path or self.browser_session.browser_profile.storage_state
|
|
237
|
+
if not load_path or not os.path.exists(str(load_path)):
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
# Read the storage state file asynchronously
|
|
242
|
+
import anyio
|
|
243
|
+
|
|
244
|
+
content = await anyio.Path(str(load_path)).read_text()
|
|
245
|
+
storage = json.loads(content)
|
|
246
|
+
|
|
247
|
+
# Apply cookies if present
|
|
248
|
+
if 'cookies' in storage and storage['cookies']:
|
|
249
|
+
await self.browser_session._cdp_set_cookies(storage['cookies'])
|
|
250
|
+
self._last_cookie_state = storage['cookies'].copy()
|
|
251
|
+
self.logger.debug(f'[StorageStateWatchdog] Added {len(storage["cookies"])} cookies from storage state')
|
|
252
|
+
|
|
253
|
+
# Apply origins (localStorage/sessionStorage) if present
|
|
254
|
+
if 'origins' in storage and storage['origins']:
|
|
255
|
+
for origin in storage['origins']:
|
|
256
|
+
if 'localStorage' in origin:
|
|
257
|
+
for item in origin['localStorage']:
|
|
258
|
+
script = f"""
|
|
259
|
+
window.localStorage.setItem({json.dumps(item['name'])}, {json.dumps(item['value'])});
|
|
260
|
+
"""
|
|
261
|
+
await self.browser_session._cdp_add_init_script(script)
|
|
262
|
+
if 'sessionStorage' in origin:
|
|
263
|
+
for item in origin['sessionStorage']:
|
|
264
|
+
script = f"""
|
|
265
|
+
window.sessionStorage.setItem({json.dumps(item['name'])}, {json.dumps(item['value'])});
|
|
266
|
+
"""
|
|
267
|
+
await self.browser_session._cdp_add_init_script(script)
|
|
268
|
+
self.logger.debug(
|
|
269
|
+
f'[StorageStateWatchdog] Applied localStorage/sessionStorage from {len(storage["origins"])} origins'
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
self.event_bus.dispatch(
|
|
273
|
+
StorageStateLoadedEvent(
|
|
274
|
+
path=str(load_path),
|
|
275
|
+
cookies_count=len(storage.get('cookies', [])),
|
|
276
|
+
origins_count=len(storage.get('origins', [])),
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
self.logger.debug(f'[StorageStateWatchdog] Loaded storage state from: {load_path}')
|
|
281
|
+
|
|
282
|
+
except Exception as e:
|
|
283
|
+
self.logger.error(f'[StorageStateWatchdog] Failed to load storage state: {e}')
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def _merge_storage_states(existing: dict[str, Any], new: dict[str, Any]) -> dict[str, Any]:
|
|
287
|
+
"""Merge two storage states, with new values taking precedence."""
|
|
288
|
+
merged = existing.copy()
|
|
289
|
+
|
|
290
|
+
# Merge cookies
|
|
291
|
+
existing_cookies = {(c['name'], c['domain'], c['path']): c for c in existing.get('cookies', [])}
|
|
292
|
+
|
|
293
|
+
for cookie in new.get('cookies', []):
|
|
294
|
+
key = (cookie['name'], cookie['domain'], cookie['path'])
|
|
295
|
+
existing_cookies[key] = cookie
|
|
296
|
+
|
|
297
|
+
merged['cookies'] = list(existing_cookies.values())
|
|
298
|
+
|
|
299
|
+
# Merge origins
|
|
300
|
+
existing_origins = {origin['origin']: origin for origin in existing.get('origins', [])}
|
|
301
|
+
|
|
302
|
+
for origin in new.get('origins', []):
|
|
303
|
+
existing_origins[origin['origin']] = origin
|
|
304
|
+
|
|
305
|
+
merged['origins'] = list(existing_origins.values())
|
|
306
|
+
|
|
307
|
+
return merged
|
|
308
|
+
|
|
309
|
+
async def get_current_cookies(self) -> list[dict[str, Any]]:
|
|
310
|
+
"""Get current cookies using CDP."""
|
|
311
|
+
if not self.browser_session.cdp_client:
|
|
312
|
+
return []
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
cookies = await self.browser_session._cdp_get_cookies()
|
|
316
|
+
# Cookie is a TypedDict, cast to dict for compatibility
|
|
317
|
+
return [dict(cookie) for cookie in cookies]
|
|
318
|
+
except Exception as e:
|
|
319
|
+
self.logger.error(f'[StorageStateWatchdog] Failed to get cookies: {e}')
|
|
320
|
+
return []
|
|
321
|
+
|
|
322
|
+
async def add_cookies(self, cookies: list[dict[str, Any]]) -> None:
|
|
323
|
+
"""Add cookies using CDP."""
|
|
324
|
+
if not self.browser_session.cdp_client:
|
|
325
|
+
self.logger.warning('[StorageStateWatchdog] No CDP client available for adding cookies')
|
|
326
|
+
return
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
# Convert dicts to Cookie objects
|
|
330
|
+
cookie_objects = [Cookie(**cookie_dict) if isinstance(cookie_dict, dict) else cookie_dict for cookie_dict in cookies]
|
|
331
|
+
# Set cookies using CDP
|
|
332
|
+
await self.browser_session._cdp_set_cookies(cookie_objects)
|
|
333
|
+
self.logger.debug(f'[StorageStateWatchdog] Added {len(cookies)} cookies')
|
|
334
|
+
except Exception as e:
|
|
335
|
+
self.logger.error(f'[StorageStateWatchdog] Failed to add cookies: {e}')
|