@grinev/opencode-telegram-bot 0.3.0 → 0.4.0

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/README.md CHANGED
@@ -95,6 +95,7 @@ opencode-telegram config
95
95
  | `/projects` | Switch between OpenCode projects |
96
96
  | `/model` | Choose a model from your favorites |
97
97
  | `/agent` | Switch agent mode (Plan / Build) |
98
+ | `/rename` | Rename the current session |
98
99
  | `/opencode_start` | Start the OpenCode server remotely |
99
100
  | `/opencode_stop` | Stop the OpenCode server remotely |
100
101
  | `/help` | Show available commands |
@@ -11,6 +11,7 @@ const COMMAND_DEFINITIONS = [
11
11
  { command: "projects", descriptionKey: "cmd.description.projects" },
12
12
  { command: "model", descriptionKey: "cmd.description.model" },
13
13
  { command: "agent", descriptionKey: "cmd.description.agent" },
14
+ { command: "rename", descriptionKey: "cmd.description.rename" },
14
15
  { command: "opencode_start", descriptionKey: "cmd.description.opencode_start" },
15
16
  { command: "opencode_stop", descriptionKey: "cmd.description.opencode_stop" },
16
17
  { command: "help", descriptionKey: "cmd.description.help" },
@@ -0,0 +1,91 @@
1
+ import { InlineKeyboard } from "grammy";
2
+ import { opencodeClient } from "../../opencode/client.js";
3
+ import { getCurrentSession, setCurrentSession } from "../../session/manager.js";
4
+ import { renameManager } from "../../rename/manager.js";
5
+ import { pinnedMessageManager } from "../../pinned/manager.js";
6
+ import { logger } from "../../utils/logger.js";
7
+ import { t } from "../../i18n/index.js";
8
+ export async function renameCommand(ctx) {
9
+ try {
10
+ const currentSession = getCurrentSession();
11
+ if (!currentSession) {
12
+ await ctx.reply(t("rename.no_session"));
13
+ return;
14
+ }
15
+ const keyboard = new InlineKeyboard().text(t("rename.button.cancel"), "rename:cancel");
16
+ const message = await ctx.reply(t("rename.prompt", { title: currentSession.title }), {
17
+ reply_markup: keyboard,
18
+ });
19
+ renameManager.startWaiting(currentSession.id, currentSession.directory, currentSession.title);
20
+ renameManager.setMessageId(message.message_id);
21
+ logger.info(`[RenameCommand] Waiting for new title for session: ${currentSession.id}`);
22
+ }
23
+ catch (error) {
24
+ logger.error("[RenameCommand] Error starting rename flow:", error);
25
+ await ctx.reply(t("rename.error"));
26
+ }
27
+ }
28
+ export async function handleRenameCancel(ctx) {
29
+ const data = ctx.callbackQuery?.data;
30
+ if (!data || data !== "rename:cancel") {
31
+ return false;
32
+ }
33
+ logger.debug("[RenameHandler] Cancel callback received");
34
+ renameManager.clear();
35
+ await ctx.answerCallbackQuery();
36
+ await ctx.editMessageText(t("rename.cancelled"));
37
+ return true;
38
+ }
39
+ export async function handleRenameTextAnswer(ctx) {
40
+ if (!renameManager.isWaitingForName()) {
41
+ return false;
42
+ }
43
+ const text = ctx.message?.text;
44
+ if (!text) {
45
+ return false;
46
+ }
47
+ if (text.startsWith("/")) {
48
+ return false;
49
+ }
50
+ const sessionInfo = renameManager.getSessionInfo();
51
+ if (!sessionInfo) {
52
+ renameManager.clear();
53
+ return false;
54
+ }
55
+ const newTitle = text.trim();
56
+ if (!newTitle) {
57
+ await ctx.reply(t("rename.empty_title"));
58
+ return true;
59
+ }
60
+ logger.info(`[RenameHandler] Renaming session ${sessionInfo.sessionId} to: ${newTitle}`);
61
+ try {
62
+ const { data: updatedSession, error } = await opencodeClient.session.update({
63
+ sessionID: sessionInfo.sessionId,
64
+ directory: sessionInfo.directory,
65
+ title: newTitle,
66
+ });
67
+ if (error || !updatedSession) {
68
+ throw error || new Error("Failed to update session");
69
+ }
70
+ setCurrentSession({
71
+ id: sessionInfo.sessionId,
72
+ title: newTitle,
73
+ directory: sessionInfo.directory,
74
+ });
75
+ if (pinnedMessageManager.isInitialized()) {
76
+ await pinnedMessageManager.onSessionChange(sessionInfo.sessionId, newTitle);
77
+ }
78
+ const messageId = renameManager.getMessageId();
79
+ if (messageId && ctx.chat) {
80
+ await ctx.api.deleteMessage(ctx.chat.id, messageId).catch(() => { });
81
+ }
82
+ await ctx.reply(t("rename.success", { title: newTitle }));
83
+ logger.info(`[RenameHandler] Session renamed successfully: ${newTitle}`);
84
+ }
85
+ catch (error) {
86
+ logger.error("[RenameHandler] Error renaming session:", error);
87
+ await ctx.reply(t("rename.error"));
88
+ }
89
+ renameManager.clear();
90
+ return true;
91
+ }
package/dist/bot/index.js CHANGED
@@ -19,6 +19,7 @@ import { opencodeStartCommand } from "./commands/opencode-start.js";
19
19
  import { opencodeStopCommand } from "./commands/opencode-stop.js";
20
20
  import { handleAgentCommand } from "./commands/agent.js";
21
21
  import { handleModelCommand } from "./commands/model.js";
22
+ import { renameCommand, handleRenameCancel, handleRenameTextAnswer } from "./commands/rename.js";
22
23
  import { handleQuestionCallback, showCurrentQuestion, handleQuestionTextAnswer, } from "./handlers/question.js";
23
24
  import { handlePermissionCallback, showPermissionRequest } from "./handlers/permission.js";
24
25
  import { handleAgentSelect, showAgentSelectionMenu } from "./handlers/agent.js";
@@ -375,6 +376,7 @@ export function createBot() {
375
376
  bot.command("agent", handleAgentCommand);
376
377
  bot.command("model", handleModelCommand);
377
378
  bot.command("stop", stopCommand);
379
+ bot.command("rename", renameCommand);
378
380
  bot.on("callback_query:data", async (ctx) => {
379
381
  logger.debug(`[Bot] Received callback_query:data: ${ctx.callbackQuery?.data}`);
380
382
  logger.debug(`[Bot] Callback context: from=${ctx.from?.id}, chat=${ctx.chat?.id}`);
@@ -388,7 +390,8 @@ export function createBot() {
388
390
  const handledVariant = await handleVariantSelect(ctx);
389
391
  const handledCompactConfirm = await handleCompactConfirm(ctx);
390
392
  const handledCompactCancel = await handleCompactCancel(ctx);
391
- logger.debug(`[Bot] Callback handled: session=${handledSession}, project=${handledProject}, question=${handledQuestion}, permission=${handledPermission}, agent=${handledAgent}, model=${handledModel}, variant=${handledVariant}, compact=${handledCompactConfirm || handledCompactCancel}`);
393
+ const handledRenameCancel = await handleRenameCancel(ctx);
394
+ logger.debug(`[Bot] Callback handled: session=${handledSession}, project=${handledProject}, question=${handledQuestion}, permission=${handledPermission}, agent=${handledAgent}, model=${handledModel}, variant=${handledVariant}, compact=${handledCompactConfirm || handledCompactCancel}, rename=${handledRenameCancel}`);
392
395
  if (!handledSession &&
393
396
  !handledProject &&
394
397
  !handledQuestion &&
@@ -397,7 +400,8 @@ export function createBot() {
397
400
  !handledModel &&
398
401
  !handledVariant &&
399
402
  !handledCompactConfirm &&
400
- !handledCompactCancel) {
403
+ !handledCompactCancel &&
404
+ !handledRenameCancel) {
401
405
  logger.debug("Unknown callback query:", ctx.callbackQuery?.data);
402
406
  await ctx.answerCallbackQuery({ text: t("callback.unknown_command") });
403
407
  }
@@ -496,6 +500,10 @@ export function createBot() {
496
500
  await handleQuestionTextAnswer(ctx);
497
501
  return;
498
502
  }
503
+ const handledRename = await handleRenameTextAnswer(ctx);
504
+ if (handledRename) {
505
+ return;
506
+ }
499
507
  const currentProject = getCurrentProject();
500
508
  if (!currentProject) {
501
509
  await ctx.reply(t("bot.project_not_selected"));
package/dist/i18n/en.js CHANGED
@@ -187,6 +187,14 @@ export const en = {
187
187
  "runtime.wizard.saved": "Configuration saved:\n- {envPath}\n- {settingsPath}\n",
188
188
  "runtime.wizard.not_configured_starting": "Application is not configured yet. Starting wizard...\n",
189
189
  "runtime.wizard.tty_required": "Interactive wizard requires a TTY terminal. Run `opencode-telegram config` in an interactive shell.",
190
+ "rename.no_session": "⚠️ No active session. Create or select a session first.",
191
+ "rename.prompt": "📝 Enter new title for session:\n\nCurrent: {title}",
192
+ "rename.empty_title": "⚠️ Title cannot be empty.",
193
+ "rename.success": "✅ Session renamed to: {title}",
194
+ "rename.error": "🔴 Failed to rename session.",
195
+ "rename.cancelled": "❌ Rename cancelled.",
196
+ "rename.button.cancel": "❌ Cancel",
197
+ "cmd.description.rename": "Rename current session",
190
198
  "cli.usage": "Usage:\n opencode-telegram [start] [--mode sources|installed]\n opencode-telegram status\n opencode-telegram stop\n opencode-telegram config\n\nNotes:\n - No command defaults to `start`\n - `--mode` is currently supported for `start` only",
191
199
  "cli.placeholder.status": "Command `status` is currently a placeholder. Real status checks will be added in service layer (Phase 5).",
192
200
  "cli.placeholder.stop": "Command `stop` is currently a placeholder. Real background process stop will be added in service layer (Phase 5).",
package/dist/i18n/ru.js CHANGED
@@ -187,6 +187,14 @@ export const ru = {
187
187
  "runtime.wizard.saved": "Конфигурация сохранена:\n- {envPath}\n- {settingsPath}\n",
188
188
  "runtime.wizard.not_configured_starting": "Приложение еще не сконфигурировано. Запускаю wizard...\n",
189
189
  "runtime.wizard.tty_required": "Интерактивный wizard требует TTY-терминал. Запустите `opencode-telegram config` в интерактивной оболочке.",
190
+ "rename.no_session": "⚠️ Нет активной сессии. Сначала создайте или выберите сессию.",
191
+ "rename.prompt": "📝 Введите новое название сессии:\n\nТекущее: {title}",
192
+ "rename.empty_title": "⚠️ Название не может быть пустым.",
193
+ "rename.success": "✅ Сессия переименована в: {title}",
194
+ "rename.error": "🔴 Не удалось переименовать сессию.",
195
+ "rename.cancelled": "❌ Переименование отменено.",
196
+ "rename.button.cancel": "❌ Отмена",
197
+ "cmd.description.rename": "Переименовать текущую сессию",
190
198
  "cli.usage": "Использование:\n opencode-telegram [start] [--mode sources|installed]\n opencode-telegram status\n opencode-telegram stop\n opencode-telegram config\n\nЗаметки:\n - Без команды по умолчанию используется `start`\n - `--mode` сейчас поддерживается только для `start`",
191
199
  "cli.placeholder.status": "Команда `status` пока работает как заглушка. Реальная проверка статуса появится на этапе service-слоя (Этап 5).",
192
200
  "cli.placeholder.stop": "Команда `stop` пока работает как заглушка. Реальная остановка фонового процесса появится на этапе service-слоя (Этап 5).",
@@ -0,0 +1,50 @@
1
+ import { logger } from "../utils/logger.js";
2
+ class RenameManager {
3
+ state = {
4
+ isWaiting: false,
5
+ sessionId: null,
6
+ sessionDirectory: null,
7
+ currentTitle: null,
8
+ messageId: null,
9
+ };
10
+ startWaiting(sessionId, directory, currentTitle) {
11
+ logger.info(`[RenameManager] Starting rename flow for session: ${sessionId}`);
12
+ this.state = {
13
+ isWaiting: true,
14
+ sessionId,
15
+ sessionDirectory: directory,
16
+ currentTitle,
17
+ messageId: null,
18
+ };
19
+ }
20
+ setMessageId(messageId) {
21
+ this.state.messageId = messageId;
22
+ }
23
+ getMessageId() {
24
+ return this.state.messageId;
25
+ }
26
+ isWaitingForName() {
27
+ return this.state.isWaiting;
28
+ }
29
+ getSessionInfo() {
30
+ if (!this.state.isWaiting || !this.state.sessionId) {
31
+ return null;
32
+ }
33
+ return {
34
+ sessionId: this.state.sessionId,
35
+ directory: this.state.sessionDirectory,
36
+ currentTitle: this.state.currentTitle,
37
+ };
38
+ }
39
+ clear() {
40
+ logger.debug("[RenameManager] Clearing rename state");
41
+ this.state = {
42
+ isWaiting: false,
43
+ sessionId: null,
44
+ sessionDirectory: null,
45
+ currentTitle: null,
46
+ messageId: null,
47
+ };
48
+ }
49
+ }
50
+ export const renameManager = new RenameManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinev/opencode-telegram-bot",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Telegram bot client for OpenCode to run and monitor coding tasks from chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",