@wonderwhy-er/desktop-commander 0.2.15 → 0.2.17
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 +3 -2
- package/dist/data/spec-kit-prompts.json +123 -0
- package/dist/handlers/filesystem-handlers.js +5 -2
- package/dist/handlers/history-handlers.d.ts +5 -0
- package/dist/handlers/history-handlers.js +35 -0
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +1 -0
- package/dist/handlers/search-handlers.js +4 -0
- package/dist/http-index.d.ts +45 -0
- package/dist/http-index.js +51 -0
- package/dist/search-manager.d.ts +2 -1
- package/dist/search-manager.js +14 -16
- package/dist/server.js +284 -110
- package/dist/tools/config.js +11 -9
- package/dist/tools/filesystem.d.ts +1 -1
- package/dist/tools/filesystem.js +51 -3
- package/dist/tools/schemas.d.ts +16 -0
- package/dist/tools/schemas.js +7 -0
- package/dist/utils/system-info.d.ts +12 -0
- package/dist/utils/system-info.js +58 -7
- package/dist/utils/toolHistory.d.ts +73 -0
- package/dist/utils/toolHistory.js +197 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -1
package/dist/tools/filesystem.js
CHANGED
|
@@ -757,10 +757,58 @@ export async function createDirectory(dirPath) {
|
|
|
757
757
|
const validPath = await validatePath(dirPath);
|
|
758
758
|
await fs.mkdir(validPath, { recursive: true });
|
|
759
759
|
}
|
|
760
|
-
export async function listDirectory(dirPath) {
|
|
760
|
+
export async function listDirectory(dirPath, depth = 2) {
|
|
761
761
|
const validPath = await validatePath(dirPath);
|
|
762
|
-
const
|
|
763
|
-
|
|
762
|
+
const results = [];
|
|
763
|
+
const MAX_NESTED_ITEMS = 100; // Maximum items to show per nested directory
|
|
764
|
+
async function listRecursive(currentPath, currentDepth, relativePath = '', isTopLevel = true) {
|
|
765
|
+
if (currentDepth <= 0)
|
|
766
|
+
return;
|
|
767
|
+
let entries;
|
|
768
|
+
try {
|
|
769
|
+
entries = await fs.readdir(currentPath, { withFileTypes: true });
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
// If we can't read this directory (permission denied), show as denied
|
|
773
|
+
const displayPath = relativePath || path.basename(currentPath);
|
|
774
|
+
results.push(`[DENIED] ${displayPath}`);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
// Apply filtering for nested directories (not top level)
|
|
778
|
+
const totalEntries = entries.length;
|
|
779
|
+
let entriesToShow = entries;
|
|
780
|
+
let filteredCount = 0;
|
|
781
|
+
if (!isTopLevel && totalEntries > MAX_NESTED_ITEMS) {
|
|
782
|
+
entriesToShow = entries.slice(0, MAX_NESTED_ITEMS);
|
|
783
|
+
filteredCount = totalEntries - MAX_NESTED_ITEMS;
|
|
784
|
+
}
|
|
785
|
+
for (const entry of entriesToShow) {
|
|
786
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
787
|
+
const displayPath = relativePath ? path.join(relativePath, entry.name) : entry.name;
|
|
788
|
+
// Add this entry to results
|
|
789
|
+
results.push(`${entry.isDirectory() ? "[DIR]" : "[FILE]"} ${displayPath}`);
|
|
790
|
+
// If it's a directory and we have depth remaining, recurse
|
|
791
|
+
if (entry.isDirectory() && currentDepth > 1) {
|
|
792
|
+
try {
|
|
793
|
+
// Validate the path before recursing
|
|
794
|
+
await validatePath(fullPath);
|
|
795
|
+
await listRecursive(fullPath, currentDepth - 1, displayPath, false);
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
// If validation fails or we can't access it, it will be marked as denied
|
|
799
|
+
// when we try to read it in the recursive call
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
// Add warning message if items were filtered
|
|
805
|
+
if (filteredCount > 0) {
|
|
806
|
+
const displayPath = relativePath || path.basename(currentPath);
|
|
807
|
+
results.push(`[WARNING] ${displayPath}: ${filteredCount} items hidden (showing first ${MAX_NESTED_ITEMS} of ${totalEntries} total)`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
await listRecursive(validPath, depth, '', true);
|
|
811
|
+
return results;
|
|
764
812
|
}
|
|
765
813
|
export async function moveFile(sourcePath, destinationPath) {
|
|
766
814
|
const validSourcePath = await validatePath(sourcePath);
|
package/dist/tools/schemas.d.ts
CHANGED
|
@@ -94,10 +94,13 @@ export declare const CreateDirectoryArgsSchema: z.ZodObject<{
|
|
|
94
94
|
}>;
|
|
95
95
|
export declare const ListDirectoryArgsSchema: z.ZodObject<{
|
|
96
96
|
path: z.ZodString;
|
|
97
|
+
depth: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
97
98
|
}, "strip", z.ZodTypeAny, {
|
|
98
99
|
path: string;
|
|
100
|
+
depth: number;
|
|
99
101
|
}, {
|
|
100
102
|
path: string;
|
|
103
|
+
depth?: number | undefined;
|
|
101
104
|
}>;
|
|
102
105
|
export declare const MoveFileArgsSchema: z.ZodObject<{
|
|
103
106
|
source: z.ZodString;
|
|
@@ -221,3 +224,16 @@ export declare const GetPromptsArgsSchema: z.ZodObject<{
|
|
|
221
224
|
category?: string | undefined;
|
|
222
225
|
promptId?: string | undefined;
|
|
223
226
|
}>;
|
|
227
|
+
export declare const GetRecentToolCallsArgsSchema: z.ZodObject<{
|
|
228
|
+
maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
229
|
+
toolName: z.ZodOptional<z.ZodString>;
|
|
230
|
+
since: z.ZodOptional<z.ZodString>;
|
|
231
|
+
}, "strip", z.ZodTypeAny, {
|
|
232
|
+
maxResults: number;
|
|
233
|
+
toolName?: string | undefined;
|
|
234
|
+
since?: string | undefined;
|
|
235
|
+
}, {
|
|
236
|
+
maxResults?: number | undefined;
|
|
237
|
+
toolName?: string | undefined;
|
|
238
|
+
since?: string | undefined;
|
|
239
|
+
}>;
|
package/dist/tools/schemas.js
CHANGED
|
@@ -50,6 +50,7 @@ export const CreateDirectoryArgsSchema = z.object({
|
|
|
50
50
|
});
|
|
51
51
|
export const ListDirectoryArgsSchema = z.object({
|
|
52
52
|
path: z.string(),
|
|
53
|
+
depth: z.number().optional().default(2),
|
|
53
54
|
});
|
|
54
55
|
export const MoveFileArgsSchema = z.object({
|
|
55
56
|
source: z.string(),
|
|
@@ -112,3 +113,9 @@ export const GetPromptsArgsSchema = z.object({
|
|
|
112
113
|
category: z.string().optional(),
|
|
113
114
|
promptId: z.string().optional(),
|
|
114
115
|
});
|
|
116
|
+
// Tool history schema
|
|
117
|
+
export const GetRecentToolCallsArgsSchema = z.object({
|
|
118
|
+
maxResults: z.number().min(1).max(1000).optional().default(50),
|
|
119
|
+
toolName: z.string().optional(),
|
|
120
|
+
since: z.string().datetime().optional(),
|
|
121
|
+
});
|
|
@@ -29,6 +29,18 @@ export interface SystemInfo {
|
|
|
29
29
|
isMacOS: boolean;
|
|
30
30
|
isLinux: boolean;
|
|
31
31
|
docker: ContainerInfo;
|
|
32
|
+
isDXT: boolean;
|
|
33
|
+
nodeInfo?: {
|
|
34
|
+
version: string;
|
|
35
|
+
path: string;
|
|
36
|
+
npmVersion?: string;
|
|
37
|
+
};
|
|
38
|
+
processInfo: {
|
|
39
|
+
pid: number;
|
|
40
|
+
arch: string;
|
|
41
|
+
platform: string;
|
|
42
|
+
versions: NodeJS.ProcessVersions;
|
|
43
|
+
};
|
|
32
44
|
examplePaths: {
|
|
33
45
|
home: string;
|
|
34
46
|
temp: string;
|
|
@@ -305,6 +305,27 @@ function getContainerEnvironment(containerType) {
|
|
|
305
305
|
}
|
|
306
306
|
return Object.keys(env).length > 0 ? env : undefined;
|
|
307
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Detect Node.js installation and version from current process
|
|
310
|
+
*/
|
|
311
|
+
function detectNodeInfo() {
|
|
312
|
+
try {
|
|
313
|
+
// Get Node.js version from current process
|
|
314
|
+
const version = process.version.replace('v', ''); // Remove 'v' prefix
|
|
315
|
+
// Get Node.js executable path from current process
|
|
316
|
+
const path = process.execPath;
|
|
317
|
+
// Get npm version from environment if available
|
|
318
|
+
const npmVersion = process.env.npm_version;
|
|
319
|
+
return {
|
|
320
|
+
version,
|
|
321
|
+
path,
|
|
322
|
+
...(npmVersion && { npmVersion })
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
308
329
|
/**
|
|
309
330
|
* Get comprehensive system information for tool prompts
|
|
310
331
|
*/
|
|
@@ -397,6 +418,15 @@ export function getSystemInfo() {
|
|
|
397
418
|
examplePaths.accessible = mountPoints.map(mount => mount.containerPath);
|
|
398
419
|
}
|
|
399
420
|
}
|
|
421
|
+
// Detect Node.js installation from current process
|
|
422
|
+
const nodeInfo = detectNodeInfo();
|
|
423
|
+
// Get process information
|
|
424
|
+
const processInfo = {
|
|
425
|
+
pid: process.pid,
|
|
426
|
+
arch: process.arch,
|
|
427
|
+
platform: process.platform,
|
|
428
|
+
versions: process.versions
|
|
429
|
+
};
|
|
400
430
|
return {
|
|
401
431
|
platform,
|
|
402
432
|
platformName,
|
|
@@ -415,6 +445,9 @@ export function getSystemInfo() {
|
|
|
415
445
|
mountPoints,
|
|
416
446
|
containerEnvironment: getContainerEnvironment(containerDetection.containerType)
|
|
417
447
|
},
|
|
448
|
+
isDXT: !!process.env.MCP_DXT,
|
|
449
|
+
nodeInfo,
|
|
450
|
+
processInfo,
|
|
418
451
|
examplePaths
|
|
419
452
|
};
|
|
420
453
|
}
|
|
@@ -545,33 +578,51 @@ LINUX-SPECIFIC NOTES:
|
|
|
545
578
|
* Get common development tool guidance based on OS
|
|
546
579
|
*/
|
|
547
580
|
export function getDevelopmentToolGuidance(systemInfo) {
|
|
548
|
-
const { isWindows, isMacOS, isLinux, platformName } = systemInfo;
|
|
581
|
+
const { isWindows, isMacOS, isLinux, platformName, nodeInfo, processInfo } = systemInfo;
|
|
582
|
+
// Add detected Node.js info to guidance
|
|
583
|
+
const nodeGuidance = nodeInfo
|
|
584
|
+
? `Node.js: v${nodeInfo.version} (${nodeInfo.path})${nodeInfo.npmVersion ? ` | npm: v${nodeInfo.npmVersion}` : ''}`
|
|
585
|
+
: 'Node.js: Not detected';
|
|
586
|
+
// Add process environment info
|
|
587
|
+
const envInfo = `
|
|
588
|
+
Current Process Environment:
|
|
589
|
+
- Node: v${processInfo.versions.node}
|
|
590
|
+
- V8: v${processInfo.versions.v8}
|
|
591
|
+
- Architecture: ${processInfo.arch}
|
|
592
|
+
- Platform: ${processInfo.platform}
|
|
593
|
+
- Process ID: ${processInfo.pid}`;
|
|
549
594
|
if (isWindows) {
|
|
550
595
|
return `
|
|
551
596
|
COMMON WINDOWS DEVELOPMENT TOOLS:
|
|
552
|
-
-
|
|
597
|
+
- ${nodeGuidance}
|
|
553
598
|
- Python: May be 'python' or 'py' command, check both
|
|
554
599
|
- Git: Git Bash provides Unix-like environment
|
|
555
600
|
- WSL: Windows Subsystem for Linux available for Unix tools
|
|
556
|
-
- Visual Studio tools: cl, msbuild for C++ compilation
|
|
601
|
+
- Visual Studio tools: cl, msbuild for C++ compilation
|
|
602
|
+
|
|
603
|
+
${envInfo}`;
|
|
557
604
|
}
|
|
558
605
|
else if (isMacOS) {
|
|
559
606
|
return `
|
|
560
607
|
COMMON MACOS DEVELOPMENT TOOLS:
|
|
561
608
|
- Xcode Command Line Tools: Required for many development tools
|
|
562
609
|
- Homebrew: Primary package manager for development tools
|
|
610
|
+
- ${nodeGuidance}
|
|
563
611
|
- Python: Usually python3, check if python points to Python 2
|
|
564
|
-
-
|
|
565
|
-
|
|
612
|
+
- Ruby: System Ruby available, rbenv/rvm for version management
|
|
613
|
+
|
|
614
|
+
${envInfo}`;
|
|
566
615
|
}
|
|
567
616
|
else {
|
|
568
617
|
return `
|
|
569
618
|
COMMON LINUX DEVELOPMENT TOOLS:
|
|
570
619
|
- Package managers: Install tools via distribution package manager
|
|
571
620
|
- Python: Usually python3, python may point to Python 2
|
|
572
|
-
-
|
|
621
|
+
- ${nodeGuidance}
|
|
573
622
|
- Build tools: gcc, make typically available or easily installed
|
|
574
|
-
- Container tools: docker, podman common for development
|
|
623
|
+
- Container tools: docker, podman common for development
|
|
624
|
+
|
|
625
|
+
${envInfo}`;
|
|
575
626
|
}
|
|
576
627
|
}
|
|
577
628
|
/**
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ServerResult } from '../types.js';
|
|
2
|
+
export interface ToolCallRecord {
|
|
3
|
+
timestamp: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
arguments: any;
|
|
6
|
+
output: ServerResult;
|
|
7
|
+
duration?: number;
|
|
8
|
+
}
|
|
9
|
+
interface FormattedToolCallRecord extends Omit<ToolCallRecord, 'timestamp'> {
|
|
10
|
+
timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
declare class ToolHistory {
|
|
13
|
+
private history;
|
|
14
|
+
private readonly MAX_ENTRIES;
|
|
15
|
+
private readonly historyFile;
|
|
16
|
+
private writeQueue;
|
|
17
|
+
private isWriting;
|
|
18
|
+
private writeInterval?;
|
|
19
|
+
constructor();
|
|
20
|
+
/**
|
|
21
|
+
* Load history from disk (all instances share the same file)
|
|
22
|
+
*/
|
|
23
|
+
private loadFromDisk;
|
|
24
|
+
/**
|
|
25
|
+
* Trim history file to prevent it from growing indefinitely
|
|
26
|
+
*/
|
|
27
|
+
private trimHistoryFile;
|
|
28
|
+
/**
|
|
29
|
+
* Async write processor - batches writes to avoid blocking
|
|
30
|
+
*/
|
|
31
|
+
private startWriteProcessor;
|
|
32
|
+
/**
|
|
33
|
+
* Flush queued writes to disk
|
|
34
|
+
*/
|
|
35
|
+
private flushToDisk;
|
|
36
|
+
/**
|
|
37
|
+
* Add a tool call to history
|
|
38
|
+
*/
|
|
39
|
+
addCall(toolName: string, args: any, output: ServerResult, duration?: number): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get recent tool calls with filters
|
|
42
|
+
*/
|
|
43
|
+
getRecentCalls(options: {
|
|
44
|
+
maxResults?: number;
|
|
45
|
+
toolName?: string;
|
|
46
|
+
since?: string;
|
|
47
|
+
}): ToolCallRecord[];
|
|
48
|
+
/**
|
|
49
|
+
* Get recent calls formatted with local timezone
|
|
50
|
+
*/
|
|
51
|
+
getRecentCallsFormatted(options: {
|
|
52
|
+
maxResults?: number;
|
|
53
|
+
toolName?: string;
|
|
54
|
+
since?: string;
|
|
55
|
+
}): FormattedToolCallRecord[];
|
|
56
|
+
/**
|
|
57
|
+
* Get current stats
|
|
58
|
+
*/
|
|
59
|
+
getStats(): {
|
|
60
|
+
totalEntries: number;
|
|
61
|
+
oldestEntry: string;
|
|
62
|
+
newestEntry: string;
|
|
63
|
+
historyFile: string;
|
|
64
|
+
queuedWrites: number;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Cleanup method - clears interval and flushes pending writes
|
|
68
|
+
* Call this during shutdown or in tests
|
|
69
|
+
*/
|
|
70
|
+
cleanup(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
export declare const toolHistory: ToolHistory;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
// Format timestamp in local timezone for display
|
|
5
|
+
function formatLocalTimestamp(isoTimestamp) {
|
|
6
|
+
const date = new Date(isoTimestamp);
|
|
7
|
+
return date.toLocaleString('en-US', {
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
day: '2-digit',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
second: '2-digit',
|
|
14
|
+
hour12: false
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
class ToolHistory {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.history = [];
|
|
20
|
+
this.MAX_ENTRIES = 1000;
|
|
21
|
+
this.writeQueue = [];
|
|
22
|
+
this.isWriting = false;
|
|
23
|
+
// Store history in same directory as config to keep everything together
|
|
24
|
+
const historyDir = path.join(os.homedir(), '.claude-server-commander');
|
|
25
|
+
// Ensure directory exists
|
|
26
|
+
if (!fs.existsSync(historyDir)) {
|
|
27
|
+
fs.mkdirSync(historyDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
// Use append-only JSONL format (JSON Lines)
|
|
30
|
+
this.historyFile = path.join(historyDir, 'tool-history.jsonl');
|
|
31
|
+
// Load existing history on startup
|
|
32
|
+
this.loadFromDisk();
|
|
33
|
+
// Start async write processor
|
|
34
|
+
this.startWriteProcessor();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load history from disk (all instances share the same file)
|
|
38
|
+
*/
|
|
39
|
+
loadFromDisk() {
|
|
40
|
+
try {
|
|
41
|
+
if (!fs.existsSync(this.historyFile)) {
|
|
42
|
+
console.error('[ToolHistory] No history file found, starting fresh');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const content = fs.readFileSync(this.historyFile, 'utf-8');
|
|
46
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
47
|
+
// Parse each line as JSON
|
|
48
|
+
const records = [];
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
try {
|
|
51
|
+
records.push(JSON.parse(line));
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.error('[ToolHistory] Failed to parse line:', line);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Keep only last 1000 entries
|
|
58
|
+
this.history = records.slice(-this.MAX_ENTRIES);
|
|
59
|
+
console.error(`[ToolHistory] Loaded ${this.history.length} entries from disk`);
|
|
60
|
+
// If file is getting too large, trim it
|
|
61
|
+
if (lines.length > this.MAX_ENTRIES * 2) {
|
|
62
|
+
this.trimHistoryFile();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('[ToolHistory] Failed to load history:', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Trim history file to prevent it from growing indefinitely
|
|
71
|
+
*/
|
|
72
|
+
trimHistoryFile() {
|
|
73
|
+
try {
|
|
74
|
+
console.error('[ToolHistory] Trimming history file...');
|
|
75
|
+
// Keep last 1000 entries in memory
|
|
76
|
+
const keepEntries = this.history.slice(-this.MAX_ENTRIES);
|
|
77
|
+
// Write them back
|
|
78
|
+
const lines = keepEntries.map(entry => JSON.stringify(entry)).join('\n') + '\n';
|
|
79
|
+
fs.writeFileSync(this.historyFile, lines, 'utf-8');
|
|
80
|
+
console.error(`[ToolHistory] Trimmed to ${keepEntries.length} entries`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('[ToolHistory] Failed to trim history file:', error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Async write processor - batches writes to avoid blocking
|
|
88
|
+
*/
|
|
89
|
+
startWriteProcessor() {
|
|
90
|
+
this.writeInterval = setInterval(() => {
|
|
91
|
+
if (this.writeQueue.length > 0 && !this.isWriting) {
|
|
92
|
+
this.flushToDisk();
|
|
93
|
+
}
|
|
94
|
+
}, 1000); // Flush every second
|
|
95
|
+
// Prevent interval from keeping process alive during shutdown/tests
|
|
96
|
+
this.writeInterval.unref();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Flush queued writes to disk
|
|
100
|
+
*/
|
|
101
|
+
async flushToDisk() {
|
|
102
|
+
if (this.isWriting || this.writeQueue.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
this.isWriting = true;
|
|
105
|
+
const toWrite = [...this.writeQueue];
|
|
106
|
+
this.writeQueue = [];
|
|
107
|
+
try {
|
|
108
|
+
// Append to file (atomic append operation)
|
|
109
|
+
const lines = toWrite.map(entry => JSON.stringify(entry)).join('\n') + '\n';
|
|
110
|
+
fs.appendFileSync(this.historyFile, lines, 'utf-8');
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('[ToolHistory] Failed to write to disk:', error);
|
|
114
|
+
// Put back in queue on failure
|
|
115
|
+
this.writeQueue.unshift(...toWrite);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
this.isWriting = false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Add a tool call to history
|
|
123
|
+
*/
|
|
124
|
+
addCall(toolName, args, output, duration) {
|
|
125
|
+
const record = {
|
|
126
|
+
timestamp: new Date().toISOString(),
|
|
127
|
+
toolName,
|
|
128
|
+
arguments: args,
|
|
129
|
+
output,
|
|
130
|
+
duration
|
|
131
|
+
};
|
|
132
|
+
this.history.push(record);
|
|
133
|
+
// Keep only last 1000 in memory
|
|
134
|
+
if (this.history.length > this.MAX_ENTRIES) {
|
|
135
|
+
this.history.shift();
|
|
136
|
+
}
|
|
137
|
+
// Queue for async write
|
|
138
|
+
this.writeQueue.push(record);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get recent tool calls with filters
|
|
142
|
+
*/
|
|
143
|
+
getRecentCalls(options) {
|
|
144
|
+
let results = [...this.history];
|
|
145
|
+
// Filter by tool name
|
|
146
|
+
if (options.toolName) {
|
|
147
|
+
results = results.filter(r => r.toolName === options.toolName);
|
|
148
|
+
}
|
|
149
|
+
// Filter by timestamp
|
|
150
|
+
if (options.since) {
|
|
151
|
+
const sinceDate = new Date(options.since);
|
|
152
|
+
results = results.filter(r => new Date(r.timestamp) >= sinceDate);
|
|
153
|
+
}
|
|
154
|
+
// Limit results (default 50, max 1000)
|
|
155
|
+
const limit = Math.min(options.maxResults || 50, 1000);
|
|
156
|
+
return results.slice(-limit);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get recent calls formatted with local timezone
|
|
160
|
+
*/
|
|
161
|
+
getRecentCallsFormatted(options) {
|
|
162
|
+
const calls = this.getRecentCalls(options);
|
|
163
|
+
// Format timestamps to local timezone
|
|
164
|
+
return calls.map(call => ({
|
|
165
|
+
...call,
|
|
166
|
+
timestamp: formatLocalTimestamp(call.timestamp)
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get current stats
|
|
171
|
+
*/
|
|
172
|
+
getStats() {
|
|
173
|
+
return {
|
|
174
|
+
totalEntries: this.history.length,
|
|
175
|
+
oldestEntry: this.history[0]?.timestamp,
|
|
176
|
+
newestEntry: this.history[this.history.length - 1]?.timestamp,
|
|
177
|
+
historyFile: this.historyFile,
|
|
178
|
+
queuedWrites: this.writeQueue.length
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Cleanup method - clears interval and flushes pending writes
|
|
183
|
+
* Call this during shutdown or in tests
|
|
184
|
+
*/
|
|
185
|
+
async cleanup() {
|
|
186
|
+
// Clear the interval
|
|
187
|
+
if (this.writeInterval) {
|
|
188
|
+
clearInterval(this.writeInterval);
|
|
189
|
+
this.writeInterval = undefined;
|
|
190
|
+
}
|
|
191
|
+
// Flush any remaining writes
|
|
192
|
+
if (this.writeQueue.length > 0) {
|
|
193
|
+
await this.flushToDisk();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export const toolHistory = new ToolHistory();
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.2.
|
|
1
|
+
export declare const VERSION = "0.2.17";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.2.
|
|
1
|
+
export const VERSION = '0.2.17';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderwhy-er/desktop-commander",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"description": "MCP server for terminal operations and file editing",
|
|
5
5
|
"mcpName": "io.github.wonderwhy-er/desktop-commander",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"link:local": "npm run build && npm link",
|
|
43
43
|
"unlink:local": "npm unlink",
|
|
44
44
|
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
|
|
45
|
+
"build:mcpb": "node scripts/build-mcpb.cjs",
|
|
46
|
+
"release": "node scripts/publish-release.cjs",
|
|
47
|
+
"release:minor": "node scripts/publish-release.cjs --minor",
|
|
48
|
+
"release:major": "node scripts/publish-release.cjs --major",
|
|
49
|
+
"release:dry": "node scripts/publish-release.cjs --dry-run",
|
|
45
50
|
"logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
|
|
46
51
|
"logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
|
|
47
52
|
"logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",
|