metheus-governance-mcp-cli 0.2.55 → 0.2.58
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 +264 -3
- package/package.json +1 -1
- package/scripts/local-bot-ai-bridge.mjs +53 -6
package/cli.mjs
CHANGED
|
@@ -897,6 +897,80 @@ function parseArchivedChatComment(rawBody) {
|
|
|
897
897
|
};
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
+
function joinTextParts(parts, separator = " ") {
|
|
901
|
+
return ensureArray(parts)
|
|
902
|
+
.map((value) => String(value || "").trim())
|
|
903
|
+
.filter(Boolean)
|
|
904
|
+
.join(separator);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function toISOStringFromUnix(rawValue) {
|
|
908
|
+
const numeric = Number(rawValue || 0);
|
|
909
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
910
|
+
return new Date().toISOString();
|
|
911
|
+
}
|
|
912
|
+
return new Date(numeric * 1000).toISOString();
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function collectTelegramUpdateText(message) {
|
|
916
|
+
return firstNonEmptyString([message?.text, message?.caption]);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
920
|
+
const update = safeObject(rawUpdate);
|
|
921
|
+
const message = safeObject(update.message || update.edited_message);
|
|
922
|
+
if (!message || Object.keys(message).length === 0) {
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
const chat = safeObject(message.chat);
|
|
926
|
+
const from = safeObject(message.from);
|
|
927
|
+
const replyTo = safeObject(message.reply_to_message);
|
|
928
|
+
const text = collectTelegramUpdateText(message);
|
|
929
|
+
if (!text) {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
eventName: update.edited_message ? "telegram.message.updated" : "telegram.message.created",
|
|
934
|
+
updateID: intFromRawAllowZero(update.update_id, 0),
|
|
935
|
+
messageID: intFromRawAllowZero(message.message_id, 0),
|
|
936
|
+
chatID: String(chat.id || "").trim(),
|
|
937
|
+
chatType: String(chat.type || "").trim(),
|
|
938
|
+
chatTitle: firstNonEmptyString([chat.title, chat.username, joinTextParts([chat.first_name, chat.last_name]), chat.id]),
|
|
939
|
+
fromID: String(from.id || "").trim(),
|
|
940
|
+
fromName: firstNonEmptyString([joinTextParts([from.first_name, from.last_name]), from.username, from.id]),
|
|
941
|
+
fromUsername: String(from.username || "").trim(),
|
|
942
|
+
fromIsBot: Boolean(from.is_bot),
|
|
943
|
+
text,
|
|
944
|
+
occurredAt: toISOStringFromUnix(message.edit_date || message.date),
|
|
945
|
+
messageThreadID: String(message.message_thread_id || "").trim(),
|
|
946
|
+
replyToMessageID: intFromRawAllowZero(replyTo.message_id, 0),
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function buildArchivedInboundMessageKey(chatID, messageID) {
|
|
951
|
+
return `${String(chatID || "").trim()}:${intFromRawAllowZero(messageID, 0)}`;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function formatTelegramInboundArchiveComment(normalized) {
|
|
955
|
+
const headerLines = [
|
|
956
|
+
`[Telegram ${normalized.eventName === "telegram.message.updated" ? "edited" : "message"}]`,
|
|
957
|
+
`chat_id: ${normalized.chatID || "<missing>"}`,
|
|
958
|
+
`message_id: ${normalized.messageID || "<missing>"}`,
|
|
959
|
+
`occurred_at: ${normalized.occurredAt || new Date().toISOString()}`,
|
|
960
|
+
`sender: ${normalized.fromName || normalized.fromUsername || normalized.fromID || "unknown"}`,
|
|
961
|
+
];
|
|
962
|
+
if (normalized.fromUsername) {
|
|
963
|
+
headerLines.push(`telegram_username: @${normalized.fromUsername.replace(/^@+/, "")}`);
|
|
964
|
+
}
|
|
965
|
+
if (normalized.messageThreadID) {
|
|
966
|
+
headerLines.push(`telegram_topic_id: ${normalized.messageThreadID}`);
|
|
967
|
+
}
|
|
968
|
+
if (normalized.replyToMessageID > 0) {
|
|
969
|
+
headerLines.push(`reply_to_message_id: ${normalized.replyToMessageID}`);
|
|
970
|
+
}
|
|
971
|
+
return `${headerLines.join("\n")}\n\n${String(normalized.text || "").trim()}`;
|
|
972
|
+
}
|
|
973
|
+
|
|
900
974
|
function formatBotReplyArchiveComment({
|
|
901
975
|
provider,
|
|
902
976
|
bot,
|
|
@@ -1026,6 +1100,65 @@ async function createThreadComment({
|
|
|
1026
1100
|
return safeObject(parseJSONText(responseText));
|
|
1027
1101
|
}
|
|
1028
1102
|
|
|
1103
|
+
async function getTelegramAPIJSON(token, methodName, timeoutSeconds, query = {}) {
|
|
1104
|
+
const requestURL = new URL(`https://api.telegram.org/bot${token}/${methodName}`);
|
|
1105
|
+
Object.entries(safeObject(query)).forEach(([key, value]) => {
|
|
1106
|
+
if (value == null || value === "") return;
|
|
1107
|
+
requestURL.searchParams.set(key, String(value));
|
|
1108
|
+
});
|
|
1109
|
+
const parsed = safeObject(await getJSONWithoutAuth(requestURL.toString(), timeoutSeconds));
|
|
1110
|
+
if (parsed.ok === false) {
|
|
1111
|
+
throw new Error(String(parsed.description || `${methodName} failed`).trim());
|
|
1112
|
+
}
|
|
1113
|
+
return parsed;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
async function postTelegramAPIJSON(token, methodName, timeoutSeconds, payload = {}) {
|
|
1117
|
+
const requestURL = `https://api.telegram.org/bot${token}/${methodName}`;
|
|
1118
|
+
const response = await postJSONWithoutAuth(requestURL, timeoutSeconds, payload);
|
|
1119
|
+
const parsed = safeObject(parseJSONText(response.bodyText));
|
|
1120
|
+
if (!(response.statusCode >= 200 && response.statusCode < 300) || parsed.ok === false) {
|
|
1121
|
+
const error = new Error(String(parsed.description || response.bodyText || `${methodName} failed`).trim() || `${methodName} failed`);
|
|
1122
|
+
error.statusCode = response.statusCode;
|
|
1123
|
+
error.responseBody = response.bodyText;
|
|
1124
|
+
throw error;
|
|
1125
|
+
}
|
|
1126
|
+
return parsed;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
async function getTelegramWebhookInfo(token, timeoutSeconds) {
|
|
1130
|
+
return safeObject(await getTelegramAPIJSON(token, "getWebhookInfo", timeoutSeconds));
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
async function deleteTelegramWebhook(token, timeoutSeconds) {
|
|
1134
|
+
return safeObject(await postTelegramAPIJSON(token, "deleteWebhook", timeoutSeconds, {
|
|
1135
|
+
drop_pending_updates: false,
|
|
1136
|
+
}));
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
async function getTelegramUpdates(token, timeoutSeconds, offset = 0) {
|
|
1140
|
+
try {
|
|
1141
|
+
return safeObject(await getTelegramAPIJSON(token, "getUpdates", timeoutSeconds, {
|
|
1142
|
+
timeout: 0,
|
|
1143
|
+
offset: offset > 0 ? offset : "",
|
|
1144
|
+
allowed_updates: JSON.stringify(["message", "edited_message"]),
|
|
1145
|
+
}));
|
|
1146
|
+
} catch (err) {
|
|
1147
|
+
const statusCode = Number(err?.statusCode || 0);
|
|
1148
|
+
const errorText = String(err?.responseBody || err?.message || "").toLowerCase();
|
|
1149
|
+
const webhookConflict = statusCode === 409 || errorText.includes("webhook") || errorText.includes("getupdates");
|
|
1150
|
+
if (!webhookConflict) {
|
|
1151
|
+
throw err;
|
|
1152
|
+
}
|
|
1153
|
+
await deleteTelegramWebhook(token, timeoutSeconds);
|
|
1154
|
+
return safeObject(await getTelegramAPIJSON(token, "getUpdates", timeoutSeconds, {
|
|
1155
|
+
timeout: 0,
|
|
1156
|
+
offset: offset > 0 ? offset : "",
|
|
1157
|
+
allowed_updates: JSON.stringify(["message", "edited_message"]),
|
|
1158
|
+
}));
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1029
1162
|
function looksLikeArchiveWorkItemTitle(title, provider) {
|
|
1030
1163
|
const text = String(title || "").trim().toLowerCase();
|
|
1031
1164
|
if (!text) return false;
|
|
@@ -1118,6 +1251,101 @@ async function discoverArchiveThreadForDestination({
|
|
|
1118
1251
|
);
|
|
1119
1252
|
}
|
|
1120
1253
|
|
|
1254
|
+
async function archiveLocalTelegramMessagesForRoute({
|
|
1255
|
+
routeKey,
|
|
1256
|
+
routeState,
|
|
1257
|
+
runtime,
|
|
1258
|
+
destination,
|
|
1259
|
+
archiveThread,
|
|
1260
|
+
}) {
|
|
1261
|
+
const envConfig = loadProviderEnvConfig("telegram");
|
|
1262
|
+
if (!envConfig.ok) {
|
|
1263
|
+
throw new Error(envConfig.error || "TELEGRAM_BOT_TOKEN is not configured");
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const state = safeObject(routeState);
|
|
1267
|
+
let webhookClearedURL = "";
|
|
1268
|
+
if (!boolFromRaw(state.local_telegram_polling_ready, false)) {
|
|
1269
|
+
const webhookInfo = safeObject(await getTelegramWebhookInfo(envConfig.token, runtime.timeoutSeconds).catch(() => ({})));
|
|
1270
|
+
const currentWebhookURL = String(safeObject(webhookInfo.result).url || "").trim();
|
|
1271
|
+
if (currentWebhookURL) {
|
|
1272
|
+
await deleteTelegramWebhook(envConfig.token, runtime.timeoutSeconds);
|
|
1273
|
+
webhookClearedURL = currentWebhookURL;
|
|
1274
|
+
}
|
|
1275
|
+
saveRunnerRouteState(routeKey, {
|
|
1276
|
+
local_receive_mode: "telegram_get_updates",
|
|
1277
|
+
local_telegram_polling_ready: true,
|
|
1278
|
+
local_telegram_webhook_cleared_url: webhookClearedURL,
|
|
1279
|
+
local_telegram_webhook_cleared_at: webhookClearedURL ? new Date().toISOString() : state.local_telegram_webhook_cleared_at,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
const lastUpdateID = intFromRawAllowZero(state.last_provider_update_id ?? state.last_telegram_update_id, 0);
|
|
1284
|
+
const updatesEnvelope = await getTelegramUpdates(envConfig.token, runtime.timeoutSeconds, lastUpdateID > 0 ? lastUpdateID + 1 : 0);
|
|
1285
|
+
const updates = ensureArray(updatesEnvelope.result)
|
|
1286
|
+
.map(normalizeLocalTelegramUpdate)
|
|
1287
|
+
.filter(Boolean);
|
|
1288
|
+
const highestUpdateID = updates.reduce((max, item) => Math.max(max, intFromRawAllowZero(item.updateID, 0)), lastUpdateID);
|
|
1289
|
+
|
|
1290
|
+
saveRunnerRouteState(routeKey, {
|
|
1291
|
+
local_receive_mode: "telegram_get_updates",
|
|
1292
|
+
local_telegram_polling_ready: true,
|
|
1293
|
+
last_provider_update_id: highestUpdateID,
|
|
1294
|
+
last_local_poll_at: new Date().toISOString(),
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
if (!updates.length) {
|
|
1298
|
+
return {
|
|
1299
|
+
importedCommentIDs: [],
|
|
1300
|
+
importedCount: 0,
|
|
1301
|
+
lastUpdateID: highestUpdateID,
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
const existingComments = await listThreadComments({
|
|
1306
|
+
siteBaseURL: runtime.baseURL,
|
|
1307
|
+
threadID: archiveThread.threadID,
|
|
1308
|
+
token: runtime.token,
|
|
1309
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
1310
|
+
limit: 200,
|
|
1311
|
+
actorUserID: runtime.actor.user_id,
|
|
1312
|
+
});
|
|
1313
|
+
const existingKeys = new Set(
|
|
1314
|
+
existingComments
|
|
1315
|
+
.map(normalizeArchiveCommentRecord)
|
|
1316
|
+
.map((record) => record.parsedArchive)
|
|
1317
|
+
.filter((parsed) => parsed && isInboundArchiveKind(parsed.kind) && parsed.chatID)
|
|
1318
|
+
.map((parsed) => buildArchivedInboundMessageKey(parsed.chatID, parsed.messageID)),
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
const importedCommentIDs = [];
|
|
1322
|
+
for (const update of updates) {
|
|
1323
|
+
if (String(update.chatID || "").trim() !== String(destination.chatID || "").trim()) continue;
|
|
1324
|
+
if (update.fromIsBot) continue;
|
|
1325
|
+
if (!String(update.text || "").trim()) continue;
|
|
1326
|
+
const dedupeKey = buildArchivedInboundMessageKey(update.chatID, update.messageID);
|
|
1327
|
+
if (existingKeys.has(dedupeKey)) continue;
|
|
1328
|
+
const createdComment = await createThreadComment({
|
|
1329
|
+
siteBaseURL: runtime.baseURL,
|
|
1330
|
+
token: runtime.token,
|
|
1331
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
1332
|
+
threadID: archiveThread.threadID,
|
|
1333
|
+
actorUserID: runtime.actor.user_id,
|
|
1334
|
+
body: formatTelegramInboundArchiveComment(update),
|
|
1335
|
+
});
|
|
1336
|
+
if (String(createdComment.id || "").trim()) {
|
|
1337
|
+
importedCommentIDs.push(String(createdComment.id || "").trim());
|
|
1338
|
+
}
|
|
1339
|
+
existingKeys.add(dedupeKey);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
return {
|
|
1343
|
+
importedCommentIDs,
|
|
1344
|
+
importedCount: importedCommentIDs.length,
|
|
1345
|
+
lastUpdateID: highestUpdateID,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1121
1349
|
function buildRunnerShellInvocation(command) {
|
|
1122
1350
|
const text = String(command || "").trim();
|
|
1123
1351
|
if (!text) {
|
|
@@ -1244,9 +1472,11 @@ function buildRunnerRouteStateFromComment(record, patch = {}) {
|
|
|
1244
1472
|
|
|
1245
1473
|
function saveRunnerRouteState(routeKey, routeState) {
|
|
1246
1474
|
const current = loadBotRunnerState();
|
|
1475
|
+
const previous = safeObject(current.routes[routeKey]);
|
|
1247
1476
|
const nextRoutes = {
|
|
1248
1477
|
...safeObject(current.routes),
|
|
1249
1478
|
[routeKey]: {
|
|
1479
|
+
...previous,
|
|
1250
1480
|
...safeObject(routeState),
|
|
1251
1481
|
updated_at: new Date().toISOString(),
|
|
1252
1482
|
},
|
|
@@ -1462,6 +1692,20 @@ async function processRunnerRouteOnce(route, runtime, mode) {
|
|
|
1462
1692
|
archiveThreadID: normalizedRoute.archiveThreadID,
|
|
1463
1693
|
archiveWorkItemID: normalizedRoute.archiveWorkItemID,
|
|
1464
1694
|
});
|
|
1695
|
+
const importOutcome = await archiveLocalTelegramMessagesForRoute({
|
|
1696
|
+
routeKey,
|
|
1697
|
+
routeState: currentState,
|
|
1698
|
+
runtime,
|
|
1699
|
+
destination,
|
|
1700
|
+
archiveThread,
|
|
1701
|
+
});
|
|
1702
|
+
saveRunnerRouteState(routeKey, {
|
|
1703
|
+
local_receive_mode: "telegram_get_updates",
|
|
1704
|
+
local_telegram_polling_ready: true,
|
|
1705
|
+
last_provider_update_id: intFromRawAllowZero(importOutcome.lastUpdateID, 0),
|
|
1706
|
+
last_local_poll_at: new Date().toISOString(),
|
|
1707
|
+
});
|
|
1708
|
+
const refreshedState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
1465
1709
|
const comments = await listThreadComments({
|
|
1466
1710
|
siteBaseURL: runtime.baseURL,
|
|
1467
1711
|
threadID: archiveThread.threadID,
|
|
@@ -1470,7 +1714,22 @@ async function processRunnerRouteOnce(route, runtime, mode) {
|
|
|
1470
1714
|
actorUserID: runtime.actor.user_id,
|
|
1471
1715
|
limit: Math.max(50, normalizedRoute.contextComments * 6),
|
|
1472
1716
|
});
|
|
1473
|
-
const
|
|
1717
|
+
const orderedComments = ensureArray(comments)
|
|
1718
|
+
.map(normalizeArchiveCommentRecord)
|
|
1719
|
+
.filter((record) => record.id && record.parsedArchive)
|
|
1720
|
+
.sort(compareArchiveCommentRecords);
|
|
1721
|
+
const inboundComments = orderedComments.filter((record) => isInboundArchiveKind(record.parsedArchive.kind));
|
|
1722
|
+
const importedRecords = ensureArray(importOutcome.importedCommentIDs)
|
|
1723
|
+
.map((commentID) => orderedComments.find((record) => record.id === commentID))
|
|
1724
|
+
.filter(Boolean);
|
|
1725
|
+
const pending = importedRecords.length > 0
|
|
1726
|
+
? {
|
|
1727
|
+
ordered: orderedComments,
|
|
1728
|
+
latest: inboundComments.length ? inboundComments[inboundComments.length - 1] : null,
|
|
1729
|
+
shouldPrime: false,
|
|
1730
|
+
pending: importedRecords,
|
|
1731
|
+
}
|
|
1732
|
+
: selectPendingArchiveComments(comments, refreshedState, mode);
|
|
1474
1733
|
if (pending.shouldPrime && pending.latest) {
|
|
1475
1734
|
saveRunnerRouteState(routeKey, buildRunnerRouteStateFromComment(pending.latest, { primed: true }));
|
|
1476
1735
|
return {
|
|
@@ -1487,7 +1746,9 @@ async function processRunnerRouteOnce(route, runtime, mode) {
|
|
|
1487
1746
|
route_key: routeKey,
|
|
1488
1747
|
route_name: normalizedRoute.name,
|
|
1489
1748
|
outcome: "idle",
|
|
1490
|
-
detail:
|
|
1749
|
+
detail: importOutcome.importedCount > 0
|
|
1750
|
+
? "local telegram updates imported but no pending archive comments were selected"
|
|
1751
|
+
: "no new local telegram messages or archived inbound messages",
|
|
1491
1752
|
thread_id: archiveThread.threadID,
|
|
1492
1753
|
};
|
|
1493
1754
|
}
|
|
@@ -1596,7 +1857,6 @@ async function runRunnerOnce(flags) {
|
|
|
1596
1857
|
}
|
|
1597
1858
|
|
|
1598
1859
|
async function runRunnerStart(flags) {
|
|
1599
|
-
const runtime = await resolveRunnerContext(flags);
|
|
1600
1860
|
const jsonMode = boolFromRaw(flags.json, false);
|
|
1601
1861
|
const routes = resolveRunnerRoutes(flags, "start");
|
|
1602
1862
|
const schedules = new Map();
|
|
@@ -1621,6 +1881,7 @@ async function runRunnerStart(flags) {
|
|
|
1621
1881
|
continue;
|
|
1622
1882
|
}
|
|
1623
1883
|
try {
|
|
1884
|
+
const runtime = await resolveRunnerContext(flags);
|
|
1624
1885
|
const result = await processRunnerRouteOnce(normalizedRoute, runtime, "start");
|
|
1625
1886
|
printRunnerResult("start", result, jsonMode);
|
|
1626
1887
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -56,6 +56,50 @@ function resolveWorkspaceDir(rawValue) {
|
|
|
56
56
|
return process.cwd();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function resolveLocalCliCommand(commandName) {
|
|
60
|
+
const name = String(commandName || "").trim();
|
|
61
|
+
if (!name) {
|
|
62
|
+
throw new Error("command name is required");
|
|
63
|
+
}
|
|
64
|
+
if (process.platform === "win32") {
|
|
65
|
+
const candidates = [
|
|
66
|
+
path.join(process.env.APPDATA || "", "npm", `${name}.cmd`),
|
|
67
|
+
path.join(process.env.APPDATA || "", "npm", `${name}.ps1`),
|
|
68
|
+
path.join(process.env.APPDATA || "", "npm", name),
|
|
69
|
+
].filter(Boolean);
|
|
70
|
+
for (const candidate of candidates) {
|
|
71
|
+
if (candidate && fs.existsSync(candidate)) {
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function quoteWindowsShellArg(value) {
|
|
80
|
+
const text = String(value ?? "");
|
|
81
|
+
if (!text) return '""';
|
|
82
|
+
if (!/[\s"&|<>^()]/.test(text)) return text;
|
|
83
|
+
return `"${text.replace(/"/g, '""')}"`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function spawnCli(commandPath, args, options) {
|
|
87
|
+
const executable = String(commandPath || "").trim();
|
|
88
|
+
if (!executable) {
|
|
89
|
+
throw new Error("command path is required");
|
|
90
|
+
}
|
|
91
|
+
const normalizedArgs = Array.isArray(args) ? args.map((item) => String(item)) : [];
|
|
92
|
+
if (process.platform === "win32" && /\.(cmd|bat)$/i.test(executable)) {
|
|
93
|
+
const commandLine = [quoteWindowsShellArg(executable), ...normalizedArgs.map(quoteWindowsShellArg)].join(" ");
|
|
94
|
+
return spawnSync(
|
|
95
|
+
process.env.ComSpec || "cmd.exe",
|
|
96
|
+
["/d", "/s", "/c", commandLine],
|
|
97
|
+
options,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return spawnSync(executable, normalizedArgs, options);
|
|
101
|
+
}
|
|
102
|
+
|
|
59
103
|
function buildPrompt(payload, { terse = true } = {}) {
|
|
60
104
|
const safePayload = payload && typeof payload === "object" ? payload : {};
|
|
61
105
|
const trigger = safePayload.trigger && typeof safePayload.trigger === "object" ? safePayload.trigger : {};
|
|
@@ -117,8 +161,9 @@ function normalizeCliResponse(rawText) {
|
|
|
117
161
|
|
|
118
162
|
function runCodex(promptText, workspaceDir) {
|
|
119
163
|
const outputPath = path.join(os.tmpdir(), `metheus-runner-codex-${Date.now()}.txt`);
|
|
120
|
-
const
|
|
121
|
-
|
|
164
|
+
const codexCommand = resolveLocalCliCommand("codex");
|
|
165
|
+
const result = spawnCli(
|
|
166
|
+
codexCommand,
|
|
122
167
|
[
|
|
123
168
|
"exec",
|
|
124
169
|
"-",
|
|
@@ -148,8 +193,9 @@ function runCodex(promptText, workspaceDir) {
|
|
|
148
193
|
}
|
|
149
194
|
|
|
150
195
|
function runClaude(promptText) {
|
|
151
|
-
const
|
|
152
|
-
|
|
196
|
+
const claudeCommand = resolveLocalCliCommand("claude");
|
|
197
|
+
const result = spawnCli(
|
|
198
|
+
claudeCommand,
|
|
153
199
|
[
|
|
154
200
|
"-p",
|
|
155
201
|
promptText,
|
|
@@ -175,8 +221,9 @@ function runClaude(promptText) {
|
|
|
175
221
|
}
|
|
176
222
|
|
|
177
223
|
function runGemini(promptText) {
|
|
178
|
-
const
|
|
179
|
-
|
|
224
|
+
const geminiCommand = resolveLocalCliCommand("gemini");
|
|
225
|
+
const result = spawnCli(
|
|
226
|
+
geminiCommand,
|
|
180
227
|
[
|
|
181
228
|
"-p",
|
|
182
229
|
promptText,
|