import { spawn } from 'child_process';
import fetch from 'node-fetch';
const SERVER_URL = 'http://127.0.0.1:4096';
const HEALTH_CHECK_INTERVAL = 500;
const MAX_RETRIES = 20;
const MODEL_CONFIG = {
modelID: 'big-pickle',
providerID: 'opencode'
};
let serverProcess = null;
* 启动 opencode 服务器
* @returns {ChildProcess} 服务器进程
*/
export function startOpencodeServer() {
console.log('Starting opencode server...');
const server = spawn('opencode', ['serve', '--port', '4096', '--hostname', '127.0.0.1'], {
stdio: 'inherit',
shell: true,
});
server.on('error', (err) => {
if (err.message.includes('ENOENT')) {
console.error('❌ opencode command not found. Please install it first:');
console.error(' npm install -g @anomalyco/opencode');
} else {
console.error('Failed to start server:', err);
}
process.exit(1);
});
serverProcess = server;
return server;
}
* 等待服务器就绪
* @returns {Promise<boolean>} 服务器是否就绪
*/
export async function waitForServer() {
let retries = 0;
while (retries < MAX_RETRIES) {
try {
const res = await fetch(`${SERVER_URL}/global/health`);
if (res.ok) {
const data = await res.json();
console.log(`✅ Server ready (version: ${data.version})`);
return true;
}
} catch (e) {
}
await new Promise(r => setTimeout(r, HEALTH_CHECK_INTERVAL));
retries++;
}
throw new Error('Server did not start in time');
}
* 创建会话
* @returns {Promise<string>} 会话 ID
*/
export async function createSession() {
console.log('Creating session...');
const sessionRes = await fetch(`${SERVER_URL}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'JS Client Session' })
});
if (!sessionRes.ok) throw new Error('Failed to create session');
const session = await sessionRes.json();
const sessionId = session.id;
console.log(`Intialized session: ${sessionId}`);
return sessionId;
}
* 发送消息到 opencode 服务器
* @param {string} message 要发送的消息
* @param {string} sessionId 可选的会话 ID,如果未提供则创建新会话
* @returns {Promise<string>} AI 的回复
*/
export async function sendMessage(message, sessionId) {
if (!sessionId) {
sessionId = await createSession();
} else {
console.log(`Using existing session: ${sessionId}`);
}
console.log(`Sending message: "${message}"`);
const messageRes = await fetch(`${SERVER_URL}/session/${sessionId}/message`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agent: 'build',
model: MODEL_CONFIG,
parts: [{ type: 'text', text: message }]
})
});
if (!messageRes.ok) {
const errorText = await messageRes.text();
throw new Error(`Message send failed: ${messageRes.status} ${errorText}`);
}
const response = await messageRes.json();
let aiReply = '';
for (const part of response.parts) {
if (part.type === 'text') aiReply += part.text;
}
return aiReply;
}
* 停止服务器进程
*/
export function stopServer() {
if (serverProcess) {
console.log('Shutting down server...');
serverProcess.kill();
serverProcess = null;
}
}
* 初始化并确保服务器就绪
* @returns {Promise<void>}
*/
export async function initializeServer() {
try {
await waitForServer();
} catch (e) {
console.log('No running server detected. Starting one...');
startOpencodeServer();
await waitForServer();
}
}