Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 1x 1x 1x 65x 65x 65x 65x 1x 65x 65x 65x 65x 65x 65x 65x 59x 65x 65x 6x 65x 3x 3x 3x 3x 3x 2x 3x 65x 64x 64x 65x 1x 1x | import { Injectable } from '@nestjs/common';
import { spawn, ChildProcess } from 'child_process';
export interface IProcessResult {
exitCode: number | null;
stdout: string;
stderr: string;
timedOut: boolean;
}
@Injectable()
export class ProcessRunnerService {
async runProcess(
command: string,
args: string[],
cwd: string,
timeoutMs: number
): Promise<IProcessResult> {
return new Promise((resolve) => {
// With shell: true, pass the full command as a single string
// If args are provided, join them; otherwise use command as-is
const shellCommand = args.length > 0
? `${command} ${args.join(' ')}`
: command;
// Explicitly wrap in sh -c to ensure shell interpretation
// This ensures pipes and other shell operators are properly interpreted
const childProcess: ChildProcess = spawn(
'sh',
[ '-c', shellCommand ],
{
cwd,
// We're explicitly invoking sh, so no need for shell: true
shell: false,
detached: false
}
);
if (process.env.PROCESS_RUNNER_DEBUG === 'true') {
// eslint-disable-next-line no-console
console.log(
`[ProcessRunner] spawn command="${shellCommand}" cwd="${cwd}"`
);
}
// Close stdin so commands expecting EOF when run without a TTY
// (like cursor-agent in non-interactive mode) can exit gracefully.
Eif (childProcess.stdin) {
childProcess.stdin.end();
}
let stdout = '';
let stderr = '';
let timedOut = false;
Eif (childProcess.stdout) {
childProcess.stdout.on('data', (data) => {
stdout += data.toString();
});
}
Eif (childProcess.stderr) {
childProcess.stderr.on('data', (data) => {
stderr += data.toString();
});
}
const timeout = setTimeout(() => {
timedOut = true;
// Kill the process group to ensure all children are terminated
try {
Eif (childProcess.pid) {
childProcess.kill('SIGTERM');
// Give it a moment, then force kill
setTimeout(() => {
Iif (!childProcess.killed) {
childProcess.kill('SIGKILL');
}
}, 1000);
}
} catch {
// Ignore kill errors
}
resolve({
exitCode: null,
stdout,
stderr,
timedOut: true
});
}, timeoutMs);
childProcess.on('close', (code) => {
clearTimeout(timeout);
resolve({
exitCode: code,
stdout,
stderr,
timedOut
});
});
childProcess.on('error', () => {
clearTimeout(timeout);
resolve({
exitCode: null,
stdout,
stderr,
timedOut
});
});
});
}
}
|