Справочник HTTP API

Демон ccmux предоставляет HTTP и JSON API для листинга сеансов, чтения данных проекта и создания удаленных клиентов. В этом справочнике описывается настройка соединения, модель доверия, конечные точки и типы ответов.

Маршруты и типы сохраняются в ccmux source. Старые версии демонов могут не поддерживать все описанные здесь конечные точки; проверьте конечную точку работоспособности для работающей версии.

Транспорт и доступность

Одни и те же маршруты обслуживаются двумя транспортами:

Транспорт Адрес По умолчанию
Локальный сокет Unix ~/.local/state/ccmux/ccmuxd.sock (режим 0600) всегда включен
tailnet HTTP http://TAILNET-IPv4:PORT (порт по умолчанию 7474) выкл.

Чтобы связаться с демоном с другого устройства, на демон хост: установлено daemon.listen_tailnet = true (необязательно daemon.tailnet_port) в ~/.config/ccmux/config.toml и перезапустите ccmuxd. Адрес привязки — это адрес хоста tailscale ip -4. Если Tailscale не запущен, прослушиватель tailnet не запускается автоматически (обслуживается только сокет Unix).

Модель аутентификации и доверия — прочтите это в первую очередь

На HTTP API нет аутентификации на уровне приложения. Ни токена носителя, ни ключа API, ни подписи каждого запроса, ни списка разрешенных IP-адресов. Граница доверия ваша tailnet Tailscale: любой хост, который может связаться с демоном. 100.x.x.x:7474 может вызывать любую конечную точку — список/создать/уничтожить/переименовать/отправить ключи, прикрепить к полностью интерактивному терминалуи читайте заметки/разговоры/использование.

Защитите это с помощью Tailscale ACL, а не аутентификация на уровне приложения. Считайте все, что может маршрутизироваться к IP-адресу tailnet демона, полностью доверенным.

Две конечные точки проверяют каждый запрос и только для начальной загрузки устройства Trust + Push, а не для защиты API:

Форма ошибки

Ошибки обычный текст (text/plain): однострочное сообщение, нет конверт JSON. Проверьте код состоянияи прочитайте тело как строку человеческого сообщения. Успешные ответы JSON: application/json.

Код Значение
200 ОК (тело JSON)
204 ОК, нет тела (уничтожить/отправить ключи/регистрировать устройство)
400 неправильный ввод/сбой проверки
401 недействительный или просроченный токен сопряжения (/v1/pair только)
404 не найдено - или этот демон предшествует конечной точке
405 неправильный метод HTTP
500 сервер / tmux / сбой платформы
503 функция недоступна (например. /v1/pair-token с listen_tailnet выкл.)

API развивается добавление Поля JSON (во всех необязательных полях используются omitempty) и добавление маршруты. лечить 404 на всей конечной точке как «этот демон старше этой функции» и плавно деградировать.

Сводка конечных точек

Метод Путь Ответ
GET /v1/health HealthInfo
GET /v1/peers []PeerInfo
GET /v1/sessions []SessionState
POST /v1/sessions SessionState (создать или прикрепить)
POST /v1/sessions/bare NewBareSessionResponse
POST /v1/sessions/NAME/kill 204
POST /v1/sessions/NAME/rename SessionState
POST /v1/sessions/NAME/send-keys 204
GET /v1/sessions/NAME/preview PreviewResponse
GET /v1/sessions/NAME/attach WebSocket (интерактивный PTY)
GET /v1/projects []ProjectInfo
POST /v1/projects NewProjectResponse
GET /v1/conversations []Conversation
GET /v1/usage?window=… AgentUsage
GET /v1/notes?project=…[&file=…] []NoteEntry или NoteContent
GET /v1/notes/search?project=…&q=… []SearchHit
GET /v1/events SSE-поток SessionEvent
POST /v1/pair-token (только Unix) PairTokenResponse
POST /v1/pair PairResponse
POST /v1/devices 204
POST /v1/devices/test 204

(Там, где указан путь NAME это сегмент пути имени сеанса tmux, т.е. /v1/sessions/{name}/kill.)

