@locusai/locus-telegram 0.23.0 → 0.23.2
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/bin/locus-telegram.js +79 -26
- package/package.json +4 -4
package/bin/locus-telegram.js
CHANGED
|
@@ -10250,8 +10250,14 @@ var init_cancel = __esm(() => {
|
|
|
10250
10250
|
});
|
|
10251
10251
|
|
|
10252
10252
|
// src/ui/keyboards.ts
|
|
10253
|
-
function planKeyboard() {
|
|
10254
|
-
return new import_grammy2.InlineKeyboard().text("✅ Approve Plan", CB.APPROVE_PLAN).text("❌ Reject Plan", CB.REJECT_PLAN)
|
|
10253
|
+
function planKeyboard(planId) {
|
|
10254
|
+
return new import_grammy2.InlineKeyboard().text("✅ Approve Plan", `${CB.APPROVE_PLAN}${planId}`).text("❌ Reject Plan", CB.REJECT_PLAN);
|
|
10255
|
+
}
|
|
10256
|
+
function sprintActiveKeyboard(sprintName) {
|
|
10257
|
+
return new import_grammy2.InlineKeyboard().text("\uD83D\uDE80 Activate Sprint", `${CB.SPRINT_ACTIVE}${sprintName}`);
|
|
10258
|
+
}
|
|
10259
|
+
function runKeyboard() {
|
|
10260
|
+
return new import_grammy2.InlineKeyboard().text("\uD83D\uDE80 Run", CB.RUN_START);
|
|
10255
10261
|
}
|
|
10256
10262
|
function runCompleteKeyboard(issueNumber) {
|
|
10257
10263
|
const kb = new import_grammy2.InlineKeyboard;
|
|
@@ -10274,9 +10280,10 @@ var import_grammy2, CB;
|
|
|
10274
10280
|
var init_keyboards = __esm(() => {
|
|
10275
10281
|
import_grammy2 = __toESM(require_mod(), 1);
|
|
10276
10282
|
CB = {
|
|
10277
|
-
APPROVE_PLAN: "plan:approve",
|
|
10283
|
+
APPROVE_PLAN: "plan:approve:",
|
|
10278
10284
|
REJECT_PLAN: "plan:reject",
|
|
10279
|
-
|
|
10285
|
+
SPRINT_ACTIVE: "sprint:active:",
|
|
10286
|
+
RUN_START: "run:start",
|
|
10280
10287
|
CONFIRM_ACTION: "confirm:yes",
|
|
10281
10288
|
CANCEL_ACTION: "confirm:no",
|
|
10282
10289
|
VIEW_PR: "pr:view:",
|
|
@@ -10706,7 +10713,7 @@ async function handleLocusCommand(ctx, command, args) {
|
|
|
10706
10713
|
if (isStreaming) {
|
|
10707
10714
|
await handleStreamingCommand(ctx, displayCmd, fullArgs, command, args);
|
|
10708
10715
|
} else {
|
|
10709
|
-
await handleBufferedCommand(ctx, displayCmd, fullArgs, command);
|
|
10716
|
+
await handleBufferedCommand(ctx, displayCmd, fullArgs, command, args);
|
|
10710
10717
|
}
|
|
10711
10718
|
}
|
|
10712
10719
|
async function handleStreamingCommand(ctx, displayCmd, fullArgs, command, args) {
|
|
@@ -10748,7 +10755,7 @@ async function handleStreamingCommand(ctx, displayCmd, fullArgs, command, args)
|
|
|
10748
10755
|
try {
|
|
10749
10756
|
await ctx.api.editMessageText(msg.chat.id, msg.message_id, formatStreamingMessage(displayCmd, output, true), { parse_mode: "HTML" });
|
|
10750
10757
|
} catch {}
|
|
10751
|
-
const keyboard = getPostCommandKeyboard(command, args, exitCode ?? 0);
|
|
10758
|
+
const keyboard = getPostCommandKeyboard(command, args, exitCode ?? 0, output);
|
|
10752
10759
|
if (keyboard) {
|
|
10753
10760
|
await ctx.reply(exitCode === 0 ? "What would you like to do next?" : "Command failed.", { reply_markup: keyboard });
|
|
10754
10761
|
}
|
|
@@ -10756,13 +10763,13 @@ async function handleStreamingCommand(ctx, displayCmd, fullArgs, command, args)
|
|
|
10756
10763
|
});
|
|
10757
10764
|
});
|
|
10758
10765
|
}
|
|
10759
|
-
async function handleBufferedCommand(ctx, displayCmd, fullArgs, command) {
|
|
10766
|
+
async function handleBufferedCommand(ctx, displayCmd, fullArgs, command, args) {
|
|
10760
10767
|
const chatId = ctx.chat?.id;
|
|
10761
10768
|
if (!chatId)
|
|
10762
10769
|
return;
|
|
10763
10770
|
const sessionId = String(chatId);
|
|
10764
10771
|
const child = invokeLocusStream(fullArgs);
|
|
10765
|
-
const trackingId = commandTracker.track(sessionId, command,
|
|
10772
|
+
const trackingId = commandTracker.track(sessionId, command, args, child);
|
|
10766
10773
|
let output = "";
|
|
10767
10774
|
child.stdout?.on("data", (chunk) => {
|
|
10768
10775
|
output += chunk.toString();
|
|
@@ -10774,7 +10781,7 @@ async function handleBufferedCommand(ctx, displayCmd, fullArgs, command) {
|
|
|
10774
10781
|
child.on("close", async (exitCode) => {
|
|
10775
10782
|
commandTracker.untrack(sessionId, trackingId);
|
|
10776
10783
|
const result = formatCommandResult(displayCmd, output, exitCode ?? 0);
|
|
10777
|
-
const keyboard = getPostCommandKeyboard(command,
|
|
10784
|
+
const keyboard = getPostCommandKeyboard(command, args, exitCode ?? 0, output);
|
|
10778
10785
|
await ctx.reply(result, {
|
|
10779
10786
|
parse_mode: "HTML",
|
|
10780
10787
|
reply_markup: keyboard ?? undefined
|
|
@@ -10783,12 +10790,43 @@ async function handleBufferedCommand(ctx, displayCmd, fullArgs, command) {
|
|
|
10783
10790
|
});
|
|
10784
10791
|
});
|
|
10785
10792
|
}
|
|
10786
|
-
function
|
|
10793
|
+
function stripAnsi(text) {
|
|
10794
|
+
return text.replace(/\x1B\[[0-9;]*m/g, "");
|
|
10795
|
+
}
|
|
10796
|
+
function extractPlanId(output) {
|
|
10797
|
+
const clean = stripAnsi(output);
|
|
10798
|
+
const match = clean.match(/Plan saved:\s*(\S+)/);
|
|
10799
|
+
return match?.[1] ?? null;
|
|
10800
|
+
}
|
|
10801
|
+
function extractSprintName(output) {
|
|
10802
|
+
const clean = stripAnsi(output);
|
|
10803
|
+
const match = clean.match(/Sprint:\s*(.+?)$/m);
|
|
10804
|
+
return match?.[1]?.trim() ?? null;
|
|
10805
|
+
}
|
|
10806
|
+
function getPostCommandKeyboard(command, args, exitCode, output = "") {
|
|
10787
10807
|
if (exitCode !== 0)
|
|
10788
10808
|
return null;
|
|
10789
10809
|
switch (command) {
|
|
10790
|
-
case "plan":
|
|
10791
|
-
|
|
10810
|
+
case "plan": {
|
|
10811
|
+
if (args[0] === "approve") {
|
|
10812
|
+
const sprint = extractSprintName(output);
|
|
10813
|
+
if (sprint)
|
|
10814
|
+
return sprintActiveKeyboard(sprint);
|
|
10815
|
+
return runKeyboard();
|
|
10816
|
+
}
|
|
10817
|
+
if (args[0] !== "list" && args[0] !== "show") {
|
|
10818
|
+
const planId = extractPlanId(output);
|
|
10819
|
+
if (planId)
|
|
10820
|
+
return planKeyboard(planId);
|
|
10821
|
+
}
|
|
10822
|
+
return null;
|
|
10823
|
+
}
|
|
10824
|
+
case "sprint": {
|
|
10825
|
+
if (args[0] === "active" && args.length > 1) {
|
|
10826
|
+
return runKeyboard();
|
|
10827
|
+
}
|
|
10828
|
+
return null;
|
|
10829
|
+
}
|
|
10792
10830
|
case "review":
|
|
10793
10831
|
if (args.length > 0) {
|
|
10794
10832
|
return reviewKeyboard(Number(args[0]));
|
|
@@ -10877,6 +10915,8 @@ var exports_bot = {};
|
|
|
10877
10915
|
__export(exports_bot, {
|
|
10878
10916
|
createBot: () => createBot
|
|
10879
10917
|
});
|
|
10918
|
+
import { exec as execCb3 } from "node:child_process";
|
|
10919
|
+
import { promisify as promisify3 } from "node:util";
|
|
10880
10920
|
function createBot(config) {
|
|
10881
10921
|
const bot = new import_grammy3.Bot(config.botToken);
|
|
10882
10922
|
bot.use(async (ctx, next) => {
|
|
@@ -10964,13 +11004,11 @@ function createBot(config) {
|
|
|
10964
11004
|
return bot;
|
|
10965
11005
|
}
|
|
10966
11006
|
function registerCallbackHandlers(bot) {
|
|
10967
|
-
bot.callbackQuery(
|
|
10968
|
-
|
|
11007
|
+
bot.callbackQuery(/^plan:approve:(.+)$/, async (ctx) => {
|
|
11008
|
+
const planId = ctx.match[1];
|
|
11009
|
+
await ctx.answerCallbackQuery({ text: "Approving plan..." });
|
|
10969
11010
|
await ctx.editMessageReplyMarkup({ reply_markup: undefined });
|
|
10970
|
-
await ctx
|
|
10971
|
-
parse_mode: "HTML"
|
|
10972
|
-
});
|
|
10973
|
-
await handleLocusCommand(ctx, "run", []);
|
|
11011
|
+
await handleLocusCommand(ctx, "plan", ["approve", planId]);
|
|
10974
11012
|
});
|
|
10975
11013
|
bot.callbackQuery(CB.REJECT_PLAN, async (ctx) => {
|
|
10976
11014
|
await ctx.answerCallbackQuery({ text: "Plan rejected." });
|
|
@@ -10979,9 +11017,16 @@ function registerCallbackHandlers(bot) {
|
|
|
10979
11017
|
parse_mode: "HTML"
|
|
10980
11018
|
});
|
|
10981
11019
|
});
|
|
10982
|
-
bot.callbackQuery(
|
|
10983
|
-
|
|
10984
|
-
await
|
|
11020
|
+
bot.callbackQuery(/^sprint:active:(.+)$/, async (ctx) => {
|
|
11021
|
+
const sprintName = ctx.match[1];
|
|
11022
|
+
await ctx.answerCallbackQuery({ text: "Activating sprint..." });
|
|
11023
|
+
await ctx.editMessageReplyMarkup({ reply_markup: undefined });
|
|
11024
|
+
await handleLocusCommand(ctx, "sprint", ["active", sprintName]);
|
|
11025
|
+
});
|
|
11026
|
+
bot.callbackQuery(CB.RUN_START, async (ctx) => {
|
|
11027
|
+
await ctx.answerCallbackQuery({ text: "Starting run..." });
|
|
11028
|
+
await ctx.editMessageReplyMarkup({ reply_markup: undefined });
|
|
11029
|
+
await handleLocusCommand(ctx, "run", []);
|
|
10985
11030
|
});
|
|
10986
11031
|
bot.callbackQuery(CB.VIEW_LOGS, async (ctx) => {
|
|
10987
11032
|
await ctx.answerCallbackQuery();
|
|
@@ -10997,9 +11042,16 @@ function registerCallbackHandlers(bot) {
|
|
|
10997
11042
|
const pr = ctx.match[1];
|
|
10998
11043
|
await ctx.answerCallbackQuery({ text: "Approving PR..." });
|
|
10999
11044
|
await ctx.editMessageReplyMarkup({ reply_markup: undefined });
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11045
|
+
try {
|
|
11046
|
+
await exec3(`gh pr review ${pr} --approve`, { cwd: process.cwd() });
|
|
11047
|
+
await ctx.reply(formatSuccess(`Approved PR #${pr}`), {
|
|
11048
|
+
parse_mode: "HTML"
|
|
11049
|
+
});
|
|
11050
|
+
} catch (error) {
|
|
11051
|
+
await ctx.reply(formatError(`Failed to approve PR #${pr}`, String(error)), {
|
|
11052
|
+
parse_mode: "HTML"
|
|
11053
|
+
});
|
|
11054
|
+
}
|
|
11003
11055
|
});
|
|
11004
11056
|
bot.callbackQuery(/^review:changes:(\d+)$/, async (ctx) => {
|
|
11005
11057
|
const pr = ctx.match[1];
|
|
@@ -11010,7 +11062,7 @@ function registerCallbackHandlers(bot) {
|
|
|
11010
11062
|
bot.callbackQuery(/^review:diff:(\d+)$/, async (ctx) => {
|
|
11011
11063
|
const pr = ctx.match[1];
|
|
11012
11064
|
await ctx.answerCallbackQuery();
|
|
11013
|
-
await handleLocusCommand(ctx, "review", [pr
|
|
11065
|
+
await handleLocusCommand(ctx, "review", [pr]);
|
|
11014
11066
|
});
|
|
11015
11067
|
bot.callbackQuery(CB.RUN_SPRINT, async (ctx) => {
|
|
11016
11068
|
await ctx.answerCallbackQuery({ text: "Starting sprint run..." });
|
|
@@ -11056,7 +11108,7 @@ function parseArgs(text, command) {
|
|
|
11056
11108
|
return [];
|
|
11057
11109
|
return rest.split(/\s+/);
|
|
11058
11110
|
}
|
|
11059
|
-
var import_grammy3, logger2;
|
|
11111
|
+
var import_grammy3, exec3, logger2;
|
|
11060
11112
|
var init_bot = __esm(() => {
|
|
11061
11113
|
init_dist2();
|
|
11062
11114
|
init_cancel();
|
|
@@ -11066,6 +11118,7 @@ var init_bot = __esm(() => {
|
|
|
11066
11118
|
init_keyboards();
|
|
11067
11119
|
init_messages();
|
|
11068
11120
|
import_grammy3 = __toESM(require_mod(), 1);
|
|
11121
|
+
exec3 = promisify3(execCb3);
|
|
11069
11122
|
logger2 = createLogger("telegram");
|
|
11070
11123
|
});
|
|
11071
11124
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/locus-telegram",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.2",
|
|
4
4
|
"description": "Remote-control Locus via Telegram with full CLI mapping, git operations, and PM2 management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@grammyjs/runner": "^2.0.3",
|
|
30
|
-
"@locusai/locus-gateway": "^0.23.
|
|
31
|
-
"@locusai/locus-pm2": "^0.23.
|
|
32
|
-
"@locusai/sdk": "^0.23.
|
|
30
|
+
"@locusai/locus-gateway": "^0.23.2",
|
|
31
|
+
"@locusai/locus-pm2": "^0.23.2",
|
|
32
|
+
"@locusai/sdk": "^0.23.2",
|
|
33
33
|
"grammy": "^1.35.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|