wave-code 0.0.2
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.
- package/README.md +120 -0
- package/bin/wave-code.js +16 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/components/App.d.ts +8 -0
- package/dist/components/App.d.ts.map +1 -0
- package/dist/components/App.js +10 -0
- package/dist/components/BashHistorySelector.d.ts +10 -0
- package/dist/components/BashHistorySelector.d.ts.map +1 -0
- package/dist/components/BashHistorySelector.js +83 -0
- package/dist/components/BashShellManager.d.ts +6 -0
- package/dist/components/BashShellManager.d.ts.map +1 -0
- package/dist/components/BashShellManager.js +116 -0
- package/dist/components/ChatInterface.d.ts +3 -0
- package/dist/components/ChatInterface.d.ts.map +1 -0
- package/dist/components/ChatInterface.js +31 -0
- package/dist/components/CommandOutputDisplay.d.ts +9 -0
- package/dist/components/CommandOutputDisplay.d.ts.map +1 -0
- package/dist/components/CommandOutputDisplay.js +40 -0
- package/dist/components/CommandSelector.d.ts +11 -0
- package/dist/components/CommandSelector.d.ts.map +1 -0
- package/dist/components/CommandSelector.js +60 -0
- package/dist/components/CompressDisplay.d.ts +9 -0
- package/dist/components/CompressDisplay.d.ts.map +1 -0
- package/dist/components/CompressDisplay.js +17 -0
- package/dist/components/DiffViewer.d.ts +9 -0
- package/dist/components/DiffViewer.d.ts.map +1 -0
- package/dist/components/DiffViewer.js +221 -0
- package/dist/components/FileSelector.d.ts +13 -0
- package/dist/components/FileSelector.d.ts.map +1 -0
- package/dist/components/FileSelector.js +48 -0
- package/dist/components/InputBox.d.ts +23 -0
- package/dist/components/InputBox.d.ts.map +1 -0
- package/dist/components/InputBox.js +124 -0
- package/dist/components/McpManager.d.ts +10 -0
- package/dist/components/McpManager.d.ts.map +1 -0
- package/dist/components/McpManager.js +123 -0
- package/dist/components/MemoryDisplay.d.ts +8 -0
- package/dist/components/MemoryDisplay.d.ts.map +1 -0
- package/dist/components/MemoryDisplay.js +25 -0
- package/dist/components/MemoryTypeSelector.d.ts +8 -0
- package/dist/components/MemoryTypeSelector.d.ts.map +1 -0
- package/dist/components/MemoryTypeSelector.js +38 -0
- package/dist/components/MessageList.d.ts +12 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +36 -0
- package/dist/components/ToolResultDisplay.d.ts +9 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -0
- package/dist/components/ToolResultDisplay.js +52 -0
- package/dist/contexts/useAppConfig.d.ts +11 -0
- package/dist/contexts/useAppConfig.d.ts.map +1 -0
- package/dist/contexts/useAppConfig.js +13 -0
- package/dist/contexts/useChat.d.ts +36 -0
- package/dist/contexts/useChat.d.ts.map +1 -0
- package/dist/contexts/useChat.js +208 -0
- package/dist/hooks/useBashHistorySelector.d.ts +15 -0
- package/dist/hooks/useBashHistorySelector.d.ts.map +1 -0
- package/dist/hooks/useBashHistorySelector.js +61 -0
- package/dist/hooks/useCommandSelector.d.ts +24 -0
- package/dist/hooks/useCommandSelector.d.ts.map +1 -0
- package/dist/hooks/useCommandSelector.js +98 -0
- package/dist/hooks/useFileSelector.d.ts +16 -0
- package/dist/hooks/useFileSelector.d.ts.map +1 -0
- package/dist/hooks/useFileSelector.js +174 -0
- package/dist/hooks/useImageManager.d.ts +13 -0
- package/dist/hooks/useImageManager.d.ts.map +1 -0
- package/dist/hooks/useImageManager.js +46 -0
- package/dist/hooks/useInputHistory.d.ts +11 -0
- package/dist/hooks/useInputHistory.d.ts.map +1 -0
- package/dist/hooks/useInputHistory.js +64 -0
- package/dist/hooks/useInputKeyboardHandler.d.ts +83 -0
- package/dist/hooks/useInputKeyboardHandler.d.ts.map +1 -0
- package/dist/hooks/useInputKeyboardHandler.js +507 -0
- package/dist/hooks/useInputState.d.ts +14 -0
- package/dist/hooks/useInputState.d.ts.map +1 -0
- package/dist/hooks/useInputState.js +57 -0
- package/dist/hooks/useMemoryTypeSelector.d.ts +9 -0
- package/dist/hooks/useMemoryTypeSelector.d.ts.map +1 -0
- package/dist/hooks/useMemoryTypeSelector.js +27 -0
- package/dist/hooks/usePagination.d.ts +20 -0
- package/dist/hooks/usePagination.d.ts.map +1 -0
- package/dist/hooks/usePagination.js +168 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/plain-cli.d.ts +7 -0
- package/dist/plain-cli.d.ts.map +1 -0
- package/dist/plain-cli.js +49 -0
- package/dist/utils/clipboard.d.ts +22 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +347 -0
- package/dist/utils/constants.d.ts +17 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +18 -0
- package/dist/utils/logger.d.ts +72 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +245 -0
- package/package.json +60 -0
- package/src/cli.tsx +82 -0
- package/src/components/App.tsx +31 -0
- package/src/components/BashHistorySelector.tsx +163 -0
- package/src/components/BashShellManager.tsx +306 -0
- package/src/components/ChatInterface.tsx +88 -0
- package/src/components/CommandOutputDisplay.tsx +81 -0
- package/src/components/CommandSelector.tsx +144 -0
- package/src/components/CompressDisplay.tsx +58 -0
- package/src/components/DiffViewer.tsx +321 -0
- package/src/components/FileSelector.tsx +137 -0
- package/src/components/InputBox.tsx +310 -0
- package/src/components/McpManager.tsx +328 -0
- package/src/components/MemoryDisplay.tsx +62 -0
- package/src/components/MemoryTypeSelector.tsx +96 -0
- package/src/components/MessageList.tsx +215 -0
- package/src/components/ToolResultDisplay.tsx +138 -0
- package/src/contexts/useAppConfig.tsx +32 -0
- package/src/contexts/useChat.tsx +300 -0
- package/src/hooks/useBashHistorySelector.ts +77 -0
- package/src/hooks/useCommandSelector.ts +131 -0
- package/src/hooks/useFileSelector.ts +227 -0
- package/src/hooks/useImageManager.ts +64 -0
- package/src/hooks/useInputHistory.ts +74 -0
- package/src/hooks/useInputKeyboardHandler.ts +778 -0
- package/src/hooks/useInputState.ts +66 -0
- package/src/hooks/useMemoryTypeSelector.ts +40 -0
- package/src/hooks/usePagination.ts +203 -0
- package/src/index.ts +108 -0
- package/src/plain-cli.ts +66 -0
- package/src/utils/clipboard.ts +384 -0
- package/src/utils/constants.ts +22 -0
- package/src/utils/logger.ts +301 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { unlinkSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
/**
|
|
5
|
+
* Read image from clipboard
|
|
6
|
+
* @returns Promise<ClipboardImageResult> Result containing image path or error information
|
|
7
|
+
*/
|
|
8
|
+
export async function readClipboardImage() {
|
|
9
|
+
try {
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
if (platform === "darwin") {
|
|
12
|
+
return await readClipboardImageMac();
|
|
13
|
+
}
|
|
14
|
+
else if (platform === "win32") {
|
|
15
|
+
return await readClipboardImageWindows();
|
|
16
|
+
}
|
|
17
|
+
else if (platform === "linux") {
|
|
18
|
+
return await readClipboardImageLinux();
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: `Clipboard image reading is not supported on platform: ${platform}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: `Failed to read clipboard image: ${error instanceof Error ? error.message : String(error)}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Read clipboard image on macOS
|
|
36
|
+
*/
|
|
37
|
+
async function readClipboardImageMac() {
|
|
38
|
+
const { exec } = await import("child_process");
|
|
39
|
+
const { promisify } = await import("util");
|
|
40
|
+
const execAsync = promisify(exec);
|
|
41
|
+
try {
|
|
42
|
+
// Try to read image data directly to check if image exists
|
|
43
|
+
const testScript = `
|
|
44
|
+
tell application "System Events"
|
|
45
|
+
try
|
|
46
|
+
set imageData to the clipboard as «class PNGf»
|
|
47
|
+
return true
|
|
48
|
+
on error
|
|
49
|
+
return false
|
|
50
|
+
end try
|
|
51
|
+
end tell
|
|
52
|
+
`;
|
|
53
|
+
let hasImage = false;
|
|
54
|
+
try {
|
|
55
|
+
const { stdout: testResult } = await execAsync(`osascript -e '${testScript}'`);
|
|
56
|
+
hasImage = testResult.trim() === "true";
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
hasImage = false;
|
|
60
|
+
}
|
|
61
|
+
if (!hasImage) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: "No image found in clipboard",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Generate temporary file path
|
|
68
|
+
const tempFilePath = join(tmpdir(), `clipboard-image-${Date.now()}.png`);
|
|
69
|
+
// Use osascript to save clipboard image as file
|
|
70
|
+
const saveScript = `
|
|
71
|
+
tell application "System Events"
|
|
72
|
+
try
|
|
73
|
+
set imageData to the clipboard as «class PNGf»
|
|
74
|
+
set fileRef to open for access POSIX file "${tempFilePath}" with write permission
|
|
75
|
+
write imageData to fileRef
|
|
76
|
+
close access fileRef
|
|
77
|
+
return true
|
|
78
|
+
on error errMsg
|
|
79
|
+
try
|
|
80
|
+
close access fileRef
|
|
81
|
+
end try
|
|
82
|
+
error errMsg
|
|
83
|
+
end try
|
|
84
|
+
end tell
|
|
85
|
+
`;
|
|
86
|
+
await execAsync(`osascript -e '${saveScript}'`);
|
|
87
|
+
// Verify if file was created successfully
|
|
88
|
+
if (!existsSync(tempFilePath)) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: "Failed to save clipboard image to temporary file",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
imagePath: tempFilePath,
|
|
97
|
+
mimeType: "image/png",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: `Failed to read clipboard image on macOS: ${error instanceof Error ? error.message : String(error)}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read clipboard image on Windows
|
|
109
|
+
*/
|
|
110
|
+
async function readClipboardImageWindows() {
|
|
111
|
+
try {
|
|
112
|
+
const { exec } = await import("child_process");
|
|
113
|
+
const { promisify } = await import("util");
|
|
114
|
+
const execAsync = promisify(exec);
|
|
115
|
+
// Use PowerShell to check if clipboard contains image
|
|
116
|
+
const checkScript = `
|
|
117
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
118
|
+
if ([System.Windows.Forms.Clipboard]::ContainsImage()) {
|
|
119
|
+
Write-Output "true"
|
|
120
|
+
} else {
|
|
121
|
+
Write-Output "false"
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
try {
|
|
125
|
+
const { stdout } = await execAsync(`powershell -Command "${checkScript}"`);
|
|
126
|
+
const hasImage = stdout.trim() === "true";
|
|
127
|
+
if (!hasImage) {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: "No image found in clipboard",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Generate temporary file path
|
|
134
|
+
const tempFilePath = join(tmpdir(), `clipboard-image-${Date.now()}.png`);
|
|
135
|
+
// Use PowerShell to save clipboard image
|
|
136
|
+
const saveScript = `
|
|
137
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
138
|
+
Add-Type -AssemblyName System.Drawing
|
|
139
|
+
$image = [System.Windows.Forms.Clipboard]::GetImage()
|
|
140
|
+
if ($image -ne $null) {
|
|
141
|
+
$image.Save("${tempFilePath.replace(/\\/g, "\\\\")}", [System.Drawing.Imaging.ImageFormat]::Png)
|
|
142
|
+
Write-Output "true"
|
|
143
|
+
} else {
|
|
144
|
+
Write-Output "false"
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
const { stdout: saveResult } = await execAsync(`powershell -Command "${saveScript}"`);
|
|
148
|
+
if (saveResult.trim() !== "true" || !existsSync(tempFilePath)) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: "Failed to save clipboard image to temporary file",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
imagePath: tempFilePath,
|
|
157
|
+
mimeType: "image/png",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
error: `Failed to access clipboard on Windows: ${err instanceof Error ? err.message : String(err)}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
error: `Failed to read clipboard image on Windows: ${err instanceof Error ? err.message : String(err)}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Read clipboard image on Linux
|
|
176
|
+
*/
|
|
177
|
+
async function readClipboardImageLinux() {
|
|
178
|
+
try {
|
|
179
|
+
// Linux can use tools like xclip or wl-clipboard
|
|
180
|
+
const { exec } = await import("child_process");
|
|
181
|
+
const { promisify } = await import("util");
|
|
182
|
+
const execAsync = promisify(exec);
|
|
183
|
+
// Check if xclip is available
|
|
184
|
+
try {
|
|
185
|
+
await execAsync("which xclip");
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
error: "xclip is required for clipboard image operations on Linux. Please install it: sudo apt-get install xclip",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// Check if clipboard contains image
|
|
194
|
+
try {
|
|
195
|
+
await execAsync("xclip -selection clipboard -t image/png -o > /dev/null 2>&1");
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
error: "No image found in clipboard",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Generate temporary file path
|
|
204
|
+
const tempFilePath = join(tmpdir(), `clipboard-image-${Date.now()}.png`);
|
|
205
|
+
// Use xclip to save clipboard image
|
|
206
|
+
try {
|
|
207
|
+
await execAsync(`xclip -selection clipboard -t image/png -o > "${tempFilePath}"`);
|
|
208
|
+
if (!existsSync(tempFilePath)) {
|
|
209
|
+
return {
|
|
210
|
+
success: false,
|
|
211
|
+
error: "Failed to save clipboard image to temporary file",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
imagePath: tempFilePath,
|
|
217
|
+
mimeType: "image/png",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: `Failed to save clipboard image: ${err instanceof Error ? err.message : String(err)}`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
return {
|
|
229
|
+
success: false,
|
|
230
|
+
error: `Failed to read clipboard image on Linux: ${err instanceof Error ? err.message : String(err)}`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Clean up temporary image file
|
|
236
|
+
* @param imagePath Path to the image file to clean up
|
|
237
|
+
*/
|
|
238
|
+
export function cleanupTempImage(imagePath) {
|
|
239
|
+
try {
|
|
240
|
+
if (existsSync(imagePath)) {
|
|
241
|
+
unlinkSync(imagePath);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
console.warn(`Failed to cleanup temporary image file: ${imagePath}`, error);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Check if clipboard contains image (quick check, does not save file)
|
|
250
|
+
* @returns Promise<boolean> Whether it contains image
|
|
251
|
+
*/
|
|
252
|
+
export async function hasClipboardImage() {
|
|
253
|
+
try {
|
|
254
|
+
const platform = process.platform;
|
|
255
|
+
if (platform === "darwin") {
|
|
256
|
+
return await hasClipboardImageMac();
|
|
257
|
+
}
|
|
258
|
+
else if (platform === "win32") {
|
|
259
|
+
return await hasClipboardImageWindows();
|
|
260
|
+
}
|
|
261
|
+
else if (platform === "linux") {
|
|
262
|
+
return await hasClipboardImageLinux();
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Check if clipboard contains image on macOS
|
|
274
|
+
*/
|
|
275
|
+
async function hasClipboardImageMac() {
|
|
276
|
+
try {
|
|
277
|
+
const { exec } = await import("child_process");
|
|
278
|
+
const { promisify } = await import("util");
|
|
279
|
+
const execAsync = promisify(exec);
|
|
280
|
+
const checkScript = `
|
|
281
|
+
tell application "System Events"
|
|
282
|
+
try
|
|
283
|
+
set imageData to the clipboard as «class PNGf»
|
|
284
|
+
return true
|
|
285
|
+
on error
|
|
286
|
+
return false
|
|
287
|
+
end try
|
|
288
|
+
end tell
|
|
289
|
+
`;
|
|
290
|
+
const { stdout } = await execAsync(`osascript -e '${checkScript}'`);
|
|
291
|
+
return stdout.trim() === "true";
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Check if clipboard contains image on Windows
|
|
299
|
+
*/
|
|
300
|
+
async function hasClipboardImageWindows() {
|
|
301
|
+
try {
|
|
302
|
+
const { exec } = await import("child_process");
|
|
303
|
+
const { promisify } = await import("util");
|
|
304
|
+
const execAsync = promisify(exec);
|
|
305
|
+
const checkScript = `
|
|
306
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
307
|
+
if ([System.Windows.Forms.Clipboard]::ContainsImage()) {
|
|
308
|
+
Write-Output "true"
|
|
309
|
+
} else {
|
|
310
|
+
Write-Output "false"
|
|
311
|
+
}
|
|
312
|
+
`;
|
|
313
|
+
const { stdout } = await execAsync(`powershell -Command "${checkScript}"`);
|
|
314
|
+
return stdout.trim() === "true";
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check if clipboard contains image on Linux
|
|
322
|
+
*/
|
|
323
|
+
async function hasClipboardImageLinux() {
|
|
324
|
+
try {
|
|
325
|
+
const { exec } = await import("child_process");
|
|
326
|
+
const { promisify } = await import("util");
|
|
327
|
+
const execAsync = promisify(exec);
|
|
328
|
+
// Check if xclip is available
|
|
329
|
+
try {
|
|
330
|
+
await execAsync("which xclip");
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
// Check if clipboard contains image
|
|
336
|
+
try {
|
|
337
|
+
await execAsync("xclip -selection clipboard -t image/png -o > /dev/null 2>&1");
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application constants definition
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Application data storage directory
|
|
6
|
+
* Used to store debug logs, command history and other data
|
|
7
|
+
*/
|
|
8
|
+
export declare const DATA_DIRECTORY: string;
|
|
9
|
+
/**
|
|
10
|
+
* Application log file path
|
|
11
|
+
*/
|
|
12
|
+
export declare const LOG_FILE: string;
|
|
13
|
+
/**
|
|
14
|
+
* Pagination related constants
|
|
15
|
+
*/
|
|
16
|
+
export declare const MESSAGES_PER_PAGE = 20;
|
|
17
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAmC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application constants definition
|
|
3
|
+
*/
|
|
4
|
+
import path from "path";
|
|
5
|
+
import os from "os";
|
|
6
|
+
/**
|
|
7
|
+
* Application data storage directory
|
|
8
|
+
* Used to store debug logs, command history and other data
|
|
9
|
+
*/
|
|
10
|
+
export const DATA_DIRECTORY = path.join(os.homedir(), ".wave");
|
|
11
|
+
/**
|
|
12
|
+
* Application log file path
|
|
13
|
+
*/
|
|
14
|
+
export const LOG_FILE = path.join(DATA_DIRECTORY, "app.log");
|
|
15
|
+
/**
|
|
16
|
+
* Pagination related constants
|
|
17
|
+
*/
|
|
18
|
+
export const MESSAGES_PER_PAGE = 20; // Number of messages displayed per page
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility module
|
|
3
|
+
* Supports filtering by log level and keywords
|
|
4
|
+
* Logs are written to files instead of terminal to avoid being cleared by Ink app
|
|
5
|
+
*
|
|
6
|
+
* Performance optimization:
|
|
7
|
+
* - In test environment, you can disable all file and console I/O operations by setting environment variable DISABLE_LOGGER_IO=true
|
|
8
|
+
* - This can significantly improve test execution performance by avoiding unnecessary disk writes
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Log level enumeration
|
|
12
|
+
*/
|
|
13
|
+
export declare enum LogLevel {
|
|
14
|
+
DEBUG = 0,
|
|
15
|
+
INFO = 1,
|
|
16
|
+
WARN = 2,
|
|
17
|
+
ERROR = 3
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Log configuration interface
|
|
21
|
+
*/
|
|
22
|
+
interface LogConfig {
|
|
23
|
+
readonly level: LogLevel;
|
|
24
|
+
readonly keywords: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Logger object
|
|
28
|
+
*/
|
|
29
|
+
export declare const logger: {
|
|
30
|
+
/**
|
|
31
|
+
* Debug log output function
|
|
32
|
+
*/
|
|
33
|
+
debug: (...args: unknown[]) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Info log output function
|
|
36
|
+
*/
|
|
37
|
+
info: (...args: unknown[]) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Warning log output function
|
|
40
|
+
*/
|
|
41
|
+
warn: (...args: unknown[]) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Error log output function
|
|
44
|
+
*/
|
|
45
|
+
error: (...args: unknown[]) => void;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Get current log configuration
|
|
49
|
+
*/
|
|
50
|
+
export declare const getLogConfig: () => LogConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Get log file path
|
|
53
|
+
*/
|
|
54
|
+
export declare const getLogFile: () => string;
|
|
55
|
+
/**
|
|
56
|
+
* Log cleanup configuration
|
|
57
|
+
*/
|
|
58
|
+
interface LogCleanupConfig {
|
|
59
|
+
/** Maximum size of current log file (bytes), default 10MB */
|
|
60
|
+
maxFileSize: number;
|
|
61
|
+
/** Number of lines to keep when truncating, default 1000 lines */
|
|
62
|
+
keepLines: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Execute log cleanup
|
|
66
|
+
* Truncate current log file (if needed)
|
|
67
|
+
*
|
|
68
|
+
* @param customConfig Custom cleanup configuration, if not provided uses default configuration
|
|
69
|
+
*/
|
|
70
|
+
export declare const cleanupLogs: (customConfig?: Partial<LogCleanupConfig>) => Promise<void>;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;GAEG;AACH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAYD;;GAEG;AACH,UAAU,SAAS;IACjB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;CAC7B;AA8HD;;GAEG;AACH,eAAO,MAAM,MAAM;IACjB;;OAEG;qBACc,OAAO,EAAE,KAAG,IAAI;IAIjC;;OAEG;oBACa,OAAO,EAAE,KAAG,IAAI;IAIhC;;OAEG;oBACa,OAAO,EAAE,KAAG,IAAI;IAIhC;;OAEG;qBACc,OAAO,EAAE,KAAG,IAAI;CAGlC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,SAE/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,QAAO,MAE7B,CAAC;AAEF;;GAEG;AACH,UAAU,gBAAgB;IACxB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAqDD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GACtB,eAAe,OAAO,CAAC,gBAAgB,CAAC,KACvC,OAAO,CAAC,IAAI,CAiBd,CAAC"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility module
|
|
3
|
+
* Supports filtering by log level and keywords
|
|
4
|
+
* Logs are written to files instead of terminal to avoid being cleared by Ink app
|
|
5
|
+
*
|
|
6
|
+
* Performance optimization:
|
|
7
|
+
* - In test environment, you can disable all file and console I/O operations by setting environment variable DISABLE_LOGGER_IO=true
|
|
8
|
+
* - This can significantly improve test execution performance by avoiding unnecessary disk writes
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import { LOG_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
12
|
+
const logFile = process.env.LOG_FILE || LOG_FILE;
|
|
13
|
+
/**
|
|
14
|
+
* Log level enumeration
|
|
15
|
+
*/
|
|
16
|
+
export var LogLevel;
|
|
17
|
+
(function (LogLevel) {
|
|
18
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
19
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
20
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
21
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
22
|
+
})(LogLevel || (LogLevel = {}));
|
|
23
|
+
/**
|
|
24
|
+
* Log level name mapping
|
|
25
|
+
*/
|
|
26
|
+
const LOG_LEVEL_NAMES = {
|
|
27
|
+
[LogLevel.DEBUG]: "DEBUG",
|
|
28
|
+
[LogLevel.INFO]: "INFO",
|
|
29
|
+
[LogLevel.WARN]: "WARN",
|
|
30
|
+
[LogLevel.ERROR]: "ERROR",
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Parse log level from environment variable
|
|
34
|
+
*/
|
|
35
|
+
const parseLogLevel = (levelStr) => {
|
|
36
|
+
if (!levelStr)
|
|
37
|
+
return LogLevel.INFO;
|
|
38
|
+
const upperLevel = levelStr.toUpperCase();
|
|
39
|
+
switch (upperLevel) {
|
|
40
|
+
case "DEBUG":
|
|
41
|
+
return LogLevel.DEBUG;
|
|
42
|
+
case "INFO":
|
|
43
|
+
return LogLevel.INFO;
|
|
44
|
+
case "WARN":
|
|
45
|
+
return LogLevel.WARN;
|
|
46
|
+
case "ERROR":
|
|
47
|
+
return LogLevel.ERROR;
|
|
48
|
+
default:
|
|
49
|
+
return LogLevel.INFO;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Parse keyword filter from environment variable
|
|
54
|
+
*/
|
|
55
|
+
const parseKeywords = (keywordsStr) => {
|
|
56
|
+
if (!keywordsStr)
|
|
57
|
+
return [];
|
|
58
|
+
return keywordsStr
|
|
59
|
+
.split(",")
|
|
60
|
+
.map((k) => k.trim().toLowerCase())
|
|
61
|
+
.filter((k) => k.length > 0);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Load log configuration from environment variables
|
|
65
|
+
*
|
|
66
|
+
* Supported environment variables:
|
|
67
|
+
* - LOG_LEVEL: Log level (DEBUG, INFO, WARN, ERROR), default INFO
|
|
68
|
+
* - LOG_KEYWORDS: Keyword filter, comma-separated, default no filter
|
|
69
|
+
*/
|
|
70
|
+
const logConfig = {
|
|
71
|
+
level: parseLogLevel(process.env.LOG_LEVEL),
|
|
72
|
+
keywords: parseKeywords(process.env.LOG_KEYWORDS),
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Check if log should be recorded
|
|
76
|
+
*/
|
|
77
|
+
const shouldLog = (level, message) => {
|
|
78
|
+
// Check log level
|
|
79
|
+
if (level < logConfig.level) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// If no keyword filter is set, record all logs that meet the level requirement
|
|
83
|
+
if (logConfig.keywords.length === 0) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
// Check keyword filter
|
|
87
|
+
const lowerMessage = message.toLowerCase();
|
|
88
|
+
return logConfig.keywords.some((keyword) => lowerMessage.includes(keyword));
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Format log arguments
|
|
92
|
+
*/
|
|
93
|
+
const formatArg = (arg) => {
|
|
94
|
+
if (arg === null)
|
|
95
|
+
return "null";
|
|
96
|
+
if (arg === undefined)
|
|
97
|
+
return "undefined";
|
|
98
|
+
if (arg instanceof Error) {
|
|
99
|
+
// Special handling for Error objects, display stack or message
|
|
100
|
+
return arg.stack || arg.message || String(arg);
|
|
101
|
+
}
|
|
102
|
+
if (typeof arg === "object") {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(arg, null, 2);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// If JSON.stringify fails, fallback to String()
|
|
108
|
+
return String(arg);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return String(arg);
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Generic log output function
|
|
115
|
+
*/
|
|
116
|
+
const logMessage = (level, ...args) => {
|
|
117
|
+
const messageText = args.map(formatArg).join(" ");
|
|
118
|
+
// Check if this log should be recorded
|
|
119
|
+
if (!shouldLog(level, messageText)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// If logger I/O operations are disabled, return directly to save performance
|
|
123
|
+
if (process.env.DISABLE_LOGGER_IO === "true") {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const levelName = LOG_LEVEL_NAMES[level];
|
|
127
|
+
const timestamp = new Date().toISOString();
|
|
128
|
+
const formattedMessage = `[${timestamp}] [${levelName}] ${messageText}\n`;
|
|
129
|
+
try {
|
|
130
|
+
// Ensure directory exists
|
|
131
|
+
if (!fs.existsSync(DATA_DIRECTORY)) {
|
|
132
|
+
fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
// Write log to file
|
|
135
|
+
fs.appendFileSync(logFile, formattedMessage);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
// If file write fails, fallback to stderr
|
|
139
|
+
process.stderr.write(`[${levelName}] Failed to write to log file: ${error}\n`);
|
|
140
|
+
process.stderr.write(formattedMessage);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Logger object
|
|
145
|
+
*/
|
|
146
|
+
export const logger = {
|
|
147
|
+
/**
|
|
148
|
+
* Debug log output function
|
|
149
|
+
*/
|
|
150
|
+
debug: (...args) => {
|
|
151
|
+
logMessage(LogLevel.DEBUG, ...args);
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* Info log output function
|
|
155
|
+
*/
|
|
156
|
+
info: (...args) => {
|
|
157
|
+
logMessage(LogLevel.INFO, ...args);
|
|
158
|
+
},
|
|
159
|
+
/**
|
|
160
|
+
* Warning log output function
|
|
161
|
+
*/
|
|
162
|
+
warn: (...args) => {
|
|
163
|
+
logMessage(LogLevel.WARN, ...args);
|
|
164
|
+
},
|
|
165
|
+
/**
|
|
166
|
+
* Error log output function
|
|
167
|
+
*/
|
|
168
|
+
error: (...args) => {
|
|
169
|
+
logMessage(LogLevel.ERROR, ...args);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Get current log configuration
|
|
174
|
+
*/
|
|
175
|
+
export const getLogConfig = () => {
|
|
176
|
+
return logConfig;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Get log file path
|
|
180
|
+
*/
|
|
181
|
+
export const getLogFile = () => {
|
|
182
|
+
return logFile;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Get log cleanup configuration
|
|
186
|
+
* Can override default configuration with environment variables
|
|
187
|
+
*/
|
|
188
|
+
const getCleanupConfig = () => {
|
|
189
|
+
return {
|
|
190
|
+
maxFileSize: parseInt(process.env.LOG_MAX_FILE_SIZE || "10485760", 10), // 10MB
|
|
191
|
+
keepLines: parseInt(process.env.LOG_KEEP_LINES || "1000", 10),
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Truncate current log file if too large
|
|
196
|
+
* Keep the last specified number of lines
|
|
197
|
+
*/
|
|
198
|
+
const truncateLogFileIfNeeded = (config) => {
|
|
199
|
+
// If logger I/O operations are disabled, return directly to save performance
|
|
200
|
+
if (process.env.DISABLE_LOGGER_IO === "true") {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
if (!fs.existsSync(logFile)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const stats = fs.statSync(logFile);
|
|
208
|
+
// If file size exceeds limit, truncate file
|
|
209
|
+
if (stats.size > config.maxFileSize) {
|
|
210
|
+
const content = fs.readFileSync(logFile, "utf8");
|
|
211
|
+
const lines = content.split("\n");
|
|
212
|
+
// Keep the last specified number of lines
|
|
213
|
+
const keepLines = Math.min(config.keepLines, lines.length);
|
|
214
|
+
const truncatedContent = lines.slice(-keepLines).join("\n");
|
|
215
|
+
// Write truncated content
|
|
216
|
+
fs.writeFileSync(logFile, truncatedContent);
|
|
217
|
+
// Record truncation operation
|
|
218
|
+
const removedLines = lines.length - keepLines;
|
|
219
|
+
logger.info(`Log file truncated: removed ${removedLines} lines, kept last ${keepLines} lines`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
logger.warn("Failed to truncate log file:", error);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Execute log cleanup
|
|
228
|
+
* Truncate current log file (if needed)
|
|
229
|
+
*
|
|
230
|
+
* @param customConfig Custom cleanup configuration, if not provided uses default configuration
|
|
231
|
+
*/
|
|
232
|
+
export const cleanupLogs = async (customConfig) => {
|
|
233
|
+
// If logger I/O operations are disabled, return directly to save performance
|
|
234
|
+
if (process.env.DISABLE_LOGGER_IO === "true") {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const config = { ...getCleanupConfig(), ...customConfig };
|
|
238
|
+
logger.info("Starting log cleanup...", {
|
|
239
|
+
maxFileSize: config.maxFileSize,
|
|
240
|
+
keepLines: config.keepLines,
|
|
241
|
+
});
|
|
242
|
+
// Truncate current log file (if needed)
|
|
243
|
+
truncateLogFileIfNeeded(config);
|
|
244
|
+
logger.info("Log cleanup completed");
|
|
245
|
+
};
|