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.

Files changed (34) hide show
  1. vibe_surf/_version.py +2 -2
  2. vibe_surf/agents/browser_use_agent.py +1 -1
  3. vibe_surf/agents/prompts/vibe_surf_prompt.py +6 -0
  4. vibe_surf/agents/report_writer_agent.py +50 -0
  5. vibe_surf/agents/vibe_surf_agent.py +55 -0
  6. vibe_surf/backend/api/composio.py +952 -0
  7. vibe_surf/backend/database/migrations/v005_add_composio_integration.sql +33 -0
  8. vibe_surf/backend/database/migrations/v006_add_credentials_table.sql +26 -0
  9. vibe_surf/backend/database/models.py +53 -1
  10. vibe_surf/backend/database/queries.py +312 -2
  11. vibe_surf/backend/main.py +28 -0
  12. vibe_surf/backend/shared_state.py +123 -9
  13. vibe_surf/chrome_extension/scripts/api-client.js +32 -0
  14. vibe_surf/chrome_extension/scripts/settings-manager.js +954 -1
  15. vibe_surf/chrome_extension/sidepanel.html +190 -0
  16. vibe_surf/chrome_extension/styles/settings-integrations.css +927 -0
  17. vibe_surf/chrome_extension/styles/settings-modal.css +7 -3
  18. vibe_surf/chrome_extension/styles/settings-responsive.css +37 -5
  19. vibe_surf/cli.py +98 -3
  20. vibe_surf/telemetry/__init__.py +60 -0
  21. vibe_surf/telemetry/service.py +112 -0
  22. vibe_surf/telemetry/views.py +156 -0
  23. vibe_surf/tools/composio_client.py +456 -0
  24. vibe_surf/tools/mcp_client.py +21 -2
  25. vibe_surf/tools/vibesurf_tools.py +290 -87
  26. vibe_surf/tools/views.py +16 -0
  27. vibe_surf/tools/website_api/youtube/client.py +35 -13
  28. vibe_surf/utils.py +13 -0
  29. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/METADATA +11 -9
  30. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/RECORD +34 -25
  31. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/WHEEL +0 -0
  32. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/entry_points.txt +0 -0
  33. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/licenses/LICENSE +0 -0
  34. {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,15 @@ class VibeSurfSettingsManager {
9
9
  mcpProfiles: [],
10
10
  voiceProfiles: [],
11
11
  settings: {},
12
- currentProfileForm: null
12
+ currentProfileForm: null,
13
+ // Integrations state
14
+ composioApiKey: null,
15
+ composioKeyValid: false,
16
+ toolkits: [],
17
+ filteredToolkits: [],
18
+ currentToolkit: null,
19
+ searchQuery: '',
20
+ filterStatus: 'all'
13
21
  };
14
22
  this.elements = {};
15
23
  this.eventListeners = new Map();
@@ -46,6 +54,43 @@ class VibeSurfSettingsManager {
46
54
  voiceProfilesContainer: document.getElementById('voice-profiles-container'),
47
55
  addVoiceProfileBtn: document.getElementById('add-voice-profile-btn'),
48
56
 
57
+ // Integrations
58
+ integrationsContainer: document.getElementById('integrations-container'),
59
+ setupApiKeyBtn: document.getElementById('setup-api-key-btn'),
60
+ composioStatus: document.getElementById('composio-status'),
61
+ apiKeySetup: document.getElementById('api-key-setup'),
62
+ toolkitsSection: document.getElementById('toolkits-section'),
63
+ toolkitSearch: document.getElementById('toolkit-search'),
64
+ toolkitFilter: document.getElementById('toolkit-filter'),
65
+ toolkitsList: document.getElementById('toolkits-list'),
66
+ toolkitsLoading: document.getElementById('toolkits-loading'),
67
+
68
+ // Composio API Key Modal
69
+ composioApiKeyModal: document.getElementById('composio-api-key-modal'),
70
+ composioApiKeyInput: document.getElementById('composio-api-key-input'),
71
+ openComposioLink: document.getElementById('open-composio-link'),
72
+ apiKeyCancel: document.getElementById('api-key-cancel'),
73
+ apiKeyConfirm: document.getElementById('api-key-confirm'),
74
+ apiKeyValidation: document.getElementById('api-key-validation'),
75
+
76
+ // Tools Management Modal
77
+ toolsManagementModal: document.getElementById('tools-management-modal'),
78
+ toolsModalTitle: document.getElementById('tools-modal-title'),
79
+ toolkitLogo: document.getElementById('toolkit-logo-img'),
80
+ toolkitName: document.getElementById('toolkit-name'),
81
+ toolkitDescription: document.getElementById('toolkit-description'),
82
+ toolsList: document.getElementById('tools-list'),
83
+ toolsLoading: document.getElementById('tools-loading'),
84
+ selectAllTools: document.getElementById('select-all-tools'),
85
+ deselectAllTools: document.getElementById('deselect-all-tools'),
86
+ toolsCancel: document.getElementById('tools-cancel'),
87
+ toolsSave: document.getElementById('tools-save'),
88
+
89
+ // OAuth Confirmation Modal
90
+ oauthConfirmationModal: document.getElementById('oauth-confirmation-modal'),
91
+ oauthNotCompleted: document.getElementById('oauth-not-completed'),
92
+ oauthCompleted: document.getElementById('oauth-completed'),
93
+
49
94
  // Profile Form Modal
50
95
  profileFormModal: document.getElementById('profile-form-modal'),
51
96
  profileFormTitle: document.getElementById('profile-form-title'),
@@ -104,6 +149,46 @@ class VibeSurfSettingsManager {
104
149
  // Backend URL
105
150
  this.elements.backendUrl?.addEventListener('change', this.handleBackendUrlChange.bind(this));
106
151
 
152
+ // Integrations
153
+ this.elements.setupApiKeyBtn?.addEventListener('click', this.handleSetupApiKey.bind(this));
154
+ this.elements.openComposioLink?.addEventListener('click', this.handleOpenComposioLink.bind(this));
155
+ this.elements.apiKeyCancel?.addEventListener('click', this.hideApiKeyModal.bind(this));
156
+ this.elements.apiKeyConfirm?.addEventListener('click', this.handleApiKeyConfirm.bind(this));
157
+ this.elements.toolkitSearch?.addEventListener('input', this.handleToolkitSearch.bind(this));
158
+ this.elements.toolkitFilter?.addEventListener('change', this.handleToolkitFilter.bind(this));
159
+
160
+ // Tools Management Modal
161
+ this.elements.selectAllTools?.addEventListener('click', this.handleSelectAllTools.bind(this));
162
+ this.elements.deselectAllTools?.addEventListener('click', this.handleDeselectAllTools.bind(this));
163
+ this.elements.toolsCancel?.addEventListener('click', this.hideToolsModal.bind(this));
164
+ this.elements.toolsSave?.addEventListener('click', this.handleToolsSave.bind(this));
165
+
166
+ // OAuth Confirmation Modal
167
+ this.elements.oauthNotCompleted?.addEventListener('click', this.hideOAuthModal.bind(this));
168
+ this.elements.oauthCompleted?.addEventListener('click', this.handleOAuthCompleted.bind(this));
169
+
170
+ // API key toggle visibility
171
+ const apiKeyToggle = this.elements.composioApiKeyModal?.querySelector('.api-key-toggle');
172
+ if (apiKeyToggle) {
173
+ apiKeyToggle.addEventListener('click', this.handleApiKeyToggle.bind(this));
174
+ }
175
+
176
+ // Modal close buttons for integrations modals
177
+ const composioModalClose = this.elements.composioApiKeyModal?.querySelector('.modal-close');
178
+ if (composioModalClose) {
179
+ composioModalClose.addEventListener('click', this.hideApiKeyModal.bind(this));
180
+ }
181
+
182
+ const toolsModalClose = this.elements.toolsManagementModal?.querySelector('.modal-close');
183
+ if (toolsModalClose) {
184
+ toolsModalClose.addEventListener('click', this.hideToolsModal.bind(this));
185
+ }
186
+
187
+ const oauthModalClose = this.elements.oauthConfirmationModal?.querySelector('.modal-close');
188
+ if (oauthModalClose) {
189
+ oauthModalClose.addEventListener('click', this.hideOAuthModal.bind(this));
190
+ }
191
+
107
192
  // Global keyboard shortcuts
108
193
  document.addEventListener('keydown', this.handleKeydown.bind(this));
109
194
  }
@@ -224,6 +309,11 @@ class VibeSurfSettingsManager {
224
309
  if (targetTabId === 'general') {
225
310
  this.loadEnvironmentVariables();
226
311
  }
312
+
313
+ // If switching to integrations tab, load integrations data
314
+ if (targetTabId === 'integrations') {
315
+ this.loadIntegrationsData();
316
+ }
227
317
  }
228
318
 
229
319
  // Data Loading
@@ -1925,6 +2015,869 @@ class VibeSurfSettingsManager {
1925
2015
  }
1926
2016
  }, 100);
