@friggframework/devtools 2.0.0--canary.398.7664c46.0 → 2.0.0--canary.400.bed3308.0

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.
Files changed (167) hide show
  1. package/frigg-cli/.eslintrc.js +141 -0
  2. package/frigg-cli/__tests__/jest.config.js +102 -0
  3. package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
  5. package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
  6. package/frigg-cli/__tests__/utils/command-tester.js +170 -0
  7. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  8. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  9. package/frigg-cli/__tests__/utils/test-setup.js +286 -0
  10. package/frigg-cli/build-command/index.js +15 -2
  11. package/frigg-cli/deploy-command/index.js +15 -2
  12. package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
  13. package/frigg-cli/generate-command/azure-generator.js +43 -0
  14. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  15. package/frigg-cli/generate-command/index.js +350 -0
  16. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  17. package/frigg-cli/index.js +66 -4
  18. package/frigg-cli/install-command/index.js +15 -2
  19. package/frigg-cli/package.json +75 -0
  20. package/frigg-cli/start-command/index.js +17 -2
  21. package/frigg-cli/ui-command/index.js +167 -0
  22. package/frigg-cli/utils/app-resolver.js +319 -0
  23. package/frigg-cli/utils/backend-path.js +38 -0
  24. package/frigg-cli/utils/process-manager.js +199 -0
  25. package/frigg-cli/utils/repo-detection.js +405 -0
  26. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +43 -19
  27. package/infrastructure/IAM-POLICY-TEMPLATES.md +1 -1
  28. package/infrastructure/frigg-deployment-iam-stack.yaml +16 -2
  29. package/infrastructure/iam-generator.js +129 -6
  30. package/infrastructure/iam-policy-basic.json +29 -5
  31. package/infrastructure/iam-policy-full.json +28 -5
  32. package/infrastructure/serverless-template.js +209 -3
  33. package/infrastructure/serverless-template.test.js +12 -0
  34. package/management-ui/.eslintrc.js +22 -0
  35. package/management-ui/README.md +203 -0
  36. package/management-ui/components.json +21 -0
  37. package/management-ui/{dist/index.html → index.html} +1 -2
  38. package/management-ui/merge-conflict-cleaner.py +371 -0
  39. package/management-ui/package-lock.json +10997 -0
  40. package/management-ui/package.json +76 -0
  41. package/management-ui/postcss.config.js +6 -0
  42. package/management-ui/server/api/backend.js +256 -0
  43. package/management-ui/server/api/cli.js +315 -0
  44. package/management-ui/server/api/codegen.js +663 -0
  45. package/management-ui/server/api/connections.js +857 -0
  46. package/management-ui/server/api/discovery.js +185 -0
  47. package/management-ui/server/api/environment/index.js +1 -0
  48. package/management-ui/server/api/environment/router.js +378 -0
  49. package/management-ui/server/api/environment.js +328 -0
  50. package/management-ui/server/api/integrations.js +479 -0
  51. package/management-ui/server/api/logs.js +248 -0
  52. package/management-ui/server/api/monitoring.js +282 -0
  53. package/management-ui/server/api/open-ide.js +31 -0
  54. package/management-ui/server/api/project.js +553 -0
  55. package/management-ui/server/api/users/sessions.js +371 -0
  56. package/management-ui/server/api/users/simulation.js +254 -0
  57. package/management-ui/server/api/users.js +362 -0
  58. package/management-ui/server/api-contract.md +275 -0
  59. package/management-ui/server/index.js +428 -0
  60. package/management-ui/server/middleware/errorHandler.js +70 -0
  61. package/management-ui/server/middleware/security.js +32 -0
  62. package/management-ui/server/processManager.js +296 -0
  63. package/management-ui/server/server.js +188 -0
  64. package/management-ui/server/services/aws-monitor.js +413 -0
  65. package/management-ui/server/services/npm-registry.js +347 -0
  66. package/management-ui/server/services/template-engine.js +538 -0
  67. package/management-ui/server/utils/cliIntegration.js +220 -0
  68. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  69. package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
  70. package/management-ui/server/utils/environment/encryption.js +278 -0
  71. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  72. package/management-ui/server/utils/import-commonjs.js +28 -0
  73. package/management-ui/server/utils/response.js +83 -0
  74. package/management-ui/server/websocket/handler.js +325 -0
  75. package/management-ui/src/App.jsx +51 -0
  76. package/management-ui/src/components/AppRouter.jsx +65 -0
  77. package/management-ui/src/components/Button.jsx +2 -0
  78. package/management-ui/src/components/Card.jsx +9 -0
  79. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  80. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  81. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  82. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  83. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  84. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  85. package/management-ui/src/components/IntegrationCard.jsx +199 -0
  86. package/management-ui/src/components/IntegrationCardEnhanced.jsx +490 -0
  87. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  88. package/management-ui/src/components/IntegrationStatus.jsx +235 -0
  89. package/management-ui/src/components/Layout.jsx +250 -0
  90. package/management-ui/src/components/LoadingSpinner.jsx +45 -0
  91. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  92. package/management-ui/src/components/SessionMonitor.jsx +255 -0
  93. package/management-ui/src/components/StatusBadge.jsx +70 -0
  94. package/management-ui/src/components/UserContextSwitcher.jsx +154 -0
  95. package/management-ui/src/components/UserSimulation.jsx +299 -0
  96. package/management-ui/src/components/Welcome.jsx +434 -0
  97. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  98. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  99. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  100. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  101. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  102. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  103. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  104. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  105. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  106. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  107. package/management-ui/src/components/codegen/index.js +10 -0
  108. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  109. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  110. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  111. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  112. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  113. package/management-ui/src/components/connections/index.js +5 -0
  114. package/management-ui/src/components/index.js +21 -0
  115. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  116. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  117. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  118. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  119. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  120. package/management-ui/src/components/monitoring/index.js +6 -0
  121. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  122. package/management-ui/src/components/theme-provider.jsx +52 -0
  123. package/management-ui/src/components/theme-toggle.jsx +39 -0
  124. package/management-ui/src/components/ui/badge.tsx +36 -0
  125. package/management-ui/src/components/ui/button.test.jsx +56 -0
  126. package/management-ui/src/components/ui/button.tsx +57 -0
  127. package/management-ui/src/components/ui/card.tsx +76 -0
  128. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  129. package/management-ui/src/components/ui/select.tsx +157 -0
  130. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  131. package/management-ui/src/hooks/useFrigg.jsx +387 -0
  132. package/management-ui/src/hooks/useSocket.jsx +58 -0
  133. package/management-ui/src/index.css +194 -0
  134. package/management-ui/src/lib/utils.ts +6 -0
  135. package/management-ui/src/main.jsx +10 -0
  136. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  137. package/management-ui/src/pages/Connections.jsx +252 -0
  138. package/management-ui/src/pages/ConnectionsEnhanced.jsx +427 -0
  139. package/management-ui/src/pages/Dashboard.jsx +311 -0
  140. package/management-ui/src/pages/Environment.jsx +314 -0
  141. package/management-ui/src/pages/IntegrationConfigure.jsx +544 -0
  142. package/management-ui/src/pages/IntegrationDiscovery.jsx +479 -0
  143. package/management-ui/src/pages/IntegrationTest.jsx +494 -0
  144. package/management-ui/src/pages/Integrations.jsx +254 -0
  145. package/management-ui/src/pages/Monitoring.jsx +17 -0
  146. package/management-ui/src/pages/Simulation.jsx +155 -0
  147. package/management-ui/src/pages/Users.jsx +492 -0
  148. package/management-ui/src/services/api.js +41 -0
  149. package/management-ui/src/services/apiModuleService.js +193 -0
  150. package/management-ui/src/services/websocket-handlers.js +120 -0
  151. package/management-ui/src/test/api/project.test.js +273 -0
  152. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  153. package/management-ui/src/test/mocks/server.js +178 -0
  154. package/management-ui/src/test/setup.js +61 -0
  155. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  156. package/management-ui/src/utils/repository.js +98 -0
  157. package/management-ui/src/utils/repository.test.js +118 -0
  158. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  159. package/management-ui/tailwind.config.js +63 -0
  160. package/management-ui/tsconfig.json +37 -0
  161. package/management-ui/tsconfig.node.json +10 -0
  162. package/management-ui/vite.config.js +26 -0
  163. package/management-ui/vitest.config.js +38 -0
  164. package/package.json +5 -5
  165. package/management-ui/dist/assets/index-CbM64Oba.js +0 -1221
  166. package/management-ui/dist/assets/index-CkvseXTC.css +0 -1
  167. /package/management-ui/{dist/assets/FriggLogo-B7Xx8ZW1.svg → src/assets/FriggLogo.svg} +0 -0
