vibesurf 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- {vibesurf-0.1.2 → vibesurf-0.1.4}/PKG-INFO +6 -1
- {vibesurf-0.1.2 → vibesurf-0.1.4}/README.md +4 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/pyproject.toml +1 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/_version.py +3 -3
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/config.py +18 -4
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/task.py +5 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/shared_state.py +4 -3
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/utils/llm_factory.py +23 -6
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/scripts/ui-manager.js +118 -1
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/styles/components.css +40 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/PKG-INFO +6 -1
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/requires.txt +1 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/.env.example +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/.github/workflows/publish.yml +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/.gitignore +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/.python-version +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/LICENSE +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/MANIFEST.in +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/docs/PYPI_SETUP.md +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/setup.cfg +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/tests/test_agents.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/tests/test_backend_api.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/tests/test_browser.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/tests/test_controller.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/browser_use_agent.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/prompts/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/prompts/vibe_surf_prompt.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/report_writer_agent.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/agents/vibe_surf_agent.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/activity.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/files.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/api/models.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/database/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/database/manager.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/database/models.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/database/queries.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/database/schemas.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/llm_config.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/main.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/migrations/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/migrations/init_db.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/migrations/seed_data.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/utils/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/backend/utils/encryption.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/agen_browser_profile.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/agent_browser_session.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/browser_manager.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/utils.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/watchdogs/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/watchdogs/action_watchdog.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/browser/watchdogs/dom_watchdog.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/background.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/config.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/content.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/dev-reload.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/icons/convert-svg.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/icons/logo-preview.html +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/icons/logo.png +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/manifest.json +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/popup.html +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/scripts/api-client.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/scripts/main.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/scripts/markdown-it.min.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/scripts/session-manager.js +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/sidepanel.html +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/styles/animations.css +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/styles/main.css +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/chrome_extension/styles/settings.css +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/cli.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/controller/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/controller/file_system.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/controller/mcp_client.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/controller/vibesurf_controller.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/controller/views.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/llm/__init__.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibe_surf/llm/openai_compatible.py +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/SOURCES.txt +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/dependency_links.txt +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/entry_points.txt +0 -0
- {vibesurf-0.1.2 → vibesurf-0.1.4}/vibesurf.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vibesurf
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: VibeSurf: A powerful browser assistant for vibe surfing
|
|
5
5
|
Author: Shao Warm
|
|
6
6
|
License: Apache-2.0
|
|
@@ -36,12 +36,17 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
36
36
|
Requires-Dist: sqlalchemy>=2.0.43
|
|
37
37
|
Requires-Dist: aiosqlite>=0.21.0
|
|
38
38
|
Requires-Dist: rich>=13.0.0
|
|
39
|
+
Requires-Dist: greenlet>=3.2.4
|
|
39
40
|
Dynamic: license-file
|
|
40
41
|
|
|
41
42
|
# VibeSurf: A powerful browser assistant for vibe surfing
|
|
43
|
+
[](https://discord.gg/TXNnP9gJ)
|
|
44
|
+
[](https://x.com/warmshao)
|
|
42
45
|
|
|
43
46
|
VibeSurf is an open-source AI agentic browser that revolutionizes browser automation and research.
|
|
44
47
|
|
|
48
|
+
If you're as excited about open-source AI browsing as I am, give it a star! ⭐
|
|
49
|
+
|
|
45
50
|
## ✨ Key Features
|
|
46
51
|
|
|
47
52
|
- 🧠 **Advanced AI Automation**: Beyond browser automation, VibeSurf performs deep research, intelligent crawling, content summarization, and more to exploration.
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# VibeSurf: A powerful browser assistant for vibe surfing
|
|
2
|
+
[](https://discord.gg/TXNnP9gJ)
|
|
3
|
+
[](https://x.com/warmshao)
|
|
2
4
|
|
|
3
5
|
VibeSurf is an open-source AI agentic browser that revolutionizes browser automation and research.
|
|
4
6
|
|
|
7
|
+
If you're as excited about open-source AI browsing as I am, give it a star! ⭐
|
|
8
|
+
|
|
5
9
|
## ✨ Key Features
|
|
6
10
|
|
|
7
11
|
- 🧠 **Advanced AI Automation**: Beyond browser automation, VibeSurf performs deep research, intelligent crawling, content summarization, and more to exploration.
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.4'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 4)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gec7bc3b2e'
|
|
@@ -115,10 +115,24 @@ async def create_llm_profile(
|
|
|
115
115
|
|
|
116
116
|
except Exception as e:
|
|
117
117
|
logger.error(f"Failed to create LLM profile: {e}")
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
|
|
119
|
+
# Handle specific database constraint errors
|
|
120
|
+
error_msg = str(e)
|
|
121
|
+
if "UNIQUE constraint failed: llm_profiles.profile_name" in error_msg:
|
|
122
|
+
raise HTTPException(
|
|
123
|
+
status_code=400,
|
|
124
|
+
detail=f"Profile with name '{profile_request.profile_name}' already exists. Please choose a different name."
|
|
125
|
+
)
|
|
126
|
+
elif "IntegrityError" in error_msg and "profile_name" in error_msg:
|
|
127
|
+
raise HTTPException(
|
|
128
|
+
status_code=400,
|
|
129
|
+
detail=f"Profile name '{profile_request.profile_name}' is already in use. Please choose a different name."
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
raise HTTPException(
|
|
133
|
+
status_code=500,
|
|
134
|
+
detail=f"Failed to create LLM profile: {str(e)}"
|
|
135
|
+
)
|
|
122
136
|
|
|
123
137
|
@router.get("/llm-profiles", response_model=List[LLMProfileResponse])
|
|
124
138
|
async def list_llm_profiles(
|
|
@@ -80,6 +80,11 @@ async def submit_task(
|
|
|
80
80
|
if not mcp_server_config and controller and hasattr(controller, 'mcp_server_config'):
|
|
81
81
|
mcp_server_config = controller.mcp_server_config
|
|
82
82
|
|
|
83
|
+
# Ensure we have a valid MCP server config (never None)
|
|
84
|
+
if mcp_server_config is None:
|
|
85
|
+
mcp_server_config = {"mcpServers": {}}
|
|
86
|
+
logger.info("Using default empty MCP server configuration")
|
|
87
|
+
|
|
83
88
|
# DEBUG: Log the type and content of mcp_server_config
|
|
84
89
|
logger.info(f"mcp_server_config type: {type(mcp_server_config)}, value: {mcp_server_config}")
|
|
85
90
|
|
|
@@ -277,7 +277,8 @@ async def _load_active_mcp_servers():
|
|
|
277
277
|
|
|
278
278
|
try:
|
|
279
279
|
if not db_manager:
|
|
280
|
-
|
|
280
|
+
logger.info("Database manager not available, returning empty MCP config")
|
|
281
|
+
return {"mcpServers": {}}
|
|
281
282
|
|
|
282
283
|
from .database.queries import McpProfileQueries
|
|
283
284
|
|
|
@@ -298,13 +299,13 @@ async def _load_active_mcp_servers():
|
|
|
298
299
|
|
|
299
300
|
except Exception as e:
|
|
300
301
|
logger.warning(f"Failed to load MCP servers from database: {e}")
|
|
301
|
-
return {}
|
|
302
|
+
return {"mcpServers": {}}
|
|
302
303
|
finally:
|
|
303
304
|
break
|
|
304
305
|
|
|
305
306
|
except Exception as e:
|
|
306
307
|
logger.warning(f"Database not available for MCP server loading: {e}")
|
|
307
|
-
return {}
|
|
308
|
+
return {"mcpServers": {}}
|
|
308
309
|
|
|
309
310
|
|
|
310
311
|
async def initialize_vibesurf_components():
|
|
@@ -41,17 +41,34 @@ def create_llm_from_profile(llm_profile):
|
|
|
41
41
|
if not is_provider_supported(provider):
|
|
42
42
|
raise ValueError(f"Unsupported provider: {provider}. Supported: {get_supported_providers()}")
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# Define provider-specific parameter support
|
|
45
|
+
provider_param_support = {
|
|
46
|
+
"openai": ["temperature", "top_p", "frequency_penalty", "seed"],
|
|
47
|
+
"anthropic": ["temperature", "top_p"],
|
|
48
|
+
"google": ["temperature", "top_p"],
|
|
49
|
+
"azure_openai": ["temperature", "top_p", "frequency_penalty", "seed"],
|
|
50
|
+
"groq": ["temperature", "top_p"],
|
|
51
|
+
"ollama": ["temperature"],
|
|
52
|
+
"openrouter": ["temperature", "top_p"], # OpenRouter doesn't support max_tokens
|
|
53
|
+
"deepseek": ["temperature", "top_p"],
|
|
54
|
+
"aws_bedrock": ["temperature"],
|
|
55
|
+
"anthropic_bedrock": ["temperature"],
|
|
56
|
+
"openai_compatible": ["temperature", "top_p", "frequency_penalty", "seed"]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Build common parameters based on provider support
|
|
60
|
+
supported_params = provider_param_support.get(provider, ["temperature", "top_p", "frequency_penalty", "seed"])
|
|
45
61
|
common_params = {}
|
|
46
|
-
|
|
62
|
+
|
|
63
|
+
if temperature is not None and "temperature" in supported_params:
|
|
47
64
|
common_params["temperature"] = temperature
|
|
48
|
-
if max_tokens is not None:
|
|
65
|
+
if max_tokens is not None and "max_tokens" in supported_params:
|
|
49
66
|
common_params["max_tokens"] = max_tokens
|
|
50
|
-
if top_p is not None:
|
|
67
|
+
if top_p is not None and "top_p" in supported_params:
|
|
51
68
|
common_params["top_p"] = top_p
|
|
52
|
-
if frequency_penalty is not None:
|
|
69
|
+
if frequency_penalty is not None and "frequency_penalty" in supported_params:
|
|
53
70
|
common_params["frequency_penalty"] = frequency_penalty
|
|
54
|
-
if seed is not None:
|
|
71
|
+
if seed is not None and "seed" in supported_params:
|
|
55
72
|
common_params["seed"] = seed
|
|
56
73
|
|
|
57
74
|
# Add provider-specific config if available
|
|
@@ -1609,6 +1609,13 @@ class VibeSurfUIManager {
|
|
|
1609
1609
|
console.log('[UIManager] Profile form submit triggered');
|
|
1610
1610
|
|
|
1611
1611
|
const form = event.target;
|
|
1612
|
+
|
|
1613
|
+
// Prevent multiple submissions
|
|
1614
|
+
if (form.dataset.submitting === 'true') {
|
|
1615
|
+
console.log('[UIManager] Form already submitting, ignoring duplicate submission');
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1612
1619
|
const formData = new FormData(form);
|
|
1613
1620
|
const type = form.dataset.type;
|
|
1614
1621
|
const mode = form.dataset.mode;
|
|
@@ -1616,6 +1623,10 @@ class VibeSurfUIManager {
|
|
|
1616
1623
|
|
|
1617
1624
|
console.log('[UIManager] Form submission details:', { type, mode, profileId });
|
|
1618
1625
|
|
|
1626
|
+
// Set submitting state and disable form
|
|
1627
|
+
form.dataset.submitting = 'true';
|
|
1628
|
+
this.setProfileFormSubmitting(true);
|
|
1629
|
+
|
|
1619
1630
|
// Convert FormData to object
|
|
1620
1631
|
const data = {};
|
|
1621
1632
|
|
|
@@ -1746,7 +1757,113 @@ class VibeSurfUIManager {
|
|
|
1746
1757
|
|
|
1747
1758
|
} catch (error) {
|
|
1748
1759
|
console.error(`[UIManager] Failed to ${mode} ${type} profile:`, error);
|
|
1749
|
-
|
|
1760
|
+
|
|
1761
|
+
// Handle specific error types for better user experience
|
|
1762
|
+
let errorMessage = error.message || 'Unknown error occurred';
|
|
1763
|
+
|
|
1764
|
+
if (errorMessage.includes('already exists') || errorMessage.includes('already in use')) {
|
|
1765
|
+
// For duplicate profile name errors, highlight the name field
|
|
1766
|
+
this.highlightProfileNameError(errorMessage);
|
|
1767
|
+
errorMessage = errorMessage; // Use the specific error message from backend
|
|
1768
|
+
} else if (errorMessage.includes('UNIQUE constraint')) {
|
|
1769
|
+
errorMessage = `Profile name '${data.profile_name || data.display_name}' already exists. Please choose a different name.`;
|
|
1770
|
+
this.highlightProfileNameError(errorMessage);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
this.showNotification(`Failed to ${mode} ${type} profile: ${errorMessage}`, 'error');
|
|
1774
|
+
} finally {
|
|
1775
|
+
// Reset form state
|
|
1776
|
+
form.dataset.submitting = 'false';
|
|
1777
|
+
this.setProfileFormSubmitting(false);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
setProfileFormSubmitting(isSubmitting) {
|
|
1782
|
+
const form = this.elements.profileForm;
|
|
1783
|
+
const submitButton = this.elements.profileFormSubmit;
|
|
1784
|
+
const cancelButton = this.elements.profileFormCancel;
|
|
1785
|
+
|
|
1786
|
+
if (!form) return;
|
|
1787
|
+
|
|
1788
|
+
// Disable/enable form inputs
|
|
1789
|
+
const inputs = form.querySelectorAll('input, select, textarea');
|
|
1790
|
+
inputs.forEach(input => {
|
|
1791
|
+
input.disabled = isSubmitting;
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
// Update submit button
|
|
1795
|
+
if (submitButton) {
|
|
1796
|
+
submitButton.disabled = isSubmitting;
|
|
1797
|
+
submitButton.textContent = isSubmitting ? 'Saving...' : 'Save Profile';
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// Update cancel button
|
|
1801
|
+
if (cancelButton) {
|
|
1802
|
+
cancelButton.disabled = isSubmitting;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
console.log(`[UIManager] Profile form submitting state: ${isSubmitting}`);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
highlightProfileNameError(errorMessage) {
|
|
1809
|
+
const nameInput = this.elements.profileForm?.querySelector('input[name="profile_name"], input[name="display_name"]');
|
|
1810
|
+
|
|
1811
|
+
if (nameInput) {
|
|
1812
|
+
// Add error styling
|
|
1813
|
+
nameInput.classList.add('form-error');
|
|
1814
|
+
nameInput.focus();
|
|
1815
|
+
|
|
1816
|
+
// Create or update error message
|
|
1817
|
+
let errorElement = nameInput.parentElement.querySelector('.profile-name-error');
|
|
1818
|
+
if (!errorElement) {
|
|
1819
|
+
errorElement = document.createElement('div');
|
|
1820
|
+
errorElement.className = 'form-error-message profile-name-error';
|
|
1821
|
+
nameInput.parentElement.appendChild(errorElement);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
errorElement.textContent = errorMessage;
|
|
1825
|
+
|
|
1826
|
+
// Remove error styling after user starts typing
|
|
1827
|
+
const removeError = () => {
|
|
1828
|
+
nameInput.classList.remove('form-error');
|
|
1829
|
+
if (errorElement) {
|
|
1830
|
+
errorElement.remove();
|
|
1831
|
+
}
|
|
1832
|
+
nameInput.removeEventListener('input', removeError);
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
nameInput.addEventListener('input', removeError);
|
|
1836
|
+
|
|
1837
|
+
console.log('[UIManager] Highlighted profile name error:', errorMessage);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// Real-time profile name validation
|
|
1842
|
+
async validateProfileNameAvailability(profileName, profileType) {
|
|
1843
|
+
if (!profileName || profileName.trim().length < 2) {
|
|
1844
|
+
return { isValid: true, message: '' }; // Don't validate very short names
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
try {
|
|
1848
|
+
// Check if profile already exists
|
|
1849
|
+
const profiles = profileType === 'llm' ? this.state.llmProfiles : this.state.mcpProfiles;
|
|
1850
|
+
const nameField = profileType === 'llm' ? 'profile_name' : 'display_name';
|
|
1851
|
+
|
|
1852
|
+
const existingProfile = profiles.find(profile =>
|
|
1853
|
+
profile[nameField].toLowerCase() === profileName.toLowerCase()
|
|
1854
|
+
);
|
|
1855
|
+
|
|
1856
|
+
if (existingProfile) {
|
|
1857
|
+
return {
|
|
1858
|
+
isValid: false,
|
|
1859
|
+
message: `${profileType.toUpperCase()} profile "${profileName}" already exists. Please choose a different name.`
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
return { isValid: true, message: '' };
|
|
1864
|
+
} catch (error) {
|
|
1865
|
+
console.error('[UIManager] Error validating profile name:', error);
|
|
1866
|
+
return { isValid: true, message: '' }; // Don't block on validation errors
|
|
1750
1867
|
}
|
|
1751
1868
|
}
|
|
1752
1869
|
|
|
@@ -366,6 +366,46 @@
|
|
|
366
366
|
padding-right: 32px;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
/* Form input error state */
|
|
370
|
+
.form-input.form-error {
|
|
371
|
+
border-color: var(--danger-color) !important;
|
|
372
|
+
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1) !important;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.form-input.form-error:focus {
|
|
376
|
+
border-color: var(--danger-color) !important;
|
|
377
|
+
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.2) !important;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* Form error message */
|
|
381
|
+
.form-error-message {
|
|
382
|
+
font-size: var(--font-size-xs);
|
|
383
|
+
color: var(--danger-color);
|
|
384
|
+
margin-top: var(--spacing-xs);
|
|
385
|
+
display: block;
|
|
386
|
+
font-weight: var(--font-weight-medium);
|
|
387
|
+
animation: slideDown 0.2s ease-out;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.profile-name-error {
|
|
391
|
+
background-color: rgba(220, 53, 69, 0.05);
|
|
392
|
+
padding: var(--spacing-xs) var(--spacing-sm);
|
|
393
|
+
border-radius: var(--radius-sm);
|
|
394
|
+
border: 1px solid rgba(220, 53, 69, 0.2);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
@keyframes slideDown {
|
|
398
|
+
from {
|
|
399
|
+
opacity: 0;
|
|
400
|
+
transform: translateY(-10px);
|
|
401
|
+
}
|
|
402
|
+
to {
|
|
403
|
+
opacity: 1;
|
|
404
|
+
transform: translateY(0);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/* Legacy form error class for backwards compatibility */
|
|
369
409
|
.form-error {
|
|
370
410
|
font-size: var(--font-size-xs);
|
|
371
411
|
color: var(--danger-color);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vibesurf
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: VibeSurf: A powerful browser assistant for vibe surfing
|
|
5
5
|
Author: Shao Warm
|
|
6
6
|
License: Apache-2.0
|
|
@@ -36,12 +36,17 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
36
36
|
Requires-Dist: sqlalchemy>=2.0.43
|
|
37
37
|
Requires-Dist: aiosqlite>=0.21.0
|
|
38
38
|
Requires-Dist: rich>=13.0.0
|
|
39
|
+
Requires-Dist: greenlet>=3.2.4
|
|
39
40
|
Dynamic: license-file
|
|
40
41
|
|
|
41
42
|
# VibeSurf: A powerful browser assistant for vibe surfing
|
|
43
|
+
[](https://discord.gg/TXNnP9gJ)
|
|
44
|
+
[](https://x.com/warmshao)
|
|
42
45
|
|
|
43
46
|
VibeSurf is an open-source AI agentic browser that revolutionizes browser automation and research.
|
|
44
47
|
|
|
48
|
+
If you're as excited about open-source AI browsing as I am, give it a star! ⭐
|
|
49
|
+
|
|
45
50
|
## ✨ Key Features
|
|
46
51
|
|
|
47
52
|
- 🧠 **Advanced AI Automation**: Beyond browser automation, VibeSurf performs deep research, intelligent crawling, content summarization, and more to exploration.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|