@wonderwhy-er/desktop-commander 0.1.18 → 0.1.19

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.
@@ -1,9 +1,19 @@
1
1
  import { CommandExecutionResult, ActiveSession } from './types.js';
2
+ interface CompletedSession {
3
+ pid: number;
4
+ output: string;
5
+ exitCode: number | null;
6
+ startTime: Date;
7
+ endTime: Date;
8
+ }
2
9
  export declare class TerminalManager {
3
10
  private sessions;
11
+ private completedSessions;
4
12
  executeCommand(command: string, timeoutMs?: number): Promise<CommandExecutionResult>;
5
13
  getNewOutput(pid: number): string | null;
6
14
  forceTerminate(pid: number): boolean;
7
15
  listActiveSessions(): ActiveSession[];
16
+ listCompletedSessions(): CompletedSession[];
8
17
  }
9
18
  export declare const terminalManager: TerminalManager;
19
+ export {};
@@ -3,6 +3,7 @@ import { DEFAULT_COMMAND_TIMEOUT } from './config.js';
3
3
  export class TerminalManager {
4
4
  constructor() {
5
5
  this.sessions = new Map();
6
+ this.completedSessions = new Map();
6
7
  }
7
8
  async executeCommand(command, timeoutMs = DEFAULT_COMMAND_TIMEOUT) {
8
9
  const process = spawn(command, [], { shell: true });
@@ -38,8 +39,21 @@ export class TerminalManager {
38
39
  isBlocked: true
39
40
  });
40
41
  }, timeoutMs);
41
- process.on('exit', () => {
42
+ process.on('exit', (code) => {
42
43
  if (process.pid) {
44
+ // Store completed session before removing active session
45
+ this.completedSessions.set(process.pid, {
46
+ pid: process.pid,
47
+ output: output + session.lastOutput, // Combine all output
48
+ exitCode: code,
49
+ startTime: session.startTime,
50
+ endTime: new Date()
51
+ });
52
+ // Keep only last 100 completed sessions
53
+ if (this.completedSessions.size > 100) {
54
+ const oldestKey = Array.from(this.completedSessions.keys())[0];
55
+ this.completedSessions.delete(oldestKey);
56
+ }
43
57
  this.sessions.delete(process.pid);
44
58
  }
45
59
  resolve({
@@ -51,13 +65,21 @@ export class TerminalManager {
51
65
  });
52
66
  }
53
67
  getNewOutput(pid) {
68
+ // First check active sessions
54
69
  const session = this.sessions.get(pid);
55
- if (!session) {
56
- return null;
70
+ if (session) {
71
+ const output = session.lastOutput;
72
+ session.lastOutput = '';
73
+ return output;
74
+ }
75
+ // Then check completed sessions
76
+ const completedSession = this.completedSessions.get(pid);
77
+ if (completedSession) {
78
+ // Format completion message with exit code and runtime
79
+ const runtime = (completedSession.endTime.getTime() - completedSession.startTime.getTime()) / 1000;
80
+ return `Process completed with exit code ${completedSession.exitCode}\nRuntime: ${runtime}s\nFinal output:\n${completedSession.output}`;
57
81
  }
58
- const output = session.lastOutput;
59
- session.lastOutput = '';
60
- return output;
82
+ return null;
61
83
  }
62
84
  forceTerminate(pid) {
63
85
  const session = this.sessions.get(pid);
@@ -86,5 +108,8 @@ export class TerminalManager {
86
108
  runtime: now.getTime() - session.startTime.getTime()
87
109
  }));
88
110
  }
111
+ listCompletedSessions() {
112
+ return Array.from(this.completedSessions.values());
113
+ }
89
114
  }
90
115
  export const terminalManager = new TerminalManager();
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ export declare const LongRunningTaskSchema: z.ZodObject<{
3
+ total_steps: z.ZodDefault<z.ZodNumber>;
4
+ step_duration: z.ZodDefault<z.ZodNumber>;
5
+ should_fail: z.ZodDefault<z.ZodBoolean>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ total_steps: number;
8
+ step_duration: number;
9
+ should_fail: boolean;
10
+ }, {
11
+ total_steps?: number | undefined;
12
+ step_duration?: number | undefined;
13
+ should_fail?: boolean | undefined;
14
+ }>;
15
+ export declare function executeLongRunning(args: unknown, server: any, progressToken?: string): Promise<{
16
+ content: {
17
+ type: string;
18
+ text: string;
19
+ }[];
20
+ }>;
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ export const LongRunningTaskSchema = z.object({
3
+ total_steps: z.number().default(5).describe("Total number of steps to execute"),
4
+ step_duration: z.number().default(1000).describe("Duration of each step in milliseconds"),
5
+ should_fail: z.boolean().default(false).describe("Whether the task should fail midway through"),
6
+ });
7
+ export async function executeLongRunning(args, server, progressToken) {
8
+ const parsed = LongRunningTaskSchema.safeParse(args);
9
+ if (!parsed.success) {
10
+ throw new Error(`Invalid arguments for long_running_task: ${parsed.error}`);
11
+ }
12
+ const { total_steps, step_duration, should_fail } = parsed.data;
13
+ try {
14
+ for (let step = 1; step <= total_steps; step++) {
15
+ // Simulate work
16
+ await new Promise(resolve => setTimeout(resolve, step_duration));
17
+ // Send progress notification if we have a token
18
+ if (progressToken) {
19
+ await server.notification({
20
+ method: "notifications/progress",
21
+ params: {
22
+ progress: step,
23
+ total: total_steps,
24
+ progressToken,
25
+ detail: `Completed step ${step} of ${total_steps}${should_fail && step === Math.floor(total_steps / 2)
26
+ ? ' (about to fail)'
27
+ : ''}`
28
+ }
29
+ });
30
+ }
31
+ // If should_fail is true, fail halfway through
32
+ if (should_fail && step === Math.floor(total_steps / 2)) {
33
+ throw new Error("Task failed halfway through as requested");
34
+ }
35
+ }
36
+ return {
37
+ content: [{
38
+ type: "text",
39
+ text: `Long running task completed successfully after ${total_steps} steps`
40
+ }]
41
+ };
42
+ }
43
+ catch (error) {
44
+ // Send error notification if we have a token
45
+ if (progressToken) {
46
+ await server.notification({
47
+ method: "notifications/progress",
48
+ params: {
49
+ progress: Math.floor(total_steps / 2),
50
+ total: total_steps,
51
+ progressToken,
52
+ detail: error instanceof Error ? error.message : 'Unknown error',
53
+ error: true
54
+ }
55
+ });
56
+ }
57
+ throw error;
58
+ }
59
+ }
package/dist/types.d.ts CHANGED
@@ -22,3 +22,10 @@ export interface ActiveSession {
22
22
  isBlocked: boolean;
23
23
  runtime: number;
24
24
  }
25
+ export interface CompletedSession {
26
+ pid: number;
27
+ output: string;
28
+ exitCode: number | null;
29
+ startTime: Date;
30
+ endTime: Date;
31
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.18";
1
+ export declare const VERSION = "0.1.19";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.1.18';
1
+ export const VERSION = '0.1.19';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "license": "MIT",
6
6
  "author": "Eduards Ruzga",