metheus-governance-mcp-cli 0.2.58 → 0.2.59
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/cli.mjs +167 -15
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import readline from "node:readline";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
10
10
|
import { createHash, randomBytes } from "node:crypto";
|
|
11
11
|
import http from "node:http";
|
|
12
12
|
import https from "node:https";
|
|
@@ -1363,7 +1363,7 @@ function buildRunnerShellInvocation(command) {
|
|
|
1363
1363
|
};
|
|
1364
1364
|
}
|
|
1365
1365
|
|
|
1366
|
-
function runLocalAICommand({ command, inputPayload, route, destination }) {
|
|
1366
|
+
async function runLocalAICommand({ command, inputPayload, route, destination }) {
|
|
1367
1367
|
const invocation = buildRunnerShellInvocation(command);
|
|
1368
1368
|
const stdinText = `${JSON.stringify(inputPayload, null, 2)}\n`;
|
|
1369
1369
|
const env = {
|
|
@@ -1374,16 +1374,72 @@ function runLocalAICommand({ command, inputPayload, route, destination }) {
|
|
|
1374
1374
|
METHEUS_RUNNER_CHAT_ID: String(destination.chatID || "").trim(),
|
|
1375
1375
|
METHEUS_RUNNER_DESTINATION_LABEL: String(destination.label || "").trim(),
|
|
1376
1376
|
};
|
|
1377
|
-
const result =
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1377
|
+
const result = await new Promise((resolve, reject) => {
|
|
1378
|
+
const child = spawn(invocation.file, invocation.args, {
|
|
1379
|
+
env,
|
|
1380
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1381
|
+
});
|
|
1382
|
+
let stdoutText = "";
|
|
1383
|
+
let stderrText = "";
|
|
1384
|
+
let stdoutBytes = 0;
|
|
1385
|
+
let stderrBytes = 0;
|
|
1386
|
+
const maxBufferBytes = 4 * 1024 * 1024;
|
|
1387
|
+
let settled = false;
|
|
1388
|
+
|
|
1389
|
+
function finishWithError(error) {
|
|
1390
|
+
if (settled) return;
|
|
1391
|
+
settled = true;
|
|
1392
|
+
try {
|
|
1393
|
+
child.kill();
|
|
1394
|
+
} catch {}
|
|
1395
|
+
reject(error);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function appendOutput(chunk, target) {
|
|
1399
|
+
const text = String(chunk || "");
|
|
1400
|
+
if (!text) return;
|
|
1401
|
+
const bytes = Buffer.byteLength(text);
|
|
1402
|
+
if (target === "stdout") {
|
|
1403
|
+
stdoutBytes += bytes;
|
|
1404
|
+
if (stdoutBytes > maxBufferBytes) {
|
|
1405
|
+
finishWithError(new Error("runner command exceeded stdout buffer limit"));
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
stdoutText += text;
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
stderrBytes += bytes;
|
|
1412
|
+
if (stderrBytes > maxBufferBytes) {
|
|
1413
|
+
finishWithError(new Error("runner command exceeded stderr buffer limit"));
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
stderrText += text;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
child.on("error", (error) => {
|
|
1420
|
+
if (settled) return;
|
|
1421
|
+
settled = true;
|
|
1422
|
+
reject(error);
|
|
1423
|
+
});
|
|
1424
|
+
child.stdout.on("data", (chunk) => appendOutput(chunk, "stdout"));
|
|
1425
|
+
child.stderr.on("data", (chunk) => appendOutput(chunk, "stderr"));
|
|
1426
|
+
child.on("close", (status, signal) => {
|
|
1427
|
+
if (settled) return;
|
|
1428
|
+
settled = true;
|
|
1429
|
+
resolve({
|
|
1430
|
+
status,
|
|
1431
|
+
signal,
|
|
1432
|
+
stdoutText: stdoutText.trim(),
|
|
1433
|
+
stderrText: stderrText.trim(),
|
|
1434
|
+
});
|
|
1435
|
+
});
|
|
1436
|
+
child.stdin.on("error", () => {});
|
|
1437
|
+
child.stdin.end(stdinText);
|
|
1382
1438
|
});
|
|
1383
|
-
const stdoutText = String(result.
|
|
1384
|
-
const stderrText = String(result.
|
|
1385
|
-
if (result.
|
|
1386
|
-
throw new Error(
|
|
1439
|
+
const stdoutText = String(result.stdoutText || "").trim();
|
|
1440
|
+
const stderrText = String(result.stderrText || "").trim();
|
|
1441
|
+
if (result.signal) {
|
|
1442
|
+
throw new Error(stderrText || stdoutText || `runner command terminated by signal ${result.signal}`);
|
|
1387
1443
|
}
|
|
1388
1444
|
if (result.status !== 0) {
|
|
1389
1445
|
throw new Error(stderrText || stdoutText || `runner command exited with status ${result.status}`);
|
|
@@ -1768,12 +1824,41 @@ async function processRunnerRouteOnce(route, runtime, mode) {
|
|
|
1768
1824
|
selectedRecord,
|
|
1769
1825
|
contextWindow,
|
|
1770
1826
|
});
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
inputPayload: aiPayload,
|
|
1774
|
-
route: normalizedRoute,
|
|
1827
|
+
await maybeSendRunnerChatAction({
|
|
1828
|
+
provider: normalizedRoute.provider,
|
|
1775
1829
|
destination,
|
|
1830
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
1831
|
+
action: "typing",
|
|
1776
1832
|
});
|
|
1833
|
+
let typingRequestInFlight = false;
|
|
1834
|
+
const typingInterval = setInterval(() => {
|
|
1835
|
+
if (typingRequestInFlight) return;
|
|
1836
|
+
typingRequestInFlight = true;
|
|
1837
|
+
maybeSendRunnerChatAction({
|
|
1838
|
+
provider: normalizedRoute.provider,
|
|
1839
|
+
destination,
|
|
1840
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
1841
|
+
action: "typing",
|
|
1842
|
+
})
|
|
1843
|
+
.catch(() => {})
|
|
1844
|
+
.finally(() => {
|
|
1845
|
+
typingRequestInFlight = false;
|
|
1846
|
+
});
|
|
1847
|
+
}, 4000);
|
|
1848
|
+
if (typeof typingInterval.unref === "function") {
|
|
1849
|
+
typingInterval.unref();
|
|
1850
|
+
}
|
|
1851
|
+
let aiResult;
|
|
1852
|
+
try {
|
|
1853
|
+
aiResult = await runLocalAICommand({
|
|
1854
|
+
command: normalizedRoute.command,
|
|
1855
|
+
inputPayload: aiPayload,
|
|
1856
|
+
route: normalizedRoute,
|
|
1857
|
+
destination,
|
|
1858
|
+
});
|
|
1859
|
+
} finally {
|
|
1860
|
+
clearInterval(typingInterval);
|
|
1861
|
+
}
|
|
1777
1862
|
if (aiResult.skip) {
|
|
1778
1863
|
saveRunnerRouteState(
|
|
1779
1864
|
routeKey,
|
|
@@ -2177,6 +2262,73 @@ async function deliverLocalProviderMessage({
|
|
|
2177
2262
|
throw new Error(`${providerEnvConfig(normalizedProvider).label} local delivery is not implemented yet`);
|
|
2178
2263
|
}
|
|
2179
2264
|
|
|
2265
|
+
async function sendLocalProviderChatAction({
|
|
2266
|
+
provider,
|
|
2267
|
+
token,
|
|
2268
|
+
destination,
|
|
2269
|
+
action,
|
|
2270
|
+
timeoutSeconds,
|
|
2271
|
+
}) {
|
|
2272
|
+
const normalizedProvider = normalizeBotProvider(provider);
|
|
2273
|
+
if (normalizedProvider === "telegram") {
|
|
2274
|
+
const requestURL = `https://api.telegram.org/bot${token}/sendChatAction`;
|
|
2275
|
+
const payload = {
|
|
2276
|
+
chat_id: destination.chatID,
|
|
2277
|
+
action: String(action || "typing").trim() || "typing",
|
|
2278
|
+
};
|
|
2279
|
+
const response = await postJSONWithoutAuth(requestURL, timeoutSeconds, payload);
|
|
2280
|
+
const responseJSON = parseJSONText(response.bodyText);
|
|
2281
|
+
return {
|
|
2282
|
+
statusCode: response.statusCode,
|
|
2283
|
+
body: responseJSON || response.bodyText,
|
|
2284
|
+
ok: response.statusCode >= 200 && response.statusCode < 300 && Boolean(responseJSON?.ok ?? true),
|
|
2285
|
+
url: sanitizeTelegramAPIURL(requestURL),
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
return {
|
|
2289
|
+
statusCode: 204,
|
|
2290
|
+
body: { ok: true, skipped: true },
|
|
2291
|
+
ok: true,
|
|
2292
|
+
url: "",
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
async function maybeSendRunnerChatAction({
|
|
2297
|
+
provider,
|
|
2298
|
+
destination,
|
|
2299
|
+
timeoutSeconds,
|
|
2300
|
+
action = "typing",
|
|
2301
|
+
}) {
|
|
2302
|
+
const providerEnv = loadProviderEnvConfig(provider);
|
|
2303
|
+
if (!providerEnv.ok) {
|
|
2304
|
+
return {
|
|
2305
|
+
ok: false,
|
|
2306
|
+
skipped: true,
|
|
2307
|
+
detail: providerEnv.error,
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
try {
|
|
2311
|
+
const result = await sendLocalProviderChatAction({
|
|
2312
|
+
provider,
|
|
2313
|
+
token: providerEnv.token,
|
|
2314
|
+
destination,
|
|
2315
|
+
action,
|
|
2316
|
+
timeoutSeconds,
|
|
2317
|
+
});
|
|
2318
|
+
return {
|
|
2319
|
+
ok: Boolean(result.ok),
|
|
2320
|
+
skipped: false,
|
|
2321
|
+
detail: result.ok ? "" : String(safeObject(result.body).description || safeObject(result.body).error || "").trim(),
|
|
2322
|
+
};
|
|
2323
|
+
} catch (err) {
|
|
2324
|
+
return {
|
|
2325
|
+
ok: false,
|
|
2326
|
+
skipped: false,
|
|
2327
|
+
detail: String(err?.message || err),
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2180
2332
|
function resolveWorkspaceDir(rawPath) {
|
|
2181
2333
|
const input = String(rawPath || "").trim();
|
|
2182
2334
|
if (input) {
|