@oussema_mili/test-pkg-123 1.1.22

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 @oussema_mili/test-pkg-123 might be problematic. Click here for more details.

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 +1923 -0
  6. package/containerManager.js +304 -0
  7. package/daemon/agentRunner.js +429 -0
  8. package/daemon/daemonEntry.js +64 -0
  9. package/daemon/daemonManager.js +271 -0
  10. package/daemon/logManager.js +227 -0
  11. package/dist/styles.css +504 -0
  12. package/docker-actions/apps.js +3938 -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 +696 -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 +44 -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,271 @@
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 {boolean} options.force - Force kill if graceful fails
103
+ * @param {number} options.timeout - Timeout in ms to wait for graceful shutdown
104
+ * @returns {Promise<boolean>} True if stopped
105
+ */
106
+ export async function stopDaemon(options = {}) {
107
+ const { force = false, timeout = 10000 } = options;
108
+
109
+ const pid = getDaemonPid();
110
+ if (!pid) {
111
+ console.log(chalk.yellow('No daemon PID file found'));
112
+ return false;
113
+ }
114
+
115
+ // Check if process is running
116
+ if (!isDaemonRunning()) {
117
+ console.log(chalk.yellow('Daemon process not running, cleaning up files...'));
118
+ clearDaemonPid();
119
+ clearDaemonState();
120
+ return false;
121
+ }
122
+
123
+ console.log(chalk.blue(`Stopping daemon (PID: ${pid})...`));
124
+
125
+ // Send SIGTERM for graceful shutdown
126
+ try {
127
+ process.kill(pid, 'SIGTERM');
128
+ } catch (error) {
129
+ if (error.code === 'ESRCH') {
130
+ console.log(chalk.yellow('Process already terminated'));
131
+ clearDaemonPid();
132
+ clearDaemonState();
133
+ return true;
134
+ }
135
+ throw error;
136
+ }
137
+
138
+ // Wait for process to terminate
139
+ const startTime = Date.now();
140
+ while (Date.now() - startTime < timeout) {
141
+ try {
142
+ process.kill(pid, 0);
143
+ // Process still running, wait
144
+ await new Promise((resolve) => setTimeout(resolve, 500));
145
+ } catch (error) {
146
+ // Process terminated
147
+ console.log(chalk.green('Daemon stopped successfully'));
148
+ clearDaemonPid();
149
+ clearDaemonState();
150
+ return true;
151
+ }
152
+ }
153
+
154
+ // Timeout reached, try SIGKILL if force option
155
+ if (force) {
156
+ console.log(chalk.yellow('Graceful shutdown timed out, forcing...'));
157
+ try {
158
+ process.kill(pid, 'SIGKILL');
159
+ await new Promise((resolve) => setTimeout(resolve, 1000));
160
+ clearDaemonPid();
161
+ clearDaemonState();
162
+ console.log(chalk.green('Daemon force killed'));
163
+ return true;
164
+ } catch (error) {
165
+ if (error.code === 'ESRCH') {
166
+ clearDaemonPid();
167
+ clearDaemonState();
168
+ return true;
169
+ }
170
+ throw error;
171
+ }
172
+ }
173
+
174
+ throw new Error('Daemon did not stop within timeout. Use --force to kill it.');
175
+ }
176
+
177
+ /**
178
+ * Restart the daemon
179
+ * @param {Object} options - Restart options
180
+ * @returns {Promise<Object>} New daemon info
181
+ */
182
+ export async function restartDaemon(options = {}) {
183
+ const state = getDaemonState();
184
+ const port = options.port || state?.port;
185
+
186
+ // Stop existing daemon if running
187
+ if (isDaemonRunning()) {
188
+ await stopDaemon({ force: false, timeout: 10000 });
189
+ // Wait a moment before restarting
190
+ await new Promise((resolve) => setTimeout(resolve, 1000));
191
+ }
192
+
193
+ // Start new daemon
194
+ return startDaemon({ port });
195
+ }
196
+
197
+ /**
198
+ * Get daemon status
199
+ * @returns {Object} Daemon status
200
+ */
201
+ export function getDaemonStatus() {
202
+ const state = getDaemonState();
203
+ const pid = getDaemonPid();
204
+ const running = isDaemonRunning();
205
+
206
+ if (!running && state) {
207
+ // Daemon died, clean up
208
+ clearDaemonPid();
209
+ clearDaemonState();
210
+ return {
211
+ running: false,
212
+ status: 'not_running',
213
+ };
214
+ }
215
+
216
+ if (!state) {
217
+ return {
218
+ running: false,
219
+ status: 'not_running',
220
+ };
221
+ }
222
+
223
+ // Calculate uptime
224
+ let uptime = null;
225
+ if (state.startTime) {
226
+ uptime = Date.now() - new Date(state.startTime).getTime();
227
+ }
228
+
229
+ return {
230
+ running: true,
231
+ status: state.status || 'running',
232
+ pid: state.pid,
233
+ port: state.port,
234
+ startTime: state.startTime,
235
+ uptime,
236
+ userEntityRef: state.userEntityRef,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Format uptime in human readable format
242
+ * @param {number} ms - Uptime in milliseconds
243
+ * @returns {string} Formatted uptime
244
+ */
245
+ export function formatUptime(ms) {
246
+ if (!ms) return 'N/A';
247
+
248
+ const seconds = Math.floor(ms / 1000);
249
+ const minutes = Math.floor(seconds / 60);
250
+ const hours = Math.floor(minutes / 60);
251
+ const days = Math.floor(hours / 24);
252
+
253
+ if (days > 0) {
254
+ return `${days}d ${hours % 24}h ${minutes % 60}m`;
255
+ }
256
+ if (hours > 0) {
257
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
258
+ }
259
+ if (minutes > 0) {
260
+ return `${minutes}m ${seconds % 60}s`;
261
+ }
262
+ return `${seconds}s`;
263
+ }
264
+
265
+ export default {
266
+ startDaemon,
267
+ stopDaemon,
268
+ restartDaemon,
269
+ getDaemonStatus,
270
+ formatUptime,
271
+ };
@@ -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
+ };