dsh-config-manager 0.1.12 → 0.1.13
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/README.md +30 -0
- package/README.zh-CN.md +30 -0
- package/lib/adapters/test-helpers.d.ts +4 -2
- package/lib/adapters/test-helpers.js +11 -3
- package/lib/adapters/test-helpers.js.map +1 -1
- package/lib/cli/index.d.ts +44 -0
- package/lib/cli/index.js +230 -0
- package/lib/cli/index.js.map +1 -0
- package/lib/client.d.ts +260 -5
- package/lib/client.js +2519 -820
- package/lib/client.js.map +1 -1
- package/lib/core/analyzer.d.ts +18 -1
- package/lib/core/analyzer.js +29 -0
- package/lib/core/analyzer.js.map +1 -1
- package/lib/core/backup.d.ts +3 -1
- package/lib/core/backup.js +70 -0
- package/lib/core/backup.js.map +1 -1
- package/lib/core/exporter.d.ts +11 -0
- package/lib/core/exporter.js +11 -0
- package/lib/core/exporter.js.map +1 -1
- package/lib/core/importer.d.ts +3 -0
- package/lib/core/importer.js +1 -0
- package/lib/core/importer.js.map +1 -1
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.js +1 -0
- package/lib/core/index.js.map +1 -1
- package/lib/core/restore.d.ts +92 -0
- package/lib/core/restore.js +505 -0
- package/lib/core/restore.js.map +1 -0
- package/lib/core/run-registry.d.ts +65 -0
- package/lib/core/run-registry.js +120 -0
- package/lib/core/run-registry.js.map +1 -0
- package/lib/core/types.d.ts +20 -0
- package/lib/core/types.js.map +1 -1
- package/lib/index.d.ts +45 -0
- package/lib/index.js +442 -8
- package/lib/index.js.map +1 -1
- package/lib/sync/fs.d.ts +18 -0
- package/lib/sync/fs.js +47 -0
- package/lib/sync/fs.js.map +1 -0
- package/lib/sync/git/git-transport.d.ts +87 -0
- package/lib/sync/git/git-transport.js +333 -0
- package/lib/sync/git/git-transport.js.map +1 -0
- package/lib/sync/layout.d.ts +29 -0
- package/lib/sync/layout.js +166 -0
- package/lib/sync/layout.js.map +1 -0
- package/lib/sync/sync-config.d.ts +16 -0
- package/lib/sync/sync-config.js +67 -0
- package/lib/sync/sync-config.js.map +1 -0
- package/lib/sync/sync-engine.d.ts +96 -0
- package/lib/sync/sync-engine.js +236 -0
- package/lib/sync/sync-engine.js.map +1 -0
- package/lib/sync/sync-state.d.ts +31 -0
- package/lib/sync/sync-state.js +103 -0
- package/lib/sync/sync-state.js.map +1 -0
- package/lib/sync/transport.d.ts +50 -0
- package/lib/sync/transport.js +22 -0
- package/lib/sync/transport.js.map +1 -0
- package/package.json +4 -1
- package/src/adapters/test-helpers.ts +10 -3
- package/src/cli/index.ts +278 -0
- package/src/client/ConfigManagerSection.tsx +49 -14
- package/src/client/api.ts +53 -5
- package/src/client/client-types.ts +0 -6
- package/src/client/common/ProgressBar.tsx +31 -19
- package/src/client/common/progress-view-edge.test.ts +60 -0
- package/src/client/common/progress-view.test.ts +79 -0
- package/src/client/common/progress-view.ts +94 -0
- package/src/client/config-manager.module.css +99 -20
- package/src/client/export/ExportView.tsx +63 -49
- package/src/client/import/ImportWizardView.tsx +107 -66
- package/src/client/index.ts +22 -0
- package/src/client/locales.ts +53 -0
- package/src/client/run-store.test.ts +500 -0
- package/src/client/run-store.ts +772 -0
- package/src/client/snapshots/SnapshotsPanel.tsx +240 -0
- package/src/client/sync/SyncSettingsView.tsx +261 -0
- package/src/client/sync/sync-api.test.ts +117 -0
- package/src/client/sync/sync-api.ts +132 -0
- package/src/client/sync/sync-locales.ts +77 -0
- package/src/client/sync/sync-view.test.ts +211 -0
- package/src/client/sync/sync-view.ts +202 -0
- package/src/core/analyzer.ts +50 -1
- package/src/core/backup.ts +78 -2
- package/src/core/exporter.ts +21 -0
- package/src/core/importer.ts +4 -0
- package/src/core/index.ts +5 -0
- package/src/core/restore.ts +587 -0
- package/src/core/run-progress.test.ts +237 -0
- package/src/core/run-registry.test.ts +139 -0
- package/src/core/run-registry.ts +166 -0
- package/src/core/smoke.test.ts +5 -0
- package/src/core/types.ts +22 -0
- package/src/index.ts +476 -8
- package/src/security/security.test.ts +5 -0
- package/src/sync/fs.ts +46 -0
- package/src/sync/git/git-transport.test.ts +456 -0
- package/src/sync/git/git-transport.ts +390 -0
- package/src/sync/layout.test.ts +226 -0
- package/src/sync/layout.ts +183 -0
- package/src/sync/sync-config.ts +72 -0
- package/src/sync/sync-engine.test.ts +299 -0
- package/src/sync/sync-engine.ts +318 -0
- package/src/sync/sync-state.test.ts +118 -0
- package/src/sync/sync-state.ts +123 -0
- package/src/sync/transport.test.ts +110 -0
- package/src/sync/transport.ts +73 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 离线恢复引擎(M2,criterion m2-restore-restores;设计 §8 / 规范 §27)。
|
|
3
|
+
*
|
|
4
|
+
* 与 rollback.ts 的区别:rollback 是导入失败时的自动补偿(在线,经 HostContext
|
|
5
|
+
* 调 DSH 服务);本模块是用户主动的「恢复到导入前状态」——纯离线,零 DSH 运行时
|
|
6
|
+
* 依赖(仅 node 内置 + 本项目零依赖工具),直接操作文件系统与官方 dsh plugin CLI:
|
|
7
|
+
*
|
|
8
|
+
* 1. 宿主整文件还原:hostFileBackups(settings.yaml / settings.json / 用户层与
|
|
9
|
+
* profile 层 cordis.patch.yml)的 blob 写回 $DSH_HOME;快照时不存在、现已出现
|
|
10
|
+
* 的文件删除(restore = 还原到快照那一刻的状态,含移除导入期间新增激活行);
|
|
11
|
+
* 2. pre-restore 双保险:任何覆盖/删除前,把当前文件复制到
|
|
12
|
+
* <snapshotDir>/pre-restore/(可人工反悔);
|
|
13
|
+
* 3. 插件撤销:beforePlugins(导入前基线)与当前已装对比 → 导入期间新增的插件
|
|
14
|
+
* 走官方 `dsh plugin --profile <p> remove <pkg>`(runDshPlugin 通道,零新子进程代码);
|
|
15
|
+
* 4. file 类条目补偿:skills/agentPresets/pluginFiles/sessions 原文件 blob 写回 /
|
|
16
|
+
* 快照时不存在则删除(与 rollback.ts 语义一致);
|
|
17
|
+
* 5. credentials:DSH 不回读值 → existed=true 只生成 manualHint,绝不自动改写。
|
|
18
|
+
*
|
|
19
|
+
* settingsNamespace / patchLine 条目由宿主整文件还原覆盖(settings.yaml /
|
|
20
|
+
* cordis.patch.yml);workspaceRecord 离线无法经 DSH storages 恢复 → 如实提示。
|
|
21
|
+
* 旧快照(无 beforePlugins / hostFileBackups)兼容:缺基线的部分只提示不动作。
|
|
22
|
+
*/
|
|
23
|
+
import fs from 'node:fs/promises';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { resolveFileTarget } from "./backup.js";
|
|
26
|
+
import { classifyDshPluginFailure, readInstalled, resolveProfileDir, runDshPlugin, } from "./plugin-cli.js";
|
|
27
|
+
import { parseJsonSafe } from "../utils/json.js";
|
|
28
|
+
/* ------------------------------------------------------------ 工具 */
|
|
29
|
+
/** 快照目录内读取 snapshot.json(深度保护解析) */
|
|
30
|
+
async function loadSnapshot(snapshotDir) {
|
|
31
|
+
const parsed = parseJsonSafe(await fs.readFile(path.join(snapshotDir, 'snapshot.json'), 'utf8'));
|
|
32
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
33
|
+
throw new Error(`快照 ${snapshotDir} 的 snapshot.json 不是合法对象`);
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
async function fileExists(p) {
|
|
38
|
+
try {
|
|
39
|
+
await fs.access(p);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 越界防御:abs 必须等于 homeDir 或在 homeDir 之内 */
|
|
47
|
+
function isWithinHome(homeDir, abs) {
|
|
48
|
+
const root = path.resolve(homeDir);
|
|
49
|
+
return abs === root || abs.startsWith(root + path.sep);
|
|
50
|
+
}
|
|
51
|
+
/** homeDir 内绝对路径(越界抛错) */
|
|
52
|
+
function homeAbs(homeDir, relPath) {
|
|
53
|
+
const abs = path.resolve(homeDir, relPath);
|
|
54
|
+
if (!isWithinHome(homeDir, abs))
|
|
55
|
+
throw new Error(`路径越界: ${relPath}`);
|
|
56
|
+
return abs;
|
|
57
|
+
}
|
|
58
|
+
/** 快照目录内 blob 绝对路径(防 snapshot.json 里伪造 ../ 越界读) */
|
|
59
|
+
function blobAbs(snapshotDir, blobPath) {
|
|
60
|
+
const abs = path.resolve(snapshotDir, blobPath);
|
|
61
|
+
if (!isWithinHome(snapshotDir, abs))
|
|
62
|
+
throw new Error(`blob 路径越界: ${blobPath}`);
|
|
63
|
+
return abs;
|
|
64
|
+
}
|
|
65
|
+
/** 绝对路径 → 相对 $DSH_HOME(跨平台归一为 / 分隔;越界返回空串) */
|
|
66
|
+
function toRelPath(homeDir, abs) {
|
|
67
|
+
if (!isWithinHome(homeDir, abs))
|
|
68
|
+
return '';
|
|
69
|
+
return path.relative(homeDir, abs).split(path.sep).join('/');
|
|
70
|
+
}
|
|
71
|
+
/** file 类条目的目标绝对路径(复用 backup.ts 的基准目录规则;仅读 homeDir) */
|
|
72
|
+
function resolveFileAbs(homeDir, adapter, ref) {
|
|
73
|
+
return resolveFileTarget({ homeDir }, adapter, ref);
|
|
74
|
+
}
|
|
75
|
+
/** settings 文件 relPath:settingsPath 覆盖优先,否则探测 settings.yaml → settings.json */
|
|
76
|
+
async function resolveSettingsRelPath(homeDir, settingsPath) {
|
|
77
|
+
if (settingsPath !== undefined && settingsPath !== '') {
|
|
78
|
+
const rel = toRelPath(homeDir, path.resolve(homeDir, settingsPath));
|
|
79
|
+
return rel === '' ? undefined : rel;
|
|
80
|
+
}
|
|
81
|
+
for (const cand of ['settings.yaml', 'settings.json']) {
|
|
82
|
+
if (await fileExists(path.join(homeDir, cand)))
|
|
83
|
+
return cand;
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
/** 把当前文件复制到 <snapshotDir>/pre-restore/(双保险,命名含序号防碰撞) */
|
|
88
|
+
async function copyToPreRestore(preDir, absPath, label, seq) {
|
|
89
|
+
const safe = label.replace(/[\\/:*?"<>|]/g, '_');
|
|
90
|
+
await fs.mkdir(preDir, { recursive: true });
|
|
91
|
+
await fs.copyFile(absPath, path.join(preDir, `${String(seq).padStart(4, '0')}-${safe}`));
|
|
92
|
+
}
|
|
93
|
+
/** 默认插件卸载器:官方 dsh plugin remove 通道 + 失败分类诊断 */
|
|
94
|
+
async function defaultPluginUninstaller(name, profileDir, profile) {
|
|
95
|
+
const result = await runDshPlugin(profileDir, profile, ['remove', name]);
|
|
96
|
+
if (result.exitCode === 0)
|
|
97
|
+
return { ok: true };
|
|
98
|
+
const output = `${result.stderr}\n${result.stdout}`.trim();
|
|
99
|
+
const failure = classifyDshPluginFailure(output);
|
|
100
|
+
if (failure !== null) {
|
|
101
|
+
return { ok: false, message: `dsh plugin remove ${name} 失败(${failure.code}):${failure.message}` };
|
|
102
|
+
}
|
|
103
|
+
const tail = output.split('\n').slice(-8).join('\n') || '无输出';
|
|
104
|
+
return { ok: false, message: `dsh plugin remove ${name} 失败(exit ${String(result.exitCode)}):${tail}` };
|
|
105
|
+
}
|
|
106
|
+
/* ------------------------------------------------------------ planRestore */
|
|
107
|
+
/**
|
|
108
|
+
* 生成恢复动作计划(dry-run 预览的唯一入口;零写入):
|
|
109
|
+
* 读快照 + 探测当前文件/已装插件状态,输出按执行顺序排列的动作清单。
|
|
110
|
+
*/
|
|
111
|
+
export async function planRestore(opts) {
|
|
112
|
+
const { snapshotDir, homeDir, profile } = opts;
|
|
113
|
+
const snapshot = await loadSnapshot(snapshotDir);
|
|
114
|
+
const settingsRel = await resolveSettingsRelPath(homeDir, opts.settingsPath);
|
|
115
|
+
const actions = [];
|
|
116
|
+
// 1) 宿主整文件还原(hostFileBackups)
|
|
117
|
+
const hostBackups = snapshot.hostFileBackups ?? [];
|
|
118
|
+
for (const backup of hostBackups) {
|
|
119
|
+
let abs;
|
|
120
|
+
try {
|
|
121
|
+
abs = homeAbs(homeDir, backup.relPath);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
actions.push({
|
|
125
|
+
kind: 'skip',
|
|
126
|
+
description: `宿主文件路径越界,跳过: ${backup.relPath}`,
|
|
127
|
+
target: backup.relPath,
|
|
128
|
+
detail: '快照登记路径越界,拒绝恢复',
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const currentExists = await fileExists(abs);
|
|
133
|
+
if (backup.existed) {
|
|
134
|
+
if (backup.blobPath === '') {
|
|
135
|
+
actions.push({
|
|
136
|
+
kind: 'skip',
|
|
137
|
+
description: `宿主文件 ${backup.relPath} 缺少 blob 副本,跳过`,
|
|
138
|
+
target: backup.relPath,
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (!(await fileExists(blobAbs(snapshotDir, backup.blobPath)))) {
|
|
143
|
+
actions.push({
|
|
144
|
+
kind: 'skip',
|
|
145
|
+
description: `宿主文件 ${backup.relPath} 的 blob 缺失,跳过`,
|
|
146
|
+
target: backup.relPath,
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
actions.push({
|
|
151
|
+
kind: 'hostFileRestore',
|
|
152
|
+
description: `整文件还原 ${backup.relPath}`,
|
|
153
|
+
target: backup.relPath,
|
|
154
|
+
blobPath: backup.blobPath,
|
|
155
|
+
isSettings: backup.relPath === settingsRel,
|
|
156
|
+
detail: currentExists ? '还原前将备份当前文件到 pre-restore/' : '当前文件不存在,直接写入',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else if (currentExists) {
|
|
160
|
+
actions.push({
|
|
161
|
+
kind: 'hostFileRemove',
|
|
162
|
+
description: `移除快照时不存在、现已出现的文件 ${backup.relPath}`,
|
|
163
|
+
target: backup.relPath,
|
|
164
|
+
isSettings: backup.relPath === settingsRel,
|
|
165
|
+
detail: '删除前将备份当前文件到 pre-restore/',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
actions.push({
|
|
170
|
+
kind: 'skip',
|
|
171
|
+
description: `宿主文件 ${backup.relPath} 快照时不存在且当前也不存在,无动作`,
|
|
172
|
+
target: backup.relPath,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// settings 覆盖提示:settingsPath 指定的文件未被快照登记 → 无法整文件还原
|
|
177
|
+
if (settingsRel !== undefined && !hostBackups.some((b) => b.relPath === settingsRel)) {
|
|
178
|
+
actions.push({
|
|
179
|
+
kind: 'skip',
|
|
180
|
+
description: `settings 文件 ${settingsRel} 未被快照登记,无法整文件还原`,
|
|
181
|
+
isSettings: true,
|
|
182
|
+
detail: '该快照未备份此 settings 文件(可能当时不存在或宿主未暴露 profile)',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// 2) 插件撤销:beforePlugins 基线 vs 当前已装 → 导入期间新增
|
|
186
|
+
const profileDir = resolveProfileDir(homeDir, profile);
|
|
187
|
+
const baseline = snapshot.beforePlugins;
|
|
188
|
+
if (baseline === undefined) {
|
|
189
|
+
actions.push({
|
|
190
|
+
kind: 'skip',
|
|
191
|
+
description: '旧快照无 beforePlugins 基线,无法自动卸载插件',
|
|
192
|
+
detail: '请人工核对导入期间新增的插件并逐个移除',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const beforeNames = new Set(baseline.map((p) => p.name));
|
|
197
|
+
const added = Object.keys(readInstalled(profileDir)).filter((name) => !beforeNames.has(name));
|
|
198
|
+
for (const name of added) {
|
|
199
|
+
actions.push({
|
|
200
|
+
kind: 'pluginRemove',
|
|
201
|
+
description: `卸载导入期间新增插件 ${name}`,
|
|
202
|
+
target: name,
|
|
203
|
+
pluginName: name,
|
|
204
|
+
detail: baseline.length === 0 ? '快照基线为空(导入前可能未捕获插件清单),请确认后执行' : undefined,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (added.length === 0) {
|
|
208
|
+
actions.push({
|
|
209
|
+
kind: 'skip',
|
|
210
|
+
description: '无导入期间新增插件(当前已装插件 ⊆ 快照基线)',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// 3) file 类条目补偿 + credentials 提示(聚合计数供报告)
|
|
215
|
+
let namespaceCount = 0;
|
|
216
|
+
let patchLineCount = 0;
|
|
217
|
+
let workspaceCount = 0;
|
|
218
|
+
let credentialAbsentCount = 0;
|
|
219
|
+
for (const entry of snapshot.entries) {
|
|
220
|
+
switch (entry.kind) {
|
|
221
|
+
case 'file': {
|
|
222
|
+
const rel = toRelPath(homeDir, resolveFileAbs(homeDir, entry.adapter, entry.ref));
|
|
223
|
+
if (rel === '') {
|
|
224
|
+
actions.push({
|
|
225
|
+
kind: 'skip',
|
|
226
|
+
description: `文件条目路径越界,跳过: ${entry.adapter}:${entry.ref}`,
|
|
227
|
+
target: entry.ref,
|
|
228
|
+
});
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const abs = homeAbs(homeDir, rel);
|
|
232
|
+
const currentExists = await fileExists(abs);
|
|
233
|
+
if (entry.existed && entry.copiedTo) {
|
|
234
|
+
if (!(await fileExists(blobAbs(snapshotDir, entry.copiedTo)))) {
|
|
235
|
+
actions.push({
|
|
236
|
+
kind: 'skip',
|
|
237
|
+
description: `文件 ${rel} 的 blob 缺失,跳过`,
|
|
238
|
+
target: rel,
|
|
239
|
+
});
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
actions.push({
|
|
243
|
+
kind: 'fileRestore',
|
|
244
|
+
description: `还原文件 ${rel}(${entry.adapter})`,
|
|
245
|
+
target: rel,
|
|
246
|
+
blobPath: entry.copiedTo,
|
|
247
|
+
detail: currentExists ? '还原前将备份当前文件到 pre-restore/' : '当前文件不存在,直接写入',
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else if (!entry.existed) {
|
|
251
|
+
if (currentExists) {
|
|
252
|
+
actions.push({
|
|
253
|
+
kind: 'fileRemove',
|
|
254
|
+
description: `删除导入写入的文件 ${rel}(${entry.adapter})`,
|
|
255
|
+
target: rel,
|
|
256
|
+
detail: '删除前将备份当前文件到 pre-restore/',
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
actions.push({
|
|
261
|
+
kind: 'skip',
|
|
262
|
+
description: `文件 ${rel} 快照时不存在且当前也不存在,无动作`,
|
|
263
|
+
target: rel,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
actions.push({
|
|
269
|
+
kind: 'skip',
|
|
270
|
+
description: `文件 ${rel} 缺少 blob 副本信息,跳过`,
|
|
271
|
+
target: rel,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case 'credential':
|
|
277
|
+
if (entry.existed) {
|
|
278
|
+
actions.push({
|
|
279
|
+
kind: 'credentialHint',
|
|
280
|
+
description: `凭据 ${entry.ref} 需人工补录`,
|
|
281
|
+
target: entry.ref,
|
|
282
|
+
manualHint: `凭据 "${entry.ref}" 请人工重新填写(DSH 不回读凭据值)`,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
credentialAbsentCount += 1;
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
case 'settingsNamespace':
|
|
290
|
+
namespaceCount += 1;
|
|
291
|
+
break;
|
|
292
|
+
case 'patchLine':
|
|
293
|
+
patchLineCount += 1;
|
|
294
|
+
break;
|
|
295
|
+
case 'workspaceRecord':
|
|
296
|
+
workspaceCount += 1;
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// 聚合提示:整文件还原覆盖不了的条目如实说明
|
|
303
|
+
const settingsBacked = hostBackups.some((b) => b.relPath === 'settings.yaml' || b.relPath === 'settings.json');
|
|
304
|
+
const patchBacked = hostBackups.some((b) => b.relPath === 'cordis.patch.yml');
|
|
305
|
+
if (namespaceCount > 0 && !settingsBacked) {
|
|
306
|
+
actions.push({
|
|
307
|
+
kind: 'skip',
|
|
308
|
+
description: `${namespaceCount} 条 settings namespace 条目:快照无 settings 整文件备份,离线无法恢复`,
|
|
309
|
+
detail: '需经在线回滚(rollback.ts)或人工核对 settings.yaml',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (patchLineCount > 0 && !patchBacked) {
|
|
313
|
+
actions.push({
|
|
314
|
+
kind: 'skip',
|
|
315
|
+
description: `${patchLineCount} 条 patch 行条目:快照无 cordis.patch.yml 整文件备份,离线无法恢复`,
|
|
316
|
+
detail: '需经在线回滚(rollback.ts)或人工核对 patch 文件',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
if (workspaceCount > 0) {
|
|
320
|
+
actions.push({
|
|
321
|
+
kind: 'skip',
|
|
322
|
+
description: `${workspaceCount} 条 workspace 记录条目:离线无法恢复(数据在 DSH storages)`,
|
|
323
|
+
detail: '需经在线回滚(rollback.ts)或人工核对 workspace 注册表',
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (credentialAbsentCount > 0) {
|
|
327
|
+
actions.push({
|
|
328
|
+
kind: 'skip',
|
|
329
|
+
description: `${credentialAbsentCount} 条凭据快照时未配置:若导入已创建,需人工在 DSH 中删除`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
snapshotId: snapshot.id,
|
|
334
|
+
createdAt: snapshot.createdAt,
|
|
335
|
+
sourceZip: snapshot.sourceZip,
|
|
336
|
+
actions,
|
|
337
|
+
summary: summarizeActions(actions),
|
|
338
|
+
pluginBaselineConfirmed: baseline !== undefined,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function summarizeActions(actions) {
|
|
342
|
+
const summary = {
|
|
343
|
+
hostFileRestores: 0, hostFileRemoves: 0, pluginRemoves: 0,
|
|
344
|
+
fileRestores: 0, fileRemoves: 0, credentialHints: 0, skips: 0,
|
|
345
|
+
};
|
|
346
|
+
for (const a of actions) {
|
|
347
|
+
switch (a.kind) {
|
|
348
|
+
case 'hostFileRestore':
|
|
349
|
+
summary.hostFileRestores += 1;
|
|
350
|
+
break;
|
|
351
|
+
case 'hostFileRemove':
|
|
352
|
+
summary.hostFileRemoves += 1;
|
|
353
|
+
break;
|
|
354
|
+
case 'pluginRemove':
|
|
355
|
+
summary.pluginRemoves += 1;
|
|
356
|
+
break;
|
|
357
|
+
case 'fileRestore':
|
|
358
|
+
summary.fileRestores += 1;
|
|
359
|
+
break;
|
|
360
|
+
case 'fileRemove':
|
|
361
|
+
summary.fileRemoves += 1;
|
|
362
|
+
break;
|
|
363
|
+
case 'credentialHint':
|
|
364
|
+
summary.credentialHints += 1;
|
|
365
|
+
break;
|
|
366
|
+
case 'skip':
|
|
367
|
+
summary.skips += 1;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return summary;
|
|
372
|
+
}
|
|
373
|
+
/* ------------------------------------------------------------ restore */
|
|
374
|
+
/**
|
|
375
|
+
* 执行恢复:按计划顺序 整文件还原 → 插件卸载 → file 补偿,
|
|
376
|
+
* 覆盖/删除前全部先复制到 <snapshotDir>/pre-restore/(双保险),
|
|
377
|
+
* 逐项 try/catch(单项失败不拖垮其余),输出诚实报告。
|
|
378
|
+
*/
|
|
379
|
+
export async function restore(opts) {
|
|
380
|
+
const plan = await planRestore(opts);
|
|
381
|
+
const { snapshotDir, homeDir, profile } = opts;
|
|
382
|
+
const preDir = path.join(snapshotDir, 'pre-restore');
|
|
383
|
+
const profileDir = resolveProfileDir(homeDir, profile);
|
|
384
|
+
const uninstaller = opts.pluginUninstaller ?? defaultPluginUninstaller;
|
|
385
|
+
const report = {
|
|
386
|
+
snapshotId: plan.snapshotId,
|
|
387
|
+
restored: [],
|
|
388
|
+
removedPlugins: [],
|
|
389
|
+
manualHints: [],
|
|
390
|
+
failed: [],
|
|
391
|
+
skipped: [],
|
|
392
|
+
};
|
|
393
|
+
let seq = 0;
|
|
394
|
+
for (const action of plan.actions) {
|
|
395
|
+
try {
|
|
396
|
+
switch (action.kind) {
|
|
397
|
+
case 'hostFileRestore': {
|
|
398
|
+
const abs = homeAbs(homeDir, action.target);
|
|
399
|
+
if (await fileExists(abs))
|
|
400
|
+
await copyToPreRestore(preDir, abs, action.target, ++seq);
|
|
401
|
+
const data = await fs.readFile(blobAbs(snapshotDir, action.blobPath));
|
|
402
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
403
|
+
await fs.writeFile(abs, data);
|
|
404
|
+
report.restored.push(action.target);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case 'hostFileRemove': {
|
|
408
|
+
const abs = homeAbs(homeDir, action.target);
|
|
409
|
+
if (await fileExists(abs)) {
|
|
410
|
+
await copyToPreRestore(preDir, abs, action.target, ++seq);
|
|
411
|
+
await fs.rm(abs, { force: true });
|
|
412
|
+
}
|
|
413
|
+
report.restored.push(action.target);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case 'fileRestore': {
|
|
417
|
+
const abs = homeAbs(homeDir, action.target);
|
|
418
|
+
if (await fileExists(abs))
|
|
419
|
+
await copyToPreRestore(preDir, abs, action.target, ++seq);
|
|
420
|
+
const data = await fs.readFile(blobAbs(snapshotDir, action.blobPath));
|
|
421
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
422
|
+
await fs.writeFile(abs, data);
|
|
423
|
+
report.restored.push(action.target);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case 'fileRemove': {
|
|
427
|
+
const abs = homeAbs(homeDir, action.target);
|
|
428
|
+
if (await fileExists(abs)) {
|
|
429
|
+
await copyToPreRestore(preDir, abs, action.target, ++seq);
|
|
430
|
+
await fs.rm(abs, { force: true });
|
|
431
|
+
}
|
|
432
|
+
report.restored.push(action.target);
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case 'pluginRemove': {
|
|
436
|
+
const result = await uninstaller(action.pluginName, profileDir, profile);
|
|
437
|
+
if (result.ok) {
|
|
438
|
+
report.removedPlugins.push(action.pluginName);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
report.failed.push({ item: `plugin:${action.pluginName}`, reason: result.message ?? '卸载失败' });
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case 'credentialHint':
|
|
446
|
+
report.manualHints.push(action.manualHint ?? action.description);
|
|
447
|
+
break;
|
|
448
|
+
case 'skip':
|
|
449
|
+
report.skipped.push(action.description);
|
|
450
|
+
break;
|
|
451
|
+
default:
|
|
452
|
+
report.skipped.push(`未知动作 ${String(action.kind)}: ${action.description}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
report.failed.push({
|
|
457
|
+
item: action.target ?? action.pluginName ?? action.description,
|
|
458
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return report;
|
|
463
|
+
}
|
|
464
|
+
/* ------------------------------------------------------------ listSnapshots */
|
|
465
|
+
/**
|
|
466
|
+
* 扫描快照根目录下每个子目录的 snapshot.json,返回按 createdAt 倒序的元信息。
|
|
467
|
+
* 目录缺失/条目损坏自动跳过(不阻断其他快照)。
|
|
468
|
+
*/
|
|
469
|
+
export async function listSnapshots(dir) {
|
|
470
|
+
let entries;
|
|
471
|
+
try {
|
|
472
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
return [];
|
|
476
|
+
}
|
|
477
|
+
const metas = [];
|
|
478
|
+
for (const entry of entries) {
|
|
479
|
+
if (!entry.isDirectory())
|
|
480
|
+
continue;
|
|
481
|
+
try {
|
|
482
|
+
const parsed = parseJsonSafe(await fs.readFile(path.join(dir, entry.name, 'snapshot.json'), 'utf8'));
|
|
483
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
484
|
+
continue;
|
|
485
|
+
const snapshot = parsed;
|
|
486
|
+
if (typeof snapshot.id !== 'string' || snapshot.id === '')
|
|
487
|
+
continue;
|
|
488
|
+
metas.push({
|
|
489
|
+
id: snapshot.id,
|
|
490
|
+
createdAt: snapshot.createdAt,
|
|
491
|
+
sourceZip: snapshot.sourceZip,
|
|
492
|
+
status: snapshot.status,
|
|
493
|
+
entryCount: snapshot.entries?.length ?? 0,
|
|
494
|
+
hostFileBackupCount: snapshot.hostFileBackups?.length ?? 0,
|
|
495
|
+
beforePluginCount: snapshot.beforePlugins?.length ?? 0,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// 损坏 / 非快照目录:跳过
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
metas.sort((a, b) => (a.createdAt < b.createdAt ? 1 : a.createdAt > b.createdAt ? -1 : 0));
|
|
503
|
+
return metas;
|
|
504
|
+
}
|
|
505
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/core/restore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** run 类型:导出 / 导入。 */
|
|
2
|
+
export type RunKind = 'export' | 'import';
|
|
3
|
+
/** run 状态:进行中 / 完成 / 失败。 */
|
|
4
|
+
export type RunStatus = 'running' | 'done' | 'failed';
|
|
5
|
+
/** 单个 run 的实时状态(/progress 返回体;纯 JSON 可序列化)。 */
|
|
6
|
+
export interface RunState {
|
|
7
|
+
/** 不可猜测的 run id(32 hex 字符)。 */
|
|
8
|
+
runId: string;
|
|
9
|
+
kind: RunKind;
|
|
10
|
+
status: RunStatus;
|
|
11
|
+
/** 当前分区(adapter id 或导入阶段名);无则 null。 */
|
|
12
|
+
section: string | null;
|
|
13
|
+
/** 分区总数(导出 = 选中分区数;导入无此语义 = null)。 */
|
|
14
|
+
sectionTotal: number | null;
|
|
15
|
+
/** 已完成的内部计数(导出 = 当前分区序号;导入 = 已处理计划项数)。 */
|
|
16
|
+
item: number | null;
|
|
17
|
+
/** 内部计数总数(导出 = 分区总数;导入 = 计划项总数)。 */
|
|
18
|
+
itemTotal: number | null;
|
|
19
|
+
/** 非敏感进度摘要(当前分区 / 当前计划项 id)。 */
|
|
20
|
+
detail: string | null;
|
|
21
|
+
/** 完成时的业务结果(仅 JSON 可序列化、不含秘密值)。 */
|
|
22
|
+
result?: unknown;
|
|
23
|
+
/** 失败时的错误消息(非敏感)。 */
|
|
24
|
+
error?: string;
|
|
25
|
+
createdAt: number;
|
|
26
|
+
updatedAt: number;
|
|
27
|
+
}
|
|
28
|
+
/** 进行中同 kind 已有 run 时注册被拒(宿主路由 → 409)。 */
|
|
29
|
+
export declare class RunConflictError extends Error {
|
|
30
|
+
/** 已在进行中的 run id(供客户端定位既有任务)。 */
|
|
31
|
+
readonly runId: string;
|
|
32
|
+
constructor(runId: string, kind: RunKind);
|
|
33
|
+
}
|
|
34
|
+
/** 完成/失败后的默认保留时长(30 分钟)。 */
|
|
35
|
+
export declare const DEFAULT_RUN_RETENTION_MS: number;
|
|
36
|
+
export interface RunRegistryOptions {
|
|
37
|
+
/** 完成/失败后保留时长(默认 30 分钟);超期 run 在下次访问时清理。 */
|
|
38
|
+
retentionMs?: number;
|
|
39
|
+
/** 时间源(测试注入)。 */
|
|
40
|
+
now?: () => number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 内存 run 注册表。Node 单线程 + 同步 Map 读写,无并发竞争;
|
|
44
|
+
* 所有公开读方法返回状态副本,防止外部直接篡改内部状态。
|
|
45
|
+
*/
|
|
46
|
+
export declare class RunRegistry {
|
|
47
|
+
private readonly runs;
|
|
48
|
+
private readonly retentionMs;
|
|
49
|
+
private readonly now;
|
|
50
|
+
constructor(opts?: RunRegistryOptions);
|
|
51
|
+
/** 注册新 run;同 kind 已有 running 时抛 RunConflictError。 */
|
|
52
|
+
register(kind: RunKind): RunState;
|
|
53
|
+
/** 更新进度;run 不存在返回 undefined;已完成/失败后的晚到更新被忽略(防御异步回调竞态)。 */
|
|
54
|
+
update(runId: string, patch: Partial<Pick<RunState, 'section' | 'sectionTotal' | 'item' | 'itemTotal' | 'detail'>>): RunState | undefined;
|
|
55
|
+
/** 读单个 run;超过保留期或不存在返回 undefined。 */
|
|
56
|
+
get(runId: string): RunState | undefined;
|
|
57
|
+
/** 列出活跃(running)run(刷新恢复时经 /runs 重订阅用)。 */
|
|
58
|
+
listActive(): RunState[];
|
|
59
|
+
/** 标记完成并写入业务结果;run 不存在返回 undefined。 */
|
|
60
|
+
finish(runId: string, result: unknown): RunState | undefined;
|
|
61
|
+
/** 标记失败并写入错误消息;run 不存在返回 undefined。 */
|
|
62
|
+
fail(runId: string, error: string): RunState | undefined;
|
|
63
|
+
/** 惰性清理:超过保留期的 run(含长期 running 的僵死任务)全部移除。 */
|
|
64
|
+
private prune;
|
|
65
|
+
}
|