@logictan/dsh-config-manager 0.1.60 → 0.1.61
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/lib/adapters/index.d.ts +1 -1
- package/lib/adapters/index.js +1 -1
- package/lib/adapters/mcp.js +4 -4
- package/lib/adapters/plugins.d.ts +0 -1
- package/lib/adapters/plugins.js +35 -19
- package/lib/adapters/prompts.js +6 -6
- package/lib/adapters/test-helpers.d.ts +27 -0
- package/lib/adapters/test-helpers.js +40 -0
- package/lib/client.d.ts +1 -10
- package/lib/client.js +293 -294
- package/lib/core/backup.js +5 -3
- package/lib/core/patch-layers.d.ts +32 -0
- package/lib/core/patch-layers.js +39 -0
- package/lib/core/rollback.js +4 -4
- package/lib/index.d.ts +21 -12
- package/lib/index.js +28 -36
- package/lib/utils/env-lock.d.ts +16 -0
- package/lib/utils/env-lock.js +27 -6
- package/package.json +2 -2
- package/src/adapters/index.ts +1 -1
- package/src/adapters/mcp.ts +4 -4
- package/src/adapters/patch-layers.test.ts +201 -0
- package/src/adapters/plugins.test.ts +13 -5
- package/src/adapters/plugins.ts +33 -17
- package/src/adapters/prompts.ts +6 -6
- package/src/adapters/test-helpers.ts +48 -0
- package/src/client/api.ts +1 -10
- package/src/client/sync/sync-api.ts +0 -1
- package/src/core/backup.ts +5 -3
- package/src/core/config-lifecycle.test.ts +4 -1
- package/src/core/patch-layers.ts +42 -0
- package/src/core/rollback.ts +4 -4
- package/src/index.facade.test.ts +25 -2
- package/src/index.ts +26 -49
- package/src/utils/env-lock.test.ts +29 -0
- package/src/utils/env-lock.ts +27 -6
package/lib/adapters/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export type { CredentialRefsProvider } from './credentials.ts';
|
|
|
42
42
|
export { SettingsAdapter } from './settings.ts';
|
|
43
43
|
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, UI_MIGRATION_NOTES } from './ui.ts';
|
|
44
44
|
export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES, type ProviderExportEntry, type ProviderExportSection } from './providers.ts';
|
|
45
|
-
export { PluginsAdapter
|
|
45
|
+
export { PluginsAdapter } from './plugins.ts';
|
|
46
46
|
export type { LocalPluginPackHook } from './plugins.ts';
|
|
47
47
|
export { McpAdapter, extractMcpServers, buildMcpPatchLine, type McpExportEntry, type McpExportSection } from './mcp.ts';
|
|
48
48
|
export { PromptsAdapter, extractPrompts, mergePromptIntoLine, buildPromptLine, type PromptExportEntry, type PromptsExportSection } from './prompts.ts';
|
package/lib/adapters/index.js
CHANGED
|
@@ -40,7 +40,7 @@ export function createAdapters(options = {}) {
|
|
|
40
40
|
export { SettingsAdapter } from './settings.js';
|
|
41
41
|
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, UI_MIGRATION_NOTES } from './ui.js';
|
|
42
42
|
export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES } from './providers.js';
|
|
43
|
-
export { PluginsAdapter
|
|
43
|
+
export { PluginsAdapter } from './plugins.js';
|
|
44
44
|
export { McpAdapter, extractMcpServers, buildMcpPatchLine } from './mcp.js';
|
|
45
45
|
export { PromptsAdapter, extractPrompts, mergePromptIntoLine, buildPromptLine } from './prompts.js';
|
|
46
46
|
export { SkillsAdapter } from './skills.js';
|
package/lib/adapters/mcp.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { isDeepStrictEqual } from 'node:util';
|
|
10
10
|
import { msgOf, zhMsg } from '../core/messages.js';
|
|
11
|
-
import {
|
|
11
|
+
import { HOME_PATCH_FILE } from '../core/patch-layers.js';
|
|
12
12
|
/** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
|
|
13
13
|
export function extractMcpServers(lines) {
|
|
14
14
|
const servers = [];
|
|
@@ -84,7 +84,7 @@ export class McpAdapter {
|
|
|
84
84
|
const warnings = [];
|
|
85
85
|
let lines = [];
|
|
86
86
|
try {
|
|
87
|
-
lines = await ctx.patchFile.readPatchLines(
|
|
87
|
+
lines = await ctx.patchFile.readPatchLines(HOME_PATCH_FILE);
|
|
88
88
|
}
|
|
89
89
|
catch (err) {
|
|
90
90
|
warnings.push(msgOf(ctx)('adapter.patchReadFailedMCP', { reason: err instanceof Error ? err.message : String(err) }));
|
|
@@ -100,7 +100,7 @@ export class McpAdapter {
|
|
|
100
100
|
async analyzeImport(data, ctx) {
|
|
101
101
|
const msg = ctx.msg;
|
|
102
102
|
const items = [];
|
|
103
|
-
const targetLines = await ctx.target.patchFile.readPatchLines(
|
|
103
|
+
const targetLines = await ctx.target.patchFile.readPatchLines(HOME_PATCH_FILE);
|
|
104
104
|
const targetServers = extractMcpServers(targetLines);
|
|
105
105
|
for (const server of data.servers) {
|
|
106
106
|
const id = `mcp:${server.serverName}`;
|
|
@@ -149,7 +149,7 @@ export class McpAdapter {
|
|
|
149
149
|
if (!ref)
|
|
150
150
|
return { ok: false, message: msg('adapter.missingTargetRef') };
|
|
151
151
|
const raw = buildMcpPatchLine(ref, server);
|
|
152
|
-
await ctx.target.patchFile.applyPatchChanges(
|
|
152
|
+
await ctx.target.patchFile.applyPatchChanges(HOME_PATCH_FILE, [
|
|
153
153
|
{ lineId: ref, raw, action: item.kind === 'Create' ? 'insert' : 'update' },
|
|
154
154
|
]);
|
|
155
155
|
return { ok: true, needsRestart: true, message: msg('adapter.mcpWritten', { serverName }) };
|
|
@@ -2,7 +2,6 @@ import type { PackLocalPluginsResult } from '../core/local-plugin-pack.ts';
|
|
|
2
2
|
import type { MsgFunc } from '../core/messages.ts';
|
|
3
3
|
import type { PluginEntry, PluginsSection } from '../schema/types.ts';
|
|
4
4
|
import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
|
|
5
|
-
export declare const USER_PATCH_FILE = "cordis.patch.yml";
|
|
6
5
|
/**
|
|
7
6
|
* 本地源插件打包钩子(由宿主注入,见 src/index.ts createAdapters)。
|
|
8
7
|
* 返回打包结果(含字节与重写后的 spec);不注入 = 不做本地源打包(保持旧行为)。
|
package/lib/adapters/plugins.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* plugins 分区 adapter(设计 §3.3/§8):
|
|
3
|
-
* 数据源 = ctx.plugins.listInstalled()(插件清单)+
|
|
3
|
+
* 数据源 = ctx.plugins.listInstalled()(插件清单)+ 两个 patch 层(home 与 profile 的 cordis.patch.yml)。
|
|
4
4
|
*
|
|
5
5
|
* 安全不变量:绝不打包插件二进制;导入走 DSH 官方机制(dsh plugin CLI → needsRestart 提示)。
|
|
6
|
-
* patch
|
|
6
|
+
* patch 行导入(用户自定义行:启用/禁用/插入)写回该行自带的层(home 或 profile),同样 needsRestart。
|
|
7
7
|
*
|
|
8
8
|
* T1(本地源插件迁移):`link:` / `file:` 来源的插件指向本机路径,换机后必然不可达
|
|
9
9
|
* (曾导致插件被静默丢失)。导出时经注入的 `localPack` 执行 `npm pack`,把 tarball 作为
|
|
@@ -17,8 +17,8 @@ import { isLocalPluginSpec, isPackedLocalSpec, LOCAL_PLUGIN_DIR } from '../core/
|
|
|
17
17
|
import { msgOf, zhMsg } from '../core/messages.js';
|
|
18
18
|
import { isPathSafe, normalizePath } from '../utils/paths.js';
|
|
19
19
|
import { PLUGIN_PATCH_REF_PREFIX } from '../core/backup.js';
|
|
20
|
+
import { HOME_PATCH_FILE, PROFILE_PATCH_FILE, patchLayerKey, parsePatchLayerKey } from '../core/patch-layers.js';
|
|
20
21
|
import { parsePnpmPatchedDependencies, sanitizePnpmWorkspacePatches } from './pnpm-workspace.js';
|
|
21
|
-
export const USER_PATCH_FILE = 'cordis.patch.yml';
|
|
22
22
|
/** tarball 字节 → base64(零依赖,避免 Buffer 在浏览器侧类型问题) */
|
|
23
23
|
function bytesToBase64(data) {
|
|
24
24
|
if (typeof Buffer !== 'undefined')
|
|
@@ -142,14 +142,18 @@ export class PluginsAdapter {
|
|
|
142
142
|
catch (err) {
|
|
143
143
|
warnings.push(msgOf(ctx)('adapter.pluginListReadFailed', { reason: err instanceof Error ? err.message : String(err) }));
|
|
144
144
|
}
|
|
145
|
+
// 两层都读:home 层(全机偏好)与 profile 层(该 profile 专属)。缺 profile 层文件不是故障
|
|
146
|
+
// (readPatchLines 对不存在的文件返回空数组),只有真读失败才告警。
|
|
145
147
|
const patch = [];
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
for (const file of [HOME_PATCH_FILE, PROFILE_PATCH_FILE]) {
|
|
149
|
+
try {
|
|
150
|
+
const lines = await ctx.patchFile.readPatchLines(file);
|
|
151
|
+
for (const l of lines)
|
|
152
|
+
patch.push({ file, lineId: l.lineId, raw: l.raw });
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
warnings.push(msgOf(ctx)('adapter.patchReadFailed', { reason: err instanceof Error ? err.message : String(err) }));
|
|
156
|
+
}
|
|
153
157
|
}
|
|
154
158
|
// pnpm-workspace.yaml(allowBuilds / minimumReleaseAgeExclude 等):随插件分区迁移,
|
|
155
159
|
// 否则目标 profile 的 pnpm 可能因构建白名单/冷静期拒绝安装插件(§34.17 同款语义)。
|
|
@@ -391,17 +395,26 @@ export class PluginsAdapter {
|
|
|
391
395
|
}
|
|
392
396
|
// 用户 patch 行:lineId 唯一键;存在且同 → Skip;存在不同 → Conflict;不存在 → Create。
|
|
393
397
|
// mcp-client 行与 systemPrompt/planMode 行由 mcp/prompts adapter 管理,此处跳过(避免重复写入覆盖)。
|
|
394
|
-
|
|
398
|
+
// 逐层读目标端:行自带的 file 决定比对哪一层(只读 home 层会让 profile 层行在目标机
|
|
399
|
+
// 已有同值时被误判成 Create,也会让目标机 profile 层的不同值判不出 Conflict)。
|
|
400
|
+
const targetLines = new Map();
|
|
401
|
+
for (const pl of data.patch) {
|
|
402
|
+
if (!targetLines.has(pl.file)) {
|
|
403
|
+
targetLines.set(pl.file, await ctx.target.patchFile.readPatchLines(pl.file));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
395
406
|
for (const pl of data.patch) {
|
|
396
407
|
if (isManagedElsewhere(pl.raw))
|
|
397
408
|
continue;
|
|
398
|
-
|
|
399
|
-
const
|
|
409
|
+
// 层限定复合键:两层可存在同名 lineId,裸 lineId 无法指向「哪一层的哪一行」。
|
|
410
|
+
const ref = patchLayerKey(pl.file, pl.lineId);
|
|
411
|
+
const id = `patch:${ref}`;
|
|
412
|
+
const tl = targetLines.get(pl.file)?.find((l) => l.lineId === pl.lineId);
|
|
400
413
|
if (!tl) {
|
|
401
414
|
items.push({
|
|
402
415
|
id, kind: 'Create', adapter: 'plugins',
|
|
403
416
|
description: msg('adapter.patchLineCreate', { lineId: pl.lineId }), severity: 'info',
|
|
404
|
-
target: { adapter: 'plugins', ref
|
|
417
|
+
target: { adapter: 'plugins', ref },
|
|
405
418
|
});
|
|
406
419
|
}
|
|
407
420
|
else if (isDeepStrictEqual(tl.raw, pl.raw)) {
|
|
@@ -411,7 +424,7 @@ export class PluginsAdapter {
|
|
|
411
424
|
items.push({
|
|
412
425
|
id, kind: 'Conflict', adapter: 'plugins',
|
|
413
426
|
description: msg('adapter.patchLineDiff', { lineId: pl.lineId }), severity: 'warning',
|
|
414
|
-
target: { adapter: 'plugins', ref
|
|
427
|
+
target: { adapter: 'plugins', ref },
|
|
415
428
|
});
|
|
416
429
|
}
|
|
417
430
|
}
|
|
@@ -547,14 +560,17 @@ export class PluginsAdapter {
|
|
|
547
560
|
const ref = item.target?.ref;
|
|
548
561
|
if (!ref)
|
|
549
562
|
return { ok: false, message: msg('adapter.missingTargetRef') };
|
|
563
|
+
// 复合键解析出层与 lineId:只按 lineId 查找会命中数组里先出现的 home 层行,
|
|
564
|
+
// 把 profile 层的行写进 home 层(回滚同理)。
|
|
565
|
+
const { file, lineId } = parsePatchLayerKey(ref);
|
|
550
566
|
const data = ctx.sections.get('plugins');
|
|
551
|
-
const pl = data?.patch.find((p) => p.lineId ===
|
|
567
|
+
const pl = data?.patch.find((p) => p.file === file && p.lineId === lineId);
|
|
552
568
|
if (!pl)
|
|
553
569
|
return { ok: false, message: msg('adapter.patchMissing', { ref }) };
|
|
554
|
-
await ctx.target.patchFile.applyPatchChanges(
|
|
555
|
-
{ lineId
|
|
570
|
+
await ctx.target.patchFile.applyPatchChanges(file, [
|
|
571
|
+
{ lineId, raw: pl.raw, action: item.kind === 'Create' ? 'insert' : 'update' },
|
|
556
572
|
]);
|
|
557
|
-
return { ok: true, needsRestart: true, message: msg('adapter.patchWritten', { ref }) };
|
|
573
|
+
return { ok: true, needsRestart: true, message: msg('adapter.patchWritten', { ref: lineId }) };
|
|
558
574
|
}
|
|
559
575
|
async validate(data, msg = zhMsg) {
|
|
560
576
|
const issues = [];
|
package/lib/adapters/prompts.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { isDeepStrictEqual } from 'node:util';
|
|
13
13
|
import { msgOf, zhMsg } from '../core/messages.js';
|
|
14
|
-
import {
|
|
14
|
+
import { HOME_PATCH_FILE } from '../core/patch-layers.js';
|
|
15
15
|
/** 从 patch 行 config 中提取 prompt 条目(systemPrompt.persona / planMode.sections[].text) */
|
|
16
16
|
export function extractPrompts(lines) {
|
|
17
17
|
const prompts = [];
|
|
@@ -108,7 +108,7 @@ export class PromptsAdapter {
|
|
|
108
108
|
const warnings = [];
|
|
109
109
|
let lines = [];
|
|
110
110
|
try {
|
|
111
|
-
lines = await ctx.patchFile.readPatchLines(
|
|
111
|
+
lines = await ctx.patchFile.readPatchLines(HOME_PATCH_FILE);
|
|
112
112
|
}
|
|
113
113
|
catch (err) {
|
|
114
114
|
warnings.push(msgOf(ctx)('adapter.patchReadFailedPrompts', { reason: err instanceof Error ? err.message : String(err) }));
|
|
@@ -124,7 +124,7 @@ export class PromptsAdapter {
|
|
|
124
124
|
async analyzeImport(data, ctx) {
|
|
125
125
|
const msg = ctx.msg;
|
|
126
126
|
const items = [];
|
|
127
|
-
const targetLines = await ctx.target.patchFile.readPatchLines(
|
|
127
|
+
const targetLines = await ctx.target.patchFile.readPatchLines(HOME_PATCH_FILE);
|
|
128
128
|
const targetPrompts = extractPrompts(targetLines);
|
|
129
129
|
for (const p of data.prompts) {
|
|
130
130
|
const id = p.id;
|
|
@@ -191,18 +191,18 @@ export class PromptsAdapter {
|
|
|
191
191
|
// Create:目标无来源行 → 用记录的行名重建 patch 行(insert)
|
|
192
192
|
if (item.kind === 'Create') {
|
|
193
193
|
const raw = buildPromptLine(ref, prompt);
|
|
194
|
-
await ctx.target.patchFile.applyPatchChanges(
|
|
194
|
+
await ctx.target.patchFile.applyPatchChanges(HOME_PATCH_FILE, [
|
|
195
195
|
{ lineId: ref, raw, action: 'insert' },
|
|
196
196
|
]);
|
|
197
197
|
return { ok: true, needsRestart: true, message: msg('adapter.promptCreated', { name: prompt.name, ref }) };
|
|
198
198
|
}
|
|
199
199
|
// Update / Conflict(useImported):合并进目标行 config
|
|
200
|
-
const lines = await ctx.target.patchFile.readPatchLines(
|
|
200
|
+
const lines = await ctx.target.patchFile.readPatchLines(HOME_PATCH_FILE);
|
|
201
201
|
const line = lines.find((l) => l.lineId === ref);
|
|
202
202
|
if (!line)
|
|
203
203
|
return { ok: false, message: msg('adapter.patchLineMissing', { ref }) };
|
|
204
204
|
const newRaw = mergePromptIntoLine(line.raw, prompt);
|
|
205
|
-
await ctx.target.patchFile.applyPatchChanges(
|
|
205
|
+
await ctx.target.patchFile.applyPatchChanges(HOME_PATCH_FILE, [
|
|
206
206
|
{ lineId: ref, raw: newRaw, action: 'update' },
|
|
207
207
|
]);
|
|
208
208
|
return { ok: true, needsRestart: true, message: msg('adapter.promptWritten', { name: prompt.name, ref }) };
|
|
@@ -94,6 +94,30 @@ export declare class MemPatch implements PatchFileFacade {
|
|
|
94
94
|
action: 'insert' | 'update' | 'remove';
|
|
95
95
|
}[]): Promise<void>;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* 分层 patch 门面 mock:patch 行按 `file`(层 token)分桶。
|
|
99
|
+
* 单层的 `MemPatch` 无法表达「同名 lineId 分布在两层」——而那正是层寻址契约的核心,
|
|
100
|
+
* 故另立一个按层键控的实现,供 plugins 适配器与快照/回滚的契约测试使用。
|
|
101
|
+
*/
|
|
102
|
+
export declare class MemLayeredPatch implements PatchFileFacade {
|
|
103
|
+
files: Map<string, Map<string, {
|
|
104
|
+
lineId: string;
|
|
105
|
+
raw: unknown;
|
|
106
|
+
}>>;
|
|
107
|
+
/** 直接落一行(测试夹具;等价于目标机该层已存在该行)。 */
|
|
108
|
+
set(file: string, lineId: string, raw: unknown): void;
|
|
109
|
+
has(file: string, lineId: string): boolean;
|
|
110
|
+
rawOf(file: string, lineId: string): unknown;
|
|
111
|
+
readPatchLines(file: string): Promise<{
|
|
112
|
+
lineId: string;
|
|
113
|
+
raw: unknown;
|
|
114
|
+
}[]>;
|
|
115
|
+
applyPatchChanges(file: string, changes: {
|
|
116
|
+
lineId: string;
|
|
117
|
+
raw: unknown;
|
|
118
|
+
action: 'insert' | 'update' | 'remove';
|
|
119
|
+
}[]): Promise<void>;
|
|
120
|
+
}
|
|
97
121
|
export declare class MemSnapshotStore implements SnapshotStore {
|
|
98
122
|
snapshots: Map<string, Snapshot>;
|
|
99
123
|
blobs: Map<string, Uint8Array<ArrayBufferLike>>;
|
|
@@ -116,6 +140,9 @@ export declare class MockHostContext implements HostContext {
|
|
|
116
140
|
patchFile: MemPatch;
|
|
117
141
|
fs: MemFs;
|
|
118
142
|
constructor(platform: string, homeDir: string, profile?: string);
|
|
143
|
+
/** 换成按层键控的 patch 门面(层寻址契约测试用)。
|
|
144
|
+
* 字段的声明类型仍是单层的 MemPatch,以免既有夹具的 `.lines` 用法失型。 */
|
|
145
|
+
useLayeredPatch(): MemLayeredPatch;
|
|
119
146
|
}
|
|
120
147
|
export declare function makeContext(platform: string, homeDir: string, profile?: string): MockHostContext;
|
|
121
148
|
/** 构造最小合法 manifest(adapter 单测用;真实 manifest 由 exporter 生成) */
|
|
@@ -150,6 +150,39 @@ export class MemPatch {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* 分层 patch 门面 mock:patch 行按 `file`(层 token)分桶。
|
|
155
|
+
* 单层的 `MemPatch` 无法表达「同名 lineId 分布在两层」——而那正是层寻址契约的核心,
|
|
156
|
+
* 故另立一个按层键控的实现,供 plugins 适配器与快照/回滚的契约测试使用。
|
|
157
|
+
*/
|
|
158
|
+
export class MemLayeredPatch {
|
|
159
|
+
files = new Map();
|
|
160
|
+
/** 直接落一行(测试夹具;等价于目标机该层已存在该行)。 */
|
|
161
|
+
set(file, lineId, raw) {
|
|
162
|
+
const layer = this.files.get(file) ?? new Map();
|
|
163
|
+
layer.set(lineId, { lineId, raw });
|
|
164
|
+
this.files.set(file, layer);
|
|
165
|
+
}
|
|
166
|
+
has(file, lineId) {
|
|
167
|
+
return this.files.get(file)?.has(lineId) ?? false;
|
|
168
|
+
}
|
|
169
|
+
rawOf(file, lineId) {
|
|
170
|
+
return this.files.get(file)?.get(lineId)?.raw;
|
|
171
|
+
}
|
|
172
|
+
async readPatchLines(file) {
|
|
173
|
+
return [...(this.files.get(file)?.values() ?? [])];
|
|
174
|
+
}
|
|
175
|
+
async applyPatchChanges(file, changes) {
|
|
176
|
+
const layer = this.files.get(file) ?? new Map();
|
|
177
|
+
this.files.set(file, layer);
|
|
178
|
+
for (const c of changes) {
|
|
179
|
+
if (c.action === 'remove')
|
|
180
|
+
layer.delete(c.lineId);
|
|
181
|
+
else
|
|
182
|
+
layer.set(c.lineId, { lineId: c.lineId, raw: c.raw });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
153
186
|
export class MemSnapshotStore {
|
|
154
187
|
snapshots = new Map();
|
|
155
188
|
blobs = new Map();
|
|
@@ -198,6 +231,13 @@ export class MockHostContext {
|
|
|
198
231
|
this.fs = new MemFs(homeDir);
|
|
199
232
|
this.log = createLogger({ level: 'error', sink: () => { } });
|
|
200
233
|
}
|
|
234
|
+
/** 换成按层键控的 patch 门面(层寻址契约测试用)。
|
|
235
|
+
* 字段的声明类型仍是单层的 MemPatch,以免既有夹具的 `.lines` 用法失型。 */
|
|
236
|
+
useLayeredPatch() {
|
|
237
|
+
const layered = new MemLayeredPatch();
|
|
238
|
+
this.patchFile = layered;
|
|
239
|
+
return layered;
|
|
240
|
+
}
|
|
201
241
|
}
|
|
202
242
|
export function makeContext(platform, homeDir, profile) {
|
|
203
243
|
return new MockHostContext(platform, homeDir, profile);
|
package/lib/client.d.ts
CHANGED
|
@@ -293,19 +293,10 @@ type UiTextKey = keyof typeof uiZh;
|
|
|
293
293
|
type UiT = (key: UiTextKey, params?: Record<string, string | number>) => string;
|
|
294
294
|
//#endregion
|
|
295
295
|
//#region src/client/api.d.ts
|
|
296
|
-
/** Host
|
|
296
|
+
/** Host 半状态响应:设置页页脚版本行的两个字段(消费方只有 ConfigManagerSection) */
|
|
297
297
|
interface ServiceStatus {
|
|
298
|
-
ready: boolean;
|
|
299
298
|
pluginVersion: string;
|
|
300
299
|
dshVersion: string;
|
|
301
|
-
platform: string;
|
|
302
|
-
arch: string;
|
|
303
|
-
homeDir?: string;
|
|
304
|
-
profile?: string;
|
|
305
|
-
profileManifestReadable?: boolean;
|
|
306
|
-
installedPluginCount?: number;
|
|
307
|
-
installedPluginNames?: string[];
|
|
308
|
-
bundles?: string[];
|
|
309
300
|
}
|
|
310
301
|
/** 携带路由 JSON error 消息的错误类型 */
|
|
311
302
|
declare class ConfigManagerApiError extends Error {
|