@oussema_mili/test-pkg-123 1.1.32

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 (49) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +220 -0
  3. package/auth-callback.html +97 -0
  4. package/auth.js +276 -0
  5. package/cli-commands.js +1921 -0
  6. package/containerManager.js +304 -0
  7. package/daemon/agentRunner.js +491 -0
  8. package/daemon/daemonEntry.js +64 -0
  9. package/daemon/daemonManager.js +266 -0
  10. package/daemon/logManager.js +227 -0
  11. package/dist/styles.css +504 -0
  12. package/docker-actions/apps.js +3913 -0
  13. package/docker-actions/config-transformer.js +380 -0
  14. package/docker-actions/containers.js +355 -0
  15. package/docker-actions/general.js +171 -0
  16. package/docker-actions/images.js +1128 -0
  17. package/docker-actions/logs.js +224 -0
  18. package/docker-actions/metrics.js +270 -0
  19. package/docker-actions/registry.js +1100 -0
  20. package/docker-actions/setup-tasks.js +859 -0
  21. package/docker-actions/terminal.js +247 -0
  22. package/docker-actions/volumes.js +713 -0
  23. package/helper-functions.js +193 -0
  24. package/index.html +83 -0
  25. package/index.js +341 -0
  26. package/package.json +82 -0
  27. package/postcss.config.mjs +5 -0
  28. package/scripts/release.sh +212 -0
  29. package/setup/setupWizard.js +403 -0
  30. package/store/agentSessionStore.js +51 -0
  31. package/store/agentStore.js +113 -0
  32. package/store/configStore.js +171 -0
  33. package/store/daemonStore.js +217 -0
  34. package/store/deviceCredentialStore.js +107 -0
  35. package/store/npmTokenStore.js +65 -0
  36. package/store/registryStore.js +329 -0
  37. package/store/setupState.js +147 -0
  38. package/styles.css +1 -0
  39. package/utils/appLogger.js +223 -0
  40. package/utils/deviceInfo.js +98 -0
  41. package/utils/ecrAuth.js +225 -0
  42. package/utils/encryption.js +112 -0
  43. package/utils/envSetup.js +41 -0
  44. package/utils/errorHandler.js +327 -0
  45. package/utils/portUtils.js +59 -0
  46. package/utils/prerequisites.js +323 -0
  47. package/utils/prompts.js +318 -0
  48. package/utils/ssl-certificates.js +256 -0
  49. package/websocket-server.js +415 -0
