init
This commit is contained in:
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
import { createOpencode, createOpencodeClient, } from "@opencode-ai/sdk/v2";
|
||||
import { config } from "../config.js";
|
||||
import { logger } from "../logger.js";
|
||||
export class OpencodeRuntimeAdapter {
|
||||
clientPromise = null;
|
||||
closeServer = null;
|
||||
async ensureClient() {
|
||||
if (!this.clientPromise) {
|
||||
this.clientPromise = this.bootstrapClient();
|
||||
}
|
||||
return this.clientPromise;
|
||||
}
|
||||
async health() {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.global.health();
|
||||
return requireData(response.data, "global.health");
|
||||
}
|
||||
async createSession(title) {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.session.create({
|
||||
title,
|
||||
});
|
||||
return requireData(response.data, "session.create");
|
||||
}
|
||||
async getSession(id) {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.session.get({
|
||||
sessionID: id,
|
||||
});
|
||||
return requireData(response.data, "session.get");
|
||||
}
|
||||
async sendPrompt(sessionId, text) {
|
||||
const client = await this.ensureClient();
|
||||
await client.session.prompt({
|
||||
sessionID: sessionId,
|
||||
parts: [{ type: "text", text }],
|
||||
});
|
||||
// 当前 SDK 响应风格下,prompt() 本身不会直接返回完整 assistant parts,
|
||||
// 所以这里紧跟一次 messages() 回读,给上层路由统一消费。
|
||||
const messages = await client.session.messages({
|
||||
sessionID: sessionId,
|
||||
limit: 20,
|
||||
});
|
||||
return requireData(messages.data, "session.messages");
|
||||
}
|
||||
async abortSession(sessionId) {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.session.abort({
|
||||
sessionID: sessionId,
|
||||
});
|
||||
return requireData(response.data, "session.abort");
|
||||
}
|
||||
async subscribeEvents() {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.event.subscribe();
|
||||
return response.stream;
|
||||
}
|
||||
async dispose() {
|
||||
this.closeServer?.();
|
||||
this.closeServer = null;
|
||||
this.clientPromise = null;
|
||||
}
|
||||
async bootstrapClient() {
|
||||
if (config.OPENCODE_BASE_URL) {
|
||||
logger.info({ baseUrl: config.OPENCODE_BASE_URL }, "connecting to external opencode server");
|
||||
return createOpencodeClient({
|
||||
baseUrl: config.OPENCODE_BASE_URL,
|
||||
});
|
||||
}
|
||||
// embedded 模式下,把服务内工具桥地址注入到 opencode 进程环境里,
|
||||
// 这样 .opencode/tools 下的自定义工具可以回调本服务。
|
||||
process.env.TJWATER_AGENT_INTERNAL_BASE_URL = `http://127.0.0.1:${config.PORT}`;
|
||||
process.env.TJWATER_AGENT_INTERNAL_TOKEN =
|
||||
config.AGENT_INTERNAL_TOKEN ?? process.env.TJWATER_AGENT_INTERNAL_TOKEN ?? "";
|
||||
logger.info({
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
port: config.OPENCODE_PORT,
|
||||
model: config.OPENCODE_MODEL,
|
||||
}, "starting embedded opencode server");
|
||||
const runtime = await createOpencode({
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
port: config.OPENCODE_PORT,
|
||||
timeout: config.OPENCODE_TIMEOUT_MS,
|
||||
config: {
|
||||
model: config.OPENCODE_MODEL,
|
||||
},
|
||||
});
|
||||
this.closeServer = () => {
|
||||
runtime.server.close();
|
||||
};
|
||||
return runtime.client;
|
||||
}
|
||||
}
|
||||
export const opencodeRuntime = new OpencodeRuntimeAdapter();
|
||||
function requireData(data, operation) {
|
||||
if (data === undefined) {
|
||||
throw new Error(`${operation} returned no data`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user