mcp-compose 0.4.0 → 0.6.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.
Files changed (50) hide show
  1. package/README.md +106 -12
  2. package/dist/bin/mcp-compose.js +36 -26
  3. package/dist/bin/mcp-compose.js.map +1 -1
  4. package/dist/config.sample.json +60 -0
  5. package/dist/package.json +3 -2
  6. package/dist/src/config-template.d.ts +2 -0
  7. package/dist/src/config-template.d.ts.map +1 -0
  8. package/dist/src/config-template.js +8 -0
  9. package/dist/src/config-template.js.map +1 -0
  10. package/dist/src/config.d.ts.map +1 -1
  11. package/dist/src/config.js +17 -6
  12. package/dist/src/config.js.map +1 -1
  13. package/dist/src/index.d.ts +3 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +3 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/sync-clients.d.ts +5 -0
  18. package/dist/src/sync-clients.d.ts.map +1 -0
  19. package/dist/src/sync-clients.js +51 -0
  20. package/dist/src/sync-clients.js.map +1 -0
  21. package/dist/src/sync-deepseek.d.ts +16 -0
  22. package/dist/src/sync-deepseek.d.ts.map +1 -0
  23. package/dist/src/sync-deepseek.js +209 -0
  24. package/dist/src/sync-deepseek.js.map +1 -0
  25. package/dist/src/sync-engine.d.ts +48 -0
  26. package/dist/src/sync-engine.d.ts.map +1 -0
  27. package/dist/src/sync-engine.js +104 -0
  28. package/dist/src/sync-engine.js.map +1 -0
  29. package/dist/src/sync-file.d.ts +4 -0
  30. package/dist/src/sync-file.d.ts.map +1 -0
  31. package/dist/src/sync-file.js +61 -0
  32. package/dist/src/sync-file.js.map +1 -0
  33. package/dist/src/sync-state.d.ts +10 -0
  34. package/dist/src/sync-state.d.ts.map +1 -0
  35. package/dist/src/sync-state.js +50 -0
  36. package/dist/src/sync-state.js.map +1 -0
  37. package/dist/src/sync-targets.d.ts +11 -0
  38. package/dist/src/sync-targets.d.ts.map +1 -0
  39. package/dist/src/sync-targets.js +31 -0
  40. package/dist/src/sync-targets.js.map +1 -0
  41. package/dist/src/sync.d.ts +5 -4
  42. package/dist/src/sync.d.ts.map +1 -1
  43. package/dist/src/sync.js +40 -235
  44. package/dist/src/sync.js.map +1 -1
  45. package/dist/src/types.d.ts +29 -4
  46. package/dist/src/types.d.ts.map +1 -1
  47. package/dist/src/validation.d.ts.map +1 -1
  48. package/dist/src/validation.js +55 -7
  49. package/dist/src/validation.js.map +1 -1
  50. package/package.json +3 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-deepseek.d.ts","sourceRoot":"","sources":["../../src/sync-deepseek.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,QAAA,MAAM,UAAU,gCAAgC,CAAC;AAKjD,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,UAAU,CAAC;IACxB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,iBAAiB,CAAC;QAC7B,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;CACH;AA4MD,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,yBAA+B,GACtE,oBAAoB,CAAC,QAAQ,CAAC,CAEhC"}