1927
2017
  }
2018
+
2019
+ // === INTEGRATIONS FUNCTIONALITY ===
2020
+
2021
+ // Load integrations data when tab is opened
2022
+ async loadIntegrationsData() {
2023
+ try {
2024
+ console.log('[SettingsManager] Loading integrations data...');
2025
+ console.log('[SettingsManager] API client available:', !!this.apiClient);
2026
+
2027
+ // Show loading state immediately
2028
+ this.showIntegrationsLoading();
2029
+
2030
+ // Check Composio status (which will handle instance restoration if needed)
2031
+ const status = await this.checkComposioStatus();
2032
+
2033
+ console.log('[SettingsManager] Status check result:', status);
2034
+
2035
+ if (status && status.connected && status.key_valid) {
2036
+ console.log('[SettingsManager] Valid Composio connection found, loading toolkits from database...');
2037
+ // Load toolkits from database (no sync by default for faster loading)
2038
+ await this.loadToolkits(false);
2039
+ console.log('[SettingsManager] Toolkits loaded, showing integrations content...');
2040
+ this.showIntegrationsContent();
2041
+ console.log('[SettingsManager] Integrations content shown');
2042
+ } else {
2043
+ console.log('[SettingsManager] No valid Composio connection, showing setup modal automatically');
2044
+ console.log('[SettingsManager] Status details - connected:', status?.connected, 'key_valid:', status?.key_valid);
2045
+ // Automatically show setup modal instead of setup UI
2046
+ this.hideIntegrationsLoading();
2047
+ this.showApiKeyModal();
2048
+ }
2049
+ } catch (error) {
2050
+ console.error('[SettingsManager] Failed to load integrations data:', error);
2051
+ this.hideIntegrationsLoading();
2052
+ this.emit('notification', {
2053
+ message: 'Failed to load integrations data',
2054
+ type: 'error'
2055
+ });
2056
+ }
2057
+ }
2058
+
2059
+ // Check Composio API key status
2060
+ async checkComposioStatus() {
2061
+ try {
2062
+ console.log('[SettingsManager] Checking Composio status...');
2063
+ const response = await this.apiClient.getComposioStatus();
2064
+ console.log('[SettingsManager] Composio status response:', response);
2065
+
2066
+ // Use the new response format
2067
+ this.state.composioKeyValid = response.connected && response.key_valid;
2068
+ this.state.composioApiKey = response.has_key ? '***' : null;
2069
+
2070
+ console.log('[SettingsManager] Composio state updated:', {
2071
+ connected: response.connected,
2072
+ keyValid: response.key_valid,
2073
+ hasKey: response.has_key,
2074
+ instanceAvailable: response.instance_available,
2075
+ message: response.message
2076
+ });
2077
+
2078
+ return response;
2079
+ } catch (error) {
2080
+ console.error('[SettingsManager] Failed to check Composio status:', error);
2081
+ this.state.composioKeyValid = false;
2082
+ this.state.composioApiKey = null;
2083
+ return {
2084
+ connected: false,
2085
+ key_valid: false,
2086
+ has_key: false,
2087
+ instance_available: false,
2088
+ message: 'Status check failed'
2089
+ };
2090
+ }
2091
+ }
2092
+
2093
+ // Show integrations loading state
2094
+ showIntegrationsLoading() {
2095
+ if (this.elements.composioStatus) {
2096
+ this.elements.composioStatus.innerHTML = `
2097
+ <div class="status-item info">
2098
+ <div class="status-icon loading-spinner">⟳</div>
2099
+ <div class="status-content">
2100
+ <div class="status-title">Checking Connection</div>
2101
+ <div class="status-description">Verifying Composio integration status...</div>
2102
+ </div>
2103
+ </div>
2104
+ `;
2105
+ }
2106
+
2107
+ if (this.elements.apiKeySetup) {
2108
+ this.elements.apiKeySetup.style.display = 'none';
2109
+ }
2110
+
2111
+ if (this.elements.toolkitsSection) {
2112
+ this.elements.toolkitsSection.style.display = 'none';
2113
+ }
2114
+ }
2115
+
2116
+ // Hide integrations loading state
2117
+ hideIntegrationsLoading() {
2118
+ // Loading state will be replaced by either connected status or setup modal
2119
+ }
2120
+
2121
+ // Show integrations main content
2122
+ showIntegrationsContent() {
2123
+ console.log('[SettingsManager] showIntegrationsContent called');
2124
+ console.log('[SettingsManager] composioStatus element exists:', !!this.elements.composioStatus);
2125
+ console.log('[SettingsManager] toolkitsSection element exists:', !!this.elements.toolkitsSection);
2126
+
2127
+ if (this.elements.composioStatus) {
2128
+ this.elements.composioStatus.innerHTML = `
2129
+ <div class="status-item success">
2130
+ <div class="status-icon">✓</div>
2131
+ <div class="status-content">
2132
+ <div class="status-title">Composio Connected</div>
2133
+ <div class="status-description">API key is valid and ready to use</div>
2134
+ </div>
2135
+ <button id="update-api-key-btn" class="btn btn-secondary btn-sm">
2136
+ Update Key
2137
+ </button>
2138
+ </div>
2139
+ `;
2140
+
2141
+ // Re-bind the update button event
2142
+ const updateBtn = document.getElementById('update-api-key-btn');
2143
+ if (updateBtn) {
2144
+ updateBtn.addEventListener('click', this.handleSetupApiKey.bind(this));
2145
+ }
2146
+ }
2147
+
2148
+ if (this.elements.apiKeySetup) {
2149
+ this.elements.apiKeySetup.style.display = 'none';
2150
+ }
2151
+
2152
+ if (this.elements.toolkitsSection) {
2153
+ this.elements.toolkitsSection.classList.remove('hidden');
2154
+ this.elements.toolkitsSection.style.display = 'block';
2155
+ console.log('[SettingsManager] toolkitsSection display set to block and hidden class removed');
2156
+ } else {
2157
+ console.error('[SettingsManager] toolkitsSection element not found!');
2158
+ }
2159
+ }
2160
+
2161
+ // Handle API key setup button click
2162
+ handleSetupApiKey() {
2163
+ console.log('[SettingsManager] Setup API key button clicked');
2164
+ this.showApiKeyModal();
2165
+ }
2166
+
2167
+ // Show API key modal
2168
+ showApiKeyModal() {
2169
+ console.log('[SettingsManager] Showing API key modal');
2170
+ console.log('[SettingsManager] Modal element exists:', !!this.elements.composioApiKeyModal);
2171
+
2172
+ if (this.elements.composioApiKeyModal) {
2173
+ this.elements.composioApiKeyModal.classList.remove('hidden');
2174
+
2175
+ // Clear previous input and validation
2176
+ if (this.elements.composioApiKeyInput) {
2177
+ this.elements.composioApiKeyInput.value = '';
2178
+ }
2179
+ this.hideApiKeyValidation();
2180
+
2181
+ // Focus on input
2182
+ setTimeout(() => {
2183
+ if (this.elements.composioApiKeyInput) {
2184
+ this.elements.composioApiKeyInput.focus();
2185
+ }
2186
+ }, 100);
2187
+
2188
+ console.log('[SettingsManager] API key modal shown successfully');
2189
+ } else {
2190
+ console.error('[SettingsManager] API key modal element not found!');
2191
+ }
2192
+ }
2193
+
2194
+ // Hide API key modal
2195
+ hideApiKeyModal() {
2196
+ if (this.elements.composioApiKeyModal) {
2197
+ this.elements.composioApiKeyModal.classList.add('hidden');
2198
+ }
2199
+ }
2200
+
2201
+ // Handle Composio link open
2202
+ handleOpenComposioLink() {
2203
+ // Open Composio website in new tab using Chrome extension API
2204
+ if (typeof chrome !== 'undefined' && chrome.tabs) {
2205
+ chrome.tabs.create({ url: 'https://composio.dev/' });
2206
+ } else {
2207
+ // Fallback for non-extension context
2208
+ window.open('https://composio.dev/', '_blank');
2209
+ }
2210
+ }
2211
+
2212
+ // Handle API key confirm
2213
+ async handleApiKeyConfirm() {
2214
+ const apiKey = this.elements.composioApiKeyInput?.value?.trim();
2215
+
2216
+ if (!apiKey) {
2217
+ this.showApiKeyValidation('Please enter an API key', 'error');
2218
+ return;
2219
+ }
2220
+
2221
+ try {
2222
+ this.showApiKeyValidation('Validating API key...', 'info');
2223
+
2224
+ const response = await this.apiClient.verifyComposioKey(apiKey);
2225
+
2226
+ if (response.valid) {
2227
+ this.showApiKeyValidation('API key is valid!', 'success');
2228
+
2229
+ // Update state
2230
+ this.state.composioKeyValid = true;
2231
+ this.state.composioApiKey = '***';
2232
+
2233
+ // Show integrations content and load toolkits
2234
+ this.showIntegrationsContent();
2235
+ await this.loadToolkits(false);
2236
+
2237
+ // Close modal after short delay
2238
+ setTimeout(() => {
2239
+ this.hideApiKeyModal();
2240
+ }, 1000);
2241
+
2242
+ this.emit('notification', {
2243
+ message: 'Composio API key validated and saved successfully',
2244
+ type: 'success'
2245
+ });
2246
+
2247
+ } else {
2248
+ this.showApiKeyValidation('Invalid API key. Please check and try again.', 'error');
2249
+ }
2250
+
2251
+ } catch (error) {
2252
+ console.error('[SettingsManager] Failed to verify API key:', error);
2253
+ this.showApiKeyValidation(`Failed to verify API key: ${error.message}`, 'error');
2254
+ }
2255
+ }
2256
+
2257
+ // Show API key validation message
2258
+ showApiKeyValidation(message, type) {
2259
+ if (!this.elements.apiKeyValidation) return;
2260
+
2261
+ const className = type === 'success' ? 'success' : type === 'error' ? 'error' : 'info';
2262
+ this.elements.apiKeyValidation.innerHTML = `
2263
+ <div class="validation-message ${className}">
2264
+ ${this.escapeHtml(message)}
2265
+ </div>
2266
+ `;
2267
+ this.elements.apiKeyValidation.style.display = 'block';
2268
+ }
2269
+
2270
+ // Hide API key validation message
2271
+ hideApiKeyValidation() {
2272
+ if (this.elements.apiKeyValidation) {
2273
+ this.elements.apiKeyValidation.style.display = 'none';
2274
+ this.elements.apiKeyValidation.innerHTML = '';
2275
+ }
2276
+ }
2277
+
2278
+ // Handle API key toggle visibility
2279
+ handleApiKeyToggle() {
2280
+ const input = this.elements.composioApiKeyInput;
2281
+ if (!input) return;
2282
+
2283
+ const toggle = this.elements.composioApiKeyModal?.querySelector('.api-key-toggle');
2284
+ if (!toggle) return;
2285
+
2286
+ const isPassword = input.type === 'password';
2287
+ input.type = isPassword ? 'text' : 'password';
2288
+
2289
+ // Update icon
2290
+ const svg = toggle.querySelector('svg');
2291
+ if (svg) {
2292
+ svg.innerHTML = isPassword ?
2293
+ '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20C7 20 2.73 16.39 1 12A18.45 18.45 0 0 1 5.06 5.06L17.94 17.94ZM9.9 4.24A9.12 9.12 0 0 1 12 4C17 4 21.27 7.61 23 12A18.5 18.5 0 0 1 19.42 16.42" stroke="currentColor" stroke-width="2" fill="none"/><path d="M1 1L23 23" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" fill="none"/>' :
2294
+ '<path d="M1 12S5 4 12 4S23 12 23 12S19 20 12 20S1 12 1 12Z" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2"/>';
2295
+ }
2296
+ }
2297
+
2298
+ // Load toolkits from API
2299
+ async loadToolkits(forceSync = false) {
2300
+ try {
2301
+ console.log('[SettingsManager] Loading Composio toolkits...');
2302
+
2303
+ if (this.elements.toolkitsLoading) {
2304
+ this.elements.toolkitsLoading.style.display = 'block';
2305
+ }
2306
+
2307
+ // Load toolkits from database first (no API sync by default)
2308
+ const params = forceSync ? { sync_with_api: true } : { sync_with_api: false };
2309
+ const response = await this.apiClient.getComposioToolkits(params);
2310
+
2311
+ // Handle different response structures
2312
+ this.state.toolkits = response.toolkits || response || [];
2313
+
2314
+ // Convert toolkit data to frontend format with connection status
2315
+ this.state.toolkits = this.state.toolkits.map(toolkit => ({
2316
+ id: toolkit.id,
2317
+ name: toolkit.name,
2318
+ slug: toolkit.slug,
2319
+ description: toolkit.description || '',
2320
+ logo: toolkit.logo || '',
2321
+ app_url: toolkit.app_url || '',
2322
+ enabled: toolkit.enabled || false,
2323
+ tools: toolkit.tools || {},
2324
+ connected: false, // Will be updated by connection status check
2325
+ connection_status: toolkit.connection_status || 'unknown'
2326
+ }));
2327
+
2328
+ // Check connection status for enabled toolkits
2329
+ await this.updateToolkitConnectionStatuses();
2330
+
2331
+ // Apply current search and filter
2332
+ this.filterToolkits();
2333
+
2334
+ console.log('[SettingsManager] Loaded toolkits:', this.state.toolkits.length);
2335
+ console.log('[SettingsManager] Filtered toolkits:', this.state.filteredToolkits.length);
2336
+
2337
+ if (forceSync && response.synced_count > 0) {
2338
+ this.emit('notification', {
2339
+ message: `Synced ${response.synced_count} new toolkits from Composio`,
2340
+ type: 'success'
2341
+ });
2342
+ }
2343
+
2344
+ } catch (error) {
2345
+ console.error('[SettingsManager] Failed to load toolkits:', error);
2346
+ this.state.toolkits = [];
2347
+ this.state.filteredToolkits = [];
2348
+ this.renderToolkits();
2349
+
2350
+ this.emit('notification', {
2351
+ message: 'Failed to load Composio toolkits',
2352
+ type: 'error'
2353
+ });
2354
+ } finally {
2355
+ if (this.elements.toolkitsLoading) {
2356
+ this.elements.toolkitsLoading.style.display = 'none';
2357
+ }
2358
+ }
2359
+ }
2360
+
2361
+ // Handle toolkit search
2362
+ handleToolkitSearch(event) {
2363
+ this.state.searchQuery = event.target.value.toLowerCase().trim();
2364
+ this.filterToolkits();
2365
+ }
2366
+
2367
+ // Handle toolkit filter
2368
+ handleToolkitFilter(event) {
2369
+ this.state.filterStatus = event.target.value;
2370
+ this.filterToolkits();
2371
+ }
2372
+
2373
+ // Filter toolkits based on search and filter
2374
+ filterToolkits() {
2375
+ let filtered = [...this.state.toolkits];
2376
+
2377
+ // Apply search filter
2378
+ if (this.state.searchQuery) {
2379
+ filtered = filtered.filter(toolkit =>
2380
+ toolkit.name.toLowerCase().includes(this.state.searchQuery) ||
2381
+ toolkit.description.toLowerCase().includes(this.state.searchQuery)
2382
+ );
2383
+ }
2384
+
2385
+ // Apply status filter
2386
+ if (this.state.filterStatus !== 'all') {
2387
+ if (this.state.filterStatus === 'enabled') {
2388
+ filtered = filtered.filter(toolkit => toolkit.enabled === true);
2389
+ } else if (this.state.filterStatus === 'disabled') {
2390
+ filtered = filtered.filter(toolkit => toolkit.enabled === false);
2391
+ } else if (this.state.filterStatus === 'connected') {
2392
+ filtered = filtered.filter(toolkit => toolkit.connected === true);
2393
+ } else if (this.state.filterStatus === 'unconnected') {
2394
+ filtered = filtered.filter(toolkit => toolkit.connected === false);
2395
+ }
2396
+ }
2397
+
2398
+ this.state.filteredToolkits = filtered;
2399
+ console.log('[SettingsManager] Filtered toolkits for rendering:', filtered.length);
2400
+ this.renderToolkits();
2401
+ }
2402
+
2403
+ // Render toolkits list
2404
+ renderToolkits() {
2405
+ console.log('[SettingsManager] renderToolkits called');
2406
+ console.log('[SettingsManager] toolkitsList element exists:', !!this.elements.toolkitsList);
2407
+
2408
+ if (!this.elements.toolkitsList) {
2409
+ console.error('[SettingsManager] toolkitsList element not found!');
2410
+ return;
2411
+ }
2412
+
2413
+ const toolkits = this.state.filteredToolkits;
2414
+ console.log('[SettingsManager] Rendering toolkits:', toolkits.length);
2415
+
2416
+ if (toolkits.length === 0) {
2417
+ const isEmpty = this.state.toolkits.length === 0;
2418
+ this.elements.toolkitsList.innerHTML = `
2419
+ <div class="empty-state">
2420
+ <div class="empty-state-icon">🔧</div>
2421
+ <div class="empty-state-title">${isEmpty ? 'No Toolkits Available' : 'No Matching Toolkits'}</div>
2422
+ <div class="empty-state-description">
2423
+ ${isEmpty ? 'No toolkits are available at the moment.' : 'Try adjusting your search or filter criteria.'}
2424
+ </div>
2425
+ </div>
2426
+ `;
2427
+ return;
2428
+ }
2429
+
2430
+ const toolkitsHTML = toolkits.map(toolkit => `
2431
+ <div class="toolkit-card" data-toolkit-slug="${toolkit.slug}">
2432
+ <div class="toolkit-header">
2433
+ <div class="toolkit-logo">
2434
+ <img src="${toolkit.logo || 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyIDJMMiA3TDEyIDEyTDIyIDdMMTIgMloiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMiAxN0wxMiAyMkwyMiAxNyIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0yIDEyTDEyIDE3TDIyIDEyIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+'}" alt="${this.escapeHtml(toolkit.name)}" class="toolkit-logo-img">
2435
+ </div>
2436
+ <div class="toolkit-info">
2437
+ <div class="toolkit-name">${this.escapeHtml(toolkit.name)}</div>
2438
+ <div class="toolkit-description">${this.escapeHtml(toolkit.description)}</div>
2439
+ </div>
2440
+ <div class="toolkit-actions">
2441
+ <label class="toolkit-toggle">
2442
+ <input type="checkbox" ${toolkit.enabled ? 'checked' : ''}
2443
+ data-toolkit-slug="${toolkit.slug}"
2444
+ class="toolkit-toggle-input">
2445
+ <span class="toggle-slider"></span>
2446
+ </label>
2447
+ </div>
2448
+ </div>
2449
+ <div class="toolkit-footer">
2450
+ <button class="btn btn-secondary btn-sm toolkit-tools-btn"
2451
+ data-toolkit-slug="${toolkit.slug}"
2452
+ ${!toolkit.connected ? 'disabled' : ''}>
2453
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
2454
+ <path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2"/>
2455
+ <path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2"/>
2456
+ <path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2"/>
2457
+ </svg>
2458
+ Manage Tools
2459
+ </button>
2460
+ <div class="toolkit-status ${toolkit.connected ? 'connected' : 'disconnected'}">
2461
+ ${toolkit.connected ? 'Connected' : 'Not Connected'}
2462
+ </div>
2463
+ </div>
2464
+ </div>
2465
+ `).join('');
2466
+
2467
+ console.log('[SettingsManager] Setting toolkits HTML, length:', toolkitsHTML.length);
2468
+ this.elements.toolkitsList.innerHTML = toolkitsHTML;
2469
+ console.log('[SettingsManager] HTML set successfully');
2470
+
2471
+ // Bind event listeners for toolkit interactions
2472
+ this.bindToolkitEvents();
2473
+ console.log('[SettingsManager] Event binding completed');
2474
+ }
2475
+
2476
+ // Bind toolkit event listeners
2477
+ bindToolkitEvents() {
2478
+ if (!this.elements.toolkitsList) return;
2479
+
2480
+ // Bind toggle inputs
2481
+ const toggleInputs = this.elements.toolkitsList.querySelectorAll('.toolkit-toggle-input');
2482
+ toggleInputs.forEach(input => {
2483
+ input.addEventListener('change', this.handleToolkitToggle.bind(this));
2484
+ });
2485
+
2486
+ // Bind manage tools buttons
2487
+ const toolsButtons = this.elements.toolkitsList.querySelectorAll('.toolkit-tools-btn');
2488
+ toolsButtons.forEach(button => {
2489
+ button.addEventListener('click', (e) => {
2490
+ const toolkitSlug = e.target.dataset.toolkitSlug;
2491
+ if (toolkitSlug && !e.target.disabled) {
2492
+ this.handleManageTools(toolkitSlug);
2493
+ }
2494
+ });
2495
+ });
2496
+
2497
+ // Bind image error handlers to replace CSP-blocked onerror attributes
2498
+ const logoImages = this.elements.toolkitsList.querySelectorAll('.toolkit-logo-img');
2499
+ logoImages.forEach(img => {
2500
+ img.addEventListener('error', () => {
2501
+ img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyIDJMMiA3TDEyIDEyTDIyIDdMMTIgMloiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMiAxN0wxMiAyMkwyMiAxNyIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0yIDEyTDEyIDE3TDIyIDEyIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+';
2502
+ });
2503
+ });
2504
+ }
2505
+
2506
+ // Handle toolkit toggle
2507
+ async handleToolkitToggle(event) {
2508
+ const checkbox = event.target;
2509
+ const toolkitSlug = checkbox.dataset.toolkitSlug;
2510
+ const isEnabled = checkbox.checked;
2511
+
2512
+ try {
2513
+ console.log(`[SettingsManager] Toggling toolkit ${toolkitSlug}: ${isEnabled}`);
2514
+
2515
+ const response = await this.apiClient.toggleComposioToolkit(toolkitSlug, isEnabled);
2516
+
2517
+ if (response.requires_oauth && !response.connected) {
2518
+ // OAuth flow required
2519
+ console.log(`[SettingsManager] OAuth required for ${toolkitSlug}, URL:`, response.oauth_url);
2520
+ await this.handleOAuthFlow(toolkitSlug, response.oauth_url);
2521
+
2522
+ // Revert toggle until OAuth is complete
2523
+ checkbox.checked = !isEnabled;
2524
+ return;
2525
+ }
2526
+
2527
+ // Update toolkit state
2528
+ const toolkit = this.state.toolkits.find(t => t.slug === toolkitSlug);
2529
+ if (toolkit) {
2530
+ toolkit.enabled = response.enabled;
2531
+ toolkit.connected = response.connected;
2532
+ }
2533
+
2534
+ // Re-filter and render
2535
+ this.filterToolkits();
2536
+
2537
+ this.emit('notification', {
2538
+ message: `Toolkit ${toolkit?.name || toolkitSlug} ${isEnabled ? 'enabled' : 'disabled'}`,
2539
+ type: 'success'
2540
+ });
2541
+
2542
+ } catch (error) {
2543
+ console.error('[SettingsManager] Failed to toggle toolkit:', error);
2544
+
2545
+ // Revert checkbox state
2546
+ checkbox.checked = !isEnabled;
2547
+
2548
+ this.emit('notification', {
2549
+ message: `Failed to ${isEnabled ? 'enable' : 'disable'} toolkit: ${error.message}`,
2550
+ type: 'error'
2551
+ });
2552
+ }
2553
+ }
2554
+
2555
+ // Handle OAuth flow
2556
+ async handleOAuthFlow(toolkitSlug, oauthUrl) {
2557
+ try {
2558
+ console.log(`[SettingsManager] Starting OAuth flow for ${toolkitSlug}`);
2559
+
2560
+ // Open OAuth URL in new tab using Chrome extension API
2561
+ if (typeof chrome !== 'undefined' && chrome.tabs) {
2562
+ chrome.tabs.create({ url: oauthUrl });
2563
+ } else {
2564
+ // Fallback for non-extension context
2565
+ window.open(oauthUrl, '_blank');
2566
+ }
2567
+
2568
+ // Show OAuth confirmation modal
2569
+ this.showOAuthModal(toolkitSlug);
2570
+
2571
+ } catch (error) {
2572
+ console.error('[SettingsManager] Failed to handle OAuth flow:', error);
2573
+ this.emit('notification', {
2574
+ message: 'Failed to start OAuth flow',
2575
+ type: 'error'
2576
+ });
2577
+ }
2578
+ }
2579
+
2580
+ // Show OAuth confirmation modal
2581
+ showOAuthModal(toolkitSlug) {
2582
+ this.state.currentToolkit = toolkitSlug;
2583
+
2584
+ if (this.elements.oauthConfirmationModal) {
2585
+ this.elements.oauthConfirmationModal.classList.remove('hidden');
2586
+ }
2587
+ }
2588
+
2589
+ // Hide OAuth confirmation modal
2590
+ hideOAuthModal() {
2591
+ if (this.elements.oauthConfirmationModal) {
2592
+ this.elements.oauthConfirmationModal.classList.add('hidden');
2593
+ }
2594
+ this.state.currentToolkit = null;
2595
+ }
2596
+
2597
+ // Handle OAuth completed
2598
+ async handleOAuthCompleted() {
2599
+ if (!this.state.currentToolkit) {
2600
+ this.hideOAuthModal();
2601
+ return;
2602
+ }
2603
+
2604
+ try {
2605
+ console.log(`[SettingsManager] Checking OAuth completion for ${this.state.currentToolkit}`);
2606
+
2607
+ // Check connection status for the specific toolkit
2608
+ const statusResponse = await this.apiClient.getComposioToolkitConnectionStatus(this.state.currentToolkit);
2609
+
2610
+ if (statusResponse.connected) {
2611
+ // Update state
2612
+ const stateToolkit = this.state.toolkits.find(t => t.slug === this.state.currentToolkit);
2613
+ if (stateToolkit) {
2614
+ stateToolkit.connected = true;
2615
+ stateToolkit.enabled = true;
2616
+ }
2617
+
2618
+ // Re-load toolkits to get latest state
2619
+ await this.loadToolkits();
2620
+
2621
+ this.emit('notification', {
2622
+ message: `${stateToolkit?.name || this.state.currentToolkit} connected successfully!`,
2623
+ type: 'success'
2624
+ });
2625
+ } else {
2626
+ this.emit('notification', {
2627
+ message: 'OAuth connection not detected. Please try again.',
2628
+ type: 'warning'
2629
+ });
2630
+ }
2631
+
2632
+ } catch (error) {
2633
+ console.error('[SettingsManager] Failed to check OAuth completion:', error);
2634
+ this.emit('notification', {
2635
+ message: 'Failed to verify OAuth connection',
2636
+ type: 'error'
2637
+ });
2638
+ } finally {
2639
+ this.hideOAuthModal();
2640
+ }
2641
+ }
2642
+
2643
+ // Handle manage tools
2644
+ async handleManageTools(toolkitSlug) {
2645
+ this.state.currentToolkit = toolkitSlug;
2646
+
2647
+ try {
2648
+ const toolkit = this.state.toolkits.find(t => t.slug === toolkitSlug);
2649
+ if (!toolkit) {
2650
+ throw new Error('Toolkit not found');
2651
+ }
2652
+
2653
+ this.showToolsModal(toolkit);
2654
+ await this.loadToolkitTools(toolkitSlug);
2655
+
2656
+ } catch (error) {
2657
+ console.error('[SettingsManager] Failed to manage tools:', error);
2658
+ this.emit('notification', {
2659
+ message: 'Failed to load toolkit tools',
2660
+ type: 'error'
2661
+ });
2662
+ }
2663
+ }
2664
+
2665
+ // Show tools management modal
2666
+ showToolsModal(toolkit) {
2667
+ if (!this.elements.toolsManagementModal) return;
2668
+
2669
+ // Update modal title and info
2670
+ if (this.elements.toolsModalTitle) {
2671
+ this.elements.toolsModalTitle.textContent = `Manage ${toolkit.name} Tools`;
2672
+ }
2673
+
2674
+ if (this.elements.toolkitLogo) {
2675
+ this.elements.toolkitLogo.src = toolkit.logo || '/default-toolkit-logo.svg';
2676
+ this.elements.toolkitLogo.alt = toolkit.name;
2677
+ }
2678
+
2679
+ if (this.elements.toolkitName) {
2680
+ this.elements.toolkitName.textContent = toolkit.name;
2681
+ }
2682
+
2683
+ // Show modal
2684
+ this.elements.toolsManagementModal.classList.remove('hidden');
2685
+ }
2686
+
2687
+ // Hide tools management modal
2688
+ hideToolsModal() {
2689
+ if (this.elements.toolsManagementModal) {
2690
+ this.elements.toolsManagementModal.classList.add('hidden');
2691
+ }
2692
+ this.state.currentToolkit = null;
2693
+ }
2694
+
2695
+ // Load toolkit tools
2696
+ async loadToolkitTools(toolkitSlug) {
2697
+ if (!this.elements.toolsList || !this.elements.toolsLoading) return;
2698
+
2699
+ try {
2700
+ this.elements.toolsLoading.style.display = 'block';
2701
+ this.elements.toolsList.innerHTML = '';
2702
+
2703
+ const response = await this.apiClient.getComposioToolkitTools(toolkitSlug);
2704
+ const tools = response.tools || response || [];
2705
+
2706
+ this.renderTools(tools);
2707
+
2708
+ } catch (error) {
2709
+ console.error('[SettingsManager] Failed to load toolkit tools:', error);
2710
+ this.elements.toolsList.innerHTML = `
2711
+ <div class="empty-state">
2712
+ <div class="empty-state-icon">⚠</div>
2713
+ <div class="empty-state-title">Failed to Load Tools</div>
2714
+ <div class="empty-state-description">Unable to fetch tools for this toolkit.</div>
2715
+ </div>
2716
+ `;
2717
+ } finally {
2718
+ this.elements.toolsLoading.style.display = 'none';
2719
+ }
2720
+ }
2721
+
2722
+ // Render tools list
2723
+ renderTools(tools) {
2724
+ if (!this.elements.toolsList) return;
2725
+
2726
+ if (tools.length === 0) {
2727
+ this.elements.toolsList.innerHTML = `
2728
+ <div class="empty-state">
2729
+ <div class="empty-state-icon">🔧</div>
2730
+ <div class="empty-state-title">No Tools Available</div>
2731
+ <div class="empty-state-description">This toolkit has no tools available.</div>
2732
+ </div>
2733
+ `;
2734
+ return;
2735
+ }
2736
+
2737
+ const toolsHTML = `
2738
+ <div class="tools-table">
2739
+ <div class="tools-header">
2740
+ <div class="tool-cell tool-checkbox">
2741
+ <input type="checkbox" id="select-all-tools-checkbox">
2742
+ </div>
2743
+ <div class="tool-cell tool-name">Tool Name</div>
2744
+ <div class="tool-cell tool-description">Description</div>
2745
+ </div>
2746
+ <div class="tools-body">
2747
+ ${tools.map(tool => `
2748
+ <div class="tool-row">
2749
+ <div class="tool-cell tool-checkbox">
2750
+ <input type="checkbox" class="tool-checkbox-input"
2751
+ data-tool-name="${tool.name}"
2752
+ ${tool.enabled ? 'checked' : ''}>
2753
+ </div>
2754
+ <div class="tool-cell tool-name">${this.escapeHtml(tool.name)}</div>
2755
+ <div class="tool-cell tool-description">${this.escapeHtml(tool.description || 'No description available')}</div>
2756
+ </div>
2757
+ `).join('')}
2758
+ </div>
2759
+ </div>
2760
+ `;
2761
+
2762
+ this.elements.toolsList.innerHTML = toolsHTML;
2763
+
2764
+ // Setup select all functionality
2765
+ const selectAllCheckbox = this.elements.toolsList.querySelector('#select-all-tools-checkbox');
2766
+ if (selectAllCheckbox) {
2767
+ selectAllCheckbox.addEventListener('change', (e) => {
2768
+ const toolCheckboxes = this.elements.toolsList.querySelectorAll('.tool-checkbox-input');
2769
+ toolCheckboxes.forEach(checkbox => {
2770
+ checkbox.checked = e.target.checked;
2771
+ });
2772
+ });
2773
+ }
2774
+ }
2775
+
2776
+ // Handle select all tools
2777
+ handleSelectAllTools() {
2778
+ const toolCheckboxes = this.elements.toolsList?.querySelectorAll('.tool-checkbox-input');
2779
+ if (toolCheckboxes) {
2780
+ toolCheckboxes.forEach(checkbox => {
2781
+ checkbox.checked = true;
2782
+ });
2783
+ }
2784
+ }
2785
+
2786
+ // Handle deselect all tools
2787
+ handleDeselectAllTools() {
2788
+ const toolCheckboxes = this.elements.toolsList?.querySelectorAll('.tool-checkbox-input');
2789
+ if (toolCheckboxes) {
2790
+ toolCheckboxes.forEach(checkbox => {
2791
+ checkbox.checked = false;
2792
+ });
2793
+ }
2794
+ }
2795
+
2796
+ // Handle tools save
2797
+ async handleToolsSave() {
2798
+ if (!this.state.currentToolkit) {
2799
+ console.error('[SettingsManager] No current toolkit selected for save');
2800
+ return;
2801
+ }
2802
+
2803
+ try {
2804
+ console.log(`[SettingsManager] Saving tools for ${this.state.currentToolkit}`);
2805
+
2806
+ // Get all tools with their selection status as dictionary
2807
+ const toolCheckboxes = this.elements.toolsList?.querySelectorAll('.tool-checkbox-input');
2808
+ const selectedTools = {};
2809
+
2810
+ if (toolCheckboxes) {
2811
+ console.log(`[SettingsManager] Found ${toolCheckboxes.length} tool checkboxes`);
2812
+ toolCheckboxes.forEach(checkbox => {
2813
+ const toolName = checkbox.dataset.toolName;
2814
+ const isSelected = checkbox.checked;
2815
+ selectedTools[toolName] = isSelected;
2816
+ console.log(`[SettingsManager] Tool ${toolName}: ${isSelected ? 'selected' : 'not selected'}`);
2817
+ });
2818
+ } else {
2819
+ console.warn('[SettingsManager] No tool checkboxes found');
2820
+ }
2821
+
2822
+ console.log(`[SettingsManager] Selected tools:`, selectedTools);
2823
+
2824
+ // Save tools selection
2825
+ const response = await this.apiClient.updateComposioToolkitTools(this.state.currentToolkit, selectedTools);
2826
+ console.log(`[SettingsManager] Save response:`, response);
2827
+
2828
+ this.emit('notification', {
2829
+ message: 'Tools configuration saved successfully',
2830
+ type: 'success'
2831
+ });
2832
+
2833
+ // Close modal
2834
+ this.hideToolsModal();
2835
+
2836
+ } catch (error) {
2837
+ console.error('[SettingsManager] Failed to save tools:', error);
2838
+ this.emit('notification', {
2839
+ message: `Failed to save tools configuration: ${error.message}`,
2840
+ type: 'error'
2841
+ });
2842
+ }
2843
+ }
2844
+
2845
+ // Update connection status for all enabled toolkits
2846
+ async updateToolkitConnectionStatuses() {
2847
+ try {
2848
+ const enabledToolkits = this.state.toolkits.filter(toolkit => toolkit.enabled);
2849
+
2850
+ if (enabledToolkits.length === 0) {
2851
+ return;
2852
+ }
2853
+
2854
+ // Check connection status for each enabled toolkit
2855
+ const connectionPromises = enabledToolkits.map(async (toolkit) => {
2856
+ try {
2857
+ const statusResponse = await this.apiClient.getComposioToolkitConnectionStatus(toolkit.slug);
2858
+
2859
+ // Update toolkit connection status
2860
+ const toolkitIndex = this.state.toolkits.findIndex(t => t.slug === toolkit.slug);
2861
+ if (toolkitIndex !== -1) {
2862
+ this.state.toolkits[toolkitIndex].connected = statusResponse.connected;
2863
+ this.state.toolkits[toolkitIndex].connection_status = statusResponse.status;
2864
+ }
2865
+
2866
+ return { slug: toolkit.slug, connected: statusResponse.connected, status: statusResponse.status };
2867
+ } catch (error) {
2868
+ console.error(`[SettingsManager] Failed to check connection status for ${toolkit.slug}:`, error);
2869
+ // Keep as disconnected on error
2870
+ return { slug: toolkit.slug, connected: false, status: 'error' };
2871
+ }
2872
+ });
2873
+
2874
+ const results = await Promise.all(connectionPromises);
2875
+ console.log('[SettingsManager] Connection status check results:', results);
2876
+
2877
+ } catch (error) {
2878
+ console.error('[SettingsManager] Failed to update toolkit connection statuses:', error);
2879
+ }
2880
+ }
1928
2881
  }
1929
2882
 
1930
2883
  // Export for use in other modules