@@ -0,0 +1,266 @@
1
+ import { spawn } from 'child_process';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname } from 'path';
6
+ import chalk from 'chalk';
7
+
8
+ import {
9
+ getDaemonPid,
10
+ getDaemonState,
11
+ clearDaemonPid,
12
+ clearDaemonState,
13
+ isDaemonRunning,
14
+ } from '../store/daemonStore.js';
15
+ import { getLogPath } from './logManager.js';
16
+
17
+ // ES module helpers
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ // Path to the daemon entry script
22
+ const DAEMON_SCRIPT = path.join(__dirname, 'daemonEntry.js');
23
+
24
+ /**
25
+ * Start the agent as a background daemon
26
+ * @param {Object} options - Daemon options
27
+ * @param {number} options.port - Preferred WebSocket port
28
+ * @returns {Promise<Object>} Daemon info
29
+ */
30
+ export async function startDaemon(options = {}) {
31
+ // Check if daemon is already running
32
+ if (isDaemonRunning()) {
33
+ const state = getDaemonState();
34
+ throw new Error(
35
+ `Agent is already running (PID: ${state?.pid}, Port: ${state?.port}). ` +
36
+ 'Use "fenwave service stop" to stop it first.'
37
+ );
38
+ }
39
+
40
+ // Clean up any stale files
41
+ clearDaemonPid();
42
+ clearDaemonState();
43
+
44
+ // Prepare daemon arguments
45
+ const args = [];
46
+ if (options.port) {
47
+ args.push('--port', String(options.port));
48
+ }
49
+
50
+ // Get log file paths for stdout/stderr redirection
51
+ const logPath = getLogPath();
52
+ const errorLogPath = getLogPath('agent.error');
53
+
54
+ // Ensure log directory exists
55
+ const logDir = path.dirname(logPath);
56
+ if (!fs.existsSync(logDir)) {
57
+ fs.mkdirSync(logDir, { recursive: true, mode: 0o700 });
58
+ }
59
+
60
+ // Open log files for writing
61
+ const outFd = fs.openSync(logPath, 'a');
62
+ const errFd = fs.openSync(errorLogPath, 'a');
63
+
64
+ // Spawn daemon process
65
+ const daemon = spawn('node', [DAEMON_SCRIPT, ...args], {
66
+ detached: true,
67
+ stdio: ['ignore', outFd, errFd],
68
+ env: {
69
+ ...process.env,
70
+ FENWAVE_DAEMON: 'true',
71
+ },
72
+ });
73
+
74
+ // Unref to allow parent to exit
75
+ daemon.unref();
76
+
77
+ // Close file descriptors in parent
78
+ fs.closeSync(outFd);
79
+ fs.closeSync(errFd);
80
+
81
+ // Wait a moment for daemon to start and write state
82
+ await new Promise((resolve) => setTimeout(resolve, 2000));
83
+
84
+ // Check if daemon started successfully
85
+ const state = getDaemonState();
86
+ if (!state || state.status !== 'running') {
87
+ throw new Error(
88
+ 'Daemon failed to start. Check logs with "fenwave service logs"'
89
+ );
90
+ }
91
+
92
+ return {
93
+ pid: state.pid,
94
+ port: state.port,
95
+ startTime: state.startTime,
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Stop the running daemon
101
+ * @param {Object} options - Stop options
102
+ * @param {number} options.timeout - Timeout in ms to wait for graceful shutdown
103
+ * @returns {Promise<boolean>} True if stopped
104
+ */
105
+ export async function stopDaemon(options = {}) {
106
+ const { timeout = 20000 } = options;
107
+
108
+ const pid = getDaemonPid();
109
+ if (!pid) {
110
+ console.log(chalk.yellow('No daemon PID file found'));
111
+ return false;
112
+ }
113
+
114
+ // Check if process is running
115
+ if (!isDaemonRunning()) {
116
+ console.log(chalk.yellow('Daemon process not running, cleaning up files...'));
117
+ clearDaemonPid();
118
+ clearDaemonState();
119
+ return false;
120
+ }
121
+
122
+ console.log(chalk.blue(`Stopping daemon (PID: ${pid})...`));
123
+
124
+ // Send SIGTERM for graceful shutdown
125
+ try {
126
+ process.kill(pid, 'SIGTERM');
127
+ } catch (error) {
128
+ if (error.code === 'ESRCH') {
129
+ console.log(chalk.yellow('Process already terminated'));
130
+ clearDaemonPid();
131
+ clearDaemonState();
132
+ return true;
133
+ }
134
+ throw error;
135
+ }
136
+
137
+ // Wait for process to terminate
138
+ const startTime = Date.now();
139
+ while (Date.now() - startTime < timeout) {
140
+ try {
141
+ process.kill(pid, 0);
142
+ // Process still running, wait
143
+ await new Promise((resolve) => setTimeout(resolve, 500));
144
+ } catch (error) {
145
+ // Process terminated
146
+ console.log(chalk.green('Daemon stopped successfully'));
147
+ clearDaemonPid();
148
+ clearDaemonState();
149
+ return true;
150
+ }
151
+ }
152
+
153
+ // Timeout reached, force kill (always happens automatically)
154
+ console.log(chalk.yellow('Graceful shutdown timed out, forcing stop...'));
155
+ try {
156
+ process.kill(pid, 'SIGKILL');
157
+ await new Promise((resolve) => setTimeout(resolve, 1000));
158
+ clearDaemonPid();
159
+ clearDaemonState();
160
+ console.log(chalk.green('Daemon stopped'));
161
+ return true;
162
+ } catch (error) {
163
+ if (error.code === 'ESRCH') {
164
+ clearDaemonPid();
165
+ clearDaemonState();
166
+ return true;
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Restart the daemon
174
+ * @param {Object} options - Restart options
175
+ * @returns {Promise<Object>} New daemon info
176
+ */
177
+ export async function restartDaemon(options = {}) {
178
+ const state = getDaemonState();
179
+ const port = options.port || state?.port;
180
+
181
+ // Stop existing daemon if running
182
+ if (isDaemonRunning()) {
183
+ await stopDaemon({ force: false, timeout: 10000 });
184
+ // Wait a moment before restarting
185
+ await new Promise((resolve) => setTimeout(resolve, 1000));
186
+ }
187
+
188
+ // Start new daemon
189
+ return startDaemon({ port });
190
+ }
191
+
192
+ /**
193
+ * Get daemon status
194
+ * @returns {Object} Daemon status
195
+ */
196
+ export function getDaemonStatus() {
197
+ const state = getDaemonState();
198
+ const pid = getDaemonPid();
199
+ const running = isDaemonRunning();
200
+
201
+ if (!running && state) {
202
+ // Daemon died, clean up
203
+ clearDaemonPid();
204
+ clearDaemonState();
205
+ return {
206
+ running: false,
207
+ status: 'not_running',
208
+ };
209
+ }
210
+
211
+ if (!state) {
212
+ return {
213
+ running: false,
214
+ status: 'not_running',
215
+ };
216
+ }
217
+
218
+ // Calculate uptime
219
+ let uptime = null;
220
+ if (state.startTime) {
221
+ uptime = Date.now() - new Date(state.startTime).getTime();
222
+ }
223
+
224
+ return {
225
+ running: true,
226
+ status: state.status || 'running',
227
+ pid: state.pid,
228
+ port: state.port,
229
+ startTime: state.startTime,
230
+ uptime,
231
+ userEntityRef: state.userEntityRef,
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Format uptime in human readable format
237
+ * @param {number} ms - Uptime in milliseconds
238
+ * @returns {string} Formatted uptime
239
+ */
240
+ export function formatUptime(ms) {
241
+ if (!ms) return 'N/A';
242
+
243
+ const seconds = Math.floor(ms / 1000);
244
+ const minutes = Math.floor(seconds / 60);
245
+ const hours = Math.floor(minutes / 60);
246
+ const days = Math.floor(hours / 24);
247
+
248
+ if (days > 0) {
249
+ return `${days}d ${hours % 24}h ${minutes % 60}m`;
250
+ }
251
+ if (hours > 0) {
252
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
253
+ }
254
+ if (minutes > 0) {
255
+ return `${minutes}m ${seconds % 60}s`;
256
+ }
257
+ return `${seconds}s`;
258
+ }
259
+
260
+ export default {
261
+ startDaemon,
262
+ stopDaemon,
263
+ restartDaemon,
264
+ getDaemonStatus,
265
+ formatUptime,
266
+ };
@@ -0,0 +1,227 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ const FENWAVE_DIR = path.join(os.homedir(), '.fenwave');
6
+ const LOGS_DIR = path.join(FENWAVE_DIR, 'logs');
7
+
8
+ // Log rotation settings
9
+ const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB
10
+ const MAX_LOG_FILES = 5;
11
+
12
+ /**
13
+ * Ensure logs directory exists
14
+ */
15
+ function ensureLogsDir() {
16
+ if (!fs.existsSync(LOGS_DIR)) {
17
+ fs.mkdirSync(LOGS_DIR, { recursive: true, mode: 0o700 });
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Get path to log file
23
+ * @param {string} name - Log file name (without extension)
24
+ * @returns {string} Full path to log file
25
+ */
26
+ export function getLogPath(name = 'agent') {
27
+ ensureLogsDir();
28
+ return path.join(LOGS_DIR, `${name}.log`);
29
+ }
30
+
31
+ /**
32
+ * Get path to error log file
33
+ * @returns {string} Full path to error log file
34
+ */
35
+ export function getErrorLogPath() {
36
+ return getLogPath('agent.error');
37
+ }
38
+
39
+ /**
40
+ * Rotate log files if necessary
41
+ * @param {string} logPath - Path to log file
42
+ */
43
+ export function rotateLogIfNeeded(logPath) {
44
+ try {
45
+ if (!fs.existsSync(logPath)) return;
46
+
47
+ const stats = fs.statSync(logPath);
48
+ if (stats.size < MAX_LOG_SIZE) return;
49
+
50
+ // Rotate existing files
51
+ for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
52
+ const oldPath = `${logPath}.${i}`;
53
+ const newPath = `${logPath}.${i + 1}`;
54
+ if (fs.existsSync(oldPath)) {
55
+ if (i === MAX_LOG_FILES - 1) {
56
+ // Delete oldest file
57
+ fs.unlinkSync(oldPath);
58
+ } else {
59
+ fs.renameSync(oldPath, newPath);
60
+ }
61
+ }
62
+ }
63
+
64
+ // Rename current log to .1
65
+ fs.renameSync(logPath, `${logPath}.1`);
66
+ } catch (error) {
67
+ console.error('Failed to rotate log:', error.message);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Write to log file with rotation
73
+ * @param {string} message - Message to log
74
+ * @param {string} level - Log level (info, warn, error)
75
+ */
76
+ export function writeLog(message, level = 'info') {
77
+ const logPath = getLogPath();
78
+ rotateLogIfNeeded(logPath);
79
+
80
+ const timestamp = new Date().toISOString();
81
+ const logLine = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
82
+
83
+ try {
84
+ fs.appendFileSync(logPath, logLine);
85
+ } catch (error) {
86
+ // If main log fails, try to at least write to console
87
+ console.error('Failed to write to log file:', error.message);
88
+ }
89
+
90
+ // Also write errors to error log
91
+ if (level === 'error') {
92
+ const errorLogPath = getErrorLogPath();
93
+ rotateLogIfNeeded(errorLogPath);
94
+ try {
95
+ fs.appendFileSync(errorLogPath, logLine);
96
+ } catch (e) {
97
+ // Ignore
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Create a logger instance for daemon use
104
+ * @returns {Object} Logger with info, warn, error methods
105
+ */
106
+ export function createLogger() {
107
+ return {
108
+ info: (message) => writeLog(message, 'info'),
109
+ warn: (message) => writeLog(message, 'warn'),
110
+ error: (message) => writeLog(message, 'error'),
111
+ debug: (message) => {
112
+ if (process.env.DEBUG === 'true') {
113
+ writeLog(message, 'debug');
114
+ }
115
+ },
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Read log file contents
121
+ * @param {number} lines - Number of lines to read from end (0 = all)
122
+ * @param {boolean} includeRotated - Include rotated log files
123
+ * @returns {string} Log content
124
+ */
125
+ export function readLogs(lines = 100, includeRotated = false) {
126
+ const logPath = getLogPath();
127
+ let content = '';
128
+
129
+ try {
130
+ // Read rotated files first if requested
131
+ if (includeRotated) {
132
+ for (let i = MAX_LOG_FILES; i >= 1; i--) {
133
+ const rotatedPath = `${logPath}.${i}`;
134
+ if (fs.existsSync(rotatedPath)) {
135
+ content += fs.readFileSync(rotatedPath, 'utf8');
136
+ }
137
+ }
138
+ }
139
+
140
+ // Read current log file
141
+ if (fs.existsSync(logPath)) {
142
+ content += fs.readFileSync(logPath, 'utf8');
143
+ }
144
+
145
+ if (lines > 0) {
146
+ const allLines = content.split('\n');
147
+ return allLines.slice(-lines).join('\n');
148
+ }
149
+
150
+ return content;
151
+ } catch (error) {
152
+ return `Error reading logs: ${error.message}`;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Clear all log files
158
+ */
159
+ export function clearLogs() {
160
+ const logPath = getLogPath();
161
+ const errorLogPath = getErrorLogPath();
162
+
163
+ try {
164
+ // Clear main log and rotated files
165
+ for (const basePath of [logPath, errorLogPath]) {
166
+ if (fs.existsSync(basePath)) {
167
+ fs.unlinkSync(basePath);
168
+ }
169
+ for (let i = 1; i <= MAX_LOG_FILES; i++) {
170
+ const rotatedPath = `${basePath}.${i}`;
171
+ if (fs.existsSync(rotatedPath)) {
172
+ fs.unlinkSync(rotatedPath);
173
+ }
174
+ }
175
+ }
176
+ } catch (error) {
177
+ console.error('Failed to clear logs:', error.message);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Get logs directory path
183
+ * @returns {string} Path to logs directory
184
+ */
185
+ export function getLogsDir() {
186
+ ensureLogsDir();
187
+ return LOGS_DIR;
188
+ }
189
+
190
+ /**
191
+ * List all log files
192
+ * @returns {Array<Object>} Array of log file info
193
+ */
194
+ export function listLogFiles() {
195
+ ensureLogsDir();
196
+
197
+ try {
198
+ const files = fs.readdirSync(LOGS_DIR);
199
+ return files
200
+ .filter((f) => f.endsWith('.log') || f.match(/\.log\.\d+$/))
201
+ .map((f) => {
202
+ const filePath = path.join(LOGS_DIR, f);
203
+ const stats = fs.statSync(filePath);
204
+ return {
205
+ name: f,
206
+ path: filePath,
207
+ size: stats.size,
208
+ modified: stats.mtime,
209
+ };
210
+ })
211
+ .sort((a, b) => b.modified - a.modified);
212
+ } catch (error) {
213
+ return [];
214
+ }
215
+ }
216
+
217
+ export default {
218
+ getLogPath,
219
+ getErrorLogPath,
220
+ rotateLogIfNeeded,
221
+ writeLog,
222
+ createLogger,
223
+ readLogs,
224
+ clearLogs,
225
+ getLogsDir,
226
+ listLogFiles,
227
+ };