metheus-governance-mcp-cli 0.2.92 → 0.2.93
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 -0
- package/cli.mjs +147 -1
- package/lib/bot-commands.mjs +16 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,6 +164,8 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
|
|
|
164
164
|
- `~/.metheus/kakaotalk.env`
|
|
165
165
|
- `~/.metheus/bot-runner.json`
|
|
166
166
|
|
|
167
|
+
When `telegram.env` already exists, bootstrap/setup keeps your secrets but auto-normalizes the file to the latest named-entry layout so stale keys such as `TELEGRAM_BOT_<NAME>_BOT_*` are rewritten to `TELEGRAM_BOT_<NAME>_*`.
|
|
168
|
+
|
|
167
169
|
Fill provider bot secrets and provider-local transport options locally. Project chat destination identifiers should be managed on the Metheus server as project chat destinations, not as local env values and not inside legacy Chat Hooks/webhooks.
|
|
168
170
|
|
|
169
171
|
`~/.metheus/bot-runner.json` is the local automation profile for:
|
package/cli.mjs
CHANGED
|
@@ -1053,7 +1053,12 @@ function ensureProviderEnvTemplate(provider) {
|
|
|
1053
1053
|
const filePath = providerEnvFilePath(provider);
|
|
1054
1054
|
try {
|
|
1055
1055
|
if (fs.existsSync(filePath)) {
|
|
1056
|
-
return {
|
|
1056
|
+
return {
|
|
1057
|
+
filePath,
|
|
1058
|
+
created: false,
|
|
1059
|
+
existed: true,
|
|
1060
|
+
normalized: normalizeExistingProviderEnvFile(provider, filePath),
|
|
1061
|
+
};
|
|
1057
1062
|
}
|
|
1058
1063
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1059
1064
|
fs.writeFileSync(filePath, providerEnvTemplate(provider), "utf8");
|
|
@@ -2636,6 +2641,15 @@ function parseSimpleEnvText(rawText) {
|
|
|
2636
2641
|
return out;
|
|
2637
2642
|
}
|
|
2638
2643
|
|
|
2644
|
+
function formatProviderEnvValue(rawValue) {
|
|
2645
|
+
const text = String(rawValue ?? "");
|
|
2646
|
+
if (!text) return "";
|
|
2647
|
+
if (/^[A-Za-z0-9_./:@,\-]+$/.test(text)) {
|
|
2648
|
+
return text;
|
|
2649
|
+
}
|
|
2650
|
+
return JSON.stringify(text);
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2639
2653
|
function normalizeTelegramBotEnvKey(rawValue, fallback = "") {
|
|
2640
2654
|
const normalized = String(rawValue || "")
|
|
2641
2655
|
.trim()
|
|
@@ -2705,6 +2719,124 @@ function collectTelegramEnvBotEntries(parsedEnv) {
|
|
|
2705
2719
|
return Array.from(entries.values());
|
|
2706
2720
|
}
|
|
2707
2721
|
|
|
2722
|
+
function isTelegramEntryEnvKey(rawKey) {
|
|
2723
|
+
return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
|
|
2724
|
+
.test(String(rawKey || "").trim());
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
function telegramEntryCommentNameForEnv(entry) {
|
|
2728
|
+
const current = safeObject(entry);
|
|
2729
|
+
const username = normalizeTelegramBotUsername(current.username || "");
|
|
2730
|
+
if (username) return username;
|
|
2731
|
+
const key = normalizeTelegramBotEnvKey(current.key || "", "telegram_bot");
|
|
2732
|
+
if (!key) return "telegram_bot";
|
|
2733
|
+
return key.endsWith("_bot") ? key : `${key}_bot`;
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
function renderNormalizedTelegramEnv(parsedEnv) {
|
|
2737
|
+
const parsed = safeObject(parsedEnv);
|
|
2738
|
+
const entries = collectTelegramEnvBotEntries(parsed)
|
|
2739
|
+
.filter((entry) => (
|
|
2740
|
+
entry.token
|
|
2741
|
+
|| entry.username
|
|
2742
|
+
|| entry.serverBotID
|
|
2743
|
+
|| entry.roleProfile
|
|
2744
|
+
|| entry.client
|
|
2745
|
+
|| entry.model
|
|
2746
|
+
|| entry.permissionMode
|
|
2747
|
+
|| entry.reasoningEffort
|
|
2748
|
+
))
|
|
2749
|
+
.sort((left, right) => String(left.key || "").localeCompare(String(right.key || "")));
|
|
2750
|
+
const defaultBotKey = normalizeTelegramBotEnvKey(parsed.TELEGRAM_DEFAULT_BOT_KEY || "", "");
|
|
2751
|
+
if (defaultBotKey) {
|
|
2752
|
+
entries.sort((left, right) => {
|
|
2753
|
+
if (left.key === defaultBotKey) return -1;
|
|
2754
|
+
if (right.key === defaultBotKey) return 1;
|
|
2755
|
+
return String(left.key || "").localeCompare(String(right.key || ""));
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
const lines = [
|
|
2759
|
+
"# Metheus local Telegram bot settings",
|
|
2760
|
+
"# Keep this file on your machine only. Do not commit it.",
|
|
2761
|
+
"# Store Telegram bot secrets, AI bindings, and local transport options only.",
|
|
2762
|
+
"",
|
|
2763
|
+
`TELEGRAM_API_BASE_URL=${formatProviderEnvValue(parsed.TELEGRAM_API_BASE_URL || "")}`,
|
|
2764
|
+
`TELEGRAM_AUTO_CLEAR_WEBHOOK=${boolFromRaw(parsed.TELEGRAM_AUTO_CLEAR_WEBHOOK, true) ? "true" : "false"}`,
|
|
2765
|
+
`TELEGRAM_ALLOWED_UPDATES=${formatProviderEnvValue(String(parsed.TELEGRAM_ALLOWED_UPDATES || "").trim() || "message,edited_message")}`,
|
|
2766
|
+
`TELEGRAM_DEFAULT_BOT_KEY=${formatProviderEnvValue(defaultBotKey || "")}`,
|
|
2767
|
+
"",
|
|
2768
|
+
"# Legacy fallback",
|
|
2769
|
+
`TELEGRAM_BOT_TOKEN=${formatProviderEnvValue(parsed.TELEGRAM_BOT_TOKEN || "")}`,
|
|
2770
|
+
"",
|
|
2771
|
+
];
|
|
2772
|
+
if (!entries.length) {
|
|
2773
|
+
lines.push("# No named Telegram bot entries configured yet", "");
|
|
2774
|
+
} else {
|
|
2775
|
+
entries.forEach((entry) => {
|
|
2776
|
+
const upper = String(entry.key || "").trim().toUpperCase();
|
|
2777
|
+
lines.push(
|
|
2778
|
+
`# Telegram bot entry: ${telegramEntryCommentNameForEnv(entry)}`,
|
|
2779
|
+
`TELEGRAM_BOT_${upper}_SERVER_BOT_ID=${formatProviderEnvValue(entry.serverBotID || "")}`,
|
|
2780
|
+
);
|
|
2781
|
+
if (String(entry.username || "").trim()) {
|
|
2782
|
+
lines.push(`TELEGRAM_BOT_${upper}_USERNAME=${formatProviderEnvValue(entry.username || "")}`);
|
|
2783
|
+
}
|
|
2784
|
+
lines.push(
|
|
2785
|
+
`TELEGRAM_BOT_${upper}_TOKEN=${formatProviderEnvValue(entry.token || "")}`,
|
|
2786
|
+
`TELEGRAM_BOT_${upper}_ROLE_PROFILE=${formatProviderEnvValue(entry.roleProfile || "")}`,
|
|
2787
|
+
`TELEGRAM_BOT_${upper}_AI_CLIENT=${formatProviderEnvValue(entry.client || "")}`,
|
|
2788
|
+
`TELEGRAM_BOT_${upper}_AI_MODEL=${formatProviderEnvValue(entry.model || "")}`,
|
|
2789
|
+
`TELEGRAM_BOT_${upper}_AI_PERMISSION_MODE=${formatProviderEnvValue(entry.permissionMode || "")}`,
|
|
2790
|
+
`TELEGRAM_BOT_${upper}_AI_REASONING_EFFORT=${formatProviderEnvValue(entry.reasoningEffort || "")}`,
|
|
2791
|
+
"",
|
|
2792
|
+
);
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
2795
|
+
const knownKeys = new Set([
|
|
2796
|
+
"TELEGRAM_API_BASE_URL",
|
|
2797
|
+
"TELEGRAM_AUTO_CLEAR_WEBHOOK",
|
|
2798
|
+
"TELEGRAM_ALLOWED_UPDATES",
|
|
2799
|
+
"TELEGRAM_DEFAULT_BOT_KEY",
|
|
2800
|
+
"TELEGRAM_BOT_TOKEN",
|
|
2801
|
+
]);
|
|
2802
|
+
entries.forEach((entry) => {
|
|
2803
|
+
const upper = String(entry.key || "").trim().toUpperCase();
|
|
2804
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_SERVER_BOT_ID`);
|
|
2805
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_USERNAME`);
|
|
2806
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_TOKEN`);
|
|
2807
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_ROLE_PROFILE`);
|
|
2808
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_AI_CLIENT`);
|
|
2809
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_AI_MODEL`);
|
|
2810
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_AI_PERMISSION_MODE`);
|
|
2811
|
+
knownKeys.add(`TELEGRAM_BOT_${upper}_AI_REASONING_EFFORT`);
|
|
2812
|
+
});
|
|
2813
|
+
const extras = Object.keys(parsed)
|
|
2814
|
+
.filter((key) => !knownKeys.has(key) && !isTelegramEntryEnvKey(key))
|
|
2815
|
+
.sort((left, right) => left.localeCompare(right));
|
|
2816
|
+
if (extras.length) {
|
|
2817
|
+
lines.push("# Additional preserved keys");
|
|
2818
|
+
extras.forEach((key) => {
|
|
2819
|
+
lines.push(`${key}=${formatProviderEnvValue(parsed[key])}`);
|
|
2820
|
+
});
|
|
2821
|
+
lines.push("");
|
|
2822
|
+
}
|
|
2823
|
+
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}\n`;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
function normalizeExistingProviderEnvFile(provider, filePath) {
|
|
2827
|
+
if (normalizeBotProvider(provider) !== "telegram") return false;
|
|
2828
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
2829
|
+
const rendered = renderNormalizedTelegramEnv(parseSimpleEnvText(raw));
|
|
2830
|
+
if (raw.replace(/\r\n/g, "\n") === rendered.replace(/\r\n/g, "\n")) {
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
fs.writeFileSync(filePath, rendered, {
|
|
2834
|
+
encoding: "utf8",
|
|
2835
|
+
mode: 0o600,
|
|
2836
|
+
});
|
|
2837
|
+
return true;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2708
2840
|
function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
2709
2841
|
const parsed = safeObject(parsedEnv);
|
|
2710
2842
|
const legacyToken = String(parsed.TELEGRAM_BOT_TOKEN || "").trim();
|
|
@@ -5114,6 +5246,20 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5114
5246
|
&& telegramEnvV2ByUsername.allowedUpdates.includes("channel_post"),
|
|
5115
5247
|
`bot=${String(telegramEnvV2ByUsername.botKey || "(none)")} updates=${String((telegramEnvV2ByUsername.allowedUpdates || []).join(","))}`,
|
|
5116
5248
|
);
|
|
5249
|
+
const telegramEnvNormalizedText = renderNormalizedTelegramEnv(parseSimpleEnvText(`
|
|
5250
|
+
TELEGRAM_DEFAULT_BOT_KEY=ryoai_bot
|
|
5251
|
+
TELEGRAM_BOT_RYOAI_BOT_SERVER_BOT_ID=bot-ryoai
|
|
5252
|
+
TELEGRAM_BOT_RYOAI_BOT_TOKEN=ryoai-token
|
|
5253
|
+
`));
|
|
5254
|
+
push(
|
|
5255
|
+
"telegram_env_existing_file_auto_normalizes_named_keys",
|
|
5256
|
+
telegramEnvNormalizedText.includes("TELEGRAM_DEFAULT_BOT_KEY=ryoai")
|
|
5257
|
+
&& telegramEnvNormalizedText.includes("TELEGRAM_BOT_RYOAI_SERVER_BOT_ID=bot-ryoai")
|
|
5258
|
+
&& telegramEnvNormalizedText.includes("TELEGRAM_BOT_RYOAI_TOKEN=ryoai-token")
|
|
5259
|
+
&& telegramEnvNormalizedText.includes("# Telegram bot entry: ryoai_bot")
|
|
5260
|
+
&& !telegramEnvNormalizedText.includes("TELEGRAM_BOT_RYOAI_BOT_TOKEN"),
|
|
5261
|
+
telegramEnvNormalizedText,
|
|
5262
|
+
);
|
|
5117
5263
|
const telegramSupport = getProviderSupport("telegram");
|
|
5118
5264
|
push(
|
|
5119
5265
|
"provider_support_telegram_full_runner",
|
package/lib/bot-commands.mjs
CHANGED
|
@@ -441,6 +441,15 @@ function telegramEntryEnvKeys(botKey) {
|
|
|
441
441
|
};
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
function telegramEntryCommentName(entry) {
|
|
445
|
+
const current = safeObject(entry);
|
|
446
|
+
const username = String(current.username || "").trim();
|
|
447
|
+
if (username) return username;
|
|
448
|
+
const key = String(current.key || "").trim();
|
|
449
|
+
if (!key) return "telegram_bot";
|
|
450
|
+
return key.endsWith("_bot") ? key : `${key}_bot`;
|
|
451
|
+
}
|
|
452
|
+
|
|
444
453
|
function persistTelegramUsername(entry) {
|
|
445
454
|
const current = safeObject(entry);
|
|
446
455
|
if (current.__preferServerIdentity) return "";
|
|
@@ -491,6 +500,11 @@ function telegramKnownKeys(parsedEnv, deps) {
|
|
|
491
500
|
return keys;
|
|
492
501
|
}
|
|
493
502
|
|
|
503
|
+
function isTelegramEntryEnvKey(rawKey) {
|
|
504
|
+
return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
|
|
505
|
+
.test(String(rawKey || "").trim());
|
|
506
|
+
}
|
|
507
|
+
|
|
494
508
|
function renderTelegramEnv(parsedEnv, deps) {
|
|
495
509
|
const parsed = safeObject(parsedEnv);
|
|
496
510
|
const collectEntries = requireDependency(deps, "collectTelegramEnvBotEntries");
|
|
@@ -535,7 +549,7 @@ function renderTelegramEnv(parsedEnv, deps) {
|
|
|
535
549
|
entries.forEach((entry) => {
|
|
536
550
|
const keys = telegramEntryEnvKeys(entry.key);
|
|
537
551
|
const entryLines = [
|
|
538
|
-
`# Telegram bot entry: ${entry
|
|
552
|
+
`# Telegram bot entry: ${telegramEntryCommentName(entry)}`,
|
|
539
553
|
`${keys.serverBotID}=${formatEnvValue(entry.serverBotID || "")}`,
|
|
540
554
|
];
|
|
541
555
|
if (String(entry.username || "").trim()) {
|
|
@@ -555,7 +569,7 @@ function renderTelegramEnv(parsedEnv, deps) {
|
|
|
555
569
|
}
|
|
556
570
|
const knownKeys = telegramKnownKeys(parsed, deps);
|
|
557
571
|
const extras = Object.keys(parsed)
|
|
558
|
-
.filter((key) => !knownKeys.has(key))
|
|
572
|
+
.filter((key) => !knownKeys.has(key) && !isTelegramEntryEnvKey(key))
|
|
559
573
|
.sort((left, right) => left.localeCompare(right));
|
|
560
574
|
if (extras.length) {
|
|
561
575
|
lines.push("# Additional preserved keys");
|