engramx 2.0.2 → 3.0.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/CHANGELOG.md +271 -0
- package/README.md +161 -17
- package/dist/{aider-context-BC5R2ZTA.js → aider-context-6IDE3R7U.js} +1 -1
- package/dist/check-2Z3MPZEJ.js +12 -0
- package/dist/{chunk-PEH54LYC.js → chunk-645NBY6L.js} +42 -5
- package/dist/chunk-73IBCRFI.js +215 -0
- package/dist/{chunk-SJT7VS2G.js → chunk-B4UOE64J.js} +46 -11
- package/dist/chunk-FKY6HIT2.js +99 -0
- package/dist/{chunk-533LR4I7.js → chunk-G4U3QOOW.js} +13 -97
- package/dist/chunk-RJC6RNXJ.js +1405 -0
- package/dist/chunk-RM2TBOVW.js +121 -0
- package/dist/chunk-SMU4WR3D.js +187 -0
- package/dist/{chunk-C6GBUOAL.js → chunk-VLTWBTQ7.js} +14 -15
- package/dist/chunk-XVYE4OX2.js +232 -0
- package/dist/chunk-ZUC6OXSL.js +178 -0
- package/dist/cli.js +818 -1533
- package/dist/{core-6IY5L6II.js → core-77F2BVYV.js} +2 -2
- package/dist/{cursor-mdc-GJ7E5LDD.js → cursor-mdc-EEO7PYZ3.js} +1 -1
- package/dist/{exporter-GWU2GF23.js → exporter-ZYJ4WM2F.js} +1 -1
- package/dist/{importer-V62NGZRK.js → importer-4UWQDH4W.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/install-YVMVCFQW.js +121 -0
- package/dist/mcp-client-ROOJF76V.js +9 -0
- package/dist/mcp-config-QD4NPVXB.js +12 -0
- package/dist/{migrate-UKCO6BUU.js → migrate-KJ5K5NWO.js} +1 -1
- package/dist/notify-5POGKMRX.js +36 -0
- package/dist/{plugin-loader-STTGYIL5.js → plugin-loader-SQQB6V74.js} +69 -23
- package/dist/report-C3GTM3HY.js +12 -0
- package/dist/resolver-H7GXVP73.js +21 -0
- package/dist/serve.js +5 -4
- package/dist/{server-KUG7U6SG.js → server-2ZQKXJ5M.js} +74 -4
- package/dist/{windsurf-rules-C7SVDHBL.js → windsurf-rules-XF7MYF6J.js} +1 -1
- package/dist/wizard-UH27IO4I.js +274 -0
- package/package.json +3 -2
- package/dist/{tuner-KFNNGKG3.js → tuner-Y2YENAZC.js} +3 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/providers/mcp-config.ts
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
function getMcpConfigPath() {
|
|
6
|
+
const override = process.env.ENGRAM_MCP_CONFIG_PATH;
|
|
7
|
+
if (override && override.length > 0) return override;
|
|
8
|
+
return join(homedir(), ".engram", "mcp-providers.json");
|
|
9
|
+
}
|
|
10
|
+
function loadMcpConfigs(path = getMcpConfigPath()) {
|
|
11
|
+
if (!existsSync(path)) {
|
|
12
|
+
return { configs: [], failed: [] };
|
|
13
|
+
}
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = readFileSync(path, "utf-8");
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
configs: [],
|
|
20
|
+
failed: [
|
|
21
|
+
{
|
|
22
|
+
index: -1,
|
|
23
|
+
reason: `failed to read config file: ${err.message}`
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = JSON.parse(raw);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
configs: [],
|
|
34
|
+
failed: [
|
|
35
|
+
{
|
|
36
|
+
index: -1,
|
|
37
|
+
reason: `invalid JSON in ${path}: ${err.message}`
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!isMcpProvidersFile(parsed)) {
|
|
43
|
+
return {
|
|
44
|
+
configs: [],
|
|
45
|
+
failed: [
|
|
46
|
+
{
|
|
47
|
+
index: -1,
|
|
48
|
+
reason: `expected { providers: [...] } shape in ${path}`
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const valid = [];
|
|
54
|
+
const failed = [];
|
|
55
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
56
|
+
for (let i = 0; i < parsed.providers.length; i++) {
|
|
57
|
+
const entry = parsed.providers[i];
|
|
58
|
+
const validation = validateProviderConfig(entry);
|
|
59
|
+
if (validation.ok) {
|
|
60
|
+
if (seenNames.has(validation.value.name)) {
|
|
61
|
+
failed.push({
|
|
62
|
+
index: i,
|
|
63
|
+
reason: `duplicate provider name '${validation.value.name}' \u2014 first occurrence wins`
|
|
64
|
+
});
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
seenNames.add(validation.value.name);
|
|
68
|
+
valid.push(validation.value);
|
|
69
|
+
} else {
|
|
70
|
+
failed.push({ index: i, reason: validation.reason });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { configs: valid, failed };
|
|
74
|
+
}
|
|
75
|
+
function isMcpProvidersFile(v) {
|
|
76
|
+
if (!v || typeof v !== "object") return false;
|
|
77
|
+
const obj = v;
|
|
78
|
+
return Array.isArray(obj.providers);
|
|
79
|
+
}
|
|
80
|
+
function validateProviderConfig(raw) {
|
|
81
|
+
if (!raw || typeof raw !== "object") {
|
|
82
|
+
return { ok: false, reason: "entry is not an object" };
|
|
83
|
+
}
|
|
84
|
+
const o = raw;
|
|
85
|
+
if (typeof o.name !== "string" || o.name.length === 0) {
|
|
86
|
+
return { ok: false, reason: "`name` must be a non-empty string" };
|
|
87
|
+
}
|
|
88
|
+
if (typeof o.label !== "string" || o.label.length === 0) {
|
|
89
|
+
return { ok: false, reason: `[${o.name}] 'label' must be a non-empty string` };
|
|
90
|
+
}
|
|
91
|
+
if (o.transport !== "stdio" && o.transport !== "http") {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
reason: `[${o.name}] 'transport' must be 'stdio' or 'http'`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(o.tools)) {
|
|
98
|
+
return { ok: false, reason: `[${o.name}] 'tools' must be an array` };
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < o.tools.length; i++) {
|
|
101
|
+
const t = o.tools[i];
|
|
102
|
+
if (!t || typeof t.name !== "string" || t.name.length === 0) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
reason: `[${o.name}] tools[${i}].name must be a non-empty string`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (t.args !== void 0 && (typeof t.args !== "object" || t.args === null)) {
|
|
109
|
+
return { ok: false, reason: `[${o.name}] tools[${i}].args must be an object` };
|
|
110
|
+
}
|
|
111
|
+
if (t.confidence !== void 0) {
|
|
112
|
+
if (typeof t.confidence !== "number" || t.confidence < 0 || t.confidence > 1) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
reason: `[${o.name}] tools[${i}].confidence must be in [0, 1]`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (o.transport === "stdio") {
|
|
121
|
+
if (typeof o.command !== "string" || o.command.length === 0) {
|
|
122
|
+
return { ok: false, reason: `[${o.name}] 'command' required for stdio transport` };
|
|
123
|
+
}
|
|
124
|
+
if (o.args !== void 0 && !Array.isArray(o.args)) {
|
|
125
|
+
return { ok: false, reason: `[${o.name}] 'args' must be an array of strings` };
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
if (typeof o.url !== "string" || o.url.length === 0) {
|
|
129
|
+
return { ok: false, reason: `[${o.name}] 'url' required for http transport` };
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
new URL(o.url);
|
|
133
|
+
} catch {
|
|
134
|
+
return { ok: false, reason: `[${o.name}] 'url' is not a valid URL` };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const field of ["tokenBudget", "timeoutMs", "cacheTtlSec", "priority"]) {
|
|
138
|
+
if (o[field] !== void 0) {
|
|
139
|
+
if (typeof o[field] !== "number" || o[field] < 0) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
reason: `[${o.name}] '${field}' must be a non-negative number`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { ok: true, value: raw };
|
|
148
|
+
}
|
|
149
|
+
function applyArgTemplate(template, ctx) {
|
|
150
|
+
const defaults = { path: "{filePath}" };
|
|
151
|
+
const src = template ?? defaults;
|
|
152
|
+
const out = {};
|
|
153
|
+
const basename = ctx.fileBasename ?? ctx.filePath.split(/[\\/]/).pop() ?? ctx.filePath;
|
|
154
|
+
const tokens = {
|
|
155
|
+
filePath: ctx.filePath,
|
|
156
|
+
projectRoot: ctx.projectRoot,
|
|
157
|
+
imports: ctx.imports.join(","),
|
|
158
|
+
fileBasename: basename
|
|
159
|
+
};
|
|
160
|
+
for (const [key, value] of Object.entries(src)) {
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
out[key] = value.replace(
|
|
163
|
+
/\{(\w+)\}/g,
|
|
164
|
+
(match, token) => Object.prototype.hasOwnProperty.call(tokens, token) ? tokens[token] : match
|
|
165
|
+
);
|
|
166
|
+
} else {
|
|
167
|
+
out[key] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export {
|
|
174
|
+
getMcpConfigPath,
|
|
175
|
+
loadMcpConfigs,
|
|
176
|
+
validateProviderConfig,
|
|
177
|
+
applyArgTemplate
|
|
178
|
+
};
|