UNPKG

braiin

Version:

Behavioral Reasoning AI for Intelligent Navigation

114 lines (113 loc) 4.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createClaudeCodeBackend = exports.parseClaudeCodeJsonOutput = exports.buildClaudeCodeArgs = void 0; const node_child_process_1 = require("node:child_process"); const llm_1 = require("../model/llm"); const stream_filter_1 = require("./stream.filter"); const DEFAULT_CLI_PATH = 'claude'; const MODEL_TAG = 'claude-code'; const buildClaudeCodeArgs = (config, systemPrompt, prompt) => [ '-p', '--session-id', config.sessionId, '--system-prompt', systemPrompt, '--tools', '', '--output-format', 'json', prompt ]; exports.buildClaudeCodeArgs = buildClaudeCodeArgs; const errorResponse = (message, type) => ({ id: '', object: '', created: 0, model: MODEL_TAG, choices: [], error: { message, type, param: '', code: '' } }); const parseClaudeCodeJsonOutput = (stdout) => { const trimmed = stdout.trim(); if (!trimmed) { return errorResponse('Claude CLI returned empty output', 'cli_error'); } let parsed; try { parsed = JSON.parse(trimmed); } catch (e) { return errorResponse(`Failed to parse claude CLI output: ${e.message}`, 'parse_error'); } if (parsed.is_error) { return errorResponse(parsed.result ?? 'Claude CLI returned an error', 'cli_error'); } return { id: parsed.session_id ?? '', object: 'chat.completion', created: Math.floor(Date.now() / 1000), model: MODEL_TAG, choices: [{ index: 0, message: { role: llm_1.LLMMessageRole.ASSISTANT, content: parsed.result ?? '' }, finish_reason: parsed.stop_reason ?? 'stop' }] }; }; exports.parseClaudeCodeJsonOutput = parseClaudeCodeJsonOutput; const buildRequestSignal = (config) => { const signals = []; if (config.signal) signals.push(config.signal); if (config.timeoutMs !== undefined) signals.push(AbortSignal.timeout(config.timeoutMs)); if (signals.length === 0) return undefined; if (signals.length === 1) return signals[0]; return AbortSignal.any(signals); }; const runClaudeCli = (cliPath, args, signal) => { return new Promise((resolve, reject) => { const child = (0, node_child_process_1.spawn)(cliPath, args, { signal }); let stdout = ''; let stderr = ''; child.stdout.on('data', (d) => { stdout += d.toString(); }); child.stderr.on('data', (d) => { stderr += d.toString(); }); child.on('error', reject); child.on('close', code => resolve({ stdout, stderr, exitCode: code ?? -1 })); }); }; const createClaudeCodeBackend = (config) => { const cliPath = config.cliPath ?? DEFAULT_CLI_PATH; return { ask: async (systemPrompt, prompt, _history, callback) => { const args = (0, exports.buildClaudeCodeArgs)(config, systemPrompt, prompt); const signal = buildRequestSignal(config); try { const { stdout, stderr, exitCode } = await runClaudeCli(cliPath, args, signal); if (exitCode !== 0) { const reason = stderr.trim() || `exit code ${exitCode}`; return errorResponse(`claude CLI failed: ${reason}`, 'cli_error'); } const response = (0, exports.parseClaudeCodeJsonOutput)(stdout); if (callback) { const content = response.choices[0]?.message.content; if (content) callback(content); callback(stream_filter_1.STREAM_END_MARKER); } return response; } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError' || error.name === 'TimeoutError') { return errorResponse(`Request aborted: ${error.message}`, 'timeout_error'); } return errorResponse(error.message, 'cli_error'); } return errorResponse('Unknown error', 'cli_error'); } } }; }; exports.createClaudeCodeBackend = createClaudeCodeBackend;