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 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, and prints the effective runtime role profile summary that the runner will use.
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.
@@ -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 payload = buildBotShowPayload(provider, state, entry, deps, { serverBinding });
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.73",
3
+ "version": "0.2.74",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [