dsh-config-manager 0.1.0
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 +216 -0
- package/README.zh-CN.md +218 -0
- package/cordis.patch.yml +45 -0
- package/dsh.bundle.patch +21 -0
- package/dsh.client +39 -0
- package/lib/adapters/agent-presets.d.ts +13 -0
- package/lib/adapters/agent-presets.js +14 -0
- package/lib/adapters/agent-presets.js.map +1 -0
- package/lib/adapters/credentials.d.ts +36 -0
- package/lib/adapters/credentials.js +121 -0
- package/lib/adapters/credentials.js.map +1 -0
- package/lib/adapters/file-collection.d.ts +14 -0
- package/lib/adapters/file-collection.js +102 -0
- package/lib/adapters/file-collection.js.map +1 -0
- package/lib/adapters/index.d.ts +38 -0
- package/lib/adapters/index.js +46 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/mcp.d.ts +27 -0
- package/lib/adapters/mcp.js +175 -0
- package/lib/adapters/mcp.js.map +1 -0
- package/lib/adapters/plugin-files.d.ts +15 -0
- package/lib/adapters/plugin-files.js +91 -0
- package/lib/adapters/plugin-files.js.map +1 -0
- package/lib/adapters/plugins.d.ts +13 -0
- package/lib/adapters/plugins.js +177 -0
- package/lib/adapters/plugins.js.map +1 -0
- package/lib/adapters/prompts.d.ts +29 -0
- package/lib/adapters/prompts.js +228 -0
- package/lib/adapters/prompts.js.map +1 -0
- package/lib/adapters/providers.d.ts +33 -0
- package/lib/adapters/providers.js +143 -0
- package/lib/adapters/providers.js.map +1 -0
- package/lib/adapters/sessions.d.ts +14 -0
- package/lib/adapters/sessions.js +15 -0
- package/lib/adapters/sessions.js.map +1 -0
- package/lib/adapters/settings.d.ts +23 -0
- package/lib/adapters/settings.js +142 -0
- package/lib/adapters/settings.js.map +1 -0
- package/lib/adapters/skills.d.ts +13 -0
- package/lib/adapters/skills.js +14 -0
- package/lib/adapters/skills.js.map +1 -0
- package/lib/adapters/test-helpers.d.ts +99 -0
- package/lib/adapters/test-helpers.js +192 -0
- package/lib/adapters/test-helpers.js.map +1 -0
- package/lib/adapters/ui.d.ts +26 -0
- package/lib/adapters/ui.js +88 -0
- package/lib/adapters/ui.js.map +1 -0
- package/lib/adapters/workspaces.d.ts +12 -0
- package/lib/adapters/workspaces.js +92 -0
- package/lib/adapters/workspaces.js.map +1 -0
- package/lib/client.d.ts +377 -0
- package/lib/client.js +2598 -0
- package/lib/client.js.map +1 -0
- package/lib/core/analyzer.d.ts +35 -0
- package/lib/core/analyzer.js +563 -0
- package/lib/core/analyzer.js.map +1 -0
- package/lib/core/backup.d.ts +29 -0
- package/lib/core/backup.js +184 -0
- package/lib/core/backup.js.map +1 -0
- package/lib/core/exporter.d.ts +37 -0
- package/lib/core/exporter.js +222 -0
- package/lib/core/exporter.js.map +1 -0
- package/lib/core/importer.d.ts +42 -0
- package/lib/core/importer.js +25 -0
- package/lib/core/importer.js.map +1 -0
- package/lib/core/index.d.ts +12 -0
- package/lib/core/index.js +11 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/rollback.d.ts +9 -0
- package/lib/core/rollback.js +136 -0
- package/lib/core/rollback.js.map +1 -0
- package/lib/core/types.d.ts +331 -0
- package/lib/core/types.js +18 -0
- package/lib/core/types.js.map +1 -0
- package/lib/core/validator.d.ts +18 -0
- package/lib/core/validator.js +77 -0
- package/lib/core/validator.js.map +1 -0
- package/lib/index.d.ts +51 -0
- package/lib/index.js +875 -0
- package/lib/index.js.map +1 -0
- package/lib/migrations/index.d.ts +29 -0
- package/lib/migrations/index.js +56 -0
- package/lib/migrations/index.js.map +1 -0
- package/lib/migrations/v1-to-v2.d.ts +9 -0
- package/lib/migrations/v1-to-v2.js +10 -0
- package/lib/migrations/v1-to-v2.js.map +1 -0
- package/lib/profiles/index.d.ts +4 -0
- package/lib/profiles/index.js +5 -0
- package/lib/profiles/index.js.map +1 -0
- package/lib/profiles/profile-manager.d.ts +102 -0
- package/lib/profiles/profile-manager.js +522 -0
- package/lib/profiles/profile-manager.js.map +1 -0
- package/lib/schema/config.d.ts +28 -0
- package/lib/schema/config.js +136 -0
- package/lib/schema/config.js.map +1 -0
- package/lib/schema/manifest.d.ts +28 -0
- package/lib/schema/manifest.js +109 -0
- package/lib/schema/manifest.js.map +1 -0
- package/lib/schema/types.d.ts +169 -0
- package/lib/schema/types.js +8 -0
- package/lib/schema/types.js.map +1 -0
- package/lib/schema/versions.d.ts +27 -0
- package/lib/schema/versions.js +50 -0
- package/lib/schema/versions.js.map +1 -0
- package/lib/security/encryption.d.ts +41 -0
- package/lib/security/encryption.js +152 -0
- package/lib/security/encryption.js.map +1 -0
- package/lib/security/index.d.ts +16 -0
- package/lib/security/index.js +17 -0
- package/lib/security/index.js.map +1 -0
- package/lib/security/integrity.d.ts +30 -0
- package/lib/security/integrity.js +78 -0
- package/lib/security/integrity.js.map +1 -0
- package/lib/security/redaction.d.ts +26 -0
- package/lib/security/redaction.js +95 -0
- package/lib/security/redaction.js.map +1 -0
- package/lib/security/secret-scanner.d.ts +48 -0
- package/lib/security/secret-scanner.js +253 -0
- package/lib/security/secret-scanner.js.map +1 -0
- package/lib/security/zip-security.d.ts +27 -0
- package/lib/security/zip-security.js +154 -0
- package/lib/security/zip-security.js.map +1 -0
- package/lib/ui/conflict-view.d.ts +28 -0
- package/lib/ui/conflict-view.js +49 -0
- package/lib/ui/conflict-view.js.map +1 -0
- package/lib/ui/errors.d.ts +17 -0
- package/lib/ui/errors.js +76 -0
- package/lib/ui/errors.js.map +1 -0
- package/lib/ui/export-flow.d.ts +62 -0
- package/lib/ui/export-flow.js +79 -0
- package/lib/ui/export-flow.js.map +1 -0
- package/lib/ui/import-wizard.d.ts +67 -0
- package/lib/ui/import-wizard.js +169 -0
- package/lib/ui/import-wizard.js.map +1 -0
- package/lib/ui/path-mapping.d.ts +38 -0
- package/lib/ui/path-mapping.js +75 -0
- package/lib/ui/path-mapping.js.map +1 -0
- package/lib/ui/progress.d.ts +27 -0
- package/lib/ui/progress.js +76 -0
- package/lib/ui/progress.js.map +1 -0
- package/lib/ui/report.d.ts +21 -0
- package/lib/ui/report.js +146 -0
- package/lib/ui/report.js.map +1 -0
- package/lib/ui/test-helpers.d.ts +62 -0
- package/lib/ui/test-helpers.js +147 -0
- package/lib/ui/test-helpers.js.map +1 -0
- package/lib/ui/types.d.ts +129 -0
- package/lib/ui/types.js +21 -0
- package/lib/ui/types.js.map +1 -0
- package/lib/utils/hashing.d.ts +17 -0
- package/lib/utils/hashing.js +41 -0
- package/lib/utils/hashing.js.map +1 -0
- package/lib/utils/json.d.ts +24 -0
- package/lib/utils/json.js +76 -0
- package/lib/utils/json.js.map +1 -0
- package/lib/utils/logger.d.ts +39 -0
- package/lib/utils/logger.js +82 -0
- package/lib/utils/logger.js.map +1 -0
- package/lib/utils/paths.d.ts +31 -0
- package/lib/utils/paths.js +132 -0
- package/lib/utils/paths.js.map +1 -0
- package/lib/utils/zip.d.ts +54 -0
- package/lib/utils/zip.js +285 -0
- package/lib/utils/zip.js.map +1 -0
- package/package.json +109 -0
- package/src/adapters/agent-presets.ts +14 -0
- package/src/adapters/credentials.test.ts +90 -0
- package/src/adapters/credentials.ts +141 -0
- package/src/adapters/file-collection.ts +110 -0
- package/src/adapters/files.test.ts +113 -0
- package/src/adapters/index.ts +69 -0
- package/src/adapters/mcp.test.ts +96 -0
- package/src/adapters/mcp.ts +181 -0
- package/src/adapters/plugin-files.ts +96 -0
- package/src/adapters/plugins.test.ts +72 -0
- package/src/adapters/plugins.ts +176 -0
- package/src/adapters/prompts.test.ts +110 -0
- package/src/adapters/prompts.ts +226 -0
- package/src/adapters/providers.test.ts +125 -0
- package/src/adapters/providers.ts +161 -0
- package/src/adapters/roundtrip.test.ts +188 -0
- package/src/adapters/sessions.ts +15 -0
- package/src/adapters/settings.test.ts +93 -0
- package/src/adapters/settings.ts +165 -0
- package/src/adapters/skills.ts +14 -0
- package/src/adapters/test-helpers.ts +207 -0
- package/src/adapters/ui.test.ts +56 -0
- package/src/adapters/ui.ts +106 -0
- package/src/adapters/workspaces.test.ts +79 -0
- package/src/adapters/workspaces.ts +95 -0
- package/src/client/CLIENT_DEPENDENCIES.md +101 -0
- package/src/client/ConfigManagerSection.tsx +60 -0
- package/src/client/api.ts +270 -0
- package/src/client/client-types.ts +27 -0
- package/src/client/common/ErrorBanner.tsx +68 -0
- package/src/client/common/ProgressBar.tsx +49 -0
- package/src/client/common/ReportView.tsx +117 -0
- package/src/client/common/ui.tsx +165 -0
- package/src/client/config-manager.module.css +759 -0
- package/src/client/css-modules.d.ts +8 -0
- package/src/client/export/ExportView.tsx +222 -0
- package/src/client/import/ConflictList.tsx +72 -0
- package/src/client/import/ImportWizardView.tsx +406 -0
- package/src/client/import/PathMappingForm.tsx +77 -0
- package/src/client/index.ts +65 -0
- package/src/client/locales.ts +182 -0
- package/src/core/analyzer.ts +631 -0
- package/src/core/backup.ts +212 -0
- package/src/core/exporter.ts +246 -0
- package/src/core/importer.ts +66 -0
- package/src/core/index.ts +39 -0
- package/src/core/rollback.ts +140 -0
- package/src/core/smoke.test.ts +717 -0
- package/src/core/types.ts +343 -0
- package/src/core/validator.ts +84 -0
- package/src/index.ts +942 -0
- package/src/migrations/index.ts +71 -0
- package/src/migrations/v1-to-v2.ts +17 -0
- package/src/profiles/index.ts +8 -0
- package/src/profiles/profile-manager.test.ts +280 -0
- package/src/profiles/profile-manager.ts +606 -0
- package/src/schema/config.ts +137 -0
- package/src/schema/manifest.ts +124 -0
- package/src/schema/types.ts +179 -0
- package/src/schema/versions.ts +55 -0
- package/src/security/encryption.ts +180 -0
- package/src/security/index.ts +16 -0
- package/src/security/integrity.ts +102 -0
- package/src/security/redaction.ts +101 -0
- package/src/security/secret-scanner.ts +275 -0
- package/src/security/security.test.ts +796 -0
- package/src/security/zip-security.ts +180 -0
- package/src/ui/conflict-view.test.ts +62 -0
- package/src/ui/conflict-view.ts +65 -0
- package/src/ui/errors.test.ts +44 -0
- package/src/ui/errors.ts +95 -0
- package/src/ui/export-flow.test.ts +75 -0
- package/src/ui/export-flow.ts +128 -0
- package/src/ui/import-wizard.test.ts +128 -0
- package/src/ui/import-wizard.ts +208 -0
- package/src/ui/path-mapping.test.ts +67 -0
- package/src/ui/path-mapping.ts +103 -0
- package/src/ui/progress.test.ts +42 -0
- package/src/ui/progress.ts +90 -0
- package/src/ui/report.test.ts +93 -0
- package/src/ui/report.ts +140 -0
- package/src/ui/test-helpers.ts +177 -0
- package/src/ui/types.ts +193 -0
- package/src/utils/hashing.ts +53 -0
- package/src/utils/json.ts +77 -0
- package/src/utils/logger.ts +114 -0
- package/src/utils/paths.ts +122 -0
- package/src/utils/zip.ts +332 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 核心引擎契约类型(对齐 Docs/design/architecture.md §13.2/§13.3/§13.5)。
|
|
3
|
+
*
|
|
4
|
+
* 关键解耦原则:核心引擎只依赖这里的接口,绝不 import DSH 运行时包。
|
|
5
|
+
* HostContext 是 m3 定义、m5 实现的 DSH Service 门面(研究报告 §3.2 的叶子方法最小集),
|
|
6
|
+
* 测试用内存 mock 即可驱动完整导出→导入往返。
|
|
7
|
+
*/
|
|
8
|
+
import type { EncryptionInfo, Manifest, SectionId, WorkspaceRecord } from '../schema/types.ts';
|
|
9
|
+
import type { Logger } from '../utils/logger.ts';
|
|
10
|
+
|
|
11
|
+
/* ---------------- 导出选项与分区产出 ---------------- */
|
|
12
|
+
|
|
13
|
+
export interface ExportOptions {
|
|
14
|
+
/** 是否包含真实秘密(必须配合 encryption 提供者;缺省 false = 只导状态) */
|
|
15
|
+
includeSecrets: boolean;
|
|
16
|
+
/** 仅导出指定分区(缺省 = 全部默认包含分区) */
|
|
17
|
+
only?: SectionId[];
|
|
18
|
+
/** 导出文件路径(缺省自动生成 dsh-config-<date>.zip) */
|
|
19
|
+
outPath?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** adapter.export() 的产出:数据 + 文件 + 报告计数 + 告警 */
|
|
23
|
+
export interface ExportSection<T = unknown> {
|
|
24
|
+
sectionId: SectionId;
|
|
25
|
+
data: T;
|
|
26
|
+
/** 文件类分区(skills/agentPresets/pluginFiles/sessions)以真实文件形式进入 ZIP */
|
|
27
|
+
files?: { relativePath: string; data: Uint8Array }[];
|
|
28
|
+
counts: Record<string, number>;
|
|
29
|
+
warnings: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ValidationResult {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
issues: { path: string; message: string; severity: 'error' | 'warning' }[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ---------------- Secret 扫描(m4 可替换实现) ---------------- */
|
|
38
|
+
|
|
39
|
+
export interface SensitiveHit { path: string; field: string; }
|
|
40
|
+
|
|
41
|
+
export interface SecretScanner {
|
|
42
|
+
/** 递归扫描 + 剥离敏感字段值;返回清洗后的数据与命中清单(不泄值) */
|
|
43
|
+
scanAndRedact(data: unknown): { sanitized: unknown; hits: SensitiveHit[] };
|
|
44
|
+
/** 文本级扫描(文件类分区内容),供强化版使用 */
|
|
45
|
+
scanText?(text: string): SensitiveHit[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* ---------------- DSH Service 门面(m5 对接真实 ctx) ---------------- */
|
|
49
|
+
|
|
50
|
+
export interface NamespaceInfo {
|
|
51
|
+
value: unknown;
|
|
52
|
+
base?: unknown;
|
|
53
|
+
revision: number;
|
|
54
|
+
applies?: string[];
|
|
55
|
+
secrets: { path: string[]; set: boolean }[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface SettingsFacade {
|
|
59
|
+
/** 读某 namespace(redactSecrets 剥离密钥值);返回含乐观锁 revision */
|
|
60
|
+
describe(namespace: string, opts?: { redactSecrets?: boolean }): Promise<NamespaceInfo>;
|
|
61
|
+
/** 整体替换(导入/回滚主通道);expectedRevision 不一致时抛 SETTINGS_CONFLICT 类错误 */
|
|
62
|
+
replace(namespace: string, value: unknown, expectedRevision?: number): Promise<void>;
|
|
63
|
+
/** 部分更新(未来 Merge 策略用) */
|
|
64
|
+
update?(namespace: string, patch: unknown, expectedRevision?: number): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CredentialsFacade {
|
|
68
|
+
/** 只返回状态,永不返回值 */
|
|
69
|
+
describe(ref: string): Promise<{ configured: boolean; source?: string; writable?: boolean }>;
|
|
70
|
+
set(ref: string, value: string): Promise<void>;
|
|
71
|
+
unset(ref: string): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PluginInfo { name: string; version: string; enabled: boolean; isBundle?: boolean; inBundles?: string[]; }
|
|
75
|
+
|
|
76
|
+
export interface PluginsFacade {
|
|
77
|
+
listInstalled(): Promise<PluginInfo[]>;
|
|
78
|
+
install(pkg: string): Promise<{ needsRestart: boolean }>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface WorkspaceFacade {
|
|
82
|
+
listRecords(): Promise<WorkspaceRecord[]>;
|
|
83
|
+
writeRecord(record: WorkspaceRecord): Promise<void>;
|
|
84
|
+
removeRecord?(id: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface PatchChange { lineId: string; raw: unknown; action: 'insert' | 'update' | 'remove'; }
|
|
88
|
+
|
|
89
|
+
export interface PatchFileFacade {
|
|
90
|
+
readPatchLines(file: string): Promise<{ lineId: string; raw: unknown }[]>;
|
|
91
|
+
applyPatchChanges(file: string, changes: PatchChange[]): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface FileSystemFacade {
|
|
95
|
+
readFile(relPath: string): Promise<Uint8Array>;
|
|
96
|
+
writeFile(relPath: string, data: Uint8Array): Promise<void>;
|
|
97
|
+
exists(relPath: string): Promise<boolean>;
|
|
98
|
+
copy(from: string, to: string): Promise<void>;
|
|
99
|
+
remove(relPath: string): Promise<void>;
|
|
100
|
+
listRecursive(dir: string): Promise<string[]>;
|
|
101
|
+
mkdir(dir: string): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** DSH 运行时门面:m3 只依赖此接口;m5 用真实 ctx.settings/credentials/… 实现 */
|
|
105
|
+
export interface HostContext {
|
|
106
|
+
platform: string;
|
|
107
|
+
arch: string;
|
|
108
|
+
homeDir: string;
|
|
109
|
+
dshVersion: string;
|
|
110
|
+
log: Logger;
|
|
111
|
+
settings: SettingsFacade;
|
|
112
|
+
credentials: CredentialsFacade;
|
|
113
|
+
plugins: PluginsFacade;
|
|
114
|
+
workspace: WorkspaceFacade;
|
|
115
|
+
patchFile: PatchFileFacade;
|
|
116
|
+
fs: FileSystemFacade;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ---------------- 导入计划(§13.3 十类 + 决策) ---------------- */
|
|
120
|
+
|
|
121
|
+
export type PlanItemKind =
|
|
122
|
+
| 'Create' | 'Update' | 'Skip' | 'Conflict' | 'Install'
|
|
123
|
+
| 'MissingSecret' | 'MissingDependency' | 'PathMapping' | 'Warning' | 'Error';
|
|
124
|
+
|
|
125
|
+
export type ItemResolution = 'keepCurrent' | 'useImported' | 'review';
|
|
126
|
+
export type GlobalConflictStrategy = 'merge' | 'replace' | 'skipExisting';
|
|
127
|
+
|
|
128
|
+
export interface ConflictDecision { itemId: string; resolution: ItemResolution; }
|
|
129
|
+
|
|
130
|
+
export interface PathMapping {
|
|
131
|
+
oldPrefix: string;
|
|
132
|
+
newPrefix: string;
|
|
133
|
+
appliesTo: ('workspaces' | 'mcp' | 'pluginConfig' | 'skills')[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface PathIssue {
|
|
137
|
+
kind: 'missing' | 'platformMismatch' | 'homeMismatch';
|
|
138
|
+
value: string;
|
|
139
|
+
mappedTo?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface SnapshotTarget { adapter: SectionId; ref: string; }
|
|
143
|
+
|
|
144
|
+
export interface PlanItem {
|
|
145
|
+
id: string; // 稳定项 id(plugin:pkg / prompt:name / workspace:<id> …)
|
|
146
|
+
kind: PlanItemKind;
|
|
147
|
+
adapter: SectionId;
|
|
148
|
+
description: string;
|
|
149
|
+
detail?: string;
|
|
150
|
+
severity: 'info' | 'warning' | 'error';
|
|
151
|
+
conflict?: ConflictDecision;
|
|
152
|
+
pathMapping?: PathMapping;
|
|
153
|
+
missingDependency?: string;
|
|
154
|
+
target?: SnapshotTarget; // 该项将修改的目标(快照登记用)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface ApplyResult { ok: boolean; message?: string; needsRestart?: boolean; }
|
|
158
|
+
|
|
159
|
+
/* ---------------- 三段式输入输出 ---------------- */
|
|
160
|
+
|
|
161
|
+
export interface ImportAnalysis {
|
|
162
|
+
valid: boolean;
|
|
163
|
+
errors: string[];
|
|
164
|
+
warnings: string[];
|
|
165
|
+
compatibility: 'excellent' | 'good' | 'partial' | 'unsupported';
|
|
166
|
+
sectionsInZip: SectionId[];
|
|
167
|
+
pluginSummary: { installed: number; toInstall: number };
|
|
168
|
+
pathIssues: PathIssue[];
|
|
169
|
+
secretCount: number;
|
|
170
|
+
dependencyIssues: { item: string; dependency: string }[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ImportDecisions {
|
|
174
|
+
strategy: GlobalConflictStrategy;
|
|
175
|
+
resolutions: Record<string, ItemResolution>;
|
|
176
|
+
pathMappings: PathMapping[];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ImportPlan {
|
|
180
|
+
items: PlanItem[];
|
|
181
|
+
globalStrategy: GlobalConflictStrategy;
|
|
182
|
+
pathMappings: PathMapping[];
|
|
183
|
+
missingSecrets: { ref: string; required: boolean }[];
|
|
184
|
+
needsRestart: boolean;
|
|
185
|
+
estimatedActions: Record<SectionId, number>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface ExecutedItem {
|
|
189
|
+
itemId: string;
|
|
190
|
+
status: 'ok' | 'skipped' | 'failed';
|
|
191
|
+
message?: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface ImportResult {
|
|
195
|
+
ok: boolean;
|
|
196
|
+
executed: ExecutedItem[];
|
|
197
|
+
needsRestart: boolean;
|
|
198
|
+
missingSecrets: string[];
|
|
199
|
+
warnings: string[];
|
|
200
|
+
rollback: RollbackReport | null;
|
|
201
|
+
snapshotId: string | null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* ---------------- 导入上下文(传给 adapter.analyzeImport / applyItem) ---------------- */
|
|
205
|
+
|
|
206
|
+
export interface ImportContext {
|
|
207
|
+
manifest: Manifest;
|
|
208
|
+
targetPlatform: string;
|
|
209
|
+
/** 目标 DSH 门面(adapter 比较目标状态 / 写入 / 读取数据用) */
|
|
210
|
+
target: HostContext;
|
|
211
|
+
/** 已由 PathMapper 处理后的各分区最终数据(adapter.applyItem 从这取写入内容) */
|
|
212
|
+
sections: Map<SectionId, unknown>;
|
|
213
|
+
pathMappings: PathMapping[];
|
|
214
|
+
resolutions: Record<string, ItemResolution>;
|
|
215
|
+
/** 用户补录的秘密值(仅内存,永不落盘/日志) */
|
|
216
|
+
secretInputs: Record<string, string>;
|
|
217
|
+
decryptedCredentials?: Map<string, string>;
|
|
218
|
+
log: Logger;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ---------------- Snapshot / Rollback(§8) ---------------- */
|
|
222
|
+
|
|
223
|
+
export type SnapshotEntryKind = 'settingsNamespace' | 'credential' | 'patchLine' | 'file' | 'workspaceRecord';
|
|
224
|
+
|
|
225
|
+
export interface SnapshotEntry {
|
|
226
|
+
kind: SnapshotEntryKind;
|
|
227
|
+
adapter: SectionId;
|
|
228
|
+
ref: string;
|
|
229
|
+
/** 原值(credential 条目不含值,仅 existed 标志) */
|
|
230
|
+
before: unknown;
|
|
231
|
+
revision?: number;
|
|
232
|
+
/** file 条目:原文件副本在快照存储中的位置 */
|
|
233
|
+
copiedTo?: string;
|
|
234
|
+
/** credential/file 条目:原目标是否存在 */
|
|
235
|
+
existed?: boolean;
|
|
236
|
+
/** 所属快照 id(file 条目回滚读取 blob 时使用) */
|
|
237
|
+
snapshotId?: string;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface Snapshot {
|
|
241
|
+
id: string;
|
|
242
|
+
createdAt: string;
|
|
243
|
+
sourceZip: string;
|
|
244
|
+
entries: SnapshotEntry[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** 快照存储(默认文件实现见 core/backup.ts;测试可用内存实现) */
|
|
248
|
+
export interface SnapshotStore {
|
|
249
|
+
save(snapshot: Snapshot, blobs?: Map<string, Uint8Array>): Promise<string>;
|
|
250
|
+
load(id: string): Promise<Snapshot>;
|
|
251
|
+
readBlob(id: string, blobPath: string): Promise<Uint8Array>;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface RollbackReport {
|
|
255
|
+
full: boolean;
|
|
256
|
+
restored: string[];
|
|
257
|
+
failed: { item: string; reason: string; manualHint?: string }[];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* ---------------- 兼容性(§13.3 注释规则) ---------------- */
|
|
261
|
+
|
|
262
|
+
export interface CompatibilityInput {
|
|
263
|
+
sourceDsh: string;
|
|
264
|
+
targetDsh: string;
|
|
265
|
+
sourcePlatform: string;
|
|
266
|
+
targetPlatform: string;
|
|
267
|
+
schemaVersion: number;
|
|
268
|
+
missingSections: SectionId[];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type CompatibilityScore = 'excellent' | 'good' | 'partial' | 'unsupported';
|
|
272
|
+
|
|
273
|
+
/* ---------------- 导出报告(规范 §21) ---------------- */
|
|
274
|
+
|
|
275
|
+
export interface ExportReport {
|
|
276
|
+
included: { section: SectionId; counts: Record<string, number> }[];
|
|
277
|
+
excluded: SectionId[];
|
|
278
|
+
security: {
|
|
279
|
+
secretsExcluded: boolean;
|
|
280
|
+
containsSecrets: boolean;
|
|
281
|
+
encrypted: boolean;
|
|
282
|
+
redactedHits: number;
|
|
283
|
+
};
|
|
284
|
+
file: { name: string; sizeBytes: number };
|
|
285
|
+
warnings: string[];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* ---------------- ConfigAdapter 契约(m5 实现;§13.2) ---------------- */
|
|
289
|
+
|
|
290
|
+
export type Portability = 'portable' | 'deviceSpecific' | 'platformSpecific';
|
|
291
|
+
|
|
292
|
+
export interface ConfigAdapter<TSection = unknown> {
|
|
293
|
+
readonly id: SectionId;
|
|
294
|
+
readonly displayName: string;
|
|
295
|
+
readonly defaultIncluded: boolean;
|
|
296
|
+
readonly portability: Portability;
|
|
297
|
+
|
|
298
|
+
/** 读取当前 DSH 该类别配置 → 导出数据(无秘密值) */
|
|
299
|
+
export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<TSection>>;
|
|
300
|
+
|
|
301
|
+
/** 分析导入数据与目标 DSH 的差异 → 计划项(纯计算,零写入) */
|
|
302
|
+
analyzeImport(data: TSection, ctx: ImportContext): Promise<PlanItem[]>;
|
|
303
|
+
|
|
304
|
+
/** 执行单个计划项(Importer 引擎按阶段调度) */
|
|
305
|
+
applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
|
|
306
|
+
|
|
307
|
+
/** 结构校验 */
|
|
308
|
+
validate(data: TSection): Promise<ValidationResult>;
|
|
309
|
+
|
|
310
|
+
/** 可选:导入前快照(缺省用引擎通用快照) */
|
|
311
|
+
snapshot?(targets: SnapshotTarget[], ctx: HostContext): Promise<SnapshotEntry[]>;
|
|
312
|
+
|
|
313
|
+
/** 可选:针对本 adapter 的补偿动作 */
|
|
314
|
+
rollback?(entries: SnapshotEntry[], ctx: HostContext): Promise<void>;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* ---------------- 加密提供者(m4 用 node:crypto 实现) ---------------- */
|
|
318
|
+
|
|
319
|
+
export interface EncryptionProvider {
|
|
320
|
+
encrypt(plaintext: string): Promise<{ blob: Uint8Array; info: EncryptionInfo }>;
|
|
321
|
+
/** authTag 校验失败必须抛错 */
|
|
322
|
+
decrypt(blob: Uint8Array, info: EncryptionInfo, password: string): Promise<string>;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* ---------------- 错误类型 ---------------- */
|
|
326
|
+
|
|
327
|
+
/** 导入被确认前拒绝执行(安全阀) */
|
|
328
|
+
export class ImportNotConfirmedError extends Error {
|
|
329
|
+
constructor() {
|
|
330
|
+
super('导入未确认:必须显式 confirm 后才允许修改任何数据');
|
|
331
|
+
this.name = 'ImportNotConfirmedError';
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** 导入失败:触发回滚后抛出,携带结果供 UI 展示 */
|
|
336
|
+
export class ImportFailedError extends Error {
|
|
337
|
+
readonly result: ImportResult;
|
|
338
|
+
constructor(message: string, result: ImportResult) {
|
|
339
|
+
super(message);
|
|
340
|
+
this.name = 'ImportFailedError';
|
|
341
|
+
this.result = result;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 整体校验与兼容性评分(规范 §18 完整性 / §30 兼容性评分 / 导入后校验)。
|
|
3
|
+
*/
|
|
4
|
+
import { validateManifest } from '../schema/manifest.ts';
|
|
5
|
+
import { validateSectionData } from '../schema/config.ts';
|
|
6
|
+
import { canImport, describeVersion } from '../schema/versions.ts';
|
|
7
|
+
import type { SectionId } from '../schema/types.ts';
|
|
8
|
+
import type { CompatibilityInput, CompatibilityScore, ValidationResult } from './types.ts';
|
|
9
|
+
|
|
10
|
+
export { validateManifest } from '../schema/manifest.ts';
|
|
11
|
+
export { validateSectionData } from '../schema/config.ts';
|
|
12
|
+
|
|
13
|
+
/** 校验 ZIP 内分区数据集合(空对象=合法);返回合并的校验结果 */
|
|
14
|
+
export function validateSections(
|
|
15
|
+
sections: ReadonlyMap<SectionId, unknown>,
|
|
16
|
+
): ValidationResult {
|
|
17
|
+
const issues: ValidationResult['issues'] = [];
|
|
18
|
+
for (const [sectionId, data] of sections) {
|
|
19
|
+
const sectionIssues = validateSectionData(sectionId, data);
|
|
20
|
+
for (const issue of sectionIssues) {
|
|
21
|
+
issues.push({ ...issue, path: `${sectionId}:${issue.path}` });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { valid: issues.every((i) => i.severity !== 'error'), issues };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseVersion(v: string): { major: number; minor: number; patch: number; pre: number } {
|
|
28
|
+
const m = /^(\d+)\.(\d+)(?:\.(\d+))?(?:-(?:rc\.?)?(\d+))?/i.exec(v);
|
|
29
|
+
if (!m) return { major: 0, minor: 0, patch: 0, pre: 0 };
|
|
30
|
+
return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3] ?? 0), pre: Number(m[4] ?? Infinity) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 版本比较:-1=a<b,0=相等,1=a>b(预发布 < 正式版) */
|
|
34
|
+
function compareVersions(a: ReturnType<typeof parseVersion>, b: ReturnType<typeof parseVersion>): -1 | 0 | 1 {
|
|
35
|
+
for (const k of ['major', 'minor', 'patch'] as const) {
|
|
36
|
+
if (a[k] !== b[k]) return a[k] < b[k] ? -1 : 1;
|
|
37
|
+
}
|
|
38
|
+
if (a.pre !== b.pre) return a.pre < b.pre ? -1 : 1;
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 兼容性评分(规则驱动,不凭感觉):
|
|
44
|
+
* unsupported — schema 超出本插件支持范围(过高/过低)
|
|
45
|
+
* partial — 跨平台、分区缺失、或备份 DSH 比目标新
|
|
46
|
+
* good — 备份 DSH 比目标旧(向后兼容)且其余正常
|
|
47
|
+
* excellent — 同平台、无缺失、schema 支持
|
|
48
|
+
*/
|
|
49
|
+
export function computeCompatibility(input: CompatibilityInput): CompatibilityScore {
|
|
50
|
+
if (!canImport(input.schemaVersion)) return 'unsupported';
|
|
51
|
+
|
|
52
|
+
let score: CompatibilityScore = 'excellent';
|
|
53
|
+
|
|
54
|
+
if (input.sourcePlatform !== input.targetPlatform) score = 'partial';
|
|
55
|
+
if (input.missingSections.length > 0) score = 'partial';
|
|
56
|
+
|
|
57
|
+
const src = parseVersion(input.sourceDsh);
|
|
58
|
+
const tgt = parseVersion(input.targetDsh);
|
|
59
|
+
const cmp = compareVersions(src, tgt);
|
|
60
|
+
if (cmp > 0) {
|
|
61
|
+
// 备份来自更新的 DSH → 可能有目标版本不认识的配置
|
|
62
|
+
score = 'partial';
|
|
63
|
+
} else if (cmp < 0) {
|
|
64
|
+
// 备份来自更旧的 DSH → 目标向后兼容
|
|
65
|
+
score = 'good';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return score;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 兼容性得分的可读描述(UI 报告用) */
|
|
72
|
+
export function describeCompatibility(score: CompatibilityScore): string {
|
|
73
|
+
switch (score) {
|
|
74
|
+
case 'excellent': return 'Excellent — 完美兼容(同平台、无缺失、schema 支持)';
|
|
75
|
+
case 'good': return 'Good — 兼容(旧版备份导入新版 DSH)';
|
|
76
|
+
case 'partial': return 'Partial — 部分兼容(跨平台/分区缺失/版本超前,需人工确认)';
|
|
77
|
+
case 'unsupported': return 'Unsupported — 不受支持(schema 超出范围)';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 用 describeVersion 生成 schema 状态的用户可读说明 */
|
|
82
|
+
export function describeSchemaStatus(schemaVersion: number): string {
|
|
83
|
+
return describeVersion(schemaVersion);
|
|
84
|
+
}
|