@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.
- package/LICENSE +29 -0
- package/README.md +220 -0
- package/auth-callback.html +97 -0
- package/auth.js +276 -0
- package/cli-commands.js +1923 -0
- package/containerManager.js +304 -0
- package/daemon/agentRunner.js +429 -0
- package/daemon/daemonEntry.js +64 -0
- package/daemon/daemonManager.js +271 -0
- package/daemon/logManager.js +227 -0
- package/dist/styles.css +504 -0
- package/docker-actions/apps.js +3938 -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 +696 -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 +44 -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,429 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
+
|
|
9
|
+
import { setupWebSocketServer, readWsToken } from '../websocket-server.js';
|
|
10
|
+
import containerManager from '../containerManager.js';
|
|
11
|
+
import registryStore from '../store/registryStore.js';
|
|
12
|
+
import agentStore from '../store/agentStore.js';
|
|
13
|
+
import { loadConfig } from '../store/configStore.js';
|
|
14
|
+
import { initializeAgentStartTime } from '../docker-actions/general.js';
|
|
15
|
+
import { checkAppHasBeenRun } from '../docker-actions/apps.js';
|
|
16
|
+
import {
|
|
17
|
+
loadSession,
|
|
18
|
+
isSessionValid,
|
|
19
|
+
handleSessionExpiry,
|
|
20
|
+
setupSessionWatcher,
|
|
21
|
+
} from '../auth.js';
|
|
22
|
+
import { findAvailablePort } from '../utils/portUtils.js';
|
|
23
|
+
import {
|
|
24
|
+
saveDaemonState,
|
|
25
|
+
updateDaemonState,
|
|
26
|
+
clearDaemonState,
|
|
27
|
+
saveDaemonPid,
|
|
28
|
+
clearDaemonPid,
|
|
29
|
+
acquireLock,
|
|
30
|
+
releaseLock,
|
|
31
|
+
} from '../store/daemonStore.js';
|
|
32
|
+
import { createLogger, writeLog } from './logManager.js';
|
|
33
|
+
|
|
34
|
+
// Load configuration
|
|
35
|
+
const config = loadConfig();
|
|
36
|
+
const BACKEND_URL = config.backendUrl;
|
|
37
|
+
const CONTAINER_PORT = config.containerPort;
|
|
38
|
+
|
|
39
|
+
// Store active connections
|
|
40
|
+
const clients = new Map();
|
|
41
|
+
|
|
42
|
+
// Server instances for cleanup
|
|
43
|
+
let serverInstances = null;
|
|
44
|
+
|
|
45
|
+
// Logger for daemon mode
|
|
46
|
+
const logger = createLogger();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Log message to both console and file
|
|
50
|
+
*/
|
|
51
|
+
function log(message, level = 'info') {
|
|
52
|
+
const coloredMessage =
|
|
53
|
+
level === 'error'
|
|
54
|
+
? chalk.red(message)
|
|
55
|
+
: level === 'warn'
|
|
56
|
+
? chalk.yellow(message)
|
|
57
|
+
: chalk.blue(message);
|
|
58
|
+
|
|
59
|
+
console.log(coloredMessage);
|
|
60
|
+
writeLog(message, level);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Acknowledge a version_published event
|
|
65
|
+
*/
|
|
66
|
+
async function acknowledgeVersionPublishedEvent(sessionToken, eventId, changeId) {
|
|
67
|
+
try {
|
|
68
|
+
await axios.post(
|
|
69
|
+
`${BACKEND_URL}/api/agent-cli/acknowledge-event`,
|
|
70
|
+
{
|
|
71
|
+
token: sessionToken,
|
|
72
|
+
eventId: eventId,
|
|
73
|
+
changeId: changeId,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
timeout: 5000,
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
log(`Failed to acknowledge event ${changeId}: ${error.message}`, 'warn');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Poll for app update events and broadcast to connected clients
|
|
87
|
+
*/
|
|
88
|
+
async function pollAppUpdates(sessionToken, wss) {
|
|
89
|
+
try {
|
|
90
|
+
const response = await axios.post(
|
|
91
|
+
`${BACKEND_URL}/api/agent-cli/poll-events`,
|
|
92
|
+
{ token: sessionToken },
|
|
93
|
+
{
|
|
94
|
+
headers: { 'Content-Type': 'application/json' },
|
|
95
|
+
timeout: 10000,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (response.data?.events?.length > 0) {
|
|
100
|
+
const events = response.data.events;
|
|
101
|
+
let broadcastedCount = 0;
|
|
102
|
+
|
|
103
|
+
for (const event of events) {
|
|
104
|
+
const eventType = event.event_type || 'blueprint_changed';
|
|
105
|
+
|
|
106
|
+
if (eventType === 'blueprint_changed') {
|
|
107
|
+
const hasBeenRun = await checkAppHasBeenRun(event.app_name);
|
|
108
|
+
if (!hasBeenRun) {
|
|
109
|
+
acknowledgeVersionPublishedEvent(sessionToken, event.id, event.change_id);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
broadcastedCount++;
|
|
115
|
+
|
|
116
|
+
const message = JSON.stringify({
|
|
117
|
+
type: eventType,
|
|
118
|
+
appId: event.app_id,
|
|
119
|
+
fwId: event.app_id,
|
|
120
|
+
appName: event.app_name,
|
|
121
|
+
version: event.version,
|
|
122
|
+
changeId: event.change_id,
|
|
123
|
+
timestamp: event.created_at,
|
|
124
|
+
actor: event.actor,
|
|
125
|
+
changedAt: event.created_at,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
wss.clients.forEach((client) => {
|
|
129
|
+
if (client.readyState === 1) {
|
|
130
|
+
client.send(message);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (eventType === 'version_published') {
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
acknowledgeVersionPublishedEvent(sessionToken, event.id, event.change_id);
|
|
137
|
+
}, 2000);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (broadcastedCount > 0) {
|
|
142
|
+
log(
|
|
143
|
+
`Received ${broadcastedCount} app update ${broadcastedCount === 1 ? "event" : "events"}`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error.response?.status !== 401) {
|
|
149
|
+
const isConnRefused =
|
|
150
|
+
error.message?.includes('ECONNREFUSED') || error.code === 'ECONNREFUSED';
|
|
151
|
+
|
|
152
|
+
if (isConnRefused) {
|
|
153
|
+
log('App update polling error: Please ensure Fenwave is running.', 'error');
|
|
154
|
+
} else {
|
|
155
|
+
log(`App update polling error: ${error.message}`, 'error');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Start periodic polling for app updates
|
|
163
|
+
*/
|
|
164
|
+
function startAppUpdatePolling(sessionToken, wss) {
|
|
165
|
+
const pollInterval = setInterval(() => {
|
|
166
|
+
pollAppUpdates(sessionToken, wss);
|
|
167
|
+
}, 30000);
|
|
168
|
+
|
|
169
|
+
log('Started polling for app updates (every 30 seconds)');
|
|
170
|
+
return pollInterval;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Enhanced graceful shutdown with connection draining
|
|
175
|
+
*/
|
|
176
|
+
async function gracefulShutdown(signal = 'SIGTERM', drainTimeout = 10000) {
|
|
177
|
+
log(`Agent received ${signal}, initiating graceful shutdown...`);
|
|
178
|
+
|
|
179
|
+
// Update daemon state to indicate shutdown
|
|
180
|
+
updateDaemonState({ status: 'shutting_down' });
|
|
181
|
+
|
|
182
|
+
if (serverInstances) {
|
|
183
|
+
// Clear polling interval
|
|
184
|
+
if (serverInstances.pollInterval) {
|
|
185
|
+
clearInterval(serverInstances.pollInterval);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Close session watcher
|
|
189
|
+
if (serverInstances.sessionWatcher) {
|
|
190
|
+
serverInstances.sessionWatcher.close();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Notify clients about shutdown with reconnect hint
|
|
194
|
+
if (serverInstances.wss) {
|
|
195
|
+
const shutdownMessage = JSON.stringify({
|
|
196
|
+
type: 'agent_shutdown',
|
|
197
|
+
message: 'Agent is shutting down',
|
|
198
|
+
reconnectAfter: 5000, // Hint to reconnect after 5 seconds
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
serverInstances.wss.clients.forEach((client) => {
|
|
202
|
+
try {
|
|
203
|
+
client.send(shutdownMessage);
|
|
204
|
+
} catch (e) {
|
|
205
|
+
// Ignore send errors during shutdown
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Wait for active operations to complete (connection draining)
|
|
210
|
+
log(`Waiting up to ${drainTimeout / 1000}s for active operations...`);
|
|
211
|
+
|
|
212
|
+
await new Promise((resolve) => {
|
|
213
|
+
const drainTimer = setTimeout(resolve, drainTimeout);
|
|
214
|
+
|
|
215
|
+
// Check if all clients have disconnected
|
|
216
|
+
const checkInterval = setInterval(() => {
|
|
217
|
+
if (serverInstances.wss.clients.size === 0) {
|
|
218
|
+
clearInterval(checkInterval);
|
|
219
|
+
clearTimeout(drainTimer);
|
|
220
|
+
resolve();
|
|
221
|
+
}
|
|
222
|
+
}, 500);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Close remaining connections
|
|
226
|
+
serverInstances.wss.clients.forEach((client) => {
|
|
227
|
+
client.close();
|
|
228
|
+
});
|
|
229
|
+
serverInstances.wss.close();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Close HTTP server
|
|
233
|
+
if (serverInstances.server) {
|
|
234
|
+
serverInstances.server.close();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Clear agent info
|
|
239
|
+
try {
|
|
240
|
+
await agentStore.clearAgentInfo();
|
|
241
|
+
} catch (err) {
|
|
242
|
+
log(`Failed to clear agent info: ${err.message}`, 'warn');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Stop container
|
|
246
|
+
try {
|
|
247
|
+
await containerManager.stopContainerGracefully();
|
|
248
|
+
} catch (error) {
|
|
249
|
+
log(`Error stopping container: ${error.message}`, 'warn');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Clean up daemon files
|
|
253
|
+
clearDaemonState();
|
|
254
|
+
clearDaemonPid();
|
|
255
|
+
releaseLock();
|
|
256
|
+
|
|
257
|
+
log('Agent shutdown complete');
|
|
258
|
+
process.exit(0);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Run the agent in the current process
|
|
263
|
+
* @param {Object} options - Options
|
|
264
|
+
* @param {number} options.preferredPort - Preferred WebSocket port
|
|
265
|
+
* @param {boolean} options.isDaemon - Whether running as daemon
|
|
266
|
+
* @returns {Promise<Object>} Server instances
|
|
267
|
+
*/
|
|
268
|
+
export async function runAgent(options = {}) {
|
|
269
|
+
const { preferredPort = config.wsPort, isDaemon = false } = options;
|
|
270
|
+
|
|
271
|
+
// Check for existing session
|
|
272
|
+
log('Checking for existing session...');
|
|
273
|
+
const session = loadSession();
|
|
274
|
+
|
|
275
|
+
if (!session) {
|
|
276
|
+
log('No session found. Please run "fenwave login" first.', 'error');
|
|
277
|
+
throw new Error('No session found. Please run "fenwave login" first.');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!isSessionValid(session)) {
|
|
281
|
+
log('Session has expired. Please run "fenwave login" again.', 'error');
|
|
282
|
+
throw new Error('Session expired. Please run "fenwave login" again.');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
log(`Found valid session for ${session.userEntityRef}`);
|
|
286
|
+
|
|
287
|
+
// Acquire lock for single instance enforcement
|
|
288
|
+
if (!acquireLock()) {
|
|
289
|
+
log('Another instance of the agent is already running.', 'error');
|
|
290
|
+
throw new Error('Another instance of the agent is already running.');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Find available port
|
|
294
|
+
let actualPort;
|
|
295
|
+
try {
|
|
296
|
+
actualPort = await findAvailablePort(preferredPort);
|
|
297
|
+
if (actualPort !== preferredPort) {
|
|
298
|
+
log(`Port ${preferredPort} is in use, using port ${actualPort}`, 'warn');
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
releaseLock();
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Initialize registry store
|
|
306
|
+
await registryStore.initialize();
|
|
307
|
+
|
|
308
|
+
// Start container
|
|
309
|
+
log('Starting container...');
|
|
310
|
+
try {
|
|
311
|
+
await containerManager.startContainer();
|
|
312
|
+
log('Container started successfully');
|
|
313
|
+
} catch (containerError) {
|
|
314
|
+
log(`Failed to start container: ${containerError.message}`, 'warn');
|
|
315
|
+
log('Starting agent without container...');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Create HTTP server
|
|
319
|
+
const agentId = uuidv4();
|
|
320
|
+
const existingWsToken = readWsToken();
|
|
321
|
+
const server = http.createServer();
|
|
322
|
+
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
server.listen(actualPort, async () => {
|
|
325
|
+
try {
|
|
326
|
+
// Initialize agent start time
|
|
327
|
+
await initializeAgentStartTime();
|
|
328
|
+
|
|
329
|
+
// Save daemon state
|
|
330
|
+
saveDaemonPid(process.pid);
|
|
331
|
+
saveDaemonState({
|
|
332
|
+
pid: process.pid,
|
|
333
|
+
port: actualPort,
|
|
334
|
+
startTime: new Date().toISOString(),
|
|
335
|
+
status: 'running',
|
|
336
|
+
userEntityRef: session.userEntityRef,
|
|
337
|
+
isDaemon,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Setup WebSocket server
|
|
341
|
+
const wss = setupWebSocketServer(
|
|
342
|
+
server,
|
|
343
|
+
clients,
|
|
344
|
+
agentId,
|
|
345
|
+
{
|
|
346
|
+
userEntityRef: session.userEntityRef,
|
|
347
|
+
expiresAt: session.expiresAt,
|
|
348
|
+
},
|
|
349
|
+
existingWsToken
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// Setup session watcher
|
|
353
|
+
const sessionWatcher = setupSessionWatcher(handleSessionExpiry, server, wss);
|
|
354
|
+
|
|
355
|
+
// Start app update polling
|
|
356
|
+
const pollInterval = startAppUpdatePolling(session.token, wss);
|
|
357
|
+
|
|
358
|
+
// Store server instances for cleanup
|
|
359
|
+
serverInstances = {
|
|
360
|
+
server,
|
|
361
|
+
wss,
|
|
362
|
+
sessionToken: session.token,
|
|
363
|
+
userEntityRef: session.userEntityRef,
|
|
364
|
+
agentId,
|
|
365
|
+
sessionWatcher,
|
|
366
|
+
pollInterval,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// Display startup info
|
|
370
|
+
console.log(
|
|
371
|
+
chalk.green('\n' + '='.repeat(66))
|
|
372
|
+
);
|
|
373
|
+
console.log(chalk.green(' Fenwave Agent Started Successfully'));
|
|
374
|
+
console.log(chalk.green('='.repeat(66) + '\n'));
|
|
375
|
+
console.log(chalk.white(' Fenwave DevApp Dashboard:'));
|
|
376
|
+
console.log(chalk.cyan(` http://localhost:${CONTAINER_PORT}\n`));
|
|
377
|
+
console.log(chalk.white(' WebSocket Server:'));
|
|
378
|
+
console.log(chalk.cyan(` ws://localhost:${actualPort}\n`));
|
|
379
|
+
console.log(chalk.white(' User:'));
|
|
380
|
+
console.log(chalk.cyan(` ${session.userEntityRef}\n`));
|
|
381
|
+
if (isDaemon) {
|
|
382
|
+
console.log(chalk.white(' Mode:'));
|
|
383
|
+
console.log(chalk.cyan(' Background daemon\n'));
|
|
384
|
+
}
|
|
385
|
+
console.log(chalk.green('='.repeat(66) + '\n'));
|
|
386
|
+
|
|
387
|
+
log('Agent is ready to receive connections');
|
|
388
|
+
|
|
389
|
+
resolve(serverInstances);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
log(`Failed to setup WebSocket server: ${error.message}`, 'error');
|
|
392
|
+
releaseLock();
|
|
393
|
+
reject(error);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
server.on('error', (error) => {
|
|
398
|
+
log(`Server error: ${error.message}`, 'error');
|
|
399
|
+
releaseLock();
|
|
400
|
+
reject(error);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Setup signal handlers for graceful shutdown
|
|
407
|
+
*/
|
|
408
|
+
export function setupSignalHandlers() {
|
|
409
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
410
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get current server instances
|
|
415
|
+
* @returns {Object|null} Server instances
|
|
416
|
+
*/
|
|
417
|
+
export function getServerInstances() {
|
|
418
|
+
return serverInstances;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export { clients, gracefulShutdown };
|
|
422
|
+
|
|
423
|
+
export default {
|
|
424
|
+
runAgent,
|
|
425
|
+
setupSignalHandlers,
|
|
426
|
+
getServerInstances,
|
|
427
|
+
gracefulShutdown,
|
|
428
|
+
clients,
|
|
429
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Daemon Entry Point
|
|
5
|
+
*
|
|
6
|
+
* This script is spawned as a detached child process when running
|
|
7
|
+
* "fenwave service start". It runs the agent in daemon mode.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { runAgent, setupSignalHandlers } from './agentRunner.js';
|
|
11
|
+
import { writeLog } from './logManager.js';
|
|
12
|
+
import { loadConfig } from '../store/configStore.js';
|
|
13
|
+
|
|
14
|
+
// Parse command line arguments
|
|
15
|
+
function parseArgs() {
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const options = {};
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
|
20
|
+
if (args[i] === '--port' && args[i + 1]) {
|
|
21
|
+
options.port = parseInt(args[i + 1], 10);
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return options;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function main() {
|
|
30
|
+
const options = parseArgs();
|
|
31
|
+
const config = loadConfig();
|
|
32
|
+
|
|
33
|
+
writeLog('Daemon starting...');
|
|
34
|
+
writeLog(`PID: ${process.pid}`);
|
|
35
|
+
writeLog(`Preferred port: ${options.port || config.wsPort}`);
|
|
36
|
+
|
|
37
|
+
// Setup signal handlers
|
|
38
|
+
setupSignalHandlers();
|
|
39
|
+
|
|
40
|
+
// Handle uncaught exceptions
|
|
41
|
+
process.on('uncaughtException', (error) => {
|
|
42
|
+
writeLog(`Uncaught exception: ${error.message}`, 'error');
|
|
43
|
+
writeLog(error.stack, 'error');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
48
|
+
writeLog(`Unhandled rejection: ${reason}`, 'error');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await runAgent({
|
|
53
|
+
preferredPort: options.port || config.wsPort,
|
|
54
|
+
isDaemon: true,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
writeLog('Daemon started successfully');
|
|
58
|
+
} catch (error) {
|
|
59
|
+
writeLog(`Daemon failed to start: ${error.message}`, 'error');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main();
|