metheus-governance-mcp-cli 0.2.73 → 0.2.74
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 +2 -2
- package/lib/bot-commands.mjs +88 -2
- package/lib/selftest-bot-commands.mjs +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -247,8 +247,8 @@ Behavior:
|
|
|
247
247
|
- `AI_PERMISSION_MODE`
|
|
248
248
|
- `AI_REASONING_EFFORT`
|
|
249
249
|
- Slack and KakaoTalk currently use a single local token entry per provider in this command flow.
|
|
250
|
-
- `bot verify` checks the configured local token, cross-checks the server bot binding,
|
|
251
|
-
- `bot show` prints one local bot entry in detail, including grouped role summaries when one server bot name expands to multiple roles.
|
|
250
|
+
- `bot verify` checks the configured local token, cross-checks the server bot binding, prints the effective runtime role profile summary that the runner will use, and shows which local runner routes currently point at this bot entry.
|
|
251
|
+
- `bot show` prints one local bot entry in detail, including grouped role summaries when one server bot name expands to multiple roles and any linked local runner routes.
|
|
252
252
|
- `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and default bot key.
|
|
253
253
|
- `bot set-default` updates `TELEGRAM_DEFAULT_BOT_KEY`.
|
|
254
254
|
- `bot migrate` moves legacy `TELEGRAM_BOT_TOKEN` into a named Telegram bot entry.
|
package/lib/bot-commands.mjs
CHANGED
|
@@ -840,6 +840,7 @@ function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
|
|
|
840
840
|
reasoningEffort: selectedEntry.reasoningEffort,
|
|
841
841
|
} : null,
|
|
842
842
|
serverBinding: safeObject(extras.serverBinding),
|
|
843
|
+
routeLinks: safeObject(extras.routeLinks),
|
|
843
844
|
...safeObject(extras),
|
|
844
845
|
};
|
|
845
846
|
}
|
|
@@ -857,6 +858,60 @@ function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
|
|
|
857
858
|
};
|
|
858
859
|
}
|
|
859
860
|
|
|
861
|
+
function describeRouteRuntimeProfile(routeRole, serverBinding) {
|
|
862
|
+
const binding = safeObject(serverBinding);
|
|
863
|
+
if (binding.mode === "group") {
|
|
864
|
+
return safeObject(safeObject(binding.effectiveRoleProfiles)[String(routeRole || "").trim()]);
|
|
865
|
+
}
|
|
866
|
+
return safeObject(binding.effectiveRoleProfile);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function summarizeTelegramRouteLinks(parsedEnv, entry, serverBinding, deps) {
|
|
870
|
+
const selectedEntry = safeObject(entry);
|
|
871
|
+
const binding = safeObject(serverBinding);
|
|
872
|
+
const config = safeObject(requireDependency(deps, "loadBotRunnerConfig")({ persistIfNeeded: true }));
|
|
873
|
+
const routes = ensureArray(config.routes);
|
|
874
|
+
const entryKey = String(selectedEntry.key || "").trim();
|
|
875
|
+
const defaultBotKey = String(safeObject(parsedEnv).TELEGRAM_DEFAULT_BOT_KEY || "").trim();
|
|
876
|
+
const serverBotID = String(binding.serverBotID || selectedEntry.serverBotID || "").trim();
|
|
877
|
+
const normalizedNames = new Set(
|
|
878
|
+
[
|
|
879
|
+
normalizeServerBotIdentityText(selectedEntry.username),
|
|
880
|
+
normalizeServerBotIdentityText(binding.name),
|
|
881
|
+
normalizeServerBotIdentityText(entryKey),
|
|
882
|
+
].filter(Boolean),
|
|
883
|
+
);
|
|
884
|
+
const linkedRoutes = [];
|
|
885
|
+
routes.forEach((rawRoute, index) => {
|
|
886
|
+
const route = safeObject(rawRoute);
|
|
887
|
+
if (route.enabled === false) return;
|
|
888
|
+
if (String(route.provider || "").trim().toLowerCase() !== "telegram") return;
|
|
889
|
+
const routeName = String(route.name || route.route_name || `telegram-route-${index + 1}`).trim();
|
|
890
|
+
const routeBotID = String(route.bot_id || route.botID || "").trim();
|
|
891
|
+
const routeBotName = normalizeServerBotIdentityText(route.bot_name || route.botName || "");
|
|
892
|
+
const routeRole = String(route.role_profile || route.roleProfile || route.role || "").trim();
|
|
893
|
+
let matchedBy = "";
|
|
894
|
+
if (serverBotID && routeBotID === serverBotID) {
|
|
895
|
+
matchedBy = "bot_id";
|
|
896
|
+
} else if (routeBotName && normalizedNames.has(routeBotName)) {
|
|
897
|
+
matchedBy = "bot_name";
|
|
898
|
+
} else if (!routeBotID && !routeBotName && entryKey && defaultBotKey && entryKey === defaultBotKey) {
|
|
899
|
+
matchedBy = "default_bot";
|
|
900
|
+
}
|
|
901
|
+
if (!matchedBy) return;
|
|
902
|
+
linkedRoutes.push({
|
|
903
|
+
routeName,
|
|
904
|
+
matchedBy,
|
|
905
|
+
routeRole,
|
|
906
|
+
runtimeRoleProfile: describeRouteRuntimeProfile(routeRole, binding),
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
return {
|
|
910
|
+
total: linkedRoutes.length,
|
|
911
|
+
routes: linkedRoutes,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
860
915
|
function printBotList(provider, state, deps) {
|
|
861
916
|
process.stdout.write(`${providerLabel(provider, deps)}\n`);
|
|
862
917
|
process.stdout.write(` file: ${state.filePath}\n`);
|
|
@@ -895,6 +950,7 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
|
895
950
|
if (provider === "telegram") {
|
|
896
951
|
const selectedEntry = entry || {};
|
|
897
952
|
const serverBinding = safeObject(extras.serverBinding);
|
|
953
|
+
const routeLinks = safeObject(extras.routeLinks);
|
|
898
954
|
process.stdout.write(`${providerLabel(provider, deps)} bot\n`);
|
|
899
955
|
process.stdout.write(` file: ${state.filePath}\n`);
|
|
900
956
|
process.stdout.write(` name: ${telegramEntryDisplayName(selectedEntry)}\n`);
|
|
@@ -924,6 +980,16 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
|
924
980
|
process.stdout.write(` runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}\n`);
|
|
925
981
|
}
|
|
926
982
|
}
|
|
983
|
+
process.stdout.write(` route_links: ${intFromRaw(routeLinks.total, 0)}\n`);
|
|
984
|
+
ensureArray(routeLinks.routes).forEach((route) => {
|
|
985
|
+
const routeRuntime = safeObject(route.runtimeRoleProfile);
|
|
986
|
+
const routeDetail = [
|
|
987
|
+
`matched_by=${String(route.matchedBy || "-")}`,
|
|
988
|
+
route.routeRole ? `route_role=${String(route.routeRole || "").trim()}` : "",
|
|
989
|
+
Object.keys(routeRuntime).length ? `runtime=${formatRoleProfileOutputLine(routeRuntime)}` : "",
|
|
990
|
+
].filter(Boolean).join(" ");
|
|
991
|
+
process.stdout.write(` linked_route[${String(route.routeName || "-")}]: ${routeDetail || "-"}\n`);
|
|
992
|
+
});
|
|
927
993
|
return;
|
|
928
994
|
}
|
|
929
995
|
const tokenKey = providerTokenKey(provider, deps);
|
|
@@ -2047,11 +2113,17 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2047
2113
|
);
|
|
2048
2114
|
let overallOK = Boolean(result.ok);
|
|
2049
2115
|
let serverBinding = null;
|
|
2116
|
+
let routeLinks = null;
|
|
2050
2117
|
if (provider === "telegram") {
|
|
2051
2118
|
serverBinding = await resolveTelegramServerBindingDetails(envConfig, flags, deps);
|
|
2052
2119
|
if (serverBinding && !serverBinding.ok) {
|
|
2053
2120
|
overallOK = false;
|
|
2054
2121
|
}
|
|
2122
|
+
routeLinks = summarizeTelegramRouteLinks(state.parsed, {
|
|
2123
|
+
key: envConfig.botKey,
|
|
2124
|
+
serverBotID: envConfig.serverBotID,
|
|
2125
|
+
username: envConfig.botUsername,
|
|
2126
|
+
}, serverBinding, deps);
|
|
2055
2127
|
}
|
|
2056
2128
|
const interactiveJsonChoice = (
|
|
2057
2129
|
!boolFromRaw(flags.json, false)
|
|
@@ -2084,6 +2156,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2084
2156
|
permissionMode: envConfig.permissionMode || "",
|
|
2085
2157
|
reasoningEffort: envConfig.reasoningEffort || "",
|
|
2086
2158
|
serverBinding,
|
|
2159
|
+
routeLinks,
|
|
2087
2160
|
}, null, 2)}\n`,
|
|
2088
2161
|
);
|
|
2089
2162
|
} else {
|
|
@@ -2099,6 +2172,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2099
2172
|
provider === "telegram" ? `reasoning_effort: ${envConfig.reasoningEffort || "-"}` : "",
|
|
2100
2173
|
`detail: ${result.detail || "-"}`,
|
|
2101
2174
|
serverBinding ? `server_binding: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}` : "",
|
|
2175
|
+
routeLinks ? `route_links: ${intFromRaw(routeLinks.total, 0)}` : "",
|
|
2102
2176
|
].filter(Boolean);
|
|
2103
2177
|
if (provider === "telegram" && serverBinding) {
|
|
2104
2178
|
lines.push(`server_binding_mode: ${serverBinding.mode || "-"}`);
|
|
@@ -2113,6 +2187,17 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2113
2187
|
lines.push(`runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}`);
|
|
2114
2188
|
}
|
|
2115
2189
|
}
|
|
2190
|
+
if (provider === "telegram" && routeLinks) {
|
|
2191
|
+
ensureArray(routeLinks.routes).forEach((route) => {
|
|
2192
|
+
const routeRuntime = safeObject(route.runtimeRoleProfile);
|
|
2193
|
+
const routeDetail = [
|
|
2194
|
+
`matched_by=${String(route.matchedBy || "-")}`,
|
|
2195
|
+
route.routeRole ? `route_role=${String(route.routeRole || "").trim()}` : "",
|
|
2196
|
+
Object.keys(routeRuntime).length ? `runtime=${formatRoleProfileOutputLine(routeRuntime)}` : "",
|
|
2197
|
+
].filter(Boolean).join(" ");
|
|
2198
|
+
lines.push(`linked_route[${String(route.routeName || "-")}]: ${routeDetail || "-"}`);
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2116
2201
|
process.stdout.write(`${lines.join("\n")}\n`);
|
|
2117
2202
|
}
|
|
2118
2203
|
if (!overallOK) {
|
|
@@ -2281,12 +2366,13 @@ async function runBotShow(ui, flags, deps, explicitProvider = "") {
|
|
|
2281
2366
|
flags,
|
|
2282
2367
|
deps,
|
|
2283
2368
|
);
|
|
2284
|
-
const
|
|
2369
|
+
const routeLinks = summarizeTelegramRouteLinks(state.parsed, entry, serverBinding, deps);
|
|
2370
|
+
const payload = buildBotShowPayload(provider, state, entry, deps, { serverBinding, routeLinks });
|
|
2285
2371
|
if (boolFromRaw(flags.json, false)) {
|
|
2286
2372
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
2287
2373
|
return;
|
|
2288
2374
|
}
|
|
2289
|
-
printBotShow(provider, state, entry, deps, { serverBinding });
|
|
2375
|
+
printBotShow(provider, state, entry, deps, { serverBinding, routeLinks });
|
|
2290
2376
|
return;
|
|
2291
2377
|
}
|
|
2292
2378
|
const payload = buildBotShowPayload(provider, state, null, deps);
|
|
@@ -84,6 +84,11 @@ function readTrailingJSON(rawText) {
|
|
|
84
84
|
return readJSON(text.slice(start));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function intFromRaw(rawValue, fallback = 0) {
|
|
88
|
+
const parsed = Number.parseInt(String(rawValue ?? "").trim(), 10);
|
|
89
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
90
|
+
}
|
|
91
|
+
|
|
87
92
|
function createMockServer(options = {}) {
|
|
88
93
|
const serverBots = ensureArray(options.serverBots).length
|
|
89
94
|
? ensureArray(options.serverBots)
|
|
@@ -434,6 +439,37 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
434
439
|
env,
|
|
435
440
|
});
|
|
436
441
|
const showPayload = readJSON(showResult.stdout);
|
|
442
|
+
const runnerConfigPath = path.join(tempHome, ".metheus", "bot-runner.json");
|
|
443
|
+
const runnerConfig = readJSON(fs.readFileSync(runnerConfigPath, "utf8"));
|
|
444
|
+
runnerConfig.routes = [
|
|
445
|
+
{
|
|
446
|
+
name: "telegram-monitor",
|
|
447
|
+
enabled: true,
|
|
448
|
+
project_id: "03c586a2-006d-4051-83b4-f353a5813176",
|
|
449
|
+
provider: "telegram",
|
|
450
|
+
bot_id: mock.bots[0].id,
|
|
451
|
+
role_profile: "monitor",
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: "telegram-default",
|
|
455
|
+
enabled: true,
|
|
456
|
+
project_id: "03c586a2-006d-4051-83b4-f353a5813176",
|
|
457
|
+
provider: "telegram",
|
|
458
|
+
},
|
|
459
|
+
];
|
|
460
|
+
fs.writeFileSync(runnerConfigPath, `${JSON.stringify(runnerConfig, null, 2)}\n`, "utf8");
|
|
461
|
+
const showWithRoutesResult = await runCLI({
|
|
462
|
+
cliPath,
|
|
463
|
+
args: [
|
|
464
|
+
"bot", "show",
|
|
465
|
+
"--provider", "telegram",
|
|
466
|
+
"--bot-key", "monitorselftestbot",
|
|
467
|
+
"--base-url", baseURL,
|
|
468
|
+
"--json", "true",
|
|
469
|
+
],
|
|
470
|
+
env,
|
|
471
|
+
});
|
|
472
|
+
const showWithRoutesPayload = readJSON(showWithRoutesResult.stdout);
|
|
437
473
|
push(
|
|
438
474
|
"bot_show_returns_selected_telegram_entry",
|
|
439
475
|
safeObject(showPayload.entry).key === "monitorselftestbot"
|
|
@@ -441,6 +477,15 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
441
477
|
&& safeObject(showPayload.serverBinding).mode === "single",
|
|
442
478
|
`key=${String(safeObject(showPayload.entry).key || "")} client=${String(safeObject(showPayload.entry).client || "")} mode=${String(safeObject(showPayload.serverBinding).mode || "")}`,
|
|
443
479
|
);
|
|
480
|
+
push(
|
|
481
|
+
"bot_show_reports_linked_runner_routes",
|
|
482
|
+
intFromRaw(safeObject(showWithRoutesPayload.routeLinks).total, 0) >= 1
|
|
483
|
+
&& ensureArray(safeObject(showWithRoutesPayload.routeLinks).routes).some((route) => (
|
|
484
|
+
String(safeObject(route).routeName || "") === "telegram-monitor"
|
|
485
|
+
&& String(safeObject(route).matchedBy || "") === "bot_id"
|
|
486
|
+
)),
|
|
487
|
+
`routes=${JSON.stringify(safeObject(showWithRoutesPayload.routeLinks))}`,
|
|
488
|
+
);
|
|
444
489
|
|
|
445
490
|
await runCLI({
|
|
446
491
|
cliPath,
|
|
@@ -584,6 +629,14 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
584
629
|
&& String(verifyPayload.client || "") === "claude",
|
|
585
630
|
`verify=${String(verifyPayload.ok)} mode=${String(safeObject(verifyPayload.serverBinding).mode || "")} server=${String(safeObject(verifyPayload.serverBinding).detail || "")}`,
|
|
586
631
|
);
|
|
632
|
+
push(
|
|
633
|
+
"bot_verify_reports_linked_runner_routes",
|
|
634
|
+
intFromRaw(safeObject(verifyPayload.routeLinks).total, 0) >= 1
|
|
635
|
+
&& ensureArray(safeObject(verifyPayload.routeLinks).routes).some((route) => (
|
|
636
|
+
String(safeObject(route).routeName || "") === "telegram-monitor"
|
|
637
|
+
)),
|
|
638
|
+
`routes=${JSON.stringify(safeObject(verifyPayload.routeLinks))}`,
|
|
639
|
+
);
|
|
587
640
|
|
|
588
641
|
await runCLI({
|
|
589
642
|
cliPath,
|