opencode-db 0.3.0__py3-none-any.whl
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.
- cli.py +70 -0
- cmd_costs.py +197 -0
- cmd_delete.py +58 -0
- cmd_export.py +189 -0
- cmd_help.py +210 -0
- cmd_info.py +152 -0
- cmd_list.py +124 -0
- cmd_projects.py +82 -0
- cmd_prune.py +127 -0
- cmd_search.py +111 -0
- cmd_stats.py +115 -0
- cmd_todos.py +90 -0
- cmd_tree.py +95 -0
- cmd_vacuum.py +67 -0
- commands/__init__.py +51 -0
- config.py +14 -0
- db.py +374 -0
- formatters.py +208 -0
- i18n.py +532 -0
- opencode_db-0.3.0.dist-info/METADATA +7 -0
- opencode_db-0.3.0.dist-info/RECORD +26 -0
- opencode_db-0.3.0.dist-info/WHEEL +5 -0
- opencode_db-0.3.0.dist-info/entry_points.txt +2 -0
- opencode_db-0.3.0.dist-info/licenses/LICENSE +25 -0
- opencode_db-0.3.0.dist-info/top_level.txt +20 -0
- utils.py +115 -0
i18n.py
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"""Интернационализация: поддержка русского и английского языка.
|
|
2
|
+
|
|
3
|
+
Использование:
|
|
4
|
+
from i18n import _, set_lang
|
|
5
|
+
|
|
6
|
+
set_lang("en")
|
|
7
|
+
print(_("list.header.id")) # → "ID"
|
|
8
|
+
print(_("export.saved", path="f")) # → "✅ Диалог сохранён: f"
|
|
9
|
+
|
|
10
|
+
Структура словаря:
|
|
11
|
+
L[ключ] = {"ru": "...", "en": "..."}
|
|
12
|
+
|
|
13
|
+
Если ключа нет — возвращается сам ключ.
|
|
14
|
+
Если язык не указан — русский (по умолчанию).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
current_lang = os.environ.get("OPENCODE_DB_LANG", "ru")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
L: dict[str, dict[str, str]] = {
|
|
23
|
+
# ==================================================================
|
|
24
|
+
# ГЛОБАЛЬНЫЕ
|
|
25
|
+
# ==================================================================
|
|
26
|
+
"db.not_found": {
|
|
27
|
+
"ru": "❌ База OpenCode не найдена: {path}",
|
|
28
|
+
"en": "❌ OpenCode database not found: {path}",
|
|
29
|
+
},
|
|
30
|
+
"db.not_found_hint": {
|
|
31
|
+
"ru": " Убедись, что opencode установлен и хотя бы раз запускался.",
|
|
32
|
+
"en": " Make sure OpenCode is installed and has been run at least once.",
|
|
33
|
+
},
|
|
34
|
+
"session.not_found": {
|
|
35
|
+
"ru": "❌ Сессия {session_id} не найдена",
|
|
36
|
+
"en": "❌ Session {session_id} not found",
|
|
37
|
+
},
|
|
38
|
+
"session.no_sessions": {
|
|
39
|
+
"ru": "❌ Нет сессий в базе",
|
|
40
|
+
"en": "❌ No sessions in the database",
|
|
41
|
+
},
|
|
42
|
+
"session.multiple_match": {
|
|
43
|
+
"ru": "❌ Найдено несколько сессий с префиксом {session_id}:",
|
|
44
|
+
"en": "❌ Multiple sessions match prefix {session_id}:",
|
|
45
|
+
},
|
|
46
|
+
"session.specify_id": {
|
|
47
|
+
"ru": " Уточни ID",
|
|
48
|
+
"en": " Use a longer prefix or the full ID",
|
|
49
|
+
},
|
|
50
|
+
"session.latest": {
|
|
51
|
+
"ru": " 🕐 Последняя сессия: {title}",
|
|
52
|
+
"en": " 🕐 Latest session: {title}",
|
|
53
|
+
},
|
|
54
|
+
"cmd.unknown": {
|
|
55
|
+
"ru": "❌ Неизвестная команда: {cmd}",
|
|
56
|
+
"en": "❌ Unknown command: {cmd}",
|
|
57
|
+
},
|
|
58
|
+
"empty": {
|
|
59
|
+
"ru": " (пусто)",
|
|
60
|
+
"en": " (empty)",
|
|
61
|
+
},
|
|
62
|
+
"total_count": {
|
|
63
|
+
"ru": " Всего: {n}",
|
|
64
|
+
"en": " Total: {n}",
|
|
65
|
+
},
|
|
66
|
+
"canceled": {
|
|
67
|
+
"ru": " Отменено.",
|
|
68
|
+
"en": " Canceled.",
|
|
69
|
+
},
|
|
70
|
+
"yes_no_prompt": {
|
|
71
|
+
"ru": "Подтвердить операцию?",
|
|
72
|
+
"en": "Proceed with the operation?",
|
|
73
|
+
},
|
|
74
|
+
"yes_no_true": {
|
|
75
|
+
"ru": " [Y/n]: ",
|
|
76
|
+
"en": " [Y/n]: ",
|
|
77
|
+
},
|
|
78
|
+
"yes_no_false": {
|
|
79
|
+
"ru": " [y/N]: ",
|
|
80
|
+
"en": " [y/N]: ",
|
|
81
|
+
},
|
|
82
|
+
"not_obsidian": {
|
|
83
|
+
"ru": "ℹ️ log.md пропущен (не Obsidian-проект)",
|
|
84
|
+
"en": "ℹ️ log.md skipped (not an Obsidian project)",
|
|
85
|
+
},
|
|
86
|
+
"log_updated": {
|
|
87
|
+
"ru": "✅ log.md обновлён: {path}",
|
|
88
|
+
"en": "✅ log.md updated: {path}",
|
|
89
|
+
},
|
|
90
|
+
# ==================================================================
|
|
91
|
+
# LIST
|
|
92
|
+
# ==================================================================
|
|
93
|
+
"list.header.id": {"ru": "ID", "en": "ID"},
|
|
94
|
+
"list.header.title": {"ru": "Название", "en": "Title"},
|
|
95
|
+
"list.header.project": {"ru": "Проект", "en": "Project"},
|
|
96
|
+
"list.header.model": {"ru": "Модель", "en": "Model"},
|
|
97
|
+
"list.header.activity": {"ru": "Активность", "en": "Activity"},
|
|
98
|
+
"list.header.messages": {"ru": "Сообщ.", "en": "Msgs"},
|
|
99
|
+
"list.header.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
100
|
+
# ==================================================================
|
|
101
|
+
# INFO
|
|
102
|
+
# ==================================================================
|
|
103
|
+
"info.id": {"ru": "ID", "en": "ID"},
|
|
104
|
+
"info.project": {"ru": "Проект", "en": "Project"},
|
|
105
|
+
"info.model": {"ru": "Модель", "en": "Model"},
|
|
106
|
+
"info.agent": {"ru": "Агент", "en": "Agent"},
|
|
107
|
+
"info.version": {"ru": "Версия", "en": "Version"},
|
|
108
|
+
"info.created": {"ru": "Создана", "en": "Created"},
|
|
109
|
+
"info.updated": {"ru": "Активна", "en": "Last active"},
|
|
110
|
+
"info.duration": {"ru": "Длит-сть", "en": "Duration"},
|
|
111
|
+
"info.parent": {"ru": "Родитель", "en": "Parent"},
|
|
112
|
+
"info.branches": {"ru": "Веток", "en": "Children"},
|
|
113
|
+
"info.messages": {"ru": "Сообщений", "en": "Messages"},
|
|
114
|
+
"info.parts": {"ru": "Частей", "en": "Parts"},
|
|
115
|
+
"info.tokens_input": {"ru": "Токены ввода", "en": "Input tokens"},
|
|
116
|
+
"info.tokens_output": {"ru": "Токены вывода", "en": "Output tokens"},
|
|
117
|
+
"info.tokens_reasoning": {"ru": "Токены reasoning", "en": "Reasoning tokens"},
|
|
118
|
+
"info.cache_read": {"ru": "Кэш (чтение)", "en": "Cache (read)"},
|
|
119
|
+
"info.cache_write": {"ru": "Кэш (запись)", "en": "Cache (write)"},
|
|
120
|
+
"info.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
|
|
121
|
+
"info.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
122
|
+
"info.code_changes": {"ru": "Изменения кода", "en": "Code changes"},
|
|
123
|
+
"info.files_changed": {"ru": "Файлов", "en": "Files"},
|
|
124
|
+
"info.lines_added": {"ru": "Добавлено", "en": "Added"},
|
|
125
|
+
"info.lines_deleted": {"ru": "Удалено", "en": "Deleted"},
|
|
126
|
+
"info.todos": {"ru": "Задачи (todo)", "en": "Tasks (todo)"},
|
|
127
|
+
"info.todos_status": {
|
|
128
|
+
"ru": "Статус: {status}, создано: {created}",
|
|
129
|
+
"en": "Status: {status}, created: {created}",
|
|
130
|
+
},
|
|
131
|
+
"info.unnamed": {"ru": "Безымянная сессия", "en": "Unnamed session"},
|
|
132
|
+
"info.children_prefix": {"ru": " └─ {id} — {title}", "en": " └─ {id} — {title}"},
|
|
133
|
+
"info.parent_fmt": {"ru": " {id} — {title}", "en": " {id} — {title}"},
|
|
134
|
+
"info.not_found": {
|
|
135
|
+
"ru": "❌ Сессия {session_id} не найдена",
|
|
136
|
+
"en": "❌ Session {session_id} not found",
|
|
137
|
+
},
|
|
138
|
+
# ==================================================================
|
|
139
|
+
# EXPORT
|
|
140
|
+
# ==================================================================
|
|
141
|
+
"export.saved": {"ru": "✅ Диалог сохранён: {path}", "en": "✅ Dialog saved: {path}"},
|
|
142
|
+
"export.messages": {"ru": " Сообщений: {n}", "en": " Messages: {n}"},
|
|
143
|
+
"export.exists": {
|
|
144
|
+
"ru": "❌ Файл уже существует: {path}",
|
|
145
|
+
"en": "❌ File already exists: {path}",
|
|
146
|
+
},
|
|
147
|
+
"export.exists_hint": {
|
|
148
|
+
"ru": " Используй --force для перезаписи",
|
|
149
|
+
"en": " Use --force to overwrite",
|
|
150
|
+
},
|
|
151
|
+
"export.interactive_header": {"ru": "Последние сессии:", "en": "Recent sessions:"},
|
|
152
|
+
"export.interactive_prompt": {
|
|
153
|
+
"ru": "Выбери номер (или Enter для отмены): ",
|
|
154
|
+
"en": "Pick a number (or Enter to cancel): ",
|
|
155
|
+
},
|
|
156
|
+
"export.interactive_error": {
|
|
157
|
+
"ru": "Введи число от 1 до {n}",
|
|
158
|
+
"en": "Enter a number from 1 to {n}",
|
|
159
|
+
},
|
|
160
|
+
"export.interactive_exporting": {"ru": " Экспорт: {title}", "en": " Exporting: {title}"},
|
|
161
|
+
"export.no_sessions": {"ru": "❌ Нет сессий для экспорта.", "en": "❌ No sessions to export."},
|
|
162
|
+
"export.interactive_abort": {"ru": " Отменено.", "en": " Canceled."},
|
|
163
|
+
# ==================================================================
|
|
164
|
+
# DELETE
|
|
165
|
+
# ==================================================================
|
|
166
|
+
"delete.header": {"ru": "УДАЛЕНИЕ СЕССИИ", "en": "DELETE SESSION"},
|
|
167
|
+
"delete.id": {"ru": "ID", "en": "ID"},
|
|
168
|
+
"delete.title": {"ru": "Название", "en": "Title"},
|
|
169
|
+
"delete.model": {"ru": "Модель", "en": "Model"},
|
|
170
|
+
"delete.created": {"ru": "Создана", "en": "Created"},
|
|
171
|
+
"delete.messages": {"ru": "Сообщений", "en": "Messages"},
|
|
172
|
+
"delete.parts": {"ru": "Частей", "en": "Parts"},
|
|
173
|
+
"delete.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
174
|
+
"delete.dry_run": {
|
|
175
|
+
"ru": " 🔍 Сухой запуск — ничего не удалено.",
|
|
176
|
+
"en": " 🔍 Dry run — nothing deleted.",
|
|
177
|
+
},
|
|
178
|
+
"delete.dry_run_hint": {
|
|
179
|
+
"ru": " Для удаления запусти без --dry-run",
|
|
180
|
+
"en": " Run without --dry-run to actually delete",
|
|
181
|
+
},
|
|
182
|
+
"delete.confirm": {"ru": "Удалить сессию {id}?", "en": "Delete session {id}?"},
|
|
183
|
+
"delete.done": {"ru": " ✅ Сессия удалена: {id}…", "en": " ✅ Session deleted: {id}…"},
|
|
184
|
+
"delete.done_detail": {
|
|
185
|
+
"ru": " Сообщений: {msg}, частей: {parts}",
|
|
186
|
+
"en": " Messages: {msg}, parts: {parts}",
|
|
187
|
+
},
|
|
188
|
+
# ==================================================================
|
|
189
|
+
# COSTS
|
|
190
|
+
# ==================================================================
|
|
191
|
+
"costs.session": {"ru": "Сессия", "en": "Session"},
|
|
192
|
+
"costs.model": {"ru": "Модель", "en": "Model"},
|
|
193
|
+
"costs.input": {"ru": "Ввод", "en": "Input"},
|
|
194
|
+
"costs.output": {"ru": "Вывод", "en": "Output"},
|
|
195
|
+
"costs.reasoning": {"ru": "Reasoning", "en": "Reasoning"},
|
|
196
|
+
"costs.total": {"ru": "Всего", "en": "Total"},
|
|
197
|
+
"costs.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
198
|
+
"costs.total_header": {"ru": "ИТОГО ПО {scope}", "en": "TOTAL BY {scope}"},
|
|
199
|
+
"costs.scope_all": {"ru": "ВСЕЙ БД", "en": "DATABASE"},
|
|
200
|
+
"costs.scope_project": {"ru": "ПРОЕКТУ", "en": "PROJECT"},
|
|
201
|
+
"costs.sessions": {"ru": "Сессий", "en": "Sessions"},
|
|
202
|
+
"costs.tokens_input": {"ru": "Токенов ввода", "en": "Input tokens"},
|
|
203
|
+
"costs.tokens_output": {"ru": "Токенов вывода", "en": "Output tokens"},
|
|
204
|
+
"costs.tokens_reasoning": {"ru": "Токенов reasoning", "en": "Reasoning tokens"},
|
|
205
|
+
"costs.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
|
|
206
|
+
"costs.cache_read": {"ru": "Кэш (чтение)", "en": "Cache (read)"},
|
|
207
|
+
"costs.cache_write": {"ru": "Кэш (запись)", "en": "Cache (write)"},
|
|
208
|
+
# ==================================================================
|
|
209
|
+
# PRUNE
|
|
210
|
+
# ==================================================================
|
|
211
|
+
"prune.header": {"ru": "Будет удалено сессий: {n}", "en": "Sessions to delete: {n}"},
|
|
212
|
+
"prune.none": {"ru": " ✅ Нет сессий для удаления.", "en": " ✅ No sessions to delete."},
|
|
213
|
+
"prune.filter_required": {
|
|
214
|
+
"ru": "❌ Укажи хотя бы один фильтр: --older-than, --keep-last или --project",
|
|
215
|
+
"en": "❌ Specify at least one filter: --older-than, --keep-last, or --project",
|
|
216
|
+
},
|
|
217
|
+
"prune.filter_example": {
|
|
218
|
+
"ru": " Пример: opencode-db prune --older-than 90d --dry-run",
|
|
219
|
+
"en": " Example: opencode-db prune --older-than 90d --dry-run",
|
|
220
|
+
},
|
|
221
|
+
"prune.bad_spec": {
|
|
222
|
+
"ru": "❌ Не могу разобрать: --older-than {spec}",
|
|
223
|
+
"en": "❌ Cannot parse: --older-than {spec}",
|
|
224
|
+
},
|
|
225
|
+
"prune.bad_spec_hint": {
|
|
226
|
+
"ru": " Используй формат: 30d, 90d, 6m, 1y",
|
|
227
|
+
"en": " Use format: 30d, 90d, 6m, 1y",
|
|
228
|
+
},
|
|
229
|
+
"prune.dry_run": {
|
|
230
|
+
"ru": " 🔍 Сухой запуск — ничего не удалено.",
|
|
231
|
+
"en": " 🔍 Dry run — nothing deleted.",
|
|
232
|
+
},
|
|
233
|
+
"prune.confirm": {"ru": "Удалить {n} сессий?", "en": "Delete {n} sessions?"},
|
|
234
|
+
"prune.done": {"ru": " ✅ Удалено {n} сессий.", "en": " ✅ Deleted {n} sessions."},
|
|
235
|
+
"prune.cost_total": {
|
|
236
|
+
"ru": "Стоимость удаляемых: {cost}",
|
|
237
|
+
"en": "Cost of deleted sessions: {cost}",
|
|
238
|
+
},
|
|
239
|
+
"prune.tokens_total": {"ru": "Освободится токенов: ~{n}", "en": "Tokens freed: ~{n}"},
|
|
240
|
+
# ==================================================================
|
|
241
|
+
# SEARCH
|
|
242
|
+
# ==================================================================
|
|
243
|
+
"search.header": {
|
|
244
|
+
"ru": "🔍 Результаты поиска: «{query}»",
|
|
245
|
+
"en": "🔍 Search results: «{query}»",
|
|
246
|
+
},
|
|
247
|
+
"search.none": {
|
|
248
|
+
"ru": "\n 🔍 Ничего не найдено по запросу: {query}",
|
|
249
|
+
"en": "\n 🔍 Nothing found for: {query}",
|
|
250
|
+
},
|
|
251
|
+
"search.found": {"ru": "Найдено: {n}", "en": "Found: {n}"},
|
|
252
|
+
# ==================================================================
|
|
253
|
+
# TREE
|
|
254
|
+
# ==================================================================
|
|
255
|
+
"tree.header": {
|
|
256
|
+
"ru": "🌳 Дерево сессий (от: {title})",
|
|
257
|
+
"en": "🌳 Session tree (from: {title})",
|
|
258
|
+
},
|
|
259
|
+
"tree.header_all": {
|
|
260
|
+
"ru": "🌳 Все корневые сессии (ветвление)",
|
|
261
|
+
"en": "🌳 All root sessions (branching)",
|
|
262
|
+
},
|
|
263
|
+
"tree.header_project": {
|
|
264
|
+
"ru": "🌳 Дерево сессий проекта {project}",
|
|
265
|
+
"en": "🌳 Session tree for project {project}",
|
|
266
|
+
},
|
|
267
|
+
# ==================================================================
|
|
268
|
+
# PROJECTS
|
|
269
|
+
# ==================================================================
|
|
270
|
+
"projects.header.id": {"ru": "ID", "en": "ID"},
|
|
271
|
+
"projects.header.name": {"ru": "Название", "en": "Name"},
|
|
272
|
+
"projects.header.activity": {"ru": "Активность", "en": "Activity"},
|
|
273
|
+
"projects.header.sessions": {"ru": "Сессии", "en": "Sessions"},
|
|
274
|
+
"projects.header.tokens": {"ru": "Токены", "en": "Tokens"},
|
|
275
|
+
"projects.header.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
276
|
+
# ==================================================================
|
|
277
|
+
# STATS
|
|
278
|
+
# ==================================================================
|
|
279
|
+
"stats.header": {"ru": "📊 СТАТИСТИКА БАЗЫ OPENCODE", "en": "📊 OPENCODE DATABASE STATISTICS"},
|
|
280
|
+
"stats.projects": {"ru": "Проектов", "en": "Projects"},
|
|
281
|
+
"stats.sessions": {"ru": "Сессий", "en": "Sessions"},
|
|
282
|
+
"stats.messages": {"ru": "Сообщений", "en": "Messages"},
|
|
283
|
+
"stats.parts": {"ru": "Частей", "en": "Parts"},
|
|
284
|
+
"stats.todos": {"ru": "Задач (todo)", "en": "Tasks (todo)"},
|
|
285
|
+
"stats.first_session": {"ru": "Первая сессия", "en": "First session"},
|
|
286
|
+
"stats.last_session": {"ru": "Последняя", "en": "Last session"},
|
|
287
|
+
"stats.tokens_input": {"ru": "Токенов ввода", "en": "Input tokens"},
|
|
288
|
+
"stats.tokens_output": {"ru": "Токенов вывода", "en": "Output tokens"},
|
|
289
|
+
"stats.tokens_reasoning": {"ru": "Токенов reasoning", "en": "Reasoning tokens"},
|
|
290
|
+
"stats.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
|
|
291
|
+
"stats.cost": {"ru": "Стоимость", "en": "Cost"},
|
|
292
|
+
"stats.top_models": {"ru": "Топ моделей", "en": "Top models"},
|
|
293
|
+
"stats.agents": {"ru": "Агенты", "en": "Agents"},
|
|
294
|
+
# ==================================================================
|
|
295
|
+
# TODOS
|
|
296
|
+
# ==================================================================
|
|
297
|
+
"todos.header": {"ru": "📝 Задачи ({n})", "en": "📝 Tasks ({n})"},
|
|
298
|
+
"todos.none": {"ru": "\n 📝 Нет задач.", "en": "\n 📝 No tasks."},
|
|
299
|
+
"todos.session": {"ru": "Сессия", "en": "Session"},
|
|
300
|
+
"todos.status": {"ru": "Статус", "en": "Status"},
|
|
301
|
+
"todos.created": {"ru": "создано", "en": "created"},
|
|
302
|
+
# ==================================================================
|
|
303
|
+
# VACUUM
|
|
304
|
+
# ==================================================================
|
|
305
|
+
"vacuum.header": {"ru": "🗄️ Оптимизация базы данных", "en": "🗄️ Database optimization"},
|
|
306
|
+
"vacuum.size_before": {"ru": "Размер до", "en": "Size before"},
|
|
307
|
+
"vacuum.size_after": {"ru": "Размер после", "en": "Size after"},
|
|
308
|
+
"vacuum.path": {"ru": "Путь", "en": "Path"},
|
|
309
|
+
"vacuum.freed": {"ru": "Освобождено", "en": "Freed"},
|
|
310
|
+
"vacuum.vacuum": {"ru": "Выполняется VACUUM...", "en": "Running VACUUM..."},
|
|
311
|
+
"vacuum.reindex": {"ru": "Выполняется REINDEX...", "en": "Running REINDEX..."},
|
|
312
|
+
"vacuum.analyze": {"ru": "Выполняется ANALYZE...", "en": "Running ANALYZE..."},
|
|
313
|
+
"vacuum.confirm": {"ru": "Запустить VACUUM?", "en": "Run VACUUM?"},
|
|
314
|
+
"vacuum.error": {"ru": "❌ Ошибка: {error}", "en": "❌ Error: {error}"},
|
|
315
|
+
"vacuum.done": {"ru": "✅", "en": "✅"},
|
|
316
|
+
"vacuum.canceled": {"ru": " Отменено.", "en": " Canceled."},
|
|
317
|
+
# ==================================================================
|
|
318
|
+
# HELP
|
|
319
|
+
# ==================================================================
|
|
320
|
+
"help.header_box_top": {
|
|
321
|
+
"ru": "╔══════════════════════════════════════════════════════════╗",
|
|
322
|
+
"en": "╔══════════════════════════════════════════════════════════╗",
|
|
323
|
+
},
|
|
324
|
+
"help.header_title": {
|
|
325
|
+
"ru": "║ opencode-db — справка ║",
|
|
326
|
+
"en": "║ opencode-db — help ║",
|
|
327
|
+
},
|
|
328
|
+
"help.header_box_bottom": {
|
|
329
|
+
"ru": "╚══════════════════════════════════════════════════════════╝",
|
|
330
|
+
"en": "╚══════════════════════════════════════════════════════════╝",
|
|
331
|
+
},
|
|
332
|
+
"help.intro": {
|
|
333
|
+
"ru": "opencode-db — управление базой данных OpenCode:",
|
|
334
|
+
"en": "opencode-db — OpenCode database manager:",
|
|
335
|
+
},
|
|
336
|
+
"help.intro2": {
|
|
337
|
+
"ru": "просмотр, экспорт, аналитика, очистка сессий.",
|
|
338
|
+
"en": "browse, export, analyze, clean up sessions.",
|
|
339
|
+
},
|
|
340
|
+
"help.usage": {
|
|
341
|
+
"ru": "Использование: opencode-db <команда> [флаги]",
|
|
342
|
+
"en": "Usage: opencode-db <command> [flags]",
|
|
343
|
+
},
|
|
344
|
+
"help.footer": {
|
|
345
|
+
"ru": "opencode-db help <команда> — справка по конкретной команде",
|
|
346
|
+
"en": "opencode-db help <command> — help for a specific command",
|
|
347
|
+
},
|
|
348
|
+
"help.footer2": {
|
|
349
|
+
"ru": "opencode-db <команда> --help — флаги команды",
|
|
350
|
+
"en": "opencode-db <command> --help — command flags",
|
|
351
|
+
},
|
|
352
|
+
"help.footer3": {
|
|
353
|
+
"ru": "opencode-db --help — краткий список команд",
|
|
354
|
+
"en": "opencode-db --help — brief command list",
|
|
355
|
+
},
|
|
356
|
+
"help.unknown": {"ru": "❌ Неизвестная команда: {cmd}", "en": "❌ Unknown command: {cmd}"},
|
|
357
|
+
"help.available": {"ru": " Доступные: {cmds}", "en": " Available: {cmds}"},
|
|
358
|
+
# ==================================================================
|
|
359
|
+
# HELP — команды (краткие описания в справке)
|
|
360
|
+
# ==================================================================
|
|
361
|
+
"help.cmd.list": {"ru": "Список сессий", "en": "List sessions"},
|
|
362
|
+
"help.cmd.info": {"ru": "Детальная информация о сессии", "en": "Session details"},
|
|
363
|
+
"help.cmd.export": {"ru": "Экспорт диалога в Markdown", "en": "Export dialog to Markdown"},
|
|
364
|
+
"help.cmd.delete": {"ru": "Удаление сессии", "en": "Delete a session"},
|
|
365
|
+
"help.cmd.costs": {"ru": "Анализ расходов на токены", "en": "Token cost analysis"},
|
|
366
|
+
"help.cmd.prune": {"ru": "Массовая очистка сессий", "en": "Bulk session cleanup"},
|
|
367
|
+
"help.cmd.search": {"ru": "Поиск по сообщениям", "en": "Search messages"},
|
|
368
|
+
"help.cmd.tree": {"ru": "Дерево ветвления сессий", "en": "Session branching tree"},
|
|
369
|
+
"help.cmd.projects": {"ru": "Список проектов", "en": "Project list"},
|
|
370
|
+
"help.cmd.stats": {"ru": "Статистика БД", "en": "Database statistics"},
|
|
371
|
+
"help.cmd.todos": {"ru": "Задачи из сессий", "en": "Tasks from sessions"},
|
|
372
|
+
"help.cmd.vacuum": {"ru": "Оптимизация БД", "en": "Database optimization"},
|
|
373
|
+
"help.cmd.help": {"ru": "Подробная справка", "en": "Detailed help"},
|
|
374
|
+
# ==================================================================
|
|
375
|
+
# HELP — примеры
|
|
376
|
+
# ==================================================================
|
|
377
|
+
"help.list.e0": {"ru": "Сессии за последнее время", "en": "Recent sessions"},
|
|
378
|
+
"help.list.e1": {"ru": "Показать 10 последних", "en": "Show last 10"},
|
|
379
|
+
"help.list.e2": {"ru": "Сортировка по стоимости", "en": "Sort by cost"},
|
|
380
|
+
"help.list.e3": {"ru": "Фильтр по проекту", "en": "Filter by project"},
|
|
381
|
+
"help.list.e4": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
382
|
+
"help.info.e0": {
|
|
383
|
+
"ru": "Все метаданные, токены, todo, ветвление",
|
|
384
|
+
"en": "Metadata, tokens, todos, branching",
|
|
385
|
+
},
|
|
386
|
+
"help.info.e1": {"ru": "То же в JSON", "en": "Same in JSON"},
|
|
387
|
+
"help.export.e0": {"ru": "Экспорт по ID", "en": "Export by ID"},
|
|
388
|
+
"help.export.e1": {"ru": "Самая свежая сессия", "en": "Most recent session"},
|
|
389
|
+
"help.export.e2": {"ru": "Интерактивный выбор из списка", "en": "Interactive picker"},
|
|
390
|
+
"help.export.e3": {
|
|
391
|
+
"ru": "Полный вывод без обрезания output'ов",
|
|
392
|
+
"en": "Full output, no truncation",
|
|
393
|
+
},
|
|
394
|
+
"help.export.e4": {"ru": "Перезаписать существующий файл", "en": "Overwrite existing file"},
|
|
395
|
+
"help.export.e5": {"ru": "Добавить заметку в log.md", "en": "Add a note to log.md"},
|
|
396
|
+
"help.delete.e0": {"ru": "Удалить с подтверждением", "en": "Delete with confirmation"},
|
|
397
|
+
"help.delete.e1": {"ru": "Показать что будет удалено", "en": "Preview what will be deleted"},
|
|
398
|
+
"help.delete.e2": {"ru": "Без подтверждения", "en": "Skip confirmation"},
|
|
399
|
+
"help.costs.e0": {"ru": "Топ сессий по расходам", "en": "Top sessions by cost"},
|
|
400
|
+
"help.costs.e1": {"ru": "Детально по сессии", "en": "Single session breakdown"},
|
|
401
|
+
"help.costs.e2": {"ru": "Сумма по всей БД", "en": "Total across all sessions"},
|
|
402
|
+
"help.costs.e3": {"ru": "Сумма по проекту", "en": "Total by project"},
|
|
403
|
+
"help.costs.e4": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
404
|
+
"help.prune.e0": {"ru": "Удалить старше 90 дней", "en": "Delete older than 90 days"},
|
|
405
|
+
"help.prune.e1": {"ru": "Оставить 20 последних", "en": "Keep 20 most recent"},
|
|
406
|
+
"help.prune.e2": {"ru": "Показать что будет удалено", "en": "Preview deletions"},
|
|
407
|
+
"help.prune.e3": {"ru": "Без подтверждения", "en": "Skip confirmation"},
|
|
408
|
+
"help.search.e0": {"ru": "Поиск по всем сессиям", "en": "Search all sessions"},
|
|
409
|
+
"help.search.e1": {"ru": "В конкретной сессии", "en": "Within a specific session"},
|
|
410
|
+
"help.search.e2": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
411
|
+
"help.tree.e0": {"ru": "Корневые сессии с детьми", "en": "Root sessions with children"},
|
|
412
|
+
"help.tree.e1": {"ru": "Ветка от указанной сессии", "en": "Branch from a session"},
|
|
413
|
+
"help.tree.e2": {"ru": "Глубина 3 уровня", "en": "Max depth of 3"},
|
|
414
|
+
"help.projects.e0": {"ru": "Проекты со статистикой", "en": "Projects with statistics"},
|
|
415
|
+
"help.projects.e1": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
416
|
+
"help.stats.e0": {
|
|
417
|
+
"ru": "Сессии, сообщения, токены, стоимость, топ моделей",
|
|
418
|
+
"en": "Sessions, messages, tokens, costs, top models",
|
|
419
|
+
},
|
|
420
|
+
"help.stats.e1": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
421
|
+
"help.todos.e0": {"ru": "Все задачи", "en": "All tasks"},
|
|
422
|
+
"help.todos.e1": {"ru": "Задачи конкретной сессии", "en": "Tasks for a session"},
|
|
423
|
+
"help.todos.e2": {"ru": "Только ожидающие", "en": "Pending only"},
|
|
424
|
+
"help.todos.e3": {"ru": "Вывод в JSON", "en": "JSON output"},
|
|
425
|
+
"help.vacuum.e0": {
|
|
426
|
+
"ru": "VACUUM + REINDEX + ANALYZE с подтверждением",
|
|
427
|
+
"en": "VACUUM + REINDEX + ANALYZE with confirmation",
|
|
428
|
+
},
|
|
429
|
+
"help.vacuum.e1": {"ru": "Без подтверждения", "en": "Skip confirmation"},
|
|
430
|
+
# ==================================================================
|
|
431
|
+
# MARKDOWN EXPORT (formatters.py, config.py)
|
|
432
|
+
# ==================================================================
|
|
433
|
+
"md.reasoning": {"ru": "Рассуждение", "en": "Reasoning"},
|
|
434
|
+
"md.sound": {"ru": "Аудиосообщение", "en": "Voice message"},
|
|
435
|
+
"md.result": {"ru": "Результат", "en": "Result"},
|
|
436
|
+
"md.header.title": {"ru": "Диалог", "en": "Conversation"},
|
|
437
|
+
"md.header.date": {"ru": "Дата", "en": "Date"},
|
|
438
|
+
"md.header.model": {"ru": "Модель", "en": "Model"},
|
|
439
|
+
"md.header.session_id": {"ru": "ID сессии", "en": "Session ID"},
|
|
440
|
+
"md.header.messages": {"ru": "Сообщений", "en": "Messages"},
|
|
441
|
+
"md.log_agents": {"ru": "AGENTS.md: {v}", "en": "AGENTS.md: {v}"},
|
|
442
|
+
"md.log_files": {
|
|
443
|
+
"ru": "Файлов: {files}, директорий: {dirs}",
|
|
444
|
+
"en": "Files: {files}, directories: {dirs}",
|
|
445
|
+
},
|
|
446
|
+
"md.log_not_found": {"ru": "не найден", "en": "not found"},
|
|
447
|
+
"md.log_lines": {
|
|
448
|
+
"ru": "{lines} строк, {sections} секций",
|
|
449
|
+
"en": "{lines} lines, {sections} sections",
|
|
450
|
+
},
|
|
451
|
+
# ==================================================================
|
|
452
|
+
# SCHEMA GUARD
|
|
453
|
+
# ==================================================================
|
|
454
|
+
"schema.table_missing": {
|
|
455
|
+
"ru": " ⚠️ Таблица '{table}' не найдена в БД. Возможно, OpenCode обновился.",
|
|
456
|
+
"en": " ⚠️ Table '{table}' not found in the database. OpenCode may have been updated.",
|
|
457
|
+
},
|
|
458
|
+
"schema.column_missing": {
|
|
459
|
+
"ru": " ⚠️ В таблице '{table}' отсутствуют колонки: {columns}",
|
|
460
|
+
"en": " ⚠️ Table '{table}' is missing columns: {columns}",
|
|
461
|
+
},
|
|
462
|
+
"schema.check_upgrade": {
|
|
463
|
+
"ru": " Проверь совместимость версий opencode-db и OpenCode.",
|
|
464
|
+
"en": " Check opencode-db and OpenCode version compatibility.",
|
|
465
|
+
},
|
|
466
|
+
# ==================================================================
|
|
467
|
+
# ФОРМАТТЕРЫ
|
|
468
|
+
# ==================================================================
|
|
469
|
+
"fmt.rows_count": {"ru": "\n Всего: {n}", "en": "\n Total: {n}"},
|
|
470
|
+
# ==================================================================
|
|
471
|
+
# HELP — лейблы для _print_command_help
|
|
472
|
+
# ==================================================================
|
|
473
|
+
"help.cmd_header": {"ru": " ── {cmd} ── {desc}", "en": " ── {cmd} ── {desc}"},
|
|
474
|
+
# ==================================================================
|
|
475
|
+
# INFO — duration helpers
|
|
476
|
+
# ==================================================================
|
|
477
|
+
"info.dur_sec": {"ru": "{n} сек", "en": "{n} sec"},
|
|
478
|
+
"info.dur_min": {"ru": "{n} мин", "en": "{n} min"},
|
|
479
|
+
"info.dur_hours": {"ru": "{h}ч {m}мин", "en": "{h}h {m}m"},
|
|
480
|
+
# ==================================================================
|
|
481
|
+
# CLI — da
|
|
482
|
+
# ==================================================================
|
|
483
|
+
"cli.yn_yes": {"ru": "да", "en": "yes"},
|
|
484
|
+
# ==================================================================
|
|
485
|
+
# MARKDOWN ROLES (export headers)
|
|
486
|
+
# ==================================================================
|
|
487
|
+
"export.role_user": {"ru": "👤 Вы ({ts})", "en": "👤 You ({ts})"},
|
|
488
|
+
"export.role_assistant": {
|
|
489
|
+
"ru": "🤖 Ассистент{agent} ({ts})",
|
|
490
|
+
"en": "🤖 Assistant{agent} ({ts})",
|
|
491
|
+
},
|
|
492
|
+
"export.agent_tag": {"ru": " ({agent})", "en": " ({agent})"},
|
|
493
|
+
# ==================================================================
|
|
494
|
+
# COSTS — scope labels
|
|
495
|
+
# ==================================================================
|
|
496
|
+
"costs.by_project": {"ru": "ПРОЕКТУ", "en": "PROJECT"},
|
|
497
|
+
"costs.by_all": {"ru": "ВСЕЙ БД", "en": "DATABASE"},
|
|
498
|
+
# ==================================================================
|
|
499
|
+
# TABLE — header separators
|
|
500
|
+
# ==================================================================
|
|
501
|
+
# (эмодзи и символы не переводим)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def set_lang(lang: str):
|
|
506
|
+
"""Устанавливает язык вывода.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
lang: "ru" или "en"
|
|
510
|
+
"""
|
|
511
|
+
global current_lang
|
|
512
|
+
if lang in ("ru", "en"):
|
|
513
|
+
current_lang = lang
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _(key: str, **kwargs) -> str:
|
|
517
|
+
"""Возвращает локализованную строку по ключу.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
key: ключ в словаре L (например "list.header.id")
|
|
521
|
+
**kwargs: значения для str.format()
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
str на текущем языке, или сам key если ключ не найден
|
|
525
|
+
"""
|
|
526
|
+
entry = L.get(key)
|
|
527
|
+
if entry is None:
|
|
528
|
+
return key
|
|
529
|
+
text = entry.get(current_lang, entry.get("ru", key))
|
|
530
|
+
if kwargs:
|
|
531
|
+
return text.format(**kwargs)
|
|
532
|
+
return text
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
cli.py,sha256=4KDlBmuoYzeFy79sbExPTcPswGCacASw44Q0xwUMdXQ,2040
|
|
2
|
+
cmd_costs.py,sha256=XAs7wj9tZe3QnrUoJ54DTL6NFccXPSeWrjrQCrZERHY,5970
|
|
3
|
+
cmd_delete.py,sha256=dRgm7QML290wrGscMCz3818l6eZ69flH5YneI1WccVQ,1879
|
|
4
|
+
cmd_export.py,sha256=DtHYLrEJxQtjYsBCtS3xDmdIsTnb1DlIhXBgoMZJWIU,6057
|
|
5
|
+
cmd_help.py,sha256=jHJ6RRSt_uvOWrsfVFlJXWVnB4qop-2_eB3N_V16_6k,5596
|
|
6
|
+
cmd_info.py,sha256=dumw4vSZcjdFapLB5vH2XVk8lGIazBpTjl9t9WUkxNo,5826
|
|
7
|
+
cmd_list.py,sha256=NLvcGIna56gj683L2BunUyVEJRVxbT-W4-AyhCETEwQ,3998
|
|
8
|
+
cmd_projects.py,sha256=oSX8ngjK5GWbObLIUJGm96WkCTZV1I3GNkRz9qCJ9ks,2701
|
|
9
|
+
cmd_prune.py,sha256=RUyNi-iu47aQATBSfiLuHKvC4QxGYiP3cOybxjSyKuU,4297
|
|
10
|
+
cmd_search.py,sha256=FU420LHUGjoI86-FChZSvWe98tcBJzxjLWKYJuRFWYY,3390
|
|
11
|
+
cmd_stats.py,sha256=zcArLn4MskoYJtZ2zNQ9eBx43y3GVt1HWU9LQ5P-HR0,4555
|
|
12
|
+
cmd_todos.py,sha256=TQtCVtvcLNc3Rtn7WBRbV0Y6Trs_aIRetaGoUZ4OJL8,2756
|
|
13
|
+
cmd_tree.py,sha256=Js9Rl7ZudWA2PO_IPTbZVVOENtRkGxZLBcYsI8_Uo10,2894
|
|
14
|
+
cmd_vacuum.py,sha256=bf1CWVzif0ufmAv_iUOd6zCtnWc23ttw_qLEg7OzAno,1911
|
|
15
|
+
config.py,sha256=Nrre_pS2JI5UGOqdLBJFuG5NUXAk2FImapxgyahyEAU,655
|
|
16
|
+
db.py,sha256=OX35DUVGLgcoHIhaFOcgMuJHHVHz-M9Cunkz-EAgAzY,11640
|
|
17
|
+
formatters.py,sha256=cMRV88fTwu1H53qyvxrsi0SBjgLMiL7lslbC62kORuY,6609
|
|
18
|
+
i18n.py,sha256=Rj2UAOZvSnDpJQ3E31KVCWX3K5gO3oDr-U_HgbObPzo,30006
|
|
19
|
+
utils.py,sha256=YI73dNbRnM2mLl-0jGJukUmWLiIvdUqM3uIX6dnHOdA,3658
|
|
20
|
+
commands/__init__.py,sha256=YETQJBFAkefRCNHqXTa67blS0g8766ApVcg6OhG81oE,1252
|
|
21
|
+
opencode_db-0.3.0.dist-info/licenses/LICENSE,sha256=NhUr8ufTYb5u9F08J5bmDQ0xTVmCOexoq1ki_RdCqys,1210
|
|
22
|
+
opencode_db-0.3.0.dist-info/METADATA,sha256=2xT2S7eTV30z1wAvQB-PUg1EOblFZc4expdXh1dbzlQ,209
|
|
23
|
+
opencode_db-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
24
|
+
opencode_db-0.3.0.dist-info/entry_points.txt,sha256=dMHc-RWkGEu0mTNvHHThSEKQdGbCdwdKUCO-cLF7TCw,41
|
|
25
|
+
opencode_db-0.3.0.dist-info/top_level.txt,sha256=lKPHa0NndvZjjoZ5mIatAdvtFlPA3i-Fm_tEl3JvgTg,178
|
|
26
|
+
opencode_db-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vasilev Stas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
© 2026 Vasilev Stas — код можно свободно использовать, изменять и распространять
|