@grinev/opencode-telegram-bot 0.20.6 → 0.21.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/de.js CHANGED
@@ -4,6 +4,7 @@ export const de = {
4
4
  "cmd.description.stop": "Aktuelle Aktion stoppen",
5
5
  "cmd.description.detach": "Von aktueller Sitzung trennen",
6
6
  "cmd.description.sessions": "Sitzungen auflisten",
7
+ "cmd.description.messages": "Sitzungsnachrichten durchsuchen",
7
8
  "cmd.description.tts": "Audioantworten umschalten",
8
9
  "cmd.description.projects": "Projekte auflisten",
9
10
  "cmd.description.worktree": "Git-Worktrees wechseln",
@@ -126,6 +127,26 @@ export const de = {
126
127
  "sessions.preview.title": "Letzte Nachrichten:",
127
128
  "sessions.preview.you": "Du:",
128
129
  "sessions.preview.agent": "Agent:",
130
+ "messages.project_not_selected": "🏗 Kein Projekt ausgewählt.\n\nWähle zuerst ein Projekt mit /projects.",
131
+ "messages.session_not_selected": "💬 Keine Sitzung ausgewählt.\n\nWähle zuerst eine Sitzung mit /sessions oder erstelle eine mit /new.",
132
+ "messages.session_project_mismatch": "⚠️ Die ausgewählte Sitzung passt nicht zum aktuellen Projekt. Wähle die Sitzung erneut über /sessions.",
133
+ "messages.empty": "📭 Keine Benutzernachrichten in der aktuellen Sitzung.",
134
+ "messages.select": "Wähle eine Nachricht:",
135
+ "messages.select_page": "Wähle eine Nachricht (Seite {page}):",
136
+ "messages.fetch_error": "🔴 OpenCode Server ist nicht erreichbar oder beim Laden der Nachrichten ist ein Fehler aufgetreten.",
137
+ "messages.inactive_callback": "Dieses Nachrichtenmenü ist nicht mehr aktiv",
138
+ "messages.cancelled_callback": "Abgebrochen",
139
+ "messages.page_empty_callback": "Keine Nachrichten auf dieser Seite",
140
+ "messages.button.prev_page": "⬅️ Zurück",
141
+ "messages.button.next_page": "Weiter ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ Zurück",
145
+ "messages.button.cancel": "❌ Abbrechen",
146
+ "messages.revert_success": "✅ Zurück zur Nachricht:\n\n{text}",
147
+ "messages.revert_error": "❌ Nachricht konnte nicht zurückgesetzt werden. Bitte versuche es erneut.",
148
+ "messages.fork_success": "🔀 Fork erstellt von Nachricht:\n\n{text}",
149
+ "messages.fork_error": "❌ Fork konnte nicht erstellt werden. Bitte versuche es erneut.",
129
150
  "attach.project_not_selected": "🏗 Projekt ist nicht ausgewählt.\n\nWähle zuerst ein Projekt mit /projects.",
130
151
  "attach.session_not_selected": "💬 Keine Sitzung ausgewählt.\n\nWähle zuerst mit /sessions eine Sitzung aus.",
131
152
  "attach.session_project_mismatch": "⚠️ Die ausgewählte Sitzung passt nicht zum aktuellen Projekt. Wähle sie über /sessions erneut aus.",
@@ -190,6 +211,12 @@ export const de = {
190
211
  "model.menu.recent_empty": "— Leer.",
191
212
  "model.menu.favorites_hint": "ℹ️ Füge Modelle in OpenCode CLI zu den Favoriten hinzu, damit sie oben angezeigt werden.",
192
213
  "model.menu.error": "🔴 Modellliste konnte nicht geladen werden",
214
+ "model.search.button": "🔍 Suche",
215
+ "model.search.prompt": "🔍 Modellnamen zum Suchen eingeben:",
216
+ "model.search.results_title": "Suchergebnisse für \"{query}\":",
217
+ "model.search.no_results": "Keine Modelle gefunden für \"{query}\"",
218
+ "model.search.search_again": "↩ Erneut suchen",
219
+ "model.search.error": "Suche fehlgeschlagen",
193
220
  "variant.model_not_selected_callback": "Fehler: Modell ist nicht ausgewählt",
194
221
  "variant.changed_callback": "Variante geändert: {name}",
195
222
  "variant.changed_message": "✅ Variante geändert zu: {name}",
package/dist/i18n/en.js CHANGED
@@ -4,6 +4,7 @@ export const en = {
4
4
  "cmd.description.stop": "Stop current action",
5
5
  "cmd.description.detach": "Detach from current session",
6
6
  "cmd.description.sessions": "List sessions",
7
+ "cmd.description.messages": "Browse session messages",
7
8
  "cmd.description.tts": "Toggle audio replies",
8
9
  "cmd.description.projects": "List projects",
9
10
  "cmd.description.worktree": "Switch git worktrees",
@@ -126,6 +127,26 @@ export const en = {
126
127
  "sessions.preview.title": "Recent messages:",
127
128
  "sessions.preview.you": "You:",
128
129
  "sessions.preview.agent": "Agent:",
130
+ "messages.project_not_selected": "🏗 Project is not selected.\n\nFirst select a project with /projects.",
131
+ "messages.session_not_selected": "💬 Session is not selected.\n\nFirst choose a session with /sessions or create one with /new.",
132
+ "messages.session_project_mismatch": "⚠️ The selected session does not match the current project. Choose the session again via /sessions.",
133
+ "messages.empty": "📭 No user messages in the current session.",
134
+ "messages.select": "Choose a message:",
135
+ "messages.select_page": "Choose a message (page {page}):",
136
+ "messages.fetch_error": "🔴 OpenCode Server is unavailable or an error occurred while loading messages.",
137
+ "messages.inactive_callback": "This messages menu is inactive",
138
+ "messages.cancelled_callback": "Cancelled",
139
+ "messages.page_empty_callback": "No messages on this page",
140
+ "messages.button.prev_page": "⬅️ Prev",
141
+ "messages.button.next_page": "Next ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ Back",
145
+ "messages.button.cancel": "❌ Cancel",
146
+ "messages.revert_success": "✅ Reverted to message:\n\n{text}",
147
+ "messages.revert_error": "❌ Failed to revert message. Please try again.",
148
+ "messages.fork_success": "🔀 Fork created from message:\n\n{text}",
149
+ "messages.fork_error": "❌ Failed to create fork. Please try again.",
129
150
  "attach.project_not_selected": "🏗 Project is not selected.\n\nFirst select a project with /projects.",
130
151
  "attach.session_not_selected": "💬 Session is not selected.\n\nFirst choose a session with /sessions.",
131
152
  "attach.session_project_mismatch": "⚠️ The selected session does not match the current project. Choose the session again via /sessions.",
@@ -190,6 +211,12 @@ export const en = {
190
211
  "model.menu.recent_empty": "— Empty.",
191
212
  "model.menu.favorites_hint": "ℹ️ Add models to favorites in OpenCode CLI to keep them at the top.",
192
213
  "model.menu.error": "🔴 Failed to get models list",
214
+ "model.search.button": "🔍 Search",
215
+ "model.search.prompt": "🔍 Enter model name to search:",
216
+ "model.search.results_title": "Search results for \"{query}\":",
217
+ "model.search.no_results": "No models found for \"{query}\"",
218
+ "model.search.search_again": "↩ Search again",
219
+ "model.search.error": "Search failed",
193
220
  "variant.model_not_selected_callback": "Error: model is not selected",
194
221
  "variant.changed_callback": "Variant changed: {name}",
195
222
  "variant.changed_message": "✅ Variant changed to: {name}",
package/dist/i18n/es.js CHANGED
@@ -4,6 +4,7 @@ export const es = {
4
4
  "cmd.description.stop": "Detener la acción actual",
5
5
  "cmd.description.detach": "Desconectar de la sesión actual",
6
6
  "cmd.description.sessions": "Listar sesiones",
7
+ "cmd.description.messages": "Ver mensajes de la sesión",
7
8
  "cmd.description.tts": "Alternar respuestas de audio",
8
9
  "cmd.description.projects": "Listar proyectos",
9
10
  "cmd.description.worktree": "Cambiar worktrees de git",
@@ -126,6 +127,26 @@ export const es = {
126
127
  "sessions.preview.title": "Mensajes recientes:",
127
128
  "sessions.preview.you": "Tú:",
128
129
  "sessions.preview.agent": "Agente:",
130
+ "messages.project_not_selected": "🏗 No hay ningún proyecto seleccionado.\n\nPrimero selecciona un proyecto con /projects.",
131
+ "messages.session_not_selected": "💬 No hay ninguna sesión seleccionada.\n\nPrimero elige una sesión con /sessions o crea una con /new.",
132
+ "messages.session_project_mismatch": "⚠️ La sesión seleccionada no coincide con el proyecto actual. Vuelve a elegir la sesión con /sessions.",
133
+ "messages.empty": "📭 No hay mensajes de usuario en la sesión actual.",
134
+ "messages.select": "Elige un mensaje:",
135
+ "messages.select_page": "Elige un mensaje (página {page}):",
136
+ "messages.fetch_error": "🔴 OpenCode Server no está disponible o se produjo un error al cargar los mensajes.",
137
+ "messages.inactive_callback": "Este menú de mensajes ya no está activo",
138
+ "messages.cancelled_callback": "Cancelado",
139
+ "messages.page_empty_callback": "No hay mensajes en esta página",
140
+ "messages.button.prev_page": "⬅️ Anterior",
141
+ "messages.button.next_page": "Siguiente ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ Volver",
145
+ "messages.button.cancel": "❌ Cancelar",
146
+ "messages.revert_success": "✅ Revertido al mensaje:\n\n{text}",
147
+ "messages.revert_error": "❌ No se pudo revertir el mensaje. Inténtalo de nuevo.",
148
+ "messages.fork_success": "🔀 Fork creado desde el mensaje:\n\n{text}",
149
+ "messages.fork_error": "❌ No se pudo crear el fork. Inténtalo de nuevo.",
129
150
  "attach.project_not_selected": "🏗 No hay un proyecto seleccionado.\n\nPrimero selecciona un proyecto con /projects.",
130
151
  "attach.session_not_selected": "💬 No hay una sesión seleccionada.\n\nPrimero selecciona una sesión con /sessions.",
131
152
  "attach.session_project_mismatch": "⚠️ La sesión seleccionada no coincide con el proyecto actual. Vuelve a seleccionarla con /sessions.",
@@ -190,6 +211,12 @@ export const es = {
190
211
  "model.menu.recent_empty": "— Vacío.",
191
212
  "model.menu.favorites_hint": "ℹ️ Agrega modelos a favoritos en OpenCode CLI para mantenerlos arriba de la lista.",
192
213
  "model.menu.error": "🔴 No se pudo obtener la lista de modelos",
214
+ "model.search.button": "🔍 Buscar",
215
+ "model.search.prompt": "🔍 Ingrese el nombre del modelo para buscar:",
216
+ "model.search.results_title": "Resultados de búsqueda para \"{query}\":",
217
+ "model.search.no_results": "No se encontraron modelos para \"{query}\"",
218
+ "model.search.search_again": "↩ Buscar de nuevo",
219
+ "model.search.error": "Búsqueda fallida",
193
220
  "variant.model_not_selected_callback": "Error: no hay un modelo seleccionado",
194
221
  "variant.changed_callback": "Variante cambiada: {name}",
195
222
  "variant.changed_message": "✅ Variante cambiada a: {name}",
package/dist/i18n/fr.js CHANGED
@@ -4,6 +4,7 @@ export const fr = {
4
4
  "cmd.description.stop": "Arrêter l'action en cours",
5
5
  "cmd.description.detach": "Se détacher de la session actuelle",
6
6
  "cmd.description.sessions": "Lister les sessions",
7
+ "cmd.description.messages": "Parcourir les messages de session",
7
8
  "cmd.description.tts": "Basculer les réponses audio",
8
9
  "cmd.description.projects": "Lister les projets",
9
10
  "cmd.description.worktree": "Changer de worktree git",
@@ -126,6 +127,26 @@ export const fr = {
126
127
  "sessions.preview.title": "Messages récents :",
127
128
  "sessions.preview.you": "Vous :",
128
129
  "sessions.preview.agent": "Agent :",
130
+ "messages.project_not_selected": "🏗 Aucun projet sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
131
+ "messages.session_not_selected": "💬 Aucune session sélectionnée.\n\nChoisissez d'abord une session avec /sessions ou créez-en une avec /new.",
132
+ "messages.session_project_mismatch": "⚠️ La session sélectionnée ne correspond pas au projet actuel. Choisissez à nouveau la session via /sessions.",
133
+ "messages.empty": "📭 Aucun message utilisateur dans la session actuelle.",
134
+ "messages.select": "Choisissez un message :",
135
+ "messages.select_page": "Choisissez un message (page {page}) :",
136
+ "messages.fetch_error": "🔴 OpenCode Server est indisponible ou une erreur est survenue pendant le chargement des messages.",
137
+ "messages.inactive_callback": "Ce menu de messages est inactif",
138
+ "messages.cancelled_callback": "Annulé",
139
+ "messages.page_empty_callback": "Aucun message sur cette page",
140
+ "messages.button.prev_page": "⬅️ Précédent",
141
+ "messages.button.next_page": "Suivant ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ Retour",
145
+ "messages.button.cancel": "❌ Annuler",
146
+ "messages.revert_success": "✅ Retour au message :\n\n{text}",
147
+ "messages.revert_error": "❌ Impossible de revenir au message. Veuillez réessayer.",
148
+ "messages.fork_success": "🔀 Fork créé à partir du message :\n\n{text}",
149
+ "messages.fork_error": "❌ Échec de la création du fork. Veuillez réessayer.",
129
150
  "attach.project_not_selected": "🏗 Aucun projet sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
130
151
  "attach.session_not_selected": "💬 Aucune session sélectionnée.\n\nSélectionnez d'abord une session avec /sessions.",
131
152
  "attach.session_project_mismatch": "⚠️ La session sélectionnée ne correspond pas au projet actuel. Sélectionnez-la de nouveau via /sessions.",
@@ -190,6 +211,12 @@ export const fr = {
190
211
  "model.menu.recent_empty": "— Vide.",
191
212
  "model.menu.favorites_hint": "ℹ️ Ajoutez des modèles aux favoris dans l'interface OpenCode pour les garder en tête de liste.",
192
213
  "model.menu.error": "🔴 Impossible de récupérer la liste des modèles",
214
+ "model.search.button": "🔍 Rechercher",
215
+ "model.search.prompt": "🔍 Entrez le nom du modèle à rechercher :",
216
+ "model.search.results_title": "Résultats de recherche pour \"{query}\" :",
217
+ "model.search.no_results": "Aucun modèle trouvé pour \"{query}\"",
218
+ "model.search.search_again": "↩ Rechercher à nouveau",
219
+ "model.search.error": "Échec de la recherche",
193
220
  "variant.model_not_selected_callback": "Erreur : aucun modèle sélectionné",
194
221
  "variant.changed_callback": "Variante modifiée : {name}",
195
222
  "variant.changed_message": "✅ Variante définie sur : {name}",
@@ -1,4 +1,5 @@
1
1
  import { en } from "./en.js";
2
+ import { ar } from "./ar.js";
2
3
  import { de } from "./de.js";
3
4
  import { es } from "./es.js";
4
5
  import { fr } from "./fr.js";
@@ -11,6 +12,12 @@ const LOCALE_DEFINITIONS = [
11
12
  dateLocale: "en-US",
12
13
  dictionary: en,
13
14
  },
15
+ {
16
+ code: "ar",
17
+ label: "العربية",
18
+ dateLocale: "ar-SA",
19
+ dictionary: ar,
20
+ },
14
21
  {
15
22
  code: "de",
16
23
  label: "Deutsch",
package/dist/i18n/ru.js CHANGED
@@ -4,6 +4,7 @@ export const ru = {
4
4
  "cmd.description.stop": "Прервать текущее действие",
5
5
  "cmd.description.detach": "Отсоединиться от текущей сессии",
6
6
  "cmd.description.sessions": "Список сессий",
7
+ "cmd.description.messages": "Сообщения текущей сессии",
7
8
  "cmd.description.tts": "Переключить аудиоответы",
8
9
  "cmd.description.projects": "Список проектов",
9
10
  "cmd.description.worktree": "Переключить git worktree",
@@ -126,6 +127,26 @@ export const ru = {
126
127
  "sessions.preview.title": "Последние сообщения:",
127
128
  "sessions.preview.you": "Вы:",
128
129
  "sessions.preview.agent": "Агент:",
130
+ "messages.project_not_selected": "🏗 Проект не выбран.\n\nСначала выберите проект командой /projects.",
131
+ "messages.session_not_selected": "💬 Сессия не выбрана.\n\nСначала выберите сессию через /sessions или создайте новую через /new.",
132
+ "messages.session_project_mismatch": "⚠️ Выбранная сессия не соответствует текущему проекту. Повторно выберите сессию через /sessions.",
133
+ "messages.empty": "📭 В текущей сессии нет сообщений пользователя.",
134
+ "messages.select": "Выберите сообщение:",
135
+ "messages.select_page": "Выберите сообщение (страница {page}):",
136
+ "messages.fetch_error": "🔴 OpenCode Server недоступен или произошла ошибка при получении списка сообщений.",
137
+ "messages.inactive_callback": "Это меню сообщений уже неактивно",
138
+ "messages.cancelled_callback": "Отменено",
139
+ "messages.page_empty_callback": "На этой странице нет сообщений",
140
+ "messages.button.prev_page": "⬅️ Назад",
141
+ "messages.button.next_page": "Вперёд ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ Назад",
145
+ "messages.button.cancel": "❌ Отмена",
146
+ "messages.revert_success": "✅ Откатили до сообщения:\n\n{text}",
147
+ "messages.revert_error": "❌ Не удалось откатить сообщение. Попробуйте ещё раз.",
148
+ "messages.fork_success": "🔀 Создан форк от сообщения:\n\n{text}",
149
+ "messages.fork_error": "❌ Не удалось создать форк. Попробуйте ещё раз.",
129
150
  "attach.project_not_selected": "🏗 Проект не выбран.\n\nСначала выберите проект командой /projects.",
130
151
  "attach.session_not_selected": "💬 Сессия не выбрана.\n\nСначала выберите сессию через /sessions.",
131
152
  "attach.session_project_mismatch": "⚠️ Выбранная сессия не соответствует текущему проекту. Повторно выберите сессию через /sessions.",
@@ -190,6 +211,12 @@ export const ru = {
190
211
  "model.menu.recent_empty": "— Список пуст.",
191
212
  "model.menu.favorites_hint": "ℹ️ Добавляйте модели в избранное через OpenCode CLI, чтобы они были вверху списка.",
192
213
  "model.menu.error": "🔴 Не удалось получить список моделей",
214
+ "model.search.button": "🔍 Поиск",
215
+ "model.search.prompt": "🔍 Введите название модели для поиска:",
216
+ "model.search.results_title": "Результаты поиска для \"{query}\":",
217
+ "model.search.no_results": "Модели не найдены для \"{query}\"",
218
+ "model.search.search_again": "↩ Искать снова",
219
+ "model.search.error": "Ошибка поиска",
193
220
  "variant.model_not_selected_callback": "Ошибка: модель не выбрана",
194
221
  "variant.changed_callback": "Вариант изменен: {name}",
195
222
  "variant.changed_message": "✅ Вариант изменен на: {name}",
package/dist/i18n/zh.js CHANGED
@@ -4,6 +4,7 @@ export const zh = {
4
4
  "cmd.description.stop": "停止当前操作",
5
5
  "cmd.description.detach": "从当前会话分离",
6
6
  "cmd.description.sessions": "列出会话",
7
+ "cmd.description.messages": "浏览会话消息",
7
8
  "cmd.description.tts": "切换语音回复",
8
9
  "cmd.description.projects": "列出项目",
9
10
  "cmd.description.worktree": "切换 git worktree",
@@ -126,6 +127,26 @@ export const zh = {
126
127
  "sessions.preview.title": "最近消息:",
127
128
  "sessions.preview.you": "你:",
128
129
  "sessions.preview.agent": "代理:",
130
+ "messages.project_not_selected": "🏗 未选择项目。\n\n请先使用 /projects 选择项目。",
131
+ "messages.session_not_selected": "💬 未选择会话。\n\n请先使用 /sessions 选择会话,或使用 /new 创建会话。",
132
+ "messages.session_project_mismatch": "⚠️ 所选会话与当前项目不匹配。请通过 /sessions 重新选择会话。",
133
+ "messages.empty": "📭 当前会话中没有用户消息。",
134
+ "messages.select": "选择一条消息:",
135
+ "messages.select_page": "选择一条消息(第 {page} 页):",
136
+ "messages.fetch_error": "🔴 OpenCode Server 不可用,或加载消息时发生错误。",
137
+ "messages.inactive_callback": "此消息菜单已失效",
138
+ "messages.cancelled_callback": "已取消",
139
+ "messages.page_empty_callback": "此页面没有消息",
140
+ "messages.button.prev_page": "⬅️ 上一页",
141
+ "messages.button.next_page": "下一页 ➡️",
142
+ "messages.button.revert": "↩️ Revert",
143
+ "messages.button.fork": "🔀 Fork",
144
+ "messages.button.back": "⬅️ 返回",
145
+ "messages.button.cancel": "❌ 取消",
146
+ "messages.revert_success": "✅ 已恢复到消息:\n\n{text}",
147
+ "messages.revert_error": "❌ 无法恢复消息。请重试。",
148
+ "messages.fork_success": "🔀 从消息创建分支:\n\n{text}",
149
+ "messages.fork_error": "❌ 创建分支失败。请重试。",
129
150
  "attach.project_not_selected": "🏗 未选择项目。\n\n请先使用 /projects 选择一个项目。",
130
151
  "attach.session_not_selected": "💬 未选择会话。\n\n请先使用 /sessions 选择一个会话。",
131
152
  "attach.session_project_mismatch": "⚠️ 选中的会话与当前项目不匹配。请通过 /sessions 重新选择。",
@@ -190,6 +211,12 @@ export const zh = {
190
211
  "model.menu.recent_empty": "— 列表为空。",
191
212
  "model.menu.favorites_hint": "ℹ️ 可在 OpenCode CLI 中将模型加入收藏,使其显示在列表顶部。",
192
213
  "model.menu.error": "🔴 获取模型列表失败",
214
+ "model.search.button": "🔍 搜索",
215
+ "model.search.prompt": "🔍 输入模型名称进行搜索:",
216
+ "model.search.results_title": "\"{query}\" 的搜索结果:",
217
+ "model.search.no_results": "未找到 \"{query}\" 的模型",
218
+ "model.search.search_again": "↩ 重新搜索",
219
+ "model.search.error": "搜索失败",
193
220
  "variant.model_not_selected_callback": "错误:未选择模型",
194
221
  "variant.changed_callback": "变体已更改:{name}",
195
222
  "variant.changed_message": "✅ 变体已切换为:{name}",
@@ -11,8 +11,10 @@ const SERVER_UNAVAILABLE_ERROR_MARKERS = [
11
11
  "connect refused",
12
12
  ];
13
13
  let cachedValidModelKeys = null;
14
+ let cachedAllModels = null;
14
15
  let modelCatalogCacheExpiresAt = 0;
15
16
  let modelCatalogFetchInFlight = null;
17
+ const SEARCH_RESULTS_LIMIT = 10;
16
18
  function getModelKey(providerID, modelID) {
17
19
  return `${providerID}/${modelID}`;
18
20
  }
@@ -117,12 +119,15 @@ async function getValidModelKeys(options) {
117
119
  return null;
118
120
  }
119
121
  const validModelKeys = new Set();
122
+ const allModels = [];
120
123
  for (const provider of response.data.providers) {
121
124
  for (const modelID of Object.keys(provider.models)) {
122
125
  validModelKeys.add(getModelKey(provider.id, modelID));
126
+ allModels.push({ providerID: provider.id, modelID });
123
127
  }
124
128
  }
125
129
  cachedValidModelKeys = validModelKeys;
130
+ cachedAllModels = allModels;
126
131
  modelCatalogCacheExpiresAt = Date.now() + MODEL_CATALOG_CACHE_TTL_MS;
127
132
  logger.debug(`[ModelManager] Model catalog refreshed: providers=${response.data.providers.length}, models=${validModelKeys.size}`);
128
133
  return cachedValidModelKeys;
@@ -261,6 +266,7 @@ export async function reconcileStoredModelSelection(options) {
261
266
  }
262
267
  export function __resetModelCatalogCacheForTests() {
263
268
  cachedValidModelKeys = null;
269
+ cachedAllModels = null;
264
270
  modelCatalogCacheExpiresAt = 0;
265
271
  modelCatalogFetchInFlight = null;
266
272
  }
@@ -272,6 +278,38 @@ export async function getFavoriteModels() {
272
278
  const { favorites } = await getModelSelectionLists();
273
279
  return favorites;
274
280
  }
281
+ /**
282
+ * Search models across the full provider catalog.
283
+ * Matches case-insensitive substring against "providerID/modelID".
284
+ * Returns at most SEARCH_RESULTS_LIMIT results sorted alphabetically.
285
+ * @param query Search query string
286
+ * @returns Matching models, empty array if catalog unavailable or no matches
287
+ */
288
+ export async function searchModels(query) {
289
+ const normalizedQuery = query.trim().toLowerCase();
290
+ if (!normalizedQuery) {
291
+ return [];
292
+ }
293
+ // Ensure catalog is loaded (uses cache if fresh)
294
+ const validKeys = await getValidModelKeys();
295
+ if (!validKeys || !cachedAllModels) {
296
+ logger.warn("[ModelManager] Model catalog unavailable, skipping search");
297
+ return [];
298
+ }
299
+ const results = cachedAllModels
300
+ .filter((model) => {
301
+ const key = getModelKey(model.providerID, model.modelID).toLowerCase();
302
+ return key.includes(normalizedQuery);
303
+ })
304
+ .sort((a, b) => {
305
+ const keyA = getModelKey(a.providerID, a.modelID).toLowerCase();
306
+ const keyB = getModelKey(b.providerID, b.modelID).toLowerCase();
307
+ return keyA.localeCompare(keyB);
308
+ })
309
+ .slice(0, SEARCH_RESULTS_LIMIT);
310
+ logger.debug(`[ModelManager] Model search: query="${query}", results=${results.length}`);
311
+ return results;
312
+ }
275
313
  /**
276
314
  * Get current model from settings or fallback to config
277
315
  * @returns Current model info
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinev/opencode-telegram-bot",
3
- "version": "0.20.6",
3
+ "version": "0.21.1",
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",