Metadata-Version: 2.5
Name: ktalk-cli
Version: 4.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. Годится и как
самостоятельный инструмент, и как предусловие плагина Claude Code `ktalk` — подробнее в
разделе «Пакет и плагин Claude Code» ниже.

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

Умеет:
- Список записей конференций и детали одной записи.
- Транскрипты (речь по спикерам с таймкодами, с чанкингом для длинных).
- Саммари и протоколы встреч.
- Полный состав участников записи (обходит лимит в 6 из списковых ответов).
- Скачивание видеофайла записи.
- Историю чата встречи.
- Конфигурацию комнаты и календарь запланированных встреч.
- Предпросмотр и создание новой встречи — создание требует интерактивного
  терминала и явного подтверждения, см. «Планирование встречи» ниже.
- Диагностику авторизации — жив ли токен и почему запрос не проходит.
- Операционный реестр обработки записей на 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
```

**Проверка версии** — после установки или обновления:

```bash
ktalk --version    # печатает, например: ktalk-cli 2.1.0
```

**Обновление** до последней версии — та же команда `install`, только `upgrade`:

```bash
uv tool upgrade ktalk-cli
```

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

**С версии 4.0.0 конфигурация каждого значения имеет ровно один источник**
(ADR-027): session token — только файл, записанный `ktalk token set`; адрес
стенда — только `config.toml`, записанный `ktalk config set base-url`. Ни одна
переменная `KTALK_*` и ни один `.env` в рабочем каталоге больше не читаются как
источник значения — их присутствие не проходит молча: `ktalk auth-status`,
`ktalk doctor` и тексты отказов называют обнаруженную снятую переменную по
имени (значение не печатается никогда) и советуют её убрать.

**С версии 3.0.0 CLI работает только через session token** (кука браузера) — режим
персонального API-ключа (`KTALK_PERSONAL_API_KEY`) снят целиком (ADR-025): он
конкурировал с сессией молча (при обеих заданных переменных побеждал ключ без
объяснения в тексте отказа) и диагностика `auth-status` объявляла заведомо
невалидный ключ «валидным» на 403.

**Цена снятия для тех, кто держал постоянный ключ:** персональный ключ не протухал
без предупреждения, session token — протухает. Постоянная работа теперь требует
ручного обновления токена по мере его протухания (`ktalk token set -`, см. ниже) —
это не восстанавливается автоматически снятием ключа.

Переменные `KTALK_PERSONAL_API_KEY`, `KTALK_SESSION_TOKEN` и остальные
`KTALK_*`, если они всё ещё заданы в окружении (частая причина — блок `env` в
`~/.claude/settings.json`), не читаются ни на одном шаге — CLI печатает об этом
одно предупреждение на stderr при каждом вызове и продолжает работу на файле
токена/`config.toml`.

### Session token

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

**Два шага.** На вкладке, где вы залогинены в `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     # жива ли авторизация — реальный запрос, не имитация
```

Путь файла — `${XDG_CONFIG_HOME:-~/.config}/ktalk-mcp/token`; переменной-
переопределения нет (снятая `KTALK_TOKEN_FILE` не читается, ADR-027).

**Единственный источник токена** — этот файл (ADR-027). Переменная
`KTALK_SESSION_TOKEN` и `.env` в рабочем каталоге не читаются вовсе: заданная
переменная больше не перекрывает файл ни молча, ни с предупреждением — она
просто не участвует в разрешении credential, а CLI называет её в выводе
`auth-status`/`doctor` как снятую.

> Ротация больше не может «застрять» на невидимой переменной: `ktalk token set -`
> перезаписывает файл, и следующий же вызов читает новое значение. Разводка
> источников из старых установок (переменная против файла, рунбук
> [OPS-003](content/70-operations/OPS-003-env-var-shadows-token-file.md)) снята
> самим устройством 4.0.0 — переменную достаточно убрать из окружения.

Ни один запрос не несёт заголовок `X-Auth-Token` — единственный транспорт credential
теперь query-параметр `sessionToken`.

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

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

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

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

### Конфигурация: адрес стенда (`config.toml`)

Адрес контура задаётся один раз, машинной командой:

```bash
ktalk config set base-url https://your-domain.ktalk.ru
```

Команда пишет `${XDG_CONFIG_HOME:-~/.config}/ktalk-mcp/config.toml` (рядом с
файлом токена, права `0644` — адрес не секрет) и отвергает значение без схемы
http(s) или без хоста до записи. Проверка — `ktalk config show`: секция
`## Толк` называет адрес, путь `config.toml` и статус файла токена.

