@teampitch/mcpx 0.2.1
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/LICENSE +179 -0
- package/README.md +33 -0
- package/mcpx.example.json +43 -0
- package/package.json +52 -0
- package/src/ast.test.ts +41 -0
- package/src/ast.ts +42 -0
- package/src/auth.test.ts +150 -0
- package/src/auth.ts +144 -0
- package/src/backends.test.ts +205 -0
- package/src/backends.ts +159 -0
- package/src/config.test.ts +149 -0
- package/src/config.ts +134 -0
- package/src/executor.test.ts +155 -0
- package/src/executor.ts +195 -0
- package/src/index.ts +364 -0
- package/src/init.ts +115 -0
- package/src/k8s-controller.test.ts +108 -0
- package/src/k8s-controller.ts +201 -0
- package/src/oauth.test.ts +232 -0
- package/src/oauth.ts +265 -0
- package/src/openapi.test.ts +223 -0
- package/src/openapi.ts +253 -0
- package/src/stdio.ts +176 -0
- package/src/watcher.ts +100 -0
package/src/watcher.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { watch } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { connectBackends, type Backend } from "./backends.js";
|
|
4
|
+
import { loadConfig, type McpxConfig, type BackendConfig } from "./config.js";
|
|
5
|
+
|
|
6
|
+
export interface ReloadResult {
|
|
7
|
+
added: string[];
|
|
8
|
+
removed: string[];
|
|
9
|
+
changed: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function diffBackends(
|
|
13
|
+
oldConfigs: Record<string, BackendConfig>,
|
|
14
|
+
newConfigs: Record<string, BackendConfig>,
|
|
15
|
+
): ReloadResult {
|
|
16
|
+
const added: string[] = [];
|
|
17
|
+
const removed: string[] = [];
|
|
18
|
+
const changed: string[] = [];
|
|
19
|
+
|
|
20
|
+
for (const name of Object.keys(newConfigs)) {
|
|
21
|
+
if (!(name in oldConfigs)) {
|
|
22
|
+
added.push(name);
|
|
23
|
+
} else if (JSON.stringify(oldConfigs[name]) !== JSON.stringify(newConfigs[name])) {
|
|
24
|
+
changed.push(name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const name of Object.keys(oldConfigs)) {
|
|
29
|
+
if (!(name in newConfigs)) {
|
|
30
|
+
removed.push(name);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { added, removed, changed };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Watch a config file and call onReload when it changes */
|
|
38
|
+
export function watchConfig(
|
|
39
|
+
configPath: string,
|
|
40
|
+
backends: Map<string, Backend>,
|
|
41
|
+
onReload: (config: McpxConfig, result: ReloadResult) => void,
|
|
42
|
+
): () => void {
|
|
43
|
+
let currentConfig = loadConfig(configPath);
|
|
44
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
45
|
+
|
|
46
|
+
const watcher = watch(configPath, () => {
|
|
47
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
48
|
+
debounceTimer = setTimeout(async () => {
|
|
49
|
+
try {
|
|
50
|
+
const newConfig = loadConfig(configPath);
|
|
51
|
+
const diff = diffBackends(currentConfig.backends, newConfig.backends);
|
|
52
|
+
|
|
53
|
+
if (!diff.added.length && !diff.removed.length && !diff.changed.length) {
|
|
54
|
+
return; // No backend changes
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Remove deleted backends
|
|
58
|
+
for (const name of diff.removed) {
|
|
59
|
+
const backend = backends.get(name);
|
|
60
|
+
if (backend) {
|
|
61
|
+
await backend.client.close().catch(() => {});
|
|
62
|
+
backends.delete(name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Disconnect changed backends
|
|
67
|
+
for (const name of diff.changed) {
|
|
68
|
+
const backend = backends.get(name);
|
|
69
|
+
if (backend) {
|
|
70
|
+
await backend.client.close().catch(() => {});
|
|
71
|
+
backends.delete(name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Connect new + changed backends
|
|
76
|
+
const toConnect: Record<string, BackendConfig> = {};
|
|
77
|
+
for (const name of [...diff.added, ...diff.changed]) {
|
|
78
|
+
toConnect[name] = newConfig.backends[name];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Object.keys(toConnect).length > 0) {
|
|
82
|
+
const newBackends = await connectBackends(toConnect);
|
|
83
|
+
for (const [name, backend] of newBackends) {
|
|
84
|
+
backends.set(name, backend);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
currentConfig = newConfig;
|
|
89
|
+
onReload(newConfig, diff);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error("Config reload failed:", (err as Error).message);
|
|
92
|
+
}
|
|
93
|
+
}, 500);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return () => {
|
|
97
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
98
|
+
watcher.close();
|
|
99
|
+
};
|
|
100
|
+
}
|