@friggframework/devtools 2.0.0-next.29 → 2.0.0-next.30
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.
- package/frigg-cli/.eslintrc.js +141 -0
- package/frigg-cli/__tests__/jest.config.js +102 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
- package/frigg-cli/__tests__/utils/command-tester.js +170 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +286 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/index.js +19 -1
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/package.json +51 -0
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +25 -0
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/serverless-template.js +177 -292
- package/management-ui/.eslintrc.js +22 -0
- package/management-ui/README.md +203 -0
- package/management-ui/components.json +21 -0
- package/management-ui/docs/phase2-integration-guide.md +320 -0
- package/management-ui/{dist/index.html → index.html} +1 -2
- package/management-ui/package-lock.json +16517 -0
- package/management-ui/package.json +76 -0
- package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
- package/management-ui/postcss.config.js +6 -0
- package/management-ui/server/api/backend.js +256 -0
- package/management-ui/server/api/cli.js +315 -0
- package/management-ui/server/api/codegen.js +663 -0
- package/management-ui/server/api/connections.js +857 -0
- package/management-ui/server/api/discovery.js +185 -0
- package/management-ui/server/api/environment/index.js +1 -0
- package/management-ui/server/api/environment/router.js +378 -0
- package/management-ui/server/api/environment.js +328 -0
- package/management-ui/server/api/integrations.js +876 -0
- package/management-ui/server/api/logs.js +248 -0
- package/management-ui/server/api/monitoring.js +282 -0
- package/management-ui/server/api/open-ide.js +31 -0
- package/management-ui/server/api/project.js +1029 -0
- package/management-ui/server/api/users/sessions.js +371 -0
- package/management-ui/server/api/users/simulation.js +254 -0
- package/management-ui/server/api/users.js +362 -0
- package/management-ui/server/api-contract.md +275 -0
- package/management-ui/server/index.js +873 -0
- package/management-ui/server/middleware/errorHandler.js +93 -0
- package/management-ui/server/middleware/security.js +32 -0
- package/management-ui/server/processManager.js +296 -0
- package/management-ui/server/server.js +346 -0
- package/management-ui/server/services/aws-monitor.js +413 -0
- package/management-ui/server/services/npm-registry.js +347 -0
- package/management-ui/server/services/template-engine.js +538 -0
- package/management-ui/server/utils/cliIntegration.js +220 -0
- package/management-ui/server/utils/environment/auditLogger.js +471 -0
- package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
- package/management-ui/server/utils/environment/encryption.js +278 -0
- package/management-ui/server/utils/environment/envFileManager.js +286 -0
- package/management-ui/server/utils/import-commonjs.js +28 -0
- package/management-ui/server/utils/response.js +83 -0
- package/management-ui/server/websocket/handler.js +325 -0
- package/management-ui/src/App.jsx +109 -0
- package/management-ui/src/components/AppRouter.jsx +65 -0
- package/management-ui/src/components/Button.jsx +70 -0
- package/management-ui/src/components/Card.jsx +97 -0
- package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
- package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
- package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
- package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
- package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
- package/management-ui/src/components/ErrorBoundary.jsx +73 -0
- package/management-ui/src/components/IntegrationCard.jsx +481 -0
- package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
- package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
- package/management-ui/src/components/IntegrationStatus.jsx +336 -0
- package/management-ui/src/components/Layout.jsx +716 -0
- package/management-ui/src/components/LoadingSpinner.jsx +113 -0
- package/management-ui/src/components/RepositoryPicker.jsx +248 -0
- package/management-ui/src/components/SessionMonitor.jsx +350 -0
- package/management-ui/src/components/StatusBadge.jsx +208 -0
- package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
- package/management-ui/src/components/UserSimulation.jsx +327 -0
- package/management-ui/src/components/Welcome.jsx +434 -0
- package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
- package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
- package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
- package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
- package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
- package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
- package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
- package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
- package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
- package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
- package/management-ui/src/components/codegen/index.js +10 -0
- package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
- package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
- package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
- package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
- package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
- package/management-ui/src/components/connections/index.js +5 -0
- package/management-ui/src/components/index.js +21 -0
- package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
- package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
- package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
- package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
- package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
- package/management-ui/src/components/monitoring/index.js +6 -0
- package/management-ui/src/components/monitoring/monitoring.css +218 -0
- package/management-ui/src/components/theme-provider.jsx +52 -0
- package/management-ui/src/components/theme-toggle.jsx +39 -0
- package/management-ui/src/components/ui/badge.tsx +36 -0
- package/management-ui/src/components/ui/button.test.jsx +56 -0
- package/management-ui/src/components/ui/button.tsx +57 -0
- package/management-ui/src/components/ui/card.tsx +76 -0
- package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
- package/management-ui/src/components/ui/select.tsx +157 -0
- package/management-ui/src/components/ui/skeleton.jsx +15 -0
- package/management-ui/src/hooks/useFrigg.jsx +601 -0
- package/management-ui/src/hooks/useSocket.jsx +58 -0
- package/management-ui/src/index.css +193 -0
- package/management-ui/src/lib/utils.ts +6 -0
- package/management-ui/src/main.jsx +10 -0
- package/management-ui/src/pages/CodeGeneration.jsx +14 -0
- package/management-ui/src/pages/Connections.jsx +252 -0
- package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
- package/management-ui/src/pages/Dashboard.jsx +311 -0
- package/management-ui/src/pages/Environment.jsx +314 -0
- package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
- package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
- package/management-ui/src/pages/IntegrationTest.jsx +742 -0
- package/management-ui/src/pages/Integrations.jsx +253 -0
- package/management-ui/src/pages/Monitoring.jsx +17 -0
- package/management-ui/src/pages/Simulation.jsx +155 -0
- package/management-ui/src/pages/Users.jsx +492 -0
- package/management-ui/src/services/api.js +41 -0
- package/management-ui/src/services/apiModuleService.js +193 -0
- package/management-ui/src/services/websocket-handlers.js +120 -0
- package/management-ui/src/test/api/project.test.js +273 -0
- package/management-ui/src/test/components/Welcome.test.jsx +378 -0
- package/management-ui/src/test/mocks/server.js +178 -0
- package/management-ui/src/test/setup.js +61 -0
- package/management-ui/src/test/utils/test-utils.jsx +134 -0
- package/management-ui/src/utils/repository.js +98 -0
- package/management-ui/src/utils/repository.test.js +118 -0
- package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
- package/management-ui/tailwind.config.js +63 -0
- package/management-ui/tsconfig.json +37 -0
- package/management-ui/tsconfig.node.json +10 -0
- package/management-ui/vite.config.js +26 -0
- package/management-ui/vitest.config.js +38 -0
- package/package.json +5 -5
- package/management-ui/dist/assets/index-BA21WgFa.js +0 -1221
- package/management-ui/dist/assets/index-CbM64Oba.js +0 -1221
- package/management-ui/dist/assets/index-CkvseXTC.css +0 -1
- /package/management-ui/{dist/assets/FriggLogo-B7Xx8ZW1.svg → src/assets/FriggLogo.svg} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createErrorResponse, ERROR_CODES } from '../utils/response.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enhanced error handler middleware that creates standardized error responses
|
|
5
|
+
* @param {Error} err - The error object
|
|
6
|
+
* @param {Request} req - Express request object
|
|
7
|
+
* @param {Response} res - Express response object
|
|
8
|
+
* @param {Function} next - Express next function
|
|
9
|
+
*/
|
|
10
|
+
const errorHandler = (err, req, res, next) => {
|
|
11
|
+
console.error('API Error:', {
|
|
12
|
+
error: err.message,
|
|
13
|
+
stack: err.stack,
|
|
14
|
+
path: req.path,
|
|
15
|
+
method: req.method,
|
|
16
|
+
timestamp: new Date().toISOString()
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Default error response
|
|
20
|
+
let status = 500;
|
|
21
|
+
let code = ERROR_CODES.INTERNAL_ERROR;
|
|
22
|
+
let message = 'Internal Server Error';
|
|
23
|
+
let details = null;
|
|
24
|
+
|
|
25
|
+
// Handle specific error types
|
|
26
|
+
if (err.code === 'ENOENT') {
|
|
27
|
+
status = 404;
|
|
28
|
+
code = ERROR_CODES.NOT_FOUND;
|
|
29
|
+
message = 'File or resource not found';
|
|
30
|
+
} else if (err.code === 'EACCES') {
|
|
31
|
+
status = 403;
|
|
32
|
+
code = ERROR_CODES.FORBIDDEN;
|
|
33
|
+
message = 'Permission denied';
|
|
34
|
+
} else if (err.name === 'ValidationError') {
|
|
35
|
+
status = 400;
|
|
36
|
+
code = ERROR_CODES.INVALID_REQUEST;
|
|
37
|
+
message = err.message;
|
|
38
|
+
} else if (err.status && err.code) {
|
|
39
|
+
// Custom application errors
|
|
40
|
+
status = err.status;
|
|
41
|
+
code = err.code;
|
|
42
|
+
message = err.message;
|
|
43
|
+
details = err.details;
|
|
44
|
+
} else if (err.status || err.statusCode) {
|
|
45
|
+
status = err.status || err.statusCode;
|
|
46
|
+
message = err.message;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Include stack trace in development
|
|
50
|
+
if (process.env.NODE_ENV === 'development') {
|
|
51
|
+
details = {
|
|
52
|
+
...details,
|
|
53
|
+
stack: err.stack
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const errorResponse = createErrorResponse(code, message, details);
|
|
58
|
+
res.status(status).json(errorResponse);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
<<<<<<< HEAD
|
|
62
|
+
<<<<<<< HEAD
|
|
63
|
+
=======
|
|
64
|
+
<<<<<<< HEAD
|
|
65
|
+
<<<<<<< HEAD
|
|
66
|
+
=======
|
|
67
|
+
>>>>>>> f153939e (refactor: clean up CLI help display and remove unused dependencies)
|
|
68
|
+
>>>>>>> 860052b4 (feat: integrate complete management-ui and additional features)
|
|
69
|
+
=======
|
|
70
|
+
>>>>>>> 7e97f01c (fix: resolve ui-command merge conflicts and update package.json)
|
|
71
|
+
/**
|
|
72
|
+
* Async handler wrapper to catch errors in async route handlers
|
|
73
|
+
* @param {Function} fn - Async route handler function
|
|
74
|
+
* @returns {Function} Express middleware function
|
|
75
|
+
*/
|
|
76
|
+
const asyncHandler = (fn) => (req, res, next) => {
|
|
77
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export { errorHandler, asyncHandler };
|
|
81
|
+
<<<<<<< HEAD
|
|
82
|
+
=======
|
|
83
|
+
<<<<<<< HEAD
|
|
84
|
+
export { errorHandler, asyncHandler };
|
|
85
|
+
=======
|
|
86
|
+
export { errorHandler };
|
|
87
|
+
>>>>>>> 652520a5 (Claude Flow RFC related development)
|
|
88
|
+
=======
|
|
89
|
+
export { errorHandler, asyncHandler };
|
|
90
|
+
>>>>>>> f153939e (refactor: clean up CLI help display and remove unused dependencies)
|
|
91
|
+
>>>>>>> 860052b4 (feat: integrate complete management-ui and additional features)
|
|
92
|
+
=======
|
|
93
|
+
>>>>>>> 7e97f01c (fix: resolve ui-command merge conflicts and update package.json)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security middleware to ensure the server is only accessible from localhost
|
|
3
|
+
*/
|
|
4
|
+
const securityMiddleware = (req, res, next) => {
|
|
5
|
+
const clientIp = req.ip || req.connection.remoteAddress;
|
|
6
|
+
|
|
7
|
+
// Extract the actual IP if it's in IPv6 format
|
|
8
|
+
const actualIp = clientIp.includes('::ffff:')
|
|
9
|
+
? clientIp.split('::ffff:')[1]
|
|
10
|
+
: clientIp;
|
|
11
|
+
|
|
12
|
+
// Allow only localhost connections
|
|
13
|
+
const allowedIps = ['127.0.0.1', 'localhost', '::1'];
|
|
14
|
+
|
|
15
|
+
if (!allowedIps.includes(actualIp)) {
|
|
16
|
+
console.warn(`Blocked connection attempt from ${actualIp}`);
|
|
17
|
+
return res.status(403).json({
|
|
18
|
+
error: 'Forbidden',
|
|
19
|
+
message: 'This server is only accessible from localhost'
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Add security headers
|
|
24
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
25
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
26
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
27
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
28
|
+
|
|
29
|
+
next();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { securityMiddleware };
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import fs from 'fs/promises'
|
|
4
|
+
|
|
5
|
+
class FriggProcessManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.process = null
|
|
8
|
+
this.status = 'stopped'
|
|
9
|
+
this.listeners = new Set()
|
|
10
|
+
this.logs = []
|
|
11
|
+
this.maxLogs = 1000
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Add status change listener
|
|
15
|
+
addStatusListener(listener) {
|
|
16
|
+
this.listeners.add(listener)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Remove status change listener
|
|
20
|
+
removeStatusListener(listener) {
|
|
21
|
+
this.listeners.delete(listener)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Notify all listeners of status change
|
|
25
|
+
notifyListeners(status, data = {}) {
|
|
26
|
+
this.status = status
|
|
27
|
+
this.listeners.forEach(listener => {
|
|
28
|
+
try {
|
|
29
|
+
listener({ status, ...data })
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error notifying status listener:', error)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Add log entry
|
|
37
|
+
addLog(type, message) {
|
|
38
|
+
const logEntry = {
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
type, // 'stdout', 'stderr', 'system'
|
|
41
|
+
message
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.logs.push(logEntry)
|
|
45
|
+
|
|
46
|
+
// Keep only the last maxLogs entries
|
|
47
|
+
if (this.logs.length > this.maxLogs) {
|
|
48
|
+
this.logs = this.logs.slice(-this.maxLogs)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Notify listeners of new log
|
|
52
|
+
this.listeners.forEach(listener => {
|
|
53
|
+
try {
|
|
54
|
+
listener({ status: this.status, log: logEntry })
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Error notifying log listener:', error)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get current status
|
|
62
|
+
getStatus() {
|
|
63
|
+
return {
|
|
64
|
+
status: this.status,
|
|
65
|
+
pid: this.process?.pid || null,
|
|
66
|
+
uptime: this.process ? Date.now() - this.startTime : 0
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get recent logs
|
|
71
|
+
getLogs(limit = 100) {
|
|
72
|
+
return this.logs.slice(-limit)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Find project root with infrastructure.js
|
|
76
|
+
async findProjectRoot(startPath = process.cwd()) {
|
|
77
|
+
let currentPath = startPath
|
|
78
|
+
|
|
79
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
80
|
+
try {
|
|
81
|
+
const infraPath = path.join(currentPath, 'infrastructure.js')
|
|
82
|
+
await fs.access(infraPath)
|
|
83
|
+
return currentPath
|
|
84
|
+
} catch {
|
|
85
|
+
currentPath = path.dirname(currentPath)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error('Could not find infrastructure.js file in project hierarchy')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Start Frigg process
|
|
93
|
+
async start(options = {}) {
|
|
94
|
+
if (this.status === 'running') {
|
|
95
|
+
throw new Error('Frigg is already running')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.status === 'starting') {
|
|
99
|
+
throw new Error('Frigg is already starting')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
this.notifyListeners('starting')
|
|
104
|
+
this.addLog('system', 'Starting Frigg server...')
|
|
105
|
+
|
|
106
|
+
// Find project root
|
|
107
|
+
const projectRoot = await this.findProjectRoot()
|
|
108
|
+
this.addLog('system', `Project root found: ${projectRoot}`)
|
|
109
|
+
|
|
110
|
+
// Suppress AWS SDK warning
|
|
111
|
+
const env = {
|
|
112
|
+
...process.env,
|
|
113
|
+
AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE: '1'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Build serverless command
|
|
117
|
+
const command = 'serverless'
|
|
118
|
+
const args = [
|
|
119
|
+
'offline',
|
|
120
|
+
'--config',
|
|
121
|
+
'infrastructure.js',
|
|
122
|
+
'--stage',
|
|
123
|
+
options.stage || 'dev'
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
if (options.verbose) {
|
|
127
|
+
args.push('--verbose')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.addLog('system', `Executing: ${command} ${args.join(' ')}`)
|
|
131
|
+
this.addLog('system', `Working directory: ${projectRoot}`)
|
|
132
|
+
|
|
133
|
+
// Spawn the process
|
|
134
|
+
this.process = spawn(command, args, {
|
|
135
|
+
cwd: projectRoot,
|
|
136
|
+
env,
|
|
137
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
this.startTime = Date.now()
|
|
141
|
+
|
|
142
|
+
// Handle stdout
|
|
143
|
+
this.process.stdout.on('data', (data) => {
|
|
144
|
+
const message = data.toString().trim()
|
|
145
|
+
if (message) {
|
|
146
|
+
this.addLog('stdout', message)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// Handle stderr
|
|
151
|
+
this.process.stderr.on('data', (data) => {
|
|
152
|
+
const message = data.toString().trim()
|
|
153
|
+
if (message) {
|
|
154
|
+
this.addLog('stderr', message)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Handle process events
|
|
159
|
+
this.process.on('spawn', () => {
|
|
160
|
+
this.addLog('system', `Process spawned with PID: ${this.process.pid}`)
|
|
161
|
+
this.notifyListeners('running', { pid: this.process.pid })
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
this.process.on('error', (error) => {
|
|
165
|
+
this.addLog('system', `Process error: ${error.message}`)
|
|
166
|
+
this.notifyListeners('stopped', { error: error.message })
|
|
167
|
+
this.cleanup()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
this.process.on('close', (code, signal) => {
|
|
171
|
+
const message = signal
|
|
172
|
+
? `Process terminated by signal: ${signal}`
|
|
173
|
+
: `Process exited with code: ${code}`
|
|
174
|
+
|
|
175
|
+
this.addLog('system', message)
|
|
176
|
+
this.notifyListeners('stopped', { code, signal })
|
|
177
|
+
this.cleanup()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// Return promise that resolves when process is fully started
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const timeout = setTimeout(() => {
|
|
183
|
+
reject(new Error('Timeout waiting for Frigg to start'))
|
|
184
|
+
}, 30000) // 30 second timeout
|
|
185
|
+
|
|
186
|
+
const checkStarted = () => {
|
|
187
|
+
if (this.status === 'running') {
|
|
188
|
+
clearTimeout(timeout)
|
|
189
|
+
resolve(this.getStatus())
|
|
190
|
+
} else if (this.status === 'stopped') {
|
|
191
|
+
clearTimeout(timeout)
|
|
192
|
+
reject(new Error('Failed to start Frigg'))
|
|
193
|
+
} else {
|
|
194
|
+
setTimeout(checkStarted, 100)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
checkStarted()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.addLog('system', `Failed to start: ${error.message}`)
|
|
203
|
+
this.notifyListeners('stopped', { error: error.message })
|
|
204
|
+
this.cleanup()
|
|
205
|
+
throw error
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Stop Frigg process
|
|
210
|
+
async stop(force = false) {
|
|
211
|
+
if (this.status === 'stopped') {
|
|
212
|
+
throw new Error('Frigg is already stopped')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
if (!this.process) {
|
|
217
|
+
this.notifyListeners('stopped')
|
|
218
|
+
resolve()
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.addLog('system', force ? 'Force stopping Frigg server...' : 'Stopping Frigg server...')
|
|
223
|
+
this.notifyListeners('stopping')
|
|
224
|
+
|
|
225
|
+
// Set up cleanup timeout
|
|
226
|
+
const timeout = setTimeout(() => {
|
|
227
|
+
if (this.process && !this.process.killed) {
|
|
228
|
+
this.addLog('system', 'Force killing process after timeout')
|
|
229
|
+
this.process.kill('SIGKILL')
|
|
230
|
+
}
|
|
231
|
+
}, 5000) // 5 second timeout for graceful shutdown
|
|
232
|
+
|
|
233
|
+
// Listen for process to actually exit
|
|
234
|
+
const onClose = () => {
|
|
235
|
+
clearTimeout(timeout)
|
|
236
|
+
resolve()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (this.process.exitCode !== null || this.process.killed) {
|
|
240
|
+
// Process already exited
|
|
241
|
+
onClose()
|
|
242
|
+
} else {
|
|
243
|
+
this.process.once('close', onClose)
|
|
244
|
+
|
|
245
|
+
// Send termination signal
|
|
246
|
+
if (force) {
|
|
247
|
+
this.process.kill('SIGKILL')
|
|
248
|
+
} else {
|
|
249
|
+
this.process.kill('SIGTERM')
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Restart Frigg process
|
|
256
|
+
async restart(options = {}) {
|
|
257
|
+
this.addLog('system', 'Restarting Frigg server...')
|
|
258
|
+
|
|
259
|
+
if (this.status !== 'stopped') {
|
|
260
|
+
await this.stop()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Wait a bit before restarting
|
|
264
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
265
|
+
|
|
266
|
+
return this.start(options)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Clean up process references
|
|
270
|
+
cleanup() {
|
|
271
|
+
if (this.process) {
|
|
272
|
+
this.process.removeAllListeners()
|
|
273
|
+
this.process = null
|
|
274
|
+
}
|
|
275
|
+
this.startTime = null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get process metrics
|
|
279
|
+
getMetrics() {
|
|
280
|
+
if (!this.process || this.status !== 'running') {
|
|
281
|
+
return null
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
pid: this.process.pid,
|
|
286
|
+
uptime: Date.now() - this.startTime,
|
|
287
|
+
memoryUsage: process.memoryUsage(), // This is the manager's memory, not the child's
|
|
288
|
+
status: this.status
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Create singleton instance
|
|
294
|
+
const processManager = new FriggProcessManager()
|
|
295
|
+
|
|
296
|
+
export default processManager
|