@fenwave/agent 1.1.0
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/.claude/settings.local.json +11 -0
- package/Dockerfile +12 -0
- package/LICENSE +29 -0
- package/README.md +434 -0
- package/auth.js +276 -0
- package/cli-commands.js +1185 -0
- package/containerManager.js +385 -0
- package/convert-to-esm.sh +62 -0
- package/docker-actions/apps.js +3256 -0
- package/docker-actions/config-transformer.js +380 -0
- package/docker-actions/containers.js +346 -0
- package/docker-actions/general.js +171 -0
- package/docker-actions/images.js +1128 -0
- package/docker-actions/logs.js +188 -0
- package/docker-actions/metrics.js +270 -0
- package/docker-actions/registry.js +1100 -0
- package/docker-actions/terminal.js +247 -0
- package/docker-actions/volumes.js +696 -0
- package/helper-functions.js +193 -0
- package/index.html +60 -0
- package/index.js +988 -0
- package/package.json +49 -0
- package/setup/setupWizard.js +499 -0
- package/store/agentSessionStore.js +51 -0
- package/store/agentStore.js +113 -0
- package/store/configStore.js +174 -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/utils/deviceInfo.js +98 -0
- package/utils/ecrAuth.js +225 -0
- package/utils/encryption.js +112 -0
- package/utils/envSetup.js +54 -0
- package/utils/errorHandler.js +327 -0
- package/utils/prerequisites.js +323 -0
- package/utils/prompts.js +318 -0
- package/websocket-server.js +364 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
|
|
3
|
+
// Format container uptime in a human-readable format
|
|
4
|
+
const formatUptime = (ms) => {
|
|
5
|
+
const seconds = Math.floor(ms / 1000);
|
|
6
|
+
const minutes = Math.floor(seconds / 60);
|
|
7
|
+
const hours = Math.floor(minutes / 60);
|
|
8
|
+
const days = Math.floor(hours / 24);
|
|
9
|
+
|
|
10
|
+
if (days > 0) return `${days} day${days > 1 ? 's' : ''}`;
|
|
11
|
+
if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''}`;
|
|
12
|
+
if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''}`;
|
|
13
|
+
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Format image size to human-readable format
|
|
17
|
+
const formatSize = (size) => {
|
|
18
|
+
const kb = size / 1000;
|
|
19
|
+
if (kb < 1000) return `${Math.round(kb)} KB`;
|
|
20
|
+
|
|
21
|
+
const mb = size / 1_000_000;
|
|
22
|
+
if (mb < 1000) return `${Math.round(mb)} MB`;
|
|
23
|
+
|
|
24
|
+
const gb = size / 1_000_000_000;
|
|
25
|
+
return `${gb.toFixed(2)} GB`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Format image creation time to relative time
|
|
29
|
+
const formatCreatedTime = (created) => {
|
|
30
|
+
const now = Date.now() / 1000;
|
|
31
|
+
const diff = now - created;
|
|
32
|
+
|
|
33
|
+
const minutes = Math.floor(diff / 60);
|
|
34
|
+
const hours = Math.floor(minutes / 60);
|
|
35
|
+
const days = Math.floor(hours / 24);
|
|
36
|
+
const months = Math.floor(days / 30);
|
|
37
|
+
const years = Math.floor(months / 12);
|
|
38
|
+
|
|
39
|
+
if (years > 0) return `${years} year${years > 1 ? 's' : ''} ago`;
|
|
40
|
+
if (months > 0) return `${months} month${months > 1 ? 's' : ''} ago`;
|
|
41
|
+
if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago`;
|
|
42
|
+
if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
|
43
|
+
if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
|
44
|
+
return 'Just now';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Parse log line to extract timestamp, level, and message
|
|
48
|
+
function parseLogLine(line, containerId) {
|
|
49
|
+
let rawLine = line.toString();
|
|
50
|
+
|
|
51
|
+
// Docker logs can contain binary headers (8 bytes) that need to be stripped
|
|
52
|
+
// The first byte indicates the stream type (1=stdout, 2=stderr)
|
|
53
|
+
// Bytes 1-3 are padding, bytes 4-7 contain the length
|
|
54
|
+
if (rawLine.length > 8) {
|
|
55
|
+
const firstByte = rawLine.charCodeAt(0);
|
|
56
|
+
// Check if this looks like a Docker log header
|
|
57
|
+
if (firstByte === 1 || firstByte === 2) {
|
|
58
|
+
// Skip the 8-byte header
|
|
59
|
+
rawLine = rawLine.substring(8);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let message = rawLine;
|
|
64
|
+
let timestamp = new Date().toISOString();
|
|
65
|
+
let level = 'INFO';
|
|
66
|
+
const service = 'container';
|
|
67
|
+
let isEmptyLine = false;
|
|
68
|
+
|
|
69
|
+
// Check if this is an empty line (only whitespace or completely empty)
|
|
70
|
+
if (rawLine.trim() === '') {
|
|
71
|
+
isEmptyLine = true;
|
|
72
|
+
message = ''; // Keep as empty for empty lines
|
|
73
|
+
} else {
|
|
74
|
+
// Extract Docker timestamp if present (format: 2025-09-12T14:44:10.598127086Z)
|
|
75
|
+
const timestampMatch = message.match(
|
|
76
|
+
/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s*(.*)$/
|
|
77
|
+
);
|
|
78
|
+
if (timestampMatch) {
|
|
79
|
+
timestamp = timestampMatch[1];
|
|
80
|
+
message = timestampMatch[2] || ''; // This is the actual log content after the Docker timestamp
|
|
81
|
+
|
|
82
|
+
// If after extracting timestamp, message is empty, mark as empty line
|
|
83
|
+
if (message.trim() === '') {
|
|
84
|
+
isEmptyLine = true;
|
|
85
|
+
message = '';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Clean up any remaining malformed timestamp artifacts
|
|
90
|
+
message = message.replace(
|
|
91
|
+
/^-?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s*/,
|
|
92
|
+
''
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Try to detect log level from content (only for non-empty lines)
|
|
96
|
+
if (!isEmptyLine) {
|
|
97
|
+
const lowerMessage = message.toLowerCase();
|
|
98
|
+
if (
|
|
99
|
+
lowerMessage.includes('"s":"e"') ||
|
|
100
|
+
lowerMessage.includes('error') ||
|
|
101
|
+
lowerMessage.includes('err') ||
|
|
102
|
+
lowerMessage.includes('fatal')
|
|
103
|
+
) {
|
|
104
|
+
level = 'ERROR';
|
|
105
|
+
} else if (
|
|
106
|
+
lowerMessage.includes('"s":"w"') ||
|
|
107
|
+
lowerMessage.includes('warn')
|
|
108
|
+
) {
|
|
109
|
+
level = 'WARN';
|
|
110
|
+
} else if (
|
|
111
|
+
lowerMessage.includes('"s":"d"') ||
|
|
112
|
+
lowerMessage.includes('debug')
|
|
113
|
+
) {
|
|
114
|
+
level = 'DEBUG';
|
|
115
|
+
} else if (
|
|
116
|
+
lowerMessage.includes('"s":"i"') ||
|
|
117
|
+
lowerMessage.includes('info') ||
|
|
118
|
+
lowerMessage.includes('log:')
|
|
119
|
+
) {
|
|
120
|
+
level = 'INFO';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
id: `log-${containerId}-${Date.now()}-${Math.random()
|
|
127
|
+
.toString(36)
|
|
128
|
+
.substring(2, 9)}`,
|
|
129
|
+
timestamp,
|
|
130
|
+
level,
|
|
131
|
+
service,
|
|
132
|
+
message,
|
|
133
|
+
isEmptyLine, // Add flag to identify empty lines
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Parse size strings like "10 GB" to bytes
|
|
138
|
+
function parseSize(sizeStr) {
|
|
139
|
+
if (!sizeStr || typeof sizeStr !== 'string') return 0;
|
|
140
|
+
|
|
141
|
+
const match = sizeStr.match(/^(\d+(\.\d+)?)\s*([KMGT]?B)$/);
|
|
142
|
+
if (!match) return 0;
|
|
143
|
+
|
|
144
|
+
const size = Number.parseFloat(match[1]);
|
|
145
|
+
const unit = match[3];
|
|
146
|
+
|
|
147
|
+
switch (unit) {
|
|
148
|
+
case 'B':
|
|
149
|
+
return size;
|
|
150
|
+
case 'KB':
|
|
151
|
+
return size * 1024;
|
|
152
|
+
case 'MB':
|
|
153
|
+
return size * 1024 * 1024;
|
|
154
|
+
case 'GB':
|
|
155
|
+
return size * 1024 * 1024 * 1024;
|
|
156
|
+
case 'TB':
|
|
157
|
+
return size * 1024 * 1024 * 1024 * 1024;
|
|
158
|
+
default:
|
|
159
|
+
return size;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Simple CPU usage estimation without native modules
|
|
164
|
+
function estimateCpuUsage() {
|
|
165
|
+
try {
|
|
166
|
+
const cpus = os.cpus();
|
|
167
|
+
let idle = 0;
|
|
168
|
+
let total = 0;
|
|
169
|
+
|
|
170
|
+
for (const cpu of cpus) {
|
|
171
|
+
for (const type in cpu.times) {
|
|
172
|
+
total += cpu.times[type];
|
|
173
|
+
}
|
|
174
|
+
idle += cpu.times.idle;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Calculate CPU usage as percentage of non-idle time
|
|
178
|
+
const usage = 100 - (idle / total) * 100;
|
|
179
|
+
return Math.round(usage * 100) / 100;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Error estimating CPU usage:', error.message);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
formatUptime,
|
|
188
|
+
formatSize,
|
|
189
|
+
formatCreatedTime,
|
|
190
|
+
parseLogLine,
|
|
191
|
+
parseSize,
|
|
192
|
+
estimateCpuUsage,
|
|
193
|
+
};
|
package/index.html
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Authorization Successful</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<style>
|
|
9
|
+
@keyframes fadeIn {
|
|
10
|
+
from {
|
|
11
|
+
opacity: 0;
|
|
12
|
+
}
|
|
13
|
+
to {
|
|
14
|
+
opacity: 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
.fade-in {
|
|
18
|
+
animation: fadeIn 0.5s ease-in-out;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body class="bg-gray-50 min-h-screen">
|
|
23
|
+
<div class="flex justify-center items-center min-h-[80vh]">
|
|
24
|
+
<div
|
|
25
|
+
class="fade-in max-w-sm mx-auto mt-8 p-8 text-center bg-white rounded-lg shadow-lg"
|
|
26
|
+
>
|
|
27
|
+
<!-- Success Icon Circle -->
|
|
28
|
+
<div
|
|
29
|
+
class="w-16 h-16 mx-auto mb-6 flex items-center justify-center rounded-full bg-green-500 text-white"
|
|
30
|
+
>
|
|
31
|
+
<svg
|
|
32
|
+
class="w-8 h-8"
|
|
33
|
+
fill="currentColor"
|
|
34
|
+
viewBox="0 0 20 20"
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
>
|
|
37
|
+
<path
|
|
38
|
+
fill-rule="evenodd"
|
|
39
|
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
40
|
+
clip-rule="evenodd"
|
|
41
|
+
></path>
|
|
42
|
+
</svg>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- Main Heading -->
|
|
46
|
+
<h2 class="text-2xl font-medium text-gray-900 mb-4">
|
|
47
|
+
Authorization Successful
|
|
48
|
+
</h2>
|
|
49
|
+
|
|
50
|
+
<!-- Description Text -->
|
|
51
|
+
<p class="text-base text-gray-600 mb-8">
|
|
52
|
+
Your agent has been successfully authorized.
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<!-- Footer Text -->
|
|
56
|
+
<p class="text-sm text-gray-500">You may now return to your CLI.</p>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</body>
|
|
60
|
+
</html>
|