@@ -0,0 +1,479 @@
1
+ import express from 'express'
2
+ import { exec } from 'child_process'
3
+ import { promisify } from 'util'
4
+ import path from 'path'
5
+ import fs from 'fs-extra'
6
+ import fetch from 'node-fetch'
7
+ import { createStandardResponse, createErrorResponse, ERROR_CODES, asyncHandler } from '../utils/response.js'
8
+ import { importCommonJS } from '../utils/import-commonjs.js'
9
+ import { wsHandler } from '../websocket/handler.js'
10
+
11
+ const router = express.Router();
12
+ const execAsync = promisify(exec);
13
+
14
+ async function getAvailableIntegrations() {
15
+ try {
16
+ // Search NPM registry for @friggframework/api-module-* packages
17
+ const searchUrl = 'https://registry.npmjs.org/-/v1/search?text=@friggframework%20api-module&size=100';
18
+
19
+ const response = await fetch(searchUrl);
20
+ if (!response.ok) {
21
+ throw new Error(`NPM search failed: ${response.statusText}`);
22
+ }
23
+
24
+ const data = await response.json();
25
+
26
+ // Filter and format integration packages
27
+ const integrations = data.objects
28
+ .filter(pkg => pkg.package.name.includes('@friggframework/api-module-'))
29
+ .map(pkg => ({
30
+ name: pkg.package.name,
31
+ version: pkg.package.version,
32
+ description: pkg.package.description || 'No description available',
33
+ category: detectCategory(pkg.package.name, pkg.package.description || '', pkg.package.keywords || []),
34
+ installed: false,
35
+ tags: pkg.package.keywords || [],
36
+ npmUrl: `https://www.npmjs.com/package/${pkg.package.name}`
37
+ }));
38
+
39
+ console.log(`Found ${integrations.length} available integrations from NPM`);
40
+ return integrations;
41
+ } catch (error) {
42
+ console.error('Error fetching integrations from NPM:', error);
43
+ // Fallback to basic list if NPM search fails
44
+ return [
45
+ {
46
+ name: '@friggframework/api-module-hubspot',
47
+ version: 'latest',
48
+ description: 'HubSpot CRM integration for Frigg',
49
+ category: 'CRM',
50
+ installed: false
51
+ }
52
+ ];
53
+ }
54
+ }
55
+
56
+ // Helper to detect integration category
57
+ function detectCategory(name, description, keywords) {
58
+ const text = `${name} ${description} ${keywords.join(' ')}`.toLowerCase();
59
+
60
+ const categoryPatterns = {
61
+ 'CRM': ['crm', 'customer', 'salesforce', 'hubspot', 'pipedrive'],
62
+ 'Communication': ['email', 'sms', 'chat', 'slack', 'discord', 'teams'],
63
+ 'E-commerce': ['ecommerce', 'shop', 'store', 'payment', 'stripe', 'paypal'],
64
+ 'Marketing': ['marketing', 'campaign', 'mailchimp', 'activecampaign'],
65
+ 'Productivity': ['task', 'project', 'asana', 'trello', 'notion', 'jira'],
66
+ 'Analytics': ['analytics', 'tracking', 'google', 'mixpanel', 'segment'],
67
+ 'Support': ['support', 'helpdesk', 'ticket', 'zendesk', 'intercom'],
68
+ 'Finance': ['accounting', 'invoice', 'quickbooks', 'xero', 'billing'],
69
+ 'Developer Tools': ['github', 'gitlab', 'bitbucket', 'api', 'webhook'],
70
+ 'Social Media': ['social', 'facebook', 'twitter', 'instagram', 'linkedin']
71
+ };
72
+
73
+ for (const [category, patterns] of Object.entries(categoryPatterns)) {
74
+ for (const pattern of patterns) {
75
+ if (text.includes(pattern)) {
76
+ return category;
77
+ }
78
+ }
79
+ }
80
+
81
+ return 'Other';
82
+ }
83
+
84
+ // Helper to get actual integrations from backend.js appDefinition
85
+ async function getInstalledIntegrations() {
86
+ try {
87
+ // Try multiple possible backend locations
88
+ const possiblePaths = [
89
+ path.join(process.cwd(), '../../../backend'),
90
+ path.join(process.cwd(), '../../backend'),
91
+ path.join(process.cwd(), '../backend'),
92
+ path.join(process.cwd(), 'backend'),
93
+ // Also check template backend
94
+ path.join(process.cwd(), '../frigg-cli/templates/backend')
95
+ ];
96
+
97
+ for (const backendPath of possiblePaths) {
98
+ const backendJsPath = path.join(backendPath, 'backend.js');
99
+ const indexJsPath = path.join(backendPath, 'index.js');
100
+
101
+ // Try both backend.js and index.js
102
+ const targetFile = await fs.pathExists(backendJsPath) ? backendJsPath :
103
+ await fs.pathExists(indexJsPath) ? indexJsPath : null;
104
+
105
+ if (targetFile) {
106
+ console.log(`Found backend file at: ${targetFile}`);
107
+
108
+ try {
109
+ // Dynamically import the backend file to get the actual appDefinition
110
+ // Use importCommonJS helper to handle both ESM and CommonJS modules
111
+ const backendModule = await importCommonJS(targetFile);
112
+
113
+ // Extract appDefinition - could be default export, named export, or variable
114
+ const appDefinition = backendModule.default?.appDefinition ||
115
+ backendModule.appDefinition ||
116
+ backendModule.default ||
117
+ backendModule;
118
+
119
+ if (appDefinition && appDefinition.integrations && Array.isArray(appDefinition.integrations)) {
120
+ console.log(`Found ${appDefinition.integrations.length} integrations in appDefinition`);
121
+
122
+ const integrations = appDefinition.integrations.map((IntegrationClass, index) => {
123
+ try {
124
+ // Get integration metadata from static properties
125
+ const config = IntegrationClass.Config || {};
126
+ const options = IntegrationClass.Options || {};
127
+ const modules = IntegrationClass.modules || {};
128
+ const display = options.display || {};
129
+
130
+ // Extract service name from class name
131
+ const className = IntegrationClass.name || `Integration${index}`;
132
+ const serviceName = className.replace(/Integration$/, '');
133
+
134
+ return {
135
+ name: config.name || serviceName.toLowerCase(),
136
+ displayName: display.name || serviceName,
137
+ description: display.description || `${serviceName} integration`,
138
+ category: display.category || detectCategory(serviceName.toLowerCase(), display.description || '', []),
139
+ version: config.version || '1.0.0',
140
+ installed: true,
141
+ status: 'active',
142
+ type: 'integration',
143
+ className: className,
144
+
145
+ // Integration configuration details
146
+ events: config.events || [],
147
+ supportedVersions: config.supportedVersions || [],
148
+ hasUserConfig: options.hasUserConfig || false,
149
+
150
+ // Display properties
151
+ icon: display.icon,
152
+ detailsUrl: display.detailsUrl,
153
+
154
+ // API Modules information
155
+ apiModules: Object.keys(modules).map(key => ({
156
+ name: key,
157
+ module: modules[key]?.name || key,
158
+ description: `API module for ${key}`
159
+ })),
160
+
161
+ // Constructor details
162
+ constructor: {
163
+ name: className,
164
+ hasConfig: !!config,
165
+ hasOptions: !!options,
166
+ hasModules: Object.keys(modules).length > 0
167
+ }
168
+ };
169
+ } catch (classError) {
170
+ console.error(`Error processing integration class ${IntegrationClass.name}:`, classError);
171
+ return {
172
+ name: `unknown-${index}`,
173
+ displayName: `Unknown Integration ${index}`,
174
+ description: 'Error processing integration',
175
+ category: 'Other',
176
+ installed: true,
177
+ status: 'error',
178
+ type: 'integration',
179
+ error: classError.message
180
+ };
181
+ }
182
+ });
183
+
184
+ console.log(`Successfully processed ${integrations.length} integrations:`,
185
+ integrations.map(i => `${i.displayName} (${i.name})`));
186
+ return integrations;
187
+ } else {
188
+ console.log('No integrations array found in appDefinition');
189
+ }
190
+ } catch (importError) {
191
+ console.error(`Error importing ${targetFile}:`, importError);
192
+ // Fall back to file parsing if dynamic import fails
193
+ return await parseBackendFile(targetFile);
194
+ }
195
+ }
196
+ }
197
+
198
+ console.log('No backend file found in any expected location');
199
+ return [];
200
+ } catch (error) {
201
+ console.error('Error reading installed integrations:', error);
202
+ return [];
203
+ }
204
+ }
205
+
206
+ // Fallback function to parse backend file if dynamic import fails
207
+ async function parseBackendFile(filePath) {
208
+ try {
209
+ const backendContent = await fs.readFile(filePath, 'utf8');
210
+ const integrations = [];
211
+
212
+ // Look for integration imports and uses
213
+ const importMatches = backendContent.match(/import.*Integration.*from.*@friggframework/g) || [];
214
+ const classMatches = backendContent.match(/class.*Integration/g) || [];
215
+ const allMatches = [...importMatches, ...classMatches];
216
+
217
+ for (const match of allMatches) {
218
+ const nameMatch = match.match(/(\w+Integration)/);
219
+ if (nameMatch) {
220
+ const integrationName = nameMatch[1];
221
+ const serviceName = integrationName.replace('Integration', '');
222
+
223
+ // Check if this integration is in the integrations array
224
+ if (backendContent.includes(integrationName)) {
225
+ integrations.push({
226
+ name: serviceName.toLowerCase(),
227
+ displayName: serviceName,
228
+ description: `${serviceName} integration`,
229
+ category: detectCategory(serviceName.toLowerCase(), '', []),
230
+ installed: true,
231
+ status: 'active',
232
+ type: 'integration',
233
+ className: integrationName,
234
+ constructor: {
235
+ name: integrationName,
236
+ hasConfig: true,
237
+ hasOptions: true,
238
+ hasModules: true
239
+ },
240
+ note: 'Parsed from file (dynamic loading failed)'
241
+ });
242
+ }
243
+ }
244
+ }
245
+
246
+ return integrations;
247
+ } catch (error) {
248
+ console.error('Error parsing backend file:', error);
249
+ return [];
250
+ }
251
+ }
252
+
253
+ // List all integrations
254
+ router.get('/', async (req, res) => {
255
+ try {
256
+ const [availableApiModules, installedIntegrations] = await Promise.all([
257
+ getAvailableIntegrations(),
258
+ getInstalledIntegrations()
259
+ ]);
260
+
261
+ // Format available API modules (not yet integrations)
262
+ const formattedAvailable = availableApiModules.map(apiModule => ({
263
+ ...apiModule,
264
+ displayName: apiModule.name.replace('@friggframework/api-module-', '').replace(/-/g, ' '),
265
+ installed: false,
266
+ status: 'available',
267
+ type: 'api-module' // These are just API modules, not full integrations
268
+ }));
269
+
270
+ // Actual integrations already properly formatted from appDefinition
271
+ const formattedIntegrations = installedIntegrations.map(integration => ({
272
+ ...integration,
273
+ installed: true,
274
+ status: integration.status || 'active'
275
+ }));
276
+
277
+ res.json({
278
+ // Main integrations array contains actual integrations from appDefinition
279
+ integrations: formattedIntegrations,
280
+
281
+ // Available API modules that could become integrations
282
+ availableApiModules: formattedAvailable,
283
+
284
+ // Summary counts
285
+ total: formattedIntegrations.length + formattedAvailable.length,
286
+ activeIntegrations: formattedIntegrations.length,
287
+ availableModules: formattedAvailable.length,
288
+
289
+ // Metadata about the response
290
+ source: 'appDefinition',
291
+ message: formattedIntegrations.length > 0
292
+ ? `Found ${formattedIntegrations.length} active integrations from backend appDefinition`
293
+ : 'No integrations found in backend appDefinition'
294
+ });
295
+ } catch (error) {
296
+ res.status(500).json({
297
+ error: error.message,
298
+ details: 'Failed to fetch integrations'
299
+ });
300
+ }
301
+ });
302
+
303
+ // Install an integration
304
+ router.post('/install', async (req, res) => {
305
+ const { packageName } = req.body;
306
+
307
+ if (!packageName) {
308
+ return res.status(400).json({
309
+ error: 'Package name is required'
310
+ });
311
+ }
312
+
313
+ try {
314
+ // Broadcast installation start
315
+ wsHandler.broadcast('integration-install', {
316
+ status: 'installing',
317
+ packageName,
318
+ message: `Installing ${packageName}...`
319
+ });
320
+
321
+ // Run frigg install command
322
+ const { stdout, stderr } = await execAsync(
323
+ `npx frigg install ${packageName}`,
324
+ { cwd: path.join(process.cwd(), '../../../backend') }
325
+ );
326
+
327
+ // Broadcast success
328
+ wsHandler.broadcast('integration-install', {
329
+ status: 'installed',
330
+ packageName,
331
+ message: `Successfully installed ${packageName}`,
332
+ output: stdout
333
+ });
334
+
335
+ res.json({
336
+ status: 'success',
337
+ message: `Integration ${packageName} installed successfully`,
338
+ output: stdout
339
+ });
340
+
341
+ } catch (error) {
342
+ // Broadcast error
343
+ wsHandler.broadcast('integration-install', {
344
+ status: 'error',
345
+ packageName,
346
+ message: `Failed to install ${packageName}`,
347
+ error: error.message
348
+ });
349
+
350
+ res.status(500).json({
351
+ error: error.message,
352
+ details: 'Failed to install integration',
353
+ stderr: error.stderr
354
+ });
355
+ }
356
+ });
357
+
358
+ // Configure an integration
359
+ router.post('/:integrationName/configure', async (req, res) => {
360
+ const { integrationName } = req.params;
361
+ const { config } = req.body;
362
+
363
+ try {
364
+ // This would typically update the integration configuration
365
+ // For now, we'll store it in a config file
366
+ const configPath = path.join(
367
+ process.cwd(),
368
+ '../../../backend',
369
+ 'config',
370
+ 'integrations',
371
+ `${integrationName}.json`
372
+ );
373
+
374
+ await fs.ensureDir(path.dirname(configPath));
375
+ await fs.writeJson(configPath, config, { spaces: 2 });
376
+
377
+ res.json({
378
+ status: 'success',
379
+ message: `Configuration saved for ${integrationName}`,
380
+ config
381
+ });
382
+
383
+ } catch (error) {
384
+ res.status(500).json({
385
+ error: error.message,
386
+ details: 'Failed to configure integration'
387
+ });
388
+ }
389
+ });
390
+
391
+ // Get integration configuration
392
+ router.get('/:integrationName/config', async (req, res) => {
393
+ const { integrationName } = req.params;
394
+
395
+ try {
396
+ const configPath = path.join(
397
+ process.cwd(),
398
+ '../../../backend',
399
+ 'config',
400
+ 'integrations',
401
+ `${integrationName}.json`
402
+ );
403
+
404
+ if (await fs.pathExists(configPath)) {
405
+ const config = await fs.readJson(configPath);
406
+ res.json({ config });
407
+ } else {
408
+ res.json({ config: {} });
409
+ }
410
+
411
+ } catch (error) {
412
+ res.status(500).json({
413
+ error: error.message,
414
+ details: 'Failed to read integration configuration'
415
+ });
416
+ }
417
+ });
418
+
419
+ // Remove an integration
420
+ router.delete('/:integrationName', async (req, res) => {
421
+ const { integrationName } = req.params;
422
+
423
+ try {
424
+ // Broadcast removal start
425
+ wsHandler.broadcast('integration-remove', {
426
+ status: 'removing',
427
+ packageName: integrationName,
428
+ message: `Removing ${integrationName}...`
429
+ });
430
+
431
+ // Remove the package
432
+ const { stdout, stderr } = await execAsync(
433
+ `npm uninstall ${integrationName}`,
434
+ { cwd: path.join(process.cwd(), '../../../backend') }
435
+ );
436
+
437
+ // Remove config if exists
438
+ const configPath = path.join(
439
+ process.cwd(),
440
+ '../../../backend',
441
+ 'config',
442
+ 'integrations',
443
+ `${integrationName}.json`
444
+ );
445
+
446
+ if (await fs.pathExists(configPath)) {
447
+ await fs.remove(configPath);
448
+ }
449
+
450
+ // Broadcast success
451
+ wsHandler.broadcast('integration-remove', {
452
+ status: 'removed',
453
+ packageName: integrationName,
454
+ message: `Successfully removed ${integrationName}`
455
+ });
456
+
457
+ res.json({
458
+ status: 'success',
459
+ message: `Integration ${integrationName} removed successfully`
460
+ });
461
+
462
+ } catch (error) {
463
+ // Broadcast error
464
+ wsHandler.broadcast('integration-remove', {
465
+ status: 'error',
466
+ packageName: integrationName,
467
+ message: `Failed to remove ${integrationName}`,
468
+ error: error.message
469
+ });
470
+
471
+ res.status(500).json({
472
+ error: error.message,
473
+ details: 'Failed to remove integration'
474
+ });
475
+ }
476
+ });
477
+
478
+ export { getInstalledIntegrations }
479
+ export default router