metheus-governance-mcp-cli 0.2.74 → 0.2.75
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/lib/bot-commands.mjs +148 -11
- package/package.json +1 -1
package/lib/bot-commands.mjs
CHANGED
|
@@ -108,6 +108,70 @@ function maskSecret(rawValue) {
|
|
|
108
108
|
return `${text.slice(0, 4)}...${text.slice(-4)}`;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
function supportsANSIColors() {
|
|
112
|
+
if (process.env.NO_COLOR) return false;
|
|
113
|
+
if (!process.stdout?.isTTY) return false;
|
|
114
|
+
const term = String(process.env.TERM || "").trim().toLowerCase();
|
|
115
|
+
return term !== "dumb";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function colorText(text, colorCode) {
|
|
119
|
+
const value = String(text || "");
|
|
120
|
+
if (!value || !supportsANSIColors()) return value;
|
|
121
|
+
return `\u001b[${colorCode}m${value}\u001b[0m`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function padRight(text, width) {
|
|
125
|
+
const value = String(text || "");
|
|
126
|
+
if (value.length >= width) return value.slice(0, width);
|
|
127
|
+
return `${value}${" ".repeat(width - value.length)}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function wrapAsciiLine(text, width) {
|
|
131
|
+
const value = String(text || "");
|
|
132
|
+
if (!value) return [""];
|
|
133
|
+
const words = value.split(/\s+/).filter(Boolean);
|
|
134
|
+
if (!words.length) return [""];
|
|
135
|
+
const lines = [];
|
|
136
|
+
let current = "";
|
|
137
|
+
words.forEach((word) => {
|
|
138
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
139
|
+
if (candidate.length <= width) {
|
|
140
|
+
current = candidate;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (current) {
|
|
144
|
+
lines.push(current);
|
|
145
|
+
current = word;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
let remaining = word;
|
|
149
|
+
while (remaining.length > width) {
|
|
150
|
+
lines.push(remaining.slice(0, width));
|
|
151
|
+
remaining = remaining.slice(width);
|
|
152
|
+
}
|
|
153
|
+
current = remaining;
|
|
154
|
+
});
|
|
155
|
+
if (current) lines.push(current);
|
|
156
|
+
return lines.length ? lines : [""];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function formatAsciiBlock(title, lines = [], { accentColor = "36" } = {}) {
|
|
160
|
+
const contentWidth = 62;
|
|
161
|
+
const blockLines = [
|
|
162
|
+
title ? String(title || "").trim() : "",
|
|
163
|
+
...ensureArray(lines).flatMap((line) => wrapAsciiLine(line, contentWidth)),
|
|
164
|
+
].filter((line, index, all) => !(index > 0 && !line && !all[index - 1]));
|
|
165
|
+
const border = `+${"-".repeat(contentWidth + 2)}+`;
|
|
166
|
+
const formatted = [colorText(border, accentColor)];
|
|
167
|
+
blockLines.forEach((line, index) => {
|
|
168
|
+
const rendered = `| ${padRight(line, contentWidth)} |`;
|
|
169
|
+
formatted.push(index === 0 ? colorText(rendered, accentColor) : rendered);
|
|
170
|
+
});
|
|
171
|
+
formatted.push(colorText(border, accentColor));
|
|
172
|
+
return `${formatted.join("\n")}\n`;
|
|
173
|
+
}
|
|
174
|
+
|
|
111
175
|
function formatEnvValue(rawValue) {
|
|
112
176
|
const text = String(rawValue ?? "");
|
|
113
177
|
if (!text) return "";
|
|
@@ -125,6 +189,13 @@ function providerTokenKey(provider, deps) {
|
|
|
125
189
|
return safeObject(requireDependency(deps, "providerEnvConfig")(provider)).tokenKey || "";
|
|
126
190
|
}
|
|
127
191
|
|
|
192
|
+
function shouldRenderPromptChrome(flags) {
|
|
193
|
+
const parsedFlags = safeObject(flags);
|
|
194
|
+
if (boolFromRaw(parsedFlags["non-interactive"] ?? parsedFlags.yes, false)) return false;
|
|
195
|
+
if (boolFromRaw(parsedFlags.json, false)) return false;
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
128
199
|
function printBotUsage(deps) {
|
|
129
200
|
const cliName = String(deps?.cliName || "metheus-governance-mcp-cli").trim() || "metheus-governance-mcp-cli";
|
|
130
201
|
process.stdout.write(
|
|
@@ -156,6 +227,24 @@ function printBotUsage(deps) {
|
|
|
156
227
|
}
|
|
157
228
|
|
|
158
229
|
function createPrompter() {
|
|
230
|
+
let flowTitle = "";
|
|
231
|
+
let flowSubtitle = "";
|
|
232
|
+
let stepIndex = 0;
|
|
233
|
+
function write(text) {
|
|
234
|
+
process.stdout.write(String(text || ""));
|
|
235
|
+
}
|
|
236
|
+
function printFlowBanner() {
|
|
237
|
+
if (!flowTitle) return;
|
|
238
|
+
const lines = flowSubtitle ? [flowSubtitle] : [];
|
|
239
|
+
write(`\n${formatAsciiBlock(flowTitle, lines, { accentColor: "35" })}`);
|
|
240
|
+
}
|
|
241
|
+
function beginPrompt(title, lines = []) {
|
|
242
|
+
stepIndex += 1;
|
|
243
|
+
const bannerTitle = flowTitle
|
|
244
|
+
? `${flowTitle} | STEP ${stepIndex}`
|
|
245
|
+
: `STEP ${stepIndex}`;
|
|
246
|
+
write(`\n${formatAsciiBlock(bannerTitle, [title, ...ensureArray(lines)], { accentColor: "36" })}`);
|
|
247
|
+
}
|
|
159
248
|
const scriptedPromptAnswersRaw = String(process.env.METHEUS_SCRIPTED_PROMPT_ANSWERS || "").trim();
|
|
160
249
|
if (scriptedPromptAnswersRaw) {
|
|
161
250
|
let scriptedAnswers = [];
|
|
@@ -167,17 +256,31 @@ function createPrompter() {
|
|
|
167
256
|
}
|
|
168
257
|
let answerIndex = 0;
|
|
169
258
|
return {
|
|
259
|
+
setFlow(title, subtitle = "") {
|
|
260
|
+
flowTitle = String(title || "").trim();
|
|
261
|
+
flowSubtitle = String(subtitle || "").trim();
|
|
262
|
+
stepIndex = 0;
|
|
263
|
+
printFlowBanner();
|
|
264
|
+
},
|
|
265
|
+
beginPrompt,
|
|
170
266
|
ask(promptText) {
|
|
171
|
-
|
|
267
|
+
write(promptText);
|
|
172
268
|
const answer = answerIndex < scriptedAnswers.length ? scriptedAnswers[answerIndex] : "";
|
|
173
269
|
answerIndex += 1;
|
|
174
|
-
|
|
270
|
+
write(`${answer}\n`);
|
|
175
271
|
return Promise.resolve(String(answer || ""));
|
|
176
272
|
},
|
|
177
273
|
close() {},
|
|
178
274
|
};
|
|
179
275
|
}
|
|
180
276
|
return {
|
|
277
|
+
setFlow(title, subtitle = "") {
|
|
278
|
+
flowTitle = String(title || "").trim();
|
|
279
|
+
flowSubtitle = String(subtitle || "").trim();
|
|
280
|
+
stepIndex = 0;
|
|
281
|
+
printFlowBanner();
|
|
282
|
+
},
|
|
283
|
+
beginPrompt,
|
|
181
284
|
ask(promptText) {
|
|
182
285
|
return new Promise((resolve) => {
|
|
183
286
|
const rl = readline.createInterface({
|
|
@@ -195,8 +298,13 @@ function createPrompter() {
|
|
|
195
298
|
}
|
|
196
299
|
|
|
197
300
|
async function promptLine(ui, promptText, defaultValue = "") {
|
|
301
|
+
ui.beginPrompt(promptText, [
|
|
302
|
+
String(defaultValue || "").trim()
|
|
303
|
+
? `Press Enter to keep the current value: ${defaultValue}`
|
|
304
|
+
: "Type a value and press Enter.",
|
|
305
|
+
]);
|
|
198
306
|
const suffix = String(defaultValue || "").trim() ? ` [${defaultValue}]` : "";
|
|
199
|
-
const answer = await ui.ask(`${promptText}${suffix}: `);
|
|
307
|
+
const answer = await ui.ask(`${colorText(">", "32")} ${promptText}${suffix}: `);
|
|
200
308
|
const text = String(answer || "").trim();
|
|
201
309
|
return text || String(defaultValue || "").trim();
|
|
202
310
|
}
|
|
@@ -205,18 +313,19 @@ async function promptRequiredLine(ui, promptText, defaultValue = "") {
|
|
|
205
313
|
while (true) {
|
|
206
314
|
const answer = await promptLine(ui, promptText, defaultValue);
|
|
207
315
|
if (answer) return answer;
|
|
208
|
-
process.stdout.write("Value is required.\n");
|
|
316
|
+
process.stdout.write(`${colorText("Value is required.\n", "31")}`);
|
|
209
317
|
}
|
|
210
318
|
}
|
|
211
319
|
|
|
212
320
|
async function promptYesNo(ui, promptText, defaultValue = true) {
|
|
321
|
+
ui.beginPrompt(promptText, ["Choose y or n, then press Enter."]);
|
|
213
322
|
const hint = defaultValue ? "Y/n" : "y/N";
|
|
214
323
|
while (true) {
|
|
215
|
-
const answer = String(await ui.ask(`${promptText} [${hint}]: `) || "").trim().toLowerCase();
|
|
324
|
+
const answer = String(await ui.ask(`${colorText(">", "32")} ${promptText} [${hint}]: `) || "").trim().toLowerCase();
|
|
216
325
|
if (!answer) return defaultValue;
|
|
217
326
|
if (["y", "yes", "1", "true"].includes(answer)) return true;
|
|
218
327
|
if (["n", "no", "0", "false"].includes(answer)) return false;
|
|
219
|
-
process.stdout.write("Choose y or n.\n");
|
|
328
|
+
process.stdout.write(`${colorText("Choose y or n.\n", "31")}`);
|
|
220
329
|
}
|
|
221
330
|
}
|
|
222
331
|
|
|
@@ -249,16 +358,20 @@ async function promptChoice(ui, title, options, { defaultIndex = 0, allowCancel
|
|
|
249
358
|
if (!list.length) {
|
|
250
359
|
throw new Error(`no options available for ${title}`);
|
|
251
360
|
}
|
|
361
|
+
const guidance = [
|
|
362
|
+
"Choose one option by number and press Enter.",
|
|
363
|
+
allowCancel ? "Enter 0 to cancel." : "",
|
|
364
|
+
].filter(Boolean);
|
|
365
|
+
ui.beginPrompt(title, guidance);
|
|
252
366
|
while (true) {
|
|
253
|
-
process.stdout.write(`${title}\n`);
|
|
254
367
|
list.forEach((option, index) => {
|
|
255
|
-
process.stdout.write(` ${index + 1}
|
|
368
|
+
process.stdout.write(` ${colorText(`[${index + 1}]`, "33")} ${formatChoiceLabel(option)}\n`);
|
|
256
369
|
});
|
|
257
370
|
if (allowCancel) {
|
|
258
|
-
process.stdout.write("
|
|
371
|
+
process.stdout.write(` ${colorText("[0]", "33")} Cancel\n`);
|
|
259
372
|
}
|
|
260
373
|
const defaultLabel = defaultIndex >= 0 && defaultIndex < list.length ? String(defaultIndex + 1) : "";
|
|
261
|
-
const answer = String(await ui.ask(
|
|
374
|
+
const answer = String(await ui.ask(`${colorText(">", "32")} ${defaultLabel ? `[${defaultLabel}] ` : ""}`) || "").trim();
|
|
262
375
|
if (!answer && defaultLabel) {
|
|
263
376
|
return list[defaultIndex];
|
|
264
377
|
}
|
|
@@ -267,7 +380,7 @@ async function promptChoice(ui, title, options, { defaultIndex = 0, allowCancel
|
|
|
267
380
|
if (Number.isFinite(numeric) && numeric >= 1 && numeric <= list.length) {
|
|
268
381
|
return list[numeric - 1];
|
|
269
382
|
}
|
|
270
|
-
process.stdout.write("Select a valid number.\n");
|
|
383
|
+
process.stdout.write(`${colorText("Select a valid number.\n", "31")}`);
|
|
271
384
|
}
|
|
272
385
|
}
|
|
273
386
|
|
|
@@ -2254,6 +2367,9 @@ async function removeTokenOnlyProvider(ui, provider, flags, deps) {
|
|
|
2254
2367
|
}
|
|
2255
2368
|
|
|
2256
2369
|
async function runBotSetup(ui, flags, deps) {
|
|
2370
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2371
|
+
ui.setFlow("BOT SETUP", "Guided local bot management");
|
|
2372
|
+
}
|
|
2257
2373
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2258
2374
|
const telegramActions = [
|
|
2259
2375
|
{ value: "list", label: "List bots" },
|
|
@@ -2384,6 +2500,9 @@ async function runBotShow(ui, flags, deps, explicitProvider = "") {
|
|
|
2384
2500
|
}
|
|
2385
2501
|
|
|
2386
2502
|
async function runBotAdd(ui, flags, deps) {
|
|
2503
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2504
|
+
ui.setFlow("BOT ADD", "Create a local bot entry from server bot identity and local token");
|
|
2505
|
+
}
|
|
2387
2506
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2388
2507
|
if (provider === "telegram") {
|
|
2389
2508
|
await addTelegramBot(ui, flags, deps);
|
|
@@ -2393,6 +2512,9 @@ async function runBotAdd(ui, flags, deps) {
|
|
|
2393
2512
|
}
|
|
2394
2513
|
|
|
2395
2514
|
async function runBotEdit(ui, flags, deps) {
|
|
2515
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2516
|
+
ui.setFlow("BOT EDIT", "Update an existing local bot entry");
|
|
2517
|
+
}
|
|
2396
2518
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2397
2519
|
if (provider === "telegram") {
|
|
2398
2520
|
await editTelegramBot(ui, flags, deps);
|
|
@@ -2402,6 +2524,9 @@ async function runBotEdit(ui, flags, deps) {
|
|
|
2402
2524
|
}
|
|
2403
2525
|
|
|
2404
2526
|
async function runBotRemove(ui, flags, deps) {
|
|
2527
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2528
|
+
ui.setFlow("BOT REMOVE", "Delete a local bot entry");
|
|
2529
|
+
}
|
|
2405
2530
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2406
2531
|
if (provider === "telegram") {
|
|
2407
2532
|
const nonInteractive = boolFromRaw(flags["non-interactive"] ?? flags.yes, false);
|
|
@@ -2427,11 +2552,17 @@ async function runBotRemove(ui, flags, deps) {
|
|
|
2427
2552
|
}
|
|
2428
2553
|
|
|
2429
2554
|
async function runBotVerify(ui, flags, deps) {
|
|
2555
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2556
|
+
ui.setFlow("BOT VERIFY", "Check local token, server binding, and linked routes");
|
|
2557
|
+
}
|
|
2430
2558
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2431
2559
|
await verifyProviderEntry(ui, provider, flags, deps);
|
|
2432
2560
|
}
|
|
2433
2561
|
|
|
2434
2562
|
async function runBotSetDefault(ui, flags, deps) {
|
|
2563
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2564
|
+
ui.setFlow("BOT SET-DEFAULT", "Choose the default Telegram local entry");
|
|
2565
|
+
}
|
|
2435
2566
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2436
2567
|
if (provider !== "telegram") {
|
|
2437
2568
|
throw new Error("bot set-default currently supports only --provider telegram");
|
|
@@ -2461,6 +2592,9 @@ async function runBotSetDefault(ui, flags, deps) {
|
|
|
2461
2592
|
}
|
|
2462
2593
|
|
|
2463
2594
|
async function runBotMigrate(ui, flags, deps) {
|
|
2595
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2596
|
+
ui.setFlow("BOT MIGRATE", "Convert legacy Telegram token config into a named entry");
|
|
2597
|
+
}
|
|
2464
2598
|
const provider = String(flags.provider || "").trim()
|
|
2465
2599
|
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
2466
2600
|
: "telegram";
|
|
@@ -2511,6 +2645,9 @@ async function runBotMigrate(ui, flags, deps) {
|
|
|
2511
2645
|
}
|
|
2512
2646
|
|
|
2513
2647
|
async function runBotGlobal(ui, flags, deps) {
|
|
2648
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2649
|
+
ui.setFlow("BOT GLOBAL", "Edit Telegram global local settings");
|
|
2650
|
+
}
|
|
2514
2651
|
const provider = String(flags.provider || "").trim()
|
|
2515
2652
|
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
2516
2653
|
: "telegram";
|