Подробности конечной точки

Сессии

Интерактивный терминал — GET /v1/sessions/NAME/attach (Веб-сокет)

Обновление до WebSocket, подключенного к реальному tmux attach-session в PTY: настоящий интерактивный терминал (живой вывод, ввод, изменение размера) без ssh/мош. Использование coder/websocket кадр.

Начальный размер PTY 80x24 до первого изменения размера. Сервер пингует каждые 25 секунд с крайним сроком в 10 секунд — отвечайте на запросы или ожидайте отключения. InsecureSkipVerify установлен (нет Origin проверьте). Только закрытие сокета отсоединяется; Сеанс tmux продолжает работать. Предпочитаю это опросу /preview для интерактивного просмотра.

Проекты

Разговоры, использование, заметки

Постоянные обновления — GET /v1/events (ССЕ)

text/event-stream. Каждый data: рама JSON SessionEvent; kind — это created / killed / state_change / needs_input. Сердцебиение: : connected при открытии, : ping каждые 20 секунд (строки комментариев начинаются с : игнорируются). Ан event: drops / data: N фрейм означает, что вы пропустили N событий и должны выполнить повторную выборку /v1/sessions для повторной синхронизации.

Здоровье и открытия

Сопряжение и отправка (мобильное устройство)

Дополнительный процесс для встроенной отправки (APNs/iOS, FCM/Android) и установки ключа SSH, чтобы устройство могло ssh/mosh прикрепляю. Нет требуется для использования конечных точек чтения/действия через tailnet.

Вызывает огонь при двух переходах: вход в сеанс needs_inputи active → idle («агент закончил работу»). Идентификатор сеанса push-уведомления: local/SESSIONNAME. APN и FCM по умолчанию отключено и требуется настройка на стороне сервера; Маршрутизация FCM существует, но доставка Android еще не подключена.

Типы

Скопировано из internal/daemon/protocol.go (структуры Go с их тегами JSON).

type SessionState struct {
	Name        string    `json:"name"`
	Host        string    `json:"host"`         // "local" or a remote host name
	Project     string    `json:"project"`
	Path        string    `json:"path"`         // session working directory
	State       string    `json:"state"`        // active|idle|needs_input|error|unknown
	Attached    bool      `json:"attached"`
	Windows     int       `json:"windows"`
	Created     time.Time `json:"created"`
	LastChange  time.Time `json:"last_change"`
	PromptCount int       `json:"prompt_count"`
	Agent       string    `json:"agent,omitempty"`
}

type HealthInfo struct {
	OK        bool   `json:"ok"`
	Hostname  string `json:"hostname"`
	Version   string `json:"version"`
	Sessions  int    `json:"sessions"`
	SleepMode string `json:"sleep_mode"` // off|safe|dangerous|very_dangerous
}

type SessionEvent struct {
	At      time.Time    `json:"at"`
	Kind    string       `json:"kind"` // state_change|created|killed|needs_input
	Session SessionState `json:"session"`
}

type NewSessionRequest struct {
	Project  string `json:"project"`
	Path     string `json:"path"`     // defaults to PROJECTS_ROOT/PROJECT
	Continue bool   `json:"continue"` // start the agent with --continue
	Name     string `json:"name,omitempty"`
	Agent    string `json:"agent,omitempty"`
}

type NewBareSessionRequest struct {
	Name  string `json:"name,omitempty"`
	Path  string `json:"path,omitempty"`
	Agent string `json:"agent,omitempty"`
}
type NewBareSessionResponse struct {
	Session string `json:"session"`
	Path    string `json:"path"`
	Host    string `json:"host"`
}

type NewProjectRequest struct {
	Name  string `json:"name"`
	Agent string `json:"agent,omitempty"`
}
type NewProjectResponse struct {
	Session string `json:"session"`
	Path    string `json:"path"`
	Host    string `json:"host"`
}

type ProjectInfo struct {
	Name      string    `json:"name"`
	Host      string    `json:"host"`
	Path      string    `json:"path"` // absolute on the daemon host
	HasGit    bool      `json:"has_git"`
	HasCM     bool      `json:"has_cm"`
	HasAgents bool      `json:"has_agents,omitempty"`
	HasDocs   bool      `json:"has_docs"`
	Agent     string    `json:"agent,omitempty"`
	Modified  time.Time `json:"modified"`
}

type PeerInfo struct {
	Hostname   string `json:"hostname"`
	Addr       string `json:"addr"` // tailnet IPv4
	OS         string `json:"os"`
	Online     bool   `json:"online"`
	RunsCCMuxd bool   `json:"runs_ccmuxd"`
	Port       *int   `json:"port,omitempty"`
}

type Conversation struct {
	ID       string    `json:"id"` // agent UUID (its --resume id)
	Agent    string    `json:"agent"`
	Project  string    `json:"project,omitempty"`
	Path     string    `json:"path,omitempty"`
	Preview  string    `json:"preview,omitempty"`
	Modified time.Time `json:"modified"`
}

type AgentUsage struct {
	Claude      UsageSummary `json:"claude"`
	Codex       UsageSummary `json:"codex"`
	Antigravity UsageSummary `json:"antigravity"`
}
type UsageSummary struct {
	HasData       bool    `json:"has_data"`
	WindowSeconds int     `json:"window_seconds"`
	Prompts       int     `json:"prompts"`
	InputTokens   int     `json:"input_tokens"`
	OutputTokens  int     `json:"output_tokens"`
	EstimatedCost float64 `json:"estimated_cost"` // USD
}

type NoteEntry struct {
	Rel      string    `json:"rel"`
	Dir      string    `json:"dir"`
	Display  string    `json:"display"`
	Modified time.Time `json:"modified"`
}
type NoteContent struct {
	Rel     string `json:"rel"`
	Content string `json:"content"`
}
type SearchHit struct {
	Rel     string `json:"rel"`
	LineNum int    `json:"line_num"`
	Snippet string `json:"snippet"`
}

type PreviewResponse struct {
	Lines   int    `json:"lines"`
	Content string `json:"content"`
}

type RenameRequest   struct{ Name string `json:"name"` }
type SendKeysRequest struct{ Keys string `json:"keys"` }

type PairRequest struct {
	Token       string `json:"token"`
	PublicKey   string `json:"public_key"`
	DeviceToken string `json:"device_token,omitempty"`
	APNsEnv     string `json:"apns_env,omitempty"` // development|production
}
type PairResponse      struct{ Hostname string `json:"hostname"`; Version string `json:"version"` }
type PairTokenResponse struct{ Token string `json:"token"`; URL string `json:"url"` }
type RegisterDeviceRequest struct {
	Token     string `json:"token"`
	Env       string `json:"env,omitempty"` // apns: development|production; fcm: empty
	PublicKey string `json:"public_key"`
	Provider  string `json:"provider,omitempty"` // apns (default) | fcm
}
// POST /v1/devices/test body is { "public_key": "..." }

Правила проверки для зеркалирования на стороне клиента

Примеры

# Health (over the tailnet)
curl -s http://100.75.64.20:7474/v1/health | jq

# List sessions
curl -s http://100.75.64.20:7474/v1/sessions | jq

# Type a reply into a session and press Enter
curl -s -X POST http://100.75.64.20:7474/v1/sessions/c-auth/send-keys \
  -H 'content-type: application/json' \
  -d '{"keys":"yes, ship it\n"}'

# Peek at the last 40 lines of a pane
curl -s 'http://100.75.64.20:7474/v1/sessions/c-auth/preview?lines=40' | jq -r .content

# Subscribe to live events
curl -sN http://100.75.64.20:7474/v1/events

Эталонный клиент Go в internal/daemon/client.go — канонический потребитель и полезная карта метода → конечной точки.

Помогите улучшить ccmux

Нашли ошибку, непонятную инструкцию или неудачный перевод? Мы рады любым улучшениям. Создайте issue или отправьте pull request.


Обнаружили ошибку или что-то устарело? Отредактируйте эту страницу на GitHub..