lsh-framework 3.2.5 → 3.5.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -34
  3. package/dist/commands/ipfs.js +7 -12
  4. package/dist/commands/sync.js +49 -38
  5. package/dist/constants/config.js +3 -0
  6. package/dist/lib/floating-point-arithmetic.js +2 -2
  7. package/dist/lib/ipfs-client-manager.js +51 -13
  8. package/dist/lib/ipfs-secrets-storage.js +21 -16
  9. package/dist/lib/ipfs-sync.js +88 -14
  10. package/dist/lib/secrets-manager.js +117 -47
  11. package/dist/lib/sync-key-store.js +87 -0
  12. package/dist/services/secrets/secrets.js +77 -39
  13. package/package.json +16 -16
  14. package/dist/__tests__/fixtures/job-fixtures.js +0 -204
  15. package/dist/__tests__/fixtures/supabase-mocks.js +0 -252
  16. package/dist/daemon/job-registry.js +0 -556
  17. package/dist/daemon/lshd.js +0 -968
  18. package/dist/daemon/saas-api-routes.js +0 -599
  19. package/dist/daemon/saas-api-server.js +0 -231
  20. package/dist/examples/supabase-integration.js +0 -106
  21. package/dist/lib/api-response.js +0 -226
  22. package/dist/lib/base-command-registrar.js +0 -287
  23. package/dist/lib/base-job-manager.js +0 -295
  24. package/dist/lib/cloud-config-manager.js +0 -348
  25. package/dist/lib/cron-job-manager.js +0 -368
  26. package/dist/lib/daemon-client-helper.js +0 -145
  27. package/dist/lib/daemon-client.js +0 -513
  28. package/dist/lib/database-persistence.js +0 -727
  29. package/dist/lib/database-schema.js +0 -259
  30. package/dist/lib/database-types.js +0 -90
  31. package/dist/lib/enhanced-history-system.js +0 -247
  32. package/dist/lib/history-system.js +0 -246
  33. package/dist/lib/job-manager.js +0 -436
  34. package/dist/lib/job-storage-database.js +0 -164
  35. package/dist/lib/job-storage-memory.js +0 -73
  36. package/dist/lib/local-storage-adapter.js +0 -507
  37. package/dist/lib/optimized-job-scheduler.js +0 -356
  38. package/dist/lib/saas-audit.js +0 -215
  39. package/dist/lib/saas-auth.js +0 -465
  40. package/dist/lib/saas-billing.js +0 -503
  41. package/dist/lib/saas-email.js +0 -403
  42. package/dist/lib/saas-encryption.js +0 -221
  43. package/dist/lib/saas-organizations.js +0 -662
  44. package/dist/lib/saas-secrets.js +0 -408
  45. package/dist/lib/saas-types.js +0 -165
  46. package/dist/lib/supabase-client.js +0 -125
  47. package/dist/lib/supabase-utils.js +0 -396
  48. package/dist/services/cron/cron-registrar.js +0 -240
  49. package/dist/services/cron/cron.js +0 -9
  50. package/dist/services/daemon/daemon-registrar.js +0 -585
  51. package/dist/services/daemon/daemon.js +0 -9
  52. package/dist/services/supabase/supabase-registrar.js +0 -375
  53. package/dist/services/supabase/supabase.js +0 -9
