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.
- package/CHANGELOG.ja.md +19 -0
- package/CHANGELOG.ko.md +18 -0
- package/CHANGELOG.md +40 -0
- package/dsh.plugin.json +2 -2
- package/lib/client.js +167 -33
- package/lib/index.d.ts +123 -8
- package/lib/index.js +1147 -815
- package/package.json +125 -122
- package/src/client/card.tsx +150 -153
- package/src/client/health-panel.tsx +51 -0
- package/src/client/host-api.ts +14 -1
- package/src/client/index.ts +337 -234
- package/src/client/locales.ts +15 -0
- package/src/host/health/gates.ts +4 -1
- package/src/host/health/source-kind.ts +350 -0
- package/src/host/history/archive.ts +4 -1
- package/src/host/history/purge.ts +51 -4
- package/src/index.ts +56 -48
|
@@ -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' }, [
|
package/src/client/host-api.ts
CHANGED
|
@@ -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
|