vibesurf 0.1.35__py3-none-any.whl → 0.1.37__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.
@@ -41,6 +41,7 @@ from vibe_surf.browser.browser_manager import BrowserManager
41
41
  from vibe_surf.tools.vibesurf_registry import VibeSurfRegistry
42
42
  from bs4 import BeautifulSoup
43
43
  from vibe_surf.logger import get_logger
44
+ from vibe_surf.tools.utils import clean_html_basic
44
45
 
45
46
  logger = get_logger(__name__)
46
47
 
@@ -49,123 +50,6 @@ Context = TypeVar('Context')
49
50
  T = TypeVar('T', bound=BaseModel)
50
51
 
51
52
 
52
- def clean_html_basic(page_html_content, max_text_length=100):
53
- soup = BeautifulSoup(page_html_content, 'html.parser')
54
-
55
- for script in soup(["script", "style"]):
56
- script.decompose()
57
-
58
- from bs4 import Comment
59
- comments = soup.findAll(text=lambda text: isinstance(text, Comment))
60
- for comment in comments:
61
- comment.extract()
62
-
63
- for text_node in soup.find_all(string=True):
64
- if text_node.parent.name not in ['script', 'style']:
65
- clean_text = ' '.join(text_node.split())
66
-
67
- if len(clean_text) > max_text_length:
68
- clean_text = clean_text[:max_text_length].rstrip() + "..."
69
-
70
- if clean_text != text_node:
71
- text_node.replace_with(clean_text)
72
-
73
- important_attrs = ['id', 'class', 'name', 'role', 'type',
74
- 'colspan', 'rowspan', 'headers', 'scope',
75
- 'href', 'src', 'alt', 'title']
76
-
77
- for tag in soup.find_all():
78
- attrs_to_keep = {}
79
- for attr in list(tag.attrs.keys()):
80
- if (attr in important_attrs or
81
- attr.startswith('data-') or
82
- attr.startswith('aria-')):
83
- attrs_to_keep[attr] = tag.attrs[attr]
84
- tag.attrs = attrs_to_keep
85
-
86
- return str(soup)
87
-
88
-
89
- def get_sibling_position(node: EnhancedDOMTreeNode) -> int:
90
- """Get the position of node among its siblings with the same tag"""
91
- if not node.parent_node:
92
- return 1
93
-
94
- tag_name = node.tag_name
95
- position = 1
96
-
97
- # Find siblings with same tag name before this node
98
- for sibling in node.parent_node.children:
99
- if sibling == node:
100
- break
101
- if sibling.tag_name == tag_name:
102
- position += 1
103
-
104
- return position
105
-
106
-
107
- def extract_css_hints(node: EnhancedDOMTreeNode) -> dict:
108
- """Extract CSS selector construction hints"""
109
- hints = {}
110
-
111
- if "id" in node.attributes:
112
- hints["id"] = f"#{node.attributes['id']}"
113
-
114
- if "class" in node.attributes:
115
- classes = node.attributes["class"].split()
116
- hints["class"] = f".{'.'.join(classes[:3])}" # Limit class count
117
-
118
- # Attribute selector hints
119
- for attr in ["name", "data-testid", "type"]:
120
- if attr in node.attributes:
121
- hints[f"attr_{attr}"] = f"[{attr}='{node.attributes[attr]}']"
122
-
123
- return hints
124
-
125
-
126
- def convert_selector_map_for_llm(selector_map) -> dict:
127
- """
128
- Convert complex selector_map to simplified format suitable for LLM understanding and JS code writing
129
- """
130
- simplified_elements = []
131
-
132
- for element_index, node in selector_map.items():
133
- if node.is_visible and node.element_index is not None: # Only include visible interactive elements
134
- element_info = {
135
- "tag": node.tag_name,
136
- "text": node.get_meaningful_text_for_llm()[:200], # Limit text length
137
-
138
- # Selector information - most needed for JS code
139
- "selectors": {
140
- "xpath": node.xpath,
141
- "css_hints": extract_css_hints(node), # Extract id, class etc
142
- },
143
-
144
- # Element semantics
145
- "role": node.ax_node.role if node.ax_node else None,
146
- "type": node.attributes.get("type"),
147
- "aria_label": node.attributes.get("aria-label"),
148
-
149
- # Key attributes
150
- "attributes": {k: v for k, v in node.attributes.items()
151
- if k in ["id", "class", "name", "href", "src", "value", "placeholder", "data-testid"]},
152
-
153
- # Interactivity
154
- "is_clickable": node.snapshot_node.is_clickable if node.snapshot_node else False,
155
- "is_input": node.tag_name.lower() in ["input", "textarea", "select"],
156
-
157
- # Structure information
158
- "parent_tag": node.parent_node.tag_name if node.parent_node else None,
159
- "position_info": f"{node.tag_name}[{get_sibling_position(node)}]"
160
- }
161
- simplified_elements.append(element_info)
162
-
163
- return {
164
- "page_elements": simplified_elements,
165
- "total_elements": len(simplified_elements)
166
- }
167
-
168
-
169
53
  class VibeSurfTools:
170
54
  def __init__(self, exclude_actions: list[str] = [], mcp_server_config: Optional[Dict[str, Any]] = None,
171
55
  composio_client: ComposioClient = None):
@@ -182,7 +66,7 @@ class VibeSurfTools:
182
66
 
