ralphblaster-agent 0.1.1

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/bin/agent-dashboard.sh +168 -0
  4. package/bin/monitor-agent.sh +264 -0
  5. package/bin/ralphblaster.js +247 -0
  6. package/package.json +64 -0
  7. package/postinstall-colored.js +66 -0
  8. package/src/api-client.js +764 -0
  9. package/src/claude-plugin/.claude-plugin/plugin.json +9 -0
  10. package/src/claude-plugin/README.md +42 -0
  11. package/src/claude-plugin/skills/ralph/SKILL.md +259 -0
  12. package/src/commands/add-project.js +257 -0
  13. package/src/commands/init.js +79 -0
  14. package/src/config-file-manager.js +84 -0
  15. package/src/config.js +66 -0
  16. package/src/error-window.js +86 -0
  17. package/src/executor/claude-runner.js +716 -0
  18. package/src/executor/error-handler.js +65 -0
  19. package/src/executor/git-helper.js +196 -0
  20. package/src/executor/index.js +296 -0
  21. package/src/executor/job-handlers/clarifying-questions.js +213 -0
  22. package/src/executor/job-handlers/code-execution.js +145 -0
  23. package/src/executor/job-handlers/prd-generation.js +259 -0
  24. package/src/executor/path-helper.js +74 -0
  25. package/src/executor/prompt-validator.js +51 -0
  26. package/src/executor.js +4 -0
  27. package/src/index.js +342 -0
  28. package/src/logger.js +193 -0
  29. package/src/logging/README.md +93 -0
  30. package/src/logging/config.js +179 -0
  31. package/src/logging/destinations/README.md +290 -0
  32. package/src/logging/destinations/api-destination-unbatched.js +118 -0
  33. package/src/logging/destinations/api-destination.js +40 -0
  34. package/src/logging/destinations/base-destination.js +85 -0
  35. package/src/logging/destinations/batched-destination.js +198 -0
  36. package/src/logging/destinations/console-destination.js +172 -0
  37. package/src/logging/destinations/file-destination.js +208 -0
  38. package/src/logging/destinations/index.js +29 -0
  39. package/src/logging/destinations/progress-batch-destination-unbatched.js +92 -0
  40. package/src/logging/destinations/progress-batch-destination.js +41 -0
  41. package/src/logging/formatter.js +288 -0
  42. package/src/logging/log-manager.js +426 -0
  43. package/src/progress-throttle.js +101 -0
  44. package/src/system-monitor.js +64 -0
  45. package/src/utils/format.js +16 -0
  46. package/src/utils/log-file-helper.js +265 -0
  47. package/src/utils/progress-parser.js +250 -0
  48. package/src/worktree-manager.js +255 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * BaseDestination - Abstract base class for log destinations
3
+ *
4
+ * Defines the common interface that all log destinations must implement.
5
+ * Each destination handles writing logs to a specific output (console, file, API, etc.)
6
+ * and manages its own lifecycle (initialization, flushing, cleanup).
7
+ *
8
+ * Destinations are pluggable and independent - they can be added or removed without
9
+ * affecting other destinations.
10
+ */
11
+ class BaseDestination {
12
+ /**
13
+ * Create a new log destination
14
+ * @param {Object} [config={}] - Destination-specific configuration
15
+ */
16
+ constructor(config = {}) {
17
+ this.config = config;
18
+ this.isShuttingDown = false;
19
+ }
20
+
21
+ /**
22
+ * Write a log entry to this destination
23
+ * Must be implemented by subclasses.
24
+ * @param {string} level - Log level ('error', 'warn', 'info', 'debug')
25
+ * @param {string} message - Log message
26
+ * @param {Object} [metadata={}] - Structured metadata for filtering/searching
27
+ * @returns {Promise<void>}
28
+ * @abstract
29
+ */
30
+ async write(level, message, metadata = {}) {
31
+ throw new Error('write() must be implemented by subclass');
32
+ }
33
+
34
+ /**
35
+ * Flush any buffered logs
36
+ * Some destinations (like API batchers) buffer logs for efficiency.
37
+ * This ensures all pending logs are written immediately.
38
+ * @returns {Promise<void>}
39
+ */
40
+ async flush() {
41
+ // Default implementation - no buffering
42
+ // Override in subclasses that implement buffering
43
+ }
44
+
45
+ /**
46
+ * Close the destination and release resources
47
+ * Called during shutdown to clean up connections, streams, timers, etc.
48
+ * Should call flush() to ensure no logs are lost.
49
+ * @returns {Promise<void>}
50
+ */
51
+ async close() {
52
+ this.isShuttingDown = true;
53
+ await this.flush();
54
+ }
55
+
56
+ /**
57
+ * Check if this destination should accept a log at the given level
58
+ * Allows destinations to filter logs based on their own criteria.
59
+ * @param {string} level - Log level to check
60
+ * @returns {boolean} True if this destination should handle this level
61
+ */
62
+ shouldLog(level) {
63
+ // Default: accept all levels
64
+ // Override in subclasses to implement filtering
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Handle errors that occur during write operations
70
+ * Provides a consistent error handling strategy across destinations.
71
+ * By default, errors are silently caught to prevent log failures from
72
+ * disrupting the application.
73
+ * @param {Error} error - The error that occurred
74
+ * @param {string} level - Log level of the failed write
75
+ * @param {string} message - Log message of the failed write
76
+ * @protected
77
+ */
78
+ handleError(error, level, message) {
79
+ // Silent by default to prevent cascading failures
80
+ // Subclasses can override to implement error reporting
81
+ // (e.g., console.error for console destination)
82
+ }
83
+ }
84
+
85
+ module.exports = BaseDestination;
@@ -0,0 +1,198 @@
1
+ const BaseDestination = require('./base-destination');
2
+
3
+ /**
4
+ * BatchedDestination - Generic batching wrapper for any log destination
5
+ *
6
+ * Wraps any destination implementing the BaseDestination interface and adds batching
7
+ * capabilities. This reduces overhead for destinations that benefit from batched sends
8
+ * (e.g., network-based destinations like API, syslog, etc.).
9
+ *
10
+ * Key features:
11
+ * - Buffers logs and sends in batches to reduce overhead
12
+ * - Automatic flush on buffer size or time interval
13
+ * - Immediate flush on shutdown to prevent log loss
14
+ * - Graceful fallback to individual sends if batch send fails
15
+ * - Composable - can wrap any destination that implements write()
16
+ *
17
+ * Example usage:
18
+ * ```javascript
19
+ * const apiDestination = new ApiDestination(config);
20
+ * const batchedApi = new BatchedDestination(apiDestination, {
21
+ * maxBatchSize: 10,
22
+ * flushInterval: 2000
23
+ * });
24
+ * ```
25
+ */
26
+ class BatchedDestination extends BaseDestination {
27
+ /**
28
+ * Create a new batched destination wrapper
29
+ * @param {BaseDestination} destination - The underlying destination to wrap
30
+ * @param {Object} [config={}] - Batching configuration
31
+ * @param {number} [config.maxBatchSize=10] - Maximum logs to buffer before flushing
32
+ * @param {number} [config.flushInterval=2000] - Interval in ms to auto-flush buffered logs
33
+ * @param {boolean} [config.useBatchSend=true] - Whether to try batch sending (via sendBatch)
34
+ */
35
+ constructor(destination, config = {}) {
36
+ super(config);
37
+
38
+ if (!destination) {
39
+ throw new Error('BatchedDestination requires a destination to wrap');
40
+ }
41
+
42
+ this.destination = destination;
43
+ this.buffer = [];
44
+
45
+ // Configuration
46
+ this.maxBatchSize = config.maxBatchSize || 10;
47
+ this.flushInterval = config.flushInterval || 2000; // 2 seconds
48
+ this.useBatchSend = config.useBatchSend !== false; // Default true
49
+
50
+ // Start automatic flush timer
51
+ this.flushTimer = setInterval(() => this.flush(), this.flushInterval);
52
+
53
+ // Track if we're shutting down
54
+ this.isShuttingDown = false;
55
+ }
56
+
57
+ /**
58
+ * Add a log entry to the buffer
59
+ * If buffer is full, automatically flushes.
60
+ * If shutting down, sends immediately without batching.
61
+ * @param {string} level - Log level ('error', 'warn', 'info', 'debug')
62
+ * @param {string} message - Log message
63
+ * @param {Object} [metadata={}] - Structured metadata
64
+ * @returns {Promise<void>}
65
+ */
66
+ async write(level, message, metadata = {}) {
67
+ if (this.isShuttingDown) {
68
+ // If shutting down, send immediately without batching
69
+ try {
70
+ await this.destination.write(level, message, metadata);
71
+ } catch (error) {
72
+ this.handleError(error, level, message);
73
+ }
74
+ return;
75
+ }
76
+
77
+ const logEntry = {
78
+ timestamp: new Date().toISOString(),
79
+ level,
80
+ message,
81
+ metadata: metadata && Object.keys(metadata).length > 0 ? metadata : undefined
82
+ };
83
+
84
+ this.buffer.push(logEntry);
85
+
86
+ // Flush if buffer is full
87
+ if (this.buffer.length >= this.maxBatchSize) {
88
+ await this.flush();
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Flush buffered logs to the wrapped destination
94
+ * Tries batch send first (if destination supports it), falls back to individual sends.
95
+ * Safe to call when buffer is empty (no-op).
96
+ * @returns {Promise<void>}
97
+ */
98
+ async flush() {
99
+ if (this.buffer.length === 0) return;
100
+
101
+ const batch = [...this.buffer];
102
+ this.buffer = [];
103
+
104
+ try {
105
+ // Check if destination supports batch sending
106
+ if (this.useBatchSend && typeof this.destination.sendBatch === 'function') {
107
+ // Try batch send (more efficient)
108
+ await this.destination.sendBatch(batch);
109
+ } else {
110
+ // Fall back to individual sends
111
+ await this.sendIndividually(batch);
112
+ }
113
+ } catch (error) {
114
+ // If batch send fails, try individual sends as fallback
115
+ try {
116
+ await this.sendIndividually(batch);
117
+ } catch (fallbackError) {
118
+ this.handleError(fallbackError, 'error', 'Failed to send buffered logs');
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Send logs individually (fallback method)
125
+ * Used when batch sending fails or is unavailable.
126
+ * Silently fails individual log sends to prevent cascading errors.
127
+ * @param {Array<Object>} logs - Array of log entries with {timestamp, level, message, metadata}
128
+ * @returns {Promise<void>}
129
+ * @private
130
+ */
131
+ async sendIndividually(logs) {
132
+ const promises = logs.map(log =>
133
+ this.destination.write(log.level, log.message, log.metadata)
134
+ .catch((error) => {
135
+ this.handleError(error, log.level, log.message);
136
+ })
137
+ );
138
+
139
+ await Promise.all(promises);
140
+ }
141
+
142
+ /**
143
+ * Shutdown the batcher and flush remaining logs
144
+ * Stops the automatic flush timer and ensures all buffered logs are sent.
145
+ * @returns {Promise<void>}
146
+ */
147
+ async close() {
148
+ this.isShuttingDown = true;
149
+
150
+ // Stop automatic flush timer
151
+ if (this.flushTimer) {
152
+ clearInterval(this.flushTimer);
153
+ this.flushTimer = null;
154
+ }
155
+
156
+ // Flush any remaining logs
157
+ await this.flush();
158
+
159
+ // Close the wrapped destination
160
+ if (this.destination && typeof this.destination.close === 'function') {
161
+ await this.destination.close();
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Get current buffer size (for testing/debugging)
167
+ * @returns {number} Number of logs currently buffered
168
+ */
169
+ getBufferSize() {
170
+ return this.buffer.length;
171
+ }
172
+
173
+ /**
174
+ * Check if this destination should accept a log at the given level
175
+ * Delegates to the wrapped destination
176
+ * @param {string} level - Log level to check
177
+ * @returns {boolean} True if this destination should handle this level
178
+ */
179
+ shouldLog(level) {
180
+ return this.destination.shouldLog(level);
181
+ }
182
+
183
+ /**
184
+ * Handle errors during batching operations
185
+ * @param {Error} error - The error that occurred
186
+ * @param {string} level - Log level of failed write
187
+ * @param {string} message - Log message of failed write
188
+ * @protected
189
+ */
190
+ handleError(error, level, message) {
191
+ // Delegate to wrapped destination's error handling
192
+ if (typeof this.destination.handleError === 'function') {
193
+ this.destination.handleError(error, level, message);
194
+ }
195
+ }
196
+ }
197
+
198
+ module.exports = BatchedDestination;
@@ -0,0 +1,172 @@
1
+ const BaseDestination = require('./base-destination');
2
+ const {
3
+ formatMessage,
4
+ formatLevel,
5
+ formatMetadata,
6
+ redactSensitiveData,
7
+ formatConsoleData
8
+ } = require('../formatter');
9
+
10
+ /**
11
+ * ConsoleDestination - Outputs logs to console (stdout/stderr)
12
+ *
13
+ * Formats logs for human-readable console output with optional colors.
14
+ * Supports both pretty (formatted) and JSON output modes.
15
+ * Does not buffer - writes immediately for real-time visibility.
16
+ */
17
+ class ConsoleDestination extends BaseDestination {
18
+ /**
19
+ * Create a new console destination
20
+ * @param {Object} [config={}] - Configuration options
21
+ * @param {boolean} [config.colors=true] - Enable colored output
22
+ * @param {string} [config.format='pretty'] - Output format: 'pretty' or 'json'
23
+ * @param {string} [config.minLevel='info'] - Minimum log level to output
24
+ */
25
+ constructor(config = {}) {
26
+ super(config);
27
+
28
+ this.colors = config.colors !== false;
29
+ this.format = config.format || 'pretty';
30
+ this.minLevel = config.minLevel || 'info';
31
+
32
+ // Log level priorities for filtering
33
+ this.levelPriorities = {
34
+ error: 0,
35
+ warn: 1,
36
+ info: 2,
37
+ debug: 3
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Check if this destination should log at the given level
43
+ * @param {string} level - Log level to check
44
+ * @returns {boolean} True if should log
45
+ */
46
+ shouldLog(level) {
47
+ const levelPriority = this.levelPriorities[level];
48
+ const minPriority = this.levelPriorities[this.minLevel];
49
+
50
+ if (levelPriority === undefined || minPriority === undefined) {
51
+ return false;
52
+ }
53
+
54
+ return levelPriority <= minPriority;
55
+ }
56
+
57
+ /**
58
+ * Write a log entry to console
59
+ * @param {string} level - Log level
60
+ * @param {string} message - Log message
61
+ * @param {Object} [metadata={}] - Structured metadata
62
+ * @returns {Promise<void>}
63
+ */
64
+ async write(level, message, metadata = {}) {
65
+ if (!this.shouldLog(level)) {
66
+ return;
67
+ }
68
+
69
+ try {
70
+ // Redact sensitive data before output
71
+ const safeMessage = redactSensitiveData(message);
72
+ const safeMetadata = redactSensitiveData(metadata);
73
+
74
+ let output;
75
+
76
+ if (this.format === 'json') {
77
+ output = this.formatJson(level, safeMessage, safeMetadata);
78
+ } else {
79
+ output = this.formatPretty(level, safeMessage, safeMetadata);
80
+ }
81
+
82
+ // Write to stderr for errors, stdout for everything else
83
+ if (level === 'error') {
84
+ console.error(output);
85
+ } else {
86
+ console.log(output);
87
+ }
88
+ } catch (error) {
89
+ this.handleError(error, level, message);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Format log entry as JSON
95
+ * @param {string} level - Log level
96
+ * @param {string} message - Log message
97
+ * @param {Object} metadata - Log metadata
98
+ * @returns {string} JSON formatted log
99
+ * @private
100
+ */
101
+ formatJson(level, message, metadata) {
102
+ const entry = {
103
+ timestamp: new Date().toISOString(),
104
+ level,
105
+ message
106
+ };
107
+
108
+ // Add metadata if present
109
+ if (metadata && Object.keys(metadata).length > 0) {
110
+ entry.metadata = metadata;
111
+ }
112
+
113
+ return formatMetadata(entry, { indent: 0 });
114
+ }
115
+
116
+ /**
117
+ * Format log entry for pretty console output
118
+ * @param {string} level - Log level
119
+ * @param {string} message - Log message
120
+ * @param {Object} metadata - Log metadata
121
+ * @returns {string} Pretty formatted log
122
+ * @private
123
+ */
124
+ formatPretty(level, message, metadata) {
125
+ const timestamp = new Date().toISOString();
126
+ const formattedLevel = formatLevel(level, {
127
+ colors: this.colors,
128
+ uppercase: true
129
+ });
130
+
131
+ // Format message with metadata inline
132
+ const formattedMessage = formatMessage(message, metadata, {
133
+ includeMetadata: true,
134
+ maxFieldLength: 100
135
+ });
136
+
137
+ // Build output line
138
+ let output = `[${timestamp}] ${formattedLevel} ${formattedMessage}`;
139
+
140
+ // Add detailed metadata if present (excluding fields already shown inline)
141
+ const detailedData = formatConsoleData(metadata, {
142
+ indent: ' ',
143
+ maxKeys: 20,
144
+ maxValueLength: 200
145
+ });
146
+
147
+ if (detailedData) {
148
+ output += detailedData;
149
+ }
150
+
151
+ return output;
152
+ }
153
+
154
+ /**
155
+ * Handle errors during console writing
156
+ * Attempts to output error to stderr as last resort
157
+ * @param {Error} error - The error that occurred
158
+ * @param {string} level - Log level of failed write
159
+ * @param {string} message - Log message of failed write
160
+ * @protected
161
+ */
162
+ handleError(error, level, message) {
163
+ try {
164
+ // Try to output a minimal error message
165
+ console.error(`[ConsoleDestination Error] Failed to write log: ${error.message}`);
166
+ } catch {
167
+ // If even this fails, give up silently
168
+ }
169
+ }
170
+ }
171
+
172
+ module.exports = ConsoleDestination;
@@ -0,0 +1,208 @@
1
+ const BaseDestination = require('./base-destination');
2
+ const LogFileHelper = require('../../utils/log-file-helper');
3
+ const fs = require('fs');
4
+ const fsPromises = require('fs').promises;
5
+
6
+ /**
7
+ * FileDestination - Writes logs to job-specific log files
8
+ *
9
+ * Wraps LogFileHelper to provide file-based logging through the destination interface.
10
+ * Supports both streaming (for real-time output) and batch writing modes.
11
+ * Each job gets its own log file in the .rb-logs directory.
12
+ */
13
+ class FileDestination extends BaseDestination {
14
+ /**
15
+ * Create a new file destination
16
+ * @param {Object} config - Configuration options
17
+ * @param {string} config.workingDir - Working directory for log files
18
+ * @param {Object} config.job - Job object with id and task_title
19
+ * @param {number} config.startTime - Job start timestamp
20
+ * @param {string} config.jobType - Type of job (e.g., 'PRD Generation')
21
+ * @param {boolean} [config.useStream=true] - Use streaming mode vs batch writes
22
+ */
23
+ constructor(config) {
24
+ super(config);
25
+
26
+ if (!config.workingDir) {
27
+ throw new Error('FileDestination requires workingDir in config');
28
+ }
29
+ if (!config.job) {
30
+ throw new Error('FileDestination requires job in config');
31
+ }
32
+ if (!config.startTime) {
33
+ throw new Error('FileDestination requires startTime in config');
34
+ }
35
+ if (!config.jobType) {
36
+ throw new Error('FileDestination requires jobType in config');
37
+ }
38
+
39
+ this.workingDir = config.workingDir;
40
+ this.job = config.job;
41
+ this.startTime = config.startTime;
42
+ this.jobType = config.jobType;
43
+ this.useStream = config.useStream !== false;
44
+
45
+ // Will be initialized on first write
46
+ this.logFile = null;
47
+ this.logStream = null;
48
+ this.initialized = false;
49
+ }
50
+
51
+ /**
52
+ * Initialize log file/stream on first write
53
+ * @returns {Promise<void>}
54
+ * @private
55
+ */
56
+ async initialize() {
57
+ if (this.initialized) {
58
+ return;
59
+ }
60
+
61
+ try {
62
+ if (this.useStream) {
63
+ // Create log file with stream for real-time writing
64
+ const result = await LogFileHelper.createJobLogStream(
65
+ this.workingDir,
66
+ this.job,
67
+ this.startTime,
68
+ this.jobType
69
+ );
70
+ this.logFile = result.logFile;
71
+ this.logStream = result.logStream;
72
+ } else {
73
+ // For non-streaming mode, we'll use append operations
74
+ // Create initial log file with header
75
+ this.logFile = await LogFileHelper.createJobLogWithContent(
76
+ this.workingDir,
77
+ this.job,
78
+ this.startTime,
79
+ this.jobType,
80
+ '' // Empty content, we'll append logs
81
+ );
82
+ }
83
+
84
+ this.initialized = true;
85
+ } catch (error) {
86
+ this.handleError(error, 'error', 'Failed to initialize file destination');
87
+ throw error; // Re-throw to indicate initialization failure
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Write a log entry to the log file
93
+ * @param {string} level - Log level
94
+ * @param {string} message - Log message
95
+ * @param {Object} [metadata={}] - Structured metadata
96
+ * @returns {Promise<void>}
97
+ */
98
+ async write(level, message, metadata = {}) {
99
+ try {
100
+ // Initialize on first write
101
+ if (!this.initialized) {
102
+ await this.initialize();
103
+ }
104
+
105
+ // Format log entry
106
+ const timestamp = new Date().toISOString();
107
+ const formattedLevel = level.toUpperCase().padEnd(5);
108
+ let logLine = `[${timestamp}] ${formattedLevel} ${message}`;
109
+
110
+ // Add metadata if present
111
+ if (metadata && Object.keys(metadata).length > 0) {
112
+ const metadataStr = JSON.stringify(metadata);
113
+ logLine += ` | ${metadataStr}`;
114
+ }
115
+
116
+ logLine += '\n';
117
+
118
+ // Write to file
119
+ if (this.useStream && this.logStream) {
120
+ // Streaming mode - write immediately
121
+ this.logStream.write(logLine);
122
+ } else if (this.logFile) {
123
+ // Batch mode - append to file
124
+ await fsPromises.appendFile(this.logFile, logLine);
125
+ }
126
+ } catch (error) {
127
+ this.handleError(error, level, message);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Flush any buffered writes
133
+ * For streams, this is a no-op as writes are immediate.
134
+ * @returns {Promise<void>}
135
+ */
136
+ async flush() {
137
+ // Stream writes are immediate, no buffering to flush
138
+ // Non-stream mode uses fsPromises.appendFile which is also immediate
139
+ }
140
+
141
+ /**
142
+ * Close the log file and write completion footer
143
+ * @returns {Promise<void>}
144
+ */
145
+ async close() {
146
+ if (!this.initialized) {
147
+ return;
148
+ }
149
+
150
+ this.isShuttingDown = true;
151
+
152
+ try {
153
+ // Flush any pending writes
154
+ await this.flush();
155
+
156
+ // Write completion footer
157
+ if (this.useStream && this.logStream) {
158
+ LogFileHelper.writeCompletionFooterToStream(
159
+ this.logStream,
160
+ this.jobType,
161
+ {}
162
+ );
163
+ // Close the stream
164
+ await new Promise((resolve, reject) => {
165
+ this.logStream.end((err) => {
166
+ if (err) reject(err);
167
+ else resolve();
168
+ });
169
+ });
170
+ } else if (this.logFile) {
171
+ await LogFileHelper.writeCompletionFooter(
172
+ this.logFile,
173
+ this.jobType,
174
+ {}
175
+ );
176
+ }
177
+ } catch (error) {
178
+ this.handleError(error, 'error', 'Failed to close file destination');
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Get the log file path
184
+ * Useful for reporting or accessing the log file after job completion.
185
+ * @returns {string|null} Path to log file, or null if not initialized
186
+ */
187
+ getLogFilePath() {
188
+ return this.logFile;
189
+ }
190
+
191
+ /**
192
+ * Handle errors during file operations
193
+ * Outputs to console.error since we can't write to the log file
194
+ * @param {Error} error - The error that occurred
195
+ * @param {string} level - Log level of failed write
196
+ * @param {string} message - Log message of failed write
197
+ * @protected
198
+ */
199
+ handleError(error, level, message) {
200
+ // Can't log to file since that's what failed, use console instead
201
+ console.error(
202
+ `[FileDestination Error] Failed to write to log file: ${error.message}`,
203
+ `\nOriginal log: [${level}] ${message}`
204
+ );
205
+ }
206
+ }
207
+
208
+ module.exports = FileDestination;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Log Destinations Module
3
+ *
4
+ * Exports all available log destinations for easy importing.
5
+ * Each destination implements the BaseDestination interface and can be
6
+ * used independently or coordinated by LogManager.
7
+ */
8
+
9
+ const BaseDestination = require('./base-destination');
10
+ const ConsoleDestination = require('./console-destination');
11
+ const FileDestination = require('./file-destination');
12
+ const ApiDestination = require('./api-destination');
13
+ const BatchedDestination = require('./batched-destination');
14
+ const ApiDestinationUnbatched = require('./api-destination-unbatched');
15
+ const ProgressBatchDestination = require('./progress-batch-destination');
16
+ const ProgressBatchDestinationUnbatched = require('./progress-batch-destination-unbatched');
17
+ const LogManager = require('../log-manager');
18
+
19
+ module.exports = {
20
+ BaseDestination,
21
+ ConsoleDestination,
22
+ FileDestination,
23
+ ApiDestination,
24
+ BatchedDestination,
25
+ ApiDestinationUnbatched,
26
+ ProgressBatchDestination,
27
+ ProgressBatchDestinationUnbatched,
28
+ LogManager
29
+ };