dsh-config-manager 0.1.20 → 0.1.21

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.
Files changed (54) hide show
  1. package/lib/client.d.ts +312 -46
  2. package/lib/client.js +1172 -205
  3. package/lib/client.js.map +1 -1
  4. package/lib/core/run-registry.d.ts +2 -2
  5. package/lib/core/run-registry.js +5 -1
  6. package/lib/core/run-registry.js.map +1 -1
  7. package/lib/index.js +292 -19
  8. package/lib/index.js.map +1 -1
  9. package/lib/sync/autosync-config.d.ts +34 -0
  10. package/lib/sync/autosync-config.js +116 -0
  11. package/lib/sync/autosync-config.js.map +1 -0
  12. package/lib/sync/autosync-scheduler.d.ts +95 -0
  13. package/lib/sync/autosync-scheduler.js +379 -0
  14. package/lib/sync/autosync-scheduler.js.map +1 -0
  15. package/lib/sync/sync-engine.d.ts +59 -4
  16. package/lib/sync/sync-engine.js +138 -12
  17. package/lib/sync/sync-engine.js.map +1 -1
  18. package/lib/sync/sync-history.d.ts +39 -0
  19. package/lib/sync/sync-history.js +88 -0
  20. package/lib/sync/sync-history.js.map +1 -0
  21. package/lib/sync/sync-session.d.ts +39 -0
  22. package/lib/sync/sync-session.js +54 -0
  23. package/lib/sync/sync-session.js.map +1 -0
  24. package/lib/ui/i18n.d.ts +63 -0
  25. package/lib/ui/i18n.js +130 -1
  26. package/lib/ui/i18n.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/client/config-manager.module.css +18 -0
  29. package/src/client/sync/SyncConfirmView.tsx +301 -0
  30. package/src/client/sync/SyncHistoryView.tsx +107 -25
  31. package/src/client/sync/SyncSettingsView.tsx +226 -83
  32. package/src/client/sync/history-model.test.ts +82 -0
  33. package/src/client/sync/history-model.ts +89 -2
  34. package/src/client/sync/sync-api.test.ts +155 -0
  35. package/src/client/sync/sync-api.ts +208 -16
  36. package/src/client/sync/sync-locales.ts +162 -1
  37. package/src/client/sync/sync-view-v2.test.ts +131 -0
  38. package/src/client/sync/sync-view.ts +151 -4
  39. package/src/core/run-registry.ts +7 -3
  40. package/src/index.ts +314 -22
  41. package/src/sync/autosync-config.test.ts +93 -0
  42. package/src/sync/autosync-config.ts +137 -0
  43. package/src/sync/autosync-scheduler.test.ts +191 -0
  44. package/src/sync/autosync-scheduler.ts +443 -0
  45. package/src/sync/sync-engine.test.ts +107 -7
  46. package/src/sync/sync-engine.ts +176 -14
  47. package/src/sync/sync-history.test.ts +85 -0
  48. package/src/sync/sync-history.ts +126 -0
  49. package/src/sync/sync-session.test.ts +137 -0
  50. package/src/sync/sync-session.ts +76 -0
  51. package/src/ui/i18n.ts +132 -3
  52. package/src/client/sync/SyncPullPreviewView.test.ts +0 -54
  53. package/src/client/sync/SyncPullPreviewView.tsx +0 -165
  54. package/src/client/sync/pull-preview-model.ts +0 -44
