@tamyla/clodo-framework 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.
Files changed (130) hide show
  1. package/CHANGELOG.md +564 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1393 -0
  4. package/bin/README.md +71 -0
  5. package/bin/clodo-service.js +416 -0
  6. package/bin/security/security-cli.js +96 -0
  7. package/bin/service-management/README.md +74 -0
  8. package/bin/service-management/create-service.js +129 -0
  9. package/bin/service-management/init-service.js +102 -0
  10. package/bin/service-management/init-service.js.backup +889 -0
  11. package/bin/shared/config/customer-cli.js +293 -0
  12. package/dist/config/ConfigurationManager.js +159 -0
  13. package/dist/config/CustomerConfigCLI.js +220 -0
  14. package/dist/config/FeatureManager.js +426 -0
  15. package/dist/config/customers.js +441 -0
  16. package/dist/config/domains.js +180 -0
  17. package/dist/config/features.js +225 -0
  18. package/dist/config/index.js +6 -0
  19. package/dist/database/database-orchestrator.js +730 -0
  20. package/dist/database/index.js +4 -0
  21. package/dist/deployment/auditor.js +971 -0
  22. package/dist/deployment/index.js +10 -0
  23. package/dist/deployment/rollback-manager.js +523 -0
  24. package/dist/deployment/testers/api-tester.js +80 -0
  25. package/dist/deployment/testers/auth-tester.js +129 -0
  26. package/dist/deployment/testers/core.js +217 -0
  27. package/dist/deployment/testers/database-tester.js +105 -0
  28. package/dist/deployment/testers/index.js +74 -0
  29. package/dist/deployment/testers/load-tester.js +120 -0
  30. package/dist/deployment/testers/performance-tester.js +105 -0
  31. package/dist/deployment/validator.js +558 -0
  32. package/dist/deployment/wrangler-deployer.js +574 -0
  33. package/dist/handlers/GenericRouteHandler.js +532 -0
  34. package/dist/index.js +39 -0
  35. package/dist/migration/MigrationAdapters.js +562 -0
  36. package/dist/modules/ModuleManager.js +668 -0
  37. package/dist/modules/security.js +98 -0
  38. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  39. package/dist/orchestration/index.js +5 -0
  40. package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
  41. package/dist/orchestration/modules/DomainResolver.js +196 -0
  42. package/dist/orchestration/modules/StateManager.js +332 -0
  43. package/dist/orchestration/multi-domain-orchestrator.js +255 -0
  44. package/dist/routing/EnhancedRouter.js +158 -0
  45. package/dist/schema/SchemaManager.js +778 -0
  46. package/dist/security/ConfigurationValidator.js +490 -0
  47. package/dist/security/DeploymentManager.js +208 -0
  48. package/dist/security/SecretGenerator.js +142 -0
  49. package/dist/security/SecurityCLI.js +228 -0
  50. package/dist/security/index.js +51 -0
  51. package/dist/security/patterns/environment-rules.js +66 -0
  52. package/dist/security/patterns/insecure-patterns.js +21 -0
  53. package/dist/service-management/ConfirmationEngine.js +411 -0
  54. package/dist/service-management/ErrorTracker.js +294 -0
  55. package/dist/service-management/GenerationEngine.js +3109 -0
  56. package/dist/service-management/InputCollector.js +237 -0
  57. package/dist/service-management/ServiceCreator.js +229 -0
  58. package/dist/service-management/ServiceInitializer.js +448 -0
  59. package/dist/service-management/ServiceOrchestrator.js +638 -0
  60. package/dist/service-management/handlers/ConfigMutator.js +130 -0
  61. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  62. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  63. package/dist/service-management/handlers/InputHandler.js +59 -0
  64. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  65. package/dist/service-management/index.js +7 -0
  66. package/dist/services/GenericDataService.js +488 -0
  67. package/dist/shared/cloudflare/domain-discovery.js +562 -0
  68. package/dist/shared/cloudflare/domain-manager.js +912 -0
  69. package/dist/shared/cloudflare/index.js +8 -0
  70. package/dist/shared/cloudflare/ops.js +387 -0
  71. package/dist/shared/config/cache.js +1167 -0
  72. package/dist/shared/config/command-config-manager.js +174 -0
  73. package/dist/shared/config/customer-cli.js +258 -0
  74. package/dist/shared/config/index.js +9 -0
  75. package/dist/shared/config/manager.js +289 -0
  76. package/dist/shared/database/connection-manager.js +338 -0
  77. package/dist/shared/database/index.js +7 -0
  78. package/dist/shared/database/orchestrator.js +632 -0
  79. package/dist/shared/deployment/auditor.js +971 -0
  80. package/dist/shared/deployment/index.js +10 -0
  81. package/dist/shared/deployment/rollback-manager.js +523 -0
  82. package/dist/shared/deployment/validator.js +558 -0
  83. package/dist/shared/index.js +32 -0
  84. package/dist/shared/monitoring/health-checker.js +250 -0
  85. package/dist/shared/monitoring/index.js +8 -0
  86. package/dist/shared/monitoring/memory-manager.js +382 -0
  87. package/dist/shared/monitoring/production-monitor.js +390 -0
  88. package/dist/shared/production-tester/api-tester.js +80 -0
  89. package/dist/shared/production-tester/auth-tester.js +129 -0
  90. package/dist/shared/production-tester/core.js +217 -0
  91. package/dist/shared/production-tester/database-tester.js +105 -0
  92. package/dist/shared/production-tester/index.js +74 -0
  93. package/dist/shared/production-tester/load-tester.js +120 -0
  94. package/dist/shared/production-tester/performance-tester.js +105 -0
  95. package/dist/shared/security/api-token-manager.js +296 -0
  96. package/dist/shared/security/index.js +8 -0
  97. package/dist/shared/security/secret-generator.js +918 -0
  98. package/dist/shared/security/secure-token-manager.js +379 -0
  99. package/dist/shared/utils/error-recovery.js +240 -0
  100. package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
  101. package/dist/shared/utils/index.js +9 -0
  102. package/dist/shared/utils/interactive-prompts.js +134 -0
  103. package/dist/shared/utils/rate-limiter.js +249 -0
  104. package/dist/utils/ErrorHandler.js +173 -0
  105. package/dist/utils/deployment/config-cache.js +1160 -0
  106. package/dist/utils/deployment/index.js +6 -0
  107. package/dist/utils/deployment/interactive-prompts.js +97 -0
  108. package/dist/utils/deployment/secret-generator.js +896 -0
  109. package/dist/utils/dirname-helper.js +35 -0
  110. package/dist/utils/domain-config.js +159 -0
  111. package/dist/utils/error-recovery.js +240 -0
  112. package/dist/utils/esm-helper.js +52 -0
  113. package/dist/utils/framework-config.js +481 -0
  114. package/dist/utils/graceful-shutdown-manager.js +379 -0
  115. package/dist/utils/health-checker.js +114 -0
  116. package/dist/utils/index.js +36 -0
  117. package/dist/utils/prompt-handler.js +98 -0
  118. package/dist/utils/usage-tracker.js +252 -0
  119. package/dist/utils/validation.js +112 -0
  120. package/dist/version/VersionDetector.js +723 -0
  121. package/dist/worker/index.js +4 -0
  122. package/dist/worker/integration.js +332 -0
  123. package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
  124. package/docs/INTEGRATION_GUIDE.md +2045 -0
  125. package/docs/README.md +82 -0
  126. package/docs/SECURITY.md +242 -0
  127. package/docs/deployment/deployment-guide.md +540 -0
  128. package/docs/overview.md +280 -0
  129. package/package.json +176 -0
  130. package/types/index.d.ts +575 -0
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Graceful Shutdown Manager
3
+ * Implements SIGTERM/SIGINT handlers and cleanup routines for CLI tools
4
+ */
5
+
6
+ export class GracefulShutdownManager {
7
+ constructor(options = {}) {
8
+ this.options = options;
9
+ this.config = null;
10
+ this.isShuttingDown = false;
11
+ this.shutdownHandlers = [];
12
+ this.shutdownPromise = null;
13
+ this.registered = false;
14
+ }
15
+
16
+ /**
17
+ * Initialize with framework configuration
18
+ */
19
+ async initialize() {
20
+ // Import framework config for consistent timing
21
+ const {
22
+ frameworkConfig
23
+ } = await import('../../../src/utils/framework-config.js');
24
+ const timing = frameworkConfig.getTiming();
25
+ this.config = {
26
+ shutdownTimeout: this.options.shutdownTimeout || timing.shutdownTimeout,
27
+ forceShutdownTimeout: this.options.forceShutdownTimeout || timing.forceShutdownTimeout,
28
+ enableLogging: this.options.enableLogging !== false,
29
+ ...this.options
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Register the shutdown manager
35
+ */
36
+ register() {
37
+ if (this.registered) return;
38
+ this.registered = true;
39
+
40
+ // Register signal handlers
41
+ process.on('SIGTERM', () => this.initiateShutdown('SIGTERM'));
42
+ process.on('SIGINT', () => this.initiateShutdown('SIGINT'));
43
+
44
+ // Handle uncaught exceptions and unhandled rejections
45
+ process.on('uncaughtException', error => {
46
+ console.error('💥 Uncaught exception:', error);
47
+ this.initiateShutdown('uncaughtException', error);
48
+ });
49
+ process.on('unhandledRejection', (reason, promise) => {
50
+ console.error('💥 Unhandled rejection at:', promise, 'reason:', reason);
51
+ this.initiateShutdown('unhandledRejection', reason);
52
+ });
53
+ if (this.config.enableLogging) {
54
+ console.log('🛑 Graceful shutdown manager registered');
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Unregister the shutdown manager
60
+ */
61
+ unregister() {
62
+ if (!this.registered) return;
63
+ this.registered = false;
64
+ process.removeAllListeners('SIGTERM');
65
+ process.removeAllListeners('SIGINT');
66
+ process.removeAllListeners('uncaughtException');
67
+ process.removeAllListeners('unhandledRejection');
68
+ if (this.config.enableLogging) {
69
+ console.log('🛑 Graceful shutdown manager unregistered');
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Add a shutdown handler
75
+ */
76
+ addShutdownHandler(handler, priority = 0) {
77
+ this.shutdownHandlers.push({
78
+ handler,
79
+ priority,
80
+ id: `handler_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
81
+ });
82
+
83
+ // Sort by priority (higher priority first)
84
+ this.shutdownHandlers.sort((a, b) => b.priority - a.priority);
85
+
86
+ // Return removal function
87
+ return () => {
88
+ const index = this.shutdownHandlers.findIndex(h => h.id === this.id);
89
+ if (index !== -1) {
90
+ this.shutdownHandlers.splice(index, 1);
91
+ }
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Remove a shutdown handler
97
+ */
98
+ removeShutdownHandler(handlerId) {
99
+ const index = this.shutdownHandlers.findIndex(h => h.id === handlerId);
100
+ if (index !== -1) {
101
+ this.shutdownHandlers.splice(index, 1);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Initiate graceful shutdown
107
+ */
108
+ async initiateShutdown(signal, error = null) {
109
+ if (this.isShuttingDown) return;
110
+ this.isShuttingDown = true;
111
+ if (this.config.enableLogging) {
112
+ console.log(`🛑 Initiating graceful shutdown: ${signal}`);
113
+ if (error) {
114
+ console.error('Shutdown triggered by error:', error);
115
+ }
116
+ }
117
+
118
+ // Prevent new work from starting
119
+ this.preventNewWork();
120
+
121
+ // Execute shutdown handlers
122
+ await this.executeShutdownHandlers(signal, error);
123
+
124
+ // Force exit if handlers don't complete in time
125
+ this.forceExitIfNeeded(signal);
126
+
127
+ // Exit with appropriate code
128
+ const exitCode = error ? 1 : 0;
129
+ process.exit(exitCode);
130
+ }
131
+
132
+ /**
133
+ * Prevent new work from starting
134
+ */
135
+ preventNewWork() {
136
+ // Set global shutdown flag
137
+ if (typeof global !== 'undefined') {
138
+ global.isShuttingDown = true;
139
+ }
140
+
141
+ // Prevent new database connections
142
+ if (typeof global !== 'undefined' && global.dbManagers) {
143
+ global.dbManagers.forEach(manager => {
144
+ if (typeof manager.preventNewConnections === 'function') {
145
+ manager.preventNewConnections();
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Execute shutdown handlers
153
+ */
154
+ async executeShutdownHandlers(signal, error) {
155
+ const shutdownPromises = [];
156
+ for (const {
157
+ handler
158
+ } of this.shutdownHandlers) {
159
+ try {
160
+ const promise = handler(signal, error);
161
+ if (promise && typeof promise.then === 'function') {
162
+ shutdownPromises.push(promise);
163
+ }
164
+ } catch (handlerError) {
165
+ console.error('Error in shutdown handler:', handlerError);
166
+ }
167
+ }
168
+
169
+ // Wait for all handlers to complete or timeout
170
+ if (shutdownPromises.length > 0) {
171
+ try {
172
+ await Promise.race([Promise.all(shutdownPromises), new Promise(resolve => setTimeout(resolve, this.config.shutdownTimeout))]);
173
+ } catch (timeoutError) {
174
+ console.error('Shutdown handlers timed out:', timeoutError);
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Force exit if needed
181
+ */
182
+ forceExitIfNeeded(signal) {
183
+ setTimeout(() => {
184
+ console.error(`💥 Force exiting after ${this.config.forceShutdownTimeout}ms`);
185
+ process.exit(1);
186
+ }, this.config.forceShutdownTimeout).unref();
187
+ }
188
+
189
+ /**
190
+ * Create a shutdown-aware interval
191
+ */
192
+ createShutdownAwareInterval(callback, delay) {
193
+ const intervalId = setInterval(() => {
194
+ if (this.isShuttingDown) {
195
+ clearInterval(intervalId);
196
+ return;
197
+ }
198
+ try {
199
+ callback();
200
+ } catch (error) {
201
+ console.error('Error in shutdown-aware interval:', error);
202
+ clearInterval(intervalId);
203
+ }
204
+ }, delay);
205
+ intervalId.unref?.();
206
+
207
+ // Add cleanup handler
208
+ this.addShutdownHandler(() => {
209
+ clearInterval(intervalId);
210
+ }, 100); // High priority
211
+
212
+ return intervalId;
213
+ }
214
+
215
+ /**
216
+ * Create a shutdown-aware timeout
217
+ */
218
+ createShutdownAwareTimeout(callback, delay) {
219
+ const timeoutId = setTimeout(() => {
220
+ if (this.isShuttingDown) return;
221
+ try {
222
+ callback();
223
+ } catch (error) {
224
+ console.error('Error in shutdown-aware timeout:', error);
225
+ }
226
+ }, delay);
227
+ timeoutId.unref?.();
228
+
229
+ // Add cleanup handler
230
+ this.addShutdownHandler(() => {
231
+ clearTimeout(timeoutId);
232
+ }, 100); // High priority
233
+
234
+ return timeoutId;
235
+ }
236
+
237
+ /**
238
+ * Wait for shutdown signal
239
+ */
240
+ async waitForShutdownSignal() {
241
+ return new Promise(resolve => {
242
+ const handler = signal => {
243
+ resolve(signal);
244
+ };
245
+ process.once('SIGTERM', () => handler('SIGTERM'));
246
+ process.once('SIGINT', () => handler('SIGINT'));
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Get shutdown status
252
+ */
253
+ getShutdownStatus() {
254
+ return {
255
+ isShuttingDown: this.isShuttingDown,
256
+ handlerCount: this.shutdownHandlers.length,
257
+ registered: this.registered,
258
+ config: this.config
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Create database cleanup handler
264
+ */
265
+ createDatabaseCleanupHandler(dbManager) {
266
+ return async signal => {
267
+ if (dbManager && typeof dbManager.closeAllConnections === 'function') {
268
+ console.log('🗃️ Closing database connections...');
269
+ await dbManager.closeAllConnections();
270
+ console.log('✅ Database connections closed');
271
+ }
272
+ };
273
+ }
274
+
275
+ /**
276
+ * Create monitoring cleanup handler
277
+ */
278
+ createMonitoringCleanupHandler(monitor) {
279
+ return async signal => {
280
+ if (monitor && typeof monitor.stopMonitoring === 'function') {
281
+ console.log('📊 Stopping monitoring...');
282
+ await monitor.stopMonitoring();
283
+ console.log('✅ Monitoring stopped');
284
+ }
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Create token cleanup handler
290
+ */
291
+ createTokenCleanupHandler(tokenManager) {
292
+ return async signal => {
293
+ // Token manager doesn't need special cleanup, but we could log the event
294
+ console.log('🔐 Token manager cleanup completed');
295
+ };
296
+ }
297
+
298
+ /**
299
+ * Setup standard handlers for common services
300
+ */
301
+ setupStandardHandlers(services = {}) {
302
+ const {
303
+ dbManager,
304
+ monitor,
305
+ tokenManager,
306
+ memoryManager
307
+ } = services;
308
+
309
+ // Database cleanup (high priority)
310
+ if (dbManager) {
311
+ this.addShutdownHandler(this.createDatabaseCleanupHandler(dbManager), 1000);
312
+ }
313
+
314
+ // Monitoring cleanup (medium priority)
315
+ if (monitor) {
316
+ this.addShutdownHandler(this.createMonitoringCleanupHandler(monitor), 500);
317
+ }
318
+
319
+ // Memory cleanup (medium priority)
320
+ if (memoryManager) {
321
+ this.addShutdownHandler(async signal => {
322
+ console.log('🧠 Stopping memory monitoring...');
323
+ memoryManager.stopMonitoring();
324
+ console.log('✅ Memory monitoring stopped');
325
+ }, 500);
326
+ }
327
+
328
+ // Token cleanup (low priority)
329
+ if (tokenManager) {
330
+ this.addShutdownHandler(this.createTokenCleanupHandler(tokenManager), 100);
331
+ }
332
+
333
+ // Final cleanup (lowest priority)
334
+ this.addShutdownHandler(async signal => {
335
+ console.log('🧹 Running final cleanup...');
336
+ // Force garbage collection if available
337
+ if (global.gc) {
338
+ global.gc();
339
+ }
340
+ console.log('✅ Final cleanup completed');
341
+ }, 1);
342
+ }
343
+ }
344
+
345
+ // Global shutdown manager instance
346
+ let globalShutdownManager = null;
347
+
348
+ /**
349
+ * Get the global shutdown manager instance
350
+ */
351
+ export function getShutdownManager(options = {}) {
352
+ if (!globalShutdownManager) {
353
+ globalShutdownManager = new GracefulShutdownManager(options);
354
+ }
355
+ return globalShutdownManager;
356
+ }
357
+
358
+ /**
359
+ * Initialize graceful shutdown handling
360
+ */
361
+ export async function initializeGracefulShutdown(services = {}, options = {}) {
362
+ const manager = getShutdownManager(options);
363
+ await manager.initialize();
364
+ manager.setupStandardHandlers(services);
365
+ manager.register();
366
+ return manager;
367
+ }
368
+
369
+ /**
370
+ * Quick shutdown for simple scripts
371
+ */
372
+ export async function withGracefulShutdown(callback, services = {}, options = {}) {
373
+ const manager = initializeGracefulShutdown(services, options);
374
+ try {
375
+ await callback();
376
+ } catch (error) {
377
+ console.error('Error in main execution:', error);
378
+ await manager.initiateShutdown('error', error);
379
+ }
380
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Utilities Module
3
+ * Exports all general utility functions and managers
4
+ */
5
+
6
+ export { InteractivePrompts } from './interactive-prompts.js';
7
+ export { ErrorRecovery } from './error-recovery.js';
8
+ export { GracefulShutdownManager } from './graceful-shutdown-manager.js';
9
+ export { RateLimiter } from './rate-limiter.js';
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Interactive Prompts Module
3
+ * Standardized user input functions for all scripts
4
+ *
5
+ * Replaces duplicate prompt code across 10+ scripts
6
+ */
7
+
8
+ import readline from 'readline';
9
+
10
+ // Create readline interface (singleton)
11
+ let rl = null;
12
+ function getReadlineInterface() {
13
+ if (!rl) {
14
+ rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout
17
+ });
18
+ }
19
+ return rl;
20
+ }
21
+ export function askUser(question, defaultValue = null) {
22
+ return new Promise(resolve => {
23
+ const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
24
+ getReadlineInterface().question(prompt, answer => {
25
+ resolve(answer.trim() || defaultValue);
26
+ });
27
+ });
28
+ }
29
+ export function askYesNo(question, defaultValue = 'n') {
30
+ return new Promise(resolve => {
31
+ const prompt = `${question} [y/N]: `;
32
+ getReadlineInterface().question(prompt, answer => {
33
+ const response = answer.trim().toLowerCase() || defaultValue;
34
+ resolve(response === 'y' || response === 'yes');
35
+ });
36
+ });
37
+ }
38
+ export function askChoice(question, choices, defaultIndex = 0) {
39
+ return new Promise(resolve => {
40
+ console.log(`\n${question}`);
41
+ choices.forEach((choice, index) => {
42
+ const marker = index === defaultIndex ? '>' : ' ';
43
+ console.log(`${marker} ${index + 1}. ${choice}`);
44
+ });
45
+ getReadlineInterface().question(`\nSelect option [1-${choices.length}]: `, answer => {
46
+ const choice = parseInt(answer) - 1;
47
+ if (isNaN(choice) || choice < 0 || choice >= choices.length) {
48
+ resolve(defaultIndex);
49
+ } else {
50
+ resolve(choice);
51
+ }
52
+ });
53
+ });
54
+ }
55
+ export function askMultiChoice(question, choices, defaultIndices = []) {
56
+ return new Promise(resolve => {
57
+ console.log(`\n${question}`);
58
+ console.log('(Enter comma-separated numbers, e.g., "1,3,5")');
59
+ choices.forEach((choice, index) => {
60
+ const marker = defaultIndices.includes(index) ? '>' : ' ';
61
+ console.log(`${marker} ${index + 1}. ${choice}`);
62
+ });
63
+ const defaultStr = defaultIndices.map(i => i + 1).join(',');
64
+ getReadlineInterface().question(`\nSelect options [${defaultStr}]: `, answer => {
65
+ if (!answer.trim()) {
66
+ resolve(defaultIndices);
67
+ return;
68
+ }
69
+ const selected = answer.split(',').map(s => parseInt(s.trim()) - 1).filter(i => !isNaN(i) && i >= 0 && i < choices.length);
70
+ resolve(selected.length > 0 ? selected : defaultIndices);
71
+ });
72
+ });
73
+ }
74
+ export function closePrompts() {
75
+ if (rl) {
76
+ rl.close();
77
+ rl = null;
78
+ }
79
+ }
80
+
81
+ // Utility function for progress indicators
82
+ export function showProgress(message, steps = ['⏳', '⚡', '✅']) {
83
+ return new Promise(resolve => {
84
+ let stepIndex = 0;
85
+ const interval = setInterval(() => {
86
+ process.stdout.write(`\r${steps[stepIndex]} ${message}`);
87
+ stepIndex = (stepIndex + 1) % steps.length;
88
+ }, 500);
89
+
90
+ // Auto-resolve after 2 seconds or when manually resolved
91
+ setTimeout(() => {
92
+ clearInterval(interval);
93
+ process.stdout.write(`\r✅ ${message}\n`);
94
+ resolve();
95
+ }, 2000);
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Ask for sensitive input (like API tokens) with hidden input
101
+ */
102
+ export function askPassword(question) {
103
+ return new Promise(resolve => {
104
+ const prompt = `${question}: `;
105
+ process.stdout.write(prompt);
106
+
107
+ // Hide input for sensitive data
108
+ process.stdin.setRawMode(true);
109
+ process.stdin.resume();
110
+ let password = '';
111
+ const onData = char => {
112
+ const charCode = char[0];
113
+ if (charCode === 13) {
114
+ // Enter key
115
+ process.stdin.setRawMode(false);
116
+ process.stdin.pause();
117
+ process.stdin.removeListener('data', onData);
118
+ process.stdout.write('\n');
119
+ resolve(password);
120
+ } else if (charCode === 127 || charCode === 8) {
121
+ // Backspace
122
+ if (password.length > 0) {
123
+ password = password.slice(0, -1);
124
+ process.stdout.write('\b \b');
125
+ }
126
+ } else if (charCode >= 32 && charCode <= 126) {
127
+ // Printable characters
128
+ password += char.toString();
129
+ process.stdout.write('*');
130
+ }
131
+ };
132
+ process.stdin.on('data', onData);
133
+ });
134
+ }