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:
+47
-3
@@ -69,7 +69,11 @@ $commandFile = Join-Path $stateDir 'command.json'
|
||||
$aliveFile = Join-Path $stateDir 'alive.json'
|
||||
# 文件桥:插件每秒写 snapshot.json(桌面壳 HTTP 网关 403,浮窗只能走文件)
|
||||
$snapshotFile = Join-Path $DshHome 'float-window\snapshot.json'
|
||||
$bridgeSnapshotFile = Join-Path $DshHome 'float-window\snapshot-bridge.json'
|
||||
$outboxFile = Join-Path $DshHome 'float-window\outbox.json'
|
||||
$watchFile = Join-Path $DshHome 'float-window\watch.json'
|
||||
# 独立镜像桥:直接读会话日志/投影缓存(DSH 还没重启、插件没在写快照时的兜底数据源)
|
||||
$bridgeScript = Join-Path (Split-Path $PSScriptRoot -Parent) 'tools\mirror-bridge.mjs'
|
||||
if ($alreadyRunning) {
|
||||
if ($Action) {
|
||||
try {
|
||||
@@ -387,7 +391,13 @@ $onWebMessage = {
|
||||
'scale' { $script:state.scale = [double]$m.value; $wv.ZoomFactor = [double]$m.value; Save-State; Push-State }
|
||||
'reload' { $wv.Reload() }
|
||||
'chat' { Write-Outbox @{ cmd = 'chat'; text = [string]$m.text } }
|
||||
'watch' { Write-Outbox @{ cmd = 'watch'; sessionId = [string]$m.sessionId } }
|
||||
'watch' {
|
||||
Write-Outbox @{ cmd = 'watch'; sessionId = [string]$m.sessionId }
|
||||
# 镜像桥也跟随同一个选择
|
||||
try {
|
||||
[System.IO.File]::WriteAllText($watchFile, (@{ sessionId = [string]$m.sessionId } | ConvertTo-Json -Compress), (New-Object System.Text.UTF8Encoding($false)))
|
||||
} catch { }
|
||||
}
|
||||
'newSession' { Write-Outbox @{ cmd = 'session'; action = 'create' } }
|
||||
'openExternal' { if ($m.url -match '^https?://') { Start-Process $m.url } }
|
||||
}
|
||||
@@ -427,7 +437,28 @@ $timer.Add_Tick({
|
||||
$s = [System.IO.File]::ReadAllText($snapshotFile, [System.Text.Encoding]::UTF8)
|
||||
if ($s -and $s -ne $script:lastSnapshotText) {
|
||||
$script:lastSnapshotText = $s
|
||||
Send-ToPage @{ type = 'snapshot'; data = ($s | ConvertFrom-Json) }
|
||||
$snap = $s | ConvertFrom-Json
|
||||
# 用镜像桥的兜底快照补齐插件缺的字段(token/上下文估算、标题)
|
||||
try {
|
||||
if (Test-Path $bridgeSnapshotFile) {
|
||||
$bs = [System.IO.File]::ReadAllText($bridgeSnapshotFile, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
if ($bs -and $bs.sessionId -eq $snap.sessionId) {
|
||||
if ($null -eq $snap.metrics.contextUsed) { $snap.metrics.contextUsed = $bs.metrics.contextUsed }
|
||||
if ($null -eq $snap.metrics.tokens) { $snap.metrics.tokens = $bs.metrics.tokens }
|
||||
if ($null -eq $snap.metrics.cacheHit) { $snap.metrics.cacheHit = $bs.metrics.cacheHit }
|
||||
if ($null -eq $snap.metrics.tps -and $bs.metrics.tps) { $snap.metrics.tps = $bs.metrics.tps }
|
||||
if (-not $snap.title -and $bs.title) { $snap.title = $bs.title }
|
||||
if ($bs.approx) { $snap.approx = $true }
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
Send-ToPage @{ type = 'snapshot'; data = $snap }
|
||||
# 每 ~5 秒记一行,用来确认数据真的到了页面
|
||||
$now = Get-Date
|
||||
if (-not $script:lastSnapLog -or ($now - $script:lastSnapLog).TotalSeconds -gt 5) {
|
||||
$script:lastSnapLog = $now
|
||||
Log "snapshot -> page: src=$($snap.source) rows=$(@($snap.messages).Count) turns=$($snap.metrics.turns) steps=$($snap.metrics.steps) tok=$($snap.metrics.tokens) ctx=$($snap.metrics.contextUsed) approx=$($snap.approx)"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { Log "snapshot error: $($_.Exception.Message)" }
|
||||
@@ -471,6 +502,16 @@ $onWvReady = {
|
||||
$wv.add_CoreWebView2InitializationCompleted($onWvReady)
|
||||
$form.Add_Shown({
|
||||
Log 'form shown'
|
||||
# 独立镜像桥:以隐藏窗口跑起来(DSH 没重启时也能出镜像与指标)
|
||||
try {
|
||||
if (Test-Path $bridgeScript) {
|
||||
$nodeExe = (Get-Command node -ErrorAction SilentlyContinue).Source
|
||||
if ($nodeExe) {
|
||||
$script:bridge = Start-Process -FilePath $nodeExe -ArgumentList @("`"$bridgeScript`"", '--home', "`"$DshHome`"", '--quiet') -WindowStyle Hidden -PassThru
|
||||
Log "mirror bridge pid=$($script:bridge.Id)"
|
||||
} else { Log 'node not found; mirror bridge skipped' }
|
||||
} else { Log "bridge script missing: $bridgeScript" }
|
||||
} catch { Log "bridge start failed: $($_.Exception.Message)" }
|
||||
$form.AllowResize = -not $script:state.sizeLock
|
||||
try {
|
||||
$udf = Join-Path $stateDir 'webview2'
|
||||
@@ -489,7 +530,10 @@ $form.Add_Shown({
|
||||
|
||||
$form.Add_ResizeEnd({ if (-not $script:state.fullscreen) { $script:state.w = $form.Width; $script:state.h = $form.Height; Save-State; Push-State } })
|
||||
$form.Add_Move({ if (-not $script:state.fullscreen -and $form.WindowState -eq 'Normal') { $script:state.x = $form.Left; $script:state.y = $form.Top } })
|
||||
$form.Add_FormClosing({ Save-State })
|
||||
$form.Add_FormClosing({
|
||||
Save-State
|
||||
try { if ($script:bridge -and -not $script:bridge.HasExited) { $script:bridge.Kill() } } catch { }
|
||||
})
|
||||
$form.Add_KeyDown({
|
||||
param($s, $e)
|
||||
if ($e.KeyCode -eq [System.Windows.Forms.Keys]::F11) { Toggle-Fullscreen }
|
||||
|
||||
+7
-5
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user