feat: 独立镜像桥(免 DSH 重启出镜像/指标)+ 去掉 IP 显示 + 上下文占比兜底

- tools/mirror-bridge.mjs:直接读 %DSH_HOME%/sessions/**/session.jsonl.zstd(多帧 zstd 按魔数切帧解)
  与 storages/session_projcache.json,算出 消息行/轮/步/tok·s⁻¹/token/上下文占比 并写
  snapshot-bridge.json(插件缺字段时由浮窗补齐);插件在写 snapshot.json 时它不抢
- 浮窗标题栏不再显示 endpoint IP,只在未连接时显示状态词;数据来自桥时指标带 ≈
- host: contextUsed 兜底按 1M 上下文算(真机 sessionProjection=n,拿不到上下文窗口)
- 实测:snapshot -> page rows=40 turns=1 steps=67 tok=214639;outbox watch 切会话生效
This commit is contained in:
2026-09-27 17:20:14 +08:00
parent cb57f75577
commit 6ee713b145
4 changed files with 278 additions and 9 deletions
+7 -5
View File
@@ -539,8 +539,7 @@
<div id="app">
<div id="bar">
<div class="dot wait" id="dot"></div>
<div id="title"><span id="titleText">DSH 悬浮窗</span><small id="sub">未连接</small></div>
<button class="tbtn" id="bSess" title="切换要镜像的会话">
<div id="title"><span id="titleText">DSH 悬浮窗</span><small id="sub">未连接</small></div> <button class="tbtn" id="bSess" title="切换要镜像的会话">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<path d="M4 8h13l-3-3M20 16H7l3 3" />
</svg>
@@ -706,7 +705,8 @@
const live = !!(ep && ep.baseUrl)
ENDPOINT = live ? ep.baseUrl : null
$('dot').className = 'dot ' + (live ? 'live' : 'wait')
$('sub').textContent = live ? ep.baseUrl.replace(/^https?:\/\//, '') : '未连接'
// 不再显示 IP:只在没连上时给一个状态词
$('sub').textContent = live ? '' : '未连接'
$('input').disabled = !live
$('send').disabled = !live
document.body.classList.toggle('linked', live)
@@ -760,7 +760,8 @@
if (!msg) return
if (msg.type === 'cmd') return handleCmd(msg.cmd)
if (msg.type === 'snapshot') { // 文件桥来的快照
markLive('本地桥')
markLive(msg.data && msg.data.approx ? '≈ 估算' : '本地桥')
APPROX = !!(msg.data && msg.data.approx)
return render(msg.data)
}
if (msg.settings && !settingsApplied) { settingsApplied = true; applyDshSettings(msg.settings) }
@@ -776,6 +777,7 @@
/* ---------------- 会话切换 ---------------- */
let SESSIONS = []
let PINNED = null
let APPROX = false
function renderSessions() {
const box = $('sessList')
box.innerHTML = ''
@@ -804,7 +806,7 @@
function metrics(m) {
$('m-turn').textContent = m.turns ?? '—'
$('m-step').textContent = m.steps ?? '—'
$('m-tps').textContent = m.tps == null ? '—' : Number(m.tps).toFixed(1)
$('m-tps').textContent = m.tps == null ? '—' : (APPROX ? '≈' : '') + Number(m.tps).toFixed(1)
$('m-mtok').textContent = m.tokens == null ? '—' : (m.tokens / 1e6).toFixed(3)
$('m-cache').textContent = m.cacheHit == null ? '—' : Math.round(m.cacheHit * 100) + '%'
if (m.contextUsed == null) {