@agi-cli/server 0.1.67 → 0.1.68

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,204 @@
1
+ /**
2
+ * Centralized logging utility
3
+ *
4
+ * Provides structured logging with debug mode awareness.
5
+ * Replaces scattered console.log calls throughout the codebase.
6
+ */
7
+
8
+ import { isDebugEnabled, isTraceEnabled } from './debug-state';
9
+
10
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
11
+
12
+ /**
13
+ * Format a log message with optional metadata
14
+ */
15
+ function _formatMessage(
16
+ level: LogLevel,
17
+ message: string,
18
+ meta?: Record<string, unknown>,
19
+ ): string {
20
+ const timestamp = new Date().toISOString();
21
+ const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
22
+
23
+ if (meta && Object.keys(meta).length > 0) {
24
+ return `${prefix} ${message} ${JSON.stringify(meta)}`;
25
+ }
26
+
27
+ return `${prefix} ${message}`;
28
+ }
29
+
30
+ /**
31
+ * Log at debug level (only when debug mode is enabled)
32
+ */
33
+ export function debug(message: string, meta?: Record<string, unknown>): void {
34
+ if (!isDebugEnabled()) return;
35
+
36
+ try {
37
+ if (meta && Object.keys(meta).length > 0) {
38
+ console.log(`[debug] ${message}`, meta);
39
+ } else {
40
+ console.log(`[debug] ${message}`);
41
+ }
42
+ } catch {
43
+ // Silently fail
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Log informational messages
49
+ */
50
+ export function info(message: string, meta?: Record<string, unknown>): void {
51
+ try {
52
+ if (meta && Object.keys(meta).length > 0) {
53
+ console.log(`[info] ${message}`, meta);
54
+ } else {
55
+ console.log(`[info] ${message}`);
56
+ }
57
+ } catch {
58
+ // Silently fail
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Log warning messages
64
+ */
65
+ export function warn(message: string, meta?: Record<string, unknown>): void {
66
+ try {
67
+ if (meta && Object.keys(meta).length > 0) {
68
+ console.warn(`[warn] ${message}`, meta);
69
+ } else {
70
+ console.warn(`[warn] ${message}`);
71
+ }
72
+ } catch {
73
+ // Silently fail
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Log error messages (only in debug mode, stack trace only with --trace)
79
+ */
80
+ export function error(
81
+ message: string,
82
+ err?: unknown,
83
+ meta?: Record<string, unknown>,
84
+ ): void {
85
+ // Only log errors when debug mode is enabled
86
+ if (!isDebugEnabled()) return;
87
+
88
+ try {
89
+ const logMeta: Record<string, unknown> = { ...meta };
90
+
91
+ if (err) {
92
+ if (err instanceof Error) {
93
+ // Always show error name and message in debug mode
94
+ logMeta.error = {
95
+ name: err.name,
96
+ message: err.message,
97
+ };
98
+
99
+ // Show full stack trace only with --trace flag
100
+ if (isTraceEnabled() && err.stack) {
101
+ logMeta.error.stack = err.stack;
102
+ }
103
+ } else if (typeof err === 'string') {
104
+ logMeta.error = err;
105
+ } else if (err && typeof err === 'object') {
106
+ // For other error objects, try to extract useful info
107
+ const errObj = err as Record<string, unknown>;
108
+ logMeta.error = {
109
+ ...(typeof errObj.name === 'string' ? { name: errObj.name } : {}),
110
+ ...(typeof errObj.message === 'string'
111
+ ? { message: errObj.message }
112
+ : {}),
113
+ ...(typeof errObj.code === 'string' ? { code: errObj.code } : {}),
114
+ ...(typeof errObj.status === 'number'
115
+ ? { status: errObj.status }
116
+ : {}),
117
+ ...(typeof errObj.statusCode === 'number'
118
+ ? { statusCode: errObj.statusCode }
119
+ : {}),
120
+ };
121
+
122
+ // Include stack in trace mode
123
+ if (isTraceEnabled() && typeof errObj.stack === 'string') {
124
+ logMeta.error.stack = errObj.stack;
125
+ }
126
+ } else {
127
+ // Fallback for primitive types
128
+ logMeta.error = String(err);
129
+ }
130
+ }
131
+
132
+ if (Object.keys(logMeta).length > 0) {
133
+ console.error(`[error] ${message}`, logMeta);
134
+ } else {
135
+ console.error(`[error] ${message}`);
136
+ }
137
+ } catch (logErr) {
138
+ // Last resort: at least try to log something
139
+ try {
140
+ console.error(`[error] ${message} (logging failed:`, logErr, ')');
141
+ } catch {
142
+ // Give up silently
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Logger object with all methods
149
+ */
150
+ export const logger = {
151
+ debug,
152
+ info,
153
+ warn,
154
+ error,
155
+ };
156
+
157
+ /**
158
+ * Timing utilities (integrates with existing debug.ts timing)
159
+ */
160
+ function nowMs(): number {
161
+ const perf = (globalThis as { performance?: { now?: () => number } })
162
+ .performance;
163
+ if (perf && typeof perf.now === 'function') return perf.now();
164
+ return Date.now();
165
+ }
166
+
167
+ type Timer = {
168
+ end(meta?: Record<string, unknown>): void;
169
+ };
170
+
171
+ /**
172
+ * Create a timer for performance measurement
173
+ * Only active when debug mode is enabled
174
+ */
175
+ export function time(label: string): Timer {
176
+ if (!isDebugEnabled()) {
177
+ return { end() {} };
178
+ }
179
+
180
+ const start = nowMs();
181
+ let finished = false;
182
+
183
+ return {
184
+ end(meta?: Record<string, unknown>) {
185
+ if (finished) return;
186
+ finished = true;
187
+ const duration = nowMs() - start;
188
+
189
+ try {
190
+ const line = `[timing] ${label} ${duration.toFixed(1)}ms`;
191
+ if (meta && Object.keys(meta).length) {
192
+ console.log(line, meta);
193
+ } else {
194
+ console.log(line);
195
+ }
196
+ } catch {
197
+ // Silently fail
198
+ }
199
+ },
200
+ };
201
+ }
202
+
203
+ // Export legacy compatibility
204
+ export { isDebugEnabled, isTraceEnabled };