Перейти к содержимому

Локальный HTTP API

Santiago запускает локальный демон, который предоставляет HTTP API по адресу http://localhost:7891. Каждое действие в браузере, которое выполняет ваш код или AI-агент, проходит через этот демон, а он управляет запущенным браузером Camoufox через Playwright. На этой странице описаны базовые пути, конверт ответа, коды статусов и ошибок, а также то, как демон защищает эти эндпоинты от других вкладок на вашей машине.

Демон слушает только localhost — он никогда не доступен по сети. Если вы используете готовый агентский скилл, он уже общается по этому протоколу за вас; см. Установка агентского скилла. Полный каталог действий находится в справочнике API.

Под http://localhost:7891/api есть два базовых пути:

НазначениеБазовый путь
Управление демоном (лицензия, профили, обновление и т. д.)http://localhost:7891/api
Действия автоматизации браузераhttp://localhost:7891/api/automation/:profileId/<action>

Эндпоинты автоматизации всегда нацелены на один запущенный профиль, определяемый его :profileId в пути. Сегмент <action> — это операция, например navigate, click, snapshot или screenshot.

http://localhost:7891/api/automation/:profileId/<action>

В каждом примере ниже используется переменная оболочки для идентификатора профиля, чтобы можно было копировать команды без правки пути каждый раз. Экспортируйте её один раз с идентификатором запущенного профиля:

Set the profile id once
export PROFILE="3f9a1c20-7b6e-4f0a-9c2d-1e8b5a6d4c01"

Все последующие сниппеты curl ссылаются на $PROFILE в URL.

Каждый эндпоинт возвращает JSON в одной из двух форм. При успехе ok равно true, а полезная нагрузка лежит в data:

Success
{ "ok": true, "data": { "url": "https://example.com/", "title": "Example Domain" } }

При неудаче ok равно false, а error несёт машиночитаемый code и понятное человеку сообщение message:

Failure
{ "ok": false, "error": { "code": "PROFILE_NOT_RUNNING", "message": "Profile is not running" } }

Некоторые действия, которые просто завершаются успехом без возврата данных, отвечают только { "ok": true } — например click, hover и scroll-to.

Перейдите в запущенном профиле по URL. navigate возвращает итоговый url и title страницы:

Navigate the profile to a URL
curl -s -X POST "http://localhost:7891/api/automation/$PROFILE/navigate" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'
Response
{ "ok": true, "data": { "url": "https://example.com/", "title": "Example Domain" } }

Прочитать страницу так же просто. Действие snapshot возвращает дерево доступности, где каждый интерактивный элемент несёт [ref=…], который вы можете переиспользовать в последующих действиях:

Capture the page structure
curl -s -X POST "http://localhost:7891/api/automation/$PROFILE/snapshot" \
-H "Content-Type: application/json" \
-d '{}'
Response
{ "ok": true, "data": { "snapshot": "- heading \"Example\" [ref=e1]\n- textbox \"Email\" [ref=e2]\n..." } }

Затем вы нацеливаетесь на элементы по этому ref или по CSS-selector — большинство эндпоинтов взаимодействия принимают любой из них:

Click an element by ref
curl -s -X POST "http://localhost:7891/api/automation/$PROFILE/click" \
-H "Content-Type: application/json" \
-d '{"ref":"e1"}'
Response
{ "ok": true }

Демон сопровождает каждый конверт ошибки соответствующим HTTP-статусом, так что для грубой обработки можно полагаться только на строку статуса.

СтатусЗначениеКогда вы его видите
200УспехДействие выполнено; читайте data.
400Некорректный запросОбязательное поле отсутствует или недопустимо (например, пустой массив actions, индекс вкладки вне диапазона или попытка закрыть последнюю вкладку).
401Не авторизованТокены вашего аккаунта истекли или недействительны. Приложению нужно снова войти в систему.
404Не найденоПрофиль не запущен (PROFILE_NOT_RUNNING) либо нет ожидающего диалога (NO_DIALOG).
500Действие не удалосьОперация в браузере выбросила ошибку — элемент не найден, навигация по таймауту, ошибка в JavaScript и т. п.

