metheus-governance-mcp-cli 0.2.63 → 0.2.65
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 +14 -2
- package/cli.mjs +82 -2
- package/lib/bot-commands.mjs +364 -36
- package/lib/selftest-bot-commands.mjs +149 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -213,8 +213,11 @@ Direct commands:
|
|
|
213
213
|
metheus-governance-mcp-cli bot list
|
|
214
214
|
metheus-governance-mcp-cli bot show --provider telegram --bot-key main
|
|
215
215
|
metheus-governance-mcp-cli bot add --provider telegram
|
|
216
|
+
metheus-governance-mcp-cli bot edit
|
|
216
217
|
metheus-governance-mcp-cli bot edit --provider telegram
|
|
217
218
|
metheus-governance-mcp-cli bot remove --provider telegram
|
|
219
|
+
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key main
|
|
220
|
+
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key main
|
|
218
221
|
metheus-governance-mcp-cli bot global --provider telegram
|
|
219
222
|
metheus-governance-mcp-cli bot verify --provider telegram --bot-key main
|
|
220
223
|
```
|
|
@@ -222,6 +225,8 @@ metheus-governance-mcp-cli bot verify --provider telegram --bot-key main
|
|
|
222
225
|
Behavior:
|
|
223
226
|
|
|
224
227
|
- `bot setup` asks for `Telegram / Slack / KakaoTalk` first, then prompts with numbered actions.
|
|
228
|
+
- `bot add` without flags starts a guided question flow: provider -> server bot -> local bot key -> token -> verify -> role/AI binding -> default bot choice.
|
|
229
|
+
- `bot edit` without flags starts a guided numbered flow: provider -> bot entry -> guided edit -> step-by-step field choices.
|
|
225
230
|
- Telegram supports named local bot entries with:
|
|
226
231
|
- `SERVER_BOT_ID`
|
|
227
232
|
- `USERNAME`
|
|
@@ -235,13 +240,17 @@ Behavior:
|
|
|
235
240
|
- `bot verify` checks the configured local token and prints the current AI binding summary.
|
|
236
241
|
- `bot show` prints one local bot entry in detail.
|
|
237
242
|
- `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and default bot key.
|
|
243
|
+
- `bot set-default` updates `TELEGRAM_DEFAULT_BOT_KEY`.
|
|
244
|
+
- `bot migrate` moves legacy `TELEGRAM_BOT_TOKEN` into a named Telegram bot entry.
|
|
238
245
|
|
|
239
246
|
Non-interactive examples:
|
|
240
247
|
|
|
241
248
|
```bash
|
|
242
249
|
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
|
|
243
|
-
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --bot-key main --bot-id <server_bot_uuid> --token <telegram_bot_token> --role-profile monitor --client codex --permission-mode read_only --reasoning-effort low --default true
|
|
244
|
-
metheus-governance-mcp-cli bot edit --provider telegram --bot-key main --non-interactive true --client claude --model claude-
|
|
250
|
+
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --bot-key main --server-bot-id <server_bot_uuid> --username <telegram_username> --token <telegram_bot_token> --role-profile monitor --ai-client codex --ai-model gpt-5-codex --ai-permission-mode read_only --ai-reasoning-effort low --default true
|
|
251
|
+
metheus-governance-mcp-cli bot edit --provider telegram --bot-key main --non-interactive true --ai-client claude --ai-model claude-sonnet --ai-permission-mode workspace_write --ai-reasoning-effort medium
|
|
252
|
+
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key main --non-interactive true
|
|
253
|
+
metheus-governance-mcp-cli bot migrate --provider telegram --bot-key main --server-bot-id <server_bot_uuid> --bot-name <telegram_username> --role-profile monitor --ai-client codex --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
|
|
245
254
|
metheus-governance-mcp-cli bot remove --provider telegram --bot-key main --non-interactive true
|
|
246
255
|
metheus-governance-mcp-cli bot verify --provider telegram --bot-key main --json true
|
|
247
256
|
```
|
|
@@ -276,6 +285,9 @@ Checks:
|
|
|
276
285
|
- role profile -> local CLI availability
|
|
277
286
|
- route dry-run / trigger-policy safety warnings
|
|
278
287
|
- local provider token presence for active project destinations
|
|
288
|
+
- enabled runner route -> local bot env binding resolution
|
|
289
|
+
- enabled runner route -> server bot UUID cross-check
|
|
290
|
+
- legacy `TELEGRAM_BOT_TOKEN` fallback still in use
|
|
279
291
|
- codex/claude/gemini/antigravity/cursor registration state
|
|
280
292
|
- gateway `tools/list` reachability
|
|
281
293
|
- `project.summary` access
|
package/cli.mjs
CHANGED
|
@@ -240,6 +240,8 @@ function printUsage() {
|
|
|
240
240
|
` ${cmd} bot add [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>]`,
|
|
241
241
|
` ${cmd} bot edit [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>]`,
|
|
242
242
|
` ${cmd} bot remove [--provider <telegram|slack|kakaotalk>]`,
|
|
243
|
+
` ${cmd} bot set-default --provider telegram [--bot-key <key>]`,
|
|
244
|
+
` ${cmd} bot migrate --provider telegram [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>]`,
|
|
243
245
|
` ${cmd} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--timeout-seconds <n>] [--json <true|false>]`,
|
|
244
246
|
` ${cmd} bot global --provider telegram [--api-base-url <url>] [--auto-clear-webhook <true|false>] [--allowed-updates <csv>] [--default-bot-key <key>]`,
|
|
245
247
|
` ${cmd} doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>] [--strict <true|false>]`,
|
|
@@ -3527,6 +3529,8 @@ async function runDoctor(flags) {
|
|
|
3527
3529
|
includeDrafts: true,
|
|
3528
3530
|
}, { defaultBaseURL: DEFAULT_BASE_URL });
|
|
3529
3531
|
const rows = [];
|
|
3532
|
+
let runnerConfig = null;
|
|
3533
|
+
let enabledRoutes = [];
|
|
3530
3534
|
const providerTemplates = ensureAllProviderEnvTemplates();
|
|
3531
3535
|
const runnerTemplate = ensureBotRunnerConfigTemplate();
|
|
3532
3536
|
for (const provider of PROVIDER_ENV_ORDER) {
|
|
@@ -3541,9 +3545,9 @@ async function runDoctor(flags) {
|
|
|
3541
3545
|
if (runnerTemplate.error) {
|
|
3542
3546
|
addDoctorCheck(rows, "warn", "local bot runner config", `${runnerTemplate.error} (${runnerTemplate.filePath})`);
|
|
3543
3547
|
} else {
|
|
3544
|
-
|
|
3548
|
+
runnerConfig = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
3545
3549
|
const runnerState = loadBotRunnerState();
|
|
3546
|
-
|
|
3550
|
+
enabledRoutes = ensureArray(runnerConfig.routes).filter((routeRaw) => safeObject(routeRaw).enabled);
|
|
3547
3551
|
if (!enabledRoutes.length) {
|
|
3548
3552
|
addDoctorCheck(rows, "warn", "local bot runner config", `template ready (${runnerTemplate.filePath}), no enabled routes`);
|
|
3549
3553
|
} else {
|
|
@@ -3662,6 +3666,82 @@ async function runDoctor(flags) {
|
|
|
3662
3666
|
deps: buildDoctorChecksDeps(),
|
|
3663
3667
|
});
|
|
3664
3668
|
}
|
|
3669
|
+
|
|
3670
|
+
if (runnerConfig && enabledRoutes.length > 0) {
|
|
3671
|
+
let serverBots = null;
|
|
3672
|
+
try {
|
|
3673
|
+
serverBots = await listUserBotsForRunner({
|
|
3674
|
+
siteBaseURL: normalizeSiteBaseURL(context.baseURL),
|
|
3675
|
+
token,
|
|
3676
|
+
timeoutSeconds,
|
|
3677
|
+
}, buildRunnerDataDeps());
|
|
3678
|
+
} catch {
|
|
3679
|
+
serverBots = null;
|
|
3680
|
+
}
|
|
3681
|
+
for (const routeRaw of enabledRoutes) {
|
|
3682
|
+
const route = normalizeRunnerRoute(routeRaw);
|
|
3683
|
+
const routeLabel = route.name || route.projectID || route.provider;
|
|
3684
|
+
const envConfig = loadProviderEnvConfig(route.provider, {
|
|
3685
|
+
botID: route.botID,
|
|
3686
|
+
botName: route.botName,
|
|
3687
|
+
route,
|
|
3688
|
+
});
|
|
3689
|
+
if (!envConfig.ok) {
|
|
3690
|
+
addDoctorCheck(
|
|
3691
|
+
rows,
|
|
3692
|
+
"fail",
|
|
3693
|
+
`runner route ${routeLabel} binding`,
|
|
3694
|
+
`${providerEnvConfig(route.provider).label} local bot env is unresolved (${envConfig.error})`,
|
|
3695
|
+
);
|
|
3696
|
+
continue;
|
|
3697
|
+
}
|
|
3698
|
+
const detailParts = [];
|
|
3699
|
+
if (envConfig.botKey) detailParts.push(`bot=${envConfig.botKey}`);
|
|
3700
|
+
if (envConfig.botUsername) detailParts.push(`@${envConfig.botUsername}`);
|
|
3701
|
+
if (envConfig.serverBotID) detailParts.push(`server_bot_id=${envConfig.serverBotID}`);
|
|
3702
|
+
if (envConfig.client) detailParts.push(`ai=${envConfig.client}`);
|
|
3703
|
+
addDoctorCheck(
|
|
3704
|
+
rows,
|
|
3705
|
+
"ok",
|
|
3706
|
+
`runner route ${routeLabel} binding`,
|
|
3707
|
+
detailParts.join(", ") || "resolved",
|
|
3708
|
+
);
|
|
3709
|
+
if (route.botID && envConfig.serverBotID && route.botID !== envConfig.serverBotID) {
|
|
3710
|
+
addDoctorCheck(
|
|
3711
|
+
rows,
|
|
3712
|
+
strictMode ? "fail" : "warn",
|
|
3713
|
+
`runner route ${routeLabel} server_bot_id`,
|
|
3714
|
+
`route bot_id ${route.botID} != local env ${envConfig.serverBotID}`,
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
if (route.botID && !envConfig.serverBotID) {
|
|
3718
|
+
addDoctorCheck(
|
|
3719
|
+
rows,
|
|
3720
|
+
strictMode ? "fail" : "warn",
|
|
3721
|
+
`runner route ${routeLabel} server_bot_id`,
|
|
3722
|
+
`route bot_id ${route.botID} is set, but local env entry does not carry SERVER_BOT_ID`,
|
|
3723
|
+
);
|
|
3724
|
+
}
|
|
3725
|
+
if (serverBots && route.botID) {
|
|
3726
|
+
const match = ensureArray(serverBots).find((bot) => String(bot.id || "").trim() === String(route.botID || "").trim());
|
|
3727
|
+
addDoctorCheck(
|
|
3728
|
+
rows,
|
|
3729
|
+
match ? "ok" : (strictMode ? "fail" : "warn"),
|
|
3730
|
+
`runner route ${routeLabel} server bot`,
|
|
3731
|
+
match ? `${match.name || "(unnamed)"} [${match.role || "-"}]` : `server bot ${route.botID} not found`,
|
|
3732
|
+
);
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
const telegramDefaultConfig = loadProviderEnvConfig("telegram");
|
|
3736
|
+
if (telegramDefaultConfig.ok && !telegramDefaultConfig.botKey && String(telegramDefaultConfig.token || "").trim()) {
|
|
3737
|
+
addDoctorCheck(
|
|
3738
|
+
rows,
|
|
3739
|
+
"warn",
|
|
3740
|
+
"telegram legacy token",
|
|
3741
|
+
"TELEGRAM_BOT_TOKEN fallback is still active; run bot migrate --provider telegram to move to named entries",
|
|
3742
|
+
);
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3665
3745
|
}
|
|
3666
3746
|
|
|
3667
3747
|
for (const cliBin of MCP_CLIENTS) {
|
package/lib/bot-commands.mjs
CHANGED
|
@@ -43,6 +43,35 @@ function firstNonEmptyString(values) {
|
|
|
43
43
|
return "";
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function hasOwnFlag(flags, key) {
|
|
47
|
+
return Object.prototype.hasOwnProperty.call(safeObject(flags), key);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getServerBotIDFlag(flags) {
|
|
51
|
+
const parsedFlags = safeObject(flags);
|
|
52
|
+
return firstNonEmptyString([parsedFlags["server-bot-id"], parsedFlags["bot-id"]]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getAIClientFlag(flags) {
|
|
56
|
+
const parsedFlags = safeObject(flags);
|
|
57
|
+
return firstNonEmptyString([parsedFlags["ai-client"], parsedFlags.client]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getAIModelFlag(flags) {
|
|
61
|
+
const parsedFlags = safeObject(flags);
|
|
62
|
+
return firstNonEmptyString([parsedFlags["ai-model"], parsedFlags.model]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getAIPermissionModeFlag(flags) {
|
|
66
|
+
const parsedFlags = safeObject(flags);
|
|
67
|
+
return firstNonEmptyString([parsedFlags["ai-permission-mode"], parsedFlags["permission-mode"]]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getAIReasoningEffortFlag(flags) {
|
|
71
|
+
const parsedFlags = safeObject(flags);
|
|
72
|
+
return firstNonEmptyString([parsedFlags["ai-reasoning-effort"], parsedFlags["reasoning-effort"]]);
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
function maskSecret(rawValue) {
|
|
47
76
|
const text = String(rawValue || "").trim();
|
|
48
77
|
if (!text) return "";
|
|
@@ -76,9 +105,11 @@ function printBotUsage(deps) {
|
|
|
76
105
|
` ${cliName} bot setup`,
|
|
77
106
|
` ${cliName} bot list [--provider <telegram|slack|kakaotalk>] [--json <true|false>]`,
|
|
78
107
|
` ${cliName} bot show [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>] [--json <true|false>]`,
|
|
79
|
-
` ${cliName} bot add [--provider <telegram|slack|kakaotalk>] [--non-interactive <true|false>] [--
|
|
80
|
-
` ${cliName} bot edit [--provider <telegram|slack|kakaotalk>] [--non-interactive <true|false>] [--bot-key <key>]`,
|
|
108
|
+
` ${cliName} bot add [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>] [--non-interactive <true|false>] [--bot-key <key>] [--server-bot-id <uuid>] [--username <name>] [--token <token>] [--role-profile <name>] [--ai-client <name>] [--ai-model <name>] [--ai-permission-mode <mode>] [--ai-reasoning-effort <level>] [--default <true|false>] [--verify <true|false>]`,
|
|
109
|
+
` ${cliName} bot edit [--provider <telegram|slack|kakaotalk>] [--base-url <url>] [--timeout-seconds <n>] [--non-interactive <true|false>] [--bot-key <key>] [--server-bot-id <uuid>] [--username <name>] [--token <token>] [--role-profile <name>] [--ai-client <name>] [--ai-model <name>] [--ai-permission-mode <mode>] [--ai-reasoning-effort <level>]`,
|
|
81
110
|
` ${cliName} bot remove [--provider <telegram|slack|kakaotalk>] [--non-interactive <true|false>] [--bot-key <key>]`,
|
|
111
|
+
` ${cliName} bot set-default --provider telegram [--bot-key <key>] [--non-interactive <true|false>]`,
|
|
112
|
+
` ${cliName} bot migrate --provider telegram [--bot-key <key>] [--bot-id <uuid>] [--bot-name <name>] [--keep-legacy-token <true|false>] [--non-interactive <true|false>]`,
|
|
82
113
|
` ${cliName} bot verify [--provider <telegram|slack|kakaotalk>] [--bot-key <key>] [--timeout-seconds <n>] [--json <true|false>]`,
|
|
83
114
|
` ${cliName} bot global --provider telegram [--non-interactive <true|false>] [--api-base-url <url>] [--auto-clear-webhook <true|false>] [--allowed-updates <csv>] [--default-bot-key <key>]`,
|
|
84
115
|
"",
|
|
@@ -87,6 +118,27 @@ function printBotUsage(deps) {
|
|
|
87
118
|
}
|
|
88
119
|
|
|
89
120
|
function createPrompter() {
|
|
121
|
+
const scriptedPromptAnswersRaw = String(process.env.METHEUS_SCRIPTED_PROMPT_ANSWERS || "").trim();
|
|
122
|
+
if (scriptedPromptAnswersRaw) {
|
|
123
|
+
let scriptedAnswers = [];
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(scriptedPromptAnswersRaw);
|
|
126
|
+
scriptedAnswers = ensureArray(parsed).map((value) => String(value ?? ""));
|
|
127
|
+
} catch {
|
|
128
|
+
scriptedAnswers = scriptedPromptAnswersRaw.split(/\r?\n/).map((value) => String(value ?? ""));
|
|
129
|
+
}
|
|
130
|
+
let answerIndex = 0;
|
|
131
|
+
return {
|
|
132
|
+
ask(promptText) {
|
|
133
|
+
process.stdout.write(promptText);
|
|
134
|
+
const answer = answerIndex < scriptedAnswers.length ? scriptedAnswers[answerIndex] : "";
|
|
135
|
+
answerIndex += 1;
|
|
136
|
+
process.stdout.write(`${answer}\n`);
|
|
137
|
+
return Promise.resolve(String(answer || ""));
|
|
138
|
+
},
|
|
139
|
+
close() {},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
90
142
|
return {
|
|
91
143
|
ask(promptText) {
|
|
92
144
|
return new Promise((resolve) => {
|
|
@@ -161,6 +213,19 @@ async function promptChoice(ui, title, options, { defaultIndex = 0, allowCancel
|
|
|
161
213
|
}
|
|
162
214
|
}
|
|
163
215
|
|
|
216
|
+
async function promptKeepChangeClear(ui, title, { allowClear = true, defaultValue = "keep" } = {}) {
|
|
217
|
+
const options = [
|
|
218
|
+
{ value: "keep", label: "Keep current value" },
|
|
219
|
+
{ value: "change", label: "Change value" },
|
|
220
|
+
];
|
|
221
|
+
if (allowClear) {
|
|
222
|
+
options.push({ value: "clear", label: "Clear value" });
|
|
223
|
+
}
|
|
224
|
+
const defaultIndex = Math.max(0, options.findIndex((option) => option.value === defaultValue));
|
|
225
|
+
const selected = await promptChoice(ui, title, options, { defaultIndex });
|
|
226
|
+
return selected?.value || "keep";
|
|
227
|
+
}
|
|
228
|
+
|
|
164
229
|
function loadProviderEnvState(provider, deps) {
|
|
165
230
|
const ensureTemplate = requireDependency(deps, "ensureProviderEnvTemplate");
|
|
166
231
|
const filePathResolver = requireDependency(deps, "providerEnvFilePath");
|
|
@@ -201,6 +266,10 @@ function removeTelegramEntry(parsedEnv, botKey) {
|
|
|
201
266
|
return parsed;
|
|
202
267
|
}
|
|
203
268
|
|
|
269
|
+
function hasNamedTelegramEntry(parsedEnv, deps) {
|
|
270
|
+
return telegramEntriesForDisplay(parsedEnv, deps).length > 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
204
273
|
function upsertTelegramEntry(parsedEnv, entry) {
|
|
205
274
|
const parsed = removeTelegramEntry(parsedEnv, entry.key);
|
|
206
275
|
const keys = telegramEntryEnvKeys(entry.key);
|
|
@@ -393,6 +462,175 @@ function findTelegramEntryByFlags(parsedEnv, flags, deps) {
|
|
|
393
462
|
return null;
|
|
394
463
|
}
|
|
395
464
|
|
|
465
|
+
function saveTelegramBotEdit(parsed, selected, current, deps) {
|
|
466
|
+
let nextParsed = parsed;
|
|
467
|
+
if (current.key !== selected.key) {
|
|
468
|
+
nextParsed = removeTelegramEntry(nextParsed, selected.key);
|
|
469
|
+
}
|
|
470
|
+
nextParsed = upsertTelegramEntry(nextParsed, current);
|
|
471
|
+
if (String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() === selected.key) {
|
|
472
|
+
nextParsed.TELEGRAM_DEFAULT_BOT_KEY = current.key;
|
|
473
|
+
}
|
|
474
|
+
const filePath = writeProviderEnvState("telegram", nextParsed, deps);
|
|
475
|
+
process.stdout.write(`Saved Telegram bot entry "${current.key}" to ${filePath}\n`);
|
|
476
|
+
return filePath;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps) {
|
|
480
|
+
const bindingAction = await promptKeepChangeClear(ui, "Server bot binding", {
|
|
481
|
+
allowClear: true,
|
|
482
|
+
defaultValue: current.serverBotID ? "keep" : "change",
|
|
483
|
+
});
|
|
484
|
+
if (bindingAction === "change") {
|
|
485
|
+
const serverBot = await chooseServerBot(
|
|
486
|
+
ui,
|
|
487
|
+
"telegram",
|
|
488
|
+
flags["base-url"] || deps.defaultSiteURL,
|
|
489
|
+
intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
490
|
+
deps,
|
|
491
|
+
);
|
|
492
|
+
current.serverBotID = serverBot.botID;
|
|
493
|
+
if (!current.roleProfile && serverBot.role) {
|
|
494
|
+
current.roleProfile = serverBot.role;
|
|
495
|
+
}
|
|
496
|
+
} else if (bindingAction === "clear") {
|
|
497
|
+
current.serverBotID = "";
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const usernameAction = await promptKeepChangeClear(ui, "Telegram username", {
|
|
501
|
+
allowClear: true,
|
|
502
|
+
defaultValue: current.username ? "keep" : "change",
|
|
503
|
+
});
|
|
504
|
+
if (usernameAction === "change") {
|
|
505
|
+
current.username = requireDependency(deps, "normalizeTelegramBotUsername")(
|
|
506
|
+
await promptRequiredLine(ui, "Telegram username (without @)", current.username),
|
|
507
|
+
"",
|
|
508
|
+
);
|
|
509
|
+
} else if (usernameAction === "clear") {
|
|
510
|
+
current.username = "";
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const tokenAction = await promptKeepChangeClear(ui, "Telegram token", {
|
|
514
|
+
allowClear: false,
|
|
515
|
+
defaultValue: "keep",
|
|
516
|
+
});
|
|
517
|
+
if (tokenAction === "change") {
|
|
518
|
+
current.token = await promptRequiredLine(ui, "Telegram bot token", current.token);
|
|
519
|
+
if (await promptYesNo(ui, "Verify updated token now?", true)) {
|
|
520
|
+
const verifyResult = await verifyTelegramTokenCandidate(
|
|
521
|
+
"telegram",
|
|
522
|
+
current.token,
|
|
523
|
+
String(parsed.TELEGRAM_API_BASE_URL || "").trim(),
|
|
524
|
+
intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
525
|
+
deps,
|
|
526
|
+
);
|
|
527
|
+
process.stdout.write(`Verify: ${verifyResult.ok ? "OK" : "FAIL"}${verifyResult.detail ? ` - ${verifyResult.detail}` : ""}\n`);
|
|
528
|
+
const maybeUsername = extractVerifiedTelegramUsername(verifyResult.detail);
|
|
529
|
+
if (verifyResult.ok && maybeUsername && !current.username) {
|
|
530
|
+
current.username = maybeUsername;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const roleAction = await promptKeepChangeClear(ui, "Role profile", {
|
|
536
|
+
allowClear: true,
|
|
537
|
+
defaultValue: current.roleProfile ? "keep" : "change",
|
|
538
|
+
});
|
|
539
|
+
if (roleAction === "change") {
|
|
540
|
+
current.roleProfile = requireDependency(deps, "normalizeRunnerRoleProfileName")(
|
|
541
|
+
await promptTelegramRoleProfile(ui, deps, current.roleProfile),
|
|
542
|
+
);
|
|
543
|
+
} else if (roleAction === "clear") {
|
|
544
|
+
current.roleProfile = "";
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const clientAction = await promptKeepChangeClear(ui, "AI client", {
|
|
548
|
+
allowClear: true,
|
|
549
|
+
defaultValue: current.client ? "keep" : "change",
|
|
550
|
+
});
|
|
551
|
+
if (clientAction === "change") {
|
|
552
|
+
current.client = requireDependency(deps, "normalizeLocalAIClientName")(
|
|
553
|
+
await promptAIClient(ui, deps, current.client),
|
|
554
|
+
"",
|
|
555
|
+
);
|
|
556
|
+
} else if (clientAction === "clear") {
|
|
557
|
+
current.client = "";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const modelAction = await promptKeepChangeClear(ui, "AI model", {
|
|
561
|
+
allowClear: true,
|
|
562
|
+
defaultValue: current.model ? "keep" : "change",
|
|
563
|
+
});
|
|
564
|
+
if (modelAction === "change") {
|
|
565
|
+
current.model = await promptLine(ui, "AI model", current.model);
|
|
566
|
+
} else if (modelAction === "clear") {
|
|
567
|
+
current.model = "";
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const permissionAction = await promptKeepChangeClear(ui, "AI permission mode", {
|
|
571
|
+
allowClear: true,
|
|
572
|
+
defaultValue: current.permissionMode ? "keep" : "change",
|
|
573
|
+
});
|
|
574
|
+
if (permissionAction === "change") {
|
|
575
|
+
current.permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(
|
|
576
|
+
await promptPermissionMode(ui, current.permissionMode),
|
|
577
|
+
"",
|
|
578
|
+
);
|
|
579
|
+
} else if (permissionAction === "clear") {
|
|
580
|
+
current.permissionMode = "";
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const reasoningAction = await promptKeepChangeClear(ui, "AI reasoning effort", {
|
|
584
|
+
allowClear: true,
|
|
585
|
+
defaultValue: current.reasoningEffort ? "keep" : "change",
|
|
586
|
+
});
|
|
587
|
+
if (reasoningAction === "change") {
|
|
588
|
+
current.reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(
|
|
589
|
+
await promptReasoningEffort(ui, current.reasoningEffort),
|
|
590
|
+
"",
|
|
591
|
+
);
|
|
592
|
+
} else if (reasoningAction === "clear") {
|
|
593
|
+
current.reasoningEffort = "";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const botKeyAction = await promptKeepChangeClear(ui, "Local bot key", {
|
|
597
|
+
allowClear: false,
|
|
598
|
+
defaultValue: "keep",
|
|
599
|
+
});
|
|
600
|
+
if (botKeyAction === "change") {
|
|
601
|
+
const normalizeBotKey = requireDependency(deps, "normalizeTelegramBotEnvKey");
|
|
602
|
+
let nextKey = normalizeBotKey(await promptRequiredLine(ui, "Local bot key", current.key), current.key);
|
|
603
|
+
const existing = new Set(telegramEntriesForDisplay(parsed, deps).map((entry) => entry.key).filter((key) => key !== current.key));
|
|
604
|
+
while (existing.has(nextKey)) {
|
|
605
|
+
process.stdout.write(`Telegram bot key "${nextKey}" already exists.\n`);
|
|
606
|
+
nextKey = normalizeBotKey(await promptRequiredLine(ui, "Local bot key", `${nextKey}_2`), `${nextKey}_2`);
|
|
607
|
+
}
|
|
608
|
+
current.key = nextKey;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const defaultChoice = await promptChoice(
|
|
612
|
+
ui,
|
|
613
|
+
"Default Telegram bot setting",
|
|
614
|
+
[
|
|
615
|
+
{ value: "keep", label: "Keep current default setting", description: String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() === selected.key ? "currently default" : "not default" },
|
|
616
|
+
{ value: "set", label: "Set this bot as default" },
|
|
617
|
+
{ value: "unset", label: "Do not make this bot default" },
|
|
618
|
+
],
|
|
619
|
+
{ defaultIndex: 0 },
|
|
620
|
+
);
|
|
621
|
+
if (defaultChoice?.value === "set") {
|
|
622
|
+
parsed.TELEGRAM_DEFAULT_BOT_KEY = current.key;
|
|
623
|
+
} else if (defaultChoice?.value === "unset" && String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() === selected.key) {
|
|
624
|
+
parsed.TELEGRAM_DEFAULT_BOT_KEY = "";
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (!await promptYesNo(ui, `Save changes for "${current.key}" now?`, true)) {
|
|
628
|
+
process.stdout.write("Cancelled.\n");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
saveTelegramBotEdit(parsed, selected, current, deps);
|
|
632
|
+
}
|
|
633
|
+
|
|
396
634
|
async function selectProvider(ui, initialProvider, deps) {
|
|
397
635
|
const normalizeProvider = requireDependency(deps, "normalizeBotProvider");
|
|
398
636
|
const provider = String(initialProvider || "").trim();
|
|
@@ -702,7 +940,7 @@ async function addTelegramBot(ui, flags, deps) {
|
|
|
702
940
|
const nonInteractive = boolFromRaw(flags["non-interactive"] ?? flags.yes, false);
|
|
703
941
|
const serverBot = nonInteractive
|
|
704
942
|
? {
|
|
705
|
-
botID:
|
|
943
|
+
botID: getServerBotIDFlag(flags),
|
|
706
944
|
role: String(flags.role || "").trim(),
|
|
707
945
|
name: "",
|
|
708
946
|
}
|
|
@@ -777,27 +1015,27 @@ async function addTelegramBot(ui, flags, deps) {
|
|
|
777
1015
|
);
|
|
778
1016
|
const client = requireDependency(deps, "normalizeLocalAIClientName")(
|
|
779
1017
|
nonInteractive
|
|
780
|
-
?
|
|
781
|
-
: await promptAIClient(ui, deps, flags
|
|
1018
|
+
? getAIClientFlag(flags)
|
|
1019
|
+
: await promptAIClient(ui, deps, getAIClientFlag(flags)),
|
|
782
1020
|
"",
|
|
783
1021
|
);
|
|
784
|
-
const model = nonInteractive ?
|
|
1022
|
+
const model = nonInteractive ? getAIModelFlag(flags) : await promptLine(ui, "AI model", getAIModelFlag(flags));
|
|
785
1023
|
const permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(
|
|
786
1024
|
nonInteractive
|
|
787
|
-
?
|
|
788
|
-
: await promptPermissionMode(ui,
|
|
1025
|
+
? getAIPermissionModeFlag(flags)
|
|
1026
|
+
: await promptPermissionMode(ui, getAIPermissionModeFlag(flags)),
|
|
789
1027
|
"",
|
|
790
1028
|
);
|
|
791
1029
|
const reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(
|
|
792
1030
|
nonInteractive
|
|
793
|
-
?
|
|
794
|
-
: await promptReasoningEffort(ui,
|
|
1031
|
+
? getAIReasoningEffortFlag(flags)
|
|
1032
|
+
: await promptReasoningEffort(ui, getAIReasoningEffortFlag(flags)),
|
|
795
1033
|
"",
|
|
796
1034
|
);
|
|
797
1035
|
|
|
798
1036
|
const nextParsed = upsertTelegramEntry(parsed, {
|
|
799
1037
|
key: botKey,
|
|
800
|
-
serverBotID: String(flags
|
|
1038
|
+
serverBotID: String(getServerBotIDFlag(flags) || serverBot.botID || "").trim(),
|
|
801
1039
|
username,
|
|
802
1040
|
token,
|
|
803
1041
|
roleProfile,
|
|
@@ -882,8 +1120,8 @@ async function editTelegramBot(ui, flags, deps) {
|
|
|
882
1120
|
}
|
|
883
1121
|
let current = { ...selected };
|
|
884
1122
|
if (nonInteractive) {
|
|
885
|
-
if (flags
|
|
886
|
-
current.serverBotID =
|
|
1123
|
+
if (getServerBotIDFlag(flags)) {
|
|
1124
|
+
current.serverBotID = getServerBotIDFlag(flags);
|
|
887
1125
|
}
|
|
888
1126
|
if (Object.prototype.hasOwnProperty.call(flags, "username")) {
|
|
889
1127
|
current.username = requireDependency(deps, "normalizeTelegramBotUsername")(flags.username || "");
|
|
@@ -894,25 +1132,35 @@ async function editTelegramBot(ui, flags, deps) {
|
|
|
894
1132
|
if (Object.prototype.hasOwnProperty.call(flags, "role-profile")) {
|
|
895
1133
|
current.roleProfile = requireDependency(deps, "normalizeRunnerRoleProfileName")(flags["role-profile"] || "");
|
|
896
1134
|
}
|
|
897
|
-
if (
|
|
898
|
-
current.client = requireDependency(deps, "normalizeLocalAIClientName")(flags
|
|
1135
|
+
if (hasOwnFlag(flags, "client") || hasOwnFlag(flags, "ai-client")) {
|
|
1136
|
+
current.client = requireDependency(deps, "normalizeLocalAIClientName")(getAIClientFlag(flags), "");
|
|
899
1137
|
}
|
|
900
|
-
if (
|
|
901
|
-
current.model =
|
|
1138
|
+
if (hasOwnFlag(flags, "model") || hasOwnFlag(flags, "ai-model")) {
|
|
1139
|
+
current.model = getAIModelFlag(flags);
|
|
902
1140
|
}
|
|
903
|
-
if (
|
|
904
|
-
current.permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(flags
|
|
1141
|
+
if (hasOwnFlag(flags, "permission-mode") || hasOwnFlag(flags, "ai-permission-mode")) {
|
|
1142
|
+
current.permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(getAIPermissionModeFlag(flags), "");
|
|
905
1143
|
}
|
|
906
|
-
if (
|
|
907
|
-
current.reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(flags
|
|
1144
|
+
if (hasOwnFlag(flags, "reasoning-effort") || hasOwnFlag(flags, "ai-reasoning-effort")) {
|
|
1145
|
+
current.reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(getAIReasoningEffortFlag(flags), "");
|
|
908
1146
|
}
|
|
909
1147
|
if (boolFromRaw(flags.default, false)) {
|
|
910
1148
|
parsed.TELEGRAM_DEFAULT_BOT_KEY = current.key;
|
|
911
1149
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1150
|
+
saveTelegramBotEdit(parsed, selected, current, deps);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
const editMode = await promptChoice(
|
|
1154
|
+
ui,
|
|
1155
|
+
`How do you want to edit Telegram bot "${current.key}"?`,
|
|
1156
|
+
[
|
|
1157
|
+
{ value: "guided", label: "Guided edit (Recommended)", description: "Step-by-step prompts with numbered choices" },
|
|
1158
|
+
{ value: "field_menu", label: "Field menu", description: "Choose one field at a time until save" },
|
|
1159
|
+
],
|
|
1160
|
+
{ defaultIndex: 0 },
|
|
1161
|
+
);
|
|
1162
|
+
if (editMode?.value === "guided") {
|
|
1163
|
+
await editTelegramBotGuided(ui, parsed, selected, current, flags, deps);
|
|
916
1164
|
return;
|
|
917
1165
|
}
|
|
918
1166
|
while (true) {
|
|
@@ -936,16 +1184,7 @@ async function editTelegramBot(ui, flags, deps) {
|
|
|
936
1184
|
);
|
|
937
1185
|
if (!choice) return;
|
|
938
1186
|
if (choice.value === "save") {
|
|
939
|
-
|
|
940
|
-
if (current.key !== selected.key) {
|
|
941
|
-
nextParsed = removeTelegramEntry(nextParsed, selected.key);
|
|
942
|
-
}
|
|
943
|
-
nextParsed = upsertTelegramEntry(nextParsed, current);
|
|
944
|
-
if (String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() === selected.key) {
|
|
945
|
-
nextParsed.TELEGRAM_DEFAULT_BOT_KEY = current.key;
|
|
946
|
-
}
|
|
947
|
-
const filePath = writeProviderEnvState("telegram", nextParsed, deps);
|
|
948
|
-
process.stdout.write(`Saved Telegram bot entry "${current.key}" to ${filePath}\n`);
|
|
1187
|
+
saveTelegramBotEdit(parsed, selected, current, deps);
|
|
949
1188
|
return;
|
|
950
1189
|
}
|
|
951
1190
|
if (choice.value === "server_bot_id") {
|
|
@@ -1194,6 +1433,8 @@ async function runBotSetup(ui, flags, deps) {
|
|
|
1194
1433
|
{ value: "add", label: "Add bot" },
|
|
1195
1434
|
{ value: "edit", label: "Edit bot" },
|
|
1196
1435
|
{ value: "remove", label: "Remove bot" },
|
|
1436
|
+
{ value: "set_default", label: "Set default bot" },
|
|
1437
|
+
{ value: "migrate", label: "Migrate legacy token" },
|
|
1197
1438
|
{ value: "verify", label: "Verify bot" },
|
|
1198
1439
|
{ value: "global", label: "Telegram global settings" },
|
|
1199
1440
|
];
|
|
@@ -1244,6 +1485,14 @@ async function runBotSetup(ui, flags, deps) {
|
|
|
1244
1485
|
}
|
|
1245
1486
|
return;
|
|
1246
1487
|
}
|
|
1488
|
+
if (action.value === "set_default" && provider === "telegram") {
|
|
1489
|
+
await runBotSetDefault(ui, flags, deps);
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
if (action.value === "migrate" && provider === "telegram") {
|
|
1493
|
+
await runBotMigrate(ui, flags, deps);
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1247
1496
|
if (action.value === "verify") {
|
|
1248
1497
|
await verifyProviderEntry(ui, provider, flags, deps);
|
|
1249
1498
|
return;
|
|
@@ -1344,6 +1593,77 @@ async function runBotVerify(ui, flags, deps) {
|
|
|
1344
1593
|
await verifyProviderEntry(ui, provider, flags, deps);
|
|
1345
1594
|
}
|
|
1346
1595
|
|
|
1596
|
+
async function runBotSetDefault(ui, flags, deps) {
|
|
1597
|
+
const provider = String(flags.provider || "").trim()
|
|
1598
|
+
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
1599
|
+
: "telegram";
|
|
1600
|
+
if (provider !== "telegram") {
|
|
1601
|
+
throw new Error("bot set-default currently supports only --provider telegram");
|
|
1602
|
+
}
|
|
1603
|
+
const state = loadProviderEnvState("telegram", deps);
|
|
1604
|
+
const parsed = { ...state.parsed };
|
|
1605
|
+
const nonInteractive = boolFromRaw(flags["non-interactive"] ?? flags.yes, false);
|
|
1606
|
+
const selected = nonInteractive
|
|
1607
|
+
? findTelegramEntryByFlags(parsed, flags, deps)
|
|
1608
|
+
: await chooseTelegramEntry(ui, parsed, deps, "Select default Telegram bot");
|
|
1609
|
+
if (!selected) {
|
|
1610
|
+
throw new Error("Telegram bot selector is required for set-default");
|
|
1611
|
+
}
|
|
1612
|
+
parsed.TELEGRAM_DEFAULT_BOT_KEY = selected.key;
|
|
1613
|
+
const filePath = writeProviderEnvState("telegram", parsed, deps);
|
|
1614
|
+
process.stdout.write(`Set TELEGRAM_DEFAULT_BOT_KEY=${selected.key} in ${filePath}\n`);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
async function runBotMigrate(ui, flags, deps) {
|
|
1618
|
+
const provider = String(flags.provider || "").trim()
|
|
1619
|
+
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
1620
|
+
: "telegram";
|
|
1621
|
+
if (provider !== "telegram") {
|
|
1622
|
+
throw new Error("bot migrate currently supports only --provider telegram");
|
|
1623
|
+
}
|
|
1624
|
+
const state = loadProviderEnvState("telegram", deps);
|
|
1625
|
+
const parsed = { ...state.parsed };
|
|
1626
|
+
const legacyToken = String(parsed.TELEGRAM_BOT_TOKEN || "").trim();
|
|
1627
|
+
if (!legacyToken) {
|
|
1628
|
+
process.stdout.write("No legacy TELEGRAM_BOT_TOKEN value is set.\n");
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
const nonInteractive = boolFromRaw(flags["non-interactive"] ?? flags.yes, false);
|
|
1632
|
+
const normalizeBotKey = requireDependency(deps, "normalizeTelegramBotEnvKey");
|
|
1633
|
+
const existingKeys = new Set(telegramEntriesForDisplay(parsed, deps).map((entry) => entry.key));
|
|
1634
|
+
const defaultKeyHint = normalizeBotKey(flags["bot-key"] || parsed.TELEGRAM_DEFAULT_BOT_KEY || "main", "main");
|
|
1635
|
+
const botKey = normalizeBotKey(
|
|
1636
|
+
nonInteractive
|
|
1637
|
+
? String(flags["bot-key"] || defaultKeyHint).trim()
|
|
1638
|
+
: await promptRequiredLine(ui, "Local Telegram bot key for migrated entry", defaultKeyHint),
|
|
1639
|
+
defaultKeyHint,
|
|
1640
|
+
);
|
|
1641
|
+
if (existingKeys.has(botKey)) {
|
|
1642
|
+
throw new Error(`Telegram bot key "${botKey}" already exists`);
|
|
1643
|
+
}
|
|
1644
|
+
const nextParsed = upsertTelegramEntry(parsed, {
|
|
1645
|
+
key: botKey,
|
|
1646
|
+
serverBotID: getServerBotIDFlag(flags),
|
|
1647
|
+
username: requireDependency(deps, "normalizeTelegramBotUsername")(flags["bot-name"] || flags.username || ""),
|
|
1648
|
+
token: legacyToken,
|
|
1649
|
+
roleProfile: requireDependency(deps, "normalizeRunnerRoleProfileName")(flags["role-profile"] || ""),
|
|
1650
|
+
client: requireDependency(deps, "normalizeLocalAIClientName")(getAIClientFlag(flags), ""),
|
|
1651
|
+
model: getAIModelFlag(flags),
|
|
1652
|
+
permissionMode: requireDependency(deps, "normalizeLocalAIPermissionMode")(getAIPermissionModeFlag(flags), ""),
|
|
1653
|
+
reasoningEffort: requireDependency(deps, "normalizeLocalAIReasoningEffort")(getAIReasoningEffortFlag(flags), ""),
|
|
1654
|
+
});
|
|
1655
|
+
if (!hasNamedTelegramEntry(parsed, deps) || !String(nextParsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim()) {
|
|
1656
|
+
nextParsed.TELEGRAM_DEFAULT_BOT_KEY = botKey;
|
|
1657
|
+
}
|
|
1658
|
+
if (!boolFromRaw(flags["keep-legacy-token"], false)) {
|
|
1659
|
+
nextParsed.TELEGRAM_BOT_TOKEN = "";
|
|
1660
|
+
}
|
|
1661
|
+
const filePath = writeProviderEnvState("telegram", nextParsed, deps);
|
|
1662
|
+
process.stdout.write(
|
|
1663
|
+
`Migrated TELEGRAM_BOT_TOKEN to named entry "${botKey}" in ${filePath}${boolFromRaw(flags["keep-legacy-token"], false) ? " (legacy token preserved)" : ""}\n`,
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1347
1667
|
async function runBotGlobal(ui, flags, deps) {
|
|
1348
1668
|
const provider = String(flags.provider || "").trim()
|
|
1349
1669
|
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
@@ -1389,6 +1709,14 @@ export async function runBotCommand(argv, deps) {
|
|
|
1389
1709
|
await runBotRemove(ui, flags, deps);
|
|
1390
1710
|
return;
|
|
1391
1711
|
}
|
|
1712
|
+
if (command === "set-default") {
|
|
1713
|
+
await runBotSetDefault(ui, flags, deps);
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
if (command === "migrate") {
|
|
1717
|
+
await runBotMigrate(ui, flags, deps);
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1392
1720
|
if (command === "verify") {
|
|
1393
1721
|
await runBotVerify(ui, flags, deps);
|
|
1394
1722
|
return;
|
|
@@ -1397,7 +1725,7 @@ export async function runBotCommand(argv, deps) {
|
|
|
1397
1725
|
await runBotGlobal(ui, flags, deps);
|
|
1398
1726
|
return;
|
|
1399
1727
|
}
|
|
1400
|
-
throw new Error("bot requires a subcommand: setup | list | show | add | edit | remove | verify | global");
|
|
1728
|
+
throw new Error("bot requires a subcommand: setup | list | show | add | edit | remove | set-default | migrate | verify | global");
|
|
1401
1729
|
} finally {
|
|
1402
1730
|
ui.close();
|
|
1403
1731
|
}
|
|
@@ -225,29 +225,36 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
225
225
|
cliPath,
|
|
226
226
|
args: [
|
|
227
227
|
"bot", "add",
|
|
228
|
-
"--provider", "telegram",
|
|
229
|
-
"--non-interactive", "true",
|
|
230
228
|
"--base-url", baseURL,
|
|
231
229
|
"--timeout-seconds", "5",
|
|
232
|
-
"--bot-key", "main_test",
|
|
233
|
-
"--bot-id", mock.bots[0].id,
|
|
234
|
-
"--token", "selftest-main-token",
|
|
235
|
-
"--role-profile", "monitor",
|
|
236
|
-
"--client", "codex",
|
|
237
|
-
"--permission-mode", "read_only",
|
|
238
|
-
"--reasoning-effort", "low",
|
|
239
|
-
"--default", "true",
|
|
240
|
-
"--verify", "true",
|
|
241
230
|
],
|
|
242
|
-
env
|
|
231
|
+
env: {
|
|
232
|
+
...env,
|
|
233
|
+
METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
|
|
234
|
+
"1", // provider: telegram
|
|
235
|
+
"1", // server bot: first listed bot
|
|
236
|
+
"main_test",
|
|
237
|
+
"selftest-main-token",
|
|
238
|
+
"y", // verify now
|
|
239
|
+
"", // keep verified username
|
|
240
|
+
"3", // role profile: monitor
|
|
241
|
+
"2", // ai client: codex
|
|
242
|
+
"gpt-5-codex",
|
|
243
|
+
"2", // permission: read_only
|
|
244
|
+
"2", // reasoning: low
|
|
245
|
+
"y", // set as default
|
|
246
|
+
]),
|
|
247
|
+
},
|
|
243
248
|
});
|
|
244
249
|
const addState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
245
250
|
push(
|
|
246
|
-
"
|
|
251
|
+
"bot_add_guided_creates_named_telegram_entry",
|
|
247
252
|
String(addState.TELEGRAM_BOT_MAIN_TEST_SERVER_BOT_ID || "") === mock.bots[0].id
|
|
253
|
+
&& String(addState.TELEGRAM_BOT_MAIN_TEST_USERNAME || "").trim().toLowerCase() === "monitorselftestbot"
|
|
248
254
|
&& String(addState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "") === "codex"
|
|
255
|
+
&& String(addState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "") === "gpt-5-codex"
|
|
249
256
|
&& String(addState.TELEGRAM_DEFAULT_BOT_KEY || "") === "main_test",
|
|
250
|
-
`default=${String(addState.TELEGRAM_DEFAULT_BOT_KEY || "")} client=${String(addState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "")}`,
|
|
257
|
+
`default=${String(addState.TELEGRAM_DEFAULT_BOT_KEY || "")} client=${String(addState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "")} model=${String(addState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "")}`,
|
|
251
258
|
);
|
|
252
259
|
|
|
253
260
|
const showResult = await runCLI({
|
|
@@ -268,6 +275,43 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
268
275
|
`key=${String(safeObject(showPayload.entry).key || "")} client=${String(safeObject(showPayload.entry).client || "")}`,
|
|
269
276
|
);
|
|
270
277
|
|
|
278
|
+
await runCLI({
|
|
279
|
+
cliPath,
|
|
280
|
+
args: ["bot", "edit"],
|
|
281
|
+
env: {
|
|
282
|
+
...env,
|
|
283
|
+
METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
|
|
284
|
+
"1", // provider: telegram
|
|
285
|
+
"1", // bot entry: main_test
|
|
286
|
+
"1", // edit mode: guided
|
|
287
|
+
"1", // keep server bot binding
|
|
288
|
+
"1", // keep username
|
|
289
|
+
"1", // keep token
|
|
290
|
+
"1", // keep role profile
|
|
291
|
+
"2", // change AI client
|
|
292
|
+
"4", // gemini
|
|
293
|
+
"2", // change AI model
|
|
294
|
+
"guided-gemini-pro",
|
|
295
|
+
"2", // change permission mode
|
|
296
|
+
"3", // workspace_write
|
|
297
|
+
"2", // change reasoning effort
|
|
298
|
+
"3", // medium
|
|
299
|
+
"1", // keep bot key
|
|
300
|
+
"1", // keep default setting
|
|
301
|
+
"y", // save
|
|
302
|
+
]),
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
const guidedState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
306
|
+
push(
|
|
307
|
+
"bot_edit_guided_prompts_update_ai_binding_fields",
|
|
308
|
+
String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "") === "gemini"
|
|
309
|
+
&& String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "") === "guided-gemini-pro"
|
|
310
|
+
&& String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_PERMISSION_MODE || "") === "workspace_write"
|
|
311
|
+
&& String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_REASONING_EFFORT || "") === "medium",
|
|
312
|
+
`client=${String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "")} model=${String(guidedState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "")}`,
|
|
313
|
+
);
|
|
314
|
+
|
|
271
315
|
await runCLI({
|
|
272
316
|
cliPath,
|
|
273
317
|
args: [
|
|
@@ -294,6 +338,23 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
294
338
|
`client=${String(editedState.TELEGRAM_BOT_MAIN_TEST_AI_CLIENT || "")} model=${String(editedState.TELEGRAM_BOT_MAIN_TEST_AI_MODEL || "")}`,
|
|
295
339
|
);
|
|
296
340
|
|
|
341
|
+
await runCLI({
|
|
342
|
+
cliPath,
|
|
343
|
+
args: [
|
|
344
|
+
"bot", "set-default",
|
|
345
|
+
"--provider", "telegram",
|
|
346
|
+
"--bot-key", "main_test",
|
|
347
|
+
"--non-interactive", "true",
|
|
348
|
+
],
|
|
349
|
+
env,
|
|
350
|
+
});
|
|
351
|
+
const defaultState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
352
|
+
push(
|
|
353
|
+
"bot_set_default_updates_default_bot_key",
|
|
354
|
+
String(defaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "main_test",
|
|
355
|
+
`default=${String(defaultState.TELEGRAM_DEFAULT_BOT_KEY || "")}`,
|
|
356
|
+
);
|
|
357
|
+
|
|
297
358
|
const verifyResult = await runCLI({
|
|
298
359
|
cliPath,
|
|
299
360
|
args: [
|
|
@@ -341,6 +402,80 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
341
402
|
ensureArray(telegramEntry.entries).length === 0,
|
|
342
403
|
`entries=${String(ensureArray(telegramEntry.entries).length)}`,
|
|
343
404
|
);
|
|
405
|
+
|
|
406
|
+
await runCLI({
|
|
407
|
+
cliPath,
|
|
408
|
+
args: [
|
|
409
|
+
"bot", "add",
|
|
410
|
+
"--provider", "telegram",
|
|
411
|
+
"--non-interactive", "true",
|
|
412
|
+
"--base-url", baseURL,
|
|
413
|
+
"--timeout-seconds", "5",
|
|
414
|
+
"--bot-key", "explicit_test",
|
|
415
|
+
"--server-bot-id", mock.bots[0].id,
|
|
416
|
+
"--username", "MonitorSelftestBot",
|
|
417
|
+
"--token", "selftest-main-token",
|
|
418
|
+
"--role-profile", "monitor",
|
|
419
|
+
"--ai-client", "codex",
|
|
420
|
+
"--ai-model", "gpt-5-codex",
|
|
421
|
+
"--ai-permission-mode", "read_only",
|
|
422
|
+
"--ai-reasoning-effort", "low",
|
|
423
|
+
"--verify", "true",
|
|
424
|
+
],
|
|
425
|
+
env,
|
|
426
|
+
});
|
|
427
|
+
const aliasAddState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
428
|
+
push(
|
|
429
|
+
"bot_add_accepts_ai_prefixed_option_aliases",
|
|
430
|
+
String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_SERVER_BOT_ID || "") === mock.bots[0].id
|
|
431
|
+
&& String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_CLIENT || "") === "codex"
|
|
432
|
+
&& String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_MODEL || "") === "gpt-5-codex"
|
|
433
|
+
&& String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_PERMISSION_MODE || "") === "read_only"
|
|
434
|
+
&& String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_REASONING_EFFORT || "") === "low",
|
|
435
|
+
`client=${String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_CLIENT || "")} model=${String(aliasAddState.TELEGRAM_BOT_EXPLICIT_TEST_AI_MODEL || "")}`,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
await runCLI({
|
|
439
|
+
cliPath,
|
|
440
|
+
args: [
|
|
441
|
+
"bot", "remove",
|
|
442
|
+
"--provider", "telegram",
|
|
443
|
+
"--bot-key", "explicit_test",
|
|
444
|
+
"--non-interactive", "true",
|
|
445
|
+
],
|
|
446
|
+
env,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const migratedEnv = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
450
|
+
migratedEnv.TELEGRAM_BOT_TOKEN = "legacy-selftest-token";
|
|
451
|
+
fs.writeFileSync(
|
|
452
|
+
telegramEnvPath,
|
|
453
|
+
Object.entries(migratedEnv).map(([key, value]) => `${key}=${String(value ?? "")}`).join("\n") + "\n",
|
|
454
|
+
"utf8",
|
|
455
|
+
);
|
|
456
|
+
await runCLI({
|
|
457
|
+
cliPath,
|
|
458
|
+
args: [
|
|
459
|
+
"bot", "migrate",
|
|
460
|
+
"--provider", "telegram",
|
|
461
|
+
"--bot-key", "legacy_main",
|
|
462
|
+
"--bot-id", mock.bots[0].id,
|
|
463
|
+
"--bot-name", "MonitorSelftestBot",
|
|
464
|
+
"--role-profile", "monitor",
|
|
465
|
+
"--client", "codex",
|
|
466
|
+
"--permission-mode", "read_only",
|
|
467
|
+
"--reasoning-effort", "low",
|
|
468
|
+
"--non-interactive", "true",
|
|
469
|
+
],
|
|
470
|
+
env,
|
|
471
|
+
});
|
|
472
|
+
const migratedState = parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
|
|
473
|
+
push(
|
|
474
|
+
"bot_migrate_converts_legacy_token_to_named_entry",
|
|
475
|
+
String(migratedState.TELEGRAM_BOT_LEGACY_MAIN_TOKEN || "") === "legacy-selftest-token"
|
|
476
|
+
&& String(migratedState.TELEGRAM_BOT_TOKEN || "") === "",
|
|
477
|
+
`legacy_named=${String(migratedState.TELEGRAM_BOT_LEGACY_MAIN_TOKEN || "")} fallback=${String(migratedState.TELEGRAM_BOT_TOKEN || "")}`,
|
|
478
|
+
);
|
|
344
479
|
} catch (err) {
|
|
345
480
|
push("bot_commands_smoke", false, String(err?.message || err));
|
|
346
481
|
} finally {
|