@locusai/locus-telegram 0.22.12 → 0.22.14

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.
Files changed (2) hide show
  1. package/bin/locus-telegram.js +583 -182
  2. package/package.json +5 -4
@@ -9480,19 +9480,11 @@ var init_config = __esm(() => {
9480
9480
  init_dist();
9481
9481
  });
9482
9482
 
9483
- // src/pm2.ts
9483
+ // ../pm2/dist/index.js
9484
9484
  import { execSync } from "node:child_process";
9485
- import { existsSync as existsSync2 } from "node:fs";
9486
9485
  import { dirname, join as join2 } from "node:path";
9487
9486
  import { fileURLToPath } from "node:url";
9488
9487
  function getPm2Bin() {
9489
- try {
9490
- const currentFile = fileURLToPath(import.meta.url);
9491
- const packageRoot = dirname(dirname(currentFile));
9492
- const localPm2 = join2(packageRoot, "..", "..", ".bin", "pm2");
9493
- if (existsSync2(localPm2))
9494
- return localPm2;
9495
- } catch {}
9496
9488
  try {
9497
9489
  const result = execSync("which pm2", {
9498
9490
  encoding: "utf-8",
@@ -9516,51 +9508,47 @@ function pm2Exec(args) {
9516
9508
  throw new Error(err.stderr?.trim() || err.message || "PM2 command failed");
9517
9509
  }
9518
9510
  }
9519
- function getBotScriptPath() {
9520
- const currentFile = fileURLToPath(import.meta.url);
9521
- const packageRoot = dirname(dirname(currentFile));
9522
- return join2(packageRoot, "bin", "locus-telegram.js");
9523
- }
9524
- function pm2Start() {
9525
- const script = getBotScriptPath();
9511
+ function pm2Start(config) {
9512
+ const { processName, scriptPath, scriptArgs = [] } = config;
9526
9513
  const pm2 = getPm2Bin();
9527
9514
  try {
9528
9515
  const list = pm2Exec("jlist");
9529
9516
  const processes = JSON.parse(list);
9530
- const existing = processes.find((p) => p.name === PROCESS_NAME);
9517
+ const existing = processes.find((p) => p.name === processName);
9531
9518
  if (existing) {
9532
- pm2Exec(`restart ${PROCESS_NAME}`);
9533
- return `Restarted ${PROCESS_NAME}`;
9519
+ pm2Exec(`restart ${processName}`);
9520
+ return `Restarted ${processName}`;
9534
9521
  }
9535
9522
  } catch {}
9536
- execSync(`${pm2} start ${JSON.stringify(script)} --name ${PROCESS_NAME} -- bot`, {
9523
+ const argsStr = scriptArgs.length > 0 ? ` -- ${scriptArgs.join(" ")}` : "";
9524
+ execSync(`${pm2} start ${JSON.stringify(scriptPath)} --name ${processName}${argsStr}`, {
9537
9525
  encoding: "utf-8",
9538
9526
  stdio: "inherit",
9539
9527
  env: process.env
9540
9528
  });
9541
- return `Started ${PROCESS_NAME}`;
9529
+ return `Started ${processName}`;
9542
9530
  }
9543
- function pm2Stop() {
9544
- pm2Exec(`stop ${PROCESS_NAME}`);
9545
- return `Stopped ${PROCESS_NAME}`;
9531
+ function pm2Stop(config) {
9532
+ pm2Exec(`stop ${config.processName}`);
9533
+ return `Stopped ${config.processName}`;
9546
9534
  }
9547
- function pm2Restart() {
9548
- pm2Exec(`restart ${PROCESS_NAME}`);
9549
- return `Restarted ${PROCESS_NAME}`;
9535
+ function pm2Restart(config) {
9536
+ pm2Exec(`restart ${config.processName}`);
9537
+ return `Restarted ${config.processName}`;
9550
9538
  }
9551
- function pm2Delete() {
9552
- pm2Exec(`delete ${PROCESS_NAME}`);
9553
- return `Deleted ${PROCESS_NAME}`;
9539
+ function pm2Delete(config) {
9540
+ pm2Exec(`delete ${config.processName}`);
9541
+ return `Deleted ${config.processName}`;
9554
9542
  }
9555
- function pm2Status() {
9543
+ function pm2Status(config) {
9556
9544
  try {
9557
9545
  const list = pm2Exec("jlist");
9558
9546
  const processes = JSON.parse(list);
9559
- const proc = processes.find((p) => p.name === PROCESS_NAME);
9547
+ const proc = processes.find((p) => p.name === config.processName);
9560
9548
  if (!proc)
9561
9549
  return null;
9562
9550
  return {
9563
- name: PROCESS_NAME,
9551
+ name: config.processName,
9564
9552
  status: proc.pm2_env?.status ?? "unknown",
9565
9553
  pid: proc.pid ?? null,
9566
9554
  uptime: proc.pm2_env?.pm_uptime ?? null,
@@ -9571,17 +9559,285 @@ function pm2Status() {
9571
9559
  return null;
9572
9560
  }
9573
9561
  }
9574
- function pm2Logs(lines = 50) {
9562
+ function pm2Logs(config, lines = 50) {
9575
9563
  try {
9576
- return pm2Exec(`logs ${PROCESS_NAME} --nostream --lines ${lines}`);
9564
+ return pm2Exec(`logs ${config.processName} --nostream --lines ${lines}`);
9577
9565
  } catch {
9578
9566
  return "No logs available.";
9579
9567
  }
9580
9568
  }
9581
- var PROCESS_NAME = "locus-telegram";
9582
- var init_pm2 = () => {};
9569
+ function resolvePackageScript(importMetaUrl, binName) {
9570
+ const currentFile = fileURLToPath(importMetaUrl);
9571
+ const packageRoot = dirname(dirname(currentFile));
9572
+ return join2(packageRoot, "bin", `${binName}.js`);
9573
+ }
9574
+ var init_dist2 = () => {};
9583
9575
 
9584
- // src/tracker.ts
9576
+ // src/pm2.ts
9577
+ function getConfig() {
9578
+ return {
9579
+ processName: "locus-telegram",
9580
+ scriptPath: resolvePackageScript(import.meta.url, "locus-telegram"),
9581
+ scriptArgs: ["bot"]
9582
+ };
9583
+ }
9584
+ function pm2Start2() {
9585
+ return pm2Start(getConfig());
9586
+ }
9587
+ function pm2Stop2() {
9588
+ return pm2Stop(getConfig());
9589
+ }
9590
+ function pm2Restart2() {
9591
+ return pm2Restart(getConfig());
9592
+ }
9593
+ function pm2Delete2() {
9594
+ return pm2Delete(getConfig());
9595
+ }
9596
+ function pm2Status2() {
9597
+ return pm2Status(getConfig());
9598
+ }
9599
+ function pm2Logs2(lines = 50) {
9600
+ return pm2Logs(getConfig(), lines);
9601
+ }
9602
+ var init_pm2 = __esm(() => {
9603
+ init_dist2();
9604
+ });
9605
+
9606
+ // ../gateway/dist/index.js
9607
+ import { exec as execCb } from "node:child_process";
9608
+ import { promisify } from "node:util";
9609
+ import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
9610
+ function getCommandDefinition(command) {
9611
+ return COMMAND_REGISTRY[command] ?? null;
9612
+ }
9613
+ function invokeLocusStream2(args, cwd) {
9614
+ return spawn2("locus", args, {
9615
+ cwd: cwd ?? process.cwd(),
9616
+ stdio: ["ignore", "pipe", "pipe"],
9617
+ env: process.env,
9618
+ shell: false
9619
+ });
9620
+ }
9621
+ function formatData2(data) {
9622
+ if (!data || Object.keys(data).length === 0)
9623
+ return "";
9624
+ return ` ${dim2(JSON.stringify(data))}`;
9625
+ }
9626
+ function createLogger2(name) {
9627
+ const prefix = dim2(`[${name}]`);
9628
+ return {
9629
+ info(msg, data) {
9630
+ process.stderr.write(`${bold2(cyan2("●"))} ${prefix} ${msg}${formatData2(data)}
9631
+ `);
9632
+ },
9633
+ warn(msg, data) {
9634
+ process.stderr.write(`${bold2(yellow2("⚠"))} ${prefix} ${yellow2(msg)}${formatData2(data)}
9635
+ `);
9636
+ },
9637
+ error(msg, data) {
9638
+ process.stderr.write(`${bold2(red2("✗"))} ${prefix} ${red2(msg)}${formatData2(data)}
9639
+ `);
9640
+ },
9641
+ debug(msg, data) {
9642
+ if (!process.env.LOCUS_DEBUG)
9643
+ return;
9644
+ process.stderr.write(`${gray2("⋯")} ${prefix} ${gray2(msg)}${formatData2(data)}
9645
+ `);
9646
+ }
9647
+ };
9648
+ }
9649
+
9650
+ class CommandExecutor {
9651
+ tracker;
9652
+ constructor(tracker) {
9653
+ this.tracker = tracker;
9654
+ }
9655
+ getTracker() {
9656
+ return this.tracker;
9657
+ }
9658
+ async executeLocusCommand(sessionId, command, args, callbacks) {
9659
+ const definition = getCommandDefinition(command);
9660
+ if (!definition) {
9661
+ return {
9662
+ text: `Unknown command: /${command}`,
9663
+ format: "plain",
9664
+ exitCode: 1
9665
+ };
9666
+ }
9667
+ if (definition.requiresArgs && args.length === 0) {
9668
+ return {
9669
+ text: definition.requiresArgs,
9670
+ format: "plain",
9671
+ exitCode: 1
9672
+ };
9673
+ }
9674
+ const conflict = this.tracker.checkExclusiveConflict(sessionId, command);
9675
+ if (conflict) {
9676
+ return {
9677
+ text: formatConflictText(command, conflict),
9678
+ format: "plain",
9679
+ exitCode: 1
9680
+ };
9681
+ }
9682
+ const fullArgs = [...definition.cliArgs, ...args];
9683
+ if (definition.streaming && callbacks) {
9684
+ return this.executeStreaming(sessionId, command, args, fullArgs, callbacks);
9685
+ }
9686
+ return this.executeBuffered(sessionId, command, args, fullArgs);
9687
+ }
9688
+ async executeGit(sessionId, command, args, gitArgs) {
9689
+ const conflict = this.tracker.checkExclusiveConflict(sessionId, command);
9690
+ if (conflict) {
9691
+ return {
9692
+ text: formatConflictText(command, conflict),
9693
+ format: "plain",
9694
+ exitCode: 1
9695
+ };
9696
+ }
9697
+ const trackingId = this.tracker.track(sessionId, command, args);
9698
+ try {
9699
+ const { stdout } = await exec(`git ${gitArgs}`, { cwd: process.cwd() });
9700
+ return {
9701
+ text: stdout,
9702
+ format: "plain",
9703
+ exitCode: 0
9704
+ };
9705
+ } catch (error) {
9706
+ const errStr = String(error);
9707
+ return {
9708
+ text: errStr,
9709
+ format: "plain",
9710
+ exitCode: 1
9711
+ };
9712
+ } finally {
9713
+ this.tracker.untrack(sessionId, trackingId);
9714
+ }
9715
+ }
9716
+ async executeStreaming(sessionId, command, args, fullArgs, callbacks) {
9717
+ const child = invokeLocusStream2(fullArgs);
9718
+ const trackingId = this.tracker.track(sessionId, command, args, child);
9719
+ let output = "";
9720
+ let lastUpdateTime = 0;
9721
+ let updateTimer = null;
9722
+ let messageId = "";
9723
+ const displayCmd = `locus ${fullArgs.join(" ")}`;
9724
+ const startResult = await callbacks.onStart(formatStreamingText(displayCmd, "", false));
9725
+ if (startResult)
9726
+ messageId = startResult;
9727
+ const pushUpdate = async () => {
9728
+ const now = Date.now();
9729
+ if (now - lastUpdateTime < STREAM_UPDATE_INTERVAL)
9730
+ return;
9731
+ lastUpdateTime = now;
9732
+ try {
9733
+ await callbacks.onUpdate(messageId, formatStreamingText(displayCmd, output, false));
9734
+ } catch {}
9735
+ };
9736
+ child.stdout?.on("data", (chunk) => {
9737
+ output += chunk.toString();
9738
+ if (updateTimer)
9739
+ clearTimeout(updateTimer);
9740
+ updateTimer = setTimeout(pushUpdate, STREAM_UPDATE_INTERVAL);
9741
+ });
9742
+ child.stderr?.on("data", (chunk) => {
9743
+ output += chunk.toString();
9744
+ });
9745
+ return new Promise((resolve) => {
9746
+ child.on("close", async (exitCode) => {
9747
+ this.tracker.untrack(sessionId, trackingId);
9748
+ if (updateTimer)
9749
+ clearTimeout(updateTimer);
9750
+ const code = exitCode ?? 0;
9751
+ await callbacks.onComplete(messageId, formatStreamingText(displayCmd, output, true), code);
9752
+ resolve({
9753
+ text: output,
9754
+ format: "plain",
9755
+ streaming: true,
9756
+ exitCode: code
9757
+ });
9758
+ });
9759
+ });
9760
+ }
9761
+ async executeBuffered(sessionId, command, args, fullArgs) {
9762
+ const child = invokeLocusStream2(fullArgs);
9763
+ const trackingId = this.tracker.track(sessionId, command, args, child);
9764
+ let output = "";
9765
+ child.stdout?.on("data", (chunk) => {
9766
+ output += chunk.toString();
9767
+ });
9768
+ child.stderr?.on("data", (chunk) => {
9769
+ output += chunk.toString();
9770
+ });
9771
+ return new Promise((resolve) => {
9772
+ child.on("close", (exitCode) => {
9773
+ this.tracker.untrack(sessionId, trackingId);
9774
+ const code = exitCode ?? 0;
9775
+ resolve({
9776
+ text: output,
9777
+ format: "plain",
9778
+ exitCode: code
9779
+ });
9780
+ });
9781
+ });
9782
+ }
9783
+ }
9784
+ function formatConflictText(blockedCommand, conflict) {
9785
+ const running = conflict.runningCommand;
9786
+ const runningLabel = `/${running.command}${running.args.length ? ` ${running.args.join(" ")}` : ""}`;
9787
+ return `/${blockedCommand} cannot start — ${runningLabel} is already running.
9788
+
9789
+ Send /cancel to abort it, or wait for it to finish.`;
9790
+ }
9791
+ function formatStreamingText(command, output, isComplete) {
9792
+ const status = isComplete ? "[DONE]" : "[RUNNING]";
9793
+ const header = `${status} ${command}`;
9794
+ if (!output.trim()) {
9795
+ return isComplete ? `${header}
9796
+
9797
+ Completed.` : `${header}
9798
+
9799
+ Running...
9800
+
9801
+ Send /cancel to abort`;
9802
+ }
9803
+ const lines = output.trim().split(`
9804
+ `);
9805
+ const lastLines = lines.slice(-30).join(`
9806
+ `);
9807
+ const hint = isComplete ? "" : `
9808
+
9809
+ Send /cancel to abort`;
9810
+ return `${header}
9811
+
9812
+ ${lastLines}${hint}`;
9813
+ }
9814
+
9815
+ class CommandRouter {
9816
+ prefix;
9817
+ constructor(prefix = "/") {
9818
+ this.prefix = prefix;
9819
+ }
9820
+ parse(text) {
9821
+ const trimmed = text.trim();
9822
+ if (!trimmed.startsWith(this.prefix)) {
9823
+ return { type: "freetext", text: trimmed };
9824
+ }
9825
+ const withoutPrefix = trimmed.slice(this.prefix.length);
9826
+ const parts = withoutPrefix.split(/\s+/);
9827
+ const rawCommand = parts[0] ?? "";
9828
+ const command = rawCommand.replace(/@\S+$/, "").toLowerCase();
9829
+ if (!command) {
9830
+ return { type: "freetext", text: trimmed };
9831
+ }
9832
+ const args = parts.slice(1);
9833
+ return {
9834
+ type: "command",
9835
+ command,
9836
+ args,
9837
+ raw: trimmed
9838
+ };
9839
+ }
9840
+ }
9585
9841
  function getExclusivityGroup(command) {
9586
9842
  if (WORKSPACE_EXCLUSIVE.has(command))
9587
9843
  return "workspace";
@@ -9592,7 +9848,7 @@ function getExclusivityGroup(command) {
9592
9848
 
9593
9849
  class CommandTracker {
9594
9850
  active = new Map;
9595
- track(chatId, command, args, childProcess = null) {
9851
+ track(sessionId, command, args, childProcess = null) {
9596
9852
  const id = String(nextId++);
9597
9853
  const entry = {
9598
9854
  id,
@@ -9601,32 +9857,32 @@ class CommandTracker {
9601
9857
  childProcess,
9602
9858
  startedAt: new Date
9603
9859
  };
9604
- const list = this.active.get(chatId);
9860
+ const list = this.active.get(sessionId);
9605
9861
  if (list) {
9606
9862
  list.push(entry);
9607
9863
  } else {
9608
- this.active.set(chatId, [entry]);
9864
+ this.active.set(sessionId, [entry]);
9609
9865
  }
9610
9866
  return id;
9611
9867
  }
9612
- untrack(chatId, id) {
9613
- const list = this.active.get(chatId);
9868
+ untrack(sessionId, id) {
9869
+ const list = this.active.get(sessionId);
9614
9870
  if (!list)
9615
9871
  return;
9616
9872
  const idx = list.findIndex((c) => c.id === id);
9617
9873
  if (idx !== -1)
9618
9874
  list.splice(idx, 1);
9619
9875
  if (list.length === 0)
9620
- this.active.delete(chatId);
9876
+ this.active.delete(sessionId);
9621
9877
  }
9622
- getActive(chatId) {
9623
- return this.active.get(chatId) ?? [];
9878
+ getActive(sessionId) {
9879
+ return this.active.get(sessionId) ?? [];
9624
9880
  }
9625
- checkExclusiveConflict(chatId, command) {
9881
+ checkExclusiveConflict(sessionId, command) {
9626
9882
  const group = getExclusivityGroup(command);
9627
9883
  if (!group)
9628
9884
  return null;
9629
- const list = this.active.get(chatId);
9885
+ const list = this.active.get(sessionId);
9630
9886
  if (!list)
9631
9887
  return null;
9632
9888
  for (const entry of list) {
@@ -9636,8 +9892,8 @@ class CommandTracker {
9636
9892
  }
9637
9893
  return null;
9638
9894
  }
9639
- kill(chatId, id) {
9640
- const list = this.active.get(chatId);
9895
+ kill(sessionId, id) {
9896
+ const list = this.active.get(sessionId);
9641
9897
  if (!list)
9642
9898
  return false;
9643
9899
  const entry = list.find((c) => c.id === id);
@@ -9646,11 +9902,11 @@ class CommandTracker {
9646
9902
  if (entry.childProcess && !entry.childProcess.killed) {
9647
9903
  entry.childProcess.kill("SIGTERM");
9648
9904
  }
9649
- this.untrack(chatId, id);
9905
+ this.untrack(sessionId, id);
9650
9906
  return true;
9651
9907
  }
9652
- killAll(chatId) {
9653
- const list = this.active.get(chatId);
9908
+ killAll(sessionId) {
9909
+ const list = this.active.get(sessionId);
9654
9910
  if (!list)
9655
9911
  return 0;
9656
9912
  let killed = 0;
@@ -9660,25 +9916,191 @@ class CommandTracker {
9660
9916
  }
9661
9917
  killed++;
9662
9918
  }
9663
- this.active.delete(chatId);
9919
+ this.active.delete(sessionId);
9664
9920
  return killed;
9665
9921
  }
9666
9922
  }
9667
- var WORKSPACE_EXCLUSIVE, GIT_EXCLUSIVE, nextId = 1, commandTracker;
9923
+
9924
+ class Gateway {
9925
+ adapters = new Map;
9926
+ router;
9927
+ executor;
9928
+ tracker;
9929
+ onEvent;
9930
+ constructor(options = {}) {
9931
+ this.router = new CommandRouter;
9932
+ this.tracker = new CommandTracker;
9933
+ this.executor = new CommandExecutor(this.tracker);
9934
+ this.onEvent = options.onEvent;
9935
+ }
9936
+ register(adapter) {
9937
+ if (this.adapters.has(adapter.platform)) {
9938
+ throw new Error(`Adapter already registered for platform: ${adapter.platform}`);
9939
+ }
9940
+ this.adapters.set(adapter.platform, adapter);
9941
+ logger.info(`Registered adapter: ${adapter.platform}`);
9942
+ }
9943
+ getAdapter(platform) {
9944
+ return this.adapters.get(platform);
9945
+ }
9946
+ getRouter() {
9947
+ return this.router;
9948
+ }
9949
+ getExecutor() {
9950
+ return this.executor;
9951
+ }
9952
+ getTracker() {
9953
+ return this.tracker;
9954
+ }
9955
+ async handleMessage(message) {
9956
+ this.emit({ type: "message_received", message });
9957
+ const adapter = this.adapters.get(message.platform);
9958
+ if (!adapter) {
9959
+ logger.warn(`No adapter registered for platform: ${message.platform}`);
9960
+ return;
9961
+ }
9962
+ const parsed = this.router.parse(message.text);
9963
+ if (parsed.type === "freetext") {
9964
+ await this.executeCommand(adapter, message.sessionId, "exec", [parsed.text], message);
9965
+ return;
9966
+ }
9967
+ await this.executeCommand(adapter, message.sessionId, parsed.command, parsed.args, message);
9968
+ }
9969
+ async start() {
9970
+ const platforms = Array.from(this.adapters.keys());
9971
+ logger.info(`Starting gateway with adapters: ${platforms.join(", ")}`);
9972
+ for (const [platform, adapter] of this.adapters) {
9973
+ try {
9974
+ await adapter.start();
9975
+ logger.info(`Started adapter: ${platform}`);
9976
+ } catch (error) {
9977
+ logger.error(`Failed to start adapter: ${platform}`, {
9978
+ error: String(error)
9979
+ });
9980
+ throw error;
9981
+ }
9982
+ }
9983
+ }
9984
+ async stop() {
9985
+ for (const [platform, adapter] of this.adapters) {
9986
+ try {
9987
+ await adapter.stop();
9988
+ logger.info(`Stopped adapter: ${platform}`);
9989
+ } catch (error) {
9990
+ logger.warn(`Error stopping adapter: ${platform}`, {
9991
+ error: String(error)
9992
+ });
9993
+ }
9994
+ }
9995
+ }
9996
+ async executeCommand(adapter, sessionId, command, args, _originalMessage) {
9997
+ this.emit({
9998
+ type: "command_started",
9999
+ sessionId,
10000
+ command,
10001
+ args
10002
+ });
10003
+ let streamCallbacks;
10004
+ if (adapter.capabilities.supportsStreaming && adapter.edit) {
10005
+ const adapterRef = adapter;
10006
+ const sentMessageId = "";
10007
+ streamCallbacks = {
10008
+ async onStart(text) {
10009
+ await adapterRef.send(sessionId, {
10010
+ text,
10011
+ format: "plain"
10012
+ });
10013
+ return sentMessageId;
10014
+ },
10015
+ async onUpdate(messageId, text) {
10016
+ if (adapterRef.edit) {
10017
+ await adapterRef.edit(sessionId, messageId, {
10018
+ text,
10019
+ format: "plain"
10020
+ });
10021
+ }
10022
+ },
10023
+ async onComplete(messageId, text, _exitCode) {
10024
+ if (adapterRef.edit) {
10025
+ await adapterRef.edit(sessionId, messageId, {
10026
+ text,
10027
+ format: "plain"
10028
+ });
10029
+ }
10030
+ }
10031
+ };
10032
+ }
10033
+ const result = await this.executor.executeLocusCommand(sessionId, command, args, streamCallbacks);
10034
+ this.emit({
10035
+ type: "command_completed",
10036
+ sessionId,
10037
+ command,
10038
+ exitCode: result.exitCode
10039
+ });
10040
+ if (!result.streaming) {
10041
+ await adapter.send(sessionId, {
10042
+ text: result.text,
10043
+ format: result.format,
10044
+ actions: result.actions
10045
+ });
10046
+ }
10047
+ }
10048
+ emit(event) {
10049
+ if (this.onEvent) {
10050
+ try {
10051
+ this.onEvent(event);
10052
+ } catch (error) {
10053
+ logger.warn("Event handler error", { error: String(error) });
10054
+ }
10055
+ }
10056
+ }
10057
+ }
10058
+ var COMMAND_REGISTRY, STREAMING_COMMANDS, colorEnabled2 = () => process.stderr.isTTY === true && process.env.NO_COLOR === undefined, wrap2 = (open, close) => (text) => colorEnabled2() ? `${open}${text}${close}` : text, bold2, dim2, red2, yellow2, cyan2, gray2, exec, STREAM_UPDATE_INTERVAL = 2000, WORKSPACE_EXCLUSIVE, GIT_EXCLUSIVE, nextId = 1, logger;
10059
+ var init_dist3 = __esm(() => {
10060
+ COMMAND_REGISTRY = {
10061
+ run: { cliArgs: ["run"], streaming: true },
10062
+ status: { cliArgs: ["status"], streaming: false },
10063
+ issues: { cliArgs: ["issue", "list"], streaming: false },
10064
+ issue: { cliArgs: ["issue", "show"], streaming: false },
10065
+ sprint: { cliArgs: ["sprint"], streaming: false },
10066
+ plan: { cliArgs: ["plan"], streaming: true },
10067
+ review: { cliArgs: ["review"], streaming: true },
10068
+ iterate: { cliArgs: ["iterate"], streaming: true },
10069
+ discuss: {
10070
+ cliArgs: ["discuss"],
10071
+ streaming: true,
10072
+ requiresArgs: `Please provide a discussion topic.
10073
+
10074
+ Example: /discuss Should we use Redis or in-memory caching?`
10075
+ },
10076
+ exec: {
10077
+ cliArgs: ["exec"],
10078
+ streaming: true,
10079
+ requiresArgs: `Please provide a prompt.
10080
+
10081
+ Example: /exec Add error handling to the API`
10082
+ },
10083
+ logs: { cliArgs: ["logs"], streaming: false },
10084
+ config: { cliArgs: ["config"], streaming: false },
10085
+ artifacts: { cliArgs: ["artifacts"], streaming: false }
10086
+ };
10087
+ STREAMING_COMMANDS = new Set(Object.entries(COMMAND_REGISTRY).filter(([, def]) => def.streaming).map(([name]) => name));
10088
+ bold2 = wrap2("\x1B[1m", "\x1B[22m");
10089
+ dim2 = wrap2("\x1B[2m", "\x1B[22m");
10090
+ red2 = wrap2("\x1B[31m", "\x1B[39m");
10091
+ yellow2 = wrap2("\x1B[33m", "\x1B[39m");
10092
+ cyan2 = wrap2("\x1B[36m", "\x1B[39m");
10093
+ gray2 = wrap2("\x1B[90m", "\x1B[39m");
10094
+ exec = promisify(execCb);
10095
+ WORKSPACE_EXCLUSIVE = new Set(["run", "plan", "iterate", "exec"]);
10096
+ GIT_EXCLUSIVE = new Set(["stage", "commit", "checkout", "stash", "pr"]);
10097
+ logger = createLogger2("gateway");
10098
+ });
10099
+
10100
+ // src/tracker.ts
10101
+ var commandTracker;
9668
10102
  var init_tracker = __esm(() => {
9669
- WORKSPACE_EXCLUSIVE = new Set([
9670
- "run",
9671
- "plan",
9672
- "iterate",
9673
- "exec"
9674
- ]);
9675
- GIT_EXCLUSIVE = new Set([
9676
- "stage",
9677
- "commit",
9678
- "checkout",
9679
- "stash",
9680
- "pr"
9681
- ]);
10103
+ init_dist3();
9682
10104
  commandTracker = new CommandTracker;
9683
10105
  });
9684
10106
 
@@ -9690,7 +10112,7 @@ function codeBlock(text, language = "") {
9690
10112
  const truncated = truncate(text, MAX_CODE_LENGTH);
9691
10113
  return `<pre><code${language ? ` class="language-${language}"` : ""}>${escapeHtml(truncated)}</code></pre>`;
9692
10114
  }
9693
- function bold2(text) {
10115
+ function bold3(text) {
9694
10116
  return `<b>${text}</b>`;
9695
10117
  }
9696
10118
  function italic(text) {
@@ -9705,7 +10127,7 @@ function truncate(text, maxLength = MAX_MESSAGE_LENGTH) {
9705
10127
  }
9706
10128
  function formatCommandResult(command, output, exitCode) {
9707
10129
  const status = exitCode === 0 ? "✅" : "❌";
9708
- const header = `${status} ${bold2(escapeHtml(command))}`;
10130
+ const header = `${status} ${bold3(escapeHtml(command))}`;
9709
10131
  if (!output.trim()) {
9710
10132
  return exitCode === 0 ? `${header}
9711
10133
 
@@ -9719,7 +10141,7 @@ ${codeBlock(output.trim())}`;
9719
10141
  }
9720
10142
  function formatStreamingMessage(command, output, isComplete) {
9721
10143
  const status = isComplete ? "✅" : "⏳";
9722
- const header = `${status} ${bold2(escapeHtml(command))}`;
10144
+ const header = `${status} ${bold3(escapeHtml(command))}`;
9723
10145
  if (!output.trim()) {
9724
10146
  return `${header}
9725
10147
 
@@ -9740,12 +10162,12 @@ ${codeBlock(lastLines)}${hint}`;
9740
10162
  }
9741
10163
  function formatConflictMessage(blockedCommand, running) {
9742
10164
  const runningLabel = `/${escapeHtml(running.command)}${running.args.length ? ` ${escapeHtml(running.args.join(" "))}` : ""}`;
9743
- return `⚠️ ${bold2(escapeHtml(`/${blockedCommand}`))} cannot start — ${bold2(runningLabel)} is already running.
10165
+ return `⚠️ ${bold3(escapeHtml(`/${blockedCommand}`))} cannot start — ${bold3(runningLabel)} is already running.
9744
10166
 
9745
10167
  Send /cancel to abort it, or wait for it to finish.`;
9746
10168
  }
9747
10169
  function formatError(message, detail) {
9748
- let text = `❌ ${bold2("Error")}
10170
+ let text = `❌ ${bold3("Error")}
9749
10171
 
9750
10172
  ${escapeHtml(message)}`;
9751
10173
  if (detail) {
@@ -9768,7 +10190,8 @@ async function handleCancel(ctx) {
9768
10190
  const chatId = ctx.chat?.id;
9769
10191
  if (!chatId)
9770
10192
  return;
9771
- const active = commandTracker.getActive(chatId);
10193
+ const sessionId = String(chatId);
10194
+ const active = commandTracker.getActive(sessionId);
9772
10195
  if (active.length === 0) {
9773
10196
  await ctx.reply(`ℹ️ No commands are currently running.`, {
9774
10197
  parse_mode: "HTML"
@@ -9777,8 +10200,8 @@ async function handleCancel(ctx) {
9777
10200
  }
9778
10201
  if (active.length === 1) {
9779
10202
  const cmd = active[0];
9780
- commandTracker.kill(chatId, cmd.id);
9781
- await ctx.reply(`\uD83D\uDED1 Cancelled ${bold2(`/${escapeHtml(cmd.command)}`)}${cmd.args.length ? ` ${escapeHtml(cmd.args.join(" "))}` : ""}`, { parse_mode: "HTML" });
10203
+ commandTracker.kill(sessionId, cmd.id);
10204
+ await ctx.reply(`\uD83D\uDED1 Cancelled ${bold3(`/${escapeHtml(cmd.command)}`)}${cmd.args.length ? ` ${escapeHtml(cmd.args.join(" "))}` : ""}`, { parse_mode: "HTML" });
9782
10205
  return;
9783
10206
  }
9784
10207
  const kb = new import_grammy.InlineKeyboard;
@@ -9787,7 +10210,7 @@ async function handleCancel(ctx) {
9787
10210
  kb.text(`\uD83D\uDED1 ${label}`, `${CANCEL_CMD_PREFIX}${cmd.id}`).row();
9788
10211
  }
9789
10212
  kb.text("\uD83D\uDED1 Cancel All", CANCEL_ALL);
9790
- await ctx.reply(`${bold2("Multiple commands running:")}
10213
+ await ctx.reply(`${bold3("Multiple commands running:")}
9791
10214
 
9792
10215
  ${italic("Select which to cancel:")}`, { parse_mode: "HTML", reply_markup: kb });
9793
10216
  }
@@ -9798,17 +10221,18 @@ async function handleCancelCallback(ctx) {
9798
10221
  const chatId = ctx.chat?.id;
9799
10222
  if (!chatId)
9800
10223
  return;
10224
+ const sessionId = String(chatId);
9801
10225
  if (data === CANCEL_ALL) {
9802
- const count = commandTracker.killAll(chatId);
10226
+ const count = commandTracker.killAll(sessionId);
9803
10227
  await ctx.answerCallbackQuery({ text: `Cancelled ${count} command(s)` });
9804
- await ctx.editMessageText(`\uD83D\uDED1 Cancelled ${bold2(`${count} command(s)`)}.`, {
10228
+ await ctx.editMessageText(`\uD83D\uDED1 Cancelled ${bold3(`${count} command(s)`)}.`, {
9805
10229
  parse_mode: "HTML"
9806
10230
  });
9807
10231
  return;
9808
10232
  }
9809
10233
  if (data.startsWith(CANCEL_CMD_PREFIX)) {
9810
10234
  const id = data.slice(CANCEL_CMD_PREFIX.length);
9811
- const killed = commandTracker.kill(chatId, id);
10235
+ const killed = commandTracker.kill(sessionId, id);
9812
10236
  if (killed) {
9813
10237
  await ctx.answerCallbackQuery({ text: "Command cancelled" });
9814
10238
  await ctx.editMessageText(`\uD83D\uDED1 Command cancelled.`, {
@@ -9877,11 +10301,11 @@ var init_keyboards = __esm(() => {
9877
10301
  // src/ui/messages.ts
9878
10302
  function welcomeMessage() {
9879
10303
  return [
9880
- `${bold2("\uD83E\uDD16 Locus Telegram Remote Control")}`,
10304
+ `${bold3("\uD83E\uDD16 Locus Telegram Remote Control")}`,
9881
10305
  "",
9882
10306
  "Control your Locus agent directly from Telegram.",
9883
10307
  "",
9884
- `${bold2("Locus Commands")}`,
10308
+ `${bold3("Locus Commands")}`,
9885
10309
  "/run [issue#...] — Execute issues",
9886
10310
  "/status — Dashboard view",
9887
10311
  "/issues [filters] — List issues",
@@ -9896,7 +10320,7 @@ function welcomeMessage() {
9896
10320
  "/config [path] — View config",
9897
10321
  "/artifacts — View artifacts",
9898
10322
  "",
9899
- `${bold2("Git Commands")}`,
10323
+ `${bold3("Git Commands")}`,
9900
10324
  "/gitstatus — Git status",
9901
10325
  "/stage [files|.] — Stage files",
9902
10326
  "/commit &lt;message&gt; — Commit changes",
@@ -9906,10 +10330,10 @@ function welcomeMessage() {
9906
10330
  "/diff — Show diff",
9907
10331
  "/pr &lt;title&gt; — Create pull request",
9908
10332
  "",
9909
- `${bold2("Control")}`,
10333
+ `${bold3("Control")}`,
9910
10334
  "/cancel — Abort running commands",
9911
10335
  "",
9912
- `${bold2("Service Commands")}`,
10336
+ `${bold3("Service Commands")}`,
9913
10337
  "/service start|stop|restart|status|logs",
9914
10338
  "",
9915
10339
  "/help — Show this message"
@@ -9917,14 +10341,14 @@ function welcomeMessage() {
9917
10341
  `);
9918
10342
  }
9919
10343
  function runStartedMessage(target) {
9920
- return `\uD83D\uDE80 ${bold2("Run Started")} — ${escapeHtml(target)}`;
10344
+ return `\uD83D\uDE80 ${bold3("Run Started")} — ${escapeHtml(target)}`;
9921
10345
  }
9922
10346
  function reviewStartedMessage(prNumber) {
9923
- return `\uD83D\uDD0D ${bold2("Reviewing")} PR #${prNumber}...`;
10347
+ return `\uD83D\uDD0D ${bold3("Reviewing")} PR #${prNumber}...`;
9924
10348
  }
9925
10349
  function gitCommitMessage(message, hash) {
9926
10350
  return [
9927
- `✅ ${bold2("Committed")}`,
10351
+ `✅ ${bold3("Committed")}`,
9928
10352
  "",
9929
10353
  `Message: ${escapeHtml(message)}`,
9930
10354
  `Hash: ${escapeHtml(hash)}`
@@ -9932,17 +10356,17 @@ function gitCommitMessage(message, hash) {
9932
10356
  `);
9933
10357
  }
9934
10358
  function gitBranchCreatedMessage(name) {
9935
- return `✅ Branch ${bold2(escapeHtml(name))} created.`;
10359
+ return `✅ Branch ${bold3(escapeHtml(name))} created.`;
9936
10360
  }
9937
10361
  function gitCheckoutMessage(branch) {
9938
- return `✅ Switched to branch ${bold2(escapeHtml(branch))}.`;
10362
+ return `✅ Switched to branch ${bold3(escapeHtml(branch))}.`;
9939
10363
  }
9940
10364
  function gitStashMessage(action) {
9941
- return `✅ ${bold2("Stash")} — ${escapeHtml(action)} completed.`;
10365
+ return `✅ ${bold3("Stash")} — ${escapeHtml(action)} completed.`;
9942
10366
  }
9943
10367
  function prCreatedMessage(prNumber, url) {
9944
10368
  return [
9945
- `✅ ${bold2("Pull Request Created")}`,
10369
+ `✅ ${bold3("Pull Request Created")}`,
9946
10370
  "",
9947
10371
  `PR #${prNumber}: ${escapeHtml(url)}`
9948
10372
  ].join(`
@@ -9952,9 +10376,9 @@ function serviceStatusMessage(status, pid, uptime, memory, restarts) {
9952
10376
  const uptimeStr = uptime ? formatUptime(uptime) : "N/A";
9953
10377
  const memoryStr = memory ? formatMemory(memory) : "N/A";
9954
10378
  return [
9955
- `\uD83E\uDD16 ${bold2("Locus Telegram Bot")}`,
10379
+ `\uD83E\uDD16 ${bold3("Locus Telegram Bot")}`,
9956
10380
  "",
9957
- `Status: ${bold2(status)}`,
10381
+ `Status: ${bold3(status)}`,
9958
10382
  `PID: ${pid ?? "N/A"}`,
9959
10383
  `Uptime: ${uptimeStr}`,
9960
10384
  `Memory: ${memoryStr}`,
@@ -9964,10 +10388,10 @@ function serviceStatusMessage(status, pid, uptime, memory, restarts) {
9964
10388
  }
9965
10389
  function serviceNotRunningMessage() {
9966
10390
  return [
9967
- `⚠️ ${bold2("Bot Not Running")}`,
10391
+ `⚠️ ${bold3("Bot Not Running")}`,
9968
10392
  "",
9969
10393
  "Start the bot with:",
9970
- `${bold2("locus pkg telegram start")}`
10394
+ `${bold3("locus pkg telegram start")}`
9971
10395
  ].join(`
9972
10396
  `);
9973
10397
  }
@@ -9992,10 +10416,10 @@ function formatMemory(bytes) {
9992
10416
  var init_messages = () => {};
9993
10417
 
9994
10418
  // src/commands/git.ts
9995
- import { exec as execCb } from "node:child_process";
9996
- import { promisify } from "node:util";
10419
+ import { exec as execCb2 } from "node:child_process";
10420
+ import { promisify as promisify2 } from "node:util";
9997
10421
  async function git(args) {
9998
- const { stdout } = await exec(`git ${args}`, { cwd: process.cwd() });
10422
+ const { stdout } = await exec2(`git ${args}`, { cwd: process.cwd() });
9999
10423
  return stdout;
10000
10424
  }
10001
10425
  async function gitSafe(args) {
@@ -10009,18 +10433,19 @@ async function tracked(ctx, command, args, fn) {
10009
10433
  const chatId = ctx.chat?.id;
10010
10434
  if (!chatId)
10011
10435
  return;
10012
- const conflict = commandTracker.checkExclusiveConflict(chatId, command);
10436
+ const sessionId = String(chatId);
10437
+ const conflict = commandTracker.checkExclusiveConflict(sessionId, command);
10013
10438
  if (conflict) {
10014
10439
  await ctx.reply(formatConflictMessage(command, conflict.runningCommand), {
10015
10440
  parse_mode: "HTML"
10016
10441
  });
10017
10442
  return;
10018
10443
  }
10019
- const id = commandTracker.track(chatId, command, args);
10444
+ const id = commandTracker.track(sessionId, command, args);
10020
10445
  try {
10021
10446
  await fn();
10022
10447
  } finally {
10023
- commandTracker.untrack(chatId, id);
10448
+ commandTracker.untrack(sessionId, id);
10024
10449
  }
10025
10450
  }
10026
10451
  async function handleGitStatus(ctx) {
@@ -10034,11 +10459,13 @@ async function handleGitStatus(ctx) {
10034
10459
  return;
10035
10460
  }
10036
10461
  const branch = (await git("branch --show-current")).trim();
10037
- await ctx.reply(`${bold2("Branch:")} ${escapeHtml(branch)}
10462
+ await ctx.reply(`${bold3("Branch:")} ${escapeHtml(branch)}
10038
10463
 
10039
10464
  ${codeBlock(status)}`, { parse_mode: "HTML" });
10040
10465
  } catch (error) {
10041
- await ctx.reply(formatError("Failed to get git status", String(error)), { parse_mode: "HTML" });
10466
+ await ctx.reply(formatError("Failed to get git status", String(error)), {
10467
+ parse_mode: "HTML"
10468
+ });
10042
10469
  }
10043
10470
  });
10044
10471
  }
@@ -10149,7 +10576,7 @@ async function handleBranch(ctx, args) {
10149
10576
  if (args.length === 0) {
10150
10577
  const branches = await git("branch -a --format='%(refname:short)'");
10151
10578
  const current = (await git("branch --show-current")).trim();
10152
- await ctx.reply(`${bold2("Current:")} ${escapeHtml(current)}
10579
+ await ctx.reply(`${bold3("Current:")} ${escapeHtml(current)}
10153
10580
 
10154
10581
  ${codeBlock(branches)}`, { parse_mode: "HTML" });
10155
10582
  } else {
@@ -10196,14 +10623,16 @@ async function handleDiff(ctx) {
10196
10623
  parse_mode: "HTML"
10197
10624
  });
10198
10625
  } else {
10199
- await ctx.reply(`${bold2("Staged changes:")}
10626
+ await ctx.reply(`${bold3("Staged changes:")}
10200
10627
 
10201
10628
  ${codeBlock(staged)}`, { parse_mode: "HTML" });
10202
10629
  }
10203
10630
  } else {
10204
- await ctx.reply(`${bold2("Unstaged changes:")}
10631
+ await ctx.reply(`${bold3("Unstaged changes:")}
10205
10632
 
10206
- ${codeBlock(diff)}`, { parse_mode: "HTML" });
10633
+ ${codeBlock(diff)}`, {
10634
+ parse_mode: "HTML"
10635
+ });
10207
10636
  }
10208
10637
  } catch (error) {
10209
10638
  await ctx.reply(formatError("Diff failed", String(error)), {
@@ -10226,7 +10655,7 @@ async function handlePR(ctx, args) {
10226
10655
  try {
10227
10656
  await git(`push -u origin ${branch}`);
10228
10657
  } catch {}
10229
- const { stdout: result } = await exec(`gh pr create --title ${JSON.stringify(title)} --body "Created via Locus Telegram Bot" --head ${branch}`, { cwd: process.cwd() });
10658
+ const { stdout: result } = await exec2(`gh pr create --title ${JSON.stringify(title)} --body "Created via Locus Telegram Bot" --head ${branch}`, { cwd: process.cwd() });
10230
10659
  const prMatch = result.match(/\/pull\/(\d+)/);
10231
10660
  const prNumber = prMatch ? Number(prMatch[1]) : 0;
10232
10661
  await ctx.reply(prCreatedMessage(prNumber, result.trim()), {
@@ -10239,37 +10668,37 @@ async function handlePR(ctx, args) {
10239
10668
  }
10240
10669
  });
10241
10670
  }
10242
- var exec;
10671
+ var exec2;
10243
10672
  var init_git = __esm(() => {
10244
10673
  init_tracker();
10245
10674
  init_keyboards();
10246
10675
  init_messages();
10247
- exec = promisify(execCb);
10676
+ exec2 = promisify2(execCb2);
10248
10677
  });
10249
10678
 
10250
10679
  // src/commands/locus.ts
10251
10680
  async function handleLocusCommand(ctx, command, args) {
10252
- const cliArgs = COMMAND_MAP[command];
10253
- if (!cliArgs) {
10681
+ const definition = getCommandDefinition(command);
10682
+ if (!definition) {
10254
10683
  await ctx.reply(`Unknown command: /${command}`);
10255
10684
  return;
10256
10685
  }
10257
- const requiresArgsMsg = REQUIRES_ARGS[command];
10258
- if (requiresArgsMsg && args.length === 0) {
10259
- await ctx.reply(requiresArgsMsg);
10686
+ if (definition.requiresArgs && args.length === 0) {
10687
+ await ctx.reply(definition.requiresArgs);
10260
10688
  return;
10261
10689
  }
10262
10690
  const chatId = ctx.chat?.id;
10263
10691
  if (!chatId)
10264
10692
  return;
10265
- const conflict = commandTracker.checkExclusiveConflict(chatId, command);
10693
+ const sessionId = String(chatId);
10694
+ const conflict = commandTracker.checkExclusiveConflict(sessionId, command);
10266
10695
  if (conflict) {
10267
10696
  await ctx.reply(formatConflictMessage(command, conflict.runningCommand), {
10268
10697
  parse_mode: "HTML"
10269
10698
  });
10270
10699
  return;
10271
10700
  }
10272
- const fullArgs = [...cliArgs, ...args];
10701
+ const fullArgs = [...definition.cliArgs, ...args];
10273
10702
  const displayCmd = `locus ${fullArgs.join(" ")}`;
10274
10703
  const isStreaming = STREAMING_COMMANDS.has(command);
10275
10704
  if (command === "run" && args.length > 0) {
@@ -10289,8 +10718,9 @@ async function handleStreamingCommand(ctx, displayCmd, fullArgs, command, args)
10289
10718
  const chatId = ctx.chat?.id;
10290
10719
  if (!chatId)
10291
10720
  return;
10721
+ const sessionId = String(chatId);
10292
10722
  const child = invokeLocusStream(fullArgs);
10293
- const trackingId = commandTracker.track(chatId, command, args, child);
10723
+ const trackingId = commandTracker.track(sessionId, command, args, child);
10294
10724
  let output = "";
10295
10725
  let lastEditTime = 0;
10296
10726
  let editTimer = null;
@@ -10317,7 +10747,7 @@ async function handleStreamingCommand(ctx, displayCmd, fullArgs, command, args)
10317
10747
  });
10318
10748
  await new Promise((resolve) => {
10319
10749
  child.on("close", async (exitCode) => {
10320
- commandTracker.untrack(chatId, trackingId);
10750
+ commandTracker.untrack(sessionId, trackingId);
10321
10751
  if (editTimer)
10322
10752
  clearTimeout(editTimer);
10323
10753
  try {
@@ -10335,8 +10765,9 @@ async function handleBufferedCommand(ctx, displayCmd, fullArgs, command) {
10335
10765
  const chatId = ctx.chat?.id;
10336
10766
  if (!chatId)
10337
10767
  return;
10768
+ const sessionId = String(chatId);
10338
10769
  const child = invokeLocusStream(fullArgs);
10339
- const trackingId = commandTracker.track(chatId, command, [], child);
10770
+ const trackingId = commandTracker.track(sessionId, command, [], child);
10340
10771
  let output = "";
10341
10772
  child.stdout?.on("data", (chunk) => {
10342
10773
  output += chunk.toString();
@@ -10346,7 +10777,7 @@ async function handleBufferedCommand(ctx, displayCmd, fullArgs, command) {
10346
10777
  });
10347
10778
  await new Promise((resolve) => {
10348
10779
  child.on("close", async (exitCode) => {
10349
- commandTracker.untrack(chatId, trackingId);
10780
+ commandTracker.untrack(sessionId, trackingId);
10350
10781
  const result = formatCommandResult(displayCmd, output, exitCode ?? 0);
10351
10782
  const keyboard = getPostCommandKeyboard(command, [], exitCode ?? 0);
10352
10783
  await ctx.reply(result, {
@@ -10379,43 +10810,13 @@ function getPostCommandKeyboard(command, args, exitCode) {
10379
10810
  return null;
10380
10811
  }
10381
10812
  }
10382
- var COMMAND_MAP, STREAMING_COMMANDS, REQUIRES_ARGS, EDIT_INTERVAL = 2000;
10813
+ var EDIT_INTERVAL = 2000;
10383
10814
  var init_locus = __esm(() => {
10815
+ init_dist3();
10384
10816
  init_dist();
10385
10817
  init_tracker();
10386
10818
  init_keyboards();
10387
10819
  init_messages();
10388
- COMMAND_MAP = {
10389
- run: ["run"],
10390
- status: ["status"],
10391
- issues: ["issue", "list"],
10392
- issue: ["issue", "show"],
10393
- sprint: ["sprint"],
10394
- plan: ["plan"],
10395
- review: ["review"],
10396
- iterate: ["iterate"],
10397
- discuss: ["discuss"],
10398
- exec: ["exec"],
10399
- logs: ["logs"],
10400
- config: ["config"],
10401
- artifacts: ["artifacts"]
10402
- };
10403
- STREAMING_COMMANDS = new Set([
10404
- "run",
10405
- "plan",
10406
- "review",
10407
- "iterate",
10408
- "discuss",
10409
- "exec"
10410
- ]);
10411
- REQUIRES_ARGS = {
10412
- exec: `Please provide a prompt.
10413
-
10414
- Example: /exec Add error handling to the API`,
10415
- discuss: `Please provide a discussion topic.
10416
-
10417
- Example: /discuss Should we use Redis or in-memory caching?`
10418
- };
10419
10820
  });
10420
10821
 
10421
10822
  // src/commands/service.ts
@@ -10424,27 +10825,27 @@ async function handleService(ctx, args) {
10424
10825
  try {
10425
10826
  switch (subcommand) {
10426
10827
  case "start": {
10427
- const result = pm2Start();
10828
+ const result = pm2Start2();
10428
10829
  await ctx.reply(formatSuccess(result), { parse_mode: "HTML" });
10429
10830
  break;
10430
10831
  }
10431
10832
  case "stop": {
10432
- const result = pm2Stop();
10833
+ const result = pm2Stop2();
10433
10834
  await ctx.reply(formatSuccess(result), { parse_mode: "HTML" });
10434
10835
  break;
10435
10836
  }
10436
10837
  case "restart": {
10437
- const result = pm2Restart();
10838
+ const result = pm2Restart2();
10438
10839
  await ctx.reply(formatSuccess(result), { parse_mode: "HTML" });
10439
10840
  break;
10440
10841
  }
10441
10842
  case "delete": {
10442
- const result = pm2Delete();
10843
+ const result = pm2Delete2();
10443
10844
  await ctx.reply(formatSuccess(result), { parse_mode: "HTML" });
10444
10845
  break;
10445
10846
  }
10446
10847
  case "status": {
10447
- const status = pm2Status();
10848
+ const status = pm2Status2();
10448
10849
  if (!status) {
10449
10850
  await ctx.reply(serviceNotRunningMessage(), {
10450
10851
  parse_mode: "HTML"
@@ -10456,7 +10857,7 @@ async function handleService(ctx, args) {
10456
10857
  }
10457
10858
  case "logs": {
10458
10859
  const lines = args[1] ? Number(args[1]) : 50;
10459
- const logs = pm2Logs(lines);
10860
+ const logs = pm2Logs2(lines);
10460
10861
  await ctx.reply(codeBlock(logs), { parse_mode: "HTML" });
10461
10862
  break;
10462
10863
  }
@@ -10485,7 +10886,7 @@ function createBot(config) {
10485
10886
  bot.use(async (ctx, next) => {
10486
10887
  const chatId = ctx.chat?.id;
10487
10888
  if (!chatId || !config.allowedChatIds.includes(chatId)) {
10488
- logger.warn("Unauthorized access attempt", {
10889
+ logger2.warn("Unauthorized access attempt", {
10489
10890
  chatId,
10490
10891
  from: ctx.from?.username
10491
10892
  });
@@ -10659,7 +11060,7 @@ function parseArgs(text, command) {
10659
11060
  return [];
10660
11061
  return rest.split(/\s+/);
10661
11062
  }
10662
- var import_grammy3, logger;
11063
+ var import_grammy3, logger2;
10663
11064
  var init_bot = __esm(() => {
10664
11065
  init_dist();
10665
11066
  init_cancel();
@@ -10669,7 +11070,7 @@ var init_bot = __esm(() => {
10669
11070
  init_keyboards();
10670
11071
  init_messages();
10671
11072
  import_grammy3 = __toESM(require_mod(), 1);
10672
- logger = createLogger("telegram");
11073
+ logger2 = createLogger("telegram");
10673
11074
  });
10674
11075
 
10675
11076
  // src/index.ts
@@ -10705,25 +11106,25 @@ async function main(args) {
10705
11106
  }
10706
11107
  }
10707
11108
  function handleStart() {
10708
- const result = pm2Start();
10709
- logger2.info(result);
11109
+ const result = pm2Start2();
11110
+ logger3.info(result);
10710
11111
  }
10711
11112
  function handleStop() {
10712
- const result = pm2Stop();
10713
- logger2.info(result);
11113
+ const result = pm2Stop2();
11114
+ logger3.info(result);
10714
11115
  }
10715
11116
  function handleRestart() {
10716
- const result = pm2Restart();
10717
- logger2.info(result);
11117
+ const result = pm2Restart2();
11118
+ logger3.info(result);
10718
11119
  }
10719
11120
  function handleDelete() {
10720
- const result = pm2Delete();
10721
- logger2.info(result);
11121
+ const result = pm2Delete2();
11122
+ logger3.info(result);
10722
11123
  }
10723
11124
  function handleStatus() {
10724
- const status = pm2Status();
11125
+ const status = pm2Status2();
10725
11126
  if (!status) {
10726
- logger2.warn("Bot is not running");
11127
+ logger3.warn("Bot is not running");
10727
11128
  return;
10728
11129
  }
10729
11130
  const uptimeStr = status.uptime ? `${Math.floor((Date.now() - status.uptime) / 1000)}s` : "N/A";
@@ -10739,15 +11140,15 @@ function handleStatus() {
10739
11140
  }
10740
11141
  function handleLogs(args) {
10741
11142
  const lines = args[0] ? Number(args[0]) : 50;
10742
- const logs = pm2Logs(lines);
11143
+ const logs = pm2Logs2(lines);
10743
11144
  console.log(logs);
10744
11145
  }
10745
11146
  async function handleBot() {
10746
11147
  const config = loadTelegramConfig();
10747
11148
  const { createBot: createBot2 } = await Promise.resolve().then(() => (init_bot(), exports_bot));
10748
11149
  const bot = createBot2(config);
10749
- logger2.info("Starting Telegram bot...");
10750
- logger2.info(`Allowed chat IDs: ${config.allowedChatIds.join(", ")}`);
11150
+ logger3.info("Starting Telegram bot...");
11151
+ logger3.info(`Allowed chat IDs: ${config.allowedChatIds.join(", ")}`);
10751
11152
  try {
10752
11153
  await bot.api.setMyCommands([
10753
11154
  { command: "run", description: "Execute issues" },
@@ -10775,20 +11176,20 @@ async function handleBot() {
10775
11176
  { command: "service", description: "Manage bot process" },
10776
11177
  { command: "help", description: "Show help message" }
10777
11178
  ]);
10778
- logger2.info("Telegram command menu synced.");
11179
+ logger3.info("Telegram command menu synced.");
10779
11180
  } catch (err) {
10780
- logger2.warn("Failed to sync command menu", { error: String(err) });
11181
+ logger3.warn("Failed to sync command menu", { error: String(err) });
10781
11182
  }
10782
11183
  await bot.api.deleteWebhook({ drop_pending_updates: true });
10783
11184
  const runner = import_runner.run(bot);
10784
11185
  const shutdown = () => {
10785
- logger2.info("Shutting down bot...");
11186
+ logger3.info("Shutting down bot...");
10786
11187
  runner.stop();
10787
11188
  process.exit(0);
10788
11189
  };
10789
11190
  process.on("SIGINT", shutdown);
10790
11191
  process.on("SIGTERM", shutdown);
10791
- logger2.info("Bot is running. Send /help in Telegram to get started.");
11192
+ logger3.info("Bot is running. Send /help in Telegram to get started.");
10792
11193
  }
10793
11194
  function printHelp() {
10794
11195
  console.log(`
@@ -10820,13 +11221,13 @@ function printHelp() {
10820
11221
  locus pkg telegram bot # Run in foreground (development)
10821
11222
  `);
10822
11223
  }
10823
- var import_runner, logger2;
11224
+ var import_runner, logger3;
10824
11225
  var init_src = __esm(() => {
10825
11226
  init_dist();
10826
11227
  init_config();
10827
11228
  init_pm2();
10828
11229
  import_runner = __toESM(require_mod2(), 1);
10829
- logger2 = createLogger("telegram");
11230
+ logger3 = createLogger("telegram");
10830
11231
  });
10831
11232
 
10832
11233
  // src/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locusai/locus-telegram",
3
- "version": "0.22.12",
3
+ "version": "0.22.14",
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,10 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@grammyjs/runner": "^2.0.3",
30
- "@locusai/sdk": "^0.22.12",
31
- "grammy": "^1.35.0",
32
- "pm2": "^6.0.5"
30
+ "@locusai/locus-gateway": "^0.22.14",
31
+ "@locusai/locus-pm2": "^0.22.14",
32
+ "@locusai/sdk": "^0.22.14",
33
+ "grammy": "^1.35.0"
33
34
  },
34
35
  "devDependencies": {
35
36
  "typescript": "^5.8.3"