Skip to main content
Glama
cli-executor.ts3.62 kB
import { exec, ExecException } from 'child_process'; import { getConfig, getAzureScopeArgs } from './config.js'; import { withRetry, isTransientError } from './retry.js'; import { logger } from './logger.js'; export interface CommandResult { stdout: string; stderr: string; exitCode: number; success: boolean; parsedOutput?: unknown; parseError?: string; } export interface ExecuteOptions { timeout?: number; cwd?: string; env?: Record<string, string>; applyScope?: boolean; enableRetry?: boolean; } async function execInternal(command: string, options: ExecuteOptions = {}): Promise<CommandResult> { const config = getConfig(); const timeout = options.timeout ?? config.commandTimeoutMs; logger.debug('Executing', { command, timeout }); return new Promise(resolve => { exec(command, { timeout, cwd: options.cwd, env: { ...process.env, ...options.env }, maxBuffer: 10 * 1024 * 1024, }, (error: ExecException | null, stdout: string, stderr: string) => { const exitCode = error?.code ?? 0; const success = exitCode === 0; let parsedOutput: unknown; let parseError: string | undefined; if (stdout.trim()) { try { parsedOutput = JSON.parse(stdout); } catch (e) { parseError = `JSON parse failed: ${e instanceof Error ? e.message : 'unknown'}`; } } const result = { stdout: stdout.trim(), stderr: stderr.trim(), exitCode, success, parsedOutput, parseError }; if (success) { logger.debug('Command succeeded', { command, exitCode }); } else { logger.warn('Command failed', { command, exitCode, stderr: stderr.trim() }); } resolve(result); }); }); } export async function executeCommand(command: string, options: ExecuteOptions = {}): Promise<CommandResult> { const { enableRetry = false } = options; if (!enableRetry) return execInternal(command, options); return withRetry( async () => { const result = await execInternal(command, options); if (!result.success && isTransientError(result.stderr)) throw new Error(result.stderr); return result; }, 'executeCommand', { isRetryable: e => isTransientError(e) } ); } export async function executeAzCommand(command: string, options: ExecuteOptions = {}): Promise<CommandResult> { const { applyScope = false, enableRetry = true } = options; let cmd = command; if (!cmd.includes('--output') && !cmd.includes('-o ')) cmd += ' --output json'; if (applyScope) { const scope = getAzureScopeArgs(); if (scope.length) cmd += ' ' + scope.join(' '); } return executeCommand(cmd, { ...options, enableRetry }); } export async function validateAzureCLI(): Promise<boolean> { const result = await executeCommand('az --version', { enableRetry: false }); return result.success; } export async function getAzureAccountInfo(): Promise<{ user?: string; tenantId?: string; subscriptionId?: string } | null> { const result = await executeAzCommand('az account show', { enableRetry: false }); if (!result.success || !result.parsedOutput) return null; const account = result.parsedOutput as { user?: { name?: string }; tenantId?: string; id?: string }; return { user: account.user?.name, tenantId: account.tenantId, subscriptionId: account.id }; }

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/vedantparmar12/Azure-_MCP'

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