dsh-config-manager 0.1.80 → 0.1.82
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/lib/cli/actions.d.ts +5 -0
- package/lib/cli/actions.js +5 -0
- package/lib/cli/index.js +33 -17
- package/lib/cli/restore-resume.d.ts +25 -0
- package/lib/cli/restore-resume.js +49 -0
- package/lib/client.d.ts +6 -0
- package/lib/client.js +521 -519
- package/lib/core/analyzer.d.ts +3 -41
- package/lib/core/analyzer.js +9 -292
- package/lib/core/import-planning.d.ts +110 -0
- package/lib/core/import-planning.js +305 -0
- package/lib/core/journal.d.ts +12 -0
- package/lib/core/journal.js +12 -0
- package/lib/core/messages.d.ts +17 -0
- package/lib/core/messages.js +37 -0
- package/lib/core/model-tools.js +17 -5
- package/lib/core/phase3-host.d.ts +21 -0
- package/lib/core/phase3-host.js +63 -14
- package/lib/core/plugin-cli.d.ts +24 -2
- package/lib/core/plugin-cli.js +36 -3
- package/lib/core/recovery-orchestrator.d.ts +17 -1
- package/lib/core/recovery-orchestrator.js +39 -29
- package/lib/core/restore-crash-child.d.ts +1 -0
- package/lib/core/restore-crash-child.js +77 -0
- package/lib/core/restore-resume.d.ts +161 -0
- package/lib/core/restore-resume.js +274 -0
- package/lib/core/restore-wal.d.ts +88 -0
- package/lib/core/restore-wal.js +164 -0
- package/lib/core/restore.d.ts +39 -1
- package/lib/core/restore.js +229 -13
- package/lib/core/rollback-ignore-rules.d.ts +14 -0
- package/lib/core/rollback-ignore-rules.js +54 -1
- package/lib/core/rollback-point.d.ts +3 -1
- package/lib/core/rollback-point.js +10 -1
- package/lib/index.d.ts +6 -252
- package/lib/index.js +17 -561
- package/lib/market/git-file-writer.d.ts +12 -3
- package/lib/market/git-file-writer.js +42 -6
- package/lib/market/messages-market.d.ts +1 -0
- package/lib/market/messages-market.js +2 -0
- package/lib/market/reader.d.ts +14 -2
- package/lib/market/reader.js +57 -18
- package/lib/routes/consult.js +1 -1
- package/lib/routes/import.js +1 -1
- package/lib/routes/market.js +1 -1
- package/lib/routes/me.js +1 -1
- package/lib/routes/prefs.js +1 -2
- package/lib/routes/profiles.js +1 -1
- package/lib/routes/recovery.js +1 -1
- package/lib/routes/route-support.d.ts +275 -0
- package/lib/routes/route-support.js +637 -0
- package/lib/routes/snapshots.js +40 -8
- package/lib/routes/sync.js +17 -1
- package/lib/sync/autosync-scheduler.d.ts +8 -3
- package/lib/sync/autosync-scheduler.js +22 -9
- package/lib/sync/gist/gist-transport.d.ts +44 -2
- package/lib/sync/gist/gist-transport.js +133 -39
- package/lib/sync/gist/messages.d.ts +1 -0
- package/lib/sync/gist/messages.js +3 -0
- package/lib/sync/git/git-transport.d.ts +33 -0
- package/lib/sync/git/git-transport.js +111 -13
- package/lib/sync/s3/oss4-verify.d.ts +47 -0
- package/lib/sync/s3/oss4-verify.js +156 -0
- package/lib/sync/s3/s3-providers.d.ts +10 -0
- package/lib/sync/s3/s3-providers.js +29 -6
- package/lib/sync/s3/s3-transport.js +6 -1
- package/lib/sync/s3/sigv4.d.ts +23 -2
- package/lib/sync/s3/sigv4.js +19 -8
- package/lib/sync/sync-engine.d.ts +13 -0
- package/lib/sync/sync-engine.js +9 -1
- package/lib/sync/sync-session.d.ts +5 -0
- package/lib/utils/git-fast-forward.d.ts +18 -0
- package/lib/utils/git-fast-forward.js +28 -0
- package/lib/utils/workdir-lock.d.ts +58 -0
- package/lib/utils/workdir-lock.js +147 -0
- package/package.json +2 -1
- package/src/cli/actions.ts +6 -0
- package/src/cli/index-guards.test.ts +62 -0
- package/src/cli/index.ts +30 -20
- package/src/cli/restore-resume.test.ts +120 -0
- package/src/cli/restore-resume.ts +76 -0
- package/src/client/CLIENT_DEPENDENCIES.md +8 -29
- package/src/client/sync/merge-view.ts +2 -0
- package/src/core/analyzer.ts +16 -340
- package/src/core/import-planning.ts +356 -0
- package/src/core/journal.ts +13 -0
- package/src/core/messages.ts +37 -0
- package/src/core/model-tools.ts +19 -5
- package/src/core/phase3-host.ts +73 -14
- package/src/core/plugin-cli.fs.test.ts +72 -2
- package/src/core/plugin-cli.ts +35 -3
- package/src/core/recovery-orchestrator.ts +41 -19
- package/src/core/restore-crash-child.ts +85 -0
- package/src/core/restore-crash-injection.test.ts +329 -0
- package/src/core/restore-resume.test.ts +376 -0
- package/src/core/restore-resume.ts +414 -0
- package/src/core/restore-target-symlink.test.ts +232 -0
- package/src/core/restore-wal.test.ts +170 -0
- package/src/core/restore-wal.ts +222 -0
- package/src/core/restore.ts +251 -13
- package/src/core/rollback-ignore-rules.ts +53 -1
- package/src/core/rollback-point.ts +19 -2
- package/src/core/rollback-sensitive-exclusion.test.ts +226 -0
- package/src/index.facade.test.ts +49 -0
- package/src/index.ts +23 -647
- package/src/market/git-file-writer.test.ts +39 -3
- package/src/market/git-file-writer.ts +43 -7
- package/src/market/market.test.ts +31 -3
- package/src/market/messages-market.ts +2 -0
- package/src/market/reader.ts +55 -18
- package/src/routes/consult.ts +1 -1
- package/src/routes/import.ts +1 -1
- package/src/routes/market.ts +1 -1
- package/src/routes/me.ts +1 -1
- package/src/routes/prefs.ts +3 -2
- package/src/routes/profiles.ts +1 -1
- package/src/routes/recovery.ts +1 -1
- package/src/routes/route-support.ts +735 -0
- package/src/routes/snapshots.ts +47 -7
- package/src/routes/sync.ts +17 -1
- package/src/security/secret-scanner.test.ts +1 -1
- package/src/sync/autosync-scheduler.test.ts +62 -0
- package/src/sync/autosync-scheduler.ts +21 -8
- package/src/sync/gist/gist-conditional-write.test.ts +34 -15
- package/src/sync/gist/gist-delete-concurrency.test.ts +244 -0
- package/src/sync/gist/gist-overwrite-semantics.test.ts +258 -0
- package/src/sync/gist/gist-transport.ts +154 -44
- package/src/sync/gist/messages.ts +3 -0
- package/src/sync/git/git-transport.test.ts +68 -6
- package/src/sync/git/git-transport.ts +117 -14
- package/src/sync/s3/oss4-verify.ts +201 -0
- package/src/sync/s3/s3-providers.ts +32 -6
- package/src/sync/s3/s3-transport.oss4-server.test.ts +117 -0
- package/src/sync/s3/s3-transport.test.ts +44 -1
- package/src/sync/s3/s3-transport.ts +8 -1
- package/src/sync/s3/sigv4.test.ts +97 -0
- package/src/sync/s3/sigv4.ts +38 -9
- package/src/sync/sync-engine.test.ts +64 -0
- package/src/sync/sync-engine.ts +22 -1
- package/src/sync/sync-session.ts +5 -0
- package/src/utils/git-fast-forward.test.ts +44 -0
- package/src/utils/git-fast-forward.ts +31 -0
- package/src/utils/workdir-lock.test.ts +111 -0
- package/src/utils/workdir-lock.ts +162 -0
package/README.md
CHANGED
|
@@ -778,7 +778,7 @@ Found a bug, a misaligned panel, a button that does nothing — or just have an
|
|
|
778
778
|
read per layer and written back to their own layer, and skills are collected through the shell's `skills` service.
|
|
779
779
|
All three shipped with unit tests and route / source-level guards.
|
|
780
780
|
|
|
781
|
-
> Maintainers & developers:
|
|
781
|
+
> Maintainers & developers: start at the [documentation index](docs/index.md) — architecture, module docs, ADRs and known tech debt; [DEVELOPERS.md](DEVELOPERS.md) covers build, testing, auto-publishing and full technical notes.
|
|
782
782
|
|
|
783
783
|
---
|
|
784
784
|
|
package/README.zh-CN.md
CHANGED
|
@@ -776,7 +776,7 @@ $env:HTTPS_PROXY='http://127.0.0.1:7897'; dsh web
|
|
|
776
776
|
全量预览 27.6 s)。③ 修 issue #71「外壳里配好的 MCP 与 Skills 备份不到」:patch 行按层读取、写回原层,技能改经
|
|
777
777
|
外壳的 `skills` 服务收编。三个 PR 均带单测与路由 / 源码级守卫。
|
|
778
778
|
|
|
779
|
-
>
|
|
779
|
+
> 维护者与开发者:从[文档总索引](docs/index.md)开始(架构、模块文档、ADR、已知技术债);构建、测试、自动发布与完整技术说明见 [DEVELOPERS.md](DEVELOPERS.md)。
|
|
780
780
|
|
|
781
781
|
---
|
|
782
782
|
|
package/lib/cli/actions.d.ts
CHANGED
|
@@ -142,6 +142,11 @@ export interface RescueStatus {
|
|
|
142
142
|
/** 首页聚合:每一项各自 try/catch,缺一项不影响其余(但失败必须在 errors 里可见)。 */
|
|
143
143
|
export declare function readRescueStatus(paths: RescuePaths, now?: () => Date): Promise<RescueStatus>;
|
|
144
144
|
export type WriteRefusalCode = 'safe-mode' | 'stale-lock' | 'dsh-running' | 'nothing-to-do' | 'io-error';
|
|
145
|
+
/**
|
|
146
|
+
* SAFE MODE 拒因码的**单点常量**:`src/cli/index.ts` 的源码守卫(P0-11-07)禁止
|
|
147
|
+
* CLI 自带 `'safe-mode'` 字面量,判据必须复用本常量(否则改一处漏一处)。
|
|
148
|
+
*/
|
|
149
|
+
export declare const SAFE_MODE_REFUSAL_CODE: WriteRefusalCode;
|
|
145
150
|
export interface WriteGateRefusal {
|
|
146
151
|
ok: false;
|
|
147
152
|
code: WriteRefusalCode;
|
package/lib/cli/actions.js
CHANGED
|
@@ -279,6 +279,11 @@ export async function readRescueStatus(paths, now = () => new Date()) {
|
|
|
279
279
|
function errorText(error) {
|
|
280
280
|
return error instanceof Error ? error.message : String(error);
|
|
281
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* SAFE MODE 拒因码的**单点常量**:`src/cli/index.ts` 的源码守卫(P0-11-07)禁止
|
|
284
|
+
* CLI 自带 `'safe-mode'` 字面量,判据必须复用本常量(否则改一处漏一处)。
|
|
285
|
+
*/
|
|
286
|
+
export const SAFE_MODE_REFUSAL_CODE = 'safe-mode';
|
|
282
287
|
/**
|
|
283
288
|
* 写动作的前置门(与 CLI `sessions repair --fix` 同一判据)。
|
|
284
289
|
*
|
package/lib/cli/index.js
CHANGED
|
@@ -23,14 +23,15 @@ import { pathToFileURL } from 'node:url';
|
|
|
23
23
|
import { execFile } from 'node:child_process';
|
|
24
24
|
import { createInterface } from 'node:readline/promises';
|
|
25
25
|
import { stdin as processStdin, stdout as processStdout } from 'node:process';
|
|
26
|
-
import { listSnapshots, planRestore,
|
|
26
|
+
import { listSnapshots, planRestore, } from '../core/restore.js';
|
|
27
27
|
import { REINSTALL_ITEMS, buildReinstallPlan, isWindows, detectInstalledDshVersion, writeReinstallRecoveryPoint, } from '../core/reinstall.js';
|
|
28
28
|
import { EnvironmentLockManager, runWithMutationLock, EnvironmentLockUnavailableError, OWNERSHIP_FILE } from '../utils/env-lock.js';
|
|
29
29
|
import { runSessionsRepair } from './sessions-repair.js';
|
|
30
30
|
import { runSessionsInspect } from './sessions-inspect.js';
|
|
31
31
|
import { runImportSource } from './import-source.js';
|
|
32
|
+
import { probeCliRestoreResume, runCliRestore } from './restore-resume.js';
|
|
32
33
|
import { renderCommandHelp, renderOverview } from './help.js';
|
|
33
|
-
import { collectVerifyResults, readRunningInstances } from './actions.js';
|
|
34
|
+
import { checkWriteGates, collectVerifyResults, readRunningInstances, SAFE_MODE_REFUSAL_CODE } from './actions.js';
|
|
34
35
|
import { Phase3Recovery, readSafeModeMarkerSync, safeModeMarkerPath } from '../core/phase3-host.js';
|
|
35
36
|
import { verifyBackupZip, } from '../core/backup-verify.js';
|
|
36
37
|
import { collectBackupEntries, buildSectionFlags, parseSectionsArg, OFFLINE_UNAVAILABLE_SECTIONS, } from '../core/backup-plan.js';
|
|
@@ -853,19 +854,33 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
|
|
|
853
854
|
}, io, env);
|
|
854
855
|
}
|
|
855
856
|
if (options.command === 'sessions') {
|
|
856
|
-
//
|
|
857
|
+
// 离线修复:只读写会话目录,默认 dry-run(零写入)。list/doctor 纯只读,连「DSH 在跑」都不用管。
|
|
858
|
+
// **--fix 走与救急台完全相同的写入门**(actions.ts 的 checkWriteGates,顺序 SAFE MODE → 残留环境锁 → DSH 未运行):
|
|
859
|
+
// 此前这里只查「DSH 在跑」,于是 SAFE MODE 激活 / 残留环境锁时 CLI 仍能改写会话字节 —— 与救急台判据分叉(审计 P1)。
|
|
857
860
|
const home = options.home ?? resolveDshHome(env);
|
|
858
861
|
const action = options.positionals[0] ?? 'repair';
|
|
859
862
|
// T6:list / doctor 是**只读**动作 —— 不检查「DSH 是否在跑」,因为读永远不会伤害运行中的实例。
|
|
860
863
|
if (action === 'list' || action === 'doctor') {
|
|
861
864
|
return runSessionsInspect({ home, action: action === 'doctor' ? 'doctor' : 'list', json: options.json }, io);
|
|
862
865
|
}
|
|
863
|
-
// T6:repair(含 --fix/--apply
|
|
866
|
+
// T6:repair(含 --fix/--apply)先跑安全修复计划;写操作前三道门与救急台逐条同源。
|
|
864
867
|
if (options.fix === true) {
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
868
|
+
const controlOpts = { dataRoot: options.dataRoot, dataDir: options.dataDir };
|
|
869
|
+
const gate = await checkWriteGates(resolveControlRoots(controlOpts, env), {
|
|
870
|
+
needsDshStopped: true,
|
|
871
|
+
locksDir: resolveRecoverLocksDir(controlOpts, env),
|
|
872
|
+
});
|
|
873
|
+
if (!gate.ok) {
|
|
874
|
+
io.error('拒绝执行:' + gate.reason);
|
|
875
|
+
if (gate.code === 'dsh-running') {
|
|
876
|
+
io.error('DSH running: close DSH before repairing session bytes.');
|
|
877
|
+
}
|
|
878
|
+
else if (gate.code === SAFE_MODE_REFUSAL_CODE) {
|
|
879
|
+
io.error('SAFE MODE is active: resolve the pending transaction before writing session bytes.');
|
|
880
|
+
}
|
|
881
|
+
else if (gate.code === 'stale-lock') {
|
|
882
|
+
io.error('A stale environment lock is present: run "dsh-config-manager recover-stale-lock" first.');
|
|
883
|
+
}
|
|
869
884
|
return 1;
|
|
870
885
|
}
|
|
871
886
|
}
|
|
@@ -955,9 +970,12 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
|
|
|
955
970
|
// Phase 3 SAFE MODE:存在未恢复 transaction → 拒绝 destructive(CLI 不旁路 SAFE MODE)。
|
|
956
971
|
// 候选根与真实 restore 的锁目录/journal 根同源(均为 <root> = dirname(快照目录) 的那一族)。
|
|
957
972
|
const controlRoots = resolveControlRoots({ dataRoot: options.dataRoot, dataDir: options.dataDir }, env);
|
|
973
|
+
// Phase 3 P0-A:CLI restore 也建 durable journal;顺带做续跑探测(只读)——「同一个恢复崩在中途」正是 SAFE MODE 在等的结论。
|
|
974
|
+
const recovery = new Phase3Recovery({ dataDir: path.dirname(dataDir), packageVersion: '0.1.0', fingerprintDataDir: path.dirname(dataDir) });
|
|
975
|
+
const resumeProbe = await probeCliRestoreResume(recovery, restoreOptions);
|
|
958
976
|
const safeMsg2 = checkSafeModeBlocked(controlRoots);
|
|
959
|
-
if (safeMsg2 !== null) {
|
|
960
|
-
io.error(`拒绝执行:${safeMsg2}`);
|
|
977
|
+
if (safeMsg2 !== null && !resumeProbe.resumable) {
|
|
978
|
+
io.error(`拒绝执行:${safeMsg2}${resumeProbe.reason === null ? '' : `(无法自动续跑:${resumeProbe.reason})`}`);
|
|
961
979
|
return 1;
|
|
962
980
|
}
|
|
963
981
|
// Phase 2 锁:真实 restore 是 destructive(覆盖/删除 $DSH_HOME + 卸载插件),必须成功获取
|
|
@@ -970,7 +988,6 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
|
|
|
970
988
|
});
|
|
971
989
|
try {
|
|
972
990
|
// Phase 3 P0-A:CLI restore 也创建 durable journal(关闭 bypass)。
|
|
973
|
-
const recovery = new Phase3Recovery({ dataDir: path.dirname(dataDir), packageVersion: '0.1.0', fingerprintDataDir: path.dirname(dataDir) });
|
|
974
991
|
await recovery.initFingerprint().catch(() => undefined);
|
|
975
992
|
const outcome = await runWithMutationLock(lock, { op: 'cli-restore', target: id }, async (lockCtx) => {
|
|
976
993
|
// 第三道门(与救急台 actions.ts 的 checkWriteGates 同源、同序:SAFE MODE → 残留锁 → DSH 未运行):
|
|
@@ -978,12 +995,11 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
|
|
|
978
995
|
const running = await readRunningInstances(controlRoots);
|
|
979
996
|
if (running.length > 0)
|
|
980
997
|
return { kind: 'dsh-running', running };
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
};
|
|
998
|
+
// 锁内统一入口:能续跑 → resumeJournaled(接着做),否则 → runJournaled(照常新建)
|
|
999
|
+
const out = await runCliRestore(recovery, restoreOptions, lockCtx);
|
|
1000
|
+
if (out.resumed !== null)
|
|
1001
|
+
io.log(`自动续跑:已完成 ${String(out.resumed.done)} 项、待做 ${String(out.resumed.todo)} 项;SAFE MODE ${out.safeModeCleared === true ? '已解除' : '仍保持'}`);
|
|
1002
|
+
return { kind: 'restore', report: out.report };
|
|
987
1003
|
});
|
|
988
1004
|
if (outcome.kind === 'dsh-running') {
|
|
989
1005
|
io.error('检测到 DSH 正在运行(' + outcome.running.map((i) => i.name + ' pid=' + String(i.pid)).join('、')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RestoreOptions, RestoreReport } from '../core/restore.ts';
|
|
2
|
+
import type { Phase3Recovery } from '../core/phase3-host.ts';
|
|
3
|
+
import type { MutationLockContext } from '../utils/env-lock.ts';
|
|
4
|
+
/** 锁前探测结果(只用于「能否放过 SAFE MODE 门」与给用户的提示)。 */
|
|
5
|
+
export interface CliRestoreResumeProbe {
|
|
6
|
+
resumable: boolean;
|
|
7
|
+
/** 不可续跑时的人话原因(resumable=false 且没有可续记录时也为 null 之外的短句) */
|
|
8
|
+
reason: string | null;
|
|
9
|
+
done: number;
|
|
10
|
+
todo: number;
|
|
11
|
+
}
|
|
12
|
+
/** 锁前探测:只读,不写 journal、不碰目标文件。 */
|
|
13
|
+
export declare function probeCliRestoreResume(recovery: Phase3Recovery, restoreOpts: RestoreOptions): Promise<CliRestoreResumeProbe>;
|
|
14
|
+
/** 锁内执行:能续跑就接着做,否则照常新建 journal。 */
|
|
15
|
+
export interface CliRestoreRunOutcome {
|
|
16
|
+
report: RestoreReport;
|
|
17
|
+
resumed: {
|
|
18
|
+
operationId: string;
|
|
19
|
+
done: number;
|
|
20
|
+
todo: number;
|
|
21
|
+
} | null;
|
|
22
|
+
/** 续跑成功且 SAFE MODE 已按判据解除(null = 本次不是续跑,未尝试) */
|
|
23
|
+
safeModeCleared: boolean | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function runCliRestore(recovery: Phase3Recovery, restoreOpts: RestoreOptions, lockCtx: MutationLockContext | null): Promise<CliRestoreRunOutcome>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 离线 CLI 恢复的**自动续跑**薄封装(判定与执行都在 core,这里只做「CLI 口子」的三件事)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独一层:
|
|
5
|
+
* 1. `src/cli/index.ts` 卡在 1461 行棘轮上限,装配代码必须外提;
|
|
6
|
+
* 2. 续跑有三处必须与宿主/GUI 保持同一口径 —— 锁前探测(决定能否放过 SAFE MODE 门)、
|
|
7
|
+
* 锁内再判一次(决定 runJournaled 还是 resumeJournaled)、成功后按**同一个**
|
|
8
|
+
* resolveSafeModeMarker 解除 SAFE MODE。口径写在这里就不必在两处各写一遍。
|
|
9
|
+
*
|
|
10
|
+
* 注意:CLI 只续跑 `cli-restore` 自己创建的 journal。别的入口(GUI restore / model-restore)
|
|
11
|
+
* 崩掉的记录在这里仍是「需要恢复面板处理」的事故 —— 三者的执行器与参数来源不同,
|
|
12
|
+
* 跨入口续跑没有证据支撑(详见 docs/decisions/0004)。
|
|
13
|
+
*/
|
|
14
|
+
import { resolveSafeModeMarker } from '../core/recovery-orchestrator.js';
|
|
15
|
+
import { probeRestoreResume, runRestoreWithResume } from '../core/restore-resume.js';
|
|
16
|
+
/** 锁前探测:只读,不写 journal、不碰目标文件。 */
|
|
17
|
+
export async function probeCliRestoreResume(recovery, restoreOpts) {
|
|
18
|
+
const judged = await probeRestoreResume({
|
|
19
|
+
store: recovery.store,
|
|
20
|
+
restoreOpts,
|
|
21
|
+
allowedOperationTypes: ['cli-restore'],
|
|
22
|
+
});
|
|
23
|
+
if (judged.ok) {
|
|
24
|
+
return { resumable: true, reason: null, done: judged.resume.done, todo: judged.resume.todo };
|
|
25
|
+
}
|
|
26
|
+
// no-active-journal = 本来就没有事故,不算「拒绝」
|
|
27
|
+
return {
|
|
28
|
+
resumable: false,
|
|
29
|
+
reason: judged.code === 'no-active-journal' ? null : judged.detail,
|
|
30
|
+
done: 0,
|
|
31
|
+
todo: 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export async function runCliRestore(recovery, restoreOpts, lockCtx) {
|
|
35
|
+
const out = await runRestoreWithResume({
|
|
36
|
+
store: recovery.store,
|
|
37
|
+
restoreOpts,
|
|
38
|
+
operationType: 'cli-restore',
|
|
39
|
+
allowedOperationTypes: ['cli-restore'],
|
|
40
|
+
lockCtx,
|
|
41
|
+
phase3: recovery,
|
|
42
|
+
});
|
|
43
|
+
if (out.resumed === null)
|
|
44
|
+
return { report: out.report, resumed: null, safeModeCleared: null };
|
|
45
|
+
// 续跑成功:按**同一个**判据解除 SAFE MODE(还有别的未解决 incident 时保持,返回 false)
|
|
46
|
+
const cleared = await resolveSafeModeMarker(recovery.store, () => recovery.clearSafeMode(), false);
|
|
47
|
+
return { report: out.report, resumed: out.resumed, safeModeCleared: cleared };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=restore-resume.js.map
|
package/lib/client.d.ts
CHANGED
|
@@ -669,6 +669,12 @@ interface RestoreReport {
|
|
|
669
669
|
}[];
|
|
670
670
|
/** 跳过项(无动作 / 离线无法处理) */
|
|
671
671
|
skipped: string[];
|
|
672
|
+
/**
|
|
673
|
+
* 本次执行真正写入的 pre-restore 反悔副本目录(`<snapshotDir>/pre-restore/<attemptDir>/`)。
|
|
674
|
+
* 每次 restore() 新建一个 attempt 目录(D5)⇒ 崩溃后重跑**不会覆盖**上一次的反悔副本;
|
|
675
|
+
* 没有任何文件需要备份时缺省(不建空目录)。
|
|
676
|
+
*/
|
|
677
|
+
preRestoreDir?: string;
|
|
672
678
|
/**
|
|
673
679
|
* 幽灵会话(F5 失效归档清理):快照记录的 sessions 分区会话在磁盘 sessions 目录
|
|
674
680
|
* 已无任何文件(备份有记录、磁盘无文件)。DSH 官方无归档会话 API,降级本地校验,
|