# Шаблоны (/ru/docs/flows/templates)

Шаблоны — это готовые сценарии для задач, которые встречаются почти в любом API. Шаблон приносит блоки, связи между ними
и заметку на холсте. Он не знает ваш API, поэтому у его блоков «Запрос» и «Опрос» пока нет запроса: сценарий показывает
проблему для каждого, пока вы не выберете запрос, а заметка подсказывает, что выбрать.

## Создание сценария из шаблона [#create-a-flow-from-a-template]

**В приложении:**

1. На странице «Сценарии» нажмите **+** рядом с **Сценарии** на боковой панели.
2. В разделе **Из шаблона** выберите шаблон. Наведите на него указатель, чтобы прочитать, что он делает.
3. Сценарий открывается с именем шаблона и целиком помещается в видимую область. Переименуйте его в поле **Имя** в
   инспекторе.
4. Прочитайте заметку, затем выберите каждый блок «Запрос» или «Опрос» и укажите его **Запрос**. Когда счётчик проблем
   на панели инструментов исчезнет, сценарий можно запускать.

<Shot id="flows-new-from-template" alt="Меню кнопки + рядом с «Сценарии» на боковой панели: «Пустой сценарий», затем «Из шаблона» с шаблонами «Вход по коду устройства», «Обновление токена», «Постраничный экспорт», «Мониторинг доступности» и «Создание и очистка»" />

**В командной строке:**

```sh
# List the templates
echolot flow new

# Create a flow from one
echolot flow new "Export products" --template paginate-and-export -w Alpenglow.echolot
```

`echolot flow new` записывает `flows/export-products.flow.toml` и выводит его путь. Без `--template` он создаёт пустой
сценарий: блок «Старт», соединённый с блоком «Результат». Выберите запросы в приложении или в файле сценария. См.
[echolot flow](/docs/cli/flow).

Агенты тоже могут создавать сценарии из шаблонов через MCP-сервер Echolot.

## Вход по коду устройства [#device-sign-in]

`device-sign-in`. Выполняет вход по схеме OAuth 2.0 device grant (RFC 8628): запрашивает код устройства, показывает вам
код и ссылку, опрашивает конечную точку токена, пока вы не одобрите, и сохраняет токены доступа и обновления как
секретные переменные.

| Блок | Вид | Что делает |
| --- | --- | --- |
| Ask for a device code | Запрос | Отправляет запрос, который возвращает `device_code`, `user_code`, `verification_uri`, `interval` и `expires_in`. **Выберите этот запрос.** |
| Codes | Выбрать | Достаёт эти пять значений из тела (`code` — это `$.body.device_code`). |
| Show the code | Показать и ждать | Показывает `Open {{verification_uri}} and enter the code. This goes on by itself once you have approved it.`, код пользователя с кнопкой **Скопировать код** и ссылку с кнопкой **Открыть в браузере** и QR-кодом. |
| Wait for approval | Опрос | Отправляет запрос токена каждые `{{interval}}` секунд, пока не получит 200 с `access_token`, но не дольше `{{expires_in}}` секунд. Его переменная `code` — это код устройства. **Выберите этот запрос.** |
| Save the tokens | Задать переменную | Сохраняет `accessToken` и `refreshToken` как секреты в окружении. |
| Done | Шаблон | `Signed in; the tokens are in the Keychain.` |
| Output | Результат | Возвращает этот текст. |

<Shot id="flows-template-device-sign-in" alt="Сценарий из шаблона «Вход по коду устройства»: Start, Ask for a device code, Codes, рядом Show the code и Wait for approval, затем Save the tokens, Done и Output, а заметка говорит, какие запросы выбрать" />

Show the code и Wait for approval выполняются рядом: лист остаётся открытым, пока «Опрос» отправляет запросы, и сам
закрывается, когда «Опрос» успешен (его `success` связан с `until` листа).

Запрос токена отправляет `grant_type` `urn:ietf:params:oauth:grant-type:device_code` с `device_code={{code}}`. Остальные
ваши запросы затем используют `{{accessToken}}`. Поскольку токены сохраняются в окружении, запускайте сценарий с выбранным
окружением.

## Обновление токена [#refresh-token]