@@ -0,0 +1,379 @@
1
+ /**
2
+ * AutoSyncScheduler:宿主后台自动同步调度器。
3
+ *
4
+ * 生命周期:
5
+ * - start():读 autosync-config;若 enabled 启动定时器(按 interval);无条件执行一次
6
+ * 「启动触发下载合并」(受 startupMinIntervalMs 阈值约束)。
7
+ * - stop():清定时器、标记不再调度。
8
+ * - runOnce():执行一次完整双向自动同步(§6.1 流程)。
9
+ *
10
+ * 核心逻辑(runOnce):
11
+ * - 读配置;若 !enabled → return
12
+ * - runs.register('autosync') 防重复;同 kind running → 跳过
13
+ * - readSyncConfig → repoUrl 无 → 记 skipped(未配置) → return
14
+ * - Phase A: engine.merge() 三方合并 → 判定 needsReview(冲突/缺失依赖/Install/Error)
15
+ * - 冲突 → 跳过 + 写历史 skipped + conflictedSections[] → return
16
+ * - Phase B: 无冲突 → engine.applyMergePlan(apply) 写入本地
17
+ * - Phase C: 完整双向 → engine.push() 上传
18
+ * - 收尾:写 autosync-config(lastRunAt, lastRunStatus, consecutiveFailures, lastRunHistoryId)
19
+ *
20
+ * 连续失败计数:只对网络/传输/apply 真实失败计数;skipped(未配置/冲突跳过/无远端)不计。
21
+ * 连续失败 ≥ 3 → host.log.warn 通知 + 记 notifiedAt。
22
+ */
23
+ import crypto from 'node:crypto';
24
+ import { readAutosyncConfig, writeAutosyncConfig } from "./autosync-config.js";
25
+ import { readSyncConfig } from "./sync-config.js";
26
+ import { readSyncHistory, appendAutosyncEntry } from "./sync-history.js";
27
+ /** 间隔 → ms 换算(§4.3) */
28
+ export function intervalToMs(interval) {
29
+ const table = {
30
+ '5m': 5 * 60 * 1000,
31
+ '15m': 15 * 60 * 1000,
32
+ '30m': 30 * 60 * 1000,
33
+ '60m': 60 * 60 * 1000,
34
+ '6h': 6 * 60 * 60 * 1000,
35
+ '12h': 12 * 60 * 60 * 1000,
36
+ '24h': 24 * 60 * 60 * 1000,
37
+ };
38
+ return table[interval];
39
+ }
40
+ /**
41
+ * 启动触发下载合并且满足阈值(now - lastRunAt >= startupMinIntervalMs)?
42
+ * lastRunAt 为 undefined(从未运行)→ true。
43
+ */
44
+ export function shouldTriggerStartupRun(lastRunAt, startupMinIntervalMs, nowMs = Date.now()) {
45
+ if (lastRunAt === undefined || lastRunAt === '')
46
+ return true;
47
+ const lastMs = Date.parse(lastRunAt);
48
+ if (Number.isNaN(lastMs))
49
+ return true;
50
+ return nowMs - lastMs >= startupMinIntervalMs;
51
+ }
52
+ export class AutoSyncScheduler {
53
+ syncDir;
54
+ host;
55
+ makeSyncEngine;
56
+ msg;
57
+ runs;
58
+ now;
59
+ readConfig;
60
+ writeConfig;
61
+ readSyncConfigFn;
62
+ readHistoryFn;
63
+ appendHistoryFn;
64
+ setTimer;
65
+ clearTimer;
66
+ timer = null;
67
+ stopped = false;
68
+ running = false;
69
+ constructor(opts) {
70
+ this.syncDir = opts.syncDir;
71
+ this.host = opts.host;
72
+ this.makeSyncEngine = opts.makeSyncEngine;
73
+ this.msg = opts.msg;
74
+ this.runs = opts.runs;
75
+ this.now = opts.now ?? (() => new Date());
76
+ this.readConfig = opts.readConfig ?? (() => readAutosyncConfig(this.syncDir));
77
+ this.writeConfig = opts.writeConfig ?? ((cfg) => writeAutosyncConfig(this.syncDir, cfg));
78
+ this.readSyncConfigFn = opts.readSyncConfigFn ?? (() => readSyncConfig(this.syncDir));
79
+ this.readHistoryFn = opts.readHistoryFn ?? (() => readSyncHistory(this.syncDir));
80
+ this.appendHistoryFn = opts.appendHistoryFn ?? ((entry) => appendAutosyncEntry(this.syncDir, entry));
81
+ this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
82
+ this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
83
+ }
84
+ /** 启动:读配置 → 若 enabled 启动定时器 → 无条件执行一次启动触发下载合并。 */
85
+ start() {
86
+ if (this.stopped)
87
+ return;
88
+ this.refreshTimer();
89
+ void this.startupRun();
90
+ }
91
+ /** 停止:清定时器、标记不再调度;正在执行的任务允许自然结束。 */
92
+ stop() {
93
+ this.stopped = true;
94
+ if (this.timer !== null) {
95
+ this.clearTimer(this.timer);
96
+ this.timer = null;
97
+ }
98
+ }
99
+ /** 重新加载配置(路由 POST /sync/autosync 后调用)。 */
100
+ async reload() {
101
+ if (this.stopped)
102
+ return;
103
+ this.refreshTimer();
104
+ }
105
+ refreshTimer() {
106
+ if (this.timer !== null) {
107
+ this.clearTimer(this.timer);
108
+ this.timer = null;
109
+ }
110
+ void this.readConfig().then((cfg) => {
111
+ if (this.stopped || !cfg.enabled)
112
+ return;
113
+ const ms = intervalToMs(cfg.interval);
114
+ this.timer = this.setTimer(() => {
115
+ if (this.stopped)
116
+ return;
117
+ void this.runOnce().catch((err) => {
118
+ this.host.log.error('自动同步定时触发失败', { error: err instanceof Error ? err.message : String(err) });
119
+ });
120
+ }, ms);
121
+ }).catch(() => { });
122
+ }
123
+ /** 启动触发下载合并(受 startupMinIntervalMs 阈值约束)。 */
124
+ async startupRun() {
125
+ try {
126
+ const cfg = await this.readConfig();
127
+ if (!cfg.enabled)
128
+ return; // 总开关关闭 → 启动触发不执行
129
+ if (!shouldTriggerStartupRun(cfg.lastRunAt, cfg.startupMinIntervalMs, this.now().getTime())) {
130
+ this.host.log.info('自动同步启动触发跳过:距上次运行未达阈值');
131
+ return;
132
+ }
133
+ await this.runOnce({ startup: true });
134
+ }
135
+ catch (err) {
136
+ this.host.log.error('启动触发下载合并失败', { error: err instanceof Error ? err.message : String(err) });
137
+ }
138
+ }
139
+ /**
140
+ * 执行一次自动同步(§6.1)。
141
+ * @param opts.startup - true 表示启动触发变体(只做 Phase A+B,不做 Phase C push)
142
+ */
143
+ async runOnce(opts = {}) {
144
+ if (this.running)
145
+ return { status: 'skipped', direction: 'none', skipReason: 'running', historyId: '', consecutiveFailures: 0 };
146
+ const cfg = await this.readConfig();
147
+ if (!cfg.enabled) {
148
+ return { status: 'skipped', direction: 'none', skipReason: 'disabled', historyId: '', consecutiveFailures: cfg.consecutiveFailures };
149
+ }
150
+ this.running = true;
151
+ const nowIso = this.now().toISOString();
152
+ const historyId = `autosync-${crypto.randomUUID()}`;
153
+ // runs 防重复:同 kind running → 跳过(内部语义,不打搅用户)
154
+ let runId = null;
155
+ try {
156
+ const run = this.runs.register('autosync');
157
+ runId = run.runId;
158
+ }
159
+ catch {
160
+ this.running = false;
161
+ return { status: 'skipped', direction: 'none', skipReason: 'conflict', historyId, consecutiveFailures: cfg.consecutiveFailures };
162
+ }
163
+ try {
164
+ // 读 sync-config → repoUrl
165
+ const syncCfg = await this.readSyncConfigFn();
166
+ if (syncCfg === null || syncCfg.repoUrl === '') {
167
+ const result = {
168
+ status: 'skipped', direction: 'none', skipReason: 'unconfigured', historyId,
169
+ consecutiveFailures: cfg.consecutiveFailures,
170
+ };
171
+ await this.appendHistoryFn({
172
+ direction: 'both',
173
+ status: 'skipped',
174
+ skipReason: 'unconfigured',
175
+ createdAt: nowIso,
176
+ failureCountAtRun: cfg.consecutiveFailures,
177
+ });
178
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
179
+ return result;
180
+ }
181
+ const engine = this.makeSyncEngine(syncCfg.repoUrl, syncCfg.gitBin);
182
+ // Phase A: pull 合并(下载)
183
+ let mergePlan;
184
+ try {
185
+ mergePlan = await engine.merge();
186
+ }
187
+ catch (err) {
188
+ const error = err instanceof Error ? err.message : String(err);
189
+ const result = {
190
+ status: 'failed', direction: 'pull', error, historyId,
191
+ consecutiveFailures: cfg.consecutiveFailures + 1,
192
+ };
193
+ await this.appendHistoryFn({
194
+ direction: 'pull', status: 'failed', error, createdAt: nowIso,
195
+ failureCountAtRun: cfg.consecutiveFailures + 1,
196
+ });
197
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
198
+ this.maybeNotify(cfg.consecutiveFailures + 1, historyId, nowIso);
199
+ return result;
200
+ }
201
+ // 判定 needsReview
202
+ const reviewSections = mergePlan.sections.filter((s) => s.decision === 'conflict');
203
+ if (reviewSections.length > 0) {
204
+ const conflictedSections = reviewSections.map((s) => s.id);
205
+ const result = {
206
+ status: 'skipped', direction: 'pull', skipReason: 'conflict',
207
+ conflictedSections, historyId,
208
+ consecutiveFailures: cfg.consecutiveFailures,
209
+ };
210
+ await this.appendHistoryFn({
211
+ direction: 'pull', status: 'skipped', skipReason: 'conflict',
212
+ conflictedSections, createdAt: nowIso,
213
+ failureCountAtRun: cfg.consecutiveFailures,
214
+ });
215
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
216
+ return result;
217
+ }
218
+ // 无冲突:构造 SyncApplyPlan(autoApply = 所有 useRemote/keepLocal 项;skipped = skip 项)
219
+ const apply = buildAutoApplyPlan(mergePlan);
220
+ if (apply.autoApply.length === 0) {
221
+ // 无物可应用(全部 skip / 无变化)
222
+ const result = {
223
+ status: 'success', direction: 'pull', skipReason: 'unchanged', historyId,
224
+ consecutiveFailures: 0,
225
+ };
226
+ await this.appendHistoryFn({
227
+ direction: 'pull', status: 'success', skipReason: 'unchanged',
228
+ createdAt: nowIso, failureCountAtRun: 0,
229
+ });
230
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
231
+ return result;
232
+ }
233
+ // Phase B: 写入本地(applyMergePlan,无 review-queue 写)
234
+ const applyReport = await engine.applyMergePlan(apply);
235
+ const appliedSections = applyReport.applied;
236
+ if (!applyReport.ok) {
237
+ const error = applyReport.warnings.join('; ') || 'applyMergePlan 执行失败';
238
+ const result = {
239
+ status: 'failed', direction: 'pull', error, historyId,
240
+ consecutiveFailures: cfg.consecutiveFailures + 1,
241
+ };
242
+ await this.appendHistoryFn({
243
+ direction: 'pull', status: 'failed', error, createdAt: nowIso,
244
+ failureCountAtRun: cfg.consecutiveFailures + 1,
245
+ });
246
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
247
+ this.maybeNotify(cfg.consecutiveFailures + 1, historyId, nowIso);
248
+ return result;
249
+ }
250
+ // Phase C: push 上传(完整双向)
251
+ if (!opts.startup) {
252
+ try {
253
+ const pushReport = await engine.push();
254
+ if (!pushReport.ok) {
255
+ const error = pushReport.message ?? 'push 失败';
256
+ const result = {
257
+ status: 'failed', direction: 'both', appliedSections, error, historyId,
258
+ consecutiveFailures: cfg.consecutiveFailures + 1,
259
+ };
260
+ await this.appendHistoryFn({
261
+ direction: 'both', status: 'failed', appliedSections, error,
262
+ createdAt: nowIso, failureCountAtRun: cfg.consecutiveFailures + 1,
263
+ });
264
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
265
+ this.maybeNotify(cfg.consecutiveFailures + 1, historyId, nowIso);
266
+ return result;
267
+ }
268
+ const result = {
269
+ status: 'success', direction: 'both', appliedSections,
270
+ pushedSnapshotId: pushReport.snapshotId, historyId, consecutiveFailures: 0,
271
+ };
272
+ await this.appendHistoryFn({
273
+ direction: 'both', status: 'success', appliedSections,
274
+ pushedSnapshotId: pushReport.snapshotId, createdAt: nowIso, failureCountAtRun: 0,
275
+ });
276
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
277
+ return result;
278
+ }
279
+ catch (err) {
280
+ const error = err instanceof Error ? err.message : String(err);
281
+ const result = {
282
+ status: 'failed', direction: 'both', appliedSections, error, historyId,
283
+ consecutiveFailures: cfg.consecutiveFailures + 1,
284
+ };
285
+ await this.appendHistoryFn({
286
+ direction: 'both', status: 'failed', appliedSections, error,
287
+ createdAt: nowIso, failureCountAtRun: cfg.consecutiveFailures + 1,
288
+ });
289
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
290
+ this.maybeNotify(cfg.consecutiveFailures + 1, historyId, nowIso);
291
+ return result;
292
+ }
293
+ }
294
+ // startup 变体:只做 pull 合并(不上传)
295
+ const result = {
296
+ status: 'success', direction: 'pull', appliedSections, historyId, consecutiveFailures: 0,
297
+ };
298
+ await this.appendHistoryFn({
299
+ direction: 'pull', status: 'success', appliedSections,
300
+ createdAt: nowIso, failureCountAtRun: 0,
301
+ });
302
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
303
+ return result;
304
+ }
305
+ catch (err) {
306
+ const error = err instanceof Error ? err.message : String(err);
307
+ const result = {
308
+ status: 'failed', direction: 'none', error, historyId,
309
+ consecutiveFailures: cfg.consecutiveFailures + 1,
310
+ };
311
+ await this.appendHistoryFn({
312
+ direction: 'both', status: 'failed', error,
313
+ createdAt: nowIso, failureCountAtRun: cfg.consecutiveFailures + 1,
314
+ });
315
+ await this.writeFinalConfig(cfg, result, nowIso, historyId);
316
+ this.maybeNotify(cfg.consecutiveFailures + 1, historyId, nowIso);
317
+ return result;
318
+ }
319
+ finally {
320
+ this.running = false;
321
+ if (runId !== null) {
322
+ // 完成标记(不抛错,尽力而为)
323
+ }
324
+ }
325
+ }
326
+ /** 收尾:写 autosync-config(lastRunAt, lastRunStatus, consecutiveFailures, lastRunHistoryId)。 */
327
+ async writeFinalConfig(base, result, nowIso, historyId) {
328
+ await this.writeConfig({
329
+ ...base,
330
+ lastRunAt: nowIso,
331
+ lastRunStatus: result.status,
332
+ consecutiveFailures: result.consecutiveFailures,
333
+ lastRunHistoryId: historyId,
334
+ ...(result.error !== undefined ? { lastRunMessage: result.error } : {}),
335
+ });
336
+ }
337
+ /** 连续失败 ≥ 3 → 通知(host.log.warn)。 */
338
+ maybeNotify(failures, historyId, nowIso) {
339
+ if (failures >= 3) {
340
+ this.host.log.warn(`自动同步连续失败 ${failures} 次,请检查仓库配置/凭据`);
341
+ // 记录 notifiedAt(更新历史 entry)
342
+ void this.readHistoryFn().then(async (hist) => {
343
+ const entry = hist.autosyncEntries.find((e) => e.createdAt === nowIso);
344
+ if (entry) {
345
+ entry.notifiedAt = nowIso;
346
+ await this.writeConfig({
347
+ ...(await this.readConfig()),
348
+ lastRunMessage: `连续失败 ${failures} 次,已通知`,
349
+ });
350
+ }
351
+ }).catch(() => { });
352
+ }
353
+ }
354
+ }
355
+ /** 从 MergePlan 构造 SyncApplyPlan(autoApply = 所有非 skip 非 conflict 项)。 */
356
+ export function buildAutoApplyPlan(plan) {
357
+ const autoApply = [];
358
+ const review = [];
359
+ const skipped = [];
360
+ for (const s of plan.sections) {
361
+ if (s.decision === 'skip') {
362
+ skipped.push(s);
363
+ continue;
364
+ }
365
+ if (s.decision === 'conflict') {
366
+ review.push(s);
367
+ continue;
368
+ }
369
+ // useRemote / keepLocal:都有 merged 数据
370
+ if (s.merged !== undefined) {
371
+ autoApply.push(s);
372
+ }
373
+ else {
374
+ skipped.push(s);
375
+ }
376
+ }
377
+ return { autoApply, review, skipped };
378
+ }
379
+ //# sourceMappingURL=autosync-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autosync-scheduler.js","sourceRoot":"","sources":["../../src/sync/autosync-scheduler.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { Importer } from '../core/importer.ts';
2
- import type { ConfigAdapter, GlobalConflictStrategy, HostContext, PlanItem, PlanItemKind, SecretScanner } from '../core/types.ts';
2
+ import type { ConfigAdapter, GlobalConflictStrategy, HostContext, ImportAnalysis, ImportPlan, ImportResult, PlanItem, PlanItemKind, SecretScanner } from '../core/types.ts';
3
3
  import type { SectionId } from '../schema/types.ts';
