Skip to main content
Glama
push-based

Angular Toolkit MCP

by push-based
execute-process.ts5.09 kB
import { type ChildProcess, type ChildProcessByStdio, type SpawnOptionsWithStdioTuple, type StdioPipe, spawn, } from 'node:child_process'; import type { Readable, Writable } from 'node:stream'; import { formatCommandLog } from './format-command-log.js'; import { isVerbose } from './logging.js'; import { calcDuration } from './utils.js'; /** * Represents the process result. * @category Types * @public * @property {string} stdout - The stdout of the process. * @property {string} stderr - The stderr of the process. * @property {number | null} code - The exit code of the process. */ export type ProcessResult = { stdout: string; stderr: string; code: number | null; date: string; duration: number; }; /** * Error class for process errors. * Contains additional information about the process result. * @category Error * @public * @class * @extends Error * @example * const result = await executeProcess({}) * .catch((error) => { * if (error instanceof ProcessError) { * console.error(error.code); * console.error(error.stderr); * console.error(error.stdout); * } * }); * */ export class ProcessError extends Error { code: number | null; stderr: string; stdout: string; constructor(result: ProcessResult) { super(result.stderr); this.code = result.code; this.stderr = result.stderr; this.stdout = result.stdout; } } /** * Process config object. Contains the command, args and observer. * @param cfg - process config object with command, args and observer (optional) * @category Types * @public * @property {string} command - The command to execute. * @property {string[]} args - The arguments for the command. * @property {ProcessObserver} observer - The observer for the process. * * @example * * // bash command * const cfg = { * command: 'bash', * args: ['-c', 'echo "hello world"'] * }; * * // node command * const cfg = { * command: 'node', * args: ['--version'] * }; * * // npx command * const cfg = { * command: 'npx', * args: ['--version'] * */ export type ProcessConfig = Omit< SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>, 'stdio' > & { command: string; args?: string[]; observer?: ProcessObserver; ignoreExitCode?: boolean; }; /** * Process observer object. Contains the onStdout, error and complete function. * @category Types * @public * @property {function} onStdout - The onStdout function of the observer (optional). * @property {function} onError - The error function of the observer (optional). * @property {function} onComplete - The complete function of the observer (optional). * * @example * const observer = { * onStdout: (stdout) => console.info(stdout) * } */ export type ProcessObserver = { onStdout?: (stdout: string, sourceProcess?: ChildProcess) => void; onStderr?: (stderr: string, sourceProcess?: ChildProcess) => void; onError?: (error: ProcessError) => void; onComplete?: () => void; }; /** * Executes a process and returns a promise with the result as `ProcessResult`. * * @example * * // sync process execution * const result = await executeProcess({ * command: 'node', * args: ['--version'] * }); * * console.info(result); * * // async process execution * const result = await executeProcess({ * command: 'node', * args: ['download-data.js'], * observer: { * onStdout: updateProgress, * error: handleError, * complete: cleanLogs, * } * }); * * console.info(result); * * @param cfg - see {@link ProcessConfig} */ export function executeProcess(cfg: ProcessConfig): Promise<ProcessResult> { const { command, args, observer, ignoreExitCode = false, ...options } = cfg; const { onStdout, onStderr, onError, onComplete } = observer ?? {}; const date = new Date().toISOString(); const start = performance.now(); if (isVerbose()) { console.log(formatCommandLog(command, args, `${cfg.cwd ?? process.cwd()}`)); } return new Promise((resolve, reject) => { // shell:true tells Windows to use shell command for spawning a child process const spawnedProcess = spawn(command, args ?? [], { shell: true, windowsHide: true, ...options, }) as ChildProcessByStdio<Writable, Readable, Readable>; let stdout = ''; let stderr = ''; spawnedProcess.stdout.on('data', (data) => { stdout += String(data); onStdout?.(String(data), spawnedProcess); }); spawnedProcess.stderr.on('data', (data) => { stderr += String(data); onStderr?.(String(data), spawnedProcess); }); spawnedProcess.on('error', (err) => { stderr += err.toString(); }); spawnedProcess.on('close', (code) => { const timings = { date, duration: calcDuration(start) }; if (code === 0 || ignoreExitCode) { onComplete?.(); resolve({ code, stdout, stderr, ...timings }); } else { const errorMsg = new ProcessError({ code, stdout, stderr, ...timings }); onError?.(errorMsg); reject(errorMsg); } }); }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/push-based/angular-toolkit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server