**Переменные окружения и `.env` больше не читаются вовсе (4.0.0, ADR-027).**
`KTALK_BASE_URL`, `KTALK_SESSION_TOKEN`, `KTALK_PERSONAL_API_KEY`,
`KTALK_REGISTRY_DB`, `KTALK_TOKEN_FILE` и `.env` в рабочем каталоге не
участвуют в разрешении ни одного значения. Если какая-то из них всё ещё задана,
`ktalk auth-status` и `ktalk doctor` называют её по имени и советуют убрать —
значение при этом не печатается никогда.

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

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

```bash
ktalk auth-status
```

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

- **401** — токен невалиден либо истёк. Вердикт `alive: false`, код возврата `1`.
  Обновите токен: `ktalk token set -` — файл перезаписывается, права переставлять
  не нужно.
- **403** — токен рабочий, но у текущей сессии нет прав на эту операцию. Вердикт
  `alive: true`, код возврата `0`: нехватка прав не является отказом токена, и
  перевыпускать его не нужно.

У session token понятия scope и срока действия нет — диагностика выполняет реальный
пробный запрос (список записей), а не имитацию без сети.

`--json`-ответ — `{"alive": bool, "note": str | None}`. Отказ пробного запроса виден
по обоим каналам сразу: поле `alive: false` в теле ответа И ненулевой код возврата
процесса — полагаться только на один из двух нельзя.

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

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

### Коды возврата

| Код | Значение |
|---|---|
| `0` | Успех. |
| `1` | Отказ вызова — сеть, сервер, конфигурация. |
| `2` | Usage error — неверные аргументы CLI (`argparse`). |
| `3` | Только `ktalk get-transcript`. Данные получены и напечатаны полностью, но независимая сверка идентичности не сошлась (`identity_check.result == "mismatch"`) — состав участников транскрипта разошёлся с составом записи. Это не сбой команды: код 3 отличает «данные есть, но сверка не сошлась» от `0` (сошлось или не проверялось) и от `1`/`2` (данных нет вовсе). Подробности — в самом теле ответа, поле `identity_check` (ADR-024 §Д1). |