4
4
  import type { SnapshotFs } from './fs.ts';
5
- import type { SyncSnapshot, SyncTransport } from './transport.ts';
5
+ import type { SyncSnapshot, SyncSnapshotMeta, SyncTransport } from './transport.ts';
6
6
  import type { MsgFunc } from '../core/messages.ts';
7
7
  import type { MergePlan } from './merge.ts';
8
8
  import type { SyncApplyPlan } from './risk.ts';
9
+ import type { PlanItemProgress } from '../core/analyzer.ts';
9
10
  export interface SyncEngineOptions {
10
11
  ctx: HostContext;
11
12
  transport: SyncTransport;
@@ -66,6 +67,16 @@ export interface SyncPullReport {
66
67
  needsReview: boolean;
67
68
  message?: string;
68
69
  }
70
+ /** 一键同步预览结果(preview() 返回;临时 ZIP 由调用方持有并负责清理)。 */
71
+ export interface SyncPreviewResult {
72
+ ok: boolean;
73
+ /** 临时标准 ZIP 路径(apply-items 复用 executeImportPlan 需要;调用方清理) */
74
+ zipPath: string;
75
+ plan: ImportPlan | null;
76
+ analysis: ImportAnalysis | null;
77
+ snapshotId: string;
78
+ message?: string;
79
+ }
69
80
  /** 自动应用执行器(P2b)报告 */
70
81
  export interface ApplyReport {
71
82
  ok: boolean;
@@ -80,6 +91,26 @@ export interface ApplyReport {
80
91
  /** 来自 Importer.ImportResult 的 warnings;UI 可用于红条提示 */
81
92
  warnings: string[];
82
93
  }
94
+ /** applyItems 报告(§3.4 ApplyItemsResponse 的服务端形态) */
95
+ export interface ApplyItemsReport {
96
+ ok: boolean;
97
+ /** 实际写入的分区 id 列表(去重) */
98
+ applied: string[];
99
+ /** 未采纳的 itemId 列表 */
100
+ skipped?: string[];
101
+ /** 应用前快照 id(UI 一键回滚用;失败时仍透传以便排查) */
102
+ restoreId: string;
103
+ /** 任一失败是否整体回滚 */
104
+ rolledBack: boolean;
105
+ warnings: string[];
106
+ failed: {
107
+ itemId: string;
108
+ message?: string;
109
+ }[];
110
+ /** 透传 executeImportPlan 结果 */
111
+ result: ImportResult | null;
112
+ needsRestart?: boolean;
113
+ }
83
114
  export declare class SyncEngine {
84
115
  private readonly ctx;
85
116
  private readonly transport;
@@ -111,6 +142,15 @@ export declare class SyncEngine {
111
142
  * 绝不直接写配置、绝不执行导入;执行由上层按用户确认后走 Importer.executeImportPlan。
112
143
  */
113
144
  pull(opts?: SyncPullOptions): Promise<SyncPullReport>;
145
+ /** 列出远端已有快照(按 createdAt 升序)—— 供「选择历史快照」下拉。 */
146
+ listSnapshots(): Promise<SyncSnapshotMeta[]>;
147
+ /**
148
+ * 一键同步预览:拉取远端(最新或指定历史快照)→ 转临时 ZIP → Importer 分析出计划。
149
+ * 与 pull 的区别:临时 ZIP **不清理**(由调用方 / 会话持有,供 apply-items 复用),
150
+ * 并返回完整 plan/analysis/snapshotId 供会话登记。
151
+ * 调用方负责在会话消费或取消后清理 zipPath 所在目录。
152
+ */
153
+ preview(opts?: SyncPullOptions): Promise<SyncPreviewResult>;
114
154
  merge(opts?: {
115
155
  snapshotId?: string;
116
156
  }): Promise<MergePlan>;
@@ -121,11 +161,26 @@ export declare class SyncEngine {
121
161
  recordBaseline(snapshotId: string, sections: SyncSnapshot['sections'], nowIso?: string): Promise<void>;
122
162
  /**
123
163
  * 应用自动应用计划:写本地(走 Importer.executeImportPlan 标准路径;应用前调 backup.createSnapshot 兜底);
124
- * 任一 auto 项失败 → 整体 rollback + 把全部 auto 项移到 review 队列(写回 sync-review-queue.json);
125
- * 成功后 recordBaseline 更新祖先基线。
164
+ * 任一 auto 项失败 → 整体 rollback;成功后 recordBaseline 更新祖先基线。
126
165
  * 返回 ApplyReport;不抛错到调用方(失败返回 ok:false + rolledBack:true)。
166
+ * 不再写 review-queue(§2.3/§7.4:待审语义改由同步历史 skipped 标记表达)。
127
167
  */
128
168
  applyMergePlan(apply: SyncApplyPlan): Promise<ApplyReport>;
169
+ /**
170
+ * applyItems:按用户对差异项的逐项决策执行导入(§3.4/§5.3)。
171
+ *
172
+ * 与 applyMergePlan 的区别:applyItems 接收「会话级临时 ZIP + 子计划」,
173
+ * 直接执行(backup.createSnapshot 兜底 → importer.executeImportPlan →
174
+ * 成功 recordBaseline / 失败 rollback),不构造中间 SyncApplyPlan。
175
+ *
176
+ * @param zipPath 会话级临时标准 ZIP(由 sync/sync 生成,包含采纳项的 merged payload)
177
+ * @param subPlan 子计划(仅含采纳项的 ImportPlan;globalStrategy/pathMappings/needsRestart 沿用会话 plan)
178
+ * @param opts 执行选项(onItem 进度回调)
179
+ * @returns ApplyItemsReport(ok/applied/restoreId/rolledBack/warnings/result)
180
+ */
181
+ applyItems(zipPath: string, subPlan: ImportPlan, opts?: {
182
+ onItem?: (info: PlanItemProgress) => void;
183
+ }): Promise<ApplyItemsReport>;
129
184
  /** 散文件快照 → 标准导出 ZIP(临时目录,用完即删):buildManifest + checksums + 平铺分区 */
130
185
  private snapshotToZip;
131
186
  }