vibesurf 0.1.0__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/__init__.py +12 -0
- vibe_surf/_version.py +34 -0
- vibe_surf/agents/__init__.py +0 -0
- vibe_surf/agents/browser_use_agent.py +1106 -0
- vibe_surf/agents/prompts/__init__.py +1 -0
- vibe_surf/agents/prompts/vibe_surf_prompt.py +176 -0
- vibe_surf/agents/report_writer_agent.py +360 -0
- vibe_surf/agents/vibe_surf_agent.py +1632 -0
- vibe_surf/backend/__init__.py +0 -0
- vibe_surf/backend/api/__init__.py +3 -0
- vibe_surf/backend/api/activity.py +243 -0
- vibe_surf/backend/api/config.py +740 -0
- vibe_surf/backend/api/files.py +322 -0
- vibe_surf/backend/api/models.py +257 -0
- vibe_surf/backend/api/task.py +300 -0
- vibe_surf/backend/database/__init__.py +13 -0
- vibe_surf/backend/database/manager.py +129 -0
- vibe_surf/backend/database/models.py +164 -0
- vibe_surf/backend/database/queries.py +922 -0
- vibe_surf/backend/database/schemas.py +100 -0
- vibe_surf/backend/llm_config.py +182 -0
- vibe_surf/backend/main.py +137 -0
- vibe_surf/backend/migrations/__init__.py +16 -0
- vibe_surf/backend/migrations/init_db.py +303 -0
- vibe_surf/backend/migrations/seed_data.py +236 -0
- vibe_surf/backend/shared_state.py +601 -0
- vibe_surf/backend/utils/__init__.py +7 -0
- vibe_surf/backend/utils/encryption.py +164 -0
- vibe_surf/backend/utils/llm_factory.py +225 -0
- vibe_surf/browser/__init__.py +8 -0
- vibe_surf/browser/agen_browser_profile.py +130 -0
- vibe_surf/browser/agent_browser_session.py +416 -0
- vibe_surf/browser/browser_manager.py +296 -0
- vibe_surf/browser/utils.py +790 -0
- vibe_surf/browser/watchdogs/__init__.py +0 -0
- vibe_surf/browser/watchdogs/action_watchdog.py +291 -0
- vibe_surf/browser/watchdogs/dom_watchdog.py +954 -0
- vibe_surf/chrome_extension/background.js +558 -0
- vibe_surf/chrome_extension/config.js +48 -0
- vibe_surf/chrome_extension/content.js +284 -0
- vibe_surf/chrome_extension/dev-reload.js +47 -0
- vibe_surf/chrome_extension/icons/convert-svg.js +33 -0
- vibe_surf/chrome_extension/icons/logo-preview.html +187 -0
- vibe_surf/chrome_extension/icons/logo.png +0 -0
- vibe_surf/chrome_extension/manifest.json +53 -0
- vibe_surf/chrome_extension/popup.html +134 -0
- vibe_surf/chrome_extension/scripts/api-client.js +473 -0
- vibe_surf/chrome_extension/scripts/main.js +491 -0
- vibe_surf/chrome_extension/scripts/markdown-it.min.js +3 -0
- vibe_surf/chrome_extension/scripts/session-manager.js +599 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +3687 -0
- vibe_surf/chrome_extension/sidepanel.html +347 -0
- vibe_surf/chrome_extension/styles/animations.css +471 -0
- vibe_surf/chrome_extension/styles/components.css +670 -0
- vibe_surf/chrome_extension/styles/main.css +2307 -0
- vibe_surf/chrome_extension/styles/settings.css +1100 -0
- vibe_surf/cli.py +357 -0
- vibe_surf/controller/__init__.py +0 -0
- vibe_surf/controller/file_system.py +53 -0
- vibe_surf/controller/mcp_client.py +68 -0
- vibe_surf/controller/vibesurf_controller.py +616 -0
- vibe_surf/controller/views.py +37 -0
- vibe_surf/llm/__init__.py +21 -0
- vibe_surf/llm/openai_compatible.py +237 -0
- vibesurf-0.1.0.dist-info/METADATA +97 -0
- vibesurf-0.1.0.dist-info/RECORD +70 -0
- vibesurf-0.1.0.dist-info/WHEEL +5 -0
- vibesurf-0.1.0.dist-info/entry_points.txt +2 -0
- vibesurf-0.1.0.dist-info/licenses/LICENSE +201 -0
- vibesurf-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>VibeSurf</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
width: 300px;
|
|
10
|
+
height: 200px;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
14
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
15
|
+
color: white;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
text-align: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.logo {
|
|
24
|
+
font-size: 32px;
|
|
25
|
+
margin-bottom: 10px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h1 {
|
|
29
|
+
font-size: 24px;
|
|
30
|
+
margin: 0 0 10px 0;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
p {
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
margin: 0 0 20px 0;
|
|
37
|
+
opacity: 0.9;
|
|
38
|
+
line-height: 1.4;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.open-panel-btn {
|
|
42
|
+
background: rgba(255, 255, 255, 0.2);
|
|
43
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
44
|
+
color: white;
|
|
45
|
+
padding: 10px 20px;
|
|
46
|
+
border-radius: 6px;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
font-size: 14px;
|
|
49
|
+
font-weight: 500;
|
|
50
|
+
transition: all 0.2s ease;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.open-panel-btn:hover {
|
|
54
|
+
background: rgba(255, 255, 255, 0.3);
|
|
55
|
+
border-color: rgba(255, 255, 255, 0.5);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.status {
|
|
59
|
+
position: absolute;
|
|
60
|
+
bottom: 10px;
|
|
61
|
+
right: 10px;
|
|
62
|
+
font-size: 12px;
|
|
63
|
+
opacity: 0.7;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.status-dot {
|
|
67
|
+
width: 8px;
|
|
68
|
+
height: 8px;
|
|
69
|
+
border-radius: 50%;
|
|
70
|
+
background: #28a745;
|
|
71
|
+
display: inline-block;
|
|
72
|
+
margin-right: 5px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.status-dot.disconnected {
|
|
76
|
+
background: #dc3545;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
</head>
|
|
80
|
+
<body>
|
|
81
|
+
<div class="logo">🌊</div>
|
|
82
|
+
<h1>VibeSurf</h1>
|
|
83
|
+
<p>AI-powered browsing automation at your fingertips</p>
|
|
84
|
+
<button class="open-panel-btn" id="openPanelBtn">Open Side Panel</button>
|
|
85
|
+
|
|
86
|
+
<div class="status">
|
|
87
|
+
<span class="status-dot" id="statusDot"></span>
|
|
88
|
+
<span id="statusText">Ready</span>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<script>
|
|
92
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
93
|
+
const openPanelBtn = document.getElementById('openPanelBtn');
|
|
94
|
+
const statusDot = document.getElementById('statusDot');
|
|
95
|
+
const statusText = document.getElementById('statusText');
|
|
96
|
+
|
|
97
|
+
// Handle open panel button click
|
|
98
|
+
openPanelBtn.addEventListener('click', async () => {
|
|
99
|
+
try {
|
|
100
|
+
// Get current tab
|
|
101
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
102
|
+
|
|
103
|
+
// Open side panel
|
|
104
|
+
await chrome.sidePanel.open({ tabId: tab.id });
|
|
105
|
+
|
|
106
|
+
// Close popup
|
|
107
|
+
window.close();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('Failed to open side panel:', error);
|
|
110
|
+
statusText.textContent = 'Error';
|
|
111
|
+
statusDot.classList.add('disconnected');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Check backend status
|
|
116
|
+
try {
|
|
117
|
+
const response = await chrome.runtime.sendMessage({ type: 'GET_BACKEND_STATUS' });
|
|
118
|
+
|
|
119
|
+
if (response.status === 'connected') {
|
|
120
|
+
statusText.textContent = 'Connected';
|
|
121
|
+
statusDot.classList.remove('disconnected');
|
|
122
|
+
} else {
|
|
123
|
+
statusText.textContent = 'Disconnected';
|
|
124
|
+
statusDot.classList.add('disconnected');
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error('Failed to check backend status:', error);
|
|
128
|
+
statusText.textContent = 'Error';
|
|
129
|
+
statusDot.classList.add('disconnected');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
</script>
|
|
133
|
+
</body>
|
|
134
|
+
</html>
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
// API Client - VibeSurf Backend Communication
|
|
2
|
+
// Handles all HTTP requests to the VibeSurf backend API
|
|
3
|
+
|
|
4
|
+
class VibeSurfAPIClient {
|
|
5
|
+
constructor(baseURL = null) {
|
|
6
|
+
// Use configuration file values as defaults
|
|
7
|
+
const config = window.VIBESURF_CONFIG || {};
|
|
8
|
+
this.baseURL = (baseURL || config.BACKEND_URL || 'http://localhost:9335').replace(/\/$/, ''); // Remove trailing slash
|
|
9
|
+
this.apiPrefix = config.API_PREFIX || '/api';
|
|
10
|
+
this.timeout = config.DEFAULT_TIMEOUT || 30000;
|
|
11
|
+
this.retryAttempts = config.RETRY_ATTEMPTS || 3;
|
|
12
|
+
this.retryDelay = config.RETRY_DELAY || 1000;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Utility method to build full URL
|
|
16
|
+
buildURL(endpoint) {
|
|
17
|
+
const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
18
|
+
return `${this.baseURL}${this.apiPrefix}${cleanEndpoint}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Generic HTTP request method with error handling and retries
|
|
22
|
+
async request(method, endpoint, options = {}) {
|
|
23
|
+
const {
|
|
24
|
+
data,
|
|
25
|
+
params,
|
|
26
|
+
headers = {},
|
|
27
|
+
timeout = this.timeout,
|
|
28
|
+
retries = this.retryAttempts,
|
|
29
|
+
...fetchOptions
|
|
30
|
+
} = options;
|
|
31
|
+
|
|
32
|
+
const url = new URL(this.buildURL(endpoint));
|
|
33
|
+
|
|
34
|
+
// Add query parameters
|
|
35
|
+
if (params) {
|
|
36
|
+
Object.keys(params).forEach(key => {
|
|
37
|
+
if (params[key] !== undefined && params[key] !== null) {
|
|
38
|
+
url.searchParams.append(key, params[key]);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const config = {
|
|
44
|
+
method,
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
...headers
|
|
48
|
+
},
|
|
49
|
+
signal: AbortSignal.timeout(timeout),
|
|
50
|
+
...fetchOptions
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Add body for POST/PUT requests
|
|
54
|
+
if (data && method !== 'GET') {
|
|
55
|
+
if (data instanceof FormData) {
|
|
56
|
+
// Remove Content-Type for FormData (browser will set it with boundary)
|
|
57
|
+
delete config.headers['Content-Type'];
|
|
58
|
+
config.body = data;
|
|
59
|
+
} else {
|
|
60
|
+
config.body = JSON.stringify(data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let lastError;
|
|
65
|
+
|
|
66
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
67
|
+
try {
|
|
68
|
+
console.log(`[API] ${method} ${url} (attempt ${attempt + 1}/${retries + 1})`);
|
|
69
|
+
|
|
70
|
+
const response = await fetch(url, config);
|
|
71
|
+
|
|
72
|
+
// Handle different response types
|
|
73
|
+
const contentType = response.headers.get('content-type');
|
|
74
|
+
let responseData;
|
|
75
|
+
|
|
76
|
+
if (contentType && contentType.includes('application/json')) {
|
|
77
|
+
responseData = await response.json();
|
|
78
|
+
} else {
|
|
79
|
+
responseData = await response.text();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new APIError(
|
|
84
|
+
responseData.detail || responseData.message || `HTTP ${response.status}`,
|
|
85
|
+
response.status,
|
|
86
|
+
responseData
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(`[API] ${method} ${url} - Success`);
|
|
91
|
+
return responseData;
|
|
92
|
+
|
|
93
|
+
} catch (error) {
|
|
94
|
+
lastError = error;
|
|
95
|
+
console.error(`[API] ${method} ${url} - Error (attempt ${attempt + 1}):`, error);
|
|
96
|
+
|
|
97
|
+
// Don't retry on certain errors
|
|
98
|
+
if (error instanceof APIError) {
|
|
99
|
+
if (error.status >= 400 && error.status < 500) {
|
|
100
|
+
throw error; // Client errors shouldn't be retried
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Don't retry on timeout for the last attempt
|
|
105
|
+
if (attempt === retries) {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Wait before retry
|
|
110
|
+
await this.delay(this.retryDelay * (attempt + 1));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
throw lastError;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// HTTP method helpers
|
|
118
|
+
async get(endpoint, options = {}) {
|
|
119
|
+
return this.request('GET', endpoint, options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async post(endpoint, data, options = {}) {
|
|
123
|
+
return this.request('POST', endpoint, { data, ...options });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async put(endpoint, data, options = {}) {
|
|
127
|
+
return this.request('PUT', endpoint, { data, ...options });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async delete(endpoint, options = {}) {
|
|
131
|
+
return this.request('DELETE', endpoint, options);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Health check - special method that bypasses API prefix
|
|
135
|
+
async healthCheck() {
|
|
136
|
+
try {
|
|
137
|
+
// Build URL without API prefix for health endpoint
|
|
138
|
+
const url = `${this.baseURL}/health`;
|
|
139
|
+
const response = await fetch(url, {
|
|
140
|
+
method: 'GET',
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'application/json'
|
|
143
|
+
},
|
|
144
|
+
signal: AbortSignal.timeout(5000)
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
throw new Error(`HTTP ${response.status}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const data = await response.json();
|
|
152
|
+
console.log('[API] Health check - Success');
|
|
153
|
+
return { status: 'healthy', data };
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error('[API] Health check - Error:', error);
|
|
156
|
+
return { status: 'unhealthy', error: error.message };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// System status
|
|
161
|
+
async getSystemStatus() {
|
|
162
|
+
return this.get('/status');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Task Management APIs
|
|
166
|
+
async submitTask(taskData) {
|
|
167
|
+
const {
|
|
168
|
+
session_id,
|
|
169
|
+
task_description,
|
|
170
|
+
llm_profile_name,
|
|
171
|
+
upload_files_path,
|
|
172
|
+
mcp_server_config
|
|
173
|
+
} = taskData;
|
|
174
|
+
|
|
175
|
+
return this.post('/tasks/submit', {
|
|
176
|
+
session_id,
|
|
177
|
+
task_description,
|
|
178
|
+
llm_profile_name,
|
|
179
|
+
upload_files_path,
|
|
180
|
+
mcp_server_config
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async getTaskStatus() {
|
|
185
|
+
return this.get('/tasks/status');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async checkTaskRunning() {
|
|
189
|
+
try {
|
|
190
|
+
const status = await this.getTaskStatus();
|
|
191
|
+
|
|
192
|
+
console.log('[API] Task status check result:', {
|
|
193
|
+
has_active_task: status.has_active_task,
|
|
194
|
+
active_task: status.active_task
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Check if there's an active task and its status
|
|
198
|
+
const hasActiveTask = status.has_active_task;
|
|
199
|
+
const activeTask = status.active_task;
|
|
200
|
+
|
|
201
|
+
if (!hasActiveTask || !activeTask) {
|
|
202
|
+
return { isRunning: false, taskInfo: null };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check if the active task is in a "running" state
|
|
206
|
+
const runningStates = ['running', 'submitted', 'paused'];
|
|
207
|
+
const taskStatus = activeTask.status || '';
|
|
208
|
+
const isRunning = runningStates.includes(taskStatus.toLowerCase());
|
|
209
|
+
|
|
210
|
+
console.log('[API] Task running check:', {
|
|
211
|
+
taskStatus,
|
|
212
|
+
isRunning,
|
|
213
|
+
runningStates
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
isRunning,
|
|
218
|
+
taskInfo: hasActiveTask ? activeTask : null
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('[API] Failed to check task status:', error);
|
|
222
|
+
return { isRunning: false, taskInfo: null };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async pauseTask(reason = 'User requested pause') {
|
|
227
|
+
return this.post('/tasks/pause', { reason });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async resumeTask(reason = 'User requested resume') {
|
|
231
|
+
return this.post('/tasks/resume', { reason });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async stopTask(reason = 'User requested stop') {
|
|
235
|
+
return this.post('/tasks/stop', { reason });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Activity APIs
|
|
239
|
+
async getTaskInfo(taskId) {
|
|
240
|
+
return this.get(`/activity/${taskId}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async getSessionTasks(sessionId) {
|
|
244
|
+
return this.get(`/activity/sessions/${sessionId}/tasks`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async getSessionActivity(sessionId, params = {}) {
|
|
248
|
+
return this.get(`/activity/sessions/${sessionId}/activity`, { params });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async getLatestActivity(sessionId) {
|
|
252
|
+
return this.get(`/activity/sessions/${sessionId}/latest_activity`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async getRecentTasks(limit = -1) {
|
|
256
|
+
return this.get('/activity/tasks', { params: { limit } });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getAllSessions(limit = -1, offset = 0) {
|
|
260
|
+
return this.get('/activity/sessions', { params: { limit, offset } });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Real-time activity polling
|
|
264
|
+
async pollSessionActivity(sessionId, messageIndex = null, interval = 1000) {
|
|
265
|
+
const params = messageIndex !== null ? { message_index: messageIndex } : {};
|
|
266
|
+
|
|
267
|
+
console.log(`[API] Polling session activity:`, {
|
|
268
|
+
sessionId,
|
|
269
|
+
messageIndex,
|
|
270
|
+
params,
|
|
271
|
+
url: `/activity/sessions/${sessionId}/activity`
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const response = await this.getSessionActivity(sessionId, params);
|
|
276
|
+
console.log(`[API] Poll response:`, {
|
|
277
|
+
hasActivityLog: !!response.activity_log,
|
|
278
|
+
messageIndex: response.message_index,
|
|
279
|
+
agentName: response.activity_log?.agent_name,
|
|
280
|
+
agentStatus: response.activity_log?.agent_status
|
|
281
|
+
});
|
|
282
|
+
return response;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error('[API] Activity polling error:', error);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// File Management APIs
|
|
290
|
+
async uploadFiles(files, sessionId = null) {
|
|
291
|
+
const formData = new FormData();
|
|
292
|
+
|
|
293
|
+
// Add files
|
|
294
|
+
for (const file of files) {
|
|
295
|
+
formData.append('files', file);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Add session ID if provided
|
|
299
|
+
if (sessionId) {
|
|
300
|
+
formData.append('session_id', sessionId);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return this.post('/files/upload', formData);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async listFiles(sessionId = null, limit = 50, offset = 0) {
|
|
307
|
+
const params = { limit, offset };
|
|
308
|
+
if (sessionId) {
|
|
309
|
+
params.session_id = sessionId;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return this.get('/files', { params });
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async downloadFile(fileId) {
|
|
316
|
+
const url = this.buildURL(`/files/${fileId}`);
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const response = await fetch(url);
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
throw new Error(`Failed to download file: ${response.status}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return response.blob();
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error('[API] File download error:', error);
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async deleteFile(fileId) {
|
|
332
|
+
return this.delete(`/files/${fileId}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Configuration APIs
|
|
336
|
+
async getConfigStatus() {
|
|
337
|
+
return this.get('/config/status');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// LLM Profile Management
|
|
341
|
+
async getLLMProfiles(activeOnly = true, limit = 50, offset = 0) {
|
|
342
|
+
return this.get('/config/llm-profiles', {
|
|
343
|
+
params: { active_only: activeOnly, limit, offset }
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async getLLMProfile(profileName) {
|
|
348
|
+
return this.get(`/config/llm-profiles/${encodeURIComponent(profileName)}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async createLLMProfile(profileData) {
|
|
352
|
+
return this.post('/config/llm-profiles', profileData);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async updateLLMProfile(profileName, updateData) {
|
|
356
|
+
return this.put(`/config/llm-profiles/${encodeURIComponent(profileName)}`, updateData);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async deleteLLMProfile(profileName) {
|
|
360
|
+
return this.delete(`/config/llm-profiles/${encodeURIComponent(profileName)}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async setDefaultLLMProfile(profileName) {
|
|
364
|
+
return this.post(`/config/llm-profiles/${encodeURIComponent(profileName)}/set-default`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// MCP Profile Management
|
|
368
|
+
async getMCPProfiles(activeOnly = true, limit = 50, offset = 0) {
|
|
369
|
+
return this.get('/config/mcp-profiles', {
|
|
370
|
+
params: { active_only: activeOnly, limit, offset }
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async getMCPProfile(profileName) {
|
|
375
|
+
return this.get(`/config/mcp-profiles/${encodeURIComponent(profileName)}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async createMCPProfile(profileData) {
|
|
379
|
+
return this.post('/config/mcp-profiles', profileData);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async updateMCPProfile(profileName, updateData) {
|
|
383
|
+
console.log('[API Client] updateMCPProfile called with profile:', profileName);
|
|
384
|
+
const result = await this.put(`/config/mcp-profiles/${encodeURIComponent(profileName)}`, updateData);
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async deleteMCPProfile(profileName) {
|
|
389
|
+
return this.delete(`/config/mcp-profiles/${encodeURIComponent(profileName)}`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// LLM Providers and Models
|
|
393
|
+
async getLLMProviders() {
|
|
394
|
+
return this.get('/config/llm/providers');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async getLLMProviderModels(providerName) {
|
|
398
|
+
return this.get(`/config/llm/providers/${encodeURIComponent(providerName)}/models`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Environment Variables
|
|
402
|
+
async getEnvironmentVariables() {
|
|
403
|
+
return this.get('/config/environments');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async updateEnvironmentVariables(variables) {
|
|
407
|
+
return this.put('/config/environments', { environments: variables });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Controller Configuration
|
|
411
|
+
async getControllerConfig() {
|
|
412
|
+
return this.get('/config/controller');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async updateControllerConfig(configData) {
|
|
416
|
+
return this.put('/config/controller', configData);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Utility methods
|
|
420
|
+
delay(ms) {
|
|
421
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Update base URL
|
|
425
|
+
setBaseURL(newBaseURL) {
|
|
426
|
+
this.baseURL = newBaseURL.replace(/\/$/, '');
|
|
427
|
+
console.log('[API] Base URL updated to:', this.baseURL);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Create a session ID
|
|
431
|
+
// Session ID generation using backend endpoint with fallback
|
|
432
|
+
async generateSessionId(prefix = 'vibesurf_') {
|
|
433
|
+
try {
|
|
434
|
+
// Use backend endpoint for session ID generation
|
|
435
|
+
const response = await fetch(`${this.baseURL}/generate-session-id?prefix=${encodeURIComponent(prefix)}`, {
|
|
436
|
+
method: 'GET',
|
|
437
|
+
headers: {
|
|
438
|
+
'Content-Type': 'application/json'
|
|
439
|
+
},
|
|
440
|
+
signal: AbortSignal.timeout(5000)
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
if (!response.ok) {
|
|
444
|
+
throw new Error(`HTTP ${response.status}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const data = await response.json();
|
|
448
|
+
return data.session_id;
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.warn('[API] Failed to generate session ID from backend, using fallback:', error);
|
|
451
|
+
// Fallback to simple local generation
|
|
452
|
+
const timestamp = Date.now();
|
|
453
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
454
|
+
return `${prefix}${timestamp}_${random}`;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Custom error class for API errors
|
|
460
|
+
class APIError extends Error {
|
|
461
|
+
constructor(message, status, data) {
|
|
462
|
+
super(message);
|
|
463
|
+
this.name = 'APIError';
|
|
464
|
+
this.status = status;
|
|
465
|
+
this.data = data;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Export for use in other modules
|
|
470
|
+
if (typeof window !== 'undefined') {
|
|
471
|
+
window.VibeSurfAPIClient = VibeSurfAPIClient;
|
|
472
|
+
window.APIError = APIError;
|
|
473
|
+
}
|