@@ -1,585 +0,0 @@
1
- /**
2
- * Daemon Command Registrar
3
- * Registers all daemon-related CLI commands using BaseCommandRegistrar
4
- */
5
- import { BaseCommandRegistrar } from '../../lib/base-command-registrar.js';
6
- import * as fs from 'fs';
7
- import { exec } from 'child_process';
8
- import { promisify } from 'util';
9
- import { getPlatformPaths } from '../../lib/platform-utils.js';
10
- const execAsync = promisify(exec);
11
- export class DaemonCommandRegistrar extends BaseCommandRegistrar {
12
- constructor() {
13
- super('Daemon');
14
- }
15
- async register(program) {
16
- const daemonCmd = this.createCommand(program, 'daemon', 'LSH daemon management commands');
17
- this.registerDaemonControlCommands(daemonCmd);
18
- this.registerJobManagementCommands(daemonCmd);
19
- this.registerDatabaseCommands(daemonCmd);
20
- }
21
- registerDaemonControlCommands(daemonCmd) {
22
- // Status command
23
- this.addSubcommand(daemonCmd, {
24
- name: 'status',
25
- description: 'Get daemon status',
26
- action: async () => {
27
- const status = await this.withDaemonAction(async (client) => {
28
- return await client.getStatus();
29
- });
30
- this.logInfo('Daemon Status:');
31
- this.logInfo(` PID: ${status.pid}`);
32
- this.logInfo(` Uptime: ${Math.floor(status.uptime / 60)} minutes`);
33
- if (status.memoryUsage) {
34
- this.logInfo(` Memory: ${Math.round(status.memoryUsage.heapUsed / 1024 / 1024)} MB`);
35
- }
36
- if (status.jobs) {
37
- this.logInfo(` Jobs: ${status.jobs.total} total, ${status.jobs.running} running`);
38
- }
39
- }
40
- });
41
- // Start command
42
- this.addSubcommand(daemonCmd, {
43
- name: 'start',
44
- description: 'Start the daemon',
45
- action: async () => {
46
- const { spawn } = await import('child_process');
47
- const platformPaths = getPlatformPaths('lsh');
48
- const daemonProcess = spawn('node', ['dist/daemon/lshd.js', 'start', platformPaths.socketPath], {
49
- detached: true,
50
- stdio: 'ignore'
51
- });
52
- daemonProcess.unref();
53
- this.logSuccess('Daemon started');
54
- this.logInfo('Check status with: lsh daemon status');
55
- }
56
- });
57
- // Stop command
58
- this.addSubcommand(daemonCmd, {
59
- name: 'stop',
60
- description: 'Stop the daemon',
61
- action: async () => {
62
- if (!this.isDaemonRunning()) {
63
- this.logWarning('Daemon is not running');
64
- return;
65
- }
66
- await this.withDaemonAction(async (client) => {
67
- await client.stopDaemon();
68
- });
69
- this.logSuccess('Daemon stopped');
70
- }
71
- });
72
- // Restart command
73
- this.addSubcommand(daemonCmd, {
74
- name: 'restart',
75
- description: 'Restart the daemon',
76
- action: async () => {
77
- if (this.isDaemonRunning()) {
78
- await this.withDaemonAction(async (client) => {
79
- await client.restartDaemon();
80
- });
81
- this.logSuccess('Daemon restarted');
82
- }
83
- else {
84
- this.logWarning('Daemon is not running, starting...');
85
- const { spawn } = await import('child_process');
86
- const daemonProcess = spawn('node', ['dist/daemon/lshd.js', 'start'], {
87
- detached: true,
88
- stdio: 'ignore'
89
- });
90
- daemonProcess.unref();
91
- this.logSuccess('Daemon started');
92
- }
93
- }
94
- });
95
- // Cleanup command
96
- this.addSubcommand(daemonCmd, {
97
- name: 'cleanup',
98
- description: 'Clean up daemon processes and files',
99
- options: [
100
- { flags: '-f, --force', description: 'Force cleanup without prompts', defaultValue: false }
101
- ],
102
- action: async (options) => {
103
- const opts = options;
104
- await this.cleanupDaemon(opts.force);
105
- }
106
- });
107
- }
108
- registerJobManagementCommands(daemonCmd) {
109
- const jobCmd = daemonCmd
110
- .command('job')
111
- .description('Job management commands');
112
- // Create job
113
- this.addSubcommand(jobCmd, {
114
- name: 'create',
115
- description: 'Create a new cron job',
116
- options: [
117
- { flags: '-n, --name <name>', description: 'Job name' },
118
- { flags: '-c, --command <command>', description: 'Command to execute' },
119
- { flags: '-s, --schedule <schedule>', description: 'Cron schedule (e.g., "0 2 * * *")' },
120
- { flags: '-i, --interval <interval>', description: 'Interval in milliseconds' },
121
- { flags: '-d, --description <description>', description: 'Job description' },
122
- { flags: '-w, --working-dir <dir>', description: 'Working directory' },
123
- { flags: '-e, --env <env>', description: 'Environment variables (JSON)' },
124
- { flags: '-t, --tags <tags>', description: 'Comma-separated tags' },
125
- { flags: '-p, --priority <priority>', description: 'Priority (0-10)', defaultValue: '5' },
126
- { flags: '--max-retries <retries>', description: 'Maximum retries', defaultValue: '3' },
127
- { flags: '--timeout <timeout>', description: 'Timeout in milliseconds', defaultValue: '0' },
128
- { flags: '--no-database-sync', description: 'Disable database synchronization' }
129
- ],
130
- action: async (options) => {
131
- const opts = options;
132
- if (!opts.name || !opts.command || (!opts.schedule && !opts.interval)) {
133
- throw new Error('Missing required options: --name, --command, and (--schedule or --interval)');
134
- }
135
- const jobSpec = this.createJobSpec(opts);
136
- await this.withDaemonAction(async (client) => {
137
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
- await client.createDatabaseCronJob(jobSpec);
139
- });
140
- this.logSuccess('Job created successfully:');
141
- this.logInfo(` ID: ${jobSpec.id}`);
142
- this.logInfo(` Name: ${jobSpec.name}`);
143
- this.logInfo(` Command: ${jobSpec.command}`);
144
- this.logInfo(` Schedule: ${this.formatSchedule(jobSpec.schedule)}`);
145
- this.logInfo(` Database Sync: ${jobSpec.databaseSync ? 'Enabled' : 'Disabled'}`);
146
- }
147
- });
148
- // List jobs
149
- this.addSubcommand(jobCmd, {
150
- name: 'list',
151
- description: 'List all jobs',
152
- options: [
153
- { flags: '-f, --filter <filter>', description: 'Filter jobs by status' }
154
- ],
155
- action: async (options) => {
156
- const opts = options;
157
- const jobs = await this.withDaemonAction(async (client) => {
158
- return await client.listJobs(opts.filter ? { status: opts.filter } : undefined);
159
- });
160
- this.displayJobs(jobs);
161
- }
162
- });
163
- // Start job
164
- this.addSubcommand(jobCmd, {
165
- name: 'start',
166
- description: 'Start a job',
167
- arguments: [{ name: 'jobId', required: true }],
168
- action: async (jobId) => {
169
- await this.withDaemonAction(async (client) => {
170
- await client.startJob(jobId);
171
- });
172
- this.logSuccess(`Job ${jobId} started`);
173
- }
174
- });
175
- // Trigger job
176
- this.addSubcommand(jobCmd, {
177
- name: 'trigger',
178
- description: 'Trigger a job to run immediately (bypass schedule)',
179
- arguments: [{ name: 'jobId', required: true }],
180
- action: async (jobId) => {
181
- const result = await this.withDaemonAction(async (client) => await client.triggerJob(jobId), { forUser: true });
182
- this.logSuccess(`Job ${jobId} triggered successfully`);
183
- if (result.output) {
184
- this.logInfo('Output:');
185
- this.logInfo(result.output);
186
- }
187
- }
188
- });
189
- // Trigger all jobs
190
- this.addSubcommand(jobCmd, {
191
- name: 'trigger-all',
192
- description: 'Trigger all active jobs to run immediately',
193
- options: [
194
- { flags: '-f, --filter <status>', description: 'Filter by job status', defaultValue: 'created' }
195
- ],
196
- action: async (options) => {
197
- const opts = options;
198
- await this.withDaemonAction(async (client) => {
199
- const jobs = await client.listJobs({ status: opts.filter });
200
- this.logInfo(`Triggering ${jobs.length} jobs...`);
201
- for (const job of jobs) {
202
- try {
203
- this.logInfo(` Triggering ${job.name} (${job.id})...`);
204
- const result = await client.triggerJob(job.id);
205
- this.logSuccess(` ${job.name} completed`);
206
- if (result.output) {
207
- const preview = result.output.substring(0, 100);
208
- this.logInfo(` Output: ${preview}${result.output.length > 100 ? '...' : ''}`);
209
- }
210
- }
211
- catch (error) {
212
- const err = error;
213
- this.logError(` ${job.name} failed: ${err.message || err}`);
214
- }
215
- }
216
- }, { forUser: true });
217
- this.logSuccess('All jobs triggered');
218
- }
219
- });
220
- // Stop job
221
- this.addSubcommand(jobCmd, {
222
- name: 'stop',
223
- description: 'Stop a job',
224
- arguments: [{ name: 'jobId', required: true }],
225
- options: [
226
- { flags: '-s, --signal <signal>', description: 'Signal to send', defaultValue: 'SIGTERM' }
227
- ],
228
- action: async (jobId, options) => {
229
- const opts = options;
230
- await this.withDaemonAction(async (client) => {
231
- await client.stopJob(jobId, opts.signal);
232
- });
233
- this.logSuccess(`Job ${jobId} stopped with signal ${opts.signal}`);
234
- }
235
- });
236
- // Remove job
237
- this.addSubcommand(jobCmd, {
238
- name: 'remove',
239
- description: 'Remove a job',
240
- arguments: [{ name: 'jobId', required: true }],
241
- options: [
242
- { flags: '-f, --force', description: 'Force removal', defaultValue: false }
243
- ],
244
- action: async (jobId, options) => {
245
- const opts = options;
246
- await this.withDaemonAction(async (client) => {
247
- await client.removeJob(jobId, opts.force);
248
- });
249
- this.logSuccess(`Job ${jobId} removed`);
250
- }
251
- });
252
- // Job info
253
- this.addSubcommand(jobCmd, {
254
- name: 'info',
255
- description: 'Get job information',
256
- arguments: [{ name: 'jobId', required: true }],
257
- action: async (jobId) => {
258
- const job = await this.withDaemonAction(async (client) => {
259
- return await client.getJob(jobId);
260
- });
261
- if (!job) {
262
- throw new Error(`Job ${jobId} not found`);
263
- }
264
- this.logInfo(`Job Information: ${jobId}`);
265
- this.logInfo(` Name: ${job.name}`);
266
- this.logInfo(` Command: ${job.command}`);
267
- this.logInfo(` Status: ${job.status}`);
268
- this.logInfo(` Priority: ${job.priority}`);
269
- this.logInfo(` Working Directory: ${job.cwd}`);
270
- this.logInfo(` User: ${job.user}`);
271
- this.logInfo(` Tags: ${job.tags?.join(', ') || 'None'}`);
272
- if (job.schedule) {
273
- this.logInfo(` Schedule: ${this.formatSchedule(job.schedule)}`);
274
- }
275
- }
276
- });
277
- }
278
- registerDatabaseCommands(daemonCmd) {
279
- const dbCmd = daemonCmd
280
- .command('db')
281
- .description('Database integration commands');
282
- // Job history
283
- this.addSubcommand(dbCmd, {
284
- name: 'history',
285
- description: 'Get job execution history from database',
286
- options: [
287
- { flags: '-j, --job-id <jobId>', description: 'Filter by job ID' },
288
- { flags: '-l, --limit <limit>', description: 'Limit number of results', defaultValue: '50' }
289
- ],
290
- action: async (options) => {
291
- const opts = options;
292
- const jobs = await this.withDaemonAction(async (client) => await client.getJobHistory(opts.jobId, parseInt(opts.limit)), { forUser: true, requireRunning: false });
293
- this.logInfo(`Job History (${jobs.length} records):`);
294
- jobs.forEach(job => {
295
- const started = new Date(job.started_at).toLocaleString();
296
- const completed = job.completed_at ? new Date(job.completed_at).toLocaleString() : 'Running';
297
- this.logInfo(` ${job.job_id}: ${job.command}`);
298
- this.logInfo(` Started: ${started}`);
299
- this.logInfo(` Completed: ${completed}`);
300
- this.logInfo(` Status: ${job.status}`);
301
- this.logInfo(` Exit Code: ${job.exit_code || 'N/A'}`);
302
- this.logInfo('');
303
- });
304
- }
305
- });
306
- // Job statistics
307
- this.addSubcommand(dbCmd, {
308
- name: 'stats',
309
- description: 'Get job statistics from database',
310
- options: [
311
- { flags: '-j, --job-id <jobId>', description: 'Filter by job ID' }
312
- ],
313
- action: async (options) => {
314
- const opts = options;
315
- const stats = await this.withDaemonAction(async (client) => await client.getJobStatistics(opts.jobId), { forUser: true, requireRunning: false });
316
- this.logInfo('Job Statistics:');
317
- this.logInfo(` Total Jobs: ${stats.totalJobs}`);
318
- this.logInfo(` Success Rate: ${stats.successRate.toFixed(1)}%`);
319
- this.logInfo(` Last Execution: ${stats.lastExecution || 'Never'}`);
320
- this.logInfo('\n Status Breakdown:');
321
- Object.entries(stats.byStatus).forEach(([status, count]) => {
322
- this.logInfo(` ${status}: ${count}`);
323
- });
324
- }
325
- });
326
- // Recent executions
327
- this.addSubcommand(dbCmd, {
328
- name: 'recent',
329
- description: 'Show most recent job executions with output',
330
- options: [
331
- { flags: '-l, --limit <limit>', description: 'Number of recent executions to show', defaultValue: '5' }
332
- ],
333
- action: async (options) => {
334
- const opts = options;
335
- const jobs = await this.withDaemonAction(async (client) => await client.getJobHistory(undefined, parseInt(opts.limit)), { forUser: true });
336
- this.logInfo(`Recent Job Executions (${jobs.length} records):`);
337
- jobs.forEach((job, index) => {
338
- const started = new Date(job.started_at).toLocaleString();
339
- const status = job.status === 'completed' ? '✅' :
340
- job.status === 'failed' ? '❌' :
341
- job.status === 'running' ? '⏳' : '⏸️';
342
- this.logInfo(`\n${index + 1}. ${status} ${job.job_id}`);
343
- this.logInfo(` Executed: ${started}`);
344
- this.logInfo(` Status: ${job.status}`);
345
- if (job.output) {
346
- const preview = job.output.substring(0, 200);
347
- this.logInfo(` Output: ${preview}${job.output.length > 200 ? '...' : ''}`);
348
- }
349
- if (job.error) {
350
- this.logInfo(` Error: ${job.error}`);
351
- }
352
- });
353
- if (jobs.length === 0) {
354
- this.logInfo('\nNo recent executions found.');
355
- this.logInfo(' Trigger jobs to see execution history:');
356
- this.logInfo(' lsh daemon job trigger <jobId>');
357
- this.logInfo(' lsh daemon job trigger-all');
358
- }
359
- }
360
- });
361
- // Job status with executions
362
- this.addSubcommand(dbCmd, {
363
- name: 'status',
364
- description: 'Show detailed status and recent executions of a specific job',
365
- arguments: [{ name: 'jobId', required: true }],
366
- action: async (jobId) => {
367
- await this.withDaemonAction(async (client) => {
368
- const jobs = await client.listJobs();
369
- const job = jobs.find(j => j.id === jobId);
370
- if (!job) {
371
- throw new Error(`Job ${jobId} not found in daemon registry`);
372
- }
373
- this.logInfo(`Job Status: ${job.name} (${jobId})`);
374
- const cmdPreview = job.command.substring(0, 100);
375
- this.logInfo(` Command: ${cmdPreview}${job.command.length > 100 ? '...' : ''}`);
376
- this.logInfo(` Status: ${job.status}`);
377
- this.logInfo(` Schedule: ${this.formatSchedule(job.schedule)}`);
378
- this.logInfo(` Priority: ${job.priority}`);
379
- const executions = await client.getJobHistory(jobId, 5);
380
- if (executions.length > 0) {
381
- this.logInfo(`\nRecent Executions (${executions.length} records):`);
382
- executions.forEach((exec, index) => {
383
- const started = new Date(exec.started_at).toLocaleString();
384
- const status = exec.status === 'completed' ? '✅' :
385
- exec.status === 'failed' ? '❌' :
386
- exec.status === 'running' ? '⏳' : '⏸️';
387
- this.logInfo(`\n${index + 1}. ${status} ${started}`);
388
- this.logInfo(` Status: ${exec.status} (Exit Code: ${exec.exit_code || 'N/A'})`);
389
- if (exec.output) {
390
- const preview = exec.output.substring(0, 150);
391
- this.logInfo(` Output: ${preview}${exec.output.length > 150 ? '...' : ''}`);
392
- }
393
- });
394
- }
395
- else {
396
- this.logInfo('\nNo execution history found for this job.');
397
- this.logInfo(` Run: lsh daemon job trigger ${jobId}`);
398
- }
399
- }, { forUser: true });
400
- }
401
- });
402
- // Sync jobs to database
403
- this.addSubcommand(dbCmd, {
404
- name: 'sync',
405
- description: 'Sync current in-memory jobs to database',
406
- action: async () => {
407
- const synced = await this.withDaemonAction(async (client) => {
408
- const jobs = await client.listJobs();
409
- this.logInfo(`Syncing ${jobs.length} jobs to database...`);
410
- let syncCount = 0;
411
- for (const job of jobs) {
412
- try {
413
- const dbStatus = job.status === 'created' ? 'stopped' :
414
- job.status === 'running' ? 'running' :
415
- job.status === 'completed' ? 'completed' : 'failed';
416
- await client.syncJobToDatabase({
417
- id: job.id,
418
- name: job.name,
419
- command: job.command,
420
- schedule: job.schedule,
421
- enabled: true,
422
- databaseSync: true
423
- }, dbStatus);
424
- this.logSuccess(` Synced ${job.name} (${job.id}) - status: ${dbStatus}`);
425
- syncCount++;
426
- }
427
- catch (error) {
428
- const err = error;
429
- this.logError(` Failed to sync ${job.name}: ${err.message}`);
430
- }
431
- }
432
- return { syncCount, totalJobs: jobs.length };
433
- }, { forUser: true });
434
- this.logSuccess(`\nSuccessfully synced ${synced.syncCount}/${synced.totalJobs} jobs to database`);
435
- this.logInfo('\nCheck results with:');
436
- this.logInfo(' lsh daemon db stats');
437
- this.logInfo(' lsh daemon db history');
438
- }
439
- });
440
- }
441
- async cleanupDaemon(force = false) {
442
- this.logInfo('LSH Daemon Cleanup');
443
- this.logInfo('====================');
444
- this.logInfo('');
445
- try {
446
- // 1. Find daemon processes
447
- this.logInfo('Finding LSH daemon processes...');
448
- let daemonPids = [];
449
- try {
450
- const { stdout } = await execAsync('pgrep -f "lshd.js"');
451
- daemonPids = stdout.trim().split('\n').filter(pid => pid.length > 0);
452
- }
453
- catch (_error) {
454
- // No processes found
455
- }
456
- if (daemonPids.length > 0) {
457
- this.logInfo('Found daemon processes:');
458
- try {
459
- const { stdout } = await execAsync('ps aux | grep lshd | grep -v grep');
460
- stdout.split('\n').filter(line => line.trim()).forEach(line => this.logInfo(` ${line}`));
461
- }
462
- catch (_error) {
463
- this.logInfo(` PIDs: ${daemonPids.join(', ')}`);
464
- }
465
- this.logInfo('');
466
- if (force) {
467
- this.logInfo('Force mode: Killing daemon processes...');
468
- try {
469
- await execAsync('pkill -f "lshd.js"');
470
- this.logSuccess('Daemon processes killed');
471
- }
472
- catch (_error) {
473
- this.logWarning('Some processes may require manual cleanup');
474
- }
475
- }
476
- else {
477
- this.logWarning('Found running daemon processes. Use --force to kill them automatically');
478
- this.logInfo(' Or manually run: pkill -f "lshd.js"');
479
- }
480
- }
481
- else {
482
- this.logInfo('No daemon processes found');
483
- }
484
- // 2. Clean up socket files
485
- this.logInfo('Cleaning up socket files...');
486
- try {
487
- const { stdout } = await execAsync(`find /tmp -name "lsh-*daemon*.sock" 2>/dev/null || echo ""`);
488
- const socketFiles = stdout.trim().split('\n').filter(file => file.length > 0);
489
- if (socketFiles.length > 0) {
490
- this.logInfo('Found socket files:');
491
- socketFiles.forEach(file => this.logInfo(` ${file}`));
492
- for (const socket of socketFiles) {
493
- try {
494
- if (fs.existsSync(socket)) {
495
- fs.unlinkSync(socket);
496
- this.logSuccess(`Removed: ${socket}`);
497
- }
498
- }
499
- catch (error) {
500
- const err = error;
501
- this.logWarning(`Could not remove: ${socket} (${err.message})`);
502
- }
503
- }
504
- }
505
- else {
506
- this.logInfo('No socket files found');
507
- }
508
- }
509
- catch (_error) {
510
- this.logInfo('No socket files found');
511
- }
512
- // 3. Clean up PID files
513
- this.logInfo('Cleaning up PID files...');
514
- try {
515
- const { stdout } = await execAsync(`find /tmp -name "lsh-*daemon*.pid" 2>/dev/null || echo ""`);
516
- const pidFiles = stdout.trim().split('\n').filter(file => file.length > 0);
517
- if (pidFiles.length > 0) {
518
- this.logInfo('Found PID files:');
519
- pidFiles.forEach(file => this.logInfo(` ${file}`));
520
- for (const pidFile of pidFiles) {
521
- try {
522
- if (fs.existsSync(pidFile)) {
523
- fs.unlinkSync(pidFile);
524
- this.logSuccess(`Removed: ${pidFile}`);
525
- }
526
- }
527
- catch (error) {
528
- const err = error;
529
- this.logWarning(`Could not remove: ${pidFile} (${err.message})`);
530
- }
531
- }
532
- }
533
- else {
534
- this.logInfo('No PID files found');
535
- }
536
- }
537
- catch (_error) {
538
- this.logInfo('No PID files found');
539
- }
540
- // 4. Verify cleanup
541
- this.logInfo('');
542
- this.logInfo('Verifying cleanup...');
543
- let remainingProcesses = [];
544
- try {
545
- const { stdout } = await execAsync('pgrep -f "lshd.js"');
546
- remainingProcesses = stdout.trim().split('\n').filter(pid => pid.length > 0);
547
- }
548
- catch (_error) {
549
- // No processes found (good)
550
- }
551
- let remainingSockets = [];
552
- try {
553
- const { stdout } = await execAsync(`find /tmp -name "lsh-*daemon*.sock" 2>/dev/null || echo ""`);
554
- remainingSockets = stdout.trim().split('\n').filter(file => file.length > 0);
555
- }
556
- catch (_error) {
557
- // No sockets found (good)
558
- }
559
- if (remainingProcesses.length === 0 && remainingSockets.length === 0) {
560
- this.logSuccess('Cleanup completed successfully!');
561
- this.logInfo('');
562
- this.logInfo('Next steps:');
563
- this.logInfo(' 1. Start fresh daemon: lsh daemon start');
564
- this.logInfo(' 2. Check status: lsh daemon status');
565
- this.logInfo(' 3. View dashboard: ./scripts/monitor-dashboard.sh');
566
- }
567
- else {
568
- this.logWarning('Some items may still remain:');
569
- if (remainingProcesses.length > 0) {
570
- this.logInfo(` Processes: ${remainingProcesses.join(', ')}`);
571
- }
572
- if (remainingSockets.length > 0) {
573
- this.logInfo(` Sockets: ${remainingSockets.join(', ')}`);
574
- }
575
- this.logInfo('');
576
- this.logInfo('Try running with --force for complete cleanup:');
577
- this.logInfo(' lsh daemon cleanup --force');
578
- }
579
- }
580
- catch (error) {
581
- const err = error;
582
- throw new Error(`Cleanup failed: ${err.message}`);
583
- }
584
- }
585
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Daemon Service - CLI command registration
3
- * Uses DaemonCommandRegistrar for clean, maintainable command setup
4
- */
5
- import { DaemonCommandRegistrar } from './daemon-registrar.js';
6
- export async function init_daemon(program) {
7
- const registrar = new DaemonCommandRegistrar();
8
- await registrar.register(program);
9
- }