vibesurf 0.1.32__py3-none-any.whl → 0.1.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vibesurf might be problematic. Click here for more details.
- vibe_surf/_version.py +2 -2
- vibe_surf/agents/browser_use_agent.py +1 -1
- vibe_surf/agents/prompts/vibe_surf_prompt.py +6 -0
- vibe_surf/agents/report_writer_agent.py +50 -0
- vibe_surf/agents/vibe_surf_agent.py +55 -0
- vibe_surf/backend/api/composio.py +952 -0
- vibe_surf/backend/database/migrations/v005_add_composio_integration.sql +33 -0
- vibe_surf/backend/database/migrations/v006_add_credentials_table.sql +26 -0
- vibe_surf/backend/database/models.py +53 -1
- vibe_surf/backend/database/queries.py +312 -2
- vibe_surf/backend/main.py +28 -0
- vibe_surf/backend/shared_state.py +123 -9
- vibe_surf/chrome_extension/scripts/api-client.js +32 -0
- vibe_surf/chrome_extension/scripts/settings-manager.js +954 -1
- vibe_surf/chrome_extension/sidepanel.html +190 -0
- vibe_surf/chrome_extension/styles/settings-integrations.css +927 -0
- vibe_surf/chrome_extension/styles/settings-modal.css +7 -3
- vibe_surf/chrome_extension/styles/settings-responsive.css +37 -5
- vibe_surf/cli.py +98 -3
- vibe_surf/telemetry/__init__.py +60 -0
- vibe_surf/telemetry/service.py +112 -0
- vibe_surf/telemetry/views.py +156 -0
- vibe_surf/tools/composio_client.py +456 -0
- vibe_surf/tools/mcp_client.py +21 -2
- vibe_surf/tools/vibesurf_tools.py +290 -87
- vibe_surf/tools/views.py +16 -0
- vibe_surf/tools/website_api/youtube/client.py +35 -13
- vibe_surf/utils.py +13 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/METADATA +11 -9
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/RECORD +34 -25
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.32.dist-info → vibesurf-0.1.34.dist-info}/top_level.txt +0 -0
|
@@ -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
|