metheus-governance-mcp-cli 0.2.97 → 0.2.99
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 +22 -23
- package/cli.mjs +92 -25
- package/lib/selftest-bot-commands.mjs +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,6 @@ npm install -g metheus-governance-mcp-cli@latest
|
|
|
23
23
|
|
|
24
24
|
Install creates local provider settings templates here:
|
|
25
25
|
|
|
26
|
-
- `~/.metheus/telegram.env`
|
|
27
26
|
- `~/.metheus/telegram-bots/`
|
|
28
27
|
- `~/.metheus/slack.env`
|
|
29
28
|
- `~/.metheus/kakaotalk.env`
|
|
@@ -32,8 +31,9 @@ Install creates local provider settings templates here:
|
|
|
32
31
|
These files are for local provider bot secrets, local transport options, and optional per-bot local AI binding.
|
|
33
32
|
|
|
34
33
|
- Store locally:
|
|
35
|
-
-
|
|
36
|
-
-
|
|
34
|
+
- Telegram-wide settings in `~/.metheus/telegram-bots/_global.env`
|
|
35
|
+
- `TELEGRAM_BOT_TOKEN` as a legacy Telegram fallback in `_global.env`
|
|
36
|
+
- one Telegram bot file per entry in `~/.metheus/telegram-bots/<server-bot-name>.env`
|
|
37
37
|
- `SLACK_BOT_TOKEN`
|
|
38
38
|
- `KAKAOTALK_BOT_TOKEN`
|
|
39
39
|
- Server-side Metheus stores project chat destination metadata separately:
|
|
@@ -41,23 +41,23 @@ These files are for local provider bot secrets, local transport options, and opt
|
|
|
41
41
|
- `chat_id` / channel / room identifier
|
|
42
42
|
- label / active state
|
|
43
43
|
- Do not put project chat destination identifiers in local env files.
|
|
44
|
-
- Telegram-wide settings
|
|
44
|
+
- Telegram-wide settings now live in `telegram-bots/_global.env`; Telegram per-bot secrets and AI fields live in `telegram-bots/*.env`.
|
|
45
45
|
|
|
46
46
|
Example templates:
|
|
47
47
|
|
|
48
48
|
```env
|
|
49
|
-
# ~/.metheus/telegram.env
|
|
49
|
+
# ~/.metheus/telegram-bots/_global.env
|
|
50
50
|
TELEGRAM_API_BASE_URL=
|
|
51
51
|
TELEGRAM_AUTO_CLEAR_WEBHOOK=true
|
|
52
52
|
TELEGRAM_ALLOWED_UPDATES=message,edited_message
|
|
53
|
-
TELEGRAM_DEFAULT_BOT_KEY=
|
|
53
|
+
TELEGRAM_DEFAULT_BOT_KEY=ryoai_bot
|
|
54
54
|
|
|
55
55
|
# Legacy fallback
|
|
56
56
|
TELEGRAM_BOT_TOKEN=
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
```env
|
|
60
|
-
# ~/.metheus/telegram-bots/
|
|
60
|
+
# ~/.metheus/telegram-bots/ryoai_bot.env
|
|
61
61
|
TELEGRAM_BOT_NAME=ryoai_bot
|
|
62
62
|
TELEGRAM_BOT_SERVER_BOT_ID=
|
|
63
63
|
TELEGRAM_BOT_TOKEN=
|
|
@@ -164,7 +164,6 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
|
|
|
164
164
|
|
|
165
165
|
`setup` also ensures local provider templates exist:
|
|
166
166
|
|
|
167
|
-
- `~/.metheus/telegram.env`
|
|
168
167
|
- `~/.metheus/telegram-bots/`
|
|
169
168
|
- `~/.metheus/slack.env`
|
|
170
169
|
- `~/.metheus/kakaotalk.env`
|
|
@@ -172,8 +171,8 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
|
|
|
172
171
|
|
|
173
172
|
When Telegram local config already exists, bootstrap/setup keeps your secrets but auto-normalizes the layout to the latest split structure:
|
|
174
173
|
|
|
175
|
-
- Telegram-wide settings stay in `~/.metheus/telegram.env`
|
|
176
|
-
- per-bot secrets move to `~/.metheus/telegram-bots/<bot-
|
|
174
|
+
- Telegram-wide settings stay in `~/.metheus/telegram-bots/_global.env`
|
|
175
|
+
- per-bot secrets move to `~/.metheus/telegram-bots/<server-bot-name>.env`
|
|
177
176
|
- stale inline keys such as `TELEGRAM_BOT_<NAME>_BOT_*` are rewritten into generic per-bot keys
|
|
178
177
|
|
|
179
178
|
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.
|
|
@@ -183,7 +182,7 @@ Fill provider bot secrets and provider-local transport options locally. Project
|
|
|
183
182
|
- which provider/role bot profile to use
|
|
184
183
|
- which `project_id -> workspace_dir` mapping to apply locally
|
|
185
184
|
- which role profile maps to which local CLI/model/permission/reasoning policy
|
|
186
|
-
- which server bot maps to which local LLM execution profile via `telegram.env` or fallback `bot_bindings`
|
|
185
|
+
- which server bot maps to which local LLM execution profile via `telegram-bots/_global.env`, `telegram-bots/*.env`, or fallback `bot_bindings`
|
|
187
186
|
|
|
188
187
|
Built-in helper command for legacy fallback/testing:
|
|
189
188
|
|
|
@@ -224,15 +223,15 @@ Direct commands:
|
|
|
224
223
|
|
|
225
224
|
```bash
|
|
226
225
|
metheus-governance-mcp-cli bot list
|
|
227
|
-
metheus-governance-mcp-cli bot show --provider telegram --bot-key
|
|
226
|
+
metheus-governance-mcp-cli bot show --provider telegram --bot-key ryoai_bot
|
|
228
227
|
metheus-governance-mcp-cli bot add --provider telegram
|
|
229
228
|
metheus-governance-mcp-cli bot edit
|
|
230
229
|
metheus-governance-mcp-cli bot edit --provider telegram
|
|
231
230
|
metheus-governance-mcp-cli bot remove --provider telegram
|
|
232
|
-
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key
|
|
233
|
-
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key
|
|
231
|
+
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key ryoai_bot
|
|
232
|
+
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key ryoai_bot
|
|
234
233
|
metheus-governance-mcp-cli bot global --provider telegram
|
|
235
|
-
metheus-governance-mcp-cli bot verify --provider telegram --bot-key
|
|
234
|
+
metheus-governance-mcp-cli bot verify --provider telegram --bot-key ryoai_bot
|
|
236
235
|
```
|
|
237
236
|
|
|
238
237
|
Behavior:
|
|
@@ -259,7 +258,7 @@ Behavior:
|
|
|
259
258
|
- `bot set-default` without flags starts a guided numbered flow: provider -> bot entry -> confirm default change.
|
|
260
259
|
- `bot verify` without flags starts a guided numbered flow: provider -> bot entry -> output format.
|
|
261
260
|
- `bot remove` without flags starts a guided numbered flow: provider -> bot entry -> confirm removal.
|
|
262
|
-
- Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<bot-
|
|
261
|
+
- Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<server-bot-name>.env` with generic fields:
|
|
263
262
|
- `TELEGRAM_BOT_NAME`
|
|
264
263
|
- `TELEGRAM_BOT_SERVER_BOT_ID`
|
|
265
264
|
- `TELEGRAM_BOT_TOKEN`
|
|
@@ -281,11 +280,11 @@ Non-interactive examples:
|
|
|
281
280
|
metheus-governance-mcp-cli bot global --provider telegram --non-interactive true --api-base-url http://127.0.0.1:8999/telegram --auto-clear-webhook false --allowed-updates message,edited_message,channel_post
|
|
282
281
|
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --default true
|
|
283
282
|
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --ai-client gpt --ai-model "gpt-5.4" --ai-permission-mode read_only --ai-reasoning-effort low
|
|
284
|
-
metheus-governance-mcp-cli bot edit --provider telegram --bot-key
|
|
285
|
-
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key
|
|
286
|
-
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key
|
|
287
|
-
metheus-governance-mcp-cli bot remove --provider telegram --bot-key
|
|
288
|
-
metheus-governance-mcp-cli bot verify --provider telegram --bot-key
|
|
283
|
+
metheus-governance-mcp-cli bot edit --provider telegram --bot-key ryoai_bot --non-interactive true --ai-client claude --ai-model "Sonnet 4.6r" --ai-permission-mode danger_full_access --ai-reasoning-effort high
|
|
284
|
+
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key ryoai_bot --non-interactive true
|
|
285
|
+
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key ryoai_bot --server-bot-id <server_bot_uuid> --bot-name <telegram_username> --role-profile monitor --ai-client gpt --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
|
|
286
|
+
metheus-governance-mcp-cli bot remove --provider telegram --bot-key ryoai_bot --non-interactive true
|
|
287
|
+
metheus-governance-mcp-cli bot verify --provider telegram --bot-key ryoai_bot --json true
|
|
289
288
|
```
|
|
290
289
|
|
|
291
290
|
For direct Telegram adds, the CLI can derive the local entry key from the matched server bot name. You no longer need `--bot-key` or `--username` in the normal server-bound path. Treat `--bot-key` as an advanced override only when you intentionally want a different local suffix.
|
|
@@ -398,7 +397,7 @@ Recommended production path:
|
|
|
398
397
|
- keep `project_mappings.<project_id>.workspace_dir` aligned to that teammate's actual local project folder
|
|
399
398
|
- let `ctxpack pull` or project connection refresh the mapping automatically
|
|
400
399
|
- keep per-role execution policy under `role_profiles`
|
|
401
|
-
- keep
|
|
400
|
+
- keep Telegram-wide binding defaults in `~/.metheus/telegram-bots/_global.env`
|
|
402
401
|
- use `bot_bindings` in `bot-runner.json` only as local fallback/override
|
|
403
402
|
- runner resolution order is: explicit `route.role_profile` -> provider env bot binding -> `bot_bindings` -> server bot role -> `route.role`
|
|
404
403
|
|
|
@@ -471,7 +470,7 @@ Notes:
|
|
|
471
470
|
- `local-bot-bridge` reads stdin JSON from the runner and can call Codex/Claude/Gemini for you
|
|
472
471
|
- `route.command` fallback is disabled by default; enable it only temporarily with `METHEUS_ALLOW_LEGACY_RUNNER_COMMAND=1`
|
|
473
472
|
- today this automation path is implemented for Telegram end-to-end
|
|
474
|
-
- prefer `TELEGRAM_API_BASE_URL=` inside `~/.metheus/telegram.env` for
|
|
473
|
+
- prefer `TELEGRAM_API_BASE_URL=` inside `~/.metheus/telegram-bots/_global.env` for local Telegram API overrides; `METHEUS_TELEGRAM_API_BASE_URL` remains a process-level fallback mainly for mock/regression testing
|
|
475
474
|
- Slack can use direct local send, but automatic inbound runner flow is not completed yet
|
|
476
475
|
- KakaoTalk config can be stored now, but direct send/runner flow is not implemented yet
|
|
477
476
|
- `doctor` now reports provider support for both enabled runner routes and active project chat destinations
|
package/cli.mjs
CHANGED
|
@@ -154,9 +154,11 @@ const AUTH_STORE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-auth.json
|
|
|
154
154
|
const BOT_RUNNER_CONFIG_RELATIVE_PATH = path.join(".metheus", "bot-runner.json");
|
|
155
155
|
const BOT_RUNNER_STATE_RELATIVE_PATH = path.join(".metheus", "bot-runner-state.json");
|
|
156
156
|
const BOT_RUNNER_CONFIG_VERSION = 2;
|
|
157
|
+
const TELEGRAM_LEGACY_ENV_RELATIVE_PATH = path.join(".metheus", "telegram.env");
|
|
158
|
+
const TELEGRAM_GLOBAL_ENV_RELATIVE_PATH = path.join(".metheus", "telegram-bots", "_global.env");
|
|
157
159
|
const PROVIDER_ENV_CONFIG = {
|
|
158
160
|
telegram: {
|
|
159
|
-
relativePath:
|
|
161
|
+
relativePath: TELEGRAM_GLOBAL_ENV_RELATIVE_PATH,
|
|
160
162
|
tokenKey: "TELEGRAM_BOT_TOKEN",
|
|
161
163
|
label: "Telegram",
|
|
162
164
|
verifyURL: "https://api.telegram.org",
|
|
@@ -736,6 +738,10 @@ function providerEnvFilePath(provider) {
|
|
|
736
738
|
return resolveHomeFilePath(providerEnvConfig(provider).relativePath);
|
|
737
739
|
}
|
|
738
740
|
|
|
741
|
+
function telegramLegacyEnvFilePath() {
|
|
742
|
+
return resolveHomeFilePath(TELEGRAM_LEGACY_ENV_RELATIVE_PATH);
|
|
743
|
+
}
|
|
744
|
+
|
|
739
745
|
function telegramBotEntriesDirPath() {
|
|
740
746
|
return resolveHomeFilePath(".metheus/telegram-bots");
|
|
741
747
|
}
|
|
@@ -751,7 +757,8 @@ function providerEnvTemplate(provider) {
|
|
|
751
757
|
return [
|
|
752
758
|
"# Metheus local Telegram bot settings",
|
|
753
759
|
"# Keep this file on your machine only. Do not commit it.",
|
|
754
|
-
"# Store
|
|
760
|
+
"# Store Telegram-wide settings here.",
|
|
761
|
+
"# This global file now lives in ~/.metheus/telegram-bots/_global.env.",
|
|
755
762
|
"# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
|
|
756
763
|
"# Project chat destinations must be managed on the Metheus server.",
|
|
757
764
|
"",
|
|
@@ -1037,6 +1044,23 @@ function isCompatibleLegacyLocalBotBridgeCommand(command, roleProfile) {
|
|
|
1037
1044
|
}
|
|
1038
1045
|
|
|
1039
1046
|
function ensureProviderEnvTemplate(provider) {
|
|
1047
|
+
if (normalizeBotProvider(provider) === "telegram") {
|
|
1048
|
+
const filePath = providerEnvFilePath(provider);
|
|
1049
|
+
const legacyFilePath = telegramLegacyEnvFilePath();
|
|
1050
|
+
const existedBefore = fs.existsSync(filePath) || fs.existsSync(legacyFilePath);
|
|
1051
|
+
try {
|
|
1052
|
+
const state = readTelegramEnvState();
|
|
1053
|
+
writeTelegramEnvState(state.parsed);
|
|
1054
|
+
return {
|
|
1055
|
+
filePath,
|
|
1056
|
+
created: !existedBefore,
|
|
1057
|
+
existed: existedBefore,
|
|
1058
|
+
normalized: true,
|
|
1059
|
+
};
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
return { filePath, created: false, existed: false, error: String(err?.message || err) };
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1040
1064
|
const filePath = providerEnvFilePath(provider);
|
|
1041
1065
|
try {
|
|
1042
1066
|
if (fs.existsSync(filePath)) {
|
|
@@ -2642,7 +2666,6 @@ function normalizeTelegramBotEnvKey(rawValue, fallback = "") {
|
|
|
2642
2666
|
.trim()
|
|
2643
2667
|
.toLowerCase()
|
|
2644
2668
|
.replace(/[^a-z0-9]+/g, "_")
|
|
2645
|
-
.replace(/_bot$/g, "")
|
|
2646
2669
|
.replace(/^_+|_+$/g, "");
|
|
2647
2670
|
return normalized || fallback;
|
|
2648
2671
|
}
|
|
@@ -2703,24 +2726,30 @@ function collectTelegramEnvBotEntries(parsedEnv) {
|
|
|
2703
2726
|
}
|
|
2704
2727
|
entries.set(botKey, entry);
|
|
2705
2728
|
}
|
|
2706
|
-
return Array.from(entries.values())
|
|
2729
|
+
return Array.from(entries.values()).map((entryRaw) => {
|
|
2730
|
+
const entry = { ...safeObject(entryRaw) };
|
|
2731
|
+
const preferredKey = normalizeTelegramBotEnvKey(entry.username || "", "");
|
|
2732
|
+
if (preferredKey) {
|
|
2733
|
+
entry.key = preferredKey;
|
|
2734
|
+
}
|
|
2735
|
+
return entry;
|
|
2736
|
+
});
|
|
2707
2737
|
}
|
|
2708
2738
|
|
|
2709
2739
|
function telegramBotEntryDisplayNameForState(entry) {
|
|
2710
2740
|
const current = safeObject(entry);
|
|
2711
2741
|
const username = normalizeTelegramBotUsername(current.username || "");
|
|
2712
2742
|
if (username) return username;
|
|
2713
|
-
|
|
2714
|
-
if (!key) return "telegram_bot";
|
|
2715
|
-
return key.endsWith("_bot") ? key : `${key}_bot`;
|
|
2743
|
+
return normalizeTelegramBotEnvKey(current.key || "", "telegram_bot");
|
|
2716
2744
|
}
|
|
2717
2745
|
|
|
2718
2746
|
function parseTelegramBotEntryFile(filePath) {
|
|
2719
2747
|
const raw = fs.readFileSync(filePath, "utf8");
|
|
2720
2748
|
const parsed = parseSimpleEnvText(raw);
|
|
2721
2749
|
const keyFromFile = normalizeTelegramBotEnvKey(path.basename(filePath, path.extname(filePath)), "telegram_bot");
|
|
2750
|
+
const keyFromName = normalizeTelegramBotEnvKey(parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_USERNAME || "", "");
|
|
2722
2751
|
return {
|
|
2723
|
-
key: keyFromFile,
|
|
2752
|
+
key: keyFromName || keyFromFile,
|
|
2724
2753
|
username: normalizeTelegramBotUsername(parsed.TELEGRAM_BOT_NAME || parsed.TELEGRAM_BOT_USERNAME || ""),
|
|
2725
2754
|
serverBotID: String(parsed.TELEGRAM_BOT_SERVER_BOT_ID || "").trim(),
|
|
2726
2755
|
token: String(parsed.TELEGRAM_BOT_TOKEN || "").trim(),
|
|
@@ -2733,11 +2762,33 @@ function parseTelegramBotEntryFile(filePath) {
|
|
|
2733
2762
|
};
|
|
2734
2763
|
}
|
|
2735
2764
|
|
|
2765
|
+
function remapTelegramDefaultBotKey(defaultKeyRaw, entries) {
|
|
2766
|
+
const defaultKey = normalizeTelegramBotEnvKey(defaultKeyRaw || "", "");
|
|
2767
|
+
if (!defaultKey) return "";
|
|
2768
|
+
const rows = ensureArray(entries).map((entry) => safeObject(entry));
|
|
2769
|
+
if (rows.some((entry) => String(entry.key || "").trim() === defaultKey)) {
|
|
2770
|
+
return defaultKey;
|
|
2771
|
+
}
|
|
2772
|
+
const directBotMatch = rows.find((entry) => String(entry.key || "").trim() === `${defaultKey}_bot`);
|
|
2773
|
+
if (directBotMatch) {
|
|
2774
|
+
return String(directBotMatch.key || "").trim();
|
|
2775
|
+
}
|
|
2776
|
+
const compactBotMatch = rows.find((entry) => String(entry.key || "").trim() === `${defaultKey}bot`);
|
|
2777
|
+
if (compactBotMatch) {
|
|
2778
|
+
return String(compactBotMatch.key || "").trim();
|
|
2779
|
+
}
|
|
2780
|
+
const byUsername = rows.find((entry) => normalizeTelegramBotUsername(entry.username || "") === defaultKey);
|
|
2781
|
+
if (byUsername) {
|
|
2782
|
+
return String(byUsername.key || "").trim();
|
|
2783
|
+
}
|
|
2784
|
+
return defaultKey;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2736
2787
|
function loadTelegramBotEntriesFromFiles() {
|
|
2737
2788
|
const dirPath = telegramBotEntriesDirPath();
|
|
2738
2789
|
if (!fs.existsSync(dirPath)) return [];
|
|
2739
2790
|
return fs.readdirSync(dirPath, { withFileTypes: true })
|
|
2740
|
-
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
2791
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name) && item.name.toLowerCase() !== "_global.env")
|
|
2741
2792
|
.map((item) => path.join(dirPath, item.name))
|
|
2742
2793
|
.sort((left, right) => left.localeCompare(right))
|
|
2743
2794
|
.map((filePath) => {
|
|
@@ -2775,14 +2826,20 @@ function buildMergedTelegramEnvParsed(globalParsed, entries) {
|
|
|
2775
2826
|
|
|
2776
2827
|
function readTelegramEnvState() {
|
|
2777
2828
|
const filePath = providerEnvFilePath("telegram");
|
|
2829
|
+
const legacyFilePath = telegramLegacyEnvFilePath();
|
|
2830
|
+
let sourceFilePath = filePath;
|
|
2778
2831
|
try {
|
|
2779
|
-
if (!fs.existsSync(filePath)) {
|
|
2832
|
+
if (!fs.existsSync(filePath) && fs.existsSync(legacyFilePath)) {
|
|
2833
|
+
sourceFilePath = legacyFilePath;
|
|
2834
|
+
} else if (!fs.existsSync(filePath)) {
|
|
2780
2835
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2781
2836
|
fs.writeFileSync(filePath, providerEnvTemplate("telegram"), "utf8");
|
|
2782
2837
|
}
|
|
2783
2838
|
} catch (err) {
|
|
2784
2839
|
return {
|
|
2785
2840
|
filePath,
|
|
2841
|
+
legacyFilePath,
|
|
2842
|
+
sourceFilePath,
|
|
2786
2843
|
entriesDirPath: telegramBotEntriesDirPath(),
|
|
2787
2844
|
parsed: {},
|
|
2788
2845
|
globalParsed: {},
|
|
@@ -2790,7 +2847,7 @@ function readTelegramEnvState() {
|
|
|
2790
2847
|
error: String(err?.message || err),
|
|
2791
2848
|
};
|
|
2792
2849
|
}
|
|
2793
|
-
const raw = fs.existsSync(
|
|
2850
|
+
const raw = fs.existsSync(sourceFilePath) ? fs.readFileSync(sourceFilePath, "utf8") : "";
|
|
2794
2851
|
const globalParsedRaw = parseSimpleEnvText(raw);
|
|
2795
2852
|
const inlineEntries = collectTelegramEnvBotEntries(globalParsedRaw);
|
|
2796
2853
|
const fileEntries = loadTelegramBotEntriesFromFiles();
|
|
@@ -2804,8 +2861,11 @@ function readTelegramEnvState() {
|
|
|
2804
2861
|
}
|
|
2805
2862
|
});
|
|
2806
2863
|
const entries = Array.from(mergedEntries.values());
|
|
2864
|
+
globalParsed.TELEGRAM_DEFAULT_BOT_KEY = remapTelegramDefaultBotKey(globalParsed.TELEGRAM_DEFAULT_BOT_KEY || "", entries);
|
|
2807
2865
|
return {
|
|
2808
2866
|
filePath,
|
|
2867
|
+
legacyFilePath,
|
|
2868
|
+
sourceFilePath,
|
|
2809
2869
|
entriesDirPath: telegramBotEntriesDirPath(),
|
|
2810
2870
|
parsed: buildMergedTelegramEnvParsed(globalParsed, entries),
|
|
2811
2871
|
globalParsed,
|
|
@@ -2822,18 +2882,20 @@ function telegramEntryCommentNameForEnv(entry) {
|
|
|
2822
2882
|
const current = safeObject(entry);
|
|
2823
2883
|
const username = normalizeTelegramBotUsername(current.username || "");
|
|
2824
2884
|
if (username) return username;
|
|
2825
|
-
|
|
2826
|
-
if (!key) return "telegram_bot";
|
|
2827
|
-
return key.endsWith("_bot") ? key : `${key}_bot`;
|
|
2885
|
+
return normalizeTelegramBotEnvKey(current.key || "", "telegram_bot");
|
|
2828
2886
|
}
|
|
2829
2887
|
|
|
2830
2888
|
function renderNormalizedTelegramEnv(parsedEnv) {
|
|
2831
2889
|
const parsed = safeObject(parsedEnv);
|
|
2832
|
-
const defaultBotKey =
|
|
2890
|
+
const defaultBotKey = remapTelegramDefaultBotKey(
|
|
2891
|
+
parsed.TELEGRAM_DEFAULT_BOT_KEY || "",
|
|
2892
|
+
collectTelegramEnvBotEntries(parsed),
|
|
2893
|
+
);
|
|
2833
2894
|
const lines = [
|
|
2834
2895
|
"# Metheus local Telegram bot settings",
|
|
2835
2896
|
"# Keep this file on your machine only. Do not commit it.",
|
|
2836
|
-
"# Store
|
|
2897
|
+
"# Store Telegram-wide settings here.",
|
|
2898
|
+
"# This global file lives in ~/.metheus/telegram-bots/_global.env.",
|
|
2837
2899
|
"# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
|
|
2838
2900
|
"",
|
|
2839
2901
|
`TELEGRAM_API_BASE_URL=${formatProviderEnvValue(parsed.TELEGRAM_API_BASE_URL || "")}`,
|
|
@@ -2906,6 +2968,7 @@ function writeTelegramEnvState(parsedEnv) {
|
|
|
2906
2968
|
}
|
|
2907
2969
|
});
|
|
2908
2970
|
const globalFilePath = providerEnvFilePath("telegram");
|
|
2971
|
+
const legacyFilePath = telegramLegacyEnvFilePath();
|
|
2909
2972
|
fs.mkdirSync(path.dirname(globalFilePath), { recursive: true });
|
|
2910
2973
|
fs.writeFileSync(globalFilePath, renderNormalizedTelegramEnv(globalParsed), {
|
|
2911
2974
|
encoding: "utf8",
|
|
@@ -2923,13 +2986,16 @@ function writeTelegramEnvState(parsedEnv) {
|
|
|
2923
2986
|
});
|
|
2924
2987
|
});
|
|
2925
2988
|
fs.readdirSync(entriesDirPath, { withFileTypes: true })
|
|
2926
|
-
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
2989
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name) && item.name.toLowerCase() !== "_global.env")
|
|
2927
2990
|
.forEach((item) => {
|
|
2928
2991
|
const filePath = path.resolve(path.join(entriesDirPath, item.name));
|
|
2929
2992
|
if (!activeFiles.has(filePath)) {
|
|
2930
2993
|
fs.rmSync(filePath, { force: true });
|
|
2931
2994
|
}
|
|
2932
2995
|
});
|
|
2996
|
+
if (legacyFilePath !== globalFilePath && fs.existsSync(legacyFilePath)) {
|
|
2997
|
+
fs.rmSync(legacyFilePath, { force: true });
|
|
2998
|
+
}
|
|
2933
2999
|
return globalFilePath;
|
|
2934
3000
|
}
|
|
2935
3001
|
|
|
@@ -2964,7 +3030,7 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
|
2964
3030
|
selectors.route?.telegramBotKey,
|
|
2965
3031
|
selectors.route?.telegram_bot_key,
|
|
2966
3032
|
]), "");
|
|
2967
|
-
const defaultBotKey =
|
|
3033
|
+
const defaultBotKey = remapTelegramDefaultBotKey(parsed.TELEGRAM_DEFAULT_BOT_KEY || "", entries);
|
|
2968
3034
|
let selected = null;
|
|
2969
3035
|
if (desiredBotID) {
|
|
2970
3036
|
selected = entries.find((entry) => entry.serverBotID === desiredBotID) || null;
|
|
@@ -5347,7 +5413,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5347
5413
|
"telegram_env_v2_default_bot_key_selects_named_token",
|
|
5348
5414
|
telegramEnvV2Default.ok
|
|
5349
5415
|
&& telegramEnvV2Default.token === "review-token"
|
|
5350
|
-
&& telegramEnvV2Default.botKey === "
|
|
5416
|
+
&& telegramEnvV2Default.botKey === "reviewbot"
|
|
5351
5417
|
&& telegramEnvV2Default.autoClearWebhook === false,
|
|
5352
5418
|
`bot=${String(telegramEnvV2Default.botKey || "(none)")} token=${String(telegramEnvV2Default.token || "(missing)")}`,
|
|
5353
5419
|
);
|
|
@@ -5361,7 +5427,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5361
5427
|
"telegram_env_v2_bot_name_override_selects_matching_token",
|
|
5362
5428
|
telegramEnvV2ByUsername.ok
|
|
5363
5429
|
&& telegramEnvV2ByUsername.token === "ryoai-token"
|
|
5364
|
-
&& telegramEnvV2ByUsername.botKey === "
|
|
5430
|
+
&& telegramEnvV2ByUsername.botKey === "ryoai_bot"
|
|
5365
5431
|
&& Array.isArray(telegramEnvV2ByUsername.allowedUpdates)
|
|
5366
5432
|
&& telegramEnvV2ByUsername.allowedUpdates.includes("channel_post"),
|
|
5367
5433
|
`bot=${String(telegramEnvV2ByUsername.botKey || "(none)")} updates=${String((telegramEnvV2ByUsername.allowedUpdates || []).join(","))}`,
|
|
@@ -5374,7 +5440,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5374
5440
|
try {
|
|
5375
5441
|
process.env.USERPROFILE = telegramEnvNormalizeTempHome;
|
|
5376
5442
|
process.env.HOME = telegramEnvNormalizeTempHome;
|
|
5377
|
-
const normalizeTestEnvPath =
|
|
5443
|
+
const normalizeTestEnvPath = telegramLegacyEnvFilePath();
|
|
5378
5444
|
fs.mkdirSync(path.dirname(normalizeTestEnvPath), { recursive: true });
|
|
5379
5445
|
fs.writeFileSync(
|
|
5380
5446
|
normalizeTestEnvPath,
|
|
@@ -5386,9 +5452,10 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5386
5452
|
].join("\n"),
|
|
5387
5453
|
"utf8",
|
|
5388
5454
|
);
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5455
|
+
const normalizedState = readTelegramEnvState();
|
|
5456
|
+
writeTelegramEnvState(normalizedState.parsed);
|
|
5457
|
+
normalizedGlobalText = fs.readFileSync(providerEnvFilePath("telegram"), "utf8");
|
|
5458
|
+
normalizedBotText = fs.readFileSync(telegramBotEntryFilePath("ryoai_bot"), "utf8");
|
|
5392
5459
|
} finally {
|
|
5393
5460
|
if (previousUserProfile === undefined) delete process.env.USERPROFILE;
|
|
5394
5461
|
else process.env.USERPROFILE = previousUserProfile;
|
|
@@ -5397,7 +5464,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5397
5464
|
}
|
|
5398
5465
|
push(
|
|
5399
5466
|
"telegram_env_existing_file_auto_normalizes_named_keys",
|
|
5400
|
-
normalizedGlobalText.includes("TELEGRAM_DEFAULT_BOT_KEY=
|
|
5467
|
+
normalizedGlobalText.includes("TELEGRAM_DEFAULT_BOT_KEY=ryoai_bot")
|
|
5401
5468
|
&& !normalizedGlobalText.includes("TELEGRAM_BOT_RYOAI_BOT_TOKEN")
|
|
5402
5469
|
&& !normalizedGlobalText.includes("TELEGRAM_BOT_RYOAI_TOKEN")
|
|
5403
5470
|
&& normalizedBotText.includes("TELEGRAM_BOT_NAME=ryoai_bot")
|
|
@@ -288,8 +288,8 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
288
288
|
const env = buildSpawnEnv(tempHome);
|
|
289
289
|
const baseURL = `http://127.0.0.1:${mock.port}`;
|
|
290
290
|
const telegramApiBaseURL = `${baseURL}/telegram`;
|
|
291
|
-
const telegramEnvPath = path.join(tempHome, ".metheus", "telegram.env");
|
|
292
291
|
const telegramBotEntriesDir = path.join(tempHome, ".metheus", "telegram-bots");
|
|
292
|
+
const telegramEnvPath = path.join(telegramBotEntriesDir, "_global.env");
|
|
293
293
|
const readTelegramGlobals = () => parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
294
294
|
const readTelegramBotEntry = (botKey) => {
|
|
295
295
|
const filePath = path.join(telegramBotEntriesDir, `${String(botKey || "").trim()}.env`);
|
|
@@ -398,7 +398,7 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
398
398
|
]),
|
|
399
399
|
},
|
|
400
400
|
});
|
|
401
|
-
const groupedState = readTelegramBotEntry("
|
|
401
|
+
const groupedState = readTelegramBotEntry("ryoai_bot");
|
|
402
402
|
push(
|
|
403
403
|
"bot_add_guided_autoresolves_server_bot_group_without_role_prompt",
|
|
404
404
|
String(groupedState.TELEGRAM_BOT_NAME || "").toLowerCase() === "ryoai_bot"
|
|
@@ -420,7 +420,7 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
420
420
|
...env,
|
|
421
421
|
METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
|
|
422
422
|
"1", // provider: telegram
|
|
423
|
-
"2", // bot entry:
|
|
423
|
+
"2", // bot entry: ryoai_bot
|
|
424
424
|
"1", // keep token
|
|
425
425
|
"2", // grouped role settings: edit one role
|
|
426
426
|
"3", // select role to edit: worker
|
|
@@ -465,7 +465,7 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
465
465
|
args: [
|
|
466
466
|
"bot", "show",
|
|
467
467
|
"--provider", "telegram",
|
|
468
|
-
"--bot-key", "
|
|
468
|
+
"--bot-key", "ryoai_bot",
|
|
469
469
|
"--base-url", `http://127.0.0.1:${groupedMock.port}`,
|
|
470
470
|
"--json", "true",
|
|
471
471
|
],
|