@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.
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
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ Copyright (c) 2025 Fenwave (Fenleap). All Rights Reserved.
2
+
3
+ PROPRIETARY SOFTWARE LICENSE
4
+
5
+ This software and associated documentation files (the "Software") are the
6
+ proprietary property of Fenwave (Fenleap) and are protected by copyright law.
7
+
8
+ TERMS AND CONDITIONS:
9
+
10
+ 1. LICENSE GRANT: Fenwave grants authorized customers a limited, non-exclusive,
11
+ non-transferable license to use the Software solely for their internal
12
+ business purposes in connection with Fenwave services.
13
+
14
+ 2. RESTRICTIONS: You may NOT:
15
+ - Copy, modify, or distribute the Software
16
+ - Reverse engineer, decompile, or disassemble the Software
17
+ - Create derivative works based on the Software
18
+ - Sublicense, rent, lease, or lend the Software
19
+ - Use the Software for any purpose other than as authorized by Fenwave
20
+
21
+ 3. OWNERSHIP: Fenwave retains all right, title, and interest in and to the
22
+ Software, including all intellectual property rights.
23
+
24
+ 4. NO WARRANTY: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
25
+
26
+ 5. LIMITATION OF LIABILITY: IN NO EVENT SHALL FENWAVE BE LIABLE FOR ANY
27
+ DAMAGES ARISING FROM THE USE OF THIS SOFTWARE.
28
+
29
+ For licensing inquiries, contact: support@fenwave.com
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # Fenwave Agent
2
+
3
+ The Fenwave Agent is a CLI tool for managing Docker containers and local development environments. It integrates with the Fenwave platform for seamless developer experience.
4
+
5
+ ## Prerequisites
6
+
7
+ - **Docker Desktop** (version 20.10 or later)
8
+ - **Node.js** (version 20 or later)
9
+ - **Fenwave Platform Access** - Valid credentials from your organization
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install -g @fenwave/agent
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### 1. Initialize the Agent
20
+
21
+ Get your registration token from the Fenwave platform, then run:
22
+
23
+ ```bash
24
+ fenwave init --token <your-registration-token>
25
+ ```
26
+
27
+ ### 2. Authenticate
28
+
29
+ ```bash
30
+ fenwave login
31
+ ```
32
+
33
+ This opens a browser for authentication. After successful auth, you'll be prompted to start the agent.
34
+
35
+ ### 3. Start the Agent
36
+
37
+ **Background mode (recommended for production):**
38
+ ```bash
39
+ fenwave service start
40
+ ```
41
+
42
+ **Foreground mode (for development):**
43
+ ```bash
44
+ fenwave service run
45
+ ```
46
+
47
+ ### 4. Access the Dashboard
48
+
49
+ Open your browser to `http://localhost:3003`
50
+
51
+ ## Commands
52
+
53
+ ### Authentication
54
+
55
+ | Command | Description |
56
+ |---------|-------------|
57
+ | `fenwave login` | Authenticate with Backstage (does not start agent) |
58
+ | `fenwave logout` | Clear session and stop agent if running |
59
+ | `fenwave status` | Check agent and registration status |
60
+
61
+ ### Service Management
62
+
63
+ | Command | Description |
64
+ |---------|-------------|
65
+ | `fenwave service start` | Start agent as background daemon |
66
+ | `fenwave service run` | Run agent in foreground (for development) |
67
+ | `fenwave service stop` | Stop the running daemon |
68
+ | `fenwave service restart` | Restart the daemon |
69
+ | `fenwave service status` | Show daemon status (PID, port, uptime) |
70
+ | `fenwave service logs` | View daemon logs |
71
+ | `fenwave service logs -f` | Follow daemon logs in real-time |
72
+
73
+ ### Setup & Configuration
74
+
75
+ | Command | Description |
76
+ |---------|-------------|
77
+ | `fenwave init` | Interactive setup wizard |
78
+ | `fenwave register` | Register device with Backstage |
79
+ | `fenwave rotate-credentials` | Rotate device credentials |
80
+ | `fenwave uninstall` | Uninstall agent and clean up |
81
+
82
+ ### Container Management
83
+
84
+ | Command | Description |
85
+ |---------|-------------|
86
+ | `fenwave containers` | List containers |
87
+ | `fenwave start <id>` | Start container(s) |
88
+ | `fenwave stop <id>` | Stop container(s) |
89
+ | `fenwave restart <id>` | Restart container(s) |
90
+ | `fenwave rm <id>` | Remove container(s) |
91
+ | `fenwave logs <id>` | View container logs |
92
+
93
+ ### Image & Volume Management
94
+
95
+ | Command | Description |
96
+ |---------|-------------|
97
+ | `fenwave images` | List images |
98
+ | `fenwave pull <tag>` | Pull image(s) |
99
+ | `fenwave rmi <id>` | Remove image(s) |
100
+ | `fenwave volumes` | List volumes |
101
+ | `fenwave volume-create <name>` | Create volume(s) |
102
+ | `fenwave volume-rm <name>` | Remove volume(s) |
103
+
104
+ ### Other Commands
105
+
106
+ | Command | Description |
107
+ |---------|-------------|
108
+ | `fenwave info` | Display agent information |
109
+ | `fenwave registries` | List connected registries |
110
+ | `fenwave local-env` | Manage local-env container |
111
+ | `fenwave --help` | Show all available commands |
112
+
113
+ ## Architecture
114
+
115
+ The agent runs as a background daemon with the following features:
116
+
117
+ - **Port Fallback**: Automatically finds available port (3001-3010)
118
+ - **Single Instance**: Prevents multiple daemon instances
119
+ - **Heartbeat**: WebSocket health checks every 30 seconds
120
+ - **Graceful Shutdown**: Connection draining with client notification
121
+ - **Log Rotation**: Automatic rotation at 10MB, keeps 5 files
122
+
123
+ ### File Locations
124
+
125
+ ```
126
+ ~/.fenwave/
127
+ ├── daemon/
128
+ │ ├── state.json # Daemon state (PID, port, status)
129
+ │ ├── agent.pid # Process ID file
130
+ │ └── agent.lock # Instance lock file
131
+ ├── logs/
132
+ │ ├── agent.log # Main log file
133
+ │ └── agent.error.log # Error log file
134
+ ├── session/
135
+ │ └── config.json # Session credentials
136
+ ├── config/
137
+ │ └── agent.json # Agent configuration
138
+ └── ws-token # WebSocket auth token
139
+ ```
140
+
141
+ ## Dashboard Features
142
+
143
+ - Container management (start, stop, restart, logs)
144
+ - Docker image management
145
+ - Volume management
146
+ - Registry connections
147
+ - Real-time metrics
148
+ - Terminal access to containers
149
+
150
+ ## Troubleshooting
151
+
152
+ **Agent won't start?**
153
+ - Ensure Docker Desktop is running
154
+ - Check if another instance is running: `fenwave service status`
155
+ - Check logs: `fenwave service logs`
156
+ - Try stopping and starting: `fenwave service stop && fenwave service start`
157
+
158
+ **Port already in use?**
159
+ - The agent will automatically try ports 3001-3010
160
+ - Check which port is being used: `fenwave service status`
161
+
162
+ **Authentication issues?**
163
+ - Run `fenwave logout` then `fenwave login`
164
+ - Ensure Backstage is running and accessible
165
+
166
+ **View logs for debugging:**
167
+ ```bash
168
+ fenwave service logs -f
169
+ ```
170
+
171
+ **Need help?**
172
+ - Run `fenwave --help` for command options
173
+ - Contact your Fenwave administrator
174
+
175
+ ## Development
176
+
177
+ ### Local Testing
178
+
179
+ To test the agent locally before publishing:
180
+
181
+ ```bash
182
+ # Navigate to the agent directory
183
+ cd fenwave-agent
184
+
185
+ # Create a global symlink to this local package
186
+ npm link
187
+
188
+ # Now 'fenwave' command will use your local code
189
+ fenwave init --token YOUR_TOKEN --backend-url http://localhost:7007
190
+ ```
191
+
192
+ When you're done testing:
193
+
194
+ ```bash
195
+ # Remove the global symlink
196
+ npm unlink -g @fenwave/agent
197
+ ```
198
+
199
+ ### Running in Development Mode
200
+
201
+ ```bash
202
+ # Run agent in foreground with debug output
203
+ fenwave --debug service run
204
+ ```
205
+
206
+ ### Publishing
207
+
208
+ To publish a new version:
209
+
210
+ ```bash
211
+ # Bump version (patch/minor/major)
212
+ npm version patch
213
+
214
+ # Publish to npm
215
+ npm publish --access public
216
+ ```
217
+
218
+ ## License
219
+
220
+ Copyright 2025 Fenwave. All rights reserved. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,97 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Authenticating...</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
10
+ text-align: center;
11
+ padding: 50px;
12
+ background: #f5f5f5;
13
+ }
14
+ .container {
15
+ background: white;
16
+ padding: 40px;
17
+ border-radius: 8px;
18
+ max-width: 400px;
19
+ margin: 0 auto;
20
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
21
+ }
22
+ .spinner {
23
+ border: 3px solid #f3f3f3;
24
+ border-top: 3px solid #3498db;
25
+ border-radius: 50%;
26
+ width: 40px;
27
+ height: 40px;
28
+ animation: spin 1s linear infinite;
29
+ margin: 20px auto;
30
+ }
31
+ @keyframes spin {
32
+ 0% {
33
+ transform: rotate(0deg);
34
+ }
35
+ 100% {
36
+ transform: rotate(360deg);
37
+ }
38
+ }
39
+ .error {
40
+ color: #dc2626;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div class="container">
46
+ <div class="spinner" id="spinner"></div>
47
+ <h2 id="status">Processing authentication...</h2>
48
+ <p id="message">Please wait while we complete the authentication.</p>
49
+ </div>
50
+ <script>
51
+ (function () {
52
+ // Read credentials from URL fragment
53
+ let jwt, entityRef;
54
+
55
+ const hash = window.location.hash.substring(1);
56
+ if (hash) {
57
+ const hashParams = new URLSearchParams(hash);
58
+ jwt = hashParams.get("jwt");
59
+ entityRef = hashParams.get("entityRef");
60
+ }
61
+
62
+ if (!jwt || !entityRef) {
63
+ document.getElementById("spinner").style.display = "none";
64
+ document.getElementById("status").className = "error";
65
+ document.getElementById("status").textContent =
66
+ "Authentication Failed";
67
+ document.getElementById("message").textContent =
68
+ "Missing credentials. Please try again.";
69
+ return;
70
+ }
71
+
72
+ // POST credentials to the server (keeps them out of URL/logs)
73
+ fetch("/auth-complete", {
74
+ method: "POST",
75
+ headers: { "Content-Type": "application/json" },
76
+ body: JSON.stringify({ jwt, entityRef }),
77
+ })
78
+ .then((response) => response.json())
79
+ .then((data) => {
80
+ if (data.success) {
81
+ // Redirect to success page
82
+ window.location.href = data.redirectUrl;
83
+ } else {
84
+ throw new Error(data.error || "Authentication failed");
85
+ }
86
+ })
87
+ .catch((error) => {
88
+ document.getElementById("spinner").style.display = "none";
89
+ document.getElementById("status").className = "error";
90
+ document.getElementById("status").textContent =
91
+ "Authentication Failed";
92
+ document.getElementById("message").textContent = error.message;
93
+ });
94
+ })();
95
+ </script>
96
+ </body>
97
+ </html>
package/auth.js ADDED
@@ -0,0 +1,276 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import axios from 'axios';
5
+ import chalk from 'chalk';
6
+ import dotenv from 'dotenv';
7
+ dotenv.config();
8
+
9
+ const AGENT_ROOT_DIR = path.join(
10
+ os.homedir(),
11
+ process.env.AGENT_ROOT_DIR || '.fenwave'
12
+ );
13
+ const SESSION_DIR = process.env.SESSION_DIR || 'session';
14
+ const SESSION_FILE = process.env.SESSION_FILE || 'config.json';
15
+ const CONFIG_FILE = path.join(AGENT_ROOT_DIR, SESSION_DIR, SESSION_FILE);
16
+
17
+ /**
18
+ * Ensures the configuration directory exists
19
+ */
20
+ function ensureConfigDirectory() {
21
+ const sessionDir = path.dirname(CONFIG_FILE);
22
+ if (!fs.existsSync(sessionDir)) {
23
+ fs.mkdirSync(sessionDir, { recursive: true, mode: 0o700 });
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Saves session data to local config file
29
+ * @param {string} token - Session token
30
+ * @param {string} expiresAt - Expiration timestamp
31
+ * @param {string} userEntityRef - User entity reference
32
+ * @param {string} backendUrl - Optional backend URL to save
33
+ */
34
+ function saveSession(token, expiresAt, userEntityRef, backendUrl = null) {
35
+ ensureConfigDirectory();
36
+ const config = {
37
+ token,
38
+ expiresAt,
39
+ userEntityRef,
40
+ };
41
+
42
+ // Preserve existing backend URL or save new one
43
+ if (backendUrl) {
44
+ config.backendUrl = backendUrl;
45
+ } else {
46
+ const existingConfig = loadSession();
47
+ if (existingConfig && existingConfig.backendUrl) {
48
+ config.backendUrl = existingConfig.backendUrl;
49
+ }
50
+ }
51
+
52
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), {
53
+ mode: 0o600,
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Loads session data from local config file
59
+ * @returns {Object|null} Session data or null if not found
60
+ */
61
+ function loadSession() {
62
+ if (!fs.existsSync(CONFIG_FILE)) {
63
+ return null;
64
+ }
65
+ try {
66
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
67
+ return config;
68
+ } catch (error) {
69
+ console.error(chalk.red('❌ Error loading session config:'), error.message);
70
+ return null;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Checks if a session is still valid (not expired)
76
+ * @param {Object} session - Session data
77
+ * @returns {boolean} - True if session is valid
78
+ */
79
+ function isSessionValid(session) {
80
+ if (!session || !session.token || !session.expiresAt) {
81
+ return false;
82
+ }
83
+ const now = new Date();
84
+ const expiresAt = new Date(session.expiresAt);
85
+ return now < expiresAt;
86
+ }
87
+
88
+ /**
89
+ * Handles session expiry by clearing session and exiting process
90
+ * @param {string} message - Optional message to display
91
+ * @param {Object} server - Optional WebSocket server to close
92
+ * @param {Object} wss - Optional WebSocket server instance to close
93
+ */
94
+ function handleSessionExpiry(
95
+ message = 'Session has expired!',
96
+ server = null,
97
+ wss = null
98
+ ) {
99
+ console.log(chalk.red(`❌ ${message}`));
100
+ console.log(chalk.red('🔒 Fenwave Agent shutting down gracefully...'));
101
+
102
+ // Close WebSocket connections and server if provided
103
+ if (wss) {
104
+ wss.clients.forEach((client) => {
105
+ client.send(
106
+ JSON.stringify({
107
+ type: 'session_expired',
108
+ message: 'Session has expired. Please re-authenticate.',
109
+ })
110
+ );
111
+ client.close();
112
+ });
113
+ wss.close();
114
+ }
115
+
116
+ if (server) {
117
+ server.close();
118
+ }
119
+
120
+ clearSession();
121
+ console.log(chalk.yellow("💡 Please run 'fenwave login' to authenticate again."));
122
+ process.exit(1);
123
+ }
124
+
125
+ /**
126
+ * Creates a new session with the Fenwave backend
127
+ * @param {string} jwt - Token for authentication
128
+ * @param {string} backendUrl - Backend URL
129
+ * @returns {Object} Session data with token, userEntityRef and expiresAt
130
+ */
131
+ async function createSession(jwt, backendUrl) {
132
+ try {
133
+ const response = await axios.post(
134
+ `${backendUrl}/api/agent-cli/create-session`,
135
+ {},
136
+ {
137
+ headers: {
138
+ Authorization: `Bearer ${jwt}`,
139
+ 'Content-Type': 'application/json',
140
+ },
141
+ }
142
+ );
143
+ console.log(chalk.green('✅ Session created successfully!'));
144
+ const { token, expiresAt } = response.data;
145
+ return { token, expiresAt };
146
+ } catch (error) {
147
+ console.error(
148
+ chalk.red('❌ Failed to create session: '),
149
+ error.response?.data || error.message
150
+ );
151
+ throw error;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Clears the stored session
157
+ */
158
+ function clearSession() {
159
+ if (fs.existsSync(CONFIG_FILE)) {
160
+ fs.unlinkSync(CONFIG_FILE);
161
+ return true;
162
+ }
163
+ return false;
164
+ }
165
+
166
+ /**
167
+ * Sets up a file watcher to monitor session file changes
168
+ * @param {Function} onSessionExpired - Callback function to handle session expiry
169
+ * @param {Object} server - WebSocket server instance
170
+ * @param {Object} wss - WebSocket server instance
171
+ * @returns {Object} File watcher instance
172
+ */
173
+ function setupSessionWatcher(onSessionExpired, server = null, wss = null) {
174
+ let watcher = null;
175
+ let expiryTimer = null;
176
+
177
+ // Set up timer for natural expiry
178
+ const session = loadSession();
179
+ if (session && session.expiresAt) {
180
+ const expiryTime = new Date(session.expiresAt).getTime();
181
+ const currentTime = Date.now();
182
+ const timeUntilExpiry = expiryTime - currentTime;
183
+
184
+ if (timeUntilExpiry > 0) {
185
+ expiryTimer = setTimeout(() => {
186
+ clearSession();
187
+ onSessionExpired('Session expired !', server, wss);
188
+ }, timeUntilExpiry);
189
+ }
190
+ }
191
+
192
+ try {
193
+ // Watch the config file for changes/deletion
194
+ watcher = fs.watch(CONFIG_FILE, (eventType, filename) => {
195
+ // Clear the natural expiry timer since file changed
196
+ if (expiryTimer) {
197
+ clearTimeout(expiryTimer);
198
+ expiryTimer = null;
199
+ }
200
+
201
+ // Check if file was deleted or renamed (session revoked by logout)
202
+ if (eventType === 'rename' || !fs.existsSync(CONFIG_FILE)) {
203
+ if (watcher) {
204
+ watcher.close();
205
+ }
206
+ onSessionExpired('Session revoked !', server, wss);
207
+ return;
208
+ }
209
+ });
210
+ return {
211
+ close: () => {
212
+ if (watcher) watcher.close();
213
+ if (expiryTimer) clearTimeout(expiryTimer);
214
+ },
215
+ };
216
+ } catch (error) {
217
+ // Clear expiry timer if file watcher fails
218
+ if (expiryTimer) {
219
+ clearTimeout(expiryTimer);
220
+ expiryTimer = null;
221
+ }
222
+
223
+ // Fallback to periodic checking if file watching fails
224
+ const intervalId = setInterval(() => {
225
+ if (!fs.existsSync(CONFIG_FILE)) {
226
+ clearInterval(intervalId);
227
+ onSessionExpired('Session revoked !', server, wss);
228
+ return;
229
+ }
230
+
231
+ const currentSession = loadSession();
232
+ if (!currentSession || !isSessionValid(currentSession)) {
233
+ clearInterval(intervalId);
234
+ onSessionExpired('Session expired !', server, wss);
235
+ }
236
+ }, 30 * 1000); // Check every 30 seconds as fallback
237
+
238
+ return { close: () => clearInterval(intervalId) };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Loads backend URL from agent config or environment variable
244
+ * NOTE: This function loads synchronously from the config file
245
+ * @returns {string|undefined} Backend URL
246
+ */
247
+ function loadBackendUrl() {
248
+ // Load directly from config file to avoid circular dependencies
249
+ const configPath = path.join(os.homedir(), '.fenwave', 'config', 'agent.json');
250
+
251
+ // Try to load from config file
252
+ if (fs.existsSync(configPath)) {
253
+ try {
254
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
255
+ if (config.backendUrl) {
256
+ return config.backendUrl;
257
+ }
258
+ } catch (error) {
259
+ // Fall through to env variable
260
+ }
261
+ }
262
+
263
+ // Fall back to environment variable
264
+ return process.env.BACKEND_URL || 'http://localhost:7007';
265
+ }
266
+
267
+ export {
268
+ saveSession,
269
+ loadSession,
270
+ isSessionValid,
271
+ handleSessionExpiry,
272
+ createSession,
273
+ clearSession,
274
+ setupSessionWatcher,
275
+ loadBackendUrl,
276
+ };