@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.
- package/LICENSE +29 -0
- package/README.md +220 -0
- package/auth-callback.html +97 -0
- package/auth.js +276 -0
- package/cli-commands.js +1921 -0
- package/containerManager.js +304 -0
- package/daemon/agentRunner.js +491 -0
- package/daemon/daemonEntry.js +64 -0
- package/daemon/daemonManager.js +266 -0
- package/daemon/logManager.js +227 -0
- package/dist/styles.css +504 -0
- package/docker-actions/apps.js +3913 -0
- package/docker-actions/config-transformer.js +380 -0
- package/docker-actions/containers.js +355 -0
- package/docker-actions/general.js +171 -0
- package/docker-actions/images.js +1128 -0
- package/docker-actions/logs.js +224 -0
- package/docker-actions/metrics.js +270 -0
- package/docker-actions/registry.js +1100 -0
- package/docker-actions/setup-tasks.js +859 -0
- package/docker-actions/terminal.js +247 -0
- package/docker-actions/volumes.js +713 -0
- package/helper-functions.js +193 -0
- package/index.html +83 -0
- package/index.js +341 -0
- package/package.json +82 -0
- package/postcss.config.mjs +5 -0
- package/scripts/release.sh +212 -0
- package/setup/setupWizard.js +403 -0
- package/store/agentSessionStore.js +51 -0
- package/store/agentStore.js +113 -0
- package/store/configStore.js +171 -0
- package/store/daemonStore.js +217 -0
- package/store/deviceCredentialStore.js +107 -0
- package/store/npmTokenStore.js +65 -0
- package/store/registryStore.js +329 -0
- package/store/setupState.js +147 -0
- package/styles.css +1 -0
- package/utils/appLogger.js +223 -0
- package/utils/deviceInfo.js +98 -0
- package/utils/ecrAuth.js +225 -0
- package/utils/encryption.js +112 -0
- package/utils/envSetup.js +41 -0
- package/utils/errorHandler.js +327 -0
- package/utils/portUtils.js +59 -0
- package/utils/prerequisites.js +323 -0
- package/utils/prompts.js +318 -0
- package/utils/ssl-certificates.js +256 -0
- package/websocket-server.js +415 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import packageJson from '../package.json' with { type: 'json' };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Collect device information for registration
|
|
7
|
+
*
|
|
8
|
+
* @returns {Object} Device information
|
|
9
|
+
*/
|
|
10
|
+
export function collectDeviceInfo() {
|
|
11
|
+
const platform = os.platform(); // darwin, linux, win32
|
|
12
|
+
const hostname = os.hostname();
|
|
13
|
+
const arch = os.arch(); // x64, arm64, etc.
|
|
14
|
+
const cpus = os.cpus();
|
|
15
|
+
const totalMemory = os.totalmem();
|
|
16
|
+
const osVersion = os.release();
|
|
17
|
+
|
|
18
|
+
// Get Docker version if available
|
|
19
|
+
let dockerVersion = null;
|
|
20
|
+
try {
|
|
21
|
+
dockerVersion = execSync('docker --version', { encoding: 'utf8' }).trim();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// Docker not installed or not available
|
|
24
|
+
dockerVersion = 'Not available';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Get agent version from package.json
|
|
28
|
+
const agentVersion = packageJson.version;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
hostname,
|
|
32
|
+
platform,
|
|
33
|
+
arch,
|
|
34
|
+
osVersion,
|
|
35
|
+
cpuCount: cpus.length,
|
|
36
|
+
totalMemory: Math.round(totalMemory / (1024 * 1024 * 1024)), // Convert to GB
|
|
37
|
+
dockerVersion,
|
|
38
|
+
agentVersion,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a human-readable device name
|
|
44
|
+
*
|
|
45
|
+
* @returns {string} Device name (e.g., "johns-macbook-darwin")
|
|
46
|
+
*/
|
|
47
|
+
export function generateDeviceName() {
|
|
48
|
+
const hostname = os.hostname()
|
|
49
|
+
.toLowerCase()
|
|
50
|
+
.replace(/[^a-z0-9-]/g, '-') // Replace non-alphanumeric with dash
|
|
51
|
+
.replace(/-+/g, '-') // Replace multiple dashes with single dash
|
|
52
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing dashes
|
|
53
|
+
|
|
54
|
+
const platform = os.platform();
|
|
55
|
+
|
|
56
|
+
return `${hostname}-${platform}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get device metadata for storage
|
|
61
|
+
*
|
|
62
|
+
* @returns {Object} Complete device metadata
|
|
63
|
+
*/
|
|
64
|
+
export async function getDeviceMetadata() {
|
|
65
|
+
const info = collectDeviceInfo();
|
|
66
|
+
const deviceName = generateDeviceName();
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
deviceName,
|
|
70
|
+
platform: info.platform,
|
|
71
|
+
osVersion: info.osVersion,
|
|
72
|
+
agentVersion: info.agentVersion,
|
|
73
|
+
metadata: {
|
|
74
|
+
hostname: info.hostname,
|
|
75
|
+
arch: info.arch,
|
|
76
|
+
cpuCount: info.cpuCount,
|
|
77
|
+
totalMemoryGB: info.totalMemory,
|
|
78
|
+
dockerVersion: info.dockerVersion,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Display device information in a formatted way
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} deviceInfo - Device information object
|
|
87
|
+
*/
|
|
88
|
+
export function displayDeviceInfo(deviceInfo) {
|
|
89
|
+
console.log('\n📱 Device Information:');
|
|
90
|
+
console.log(` Name: ${deviceInfo.deviceName || 'N/A'}`);
|
|
91
|
+
console.log(` Platform: ${deviceInfo.platform}`);
|
|
92
|
+
console.log(` OS Version: ${deviceInfo.osVersion || 'N/A'}`);
|
|
93
|
+
console.log(` Architecture: ${deviceInfo.metadata?.arch || 'N/A'}`);
|
|
94
|
+
console.log(` CPUs: ${deviceInfo.metadata?.cpuCount || 'N/A'}`);
|
|
95
|
+
console.log(` Memory: ${deviceInfo.metadata?.totalMemoryGB || 'N/A'} GB`);
|
|
96
|
+
console.log(` Docker: ${deviceInfo.metadata?.dockerVersion || 'N/A'}`);
|
|
97
|
+
console.log(` Agent Version: ${deviceInfo.agentVersion || 'N/A'}\n`);
|
|
98
|
+
}
|
package/utils/ecrAuth.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECR Authentication Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles requesting temporary AWS ECR credentials from Fenwave
|
|
5
|
+
* and authenticating Docker with ECR.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadDeviceCredential } from '../store/deviceCredentialStore.js';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Request temporary ECR credentials from Fenwave
|
|
16
|
+
*
|
|
17
|
+
* @param {string} fenwaveUrl - Fenwave backend URL
|
|
18
|
+
* @returns {Promise<Object>} ECR credentials object
|
|
19
|
+
*/
|
|
20
|
+
export async function requestECRCredentials(fenwaveUrl) {
|
|
21
|
+
try {
|
|
22
|
+
// Load device credentials
|
|
23
|
+
const deviceCreds = await loadDeviceCredential();
|
|
24
|
+
|
|
25
|
+
if (!deviceCreds || !deviceCreds.deviceId || !deviceCreds.deviceCredential) {
|
|
26
|
+
throw new Error('Device not registered. Please run: fenwave init');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Request credentials from Fenwave
|
|
30
|
+
const url = new URL('/api/agent-cli/ecr-credentials', fenwaveUrl);
|
|
31
|
+
url.searchParams.append('deviceId', deviceCreds.deviceId);
|
|
32
|
+
url.searchParams.append('deviceCredential', deviceCreds.deviceCredential);
|
|
33
|
+
|
|
34
|
+
const response = await fetch(url.toString());
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
38
|
+
throw new Error(`Failed to get ECR credentials: ${error.error || response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const credentials = await response.json();
|
|
42
|
+
|
|
43
|
+
return credentials;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(`Failed to request ECR credentials: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set AWS environment variables for temporary credentials
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} credentials - ECR credentials object
|
|
53
|
+
*/
|
|
54
|
+
export function setAWSCredentials(credentials) {
|
|
55
|
+
process.env.AWS_ACCESS_KEY_ID = credentials.accessKeyId;
|
|
56
|
+
process.env.AWS_SECRET_ACCESS_KEY = credentials.secretAccessKey;
|
|
57
|
+
process.env.AWS_SESSION_TOKEN = credentials.sessionToken;
|
|
58
|
+
process.env.AWS_DEFAULT_REGION = credentials.region;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clear AWS environment variables
|
|
63
|
+
*/
|
|
64
|
+
export function clearAWSCredentials() {
|
|
65
|
+
delete process.env.AWS_ACCESS_KEY_ID;
|
|
66
|
+
delete process.env.AWS_SECRET_ACCESS_KEY;
|
|
67
|
+
delete process.env.AWS_SESSION_TOKEN;
|
|
68
|
+
delete process.env.AWS_DEFAULT_REGION;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Authenticate Docker with ECR using temporary credentials
|
|
73
|
+
*
|
|
74
|
+
* @param {string} fenwaveUrl - Fenwave backend URL
|
|
75
|
+
* @returns {Promise<Object>} Authentication result with registry info
|
|
76
|
+
*/
|
|
77
|
+
export async function authenticateDockerWithECR(fenwaveUrl) {
|
|
78
|
+
try {
|
|
79
|
+
console.log('🔐 Requesting temporary ECR credentials from Fenwave...');
|
|
80
|
+
|
|
81
|
+
// Request credentials
|
|
82
|
+
const credentials = await requestECRCredentials(fenwaveUrl);
|
|
83
|
+
|
|
84
|
+
console.log(`✅ Received temporary credentials (expires: ${credentials.expiration})`);
|
|
85
|
+
|
|
86
|
+
// Set AWS environment variables
|
|
87
|
+
setAWSCredentials(credentials);
|
|
88
|
+
|
|
89
|
+
// Authenticate with ECR
|
|
90
|
+
const ecrUri = `${credentials.accountId}.dkr.ecr.${credentials.region}.amazonaws.com`;
|
|
91
|
+
console.log(`🔑 Authenticating Docker with ECR: ${ecrUri}...`);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Get ECR login password
|
|
95
|
+
const loginPassword = execSync(
|
|
96
|
+
`aws ecr get-login-password --region ${credentials.region}`,
|
|
97
|
+
{ encoding: 'utf-8' }
|
|
98
|
+
).trim();
|
|
99
|
+
|
|
100
|
+
// Login to Docker
|
|
101
|
+
execSync(
|
|
102
|
+
`echo "${loginPassword}" | docker login --username AWS --password-stdin ${ecrUri}`,
|
|
103
|
+
{ stdio: 'pipe' }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
console.log('✅ Successfully authenticated with ECR');
|
|
107
|
+
|
|
108
|
+
// Return registry information
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
registryUri: ecrUri,
|
|
112
|
+
dockerImage: `${ecrUri}/${credentials.ecrRepository}:latest`,
|
|
113
|
+
region: credentials.region,
|
|
114
|
+
accountId: credentials.accountId,
|
|
115
|
+
repository: credentials.ecrRepository,
|
|
116
|
+
expiration: credentials.expiration,
|
|
117
|
+
};
|
|
118
|
+
} catch (dockerError) {
|
|
119
|
+
clearAWSCredentials();
|
|
120
|
+
throw new Error(`Docker authentication failed: ${dockerError.message}`);
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
clearAWSCredentials();
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Pull Docker image from ECR with authentication
|
|
130
|
+
*
|
|
131
|
+
* @param {string} fenwaveUrl - Fenwave backend URL
|
|
132
|
+
* @returns {Promise<Object>} Pull result with image info
|
|
133
|
+
*/
|
|
134
|
+
export async function pullDockerImageFromECR(fenwaveUrl) {
|
|
135
|
+
try {
|
|
136
|
+
// Authenticate with ECR
|
|
137
|
+
const authResult = await authenticateDockerWithECR(fenwaveUrl);
|
|
138
|
+
|
|
139
|
+
console.log(`📥 Pulling Docker image: ${authResult.dockerImage}...`);
|
|
140
|
+
|
|
141
|
+
// Pull the image
|
|
142
|
+
execSync(`docker pull ${authResult.dockerImage}`, { stdio: 'inherit' });
|
|
143
|
+
|
|
144
|
+
console.log('✅ Docker image pulled successfully');
|
|
145
|
+
|
|
146
|
+
// Clear credentials after use
|
|
147
|
+
clearAWSCredentials();
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
dockerImage: authResult.dockerImage,
|
|
152
|
+
registryUri: authResult.registryUri,
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
clearAWSCredentials();
|
|
156
|
+
throw new Error(`Failed to pull Docker image: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if credentials are expired or about to expire
|
|
162
|
+
*
|
|
163
|
+
* @param {string} expirationISO - ISO 8601 expiration timestamp
|
|
164
|
+
* @param {number} bufferMinutes - Minutes before expiration to consider expired (default: 5)
|
|
165
|
+
* @returns {boolean} True if credentials are expired or expiring soon
|
|
166
|
+
*/
|
|
167
|
+
export function areCredentialsExpired(expirationISO, bufferMinutes = 5) {
|
|
168
|
+
const expirationTime = new Date(expirationISO).getTime();
|
|
169
|
+
const bufferMs = bufferMinutes * 60 * 1000;
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
|
|
172
|
+
return now >= (expirationTime - bufferMs);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Cache credentials to disk (optional, for future use)
|
|
177
|
+
*
|
|
178
|
+
* @param {Object} credentials - ECR credentials object
|
|
179
|
+
*/
|
|
180
|
+
export async function cacheCredentials(credentials) {
|
|
181
|
+
const cacheDir = path.join(os.homedir(), '.fenwave', 'cache');
|
|
182
|
+
const cacheFile = path.join(cacheDir, 'ecr-credentials.json');
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
// Create cache directory if it doesn't exist
|
|
186
|
+
if (!fs.existsSync(cacheDir)) {
|
|
187
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Write credentials to cache
|
|
191
|
+
fs.writeFileSync(cacheFile, JSON.stringify(credentials, null, 2), 'utf-8');
|
|
192
|
+
} catch (error) {
|
|
193
|
+
// Ignore cache errors - not critical
|
|
194
|
+
console.warn('⚠️ Failed to cache credentials:', error.message);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Load cached credentials (optional, for future use)
|
|
200
|
+
*
|
|
201
|
+
* @returns {Object|null} Cached credentials or null if not found/expired
|
|
202
|
+
*/
|
|
203
|
+
export async function loadCachedCredentials() {
|
|
204
|
+
const cacheFile = path.join(os.homedir(), '.fenwave', 'cache', 'ecr-credentials.json');
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
if (!fs.existsSync(cacheFile)) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
|
|
212
|
+
|
|
213
|
+
// Check if credentials are expired
|
|
214
|
+
if (areCredentialsExpired(cached.expiration)) {
|
|
215
|
+
// Delete expired cache
|
|
216
|
+
fs.unlinkSync(cacheFile);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return cached;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
// Ignore cache errors
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { networkInterfaces } from 'os';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a machine-specific encryption key
|
|
7
|
+
* Uses hostname and MAC address to create a unique key for this machine
|
|
8
|
+
*
|
|
9
|
+
* @returns {Buffer} Encryption key
|
|
10
|
+
*/
|
|
11
|
+
function getMachineKey() {
|
|
12
|
+
const hostname = os.hostname();
|
|
13
|
+
|
|
14
|
+
// Get first MAC address
|
|
15
|
+
const interfaces = networkInterfaces();
|
|
16
|
+
let macAddress = 'default-mac';
|
|
17
|
+
|
|
18
|
+
for (const name of Object.keys(interfaces)) {
|
|
19
|
+
const iface = interfaces[name];
|
|
20
|
+
if (iface) {
|
|
21
|
+
for (const addr of iface) {
|
|
22
|
+
if (!addr.internal && addr.mac && addr.mac !== '00:00:00:00:00:00') {
|
|
23
|
+
macAddress = addr.mac;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (macAddress !== 'default-mac') break;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create key from hostname + MAC address
|
|
32
|
+
const keyMaterial = `${hostname}-${macAddress}`;
|
|
33
|
+
|
|
34
|
+
// Derive a 32-byte key using SHA-256
|
|
35
|
+
return crypto.createHash('sha256').update(keyMaterial).digest();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Encrypt data using AES-256-GCM with machine-specific key
|
|
40
|
+
*
|
|
41
|
+
* @param {string} plaintext - Data to encrypt
|
|
42
|
+
* @returns {string} Encrypted data (base64 encoded with IV and auth tag)
|
|
43
|
+
*/
|
|
44
|
+
export function encrypt(plaintext) {
|
|
45
|
+
const key = getMachineKey();
|
|
46
|
+
const iv = crypto.randomBytes(16); // 128-bit IV for GCM
|
|
47
|
+
|
|
48
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
49
|
+
|
|
50
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
|
|
51
|
+
encrypted += cipher.final('base64');
|
|
52
|
+
|
|
53
|
+
const authTag = cipher.getAuthTag();
|
|
54
|
+
|
|
55
|
+
// Combine IV + authTag + encrypted data
|
|
56
|
+
const combined = Buffer.concat([
|
|
57
|
+
iv,
|
|
58
|
+
authTag,
|
|
59
|
+
Buffer.from(encrypted, 'base64'),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
return combined.toString('base64');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Decrypt data using AES-256-GCM with machine-specific key
|
|
67
|
+
*
|
|
68
|
+
* @param {string} encryptedData - Encrypted data (base64 encoded)
|
|
69
|
+
* @returns {string} Decrypted plaintext
|
|
70
|
+
*/
|
|
71
|
+
export function decrypt(encryptedData) {
|
|
72
|
+
const key = getMachineKey();
|
|
73
|
+
const combined = Buffer.from(encryptedData, 'base64');
|
|
74
|
+
|
|
75
|
+
// Extract IV (first 16 bytes), authTag (next 16 bytes), and encrypted data
|
|
76
|
+
const iv = combined.slice(0, 16);
|
|
77
|
+
const authTag = combined.slice(16, 32);
|
|
78
|
+
const encrypted = combined.slice(32);
|
|
79
|
+
|
|
80
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
81
|
+
decipher.setAuthTag(authTag);
|
|
82
|
+
|
|
83
|
+
let decrypted = decipher.update(encrypted, undefined, 'utf8');
|
|
84
|
+
decrypted += decipher.final('utf8');
|
|
85
|
+
|
|
86
|
+
return decrypted;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Simple hash function for data integrity
|
|
91
|
+
*
|
|
92
|
+
* @param {string} data - Data to hash
|
|
93
|
+
* @returns {string} SHA-256 hash (hex)
|
|
94
|
+
*/
|
|
95
|
+
export function hash(data) {
|
|
96
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Verify if encrypted data can be decrypted (test encryption key)
|
|
101
|
+
*
|
|
102
|
+
* @param {string} encryptedData - Encrypted data to test
|
|
103
|
+
* @returns {boolean} True if decryption succeeds
|
|
104
|
+
*/
|
|
105
|
+
export function canDecrypt(encryptedData) {
|
|
106
|
+
try {
|
|
107
|
+
decrypt(encryptedData);
|
|
108
|
+
return true;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ensures environment files exist with default values
|
|
7
|
+
* Creates agent config in ~/.fenwave directory (user's home)
|
|
8
|
+
* Note: DevApp runs as a Docker container from GHCR, no local .env needed
|
|
9
|
+
*
|
|
10
|
+
* @param {string} agentDir - Path to the agent directory (used for development only)
|
|
11
|
+
* @param {boolean} showFoundMessages - Whether to show messages when files already exist (default: false)
|
|
12
|
+
*/
|
|
13
|
+
export function ensureEnvironmentFiles(agentDir, showFoundMessages = false) {
|
|
14
|
+
// Use ~/.fenwave for agent configuration (works for both global install and development)
|
|
15
|
+
const fenwareConfigDir = path.join(os.homedir(), ".fenwave");
|
|
16
|
+
|
|
17
|
+
// Ensure ~/.fenwave directory exists
|
|
18
|
+
if (!fs.existsSync(fenwareConfigDir)) {
|
|
19
|
+
fs.mkdirSync(fenwareConfigDir, { recursive: true, mode: 0o700 });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check and create .env.agent for agent if not exists
|
|
23
|
+
const agentEnvPath = path.join(fenwareConfigDir, ".env.agent");
|
|
24
|
+
if (!fs.existsSync(agentEnvPath)) {
|
|
25
|
+
const defaultAgentEnv = `BACKEND_URL=http://localhost:7007
|
|
26
|
+
FRONTEND_URL=http://localhost:3000
|
|
27
|
+
WS_PORT=3001
|
|
28
|
+
CONTAINER_PORT=3003
|
|
29
|
+
AGENT_ROOT_DIR=.fenwave
|
|
30
|
+
REGISTRIES_DIR=registries
|
|
31
|
+
AUTH_TIMEOUT_MS=60000
|
|
32
|
+
`;
|
|
33
|
+
fs.writeFileSync(agentEnvPath, defaultAgentEnv, { mode: 0o600 });
|
|
34
|
+
} else if (showFoundMessages) {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Note: DevApp runs as a Docker container from GHCR with environment variables
|
|
38
|
+
// passed via docker run -e flags. No local .env file needed.
|
|
39
|
+
|
|
40
|
+
return agentEnvPath;
|
|
41
|
+
}
|