From f234213297c4e0e4d272238579c186c79a420608 Mon Sep 17 00:00:00 2001 From: jiangchen <1703935164@qq.com> Date: Sun, 27 Sep 2026 17:25:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(host):=20=E4=BF=AE=E6=8E=89=E3=80=8E?= =?UTF-8?q?=E7=82=B9=E6=89=93=E5=BC=80=E6=B2=A1=E5=8F=8D=E5=BA=94=E3=80=8F?= =?UTF-8?q?=E2=80=94=E2=80=94=E6=8A=95=E9=80=92=E5=91=BD=E4=BB=A4=E5=90=8E?= =?UTF-8?q?=E5=BF=85=E9=A1=BB=E7=AD=89=E5=BF=83=E8=B7=B3=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=EF=BC=8C=E5=90=A6=E5=88=99=E9=87=8D=E6=96=B0=E6=8B=89=E8=B5=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 真机现象:DSH 重启会把浮窗进程一起带走(它是 DSH 进程树的后代),但 alive.json 心跳还新鲜, 插件以为它活着 → 只往 command.json 投了个 open,发给了已经不存在的进程,窗口当然不出来。 现在 controlFloat 投递后轮询心跳(最多 1.6s):没更新就判定它已死并重新 spawn(隐藏窗口)。 另:上下文占比兜底按 1M 生效,真机实测 ctx=0.176。 --- lib/host.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/host.js b/lib/host.js index 015e9f0..a5d3526 100644 --- a/lib/host.js +++ b/lib/host.js @@ -608,8 +608,12 @@ export function apply(ctx, config) { } /** 壳每 700ms 写一次心跳,据此判断浮窗是否还活着 */ + function aliveMtime() { + try { return fs.statSync(aliveFile).mtimeMs } catch { return 0 } + } function shellAlive() { - try { return Date.now() - fs.statSync(aliveFile).mtimeMs < 8000 } catch { return false } + const t = aliveMtime() + return !!t && Date.now() - t < 8000 } async function spawnShell() { @@ -626,14 +630,30 @@ export function apply(ctx, config) { } catch (e) { return { ok: false, error: String(e?.message || e) } } } - /** 浮窗已经在跑就投递命令,否则按需拉起 */ + /** 浮窗已经在跑就投递命令,否则按需拉起;投递后要等心跳确认,不能"发给死人" */ async function controlFloat(action) { - if (action === 'open' && !shellAlive()) return spawnShell() if (action === 'close' && !shellAlive()) return { ok: true, via: 'already closed' } + if (shellAlive()) { + const before = aliveMtime() + const posted = await writeCommand(action) + if (posted.ok) { + // 等心跳更新(浮窗每 700ms 写一次 alive.json);没更新说明它其实已经死了 → 重新拉起 + for (let i = 0; i < 8; i++) { + await new Promise((r) => setTimeout(r, 200)) + if (aliveMtime() > before) return { ok: true, via: 'command file' } + } + log(`float did not acknowledge "${action}" within 1.6s; respawning`) + } + } + if (action === 'open' || action === 'fullscreen' || action === 'show') return spawnShell() + // close/hide 之类:没进程就算完成 + return { ok: true, via: 'no shell running' } + } + async function writeCommand(action) { try { fs.mkdirSync(stateDir, { recursive: true }) fs.writeFileSync(cmdFile, JSON.stringify({ cmd: action, at: new Date().toISOString() })) - return { ok: true, via: 'command file' } + return { ok: true } } catch (e) { return { ok: false, error: String(e?.message || e) } } }