500 здесь обычно не означает падение: это значит, что запрошенная операция в браузере не смогла завершиться (например, локатор сработал по таймауту). error.code подскажет, какое действие не удалось, а error.message несёт исходное сообщение Playwright.

Коды ошибок сгруппированы по области, из которой они приходят. Коды, оканчивающиеся на _FAILED, всегда несут исходную ошибку браузера в message.

КодСтатусЗначение
PROFILE_NOT_RUNNING404Для :profileId нет запущенного браузера. Сначала запустите его.
BAD_REQUEST400Обязательное поле отсутствовало или было недопустимо (например, пустой actions/fields, индекс вкладки вне диапазона).
LAST_TAB400Вы попытались закрыть единственную открытую вкладку. Вместо этого остановите профиль.
NO_DIALOG404Вы вызвали /dialog, но нет ожидающего alert/confirm/prompt.
КодДействие, которое его вызвало
NAVIGATE_FAILEDnavigate, back, forward, reload
SNAPSHOT_FAILEDsnapshot
SCREENSHOT_FAILEDscreenshot
CLICK_FAILEDclick
HOVER_FAILEDhover
DRAG_FAILEDdrag
SELECT_FAILEDselect-option
SELECT_COMBOBOX_FAILEDselect-combobox
TYPE_FAILEDtype
PRESS_SEQ_FAILEDpress-sequentially
KEY_PRESS_FAILEDpress-key
FILL_FORM_FAILEDfill-form
FILL_PAGE_FAILEDfill-page
SCROLL_FAILEDscroll-to
MOUSE_FAILEDmouse/move, mouse/click, mouse/down, mouse/up, mouse/wheel
TABS_FAILEDtabs (список)
TAB_NEW_FAILEDtabs/new
TAB_SELECT_FAILEDtabs/select
TAB_CLOSE_FAILEDtabs/close
EVALUATE_FAILEDevaluate
WAIT_FAILEDwait
BATCH_FAILEDbatch
DIALOG_FAILEDdialog

Эндпоинты взаимодействия (click, hover, type, scroll-to и подобные) требуют либо ref (из снимка), либо CSS-selector. Если вы не передадите ни того, ни другого, действие завершится ошибкой с сообщением:

Either "ref" or "selector" must be provided

Когда элемента нет на странице, действие быстро завершается неудачей и возвращает соответствующий код _FAILED со статусом 500:

Click a selector that isn't there
curl -s -X POST "http://localhost:7891/api/automation/$PROFILE/click" \
-H "Content-Type: application/json" \
-d '{"selector":"#does-not-exist"}'
Response
{ "ok": false, "error": { "code": "CLICK_FAILED", "message": "locator.click: Timeout 2000ms exceeded." } }

Поскольку демон слушает localhost, любая веб-страница, открытая в любом браузере на той же машине, в принципе могла бы попытаться отправить ему POST. Разрешающая CORS-политика не останавливает это: «простой» кросс-доменный POST полностью пропускает CORS-предзапрос (preflight), и даже когда предзапрос выполняется, origin с подстановочным символом всё равно его проходит. CORS управляет лишь тем, может ли страница прочитать ответ, — а не тем, дойдёт ли запрос до демона.

Santiago закрывает эту брешь двумя слоями защиты:

  • Проверка origin при записи. Хук onRequest отклоняет кросс-доменные запросы POST, PATCH и DELETE на основе их заголовка Origin. Встроенный интерфейс Santiago отдаётся с того же origin, что и демон, поэтому его запросы проходят; запрос с другого сайта отклоняется.
  • Завершение работы, защищённое секретом. Эндпоинт завершения работы дополнительно требует внутренний секрет в заголовке. Он предназначен для вызова только процессом трея настольного приложения, но не автоматизацией.

Что это значит на практике:

  • curl и локальные скрипты работают. Клиенты командной строки и ваши собственные инструменты автоматизации не отправляют браузерный заголовок Origin, поэтому проверка origin их не блокирует. Каждый пример с curl на этой странице работает как есть с запущенным демоном.
  • Интерфейс с тем же origin работает. Встроенный интерфейс приложения разделяет origin демона и разрешён.
  • Случайные веб-страницы заблокированы. Страница с какого-то стороннего сайта, которая пытается управлять вашими профилями, отклоняется проверкой Origin.