braiin
Version:
Behavioral Reasoning AI for Intelligent Navigation
114 lines (113 loc) • 4.32 kB
JavaScript
;
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;