snow-ai 0.3.18 → 0.3.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.
- package/dist/cli.js +3 -0
- package/dist/mcp/aceCodeSearch.js +9 -1
- package/dist/mcp/bash.js +32 -4
- package/dist/utils/processManager.d.ts +27 -0
- package/dist/utils/processManager.js +75 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import App from './app.js';
|
|
|
9
9
|
import { vscodeConnection } from './utils/vscodeConnection.js';
|
|
10
10
|
import { resourceMonitor } from './utils/resourceMonitor.js';
|
|
11
11
|
import { initializeProfiles } from './utils/configManager.js';
|
|
12
|
+
import { processManager } from './utils/processManager.js';
|
|
12
13
|
const execAsync = promisify(exec);
|
|
13
14
|
// Check for updates asynchronously
|
|
14
15
|
async function checkForUpdates(currentVersion) {
|
|
@@ -125,6 +126,8 @@ process.stdout.write('\x1b[?2004l');
|
|
|
125
126
|
// Re-enable on exit to avoid polluting parent shell
|
|
126
127
|
const cleanup = () => {
|
|
127
128
|
process.stdout.write('\x1b[?2004l');
|
|
129
|
+
// Kill all child processes first
|
|
130
|
+
processManager.killAll();
|
|
128
131
|
// Stop resource monitoring
|
|
129
132
|
resourceMonitor.stopMonitoring();
|
|
130
133
|
// Disconnect VSCode connection before exit
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
4
|
import { AsyncFzf } from 'fzf';
|
|
5
|
+
import { processManager } from '../utils/processManager.js';
|
|
5
6
|
// Utility functions
|
|
6
7
|
import { detectLanguage } from './utils/aceCodeSearch/language.utils.js';
|
|
7
8
|
import { loadExclusionPatterns, shouldExcludeDirectory, readFileWithCache, } from './utils/aceCodeSearch/filesystem.utils.js';
|
|
@@ -476,7 +477,10 @@ export class ACECodeSearchService {
|
|
|
476
477
|
expandGlobBraces(glob) {
|
|
477
478
|
// Match {a,b,c} pattern
|
|
478
479
|
const braceMatch = glob.match(/^(.+)\{([^}]+)\}(.*)$/);
|
|
479
|
-
if (!braceMatch ||
|
|
480
|
+
if (!braceMatch ||
|
|
481
|
+
!braceMatch[1] ||
|
|
482
|
+
!braceMatch[2] ||
|
|
483
|
+
braceMatch[3] === undefined) {
|
|
480
484
|
return [glob];
|
|
481
485
|
}
|
|
482
486
|
const prefix = braceMatch[1];
|
|
@@ -506,6 +510,8 @@ export class ACECodeSearchService {
|
|
|
506
510
|
cwd: this.basePath,
|
|
507
511
|
windowsHide: true,
|
|
508
512
|
});
|
|
513
|
+
// Register child process for cleanup
|
|
514
|
+
processManager.register(child);
|
|
509
515
|
const stdoutChunks = [];
|
|
510
516
|
const stderrChunks = [];
|
|
511
517
|
child.stdout.on('data', chunk => stdoutChunks.push(chunk));
|
|
@@ -572,6 +578,8 @@ export class ACECodeSearchService {
|
|
|
572
578
|
cwd: this.basePath,
|
|
573
579
|
windowsHide: true,
|
|
574
580
|
});
|
|
581
|
+
// Register child process for cleanup
|
|
582
|
+
processManager.register(child);
|
|
575
583
|
const stdoutChunks = [];
|
|
576
584
|
const stderrChunks = [];
|
|
577
585
|
child.stdout.on('data', chunk => stdoutChunks.push(chunk));
|
package/dist/mcp/bash.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { exec } from 'child_process';
|
|
2
|
-
import { promisify } from 'util';
|
|
3
2
|
// Utility functions
|
|
4
3
|
import { isDangerousCommand, truncateOutput, } from './utils/bash/security.utils.js';
|
|
5
|
-
|
|
4
|
+
import { processManager } from '../utils/processManager.js';
|
|
6
5
|
/**
|
|
7
6
|
* Terminal Command Execution Service
|
|
8
7
|
* Executes terminal commands directly using the system's default shell
|
|
@@ -38,8 +37,8 @@ export class TerminalCommandService {
|
|
|
38
37
|
if (isDangerousCommand(command)) {
|
|
39
38
|
throw new Error(`Dangerous command detected and blocked: ${command.slice(0, 50)}`);
|
|
40
39
|
}
|
|
41
|
-
// Execute command using system default shell
|
|
42
|
-
const
|
|
40
|
+
// Execute command using system default shell and register the process
|
|
41
|
+
const childProcess = exec(command, {
|
|
43
42
|
cwd: this.workingDirectory,
|
|
44
43
|
timeout,
|
|
45
44
|
maxBuffer: this.maxOutputLength,
|
|
@@ -51,6 +50,35 @@ export class TerminalCommandService {
|
|
|
51
50
|
}),
|
|
52
51
|
},
|
|
53
52
|
});
|
|
53
|
+
// Register child process for cleanup
|
|
54
|
+
processManager.register(childProcess);
|
|
55
|
+
// Convert to promise
|
|
56
|
+
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
|
57
|
+
let stdoutData = '';
|
|
58
|
+
let stderrData = '';
|
|
59
|
+
childProcess.stdout?.on('data', chunk => {
|
|
60
|
+
stdoutData += chunk;
|
|
61
|
+
});
|
|
62
|
+
childProcess.stderr?.on('data', chunk => {
|
|
63
|
+
stderrData += chunk;
|
|
64
|
+
});
|
|
65
|
+
childProcess.on('error', reject);
|
|
66
|
+
childProcess.on('close', (code, signal) => {
|
|
67
|
+
if (signal) {
|
|
68
|
+
reject(new Error(`Process killed by signal ${signal}`));
|
|
69
|
+
}
|
|
70
|
+
else if (code === 0) {
|
|
71
|
+
resolve({ stdout: stdoutData, stderr: stderrData });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const error = new Error(`Process exited with code ${code}`);
|
|
75
|
+
error.code = code;
|
|
76
|
+
error.stdout = stdoutData;
|
|
77
|
+
error.stderr = stderrData;
|
|
78
|
+
reject(error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
54
82
|
// Truncate output if too long
|
|
55
83
|
return {
|
|
56
84
|
stdout: truncateOutput(stdout, this.maxOutputLength),
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Process Manager
|
|
4
|
+
* Tracks and manages all child processes to ensure proper cleanup
|
|
5
|
+
*/
|
|
6
|
+
declare class ProcessManager {
|
|
7
|
+
private processes;
|
|
8
|
+
private isShuttingDown;
|
|
9
|
+
/**
|
|
10
|
+
* Register a child process for tracking
|
|
11
|
+
*/
|
|
12
|
+
register(process: ChildProcess): void;
|
|
13
|
+
/**
|
|
14
|
+
* Kill a specific process gracefully
|
|
15
|
+
*/
|
|
16
|
+
private killProcess;
|
|
17
|
+
/**
|
|
18
|
+
* Kill all tracked processes
|
|
19
|
+
*/
|
|
20
|
+
killAll(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get count of active processes
|
|
23
|
+
*/
|
|
24
|
+
getActiveCount(): number;
|
|
25
|
+
}
|
|
26
|
+
export declare const processManager: ProcessManager;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Manager
|
|
3
|
+
* Tracks and manages all child processes to ensure proper cleanup
|
|
4
|
+
*/
|
|
5
|
+
class ProcessManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
Object.defineProperty(this, "processes", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true,
|
|
11
|
+
value: new Set()
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(this, "isShuttingDown", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: false
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Register a child process for tracking
|
|
22
|
+
*/
|
|
23
|
+
register(process) {
|
|
24
|
+
if (this.isShuttingDown) {
|
|
25
|
+
// If we're already shutting down, kill immediately
|
|
26
|
+
this.killProcess(process);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.processes.add(process);
|
|
30
|
+
// Auto-remove when process exits
|
|
31
|
+
const cleanup = () => {
|
|
32
|
+
this.processes.delete(process);
|
|
33
|
+
};
|
|
34
|
+
process.once('exit', cleanup);
|
|
35
|
+
process.once('error', cleanup);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Kill a specific process gracefully
|
|
39
|
+
*/
|
|
40
|
+
killProcess(process) {
|
|
41
|
+
try {
|
|
42
|
+
if (process.pid && !process.killed) {
|
|
43
|
+
// Try graceful termination first
|
|
44
|
+
process.kill('SIGTERM');
|
|
45
|
+
// Force kill after 1 second if still alive
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
if (process.pid && !process.killed) {
|
|
48
|
+
process.kill('SIGKILL');
|
|
49
|
+
}
|
|
50
|
+
}, 1000);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Process might already be dead, ignore errors
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Kill all tracked processes
|
|
59
|
+
*/
|
|
60
|
+
killAll() {
|
|
61
|
+
this.isShuttingDown = true;
|
|
62
|
+
for (const process of this.processes) {
|
|
63
|
+
this.killProcess(process);
|
|
64
|
+
}
|
|
65
|
+
this.processes.clear();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get count of active processes
|
|
69
|
+
*/
|
|
70
|
+
getActiveCount() {
|
|
71
|
+
return this.processes.size;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Export singleton instance
|
|
75
|
+
export const processManager = new ProcessManager();
|