metheus-governance-mcp-cli 0.2.74 → 0.2.76
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 +149 -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,21 @@ 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
|
|
374
|
+
const promptLabel = defaultLabel ? `default=${defaultLabel}` : "enter number";
|
|
375
|
+
const answer = String(await ui.ask(`${colorText(">", "32")} ${promptLabel}: `) || "").trim();
|
|
262
376
|
if (!answer && defaultLabel) {
|
|
263
377
|
return list[defaultIndex];
|
|
264
378
|
}
|
|
@@ -267,7 +381,7 @@ async function promptChoice(ui, title, options, { defaultIndex = 0, allowCancel
|
|
|
267
381
|
if (Number.isFinite(numeric) && numeric >= 1 && numeric <= list.length) {
|
|
268
382
|
return list[numeric - 1];
|
|
269
383
|
}
|
|
270
|
-
process.stdout.write("Select a valid number.\n");
|
|
384
|
+
process.stdout.write(`${colorText("Select a valid number.\n", "31")}`);
|
|
271
385
|
}
|
|
272
386
|
}
|
|
273
387
|
|
|
@@ -2254,6 +2368,9 @@ async function removeTokenOnlyProvider(ui, provider, flags, deps) {
|
|
|
2254
2368
|
}
|
|
2255
2369
|
|
|
2256
2370
|
async function runBotSetup(ui, flags, deps) {
|
|
2371
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2372
|
+
ui.setFlow("BOT SETUP", "Guided local bot management");
|
|
2373
|
+
}
|
|
2257
2374
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2258
2375
|
const telegramActions = [
|
|
2259
2376
|
{ value: "list", label: "List bots" },
|
|
@@ -2384,6 +2501,9 @@ async function runBotShow(ui, flags, deps, explicitProvider = "") {
|
|
|
2384
2501
|
}
|
|
2385
2502
|
|
|
2386
2503
|
async function runBotAdd(ui, flags, deps) {
|
|
2504
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2505
|
+
ui.setFlow("BOT ADD", "Create a local bot entry from server bot identity and local token");
|
|
2506
|
+
}
|
|
2387
2507
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2388
2508
|
if (provider === "telegram") {
|
|
2389
2509
|
await addTelegramBot(ui, flags, deps);
|
|
@@ -2393,6 +2513,9 @@ async function runBotAdd(ui, flags, deps) {
|
|
|
2393
2513
|
}
|
|
2394
2514
|
|
|
2395
2515
|
async function runBotEdit(ui, flags, deps) {
|
|
2516
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2517
|
+
ui.setFlow("BOT EDIT", "Update an existing local bot entry");
|
|
2518
|
+
}
|
|
2396
2519
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2397
2520
|
if (provider === "telegram") {
|
|
2398
2521
|
await editTelegramBot(ui, flags, deps);
|
|
@@ -2402,6 +2525,9 @@ async function runBotEdit(ui, flags, deps) {
|
|
|
2402
2525
|
}
|
|
2403
2526
|
|
|
2404
2527
|
async function runBotRemove(ui, flags, deps) {
|
|
2528
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2529
|
+
ui.setFlow("BOT REMOVE", "Delete a local bot entry");
|
|
2530
|
+
}
|
|
2405
2531
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2406
2532
|
if (provider === "telegram") {
|
|
2407
2533
|
const nonInteractive = boolFromRaw(flags["non-interactive"] ?? flags.yes, false);
|
|
@@ -2427,11 +2553,17 @@ async function runBotRemove(ui, flags, deps) {
|
|
|
2427
2553
|
}
|
|
2428
2554
|
|
|
2429
2555
|
async function runBotVerify(ui, flags, deps) {
|
|
2556
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2557
|
+
ui.setFlow("BOT VERIFY", "Check local token, server binding, and linked routes");
|
|
2558
|
+
}
|
|
2430
2559
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2431
2560
|
await verifyProviderEntry(ui, provider, flags, deps);
|
|
2432
2561
|
}
|
|
2433
2562
|
|
|
2434
2563
|
async function runBotSetDefault(ui, flags, deps) {
|
|
2564
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2565
|
+
ui.setFlow("BOT SET-DEFAULT", "Choose the default Telegram local entry");
|
|
2566
|
+
}
|
|
2435
2567
|
const provider = await selectProvider(ui, flags.provider, deps);
|
|
2436
2568
|
if (provider !== "telegram") {
|
|
2437
2569
|
throw new Error("bot set-default currently supports only --provider telegram");
|
|
@@ -2461,6 +2593,9 @@ async function runBotSetDefault(ui, flags, deps) {
|
|
|
2461
2593
|
}
|
|
2462
2594
|
|
|
2463
2595
|
async function runBotMigrate(ui, flags, deps) {
|
|
2596
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2597
|
+
ui.setFlow("BOT MIGRATE", "Convert legacy Telegram token config into a named entry");
|
|
2598
|
+
}
|
|
2464
2599
|
const provider = String(flags.provider || "").trim()
|
|
2465
2600
|
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
2466
2601
|
: "telegram";
|
|
@@ -2511,6 +2646,9 @@ async function runBotMigrate(ui, flags, deps) {
|
|
|
2511
2646
|
}
|
|
2512
2647
|
|
|
2513
2648
|
async function runBotGlobal(ui, flags, deps) {
|
|
2649
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
2650
|
+
ui.setFlow("BOT GLOBAL", "Edit Telegram global local settings");
|
|
2651
|
+
}
|
|
2514
2652
|
const provider = String(flags.provider || "").trim()
|
|
2515
2653
|
? requireDependency(deps, "normalizeBotProvider")(flags.provider)
|
|
2516
2654
|
: "telegram";
|