| Команда | Назначение |
|---|---|
| `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 токенов). Независимая сверка идентичности включена по умолчанию (`--no-verify-identity` отключает); `--chunk` вне диапазона сверку по сети не запускает вовсе, `identity_check.result == "not_checked"`/`reason: "chunk_out_of_range"`. |
| `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]` | Архив встреч за период. **Недоступна** — архив никогда не имел рабочего пути под session token; команда отказывает до сети с явным сообщением на каждый вызов (ADR-025). |
| `ktalk get-chat-messages [--recording-key K \| --conference-key K] [--channel C]` | Сообщения чата встречи; один из двух ключей обязателен. Канал не указан — определяется автоматически. |
| `ktalk get-room <room_name>` | Конфигурация комнаты — политики аудио/видео/демонстрации, модераторы, SIP, чат, маскирование. **Побочный эффект:** если комнаты с таким именем ещё нет, она создаётся. |
| `ktalk list-calendar --start ISO --end ISO [--room-name N]` | Встречи за окно дат, видимые активной авторизации — это не «ваш личный календарь», а всё, что видит текущая авторизация, включая чужие встречи. Сервер лимитирует один запрос семью днями и сотней встреч на сегмент — команда сама режет произвольное окно на сегменты; при упоре в потолок ответ предупреждает о возможно неполной выдаче. |

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

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

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

**Ни одно поле не имеет значения по умолчанию** (кроме описания встречи — пустая
строка, если не задано). Тема, начало, конец, часовой пояс, комната, участники,
анонимный доступ, 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 token) режим авторизации
(см. «Авторизация» выше) — query-параметр `sessionToken`, внутренний недокументированный
контур API:

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

Архив встреч (`list-archive`) недоступен: под session token у него нет и никогда не
было рабочего пути (ADR-025) — команда отказывает до сети с явным сообщением.

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

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

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

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

Путь к базе: флаг `--db PATH` > `registry.db_path` из `.ktalk.toml`
проекта-хозяина > машинный дефолт централизованного хранилища (ADR-013;
переменная `KTALK_REGISTRY_DB` снята в 4.0.0 и не читается, ADR-027).

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

| Команда | Назначение |
|---|---|
| `ktalk sync [--days N] [--json] [--dry-run]` | Загрузить записи из KTalk и upsert'нуть их в реестр (новые — `new`, существующие — с обновлёнными не-статусными полями; статус ни одной записи не меняется). Без `--days` окно — от момента последней синхронизации минус 1 день запаса (первый запуск — 90 дней); `--days N` — явное переопределение нижней границы. Идемпотентно. `--dry-run` — сверить id с реестром без записи, ничего не пишет. В `--json`-ответе ключа `expired` больше нет (4.0.0): смены статуса как побочного эффекта чтения не происходит. |
| `ktalk token set <значение\|->` | Записать session-токен в `~/.config/ktalk-mcp/token` (`0600`). `-` — прочитать из stdin: `pbpaste \| ktalk token set -`. Значение не печатается. |
| `ktalk token status [--json]` | Есть ли файл токена, его права и маска значения. |
| `ktalk auth-status [--json]` | Диагностика активной авторизации — жив ли токен. См. «Диагностика авторизации». |
| `ktalk config set base-url <url>` | Записать адрес стенда в `~/.config/ktalk-mcp/config.toml` (`0644`). См. «Конфигурация: адрес стенда». |
| `ktalk config show [--json]` | Машинная конфигурация оператора (адрес, путь `config.toml`, статус файла токена) и резолвленный `.ktalk.toml` проекта-хозяина. |
| `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 (токен и адрес — файлами, см. «Авторизация»)
pbpaste | uv run ktalk token set -
uv run ktalk config set base-url https://your-domain.ktalk.ru
uv run ktalk auth-status
```

## Пакет и плагин Claude Code

`ktalk-cli` работает и сам по себе, и как предусловие плагина Claude Code `ktalk`. Плагин не
обращается к KTalk напрямую и не поднимает MCP-сервер — он вызывает эту же команду `ktalk`
как единственную точку входа в контур.

Плагин пинует точную версию пакета (не нижний порог: «ровно эта версия», не «эта или новее») в
собственном файле совместимости. Если что-то в интеграции с плагином ведёт себя не так, как
описано в его документации, — первым делом сверьте версию:

```bash
ktalk --version    # см. «Проверка версии» в разделе «Установка»
```

Версия не совпадает с той, что требует плагин, — обновите пакет тем же способом, что при
установке (`uv tool upgrade ktalk-cli`, см. «Установка»); не совпадает в другую сторону
(пакет новее, чем ожидает плагин) — не откатывайте его самостоятельно, сверьтесь с тем, кто
настраивал плагин.

## Проблемы и вопросы

Нашли баг, некорректное поведение или неточность в документации — заведите issue в этом
репозитории: https://github.com/mdemyanov/ktalk-cli/issues.

## Лицензия

MIT
