add-mcp 1.7.0 → 1.9.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 +72 -2
- package/dist/index.js +1048 -23
- package/package.json +8 -6
package/dist/index.js
CHANGED
|
@@ -816,6 +816,234 @@ function isRemoteSource(parsed) {
|
|
|
816
816
|
|
|
817
817
|
// src/find.ts
|
|
818
818
|
import * as p2 from "@clack/prompts";
|
|
819
|
+
|
|
820
|
+
// src/template.ts
|
|
821
|
+
var TEMPLATE_PATTERN = /\$\{([^}]+)\}/g;
|
|
822
|
+
function findTemplateVars(value) {
|
|
823
|
+
const vars = [];
|
|
824
|
+
let match;
|
|
825
|
+
while ((match = TEMPLATE_PATTERN.exec(value)) !== null) {
|
|
826
|
+
vars.push(match[1]);
|
|
827
|
+
}
|
|
828
|
+
TEMPLATE_PATTERN.lastIndex = 0;
|
|
829
|
+
return vars;
|
|
830
|
+
}
|
|
831
|
+
function substituteTemplatePlaceholders(value, replacement) {
|
|
832
|
+
return value.replace(/\$\{([^}]+)\}/g, () => replacement);
|
|
833
|
+
}
|
|
834
|
+
async function resolveTemplates(value, ask) {
|
|
835
|
+
const vars = findTemplateVars(value);
|
|
836
|
+
if (vars.length === 0) return { resolved: value, cancelled: false };
|
|
837
|
+
let result = value;
|
|
838
|
+
for (const varName of vars) {
|
|
839
|
+
const answer = await ask(varName);
|
|
840
|
+
if (typeof answer === "symbol") return { resolved: value, cancelled: true };
|
|
841
|
+
const entered = typeof answer === "string" ? answer : "";
|
|
842
|
+
result = result.replace(`\${${varName}}`, entered);
|
|
843
|
+
}
|
|
844
|
+
return { resolved: result, cancelled: false };
|
|
845
|
+
}
|
|
846
|
+
async function resolveRecordTemplates(record, ask) {
|
|
847
|
+
const resolved = {};
|
|
848
|
+
for (const [key, value] of Object.entries(record)) {
|
|
849
|
+
const result = await resolveTemplates(value, ask);
|
|
850
|
+
if (result.cancelled) return { resolved: record, cancelled: true };
|
|
851
|
+
resolved[key] = result.resolved;
|
|
852
|
+
}
|
|
853
|
+
return { resolved, cancelled: false };
|
|
854
|
+
}
|
|
855
|
+
async function resolveArrayTemplates(values, ask) {
|
|
856
|
+
const resolved = [];
|
|
857
|
+
for (const value of values) {
|
|
858
|
+
const result = await resolveTemplates(value, ask);
|
|
859
|
+
if (result.cancelled) return { resolved: values, cancelled: true };
|
|
860
|
+
resolved.push(result.resolved);
|
|
861
|
+
}
|
|
862
|
+
return { resolved, cancelled: false };
|
|
863
|
+
}
|
|
864
|
+
function hasTemplateVars(values) {
|
|
865
|
+
const strings = Array.isArray(values) ? values : Object.values(values);
|
|
866
|
+
return strings.some((v) => findTemplateVars(v).length > 0);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/package-arguments.ts
|
|
870
|
+
var ARG_PLACEHOLDER = "<your-variable-value-here>";
|
|
871
|
+
function normalizeNamedFlag(name) {
|
|
872
|
+
const t = name.trim();
|
|
873
|
+
if (t.startsWith("-")) return t;
|
|
874
|
+
return `--${t}`;
|
|
875
|
+
}
|
|
876
|
+
function definitionsFromPackage(pkg) {
|
|
877
|
+
return pkg.packageArguments ?? [];
|
|
878
|
+
}
|
|
879
|
+
function isNamedArg(d) {
|
|
880
|
+
if (d.type === "positional") return false;
|
|
881
|
+
if (d.type === "named") return true;
|
|
882
|
+
return Boolean(d.name?.trim());
|
|
883
|
+
}
|
|
884
|
+
function labelForArg(d, index) {
|
|
885
|
+
if (isNamedArg(d)) {
|
|
886
|
+
return `Flag ${normalizeNamedFlag(d.name)}`;
|
|
887
|
+
}
|
|
888
|
+
return d.valueHint?.trim() || d.description?.trim() || `Positional argument #${index + 1}`;
|
|
889
|
+
}
|
|
890
|
+
function hasTemplates(s) {
|
|
891
|
+
return Boolean(s && findTemplateVars(s).length > 0);
|
|
892
|
+
}
|
|
893
|
+
function buildPackageArgumentsArgvNonInteractive(definitions, placeholder = ARG_PLACEHOLDER) {
|
|
894
|
+
const out = [];
|
|
895
|
+
for (let i = 0; i < definitions.length; i++) {
|
|
896
|
+
const d = definitions[i];
|
|
897
|
+
if (isNamedArg(d)) {
|
|
898
|
+
const flag = normalizeNamedFlag(d.name);
|
|
899
|
+
let val = d.value?.trim() ?? "";
|
|
900
|
+
if (hasTemplates(d.value)) {
|
|
901
|
+
val = substituteTemplatePlaceholders(d.value, placeholder);
|
|
902
|
+
} else if (!val && d.default?.trim()) {
|
|
903
|
+
val = d.default.trim();
|
|
904
|
+
}
|
|
905
|
+
if (!val) {
|
|
906
|
+
if (d.isRequired === true) {
|
|
907
|
+
out.push(flag, placeholder);
|
|
908
|
+
}
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
out.push(flag, val);
|
|
912
|
+
} else {
|
|
913
|
+
let val = d.value?.trim() ?? "";
|
|
914
|
+
if (hasTemplates(d.value)) {
|
|
915
|
+
val = substituteTemplatePlaceholders(d.value, placeholder);
|
|
916
|
+
pushPositionalSegments(out, d, val);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (val) {
|
|
920
|
+
pushPositionalSegments(out, d, val);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (d.default?.trim()) {
|
|
924
|
+
pushPositionalSegments(out, d, d.default.trim());
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
pushPositionalSegments(out, d, placeholder);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return out;
|
|
931
|
+
}
|
|
932
|
+
function pushPositionalSegments(out, d, raw) {
|
|
933
|
+
if (d.isRepeated === true) {
|
|
934
|
+
const parts = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
935
|
+
if (parts.length === 0) {
|
|
936
|
+
out.push(raw);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
for (const p4 of parts) {
|
|
940
|
+
out.push(p4);
|
|
941
|
+
}
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
out.push(raw);
|
|
945
|
+
}
|
|
946
|
+
async function buildPackageArgumentsArgvInteractive(definitions, prompt) {
|
|
947
|
+
const out = [];
|
|
948
|
+
for (let i = 0; i < definitions.length; i++) {
|
|
949
|
+
const d = definitions[i];
|
|
950
|
+
const label = labelForArg(d, i);
|
|
951
|
+
if (isNamedArg(d)) {
|
|
952
|
+
const flag = normalizeNamedFlag(d.name);
|
|
953
|
+
let val2 = d.value ?? "";
|
|
954
|
+
if (hasTemplates(d.value)) {
|
|
955
|
+
const res = await resolveTemplates(d.value, async (varName) => {
|
|
956
|
+
const answer = await prompt({
|
|
957
|
+
label: `${label} (${varName})`,
|
|
958
|
+
isRequired: true,
|
|
959
|
+
placeholder: `<${varName}>`
|
|
960
|
+
});
|
|
961
|
+
return answer;
|
|
962
|
+
});
|
|
963
|
+
if (res.cancelled) return { cancelled: true };
|
|
964
|
+
val2 = res.resolved;
|
|
965
|
+
out.push(flag, val2);
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
if (val2.trim()) {
|
|
969
|
+
out.push(flag, val2.trim());
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
const defVal2 = d.default?.trim() ?? "";
|
|
973
|
+
const raw2 = await prompt({
|
|
974
|
+
label,
|
|
975
|
+
isRequired: d.isRequired === true,
|
|
976
|
+
placeholder: defVal2 || ARG_PLACEHOLDER
|
|
977
|
+
});
|
|
978
|
+
if (typeof raw2 === "symbol") return { cancelled: true };
|
|
979
|
+
const trimmed2 = typeof raw2 === "string" ? raw2.trim() : "";
|
|
980
|
+
if (!trimmed2) {
|
|
981
|
+
if (d.isRequired === true) {
|
|
982
|
+
out.push(flag, defVal2 || ARG_PLACEHOLDER);
|
|
983
|
+
} else if (defVal2) {
|
|
984
|
+
out.push(flag, defVal2);
|
|
985
|
+
}
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
out.push(flag, trimmed2);
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
let val = d.value ?? "";
|
|
992
|
+
if (hasTemplates(d.value)) {
|
|
993
|
+
const res = await resolveTemplates(d.value, async (varName) => {
|
|
994
|
+
const answer = await prompt({
|
|
995
|
+
label: `${label} (${varName})`,
|
|
996
|
+
isRequired: true,
|
|
997
|
+
placeholder: `<${varName}>`
|
|
998
|
+
});
|
|
999
|
+
return answer;
|
|
1000
|
+
});
|
|
1001
|
+
if (res.cancelled) return { cancelled: true };
|
|
1002
|
+
val = res.resolved;
|
|
1003
|
+
pushPositionalSegments(out, d, val);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
if (val.trim()) {
|
|
1007
|
+
pushPositionalSegments(out, d, val.trim());
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
const defVal = d.default?.trim() ?? "";
|
|
1011
|
+
if (defVal) {
|
|
1012
|
+
pushPositionalSegments(out, d, defVal);
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (d.isRequired !== true) {
|
|
1016
|
+
const raw2 = await prompt({
|
|
1017
|
+
label,
|
|
1018
|
+
isRequired: false,
|
|
1019
|
+
placeholder: ARG_PLACEHOLDER
|
|
1020
|
+
});
|
|
1021
|
+
if (typeof raw2 === "symbol") return { cancelled: true };
|
|
1022
|
+
const trimmed2 = typeof raw2 === "string" ? raw2.trim() : "";
|
|
1023
|
+
if (!trimmed2) {
|
|
1024
|
+
pushPositionalSegments(out, d, ARG_PLACEHOLDER);
|
|
1025
|
+
} else {
|
|
1026
|
+
pushPositionalSegments(out, d, trimmed2);
|
|
1027
|
+
}
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
const raw = await prompt({
|
|
1031
|
+
label,
|
|
1032
|
+
isRequired: true,
|
|
1033
|
+
placeholder: ARG_PLACEHOLDER
|
|
1034
|
+
});
|
|
1035
|
+
if (typeof raw === "symbol") return { cancelled: true };
|
|
1036
|
+
const trimmed = typeof raw === "string" ? raw.trim() : "";
|
|
1037
|
+
pushPositionalSegments(
|
|
1038
|
+
out,
|
|
1039
|
+
d,
|
|
1040
|
+
trimmed.length > 0 ? trimmed : ARG_PLACEHOLDER
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
return { argv: out, cancelled: false };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/find.ts
|
|
819
1047
|
function buildPlaceholderValue(kind) {
|
|
820
1048
|
return kind === "header" ? "<your-header-value-here>" : "<your-variable-value-here>";
|
|
821
1049
|
}
|
|
@@ -1009,9 +1237,6 @@ function pickRemote(entry, preferredTransport) {
|
|
|
1009
1237
|
return preferred ?? remotes[0] ?? null;
|
|
1010
1238
|
}
|
|
1011
1239
|
function formatPackageTarget(pkg) {
|
|
1012
|
-
if (pkg.version) {
|
|
1013
|
-
return `${pkg.identifier}@${pkg.version}`;
|
|
1014
|
-
}
|
|
1015
1240
|
return pkg.identifier;
|
|
1016
1241
|
}
|
|
1017
1242
|
function transportLabel(entry) {
|
|
@@ -1064,6 +1289,15 @@ function headerFields(headers) {
|
|
|
1064
1289
|
placeholder: buildPlaceholderValue("header")
|
|
1065
1290
|
}));
|
|
1066
1291
|
}
|
|
1292
|
+
function packageVariableFields(variables) {
|
|
1293
|
+
if (!variables) return [];
|
|
1294
|
+
return variables.filter((variable) => variable.name && variable.name.trim().length > 0).map((variable) => ({
|
|
1295
|
+
key: variable.name,
|
|
1296
|
+
label: `Environment variable ${variable.name}`,
|
|
1297
|
+
isRequired: variable.isRequired === true,
|
|
1298
|
+
placeholder: buildPlaceholderValue("variable")
|
|
1299
|
+
}));
|
|
1300
|
+
}
|
|
1067
1301
|
function resolveNonInteractiveRemote(remote) {
|
|
1068
1302
|
const variableValues = {};
|
|
1069
1303
|
for (const [key, def] of Object.entries(remote.variables ?? {})) {
|
|
@@ -1098,6 +1332,55 @@ async function resolveInteractiveRemote(remote) {
|
|
|
1098
1332
|
headers: Object.keys(headerResult.values).length > 0 ? headerResult.values : void 0
|
|
1099
1333
|
};
|
|
1100
1334
|
}
|
|
1335
|
+
function resolveNonInteractivePackage(pkg) {
|
|
1336
|
+
const env = {};
|
|
1337
|
+
for (const field of packageVariableFields(pkg.environmentVariables)) {
|
|
1338
|
+
if (field.isRequired) {
|
|
1339
|
+
env[field.key] = field.placeholder;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
const headers = {};
|
|
1343
|
+
for (const field of headerFields(pkg.headers)) {
|
|
1344
|
+
if (field.isRequired) {
|
|
1345
|
+
headers[field.key] = field.placeholder;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
const argv = buildPackageArgumentsArgvNonInteractive(
|
|
1349
|
+
definitionsFromPackage(pkg),
|
|
1350
|
+
buildPlaceholderValue("variable")
|
|
1351
|
+
);
|
|
1352
|
+
return {
|
|
1353
|
+
env: Object.keys(env).length > 0 ? env : void 0,
|
|
1354
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
1355
|
+
args: argv.length > 0 ? argv : void 0
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
async function resolveInteractivePackage(pkg) {
|
|
1359
|
+
const promptPackageArg = async (info) => p2.text({
|
|
1360
|
+
message: `${info.label} ${info.isRequired ? "(required)" : "(optional)"}`,
|
|
1361
|
+
placeholder: info.placeholder
|
|
1362
|
+
});
|
|
1363
|
+
const envResult = await collectPromptValues(
|
|
1364
|
+
packageVariableFields(pkg.environmentVariables),
|
|
1365
|
+
promptValue
|
|
1366
|
+
);
|
|
1367
|
+
if (envResult.cancelled) return null;
|
|
1368
|
+
const headerResult = await collectPromptValues(
|
|
1369
|
+
headerFields(pkg.headers),
|
|
1370
|
+
promptValue
|
|
1371
|
+
);
|
|
1372
|
+
if (headerResult.cancelled) return null;
|
|
1373
|
+
const argvResult = await buildPackageArgumentsArgvInteractive(
|
|
1374
|
+
definitionsFromPackage(pkg),
|
|
1375
|
+
promptPackageArg
|
|
1376
|
+
);
|
|
1377
|
+
if (argvResult.cancelled) return null;
|
|
1378
|
+
return {
|
|
1379
|
+
env: Object.keys(envResult.values).length > 0 ? envResult.values : void 0,
|
|
1380
|
+
headers: Object.keys(headerResult.values).length > 0 ? headerResult.values : void 0,
|
|
1381
|
+
args: argvResult.argv.length > 0 ? argvResult.argv : void 0
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1101
1384
|
async function buildInstallPlanForEntry(entry, options) {
|
|
1102
1385
|
const remote = pickRemote(entry, options.preferredTransport);
|
|
1103
1386
|
const pkg = entry.package ?? null;
|
|
@@ -1130,9 +1413,14 @@ async function buildInstallPlanForEntry(entry, options) {
|
|
|
1130
1413
|
mode = hasRemote ? "remote" : "package";
|
|
1131
1414
|
}
|
|
1132
1415
|
if (mode === "package" && pkg) {
|
|
1416
|
+
const resolved2 = options.yes ? resolveNonInteractivePackage(pkg) : await resolveInteractivePackage(pkg);
|
|
1417
|
+
if (!resolved2) return null;
|
|
1133
1418
|
return {
|
|
1134
1419
|
target: formatPackageTarget(pkg),
|
|
1135
|
-
serverName: resolveServerName(entry)
|
|
1420
|
+
serverName: resolveServerName(entry),
|
|
1421
|
+
env: resolved2.env,
|
|
1422
|
+
headers: resolved2.headers,
|
|
1423
|
+
args: resolved2.args
|
|
1136
1424
|
};
|
|
1137
1425
|
}
|
|
1138
1426
|
if (!remote) return null;
|
|
@@ -1306,13 +1594,13 @@ function getNestedValue(obj, path) {
|
|
|
1306
1594
|
}
|
|
1307
1595
|
|
|
1308
1596
|
// src/formats/json.ts
|
|
1309
|
-
function detectIndent(
|
|
1597
|
+
function detectIndent(text3) {
|
|
1310
1598
|
let result = null;
|
|
1311
|
-
jsonc.visit(
|
|
1599
|
+
jsonc.visit(text3, {
|
|
1312
1600
|
onObjectProperty: (_property, offset, _length, startLine, startCharacter) => {
|
|
1313
1601
|
if (result === null && startLine > 0 && startCharacter > 0) {
|
|
1314
|
-
const lineStart =
|
|
1315
|
-
const whitespace =
|
|
1602
|
+
const lineStart = text3.lastIndexOf("\n", offset - 1) + 1;
|
|
1603
|
+
const whitespace = text3.slice(lineStart, offset);
|
|
1316
1604
|
result = {
|
|
1317
1605
|
tabSize: startCharacter,
|
|
1318
1606
|
insertSpaces: !whitespace.includes(" ")
|
|
@@ -1322,6 +1610,14 @@ function detectIndent(text2) {
|
|
|
1322
1610
|
});
|
|
1323
1611
|
return result || { tabSize: 2, insertSpaces: true };
|
|
1324
1612
|
}
|
|
1613
|
+
function readJsonConfig(filePath) {
|
|
1614
|
+
if (!existsSync2(filePath)) {
|
|
1615
|
+
return {};
|
|
1616
|
+
}
|
|
1617
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1618
|
+
const parsed = jsonc.parse(content);
|
|
1619
|
+
return parsed;
|
|
1620
|
+
}
|
|
1325
1621
|
function writeJsonConfig(filePath, config, configKey) {
|
|
1326
1622
|
const dir = dirname3(filePath);
|
|
1327
1623
|
if (!existsSync2(dir)) {
|
|
@@ -1349,6 +1645,31 @@ function writeJsonConfig(filePath, config, configKey) {
|
|
|
1349
1645
|
}
|
|
1350
1646
|
writeFileSync(filePath, JSON.stringify(mergedConfig, null, 2));
|
|
1351
1647
|
}
|
|
1648
|
+
function removeJsonConfigKey(filePath, configKey, serverName) {
|
|
1649
|
+
if (!existsSync2(filePath)) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
const originalContent = readFileSync(filePath, "utf-8");
|
|
1653
|
+
const configKeyPath = configKey.split(".");
|
|
1654
|
+
try {
|
|
1655
|
+
const edits = jsonc.modify(
|
|
1656
|
+
originalContent,
|
|
1657
|
+
[...configKeyPath, serverName],
|
|
1658
|
+
void 0,
|
|
1659
|
+
{ formattingOptions: detectIndent(originalContent) }
|
|
1660
|
+
);
|
|
1661
|
+
const updatedContent = jsonc.applyEdits(originalContent, edits);
|
|
1662
|
+
writeFileSync(filePath, updatedContent);
|
|
1663
|
+
return;
|
|
1664
|
+
} catch {
|
|
1665
|
+
}
|
|
1666
|
+
const parsed = jsonc.parse(originalContent);
|
|
1667
|
+
const servers = getNestedValue(parsed, configKey);
|
|
1668
|
+
if (servers && typeof servers === "object" && serverName in servers) {
|
|
1669
|
+
delete servers[serverName];
|
|
1670
|
+
}
|
|
1671
|
+
writeFileSync(filePath, JSON.stringify(parsed, null, 2));
|
|
1672
|
+
}
|
|
1352
1673
|
function setNestedValue(obj, path, value) {
|
|
1353
1674
|
const keys = path.split(".");
|
|
1354
1675
|
const lastKey = keys.pop();
|
|
@@ -1375,6 +1696,30 @@ function readYamlConfig(filePath) {
|
|
|
1375
1696
|
const parsed = yaml.load(content);
|
|
1376
1697
|
return parsed || {};
|
|
1377
1698
|
}
|
|
1699
|
+
function removeYamlConfigKey(filePath, configKey, serverName) {
|
|
1700
|
+
if (!existsSync3(filePath)) {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
const existing = readYamlConfig(filePath);
|
|
1704
|
+
const keys = configKey.split(".");
|
|
1705
|
+
let current = existing;
|
|
1706
|
+
for (const key of keys) {
|
|
1707
|
+
if (current && typeof current === "object" && key in current) {
|
|
1708
|
+
current = current[key];
|
|
1709
|
+
} else {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
if (current && typeof current === "object" && serverName in current) {
|
|
1714
|
+
delete current[serverName];
|
|
1715
|
+
}
|
|
1716
|
+
const content = yaml.dump(existing, {
|
|
1717
|
+
indent: 2,
|
|
1718
|
+
lineWidth: -1,
|
|
1719
|
+
noRefs: true
|
|
1720
|
+
});
|
|
1721
|
+
writeFileSync2(filePath, content);
|
|
1722
|
+
}
|
|
1378
1723
|
function writeYamlConfig(filePath, config) {
|
|
1379
1724
|
const dir = dirname4(filePath);
|
|
1380
1725
|
if (!existsSync3(dir)) {
|
|
@@ -1405,6 +1750,26 @@ function readTomlConfig(filePath) {
|
|
|
1405
1750
|
const parsed = TOML.parse(content);
|
|
1406
1751
|
return parsed;
|
|
1407
1752
|
}
|
|
1753
|
+
function removeTomlConfigKey(filePath, configKey, serverName) {
|
|
1754
|
+
if (!existsSync4(filePath)) {
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
const existing = readTomlConfig(filePath);
|
|
1758
|
+
const keys = configKey.split(".");
|
|
1759
|
+
let current = existing;
|
|
1760
|
+
for (const key of keys) {
|
|
1761
|
+
if (current && typeof current === "object" && key in current) {
|
|
1762
|
+
current = current[key];
|
|
1763
|
+
} else {
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (current && typeof current === "object" && serverName in current) {
|
|
1768
|
+
delete current[serverName];
|
|
1769
|
+
}
|
|
1770
|
+
const content = TOML.stringify(existing);
|
|
1771
|
+
writeFileSync3(filePath, content);
|
|
1772
|
+
}
|
|
1408
1773
|
function writeTomlConfig(filePath, config) {
|
|
1409
1774
|
const dir = dirname5(filePath);
|
|
1410
1775
|
if (!existsSync4(dir)) {
|
|
@@ -1420,6 +1785,33 @@ function writeTomlConfig(filePath, config) {
|
|
|
1420
1785
|
}
|
|
1421
1786
|
|
|
1422
1787
|
// src/formats/index.ts
|
|
1788
|
+
function readConfig2(filePath, format) {
|
|
1789
|
+
switch (format) {
|
|
1790
|
+
case "json":
|
|
1791
|
+
return readJsonConfig(filePath);
|
|
1792
|
+
case "yaml":
|
|
1793
|
+
return readYamlConfig(filePath);
|
|
1794
|
+
case "toml":
|
|
1795
|
+
return readTomlConfig(filePath);
|
|
1796
|
+
default:
|
|
1797
|
+
throw new Error(`Unsupported config format: ${format}`);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function removeServerFromConfig(filePath, format, configKey, serverName) {
|
|
1801
|
+
switch (format) {
|
|
1802
|
+
case "json":
|
|
1803
|
+
removeJsonConfigKey(filePath, configKey, serverName);
|
|
1804
|
+
break;
|
|
1805
|
+
case "yaml":
|
|
1806
|
+
removeYamlConfigKey(filePath, configKey, serverName);
|
|
1807
|
+
break;
|
|
1808
|
+
case "toml":
|
|
1809
|
+
removeTomlConfigKey(filePath, configKey, serverName);
|
|
1810
|
+
break;
|
|
1811
|
+
default:
|
|
1812
|
+
throw new Error(`Unsupported config format: ${format}`);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1423
1815
|
function writeConfig2(filePath, config, format, configKey) {
|
|
1424
1816
|
switch (format) {
|
|
1425
1817
|
case "json":
|
|
@@ -1458,7 +1850,7 @@ function buildServerConfig(parsed, options = {}) {
|
|
|
1458
1850
|
if (parsed.type === "command") {
|
|
1459
1851
|
const parts = parsed.value.split(" ");
|
|
1460
1852
|
const command = parts[0];
|
|
1461
|
-
const args = parts.slice(1);
|
|
1853
|
+
const args = [...parts.slice(1), ...options.args ?? []];
|
|
1462
1854
|
const config2 = {
|
|
1463
1855
|
command,
|
|
1464
1856
|
args
|
|
@@ -1470,7 +1862,7 @@ function buildServerConfig(parsed, options = {}) {
|
|
|
1470
1862
|
}
|
|
1471
1863
|
const config = {
|
|
1472
1864
|
command: "npx",
|
|
1473
|
-
args: ["-y", parsed.value]
|
|
1865
|
+
args: ["-y", parsed.value, ...options.args ?? []]
|
|
1474
1866
|
};
|
|
1475
1867
|
if (options.env && Object.keys(options.env).length > 0) {
|
|
1476
1868
|
config.env = options.env;
|
|
@@ -1574,10 +1966,126 @@ function installServer(serverName, serverConfig, agentTypes, options = {}) {
|
|
|
1574
1966
|
return results;
|
|
1575
1967
|
}
|
|
1576
1968
|
|
|
1969
|
+
// src/reader.ts
|
|
1970
|
+
function extractServerIdentity(serverConfig) {
|
|
1971
|
+
for (const key of ["url", "uri", "serverUrl"]) {
|
|
1972
|
+
const value = serverConfig[key];
|
|
1973
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1974
|
+
return value;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
const command = typeof serverConfig.command === "string" ? serverConfig.command : typeof serverConfig.cmd === "string" ? serverConfig.cmd : void 0;
|
|
1978
|
+
if (!command) {
|
|
1979
|
+
return "";
|
|
1980
|
+
}
|
|
1981
|
+
const rawArgs = Array.isArray(serverConfig.args) ? serverConfig.args.filter((a) => typeof a === "string") : Array.isArray(serverConfig.command) ? serverConfig.command.slice(1).filter((a) => typeof a === "string") : [];
|
|
1982
|
+
if (command === "npx" || command === "bunx") {
|
|
1983
|
+
const yIndex = rawArgs.indexOf("-y");
|
|
1984
|
+
const pkgIndex = yIndex >= 0 ? yIndex + 1 : 0;
|
|
1985
|
+
const pkg = rawArgs[pkgIndex];
|
|
1986
|
+
if (pkg && !pkg.startsWith("-")) {
|
|
1987
|
+
return pkg;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
if (Array.isArray(serverConfig.command)) {
|
|
1991
|
+
return serverConfig.command.join(" ");
|
|
1992
|
+
}
|
|
1993
|
+
if (rawArgs.length > 0) {
|
|
1994
|
+
return `${command} ${rawArgs.join(" ")}`;
|
|
1995
|
+
}
|
|
1996
|
+
return command;
|
|
1997
|
+
}
|
|
1998
|
+
function readServersForAgent(agentType, options) {
|
|
1999
|
+
const agent = agents[agentType];
|
|
2000
|
+
const installOptions = {
|
|
2001
|
+
local: options.scope === "local",
|
|
2002
|
+
cwd: options.cwd
|
|
2003
|
+
};
|
|
2004
|
+
const configPath = getConfigPath2(agent, installOptions);
|
|
2005
|
+
const configKey = getConfigKey(agent, installOptions);
|
|
2006
|
+
const fullConfig = readConfig2(configPath, agent.format);
|
|
2007
|
+
const serversObj = getNestedValue(fullConfig, configKey);
|
|
2008
|
+
const servers = [];
|
|
2009
|
+
if (serversObj && typeof serversObj === "object" && !Array.isArray(serversObj)) {
|
|
2010
|
+
for (const [serverName, serverConfig] of Object.entries(serversObj)) {
|
|
2011
|
+
if (serverConfig && typeof serverConfig === "object") {
|
|
2012
|
+
const config = serverConfig;
|
|
2013
|
+
servers.push({
|
|
2014
|
+
serverName,
|
|
2015
|
+
config,
|
|
2016
|
+
identity: extractServerIdentity(config),
|
|
2017
|
+
agentType,
|
|
2018
|
+
scope: options.scope,
|
|
2019
|
+
configPath
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
return {
|
|
2025
|
+
agentType,
|
|
2026
|
+
displayName: agent.displayName,
|
|
2027
|
+
detected: true,
|
|
2028
|
+
scope: options.scope,
|
|
2029
|
+
configPath,
|
|
2030
|
+
servers
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
async function gatherInstalledServers(options) {
|
|
2034
|
+
const scope = options.global ? "global" : "local";
|
|
2035
|
+
const results = [];
|
|
2036
|
+
if (options.agents && options.agents.length > 0) {
|
|
2037
|
+
const detectedSet = new Set(
|
|
2038
|
+
options.global ? await detectAllGlobalAgents() : detectProjectAgents(options.cwd)
|
|
2039
|
+
);
|
|
2040
|
+
for (const agentType of options.agents) {
|
|
2041
|
+
const detected = detectedSet.has(agentType);
|
|
2042
|
+
if (detected) {
|
|
2043
|
+
results.push(
|
|
2044
|
+
readServersForAgent(agentType, { scope, cwd: options.cwd })
|
|
2045
|
+
);
|
|
2046
|
+
} else {
|
|
2047
|
+
const agent = agents[agentType];
|
|
2048
|
+
const installOptions = {
|
|
2049
|
+
local: scope === "local",
|
|
2050
|
+
cwd: options.cwd
|
|
2051
|
+
};
|
|
2052
|
+
results.push({
|
|
2053
|
+
agentType,
|
|
2054
|
+
displayName: agent.displayName,
|
|
2055
|
+
detected: false,
|
|
2056
|
+
scope,
|
|
2057
|
+
configPath: getConfigPath2(agent, installOptions),
|
|
2058
|
+
servers: []
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
} else {
|
|
2063
|
+
const detected = options.global ? await detectAllGlobalAgents() : detectProjectAgents(options.cwd);
|
|
2064
|
+
for (const agentType of detected) {
|
|
2065
|
+
results.push(readServersForAgent(agentType, { scope, cwd: options.cwd }));
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return results;
|
|
2069
|
+
}
|
|
2070
|
+
function findMatchingServers(agentServersList, query) {
|
|
2071
|
+
const lowerQuery = query.toLowerCase();
|
|
2072
|
+
const matches = [];
|
|
2073
|
+
for (const agentServers of agentServersList) {
|
|
2074
|
+
for (const server of agentServers.servers) {
|
|
2075
|
+
const nameMatch = server.serverName.toLowerCase().includes(lowerQuery);
|
|
2076
|
+
const identityMatch = server.identity === query;
|
|
2077
|
+
if (nameMatch || identityMatch) {
|
|
2078
|
+
matches.push(server);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
return matches;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
1577
2085
|
// package.json
|
|
1578
2086
|
var package_default = {
|
|
1579
2087
|
name: "add-mcp",
|
|
1580
|
-
version: "1.
|
|
2088
|
+
version: "1.9.0",
|
|
1581
2089
|
description: "Add MCP servers to your favorite coding agents with a single command.",
|
|
1582
2090
|
author: "Andre Landgraf <andre@neon.tech>",
|
|
1583
2091
|
license: "Apache-2.0",
|
|
@@ -1593,12 +2101,13 @@ var package_default = {
|
|
|
1593
2101
|
fmt: "prettier --write .",
|
|
1594
2102
|
build: "tsup src/index.ts --format esm --dts --clean",
|
|
1595
2103
|
dev: "tsx src/index.ts",
|
|
1596
|
-
test: "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/config.test.ts && tsx tests/installer.test.ts && tsx tests/find.test.ts && tsx tests/e2e/install.test.ts && tsx tests/e2e/cli.test.ts",
|
|
1597
|
-
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/config.test.ts && tsx tests/installer.test.ts && tsx tests/find.test.ts",
|
|
2104
|
+
test: "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/config.test.ts && tsx tests/installer.test.ts && tsx tests/find.test.ts && tsx tests/package-arguments.test.ts && tsx tests/template.test.ts && tsx tests/reader.test.ts && tsx tests/formats-remove.test.ts && tsx tests/e2e/install.test.ts && tsx tests/e2e/cli.test.ts",
|
|
2105
|
+
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/config.test.ts && tsx tests/installer.test.ts && tsx tests/find.test.ts && tsx tests/package-arguments.test.ts && tsx tests/template.test.ts && tsx tests/reader.test.ts && tsx tests/formats-remove.test.ts",
|
|
1598
2106
|
"registry:sort": "tsx scripts/sort-registry.ts",
|
|
1599
2107
|
"registry:verify": "tsx scripts/verify-registry.ts",
|
|
1600
2108
|
"test:e2e": "tsx tests/e2e/install.test.ts && tsx tests/e2e/cli.test.ts",
|
|
1601
2109
|
typecheck: "tsc --noEmit",
|
|
2110
|
+
fallow: "fallow",
|
|
1602
2111
|
prepublishOnly: "npm run build"
|
|
1603
2112
|
},
|
|
1604
2113
|
keywords: [
|
|
@@ -1622,11 +2131,11 @@ var package_default = {
|
|
|
1622
2131
|
],
|
|
1623
2132
|
repository: {
|
|
1624
2133
|
type: "git",
|
|
1625
|
-
url: "git+https://github.com/
|
|
2134
|
+
url: "git+https://github.com/neon-solutions/add-mcp.git"
|
|
1626
2135
|
},
|
|
1627
|
-
homepage: "https://github.com/
|
|
2136
|
+
homepage: "https://github.com/neon-solutions/add-mcp#readme",
|
|
1628
2137
|
bugs: {
|
|
1629
|
-
url: "https://github.com/
|
|
2138
|
+
url: "https://github.com/neon-solutions/add-mcp/issues"
|
|
1630
2139
|
},
|
|
1631
2140
|
dependencies: {
|
|
1632
2141
|
"@clack/prompts": "^0.9.1",
|
|
@@ -1639,6 +2148,7 @@ var package_default = {
|
|
|
1639
2148
|
devDependencies: {
|
|
1640
2149
|
"@types/js-yaml": "^4.0.9",
|
|
1641
2150
|
"@types/node": "^22.10.0",
|
|
2151
|
+
fallow: "^2.61.0",
|
|
1642
2152
|
prettier: "^3.8.1",
|
|
1643
2153
|
tsup: "^8.3.5",
|
|
1644
2154
|
tsx: "^4.19.2",
|
|
@@ -1734,7 +2244,7 @@ function extractOptions(raw) {
|
|
|
1734
2244
|
}
|
|
1735
2245
|
return raw;
|
|
1736
2246
|
}
|
|
1737
|
-
function
|
|
2247
|
+
function extractSubcommandOptionsFromArgv() {
|
|
1738
2248
|
const argv = process.argv.slice(2);
|
|
1739
2249
|
const result = {};
|
|
1740
2250
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -1756,6 +2266,27 @@ function extractFindOptionsFromArgv() {
|
|
|
1756
2266
|
result.gitignore = true;
|
|
1757
2267
|
continue;
|
|
1758
2268
|
}
|
|
2269
|
+
if (arg === "--header" && argv[i + 1]) {
|
|
2270
|
+
const headers = result.header ? [...result.header] : [];
|
|
2271
|
+
headers.push(argv[i + 1]);
|
|
2272
|
+
result.header = headers;
|
|
2273
|
+
i += 1;
|
|
2274
|
+
continue;
|
|
2275
|
+
}
|
|
2276
|
+
if (arg === "--env" && argv[i + 1]) {
|
|
2277
|
+
const env = result.env ? [...result.env] : [];
|
|
2278
|
+
env.push(argv[i + 1]);
|
|
2279
|
+
result.env = env;
|
|
2280
|
+
i += 1;
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
if (arg === "--args" && argv[i + 1]) {
|
|
2284
|
+
const args = result.args ? [...result.args] : [];
|
|
2285
|
+
args.push(argv[i + 1]);
|
|
2286
|
+
result.args = args;
|
|
2287
|
+
i += 1;
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
1759
2290
|
if ((arg === "-n" || arg === "--name") && argv[i + 1]) {
|
|
1760
2291
|
result.name = argv[i + 1];
|
|
1761
2292
|
i += 1;
|
|
@@ -1838,6 +2369,13 @@ function parseEnv(values) {
|
|
|
1838
2369
|
}
|
|
1839
2370
|
return { env, invalid };
|
|
1840
2371
|
}
|
|
2372
|
+
function omitEmptyStringValues(record) {
|
|
2373
|
+
return Object.fromEntries(
|
|
2374
|
+
Object.entries(record).filter(
|
|
2375
|
+
([, v]) => typeof v === "string" && v.trim().length > 0
|
|
2376
|
+
)
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
1841
2379
|
program.name("add-mcp").description(
|
|
1842
2380
|
"Install MCP servers for coding agents (Claude Code, Cursor, VS Code, OpenCode, Codex, and more \u2014 run list-agents for the full list)"
|
|
1843
2381
|
).version(version).argument("[target]", "MCP server URL (remote) or package name (local stdio)").option(
|
|
@@ -1851,12 +2389,17 @@ program.name("add-mcp").description(
|
|
|
1851
2389
|
"Transport type for remote servers (http, sse)"
|
|
1852
2390
|
).option("--type <type>", "Alias for --transport").option(
|
|
1853
2391
|
"--header <header>",
|
|
1854
|
-
"HTTP header for remote servers (repeatable, 'Key: Value')",
|
|
2392
|
+
"HTTP header for remote servers (repeatable, 'Key: Value'). Placeholders ${VAR} prompt interactively when not using --yes.",
|
|
1855
2393
|
collect,
|
|
1856
2394
|
[]
|
|
1857
2395
|
).option(
|
|
1858
2396
|
"--env <env>",
|
|
1859
|
-
"Environment variable for local stdio servers (repeatable, 'KEY=VALUE')",
|
|
2397
|
+
"Environment variable for local stdio servers (repeatable, 'KEY=VALUE'). Placeholders ${VAR} prompt interactively when not using --yes.",
|
|
2398
|
+
collect,
|
|
2399
|
+
[]
|
|
2400
|
+
).option(
|
|
2401
|
+
"--args <arg>",
|
|
2402
|
+
"Argument for local stdio servers (repeatable). Placeholders ${VAR} prompt interactively when not using --yes.",
|
|
1860
2403
|
collect,
|
|
1861
2404
|
[]
|
|
1862
2405
|
).option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(async (target, options) => {
|
|
@@ -1868,7 +2411,7 @@ program.command("list-agents").description("List all supported coding agents").a
|
|
|
1868
2411
|
async function runFindCommand(keyword, rawOptions) {
|
|
1869
2412
|
const options = {
|
|
1870
2413
|
...extractOptions(rawOptions),
|
|
1871
|
-
...
|
|
2414
|
+
...extractSubcommandOptionsFromArgv()
|
|
1872
2415
|
};
|
|
1873
2416
|
const query = (keyword ?? "").trim();
|
|
1874
2417
|
const registries = await ensureFindRegistriesConfigured(options.yes);
|
|
@@ -1891,7 +2434,9 @@ async function runFindCommand(keyword, rawOptions) {
|
|
|
1891
2434
|
transport: installPlan.transport,
|
|
1892
2435
|
header: installPlan.headers ? Object.entries(installPlan.headers).map(
|
|
1893
2436
|
([key, value]) => `${key}: ${value}`
|
|
1894
|
-
) : options.header
|
|
2437
|
+
) : options.header,
|
|
2438
|
+
env: installPlan.env ? Object.entries(installPlan.env).map(([key, value]) => `${key}=${value}`) : options.env,
|
|
2439
|
+
args: installPlan.args ?? options.args
|
|
1895
2440
|
};
|
|
1896
2441
|
await main(installPlan.target, mergedOptions);
|
|
1897
2442
|
}
|
|
@@ -1919,7 +2464,437 @@ program.command("search [keyword]").description("Alias for find").option(
|
|
|
1919
2464
|
await runFindCommand(keyword, options);
|
|
1920
2465
|
}
|
|
1921
2466
|
);
|
|
2467
|
+
program.command("list").description("List installed MCP servers across detected agents").option("-g, --global", "List global configs instead of project-level").option("-a, --agent <agent>", "Filter to specific agent(s)", collect, []).action(async (rawOptions) => {
|
|
2468
|
+
const options = {
|
|
2469
|
+
...extractOptions(rawOptions),
|
|
2470
|
+
...extractSubcommandOptionsFromArgv()
|
|
2471
|
+
};
|
|
2472
|
+
await runListCommand(options);
|
|
2473
|
+
});
|
|
2474
|
+
program.command("remove <query>").description("Remove an MCP server from agent configurations").option("-g, --global", "Remove from global configs instead of project-level").option("-a, --agent <agent>", "Filter to specific agent(s)", collect, []).option("-y, --yes", "Remove all matches without prompting").action(
|
|
2475
|
+
async (query, rawOptions) => {
|
|
2476
|
+
const options = {
|
|
2477
|
+
...extractOptions(rawOptions),
|
|
2478
|
+
...extractSubcommandOptionsFromArgv()
|
|
2479
|
+
};
|
|
2480
|
+
await runRemoveCommand(query, options);
|
|
2481
|
+
}
|
|
2482
|
+
);
|
|
2483
|
+
program.command("sync").description(
|
|
2484
|
+
"Synchronize server names and installations across all detected agents"
|
|
2485
|
+
).option("-g, --global", "Sync global configs instead of project-level").option("-y, --yes", "Skip confirmation prompts").action(async (rawOptions) => {
|
|
2486
|
+
const options = {
|
|
2487
|
+
...extractOptions(rawOptions),
|
|
2488
|
+
...extractSubcommandOptionsFromArgv()
|
|
2489
|
+
};
|
|
2490
|
+
await runSyncCommand(options);
|
|
2491
|
+
});
|
|
2492
|
+
program.command("unify").description("Alias for sync").option("-g, --global", "Sync global configs instead of project-level").option("-y, --yes", "Skip confirmation prompts").action(async (rawOptions) => {
|
|
2493
|
+
const options = {
|
|
2494
|
+
...extractOptions(rawOptions),
|
|
2495
|
+
...extractSubcommandOptionsFromArgv()
|
|
2496
|
+
};
|
|
2497
|
+
await runSyncCommand(options);
|
|
2498
|
+
});
|
|
1922
2499
|
program.parse();
|
|
2500
|
+
async function runListCommand(options) {
|
|
2501
|
+
showLogo();
|
|
2502
|
+
console.log();
|
|
2503
|
+
const explicitAgents = resolveAgentFlags(options.agent);
|
|
2504
|
+
const agentServersList = await gatherInstalledServers({
|
|
2505
|
+
global: options.global,
|
|
2506
|
+
agents: explicitAgents.length > 0 ? explicitAgents : void 0
|
|
2507
|
+
});
|
|
2508
|
+
if (agentServersList.length === 0) {
|
|
2509
|
+
const hint = options.global ? "No agents detected globally. Use -a to target a specific agent." : "No agents detected in this project. Use -g for global or -a to target a specific agent.";
|
|
2510
|
+
p3.log.info(hint);
|
|
2511
|
+
console.log();
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
for (const agentServers of agentServersList) {
|
|
2515
|
+
if (!agentServers.detected) {
|
|
2516
|
+
console.log(
|
|
2517
|
+
`${TEXT}${agentServers.displayName}:${RESET} ${DIM}not detected${RESET}`
|
|
2518
|
+
);
|
|
2519
|
+
continue;
|
|
2520
|
+
}
|
|
2521
|
+
if (agentServers.servers.length === 0) {
|
|
2522
|
+
console.log(
|
|
2523
|
+
`${TEXT}${agentServers.displayName}:${RESET} ${DIM}no servers configured${RESET}`
|
|
2524
|
+
);
|
|
2525
|
+
continue;
|
|
2526
|
+
}
|
|
2527
|
+
console.log(`${TEXT}${agentServers.displayName}:${RESET}`);
|
|
2528
|
+
for (const server of agentServers.servers) {
|
|
2529
|
+
const identityHint = server.identity ? ` ${DIM}(${server.identity})${RESET}` : "";
|
|
2530
|
+
console.log(
|
|
2531
|
+
` ${DIM}-${RESET} ${TEXT}${server.serverName}${RESET}${identityHint}`
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
console.log();
|
|
2536
|
+
}
|
|
2537
|
+
async function runRemoveCommand(query, options) {
|
|
2538
|
+
showLogo();
|
|
2539
|
+
console.log();
|
|
2540
|
+
const explicitAgents = resolveAgentFlags(options.agent);
|
|
2541
|
+
const agentServersList = await gatherInstalledServers({
|
|
2542
|
+
global: options.global,
|
|
2543
|
+
agents: explicitAgents.length > 0 ? explicitAgents : void 0
|
|
2544
|
+
});
|
|
2545
|
+
const matches = findMatchingServers(agentServersList, query);
|
|
2546
|
+
if (matches.length === 0) {
|
|
2547
|
+
p3.log.info(`No matching servers found for '${query}'`);
|
|
2548
|
+
console.log();
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
const matchOptions = matches.map((m, i) => ({
|
|
2552
|
+
value: i,
|
|
2553
|
+
label: `${m.serverName} (${agents[m.agentType].displayName})`,
|
|
2554
|
+
hint: m.identity || m.configPath
|
|
2555
|
+
}));
|
|
2556
|
+
let selectedIndices;
|
|
2557
|
+
if (options.yes) {
|
|
2558
|
+
selectedIndices = matches.map((_, i) => i);
|
|
2559
|
+
p3.log.info(
|
|
2560
|
+
`Removing ${matches.length} server${matches.length !== 1 ? "s" : ""} matching '${query}'`
|
|
2561
|
+
);
|
|
2562
|
+
} else {
|
|
2563
|
+
const selected = await p3.multiselect({
|
|
2564
|
+
message: `Select servers to remove (${matches.length} match${matches.length !== 1 ? "es" : ""} found)`,
|
|
2565
|
+
options: matchOptions,
|
|
2566
|
+
required: false,
|
|
2567
|
+
initialValues: matches.map((_, i) => i)
|
|
2568
|
+
});
|
|
2569
|
+
if (p3.isCancel(selected)) {
|
|
2570
|
+
p3.log.info("No changes made");
|
|
2571
|
+
console.log();
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
selectedIndices = selected;
|
|
2575
|
+
if (selectedIndices.length === 0) {
|
|
2576
|
+
p3.log.info("No changes made");
|
|
2577
|
+
console.log();
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
let removedCount = 0;
|
|
2582
|
+
const affectedAgents = /* @__PURE__ */ new Set();
|
|
2583
|
+
for (const idx of selectedIndices) {
|
|
2584
|
+
const server = matches[idx];
|
|
2585
|
+
const agent = agents[server.agentType];
|
|
2586
|
+
try {
|
|
2587
|
+
removeServerFromConfig(
|
|
2588
|
+
server.configPath,
|
|
2589
|
+
agent.format,
|
|
2590
|
+
getConfigKeyForServer(server),
|
|
2591
|
+
server.serverName
|
|
2592
|
+
);
|
|
2593
|
+
removedCount++;
|
|
2594
|
+
affectedAgents.add(agent.displayName);
|
|
2595
|
+
} catch (error) {
|
|
2596
|
+
p3.log.error(
|
|
2597
|
+
`Failed to remove ${server.serverName} from ${agent.displayName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (removedCount > 0) {
|
|
2602
|
+
p3.log.success(
|
|
2603
|
+
`Removed ${removedCount} server${removedCount !== 1 ? "s" : ""} from ${affectedAgents.size} agent${affectedAgents.size !== 1 ? "s" : ""}`
|
|
2604
|
+
);
|
|
2605
|
+
}
|
|
2606
|
+
console.log();
|
|
2607
|
+
}
|
|
2608
|
+
function getConfigKeyForServer(server) {
|
|
2609
|
+
const agent = agents[server.agentType];
|
|
2610
|
+
if (server.scope === "local" && agent.localConfigKey) {
|
|
2611
|
+
return agent.localConfigKey;
|
|
2612
|
+
}
|
|
2613
|
+
return agent.configKey;
|
|
2614
|
+
}
|
|
2615
|
+
function deepEqual(a, b) {
|
|
2616
|
+
if (a === b) return true;
|
|
2617
|
+
if (a === null || b === null) return false;
|
|
2618
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
2619
|
+
if (typeof a !== typeof b) return false;
|
|
2620
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
2621
|
+
if (a.length !== b.length) return false;
|
|
2622
|
+
return a.every((val, i) => deepEqual(val, b[i]));
|
|
2623
|
+
}
|
|
2624
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
2625
|
+
const aObj = a;
|
|
2626
|
+
const bObj = b;
|
|
2627
|
+
const aKeys = Object.keys(aObj).sort();
|
|
2628
|
+
const bKeys = Object.keys(bObj).sort();
|
|
2629
|
+
if (!deepEqual(aKeys, bKeys)) return false;
|
|
2630
|
+
return aKeys.every((key) => deepEqual(aObj[key], bObj[key]));
|
|
2631
|
+
}
|
|
2632
|
+
return false;
|
|
2633
|
+
}
|
|
2634
|
+
function pickCanonicalName(entries) {
|
|
2635
|
+
const nameFreq = /* @__PURE__ */ new Map();
|
|
2636
|
+
for (const entry of entries) {
|
|
2637
|
+
nameFreq.set(entry.serverName, (nameFreq.get(entry.serverName) ?? 0) + 1);
|
|
2638
|
+
}
|
|
2639
|
+
const names = [...nameFreq.entries()];
|
|
2640
|
+
names.sort(([nameA, freqA], [nameB, freqB]) => {
|
|
2641
|
+
if (nameA.length !== nameB.length) return nameA.length - nameB.length;
|
|
2642
|
+
if (freqA !== freqB) return freqB - freqA;
|
|
2643
|
+
return nameA.localeCompare(nameB);
|
|
2644
|
+
});
|
|
2645
|
+
return names[0][0];
|
|
2646
|
+
}
|
|
2647
|
+
function extractConflictFields(config) {
|
|
2648
|
+
return {
|
|
2649
|
+
headers: config.headers ?? config.http_headers ?? null,
|
|
2650
|
+
env: config.env ?? config.envs ?? config.environment ?? null,
|
|
2651
|
+
args: config.args ?? null
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
function buildSyncGroups(agentServersList) {
|
|
2655
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
2656
|
+
for (const agentServers of agentServersList) {
|
|
2657
|
+
for (const server of agentServers.servers) {
|
|
2658
|
+
if (!server.identity) continue;
|
|
2659
|
+
const existing = byIdentity.get(server.identity) ?? [];
|
|
2660
|
+
existing.push(server);
|
|
2661
|
+
byIdentity.set(server.identity, existing);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
const groups = [];
|
|
2665
|
+
for (const [identity, entries] of byIdentity) {
|
|
2666
|
+
const fieldSets = entries.map((e) => extractConflictFields(e.config));
|
|
2667
|
+
const reference = fieldSets[0];
|
|
2668
|
+
let hasConflict = false;
|
|
2669
|
+
let conflictReason;
|
|
2670
|
+
for (let i = 1; i < fieldSets.length; i++) {
|
|
2671
|
+
const other = fieldSets[i];
|
|
2672
|
+
if (!deepEqual(reference.headers, other.headers)) {
|
|
2673
|
+
hasConflict = true;
|
|
2674
|
+
conflictReason = `headers differ between ${agents[entries[0].agentType].displayName} and ${agents[entries[i].agentType].displayName}`;
|
|
2675
|
+
break;
|
|
2676
|
+
}
|
|
2677
|
+
if (!deepEqual(reference.env, other.env)) {
|
|
2678
|
+
hasConflict = true;
|
|
2679
|
+
conflictReason = `env differs between ${agents[entries[0].agentType].displayName} and ${agents[entries[i].agentType].displayName}`;
|
|
2680
|
+
break;
|
|
2681
|
+
}
|
|
2682
|
+
if (!deepEqual(reference.args, other.args)) {
|
|
2683
|
+
hasConflict = true;
|
|
2684
|
+
conflictReason = `args differ between ${agents[entries[0].agentType].displayName} and ${agents[entries[i].agentType].displayName}`;
|
|
2685
|
+
break;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
groups.push({
|
|
2689
|
+
identity,
|
|
2690
|
+
entries,
|
|
2691
|
+
canonicalName: pickCanonicalName(entries),
|
|
2692
|
+
canonicalConfig: entries[0].config,
|
|
2693
|
+
hasConflict,
|
|
2694
|
+
conflictReason
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
return groups;
|
|
2698
|
+
}
|
|
2699
|
+
async function runSyncCommand(options) {
|
|
2700
|
+
showLogo();
|
|
2701
|
+
console.log();
|
|
2702
|
+
const agentServersList = await gatherInstalledServers({
|
|
2703
|
+
global: options.global
|
|
2704
|
+
});
|
|
2705
|
+
const agentsWithServers = agentServersList.filter(
|
|
2706
|
+
(a) => a.servers.length > 0
|
|
2707
|
+
);
|
|
2708
|
+
if (agentServersList.length < 2) {
|
|
2709
|
+
p3.log.info("Need at least 2 detected agents to sync");
|
|
2710
|
+
console.log();
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
const groups = buildSyncGroups(agentServersList);
|
|
2714
|
+
const detectedAgentTypes = new Set(agentServersList.map((a) => a.agentType));
|
|
2715
|
+
const renames = [];
|
|
2716
|
+
const additions = [];
|
|
2717
|
+
const skipped = [];
|
|
2718
|
+
for (const group of groups) {
|
|
2719
|
+
if (group.hasConflict) {
|
|
2720
|
+
skipped.push(group);
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2723
|
+
const presentAgents = new Set(group.entries.map((e) => e.agentType));
|
|
2724
|
+
for (const entry of group.entries) {
|
|
2725
|
+
if (entry.serverName !== group.canonicalName) {
|
|
2726
|
+
renames.push({
|
|
2727
|
+
group,
|
|
2728
|
+
agentType: entry.agentType,
|
|
2729
|
+
oldName: entry.serverName
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
for (const agentType of detectedAgentTypes) {
|
|
2734
|
+
if (!presentAgents.has(agentType)) {
|
|
2735
|
+
additions.push({ group, agentType });
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
if (renames.length === 0 && additions.length === 0 && skipped.length === 0) {
|
|
2740
|
+
p3.log.info("All servers are already in sync");
|
|
2741
|
+
console.log();
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
const planLines = [];
|
|
2745
|
+
if (renames.length > 0) {
|
|
2746
|
+
planLines.push(chalk.cyan("Renames:"));
|
|
2747
|
+
for (const r of renames) {
|
|
2748
|
+
planLines.push(
|
|
2749
|
+
` ${agents[r.agentType].displayName}: ${r.oldName} \u2192 ${r.group.canonicalName}`
|
|
2750
|
+
);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
if (additions.length > 0) {
|
|
2754
|
+
planLines.push(chalk.cyan("Additions:"));
|
|
2755
|
+
for (const a of additions) {
|
|
2756
|
+
planLines.push(
|
|
2757
|
+
` ${agents[a.agentType].displayName}: + ${a.group.canonicalName} (${a.group.identity})`
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
if (skipped.length > 0) {
|
|
2762
|
+
planLines.push(chalk.yellow("Skipped (conflicts):"));
|
|
2763
|
+
for (const s of skipped) {
|
|
2764
|
+
planLines.push(` ${s.identity}: ${s.conflictReason}`);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
if (renames.length === 0 && additions.length === 0) {
|
|
2768
|
+
p3.note(planLines.join("\n"), "Sync Plan");
|
|
2769
|
+
p3.log.info(
|
|
2770
|
+
"All servers are already in sync (some skipped due to conflicts)"
|
|
2771
|
+
);
|
|
2772
|
+
console.log();
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
p3.note(planLines.join("\n"), "Sync Plan");
|
|
2776
|
+
if (!options.yes) {
|
|
2777
|
+
const confirmed = await p3.confirm({
|
|
2778
|
+
message: "Proceed with sync?"
|
|
2779
|
+
});
|
|
2780
|
+
if (p3.isCancel(confirmed) || !confirmed) {
|
|
2781
|
+
p3.log.info("No changes made");
|
|
2782
|
+
console.log();
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
const scope = options.global ? "global" : "local";
|
|
2787
|
+
let changeCount = 0;
|
|
2788
|
+
for (const rename of renames) {
|
|
2789
|
+
const { group, agentType } = rename;
|
|
2790
|
+
const result = installServerForAgent(
|
|
2791
|
+
group.canonicalName,
|
|
2792
|
+
buildServerConfigFromStored(group.canonicalConfig),
|
|
2793
|
+
agentType,
|
|
2794
|
+
{ local: scope === "local" }
|
|
2795
|
+
);
|
|
2796
|
+
if (result.success) {
|
|
2797
|
+
changeCount++;
|
|
2798
|
+
} else {
|
|
2799
|
+
p3.log.error(
|
|
2800
|
+
`Failed to write ${group.canonicalName} to ${agents[agentType].displayName}: ${result.error}`
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
for (const addition of additions) {
|
|
2805
|
+
const { group, agentType } = addition;
|
|
2806
|
+
const result = installServerForAgent(
|
|
2807
|
+
group.canonicalName,
|
|
2808
|
+
buildServerConfigFromStored(group.canonicalConfig),
|
|
2809
|
+
agentType,
|
|
2810
|
+
{ local: scope === "local" }
|
|
2811
|
+
);
|
|
2812
|
+
if (result.success) {
|
|
2813
|
+
changeCount++;
|
|
2814
|
+
} else {
|
|
2815
|
+
p3.log.error(
|
|
2816
|
+
`Failed to add ${group.canonicalName} to ${agents[agentType].displayName}: ${result.error}`
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
for (const rename of renames) {
|
|
2821
|
+
const { group, agentType, oldName } = rename;
|
|
2822
|
+
const agentConfig = agents[agentType];
|
|
2823
|
+
const entry = group.entries.find((e) => e.agentType === agentType);
|
|
2824
|
+
if (!entry) continue;
|
|
2825
|
+
try {
|
|
2826
|
+
removeServerFromConfig(
|
|
2827
|
+
entry.configPath,
|
|
2828
|
+
agentConfig.format,
|
|
2829
|
+
getConfigKeyForServer(entry),
|
|
2830
|
+
oldName
|
|
2831
|
+
);
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
p3.log.error(
|
|
2834
|
+
`Failed to remove old alias ${oldName} from ${agentConfig.displayName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2835
|
+
);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
p3.log.success(
|
|
2839
|
+
`Synced ${changeCount} server${changeCount !== 1 ? "s" : ""} across ${detectedAgentTypes.size} agent${detectedAgentTypes.size !== 1 ? "s" : ""}`
|
|
2840
|
+
);
|
|
2841
|
+
console.log();
|
|
2842
|
+
}
|
|
2843
|
+
var TRANSPORT_ALIASES = {
|
|
2844
|
+
http: "http",
|
|
2845
|
+
sse: "sse",
|
|
2846
|
+
streamable_http: "http",
|
|
2847
|
+
streamableHttp: "http",
|
|
2848
|
+
"streamable-http": "http",
|
|
2849
|
+
remote: "http"
|
|
2850
|
+
};
|
|
2851
|
+
function normalizeTransportType(raw) {
|
|
2852
|
+
if (typeof raw === "string" && raw in TRANSPORT_ALIASES) {
|
|
2853
|
+
return TRANSPORT_ALIASES[raw];
|
|
2854
|
+
}
|
|
2855
|
+
return "http";
|
|
2856
|
+
}
|
|
2857
|
+
function buildServerConfigFromStored(config) {
|
|
2858
|
+
const url = typeof config.url === "string" ? config.url : typeof config.uri === "string" ? config.uri : typeof config.serverUrl === "string" ? config.serverUrl : void 0;
|
|
2859
|
+
if (url) {
|
|
2860
|
+
const result2 = {
|
|
2861
|
+
type: normalizeTransportType(config.type),
|
|
2862
|
+
url
|
|
2863
|
+
};
|
|
2864
|
+
const headers = config.headers && typeof config.headers === "object" ? config.headers : config.http_headers && typeof config.http_headers === "object" ? config.http_headers : void 0;
|
|
2865
|
+
if (headers && Object.keys(headers).length > 0) {
|
|
2866
|
+
result2.headers = headers;
|
|
2867
|
+
}
|
|
2868
|
+
return result2;
|
|
2869
|
+
}
|
|
2870
|
+
const command = typeof config.command === "string" ? config.command : typeof config.cmd === "string" ? config.cmd : void 0;
|
|
2871
|
+
const args = Array.isArray(config.args) ? config.args.filter((a) => typeof a === "string") : [];
|
|
2872
|
+
const env = config.env && typeof config.env === "object" ? config.env : config.envs && typeof config.envs === "object" ? config.envs : config.environment && typeof config.environment === "object" ? config.environment : void 0;
|
|
2873
|
+
const result = {};
|
|
2874
|
+
if (command) result.command = command;
|
|
2875
|
+
if (args.length > 0) result.args = args;
|
|
2876
|
+
if (env && Object.keys(env).length > 0) result.env = env;
|
|
2877
|
+
return result;
|
|
2878
|
+
}
|
|
2879
|
+
function resolveAgentFlags(agentFlags) {
|
|
2880
|
+
if (!agentFlags || agentFlags.length === 0) return [];
|
|
2881
|
+
const resolved = [];
|
|
2882
|
+
const invalid = [];
|
|
2883
|
+
for (const input of agentFlags) {
|
|
2884
|
+
const agentType = resolveAgentType(input);
|
|
2885
|
+
if (agentType) {
|
|
2886
|
+
resolved.push(agentType);
|
|
2887
|
+
} else {
|
|
2888
|
+
invalid.push(input);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
if (invalid.length > 0) {
|
|
2892
|
+
p3.log.error(`Invalid agents: ${invalid.join(", ")}`);
|
|
2893
|
+
p3.log.info(`Valid agents: ${getAgentTypes().join(", ")}`);
|
|
2894
|
+
process.exit(1);
|
|
2895
|
+
}
|
|
2896
|
+
return resolved;
|
|
2897
|
+
}
|
|
1923
2898
|
function listAgents() {
|
|
1924
2899
|
showLogo();
|
|
1925
2900
|
console.log();
|
|
@@ -2031,6 +3006,55 @@ async function main(target, options) {
|
|
|
2031
3006
|
"--env is only used for local/package/command installs, ignoring"
|
|
2032
3007
|
);
|
|
2033
3008
|
}
|
|
3009
|
+
const argsValues = options.args ?? [];
|
|
3010
|
+
const hasArgsValues = argsValues.length > 0;
|
|
3011
|
+
if (hasArgsValues && isRemote) {
|
|
3012
|
+
p3.log.warn(
|
|
3013
|
+
"--args is only used for local/package/command installs, ignoring"
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
const promptTemplateVar = (varName) => p3.text({
|
|
3017
|
+
message: `Enter value for ${varName}`,
|
|
3018
|
+
placeholder: `<${varName}>`
|
|
3019
|
+
});
|
|
3020
|
+
let resolvedArgs = [...argsValues];
|
|
3021
|
+
if (!options.yes && hasHeaderValues && hasTemplateVars(headerResult.headers)) {
|
|
3022
|
+
const result = await resolveRecordTemplates(
|
|
3023
|
+
headerResult.headers,
|
|
3024
|
+
promptTemplateVar
|
|
3025
|
+
);
|
|
3026
|
+
if (result.cancelled) {
|
|
3027
|
+
p3.cancel("Cancelled");
|
|
3028
|
+
process.exit(0);
|
|
3029
|
+
}
|
|
3030
|
+
for (const [key, value] of Object.entries(result.resolved)) {
|
|
3031
|
+
headerResult.headers[key] = value;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
if (!options.yes && hasEnvValues && hasTemplateVars(envResult.env)) {
|
|
3035
|
+
const result = await resolveRecordTemplates(
|
|
3036
|
+
envResult.env,
|
|
3037
|
+
promptTemplateVar
|
|
3038
|
+
);
|
|
3039
|
+
if (result.cancelled) {
|
|
3040
|
+
p3.cancel("Cancelled");
|
|
3041
|
+
process.exit(0);
|
|
3042
|
+
}
|
|
3043
|
+
for (const [key, value] of Object.entries(result.resolved)) {
|
|
3044
|
+
envResult.env[key] = value;
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
if (!options.yes && hasArgsValues && hasTemplateVars(resolvedArgs)) {
|
|
3048
|
+
const result = await resolveArrayTemplates(resolvedArgs, promptTemplateVar);
|
|
3049
|
+
if (result.cancelled) {
|
|
3050
|
+
p3.cancel("Cancelled");
|
|
3051
|
+
process.exit(0);
|
|
3052
|
+
}
|
|
3053
|
+
resolvedArgs = result.resolved;
|
|
3054
|
+
}
|
|
3055
|
+
const headersForConfig = isRemote && hasHeaderValues ? omitEmptyStringValues(headerResult.headers) : void 0;
|
|
3056
|
+
const envForConfig = !isRemote && hasEnvValues ? omitEmptyStringValues(envResult.env) : void 0;
|
|
3057
|
+
const argsForConfig = !isRemote && hasArgsValues ? resolvedArgs.filter((a) => a.trim().length > 0) : void 0;
|
|
2034
3058
|
const serverName = options.name || parsed.inferredName;
|
|
2035
3059
|
p3.log.info(`Server name: ${chalk.cyan(serverName)}`);
|
|
2036
3060
|
const transportValue = options.transport || options.type;
|
|
@@ -2050,8 +3074,9 @@ async function main(target, options) {
|
|
|
2050
3074
|
}
|
|
2051
3075
|
const serverConfig = buildServerConfig(parsed, {
|
|
2052
3076
|
transport: resolvedTransport,
|
|
2053
|
-
headers:
|
|
2054
|
-
env:
|
|
3077
|
+
headers: headersForConfig && Object.keys(headersForConfig).length > 0 ? headersForConfig : void 0,
|
|
3078
|
+
env: envForConfig && Object.keys(envForConfig).length > 0 ? envForConfig : void 0,
|
|
3079
|
+
args: argsForConfig && argsForConfig.length > 0 ? argsForConfig : void 0
|
|
2055
3080
|
});
|
|
2056
3081
|
let targetAgents;
|
|
2057
3082
|
const allAgentTypes = getAgentTypes();
|