@locusai/telegram 0.9.14 → 0.9.17
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/telegram.js +150 -37
- package/package.json +2 -2
package/bin/telegram.js
CHANGED
|
@@ -20094,6 +20094,10 @@ var HELP_TEXT = `<b>Locus Bot — Command Center</b>
|
|
|
20094
20094
|
/tasks — List active tasks
|
|
20095
20095
|
/rejecttask <id> <feedback> — Reject an IN_REVIEW task
|
|
20096
20096
|
|
|
20097
|
+
<b>Sprints:</b>
|
|
20098
|
+
/sprints — List all sprints
|
|
20099
|
+
/completesprint <id> — Complete a sprint
|
|
20100
|
+
|
|
20097
20101
|
<b>Execution:</b>
|
|
20098
20102
|
/run — Start agents on sprint tasks
|
|
20099
20103
|
/stop — Stop all running processes
|
|
@@ -20223,9 +20227,9 @@ async function cancelCommand(ctx, executor) {
|
|
|
20223
20227
|
var activeRunKill = null;
|
|
20224
20228
|
async function runCommand(ctx, executor, config) {
|
|
20225
20229
|
const text = (ctx.message && "text" in ctx.message ? ctx.message.text : "") || "";
|
|
20226
|
-
const input = text.replace(/^\/run\s*/, "").trim();
|
|
20230
|
+
const input = normalizeInput(text.replace(/^\/run\s*/, "").trim());
|
|
20227
20231
|
let parsedAgentCount;
|
|
20228
|
-
const agentsMatch = input.match(/(?:--agents|-a)\s+(\d+)/);
|
|
20232
|
+
const agentsMatch = input.match(/(?:--agents|-agents|-a)\s+(\d+)/);
|
|
20229
20233
|
if (agentsMatch) {
|
|
20230
20234
|
parsedAgentCount = Number.parseInt(agentsMatch[1], 10);
|
|
20231
20235
|
if (parsedAgentCount < 1 || parsedAgentCount > 5) {
|
|
@@ -20321,34 +20325,6 @@ async function stopCommand(ctx, executor) {
|
|
|
20321
20325
|
});
|
|
20322
20326
|
}
|
|
20323
20327
|
}
|
|
20324
|
-
// src/commands/status.ts
|
|
20325
|
-
async function statusCommand(ctx, executor) {
|
|
20326
|
-
console.log("[status] Checking status");
|
|
20327
|
-
const running = executor.getRunning();
|
|
20328
|
-
let msg = `<b>Locus Bot Status</b>
|
|
20329
|
-
|
|
20330
|
-
`;
|
|
20331
|
-
if (running.length === 0) {
|
|
20332
|
-
msg += `No running processes.
|
|
20333
|
-
`;
|
|
20334
|
-
} else {
|
|
20335
|
-
msg += `<b>Running processes (${running.length}):</b>
|
|
20336
|
-
`;
|
|
20337
|
-
for (const proc of running) {
|
|
20338
|
-
const elapsed = Math.round((Date.now() - proc.startedAt.getTime()) / 1000);
|
|
20339
|
-
msg += ` • <code>${escapeHtml(proc.command)}</code> (${elapsed}s)
|
|
20340
|
-
`;
|
|
20341
|
-
}
|
|
20342
|
-
}
|
|
20343
|
-
await ctx.reply(msg, { parse_mode: "HTML" });
|
|
20344
|
-
}
|
|
20345
|
-
async function agentsCommand(ctx, executor) {
|
|
20346
|
-
console.log("[agents] Listing agents");
|
|
20347
|
-
const args = executor.buildArgs(["agents", "list"]);
|
|
20348
|
-
const result = await executor.execute(args);
|
|
20349
|
-
const output = (result.stdout + result.stderr).trim();
|
|
20350
|
-
await ctx.reply(formatCommandOutput("locus agents list", output, result.exitCode), { parse_mode: "HTML" });
|
|
20351
|
-
}
|
|
20352
20328
|
// ../../node_modules/axios/lib/helpers/bind.js
|
|
20353
20329
|
function bind(fn, thisArg) {
|
|
20354
20330
|
return function wrap() {
|
|
@@ -37501,7 +37477,8 @@ var ApiResponseSchema = exports_external.object({
|
|
|
37501
37477
|
meta: exports_external.object({
|
|
37502
37478
|
pagination: PaginationMetaSchema.optional(),
|
|
37503
37479
|
timestamp: exports_external.string(),
|
|
37504
|
-
path: exports_external.string()
|
|
37480
|
+
path: exports_external.string(),
|
|
37481
|
+
requestId: exports_external.string().optional()
|
|
37505
37482
|
}).optional()
|
|
37506
37483
|
});
|
|
37507
37484
|
var SuccessResponseSchema = exports_external.object({
|
|
@@ -38423,7 +38400,7 @@ class LocusClient {
|
|
|
38423
38400
|
}
|
|
38424
38401
|
}
|
|
38425
38402
|
|
|
38426
|
-
// src/commands/
|
|
38403
|
+
// src/commands/sprints.ts
|
|
38427
38404
|
function createClient(config2) {
|
|
38428
38405
|
return new LocusClient({
|
|
38429
38406
|
baseUrl: config2.apiBase || "https://api.locusai.dev/api",
|
|
@@ -38442,6 +38419,138 @@ async function resolveWorkspaceId(client, config2) {
|
|
|
38442
38419
|
}
|
|
38443
38420
|
throw new Error("Could not resolve workspace from API key. Please set workspaceId in settings.");
|
|
38444
38421
|
}
|
|
38422
|
+
async function sprintsCommand(ctx, config2) {
|
|
38423
|
+
console.log("[sprints] Listing sprints");
|
|
38424
|
+
if (!config2.apiKey) {
|
|
38425
|
+
await ctx.reply(formatError("API key is required for /sprints. Run: locus config setup --api-key <KEY>"), { parse_mode: "HTML" });
|
|
38426
|
+
return;
|
|
38427
|
+
}
|
|
38428
|
+
try {
|
|
38429
|
+
const client = createClient(config2);
|
|
38430
|
+
const workspaceId = await resolveWorkspaceId(client, config2);
|
|
38431
|
+
const sprints2 = await client.sprints.list(workspaceId);
|
|
38432
|
+
if (sprints2.length === 0) {
|
|
38433
|
+
await ctx.reply(formatInfo("No sprints found."), {
|
|
38434
|
+
parse_mode: "HTML"
|
|
38435
|
+
});
|
|
38436
|
+
return;
|
|
38437
|
+
}
|
|
38438
|
+
const statusIcon = {
|
|
38439
|
+
["ACTIVE" /* ACTIVE */]: "\uD83D\uDFE2",
|
|
38440
|
+
["PLANNED" /* PLANNED */]: "\uD83D\uDCCB",
|
|
38441
|
+
["COMPLETED" /* COMPLETED */]: "✅"
|
|
38442
|
+
};
|
|
38443
|
+
const active = sprints2.filter((s) => s.status === "ACTIVE" /* ACTIVE */);
|
|
38444
|
+
const planned = sprints2.filter((s) => s.status === "PLANNED" /* PLANNED */);
|
|
38445
|
+
const completed = sprints2.filter((s) => s.status === "COMPLETED" /* COMPLETED */);
|
|
38446
|
+
let msg = `<b>Sprints</b>
|
|
38447
|
+
|
|
38448
|
+
`;
|
|
38449
|
+
const formatGroup = (label, items, icon) => {
|
|
38450
|
+
if (items.length === 0)
|
|
38451
|
+
return;
|
|
38452
|
+
msg += `<b>${label}</b>
|
|
38453
|
+
`;
|
|
38454
|
+
for (const sprint2 of items) {
|
|
38455
|
+
msg += `${icon} <b>${escapeHtml(sprint2.name)}</b>
|
|
38456
|
+
`;
|
|
38457
|
+
msg += ` Status: <code>${sprint2.status}</code>
|
|
38458
|
+
`;
|
|
38459
|
+
msg += ` ID: <code>${sprint2.id}</code>
|
|
38460
|
+
|
|
38461
|
+
`;
|
|
38462
|
+
}
|
|
38463
|
+
};
|
|
38464
|
+
formatGroup("Active", active, statusIcon["ACTIVE" /* ACTIVE */]);
|
|
38465
|
+
formatGroup("Planned", planned, statusIcon["PLANNED" /* PLANNED */]);
|
|
38466
|
+
formatGroup("Completed", completed, statusIcon["COMPLETED" /* COMPLETED */]);
|
|
38467
|
+
await ctx.reply(msg.trim(), {
|
|
38468
|
+
parse_mode: "HTML"
|
|
38469
|
+
});
|
|
38470
|
+
} catch (err) {
|
|
38471
|
+
console.error("[sprints] Failed:", err);
|
|
38472
|
+
await ctx.reply(formatError(`Failed to fetch sprints: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
38473
|
+
}
|
|
38474
|
+
}
|
|
38475
|
+
async function completeSprintCommand(ctx, config2) {
|
|
38476
|
+
const text = (ctx.message && "text" in ctx.message ? ctx.message.text : "") || "";
|
|
38477
|
+
const sprintId = text.replace(/^\/completesprint\s*/, "").trim();
|
|
38478
|
+
console.log(`[completesprint] Received: ${sprintId || "(empty)"}`);
|
|
38479
|
+
if (!sprintId) {
|
|
38480
|
+
await ctx.reply(formatError("Usage: /completesprint <sprint-id>"), {
|
|
38481
|
+
parse_mode: "HTML"
|
|
38482
|
+
});
|
|
38483
|
+
return;
|
|
38484
|
+
}
|
|
38485
|
+
if (!config2.apiKey) {
|
|
38486
|
+
await ctx.reply(formatError("API key is required for /completesprint. Run: locus config setup --api-key <KEY>"), { parse_mode: "HTML" });
|
|
38487
|
+
return;
|
|
38488
|
+
}
|
|
38489
|
+
try {
|
|
38490
|
+
const client = createClient(config2);
|
|
38491
|
+
const workspaceId = await resolveWorkspaceId(client, config2);
|
|
38492
|
+
const sprint2 = await client.sprints.getById(sprintId, workspaceId);
|
|
38493
|
+
if (sprint2.status === "COMPLETED" /* COMPLETED */) {
|
|
38494
|
+
await ctx.reply(formatInfo(`Sprint "${escapeHtml(sprint2.name)}" is already completed.`), { parse_mode: "HTML" });
|
|
38495
|
+
return;
|
|
38496
|
+
}
|
|
38497
|
+
await client.sprints.complete(sprintId, workspaceId);
|
|
38498
|
+
console.log(`[completesprint] Sprint ${sprintId} completed`);
|
|
38499
|
+
await ctx.reply(`✅ Sprint "<b>${escapeHtml(sprint2.name)}</b>" has been completed.
|
|
38500
|
+
|
|
38501
|
+
IN_REVIEW tasks moved to DONE. IN_PROGRESS tasks moved to BACKLOG.`, { parse_mode: "HTML" });
|
|
38502
|
+
} catch (err) {
|
|
38503
|
+
console.error("[completesprint] Failed:", err);
|
|
38504
|
+
await ctx.reply(formatError(`Failed to complete sprint: ${err instanceof Error ? err.message : String(err)}`), { parse_mode: "HTML" });
|
|
38505
|
+
}
|
|
38506
|
+
}
|
|
38507
|
+
// src/commands/status.ts
|
|
38508
|
+
async function statusCommand(ctx, executor) {
|
|
38509
|
+
console.log("[status] Checking status");
|
|
38510
|
+
const running = executor.getRunning();
|
|
38511
|
+
let msg = `<b>Locus Bot Status</b>
|
|
38512
|
+
|
|
38513
|
+
`;
|
|
38514
|
+
if (running.length === 0) {
|
|
38515
|
+
msg += `No running processes.
|
|
38516
|
+
`;
|
|
38517
|
+
} else {
|
|
38518
|
+
msg += `<b>Running processes (${running.length}):</b>
|
|
38519
|
+
`;
|
|
38520
|
+
for (const proc of running) {
|
|
38521
|
+
const elapsed = Math.round((Date.now() - proc.startedAt.getTime()) / 1000);
|
|
38522
|
+
msg += ` • <code>${escapeHtml(proc.command)}</code> (${elapsed}s)
|
|
38523
|
+
`;
|
|
38524
|
+
}
|
|
38525
|
+
}
|
|
38526
|
+
await ctx.reply(msg, { parse_mode: "HTML" });
|
|
38527
|
+
}
|
|
38528
|
+
async function agentsCommand(ctx, executor) {
|
|
38529
|
+
console.log("[agents] Listing agents");
|
|
38530
|
+
const args = executor.buildArgs(["agents", "list"]);
|
|
38531
|
+
const result = await executor.execute(args);
|
|
38532
|
+
const output = (result.stdout + result.stderr).trim();
|
|
38533
|
+
await ctx.reply(formatCommandOutput("locus agents list", output, result.exitCode), { parse_mode: "HTML" });
|
|
38534
|
+
}
|
|
38535
|
+
// src/commands/tasks.ts
|
|
38536
|
+
function createClient2(config2) {
|
|
38537
|
+
return new LocusClient({
|
|
38538
|
+
baseUrl: config2.apiBase || "https://api.locusai.dev/api",
|
|
38539
|
+
token: config2.apiKey
|
|
38540
|
+
});
|
|
38541
|
+
}
|
|
38542
|
+
async function resolveWorkspaceId2(client, config2) {
|
|
38543
|
+
if (config2.workspaceId) {
|
|
38544
|
+
return config2.workspaceId;
|
|
38545
|
+
}
|
|
38546
|
+
console.log("[workspace] Resolving workspace from API key...");
|
|
38547
|
+
const info = await client.auth.getApiKeyInfo();
|
|
38548
|
+
if (info.workspaceId) {
|
|
38549
|
+
console.log(`[workspace] Resolved workspace: ${info.workspaceId}`);
|
|
38550
|
+
return info.workspaceId;
|
|
38551
|
+
}
|
|
38552
|
+
throw new Error("Could not resolve workspace from API key. Please set workspaceId in settings.");
|
|
38553
|
+
}
|
|
38445
38554
|
async function tasksCommand(ctx, config2) {
|
|
38446
38555
|
console.log("[tasks] Listing active tasks");
|
|
38447
38556
|
if (!config2.apiKey) {
|
|
@@ -38449,8 +38558,8 @@ async function tasksCommand(ctx, config2) {
|
|
|
38449
38558
|
return;
|
|
38450
38559
|
}
|
|
38451
38560
|
try {
|
|
38452
|
-
const client =
|
|
38453
|
-
const workspaceId = await
|
|
38561
|
+
const client = createClient2(config2);
|
|
38562
|
+
const workspaceId = await resolveWorkspaceId2(client, config2);
|
|
38454
38563
|
const tasks2 = await client.tasks.list(workspaceId, {
|
|
38455
38564
|
status: [
|
|
38456
38565
|
"IN_PROGRESS" /* IN_PROGRESS */,
|
|
@@ -38512,8 +38621,8 @@ async function rejectTaskCommand(ctx, config2) {
|
|
|
38512
38621
|
return;
|
|
38513
38622
|
}
|
|
38514
38623
|
try {
|
|
38515
|
-
const client =
|
|
38516
|
-
const workspaceId = await
|
|
38624
|
+
const client = createClient2(config2);
|
|
38625
|
+
const workspaceId = await resolveWorkspaceId2(client, config2);
|
|
38517
38626
|
const task2 = await client.tasks.getById(taskId, workspaceId);
|
|
38518
38627
|
if (task2.status !== "IN_REVIEW" /* IN_REVIEW */) {
|
|
38519
38628
|
await ctx.reply(formatError(`Task "${task2.title}" is in ${task2.status} status. Only IN_REVIEW tasks can be rejected.`), { parse_mode: "HTML" });
|
|
@@ -42407,7 +42516,9 @@ Run /worktrees to see the list.`), { parse_mode: "HTML" });
|
|
|
42407
42516
|
const manager = createWorktreeManager(config2);
|
|
42408
42517
|
if (arg === "all") {
|
|
42409
42518
|
const count = manager.removeAll();
|
|
42410
|
-
await ctx.reply(formatSuccess(`Removed ${count} worktree(s).`), {
|
|
42519
|
+
await ctx.reply(formatSuccess(`Removed ${count} worktree(s).`), {
|
|
42520
|
+
parse_mode: "HTML"
|
|
42521
|
+
});
|
|
42411
42522
|
return;
|
|
42412
42523
|
}
|
|
42413
42524
|
const index = Number.parseInt(arg, 10);
|
|
@@ -42637,6 +42748,8 @@ function createBot(config2) {
|
|
|
42637
42748
|
bot.command("exec", (ctx) => execCommand(ctx, executor));
|
|
42638
42749
|
bot.command("tasks", (ctx) => tasksCommand(ctx, config2));
|
|
42639
42750
|
bot.command("rejecttask", (ctx) => rejectTaskCommand(ctx, config2));
|
|
42751
|
+
bot.command("sprints", (ctx) => sprintsCommand(ctx, config2));
|
|
42752
|
+
bot.command("completesprint", (ctx) => completeSprintCommand(ctx, config2));
|
|
42640
42753
|
bot.command("git", (ctx) => gitCommand(ctx, config2));
|
|
42641
42754
|
bot.command("dev", (ctx) => devCommand(ctx, config2));
|
|
42642
42755
|
bot.command("status", (ctx) => statusCommand(ctx, executor));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/telegram",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.17",
|
|
4
4
|
"description": "Telegram bot for Locus - remote control your AI agents from Telegram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"author": "",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@locusai/sdk": "^0.9.
|
|
35
|
+
"@locusai/sdk": "^0.9.17",
|
|
36
36
|
"dotenv": "^16.4.7",
|
|
37
37
|
"telegraf": "^4.16.3"
|
|
38
38
|
},
|