@grinev/opencode-telegram-bot 0.11.4 → 0.12.1

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/dist/i18n/es.js CHANGED
@@ -4,6 +4,8 @@ export const es = {
4
4
  "cmd.description.stop": "Detener la acción actual",
5
5
  "cmd.description.sessions": "Listar sesiones",
6
6
  "cmd.description.projects": "Listar proyectos",
7
+ "cmd.description.task": "Crear tarea programada",
8
+ "cmd.description.tasklist": "Ver tareas programadas",
7
9
  "cmd.description.commands": "Comandos personalizados",
8
10
  "cmd.description.opencode_start": "Iniciar servidor OpenCode",
9
11
  "cmd.description.opencode_stop": "Detener servidor OpenCode",
@@ -30,7 +32,7 @@ export const es = {
30
32
  "inline.cancelled_callback": "Cancelado",
31
33
  "common.unknown": "desconocido",
32
34
  "common.unknown_error": "error desconocido",
33
- "start.welcome": "👋 ¡Bienvenido a OpenCode Telegram Bot!\n\nUsa los comandos:\n/projects — seleccionar proyecto\n/sessions — lista de sesiones\n/new — sesión nueva\n/status — estado\n/help — ayuda\n\nUsa los botones inferiores para elegir modo, modelo y variante.",
35
+ "start.welcome": "👋 ¡Bienvenido a OpenCode Telegram Bot!\n\nUsa los comandos:\n/projects — seleccionar proyecto\n/sessions — lista de sesiones\n/new — sesión nueva\n/task — tarea programada\n/tasklist — tareas programadas\n/status — estado\n/help — ayuda\n\nUsa los botones inferiores para elegir modo, modelo y variante.",
34
36
  "help.keyboard_hint": "💡 Usa los botones inferiores para modo del agente, modelo, variante y acciones de contexto.",
35
37
  "help.text": "📖 **Ayuda**\n\n/status - Ver estado del servidor\n/sessions - Lista de sesiones\n/new - Crear una sesión nueva\n/help - Ayuda",
36
38
  "bot.thinking": "💭 Pensando...",
@@ -214,6 +216,7 @@ export const es = {
214
216
  "pinned.line.project": "Proyecto: {project}",
215
217
  "pinned.line.model": "Modelo: {model}",
216
218
  "pinned.line.context": "Contexto: {used} / {limit} ({percent}%)",
219
+ "pinned.line.cost": "Costo: {cost} gastado",
217
220
  "pinned.files.title": "Archivos ({count}):",
218
221
  "pinned.files.item": " {path}{diff}",
219
222
  "pinned.files.more": " ... y {count} más",
@@ -247,6 +250,41 @@ export const es = {
247
250
  "rename.blocked.expected_name": "⚠️ Introduce el nuevo nombre de la sesión como texto o toca Cancelar en el mensaje de cambio de nombre.",
248
251
  "rename.blocked.command_not_allowed": "⚠️ Este comando no está disponible mientras el cambio de nombre espera un nuevo nombre.",
249
252
  "rename.button.cancel": "❌ Cancelar",
253
+ "task.prompt.schedule": "⏰ Envía el horario de la tarea en lenguaje natural.\n\nEjemplos:\n- cada 5 minutos\n- cada día a las 17:00\n- mañana a las 12:00",
254
+ "task.schedule_empty": "⚠️ El horario no puede estar vacío.",
255
+ "task.parse.in_progress": "⏳ Analizando horario...",
256
+ "task.parse_error": "🔴 No se pudo interpretar el horario.\n\n{message}\n\nEnvía el periodo otra vez de forma más clara.",
257
+ "task.schedule_preview": "✅ Horario interpretado\n\nEntendido como: {summary}\n{cronLine}Zona horaria: {timezone}\nTipo: {kind}\nPróxima ejecución: {nextRunAt}",
258
+ "task.schedule_preview.cron": "Cron: {cron}",
259
+ "task.prompt.body": "📝 Ahora envía lo que el bot debe hacer según este horario.",
260
+ "task.prompt_empty": "⚠️ El texto de la tarea no puede estar vacío.",
261
+ "task.created": "✅ Tarea programada creada\n\nTarea: {description}\nProyecto: {project}\nModelo: {model}\nHorario: {schedule}\n{cronLine}Próxima ejecución: {nextRunAt}",
262
+ "task.created.cron": "Cron: {cron}",
263
+ "task.button.retry_schedule": "🔁 Volver a introducir horario",
264
+ "task.button.cancel": "❌ Cancelar",
265
+ "task.retry_schedule_callback": "Volviendo a introducir el horario...",
266
+ "task.cancel_callback": "Cancelando...",
267
+ "task.cancelled": "❌ Creación de la tarea programada cancelada.",
268
+ "task.inactive_callback": "Este flujo de tarea programada ya no está activo",
269
+ "task.inactive": "⚠️ La creación de la tarea programada no está activa. Ejecuta /task otra vez.",
270
+ "task.blocked.expected_input": "⚠️ Primero termina la configuración actual de la tarea programada: envía texto o usa el botón del mensaje del horario.",
271
+ "task.blocked.command_not_allowed": "⚠️ Este comando no está disponible mientras la creación de la tarea programada está activa.",
272
+ "task.limit_reached": "⚠️ Se alcanzó el límite de tareas ({limit}). Primero elimina una tarea programada existente.",
273
+ "task.schedule_too_frequent": "El horario recurrente es demasiado frecuente. El intervalo mínimo permitido es una vez cada 5 minutos.",
274
+ "task.kind.cron": "recurrente",
275
+ "task.kind.once": "única",
276
+ "task.run.success": "⏰ Tarea programada completada: {description}\n\n{result}",
277
+ "task.run.error": "🔴 La tarea programada falló: {description}\n\nError: {error}",
278
+ "tasklist.empty": "📭 Aún no hay tareas programadas.",
279
+ "tasklist.select": "Elige una tarea programada:",
280
+ "tasklist.details": "⏰ Tarea programada\n\nTarea: {prompt}\nProyecto: {project}\nHorario: {schedule}\n{cronLine}Zona horaria: {timezone}\nPróxima ejecución: {nextRunAt}\nÚltima ejecución: {lastRunAt}\nNúmero de ejecuciones: {runCount}",
281
+ "tasklist.details.cron": "Cron: {cron}",
282
+ "tasklist.button.delete": "🗑 Eliminar",
283
+ "tasklist.button.cancel": "❌ Cancelar",
284
+ "tasklist.deleted_callback": "Eliminada",
285
+ "tasklist.cancelled_callback": "Cancelado",
286
+ "tasklist.inactive_callback": "Este menú de tareas programadas está inactivo",
287
+ "tasklist.load_error": "🔴 No se pudieron cargar las tareas programadas.",
250
288
  "commands.select": "Elige un comando de OpenCode:",
251
289
  "commands.empty": "📭 No hay comandos de OpenCode disponibles para este proyecto.",
252
290
  "commands.fetch_error": "🔴 No se pudieron cargar los comandos de OpenCode.",
package/dist/i18n/fr.js CHANGED
@@ -4,6 +4,8 @@ export const fr = {
4
4
  "cmd.description.stop": "Arrêter l'action en cours",
5
5
  "cmd.description.sessions": "Lister les sessions",
6
6
  "cmd.description.projects": "Lister les projets",
7
+ "cmd.description.task": "Créer une tâche planifiée",
8
+ "cmd.description.tasklist": "Afficher les tâches planifiées",
7
9
  "cmd.description.commands": "Commandes personnalisées",
8
10
  "cmd.description.opencode_start": "Démarrer le serveur OpenCode",
9
11
  "cmd.description.opencode_stop": "Arrêter le serveur OpenCode",
@@ -30,7 +32,7 @@ export const fr = {
30
32
  "inline.cancelled_callback": "Annulé",
31
33
  "common.unknown": "inconnu",
32
34
  "common.unknown_error": "erreur inconnue",
33
- "start.welcome": "👋 Bienvenue dans OpenCode Telegram Bot !\n\nUtilisez les commandes :\n/projects — sélectionner un projet\n/sessions — liste des sessions\n/new — nouvelle session\n/status — statut\n/help — aide\n\nUtilisez les boutons du bas pour choisir le mode d'agent, le modèle et la variante.",
35
+ "start.welcome": "👋 Bienvenue dans OpenCode Telegram Bot !\n\nUtilisez les commandes :\n/projects — sélectionner un projet\n/sessions — liste des sessions\n/new — nouvelle session\n/task — tâche planifiée\n/tasklist — tâches planifiées\n/status — statut\n/help — aide\n\nUtilisez les boutons du bas pour choisir le mode d'agent, le modèle et la variante.",
34
36
  "help.keyboard_hint": "💡 Utilisez les boutons du bas pour le mode d'agent, le modèle, la variante et les actions de contexte.",
35
37
  "help.text": "📖 **Aide**\n\n/status - Vérifier l'état du serveur\n/sessions - Liste des sessions\n/new - Créer une nouvelle session\n/help - Aide",
36
38
  "bot.thinking": "💭 Réflexion en cours...",
@@ -214,6 +216,7 @@ export const fr = {
214
216
  "pinned.line.project": "Projet : {project}",
215
217
  "pinned.line.model": "Modèle : {model}",
216
218
  "pinned.line.context": "Contexte : {used} / {limit} ({percent}%)",
219
+ "pinned.line.cost": "Coût : {cost} dépensé",
217
220
  "pinned.files.title": "Fichiers ({count}) :",
218
221
  "pinned.files.item": " {path}{diff}",
219
222
  "pinned.files.more": " ... et encore {count}",
@@ -247,6 +250,41 @@ export const fr = {
247
250
  "rename.blocked.expected_name": "⚠️ Entrez le nouveau nom de la session sous forme de texte ou appuyez sur Annuler dans le message de renommage.",
248
251
  "rename.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible tant que le renommage attend un nouveau nom.",
249
252
  "rename.button.cancel": "❌ Annuler",
253
+ "task.prompt.schedule": "⏰ Envoyez le planning de la tâche en langage naturel.\n\nExemples :\n- toutes les 5 minutes\n- chaque jour à 17:00\n- demain à 12:00",
254
+ "task.schedule_empty": "⚠️ Le planning ne peut pas être vide.",
255
+ "task.parse.in_progress": "⏳ Analyse du planning...",
256
+ "task.parse_error": "🔴 Impossible d'interpréter le planning.\n\n{message}\n\nEnvoyez le créneau à nouveau de façon plus claire.",
257
+ "task.schedule_preview": "✅ Planning interprété\n\nCompris comme : {summary}\n{cronLine}Fuseau horaire : {timezone}\nType : {kind}\nProchaine exécution : {nextRunAt}",
258
+ "task.schedule_preview.cron": "Cron : {cron}",
259
+ "task.prompt.body": "📝 Envoyez maintenant ce que le bot doit faire selon ce planning.",
260
+ "task.prompt_empty": "⚠️ Le texte de la tâche ne peut pas être vide.",
261
+ "task.created": "✅ Tâche planifiée créée\n\nTâche : {description}\nProjet : {project}\nModèle : {model}\nPlanning : {schedule}\n{cronLine}Prochaine exécution : {nextRunAt}",
262
+ "task.created.cron": "Cron : {cron}",
263
+ "task.button.retry_schedule": "🔁 Ressaisir le planning",
264
+ "task.button.cancel": "❌ Annuler",
265
+ "task.retry_schedule_callback": "Retour à la saisie du planning...",
266
+ "task.cancel_callback": "Annulation...",
267
+ "task.cancelled": "❌ Création de la tâche planifiée annulée.",
268
+ "task.inactive_callback": "Ce flux de tâche planifiée n'est plus actif",
269
+ "task.inactive": "⚠️ La création de tâche planifiée n'est pas active. Relancez /task.",
270
+ "task.blocked.expected_input": "⚠️ Terminez d'abord la configuration de la tâche planifiée : envoyez du texte ou utilisez le bouton dans le message du planning.",
271
+ "task.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible pendant la création d'une tâche planifiée.",
272
+ "task.limit_reached": "⚠️ Limite de tâches atteinte ({limit}). Supprimez d'abord une tâche planifiée existante.",
273
+ "task.schedule_too_frequent": "Le planning récurrent est trop fréquent. L'intervalle minimum autorisé est d'une fois toutes les 5 minutes.",
274
+ "task.kind.cron": "récurrente",
275
+ "task.kind.once": "ponctuelle",
276
+ "task.run.success": "⏰ Tâche planifiée terminée : {description}\n\n{result}",
277
+ "task.run.error": "🔴 Échec de la tâche planifiée : {description}\n\nErreur : {error}",
278
+ "tasklist.empty": "📭 Aucune tâche planifiée pour le moment.",
279
+ "tasklist.select": "Sélectionnez une tâche planifiée :",
280
+ "tasklist.details": "⏰ Tâche planifiée\n\nTâche : {prompt}\nProjet : {project}\nPlanning : {schedule}\n{cronLine}Fuseau horaire : {timezone}\nProchaine exécution : {nextRunAt}\nDernière exécution : {lastRunAt}\nNombre d'exécutions : {runCount}",
281
+ "tasklist.details.cron": "Cron : {cron}",
282
+ "tasklist.button.delete": "🗑 Supprimer",
283
+ "tasklist.button.cancel": "❌ Annuler",
284
+ "tasklist.deleted_callback": "Supprimée",
285
+ "tasklist.cancelled_callback": "Annulé",
286
+ "tasklist.inactive_callback": "Ce menu des tâches planifiées est inactif",
287
+ "tasklist.load_error": "🔴 Impossible de charger les tâches planifiées.",
250
288
  "commands.select": "Choisissez une commande OpenCode :",
251
289
  "commands.empty": "📭 Aucune commande OpenCode n'est disponible pour ce projet.",
252
290
  "commands.fetch_error": "🔴 Impossible de charger les commandes OpenCode.",
package/dist/i18n/ru.js CHANGED
@@ -4,6 +4,8 @@ export const ru = {
4
4
  "cmd.description.stop": "Прервать текущее действие",
5
5
  "cmd.description.sessions": "Список сессий",
6
6
  "cmd.description.projects": "Список проектов",
7
+ "cmd.description.task": "Создать задачу по расписанию",
8
+ "cmd.description.tasklist": "Список задач по расписанию",
7
9
  "cmd.description.commands": "Пользовательские команды",
8
10
  "cmd.description.opencode_start": "Запустить OpenCode сервер",
9
11
  "cmd.description.opencode_stop": "Остановить OpenCode сервер",
@@ -30,7 +32,7 @@ export const ru = {
30
32
  "inline.cancelled_callback": "Отменено",
31
33
  "common.unknown": "неизвестна",
32
34
  "common.unknown_error": "неизвестная ошибка",
33
- "start.welcome": "👋 Добро пожаловать в OpenCode Telegram Bot!\n\nИспользуйте команды:\n/projects — выбрать проект\n/sessions — список сессий\n/new — новая сессия\n/status — статус\n/help — справка\n\nРежим, модель и вариант выбираются кнопками внизу.",
35
+ "start.welcome": "👋 Добро пожаловать в OpenCode Telegram Bot!\n\nИспользуйте команды:\n/projects — выбрать проект\n/sessions — список сессий\n/new — новая сессия\n/task — задача по расписанию\n/tasklist — список задач по расписанию\n/status — статус\n/help — справка\n\nРежим, модель и вариант выбираются кнопками внизу.",
34
36
  "help.keyboard_hint": "💡 Режим, модель, вариант и действия с контекстом доступны через нижние кнопки клавиатуры.",
35
37
  "help.text": "📖 **Справка**\n\n/status - Проверить статус сервера\n/sessions - Список сессий\n/new - Создать новую сессию\n/help - Справка",
36
38
  "bot.thinking": "💭 Думаю...",
@@ -214,6 +216,7 @@ export const ru = {
214
216
  "pinned.line.project": "Проект: {project}",
215
217
  "pinned.line.model": "Модель: {model}",
216
218
  "pinned.line.context": "Контекст: {used} / {limit} ({percent}%)",
219
+ "pinned.line.cost": "Стоимость: {cost} потрачено",
217
220
  "pinned.files.title": "Файлы ({count}):",
218
221
  "pinned.files.item": " {path}{diff}",
219
222
  "pinned.files.more": " ... и еще {count}",
@@ -247,6 +250,41 @@ export const ru = {
247
250
  "rename.blocked.expected_name": "⚠️ Введите новое название текстом или нажмите Отмена в сообщении переименования.",
248
251
  "rename.blocked.command_not_allowed": "⚠️ Эта команда недоступна, пока ожидается новое название сессии.",
249
252
  "rename.button.cancel": "❌ Отмена",
253
+ "task.prompt.schedule": "⏰ Отправьте расписание задачи обычным языком.\n\nПримеры:\n- каждые 5 минут\n- каждый день в 17:00\n- завтра в 12:00",
254
+ "task.schedule_empty": "⚠️ Расписание не может быть пустым.",
255
+ "task.parse.in_progress": "⏳ Распознаю расписание...",
256
+ "task.parse_error": "🔴 Не удалось распознать расписание.\n\n{message}\n\nОтправьте период еще раз в более явном виде.",
257
+ "task.schedule_preview": "✅ Расписание распознано\n\nКак я понял: {summary}\n{cronLine}Часовой пояс: {timezone}\nТип: {kind}\nСледующий запуск: {nextRunAt}",
258
+ "task.schedule_preview.cron": "Cron: {cron}",
259
+ "task.prompt.body": "📝 Теперь отправьте текст задачи, которую нужно выполнять по расписанию.",
260
+ "task.prompt_empty": "⚠️ Текст задачи не может быть пустым.",
261
+ "task.created": "✅ Задача по расписанию создана\n\nЗадача: {description}\nПроект: {project}\nМодель: {model}\nРасписание: {schedule}\n{cronLine}Следующий запуск: {nextRunAt}",
262
+ "task.created.cron": "Cron: {cron}",
263
+ "task.button.retry_schedule": "🔁 Ввести период заново",
264
+ "task.button.cancel": "❌ Отмена",
265
+ "task.retry_schedule_callback": "Возвращаю ввод периода...",
266
+ "task.cancel_callback": "Отменяю...",
267
+ "task.cancelled": "❌ Создание задачи по расписанию отменено.",
268
+ "task.inactive_callback": "Этот сценарий создания задачи уже неактивен",
269
+ "task.inactive": "⚠️ Сценарий создания задачи неактивен. Запустите /task снова.",
270
+ "task.blocked.expected_input": "⚠️ Сначала завершите создание задачи по расписанию: отправьте текст или используйте кнопку в сообщении с расписанием.",
271
+ "task.blocked.command_not_allowed": "⚠️ Эта команда недоступна, пока идет создание задачи по расписанию.",
272
+ "task.limit_reached": "⚠️ Достигнут лимит задач ({limit}). Сначала удалите одну из существующих задач по расписанию.",
273
+ "task.schedule_too_frequent": "Повторяющееся расписание слишком частое. Минимально допустимый интервал - один запуск в 5 минут.",
274
+ "task.kind.cron": "повторяющаяся",
275
+ "task.kind.once": "однократная",
276
+ "task.run.success": "⏰ Задача по расписанию выполнена: {description}\n\n{result}",
277
+ "task.run.error": "🔴 Ошибка выполнения задачи по расписанию: {description}\n\nОшибка: {error}",
278
+ "tasklist.empty": "📭 Задач по расписанию пока нет.",
279
+ "tasklist.select": "Выберите задачу по расписанию:",
280
+ "tasklist.details": "⏰ Задача по расписанию\n\nЗадача: {prompt}\nПроект: {project}\nРасписание: {schedule}\n{cronLine}Часовой пояс: {timezone}\nСледующий запуск: {nextRunAt}\nПоследний запуск: {lastRunAt}\nКоличество запусков: {runCount}",
281
+ "tasklist.details.cron": "Cron: {cron}",
282
+ "tasklist.button.delete": "🗑 Удалить",
283
+ "tasklist.button.cancel": "❌ Отмена",
284
+ "tasklist.deleted_callback": "Удалено",
285
+ "tasklist.cancelled_callback": "Отменено",
286
+ "tasklist.inactive_callback": "Это меню задач по расписанию уже неактивно",
287
+ "tasklist.load_error": "🔴 Не удалось загрузить задачи по расписанию.",
250
288
  "commands.select": "Выберите команду OpenCode:",
251
289
  "commands.empty": "📭 Для этого проекта нет доступных команд OpenCode.",
252
290
  "commands.fetch_error": "🔴 Не удалось загрузить список команд OpenCode.",
package/dist/i18n/zh.js CHANGED
@@ -4,6 +4,8 @@ export const zh = {
4
4
  "cmd.description.stop": "停止当前操作",
5
5
  "cmd.description.sessions": "列出会话",
6
6
  "cmd.description.projects": "列出项目",
7
+ "cmd.description.task": "创建定时任务",
8
+ "cmd.description.tasklist": "查看定时任务",
7
9
  "cmd.description.commands": "自定义命令",
8
10
  "cmd.description.opencode_start": "启动 OpenCode 服务器",
9
11
  "cmd.description.opencode_stop": "停止 OpenCode 服务器",
@@ -30,7 +32,7 @@ export const zh = {
30
32
  "inline.cancelled_callback": "已取消",
31
33
  "common.unknown": "未知",
32
34
  "common.unknown_error": "未知错误",
33
- "start.welcome": "👋 欢迎使用 OpenCode Telegram Bot!\n\n可用命令:\n/projects — 选择项目\n/sessions — 会话列表\n/new — 新建会话\n/status — 状态\n/help — 帮助\n\n请使用底部按钮选择模式、模型和变体。",
35
+ "start.welcome": "👋 欢迎使用 OpenCode Telegram Bot!\n\n可用命令:\n/projects — 选择项目\n/sessions — 会话列表\n/new — 新建会话\n/task — 定时任务\n/tasklist — 定时任务列表\n/status — 状态\n/help — 帮助\n\n请使用底部按钮选择模式、模型和变体。",
34
36
  "help.keyboard_hint": "💡 代理模式、模型、变体和上下文操作请使用底部键盘按钮。",
35
37
  "help.text": "📖 **帮助**\n\n/status - 查看服务器状态\n/sessions - 会话列表\n/new - 创建新会话\n/help - 帮助",
36
38
  "bot.thinking": "💭 思考中...",
@@ -214,6 +216,7 @@ export const zh = {
214
216
  "pinned.line.project": "项目: {project}",
215
217
  "pinned.line.model": "模型: {model}",
216
218
  "pinned.line.context": "上下文: {used} / {limit} ({percent}%)",
219
+ "pinned.line.cost": "费用: {cost}",
217
220
  "pinned.files.title": "文件({count}):",
218
221
  "pinned.files.item": " {path}{diff}",
219
222
  "pinned.files.more": " ... 还有 {count} 个",
@@ -247,6 +250,41 @@ export const zh = {
247
250
  "rename.blocked.expected_name": "⚠️ 请以文本输入新会话名称,或在重命名消息中点击取消。",
248
251
  "rename.blocked.command_not_allowed": "⚠️ 重命名等待新名称期间不可用此命令。",
249
252
  "rename.button.cancel": "❌ 取消",
253
+ "task.prompt.schedule": "⏰ 请用自然语言发送任务的时间安排。\n\n示例:\n- 每 5 分钟\n- 每天 17:00\n- 明天 12:00",
254
+ "task.schedule_empty": "⚠️ 时间安排不能为空。",
255
+ "task.parse.in_progress": "⏳ 正在解析时间安排...",
256
+ "task.parse_error": "🔴 无法解析时间安排。\n\n{message}\n\n请用更清晰的方式重新发送。",
257
+ "task.schedule_preview": "✅ 时间安排已解析\n\n理解为:{summary}\n{cronLine}时区:{timezone}\n类型:{kind}\n下次运行:{nextRunAt}",
258
+ "task.schedule_preview.cron": "Cron: {cron}",
259
+ "task.prompt.body": "📝 现在发送机器人按此时间安排需要执行的内容。",
260
+ "task.prompt_empty": "⚠️ 任务文本不能为空。",
261
+ "task.created": "✅ 定时任务已创建\n\n任务:{description}\n项目:{project}\n模型:{model}\n时间安排:{schedule}\n{cronLine}下次运行:{nextRunAt}",
262
+ "task.created.cron": "Cron: {cron}",
263
+ "task.button.retry_schedule": "🔁 重新输入时间安排",
264
+ "task.button.cancel": "❌ 取消",
265
+ "task.retry_schedule_callback": "正在重新输入时间安排...",
266
+ "task.cancel_callback": "正在取消...",
267
+ "task.cancelled": "❌ 定时任务创建已取消。",
268
+ "task.inactive_callback": "这个定时任务流程已失效",
269
+ "task.inactive": "⚠️ 定时任务创建流程未激活。请重新运行 /task。",
270
+ "task.blocked.expected_input": "⚠️ 请先完成当前定时任务设置:发送文本,或使用时间安排消息中的按钮。",
271
+ "task.blocked.command_not_allowed": "⚠️ 定时任务创建进行中时,此命令不可用。",
272
+ "task.limit_reached": "⚠️ 已达到任务数量上限({limit})。请先删除一个现有定时任务。",
273
+ "task.schedule_too_frequent": "重复任务过于频繁。最小允许间隔为每 5 分钟一次。",
274
+ "task.kind.cron": "重复",
275
+ "task.kind.once": "一次性",
276
+ "task.run.success": "⏰ 定时任务已完成: {description}\n\n{result}",
277
+ "task.run.error": "🔴 定时任务执行失败: {description}\n\n错误: {error}",
278
+ "tasklist.empty": "📭 还没有定时任务。",
279
+ "tasklist.select": "请选择一个定时任务:",
280
+ "tasklist.details": "⏰ 定时任务\n\n任务:{prompt}\n项目:{project}\n计划:{schedule}\n{cronLine}时区:{timezone}\n下次运行:{nextRunAt}\n上次运行:{lastRunAt}\n运行次数:{runCount}",
281
+ "tasklist.details.cron": "Cron: {cron}",
282
+ "tasklist.button.delete": "🗑 删除",
283
+ "tasklist.button.cancel": "❌ 取消",
284
+ "tasklist.deleted_callback": "已删除",
285
+ "tasklist.cancelled_callback": "已取消",
286
+ "tasklist.inactive_callback": "此定时任务菜单已失效",
287
+ "tasklist.load_error": "🔴 无法加载定时任务。",
250
288
  "commands.select": "请选择一个 OpenCode 命令:",
251
289
  "commands.empty": "📭 当前项目没有可用的 OpenCode 命令。",
252
290
  "commands.fetch_error": "🔴 加载 OpenCode 命令失败。",
@@ -1,21 +1,29 @@
1
1
  import { permissionManager } from "../permission/manager.js";
2
2
  import { questionManager } from "../question/manager.js";
3
3
  import { renameManager } from "../rename/manager.js";
4
+ import { taskCreationManager } from "../scheduled-task/creation-manager.js";
4
5
  import { interactionManager } from "./manager.js";
5
6
  import { logger } from "../utils/logger.js";
6
7
  export function clearAllInteractionState(reason) {
7
8
  const questionActive = questionManager.isActive();
8
9
  const permissionActive = permissionManager.isActive();
9
10
  const renameActive = renameManager.isWaitingForName();
11
+ const taskCreationActive = taskCreationManager.isActive();
10
12
  const interactionSnapshot = interactionManager.getSnapshot();
11
13
  questionManager.clear();
12
14
  permissionManager.clear();
13
15
  renameManager.clear();
16
+ taskCreationManager.clear();
14
17
  interactionManager.clear(reason);
15
- const hasAnyActiveState = questionActive || permissionActive || renameActive || interactionSnapshot !== null;
18
+ const hasAnyActiveState = questionActive ||
19
+ permissionActive ||
20
+ renameActive ||
21
+ taskCreationActive ||
22
+ interactionSnapshot !== null;
16
23
  const message = `[InteractionCleanup] Cleared state: reason=${reason}, ` +
17
24
  `questionActive=${questionActive}, permissionActive=${permissionActive}, ` +
18
- `renameActive=${renameActive}, interactionKind=${interactionSnapshot?.kind || "none"}`;
25
+ `renameActive=${renameActive}, taskCreationActive=${taskCreationActive}, ` +
26
+ `interactionKind=${interactionSnapshot?.kind || "none"}`;
19
27
  if (hasAnyActiveState) {
20
28
  logger.info(message);
21
29
  return;
@@ -62,6 +62,10 @@ function isAllowedRenameCancelCallback(ctx, state) {
62
62
  state.expectedInput === "text" &&
63
63
  ctx.callbackQuery?.data === "rename:cancel");
64
64
  }
65
+ function isAllowedTaskCallback(ctx, state) {
66
+ return (state.kind === "task" &&
67
+ (ctx.callbackQuery?.data === "task:cancel" || ctx.callbackQuery?.data === "task:retry-schedule"));
68
+ }
65
69
  export function resolveInteractionGuardDecision(ctx) {
66
70
  const state = interactionManager.getSnapshot();
67
71
  const { inputType, command } = classifyIncomingInput(ctx);
@@ -90,6 +94,9 @@ export function resolveInteractionGuardDecision(ctx) {
90
94
  if (inputType === "callback" && isAllowedRenameCancelCallback(ctx, state)) {
91
95
  return createAllowDecision(inputType, state, command);
92
96
  }
97
+ if (inputType === "callback" && isAllowedTaskCallback(ctx, state)) {
98
+ return createAllowDecision(inputType, state, command);
99
+ }
93
100
  if (state.expectedInput === inputType) {
94
101
  return createAllowDecision(inputType, state, command);
95
102
  }
@@ -17,6 +17,7 @@ class PinnedMessageManager {
17
17
  tokensLimit: 0,
18
18
  lastUpdated: 0,
19
19
  changedFiles: [],
20
+ cost: 0,
20
21
  };
21
22
  contextLimit = null;
22
23
  onKeyboardUpdateCallback;
@@ -40,6 +41,7 @@ class PinnedMessageManager {
40
41
  logger.info(`[PinnedManager] Session changed: ${sessionId}, title: ${sessionTitle}`);
41
42
  // Reset tokens for new session
42
43
  this.state.tokensUsed = 0;
44
+ this.state.cost = 0;
43
45
  // Update state
44
46
  this.state.sessionId = sessionId;
45
47
  this.state.sessionTitle = sessionTitle || t("pinned.default_session_title");
@@ -84,9 +86,10 @@ class PinnedMessageManager {
84
86
  logger.warn("[PinnedManager] Failed to load session history:", error);
85
87
  return;
86
88
  }
87
- // Get the maximum context size from session history
89
+ // Get the maximum context size and cost from session history
88
90
  // Context = input + cache.read (cache.read contains previously cached context)
89
91
  let maxContextSize = 0;
92
+ let maxCost = 0;
90
93
  logger.debug(`[PinnedManager] Processing ${messagesData.length} messages from history`);
91
94
  messagesData.forEach(({ info }) => {
92
95
  if (info.role === "assistant") {
@@ -99,16 +102,22 @@ class PinnedMessageManager {
99
102
  const input = assistantInfo.tokens?.input || 0;
100
103
  const cacheRead = assistantInfo.tokens?.cache?.read || 0;
101
104
  const contextSize = input + cacheRead;
102
- logger.debug(`[PinnedManager] Assistant message: input=${input}, cache.read=${cacheRead}, total=${contextSize}`);
105
+ const cost = assistantInfo.cost || 0;
106
+ logger.debug(`[PinnedManager] Assistant message: input=${input}, cache.read=${cacheRead}, total=${contextSize}, cost=$${cost.toFixed(2)}`);
103
107
  // Keep track of maximum context size (peak usage in session)
104
108
  if (contextSize > maxContextSize) {
105
109
  maxContextSize = contextSize;
106
110
  }
111
+ // Keep track of maximum cost (cumulative in OpenCode)
112
+ if (cost > maxCost) {
113
+ maxCost = cost;
114
+ }
107
115
  }
108
116
  });
109
117
  this.state.tokensUsed = maxContextSize;
118
+ this.state.cost = maxCost;
110
119
  this.state.sessionId = sessionId;
111
- logger.info(`[PinnedManager] Loaded context from history: ${this.state.tokensUsed} tokens`);
120
+ logger.info(`[PinnedManager] Loaded context from history: ${this.state.tokensUsed} tokens, cost: $${this.state.cost.toFixed(2)}`);
112
121
  await this.updatePinnedMessage();
113
122
  }
114
123
  catch (err) {
@@ -140,6 +149,14 @@ class PinnedMessageManager {
140
149
  await this.refreshSessionTitle();
141
150
  await this.updatePinnedMessage();
142
151
  }
152
+ /**
153
+ * Called when cost info is received from SSE events
154
+ */
155
+ async onCostUpdate(cost) {
156
+ this.state.cost = cost;
157
+ logger.debug(`[PinnedManager] Cost updated: $${cost.toFixed(2)}`);
158
+ await this.updatePinnedMessage();
159
+ }
143
160
  /**
144
161
  * Set callback for keyboard updates when context changes
145
162
  */
@@ -455,6 +472,9 @@ class PinnedMessageManager {
455
472
  percent: percentage,
456
473
  }),
457
474
  ];
475
+ if (this.state.cost !== undefined && this.state.cost !== null) {
476
+ lines.push(t("pinned.line.cost", { cost: `$${this.state.cost.toFixed(2)}` }));
477
+ }
458
478
  if (this.state.changedFiles.length > 0) {
459
479
  const maxFiles = 10;
460
480
  const total = this.state.changedFiles.length;
@@ -0,0 +1,113 @@
1
+ import { cloneParsedTaskSchedule, cloneScheduledTaskModel } from "./types.js";
2
+ import { logger } from "../utils/logger.js";
3
+ function cloneState(state) {
4
+ return {
5
+ ...state,
6
+ model: cloneScheduledTaskModel(state.model),
7
+ parsedSchedule: state.parsedSchedule ? cloneParsedTaskSchedule(state.parsedSchedule) : null,
8
+ };
9
+ }
10
+ class TaskCreationManager {
11
+ state = null;
12
+ start(projectId, projectWorktree, model) {
13
+ this.state = {
14
+ stage: "awaiting_schedule",
15
+ projectId,
16
+ projectWorktree,
17
+ model: cloneScheduledTaskModel(model),
18
+ scheduleText: null,
19
+ parsedSchedule: null,
20
+ scheduleRequestMessageId: null,
21
+ previewMessageId: null,
22
+ promptRequestMessageId: null,
23
+ };
24
+ logger.info(`[TaskCreationManager] Started task creation flow for project=${projectWorktree}`);
25
+ return cloneState(this.state);
26
+ }
27
+ isActive() {
28
+ return this.state !== null;
29
+ }
30
+ isWaitingForSchedule() {
31
+ return this.state?.stage === "awaiting_schedule";
32
+ }
33
+ isParsingSchedule() {
34
+ return this.state?.stage === "parsing_schedule";
35
+ }
36
+ isWaitingForPrompt() {
37
+ return this.state?.stage === "awaiting_prompt";
38
+ }
39
+ getState() {
40
+ return this.state ? cloneState(this.state) : null;
41
+ }
42
+ setParsedSchedule(scheduleText, parsedSchedule, previewMessageId) {
43
+ if (!this.state) {
44
+ return null;
45
+ }
46
+ this.state = {
47
+ ...this.state,
48
+ stage: "awaiting_prompt",
49
+ scheduleText,
50
+ parsedSchedule: cloneParsedTaskSchedule(parsedSchedule),
51
+ scheduleRequestMessageId: null,
52
+ previewMessageId,
53
+ promptRequestMessageId: null,
54
+ };
55
+ logger.info("[TaskCreationManager] Parsed schedule and switched flow to prompt input");
56
+ return cloneState(this.state);
57
+ }
58
+ markScheduleParsing() {
59
+ if (!this.state) {
60
+ return null;
61
+ }
62
+ this.state = {
63
+ ...this.state,
64
+ stage: "parsing_schedule",
65
+ };
66
+ logger.info("[TaskCreationManager] Schedule parsing started");
67
+ return cloneState(this.state);
68
+ }
69
+ setPromptRequestMessageId(messageId) {
70
+ if (!this.state) {
71
+ return null;
72
+ }
73
+ this.state = {
74
+ ...this.state,
75
+ promptRequestMessageId: messageId,
76
+ };
77
+ return cloneState(this.state);
78
+ }
79
+ setScheduleRequestMessageId(messageId) {
80
+ if (!this.state) {
81
+ return null;
82
+ }
83
+ this.state = {
84
+ ...this.state,
85
+ scheduleRequestMessageId: messageId,
86
+ };
87
+ return cloneState(this.state);
88
+ }
89
+ resetSchedule() {
90
+ if (!this.state) {
91
+ return null;
92
+ }
93
+ this.state = {
94
+ ...this.state,
95
+ stage: "awaiting_schedule",
96
+ scheduleText: null,
97
+ parsedSchedule: null,
98
+ scheduleRequestMessageId: null,
99
+ previewMessageId: null,
100
+ promptRequestMessageId: null,
101
+ };
102
+ logger.info("[TaskCreationManager] Reset task creation flow back to schedule input");
103
+ return cloneState(this.state);
104
+ }
105
+ clear() {
106
+ if (!this.state) {
107
+ return;
108
+ }
109
+ logger.debug("[TaskCreationManager] Clearing task creation state");
110
+ this.state = null;
111
+ }
112
+ }
113
+ export const taskCreationManager = new TaskCreationManager();