vibesurf 0.1.32__py3-none-any.whl → 0.1.34__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.
Potentially problematic release.
This version of vibesurf might be problematic. Click here for more details.
- vibe_surf/_version.py +2 -2
- vibe_surf/agents/browser_use_agent.py +1 -1
- vibe_surf/agents/prompts/vibe_surf_prompt.py +6 -0
- vibe_surf/agents/report_writer_agent.py +50 -0
- vibe_surf/agents/vibe_surf_agent.py +55 -0
- vibe_surf/backend/api/composio.py +952 -0
- vibe_surf/backend/database/migrations/v005_add_composio_integration.sql +33 -0
- vibe_surf/backend/database/migrations/v006_add_credentials_table.sql +26 -0
- vibe_surf/backend/database/models.py +53 -1
- vibe_surf/backend/database/queries.py +312 -2
- vibe_surf/backend/main.py +28 -0
- vibe_surf/backend/shared_state.py +123 -9
- vibe_surf/chrome_extension/scripts/api-client.js +32 -0
- vibe_surf/chrome_extension/scripts/settings-manager.js +954 -1
- vibe_surf/chrome_extension/sidepanel.html +190 -0
- vibe_surf/chrome_extension/styles/settings-integrations.css +927 -0
- vibe_surf/chrome_extension/styles/settings-modal.css +7 -3
- vibe_surf/chrome_extension/styles/settings-responsive.css +37 -5
- vibe_surf/cli.py +98 -3
- vibe_surf/telemetry/__init__.py +60 -0
- vibe_surf/telemetry/service.py +112 -0
- vibe_surf/telemetry/views.py +156 -0
- vibe_surf/tools/composio_client.py +456 -0
- vibe_surf/tools/mcp_client.py +21 -2
- vibe_surf/tools/vibesurf_tools.py +290 -87
- vibe_surf/tools/views.py +16 -0
- vibe_surf/tools/website_api/youtube/client.py +35 -13
- vibe_surf/utils.py +13 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/METADATA +11 -9
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/RECORD +34 -25
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/top_level.txt +0 -0
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
display: flex;
|
|
59
59
|
background: var(--bg-secondary);
|
|
60
60
|
border-bottom: 1px solid var(--border-color);
|
|
61
|
-
overflow
|
|
61
|
+
overflow: visible;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
.settings-tab {
|
|
65
65
|
flex: 1;
|
|
66
|
-
padding: 16px
|
|
66
|
+
padding: 16px 8px;
|
|
67
67
|
border: none;
|
|
68
68
|
background: transparent;
|
|
69
69
|
color: var(--text-secondary);
|
|
@@ -73,7 +73,11 @@
|
|
|
73
73
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
74
74
|
position: relative;
|
|
75
75
|
white-space: nowrap;
|
|
76
|
-
min-width:
|
|
76
|
+
min-width: 0;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
text-align: center;
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
.settings-tab::before {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.settings-tab {
|
|
20
|
-
padding: 14px
|
|
20
|
+
padding: 14px 6px;
|
|
21
21
|
font-size: 14px;
|
|
22
|
-
min-width:
|
|
22
|
+
min-width: 0;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.settings-tab-content {
|
|
@@ -80,9 +80,41 @@
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
.settings-tab {
|
|
83
|
-
padding: 12px
|
|
84
|
-
font-size:
|
|
85
|
-
min-width:
|
|
83
|
+
padding: 12px 4px;
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
writing-mode: horizontal-tb;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Make tabs even more compact for very narrow screens */
|
|
90
|
+
@media (max-width: 400px) {
|
|
91
|
+
.settings-tab {
|
|
92
|
+
padding: 10px 2px;
|
|
93
|
+
font-size: 11px;
|
|
94
|
+
line-height: 1.2;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.settings-tab svg {
|
|
98
|
+
display: none; /* Hide icons on very small screens */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Ultra narrow screens - consider vertical text */
|
|
103
|
+
@media (max-width: 350px) {
|
|
104
|
+
.settings-tabs {
|
|
105
|
+
flex-wrap: wrap;
|
|
106
|
+
height: auto;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.settings-tab {
|
|
110
|
+
writing-mode: vertical-rl;
|
|
111
|
+
text-orientation: mixed;
|
|
112
|
+
padding: 8px 4px;
|
|
113
|
+
min-height: 60px;
|
|
114
|
+
width: calc(20% - 2px);
|
|
115
|
+
font-size: 10px;
|
|
116
|
+
line-height: 1.1;
|
|
117
|
+
}
|
|
86
118
|
}
|
|
87
119
|
|
|
88
120
|
.settings-tab-content {
|
vibe_surf/cli.py
CHANGED
|
@@ -10,6 +10,7 @@ import glob
|
|
|
10
10
|
import json
|
|
11
11
|
import socket
|
|
12
12
|
import platform
|
|
13
|
+
import time
|
|
13
14
|
import importlib.util
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Optional
|
|
@@ -39,6 +40,9 @@ console = Console()
|
|
|
39
40
|
|
|
40
41
|
# Add logger import for the workspace directory logging
|
|
41
42
|
from vibe_surf.logger import get_logger
|
|
43
|
+
from vibe_surf.telemetry.service import ProductTelemetry
|
|
44
|
+
from vibe_surf.telemetry.views import CLITelemetryEvent
|
|
45
|
+
|
|
42
46
|
logger = get_logger(__name__)
|
|
43
47
|
|
|
44
48
|
|
|
@@ -118,6 +122,35 @@ def find_edge_browser() -> Optional[str]:
|
|
|
118
122
|
return _find_browser_from_patterns(patterns)
|
|
119
123
|
|
|
120
124
|
|
|
125
|
+
def find_brave_browser() -> Optional[str]:
|
|
126
|
+
"""Find Brave browser executable."""
|
|
127
|
+
system = platform.system()
|
|
128
|
+
patterns = []
|
|
129
|
+
|
|
130
|
+
if system == 'Darwin': # macOS
|
|
131
|
+
patterns = [
|
|
132
|
+
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
133
|
+
]
|
|
134
|
+
elif system == 'Linux':
|
|
135
|
+
patterns = [
|
|
136
|
+
'/usr/bin/brave-browser',
|
|
137
|
+
'/usr/bin/brave',
|
|
138
|
+
'/usr/local/bin/brave',
|
|
139
|
+
'/snap/bin/brave',
|
|
140
|
+
'/usr/bin/brave-browser-stable',
|
|
141
|
+
'/usr/bin/brave-browser-beta',
|
|
142
|
+
'/usr/bin/brave-browser-dev',
|
|
143
|
+
]
|
|
144
|
+
elif system == 'Windows':
|
|
145
|
+
patterns = [
|
|
146
|
+
r'C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe',
|
|
147
|
+
r'C:\Program Files (x86)\BraveSoftware\Brave-Browser\Application\brave.exe',
|
|
148
|
+
r'%LOCALAPPDATA%\BraveSoftware\Brave-Browser\Application\brave.exe',
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
return _find_browser_from_patterns(patterns)
|
|
152
|
+
|
|
153
|
+
|
|
121
154
|
def _find_browser_from_patterns(patterns: list[str]) -> Optional[str]:
|
|
122
155
|
"""Helper function to find browser from patterns."""
|
|
123
156
|
system = platform.system()
|
|
@@ -182,7 +215,7 @@ def find_available_port(start_port: int) -> int:
|
|
|
182
215
|
def select_browser() -> Optional[str]:
|
|
183
216
|
"""Interactive browser selection."""
|
|
184
217
|
console.print("\n[bold cyan]🌐 Browser Selection[/bold cyan]")
|
|
185
|
-
console.print("VibeSurf supports Chrome and
|
|
218
|
+
console.print("VibeSurf supports Chrome, Edge, and Brave browsers.\n")
|
|
186
219
|
|
|
187
220
|
options = []
|
|
188
221
|
browsers = {}
|
|
@@ -197,11 +230,19 @@ def select_browser() -> Optional[str]:
|
|
|
197
230
|
# Check for Edge
|
|
198
231
|
edge_path = find_edge_browser()
|
|
199
232
|
if edge_path:
|
|
200
|
-
option_num =
|
|
233
|
+
option_num = str(len(options) + 1)
|
|
201
234
|
options.append(option_num)
|
|
202
235
|
browsers[option_num] = ("Edge", edge_path)
|
|
203
236
|
console.print(f"[green]{option_num}.[/green] Microsoft Edge ([dim]{edge_path}[/dim])")
|
|
204
237
|
|
|
238
|
+
# Check for Brave
|
|
239
|
+
brave_path = find_brave_browser()
|
|
240
|
+
if brave_path:
|
|
241
|
+
option_num = str(len(options) + 1)
|
|
242
|
+
options.append(option_num)
|
|
243
|
+
browsers[option_num] = ("Brave", brave_path)
|
|
244
|
+
console.print(f"[green]{option_num}.[/green] Brave Browser ([dim]{brave_path}[/dim])")
|
|
245
|
+
|
|
205
246
|
# Custom browser option
|
|
206
247
|
custom_option = str(len(options) + 1)
|
|
207
248
|
options.append(custom_option)
|
|
@@ -212,7 +253,7 @@ def select_browser() -> Optional[str]:
|
|
|
212
253
|
options.append(quit_option)
|
|
213
254
|
console.print(f"[red]{quit_option}.[/red] Quit")
|
|
214
255
|
|
|
215
|
-
if not chrome_path and not edge_path:
|
|
256
|
+
if not chrome_path and not edge_path and not brave_path:
|
|
216
257
|
console.print("\n[yellow]⚠️ No supported browsers found automatically.[/yellow]")
|
|
217
258
|
|
|
218
259
|
while True:
|
|
@@ -377,6 +418,10 @@ def get_browser_execution_path() -> Optional[str]:
|
|
|
377
418
|
def main():
|
|
378
419
|
"""Main CLI entry point."""
|
|
379
420
|
try:
|
|
421
|
+
# Initialize telemetry
|
|
422
|
+
telemetry = ProductTelemetry()
|
|
423
|
+
start_time = time.time()
|
|
424
|
+
|
|
380
425
|
# Display logo
|
|
381
426
|
console.print(Panel(VIBESURF_LOGO, title="[bold cyan]VibeSurf CLI[/bold cyan]", border_style="cyan"))
|
|
382
427
|
console.print("[dim]A powerful browser automation tool for vibe surfing 🏄♂️[/dim]")
|
|
@@ -384,6 +429,14 @@ def main():
|
|
|
384
429
|
console.print(f"[dim]Version: {vibe_surf.__version__}[/dim]\n")
|
|
385
430
|
console.print(f"[dim]Author: WarmShao and Community Contributors [/dim]\n")
|
|
386
431
|
|
|
432
|
+
# Capture telemetry start event
|
|
433
|
+
start_event = CLITelemetryEvent(
|
|
434
|
+
version=vibe_surf.__version__,
|
|
435
|
+
action='start',
|
|
436
|
+
mode='interactive'
|
|
437
|
+
)
|
|
438
|
+
telemetry.capture(start_event)
|
|
439
|
+
|
|
387
440
|
# Check for existing browser path from configuration
|
|
388
441
|
browser_path = get_browser_execution_path()
|
|
389
442
|
|
|
@@ -405,10 +458,52 @@ def main():
|
|
|
405
458
|
# Start backend
|
|
406
459
|
start_backend(port)
|
|
407
460
|
|
|
461
|
+
# Capture telemetry completion event
|
|
462
|
+
end_time = time.time()
|
|
463
|
+
duration = end_time - start_time
|
|
464
|
+
completion_event = CLITelemetryEvent(
|
|
465
|
+
version=vibe_surf.__version__,
|
|
466
|
+
action='startup_completed',
|
|
467
|
+
mode='interactive',
|
|
468
|
+
duration_seconds=duration,
|
|
469
|
+
browser_path=browser_path
|
|
470
|
+
)
|
|
471
|
+
telemetry.capture(completion_event)
|
|
472
|
+
telemetry.flush()
|
|
473
|
+
|
|
408
474
|
except KeyboardInterrupt:
|
|
409
475
|
console.print("\n[yellow]👋 Goodbye![/yellow]")
|
|
476
|
+
# Capture telemetry interruption event
|
|
477
|
+
try:
|
|
478
|
+
end_time = time.time()
|
|
479
|
+
duration = end_time - start_time
|
|
480
|
+
interrupt_event = CLITelemetryEvent(
|
|
481
|
+
version=vibe_surf.__version__,
|
|
482
|
+
action='interrupted',
|
|
483
|
+
mode='interactive',
|
|
484
|
+
duration_seconds=duration
|
|
485
|
+
)
|
|
486
|
+
telemetry.capture(interrupt_event)
|
|
487
|
+
telemetry.flush()
|
|
488
|
+
except:
|
|
489
|
+
pass
|
|
410
490
|
except Exception as e:
|
|
411
491
|
console.print(f"\n[red]❌ Unexpected error: {e}[/red]")
|
|
492
|
+
# Capture telemetry error event
|
|
493
|
+
try:
|
|
494
|
+
end_time = time.time()
|
|
495
|
+
duration = end_time - start_time
|
|
496
|
+
error_event = CLITelemetryEvent(
|
|
497
|
+
version=vibe_surf.__version__,
|
|
498
|
+
action='error',
|
|
499
|
+
mode='interactive',
|
|
500
|
+
duration_seconds=duration,
|
|
501
|
+
error_message=str(e)[:200]
|
|
502
|
+
)
|
|
503
|
+
telemetry.capture(error_event)
|
|
504
|
+
telemetry.flush()
|
|
505
|
+
except:
|
|
506
|
+
pass
|
|
412
507
|
sys.exit(1)
|
|
413
508
|
|
|
414
509
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Telemetry for VibeSurf.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
# Type stubs for lazy imports
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from vibe_surf.telemetry.service import ProductTelemetry
|
|
10
|
+
from vibe_surf.telemetry.views import (
|
|
11
|
+
BaseTelemetryEvent,
|
|
12
|
+
CLITelemetryEvent,
|
|
13
|
+
MCPClientTelemetryEvent,
|
|
14
|
+
MCPServerTelemetryEvent,
|
|
15
|
+
VibeSurfAgentTelemetryEvent,
|
|
16
|
+
ReportWriterTelemetryEvent,
|
|
17
|
+
BackendTelemetryEvent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Lazy imports mapping
|
|
21
|
+
_LAZY_IMPORTS = {
|
|
22
|
+
'ProductTelemetry': ('vibe_surf.telemetry.service', 'ProductTelemetry'),
|
|
23
|
+
'BaseTelemetryEvent': ('vibe_surf.telemetry.views', 'BaseTelemetryEvent'),
|
|
24
|
+
'CLITelemetryEvent': ('vibe_surf.telemetry.views', 'CLITelemetryEvent'),
|
|
25
|
+
'MCPClientTelemetryEvent': ('vibe_surf.telemetry.views', 'MCPClientTelemetryEvent'),
|
|
26
|
+
'MCPServerTelemetryEvent': ('vibe_surf.telemetry.views', 'MCPServerTelemetryEvent'),
|
|
27
|
+
'VibeSurfAgentTelemetryEvent': ('vibe_surf.telemetry.views', 'VibeSurfAgentTelemetryEvent'),
|
|
28
|
+
'ReportWriterTelemetryEvent': ('vibe_surf.telemetry.views', 'ReportWriterTelemetryEvent'),
|
|
29
|
+
'BackendTelemetryEvent': ('vibe_surf.telemetry.views', 'BackendTelemetryEvent'),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def __getattr__(name: str):
|
|
34
|
+
"""Lazy import mechanism for telemetry components."""
|
|
35
|
+
if name in _LAZY_IMPORTS:
|
|
36
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
|
37
|
+
try:
|
|
38
|
+
from importlib import import_module
|
|
39
|
+
|
|
40
|
+
module = import_module(module_path)
|
|
41
|
+
attr = getattr(module, attr_name)
|
|
42
|
+
# Cache the imported attribute in the module's globals
|
|
43
|
+
globals()[name] = attr
|
|
44
|
+
return attr
|
|
45
|
+
except ImportError as e:
|
|
46
|
+
raise ImportError(f'Failed to import {name} from {module_path}: {e}') from e
|
|
47
|
+
|
|
48
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
'BaseTelemetryEvent',
|
|
53
|
+
'ProductTelemetry',
|
|
54
|
+
'CLITelemetryEvent',
|
|
55
|
+
'MCPClientTelemetryEvent',
|
|
56
|
+
'MCPServerTelemetryEvent',
|
|
57
|
+
'VibeSurfAgentTelemetryEvent',
|
|
58
|
+
'ReportWriterTelemetryEvent',
|
|
59
|
+
'BackendTelemetryEvent',
|
|
60
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import pdb
|
|
4
|
+
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from posthog import Posthog
|
|
7
|
+
from uuid_extensions import uuid7str
|
|
8
|
+
|
|
9
|
+
from vibe_surf.telemetry.views import BaseTelemetryEvent
|
|
10
|
+
from vibe_surf.utils import singleton
|
|
11
|
+
from vibe_surf.logger import get_logger
|
|
12
|
+
|
|
13
|
+
load_dotenv()
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
POSTHOG_EVENT_SETTINGS = {
|
|
18
|
+
'process_person_profile': True,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@singleton
|
|
23
|
+
class ProductTelemetry:
|
|
24
|
+
"""
|
|
25
|
+
Service for capturing anonymized telemetry data.
|
|
26
|
+
|
|
27
|
+
If the environment variable `ANONYMIZED_TELEMETRY=False`, anonymized telemetry will be disabled.
|
|
28
|
+
"""
|
|
29
|
+
WORKSPACE_DIR = os.getenv('VIBESURF_WORKSPACE', './vibesurf_workspace')
|
|
30
|
+
USER_ID_PATH = os.path.join(WORKSPACE_DIR, 'telemetry', 'userid')
|
|
31
|
+
PROJECT_API_KEY = 'phc_lCYnQqFlfNHAlh1TJGqaTvD8EFPCKR7ONsEHbbWuPVr'
|
|
32
|
+
HOST = 'https://us.i.posthog.com'
|
|
33
|
+
UNKNOWN_USER_ID = 'UNKNOWN'
|
|
34
|
+
|
|
35
|
+
_curr_user_id = None
|
|
36
|
+
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
telemetry_enabled = os.getenv('VIBESURF_ANONYMIZED_TELEMETRY', 'true').lower() in ("true", "1", "yes", "on")
|
|
39
|
+
self.debug_logging = os.getenv("VIBESURF_DEBUG", "false").lower() in ("true", "1", "yes", "on")
|
|
40
|
+
|
|
41
|
+
telemetry_disabled = not telemetry_enabled
|
|
42
|
+
|
|
43
|
+
if telemetry_disabled:
|
|
44
|
+
self._posthog_client = None
|
|
45
|
+
else:
|
|
46
|
+
self._posthog_client = Posthog(
|
|
47
|
+
project_api_key=self.PROJECT_API_KEY,
|
|
48
|
+
host=self.HOST,
|
|
49
|
+
disable_geoip=False,
|
|
50
|
+
enable_exception_autocapture=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Silence posthog's logging
|
|
54
|
+
if not self.debug_logging:
|
|
55
|
+
posthog_logger = logging.getLogger('posthog')
|
|
56
|
+
posthog_logger.disabled = True
|
|
57
|
+
|
|
58
|
+
if self._posthog_client is None:
|
|
59
|
+
logger.debug('Telemetry disabled')
|
|
60
|
+
|
|
61
|
+
def capture(self, event: BaseTelemetryEvent) -> None:
|
|
62
|
+
if self._posthog_client is None:
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
self._direct_capture(event)
|
|
66
|
+
|
|
67
|
+
def _direct_capture(self, event: BaseTelemetryEvent) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Should not be thread blocking because posthog magically handles it
|
|
70
|
+
"""
|
|
71
|
+
if self._posthog_client is None:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
self._posthog_client.capture(
|
|
76
|
+
distinct_id=self.user_id,
|
|
77
|
+
event=event.name,
|
|
78
|
+
properties={**event.properties, **POSTHOG_EVENT_SETTINGS},
|
|
79
|
+
)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f'Failed to send telemetry event {event.name}: {e}')
|
|
82
|
+
|
|
83
|
+
def flush(self) -> None:
|
|
84
|
+
if self._posthog_client:
|
|
85
|
+
try:
|
|
86
|
+
self._posthog_client.flush()
|
|
87
|
+
logger.debug('PostHog client telemetry queue flushed.')
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error(f'Failed to flush PostHog client: {e}')
|
|
90
|
+
else:
|
|
91
|
+
logger.debug('PostHog client not available, skipping flush.')
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def user_id(self) -> str:
|
|
95
|
+
if self._curr_user_id:
|
|
96
|
+
return self._curr_user_id
|
|
97
|
+
|
|
98
|
+
# File access may fail due to permissions or other reasons. We don't want to
|
|
99
|
+
# crash so we catch all exceptions.
|
|
100
|
+
try:
|
|
101
|
+
if not os.path.exists(self.USER_ID_PATH):
|
|
102
|
+
os.makedirs(os.path.dirname(self.USER_ID_PATH), exist_ok=True)
|
|
103
|
+
with open(self.USER_ID_PATH, 'w') as f:
|
|
104
|
+
new_user_id = uuid7str()
|
|
105
|
+
f.write(new_user_id)
|
|
106
|
+
self._curr_user_id = new_user_id
|
|
107
|
+
else:
|
|
108
|
+
with open(self.USER_ID_PATH) as f:
|
|
109
|
+
self._curr_user_id = f.read()
|
|
110
|
+
except Exception:
|
|
111
|
+
self._curr_user_id = 'UNKNOWN_USER_ID'
|
|
112
|
+
return self._curr_user_id
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from dataclasses import asdict, dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from browser_use.config import is_running_in_docker
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class BaseTelemetryEvent(ABC):
|
|
11
|
+
@property
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def name(self) -> str:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def properties(self) -> dict[str, Any]:
|
|
18
|
+
props = {k: v for k, v in asdict(self).items() if k != 'name'}
|
|
19
|
+
# Add Docker context if running in Docker
|
|
20
|
+
props['is_docker'] = is_running_in_docker()
|
|
21
|
+
return props
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class AgentTelemetryEvent(BaseTelemetryEvent):
|
|
26
|
+
# start details
|
|
27
|
+
task: str
|
|
28
|
+
model: str
|
|
29
|
+
model_provider: str
|
|
30
|
+
max_steps: int
|
|
31
|
+
max_actions_per_step: int
|
|
32
|
+
use_vision: bool
|
|
33
|
+
version: str
|
|
34
|
+
source: str
|
|
35
|
+
cdp_url: str | None
|
|
36
|
+
# step details
|
|
37
|
+
action_errors: Sequence[str | None]
|
|
38
|
+
action_history: Sequence[list[dict] | None]
|
|
39
|
+
urls_visited: Sequence[str | None]
|
|
40
|
+
# end details
|
|
41
|
+
steps: int
|
|
42
|
+
total_input_tokens: int
|
|
43
|
+
total_duration_seconds: float
|
|
44
|
+
success: bool | None
|
|
45
|
+
final_result_response: str | None
|
|
46
|
+
error_message: str | None
|
|
47
|
+
|
|
48
|
+
name: str = 'agent_event'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class MCPClientTelemetryEvent(BaseTelemetryEvent):
|
|
53
|
+
"""Telemetry event for MCP client usage"""
|
|
54
|
+
|
|
55
|
+
server_name: str
|
|
56
|
+
command: str
|
|
57
|
+
tools_discovered: int
|
|
58
|
+
version: str
|
|
59
|
+
action: str # 'connect', 'disconnect', 'tool_call'
|
|
60
|
+
tool_name: str | None = None
|
|
61
|
+
duration_seconds: float | None = None
|
|
62
|
+
error_message: str | None = None
|
|
63
|
+
|
|
64
|
+
name: str = 'mcp_client_event'
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class MCPServerTelemetryEvent(BaseTelemetryEvent):
|
|
69
|
+
"""Telemetry event for MCP server usage"""
|
|
70
|
+
|
|
71
|
+
version: str
|
|
72
|
+
action: str # 'start', 'stop', 'tool_call'
|
|
73
|
+
tool_name: str | None = None
|
|
74
|
+
duration_seconds: float | None = None
|
|
75
|
+
error_message: str | None = None
|
|
76
|
+
parent_process_cmdline: str | None = None
|
|
77
|
+
|
|
78
|
+
name: str = 'mcp_server_event'
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class ComposioTelemetryEvent(BaseTelemetryEvent):
|
|
83
|
+
"""Telemetry event for Composio client usage"""
|
|
84
|
+
|
|
85
|
+
toolkit_slugs: list[str]
|
|
86
|
+
tools_registered: int
|
|
87
|
+
version: str
|
|
88
|
+
action: str # 'register', 'unregister', 'tool_call'
|
|
89
|
+
toolkit_slug: str | None = None
|
|
90
|
+
tool_name: str | None = None
|
|
91
|
+
duration_seconds: float | None = None
|
|
92
|
+
error_message: str | None = None
|
|
93
|
+
|
|
94
|
+
name: str = 'composio_client_event'
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class CLITelemetryEvent(BaseTelemetryEvent):
|
|
99
|
+
"""Telemetry event for CLI usage"""
|
|
100
|
+
|
|
101
|
+
version: str
|
|
102
|
+
action: str # 'start', 'message_sent', 'task_completed', 'error'
|
|
103
|
+
mode: str # 'interactive', 'oneshot', 'mcp_server'
|
|
104
|
+
model: str | None = None
|
|
105
|
+
model_provider: str | None = None
|
|
106
|
+
browser_path: str | None = None
|
|
107
|
+
duration_seconds: float | None = None
|
|
108
|
+
error_message: str | None = None
|
|
109
|
+
|
|
110
|
+
name: str = 'cli_event'
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
class VibeSurfAgentTelemetryEvent(BaseTelemetryEvent):
|
|
115
|
+
"""Telemetry event for VibeSurf Agent usage"""
|
|
116
|
+
|
|
117
|
+
version: str
|
|
118
|
+
action: str # 'start', 'task_completed', 'error'
|
|
119
|
+
task_description: str | None = None
|
|
120
|
+
model: str | None = None
|
|
121
|
+
model_provider: str | None = None
|
|
122
|
+
duration_seconds: float | None = None
|
|
123
|
+
success: bool | None = None
|
|
124
|
+
error_message: str | None = None
|
|
125
|
+
session_id: str | None = None
|
|
126
|
+
|
|
127
|
+
name: str = 'vibesurf_agent_event'
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class ReportWriterTelemetryEvent(BaseTelemetryEvent):
|
|
132
|
+
"""Telemetry event for Report Writer Agent usage"""
|
|
133
|
+
|
|
134
|
+
version: str
|
|
135
|
+
action: str # 'start', 'report_completed', 'error'
|
|
136
|
+
model: str | None = None
|
|
137
|
+
model_provider: str | None = None
|
|
138
|
+
duration_seconds: float | None = None
|
|
139
|
+
success: bool | None = None
|
|
140
|
+
error_message: str | None = None
|
|
141
|
+
report_type: str | None = None
|
|
142
|
+
|
|
143
|
+
name: str = 'report_writer_event'
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class BackendTelemetryEvent(BaseTelemetryEvent):
|
|
148
|
+
"""Telemetry event for Backend API usage"""
|
|
149
|
+
|
|
150
|
+
version: str
|
|
151
|
+
action: str # 'startup', 'shutdown', 'api_call'
|
|
152
|
+
api_endpoint: str | None = None
|
|
153
|
+
duration_seconds: float | None = None
|
|
154
|
+
error_message: str | None = None
|
|
155
|
+
|
|
156
|
+
name: str = 'backend_event'
|