# 13. Веб-контент

🔵 Agent OS включает небольшую файловую систему контента: публичный сайт
рендерится из **Markdown + YAML**, целиком лежащих в конфиг-дереве
(`config/web/`), плюс можно подкладывать **сырые HTML**-страницы (например,
полностью кастомный лендинг), которые отдаются как есть. Базы данных и админ-CMS
нет — чтобы изменить страницу, правишь файл; при live reload изменение уходит
без рестарта и пересборки.

---

## 13.1 Что настраивается

| Файл (в `config/web/`) | Назначение | Обязат. |
|---|---|---|
| `index.md` | YAML front matter (title, nav, cards, demos, extra pages) + markdown-тело лендинга | только для markdown-сайта |
| `style.css` | стиль, подключаемый на каждую страницу | нет |
| `index.tpl.html` | шаблон лендинга (плейсхолдеры `{...}`) | нет (встроенный дефолт) |
| `page.tpl.html` | шаблон страниц-деталей | нет (встроенный дефолт) |
| `pages/*.md` | контент страниц-деталей | на страницу |
| `*.html` (напр. `index.html`, `landing.html`) | полный HTML-документ, отдаётся как есть | нет |

`Site::load` вызывается при старте и повторно при live reload. Сайт отдаётся под
`/` и `/site/`.

---

## 13.2 `index.md` — front matter

Front matter — YAML между строками `---`. Все поля опциональны, если не указано
иное; показаны дефолты.

```yaml
---
title: My Site
tagline: One-line description
description: SEO / OG meta description (падает на tagline)
canonical_base: https://example.com   # абсолютная база; иначе из запроса
footer: © 2026 My Site

cases_title: Case Studies            # по умолчанию "Кейсы внедрения"
demos_title: Live Demos              # по умолчанию "Живые демо"
dev_title: For Developers            # по умолчанию "Для разработчиков"
dev_href: developers.html            # по умолчанию "developers.html"
dev_label: Tools and API             # по умолчанию "Инструменты и API платформы"
dev_icon: 🔧

nav:
  - { label: Home, href: index.html }
  - { label: Docs, href: documentation.html }

cards:
  - tag: Platform
    title: Overview
    href: overview.html
    desc: One-liner shown on the card
    src: pages/overview.md           # опц.; по умолчанию pages/<href minus .html>.md

demos:
  - { icon: "🚗", title: "Cars demo", href: "/demo/overlay?model=drom-agent", desc: "…" }

extra_pages:
  - { title: "Download", href: download.html, src: pages/download.md }
---
```

### Справочник полей

| Поле | Тип | Дефолт | Смысл |
|---|---|---|---|
| `title` | string | пусто | заголовок сайта (`<h1>` и `<title>`) |
| `tagline` | string | пусто | подзаголовок; падает как `description` |
| `description` | string | `tagline` | `<meta name="description">` + OG/Twitter |
| `canonical_base` | string | из запроса | абсолютная база для canonical/OG и `sitemap.xml` |
| `footer` | string | пусто | текст футера (HTML-экранируется) |
| `cases_title` | string | "Кейсы внедрения" | заголовок секции карточек |
| `demos_title` | string | "Живые демо" | заголовок секции демо |
| `dev_title` / `dev_href` / `dev_label` / `dev_icon` | string | см. выше | секция «для разработчиков» |
| `nav` | list | `[]` | верхняя навигация (`label`, `href`) |
| `cards` | list | **обязат., непустой** | карточки кейсов; `href` задаёт имя выходного `.html` |
| `demos` | list | `[]` | карточки живых демо (открываются в новой вкладке) |
| `extra_pages` | list | `[]` | доп. страницы не карточками (`title`, `href`, `src`) |

`src` карточки по умолчанию `pages/<href minus .html>.md`; задай явно, чтобы
тянуть страницу откуда-то ещё (например док-файл вне `config/web/`). Карточка
без разрешаемого markdown-источника пропускается с предупреждением.

`cards` не должен быть пустым — иначе сайт не грузится с ошибкой (кроме случая,
когда сырой `index.html` перекрывает лендинг; см. §13.4).

---

## 13.3 Страницы-детали (`pages/*.md`)

Каждая страница — один Markdown, рендерится диалектом CommonMark (comrak).
Включены расширения:

| Расширение | Эффект |
|---|---|
| tables | GFM-таблицы |
| strikethrough | `~~текст~~` |
| autolink | голые URL становятся ссылками |
| tasklist | `- [ ]` / `- [x]` |
| tagfilter | экранирует опасный сырой HTML |

Внутренние ссылки между markdown (`[x](foo.md)`) переписываются в `foo.html`
автоматически.

### Диаграммы Mermaid

Страницы могут встраивать блоки ` ```mermaid `. Встроенный шаблон грузит
mermaid.js с CDN и рендерит их клиентом в SVG:

    ```mermaid
    graph TD; A-->B;
    ```

Content-Security-Policy сайта разрешает `https://cdn.jsdelivr.net`.

---

## 13.4 Сырые HTML-страницы (произвольный лендинг)

