dsh-config-manager 0.1.81 → 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.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/lib/cli/index.js +12 -10
  4. package/lib/cli/restore-resume.d.ts +25 -0
  5. package/lib/cli/restore-resume.js +49 -0
  6. package/lib/client.d.ts +6 -0
  7. package/lib/client.js +519 -519
  8. package/lib/core/analyzer.d.ts +3 -41
  9. package/lib/core/analyzer.js +9 -292
  10. package/lib/core/import-planning.d.ts +110 -0
  11. package/lib/core/import-planning.js +305 -0
  12. package/lib/core/journal.d.ts +12 -0
  13. package/lib/core/journal.js +12 -0
  14. package/lib/core/messages.d.ts +17 -0
  15. package/lib/core/messages.js +37 -0
  16. package/lib/core/model-tools.js +17 -5
  17. package/lib/core/phase3-host.d.ts +21 -0
  18. package/lib/core/phase3-host.js +63 -14
  19. package/lib/core/plugin-cli.d.ts +24 -2
  20. package/lib/core/plugin-cli.js +36 -3
  21. package/lib/core/recovery-orchestrator.d.ts +17 -1
  22. package/lib/core/recovery-orchestrator.js +39 -29
  23. package/lib/core/restore-crash-child.d.ts +1 -0
  24. package/lib/core/restore-crash-child.js +77 -0
  25. package/lib/core/restore-resume.d.ts +161 -0
  26. package/lib/core/restore-resume.js +274 -0
  27. package/lib/core/restore-wal.d.ts +88 -0
  28. package/lib/core/restore-wal.js +164 -0
  29. package/lib/core/restore.d.ts +39 -1
  30. package/lib/core/restore.js +229 -13
  31. package/lib/core/rollback-ignore-rules.d.ts +14 -0
  32. package/lib/core/rollback-ignore-rules.js +54 -1
  33. package/lib/core/rollback-point.d.ts +3 -1
  34. package/lib/core/rollback-point.js +10 -1
  35. package/lib/index.d.ts +6 -252
  36. package/lib/index.js +17 -561
  37. package/lib/market/git-file-writer.d.ts +12 -3
  38. package/lib/market/git-file-writer.js +42 -6
  39. package/lib/market/messages-market.d.ts +1 -0
  40. package/lib/market/messages-market.js +2 -0
  41. package/lib/market/reader.d.ts +14 -2
  42. package/lib/market/reader.js +57 -18
  43. package/lib/routes/consult.js +1 -1
  44. package/lib/routes/import.js +1 -1
  45. package/lib/routes/market.js +1 -1
  46. package/lib/routes/me.js +1 -1
  47. package/lib/routes/prefs.js +1 -2
  48. package/lib/routes/profiles.js +1 -1
  49. package/lib/routes/recovery.js +1 -1
  50. package/lib/routes/route-support.d.ts +275 -0
  51. package/lib/routes/route-support.js +637 -0
  52. package/lib/routes/snapshots.js +40 -8
  53. package/lib/routes/sync.js +1 -1
  54. package/lib/sync/gist/gist-transport.d.ts +44 -2
  55. package/lib/sync/gist/gist-transport.js +133 -39
  56. package/lib/sync/gist/messages.d.ts +1 -0
  57. package/lib/sync/gist/messages.js +3 -0
  58. package/lib/sync/git/git-transport.d.ts +33 -0
  59. package/lib/sync/git/git-transport.js +111 -13
  60. package/lib/sync/s3/oss4-verify.d.ts +47 -0
  61. package/lib/sync/s3/oss4-verify.js +156 -0
  62. package/lib/sync/s3/s3-providers.d.ts +10 -0
  63. package/lib/sync/s3/s3-providers.js +29 -6
  64. package/lib/sync/s3/s3-transport.js +6 -1
  65. package/lib/sync/s3/sigv4.d.ts +23 -2
  66. package/lib/sync/s3/sigv4.js +19 -8
  67. package/lib/utils/git-fast-forward.d.ts +18 -0
  68. package/lib/utils/git-fast-forward.js +28 -0
  69. package/lib/utils/workdir-lock.d.ts +58 -0
  70. package/lib/utils/workdir-lock.js +147 -0
  71. package/package.json +2 -1
  72. package/src/cli/index.ts +11 -11
  73. package/src/cli/restore-resume.test.ts +120 -0
  74. package/src/cli/restore-resume.ts +76 -0
  75. package/src/client/CLIENT_DEPENDENCIES.md +8 -29
  76. package/src/core/analyzer.ts +16 -340
  77. package/src/core/import-planning.ts +356 -0
  78. package/src/core/journal.ts +13 -0
  79. package/src/core/messages.ts +37 -0
  80. package/src/core/model-tools.ts +19 -5
  81. package/src/core/phase3-host.ts +73 -14
  82. package/src/core/plugin-cli.fs.test.ts +72 -2
  83. package/src/core/plugin-cli.ts +35 -3
  84. package/src/core/recovery-orchestrator.ts +41 -19
  85. package/src/core/restore-crash-child.ts +85 -0
  86. package/src/core/restore-crash-injection.test.ts +329 -0
  87. package/src/core/restore-resume.test.ts +376 -0
  88. package/src/core/restore-resume.ts +414 -0
  89. package/src/core/restore-target-symlink.test.ts +232 -0
  90. package/src/core/restore-wal.test.ts +170 -0
  91. package/src/core/restore-wal.ts +222 -0
  92. package/src/core/restore.ts +251 -13
  93. package/src/core/rollback-ignore-rules.ts +53 -1
  94. package/src/core/rollback-point.ts +19 -2
  95. package/src/core/rollback-sensitive-exclusion.test.ts +226 -0
  96. package/src/index.facade.test.ts +49 -0
  97. package/src/index.ts +23 -647
  98. package/src/market/git-file-writer.test.ts +39 -3
  99. package/src/market/git-file-writer.ts +43 -7
  100. package/src/market/market.test.ts +31 -3
  101. package/src/market/messages-market.ts +2 -0
  102. package/src/market/reader.ts +55 -18
  103. package/src/routes/consult.ts +1 -1
  104. package/src/routes/import.ts +1 -1
  105. package/src/routes/market.ts +1 -1
  106. package/src/routes/me.ts +1 -1
  107. package/src/routes/prefs.ts +3 -2
  108. package/src/routes/profiles.ts +1 -1
  109. package/src/routes/recovery.ts +1 -1
  110. package/src/routes/route-support.ts +735 -0
  111. package/src/routes/snapshots.ts +47 -7
  112. package/src/routes/sync.ts +1 -1
  113. package/src/security/secret-scanner.test.ts +1 -1
  114. package/src/sync/gist/gist-conditional-write.test.ts +34 -15
  115. package/src/sync/gist/gist-delete-concurrency.test.ts +244 -0
  116. package/src/sync/gist/gist-overwrite-semantics.test.ts +258 -0
  117. package/src/sync/gist/gist-transport.ts +154 -44
  118. package/src/sync/gist/messages.ts +3 -0
  119. package/src/sync/git/git-transport.test.ts +68 -6
  120. package/src/sync/git/git-transport.ts +117 -14
  121. package/src/sync/s3/oss4-verify.ts +201 -0
  122. package/src/sync/s3/s3-providers.ts +32 -6
  123. package/src/sync/s3/s3-transport.oss4-server.test.ts +117 -0
  124. package/src/sync/s3/s3-transport.test.ts +44 -1
  125. package/src/sync/s3/s3-transport.ts +8 -1
  126. package/src/sync/s3/sigv4.test.ts +97 -0
  127. package/src/sync/s3/sigv4.ts +38 -9
  128. package/src/utils/git-fast-forward.test.ts +44 -0
  129. package/src/utils/git-fast-forward.ts +31 -0
  130. package/src/utils/workdir-lock.test.ts +111 -0
  131. 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: see [DEVELOPERS.md](DEVELOPERS.md) for build, testing, auto-publishing and full technical notes.
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
- > 维护者与开发者:构建、测试、自动发布与完整技术说明见 [DEVELOPERS.md](DEVELOPERS.md)。
779
+ > 维护者与开发者:从[文档总索引](docs/index.md)开始(架构、模块文档、ADR、已知技术债);构建、测试、自动发布与完整技术说明见 [DEVELOPERS.md](DEVELOPERS.md)。
780
780
 
781
781
  ---
782
782
 
package/lib/cli/index.js CHANGED
@@ -23,12 +23,13 @@ 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, restore, } from '../core/restore.js';
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
34
  import { checkWriteGates, collectVerifyResults, readRunningInstances, SAFE_MODE_REFUSAL_CODE } from './actions.js';
34
35
  import { Phase3Recovery, readSafeModeMarkerSync, safeModeMarkerPath } from '../core/phase3-host.js';
@@ -969,9 +970,12 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
969
970
  // Phase 3 SAFE MODE:存在未恢复 transaction → 拒绝 destructive(CLI 不旁路 SAFE MODE)。
970
971
  // 候选根与真实 restore 的锁目录/journal 根同源(均为 <root> = dirname(快照目录) 的那一族)。
971
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);
972
976
  const safeMsg2 = checkSafeModeBlocked(controlRoots);
973
- if (safeMsg2 !== null) {
974
- io.error(`拒绝执行:${safeMsg2}`);
977
+ if (safeMsg2 !== null && !resumeProbe.resumable) {
978
+ io.error(`拒绝执行:${safeMsg2}${resumeProbe.reason === null ? '' : `(无法自动续跑:${resumeProbe.reason})`}`);
975
979
  return 1;
976
980
  }
977
981
  // Phase 2 锁:真实 restore 是 destructive(覆盖/删除 $DSH_HOME + 卸载插件),必须成功获取
@@ -984,7 +988,6 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
984
988
  });
985
989
  try {
986
990
  // Phase 3 P0-A:CLI restore 也创建 durable journal(关闭 bypass)。
987
- const recovery = new Phase3Recovery({ dataDir: path.dirname(dataDir), packageVersion: '0.1.0', fingerprintDataDir: path.dirname(dataDir) });
988
991
  await recovery.initFingerprint().catch(() => undefined);
989
992
  const outcome = await runWithMutationLock(lock, { op: 'cli-restore', target: id }, async (lockCtx) => {
990
993
  // 第三道门(与救急台 actions.ts 的 checkWriteGates 同源、同序:SAFE MODE → 残留锁 → DSH 未运行):
@@ -992,12 +995,11 @@ export async function runCli(argv, io = defaultIo, env = process.env, deps = {})
992
995
  const running = await readRunningInstances(controlRoots);
993
996
  if (running.length > 0)
994
997
  return { kind: 'dsh-running', running };
995
- return {
996
- kind: 'restore',
997
- report: lockCtx !== null
998
- ? (await recovery.runJournaled({ operationType: 'cli-restore', lockCtx, fn: () => restore(restoreOptions) })).result
999
- : await restore(restoreOptions),
1000
- };
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 };
1001
1003
  });
1002
1004
  if (outcome.kind === 'dsh-running') {
1003
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,降级本地校验,