Metadata-Version: 2.5
Name: ktalk-cli
Version: 2.0.0
Summary: CLI for accessing Kontur Talk (KTalk) recordings, transcripts and summaries
Project-URL: Homepage, https://github.com/mdemyanov/ktalk-cli
Project-URL: Repository, https://github.com/mdemyanov/ktalk-cli
Project-URL: Issues, https://github.com/mdemyanov/ktalk-cli/issues
Author-email: Maksim Demyanov <mdemyanov@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: cli,kontur,ktalk,recordings,transcripts
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Conferencing
Requires-Python: >=3.12
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic-settings>=2.0.0
Description-Content-Type: text/markdown

# ktalk-cli

[![PyPI](https://img.shields.io/pypi/v/ktalk-cli)](https://pypi.org/project/ktalk-cli/)
[![Python](https://img.shields.io/pypi/pyversions/ktalk-cli)](https://pypi.org/project/ktalk-cli/)

CLI `ktalk` для доступа к записям [Контур.Толк](https://ktalk.ru) (KTalk) — чтение
записей, транскриптов и саммари, работа с расписанием, операционный реестр
обработки записей на SQLite.

> Раньше пакет назывался `ktalk-mcp` и, помимо CLI, поднимал MCP-сервер для
> Claude Code (инструменты вида `ktalk_list_recordings`). Этот слой снят
> целиком — MCP в пакете больше нет, единственная точка входа — команда
> `ktalk`. Нужна интеграция с Claude Code — отдельный плагин `ktalk` вызывает
> эту же CLI напрямую, без MCP-сервера.

Умеет:
- Список записей конференций и детали одной записи.
- Транскрипты (речь по спикерам с таймкодами, с чанкингом для длинных).
- Саммари и протоколы встреч.
- Полный состав участников записи (обходит лимит в 6 из списковых ответов).
- Скачивание видеофайла записи.
- Архив встреч и историю чата (только с персональным API-ключом).
- Конфигурацию комнаты и календарь запланированных встреч (только с session token).
- Предпросмотр и создание новой встречи — создание требует интерактивного
  терминала и явного подтверждения, см. «Планирование встречи» ниже.
- Диагностику авторизации — какой ключ/токен активен и почему запрос не проходит.
- Операционный реестр обработки записей на SQLite — синхронизация, статусы,
  markdown-зеркало для git, см. «Реестр записей» ниже.

## Установка

Требуется Python 3.12+ и [uv](https://docs.astral.sh/uv/).

```bash
uv tool install ktalk-cli
```

Или через pip:

```bash
pip install ktalk-cli
```

**Если на машине уже стоит старый `ktalk-mcp`** (он тоже владел командой
`ktalk`), `uv tool install ktalk-cli` откажет: `uv` не отдаёт занятое имя
команды второму пакету молча. Сначала освободите имя:

```bash
uv tool uninstall ktalk-mcp
uv tool install ktalk-cli
```

## Авторизация

CLI поддерживает два способа авторизации: session token (кука браузера) и
персональный API-ключ. Способы исключают друг друга: если задать обе переменные,
побеждает `KTALK_PERSONAL_API_KEY` — `KTALK_SESSION_TOKEN` в этом случае вообще не
читается. Не задать ни один — команда завершится понятной ошибкой.

Персональный API-ключ не привязан к браузерной сессии и не протухает без предупреждения,
в отличие от session token. Берите его, если нужна стабильная работа без ручного
обновления, а не только разовый запрос.

### Session token

Session token — токен вашей браузерной сессии Толка. Быстрый способ начать, но
токен живёт недолго и протухает без предупреждения — при регулярном
использовании удобнее персональный API-ключ (ниже).

**Два шага.** На вкладке, где вы залогинены в `https://your-domain.ktalk.ru`, откройте
DevTools (`F12`, или `Cmd+Option+I` на Mac) → **Console** и выполните:

```js
copy(JSON.parse(localStorage.session).data.token)
```

Токен — в буфере обмена. Положите его в файл одной командой:

```bash
pbpaste | ktalk token set -          # macOS
xclip -o | ktalk token set -         # Linux (X11)
```

Команда сама создаёт `~/.config/ktalk-mcp/token` с правами `0600` (каталог — `0700`),
отвергает значение, не похожее на токен, и никогда не печатает его в вывод. Проверка:

```bash
ktalk token status    # есть ли файл, права, маска значения
ktalk auth-status     # жива ли авторизация — реальный запрос, не имитация
```

Путь переопределяется переменной `KTALK_TOKEN_FILE`; каталог уважает `XDG_CONFIG_HOME`.

**Порядок источников** — первый непустой выигрывает:

| # | Источник | Комментарий |
|---|---|---|
| 1 | `KTALK_PERSONAL_API_KEY` | режим персонального ключа, сессия дальше не читается |
| 2 | `KTALK_SESSION_TOKEN` (окружение или `.env` в рабочей директории) | заданное явно сильнее лежащего на диске |
| 3 | `~/.config/ktalk-mcp/token` | дефолтный путь для повседневной работы |

> Путь `~/.config/ktalk-mcp/token` не переименован вместе с пакетом и остаётся
> таким намеренно: он выбран независимо от имени дистрибутива (каталог
> `ktalk/` уже занят другим — санкцией на запись, у неё свой жизненный цикл),
> а смена пути молча лишила бы уже настроенные машины третьего источника
> авторизации.

Токен из файла обслуживает и чтение, и запись: создание и отмена встречи шлют то же
значение другим транспортом (заголовок `Authorization: Session`, а не query-параметр) —
источник значения транспорт не меняет. Санкция на запись при этом остаётся обязательной,
она к токену отношения не имеет.

Файл с правами шире `0600` читается так, будто его нет (`ktalk token status` покажет
`usable: False`) — секрет не должен молча читаться с диска, доступного другим
пользователям машины.

> **Важно:** session token имеет ограниченный срок жизни. Если команда возвращает
> ошибку авторизации, повторите те же два шага — `ktalk token set -` перезаписывает
> файл, права переставлять не нужно.

### Персональный API-ключ

Персональный API-ключ выдаётся в админке Толка на конкретного пользователя на
настраиваемый срок и не зависит от того, открыт ли браузер. Передаётся заголовком
`X-Auth-Token`, а не в URL — секрет не попадает в query-параметры и логи веб-сервера.

Выпускается и ротируется в разделе **Управление → API-ключи** админки Толка (UI-шаг,
CLI-эквивалента нет; экранные шаги здесь не расписываем — актуальный порядок действий
смотрите в справке Контура:
[«Персональный API-ключ доступа в Толке»](https://support.kontur.ru/talk/86797)).
Значение ключа показывается один раз в течение часа после создания — не скопировали
вовремя, придётся выпускать новый.

**Не путайте с ключом пространства.** В Толке есть второй, отдельный ключ —
пространственный, с заголовком `X-API-Key`, выдаётся не на пользователя, а на всё
пространство целиком. `ktalk-cli` работает только с персональным ключом
(`X-Auth-Token`); ключ пространства не поддерживается — переменная называется
`KTALK_PERSONAL_API_KEY`, а не `KTALK_API_KEY`, намеренно, чтобы их не перепутать.

При выпуске ключа в админке выбираются права (scope). Не хватает прав — запрос вернёт
403, и по виду это неотличимо от «ключ невалиден», хотя ключ рабочий (подробнее —
«Диагностика авторизации» ниже).

| Право (scope) | Даёт доступ к |
|---|---|
| `application.recording.read` | Список записей, детали, транскрипт, саммари, скачивание файла, участники |
| `application.reporting.read` | Архив встреч, чат встречи, отчёты по участникам |
| `application.applications.read` | Опционально. Без него `ktalk auth-status` не покажет состав прав и срок действия ключа — только «ключ живой / не живой» |

> **Если реестр `ktalk` уже накопил записи в session-режиме,** перед первым `ktalk sync`
> после переключения на персональный ключ обязательно выполните `ktalk sync --dry-run`.
> Внутренний и официальный контуры API отдают идентификаторы записей по-разному, и без
> сверки первый боевой sync под ключом рискует задвоить весь реестр. Команда только
> сверяет id и ничего не пишет — см. таблицу команд реестра ниже.

### Переменные окружения

```bash
export KTALK_PERSONAL_API_KEY="ваш_персональный_api_ключ"
# или
export KTALK_SESSION_TOKEN="ваш_session_token"
export KTALK_BASE_URL="https://your-domain.ktalk.ru"
```

Для session-режима переменная не обязательна: без неё читается файл
`~/.config/ktalk-mcp/token` (см. «Session token»).

Также поддерживается файл `.env` в рабочей директории:

```env
KTALK_PERSONAL_API_KEY=ваш_персональный_api_ключ
KTALK_BASE_URL=https://your-domain.ktalk.ru
```

## Диагностика авторизации

Проверьте авторизацию без запроса записей:

```bash
ktalk auth-status
```

Диагностика различает два случая, которые снаружи выглядят одинаково — просто ошибка, —
но чинятся по-разному:

- **401** — ключ или токен невалиден либо истёк. Перевыпустите его.
- **403** — ключ рабочий, но конкретному запросу не хватает прав (scope). Отредактируйте
  права ключа в админке Толка (см. таблицу в разделе «Персональный API-ключ» выше) —
  перевыпускать ключ не нужно.

У session token понятия scope нет — диагностика в этом режиме пробным запросом списка
записей сообщает только «токен работает / не работает», без прав и срока действия.

Режим ключа не проверен полностью на боевом окружении — команда описывает задуманное
поведение, а не гарантию для любого ключа.

## Команды чтения записей и справочников

Все команды поддерживают `--json` (валидный JSON в stdout; ошибки — в stderr с
ненулевым кодом возврата).

| Команда | Назначение |
|---|---|
| `ktalk list-recordings [--query Q] [--start-from ISO] [--start-to ISO] [--top N] [--order O] [--page-token T]` | Список записей. `--top` 1–1000 (по умолчанию 30); `--order`: `byTimeNewFirst` (умолчание), `byTimeOldFirst`, `byTitle`, `bySizeBigFirst`, `bySizeSmallFirst`. |
| `ktalk get-recording <recording_key>` | Детали записи — автор, дата, длительность, участники (список ограничен 6, полный состав — `get-participants`). |
| `ktalk get-transcript <recording_key> [--chunk N] [--chunk-size N]` | Транскрипт по спикерам с таймкодами. Длинный транскрипт режется на чанки по границам реплик: `--chunk 0` (умолчание) — целиком или первый чанк; `--chunk-size` — макс. символов в чанке (умолчание 30000, ~7500 токенов). |
| `ktalk get-summary <recording_key>` | Полное саммари (краткое резюме + протокол). |
| `ktalk get-summary-type <recording_key> --type shortSummary\|protocol` | Саммари одного типа. |
| `ktalk get-participants <recording_key>` | Полный состав участников, включая анонимных — обходит лимит в 6, который отдают `get-recording`/`list-recordings`. |
| `ktalk download-recording <recording_key> --target PATH [--quality Q]` | Скачивает видеофайл потоково, без буферизации в памяти. Существующий файл не перезаписывается; `--quality` не указано — берётся дефолт для записи (например `900p`). |
| `ktalk list-archive --from ISO --to ISO [--room-name N]` | Архив встреч за период. Только режим персонального ключа (право `application.reporting.read`). Читает всё окно на клиенте, без постраничного чтения. |
| `ktalk get-chat-messages [--recording-key K \| --conference-key K] [--channel C]` | Сообщения чата встречи; один из двух ключей обязателен. Только режим персонального ключа. Канал не указан — определяется автоматически. |
| `ktalk get-room <room_name>` | Конфигурация комнаты — политики аудио/видео/демонстрации, модераторы, SIP, чат, маскирование. Только режим session token. **Побочный эффект:** если комнаты с таким именем ещё нет, она создаётся. |
| `ktalk list-calendar --start ISO --end ISO [--room-name N]` | Встречи за окно дат, видимые активной авторизации — это не «ваш личный календарь», а всё, что видит текущая авторизация, включая чужие встречи. Только режим session token. Сервер лимитирует один запрос семью днями и сотней встреч на сегмент — команда сама режет произвольное окно на сегменты; при упоре в потолок ответ предупреждает о возможно неполной выдаче. |

## Планирование встречи

Создание встречи — единственная операция пакета, которая что-то меняет вне вашего
компьютера: она рассылает приглашения реальным людям. Удаление созданного события
эти письма не отзывает. Из-за этого создание устроено умышленно неудобно:

- Создание — команда `ktalk create-meeting-confirm`. Она работает только в
  интерактивном терминале (проверяет, что и ввод, и вывод — реальный TTY) и
  перед отправкой печатает предпросмотр и требует набрать слово `да`.
- Предпросмотр без создания — `ktalk create-meeting-preview`, не делает ни
  одного сетевого запроса.
- Обе команды работают только в режиме session token — в режиме персонального
  ключа создание встречи не подтверждено ни разу и потому отключено.

**Ни одно поле не имеет значения по умолчанию** (кроме описания встречи — пустая
строка, если не задано). Тема, начало, конец, часовой пояс, комната, участники,
анонимный доступ, PIN — каждое нужно передать явно; иначе команда откажет и назовёт,
какого поля не хватает. Так сделано намеренно: молчаливый часовой пояс сдвинет
встречу в календаре участников на другое время, а молчаливая автозапись незаметно
для организатора изменит, записывается ли встреча.

Из этого вытекают практические следствия:

- Часовой пояс принимает только форму `GMT±N` (пример `GMT+3`) — IANA-имена вида
  `Europe/Moscow`, смещения ISO и аббревиатуры сервер не распознаёт.
- `--enable-auto-recording` и `--allow-anonymous` принимают только явные `true`
  или `false` — «флаг просто не указан» не считается ответом.
- «Встреча без обязательных участников» — это отдельный флаг
  `--no-required-attendees`, а не просто отсутствие `--required-attendee-key`.
  Значение `--required-attendee-key` — числовой id участника, не логин.
- «Без PIN» — отдельный флаг `--no-pin-code`, а не пустая строка в `--pin-code`.
- `--anonymous-access-expiration` обязателен, только если `--allow-anonymous true`.

Повторяющиеся встречи в этой версии не поддерживаются — можно создать только
разовое событие.

При сетевом сбое во время создания команда не повторяет запрос сама: если сеть
оборвалась, неизвестно, ушло приглашение или нет, и автоматический повтор рискует
создать дубль. Решение о повторной попытке — за вами; перед ней стоит проверить
`ktalk list-calendar`, не появилась ли встреча уже.

Создание встречи ещё ни разу не выполнялось на боевом окружении — команда
реализует задуманное поведение, но не проверена живым вызовом.

```bash
# Предпросмотр — без сети, без побочных эффектов
ktalk create-meeting-preview \
  --subject "Синк по проекту" \
  --start 2026-08-20T10:00:00 --end 2026-08-20T10:30:00 --timezone GMT+3 \
  --room-name "Переговорная 1" \
  --no-required-attendees \
  --enable-auto-recording false --allow-anonymous false \
  --no-pin-code

# Создание — только в интерактивном терминале, требует ввода "да"
ktalk create-meeting-confirm \
  --subject "Синк по проекту" \
  --start 2026-08-20T10:00:00 --end 2026-08-20T10:30:00 --timezone GMT+3 \
  --room-name "Переговорная 1" \
  --required-attendee-key 123 --required-attendee-key 456 \
  --enable-auto-recording false --allow-anonymous false \
  --no-pin-code
```

## API

CLI работает с KTalk Web API. Набор путей, которые вызывает клиент, зависит от
активного режима авторизации (см. «Авторизация» выше):

- **Session-режим** — авторизация query-параметром `sessionToken`, используется
  внутренний контур API.
- **Режим персонального ключа** — авторизация заголовком `X-Auth-Token`, используются
  официальные пути интеграторского API (`talk.public.api-api-2.json`).

Транскрипт и саммари используют один и тот же путь в обоих режимах:

| Эндпоинт | Описание |
|----------|----------|
| `GET /api/recordings/{id}/transcript` | Транскрипт |
| `GET /api/recordings/v2/{id}/summary` | Полное саммари (v2) |
| `GET /api/recordings/{id}/summary/{type}` | Саммари по типу |

Список записей и детали записи используют разные пути в session- и api-key-режимах.
Архив встреч, чат, полный состав участников, скачивание файла и диагностика ключа
доступны только в режиме персонального ключа (нужные права — в таблице раздела
«Персональный API-ключ» выше).

Комната, календарь и создание встречи работают только в режиме session token — в
режиме персонального ключа эти операции отказывают осознанно, а не по случайному
пробелу: путь на api-key либо не подтверждён вовсе, либо ведёт себя необъяснимо
непоследовательно при проверке.

> OpenAPI спецификация `talk.public.api-api-2.json` включена как справочник, но содержит расхождения с реальным API (пути, формат авторизации, структура ответов).

## Реестр записей (`ktalk`)

Та же команда `ktalk` ведёт операционный реестр обработки записей на SQLite.
Вся детерминированная механика (синхронизация списка записей, дедуп,
экспирация, смена статусов, рендер дашборда и markdown-зеркала, разовая
миграция) живёт в коде, а не в рассуждениях модели.

**SQLite — операционный source of truth.** Markdown-файл `registry.md` —
генерируемое read-only зеркало для git (`ktalk export`), руками не редактируется.

Путь к базе: флаг `--db PATH` > переменная `KTALK_REGISTRY_DB` > дефолт
`95_TRANSCRIPTS/.registry.db` (относительно текущего каталога). Бинарную БД
нужно добавить в `.gitignore` (`.registry.db`, `.registry.db-wal`, `.registry.db-shm`).

`ktalk auth-status`, `ktalk create-meeting-preview` и `ktalk create-meeting-confirm`
реестр не открывают вовсе — им он не нужен. В частности, `auth-status` работает
даже если файла базы данных нет или он недоступен. Планирование встречи —
отдельный раздел «Планирование встречи» выше.

| Команда | Назначение |
|---|---|
| `ktalk sync [--days 7] [--json] [--dry-run]` | Загрузить записи из KTalk, upsert новых (`new`), экспирировать `new` старше N дней → `skipped`, показать дашборд. Идемпотентно. `--dry-run` — сверить id с реестром без записи, ничего не пишет (обязателен перед первым `sync` в режиме персонального ключа — см. «Персональный API-ключ»). |
| `ktalk token set <значение\|->` | Записать session-токен в `~/.config/ktalk-mcp/token` (`0600`). `-` — прочитать из stdin: `pbpaste \| ktalk token set -`. Значение не печатается. |
| `ktalk token status [--json]` | Есть ли файл токена, его права и маска значения. |
| `ktalk auth-status [--json]` | Диагностика активной авторизации — жив ли ключ/токен, какие права у ключа. См. «Диагностика авторизации». |
| `ktalk dashboard [--json]` | Дашборд: новые записи, статистика по статусам. |
| `ktalk list [--status S] [--json]` | Список записей с фильтром по статусу. |
| `ktalk show <id> [--json]` | Детали записи: участники, статус, пути, длительность. |
| `ktalk mark-processing <id>` | Перевести в `processing`. |
| `ktalk mark-done <id> --transcript P --protocol P [--type T]` | Завершить, проставить пути и `processed_at`. |
| `ktalk mark-partial <id> [--transcript P] [--protocol P]` | Частичная обработка. |
| `ktalk mark-skipped <id>` | Пропустить вручную. |
| `ktalk set-vault-id <id> <ktalk_id> <vault_id>` | Привязать профиль к участнику. |
| `ktalk export [--out PATH] [--full]` | Сгенерировать markdown-зеркало. |
| `ktalk migrate <vault> [--dry-run] [--json]` | Разовый импорт из markdown-реестров. |

Несколько фоновых агентов могут безопасно писать параллельно (WAL + `busy_timeout`
+ транзакция на операцию).

## Разработка

```bash
git clone https://github.com/mdemyanov/ktalk-cli.git
cd ktalk-cli
uv sync

# Запуск тестов
uv run pytest -v

# Линтинг
uv run ruff check .

# Локальный запуск CLI (session token или KTALK_PERSONAL_API_KEY — см. «Авторизация»)
KTALK_SESSION_TOKEN=... KTALK_BASE_URL=... uv run ktalk auth-status
```

## Лицензия

MIT
