dsh-manage-session 0.3.3 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ import {
12
12
  type HealthReport,
13
13
  type HealthScanResponse,
14
14
  type HealthSessionResponse,
15
+ type SourceMigrateResponse,
15
16
  } from './host-api.ts'
16
17
  import { translate, type LocaleKey } from './locales.ts'
17
18
 
@@ -24,6 +25,7 @@ const GATE_LABEL: Record<HealthGate['id'], LocaleKey> = {
24
25
  'projection-cache': 'health.gate.projection-cache',
25
26
  'lossless-json': 'health.gate.lossless-json',
26
27
  'cold-read': 'health.gate.cold-read',
28
+ 'source-kind': 'health.gate.source-kind',
27
29
  }
28
30
 
29
31
  const LEVEL_LABEL = {
@@ -65,6 +67,9 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
65
67
  const [error, setError] = useState<string | null>(null)
66
68
  const [detail, setDetail] = useState<HealthSessionResponse | null>(null)
67
69
  const [repairing, setRepairing] = useState(false)
70
+ const [migrating, setMigrating] = useState(false)
71
+ /** 署名转换结果提示(一次性;点击转换或返回列表时清除)。 */
72
+ const [migrateNote, setMigrateNote] = useState<string | null>(null)
68
73
  const [discharge, setDischarge] = useState<HealthRepairResponse | null>(null)
69
74
  const [copied, setCopied] = useState<string | null>(null)
70
75
  /** 分批扫描进度:done = 已访问会话数,total = 语料总数(0 表示尚未拿到分母)。 */
@@ -119,6 +124,7 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
119
124
  setError(null)
120
125
  setDetail(null)
121
126
  setDischarge(null)
127
+ setMigrateNote(null)
122
128
  setFindings(null)
123
129
  setCacheInfo(null)
124
130
  setProgress({ done: 0, total: 0 })
@@ -190,6 +196,7 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
190
196
  const openDetail = (sessionId: string): void => {
191
197
  setError(null)
192
198
  setDischarge(null)
199
+ setMigrateNote(null)
193
200
  void callHostAny<HealthSessionResponse>('session-health-session', { sessionId }, 120_000)
194
201
  .then((res) => {
195
202
  if (res.ok === true) {
@@ -226,6 +233,31 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
226
233
  } catch { /* 剪贴板不可用时仍然展示命令 */ }
227
234
  }
228
235
 
236
+ // 署名转换:仅当 source-kind 门报 warn(v4 日志里观测到旧署名行)时出现。
237
+ // 成败都回写最新报告;结果提示一次性展示,避免残留误导。
238
+ const runSourceMigrate = (sessionId: string): void => {
239
+ setMigrating(true)
240
+ setError(null)
241
+ setMigrateNote(null)
242
+ void callHostAny<SourceMigrateResponse>('session-health-source-migrate', { sessionId }, 120_000)
243
+ .then((res) => {
244
+ if (res.after !== undefined) {
245
+ setDetail({ ok: true, report: res.after, prescriptions: detail?.prescriptions ?? [] })
246
+ reconcileRow(res.after)
247
+ }
248
+ const changed = res.outcome?.changedRows ?? 0
249
+ if (res.ok === true && changed > 0) {
250
+ setMigrateNote(translate(t, 'health.sourceMigrated', { n: changed, backup: res.outcome?.backup ?? '' }))
251
+ } else if (res.ok === true) {
252
+ setMigrateNote(translate(t, 'health.sourceMigrateNone'))
253
+ } else {
254
+ setMigrateNote(translate(t, 'health.sourceMigrateFail', { error: res.outcome?.error ?? res.error ?? '未知原因' }))
255
+ }
256
+ })
257
+ .catch((err: unknown) => setError(String(err instanceof Error ? err.message : err)))
258
+ .finally(() => setMigrating(false))
259
+ }
260
+
229
261
  const report = detail?.report ?? discharge?.after
230
262
  const lines: (ReactElement | null)[] = []
231
263
 
@@ -348,6 +380,25 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
348
380
  createElement('span', { key: 'tx' }, repairing ? translate(t, 'health.repairing') : translate(t, 'health.repair')),
349
381
  ]),
350
382
  ]))