Любой `.html`-файл прямо в `config/web/` (кроме `*.tpl.html`) отдаётся
**как есть** — минуя markdown-рендер и шаблоны, с полным контролем над
`<head>`, скриптами, стилями и шрифтами.

| Файл | Отдаётся по |
|---|---|
| `index.html` | `/`, `/index.html`, `/site/`, `/site/index.html` — заменяет markdown-лендинг |
| любой `name.html` | `/name.html` и `/site/name.html` |

Сырая HTML-страница с тем же именем приоритетнее markdown-страницы. Сырые
страницы попадают в `sitemap.xml` (кроме `index.html`, который маппится на `/`).

Это рекомендованный способ сделать кастомный лендинг: положи полный
HTML-документ в `config/web/index.html` и встрой виджет (самостоятельный
`/widget.js`) куда угодно. Сырые страницы отдаются без строгой CSP markdown-сайта,
поэтому внешние скрипты, шрифты и iframe работают как написано.

`index.md` опционален при наличии сырых HTML-страниц: без него markdown-сайт
просто не рендерится. Для markdown-лендинга и SEO-meta по-прежнему нужен валидный
`index.md` с непустым `cards`.

---

## 13.5 Шаблоны

`index.tpl.html` и `page.tpl.html` опциональны. При наличии в `config/web/` они
заменяют встроенные дефолты. Плейсхолдеры заполняются `{key}`:

| `index.tpl.html` | `page.tpl.html` |
|---|---|
| `{title}` `{head}` `{tagline}` `{nav_links}` `{content}` `{cases_title}` `{cards}` `{demos_title}` `{demos}` `{dev_title}` `{dev_href}` `{dev_label}` `{dev_icon}` `{footer}` | `{title}` `{head}` `{site_title}` `{nav_links}` `{content}` `{footer}` |

`{head}` — сгенерированный SEO/OG meta-блок; `{content}` — рендер тела.
Встроенные дефолты — в `agent_web_bot/src/site.rs`.

---

## 13.6 SEO и поверхности краулеров

- `GET /sitemap.xml` — индекс плюс каждый `card.href`, `extra_pages.href` и
  сырая `.html`-страница (кроме `index.html`).
- `GET /robots.txt` — `Allow: /` и указатель `Sitemap:`.
- `<link rel="canonical">`, `og:*` и `twitter:card` на каждой странице, используя
  `canonical_base` (или из `Host` + `X-Forwarded-Proto`).
- `GET /favicon.ico` и `GET /site/favicon.svg` — inline SVG-favicon.

Страницы отдаются со слабым **ETag** (хэш контента) и `Cache-Control: no-cache`,
поэтому неизменённые страницы возвращают `304` при live reload.

---

## 13.7 Роутинг

| URL | Отдаёт |
|---|---|
| `/`, `/index.html` | лендинг |
| `/site` | 301-редирект на `/site/` |
| `/site/`, `/site/index.html` | лендинг |
| `/site/<page>.html` | страница-деталь (из `pages`, `src` карточки или сырого `.html`) |
| `/site/style.css` | стиль из `config/web/style.css` |
| `/site/favicon.svg`, `/favicon.ico` | favicon |
| `/robots.txt`, `/sitemap.xml` | файлы краулеров |
| `/:page` | фолбэк на обработчик страниц (напр. `/agents-overview.html`) |

Неизвестные страницы — стилизованный 404.

---

## 13.8 Live reload

При `kernel.live_reload: true` (по умолчанию) watcher перерендеривает сайт из
`config/web/` при любом изменении файла — без рестарта и пересборки. Работает
одинаково и когда конфиг приходит из **git-репозитория**
(`AGENT_OS_CONFIG_REPO` / `--config-repo`); см. §3.1.

---

## 13.9 Встраивание внешнего контента — `/proxy`

Для страниц, не лежащих в твоём конфиг-дереве, есть два эндпоинта:

| Endpoint | Поведение |
|---|---|
| `GET /proxy?url=...` | умный прокси: проверяет `X-Frame-Options` / CSP `frame-ancestors`; редиректит на прямой URL, если сайт разрешает iframe, иначе скачивает и отдаёт через прокси |
| `GET /proxy/content?url=...` | только внутренний `<body>` со снятыми скриптами, для вставки в `div` |

Оба SSRF-безопасны: приватные, loopback, link-local и reserved-диапазоны
(включая `169.254.169.254`) блокируются на каждом хопе. `proxy.allow_hosts` в
`server.yaml` дополнительно ограничивает хосты; пусто = любой публичный хост.

---

## 13.10 Веб-контент для агентов

Выше — сайт для *людей*. Для *агентов*, читающих веб-контент, инструменты
описаны в §5:

| Инструмент | Использование |
|---|---|
| `http_fetch` (встроенный) | скачать URL в контекст агента |
| `site_tool` | сконфигурированный скрапер страниц |
| `archive_tool` | полнотекстовый поиск по офлайн-архиву сайта |
| `rss_tool` | чтение/поиск RSS-лент |
| `pdf_tool` | извлечение текста из PDF |

Сайт обычно обходится `scraper`-ом в архив, который затем ищет `archive_tool`.
