dsh-manage-session 0.2.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/README.en.md +14 -0
- package/README.ja.md +14 -0
- package/README.ko.md +14 -0
- package/README.md +14 -0
- package/dsh.plugin.json +4 -4
- package/lib/client.js +179 -36
- package/lib/index.d.ts +288 -16
- package/lib/index.js +1003 -384
- package/package.json +125 -121
- package/screenshots.json +5 -1
- package/src/client/card.tsx +150 -153
- package/src/client/health-panel.tsx +56 -1
- package/src/client/host-api.ts +19 -1
- package/src/client/index.ts +337 -234
- package/src/client/locales.ts +22 -2
- package/src/host/health/cache.ts +1 -1
- package/src/host/health/decode.ts +19 -1
- package/src/host/health/gates.ts +94 -3
- package/src/host/health/generation.ts +216 -0
- package/src/host/health/repair.ts +28 -7
- package/src/host/health/scan.ts +84 -27
- 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 +83 -55
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 体检面板:体检(五门)→ 处方(命令清单)→ 出院(可逆处置 + before/after 对照)。
|
|
3
3
|
*
|
|
4
4
|
* 三态流转完全由 host 侧报告驱动,客户端只做展示与触发;重型动作都在 host,
|
|
5
5
|
* 渲染周期内不发同步重活。
|
|
@@ -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
|
|
|
@@ -19,10 +20,12 @@ import { translate, type LocaleKey } from './locales.ts'
|
|
|
19
20
|
export type PanelTranslate = (key: LocaleKey, params?: Record<string, unknown>) => string
|
|
20
21
|
|
|
21
22
|
const GATE_LABEL: Record<HealthGate['id'], LocaleKey> = {
|
|
23
|
+
'generation': 'health.gate.generation',
|
|
22
24
|
'log-integrity': 'health.gate.log-integrity',
|
|
23
25
|
'projection-cache': 'health.gate.projection-cache',
|
|
24
26
|
'lossless-json': 'health.gate.lossless-json',
|
|
25
27
|
'cold-read': 'health.gate.cold-read',
|
|
28
|
+
'source-kind': 'health.gate.source-kind',
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const LEVEL_LABEL = {
|
|
@@ -64,6 +67,9 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
64
67
|
const [error, setError] = useState<string | null>(null)
|
|
65
68
|
const [detail, setDetail] = useState<HealthSessionResponse | null>(null)
|
|
66
69
|
const [repairing, setRepairing] = useState(false)
|
|
70
|
+
const [migrating, setMigrating] = useState(false)
|
|
71
|
+
/** 署名转换结果提示(一次性;点击转换或返回列表时清除)。 */
|
|
72
|
+
const [migrateNote, setMigrateNote] = useState<string | null>(null)
|
|
67
73
|
const [discharge, setDischarge] = useState<HealthRepairResponse | null>(null)
|
|
68
74
|
const [copied, setCopied] = useState<string | null>(null)
|
|
69
75
|
/** 分批扫描进度:done = 已访问会话数,total = 语料总数(0 表示尚未拿到分母)。 */
|
|
@@ -118,6 +124,7 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
118
124
|
setError(null)
|
|
119
125
|
setDetail(null)
|
|
120
126
|
setDischarge(null)
|
|
127
|
+
setMigrateNote(null)
|
|
121
128
|
setFindings(null)
|
|
122
129
|
setCacheInfo(null)
|
|
123
130
|
setProgress({ done: 0, total: 0 })
|
|
@@ -189,6 +196,7 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
189
196
|
const openDetail = (sessionId: string): void => {
|
|
190
197
|
setError(null)
|
|
191
198
|
setDischarge(null)
|
|
199
|
+
setMigrateNote(null)
|
|
192
200
|
void callHostAny<HealthSessionResponse>('session-health-session', { sessionId }, 120_000)
|
|
193
201
|
.then((res) => {
|
|
194
202
|
if (res.ok === true) {
|
|
@@ -225,6 +233,31 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
225
233
|
} catch { /* 剪贴板不可用时仍然展示命令 */ }
|
|
226
234
|
}
|
|
227
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
|
+
|
|
228
261
|
const report = detail?.report ?? discharge?.after
|
|
229
262
|
const lines: (ReactElement | null)[] = []
|
|
230
263
|
|
|
@@ -315,6 +348,9 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
315
348
|
.join(' · ')
|
|
316
349
|
return createElement('li', { key: item.sessionId, className: 'dss_row' }, [
|
|
317
350
|
createElement('span', { key: 'lv', className: `dss_levelPill dss_level_${item.level}` }, translate(t, LEVEL_LABEL[item.level])),
|
|
351
|
+
// 优先级徽标:这些会话的当前代是从迁移暂存(TMP)发布出来的,即本插件
|
|
352
|
+
// 此前读错产物或完全发现不了的那批。列表按优先级排,再标一次让排序可解释。
|
|
353
|
+
item.priority === 'high' && createElement('span', { key: 'p', className: 'dss_meta' }, translate(t, 'health.priority.high')),
|
|
318
354
|
createElement('span', { key: 'id', className: 'dss_meta dss_uuid' }, item.sessionId),
|
|
319
355
|
reasons !== '' && createElement('span', { key: 'why', className: 'dss_why' }, reasons),
|
|
320
356
|
createElement('button', {
|
|
@@ -344,6 +380,25 @@ export function HealthPanel({ t, onClose }: { t?: PanelTranslate; onClose: () =>
|
|
|
344
380
|
createElement('span', { key: 'tx' }, repairing ? translate(t, 'health.repairing') : translate(t, 'health.repair')),
|
|
345
381
|
]),
|
|
346
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
|
+
}
|
|
347
402
|
const prescriptions = detail?.prescriptions ?? discharge?.prescriptions ?? []
|
|
348
403
|
if (prescriptions.length > 0) {
|
|
349
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: '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 }
|
|
@@ -110,6 +114,11 @@ export interface HealthReport {
|
|
|
110
114
|
sessionId: string
|
|
111
115
|
/** 总判:聚合时 `skipped` 不抬升(见 host 侧 buildSessionReport)。 */
|
|
112
116
|
level: 'ok' | 'warn' | 'fail'
|
|
117
|
+
/**
|
|
118
|
+
* 处置优先级(见 host 侧 generation.ts):`high` = 当前代是从迁移暂存(TMP)发布出来的。
|
|
119
|
+
* 与 `level` 正交:优先级答「先看谁」,档位答「要不要处置」。
|
|
120
|
+
*/
|
|
121
|
+
priority?: 'high' | 'normal'
|
|
113
122
|
gates: HealthGate[]
|
|
114
123
|
generatedAt: number
|
|
115
124
|
}
|
|
@@ -166,6 +175,15 @@ export interface HealthRepairResponse {
|
|
|
166
175
|
error?: string
|
|
167
176
|
}
|
|
168
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
|
+
|
|
169
187
|
/** 状态响应。 */
|
|
170
188
|
export interface HealthStatusResponse {
|
|
171
189
|
ok: boolean
|