metheus-governance-mcp-cli 0.2.100 → 0.2.101

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/cli.mjs CHANGED
@@ -2684,12 +2684,35 @@ function parseTelegramAllowedUpdates(rawValue) {
2684
2684
  return Array.from(new Set(values));
2685
2685
  }
2686
2686
 
2687
+ function preferredTelegramServerRoleSort(roles) {
2688
+ const order = ["monitor", "review", "worker", "approval"];
2689
+ return ensureArray(roles).slice().sort((left, right) => {
2690
+ const leftText = String(left || "").trim();
2691
+ const rightText = String(right || "").trim();
2692
+ const leftIndex = order.indexOf(leftText);
2693
+ const rightIndex = order.indexOf(rightText);
2694
+ if (leftIndex >= 0 && rightIndex >= 0) return leftIndex - rightIndex;
2695
+ if (leftIndex >= 0) return -1;
2696
+ if (rightIndex >= 0) return 1;
2697
+ return leftText.localeCompare(rightText);
2698
+ });
2699
+ }
2700
+
2701
+ function parseTelegramServerRoles(rawValue) {
2702
+ const values = String(rawValue || "")
2703
+ .split(",")
2704
+ .map((value) => String(value || "").trim())
2705
+ .filter(Boolean);
2706
+ if (!values.length) return [];
2707
+ return Array.from(new Set(preferredTelegramServerRoleSort(values)));
2708
+ }
2709
+
2687
2710
  function collectTelegramEnvBotEntries(parsedEnv) {
2688
2711
  const parsed = safeObject(parsedEnv);
2689
2712
  const entries = new Map();
2690
2713
  for (const [rawKey, rawValue] of Object.entries(parsed)) {
2691
2714
  const match = String(rawKey || "").trim().match(
2692
- /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i,
2715
+ /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i,
2693
2716
  );
2694
2717
  if (!match) continue;
2695
2718
  const botKey = normalizeTelegramBotEnvKey(match[1], "");
@@ -2700,6 +2723,8 @@ function collectTelegramEnvBotEntries(parsedEnv) {
2700
2723
  token: "",
2701
2724
  username: "",
2702
2725
  serverBotID: "",
2726
+ serverBotName: "",
2727
+ serverRoles: [],
2703
2728
  roleProfile: "",
2704
2729
  client: "",
2705
2730
  model: "",
@@ -2713,6 +2738,10 @@ function collectTelegramEnvBotEntries(parsedEnv) {
2713
2738
  entry.username = normalizeTelegramBotUsername(textValue);
2714
2739
  } else if (field === "SERVER_BOT_ID") {
2715
2740
  entry.serverBotID = textValue;
2741
+ } else if (field === "SERVER_NAME") {
2742
+ entry.serverBotName = textValue;
2743
+ } else if (field === "SERVER_ROLES") {
2744
+ entry.serverRoles = parseTelegramServerRoles(textValue);
2716
2745
  } else if (field === "ROLE_PROFILE") {
2717
2746
  entry.roleProfile = normalizeRunnerRoleProfileName(textValue);
2718
2747
  } else if (field === "AI_CLIENT") {
@@ -2738,6 +2767,8 @@ function collectTelegramEnvBotEntries(parsedEnv) {
2738
2767
 
2739
2768
  function telegramBotEntryDisplayNameForState(entry) {
2740
2769
  const current = safeObject(entry);
2770
+ const serverBotName = String(current.serverBotName || "").trim();
2771
+ if (serverBotName) return serverBotName;
2741
2772
  const username = normalizeTelegramBotUsername(current.username || "");
2742
2773
  if (username) return username;
2743
2774
  return normalizeTelegramBotEnvKey(current.key || "", "telegram_bot");
@@ -2752,6 +2783,8 @@ function parseTelegramBotEntryFile(filePath) {
2752
2783
  key: keyFromName || keyFromFile,
2753
2784
  username: normalizeTelegramBotUsername(parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_USERNAME || ""),
2754
2785
  serverBotID: String(parsed.TELEGRAM_BOT_SERVER_BOT_ID || "").trim(),
2786
+ serverBotName: String(parsed.TELEGRAM_BOT_SERVER_NAME || "").trim(),
2787
+ serverRoles: parseTelegramServerRoles(parsed.TELEGRAM_BOT_SERVER_ROLES || ""),
2755
2788
  token: String(parsed.TELEGRAM_BOT_TOKEN || "").trim(),
2756
2789
  roleProfile: normalizeRunnerRoleProfileName(parsed.TELEGRAM_BOT_ROLE_PROFILE || ""),
2757
2790
  client: normalizeLocalAIClientName(parsed.TELEGRAM_BOT_AI_CLIENT || "", ""),
@@ -2813,6 +2846,8 @@ function buildMergedTelegramEnvParsed(globalParsed, entries) {
2813
2846
  const upper = String(entry.key || "").trim().toUpperCase();
2814
2847
  if (!upper) return;
2815
2848
  merged[`TELEGRAM_BOT_${upper}_SERVER_BOT_ID`] = String(entry.serverBotID || "").trim();
2849
+ merged[`TELEGRAM_BOT_${upper}_SERVER_NAME`] = String(entry.serverBotName || "").trim();
2850
+ merged[`TELEGRAM_BOT_${upper}_SERVER_ROLES`] = ensureArray(entry.serverRoles).join(",");
2816
2851
  merged[`TELEGRAM_BOT_${upper}_USERNAME`] = normalizeTelegramBotUsername(entry.username || "");
2817
2852
  merged[`TELEGRAM_BOT_${upper}_TOKEN`] = String(entry.token || "").trim();
2818
2853
  merged[`TELEGRAM_BOT_${upper}_ROLE_PROFILE`] = String(entry.roleProfile || "").trim();
@@ -2874,12 +2909,14 @@ function readTelegramEnvState() {
2874
2909
  }
2875
2910
 
2876
2911
  function isTelegramEntryEnvKey(rawKey) {
2877
- return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
2912
+ return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
2878
2913
  .test(String(rawKey || "").trim());
2879
2914
  }
2880
2915
 
2881
2916
  function telegramEntryCommentNameForEnv(entry) {
2882
2917
  const current = safeObject(entry);
2918
+ const serverBotName = String(current.serverBotName || "").trim();
2919
+ if (serverBotName) return serverBotName;
2883
2920
  const username = normalizeTelegramBotUsername(current.username || "");
2884
2921
  if (username) return username;
2885
2922
  return normalizeTelegramBotEnvKey(current.key || "", "telegram_bot");
@@ -2930,12 +2967,15 @@ function renderNormalizedTelegramEnv(parsedEnv) {
2930
2967
  function renderTelegramBotEntryEnv(entryRaw) {
2931
2968
  const entry = safeObject(entryRaw);
2932
2969
  const botName = telegramEntryCommentNameForEnv(entry);
2970
+ const serverRoles = parseTelegramServerRoles(entry.serverRoles || "");
2933
2971
  const lines = [
2934
2972
  "# Metheus local Telegram bot entry",
2935
2973
  `# Bot: ${botName}`,
2936
2974
  "",
2937
2975
  `TELEGRAM_BOT_NAME=${formatProviderEnvValue(botName)}`,
2938
2976
  `TELEGRAM_BOT_SERVER_BOT_ID=${formatProviderEnvValue(entry.serverBotID || "")}`,
2977
+ `TELEGRAM_BOT_SERVER_NAME=${formatProviderEnvValue(entry.serverBotName || "")}`,
2978
+ `TELEGRAM_BOT_SERVER_ROLES=${formatProviderEnvValue(serverRoles.join(","))}`,
2939
2979
  `TELEGRAM_BOT_TOKEN=${formatProviderEnvValue(entry.token || "")}`,
2940
2980
  `TELEGRAM_BOT_ROLE_PROFILE=${formatProviderEnvValue(entry.roleProfile || "")}`,
2941
2981
  `TELEGRAM_BOT_AI_CLIENT=${formatProviderEnvValue(entry.client || "")}`,
@@ -2954,6 +2994,8 @@ function writeTelegramEnvState(parsedEnv) {
2954
2994
  entry.token
2955
2995
  || entry.username
2956
2996
  || entry.serverBotID
2997
+ || entry.serverBotName
2998
+ || ensureArray(entry.serverRoles).length
2957
2999
  || entry.roleProfile
2958
3000
  || entry.client
2959
3001
  || entry.model
@@ -3009,7 +3051,13 @@ function normalizeExistingProviderEnvFile(provider, filePath) {
3009
3051
  function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
3010
3052
  const parsed = safeObject(parsedEnv);
3011
3053
  const legacyToken = String(parsed.TELEGRAM_BOT_TOKEN || "").trim();
3012
- const entries = collectTelegramEnvBotEntries(parsed).filter((entry) => entry.token || entry.username || entry.serverBotID);
3054
+ const entries = collectTelegramEnvBotEntries(parsed).filter((entry) => (
3055
+ entry.token
3056
+ || entry.username
3057
+ || entry.serverBotID
3058
+ || entry.serverBotName
3059
+ || ensureArray(entry.serverRoles).length
3060
+ ));
3013
3061
  const desiredBotID = firstNonEmptyString([
3014
3062
  selectors.serverBotID,
3015
3063
  selectors.botID,
@@ -3037,7 +3085,11 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
3037
3085
  }
3038
3086
  if (!selected && desiredUsername) {
3039
3087
  selected = entries.find(
3040
- (entry) => entry.username === desiredUsername || normalizeTelegramBotUsername(entry.key) === desiredUsername,
3088
+ (entry) => (
3089
+ entry.username === desiredUsername
3090
+ || normalizeTelegramBotUsername(entry.serverBotName) === desiredUsername
3091
+ || normalizeTelegramBotUsername(entry.key) === desiredUsername
3092
+ ),
3041
3093
  ) || null;
3042
3094
  }
3043
3095
  if (!selected && desiredBotKey) {
@@ -3071,6 +3123,8 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
3071
3123
  botKey: selected.key,
3072
3124
  botUsername: selected.username,
3073
3125
  serverBotID: selected.serverBotID,
3126
+ serverBotName: selected.serverBotName,
3127
+ serverRoles: ensureArray(selected.serverRoles),
3074
3128
  roleProfile: selected.roleProfile,
3075
3129
  client: selected.client,
3076
3130
  model: selected.model,
@@ -3095,6 +3149,8 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
3095
3149
  botKey: "",
3096
3150
  botUsername: "",
3097
3151
  serverBotID: "",
3152
+ serverBotName: "",
3153
+ serverRoles: [],
3098
3154
  roleProfile: "",
3099
3155
  client: "",
3100
3156
  model: "",
@@ -442,6 +442,8 @@ function telegramEntryEnvKeys(botKey) {
442
442
  const upper = String(botKey || "").trim().toUpperCase();
443
443
  return {
444
444
  serverBotID: `TELEGRAM_BOT_${upper}_SERVER_BOT_ID`,
445
+ serverBotName: `TELEGRAM_BOT_${upper}_SERVER_NAME`,
446
+ serverRoles: `TELEGRAM_BOT_${upper}_SERVER_ROLES`,
445
447
  username: `TELEGRAM_BOT_${upper}_USERNAME`,
446
448
  token: `TELEGRAM_BOT_${upper}_TOKEN`,
447
449
  roleProfile: `TELEGRAM_BOT_${upper}_ROLE_PROFILE`,
@@ -454,6 +456,8 @@ function telegramEntryEnvKeys(botKey) {
454
456
 
455
457
  function telegramEntryCommentName(entry) {
456
458
  const current = safeObject(entry);
459
+ const serverBotName = String(current.serverBotName || "").trim();
460
+ if (serverBotName) return serverBotName;
457
461
  const username = String(current.username || "").trim();
458
462
  if (username) return username;
459
463
  const key = String(current.key || "").trim();
@@ -486,6 +490,8 @@ function upsertTelegramEntry(parsedEnv, entry) {
486
490
  const keys = telegramEntryEnvKeys(entry.key);
487
491
  const next = { ...parsed };
488
492
  next[keys.serverBotID] = String(entry.serverBotID || "").trim();
493
+ next[keys.serverBotName] = String(entry.serverBotName || "").trim();
494
+ next[keys.serverRoles] = ensureArray(entry.serverRoles).join(",");
489
495
  next[keys.username] = persistTelegramUsername(entry);
490
496
  next[keys.token] = String(entry.token || "").trim();
491
497
  next[keys.roleProfile] = String(entry.roleProfile || "").trim();
@@ -512,7 +518,7 @@ function telegramKnownKeys(parsedEnv, deps) {
512
518
  }
513
519
 
514
520
  function isTelegramEntryEnvKey(rawKey) {
515
- return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
521
+ return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
516
522
  .test(String(rawKey || "").trim());
517
523
  }
518
524
 
@@ -525,6 +531,8 @@ function renderTelegramEnv(parsedEnv, deps) {
525
531
  entry.token
526
532
  || entry.username
527
533
  || entry.serverBotID
534
+ || entry.serverBotName
535
+ || ensureArray(entry.serverRoles).length
528
536
  || entry.roleProfile
529
537
  || entry.client
530
538
  || entry.model
@@ -562,6 +570,8 @@ function renderTelegramEnv(parsedEnv, deps) {
562
570
  const entryLines = [
563
571
  `# Telegram bot entry: ${telegramEntryCommentName(entry)}`,
564
572
  `${keys.serverBotID}=${formatEnvValue(entry.serverBotID || "")}`,
573
+ `${keys.serverBotName}=${formatEnvValue(entry.serverBotName || "")}`,
574
+ `${keys.serverRoles}=${formatEnvValue(ensureArray(entry.serverRoles).join(","))}`,
565
575
  ];
566
576
  if (String(entry.username || "").trim()) {
567
577
  entryLines.push(`${keys.username}=${formatEnvValue(entry.username || "")}`);
@@ -615,6 +625,39 @@ function renderTokenOnlyProviderEnv(provider, parsedEnv, deps) {
615
625
  return `${lines.join("\n").trimEnd()}\n`;
616
626
  }
617
627
 
628
+ function renderTelegramBotEntryFile(entryRaw) {
629
+ const entry = safeObject(entryRaw);
630
+ const botName = telegramEntryCommentName(entry);
631
+ const serverRoles = ensureArray(entry.serverRoles).join(",");
632
+ const lines = [
633
+ "# Metheus local Telegram bot entry",
634
+ `# Bot: ${botName}`,
635
+ "",
636
+ `TELEGRAM_BOT_NAME=${formatEnvValue(botName)}`,
637
+ `TELEGRAM_BOT_SERVER_BOT_ID=${formatEnvValue(entry.serverBotID || "")}`,
638
+ `TELEGRAM_BOT_SERVER_NAME=${formatEnvValue(entry.serverBotName || "")}`,
639
+ `TELEGRAM_BOT_SERVER_ROLES=${formatEnvValue(serverRoles)}`,
640
+ `TELEGRAM_BOT_TOKEN=${formatEnvValue(entry.token || "")}`,
641
+ `TELEGRAM_BOT_ROLE_PROFILE=${formatEnvValue(entry.roleProfile || "")}`,
642
+ `TELEGRAM_BOT_AI_CLIENT=${formatEnvValue(entry.client || "")}`,
643
+ `TELEGRAM_BOT_AI_MODEL=${formatEnvValue(entry.model || "")}`,
644
+ `TELEGRAM_BOT_AI_PERMISSION_MODE=${formatEnvValue(entry.permissionMode || "")}`,
645
+ `TELEGRAM_BOT_AI_REASONING_EFFORT=${formatEnvValue(entry.reasoningEffort || "")}`,
646
+ "",
647
+ ];
648
+ return `${lines.join("\n").trimEnd()}\n`;
649
+ }
650
+
651
+ function rewriteTelegramEntryFile(entry, deps) {
652
+ const entryFilePath = String(requireDependency(deps, "telegramBotEntryFilePath")(entry.key) || "").trim();
653
+ if (!entryFilePath) return;
654
+ fs.mkdirSync(path.dirname(entryFilePath), { recursive: true });
655
+ fs.writeFileSync(entryFilePath, renderTelegramBotEntryFile(entry), {
656
+ encoding: "utf8",
657
+ mode: 0o600,
658
+ });
659
+ }
660
+
618
661
  function writeProviderEnvState(provider, parsedEnv, deps) {
619
662
  if (provider === "telegram" && typeof deps?.writeTelegramEnvState === "function") {
620
663
  return deps.writeTelegramEnvState(parsedEnv);
@@ -644,6 +687,8 @@ function telegramEntriesForDisplay(parsedEnv, deps) {
644
687
  entry.token
645
688
  || entry.username
646
689
  || entry.serverBotID
690
+ || entry.serverBotName
691
+ || ensureArray(entry.serverRoles).length
647
692
  || entry.roleProfile
648
693
  || entry.client
649
694
  || entry.model
@@ -659,6 +704,8 @@ function telegramEntriesForDisplay(parsedEnv, deps) {
659
704
 
660
705
  function telegramEntryDisplayName(entry) {
661
706
  const current = safeObject(entry);
707
+ const serverBotName = String(current.serverBotName || "").trim();
708
+ if (serverBotName) return serverBotName;
662
709
  const username = String(current.username || "").trim();
663
710
  if (username) return `@${username}`;
664
711
  return String(current.key || "").trim() || "(unnamed)";
@@ -722,7 +769,11 @@ function findTelegramEntryByFlags(parsedEnv, flags, deps) {
722
769
  }
723
770
  if (requestedName) {
724
771
  const match = entries.find(
725
- (entry) => entry.username === requestedName || normalizeServerBotIdentityText(entry.key) === requestedName,
772
+ (entry) => (
773
+ entry.username === requestedName
774
+ || normalizeServerBotIdentityText(entry.serverBotName) === requestedName
775
+ || normalizeServerBotIdentityText(entry.key) === requestedName
776
+ ),
726
777
  );
727
778
  if (!match) {
728
779
  throw new Error(`Telegram bot entry with server bot name "${requestedName}" was not found`);
@@ -783,6 +834,7 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
783
834
  const initialBinding = await resolveTelegramServerBindingDetails(
784
835
  {
785
836
  serverBotID: current.serverBotID,
837
+ serverBotName: current.serverBotName,
786
838
  botUsername: current.username,
787
839
  botKey: current.key,
788
840
  roleProfile: current.roleProfile,
@@ -795,8 +847,13 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
795
847
  current.__preferServerIdentity = true;
796
848
  if (initialBinding.mode === "single") {
797
849
  current.serverBotID = String(initialBinding.serverBotID || "").trim();
850
+ current.serverBotName = String(initialBinding.name || "").trim();
851
+ current.serverRoles = ensureArray(initialBinding.roles);
798
852
  current.roleProfile = String(initialBinding.role || current.roleProfile || "").trim();
799
853
  applyRoleProfileDefaults(current, resolveRoleProfileDefaults(current.roleProfile, deps));
854
+ } else if (initialBinding.mode === "group") {
855
+ current.serverBotName = String(initialBinding.name || "").trim();
856
+ current.serverRoles = ensureArray(initialBinding.roles);
800
857
  }
801
858
  }
802
859
  }
@@ -847,6 +904,8 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
847
904
  const serverBot = await autoResolveTelegramServerBot(current, flags, deps);
848
905
  if (serverBot.matchMode === "group") {
849
906
  current.serverBotID = "";
907
+ current.serverBotName = String(serverBot.name || "").trim();
908
+ current.serverRoles = preferredRoleSort(serverBot.roles);
850
909
  current.__preferServerIdentity = true;
851
910
  current.roleProfile = "";
852
911
  current.client = "";
@@ -861,6 +920,8 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
861
920
  );
862
921
  } else if (String(serverBot.botID || "").trim()) {
863
922
  current.serverBotID = String(serverBot.botID || "").trim();
923
+ current.serverBotName = String(serverBot.name || "").trim();
924
+ current.serverRoles = ensureArray(serverBot.roles);
864
925
  current.__preferServerIdentity = true;
865
926
  current.roleProfile = String(serverBot.role || current.roleProfile || "").trim();
866
927
  serverRoleAutoResolved = String(serverBot.role || current.roleProfile || "").trim() || "__server_binding__";
@@ -995,6 +1056,8 @@ function renderBotListPayload(provider, state, deps) {
995
1056
  key: entry.key,
996
1057
  isDefault: entry.isDefault,
997
1058
  serverBotID: entry.serverBotID,
1059
+ serverBotName: entry.serverBotName,
1060
+ serverRoles: ensureArray(entry.serverRoles),
998
1061
  username: entry.username,
999
1062
  tokenConfigured: Boolean(entry.token),
1000
1063
  roleProfile: entry.roleProfile,
@@ -1037,6 +1100,8 @@ function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
1037
1100
  key: selectedEntry.key,
1038
1101
  isDefault: selectedEntry.isDefault,
1039
1102
  serverBotID: selectedEntry.serverBotID,
1103
+ serverBotName: selectedEntry.serverBotName,
1104
+ serverRoles: ensureArray(selectedEntry.serverRoles),
1040
1105
  username: selectedEntry.username,
1041
1106
  tokenConfigured: Boolean(selectedEntry.token),
1042
1107
  roleProfile: selectedEntry.roleProfile,
@@ -1877,7 +1942,12 @@ async function maybePromptGroupedServerRoleProfiles(ui, serverBot, deps) {
1877
1942
 
1878
1943
  function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
1879
1944
  const current = safeObject(envConfig);
1880
- if (!String(current.serverBotID || "").trim() && !String(current.botUsername || "").trim() && !String(current.botKey || "").trim()) {
1945
+ if (
1946
+ !String(current.serverBotID || "").trim()
1947
+ && !String(current.serverBotName || "").trim()
1948
+ && !String(current.botUsername || "").trim()
1949
+ && !String(current.botKey || "").trim()
1950
+ ) {
1881
1951
  return null;
1882
1952
  }
1883
1953
  const resolveGroupedPayload = (matches, matchedBy) => {
@@ -1929,7 +1999,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
1929
1999
  detail: `${String(match.name || "").trim() || "(unnamed)"} [${String(match.role || "").trim() || "-"}]`,
1930
2000
  };
1931
2001
  }
1932
- const normalizedServerIdentity = normalizeServerBotIdentityText(current.botUsername || current.botKey);
2002
+ const normalizedServerIdentity = normalizeServerBotIdentityText(current.serverBotName || current.botUsername || current.botKey);
1933
2003
  const matches = bots.filter((bot) => normalizeServerBotIdentityText(bot.name) === normalizedServerIdentity);
1934
2004
  if (!matches.length) {
1935
2005
  return {
@@ -2014,6 +2084,7 @@ async function buildTelegramEntrySelectionRows(entries, flags, deps) {
2014
2084
  ? resolveTelegramServerBindingDetailsFromBots(
2015
2085
  {
2016
2086
  serverBotID: current.serverBotID,
2087
+ serverBotName: current.serverBotName,
2017
2088
  botUsername: current.username,
2018
2089
  botKey: current.key,
2019
2090
  roleProfile: current.roleProfile,
@@ -2062,6 +2133,7 @@ async function verifyTelegramTokenCandidate(provider, token, apiBaseURL, timeout
2062
2133
  async function autoResolveTelegramServerBot(current, flags, deps) {
2063
2134
  const existingBotID = String(current?.serverBotID || "").trim();
2064
2135
  const preferredIdentity = firstNonEmptyString([
2136
+ current?.serverBotName,
2065
2137
  current?.username,
2066
2138
  current?.key,
2067
2139
  ]);
@@ -2275,6 +2347,8 @@ async function addTelegramBot(ui, flags, deps) {
2275
2347
  const nextParsed = upsertTelegramEntry(parsed, {
2276
2348
  key: botKey,
2277
2349
  serverBotID: String(getServerBotIDFlag(flags) || serverBot.botID || "").trim(),
2350
+ serverBotName: String(serverBot.name || "").trim(),
2351
+ serverRoles: preferredRoleSort(serverBot.roles),
2278
2352
  username,
2279
2353
  __preferServerIdentity: serverBot.matchMode === "group" || Boolean(String(getServerBotIDFlag(flags) || serverBot.botID || "").trim()),
2280
2354
  token,
@@ -2391,6 +2465,23 @@ async function editTelegramBot(ui, flags, deps) {
2391
2465
  if (boolFromRaw(flags.default, false)) {
2392
2466
  parsed.TELEGRAM_DEFAULT_BOT_KEY = current.key;
2393
2467
  }
2468
+ if (current.serverBotID || current.username || current.serverBotName) {
2469
+ const resolvedServerBot = await autoResolveTelegramServerBot(current, flags, deps);
2470
+ if (resolvedServerBot.matchMode === "group") {
2471
+ current.serverBotID = String(getServerBotIDFlag(flags) || current.serverBotID || "").trim();
2472
+ current.serverBotName = String(resolvedServerBot.name || current.serverBotName || "").trim();
2473
+ current.serverRoles = preferredRoleSort(resolvedServerBot.roles);
2474
+ if (!hasOwnFlag(flags, "role-profile")) current.roleProfile = "";
2475
+ if (!(hasOwnFlag(flags, "client") || hasOwnFlag(flags, "ai-client"))) current.client = "";
2476
+ if (!(hasOwnFlag(flags, "model") || hasOwnFlag(flags, "ai-model"))) current.model = "";
2477
+ if (!(hasOwnFlag(flags, "permission-mode") || hasOwnFlag(flags, "ai-permission-mode"))) current.permissionMode = "";
2478
+ if (!(hasOwnFlag(flags, "reasoning-effort") || hasOwnFlag(flags, "ai-reasoning-effort"))) current.reasoningEffort = "";
2479
+ } else if (String(resolvedServerBot.botID || "").trim()) {
2480
+ current.serverBotID = String(resolvedServerBot.botID || "").trim();
2481
+ current.serverBotName = String(resolvedServerBot.name || "").trim();
2482
+ current.serverRoles = ensureArray(resolvedServerBot.roles);
2483
+ }
2484
+ }
2394
2485
  saveTelegramBotEdit(parsed, selected, current, deps);
2395
2486
  return;
2396
2487
  }
@@ -2462,6 +2553,43 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2462
2553
  if (serverBinding && !serverBinding.ok) {
2463
2554
  overallOK = false;
2464
2555
  }
2556
+ if (serverBinding?.ok && envConfig.botKey) {
2557
+ const selectedEntry = findTelegramEntryByFlags(
2558
+ state.parsed,
2559
+ { "bot-key": envConfig.botKey },
2560
+ deps,
2561
+ );
2562
+ if (selectedEntry) {
2563
+ const desiredServerBotName = String(serverBinding.name || "").trim();
2564
+ const desiredServerRoles = ensureArray(serverBinding.roles);
2565
+ const currentServerRoles = ensureArray(selectedEntry.serverRoles);
2566
+ const currentServerRolesJoined = currentServerRoles.join(",");
2567
+ const desiredServerRolesJoined = desiredServerRoles.join(",");
2568
+ const desiredServerBotID = (
2569
+ String(selectedEntry.serverBotID || "").trim()
2570
+ || (serverBinding.mode === "single" ? String(serverBinding.serverBotID || "").trim() : "")
2571
+ );
2572
+ const needsMetadataBackfill = (
2573
+ String(selectedEntry.serverBotName || "").trim() !== desiredServerBotName
2574
+ || currentServerRolesJoined !== desiredServerRolesJoined
2575
+ || String(selectedEntry.serverBotID || "").trim() !== desiredServerBotID
2576
+ );
2577
+ if (needsMetadataBackfill) {
2578
+ const nextEntry = {
2579
+ ...selectedEntry,
2580
+ serverBotID: desiredServerBotID,
2581
+ serverBotName: desiredServerBotName,
2582
+ serverRoles: desiredServerRoles,
2583
+ };
2584
+ const nextParsed = upsertTelegramEntry(state.parsed, nextEntry);
2585
+ writeProviderEnvState("telegram", nextParsed, deps);
2586
+ rewriteTelegramEntryFile(nextEntry, deps);
2587
+ envConfig.serverBotID = desiredServerBotID;
2588
+ envConfig.serverBotName = desiredServerBotName;
2589
+ envConfig.serverRoles = desiredServerRoles;
2590
+ }
2591
+ }
2592
+ }
2465
2593
  routeLinks = summarizeTelegramRouteLinks(state.parsed, {
2466
2594
  key: envConfig.botKey,
2467
2595
  serverBotID: envConfig.serverBotID,
@@ -2492,6 +2620,8 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2492
2620
  filePath: envConfig.filePath,
2493
2621
  botKey: envConfig.botKey || "",
2494
2622
  serverBotID: envConfig.serverBotID || "",
2623
+ serverBotName: envConfig.serverBotName || "",
2624
+ serverRoles: ensureArray(envConfig.serverRoles),
2495
2625
  detail: result.detail || "",
2496
2626
  roleProfile: envConfig.roleProfile || "",
2497
2627
  client: envConfig.client || "",
@@ -2508,6 +2638,8 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
2508
2638
  `file: ${envConfig.filePath}`,
2509
2639
  provider === "telegram" ? `bot_key: ${envConfig.botKey || "-"}` : "",
2510
2640
  provider === "telegram" ? `server_bot_id: ${envConfig.serverBotID || "-"}` : "",
2641
+ provider === "telegram" ? `stored_server_name: ${envConfig.serverBotName || "-"}` : "",
2642
+ provider === "telegram" ? `stored_server_roles: ${ensureArray(envConfig.serverRoles).join(", ") || "-"}` : "",
2511
2643
  provider === "telegram" ? `role_profile: ${envConfig.roleProfile || "-"}` : "",
2512
2644
  provider === "telegram" ? `ai_client: ${envConfig.client ? displayLocalAIClientName(envConfig.client) : "-"}` : "",
2513
2645
  provider === "telegram" ? `ai_model: ${envConfig.model || "-"}` : "",
@@ -353,13 +353,15 @@ export async function runSelftestBotCommands(push, deps) {
353
353
  push(
354
354
  "bot_add_guided_creates_named_telegram_entry",
355
355
  String(addState.TELEGRAM_BOT_NAME || "").toLowerCase().includes("monitorselftestbot")
356
+ && String(addState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
357
+ && String(addState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor"
356
358
  && String(addState.TELEGRAM_BOT_TOKEN || "") === "selftest-main-token"
357
359
  && String(addState.TELEGRAM_BOT_ROLE_PROFILE || "") === "monitor"
358
360
  && String(addState.TELEGRAM_BOT_AI_CLIENT || "") === "codex"
359
361
  && String(addState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "read_only"
360
362
  && String(addState.TELEGRAM_BOT_AI_REASONING_EFFORT || "") === "low"
361
363
  && String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "") === "monitorselftestbot",
362
- `default=${String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "")} token=${String(addState.TELEGRAM_BOT_TOKEN || "")} role=${String(addState.TELEGRAM_BOT_ROLE_PROFILE || "")} client=${String(addState.TELEGRAM_BOT_AI_CLIENT || "")}`,
364
+ `default=${String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "")} token=${String(addState.TELEGRAM_BOT_TOKEN || "")} server_name=${String(addState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(addState.TELEGRAM_BOT_SERVER_ROLES || "")} role=${String(addState.TELEGRAM_BOT_ROLE_PROFILE || "")} client=${String(addState.TELEGRAM_BOT_AI_CLIENT || "")}`,
363
365
  );
364
366
 
365
367
  const groupedMock = await createMockServer({
@@ -403,10 +405,12 @@ export async function runSelftestBotCommands(push, deps) {
403
405
  "bot_add_guided_autoresolves_server_bot_group_without_role_prompt",
404
406
  String(groupedState.TELEGRAM_BOT_NAME || "").toLowerCase() === "ryoai_bot"
405
407
  && String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "") === ""
408
+ && String(groupedState.TELEGRAM_BOT_SERVER_NAME || "") === "RyoAI_bot"
409
+ && String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor,review,worker,approval"
406
410
  && String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "") === ""
407
411
  && String(groupedState.TELEGRAM_BOT_AI_CLIENT || "") === ""
408
412
  && String(groupedState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "",
409
- `name=${String(groupedState.TELEGRAM_BOT_NAME || "")} server_bot_id=${String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "")} role=${String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "")}`,
413
+ `name=${String(groupedState.TELEGRAM_BOT_NAME || "")} server_name=${String(groupedState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "")} server_bot_id=${String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "")} role=${String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "")}`,
410
414
  );
411
415
 
412
416
  const groupedEditResult = await runCLI({
@@ -758,11 +762,13 @@ export async function runSelftestBotCommands(push, deps) {
758
762
  push(
759
763
  "bot_add_accepts_ai_prefixed_option_aliases",
760
764
  String(aliasAddState.TELEGRAM_BOT_SERVER_BOT_ID || "") === mock.bots[0].id
765
+ && String(aliasAddState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
766
+ && String(aliasAddState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor"
761
767
  && String(aliasAddState.TELEGRAM_BOT_AI_CLIENT || "") === "codex"
762
768
  && String(aliasAddState.TELEGRAM_BOT_AI_MODEL || "") === "gpt-5.4"
763
769
  && String(aliasAddState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "read_only"
764
770
  && String(aliasAddState.TELEGRAM_BOT_AI_REASONING_EFFORT || "") === "low",
765
- `client=${String(aliasAddState.TELEGRAM_BOT_AI_CLIENT || "")} model=${String(aliasAddState.TELEGRAM_BOT_AI_MODEL || "")}`,
771
+ `server_name=${String(aliasAddState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(aliasAddState.TELEGRAM_BOT_SERVER_ROLES || "")} client=${String(aliasAddState.TELEGRAM_BOT_AI_CLIENT || "")} model=${String(aliasAddState.TELEGRAM_BOT_AI_MODEL || "")}`,
766
772
  );
767
773
 
768
774
  const guidedRemoveBeforeList = await runCLI({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.100",
3
+ "version": "0.2.101",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [