thinkncollab-shell 0.0.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.
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Logger for ThinkNCollab Shell
3
+ */
4
+
5
+ const fs = require('fs-extra');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ class Logger {
10
+ constructor(options = {}) {
11
+ this.options = {
12
+ logDir: options.logDir || path.join(os.homedir(), '.thinkncollab', 'logs'),
13
+ level: options.level || 'info',
14
+ maxFiles: options.maxFiles || 5,
15
+ maxSize: options.maxSize || 10 * 1024 * 1024, // 10MB
16
+ ...options
17
+ };
18
+
19
+ // Ensure log directory exists
20
+ fs.ensureDirSync(this.options.logDir);
21
+
22
+ this.levels = {
23
+ debug: 0,
24
+ info: 1,
25
+ warn: 2,
26
+ error: 3
27
+ };
28
+
29
+ this.currentLogFile = this.getLogFileName();
30
+ }
31
+
32
+ /**
33
+ * Get log file name
34
+ */
35
+ getLogFileName() {
36
+ const date = new Date().toISOString().split('T')[0];
37
+ return path.join(this.options.logDir, `thinknsh-${date}.log`);
38
+ }
39
+
40
+ /**
41
+ * Write to log file
42
+ */
43
+ async write(level, message, data = null) {
44
+ if (this.levels[level] < this.levels[this.options.level]) {
45
+ return;
46
+ }
47
+
48
+ const logEntry = {
49
+ timestamp: new Date().toISOString(),
50
+ level,
51
+ message,
52
+ data,
53
+ pid: process.pid
54
+ };
55
+
56
+ const logLine = JSON.stringify(logEntry) + '\n';
57
+
58
+ try {
59
+ await fs.appendFile(this.currentLogFile, logLine);
60
+ await this.rotateIfNeeded();
61
+ } catch (error) {
62
+ console.error('Failed to write log:', error);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Rotate log files if needed
68
+ */
69
+ async rotateIfNeeded() {
70
+ try {
71
+ const stat = await fs.stat(this.currentLogFile);
72
+
73
+ if (stat.size > this.options.maxSize) {
74
+ // Rename current file
75
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
76
+ const rotatedFile = this.currentLogFile.replace('.log', `-${timestamp}.log`);
77
+ await fs.rename(this.currentLogFile, rotatedFile);
78
+
79
+ // Create new log file
80
+ this.currentLogFile = this.getLogFileName();
81
+
82
+ // Clean up old files
83
+ await this.cleanOldLogs();
84
+ }
85
+ } catch (error) {
86
+ // File might not exist yet
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Clean old log files
92
+ */
93
+ async cleanOldLogs() {
94
+ try {
95
+ const files = await fs.readdir(this.options.logDir);
96
+ const logFiles = files
97
+ .filter(f => f.startsWith('thinknsh-') && f.endsWith('.log'))
98
+ .map(f => ({
99
+ name: f,
100
+ path: path.join(this.options.logDir, f),
101
+ time: fs.statSync(path.join(this.options.logDir, f)).mtime.getTime()
102
+ }))
103
+ .sort((a, b) => b.time - a.time);
104
+
105
+ // Keep only maxFiles most recent
106
+ if (logFiles.length > this.options.maxFiles) {
107
+ const toDelete = logFiles.slice(this.options.maxFiles);
108
+ for (const file of toDelete) {
109
+ await fs.remove(file.path);
110
+ }
111
+ }
112
+ } catch (error) {
113
+ console.error('Failed to clean old logs:', error);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Debug log
119
+ */
120
+ debug(message, data = null) {
121
+ return this.write('debug', message, data);
122
+ }
123
+
124
+ /**
125
+ * Info log
126
+ */
127
+ info(message, data = null) {
128
+ return this.write('info', message, data);
129
+ }
130
+
131
+ /**
132
+ * Warn log
133
+ */
134
+ warn(message, data = null) {
135
+ return this.write('warn', message, data);
136
+ }
137
+
138
+ /**
139
+ * Error log
140
+ */
141
+ error(message, data = null) {
142
+ return this.write('error', message, data);
143
+ }
144
+
145
+ /**
146
+ * Get recent logs
147
+ */
148
+ async getRecentLogs(lines = 100) {
149
+ try {
150
+ if (!await fs.pathExists(this.currentLogFile)) {
151
+ return [];
152
+ }
153
+
154
+ const content = await fs.readFile(this.currentLogFile, 'utf8');
155
+ const logs = content
156
+ .split('\n')
157
+ .filter(line => line.trim())
158
+ .map(line => JSON.parse(line))
159
+ .slice(-lines);
160
+
161
+ return logs;
162
+ } catch (error) {
163
+ console.error('Failed to read logs:', error);
164
+ return [];
165
+ }
166
+ }
167
+ }
168
+
169
+ module.exports = Logger;