metheus-governance-mcp-cli 0.2.71 → 0.2.73
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 +7 -4
- package/cli.mjs +1 -0
- package/lib/bot-commands.mjs +458 -113
- package/lib/selftest-bot-commands.mjs +75 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,7 +231,8 @@ Behavior:
|
|
|
231
231
|
- For Telegram, the local env key is auto-generated from the matched server bot name or verified username, so you do not have to invent a separate local nickname first.
|
|
232
232
|
- For Telegram, the CLI tries to match the verified bot identity against the server `me/bots` list first. If the server exposes one logical bot name with multiple roles such as `approval`, `worker`, `review`, and `monitor`, the CLI does not ask you to choose one UUID. It binds by server bot name and keeps the local role/AI fields empty so runtime can use the server bot role for each route.
|
|
233
233
|
- When the Telegram username matches exactly one server bot role, the CLI still auto-fills the local `role_profile` and blank AI defaults from your local `bot-runner.json` `role_profiles` mapping.
|
|
234
|
-
- `bot edit` without flags now uses the same sequential flow every time: provider -> bot entry -> username/token review -> AI field choices -> default choice -> save.
|
|
234
|
+
- `bot edit` without flags now uses the same sequential flow every time: provider -> bot entry -> username/token review -> grouped server-role review when needed -> AI field choices -> default choice -> save.
|
|
235
|
+
- if one server bot name maps to multiple server roles, `bot edit` keeps the Telegram env entry bound to the server identity and lets you review the local `role_profiles` for each detected role instead of forcing one role/profile UUID choice up front.
|
|
235
236
|
- In the normal Telegram edit path, the CLI keeps or re-resolves the server bot binding automatically. It no longer asks you to pick `approval / worker / review / monitor` or a server bot UUID first.
|
|
236
237
|
- `bot set-default` without flags starts a guided numbered flow: provider -> bot entry -> confirm default change.
|
|
237
238
|
- `bot verify` without flags starts a guided numbered flow: provider -> bot entry -> output format.
|
|
@@ -246,8 +247,8 @@ Behavior:
|
|
|
246
247
|
- `AI_PERMISSION_MODE`
|
|
247
248
|
- `AI_REASONING_EFFORT`
|
|
248
249
|
- Slack and KakaoTalk currently use a single local token entry per provider in this command flow.
|
|
249
|
-
- `bot verify` checks the configured local token and prints the
|
|
250
|
-
- `bot show` prints one local bot entry in detail.
|
|
250
|
+
- `bot verify` checks the configured local token, cross-checks the server bot binding, and prints the effective runtime role profile summary that the runner will use.
|
|
251
|
+
- `bot show` prints one local bot entry in detail, including grouped role summaries when one server bot name expands to multiple roles.
|
|
251
252
|
- `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and default bot key.
|
|
252
253
|
- `bot set-default` updates `TELEGRAM_DEFAULT_BOT_KEY`.
|
|
253
254
|
- `bot migrate` moves legacy `TELEGRAM_BOT_TOKEN` into a named Telegram bot entry.
|
|
@@ -258,7 +259,7 @@ Non-interactive examples:
|
|
|
258
259
|
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
|
|
259
260
|
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --default true
|
|
260
261
|
metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --ai-client codex --ai-model gpt-5-codex --ai-permission-mode read_only --ai-reasoning-effort low
|
|
261
|
-
metheus-governance-mcp-cli bot edit --provider telegram --bot-key
|
|
262
|
+
metheus-governance-mcp-cli bot edit --provider telegram --bot-key ryoai_bot --non-interactive true --ai-client claude --ai-model claude-sonnet-4 --ai-permission-mode danger_full_access --ai-reasoning-effort high
|
|
262
263
|
metheus-governance-mcp-cli bot set-default --provider telegram --bot-key main --non-interactive true
|
|
263
264
|
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
|
|
264
265
|
metheus-governance-mcp-cli bot remove --provider telegram --bot-key main --non-interactive true
|
|
@@ -267,6 +268,8 @@ metheus-governance-mcp-cli bot verify --provider telegram --bot-key main --json
|
|
|
267
268
|
|
|
268
269
|
For direct Telegram adds, the CLI can derive the local entry key from the matched server bot name. You no longer need `--bot-key` or `--username` in the normal server-bound path. Treat `--bot-key` as an advanced override only when you intentionally want a different local suffix.
|
|
269
270
|
|
|
271
|
+
For direct Telegram edits, `--bot-key` still identifies which saved local entry to update. 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.
|
|
272
|
+
|
|
270
273
|
Current support status:
|
|
271
274
|
|
|
272
275
|
- Telegram: full local bot entry management, token verification, bot-to-AI binding, inbound runner support
|
package/cli.mjs
CHANGED
|
@@ -3362,6 +3362,7 @@ function buildBotCommandDeps() {
|
|
|
3362
3362
|
loadProviderEnvConfig,
|
|
3363
3363
|
verifyLocalProviderToken,
|
|
3364
3364
|
loadBotRunnerConfig,
|
|
3365
|
+
saveBotRunnerConfig,
|
|
3365
3366
|
listServerBots: async ({ provider, baseURL, timeoutSeconds }) => {
|
|
3366
3367
|
const authFlowDeps = buildAuthFlowDeps();
|
|
3367
3368
|
const resolved = resolveCurrentAccessToken(authFlowDeps);
|
package/lib/bot-commands.mjs
CHANGED
|
@@ -595,6 +595,8 @@ function buildDerivedTelegramBotKey(parsedEnv, deps, preferredValues, { excludeK
|
|
|
595
595
|
|
|
596
596
|
async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps) {
|
|
597
597
|
let serverRoleAutoResolved = "";
|
|
598
|
+
let groupedServerRoles = [];
|
|
599
|
+
let groupedServerName = "";
|
|
598
600
|
if (current.serverBotID || current.__preferServerIdentity) {
|
|
599
601
|
current.__preferServerIdentity = true;
|
|
600
602
|
serverRoleAutoResolved = String(current.roleProfile || "").trim() || "__server_binding__";
|
|
@@ -641,9 +643,15 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
|
|
|
641
643
|
current.serverBotID = "";
|
|
642
644
|
current.__preferServerIdentity = true;
|
|
643
645
|
current.roleProfile = "";
|
|
646
|
+
current.client = "";
|
|
647
|
+
current.model = "";
|
|
648
|
+
current.permissionMode = "";
|
|
649
|
+
current.reasoningEffort = "";
|
|
644
650
|
serverRoleAutoResolved = "__server_role_group__";
|
|
651
|
+
groupedServerRoles = preferredRoleSort(serverBot.roles);
|
|
652
|
+
groupedServerName = String(serverBot.name || current.username || current.key).trim();
|
|
645
653
|
process.stdout.write(
|
|
646
|
-
`Using server Telegram bot "${
|
|
654
|
+
`Using server Telegram bot "${groupedServerName}" with roles: ${groupedServerRoles.join(", ")}. Runtime will use the server role automatically.\n`,
|
|
647
655
|
);
|
|
648
656
|
} else if (String(serverBot.botID || "").trim()) {
|
|
649
657
|
current.serverBotID = String(serverBot.botID || "").trim();
|
|
@@ -672,53 +680,64 @@ async function editTelegramBotGuided(ui, parsed, selected, current, flags, deps)
|
|
|
672
680
|
}
|
|
673
681
|
}
|
|
674
682
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
+
if (serverRoleAutoResolved === "__server_role_group__") {
|
|
684
|
+
await maybePromptGroupedServerRoleProfiles(
|
|
685
|
+
ui,
|
|
686
|
+
{
|
|
687
|
+
name: groupedServerName,
|
|
688
|
+
roles: groupedServerRoles,
|
|
689
|
+
},
|
|
690
|
+
deps,
|
|
683
691
|
);
|
|
684
|
-
} else
|
|
685
|
-
|
|
686
|
-
|
|
692
|
+
} else {
|
|
693
|
+
const clientAction = await promptKeepChangeClear(ui, "AI client", {
|
|
694
|
+
allowClear: true,
|
|
695
|
+
defaultValue: current.client ? "keep" : "change",
|
|
696
|
+
});
|
|
697
|
+
if (clientAction === "change") {
|
|
698
|
+
current.client = requireDependency(deps, "normalizeLocalAIClientName")(
|
|
699
|
+
await promptAIClient(ui, deps, current.client),
|
|
700
|
+
"",
|
|
701
|
+
);
|
|
702
|
+
} else if (clientAction === "clear") {
|
|
703
|
+
current.client = "";
|
|
704
|
+
}
|
|
687
705
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
706
|
+
const modelAction = await promptKeepChangeClear(ui, "AI model", {
|
|
707
|
+
allowClear: true,
|
|
708
|
+
defaultValue: current.model ? "keep" : "change",
|
|
709
|
+
});
|
|
710
|
+
if (modelAction === "change") {
|
|
711
|
+
current.model = await promptLine(ui, "AI model", current.model);
|
|
712
|
+
} else if (modelAction === "clear") {
|
|
713
|
+
current.model = "";
|
|
714
|
+
}
|
|
697
715
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
716
|
+
const permissionAction = await promptKeepChangeClear(ui, "AI permission mode", {
|
|
717
|
+
allowClear: true,
|
|
718
|
+
defaultValue: current.permissionMode ? "keep" : "change",
|
|
719
|
+
});
|
|
720
|
+
if (permissionAction === "change") {
|
|
721
|
+
current.permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(
|
|
722
|
+
await promptPermissionMode(ui, current.permissionMode),
|
|
723
|
+
"",
|
|
724
|
+
);
|
|
725
|
+
} else if (permissionAction === "clear") {
|
|
726
|
+
current.permissionMode = "";
|
|
727
|
+
}
|
|
710
728
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
729
|
+
const reasoningAction = await promptKeepChangeClear(ui, "AI reasoning effort", {
|
|
730
|
+
allowClear: true,
|
|
731
|
+
defaultValue: current.reasoningEffort ? "keep" : "change",
|
|
732
|
+
});
|
|
733
|
+
if (reasoningAction === "change") {
|
|
734
|
+
current.reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(
|
|
735
|
+
await promptReasoningEffort(ui, current.reasoningEffort),
|
|
736
|
+
"",
|
|
737
|
+
);
|
|
738
|
+
} else if (reasoningAction === "clear") {
|
|
739
|
+
current.reasoningEffort = "";
|
|
740
|
+
}
|
|
722
741
|
}
|
|
723
742
|
|
|
724
743
|
const defaultChoice = await promptChoice(
|
|
@@ -790,6 +809,16 @@ function renderBotListPayload(provider, state, deps) {
|
|
|
790
809
|
};
|
|
791
810
|
}
|
|
792
811
|
|
|
812
|
+
function formatRoleProfileOutputLine(profile) {
|
|
813
|
+
const current = safeObject(profile);
|
|
814
|
+
return [
|
|
815
|
+
current.client ? `client=${current.client}` : "client=(blank)",
|
|
816
|
+
current.model ? `model=${current.model}` : "model=(blank)",
|
|
817
|
+
current.permissionMode ? `permission=${current.permissionMode}` : "permission=(blank)",
|
|
818
|
+
current.reasoningEffort ? `reasoning=${current.reasoningEffort}` : "reasoning=(blank)",
|
|
819
|
+
].join(" | ");
|
|
820
|
+
}
|
|
821
|
+
|
|
793
822
|
function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
|
|
794
823
|
if (provider === "telegram") {
|
|
795
824
|
const selectedEntry = entry || null;
|
|
@@ -810,6 +839,7 @@ function buildBotShowPayload(provider, state, entry, deps, extras = {}) {
|
|
|
810
839
|
permissionMode: selectedEntry.permissionMode,
|
|
811
840
|
reasoningEffort: selectedEntry.reasoningEffort,
|
|
812
841
|
} : null,
|
|
842
|
+
serverBinding: safeObject(extras.serverBinding),
|
|
813
843
|
...safeObject(extras),
|
|
814
844
|
};
|
|
815
845
|
}
|
|
@@ -864,6 +894,7 @@ function printBotList(provider, state, deps) {
|
|
|
864
894
|
function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
865
895
|
if (provider === "telegram") {
|
|
866
896
|
const selectedEntry = entry || {};
|
|
897
|
+
const serverBinding = safeObject(extras.serverBinding);
|
|
867
898
|
process.stdout.write(`${providerLabel(provider, deps)} bot\n`);
|
|
868
899
|
process.stdout.write(` file: ${state.filePath}\n`);
|
|
869
900
|
process.stdout.write(` name: ${telegramEntryDisplayName(selectedEntry)}\n`);
|
|
@@ -877,8 +908,21 @@ function printBotShow(provider, state, entry, deps, extras = {}) {
|
|
|
877
908
|
process.stdout.write(` ai_model: ${selectedEntry.model || "-"}\n`);
|
|
878
909
|
process.stdout.write(` permission_mode: ${selectedEntry.permissionMode || "-"}\n`);
|
|
879
910
|
process.stdout.write(` reasoning_effort: ${selectedEntry.reasoningEffort || "-"}\n`);
|
|
880
|
-
if (
|
|
881
|
-
process.stdout.write(` server_binding: ${
|
|
911
|
+
if (Object.keys(serverBinding).length) {
|
|
912
|
+
process.stdout.write(` server_binding: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}\n`);
|
|
913
|
+
process.stdout.write(` server_binding_mode: ${serverBinding.mode || "-"}\n`);
|
|
914
|
+
process.stdout.write(` server_bot_name: ${serverBinding.name || "-"}\n`);
|
|
915
|
+
process.stdout.write(` server_bot_id: ${serverBinding.serverBotID || selectedEntry.serverBotID || "-" }\n`);
|
|
916
|
+
if (serverBinding.mode === "group") {
|
|
917
|
+
process.stdout.write(` server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}\n`);
|
|
918
|
+
const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
|
|
919
|
+
Object.keys(groupedProfiles).forEach((role) => {
|
|
920
|
+
process.stdout.write(` runtime_role_profile[${role}]: ${formatRoleProfileOutputLine(groupedProfiles[role])}\n`);
|
|
921
|
+
});
|
|
922
|
+
} else if (serverBinding.effectiveRoleProfile) {
|
|
923
|
+
process.stdout.write(` server_role: ${serverBinding.role || "-"}\n`);
|
|
924
|
+
process.stdout.write(` runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}\n`);
|
|
925
|
+
}
|
|
882
926
|
}
|
|
883
927
|
return;
|
|
884
928
|
}
|
|
@@ -1285,6 +1329,324 @@ function applyRoleProfileDefaults(entry, defaults, { overwrite = false } = {}) {
|
|
|
1285
1329
|
}
|
|
1286
1330
|
}
|
|
1287
1331
|
|
|
1332
|
+
function preferredRoleSort(roles) {
|
|
1333
|
+
const order = ["monitor", "review", "worker", "approval"];
|
|
1334
|
+
return ensureArray(roles).slice().sort((left, right) => {
|
|
1335
|
+
const leftText = String(left || "").trim();
|
|
1336
|
+
const rightText = String(right || "").trim();
|
|
1337
|
+
const leftIndex = order.indexOf(leftText);
|
|
1338
|
+
const rightIndex = order.indexOf(rightText);
|
|
1339
|
+
if (leftIndex >= 0 && rightIndex >= 0) return leftIndex - rightIndex;
|
|
1340
|
+
if (leftIndex >= 0) return -1;
|
|
1341
|
+
if (rightIndex >= 0) return 1;
|
|
1342
|
+
return leftText.localeCompare(rightText);
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
function currentRoleProfileState(roleName, deps) {
|
|
1347
|
+
const normalizedRole = requireDependency(deps, "normalizeRunnerRoleProfileName")(roleName || "");
|
|
1348
|
+
const defaults = resolveRoleProfileDefaults(normalizedRole, deps);
|
|
1349
|
+
const config = requireDependency(deps, "loadBotRunnerConfig")({ persistIfNeeded: true });
|
|
1350
|
+
const profile = safeObject(safeObject(config.roleProfiles || {})[normalizedRole]);
|
|
1351
|
+
return {
|
|
1352
|
+
role: normalizedRole,
|
|
1353
|
+
client: requireDependency(deps, "normalizeLocalAIClientName")(profile.client || defaults.client || "", ""),
|
|
1354
|
+
model: String(profile.model || defaults.model || "").trim(),
|
|
1355
|
+
permissionMode: requireDependency(deps, "normalizeLocalAIPermissionMode")(
|
|
1356
|
+
profile.permission_mode || profile.permissionMode || defaults.permissionMode || "",
|
|
1357
|
+
"",
|
|
1358
|
+
),
|
|
1359
|
+
reasoningEffort: requireDependency(deps, "normalizeLocalAIReasoningEffort")(
|
|
1360
|
+
profile.reasoning_effort || profile.reasoningEffort || defaults.reasoningEffort || "",
|
|
1361
|
+
"",
|
|
1362
|
+
),
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
function formatRoleProfileSummary(profile) {
|
|
1367
|
+
const current = safeObject(profile);
|
|
1368
|
+
return [
|
|
1369
|
+
current.client ? `client:${current.client}` : "client:(blank)",
|
|
1370
|
+
current.model ? `model:${current.model}` : "model:(blank)",
|
|
1371
|
+
current.permissionMode ? `permission:${current.permissionMode}` : "permission:(blank)",
|
|
1372
|
+
current.reasoningEffort ? `reasoning:${current.reasoningEffort}` : "reasoning:(blank)",
|
|
1373
|
+
].join(" | ");
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function serializeRoleProfile(profile) {
|
|
1377
|
+
const current = safeObject(profile);
|
|
1378
|
+
return {
|
|
1379
|
+
role: String(current.role || "").trim(),
|
|
1380
|
+
client: String(current.client || "").trim(),
|
|
1381
|
+
model: String(current.model || "").trim(),
|
|
1382
|
+
permissionMode: String(current.permissionMode || "").trim(),
|
|
1383
|
+
reasoningEffort: String(current.reasoningEffort || "").trim(),
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function buildRoleProfileOutput(roleName, deps) {
|
|
1388
|
+
return serializeRoleProfile(currentRoleProfileState(roleName, deps));
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
function buildGroupedRoleProfileOutput(roles, deps) {
|
|
1392
|
+
const output = {};
|
|
1393
|
+
preferredRoleSort(roles).forEach((role) => {
|
|
1394
|
+
const normalizedRole = String(role || "").trim();
|
|
1395
|
+
if (!normalizedRole) return;
|
|
1396
|
+
output[normalizedRole] = buildRoleProfileOutput(normalizedRole, deps);
|
|
1397
|
+
});
|
|
1398
|
+
return output;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
function persistRoleProfileState(profile, deps) {
|
|
1402
|
+
const current = safeObject(profile);
|
|
1403
|
+
const role = requireDependency(deps, "normalizeRunnerRoleProfileName")(current.role || "");
|
|
1404
|
+
if (!role) return null;
|
|
1405
|
+
const config = requireDependency(deps, "loadBotRunnerConfig")({ persistIfNeeded: true });
|
|
1406
|
+
const nextRoleProfiles = {
|
|
1407
|
+
...safeObject(config.roleProfiles || {}),
|
|
1408
|
+
[role]: {
|
|
1409
|
+
client: String(current.client || "").trim(),
|
|
1410
|
+
model: String(current.model || "").trim(),
|
|
1411
|
+
permission_mode: String(current.permissionMode || "").trim(),
|
|
1412
|
+
reasoning_effort: String(current.reasoningEffort || "").trim(),
|
|
1413
|
+
},
|
|
1414
|
+
};
|
|
1415
|
+
const nextConfig = {
|
|
1416
|
+
...config,
|
|
1417
|
+
roleProfiles: nextRoleProfiles,
|
|
1418
|
+
};
|
|
1419
|
+
return requireDependency(deps, "saveBotRunnerConfig")(nextConfig, config.filePath);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
async function promptRoleExecutionProfile(ui, roleName, deps) {
|
|
1423
|
+
const current = currentRoleProfileState(roleName, deps);
|
|
1424
|
+
const action = await promptChoice(
|
|
1425
|
+
ui,
|
|
1426
|
+
`Role "${current.role}" local execution profile`,
|
|
1427
|
+
[
|
|
1428
|
+
{
|
|
1429
|
+
value: "keep",
|
|
1430
|
+
label: "Keep current settings",
|
|
1431
|
+
description: formatRoleProfileSummary(current),
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
value: "edit",
|
|
1435
|
+
label: "Edit settings",
|
|
1436
|
+
description: "change AI client, model, permission, and reasoning",
|
|
1437
|
+
},
|
|
1438
|
+
],
|
|
1439
|
+
{ defaultIndex: 0 },
|
|
1440
|
+
);
|
|
1441
|
+
if (action?.value !== "edit") {
|
|
1442
|
+
return { changed: false, filePath: "" };
|
|
1443
|
+
}
|
|
1444
|
+
current.client = requireDependency(deps, "normalizeLocalAIClientName")(
|
|
1445
|
+
await promptAIClient(ui, deps, current.client),
|
|
1446
|
+
"",
|
|
1447
|
+
);
|
|
1448
|
+
current.model = await promptLine(ui, `AI model for role "${current.role}"`, current.model);
|
|
1449
|
+
current.permissionMode = requireDependency(deps, "normalizeLocalAIPermissionMode")(
|
|
1450
|
+
await promptPermissionMode(ui, current.permissionMode),
|
|
1451
|
+
"",
|
|
1452
|
+
);
|
|
1453
|
+
current.reasoningEffort = requireDependency(deps, "normalizeLocalAIReasoningEffort")(
|
|
1454
|
+
await promptReasoningEffort(ui, current.reasoningEffort),
|
|
1455
|
+
"",
|
|
1456
|
+
);
|
|
1457
|
+
const filePath = persistRoleProfileState(current, deps);
|
|
1458
|
+
if (filePath) {
|
|
1459
|
+
process.stdout.write(`Saved local execution profile for role "${current.role}" to ${filePath}\n`);
|
|
1460
|
+
}
|
|
1461
|
+
return { changed: true, filePath };
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
async function maybePromptGroupedServerRoleProfiles(ui, serverBot, deps) {
|
|
1465
|
+
const roles = preferredRoleSort(ensureArray(serverBot?.roles).filter(Boolean));
|
|
1466
|
+
if (roles.length <= 1) {
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
process.stdout.write(`Current grouped role execution profiles for "${String(serverBot?.name || "").trim() || "telegram"}":\n`);
|
|
1470
|
+
roles.forEach((role) => {
|
|
1471
|
+
process.stdout.write(` - ${role}: ${formatRoleProfileSummary(currentRoleProfileState(role, deps))}\n`);
|
|
1472
|
+
});
|
|
1473
|
+
const editChoice = await promptChoice(
|
|
1474
|
+
ui,
|
|
1475
|
+
"Grouped role settings",
|
|
1476
|
+
[
|
|
1477
|
+
{
|
|
1478
|
+
value: "keep",
|
|
1479
|
+
label: "Keep current role settings",
|
|
1480
|
+
description: "use the existing role_profiles defaults as-is",
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
value: "one",
|
|
1484
|
+
label: "Edit one role",
|
|
1485
|
+
description: "change only the role that needs an update",
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
value: "all",
|
|
1489
|
+
label: "Edit all roles",
|
|
1490
|
+
description: "review each role in sequence",
|
|
1491
|
+
},
|
|
1492
|
+
],
|
|
1493
|
+
{ defaultIndex: 0 },
|
|
1494
|
+
);
|
|
1495
|
+
if (editChoice?.value === "keep") {
|
|
1496
|
+
return false;
|
|
1497
|
+
}
|
|
1498
|
+
if (editChoice?.value === "all") {
|
|
1499
|
+
for (const role of roles) {
|
|
1500
|
+
await promptRoleExecutionProfile(ui, role, deps);
|
|
1501
|
+
}
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
1504
|
+
const remaining = new Set(roles);
|
|
1505
|
+
while (remaining.size) {
|
|
1506
|
+
const roleChoice = await promptChoice(
|
|
1507
|
+
ui,
|
|
1508
|
+
"Select role to edit",
|
|
1509
|
+
[
|
|
1510
|
+
...Array.from(remaining).map((role) => ({
|
|
1511
|
+
value: role,
|
|
1512
|
+
label: role,
|
|
1513
|
+
description: formatRoleProfileSummary(currentRoleProfileState(role, deps)),
|
|
1514
|
+
})),
|
|
1515
|
+
{
|
|
1516
|
+
value: "__done__",
|
|
1517
|
+
label: "Done",
|
|
1518
|
+
description: "finish grouped role editing",
|
|
1519
|
+
},
|
|
1520
|
+
],
|
|
1521
|
+
{ defaultIndex: 0 },
|
|
1522
|
+
);
|
|
1523
|
+
if (!roleChoice || roleChoice.value === "__done__") {
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
await promptRoleExecutionProfile(ui, roleChoice.value, deps);
|
|
1527
|
+
remaining.delete(roleChoice.value);
|
|
1528
|
+
if (!remaining.size) {
|
|
1529
|
+
break;
|
|
1530
|
+
}
|
|
1531
|
+
if (!await promptYesNo(ui, "Edit another role?", false)) {
|
|
1532
|
+
break;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
async function resolveTelegramServerBindingDetails(envConfig, flags, deps) {
|
|
1539
|
+
const current = safeObject(envConfig);
|
|
1540
|
+
if (!String(current.serverBotID || "").trim() && !String(current.botUsername || "").trim() && !String(current.botKey || "").trim()) {
|
|
1541
|
+
return null;
|
|
1542
|
+
}
|
|
1543
|
+
const lookup = await requireDependency(deps, "listServerBots")({
|
|
1544
|
+
provider: "telegram",
|
|
1545
|
+
baseURL: flags["base-url"] || deps.defaultSiteURL,
|
|
1546
|
+
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
1547
|
+
});
|
|
1548
|
+
if (!lookup?.ok) {
|
|
1549
|
+
return {
|
|
1550
|
+
ok: false,
|
|
1551
|
+
mode: "lookup_error",
|
|
1552
|
+
matchedBy: "",
|
|
1553
|
+
name: "",
|
|
1554
|
+
role: "",
|
|
1555
|
+
roles: [],
|
|
1556
|
+
effectiveRoleProfile: null,
|
|
1557
|
+
effectiveRoleProfiles: {},
|
|
1558
|
+
detail: `server bot lookup unavailable: ${lookup?.error || "unknown error"}`,
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
const bots = ensureArray(lookup.bots).map((bot) => ({
|
|
1562
|
+
id: String(bot?.id || "").trim(),
|
|
1563
|
+
role: String(bot?.role || bot?.bot_role || "").trim(),
|
|
1564
|
+
name: String(bot?.name || "").trim(),
|
|
1565
|
+
})).filter((bot) => bot.id);
|
|
1566
|
+
const resolveGroupedPayload = (matches, matchedBy) => {
|
|
1567
|
+
const roles = preferredRoleSort(summarizeServerBotRoles(matches));
|
|
1568
|
+
return {
|
|
1569
|
+
ok: true,
|
|
1570
|
+
mode: "group",
|
|
1571
|
+
matchedBy,
|
|
1572
|
+
name: String(matches[0]?.name || "").trim(),
|
|
1573
|
+
role: "",
|
|
1574
|
+
roles,
|
|
1575
|
+
serverBotID: String(current.serverBotID || "").trim(),
|
|
1576
|
+
effectiveRoleProfile: null,
|
|
1577
|
+
effectiveRoleProfiles: buildGroupedRoleProfileOutput(roles, deps),
|
|
1578
|
+
detail: `${String(matches[0]?.name || "").trim() || "(unnamed)"} roles: ${roles.join(", ") || "-"}`,
|
|
1579
|
+
};
|
|
1580
|
+
};
|
|
1581
|
+
if (String(current.serverBotID || "").trim()) {
|
|
1582
|
+
const match = bots.find((bot) => bot.id === String(current.serverBotID || "").trim());
|
|
1583
|
+
if (!match) {
|
|
1584
|
+
return {
|
|
1585
|
+
ok: false,
|
|
1586
|
+
mode: "missing",
|
|
1587
|
+
matchedBy: "server_bot_id",
|
|
1588
|
+
name: "",
|
|
1589
|
+
role: "",
|
|
1590
|
+
roles: [],
|
|
1591
|
+
serverBotID: String(current.serverBotID || "").trim(),
|
|
1592
|
+
effectiveRoleProfile: null,
|
|
1593
|
+
effectiveRoleProfiles: {},
|
|
1594
|
+
detail: `server bot ${current.serverBotID} not found`,
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
const sameNameMatches = bots.filter((bot) => normalizeServerBotIdentityText(bot.name) === normalizeServerBotIdentityText(match.name));
|
|
1598
|
+
if (sameNameMatches.length > 1) {
|
|
1599
|
+
return resolveGroupedPayload(sameNameMatches, "server_bot_id");
|
|
1600
|
+
}
|
|
1601
|
+
const effectiveRoleProfile = buildRoleProfileOutput(match.role || current.roleProfile || "", deps);
|
|
1602
|
+
return {
|
|
1603
|
+
ok: true,
|
|
1604
|
+
mode: "single",
|
|
1605
|
+
matchedBy: "server_bot_id",
|
|
1606
|
+
name: match.name,
|
|
1607
|
+
role: match.role,
|
|
1608
|
+
roles: summarizeServerBotRoles([match]),
|
|
1609
|
+
serverBotID: match.id,
|
|
1610
|
+
effectiveRoleProfile,
|
|
1611
|
+
effectiveRoleProfiles: {},
|
|
1612
|
+
detail: `${String(match.name || "").trim() || "(unnamed)"} [${String(match.role || "").trim() || "-"}]`,
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
const normalizedServerIdentity = normalizeServerBotIdentityText(current.botUsername || current.botKey);
|
|
1616
|
+
const matches = bots.filter((bot) => normalizeServerBotIdentityText(bot.name) === normalizedServerIdentity);
|
|
1617
|
+
if (!matches.length) {
|
|
1618
|
+
return {
|
|
1619
|
+
ok: false,
|
|
1620
|
+
mode: "missing",
|
|
1621
|
+
matchedBy: "server_bot_name",
|
|
1622
|
+
name: "",
|
|
1623
|
+
role: "",
|
|
1624
|
+
roles: [],
|
|
1625
|
+
serverBotID: "",
|
|
1626
|
+
effectiveRoleProfile: null,
|
|
1627
|
+
effectiveRoleProfiles: {},
|
|
1628
|
+
detail: `no server bot matched ${current.botUsername ? `@${current.botUsername}` : current.botKey}`,
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
if (matches.length > 1) {
|
|
1632
|
+
return resolveGroupedPayload(matches, "server_bot_name");
|
|
1633
|
+
}
|
|
1634
|
+
const match = matches[0] || {};
|
|
1635
|
+
const effectiveRoleProfile = buildRoleProfileOutput(match.role || current.roleProfile || "", deps);
|
|
1636
|
+
return {
|
|
1637
|
+
ok: true,
|
|
1638
|
+
mode: "single",
|
|
1639
|
+
matchedBy: "server_bot_name",
|
|
1640
|
+
name: String(match.name || "").trim(),
|
|
1641
|
+
role: String(match.role || "").trim(),
|
|
1642
|
+
roles: summarizeServerBotRoles([match]),
|
|
1643
|
+
serverBotID: String(match.id || "").trim(),
|
|
1644
|
+
effectiveRoleProfile,
|
|
1645
|
+
effectiveRoleProfiles: {},
|
|
1646
|
+
detail: `${String(match.name || "").trim() || "(unnamed)"} [${String(match.role || "").trim() || "-"}]`,
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1288
1650
|
function buildTemporaryTelegramEnvConfig({ token, apiBaseURL }) {
|
|
1289
1651
|
return {
|
|
1290
1652
|
ok: true,
|
|
@@ -1300,8 +1662,11 @@ async function verifyTelegramTokenCandidate(provider, token, apiBaseURL, timeout
|
|
|
1300
1662
|
|
|
1301
1663
|
async function autoResolveTelegramServerBot(current, flags, deps) {
|
|
1302
1664
|
const existingBotID = String(current?.serverBotID || "").trim();
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1665
|
+
const preferredIdentity = firstNonEmptyString([
|
|
1666
|
+
current?.username,
|
|
1667
|
+
current?.key,
|
|
1668
|
+
]);
|
|
1669
|
+
if (!preferredIdentity) {
|
|
1305
1670
|
return {
|
|
1306
1671
|
botID: existingBotID,
|
|
1307
1672
|
role: String(current?.roleProfile || "").trim(),
|
|
@@ -1314,14 +1679,14 @@ async function autoResolveTelegramServerBot(current, flags, deps) {
|
|
|
1314
1679
|
return await resolveServerBotForNonInteractive(
|
|
1315
1680
|
"telegram",
|
|
1316
1681
|
{
|
|
1317
|
-
"bot-name":
|
|
1682
|
+
"bot-name": preferredIdentity,
|
|
1318
1683
|
"base-url": flags["base-url"] || deps.defaultSiteURL,
|
|
1319
1684
|
"timeout-seconds": intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
1320
1685
|
},
|
|
1321
1686
|
deps,
|
|
1322
1687
|
{
|
|
1323
|
-
preferredUsername,
|
|
1324
|
-
preferredName:
|
|
1688
|
+
preferredUsername: preferredIdentity,
|
|
1689
|
+
preferredName: preferredIdentity,
|
|
1325
1690
|
},
|
|
1326
1691
|
);
|
|
1327
1692
|
} catch {
|
|
@@ -1680,53 +2045,12 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
1680
2045
|
envConfig,
|
|
1681
2046
|
intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
1682
2047
|
);
|
|
1683
|
-
let serverBinding = null;
|
|
1684
2048
|
let overallOK = Boolean(result.ok);
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 15) || 15,
|
|
1690
|
-
});
|
|
1691
|
-
if (!lookup?.ok) {
|
|
1692
|
-
serverBinding = {
|
|
1693
|
-
ok: false,
|
|
1694
|
-
detail: `server bot lookup unavailable: ${lookup?.error || "unknown error"}`,
|
|
1695
|
-
};
|
|
2049
|
+
let serverBinding = null;
|
|
2050
|
+
if (provider === "telegram") {
|
|
2051
|
+
serverBinding = await resolveTelegramServerBindingDetails(envConfig, flags, deps);
|
|
2052
|
+
if (serverBinding && !serverBinding.ok) {
|
|
1696
2053
|
overallOK = false;
|
|
1697
|
-
} else {
|
|
1698
|
-
const normalizedServerIdentity = normalizeServerBotIdentityText(envConfig.botUsername || envConfig.botKey);
|
|
1699
|
-
if (String(envConfig.serverBotID || "").trim()) {
|
|
1700
|
-
const match = ensureArray(lookup.bots).find((bot) => String(bot.id || "").trim() === String(envConfig.serverBotID || "").trim());
|
|
1701
|
-
if (!match) {
|
|
1702
|
-
serverBinding = {
|
|
1703
|
-
ok: false,
|
|
1704
|
-
detail: `server bot ${envConfig.serverBotID} not found`,
|
|
1705
|
-
};
|
|
1706
|
-
overallOK = false;
|
|
1707
|
-
} else {
|
|
1708
|
-
serverBinding = {
|
|
1709
|
-
ok: true,
|
|
1710
|
-
detail: `${String(match.name || "").trim() || "(unnamed)"} [${String(match.role || "").trim() || "-"}]`,
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
} else {
|
|
1714
|
-
const matches = ensureArray(lookup.bots).filter(
|
|
1715
|
-
(bot) => normalizeServerBotIdentityText(bot?.name) === normalizedServerIdentity,
|
|
1716
|
-
);
|
|
1717
|
-
if (!matches.length) {
|
|
1718
|
-
serverBinding = {
|
|
1719
|
-
ok: false,
|
|
1720
|
-
detail: `no server bot matched ${envConfig.botUsername ? `@${envConfig.botUsername}` : envConfig.botKey}`,
|
|
1721
|
-
};
|
|
1722
|
-
overallOK = false;
|
|
1723
|
-
} else {
|
|
1724
|
-
serverBinding = {
|
|
1725
|
-
ok: true,
|
|
1726
|
-
detail: `${String(matches[0]?.name || "").trim() || "(unnamed)"} roles: ${summarizeServerBotRoles(matches).join(", ") || "-"}`,
|
|
1727
|
-
};
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
2054
|
}
|
|
1731
2055
|
}
|
|
1732
2056
|
const interactiveJsonChoice = (
|
|
@@ -1763,21 +2087,33 @@ async function verifyProviderEntry(ui, provider, flags, deps) {
|
|
|
1763
2087
|
}, null, 2)}\n`,
|
|
1764
2088
|
);
|
|
1765
2089
|
} else {
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
2090
|
+
const lines = [
|
|
2091
|
+
`${providerLabel(provider, deps)} verify: ${overallOK ? "OK" : "FAIL"}`,
|
|
2092
|
+
`file: ${envConfig.filePath}`,
|
|
2093
|
+
provider === "telegram" ? `bot_key: ${envConfig.botKey || "-"}` : "",
|
|
2094
|
+
provider === "telegram" ? `server_bot_id: ${envConfig.serverBotID || "-"}` : "",
|
|
2095
|
+
provider === "telegram" ? `role_profile: ${envConfig.roleProfile || "-"}` : "",
|
|
2096
|
+
provider === "telegram" ? `ai_client: ${envConfig.client || "-"}` : "",
|
|
2097
|
+
provider === "telegram" ? `ai_model: ${envConfig.model || "-"}` : "",
|
|
2098
|
+
provider === "telegram" ? `permission_mode: ${envConfig.permissionMode || "-"}` : "",
|
|
2099
|
+
provider === "telegram" ? `reasoning_effort: ${envConfig.reasoningEffort || "-"}` : "",
|
|
2100
|
+
`detail: ${result.detail || "-"}`,
|
|
2101
|
+
serverBinding ? `server_binding: ${serverBinding.ok ? "OK" : "FAIL"}${serverBinding.detail ? ` (${serverBinding.detail})` : ""}` : "",
|
|
2102
|
+
].filter(Boolean);
|
|
2103
|
+
if (provider === "telegram" && serverBinding) {
|
|
2104
|
+
lines.push(`server_binding_mode: ${serverBinding.mode || "-"}`);
|
|
2105
|
+
lines.push(`server_bot_name: ${serverBinding.name || "-"}`);
|
|
2106
|
+
lines.push(`server_roles: ${ensureArray(serverBinding.roles).join(", ") || "-"}`);
|
|
2107
|
+
if (serverBinding.mode === "group") {
|
|
2108
|
+
const groupedProfiles = safeObject(serverBinding.effectiveRoleProfiles);
|
|
2109
|
+
Object.keys(groupedProfiles).forEach((role) => {
|
|
2110
|
+
lines.push(`runtime_role_profile[${role}]: ${formatRoleProfileOutputLine(groupedProfiles[role])}`);
|
|
2111
|
+
});
|
|
2112
|
+
} else if (serverBinding.effectiveRoleProfile) {
|
|
2113
|
+
lines.push(`runtime_role_profile: ${formatRoleProfileOutputLine(serverBinding.effectiveRoleProfile)}`);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
1781
2117
|
}
|
|
1782
2118
|
if (!overallOK) {
|
|
1783
2119
|
process.exitCode = 1;
|
|
@@ -1936,12 +2272,21 @@ async function runBotShow(ui, flags, deps, explicitProvider = "") {
|
|
|
1936
2272
|
const state = loadProviderEnvState(provider, deps);
|
|
1937
2273
|
if (provider === "telegram") {
|
|
1938
2274
|
const entry = await resolveTelegramEntryForShow(ui, state.parsed, flags, deps);
|
|
1939
|
-
const
|
|
2275
|
+
const serverBinding = await resolveTelegramServerBindingDetails(
|
|
2276
|
+
requireDependency(deps, "loadProviderEnvConfig")(provider, {
|
|
2277
|
+
botKey: entry.key,
|
|
2278
|
+
botID: entry.serverBotID,
|
|
2279
|
+
botName: entry.username || entry.key,
|
|
2280
|
+
}),
|
|
2281
|
+
flags,
|
|
2282
|
+
deps,
|
|
2283
|
+
);
|
|
2284
|
+
const payload = buildBotShowPayload(provider, state, entry, deps, { serverBinding });
|
|
1940
2285
|
if (boolFromRaw(flags.json, false)) {
|
|
1941
2286
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1942
2287
|
return;
|
|
1943
2288
|
}
|
|
1944
|
-
printBotShow(provider, state, entry, deps);
|
|
2289
|
+
printBotShow(provider, state, entry, deps, { serverBinding });
|
|
1945
2290
|
return;
|
|
1946
2291
|
}
|
|
1947
2292
|
const payload = buildBotShowPayload(provider, state, null, deps);
|
|
@@ -341,6 +341,73 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
341
341
|
&& String(groupedState.TELEGRAM_BOT_RYOAI_BOT_AI_PERMISSION_MODE || "") === "",
|
|
342
342
|
`username=${String(groupedState.TELEGRAM_BOT_RYOAI_BOT_USERNAME || "")} server_bot_id=${String(groupedState.TELEGRAM_BOT_RYOAI_BOT_SERVER_BOT_ID || "")} role=${String(groupedState.TELEGRAM_BOT_RYOAI_BOT_ROLE_PROFILE || "")}`,
|
|
343
343
|
);
|
|
344
|
+
|
|
345
|
+
await runCLI({
|
|
346
|
+
cliPath,
|
|
347
|
+
args: [
|
|
348
|
+
"bot", "edit",
|
|
349
|
+
"--base-url", `http://127.0.0.1:${groupedMock.port}`,
|
|
350
|
+
"--timeout-seconds", "5",
|
|
351
|
+
],
|
|
352
|
+
env: {
|
|
353
|
+
...env,
|
|
354
|
+
METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
|
|
355
|
+
"1", // provider: telegram
|
|
356
|
+
"2", // bot entry: ryoai_bot
|
|
357
|
+
"1", // keep username
|
|
358
|
+
"1", // keep token
|
|
359
|
+
"2", // grouped role settings: edit one role
|
|
360
|
+
"3", // select role to edit: worker
|
|
361
|
+
"2", // worker: edit settings
|
|
362
|
+
"3", // worker AI client: claude
|
|
363
|
+
"worker-sonnet-4",
|
|
364
|
+
"4", // worker permission: danger_full_access
|
|
365
|
+
"4", // worker reasoning: high
|
|
366
|
+
"y", // edit another role
|
|
367
|
+
"3", // select role to edit: approval
|
|
368
|
+
"2", // approval: edit settings
|
|
369
|
+
"4", // approval AI client: gemini
|
|
370
|
+
"approval-pro-2",
|
|
371
|
+
"4", // approval permission: danger_full_access
|
|
372
|
+
"4", // approval reasoning: high
|
|
373
|
+
"n", // stop editing roles
|
|
374
|
+
"1", // keep current default setting
|
|
375
|
+
"y", // save
|
|
376
|
+
]),
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
const groupedRunnerConfigPath = path.join(tempHome, ".metheus", "bot-runner.json");
|
|
380
|
+
const groupedRunnerConfig = readJSON(fs.readFileSync(groupedRunnerConfigPath, "utf8"));
|
|
381
|
+
push(
|
|
382
|
+
"bot_edit_grouped_server_roles_updates_role_profiles",
|
|
383
|
+
String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).client || "") === "claude"
|
|
384
|
+
&& String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).model || "") === "worker-sonnet-4"
|
|
385
|
+
&& String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).permission_mode || "") === "danger_full_access"
|
|
386
|
+
&& String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).reasoning_effort || "") === "high"
|
|
387
|
+
&& String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).client || "") === "gemini"
|
|
388
|
+
&& String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).model || "") === "approval-pro-2",
|
|
389
|
+
`worker=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker))} approval=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval))}`,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
const groupedShowResult = await runCLI({
|
|
393
|
+
cliPath,
|
|
394
|
+
args: [
|
|
395
|
+
"bot", "show",
|
|
396
|
+
"--provider", "telegram",
|
|
397
|
+
"--bot-key", "ryoai_bot",
|
|
398
|
+
"--base-url", `http://127.0.0.1:${groupedMock.port}`,
|
|
399
|
+
"--json", "true",
|
|
400
|
+
],
|
|
401
|
+
env,
|
|
402
|
+
});
|
|
403
|
+
const groupedShowPayload = readJSON(groupedShowResult.stdout);
|
|
404
|
+
push(
|
|
405
|
+
"bot_show_reports_grouped_server_roles",
|
|
406
|
+
safeObject(groupedShowPayload.serverBinding).mode === "group"
|
|
407
|
+
&& safeObject(safeObject(groupedShowPayload.serverBinding).effectiveRoleProfiles).worker?.client === "claude"
|
|
408
|
+
&& safeObject(safeObject(groupedShowPayload.serverBinding).effectiveRoleProfiles).approval?.client === "gemini",
|
|
409
|
+
`mode=${String(safeObject(groupedShowPayload.serverBinding).mode || "")} worker=${String(safeObject(safeObject(groupedShowPayload.serverBinding).effectiveRoleProfiles).worker?.client || "")} approval=${String(safeObject(safeObject(groupedShowPayload.serverBinding).effectiveRoleProfiles).approval?.client || "")}`,
|
|
410
|
+
);
|
|
344
411
|
} finally {
|
|
345
412
|
await groupedMock.close();
|
|
346
413
|
await runCLI({
|
|
@@ -361,6 +428,7 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
361
428
|
"bot", "show",
|
|
362
429
|
"--provider", "telegram",
|
|
363
430
|
"--bot-key", "monitorselftestbot",
|
|
431
|
+
"--base-url", baseURL,
|
|
364
432
|
"--json", "true",
|
|
365
433
|
],
|
|
366
434
|
env,
|
|
@@ -369,8 +437,9 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
369
437
|
push(
|
|
370
438
|
"bot_show_returns_selected_telegram_entry",
|
|
371
439
|
safeObject(showPayload.entry).key === "monitorselftestbot"
|
|
372
|
-
&& safeObject(showPayload.entry).client === "codex"
|
|
373
|
-
|
|
440
|
+
&& safeObject(showPayload.entry).client === "codex"
|
|
441
|
+
&& safeObject(showPayload.serverBinding).mode === "single",
|
|
442
|
+
`key=${String(safeObject(showPayload.entry).key || "")} client=${String(safeObject(showPayload.entry).client || "")} mode=${String(safeObject(showPayload.serverBinding).mode || "")}`,
|
|
374
443
|
);
|
|
375
444
|
|
|
376
445
|
await runCLI({
|
|
@@ -391,7 +460,6 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
391
460
|
"3", // workspace_write
|
|
392
461
|
"2", // change reasoning effort
|
|
393
462
|
"3", // medium
|
|
394
|
-
"1", // keep bot key
|
|
395
463
|
"1", // keep default setting
|
|
396
464
|
"y", // save
|
|
397
465
|
]),
|
|
@@ -473,8 +541,9 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
473
541
|
"bot_verify_guided_can_emit_json",
|
|
474
542
|
guidedVerifyPayload.ok === true
|
|
475
543
|
&& safeObject(guidedVerifyPayload.serverBinding).ok === true
|
|
544
|
+
&& safeObject(guidedVerifyPayload.serverBinding).mode === "single"
|
|
476
545
|
&& String(guidedVerifyPayload.client || "") === "claude",
|
|
477
|
-
`verify=${String(guidedVerifyPayload.ok)} server=${String(safeObject(guidedVerifyPayload.serverBinding).detail || "")}`,
|
|
546
|
+
`verify=${String(guidedVerifyPayload.ok)} mode=${String(safeObject(guidedVerifyPayload.serverBinding).mode || "")} server=${String(safeObject(guidedVerifyPayload.serverBinding).detail || "")}`,
|
|
478
547
|
);
|
|
479
548
|
|
|
480
549
|
await runCLI({
|
|
@@ -511,8 +580,9 @@ export async function runSelftestBotCommands(push, deps) {
|
|
|
511
580
|
"bot_verify_cross_checks_server_bot_binding",
|
|
512
581
|
verifyPayload.ok === true
|
|
513
582
|
&& safeObject(verifyPayload.serverBinding).ok === true
|
|
583
|
+
&& safeObject(verifyPayload.serverBinding).mode === "single"
|
|
514
584
|
&& String(verifyPayload.client || "") === "claude",
|
|
515
|
-
`verify=${String(verifyPayload.ok)} server=${String(safeObject(verifyPayload.serverBinding).detail || "")}`,
|
|
585
|
+
`verify=${String(verifyPayload.ok)} mode=${String(safeObject(verifyPayload.serverBinding).mode || "")} server=${String(safeObject(verifyPayload.serverBinding).detail || "")}`,
|
|
516
586
|
);
|
|
517
587
|
|
|
518
588
|
await runCLI({
|