UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

291 lines 10.9 kB
// src/lib/action/actionExecutor.ts /** * CLI execution for GitHub Action * @module action/actionExecutor */ import * as exec from "@actions/exec"; import * as core from "@actions/core"; import * as fs from "fs"; import * as path from "path"; import { buildEnvironmentVariables } from "./actionInputs.js"; import { withTimeout } from "../utils/errorHandling.js"; /** Default timeout for CLI execution (5 minutes) */ const DEFAULT_EXECUTION_TIMEOUT_MS = 300000; /** * Transform CLI token usage format to action format */ function transformTokenUsage(cliUsage) { if (!cliUsage) { return undefined; } return { promptTokens: cliUsage.input, completionTokens: cliUsage.output, totalTokens: cliUsage.total, }; } /** * Transform CLI evaluation format to action format (scale 1-10 to 0-100) */ function transformEvaluation(cliEval) { if (!cliEval) { return undefined; } return { overallScore: cliEval.overall * 10, // Scale to 0-100 relevance: cliEval.relevance, accuracy: cliEval.accuracy, completeness: cliEval.completeness, }; } /** * Transform CLI response to action result format */ export function transformCliResponse(cliResponse) { return { response: cliResponse.content, responseJson: cliResponse, // Use top-level provider/model if available, otherwise fallback to analytics provider: cliResponse.provider || cliResponse.analytics?.provider, model: cliResponse.model || cliResponse.analytics?.model, usage: transformTokenUsage(cliResponse.usage), cost: cliResponse.analytics?.cost, executionTime: cliResponse.responseTime, evaluation: transformEvaluation(cliResponse.evaluation), }; } /** * Build CLI arguments from action inputs (using verified camelCase flags) */ export function buildCliArgs(inputs) { const args = [inputs.command, inputs.prompt]; // Provider & Model if (inputs.provider && inputs.provider !== "auto") { args.push("--provider", inputs.provider); } if (inputs.model) { args.push("--model", inputs.model); } // Generation parameters (camelCase flags!) if (inputs.temperature !== undefined) { args.push("--temperature", inputs.temperature.toString()); } if (inputs.maxTokens !== undefined) { args.push("--maxTokens", inputs.maxTokens.toString()); // NOT --max-tokens } if (inputs.systemPrompt) { args.push("--system", inputs.systemPrompt); } // Output format (always JSON for parsing) args.push("--format", "json"); args.push("--quiet"); args.push("--noColor"); // Multimodal inputs const { multimodal } = inputs; if (multimodal.imagePaths) { for (const img of multimodal.imagePaths) { args.push("--image", img); } } if (multimodal.pdfPaths) { for (const pdf of multimodal.pdfPaths) { args.push("--pdf", pdf); } } if (multimodal.csvPaths) { for (const csv of multimodal.csvPaths) { args.push("--csv", csv); } } if (multimodal.videoPaths) { for (const video of multimodal.videoPaths) { args.push("--video", video); } } // Extended thinking (camelCase flags!) if (inputs.thinking.enabled) { args.push("--thinking"); args.push("--thinkingLevel", inputs.thinking.level); // NOT --thinking-level args.push("--thinkingBudget", inputs.thinking.budget.toString()); // NOT --thinking-budget } // Features (camelCase flags!) if (inputs.enableAnalytics) { args.push("--enableAnalytics"); // NOT --enable-analytics } if (inputs.enableEvaluation) { args.push("--enableEvaluation"); // NOT --enable-evaluation } // Timeout if (inputs.timeout) { args.push("--timeout", inputs.timeout.toString()); } // Output file if (inputs.outputFile) { args.push("--output", inputs.outputFile); } // Debug if (inputs.debug) { args.push("--debug"); } return args; } /** * Install NeuroLink CLI */ export async function installNeurolink(version) { core.info(`Installing @juspay/neurolink@${version}...`); const installArgs = version === "latest" ? ["install", "-g", "@juspay/neurolink"] : ["install", "-g", `@juspay/neurolink@${version}`]; await exec.exec("npm", installArgs, { silent: !core.isDebug(), }); core.info("NeuroLink CLI installed successfully"); } /** * Execute NeuroLink CLI command * @param args - CLI arguments * @param env - Environment variables * @param workingDirectory - Working directory for execution * @param timeout - Timeout in milliseconds (defaults to 5 minutes) */ export async function executeNeurolink(args, env, workingDirectory, timeout = DEFAULT_EXECUTION_TIMEOUT_MS) { const startTime = Date.now(); try { // Filter out undefined values from process.env const processEnv = {}; for (const [key, value] of Object.entries(process.env)) { if (value !== undefined) { processEnv[key] = value; } } const execPromise = exec.getExecOutput("neurolink", args, { cwd: workingDirectory, env: { ...processEnv, ...env }, silent: false, ignoreReturnCode: true, }); // Wrap execution with timeout protection const { exitCode, stdout, stderr } = await withTimeout(execPromise, timeout, new Error(`CLI execution timed out after ${timeout}ms. Consider increasing the timeout parameter.`)); const executionTime = Date.now() - startTime; if (exitCode !== 0) { return { success: false, response: "", error: stderr || `CLI exited with code ${exitCode}`, executionTime, }; } // Parse JSON output try { // The CLI output may have log lines before the JSON // The CLI JSON response always has "content" as the first key // Find the line that contains '{"content"' or '{ "content"' to locate JSON start const lines = stdout.split("\n"); let jsonStartIndex = -1; // Find the line that starts the JSON response object // Look for pattern: line is "{" and next non-empty line has "content" for (let i = 0; i < lines.length; i++) { const trimmedLine = lines[i].trim(); if (trimmedLine === "{") { // Check if next non-empty line contains "content" for (let j = i + 1; j < lines.length && j < i + 3; j++) { const nextLine = lines[j].trim(); if (nextLine.includes('"content"')) { jsonStartIndex = i; break; } } if (jsonStartIndex !== -1) { break; } } } core.debug(`JSON start line index: ${jsonStartIndex}`); if (jsonStartIndex === -1) { core.debug("No JSON found, returning raw stdout"); return { success: true, response: stdout.trim(), executionTime, }; } // Extract JSON from that line onwards const jsonStr = lines.slice(jsonStartIndex).join("\n").trim(); core.debug(`JSON string starts with: ${jsonStr.substring(0, 50)}`); const cliResponse = JSON.parse(jsonStr); core.debug(`Parsed CLI response keys: ${Object.keys(cliResponse).join(", ")}`); core.debug(`cliResponse.content: ${cliResponse.content?.substring(0, 50)}`); core.debug(`cliResponse.provider: ${cliResponse.provider}`); const transformed = transformCliResponse(cliResponse); core.debug(`Transformed response: ${transformed.response?.substring(0, 50)}`); core.debug(`Transformed provider: ${transformed.provider}`); return { success: true, ...transformed, executionTime: transformed.executionTime || executionTime, }; } catch (parseError) { // If not JSON, return raw output core.debug(`JSON parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`); return { success: true, response: stdout.trim(), executionTime, }; } } catch (error) { return { success: false, response: "", error: error instanceof Error ? error.message : String(error), executionTime: Date.now() - startTime, }; } } /** * Run complete NeuroLink action */ export async function runNeurolink(inputs) { let credPath; try { // Install CLI await installNeurolink(inputs.neurolinkVersion); // Build environment const env = buildEnvironmentVariables(inputs); // Handle Google Cloud credentials (base64 decode) if (inputs.googleCloudConfig.googleApplicationCredentials) { const decoded = Buffer.from(inputs.googleCloudConfig.googleApplicationCredentials, "base64").toString("utf-8"); credPath = path.join(process.env.RUNNER_TEMP || "/tmp", `vertex-credentials-${process.pid}-${Date.now()}.json`); fs.writeFileSync(credPath, decoded, { mode: 0o600 }); env.GOOGLE_APPLICATION_CREDENTIALS = credPath; } // Build CLI arguments const args = buildCliArgs(inputs); core.info(`Executing: neurolink ${args.join(" ")}`); if (inputs.debug) { core.debug(`Working directory: ${inputs.workingDirectory}`); core.debug(`Environment keys: ${Object.keys(env) .filter((k) => k.includes("API") || k.includes("KEY")) .join(", ")}`); } // Execute with timeout (use input timeout or default) const executionTimeout = inputs.timeout || DEFAULT_EXECUTION_TIMEOUT_MS; return await executeNeurolink(args, env, inputs.workingDirectory, executionTimeout); } finally { // Clean up credential file if (credPath && fs.existsSync(credPath)) { try { fs.unlinkSync(credPath); core.debug("Cleaned up temporary credentials file"); } catch { // Ignore cleanup errors } } } } //# sourceMappingURL=actionExecutor.js.map