`refresh-token`. Получает новый токен доступа по токену обновления и сохраняет оба как секретные переменные. Задайте
блоку «Старт» [расписание](/docs/flows/running-and-triggers#schedule), чтобы токен оставался свежим.

| Блок | Вид | Что делает |
| --- | --- | --- |
| Refresh the token | Запрос | Отправляет запрос токена: `grant_type=refresh_token` с `refresh_token={{refreshToken}}`. **Выберите этот запрос.** |
| Save the tokens | Задать переменную | Сохраняет `accessToken` и `refreshToken` как секреты в окружении. Ответ без нового токена обновления оставляет старый. |
| Valid for | Выбрать | Достаёт `$.body.expires_in`. |
| Output | Результат | Возвращает, сколько секунд действует новый токен. |
| Say it failed | Уведомить | Когда запрос не удался: `The token could not be refreshed` и `The server answered {{status}}. Sign in again.` |

## Постраничный экспорт [#paginate-and-export]

`paginate-and-export`. Отправляет запрос по разу на страницу, собирает элементы всех страниц в один список и записывает
его в `exports/items.json` в папке рабочего пространства. Его вход `pages` (число, по умолчанию 3) задаёт, сколько
страниц получить.

| Блок | Вид | Что делает |
| --- | --- | --- |
| Each page | Повторить | Выполняется `{{pages}}` раз. |
| Page number | Вычислить | `return { page: index + 1 };`, так что страницы — 1, 2, 3… |
| Fetch the page | Запрос | Отправляет запрос с заданным `{{page}}`, например `?page={{page}}`. **Выберите этот запрос.** |
| Items | Выбрать | Достаёт `$.body`, список на одной странице. |
| All pages | Собрать | Собирает списки всех страниц. |
| One list | Вычислить | `return pages.flat();` объединяет их. |
| Export | Записать файл | Записывает `exports/items.json` как JSON. |
| Output | Результат | Возвращает путь к файлу. |

Если список — не всё тело, измените путь в **Items**. Для запроса **Catalog › List products** из Alpenglow добавьте в его
адрес `&page={{page}}` и задайте для **Items** значение `$.body.items`: демо-API возвращает товары в `items`.

## Мониторинг доступности [#smoke-monitor]

`smoke-monitor`. Каждый час отправляет запрос и проверяет, что он отвечает 200 в течение двух секунд; если нет, об этом
сообщает уведомление. Расписание (каждые 60 минут) уже задано в блоке «Старт»: нажмите там **Разрешить**, чтобы
запустить его, или запустите командой `echolot flow serve`.

| Блок | Вид | Что делает |
| --- | --- | --- |
| Send | Запрос | Отправляет запрос, за которым нужно следить. **Выберите этот запрос.** |
| Healthy | Проверить | Проверяет **Answers 200** (`value.status === 200`) и **Within two seconds** (`value.time < 2000`). |
| Output | Результат | Получает ответ, когда обе проверки пройдены. |
| Unhealthy | Уведомить | Когда проверка не пройдена: `Smoke check failed` и `{{url}} answered {{status}} in {{time}} ms.` |
| Failed | Уведомить | Когда запрос не удался: `Smoke check failed` и `The request failed or did not answer with a 2xx status.` |

Поскольку проверки блока «Проверить» — это тесты, `echolot flow run` завершается с ошибкой, когда API нездоров, и тот же
сценарий становится проверкой в CI.

## Создание и очистка [#seed-and-clean-up]

`seed-and-clean-up`. Создаёт по записи на каждый элемент списка, записывает в журнал, что было создано, затем снова
удаляет каждую запись. Поместите проверки, которым нужны данные, туда, где стоит журнал.

| Блок | Вид | Что делает |
| --- | --- | --- |
| Records | Значение | `[{"title": "First"}, {"title": "Second"}, {"title": "Third"}]`. |
| Each record | Для каждого | По очереди отправляет каждую запись. |
| Create | Запрос | Создаёт запись: ключи каждой записи становятся переменными отправки, так что запрос использует `{{title}}`. **Выберите этот запрос.** |
| Its id | Выбрать | Достаёт `$.body.id`. |
| Made | Собрать | Собирает новые ID. |
| Use the data | Журнал | Записывает `Seeded {{value}}`. Замените его тем, что требует данных. |
| Each id | Для каждого | По очереди отправляет каждый ID. |
| Delete | Запрос | Удаляет запись, переменная которой — `{{id}}`. **Выберите этот запрос.** |
| Deleted | Собрать | Собирает удалённое. |
| Count | Вычислить | `return { deleted: gone.length };` |
| Output | Результат | Возвращает, сколько записей удалено. |

Измените **Its id**, если новый ID находится в другом месте ответа.

## Сценарии в Alpenglow [#the-flows-in-alpenglow]

В примере рабочего пространства есть три сценария, которые полезно посмотреть рядом с шаблонами:

- **Place a test order**: входит в систему, создаёт заказ, блоком «Одобрить» спрашивает вас, оставить ли его, и отменяет
  его, если вы отказываетесь. Он предложен агентам как `place_test_order`.
- **Morning stock check**: каждый день в 6:00 получает список товаров и показывает уведомление, когда палатки в Берне
  заканчиваются.
- **Order status on webhook**: отвечает на `POST /order-status` заказом.

[Автоматизация сценариями](/docs/tutorials/automate-with-flows) строит один из них шаг за шагом.
