热重载抄 ai-mcp doReload(SDK client 三级降级+ctx.serverUrl,弃写死4096)
This commit is contained in:
+46
-11
@@ -55,8 +55,8 @@ const DB_PATH = join(homedir(), ".local", "share", "opencode", "opencode.db")
|
||||
// SQLite 写执行器(Python,OC 外部子进程跑,绕 OC 自身 DB 写锁)
|
||||
const DB_EXEC = join(homedir(), ".config", "opencode", "model-manager-db-exec.py")
|
||||
|
||||
// reload 端点(OC 热重载,/models 立即生效)
|
||||
const RELOAD_URL = "http://127.0.0.1:4096/api/location/reload"
|
||||
// 热重载通道见 doReload()(抄 ai-mcp:SDK client.location.reload 三级降级 + ctx.serverUrl 兜底,
|
||||
// 不再写死 4096 —— 用户环境 4096 无监听导致 reload 全挂、/models 不刷新)
|
||||
|
||||
function fileLog(msg: string) {
|
||||
try {
|
||||
@@ -120,17 +120,52 @@ async function execDbWrite(op: string, payload: any): Promise<string> {
|
||||
}
|
||||
|
||||
// 热重载 OC(让 /models 立即反映 catalog 变更)
|
||||
async function hotReload(): Promise<void> {
|
||||
try {
|
||||
await fetch(RELOAD_URL, { method: "POST" })
|
||||
fileLog("hot reload 已触发")
|
||||
} catch (e: any) {
|
||||
fileLog("hot reload 失败(需手动重启 OC): " + (e && e.message ? e.message : String(e)))
|
||||
// 通道整段抄自 ai-mcp 的 doReload(MCP 对调已验证此链路可用):
|
||||
// 进程内 SDK client.location.reload 三种调用形态 → HTTP POST ctx.serverUrl /api/location/reload
|
||||
// 关键差异:用 ctx.serverUrl(OC 自己告知插件的服务地址),绝不写死端口
|
||||
// V2 上下文可能有 client 属性:优先 ctx.client(若宿主提供),否则回退 ctx 本身(抄 ai-mcp pickClient)
|
||||
function pickClient(ctx: any) {
|
||||
if (!ctx) return ctx
|
||||
if (ctx.client && (ctx.client.location || ctx.client.config)) return ctx.client
|
||||
return ctx
|
||||
}
|
||||
async function doReload(ctx: any): Promise<boolean> {
|
||||
const attempts: Array<() => Promise<any>> = []
|
||||
const client = pickClient(ctx)
|
||||
if (client && client.location && typeof client.location.reload === "function") {
|
||||
attempts.push(() => client.location.reload({ location: undefined as any }))
|
||||
attempts.push(() => client.location.reload({}))
|
||||
attempts.push(() => client.location.reload())
|
||||
}
|
||||
const serverUrl = ctx && (ctx as any).serverUrl
|
||||
if (serverUrl) {
|
||||
attempts.push(async () => {
|
||||
const res = await fetch(new URL("/api/location/reload", serverUrl), {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: "{}",
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
return res
|
||||
})
|
||||
}
|
||||
let lastErr: any = null
|
||||
for (const fn of attempts) {
|
||||
try {
|
||||
await fn()
|
||||
fileLog("hot reload 已触发(/models 应立即生效)")
|
||||
return true
|
||||
} catch (e: any) {
|
||||
lastErr = e
|
||||
}
|
||||
}
|
||||
const msg = lastErr ? lastErr.message : "没有可用的 reload 通道(ctx 无 location/serverUrl)"
|
||||
fileLog("hot reload 失败(需重启 OC 生效): " + msg)
|
||||
return false
|
||||
}
|
||||
|
||||
// 从 outbox 消费一条操作并执行
|
||||
async function processOutbox(): Promise<void> {
|
||||
async function processOutbox(ctx: any): Promise<void> {
|
||||
if (!existsSync(OUTBOX)) return
|
||||
let lines: string[]
|
||||
try {
|
||||
@@ -165,7 +200,7 @@ async function processOutbox(): Promise<void> {
|
||||
|
||||
const res = await execDbWrite(op, payload)
|
||||
fileLog(`DB 写结果 op=${op}: ${res}`)
|
||||
await hotReload()
|
||||
await doReload(ctx)
|
||||
}
|
||||
|
||||
// SQLite 写执行器(Python,子进程独立连接)
|
||||
@@ -289,7 +324,7 @@ export default {
|
||||
const daemon = setInterval(async () => {
|
||||
if (stopped) return
|
||||
try {
|
||||
await processOutbox()
|
||||
await processOutbox(ctx)
|
||||
} catch (e: any) {
|
||||
fileLog("daemon tick 异常(已吞掉): " + (e && e.message ? e.message : String(e)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user