183
67
  def _register_skills(self):
184
68
  @self.registry.action(
185
- 'Skill: Advanced parallel search - analyze user intent and generate 5 different search tasks, perform parallel Google searches, and return top 10 most relevant results',
69
+ 'parallel search',
186
70
  param_model=SkillSearchAction,
187
71
  )
188
72
  async def skill_search(
@@ -373,7 +257,7 @@ Format: [index1, index2, index3, ...]
373
257
  await browser_manager.unregister_agent(agent_id, close_tabs=True)
374
258
 
375
259
  @self.registry.action(
376
- 'Skill: Crawl a web page and extract structured information from a webpage with optional tab selection',
260
+ '',
377
261
  param_model=SkillCrawlAction,
378
262
  )
379
263
  async def skill_crawl(
@@ -393,13 +277,19 @@ Format: [index1, index2, index3, ...]
393
277
  browser_session = browser_manager.main_browser_session
394
278
 
395
279
  # If tab_id is provided, switch to that tab
280
+ target_id = None
396
281
  if params.tab_id:
397
282
  target_id = await browser_session.get_target_id_from_tab_id(params.tab_id)
283
+ current_target_id = None
284
+ if browser_session.agent_focus:
285
+ current_target_id = browser_session.agent_focus.target_id
286
+ if current_target_id != target_id:
287
+ browser_session._dom_watchdog.enhanced_dom_tree = None
398
288
  await browser_session.get_or_create_cdp_session(target_id, focus=True)
399
289
 
400
290
  # Extract structured content using the existing method
401
291
  extracted_content = await self._extract_structured_content(
402
- browser_session, params.query, llm
292
+ browser_session, params.query, llm, target_id=target_id
403
293
  )
404
294
 
405
295
  current_url = await browser_session.get_current_page_url()
@@ -426,7 +316,7 @@ Format: [index1, index2, index3, ...]
426
316
  return ActionResult(error=f'Skill crawl failed: {str(e)}')
427
317
 
428
318
  @self.registry.action(
429
- 'Skill: Summarize webpage content with optional tab selection',
319
+ '',
430
320
  param_model=SkillSummaryAction,
431
321
  )
432
322
  async def skill_summary(
@@ -446,13 +336,19 @@ Format: [index1, index2, index3, ...]
446
336
  browser_session = browser_manager.main_browser_session
447
337
 
448
338
  # If tab_id is provided, switch to that tab
339
+ target_id = None
449
340
  if params.tab_id:
450
341
  target_id = await browser_session.get_target_id_from_tab_id(params.tab_id)
342
+ current_target_id = None
343
+ if browser_session.agent_focus:
344
+ current_target_id = browser_session.agent_focus.target_id
345
+ if current_target_id != target_id:
346
+ browser_session._dom_watchdog.enhanced_dom_tree = None
451
347
  await browser_session.get_or_create_cdp_session(target_id, focus=True)
452
348
 
453
349
  # Extract and summarize content
454
350
  summary = await self._extract_structured_content(
455
- browser_session, "Provide a comprehensive summary of this webpage", llm
351
+ browser_session, "Provide a comprehensive summary of this webpage", llm, target_id=target_id
456
352
  )
457
353
 
458
354
  current_url = await browser_session.get_current_page_url()
@@ -479,7 +375,7 @@ Format: [index1, index2, index3, ...]
479
375
  return ActionResult(error=f'Skill summary failed: {str(e)}')
480
376
 
481
377
  @self.registry.action(
482
- 'Skill: Take screenshot of current page or specified tab',
378
+ '',
483
379
  param_model=SkillTakeScreenshotAction,
484
380
  )
485
381
  async def skill_screenshot(
@@ -500,7 +396,7 @@ Format: [index1, index2, index3, ...]
500
396
  await browser_session.get_or_create_cdp_session(target_id, focus=True)
501
397
 
502
398
  # Take screenshot using browser session
503
- screenshot = await browser_session.take_screenshot()
399
+ screenshot_bytes = await browser_session.take_screenshot()
504
400
 
505
401
  # Generate timestamp for filename
506
402
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -520,7 +416,7 @@ Format: [index1, index2, index3, ...]
520
416
  filepath = screenshots_dir / filename
521
417
 
522
418
  with open(filepath, "wb") as f:
523
- f.write(base64.b64decode(screenshot))
419
+ f.write(screenshot_bytes)
524
420
 
525
421
  msg = f'📸 Screenshot saved to path: [{filename}]({str(filepath.relative_to(fs_dir))})'
526
422
  logger.info(msg)
@@ -536,7 +432,7 @@ Format: [index1, index2, index3, ...]
536
432
  return ActionResult(error=error_msg)
537
433
 
538
434
  @self.registry.action(
539
- 'Skill: Execute JavaScript code on webpage with optional tab selection - accepts functional requirements, code prompts, or code snippets that will be processed by LLM to generate proper executable JavaScript',
435
+ 'Execute JavaScript code on webpage',
540
436
  param_model=SkillCodeAction,
541
437
  )
542
438
  async def skill_code(
@@ -643,7 +539,7 @@ Return ONLY the JavaScript code, no explanations or markdown formatting."""
643
539
 
644
540
  USER REQUIREMENT: {params.code_requirement}
645
541
 
646
- Web Page Html Content:
542
+ Web Page Content:
647
543
  {web_page_html}
648
544
 
649
545
  Generate JavaScript code to fulfill the requirement:"""
@@ -815,7 +711,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
815
711
  return ActionResult(error=f'Skill code failed: {str(e)}')
816
712
 
817
713
  @self.registry.action(
818
- 'Skill: Deep research mode - Only return the guideline for deep research. Please follow the guideline to do real deep research actions.',
714
+ 'Return the guideline for deep research. Please follow the guideline to do real deep research actions.',
819
715
  param_model=NoParamsAction,
820
716
  )
821
717
  async def skill_deep_research(
@@ -866,7 +762,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
866
762
  )
867
763
 
868
764
  @self.registry.action(
869
- 'Skill: Get comprehensive financial data for stocks - retrieve company information, historical prices, news, earnings, dividends, analyst recommendations and other financial data using Yahoo Finance. Available methods include: get_info (company info), get_history (price history), get_news (latest news), get_dividends (dividend history), get_earnings (earnings data), get_recommendations (analyst recommendations), get_balance_sheet (balance sheet data), get_income_stmt (income statement), get_cashflow (cash flow statement), get_fast_info (quick stats), get_institutional_holders (institutional ownership), get_major_holders (major shareholders), get_sustainability (ESG data), get_upgrades_downgrades (analyst upgrades/downgrades), and more. If no methods specified, defaults to get_info.',
765
+ 'Get comprehensive financial data for stocks - retrieve company information, historical prices, news, earnings, dividends, analyst recommendations and other financial data using Yahoo Finance.',
870
766
  param_model=SkillFinanceAction,
871
767
  )
872
768
  async def skill_finance(
@@ -951,7 +847,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
951
847
  return ActionResult(error=error_msg, extracted_content=error_msg)
952
848
 
953
849
  @self.registry.action(
954
- 'Skill: Xiaohongshu API - Access Xiaohongshu (Little Red Book) platform data including search, content details, comments, user profiles, and recommendations. Methods: search_content_by_keyword, fetch_content_details, fetch_all_content_comments, get_user_profile, fetch_all_user_content, get_home_recommendations.',
850
+ '',
955
851
  param_model=SkillXhsAction,
956
852
  )
957
853
  async def skill_xhs(
@@ -1055,7 +951,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
1055
951
  return ActionResult(error=error_msg, extracted_content=error_msg)
1056
952
 
1057
953
  @self.registry.action(
1058
- 'Skill: Weibo API - Access Weibo platform data including search, post details, comments, user profiles, hot posts, and trending lists. Methods: search_posts_by_keyword, get_post_detail, get_all_post_comments, get_user_info, get_all_user_posts, get_hot_posts(推荐榜), get_trending_posts(热搜榜).',
954
+ '',
1059
955
  param_model=SkillWeiboAction,
1060
956
  )
1061
957
  async def skill_weibo(
@@ -1163,7 +1059,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
1163
1059
  return ActionResult(error=error_msg, extracted_content=error_msg)
1164
1060
 
1165
1061
  @self.registry.action(
1166
- 'Skill: Douyin API - Access Douyin platform data including search, video details, comments, user profiles, and videos. Methods: search_content_by_keyword, fetch_video_details, fetch_all_video_comments, fetch_user_info, fetch_all_user_videos.',
1062
+ '',
1167
1063
  param_model=SkillDouyinAction,
1168
1064
  )
1169
1065
  async def skill_douyin(
@@ -1264,18 +1160,7 @@ Please generate alternative JavaScript code that avoids this system error:"""
1264
1160
  return ActionResult(error=error_msg, extracted_content=error_msg)
1265
1161
 
1266
1162
  @self.registry.action(
1267
- """Skill: YouTube API - Access YouTube platform data including search, video details, comments, channel info, trending videos, and video transcripts.
1268
- Methods:
1269
- search_videos,
1270
- get_video_details,
1271
- get_video_comments,
1272
- get_channel_info,
1273
- get_channel_videos,
1274
- get_trending_videos,
1275
- get_video_transcript.
1276
-
1277
- If users want to know the specific content of this video, please use get_video_transcript to get detailed video content first.
1278
- """,
1163
+ """YouTube API - If users want to know the specific content of this video, please use get_video_transcript to get detailed video content first.""",
1279
1164
  param_model=SkillYoutubeAction,
1280
1165
  )
1281
1166
  async def skill_youtube(
@@ -1641,15 +1526,20 @@ Return results as a JSON array: [{{"title": "...", "url": "...", "summary": "...
1641
1526
  deduplicated.sort(key=relevance_score, reverse=True)
1642
1527
  return deduplicated
1643
1528
 
1644
- async def _extract_structured_content(self, browser_session, query: str, llm: BaseChatModel):
1529
+ async def _extract_structured_content(self, browser_session, query: str, llm: BaseChatModel,
1530
+ target_id: str | None = None, extract_links: bool = False):
1645
1531
  """Helper method to extract structured content from current page"""
1646
1532
  MAX_CHAR_LIMIT = 30000
1647
1533
 
1648
1534
  # Extract clean markdown using the existing method
1649
1535
  try:
1650
- content, content_stats = await self.extract_clean_markdown(browser_session, extract_links=False)
1536
+ from browser_use.dom.markdown_extractor import extract_clean_markdown
1537
+
1538
+ content, content_stats = await extract_clean_markdown(
1539
+ browser_session=browser_session, extract_links=extract_links
1540
+ )
1651
1541
  except Exception as e:
1652
- raise RuntimeError(f'Could not extract clean markdown: {type(e).__name__}')
1542
+ raise RuntimeError(f'Could not extract clean markdown: {e}')
1653
1543
 
1654
1544
  # Smart truncation with context preservation
1655
1545
  if len(content) > MAX_CHAR_LIMIT:
@@ -1697,90 +1587,9 @@ You will be given a query and the markdown of a webpage that has been filtered t
1697
1587
  logger.debug(f'Error extracting content: {e}')
1698
1588
  raise RuntimeError(str(e))
1699
1589
 
1700
- async def extract_clean_markdown(
1701
- self, browser_session: BrowserSession, extract_links: bool = True
1702
- ) -> tuple[str, dict[str, Any]]:
1703
- """Extract clean markdown from the current page."""
1704
- import re
1705
-
1706
- # Get HTML content from current page
1707
- cdp_session = await browser_session.get_or_create_cdp_session()
1708
- try:
1709
- body_id = await cdp_session.cdp_client.send.DOM.getDocument(session_id=cdp_session.session_id)
1710
- page_html_result = await cdp_session.cdp_client.send.DOM.getOuterHTML(
1711
- params={'backendNodeId': body_id['root']['backendNodeId']}, session_id=cdp_session.session_id
1712
- )
1713
- page_html = page_html_result['outerHTML']
1714
- current_url = await browser_session.get_current_page_url()
1715
- except Exception as e:
1716
- raise RuntimeError(f"Couldn't extract page content: {e}")
1717
-
1718
- original_html_length = len(page_html)
1719
-
1720
- # Use html2text for clean markdown conversion
1721
- import html2text
1722
-
1723
- h = html2text.HTML2Text()
1724
- h.ignore_links = not extract_links
1725
- h.ignore_images = True
1726
- h.ignore_emphasis = False
1727
- h.body_width = 0 # Don't wrap lines
1728
- h.unicode_snob = True
1729
- h.skip_internal_links = True
1730
- content = h.handle(page_html)
1731
-
1732
- initial_markdown_length = len(content)
1733
-
1734
- # Minimal cleanup - html2text already does most of the work
1735
- content = re.sub(r'%[0-9A-Fa-f]{2}', '', content) # Remove any remaining URL encoding
1736
-
1737
- # Apply light preprocessing to clean up excessive whitespace
1738
- content, chars_filtered = self._preprocess_markdown_content(content)
1739
-
1740
- final_filtered_length = len(content)
1741
-
1742
- # Content statistics
1743
- stats = {
1744
- 'url': current_url,
1745
- 'original_html_chars': original_html_length,
1746
- 'initial_markdown_chars': initial_markdown_length,
1747
- 'filtered_chars_removed': chars_filtered,
1748
- 'final_filtered_chars': final_filtered_length,
1749
- }
1750
-
1751
- return content, stats
1752
-
1753
- def _preprocess_markdown_content(self, content: str, max_newlines: int = 3) -> tuple[str, int]:
1754
- """Light preprocessing of html2text output - minimal cleanup since html2text is already clean."""
1755
- import re
1756
-
1757
- original_length = len(content)
1758
-
1759
- # Compress consecutive newlines (4+ newlines become max_newlines)
1760
- content = re.sub(r'\n{4,}', '\n' * max_newlines, content)
1761
-
1762
- # Remove lines that are only whitespace or very short (likely artifacts)
1763
- lines = content.split('\n')
1764
- filtered_lines = []
1765
- for line in lines:
1766
- stripped = line.strip()
1767
- # Keep lines with substantial content (html2text output is already clean)
1768
- if len(stripped) > 2:
1769
- filtered_lines.append(line)
1770
-
1771
- content = '\n'.join(filtered_lines)
1772
- content = content.strip()
1773
-
1774
- chars_filtered = original_length - len(content)
1775
- return content, chars_filtered
1776
-
1777
1590
  def _register_browser_use_agent(self):
1778
1591
  @self.registry.action(
1779
- 'Execute browser_use agent tasks. Supports both single task execution (list length=1) and '
1780
- 'parallel execution of multiple tasks for improved efficiency. '
1781
- 'Accepts a list of tasks where each task can specify a tab_id (optional), '
1782
- 'task description (focusing on goals and expected returns), and task_files (optional). '
1783
- 'Browser_use agent has strong planning and execution capabilities, only needs task descriptions and desired outcomes.',
1592
+ 'Execute browser_use agent tasks.',
1784
1593
  param_model=BrowserUseAgentExecution,
1785
1594
  )
1786
1595
  async def execute_browser_use_agent(
@@ -1804,8 +1613,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
1804
1613
 
1805
1614
  def _register_report_writer_agent(self):
1806
1615
  @self.registry.action(
1807
- 'Execute report writer agent to generate HTML reports. '
1808
- 'Task should describe report requirements, goals, insights observed, and any hints or tips for generating the report.',
1616
+ 'Execute report writer agent to generate HTML reports. ',
1809
1617
  param_model=ReportWriterTask,
1810
1618
  )
1811
1619
  async def execute_report_writer_agent(
@@ -2004,7 +1812,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
2004
1812
 
2005
1813
  def _register_file_actions(self):
2006
1814
  @self.registry.action(
2007
- 'Replace old_str with new_str in file_name. old_str must exactly match the string to replace in original text. Recommended tool to mark completed items in todo.md or change specific contents in a file.'
1815
+ ''
2008
1816
  )
2009
1817
  async def replace_file_str(file_name: str, old_str: str, new_str: str, file_system: CustomFileSystem):
2010
1818
  result = await file_system.replace_file_str(file_name, old_str, new_str)
@@ -2149,7 +1957,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
2149
1957
  raise RuntimeError(str(e))
2150
1958
 
2151
1959
  @self.registry.action(
2152
- 'Write or append content to file_path in file system. Allowed extensions are .md, .txt, .json, .csv, .pdf. For .pdf files, write the content in markdown format and it will automatically be converted to a properly formatted PDF document.'
1960
+ ''
2153
1961
  )
2154
1962
  async def write_file(
2155
1963
  file_path: str,
@@ -2171,7 +1979,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
2171
1979
  return ActionResult(extracted_content=result, long_term_memory=result)
2172
1980
 
2173
1981
  @self.registry.action(
2174
- 'Copy a file to the FileSystem. Set external_src=True to copy from external file(absolute path)to FileSystem, False to copy within FileSystem.'
1982
+ 'Set external_src=True to copy from external file(absolute path)to FileSystem, False to copy within FileSystem.'
2175
1983
  )
2176
1984
  async def copy_file(src_file_path: str, dst_file_path: str, file_system: CustomFileSystem,
2177
1985
  external_src: bool = False):
@@ -2228,7 +2036,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
2228
2036
  )
2229
2037
 
2230
2038
  @self.registry.action(
2231
- 'List contents of a directory within the FileSystem. Use empty string "" or "." to list the root data_dir, or provide relative path for subdirectory.'
2039
+ 'List a directory within the FileSystem. Use empty string "" or "." to list the root FileSystem, or provide relative path for subdirectory.'
2232
2040
  )
2233
2041
  async def list_directory(directory_path: str, file_system: CustomFileSystem):
2234
2042
  result = await file_system.list_directory(directory_path)
@@ -2240,7 +2048,7 @@ You will be given a query and the markdown of a webpage that has been filtered t
2240
2048
  )
2241
2049
 
2242
2050
  @self.registry.action(
2243
- 'Grep content from file - search for query or keywords and return surrounding context (simulates Linux grep command). For images, uses OCR to extract text first then performs grep search.',
2051
+ 'search for query or keywords and return surrounding context',
2244
2052
  param_model=GrepContentAction,
2245
2053
  )
2246
2054
  async def grep_content_from_file(
vibe_surf/tools/views.py CHANGED
@@ -56,7 +56,7 @@ class BrowserUseAgentExecution(BaseModel):
56
56
  """Parameters for executing browser_use agent tasks in parallel"""
57
57
  tasks: list[BrowserUseAgentTask] = Field(
58
58
  description='List of tasks to execute concurrently using browser_use agents for improved efficiency. '
59
- 'If only one task is provided, the agent can take over the entire browser and can also see and operate all tabs.',
59
+ 'If only one task and no tab_id is provided, the agent can take over the entire browser and can also see and operate all tabs.',
60
60
  min_length=1,
61
61
  )
62
62
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibesurf
3
- Version: 0.1.35
3
+ Version: 0.1.37
4
4
  Summary: VibeSurf: A powerful browser assistant for vibe surfing
5
5
  Author: WarmShao
6
6
  License: Apache-2.0
@@ -37,7 +37,6 @@ Requires-Dist: aiosqlite>=0.21.0
37
37
  Requires-Dist: rich>=13.0.0
38
38
  Requires-Dist: greenlet>=3.2.4
39
39
  Requires-Dist: getmac>=0.9.5
40
- Requires-Dist: browser-use==0.7.10
41
40
  Requires-Dist: markdown-pdf>=1.9
42
41
  Requires-Dist: nanoid>=2.0.0
43
42
  Requires-Dist: markdownify>=1.2.0
@@ -48,6 +47,12 @@ Requires-Dist: pyexecjs>=1.5.1
48
47
  Requires-Dist: youtube-transcript-api>=1.2.2
49
48
  Requires-Dist: composio>=0.8.20
50
49
  Requires-Dist: composio-langchain>=0.8.20
50
+ Requires-Dist: browser-use==0.8.1
51
+ Requires-Dist: google-genai<2.0.0,>=1.29.0
52
+ Requires-Dist: openai<2.0.0,>=1.99.2
53
+ Requires-Dist: google-api-python-client>=2.174.0
54
+ Requires-Dist: google-auth>=2.40.3
55
+ Requires-Dist: google-auth-oauthlib>=1.2.2
51
56
  Dynamic: license-file
52
57
 
53
58
  # VibeSurf: A powerful browser assistant for vibe surfing
@@ -103,6 +108,11 @@ Start the VibeSurf browser assistant
103
108
  uv run vibesurf
104
109
  ```
105
110
 
111
+ ### 4. Start to Use
112
+
113
+ <video src="https://github.com/user-attachments/assets/86dba2e4-3f33-4ccf-b400-d07cf1a481a0" controls="controls">Your browser does not support playing this video!</video>
114
+
115
+
106
116
  ## 👩‍💻 For Contributors
107
117
 
108
118
  Want to contribute to VibeSurf? Follow these steps to set up your development environment:
@@ -1,21 +1,21 @@
1
1
  vibe_surf/__init__.py,sha256=WtduuMFGauMD_9dpk4fnRnLTAP6ka9Lfu0feAFNzLfo,339
2
- vibe_surf/_version.py,sha256=R33we6XZbIm7oMUQa7CWkUGL7_tbZV98kEQQwmuIJeo,706
3
- vibe_surf/cli.py,sha256=o5Z-ren0-yr7CLamG6CSmUl5jhAGICuay0ssvaGnsuI,19759
2
+ vibe_surf/_version.py,sha256=Un30RAtqEXXKI1lbcmQdjd4zLY640yIy_ojmAQvwoug,706
3
+ vibe_surf/cli.py,sha256=pzJs--bjV6ZU7njJnxK_lh76l3pQ3opg_5V9Hw17PQw,21924
4
4
  vibe_surf/common.py,sha256=_WWMxen5wFwzUjEShn3yDVC1OBFUiJ6Vccadi6tuG6w,1215
5
5
  vibe_surf/logger.py,sha256=k53MFA96QX6t9OfcOf1Zws8PP0OOqjVJfhUD3Do9lKw,3043
6
6
  vibe_surf/utils.py,sha256=3j6uRkgjOAIDYmbpW8C-DpYhdf1Kvahz715KSriVIk0,261
7
7
  vibe_surf/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- vibe_surf/agents/browser_use_agent.py,sha256=c4Sy8HgSnuxbOSAK4VL55UNBiE1K1eH_Ko4L3AUPA08,45955
9
- vibe_surf/agents/report_writer_agent.py,sha256=oPub78ICAHLghUzRcRCDcwKD_yB_LYAj_ede4wJyO2s,23006
10
- vibe_surf/agents/vibe_surf_agent.py,sha256=7YOjNHb52LYoYjBfUFvkh6u_SpZEWbs-3E-gLYS91pY,76480
8
+ vibe_surf/agents/browser_use_agent.py,sha256=Uj9tbCR5_dbrSSnKuajbL4zP_E9CwLMrdvw0EmDwnC0,34473
9
+ vibe_surf/agents/report_writer_agent.py,sha256=0MqRFHQpL7-HIDJt8r3TUqI0qh9SMb3LlkZSWc3d0Sg,23903
10
+ vibe_surf/agents/vibe_surf_agent.py,sha256=y9qD72wBpx9q2Vk2nSP07t1L8cycpJ0zh6EB11-8cb0,78793
11
11
  vibe_surf/agents/views.py,sha256=yHjNJloa-aofVTGyuRy08tBYP_Y3XLqt1DUWOUmHRng,4825
12
12
  vibe_surf/agents/prompts/__init__.py,sha256=l4ieA0D8kLJthyNN85FKLNe4ExBa3stY3l-aImLDRD0,36
13
13
  vibe_surf/agents/prompts/report_writer_prompt.py,sha256=sZE8MUT1CDLmRzbnbEQzAvTwJjpITgh2Q8g1_eXmkzE,4454
14
14
  vibe_surf/agents/prompts/vibe_surf_prompt.py,sha256=UnZxgy9sgr4u1RNYr6VJBk3sEUbFasu2z75QGsILXk8,7507
15
15
  vibe_surf/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- vibe_surf/backend/llm_config.py,sha256=9V8Gg065TQALbOKQnOqFWd8RzOJjegOD8w6YOf90Q7Y,5036
16
+ vibe_surf/backend/llm_config.py,sha256=buzNzncyz_Lu__mRU7aMtCpfKmWjbgpneBzcz_cH9Jg,5697
17
17
  vibe_surf/backend/main.py,sha256=8_n9dUF972E_UqguXK4zfq5AeulTUeey4X60qOXnigY,8272
18
- vibe_surf/backend/shared_state.py,sha256=4ZFG9JtjMWnUYefst8klJbIy-udMfNXClWJsg_fYlq0,28666
18
+ vibe_surf/backend/shared_state.py,sha256=AK7MGBZMbaCxhuBgnfRtfcxMQS9SACh75W6UM8w4vMA,28654
19
19
  vibe_surf/backend/voice_model_config.py,sha256=oee4fvOexXKzKRDv2-FEKQj7Z2OznACrj6mfWRGy7h0,567
20
20
  vibe_surf/backend/api/__init__.py,sha256=XxF1jUOORpLYCfFuPrrnUGRnOrr6ClH0_MNPU-4RnSs,68
21
21
  vibe_surf/backend/api/activity.py,sha256=_cnHusqolt5Hf3KdAf6FK-3sBc-TSaadmb5dJxGI57A,9398
@@ -39,17 +39,17 @@ vibe_surf/backend/database/migrations/v004_add_voice_profiles.sql,sha256=-9arjQB
39
39
  vibe_surf/backend/database/migrations/v005_add_composio_integration.sql,sha256=QuTrRYyOmHdepRcHHwrGlLpsNq9qo5mnOB5LfXtN-CU,1172
40
40
  vibe_surf/backend/database/migrations/v006_add_credentials_table.sql,sha256=udyVO_L8CvdBKGNfmnxj-kU7UxR9EzUqMpHuV1h46vU,812
41
41
  vibe_surf/backend/utils/__init__.py,sha256=V8leMFp7apAglUAoCHPZrNNcRHthSLYIudIJE5qwjb0,184
42
- vibe_surf/backend/utils/encryption.py,sha256=CjLNh_n0Luhfa-6BB-icfzkiiDqj5b4Gu6MADU3p2eM,3754
43
- vibe_surf/backend/utils/llm_factory.py,sha256=XIJYc9Lh_L2vbwlAe96PrjptlzJtLOjCGNdHEx6fThk,9047
42
+ vibe_surf/backend/utils/encryption.py,sha256=LYTKJdOrKfQt8U7GUXIEsns6iBeXnZ7jlOPOgxvkD5c,5112
43
+ vibe_surf/backend/utils/llm_factory.py,sha256=KKxi3yi61OU6Prd3druzYkD7JQkqwArkzALWYYb8G3g,9570
44
44
  vibe_surf/backend/utils/utils.py,sha256=nY5-DGY4MfPumpQxXLQiodO0Db7lChryWIHbY9h971o,1137
45
45
  vibe_surf/browser/__init__.py,sha256=_UToO2fZfSCrfjOcxhn4Qq7ZLbYeyPuUUEmqIva-Yv8,325
46
- vibe_surf/browser/agen_browser_profile.py,sha256=J06hCBJSJ-zAFVM9yDFz8UpmiLuFyWke1EMekpU45eo,5871
47
- vibe_surf/browser/agent_browser_session.py,sha256=9bCQkPNEy88-04gB9njBrbuoFBhIYF_SxueZfFe5RCM,35662
46
+ vibe_surf/browser/agen_browser_profile.py,sha256=fHk5q6hkwHfoNIuUitUBPJLdqbsZ7xjsLU6vTTlXEQI,6177
47
+ vibe_surf/browser/agent_browser_session.py,sha256=Bzak1psvyqvQ-fmvXP0Dky-J6gfcaL4NyQEN_vrXsKc,38977
48
48
  vibe_surf/browser/browser_manager.py,sha256=PFJ9flmqR2uokuRZ3PDh_Dy6_6qcQ6kH_eRc1yT6Iq8,11257
49
49
  vibe_surf/browser/utils.py,sha256=bBtpMiyz2ixWOr31GbJwZ8pVYcnxztKjTJJO92z1BDY,35742
50
50
  vibe_surf/browser/watchdogs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- vibe_surf/browser/watchdogs/action_watchdog.py,sha256=6lM0nOR67clLzC6nVEMZ2Vam8VHDW8GRlg_jbGUHbPk,5297
52
- vibe_surf/browser/watchdogs/dom_watchdog.py,sha256=c0AJo2yckWFZqpgnPz7RbsRjcpwlhjD4mSIzCAbRn48,10994
51
+ vibe_surf/browser/watchdogs/action_watchdog.py,sha256=6J7ox8hlA3EeRI6YiBxpKCdaybe7n_zp9UpdBWSTc4s,609
52
+ vibe_surf/browser/watchdogs/dom_watchdog.py,sha256=9fNB-FjqJbeKpKp_UezPeymI-0yTUu_0LB3SEHXfdr8,11023
53
53
  vibe_surf/chrome_extension/background.js,sha256=9YLbLmBl__PPUwN6Edf0EQfE53Br1VfjlUiIkznf4n4,30181
54
54
  vibe_surf/chrome_extension/config.js,sha256=rd4I2pyEWEYgVKIj7wEcwd8ouotm7ZlGwsRRSWMmAGY,1311
55
55
  vibe_surf/chrome_extension/content.js,sha256=cB67jK3vIE5zrpXAfi3p50H3EyTqK5xockOph0Q4kQg,13708
@@ -91,20 +91,21 @@ vibe_surf/chrome_extension/styles/settings-responsive.css,sha256=jIlYWkOpom0iTWQ
91
91
  vibe_surf/chrome_extension/styles/settings-utilities.css,sha256=3PuQS2857kg83d5erLbLdo_7J95-qV-qyNWS5M-w1oQ,505
92
92
  vibe_surf/chrome_extension/styles/variables.css,sha256=enjyhsa0PeU3b-3uiXa-VkV-1-h2-Ai3m4KpmC2k0rY,2984
93
93
  vibe_surf/llm/__init__.py,sha256=_vDVPo6STf343p1SgMQrF5023hicAx0g83pK2Gbk4Ek,601
94
- vibe_surf/llm/openai_compatible.py,sha256=i0a5OLaL6QIlacVyctOG09vKr3KOi8T8Izp1v7xkD5I,16112
94
+ vibe_surf/llm/openai_compatible.py,sha256=Ftir3mvzJxi2dPfQj1fcpkqm0180Xm1HNAhEuUk-V5I,15926
95
95
  vibe_surf/telemetry/__init__.py,sha256=izfMX5F3ff92t4E6Y-6onYG8oJAx-bGtFFXRymPwFp0,2085
96
96
  vibe_surf/telemetry/service.py,sha256=7AsvouGUI5lz5hsfxitVcarCwbfmGJHjJkslDZiTEhY,3717
97
- vibe_surf/telemetry/views.py,sha256=9CVFwwlmSi6xYuM4KI9C3cGe5bxMGm0-qAAwNmXN7zw,3800
97
+ vibe_surf/telemetry/views.py,sha256=llbXm4GkYLlJc5vYKDDUqcKDCUOJ_ScS22rwzxdSUEc,4602
98
98
  vibe_surf/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
- vibe_surf/tools/browser_use_tools.py,sha256=kvDUMQY4ScrTIs1jxF4JbsDK39W3ti283KKrjf35e2A,31925
99
+ vibe_surf/tools/browser_use_tools.py,sha256=Al65_YfOoMk112oeDz1gRgHC08RsaL19DSFr3oG0vCw,30726
100
100
  vibe_surf/tools/composio_client.py,sha256=hbvUYiTSlNNRvBQmoVkxtJnd6pXm7PaevcTo1nQZEx8,18822
101
- vibe_surf/tools/file_system.py,sha256=Tw_6J5QjCahQ3fd26CXziF1zPvRxhYM0889oK4bDhlU,19304
101
+ vibe_surf/tools/file_system.py,sha256=wzEWQJUVLE7xLSOIz4Fx7MWkniQaSJhgCGQAZ9eMJ38,19374
102
102
  vibe_surf/tools/finance_tools.py,sha256=E8rmblp57e_cp0tFbdZ7BY3_upNlk4Whk0bYc_SFCJE,27284
103
103
  vibe_surf/tools/mcp_client.py,sha256=ZrwzboDBf7-rtzO8JYpBhvKGg88CMfbrXlhtIs8pXSQ,3167
104
104
  vibe_surf/tools/report_writer_tools.py,sha256=2CyTTXOahTKZo7XwyWDDhJ--1mRA0uTtUWxu_DACAY0,776
105
+ vibe_surf/tools/utils.py,sha256=-Hu60Qu0M_gJpU7Ow8WpgBbZzMv_76DsYYj7eFk6M08,4219
105
106
  vibe_surf/tools/vibesurf_registry.py,sha256=Z-8d9BrJl3RFMEK0Tw1Q5xNHX2kZGsnIGCTBZ3RM-pw,2159
106
- vibe_surf/tools/vibesurf_tools.py,sha256=oVQOVUntjoOriNxp9Zg7KkrglrySEcfD2jhyKuv4kpo,122966
107
- vibe_surf/tools/views.py,sha256=pkZuX7FgvwYz9UqCo9Z4zMq84hu_XkMmxoy84iMMcg8,12883
107
+ vibe_surf/tools/vibesurf_tools.py,sha256=feBejeVXacbfWjzPsKVqc2dnYVoI-jalAtwdXB6_N4U,113152
108
+ vibe_surf/tools/views.py,sha256=r_xHyBHR-d8cSgvZ-GJVnB4k7pQq9g5gsp882zM70mM,12897
108
109
  vibe_surf/tools/voice_asr.py,sha256=AJG0yq_Jq-j8ulDlbPhVFfK1jch9_ASesis73iki9II,4702
109
110
  vibe_surf/tools/website_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
111
  vibe_surf/tools/website_api/douyin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -119,9 +120,9 @@ vibe_surf/tools/website_api/xhs/helpers.py,sha256=Dq2RyYKClBQ2ha2yEfpS1mtZswx0z9
119
120
  vibe_surf/tools/website_api/youtube/__init__.py,sha256=QWmZWSqo1O6XtaWP-SuL3HrBLYINjEWEyOy-KCytGDw,1145
120
121
  vibe_surf/tools/website_api/youtube/client.py,sha256=0kKy7fkBNc63hRvDgXaKnguDpDJ92rG8T2nT_Y1F5MQ,51989
121
122
  vibe_surf/tools/website_api/youtube/helpers.py,sha256=GPgqfNirLYjIpk1OObvoXd2Ktq-ahKOOKHO2WwQVXCw,12931
122
- vibesurf-0.1.35.dist-info/licenses/LICENSE,sha256=vRmTjOYvD8RLiSGYYmFHnveYNswtO1uvSk1sd-Eu7sg,2037
123
- vibesurf-0.1.35.dist-info/METADATA,sha256=T3bA86HJwenL2K-qxc8eHhpIs3_Xhm0zsYG6JALB3mo,6387
124
- vibesurf-0.1.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
125
- vibesurf-0.1.35.dist-info/entry_points.txt,sha256=UxqpvMocL-PR33S6vLF2OmXn-kVzM-DneMeZeHcPMM8,48
126
- vibesurf-0.1.35.dist-info/top_level.txt,sha256=VPZGHqSb6EEqcJ4ZX6bHIuWfon5f6HXl3c7BYpbRqnY,10
127
- vibesurf-0.1.35.dist-info/RECORD,,
123
+ vibesurf-0.1.37.dist-info/licenses/LICENSE,sha256=vRmTjOYvD8RLiSGYYmFHnveYNswtO1uvSk1sd-Eu7sg,2037
124
+ vibesurf-0.1.37.dist-info/METADATA,sha256=R4NaabFRS7T1PU4X2kp1u1x4lu4uI85UzBhyqjDOP38,6787
125
+ vibesurf-0.1.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
126
+ vibesurf-0.1.37.dist-info/entry_points.txt,sha256=UxqpvMocL-PR33S6vLF2OmXn-kVzM-DneMeZeHcPMM8,48
127
+ vibesurf-0.1.37.dist-info/top_level.txt,sha256=VPZGHqSb6EEqcJ4ZX6bHIuWfon5f6HXl3c7BYpbRqnY,10
128
+ vibesurf-0.1.37.dist-info/RECORD,,