383
+ // 署名转换按钮:只在 source-kind 门报 warn(v4 日志里观测到旧署名行)时出现,
384
+ // 不随常规处置出现——v3 及更早版本线的旧行由宿主迁移负责,不该一键误触。
385
+ if (report.gates.some(gate => gate.id === 'source-kind' && gate.level === 'warn')) {
386
+ lines.push(createElement('div', { key: 'migrate', className: 'dss_phaseRow' }, [
387
+ createElement('button', {
388
+ key: 'm',
389
+ type: 'button',
390
+ className: 'dss_actBtn',
391
+ disabled: migrating,
392
+ onClick: () => { runSourceMigrate(report.sessionId) },
393
+ }, [
394
+ migrating ? createElement('span', { key: 'sp', className: 'dss_spinner', 'aria-hidden': 'true' }) : null,
395
+ createElement('span', { key: 'tx' }, migrating
396
+ ? translate(t, 'health.sourceMigrating')
397
+ : translate(t, 'health.sourceMigrate')),
398
+ ]),
399
+ migrateNote !== null && createElement('span', { key: 'note', className: 'dss_status' }, migrateNote),
400
+ ]))
401
+ }
351
402
  const prescriptions = detail?.prescriptions ?? discharge?.prescriptions ?? []
352
403
  if (prescriptions.length > 0) {
353
404
  lines.push(createElement('div', { key: 'cmds', className: 'dss_cmdBlock' }, [
@@ -73,6 +73,8 @@ export interface HistoryRow {
73
73
  bytes: number
74
74
  /** 投影缓存占用字节数。 */
75
75
  cacheBytes: number
76
+ /** 署名转换备份占用字节数(`bytes` 的子集,单列防误读)。 */
77
+ backupBytes: number
76
78
  }
77
79
 
78
80
  /** 历史文件列表响应。 */
@@ -91,6 +93,8 @@ export interface HistoryPurgeResponse {
91
93
  ok: boolean
92
94
  purged?: number
93
95
  freedBytes?: number
96
+ /** 一并删除的署名转换备份个数。 */
97
+ backupsRemoved?: number
94
98
  failures?: { sessionId: string; reason: string }[]
95
99
  requiresRestart?: boolean
96
100
  error?: string
@@ -98,7 +102,7 @@ export interface HistoryPurgeResponse {
98
102
 
99
103
  /** 一个 gate 的结果(与 host 侧 GateResult 对齐)。 */
100
104
  export interface HealthGate {
101
- id: 'generation' | 'log-integrity' | 'projection-cache' | 'lossless-json' | 'cold-read'
105
+ id: 'generation' | 'log-integrity' | 'projection-cache' | 'lossless-json' | 'cold-read' | 'source-kind'
102
106
  level: 'ok' | 'warn' | 'fail' | 'skipped'
103
107
  evidence: string
104
108
  attribution?: { projection?: string; package?: string; field?: string }
@@ -171,6 +175,15 @@ export interface HealthRepairResponse {
171
175
  error?: string
172
176
  }
173
177
 
178
+ /** 署名转换(session-health-source-migrate)响应。 */
179
+ export interface SourceMigrateResponse {
180
+ ok: boolean
181
+ outcome?: { ok: boolean; path: string; backup?: string; changedRows?: number; byPlugin?: Record<string, number>; error?: string }
182
+ /** 转换后的最新体检报告(无论成败都给出,便于面板对照)。 */
183
+ after?: HealthReport
184
+ error?: string
185
+ }
186
+
174
187
  /** 状态响应。 */
175
188
  export interface HealthStatusResponse {
176
189
  ok: boolean