mcp-server-prometheus
by loglmhq
- src
import { exec, ExecOptions } from "child_process";
import { ObjectEncodingOptions } from "fs";
type ExecResult = {
// FYI leave this type for now as a declaration of the expected shape of the result for BOTH success and failure (errors)
// do not switch to using ExecException b/c that only applies to failures
stdout: string;
stderr: string;
// message is the error message from the child process, not sure I like this naming
// - perhaps worth pushing the error logic out of messagesFor back into catch block above
message?: string;
};
/**
* Executes a file with the given arguments, piping input to stdin.
* @param {string} interpreter - The file to execute.
* @param {string} stdin_text - The string to pipe to stdin.
* @returns {Promise<ExecResult>} A promise that resolves with the stdout and stderr of the command. `message` is provided on a failure to explain the error.
*/
function execFileWithInput(
interpreter: string,
stdin_text: string,
options: ObjectEncodingOptions & ExecOptions
): Promise<ExecResult> {
// FYI for now, using `exec()` so the interpreter can have cmd+args AIO
// could switch to `execFile()` to pass args array separately
// TODO starts with fish too? "fish -..." PRN use a library to parse the command and determine this?
if (interpreter.split(" ")[0] === "fish") {
// PRN also check error from fish and add possible clarification to error message though there are legit ways to trigger that same error message! i.e. `fish .` which is not the same issue!
return fishWorkaround(interpreter, stdin_text, options);
}
return new Promise((resolve, reject) => {
const child = exec(interpreter, options, (error, stdout, stderr) => {
if (error) {
reject({ message: error.message, stdout, stderr });
} else {
resolve({ stdout, stderr });
}
});
if (stdin_text) {
if (child.stdin === null) {
reject(new Error("Unexpected failure: child.stdin is null"));
return;
}
child.stdin.write(stdin_text);
child.stdin.end();
}
});
}
async function fishWorkaround(
interpreter: string,
script: string,
options: ObjectEncodingOptions & ExecOptions
): Promise<ExecResult> {
// fish right now chokes on piped input (STDIN) + node's exec/spawn/etc, so lets use a workaround to echo the input
// base64 encode thee input, then decode in pipeline
const base64Script = Buffer.from(script).toString("base64");
const command = `${interpreter} -c "echo ${base64Script} | base64 -d | fish"`;
return new Promise((resolve, reject) => {
// const child = ... // careful with refactoring not to return that unused child
exec(command, options, (error, stdout, stderr) => {
// I like this style of error vs success handling! it's beautiful-est (prommises are underrated)
if (error) {
reject({ message: error.message, stdout, stderr });
} else {
resolve({ stdout, stderr });
}
});
});
}
export { execFileWithInput, ExecResult };