metheus-governance-mcp-cli 0.2.102 → 0.2.103
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 +19 -13
- package/cli.mjs +187 -18
- package/lib/bot-commands.mjs +212 -51
- package/lib/selftest-bot-commands.mjs +15 -4
- package/lib/setup-registration.mjs +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,9 +31,9 @@ Install creates local provider settings templates here:
|
|
|
31
31
|
These files are for local provider bot secrets, local transport options, and optional per-bot local AI binding.
|
|
32
32
|
|
|
33
33
|
- Store locally:
|
|
34
|
-
- Telegram-wide settings in `~/.metheus/telegram-bots/
|
|
35
|
-
- `TELEGRAM_BOT_TOKEN` as a legacy Telegram fallback in `
|
|
36
|
-
- one Telegram bot file per entry in `~/.metheus/telegram-bots/<
|
|
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/<ServerBotName>.env`
|
|
37
37
|
- `SLACK_BOT_TOKEN`
|
|
38
38
|
- `KAKAOTALK_BOT_TOKEN`
|
|
39
39
|
- Server-side Metheus stores project chat destination metadata separately:
|
|
@@ -41,12 +41,13 @@ 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 now live in `telegram-bots/
|
|
44
|
+
- Telegram-wide settings now live in `telegram-bots/global.env`; Telegram per-bot secrets and server metadata live in `telegram-bots/*.env`.
|
|
45
|
+
- Grouped server bots also persist `TELEGRAM_BOT_SERVER_ROLE_IDS`, so the local file keeps the exact role-to-server-bot-id mapping instead of pretending that one grouped bot has only one server UUID.
|
|
45
46
|
|
|
46
47
|
Example templates:
|
|
47
48
|
|
|
48
49
|
```env
|
|
49
|
-
# ~/.metheus/telegram-bots/
|
|
50
|
+
# ~/.metheus/telegram-bots/global.env
|
|
50
51
|
TELEGRAM_API_BASE_URL=
|
|
51
52
|
TELEGRAM_AUTO_CLEAR_WEBHOOK=true
|
|
52
53
|
TELEGRAM_ALLOWED_UPDATES=message,edited_message
|
|
@@ -57,10 +58,11 @@ TELEGRAM_BOT_TOKEN=
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
```env
|
|
60
|
-
# ~/.metheus/telegram-bots/
|
|
61
|
+
# ~/.metheus/telegram-bots/RyoAI_bot.env
|
|
61
62
|
TELEGRAM_BOT_SERVER_BOT_ID=<server_bot_uuid>
|
|
62
63
|
TELEGRAM_BOT_SERVER_NAME=RyoAI_bot
|
|
63
64
|
TELEGRAM_BOT_SERVER_ROLES=monitor,review,worker,approval
|
|
65
|
+
TELEGRAM_BOT_SERVER_ROLE_IDS=monitor:<uuid>,review:<uuid>,worker:<uuid>,approval:<uuid>
|
|
64
66
|
# Optional fallback only when server bot binding is unavailable
|
|
65
67
|
# TELEGRAM_BOT_USERNAME=ryoai_bot
|
|
66
68
|
TELEGRAM_BOT_TOKEN=
|
|
@@ -172,10 +174,12 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
|
|
|
172
174
|
- `~/.metheus/kakaotalk.env`
|
|
173
175
|
- `~/.metheus/bot-runner.json`
|
|
174
176
|
|
|
177
|
+
Bootstrap or setup does not create one file per server bot automatically. Per-bot Telegram files are created later when you run `bot add`, `bot verify`, or `bot edit` against a real server bot identity.
|
|
178
|
+
|
|
175
179
|
When Telegram local config already exists, bootstrap/setup keeps your secrets but auto-normalizes the layout to the latest split structure:
|
|
176
180
|
|
|
177
|
-
- Telegram-wide settings stay in `~/.metheus/telegram-bots/
|
|
178
|
-
- per-bot secrets move to `~/.metheus/telegram-bots/<
|
|
181
|
+
- Telegram-wide settings stay in `~/.metheus/telegram-bots/global.env`
|
|
182
|
+
- per-bot secrets move to `~/.metheus/telegram-bots/<ServerBotName>.env`
|
|
179
183
|
- stale inline keys such as `TELEGRAM_BOT_<NAME>_BOT_*` are rewritten into generic per-bot keys
|
|
180
184
|
|
|
181
185
|
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.
|
|
@@ -185,7 +189,7 @@ Fill provider bot secrets and provider-local transport options locally. Project
|
|
|
185
189
|
- which provider/role bot profile to use
|
|
186
190
|
- which `project_id -> workspace_dir` mapping to apply locally
|
|
187
191
|
- which role profile maps to which local CLI/model/permission/reasoning policy
|
|
188
|
-
- which server bot maps to which local LLM execution profile via `telegram-bots/
|
|
192
|
+
- which server bot maps to which local LLM execution profile via `telegram-bots/global.env`, `telegram-bots/*.env`, or fallback `bot_bindings`
|
|
189
193
|
|
|
190
194
|
Built-in helper command for legacy fallback/testing:
|
|
191
195
|
|
|
@@ -261,10 +265,11 @@ Behavior:
|
|
|
261
265
|
- `bot set-default` without flags starts a guided numbered flow: provider -> bot entry -> confirm default change.
|
|
262
266
|
- `bot verify` without flags starts a guided numbered flow: provider -> bot entry -> output format.
|
|
263
267
|
- `bot remove` without flags starts a guided numbered flow: provider -> bot entry -> confirm removal.
|
|
264
|
-
- Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<
|
|
268
|
+
- Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<ServerBotName>.env` with generic fields:
|
|
265
269
|
- `TELEGRAM_BOT_SERVER_BOT_ID`
|
|
266
270
|
- `TELEGRAM_BOT_SERVER_NAME`
|
|
267
271
|
- `TELEGRAM_BOT_SERVER_ROLES`
|
|
272
|
+
- `TELEGRAM_BOT_SERVER_ROLE_IDS`
|
|
268
273
|
- `TELEGRAM_BOT_USERNAME` only as a fallback when server bot binding is unavailable
|
|
269
274
|
- `TELEGRAM_BOT_TOKEN`
|
|
270
275
|
- `TELEGRAM_BOT_ROLE_PROFILE`
|
|
@@ -293,7 +298,7 @@ metheus-governance-mcp-cli bot remove --provider telegram --bot-name RyoAI_bot -
|
|
|
293
298
|
metheus-governance-mcp-cli bot verify --provider telegram --bot-name RyoAI_bot --json true
|
|
294
299
|
```
|
|
295
300
|
|
|
296
|
-
For direct Telegram adds, the CLI derives the local file name from the matched server bot name. You normally do not need `--bot-key` or `--username`. Treat `--bot-key` as an advanced compatibility selector only when you intentionally need
|
|
301
|
+
For direct Telegram adds, the CLI derives the local file name from the matched server bot name. You normally do not need `--bot-key` or `--username`. Treat `--bot-key` as an advanced compatibility selector only when you intentionally need to target an older local selector directly.
|
|
297
302
|
|
|
298
303
|
For direct Telegram edits, prefer `--bot-name` or `--bot-id` because server bot identity is the source of truth. Use `--bot-key` only when you intentionally need the advanced local selector. If one server bot name expands to multiple roles such as `approval / worker / review / monitor`, prefer the guided `bot edit` flow so you can keep the current grouped settings, edit one role only, or walk every role in sequence instead of forcing one entry-level AI override.
|
|
299
304
|
|
|
@@ -377,6 +382,7 @@ Execution model:
|
|
|
377
382
|
Commands:
|
|
378
383
|
|
|
379
384
|
```bash
|
|
385
|
+
metheus-governance-mcp-cli runner list
|
|
380
386
|
metheus-governance-mcp-cli runner once --route-name telegram-monitor
|
|
381
387
|
metheus-governance-mcp-cli runner start --route-name telegram-monitor
|
|
382
388
|
```
|
|
@@ -403,7 +409,7 @@ Recommended production path:
|
|
|
403
409
|
- keep `project_mappings.<project_id>.workspace_dir` aligned to that teammate's actual local project folder
|
|
404
410
|
- let `ctxpack pull` or project connection refresh the mapping automatically
|
|
405
411
|
- keep per-role execution policy under `role_profiles`
|
|
406
|
-
- keep Telegram-wide binding defaults in `~/.metheus/telegram-bots/
|
|
412
|
+
- keep Telegram-wide binding defaults in `~/.metheus/telegram-bots/global.env`
|
|
407
413
|
- use `bot_bindings` in `bot-runner.json` only as local fallback/override
|
|
408
414
|
- runner resolution order is: explicit `route.role_profile` -> provider env bot binding -> `bot_bindings` -> server bot role -> `route.role`
|
|
409
415
|
|
|
@@ -476,7 +482,7 @@ Notes:
|
|
|
476
482
|
- `local-bot-bridge` reads stdin JSON from the runner and can call Codex/Claude/Gemini for you
|
|
477
483
|
- `route.command` fallback is disabled by default; enable it only temporarily with `METHEUS_ALLOW_LEGACY_RUNNER_COMMAND=1`
|
|
478
484
|
- today this automation path is implemented for Telegram end-to-end
|
|
479
|
-
- prefer `TELEGRAM_API_BASE_URL=` inside `~/.metheus/telegram-bots/
|
|
485
|
+
- 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
|
|
480
486
|
- Slack can use direct local send, but automatic inbound runner flow is not completed yet
|
|
481
487
|
- KakaoTalk config can be stored now, but direct send/runner flow is not implemented yet
|
|
482
488
|
- `doctor` now reports provider support for both enabled runner routes and active project chat destinations
|
package/cli.mjs
CHANGED
|
@@ -154,8 +154,9 @@ 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
|
|
158
|
-
const
|
|
157
|
+
const TELEGRAM_ROOT_LEGACY_ENV_RELATIVE_PATH = path.join(".metheus", "telegram.env");
|
|
158
|
+
const TELEGRAM_LEGACY_GLOBAL_ENV_RELATIVE_PATH = path.join(".metheus", "telegram-bots", "_global.env");
|
|
159
|
+
const TELEGRAM_GLOBAL_ENV_RELATIVE_PATH = path.join(".metheus", "telegram-bots", "global.env");
|
|
159
160
|
const PROVIDER_ENV_CONFIG = {
|
|
160
161
|
telegram: {
|
|
161
162
|
relativePath: TELEGRAM_GLOBAL_ENV_RELATIVE_PATH,
|
|
@@ -253,6 +254,7 @@ function printUsage() {
|
|
|
253
254
|
` ${cmd} proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--include-drafts <true|false>] [--auto-pull-on-conflict <true|false>] [--timeout-seconds <n>]`,
|
|
254
255
|
` ${cmd} selftest [--json <true|false>]`,
|
|
255
256
|
` ${cmd} local-bot-bridge [--client <codex|claude|gemini|sample>] [--cwd <path>] [--model <name>] [--permission-mode <read_only|workspace_write|danger_full_access>] [--reasoning-effort <low|medium|high>]`,
|
|
257
|
+
` ${cmd} runner list [--json <true|false>]`,
|
|
256
258
|
` ${cmd} runner once [--route-name <name>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--bot-name <name>] [--bot-id <uuid>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--context-comments <n>] [--archive-replies <true|false>]`,
|
|
257
259
|
` ${cmd} runner start [--route-name <name>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--bot-name <name>] [--bot-id <uuid>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--poll-interval-ms <n>] [--context-comments <n>] [--archive-replies <true|false>]`,
|
|
258
260
|
` ${cmd} ctxpack pull [--project-id <uuid>] [--base-url <url>] [--workspace-dir <path|auto>] [--paths <csv>] [--timeout-seconds <n>]`,
|
|
@@ -739,18 +741,57 @@ function providerEnvFilePath(provider) {
|
|
|
739
741
|
}
|
|
740
742
|
|
|
741
743
|
function telegramLegacyEnvFilePath() {
|
|
742
|
-
return resolveHomeFilePath(
|
|
744
|
+
return resolveHomeFilePath(TELEGRAM_ROOT_LEGACY_ENV_RELATIVE_PATH);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function telegramLegacyGlobalEnvFilePath() {
|
|
748
|
+
return resolveHomeFilePath(TELEGRAM_LEGACY_GLOBAL_ENV_RELATIVE_PATH);
|
|
743
749
|
}
|
|
744
750
|
|
|
745
751
|
function telegramBotEntriesDirPath() {
|
|
746
752
|
return resolveHomeFilePath(".metheus/telegram-bots");
|
|
747
753
|
}
|
|
748
754
|
|
|
755
|
+
function normalizeComparablePath(rawPath) {
|
|
756
|
+
const resolved = path.resolve(String(rawPath || ""));
|
|
757
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function sanitizeTelegramBotFileStem(rawValue, fallback = "telegram_bot") {
|
|
761
|
+
const text = String(rawValue || "")
|
|
762
|
+
.trim()
|
|
763
|
+
.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_")
|
|
764
|
+
.replace(/\s+/g, "_")
|
|
765
|
+
.replace(/^_+|_+$/g, "");
|
|
766
|
+
return text || fallback;
|
|
767
|
+
}
|
|
768
|
+
|
|
749
769
|
function telegramBotEntryFilePath(botKey) {
|
|
750
770
|
const normalizedKey = normalizeTelegramBotEnvKey(botKey || "", "telegram_bot") || "telegram_bot";
|
|
751
771
|
return path.join(telegramBotEntriesDirPath(), `${normalizedKey}.env`);
|
|
752
772
|
}
|
|
753
773
|
|
|
774
|
+
function telegramBotEntryFilePathForEntry(entryRaw) {
|
|
775
|
+
const entry = safeObject(entryRaw);
|
|
776
|
+
const fallbackKey = normalizeTelegramBotEnvKey(entry.key || "", "telegram_bot") || "telegram_bot";
|
|
777
|
+
const preferredStem = sanitizeTelegramBotFileStem(
|
|
778
|
+
firstNonEmptyString([entry.serverBotName, entry.username, entry.key]),
|
|
779
|
+
fallbackKey,
|
|
780
|
+
);
|
|
781
|
+
return path.join(telegramBotEntriesDirPath(), `${preferredStem}.env`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function findTelegramEntryPathVariant(targetPath) {
|
|
785
|
+
const dirPath = path.dirname(targetPath);
|
|
786
|
+
if (!fs.existsSync(dirPath)) return "";
|
|
787
|
+
const targetComparable = normalizeComparablePath(targetPath);
|
|
788
|
+
const match = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
789
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
790
|
+
.map((item) => path.join(dirPath, item.name))
|
|
791
|
+
.find((candidatePath) => normalizeComparablePath(candidatePath) === targetComparable);
|
|
792
|
+
return String(match || "").trim();
|
|
793
|
+
}
|
|
794
|
+
|
|
754
795
|
function providerEnvTemplate(provider) {
|
|
755
796
|
const config = providerEnvConfig(provider);
|
|
756
797
|
if (normalizeBotProvider(provider) === "telegram") {
|
|
@@ -758,7 +799,7 @@ function providerEnvTemplate(provider) {
|
|
|
758
799
|
"# Metheus local Telegram bot settings",
|
|
759
800
|
"# Keep this file on your machine only. Do not commit it.",
|
|
760
801
|
"# Store Telegram-wide settings here.",
|
|
761
|
-
"# This global file now lives in ~/.metheus/telegram-bots/
|
|
802
|
+
"# This global file now lives in ~/.metheus/telegram-bots/global.env.",
|
|
762
803
|
"# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<server-bot-name>.env.",
|
|
763
804
|
"# Project chat destinations must be managed on the Metheus server.",
|
|
764
805
|
"",
|
|
@@ -2525,6 +2566,57 @@ async function runRunnerOnce(flags) {
|
|
|
2525
2566
|
}
|
|
2526
2567
|
}
|
|
2527
2568
|
|
|
2569
|
+
function buildRunnerRouteListRows() {
|
|
2570
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
2571
|
+
return ensureArray(config.routes).map((rawRoute, index) => {
|
|
2572
|
+
const route = normalizeRunnerRoute(rawRoute);
|
|
2573
|
+
return {
|
|
2574
|
+
index: index + 1,
|
|
2575
|
+
name: route.name || runnerRouteKey(route),
|
|
2576
|
+
enabled: route.enabled !== false,
|
|
2577
|
+
provider: route.provider || "-",
|
|
2578
|
+
projectID: route.projectID || "-",
|
|
2579
|
+
botName: route.botName || "-",
|
|
2580
|
+
botID: route.botID || "-",
|
|
2581
|
+
role: route.role || "-",
|
|
2582
|
+
roleProfile: route.roleProfile || "-",
|
|
2583
|
+
destinationLabel: route.destinationLabel || "-",
|
|
2584
|
+
pollIntervalMs: route.pollIntervalMs || 0,
|
|
2585
|
+
configFilePath: config.filePath,
|
|
2586
|
+
};
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
async function runRunnerList(flags) {
|
|
2591
|
+
const rows = buildRunnerRouteListRows();
|
|
2592
|
+
if (boolFromRaw(flags.json, false)) {
|
|
2593
|
+
process.stdout.write(`${JSON.stringify({ ok: true, routes: rows }, null, 2)}\n`);
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
process.stdout.write("Runner routes\n");
|
|
2597
|
+
if (!rows.length) {
|
|
2598
|
+
process.stdout.write(" none configured\n");
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
process.stdout.write(` file: ${rows[0].configFilePath}\n`);
|
|
2602
|
+
rows.forEach((row) => {
|
|
2603
|
+
process.stdout.write(
|
|
2604
|
+
[
|
|
2605
|
+
` - ${row.name}${row.enabled ? "" : " [disabled]"}`,
|
|
2606
|
+
` provider: ${row.provider}`,
|
|
2607
|
+
` project_id: ${row.projectID}`,
|
|
2608
|
+
` bot_name: ${row.botName}`,
|
|
2609
|
+
` bot_id: ${row.botID}`,
|
|
2610
|
+
` role: ${row.role}`,
|
|
2611
|
+
` role_profile: ${row.roleProfile}`,
|
|
2612
|
+
` destination_label: ${row.destinationLabel}`,
|
|
2613
|
+
` poll_interval_ms: ${row.pollIntervalMs}`,
|
|
2614
|
+
` run_once: ${CLI_NAME} runner once --route-name ${row.name}`,
|
|
2615
|
+
].join("\n") + "\n",
|
|
2616
|
+
);
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2528
2620
|
async function runRunnerStart(flags) {
|
|
2529
2621
|
const jsonMode = boolFromRaw(flags.json, false);
|
|
2530
2622
|
const routes = resolveRunnerRoutes(flags, "start");
|
|
@@ -2585,6 +2677,10 @@ async function runRunner(argv) {
|
|
|
2585
2677
|
const [subcommandRaw = "", ...rest] = argv;
|
|
2586
2678
|
const subcommand = String(subcommandRaw || "").trim().toLowerCase();
|
|
2587
2679
|
const flags = parseArgs(rest);
|
|
2680
|
+
if (subcommand === "list") {
|
|
2681
|
+
await runRunnerList(flags);
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2588
2684
|
if (subcommand === "once") {
|
|
2589
2685
|
await runRunnerOnce(flags);
|
|
2590
2686
|
return;
|
|
@@ -2593,7 +2689,7 @@ async function runRunner(argv) {
|
|
|
2593
2689
|
await runRunnerStart(flags);
|
|
2594
2690
|
return;
|
|
2595
2691
|
}
|
|
2596
|
-
throw new Error("runner requires a subcommand: once | start");
|
|
2692
|
+
throw new Error("runner requires a subcommand: list | once | start");
|
|
2597
2693
|
}
|
|
2598
2694
|
|
|
2599
2695
|
async function runLocalBotBridge(argv) {
|
|
@@ -2707,12 +2803,39 @@ function parseTelegramServerRoles(rawValue) {
|
|
|
2707
2803
|
return Array.from(new Set(preferredTelegramServerRoleSort(values)));
|
|
2708
2804
|
}
|
|
2709
2805
|
|
|
2806
|
+
function parseTelegramServerRoleIDs(rawValue) {
|
|
2807
|
+
const output = {};
|
|
2808
|
+
String(rawValue || "")
|
|
2809
|
+
.split(",")
|
|
2810
|
+
.map((value) => String(value || "").trim())
|
|
2811
|
+
.filter(Boolean)
|
|
2812
|
+
.forEach((pair) => {
|
|
2813
|
+
const separatorIndex = pair.indexOf(":");
|
|
2814
|
+
if (separatorIndex <= 0) return;
|
|
2815
|
+
const role = String(pair.slice(0, separatorIndex) || "").trim();
|
|
2816
|
+
const id = String(pair.slice(separatorIndex + 1) || "").trim();
|
|
2817
|
+
if (!role || !id) return;
|
|
2818
|
+
output[role] = id;
|
|
2819
|
+
});
|
|
2820
|
+
return preferredTelegramServerRoleSort(Object.keys(output)).reduce((acc, role) => {
|
|
2821
|
+
acc[role] = output[role];
|
|
2822
|
+
return acc;
|
|
2823
|
+
}, {});
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
function formatTelegramServerRoleIDs(roleIDs) {
|
|
2827
|
+
return preferredTelegramServerRoleSort(Object.keys(safeObject(roleIDs)))
|
|
2828
|
+
.map((role) => `${role}:${String(safeObject(roleIDs)[role] || "").trim()}`)
|
|
2829
|
+
.filter((item) => !item.endsWith(":"))
|
|
2830
|
+
.join(",");
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2710
2833
|
function collectTelegramEnvBotEntries(parsedEnv) {
|
|
2711
2834
|
const parsed = safeObject(parsedEnv);
|
|
2712
2835
|
const entries = new Map();
|
|
2713
2836
|
for (const [rawKey, rawValue] of Object.entries(parsed)) {
|
|
2714
2837
|
const match = String(rawKey || "").trim().match(
|
|
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,
|
|
2838
|
+
/^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|SERVER_ROLE_IDS|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i,
|
|
2716
2839
|
);
|
|
2717
2840
|
if (!match) continue;
|
|
2718
2841
|
const botKey = normalizeTelegramBotEnvKey(match[1], "");
|
|
@@ -2725,6 +2848,7 @@ function collectTelegramEnvBotEntries(parsedEnv) {
|
|
|
2725
2848
|
serverBotID: "",
|
|
2726
2849
|
serverBotName: "",
|
|
2727
2850
|
serverRoles: [],
|
|
2851
|
+
serverRoleIDs: {},
|
|
2728
2852
|
roleProfile: "",
|
|
2729
2853
|
client: "",
|
|
2730
2854
|
model: "",
|
|
@@ -2742,6 +2866,8 @@ function collectTelegramEnvBotEntries(parsedEnv) {
|
|
|
2742
2866
|
entry.serverBotName = textValue;
|
|
2743
2867
|
} else if (field === "SERVER_ROLES") {
|
|
2744
2868
|
entry.serverRoles = parseTelegramServerRoles(textValue);
|
|
2869
|
+
} else if (field === "SERVER_ROLE_IDS") {
|
|
2870
|
+
entry.serverRoleIDs = parseTelegramServerRoleIDs(textValue);
|
|
2745
2871
|
} else if (field === "ROLE_PROFILE") {
|
|
2746
2872
|
entry.roleProfile = normalizeRunnerRoleProfileName(textValue);
|
|
2747
2873
|
} else if (field === "AI_CLIENT") {
|
|
@@ -2788,6 +2914,7 @@ function parseTelegramBotEntryFile(filePath) {
|
|
|
2788
2914
|
serverBotID: String(parsed.TELEGRAM_BOT_SERVER_BOT_ID || "").trim(),
|
|
2789
2915
|
serverBotName: String(parsed.TELEGRAM_BOT_SERVER_NAME || "").trim(),
|
|
2790
2916
|
serverRoles: parseTelegramServerRoles(parsed.TELEGRAM_BOT_SERVER_ROLES || ""),
|
|
2917
|
+
serverRoleIDs: parseTelegramServerRoleIDs(parsed.TELEGRAM_BOT_SERVER_ROLE_IDS || ""),
|
|
2791
2918
|
token: String(parsed.TELEGRAM_BOT_TOKEN || "").trim(),
|
|
2792
2919
|
roleProfile: normalizeRunnerRoleProfileName(parsed.TELEGRAM_BOT_ROLE_PROFILE || ""),
|
|
2793
2920
|
client: normalizeLocalAIClientName(parsed.TELEGRAM_BOT_AI_CLIENT || "", ""),
|
|
@@ -2824,7 +2951,11 @@ function loadTelegramBotEntriesFromFiles() {
|
|
|
2824
2951
|
const dirPath = telegramBotEntriesDirPath();
|
|
2825
2952
|
if (!fs.existsSync(dirPath)) return [];
|
|
2826
2953
|
return fs.readdirSync(dirPath, { withFileTypes: true })
|
|
2827
|
-
.filter((item) => item.isFile() && /\.env$/i.test(item.name)
|
|
2954
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
2955
|
+
.filter((item) => {
|
|
2956
|
+
const lowered = item.name.toLowerCase();
|
|
2957
|
+
return lowered !== "_global.env" && lowered !== "global.env";
|
|
2958
|
+
})
|
|
2828
2959
|
.map((item) => path.join(dirPath, item.name))
|
|
2829
2960
|
.sort((left, right) => left.localeCompare(right))
|
|
2830
2961
|
.map((filePath) => {
|
|
@@ -2851,6 +2982,7 @@ function buildMergedTelegramEnvParsed(globalParsed, entries) {
|
|
|
2851
2982
|
merged[`TELEGRAM_BOT_${upper}_SERVER_BOT_ID`] = String(entry.serverBotID || "").trim();
|
|
2852
2983
|
merged[`TELEGRAM_BOT_${upper}_SERVER_NAME`] = String(entry.serverBotName || "").trim();
|
|
2853
2984
|
merged[`TELEGRAM_BOT_${upper}_SERVER_ROLES`] = ensureArray(entry.serverRoles).join(",");
|
|
2985
|
+
merged[`TELEGRAM_BOT_${upper}_SERVER_ROLE_IDS`] = formatTelegramServerRoleIDs(entry.serverRoleIDs);
|
|
2854
2986
|
merged[`TELEGRAM_BOT_${upper}_USERNAME`] = normalizeTelegramBotUsername(entry.username || "");
|
|
2855
2987
|
merged[`TELEGRAM_BOT_${upper}_TOKEN`] = String(entry.token || "").trim();
|
|
2856
2988
|
merged[`TELEGRAM_BOT_${upper}_ROLE_PROFILE`] = String(entry.roleProfile || "").trim();
|
|
@@ -2865,9 +2997,12 @@ function buildMergedTelegramEnvParsed(globalParsed, entries) {
|
|
|
2865
2997
|
function readTelegramEnvState() {
|
|
2866
2998
|
const filePath = providerEnvFilePath("telegram");
|
|
2867
2999
|
const legacyFilePath = telegramLegacyEnvFilePath();
|
|
3000
|
+
const legacyGlobalFilePath = telegramLegacyGlobalEnvFilePath();
|
|
2868
3001
|
let sourceFilePath = filePath;
|
|
2869
3002
|
try {
|
|
2870
|
-
if (!fs.existsSync(filePath) && fs.existsSync(
|
|
3003
|
+
if (!fs.existsSync(filePath) && fs.existsSync(legacyGlobalFilePath)) {
|
|
3004
|
+
sourceFilePath = legacyGlobalFilePath;
|
|
3005
|
+
} else if (!fs.existsSync(filePath) && fs.existsSync(legacyFilePath)) {
|
|
2871
3006
|
sourceFilePath = legacyFilePath;
|
|
2872
3007
|
} else if (!fs.existsSync(filePath)) {
|
|
2873
3008
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
@@ -2877,6 +3012,7 @@ function readTelegramEnvState() {
|
|
|
2877
3012
|
return {
|
|
2878
3013
|
filePath,
|
|
2879
3014
|
legacyFilePath,
|
|
3015
|
+
legacyGlobalFilePath,
|
|
2880
3016
|
sourceFilePath,
|
|
2881
3017
|
entriesDirPath: telegramBotEntriesDirPath(),
|
|
2882
3018
|
parsed: {},
|
|
@@ -2903,6 +3039,7 @@ function readTelegramEnvState() {
|
|
|
2903
3039
|
return {
|
|
2904
3040
|
filePath,
|
|
2905
3041
|
legacyFilePath,
|
|
3042
|
+
legacyGlobalFilePath,
|
|
2906
3043
|
sourceFilePath,
|
|
2907
3044
|
entriesDirPath: telegramBotEntriesDirPath(),
|
|
2908
3045
|
parsed: buildMergedTelegramEnvParsed(globalParsed, entries),
|
|
@@ -2912,7 +3049,7 @@ function readTelegramEnvState() {
|
|
|
2912
3049
|
}
|
|
2913
3050
|
|
|
2914
3051
|
function isTelegramEntryEnvKey(rawKey) {
|
|
2915
|
-
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
|
|
3052
|
+
return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|SERVER_ROLE_IDS|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
|
|
2916
3053
|
.test(String(rawKey || "").trim());
|
|
2917
3054
|
}
|
|
2918
3055
|
|
|
@@ -2935,7 +3072,7 @@ function renderNormalizedTelegramEnv(parsedEnv) {
|
|
|
2935
3072
|
"# Metheus local Telegram bot settings",
|
|
2936
3073
|
"# Keep this file on your machine only. Do not commit it.",
|
|
2937
3074
|
"# Store Telegram-wide settings here.",
|
|
2938
|
-
"# This global file lives in ~/.metheus/telegram-bots/
|
|
3075
|
+
"# This global file lives in ~/.metheus/telegram-bots/global.env.",
|
|
2939
3076
|
"# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<server-bot-name>.env.",
|
|
2940
3077
|
"",
|
|
2941
3078
|
`TELEGRAM_API_BASE_URL=${formatProviderEnvValue(parsed.TELEGRAM_API_BASE_URL || "")}`,
|
|
@@ -2971,6 +3108,7 @@ function renderTelegramBotEntryEnv(entryRaw) {
|
|
|
2971
3108
|
const entry = safeObject(entryRaw);
|
|
2972
3109
|
const botName = telegramEntryCommentNameForEnv(entry);
|
|
2973
3110
|
const serverRoles = parseTelegramServerRoles(entry.serverRoles || "");
|
|
3111
|
+
const serverRoleIDs = formatTelegramServerRoleIDs(entry.serverRoleIDs);
|
|
2974
3112
|
const lines = [
|
|
2975
3113
|
"# Metheus local Telegram bot entry",
|
|
2976
3114
|
`# Server bot: ${botName}`,
|
|
@@ -2978,6 +3116,7 @@ function renderTelegramBotEntryEnv(entryRaw) {
|
|
|
2978
3116
|
`TELEGRAM_BOT_SERVER_BOT_ID=${formatProviderEnvValue(entry.serverBotID || "")}`,
|
|
2979
3117
|
`TELEGRAM_BOT_SERVER_NAME=${formatProviderEnvValue(entry.serverBotName || "")}`,
|
|
2980
3118
|
`TELEGRAM_BOT_SERVER_ROLES=${formatProviderEnvValue(serverRoles.join(","))}`,
|
|
3119
|
+
`TELEGRAM_BOT_SERVER_ROLE_IDS=${formatProviderEnvValue(serverRoleIDs)}`,
|
|
2981
3120
|
];
|
|
2982
3121
|
if (!String(entry.serverBotID || "").trim() && !String(entry.serverBotName || "").trim() && String(entry.username || "").trim()) {
|
|
2983
3122
|
lines.push(
|
|
@@ -3006,6 +3145,7 @@ function writeTelegramEnvState(parsedEnv) {
|
|
|
3006
3145
|
|| entry.serverBotID
|
|
3007
3146
|
|| entry.serverBotName
|
|
3008
3147
|
|| ensureArray(entry.serverRoles).length
|
|
3148
|
+
|| Object.keys(safeObject(entry.serverRoleIDs)).length
|
|
3009
3149
|
|| entry.roleProfile
|
|
3010
3150
|
|| entry.client
|
|
3011
3151
|
|| entry.model
|
|
@@ -3021,6 +3161,7 @@ function writeTelegramEnvState(parsedEnv) {
|
|
|
3021
3161
|
});
|
|
3022
3162
|
const globalFilePath = providerEnvFilePath("telegram");
|
|
3023
3163
|
const legacyFilePath = telegramLegacyEnvFilePath();
|
|
3164
|
+
const legacyGlobalFilePath = telegramLegacyGlobalEnvFilePath();
|
|
3024
3165
|
fs.mkdirSync(path.dirname(globalFilePath), { recursive: true });
|
|
3025
3166
|
fs.writeFileSync(globalFilePath, renderNormalizedTelegramEnv(globalParsed), {
|
|
3026
3167
|
encoding: "utf8",
|
|
@@ -3030,24 +3171,46 @@ function writeTelegramEnvState(parsedEnv) {
|
|
|
3030
3171
|
fs.mkdirSync(entriesDirPath, { recursive: true });
|
|
3031
3172
|
const activeFiles = new Set();
|
|
3032
3173
|
entries.forEach((entry) => {
|
|
3033
|
-
const entryFilePath =
|
|
3034
|
-
|
|
3174
|
+
const entryFilePath = telegramBotEntryFilePathForEntry(entry);
|
|
3175
|
+
const previousEntryFilePath = String(entry.entryFilePath || "").trim();
|
|
3176
|
+
const discoveredEntryFilePath = previousEntryFilePath || findTelegramEntryPathVariant(entryFilePath);
|
|
3177
|
+
const sourceEntryFilePath = discoveredEntryFilePath || previousEntryFilePath;
|
|
3178
|
+
if (sourceEntryFilePath && sourceEntryFilePath !== entryFilePath && fs.existsSync(sourceEntryFilePath)) {
|
|
3179
|
+
const previousComparable = normalizeComparablePath(sourceEntryFilePath);
|
|
3180
|
+
const nextComparable = normalizeComparablePath(entryFilePath);
|
|
3181
|
+
if (previousComparable === nextComparable) {
|
|
3182
|
+
const tempRenamePath = path.join(
|
|
3183
|
+
path.dirname(entryFilePath),
|
|
3184
|
+
`.__rename__.${Date.now()}-${Math.random().toString(16).slice(2)}.env`,
|
|
3185
|
+
);
|
|
3186
|
+
fs.renameSync(sourceEntryFilePath, tempRenamePath);
|
|
3187
|
+
fs.renameSync(tempRenamePath, entryFilePath);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
activeFiles.add(normalizeComparablePath(entryFilePath));
|
|
3035
3191
|
fs.writeFileSync(entryFilePath, renderTelegramBotEntryEnv(entry), {
|
|
3036
3192
|
encoding: "utf8",
|
|
3037
3193
|
mode: 0o600,
|
|
3038
3194
|
});
|
|
3039
3195
|
});
|
|
3040
3196
|
fs.readdirSync(entriesDirPath, { withFileTypes: true })
|
|
3041
|
-
.filter((item) => item.isFile() && /\.env$/i.test(item.name)
|
|
3197
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
3198
|
+
.filter((item) => {
|
|
3199
|
+
const lowered = item.name.toLowerCase();
|
|
3200
|
+
return lowered !== "_global.env" && lowered !== "global.env";
|
|
3201
|
+
})
|
|
3042
3202
|
.forEach((item) => {
|
|
3043
|
-
const filePath =
|
|
3203
|
+
const filePath = normalizeComparablePath(path.join(entriesDirPath, item.name));
|
|
3044
3204
|
if (!activeFiles.has(filePath)) {
|
|
3045
|
-
fs.rmSync(
|
|
3205
|
+
fs.rmSync(path.join(entriesDirPath, item.name), { force: true });
|
|
3046
3206
|
}
|
|
3047
3207
|
});
|
|
3048
3208
|
if (legacyFilePath !== globalFilePath && fs.existsSync(legacyFilePath)) {
|
|
3049
3209
|
fs.rmSync(legacyFilePath, { force: true });
|
|
3050
3210
|
}
|
|
3211
|
+
if (legacyGlobalFilePath !== globalFilePath && fs.existsSync(legacyGlobalFilePath)) {
|
|
3212
|
+
fs.rmSync(legacyGlobalFilePath, { force: true });
|
|
3213
|
+
}
|
|
3051
3214
|
return globalFilePath;
|
|
3052
3215
|
}
|
|
3053
3216
|
|
|
@@ -3067,6 +3230,7 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
|
3067
3230
|
|| entry.serverBotID
|
|
3068
3231
|
|| entry.serverBotName
|
|
3069
3232
|
|| ensureArray(entry.serverRoles).length
|
|
3233
|
+
|| Object.keys(safeObject(entry.serverRoleIDs)).length
|
|
3070
3234
|
));
|
|
3071
3235
|
const desiredBotID = firstNonEmptyString([
|
|
3072
3236
|
selectors.serverBotID,
|
|
@@ -3118,7 +3282,8 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
|
3118
3282
|
provider: "telegram",
|
|
3119
3283
|
providerLabel: config.label,
|
|
3120
3284
|
filePath,
|
|
3121
|
-
|
|
3285
|
+
entryFilePath: telegramBotEntryFilePathForEntry(selected),
|
|
3286
|
+
error: `TELEGRAM_BOT_TOKEN is missing in ${telegramBotEntryFilePathForEntry(selected) || filePath}`,
|
|
3122
3287
|
token: "",
|
|
3123
3288
|
};
|
|
3124
3289
|
}
|
|
@@ -3127,14 +3292,16 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
|
3127
3292
|
provider: "telegram",
|
|
3128
3293
|
providerLabel: config.label,
|
|
3129
3294
|
filePath,
|
|
3295
|
+
entryFilePath: telegramBotEntryFilePathForEntry(selected),
|
|
3130
3296
|
token: selected.token,
|
|
3131
3297
|
source: "telegram_env_v2",
|
|
3132
|
-
tokenKey:
|
|
3298
|
+
tokenKey: "TELEGRAM_BOT_TOKEN",
|
|
3133
3299
|
botKey: selected.key,
|
|
3134
3300
|
botUsername: selected.username,
|
|
3135
3301
|
serverBotID: selected.serverBotID,
|
|
3136
3302
|
serverBotName: selected.serverBotName,
|
|
3137
3303
|
serverRoles: ensureArray(selected.serverRoles),
|
|
3304
|
+
serverRoleIDs: safeObject(selected.serverRoleIDs),
|
|
3138
3305
|
roleProfile: selected.roleProfile,
|
|
3139
3306
|
client: selected.client,
|
|
3140
3307
|
model: selected.model,
|
|
@@ -3161,6 +3328,7 @@ function resolveTelegramEnvConfig(parsedEnv, filePath, config, selectors = {}) {
|
|
|
3161
3328
|
serverBotID: "",
|
|
3162
3329
|
serverBotName: "",
|
|
3163
3330
|
serverRoles: [],
|
|
3331
|
+
serverRoleIDs: {},
|
|
3164
3332
|
roleProfile: "",
|
|
3165
3333
|
client: "",
|
|
3166
3334
|
model: "",
|
|
@@ -3732,6 +3900,7 @@ function buildBotCommandDeps() {
|
|
|
3732
3900
|
providerEnvFilePath,
|
|
3733
3901
|
telegramBotEntriesDirPath,
|
|
3734
3902
|
telegramBotEntryFilePath,
|
|
3903
|
+
telegramBotEntryFilePathForEntry,
|
|
3735
3904
|
ensureProviderEnvTemplate,
|
|
3736
3905
|
parseCommandAndFlags,
|
|
3737
3906
|
parseSimpleEnvText,
|
|
@@ -5521,7 +5690,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
5521
5690
|
const normalizedState = readTelegramEnvState();
|
|
5522
5691
|
writeTelegramEnvState(normalizedState.parsed);
|
|
5523
5692
|
normalizedGlobalText = fs.readFileSync(providerEnvFilePath("telegram"), "utf8");
|
|
5524
|
-
normalizedBotText = fs.readFileSync(
|
|
5693
|
+
normalizedBotText = fs.readFileSync(telegramBotEntryFilePathForEntry({ serverBotName: "RyoAI_bot", key: "ryoai_bot" }), "utf8");
|
|
5525
5694
|
} finally {
|
|
5526
5695
|
if (previousUserProfile === undefined) delete process.env.USERPROFILE;
|
|
5527
5696
|
else process.env.USERPROFILE = previousUserProfile;
|
package/lib/bot-commands.mjs
CHANGED
|
@@ -230,7 +230,7 @@ function printBotUsage(deps) {
|
|
|
230
230
|
` - bot add without flags uses the shortest practical guided flow: provider -> token -> verify -> optional username fallback -> optional default bot.`,
|
|
231
231
|
` - in the normal Telegram path, bot add does not ask for a local bot key or a server role/profile choice.`,
|
|
232
232
|
` - server bot name/UUID is the source of truth; --bot-key is an advanced local selector only.`,
|
|
233
|
-
` - runner commands use
|
|
233
|
+
` - runner commands use route names. Use runner list first, then run runner once/start with --route-name.`,
|
|
234
234
|
` - bot edit without flags asks for provider, existing entry, then walks field-by-field in a numbered flow.`,
|
|
235
235
|
` - in the normal Telegram edit path, the CLI keeps or re-resolves the server bot binding automatically instead of asking you to pick a server role/profile UUID first.`,
|
|
236
236
|
` - bot set-default / bot verify / bot remove also support guided numbered selection when run without flags.`,
|
|
@@ -445,6 +445,7 @@ function telegramEntryEnvKeys(botKey) {
|
|
|
445
445
|
serverBotID: `TELEGRAM_BOT_${upper}_SERVER_BOT_ID`,
|
|
446
446
|
serverBotName: `TELEGRAM_BOT_${upper}_SERVER_NAME`,
|
|
447
447
|
serverRoles: `TELEGRAM_BOT_${upper}_SERVER_ROLES`,
|
|
448
|
+
serverRoleIDs: `TELEGRAM_BOT_${upper}_SERVER_ROLE_IDS`,
|
|
448
449
|
username: `TELEGRAM_BOT_${upper}_USERNAME`,
|
|
449
450
|
token: `TELEGRAM_BOT_${upper}_TOKEN`,
|
|
450
451
|
roleProfile: `TELEGRAM_BOT_${upper}_ROLE_PROFILE`,
|
|
@@ -455,6 +456,52 @@ function telegramEntryEnvKeys(botKey) {
|
|
|
455
456
|
};
|
|
456
457
|
}
|
|
457
458
|
|
|
459
|
+
function parseTelegramServerRoleIDs(rawText) {
|
|
460
|
+
const output = {};
|
|
461
|
+
String(rawText || "")
|
|
462
|
+
.split(",")
|
|
463
|
+
.map((value) => String(value || "").trim())
|
|
464
|
+
.filter(Boolean)
|
|
465
|
+
.forEach((pair) => {
|
|
466
|
+
const separatorIndex = pair.indexOf(":");
|
|
467
|
+
if (separatorIndex <= 0) return;
|
|
468
|
+
const role = String(pair.slice(0, separatorIndex) || "").trim();
|
|
469
|
+
const id = String(pair.slice(separatorIndex + 1) || "").trim();
|
|
470
|
+
if (!role || !id) return;
|
|
471
|
+
output[role] = id;
|
|
472
|
+
});
|
|
473
|
+
return preferredRoleSort(Object.keys(output)).reduce((acc, role) => {
|
|
474
|
+
acc[role] = output[role];
|
|
475
|
+
return acc;
|
|
476
|
+
}, {});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function formatTelegramServerRoleIDs(roleIDs) {
|
|
480
|
+
return preferredRoleSort(Object.keys(safeObject(roleIDs)))
|
|
481
|
+
.map((role) => `${role}:${String(safeObject(roleIDs)[role] || "").trim()}`)
|
|
482
|
+
.filter((item) => !item.endsWith(":"))
|
|
483
|
+
.join(",");
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function summarizeServerRoleIDs(bots) {
|
|
487
|
+
const output = {};
|
|
488
|
+
ensureArray(bots).forEach((bot) => {
|
|
489
|
+
const role = String(bot?.role || "").trim();
|
|
490
|
+
const id = String(bot?.id || "").trim();
|
|
491
|
+
if (!role || !id || output[role]) return;
|
|
492
|
+
output[role] = id;
|
|
493
|
+
});
|
|
494
|
+
return preferredRoleSort(Object.keys(output)).reduce((acc, role) => {
|
|
495
|
+
acc[role] = output[role];
|
|
496
|
+
return acc;
|
|
497
|
+
}, {});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function normalizeComparablePath(rawPath) {
|
|
501
|
+
const resolved = path.resolve(String(rawPath || ""));
|
|
502
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
503
|
+
}
|
|
504
|
+
|
|
458
505
|
function telegramEntryCommentName(entry) {
|
|
459
506
|
const current = safeObject(entry);
|
|
460
507
|
const serverBotName = String(current.serverBotName || "").trim();
|
|
@@ -495,6 +542,7 @@ function upsertTelegramEntry(parsedEnv, entry) {
|
|
|
495
542
|
next[keys.serverBotID] = String(entry.serverBotID || "").trim();
|
|
496
543
|
next[keys.serverBotName] = String(entry.serverBotName || "").trim();
|
|
497
544
|
next[keys.serverRoles] = ensureArray(entry.serverRoles).join(",");
|
|
545
|
+
next[keys.serverRoleIDs] = formatTelegramServerRoleIDs(entry.serverRoleIDs);
|
|
498
546
|
next[keys.username] = persistTelegramUsername(entry);
|
|
499
547
|
next[keys.token] = String(entry.token || "").trim();
|
|
500
548
|
next[keys.roleProfile] = String(entry.roleProfile || "").trim();
|
|
@@ -521,7 +569,7 @@ function telegramKnownKeys(parsedEnv, deps) {
|
|
|
521
569
|
}
|
|
522
570
|
|
|
523
571
|
function isTelegramEntryEnvKey(rawKey) {
|
|
524
|
-
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
|
|
572
|
+
return /^TELEGRAM_BOT_([A-Z0-9_]+)_(TOKEN|USERNAME|SERVER_BOT_ID|SERVER_NAME|SERVER_ROLES|SERVER_ROLE_IDS|ROLE_PROFILE|AI_CLIENT|AI_MODEL|AI_PERMISSION_MODE|AI_REASONING_EFFORT)$/i
|
|
525
573
|
.test(String(rawKey || "").trim());
|
|
526
574
|
}
|
|
527
575
|
|
|
@@ -536,6 +584,7 @@ function renderTelegramEnv(parsedEnv, deps) {
|
|
|
536
584
|
|| entry.serverBotID
|
|
537
585
|
|| entry.serverBotName
|
|
538
586
|
|| ensureArray(entry.serverRoles).length
|
|
587
|
+
|| Object.keys(safeObject(entry.serverRoleIDs)).length
|
|
539
588
|
|| entry.roleProfile
|
|
540
589
|
|| entry.client
|
|
541
590
|
|| entry.model
|
|
@@ -575,6 +624,7 @@ function renderTelegramEnv(parsedEnv, deps) {
|
|
|
575
624
|
`${keys.serverBotID}=${formatEnvValue(entry.serverBotID || "")}`,
|
|
576
625
|
`${keys.serverBotName}=${formatEnvValue(entry.serverBotName || "")}`,
|
|
577
626
|
`${keys.serverRoles}=${formatEnvValue(ensureArray(entry.serverRoles).join(","))}`,
|
|
627
|
+
`${keys.serverRoleIDs}=${formatEnvValue(formatTelegramServerRoleIDs(entry.serverRoleIDs))}`,
|
|
578
628
|
];
|
|
579
629
|
if (String(entry.username || "").trim()) {
|
|
580
630
|
entryLines.push(`${keys.username}=${formatEnvValue(entry.username || "")}`);
|
|
@@ -632,6 +682,7 @@ function renderTelegramBotEntryFile(entryRaw) {
|
|
|
632
682
|
const entry = safeObject(entryRaw);
|
|
633
683
|
const botName = telegramEntryCommentName(entry);
|
|
634
684
|
const serverRoles = ensureArray(entry.serverRoles).join(",");
|
|
685
|
+
const serverRoleIDs = formatTelegramServerRoleIDs(entry.serverRoleIDs);
|
|
635
686
|
const lines = [
|
|
636
687
|
"# Metheus local Telegram bot entry",
|
|
637
688
|
`# Server bot: ${botName}`,
|
|
@@ -639,6 +690,7 @@ function renderTelegramBotEntryFile(entryRaw) {
|
|
|
639
690
|
`TELEGRAM_BOT_SERVER_BOT_ID=${formatEnvValue(entry.serverBotID || "")}`,
|
|
640
691
|
`TELEGRAM_BOT_SERVER_NAME=${formatEnvValue(entry.serverBotName || "")}`,
|
|
641
692
|
`TELEGRAM_BOT_SERVER_ROLES=${formatEnvValue(serverRoles)}`,
|
|
693
|
+
`TELEGRAM_BOT_SERVER_ROLE_IDS=${formatEnvValue(serverRoleIDs)}`,
|
|
642
694
|
];
|
|
643
695
|
if (!String(entry.serverBotID || "").trim() && !String(entry.serverBotName || "").trim() && String(entry.username || "").trim()) {
|
|
644
696
|
lines.push(
|
|
@@ -658,11 +710,47 @@ function renderTelegramBotEntryFile(entryRaw) {
|
|
|
658
710
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
659
711
|
}
|
|
660
712
|
|
|
713
|
+
function appendTextSection(lines, title, sectionLines) {
|
|
714
|
+
const rows = ensureArray(sectionLines).filter(Boolean);
|
|
715
|
+
if (!rows.length) return;
|
|
716
|
+
lines.push(`${title}:`);
|
|
717
|
+
rows.forEach((row) => {
|
|
718
|
+
lines.push(` ${row}`);
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function resolveTelegramEntryFilePath(entry, deps) {
|
|
723
|
+
const current = safeObject(entry);
|
|
724
|
+
if (!firstNonEmptyString([current.serverBotName, current.username, current.key])) {
|
|
725
|
+
return "";
|
|
726
|
+
}
|
|
727
|
+
if (typeof deps?.telegramBotEntryFilePathForEntry === "function") {
|
|
728
|
+
return String(deps.telegramBotEntryFilePathForEntry(entry) || "").trim();
|
|
729
|
+
}
|
|
730
|
+
return String(requireDependency(deps, "telegramBotEntryFilePath")(current.key) || "").trim();
|
|
731
|
+
}
|
|
732
|
+
|
|
661
733
|
function rewriteTelegramEntryFile(entry, deps) {
|
|
662
|
-
const entryFilePath =
|
|
734
|
+
const entryFilePath = resolveTelegramEntryFilePath(entry, deps);
|
|
663
735
|
if (!entryFilePath) return;
|
|
736
|
+
let rewritePath = entryFilePath;
|
|
737
|
+
if (fs.existsSync(path.dirname(entryFilePath))) {
|
|
738
|
+
const currentComparable = normalizeComparablePath(entryFilePath);
|
|
739
|
+
const existingVariant = fs.readdirSync(path.dirname(entryFilePath), { withFileTypes: true })
|
|
740
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name))
|
|
741
|
+
.map((item) => path.join(path.dirname(entryFilePath), item.name))
|
|
742
|
+
.find((candidatePath) => normalizeComparablePath(candidatePath) === currentComparable);
|
|
743
|
+
if (existingVariant && existingVariant !== entryFilePath) {
|
|
744
|
+
const tempRenamePath = path.join(
|
|
745
|
+
path.dirname(entryFilePath),
|
|
746
|
+
`.__rename__.${Date.now()}-${Math.random().toString(16).slice(2)}.env`,
|
|
747
|
+
);
|
|
748
|
+
fs.renameSync(existingVariant, tempRenamePath);
|
|
749
|
+
fs.renameSync(tempRenamePath, entryFilePath);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
664
752
|
fs.mkdirSync(path.dirname(entryFilePath), { recursive: true });
|
|
665
|
-
fs.writeFileSync(
|
|
753
|
+
fs.writeFileSync(rewritePath, renderTelegramBotEntryFile(entry), {
|
|
666
754
|
encoding: "utf8",
|
|
667
755
|
mode: 0o600,
|
|
668
756
|
});
|
|
@@ -699,6 +787,7 @@ function telegramEntriesForDisplay(parsedEnv, deps) {
|
|
|
699
787
|
|| entry.serverBotID
|
|
700
788
|
|| entry.serverBotName
|
|
701
789
|
|| ensureArray(entry.serverRoles).length
|
|
790
|
+
|| Object.keys(safeObject(entry.serverRoleIDs)).length
|
|
702
791
|
|| entry.roleProfile
|
|
703
792
|
|| entry.client
|
|
704
793
|
|| entry.model
|
|
@@ -861,11 +950,13 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
|
|
|
861
950
|
current.serverBotID = String(initialBinding.serverBotID || "").trim();
|
|
862
951
|
current.serverBotName = String(initialBinding.name || "").trim();
|
|
863
952
|
current.serverRoles = ensureArray(initialBinding.roles);
|
|
953
|
+
current.serverRoleIDs = safeObject(initialBinding.serverRoleIDs);
|
|
864
954
|
current.roleProfile = String(initialBinding.role || current.roleProfile || "").trim();
|
|
865
955
|
applyRoleProfileDefaults(current, resolveRoleProfileDefaults(current.roleProfile, deps));
|
|
866
956
|
} else if (initialBinding.mode === "group") {
|
|
867
957
|
current.serverBotName = String(initialBinding.name || "").trim();
|
|
868
958
|
current.serverRoles = ensureArray(initialBinding.roles);
|
|
959
|
+
current.serverRoleIDs = safeObject(initialBinding.serverRoleIDs);
|
|
869
960
|
}
|
|
870
961
|
}
|
|
871
962
|
}
|
|
@@ -918,6 +1009,7 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
|
|
|
918
1009
|
current.serverBotID = "";
|
|
919
1010
|
current.serverBotName = String(serverBot.name || "").trim();
|
|
920
1011
|
current.serverRoles = preferredRoleSort(serverBot.roles);
|
|
1012
|
+
current.serverRoleIDs = safeObject(serverBot.roleIDs);
|
|
921
1013
|
current.__preferServerIdentity = true;
|
|
922
1014
|
current.roleProfile = "";
|
|
923
1015
|
current.client = "";
|
|
@@ -934,6 +1026,7 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
|
|
|
934
1026
|
current.serverBotID = String(serverBot.botID || "").trim();
|
|
935
1027
|
current.serverBotName = String(serverBot.name || "").trim();
|
|
936
1028
|
current.serverRoles = ensureArray(serverBot.roles);
|
|
1029
|
+
current.serverRoleIDs = safeObject(serverBot.roleIDs);
|
|
937
1030
|
current.__preferServerIdentity = true;
|
|
938
1031
|
current.roleProfile = String(serverBot.role || current.roleProfile || "").trim();
|
|
939
1032
|
serverRoleAutoResolved = String(serverBot.role || current.roleProfile || "").trim() || "__server_binding__";
|
|
@@ -1062,14 +1155,17 @@ function renderBotListPayload(provider, state, deps) {
|
|
|
1062
1155
|
return {
|
|
1063
1156
|
provider,
|
|
1064
1157
|
filePath: state.filePath,
|
|
1158
|
+
globalFilePath: state.filePath,
|
|
1065
1159
|
support: requireDependency(deps, "summarizeProviderSupport")(provider),
|
|
1066
1160
|
defaultBotKey: String(state.parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim(),
|
|
1067
1161
|
entries: telegramEntriesForDisplay(state.parsed, deps).map((entry) => ({
|
|
1068
1162
|
key: entry.key,
|
|
1069
1163
|
isDefault: entry.isDefault,
|
|
1164
|
+
entryFilePath: resolveTelegramEntryFilePath(entry, deps),
|
|
1070
1165
|
serverBotID: entry.serverBotID,
|
|
1071
1166
|
serverBotName: entry.serverBotName,
|
|
1072
1167
|
serverRoles: ensureArray(entry.serverRoles),
|
|
1168
|
+
serverRoleIDs: safeObject(entry.serverRoleIDs),
|
|
1073
1169
|
username: entry.username,
|
|
1074
1170
|
tokenConfigured: Boolean(entry.token),
|
|
1075
1171
|
roleProfile: entry.roleProfile,
|
|
@@ -1106,14 +1202,18 @@ function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
|
|
|
1106
1202
|
return {
|
|
1107
1203
|
provider,
|
|
1108
1204
|
filePath: state.filePath,
|
|
1205
|
+
globalFilePath: state.filePath,
|
|
1206
|
+
entryFilePath: selectedEntry ? resolveTelegramEntryFilePath(selectedEntry, deps) : "",
|
|
1109
1207
|
support: requireDependency(deps, "summarizeProviderSupport")(provider),
|
|
1110
1208
|
defaultBotKey: String(state.parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim(),
|
|
1111
1209
|
entry: selectedEntry ? {
|
|
1112
1210
|
key: selectedEntry.key,
|
|
1113
1211
|
isDefault: selectedEntry.isDefault,
|
|
1212
|
+
entryFilePath: resolveTelegramEntryFilePath(selectedEntry, deps),
|
|
1114
1213
|
serverBotID: selectedEntry.serverBotID,
|
|
1115
1214
|
serverBotName: selectedEntry.serverBotName,
|
|
1116
1215
|
serverRoles: ensureArray(selectedEntry.serverRoles),
|
|
1216
|
+
serverRoleIDs: safeObject(selectedEntry.serverRoleIDs),
|
|
1117
1217
|
username: selectedEntry.username,
|
|
1118
1218
|
tokenConfigured: Boolean(selectedEntry.token),
|
|
1119
1219
|
roleProfile: selectedEntry.roleProfile,
|
|
@@ -1210,8 +1310,10 @@ function printBotList(provider, state, deps) {
|
|
|
1210
1310
|
process.stdout.write(
|
|
1211
1311
|
[
|
|
1212
1312
|
` - ${telegramEntryDisplayName(entry)}${entry.isDefault ? " [default]" : ""}`,
|
|
1313
|
+
` entry_file: ${resolveTelegramEntryFilePath(entry, deps) || "-"}`,
|
|
1213
1314
|
` local_selector: ${entry.key || "-"}${entry.key ? " (advanced)" : ""}`,
|
|
1214
1315
|
` server_bot_id: ${entry.serverBotID || "-"}`,
|
|
1316
|
+
` server_role_ids: ${formatTelegramServerRoleIDs(entry.serverRoleIDs) || "-"}`,
|
|
1215
1317
|
` fallback_username: ${entry.username ? `@${entry.username}` : "-"}`,
|
|
1216
1318
|
` token: ${entry.token ? maskSecret(entry.token) : "-"}`,
|
|
1217
1319
|
` role_profile: ${entry.roleProfile || "-"}`,
|
|
@@ -1234,38 +1336,49 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
|
1234
1336
|
const selectedEntry = entry || {};
|
|
1235
1337
|
const serverBinding = safeObject(extras.serverBinding);
|
|
1236
1338
|
const routeLinks = safeObject(extras.routeLinks);
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1339
|
+
const lines = [
|
|
1340
|
+
`${providerLabel(provider, deps)} bot`,
|
|
1341
|
+
`global_file: ${state.filePath}`,
|
|
1342
|
+
`entry_file: ${resolveTelegramEntryFilePath(selectedEntry, deps) || "-"}`,
|
|
1343
|
+
`server_name: ${telegramEntryDisplayName(selectedEntry)}`,
|
|
1344
|
+
];
|
|
1345
|
+
appendTextSection(lines, "stored_local_entry", [
|
|
1346
|
+
`local_selector: ${selectedEntry.key || "-"}${selectedEntry.key ? " (advanced)" : ""}`,
|
|
1347
|
+
`default: ${selectedEntry.isDefault ? "yes" : "no"}`,
|
|
1348
|
+
`stored_server_bot_id: ${selectedEntry.serverBotID || "-"}`,
|
|
1349
|
+
`stored_server_name: ${selectedEntry.serverBotName || "-"}`,
|
|
1350
|
+
`stored_server_roles: ${ensureArray(selectedEntry.serverRoles).join(", ") || "-"}`,
|
|
1351
|
+
`stored_server_role_ids: ${formatTelegramServerRoleIDs(selectedEntry.serverRoleIDs) || "-"}`,
|
|
1352
|
+
`fallback_username: ${selectedEntry.username ? `@${selectedEntry.username}` : "-"}`,
|
|
1353
|
+
`token: ${selectedEntry.token ? maskSecret(selectedEntry.token) : "(not configured)"}`,
|
|
1354
|
+
`role_profile: ${selectedEntry.roleProfile || "-"}`,
|
|
1355
|
+
`ai_client: ${selectedEntry.client ? displayLocalAIClientName(selectedEntry.client) : "-"}`,
|
|
1356
|
+
`ai_model: ${selectedEntry.model || "-"}`,
|
|
1357
|
+
`permission_mode: ${selectedEntry.permissionMode || "-"}`,
|
|
1358
|
+
`reasoning_effort: ${selectedEntry.reasoningEffort || "-"}`,
|
|
1359
|
+
]);
|
|
1252
1360
|
if (Object.keys(serverBinding).length) {
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1361
|
+
const liveLines = [
|
|
1362
|
+
`status: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}`,
|
|
1363
|
+
`binding_mode: ${serverBinding.mode || "-"}`,
|
|
1364
|
+
`live_server_name: ${serverBinding.name || "-"}`,
|
|
1365
|
+
`live_server_bot_id: ${serverBinding.serverBotID || selectedEntry.serverBotID || "-"}`,
|
|
1366
|
+
];
|
|
1257
1367
|
if (serverBinding.mode === "group") {
|
|
1258
|
-
|
|
1368
|
+
liveLines.push(`live_server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}`);
|
|
1369
|
+
liveLines.push(`live_server_role_ids: ${formatTelegramServerRoleIDs(serverBinding.serverRoleIDs) || "-"}`);
|
|
1259
1370
|
const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
|
|
1260
1371
|
Object.keys(groupedProfiles).forEach((role) => {
|
|
1261
|
-
|
|
1372
|
+
liveLines.push(`runtime_role_profile[${role}]: ${formatRoleProfileOutputLine(groupedProfiles[role])}`);
|
|
1262
1373
|
});
|
|
1263
1374
|
} else if (serverBinding.effectiveRoleProfile) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1375
|
+
liveLines.push(`live_server_role: ${serverBinding.role || "-"}`);
|
|
1376
|
+
liveLines.push(`live_server_role_ids: ${formatTelegramServerRoleIDs(serverBinding.serverRoleIDs) || "-"}`);
|
|
1377
|
+
liveLines.push(`runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}`);
|
|
1266
1378
|
}
|
|
1379
|
+
appendTextSection(lines, "live_server_binding", liveLines);
|
|
1267
1380
|
}
|
|
1268
|
-
|
|
1381
|
+
const routeLines = [`count: ${intFromRaw(routeLinks.total, 0)}`];
|
|
1269
1382
|
ensureArray(routeLinks.routes).forEach((route) => {
|
|
1270
1383
|
const routeRuntime = safeObject(route.runtimeRoleProfile);
|
|
1271
1384
|
const routeDetail = [
|
|
@@ -1273,8 +1386,10 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
|
1273
1386
|
route.routeRole ? `route_role=${String(route.routeRole || "").trim()}` : "",
|
|
1274
1387
|
Object.keys(routeRuntime).length ? `runtime=${formatRoleProfileOutputLine(routeRuntime)}` : "",
|
|
1275
1388
|
].filter(Boolean).join(" ");
|
|
1276
|
-
|
|
1389
|
+
routeLines.push(`${String(route.routeName || "-")}: ${routeDetail || "-"}`);
|
|
1277
1390
|
});
|
|
1391
|
+
appendTextSection(lines, "linked_runner_routes", routeLines);
|
|
1392
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
1278
1393
|
return;
|
|
1279
1394
|
}
|
|
1280
1395
|
const tokenKey = providerTokenKey(provider, deps);
|
|
@@ -1343,6 +1458,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1343
1458
|
role: "",
|
|
1344
1459
|
name: "",
|
|
1345
1460
|
roles: [],
|
|
1461
|
+
roleIDs: {},
|
|
1346
1462
|
matchMode: manualID ? "manual" : "blank",
|
|
1347
1463
|
};
|
|
1348
1464
|
}
|
|
@@ -1360,6 +1476,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1360
1476
|
role: String(match.role || "").trim(),
|
|
1361
1477
|
name: String(match.name || "").trim(),
|
|
1362
1478
|
roles: summarizeServerBotRoles([match]),
|
|
1479
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1363
1480
|
matchMode: "exact",
|
|
1364
1481
|
};
|
|
1365
1482
|
}
|
|
@@ -1369,6 +1486,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1369
1486
|
role: "",
|
|
1370
1487
|
name: String(matchedByName[0]?.name || "").trim(),
|
|
1371
1488
|
roles: summarizeServerBotRoles(matchedByName),
|
|
1489
|
+
roleIDs: summarizeServerRoleIDs(matchedByName),
|
|
1372
1490
|
matchMode: "group",
|
|
1373
1491
|
};
|
|
1374
1492
|
}
|
|
@@ -1380,6 +1498,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1380
1498
|
role: String(match.role || "").trim(),
|
|
1381
1499
|
name: String(match.name || "").trim(),
|
|
1382
1500
|
roles: summarizeServerBotRoles([match]),
|
|
1501
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1383
1502
|
matchMode: "exact",
|
|
1384
1503
|
};
|
|
1385
1504
|
}
|
|
@@ -1399,6 +1518,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1399
1518
|
role: "",
|
|
1400
1519
|
name: String(groupedBots[0]?.name || "").trim(),
|
|
1401
1520
|
roles: summarizeServerBotRoles(groupedBots),
|
|
1521
|
+
roleIDs: summarizeServerRoleIDs(groupedBots),
|
|
1402
1522
|
matchMode: "group",
|
|
1403
1523
|
};
|
|
1404
1524
|
}
|
|
@@ -1453,7 +1573,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1453
1573
|
{ defaultIndex: 0 },
|
|
1454
1574
|
);
|
|
1455
1575
|
if (!roleChoice || roleChoice.value === "__blank__") {
|
|
1456
|
-
return { botID: "", role: "", name: "", roles: [], matchMode: "blank" };
|
|
1576
|
+
return { botID: "", role: "", name: "", roles: [], roleIDs: {}, matchMode: "blank" };
|
|
1457
1577
|
}
|
|
1458
1578
|
if (roleChoice.value === "__manual__") {
|
|
1459
1579
|
const manualID = await promptLine(ui, "Server bot UUID", "");
|
|
@@ -1462,6 +1582,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1462
1582
|
role: "",
|
|
1463
1583
|
name: "",
|
|
1464
1584
|
roles: [],
|
|
1585
|
+
roleIDs: {},
|
|
1465
1586
|
matchMode: manualID ? "manual" : "blank",
|
|
1466
1587
|
};
|
|
1467
1588
|
}
|
|
@@ -1473,6 +1594,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1473
1594
|
role: String(match.role || "").trim(),
|
|
1474
1595
|
name: String(match.name || "").trim(),
|
|
1475
1596
|
roles: summarizeServerBotRoles([match]),
|
|
1597
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1476
1598
|
matchMode: "exact",
|
|
1477
1599
|
};
|
|
1478
1600
|
}
|
|
@@ -1501,7 +1623,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1501
1623
|
{ defaultIndex: 0 },
|
|
1502
1624
|
);
|
|
1503
1625
|
if (!choice || choice.value === "__blank__") {
|
|
1504
|
-
return { botID: "", role: "", name: "", roles: [], matchMode: "blank" };
|
|
1626
|
+
return { botID: "", role: "", name: "", roles: [], roleIDs: {}, matchMode: "blank" };
|
|
1505
1627
|
}
|
|
1506
1628
|
if (choice.value === "__manual__") {
|
|
1507
1629
|
const manualID = await promptLine(ui, "Server bot UUID", "");
|
|
@@ -1510,6 +1632,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1510
1632
|
role: selectedRole,
|
|
1511
1633
|
name: "",
|
|
1512
1634
|
roles: selectedRole ? [selectedRole] : [],
|
|
1635
|
+
roleIDs: {},
|
|
1513
1636
|
matchMode: manualID ? "manual" : "blank",
|
|
1514
1637
|
};
|
|
1515
1638
|
}
|
|
@@ -1519,6 +1642,7 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
|
|
|
1519
1642
|
role: String(match.role || "").trim(),
|
|
1520
1643
|
name: String(match.name || "").trim(),
|
|
1521
1644
|
roles: summarizeServerBotRoles([match]),
|
|
1645
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1522
1646
|
matchMode: "exact",
|
|
1523
1647
|
};
|
|
1524
1648
|
}
|
|
@@ -1534,7 +1658,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
|
|
|
1534
1658
|
]),
|
|
1535
1659
|
);
|
|
1536
1660
|
if (!requestedBotID && !requestedName) {
|
|
1537
|
-
return { botID: "", role: "", name: "", roles: [], matchMode: "blank" };
|
|
1661
|
+
return { botID: "", role: "", name: "", roles: [], roleIDs: {}, matchMode: "blank" };
|
|
1538
1662
|
}
|
|
1539
1663
|
const lookup = await requireDependency(deps, "listServerBots")({
|
|
1540
1664
|
provider,
|
|
@@ -1563,6 +1687,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
|
|
|
1563
1687
|
role: "",
|
|
1564
1688
|
name: match.name,
|
|
1565
1689
|
roles: summarizeServerBotRoles(sameNameMatches),
|
|
1690
|
+
roleIDs: summarizeServerRoleIDs(sameNameMatches),
|
|
1566
1691
|
matchMode: "group",
|
|
1567
1692
|
};
|
|
1568
1693
|
}
|
|
@@ -1571,6 +1696,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
|
|
|
1571
1696
|
role: match.role,
|
|
1572
1697
|
name: match.name,
|
|
1573
1698
|
roles: summarizeServerBotRoles([match]),
|
|
1699
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1574
1700
|
matchMode: "exact",
|
|
1575
1701
|
};
|
|
1576
1702
|
}
|
|
@@ -1587,6 +1713,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
|
|
|
1587
1713
|
role: String(match.role || "").trim(),
|
|
1588
1714
|
name: String(match.name || "").trim(),
|
|
1589
1715
|
roles: summarizeServerBotRoles([match]),
|
|
1716
|
+
roleIDs: summarizeServerRoleIDs([match]),
|
|
1590
1717
|
matchMode: "exact",
|
|
1591
1718
|
};
|
|
1592
1719
|
}
|
|
@@ -1595,6 +1722,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
|
|
|
1595
1722
|
role: "",
|
|
1596
1723
|
name: String(matchedByName[0]?.name || "").trim(),
|
|
1597
1724
|
roles: summarizeServerBotRoles(matchedByName),
|
|
1725
|
+
roleIDs: summarizeServerRoleIDs(matchedByName),
|
|
1598
1726
|
matchMode: "group",
|
|
1599
1727
|
};
|
|
1600
1728
|
}
|
|
@@ -1966,6 +2094,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
1966
2094
|
}
|
|
1967
2095
|
const resolveGroupedPayload = (matches, matchedBy) => {
|
|
1968
2096
|
const roles = preferredRoleSort(summarizeServerBotRoles(matches));
|
|
2097
|
+
const serverRoleIDs = summarizeServerRoleIDs(matches);
|
|
1969
2098
|
return {
|
|
1970
2099
|
ok: true,
|
|
1971
2100
|
mode: "group",
|
|
@@ -1973,6 +2102,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
1973
2102
|
name: String(matches[0]?.name || "").trim(),
|
|
1974
2103
|
role: "",
|
|
1975
2104
|
roles,
|
|
2105
|
+
serverRoleIDs,
|
|
1976
2106
|
serverBotID: String(current.serverBotID || "").trim(),
|
|
1977
2107
|
effectiveRoleProfile: null,
|
|
1978
2108
|
effectiveRoleProfiles: buildGroupedRoleProfileOutput(roles, deps),
|
|
@@ -1989,6 +2119,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
1989
2119
|
name: "",
|
|
1990
2120
|
role: "",
|
|
1991
2121
|
roles: [],
|
|
2122
|
+
serverRoleIDs: {},
|
|
1992
2123
|
serverBotID: String(current.serverBotID || "").trim(),
|
|
1993
2124
|
effectiveRoleProfile: null,
|
|
1994
2125
|
effectiveRoleProfiles: {},
|
|
@@ -2007,6 +2138,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
2007
2138
|
name: match.name,
|
|
2008
2139
|
role: match.role,
|
|
2009
2140
|
roles: summarizeServerBotRoles([match]),
|
|
2141
|
+
serverRoleIDs: summarizeServerRoleIDs([match]),
|
|
2010
2142
|
serverBotID: match.id,
|
|
2011
2143
|
effectiveRoleProfile,
|
|
2012
2144
|
effectiveRoleProfiles: {},
|
|
@@ -2023,6 +2155,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
2023
2155
|
name: "",
|
|
2024
2156
|
role: "",
|
|
2025
2157
|
roles: [],
|
|
2158
|
+
serverRoleIDs: {},
|
|
2026
2159
|
serverBotID: "",
|
|
2027
2160
|
effectiveRoleProfile: null,
|
|
2028
2161
|
effectiveRoleProfiles: {},
|
|
@@ -2041,6 +2174,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
|
|
|
2041
2174
|
name: String(match.name || "").trim(),
|
|
2042
2175
|
role: String(match.role || "").trim(),
|
|
2043
2176
|
roles: summarizeServerBotRoles([match]),
|
|
2177
|
+
serverRoleIDs: summarizeServerRoleIDs([match]),
|
|
2044
2178
|
serverBotID: String(match.id || "").trim(),
|
|
2045
2179
|
effectiveRoleProfile,
|
|
2046
2180
|
effectiveRoleProfiles: {},
|
|
@@ -2062,6 +2196,7 @@ async function resolveTelegramServerBindingDetails(envConfig, flags, deps) {
|
|
|
2062
2196
|
name: "",
|
|
2063
2197
|
role: "",
|
|
2064
2198
|
roles: [],
|
|
2199
|
+
serverRoleIDs: {},
|
|
2065
2200
|
effectiveRoleProfile: null,
|
|
2066
2201
|
effectiveRoleProfiles: {},
|
|
2067
2202
|
detail: `server bot lookup unavailable: ${lookup?.error || "unknown error"}`,
|
|
@@ -2157,6 +2292,7 @@ async function autoResolveTelegramServerBot(current, flags, deps) {
|
|
|
2157
2292
|
role: String(current?.roleProfile || "").trim(),
|
|
2158
2293
|
name: "",
|
|
2159
2294
|
roles: [],
|
|
2295
|
+
roleIDs: {},
|
|
2160
2296
|
matchMode: existingBotID ? "existing" : "blank",
|
|
2161
2297
|
};
|
|
2162
2298
|
}
|
|
@@ -2180,6 +2316,7 @@ async function autoResolveTelegramServerBot(current, flags, deps) {
|
|
|
2180
2316
|
role: String(current?.roleProfile || "").trim(),
|
|
2181
2317
|
name: "",
|
|
2182
2318
|
roles: [],
|
|
2319
|
+
roleIDs: {},
|
|
2183
2320
|
matchMode: existingBotID ? "existing" : "blank",
|
|
2184
2321
|
};
|
|
2185
2322
|
}
|
|
@@ -2363,6 +2500,7 @@ async function addTelegramBot(ui, flags, deps) {
|
|
|
2363
2500
|
serverBotID: String(getServerBotIDFlag(flags) || serverBot.botID || "").trim(),
|
|
2364
2501
|
serverBotName: String(serverBot.name || "").trim(),
|
|
2365
2502
|
serverRoles: preferredRoleSort(serverBot.roles),
|
|
2503
|
+
serverRoleIDs: safeObject(serverBot.roleIDs),
|
|
2366
2504
|
username,
|
|
2367
2505
|
__preferServerIdentity: serverBot.matchMode === "group" || Boolean(String(getServerBotIDFlag(flags) || serverBot.botID || "").trim()),
|
|
2368
2506
|
token,
|
|
@@ -2485,6 +2623,7 @@ async function editTelegramBot(ui, flags, deps) {
|
|
|
2485
2623
|
current.serverBotID = String(getServerBotIDFlag(flags) || current.serverBotID || "").trim();
|
|
2486
2624
|
current.serverBotName = String(resolvedServerBot.name || current.serverBotName || "").trim();
|
|
2487
2625
|
current.serverRoles = preferredRoleSort(resolvedServerBot.roles);
|
|
2626
|
+
current.serverRoleIDs = safeObject(resolvedServerBot.roleIDs);
|
|
2488
2627
|
if (!hasOwnFlag(flags, "role-profile")) current.roleProfile = "";
|
|
2489
2628
|
if (!(hasOwnFlag(flags, "client") || hasOwnFlag(flags, "ai-client"))) current.client = "";
|
|
2490
2629
|
if (!(hasOwnFlag(flags, "model") || hasOwnFlag(flags, "ai-model"))) current.model = "";
|
|
@@ -2494,6 +2633,7 @@ async function editTelegramBot(ui, flags, deps) {
|
|
|
2494
2633
|
current.serverBotID = String(resolvedServerBot.botID || "").trim();
|
|
2495
2634
|
current.serverBotName = String(resolvedServerBot.name || "").trim();
|
|
2496
2635
|
current.serverRoles = ensureArray(resolvedServerBot.roles);
|
|
2636
|
+
current.serverRoleIDs = safeObject(resolvedServerBot.roleIDs);
|
|
2497
2637
|
}
|
|
2498
2638
|
}
|
|
2499
2639
|
saveTelegramBotEdit(parsed, selected, current, deps);
|
|
@@ -2576,9 +2716,12 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2576
2716
|
if (selectedEntry) {
|
|
2577
2717
|
const desiredServerBotName = String(serverBinding.name || "").trim();
|
|
2578
2718
|
const desiredServerRoles = ensureArray(serverBinding.roles);
|
|
2719
|
+
const desiredServerRoleIDs = safeObject(serverBinding.serverRoleIDs);
|
|
2579
2720
|
const currentServerRoles = ensureArray(selectedEntry.serverRoles);
|
|
2580
2721
|
const currentServerRolesJoined = currentServerRoles.join(",");
|
|
2581
2722
|
const desiredServerRolesJoined = desiredServerRoles.join(",");
|
|
2723
|
+
const currentServerRoleIDs = formatTelegramServerRoleIDs(selectedEntry.serverRoleIDs);
|
|
2724
|
+
const desiredServerRoleIDsText = formatTelegramServerRoleIDs(desiredServerRoleIDs);
|
|
2582
2725
|
const desiredServerBotID = (
|
|
2583
2726
|
String(selectedEntry.serverBotID || "").trim()
|
|
2584
2727
|
|| (serverBinding.mode === "single" ? String(serverBinding.serverBotID || "").trim() : "")
|
|
@@ -2586,6 +2729,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2586
2729
|
const needsMetadataBackfill = (
|
|
2587
2730
|
String(selectedEntry.serverBotName || "").trim() !== desiredServerBotName
|
|
2588
2731
|
|| currentServerRolesJoined !== desiredServerRolesJoined
|
|
2732
|
+
|| currentServerRoleIDs !== desiredServerRoleIDsText
|
|
2589
2733
|
|| String(selectedEntry.serverBotID || "").trim() !== desiredServerBotID
|
|
2590
2734
|
);
|
|
2591
2735
|
if (needsMetadataBackfill) {
|
|
@@ -2594,6 +2738,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2594
2738
|
serverBotID: desiredServerBotID,
|
|
2595
2739
|
serverBotName: desiredServerBotName,
|
|
2596
2740
|
serverRoles: desiredServerRoles,
|
|
2741
|
+
serverRoleIDs: desiredServerRoleIDs,
|
|
2597
2742
|
};
|
|
2598
2743
|
const nextParsed = upsertTelegramEntry(state.parsed, nextEntry);
|
|
2599
2744
|
writeProviderEnvState("telegram", nextParsed, deps);
|
|
@@ -2601,6 +2746,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2601
2746
|
envConfig.serverBotID = desiredServerBotID;
|
|
2602
2747
|
envConfig.serverBotName = desiredServerBotName;
|
|
2603
2748
|
envConfig.serverRoles = desiredServerRoles;
|
|
2749
|
+
envConfig.serverRoleIDs = desiredServerRoleIDs;
|
|
2604
2750
|
}
|
|
2605
2751
|
const rewriteEntry = needsMetadataBackfill
|
|
2606
2752
|
? {
|
|
@@ -2608,6 +2754,7 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2608
2754
|
serverBotID: desiredServerBotID,
|
|
2609
2755
|
serverBotName: desiredServerBotName,
|
|
2610
2756
|
serverRoles: desiredServerRoles,
|
|
2757
|
+
serverRoleIDs: desiredServerRoleIDs,
|
|
2611
2758
|
}
|
|
2612
2759
|
: selectedEntry;
|
|
2613
2760
|
const currentEntryFilePath = String(
|
|
@@ -2651,10 +2798,13 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2651
2798
|
ok: overallOK,
|
|
2652
2799
|
provider,
|
|
2653
2800
|
filePath: envConfig.filePath,
|
|
2801
|
+
globalFilePath: envConfig.filePath,
|
|
2802
|
+
entryFilePath: provider === "telegram" ? resolveTelegramEntryFilePath(envConfig, deps) : "",
|
|
2654
2803
|
botKey: envConfig.botKey || "",
|
|
2655
2804
|
serverBotID: envConfig.serverBotID || "",
|
|
2656
2805
|
serverBotName: envConfig.serverBotName || "",
|
|
2657
2806
|
serverRoles: ensureArray(envConfig.serverRoles),
|
|
2807
|
+
serverRoleIDs: safeObject(envConfig.serverRoleIDs),
|
|
2658
2808
|
detail: result.detail || "",
|
|
2659
2809
|
roleProfile: envConfig.roleProfile || "",
|
|
2660
2810
|
client: envConfig.client || "",
|
|
@@ -2668,34 +2818,44 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2668
2818
|
} else {
|
|
2669
2819
|
const lines = [
|
|
2670
2820
|
`${providerLabel(provider, deps)} verify: ${overallOK ? "OK" : "FAIL"}`,
|
|
2671
|
-
`
|
|
2672
|
-
provider === "telegram" ? `
|
|
2673
|
-
provider === "telegram" ? `stored_server_bot_id: ${envConfig.serverBotID || "-"}` : "",
|
|
2674
|
-
provider === "telegram" ? `stored_server_name: ${envConfig.serverBotName || "-"}` : "",
|
|
2675
|
-
provider === "telegram" ? `stored_server_roles: ${ensureArray(envConfig.serverRoles).join(", ") || "-"}` : "",
|
|
2676
|
-
provider === "telegram" ? `role_profile: ${envConfig.roleProfile || "-"}` : "",
|
|
2677
|
-
provider === "telegram" ? `ai_client: ${envConfig.client ? displayLocalAIClientName(envConfig.client) : "-"}` : "",
|
|
2678
|
-
provider === "telegram" ? `ai_model: ${envConfig.model || "-"}` : "",
|
|
2679
|
-
provider === "telegram" ? `permission_mode: ${envConfig.permissionMode || "-"}` : "",
|
|
2680
|
-
provider === "telegram" ? `reasoning_effort: ${envConfig.reasoningEffort || "-"}` : "",
|
|
2821
|
+
`global_file: ${envConfig.filePath}`,
|
|
2822
|
+
provider === "telegram" ? `entry_file: ${resolveTelegramEntryFilePath(envConfig, deps) || "-"}` : "",
|
|
2681
2823
|
`detail: ${result.detail || "-"}`,
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2824
|
+
];
|
|
2825
|
+
if (provider === "telegram") {
|
|
2826
|
+
appendTextSection(lines, "stored_local_entry", [
|
|
2827
|
+
`local_selector: ${envConfig.botKey || "-"}${envConfig.botKey ? " (advanced)" : ""}`,
|
|
2828
|
+
`stored_server_bot_id: ${envConfig.serverBotID || "-"}`,
|
|
2829
|
+
`stored_server_name: ${envConfig.serverBotName || "-"}`,
|
|
2830
|
+
`stored_server_roles: ${ensureArray(envConfig.serverRoles).join(", ") || "-"}`,
|
|
2831
|
+
`stored_server_role_ids: ${formatTelegramServerRoleIDs(envConfig.serverRoleIDs) || "-"}`,
|
|
2832
|
+
`role_profile: ${envConfig.roleProfile || "-"}`,
|
|
2833
|
+
`ai_client: ${envConfig.client ? displayLocalAIClientName(envConfig.client) : "-"}`,
|
|
2834
|
+
`ai_model: ${envConfig.model || "-"}`,
|
|
2835
|
+
`permission_mode: ${envConfig.permissionMode || "-"}`,
|
|
2836
|
+
`reasoning_effort: ${envConfig.reasoningEffort || "-"}`,
|
|
2837
|
+
]);
|
|
2838
|
+
}
|
|
2685
2839
|
if (provider === "telegram" && serverBinding) {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2840
|
+
const liveLines = [
|
|
2841
|
+
`status: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}`,
|
|
2842
|
+
`binding_mode: ${serverBinding.mode || "-"}`,
|
|
2843
|
+
`live_server_name: ${serverBinding.name || "-"}`,
|
|
2844
|
+
`live_server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}`,
|
|
2845
|
+
`live_server_role_ids: ${formatTelegramServerRoleIDs(serverBinding.serverRoleIDs) || "-"}`,
|
|
2846
|
+
];
|
|
2689
2847
|
if (serverBinding.mode === "group") {
|
|
2690
2848
|
const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
|
|
2691
2849
|
Object.keys(groupedProfiles).forEach((role) => {
|
|
2692
|
-
|
|
2850
|
+
liveLines.push(`runtime_role_profile[${role}]: ${formatRoleProfileOutputLine(groupedProfiles[role])}`);
|
|
2693
2851
|
});
|
|
2694
2852
|
} else if (serverBinding.effectiveRoleProfile) {
|
|
2695
|
-
|
|
2853
|
+
liveLines.push(`runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}`);
|
|
2696
2854
|
}
|
|
2855
|
+
appendTextSection(lines, "live_server_binding", liveLines);
|
|
2697
2856
|
}
|
|
2698
2857
|
if (provider === "telegram" && routeLinks) {
|
|
2858
|
+
const routeSection = [`count: ${intFromRaw(routeLinks.total, 0)}`];
|
|
2699
2859
|
ensureArray(routeLinks.routes).forEach((route) => {
|
|
2700
2860
|
const routeRuntime = safeObject(route.runtimeRoleProfile);
|
|
2701
2861
|
const routeDetail = [
|
|
@@ -2703,8 +2863,9 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
2703
2863
|
route.routeRole ? `route_role=${String(route.routeRole || "").trim()}` : "",
|
|
2704
2864
|
Object.keys(routeRuntime).length ? `runtime=${formatRoleProfileOutputLine(routeRuntime)}` : "",
|
|
2705
2865
|
].filter(Boolean).join(" ");
|
|
2706
|
-
|
|
2866
|
+
routeSection.push(`${String(route.routeName || "-")}: ${routeDetail || "-"}`);
|
|
2707
2867
|
});
|
|
2868
|
+
appendTextSection(lines, "linked_runner_routes", routeSection);
|
|
2708
2869
|
}
|
|
2709
2870
|
process.stdout.write(`${lines.join("\n")}\n`);
|
|
2710
2871
|
}
|
|
@@ -289,11 +289,22 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
289
289
|
const baseURL = `http://127.0.0.1:${mock.port}`;
|
|
290
290
|
const telegramApiBaseURL = `${baseURL}/telegram`;
|
|
291
291
|
const telegramBotEntriesDir = path.join(tempHome, ".metheus", "telegram-bots");
|
|
292
|
-
const telegramEnvPath = path.join(telegramBotEntriesDir, "
|
|
292
|
+
const telegramEnvPath = path.join(telegramBotEntriesDir, "global.env");
|
|
293
293
|
const readTelegramGlobals = () => parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
294
|
-
const readTelegramBotEntry = (
|
|
295
|
-
const
|
|
296
|
-
|
|
294
|
+
const readTelegramBotEntry = (selector) => {
|
|
295
|
+
const requested = String(selector || "").trim().toLowerCase();
|
|
296
|
+
const fileCandidates = fs.readdirSync(telegramBotEntriesDir, { withFileTypes: true })
|
|
297
|
+
.filter((item) => item.isFile() && /\.env$/i.test(item.name) && item.name.toLowerCase() !== "global.env")
|
|
298
|
+
.map((item) => path.join(telegramBotEntriesDir, item.name));
|
|
299
|
+
const filePath = fileCandidates.find((candidatePath) => {
|
|
300
|
+
const stem = path.basename(candidatePath, path.extname(candidatePath)).toLowerCase();
|
|
301
|
+
if (stem === requested) return true;
|
|
302
|
+
const parsedCandidate = parseSimpleEnvText(fs.readFileSync(candidatePath, "utf8"));
|
|
303
|
+
const serverName = String(parsedCandidate.TELEGRAM_BOT_SERVER_NAME || "").trim().toLowerCase();
|
|
304
|
+
const username = String(parsedCandidate.TELEGRAM_BOT_USERNAME || "").trim().toLowerCase();
|
|
305
|
+
return serverName === requested || username === requested;
|
|
306
|
+
});
|
|
307
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
297
308
|
return {};
|
|
298
309
|
}
|
|
299
310
|
return parseSimpleEnvText(fs.readFileSync(filePath, "utf8"));
|
|
@@ -38,6 +38,7 @@ export function buildSetupOutputLines(summary, deps) {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
lines.push("Telegram bots: per-bot files are created later by bot add / bot verify after server bot identity is known.");
|
|
41
42
|
if (runnerTemplate?.error) {
|
|
42
43
|
lines.push(`Runner: template unavailable (${runnerTemplate.error})`);
|
|
43
44
|
} else {
|