feat(host): 版本自适应宿主调用(0.1.7 sessionController / 0.1.0 apiProxy),在 DSH Desktop 内置 0.1.7-rc.2 上实测镜像与发消息全通

This commit is contained in:
2026-09-27 14:46:07 +08:00
parent a6d2c828a8
commit 5ddca41b8a
2 changed files with 54 additions and 31 deletions
+42 -29
View File
@@ -148,7 +148,30 @@ export function apply(ctx) {
writeEndpoint()
ctx.effect(() => () => { try { fs.rmSync(endpointFile, { force: true }) } catch { } })
// ---------------------------------------------------------------- 会话镜像缓冲
// ---------------------------------------------------------------- 版本自适应的宿主调用
// dsh 0.1.x 早期:ctx.apiProxy.sessions.*(信封 { rpcId, payload })
// dsh 0.1.7+ :ctx.sessionController.*(Typert Remote 的宿主面,扁平信封)
// 两个都试,谁在就用谁;都没有就返回可读错误,绝不让插件抛出去。
async function hostCall(method, payload) {
const sc = ctx.get('sessionController')
if (sc && typeof sc[method] === 'function') {
const flat = { ...payload }
delete flat.rpcId
if (!flat.requestId) flat.requestId = randomUUID()
// 0.1.7 的 Remote 签名是 (request, signal);signal 缺失时宿主内部会
// "Cannot read properties of undefined (reading 'throwIfAborted')"
const r = await sc[method](flat, new AbortController().signal)
return { value: r?.value ?? r, via: 'sessionController.' + method }
}
const api = ctx.get('apiProxy')
if (api?.sessions && typeof api.sessions[method] === 'function') {
const r = await api.sessions[method]({ rpcId: randomUUID(), payload })
return { value: r?.result?.value ?? r?.result ?? r, via: 'apiProxy.sessions.' + method }
}
throw new Error(`no host API for "${method}" (tried sessionController / apiProxy)`)
}
const MAX_ROWS = 120
const rows = new Map() // sessionId -> row[]
const counts = new Map() // sessionId -> 事件计数兜底(没有 sessionProjection 也能出数字)
@@ -338,22 +361,16 @@ export function apply(ctx) {
}
const target = sessionId || lastSessionId
try {
const api = ctx.get('apiProxy')
if (!api?.sessions?.prompt) throw new Error('apiProxy unavailable')
if (!target) throw new Error('no session yet')
const r = await api.sessions.prompt({
rpcId: randomUUID(),
payload: {
sessionId: target,
mode: 'queue',
content: [{ type: 'text', text }],
clientTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
},
const { value, via } = await hostCall('prompt', {
sessionId: target,
mode: 'queue',
content: [{ type: 'text', text }],
clientTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
})
const result = r?.result ?? r
log(`chat -> session ${target}: ${JSON.stringify(result).slice(0, 160)}`)
log(`chat -> session ${target} via ${via}: ${short(value, 160)}`)
res.writeHead(200, { ...cors, 'Content-Type': 'application/json; charset=utf-8' })
res.end(JSON.stringify({ ok: true, sessionId: target, result }))
res.end(JSON.stringify({ ok: true, sessionId: target, result: value, via }))
} catch (e) {
const detail = e?.data || e?.message || e
log('chat failed: ' + short(detail, 200))
@@ -372,24 +389,20 @@ export function apply(ctx) {
} catch { }
const json = (obj) => { res.writeHead(200, { ...cors, 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify(obj)) }
try {
const api = ctx.get('apiProxy')
if (!api?.sessions) throw new Error('apiProxy unavailable')
if (body.action === 'create') {
const r = await api.sessions.create({
rpcId: randomUUID(),
payload: {
sessionId: body.sessionId ? String(body.sessionId) : randomUUID(),
...(body.cwd ? { cwd: String(body.cwd) } : {}),
...(body.workspaceId ? { workspaceId: body.workspaceId } : {}),
},
const { value, via } = await hostCall('create', {
sessionId: body.sessionId ? String(body.sessionId) : randomUUID(),
...(body.cwd ? { cwd: String(body.cwd) } : {}),
...(body.workspaceId ? { workspaceId: body.workspaceId } : {}),
...(body.agentPreset ? { agentPreset: String(body.agentPreset) } : {}),
})
const result = r?.result ?? r
if (result?.sessionId) lastSessionId = result.sessionId
log('session created: ' + short(result, 160))
json({ ok: true, ...result })
const sessionId = value?.sessionId || null
if (sessionId) lastSessionId = sessionId
log(`session created via ${via}: ` + short(value, 160))
json({ ok: true, sessionId, via, result: value })
} else if (body.action === 'cancel') {
const r = await api.sessions.cancel({ rpcId: randomUUID(), payload: { sessionId: body.sessionId || lastSessionId } })
json({ ok: true, result: r?.result ?? r })
const { value, via } = await hostCall('cancel', { sessionId: body.sessionId || lastSessionId })
json({ ok: true, via, result: value })
} else {
json({ ok: true, sessions: snapshot().sessions, current: lastSessionId })
}