@@ -0,0 +1,209 @@
1
+ import { createRequire } from 'node:module';
2
+ import { join } from 'node:path';
3
+ import { isMap, isScalar, isSeq, parseDocument, stringify } from 'yaml';
4
+ import { readSyncFile } from './sync-file.js';
5
+ import { FileConfigSyncTarget } from './sync-engine.js';
6
+ import { FileSyncOwnershipStore } from './sync-state.js';
7
+ const MCP_CLIENT = '@deepseek-ai/dsh-mcp-client';
8
+ const BEGIN = '# >>> mcp-compose:deepseek-harness';
9
+ const END = '# <<< mcp-compose:deepseek-harness';
10
+ const SERVER_NAME = /^[A-Za-z0-9_-]{1,32}$/;
11
+ function isRecord(value) {
12
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ }
14
+ function parsePatch(source, path) {
15
+ const document = parseDocument(source, {
16
+ // Cordis evaluates these expressions at runtime. Sync must only read them
17
+ // as inert scalar text and retain the original source outside its block.
18
+ customTags: [{ tag: 'tag:yaml.org,2002:js', resolve: (value) => value }],
19
+ prettyErrors: false,
20
+ });
21
+ if (document.errors.length > 0 || document.warnings.length > 0) {
22
+ throw new Error(`Invalid or unsupported YAML in ${path}; file was not changed.`);
23
+ }
24
+ const root = document.contents;
25
+ const empty = root === null || (isScalar(root) && root.value === null && root.source === '');
26
+ if (!empty && (!isSeq(root) || root.items.some((item) => !isMap(item)))) {
27
+ throw new Error(`Expected a Cordis patch array in ${path}; file was not changed.`);
28
+ }
29
+ return document;
30
+ }
31
+ function parseManagedRows(rows, path, owned) {
32
+ const entries = [];
33
+ const seen = new Set();
34
+ for (const row of rows) {
35
+ if (!isRecord(row) || Object.keys(row).length !== 1 || !Array.isArray(row['insert'])) {
36
+ throw new Error(`Unrecognized content in the mcp-compose block of ${path}.`);
37
+ }
38
+ for (const value of row['insert']) {
39
+ const config = isRecord(value) ? value['config'] : undefined;
40
+ if (!isRecord(value) || !isRecord(config)
41
+ || value['name'] !== MCP_CLIENT || config['transport'] !== 'streamable-http'
42
+ || typeof config['serverName'] !== 'string' || !SERVER_NAME.test(config['serverName'])
43
+ || value['id'] !== `mcp-compose-${config['serverName']}` || typeof config['url'] !== 'string'
44
+ || Object.keys(value).some((key) => !['id', 'name', 'config'].includes(key))
45
+ || Object.keys(config).some((key) => !['serverName', 'transport', 'url', 'headers'].includes(key))
46
+ || ('headers' in config && (!isRecord(config['headers'])
47
+ || Object.values(config['headers']).some((header) => typeof header !== 'string')))) {
48
+ throw new Error(`Unrecognized MCP entry in the mcp-compose block of ${path}.`);
49
+ }
50
+ const name = config['serverName'];
51
+ if (seen.has(name) || !owned.includes(name)) {
52
+ throw new Error(`Untracked or duplicate MCP entry "${name}" in ${path}; check sync-state.json.`);
53
+ }
54
+ seen.add(name);
55
+ entries.push(value);
56
+ }
57
+ }
58
+ return entries;
59
+ }
60
+ function parseProfilePatch(source, path, owned) {
61
+ const text = source ?? '';
62
+ const document = parsePatch(text, path);
63
+ const root = document.contents;
64
+ const rows = document.toJS({ maxAliasCount: 100 });
65
+ const values = Array.isArray(rows) ? rows : [];
66
+ const markers = [...text.matchAll(/^# (?:>>>|<<<) mcp-compose:deepseek-harness[^\r\n]*(?:\r?\n|$)/gm)];
67
+ const newline = text.includes('\r\n') ? '\r\n' : '\n';
68
+ if (markers.length > 0) {
69
+ const [begin, end] = markers;
70
+ if (markers.length !== 2 || !begin || !end
71
+ || begin[0].trimEnd() !== BEGIN || end[0].trimEnd() !== END) {
72
+ throw new Error(`Malformed mcp-compose markers in ${path}; file was not changed.`);
73
+ }
74
+ const start = begin.index;
75
+ const bodyStart = start + begin[0].length;
76
+ const bodyEnd = end.index;
77
+ const finish = bodyEnd + end[0].length;
78
+ const managed = [];
79
+ const unmanaged = [];
80
+ if (isSeq(root)) {
81
+ for (const [index, node] of root.items.entries()) {
82
+ if (!isMap(node) || !node.range)
83
+ throw new Error(`Invalid patch entry in ${path}.`);
84
+ const [nodeStart, nodeEnd] = node.range;
85
+ if (nodeStart >= bodyStart && nodeEnd <= bodyEnd) {
86
+ managed.push(values[index]);
87
+ }
88
+ else if (nodeEnd <= start || nodeStart >= finish) {
89
+ unmanaged.push(values[index]);
90
+ }
91
+ else {
92
+ throw new Error(`mcp-compose markers must surround complete patch entries in ${path}.`);
93
+ }
94
+ }
95
+ }
96
+ return {
97
+ prefix: text.slice(0, start), suffix: text.slice(finish), newline, unmanaged,
98
+ entries: parseManagedRows(managed, path, owned),
99
+ };
100
+ }
101
+ if (isSeq(root) && root.flow && root.items.length > 0) {
102
+ throw new Error(`Use a block-style Cordis patch array in ${path} before enabling DSH sync.`);
103
+ }
104
+ // For an empty [] replace only the root token. For a populated sequence,
105
+ // insert before trailing comments or an explicit YAML document end marker.
106
+ const start = root?.range?.[isSeq(root) && root.items.length > 0 ? 1 : 0] ?? document.range?.[1] ?? text.length;
107
+ const finish = root?.range?.[1] ?? start;
108
+ return {
109
+ prefix: text.slice(0, start), suffix: text.slice(finish),
110
+ entries: [], unmanaged: values, newline,
111
+ };
112
+ }
113
+ function assertNoCollisions(value, names, path, seen = new Set()) {
114
+ if (typeof value !== 'object' || value === null || seen.has(value))
115
+ return;
116
+ seen.add(value);
117
+ if (Array.isArray(value)) {
118
+ for (const child of value)
119
+ assertNoCollisions(child, names, path, seen);
120
+ }
121
+ else if (isRecord(value)) {
122
+ const id = value['id'];
123
+ const config = value['config'];
124
+ if (typeof id === 'string' && id.startsWith('mcp-compose-') && names.has(id.slice('mcp-compose-'.length))) {
125
+ throw new Error(`Unmanaged entry id "${id}" conflicts with mcp-compose in ${path}.`);
126
+ }
127
+ if (value['name'] === MCP_CLIENT && isRecord(config) && typeof config['serverName'] === 'string' && names.has(config['serverName'])) {
128
+ throw new Error(`Unmanaged MCP server "${config['serverName']}" conflicts with mcp-compose in ${path}.`);
129
+ }
130
+ for (const child of Object.values(value))
131
+ assertNoCollisions(child, names, path, seen);
132
+ }
133
+ }
134
+ function renderPatch(patch, entries, path) {
135
+ const { prefix, suffix, newline } = patch;
136
+ let replacement = '';
137
+ if (entries.length > 0) {
138
+ const yaml = stringify([{ insert: entries }], { defaultStringType: 'QUOTE_DOUBLE', lineWidth: 0 }).replace(/\n/g, newline);
139
+ replacement = `${BEGIN}${newline}${yaml}${END}${newline}`;
140
+ }
141
+ else if (patch.unmanaged.length === 0) {
142
+ replacement = `[]${newline}`;
143
+ }
144
+ const separator = prefix && !prefix.endsWith('\n') && replacement ? newline : '';
145
+ const output = prefix + separator + replacement + suffix;
146
+ parsePatch(output, path);
147
+ return output;
148
+ }
149
+ class DeepSeekHarnessAdapter {
150
+ profile;
151
+ target;
152
+ path;
153
+ profileDir;
154
+ constructor(home, profile) {
155
+ this.profile = profile;
156
+ this.profileDir = join(home, 'profiles', profile);
157
+ this.path = join(this.profileDir, 'cordis.patch.yml');
158
+ this.target = `DeepSeek Harness (${profile})`;
159
+ }
160
+ parse(source, owned) {
161
+ const patch = parseProfilePatch(source, this.path, owned);
162
+ return {
163
+ entries: new Map(patch.entries.map((entry) => [entry.config.serverName, entry])),
164
+ hasEntries: patch.entries.length > 0 || patch.unmanaged.length > 0,
165
+ validateNames: (names) => { assertNoCollisions(patch.unmanaged, names, this.path); },
166
+ render: (entries) => renderPatch(patch, [...entries.values()], this.path),
167
+ };
168
+ }
169
+ encode(name, endpoint) {
170
+ if (!SERVER_NAME.test(name))
171
+ return { warning: 'DSH serverName must match [A-Za-z0-9_-]{1,32}' };
172
+ if (endpoint.type === 'sse')
173
+ return { warning: 'DSH requires Streamable HTTP; use proxy:true for SSE' };
174
+ return { entry: {
175
+ id: `mcp-compose-${name}`, name: MCP_CLIENT,
176
+ config: {
177
+ serverName: name, transport: 'streamable-http', url: endpoint.url,
178
+ ...(Object.keys(endpoint.headers).length > 0 ? { headers: endpoint.headers } : {}),
179
+ },
180
+ } };
181
+ }
182
+ preflight(entries) {
183
+ const manifestPath = join(this.profileDir, 'package.json');
184
+ const manifest = readSyncFile(manifestPath);
185
+ if (manifest === undefined)
186
+ throw new Error(`DSH profile does not exist: ${this.profileDir}; initialize it with DSH first.`);
187
+ try {
188
+ const value = JSON.parse(manifest);
189
+ if (!isRecord(value))
190
+ throw new Error();
191
+ }
192
+ catch {
193
+ throw new Error(`Invalid DSH profile manifest: ${manifestPath}.`);
194
+ }
195
+ if (entries.size > 0) {
196
+ try {
197
+ createRequire(manifestPath).resolve(MCP_CLIENT);
198
+ }
199
+ catch {
200
+ throw new Error(`Cannot resolve ${MCP_CLIENT} from ${this.profileDir}. Install the version matching your DSH runtime: `
201
+ + `dsh plugin --profile ${this.profile} add '${MCP_CLIENT}@<your-dsh-version>'.`);
202
+ }
203
+ }
204
+ }
205
+ }
206
+ export function createDeepSeekHarnessSyncTarget(home, profile, ownership = new FileSyncOwnershipStore()) {
207
+ return new FileConfigSyncTarget(new DeepSeekHarnessAdapter(home, profile), ownership);
208
+ }
209
+ //# sourceMappingURL=sync-deepseek.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-deepseek.js","sourceRoot":"","sources":["../../src/sync-deepseek.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { FileSyncOwnershipStore } from './sync-state.js';
2
+ import type { NormalizedConfig, TargetRemoveResult, TargetSyncResult } from './types.js';
3
+ export interface SyncEndpoint {
4
+ type: 'http' | 'sse';
5
+ url: string;
6
+ headers: Record<string, string>;
7
+ }
8
+ export interface SyncDocument<Entry> {
9
+ entries: ReadonlyMap<string, Entry>;
10
+ hasEntries: boolean;
11
+ validateNames?(names: ReadonlySet<string>): void;
12
+ render(entries: ReadonlyMap<string, Entry>): string;
13
+ }
14
+ export interface ConfigSyncAdapter<Entry> {
15
+ readonly target: string;
16
+ readonly path: string;
17
+ parse(source: string | undefined, owned: readonly string[]): SyncDocument<Entry>;
18
+ encode(name: string, endpoint: SyncEndpoint): {
19
+ entry: Entry;
20
+ } | {
21
+ warning: string;
22
+ };
23
+ preflight?(entries: ReadonlyMap<string, Entry>): void;
24
+ }
25
+ export interface PreparedConfigSync<Result> {
26
+ commit(): Result;
27
+ }
28
+ export interface ConfigSyncTarget {
29
+ preflight(config: NormalizedConfig): void;
30
+ prepare(config: NormalizedConfig): PreparedConfigSync<TargetSyncResult>;
31
+ prepareRemove(serverNames: string[] | null): PreparedConfigSync<TargetRemoveResult>;
32
+ apply(config: NormalizedConfig): TargetSyncResult;
33
+ remove(config: NormalizedConfig, serverNames: string[] | null): TargetRemoveResult;
34
+ }
35
+ export declare class FileConfigSyncTarget<Entry> implements ConfigSyncTarget {
36
+ private readonly adapter;
37
+ private readonly ownership;
38
+ readonly target: string;
39
+ readonly path: string;
40
+ constructor(adapter: ConfigSyncAdapter<Entry>, ownership?: FileSyncOwnershipStore);
41
+ preflight(config: NormalizedConfig): void;
42
+ prepare(config: NormalizedConfig): PreparedConfigSync<TargetSyncResult>;
43
+ prepareRemove(serverNames: string[] | null): PreparedConfigSync<TargetRemoveResult>;
44
+ apply(config: NormalizedConfig): TargetSyncResult;
45
+ remove(_config: NormalizedConfig, serverNames: string[] | null): TargetRemoveResult;
46
+ private plan;
47
+ }
48
+ //# sourceMappingURL=sync-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-engine.d.ts","sourceRoot":"","sources":["../../src/sync-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAiC,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AASD,MAAM,WAAW,YAAY,CAAC,KAAK;IACjC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACjD,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC;CACrD;AAED,MAAM,WAAW,iBAAiB,CAAC,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG;QAAE,KAAK,EAAE,KAAK,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrF,SAAS,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,kBAAkB,CAAC,MAAM;IACxC,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACxE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;IACpF,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IAClD,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,kBAAkB,CAAC;CACpF;AAED,qBAAa,oBAAoB,CAAC,KAAK,CAAE,YAAW,gBAAgB;IAItD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAA4B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAH1F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEO,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,EAAmB,SAAS,yBAA+B;IAKzH,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAIzC,OAAO,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAAC,gBAAgB,CAAC;IA6BvE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,kBAAkB,CAAC,kBAAkB,CAAC;IAanF,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB;IAIjD,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,kBAAkB;IAInF,OAAO,CAAC,IAAI;CA8Bb"}
@@ -0,0 +1,104 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { readSyncFile, restoreSyncFile, writeSyncFile } from './sync-file.js';
3
+ import { FileSyncOwnershipStore } from './sync-state.js';
4
+ function endpointFor(server) {
5
+ if (server.type === 'stdio' || server.type === 'proxy') {
6
+ return { type: 'http', url: `http://localhost:${String(server.internalPort)}/mcp`, headers: {} };
7
+ }
8
+ return { type: server.type, url: server.url, headers: server.headers };
9
+ }
10
+ export class FileConfigSyncTarget {
11
+ adapter;
12
+ ownership;
13
+ target;
14
+ path;
15
+ constructor(adapter, ownership = new FileSyncOwnershipStore()) {
16
+ this.adapter = adapter;
17
+ this.ownership = ownership;
18
+ this.target = adapter.target;
19
+ this.path = adapter.path;
20
+ }
21
+ preflight(config) {
22
+ this.prepare(config);
23
+ }
24
+ prepare(config) {
25
+ const source = readSyncFile(this.path);
26
+ const owned = this.ownership.read(this.path);
27
+ const ownedSet = new Set(owned);
28
+ const document = this.adapter.parse(source, owned);
29
+ const desired = new Map();
30
+ const warnings = [];
31
+ for (const [name, server] of Object.entries(config.mcpServers)) {
32
+ const encoded = this.adapter.encode(name, endpointFor(server));
33
+ if ('warning' in encoded)
34
+ warnings.push({ server: name, reason: encoded.warning });
35
+ else
36
+ desired.set(name, encoded.entry);
37
+ }
38
+ for (const name of desired.keys()) {
39
+ if (document.entries.has(name) && !ownedSet.has(name)) {
40
+ throw new Error(`Unmanaged MCP server "${name}" conflicts with mcp-compose in ${this.path}.`);
41
+ }
42
+ }
43
+ document.validateNames?.(new Set(desired.keys()));
44
+ this.adapter.preflight?.(desired);
45
+ const next = new Map([...document.entries].filter(([name]) => !ownedSet.has(name)));
46
+ for (const [name, entry] of desired)
47
+ next.set(name, entry);
48
+ const removed = [...document.entries.keys()].filter((name) => ownedSet.has(name) && !desired.has(name));
49
+ return this.plan(source, document, next, owned, [...desired.keys()], {
50
+ target: this.target, path: this.path, servers: [...desired.keys()],
51
+ removed, merged: document.hasEntries, warnings,
52
+ });
53
+ }
54
+ prepareRemove(serverNames) {
55
+ const source = readSyncFile(this.path);
56
+ const owned = this.ownership.read(this.path);
57
+ const document = this.adapter.parse(source, owned);
58
+ const requested = new Set(serverNames ?? owned);
59
+ const toRemove = new Set(owned.filter((name) => requested.has(name)));
60
+ const next = new Map([...document.entries].filter(([name]) => !toRemove.has(name)));
61
+ const removed = [...document.entries.keys()].filter((name) => toRemove.has(name));
62
+ return this.plan(source, document, next, owned, owned.filter((name) => !toRemove.has(name)), {
63
+ target: this.target, path: this.path, removed,
64
+ });
65
+ }
66
+ apply(config) {
67
+ return this.prepare(config).commit();
68
+ }
69
+ remove(_config, serverNames) {
70
+ return this.prepareRemove(serverNames).commit();
71
+ }
72
+ plan(source, document, next, owned, nextOwned, result) {
73
+ const content = isDeepStrictEqual(document.entries, next) ? source : document.render(next);
74
+ return {
75
+ commit: () => {
76
+ this.ownership.assertUnchanged(this.path, owned);
77
+ if (readSyncFile(this.path) !== source) {
78
+ throw new Error(`Config changed during sync: ${this.path}; retry the command.`);
79
+ }
80
+ if (content !== undefined)
81
+ writeSyncFile(this.path, content, source);
82
+ try {
83
+ if (!isDeepStrictEqual(owned, nextOwned))
84
+ this.ownership.replace(this.path, nextOwned, owned);
85
+ }
86
+ catch (error) {
87
+ // The config and ledger are separate files. Undo this target's write
88
+ // on a ledger failure, but never overwrite an intervening user edit.
89
+ if (content !== source && content !== undefined) {
90
+ try {
91
+ restoreSyncFile(this.path, source, content);
92
+ }
93
+ catch (rollbackError) {
94
+ throw new AggregateError([error, rollbackError], `Could not save ownership or restore ${this.path}; check sync-state.json before retrying.`);
95
+ }
96
+ }
97
+ throw error;
98
+ }
99
+ return result;
100
+ },
101
+ };
102
+ }
103
+ }
104
+ //# sourceMappingURL=sync-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/sync-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function readSyncFile(path: string): string | undefined;
2
+ export declare function writeSyncFile(path: string, content: string, expected: string | undefined): void;
3
+ export declare function restoreSyncFile(path: string, previous: string | undefined, expected: string): void;
4
+ //# sourceMappingURL=sync-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-file.d.ts","sourceRoot":"","sources":["../../src/sync-file.ts"],"names":[],"mappings":"AAOA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA8B/F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAOlG"}
@@ -0,0 +1,61 @@
1
+ import { existsSync, lstatSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, chmodSync, mkdirSync, } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { dirname, join, basename } from 'node:path';
4
+ export function readSyncFile(path) {
5
+ try {
6
+ return readFileSync(path, 'utf8');
7
+ }
8
+ catch (error) {
9
+ if (error.code === 'ENOENT')
10
+ return undefined;
11
+ throw error;
12
+ }
13
+ }
14
+ export function writeSyncFile(path, content, expected) {
15
+ if (readSyncFile(path) !== expected) {
16
+ throw new Error(`Config changed during sync: ${path}; retry the command.`);
17
+ }
18
+ if (content === expected)
19
+ return;
20
+ // Follow managed dotfile symlinks, but atomically replace their destination so
21
+ // a client or profile watcher can never read a half-written config.
22
+ let destination = path;
23
+ let symlink = false;
24
+ try {
25
+ symlink = lstatSync(path).isSymbolicLink();
26
+ }
27
+ catch (error) {
28
+ if (error.code !== 'ENOENT')
29
+ throw error;
30
+ if (expected !== undefined)
31
+ throw error;
32
+ }
33
+ if (symlink)
34
+ destination = realpathSync(path);
35
+ const mode = existsSync(destination) ? statSync(destination).mode & 0o777 : 0o600;
36
+ mkdirSync(dirname(destination), { recursive: true });
37
+ const temporary = join(dirname(destination), `.${basename(destination)}.${randomUUID()}.tmp`);
38
+ try {
39
+ writeFileSync(temporary, content, { flag: 'wx', mode: 0o600 });
40
+ chmodSync(temporary, mode);
41
+ if (readSyncFile(path) !== expected) {
42
+ throw new Error(`Config changed during sync: ${path}; retry the command.`);
43
+ }
44
+ renameSync(temporary, destination);
45
+ }
46
+ finally {
47
+ if (existsSync(temporary))
48
+ unlinkSync(temporary);
49
+ }
50
+ }
51
+ export function restoreSyncFile(path, previous, expected) {
52
+ if (previous !== undefined) {
53
+ writeSyncFile(path, previous, expected);
54
+ }
55
+ else {
56
+ if (readSyncFile(path) !== expected)
57
+ throw new Error(`Config changed during sync: ${path}; cannot restore it.`);
58
+ unlinkSync(path);
59
+ }
60
+ }
61
+ //# sourceMappingURL=sync-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-file.js","sourceRoot":"","sources":["../../src/sync-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare class FileSyncOwnershipStore {
2
+ private readonly path;
3
+ constructor(path?: string);
4
+ private load;
5
+ read(targetPath: string): string[];
6
+ assertUnchanged(targetPath: string, expected: readonly string[]): void;
7
+ private checkExpected;
8
+ replace(targetPath: string, names: readonly string[], expected?: readonly string[]): void;
9
+ }
10
+ //# sourceMappingURL=sync-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-state.d.ts","sourceRoot":"","sources":["../../src/sync-state.ts"],"names":[],"mappings":"AASA,qBAAa,sBAAsB;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,SAAqD;IAEtF,OAAO,CAAC,IAAI;IAgBZ,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIlC,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAItE,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;CAO1F"}
@@ -0,0 +1,50 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ import { readSyncFile, writeSyncFile } from './sync-file.js';
5
+ export class FileSyncOwnershipStore {
6
+ path;
7
+ constructor(path = join(homedir(), '.mcp-compose', 'sync-state.json')) {
8
+ this.path = path;
9
+ }
10
+ load(content) {
11
+ if (content === undefined)
12
+ return { targets: {} };
13
+ try {
14
+ const parsed = JSON.parse(content);
15
+ if (typeof parsed !== 'object' || parsed === null || !('targets' in parsed))
16
+ throw new Error();
17
+ const targets = parsed.targets;
18
+ if (typeof targets !== 'object' || targets === null || Array.isArray(targets))
19
+ throw new Error();
20
+ for (const names of Object.values(targets)) {
21
+ if (!Array.isArray(names) || !names.every((name) => typeof name === 'string'))
22
+ throw new Error();
23
+ }
24
+ return { targets: targets };
25
+ }
26
+ catch {
27
+ throw new Error(`Invalid sync ownership state: ${this.path}; restore it before syncing.`);
28
+ }
29
+ }
30
+ read(targetPath) {
31
+ return this.load(readSyncFile(this.path)).targets[targetPath] ?? [];
32
+ }
33
+ assertUnchanged(targetPath, expected) {
34
+ this.checkExpected(targetPath, this.read(targetPath), expected);
35
+ }
36
+ checkExpected(targetPath, current, expected) {
37
+ if (!isDeepStrictEqual(current, expected)) {
38
+ throw new Error(`Sync ownership changed during sync: ${targetPath}; retry the command.`);
39
+ }
40
+ }
41
+ replace(targetPath, names, expected) {
42
+ const content = readSyncFile(this.path);
43
+ const state = this.load(content);
44
+ if (expected !== undefined)
45
+ this.checkExpected(targetPath, state.targets[targetPath] ?? [], expected);
46
+ state.targets[targetPath] = [...names];
47
+ writeSyncFile(this.path, JSON.stringify(state, null, 2) + '\n', content);
48
+ }
49
+ }
50
+ //# sourceMappingURL=sync-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-state.js","sourceRoot":"","sources":["../../src/sync-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { FileSyncOwnershipStore } from './sync-state.js';
2
+ import type { NormalizedConfig, TargetRemoveResult, TargetSyncResult } from './types.js';
3
+ export type { ConfigSyncTarget } from './sync-engine.js';
4
+ export declare class ConfigSyncCoordinator {
5
+ private readonly targets;
6
+ constructor(config: NormalizedConfig, ownership?: FileSyncOwnershipStore);
7
+ preflight(config: NormalizedConfig): void;
8
+ apply(config: NormalizedConfig): TargetSyncResult[];
9
+ remove(_config: NormalizedConfig, names?: string[] | null): TargetRemoveResult[];
10
+ }
11
+ //# sourceMappingURL=sync-targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-targets.d.ts","sourceRoot":"","sources":["../../src/sync-targets.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEzF,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,MAAM,EAAE,gBAAgB,EAAE,SAAS,yBAA+B;IAU9E,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAIzC,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAKnD,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,GAAE,MAAM,EAAE,GAAG,IAAW,GAAG,kBAAkB,EAAE;CAIvF"}
@@ -0,0 +1,31 @@
1
+ import { createClaudeSyncTarget, createCodexSyncTarget } from './sync-clients.js';
2
+ import { createDeepSeekHarnessSyncTarget } from './sync-deepseek.js';
3
+ import { FileSyncOwnershipStore } from './sync-state.js';
4
+ export class ConfigSyncCoordinator {
5
+ targets;
6
+ constructor(config, ownership = new FileSyncOwnershipStore()) {
7
+ this.targets = [];
8
+ const { claude, codex, dsh } = config.settings;
9
+ if (claude && !claude.disabled)
10
+ this.targets.push(createClaudeSyncTarget(claude.configPath, ownership));
11
+ if (codex && !codex.disabled)
12
+ this.targets.push(createCodexSyncTarget(codex.configPath, ownership));
13
+ if (dsh && !dsh.disabled) {
14
+ for (const profile of dsh.profiles)
15
+ this.targets.push(createDeepSeekHarnessSyncTarget(dsh.home, profile, ownership));
16
+ }
17
+ }
18
+ preflight(config) {
19
+ for (const target of this.targets)
20
+ target.preflight(config);
21
+ }
22
+ apply(config) {
23
+ const plans = this.targets.map((target) => target.prepare(config));
24
+ return plans.map((plan) => plan.commit());
25
+ }
26
+ remove(_config, names = null) {
27
+ const plans = this.targets.map((target) => target.prepareRemove(names));
28
+ return plans.map((plan) => plan.commit());
29
+ }
30
+ }
31
+ //# sourceMappingURL=sync-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-targets.js","sourceRoot":"","sources":["../../src/sync-targets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAMzD,MAAM,OAAO,qBAAqB;IACf,OAAO,CAAqB;IAE7C,YAAY,MAAwB,EAAE,SAAS,GAAG,IAAI,sBAAsB,EAAE;QAC5E,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/C,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;QACxG,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;QACpG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACzB,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,QAAQ;gBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QACvH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,MAAwB;QAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,MAAwB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,OAAyB,EAAE,QAAyB,IAAI;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;CACF"}
@@ -1,8 +1,9 @@
1
+ import { FileSyncOwnershipStore } from './sync-state.js';
1
2
  import type { NormalizedConfig, SyncResult, RemoveResult } from './types.js';
2
- export declare function syncToClaudeConfig(config: NormalizedConfig): SyncResult;
3
- export declare function removeFromClaudeConfig(config: NormalizedConfig, serverNames?: string[] | null): RemoveResult;
4
- export declare function syncToCodexConfig(config: NormalizedConfig): SyncResult & {
3
+ export declare function syncToClaudeConfig(config: NormalizedConfig, ownership?: FileSyncOwnershipStore): SyncResult;
4
+ export declare function syncToCodexConfig(config: NormalizedConfig, ownership?: FileSyncOwnershipStore): SyncResult & {
5
5
  skipped: string[];
6
6
  };
7
- export declare function removeFromCodexConfig(config: NormalizedConfig, serverNames?: string[] | null): RemoveResult;
7
+ export declare function removeFromClaudeConfig(config: NormalizedConfig, serverNames?: string[] | null, ownership?: FileSyncOwnershipStore): RemoveResult;
8
+ export declare function removeFromCodexConfig(config: NormalizedConfig, serverNames?: string[] | null, ownership?: FileSyncOwnershipStore): RemoveResult;
8
9
  //# sourceMappingURL=sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/sync.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAKhB,UAAU,EACV,YAAY,EACb,MAAM,YAAY,CAAC;AA2EpB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CA+CvE;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EACxB,WAAW,GAAE,MAAM,EAAE,GAAG,IAAW,GAClC,YAAY,CA8Cd;AA4BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA8C9F;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,WAAW,GAAE,MAAM,EAAE,GAAG,IAAW,GAClC,YAAY,CA2Cd"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAoB,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAgBjH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,yBAA+B,GAAG,UAAU,CAIjH;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,yBAA+B,GAAG,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAKxI;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EAAE,WAAW,GAAE,MAAM,EAAE,GAAG,IAAW,EAAE,SAAS,yBAA+B,GACtG,YAAY,CAKd;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EAAE,WAAW,GAAE,MAAM,EAAE,GAAG,IAAW,EAAE,SAAS,yBAA+B,GACtG,YAAY,CAKd"}