session close: local-extractor tested+archived, gitverse repo+push, systemd stays forward

This commit is contained in:
estorozhenko
2026-09-06 12:49:07 +00:00
parent 0a89884245
commit 92bc13e363
9 changed files with 186 additions and 24 deletions
+12 -12
View File
@@ -3,25 +3,24 @@
Обновлено: 2026-09-06 (сессия @session:default/20260906_...)
## Текущее состояние
Лаборатория spec-driven подхода (OpenSpec CLI 1.12.0) для задач настройки инфраструктуры Hermes/homelab. Цикл propose→apply→archive работает; 2 change заархивированы, 1 открыт. Tavily-прокси (web_extract) работает end-to-end. Локальный экстрактор написан, ждёт сетевых тестов.
Лаборатория spec-driven подхода (OpenSpec CLI 1.12.0) для задач настройки инфраструктуры Hermes/homelab. Цикл propose→apply→archive работает; 3 change заархивированы. Tavily-прокси (web_extract) работает end-to-end в forward-режиме (решение по задаче 5: оставить как есть). Локальный экстрактор (trafilatura) написан и протестирован (2.2/2.3 зелёные), change заархивирован. Репозиторий создан на gitverse.ru, main запушен.
## Сделано
- [x] OpenSpec CLI установлен (npm, 1.12.0), лаба инициализирована с --tools hermes (6 скиллов)
- [x] config.yaml с инфраструктурным контекстом (systemd, docker, /opt/<svc>/, gitverse)
- [x] change tavily-proxy-setup — ЗААРХИВИРОВАН (delta → specs/web-extract-tavily/spec.md)
- [x] web_extract работает: Hermes → 8971 → SOCKS5-туннель → Tavily (HTTP 200, контент Example Domain)
- [x] change local-extractor — ОТКРЫТ (4/4 артефакта, валиден): trafilatura 2.2.0 в venv, local-режим в tavily_extract_proxy.py
- [x] git-репо + README + remote origin → gitverse.ru (токен в obsidian)
- [x] change local-extractor — ЗААРХИВИРОВАН: trafilatura 2.2.0, local-режим в tavily_extract_proxy.py, тесты 2.2/2.3 зелёные (example.com и github через --local; tavily.com — geo-блок из РФ — через --local-socks 127.0.0.1:1080, HTTP 200)
- [x] Репозиторий создан на gitverse.ru (kpa39l/openspec-lab), git push -u origin main выполнен
- [x] systemd tavily-proxy.service — решение по задаче 5: ОСТАВЛЕН forward-режим (облачный Tavily через туннель) — рабочий провайдер без изменений
## В работе / Следующие шаги
- [ ] Сетевые тесты local-режима (tasks 2.2 example.com через --local, 2.3 через --local-socks) — требуют присутствия пользователя (внешние запросы блокируются)
- [ ] После тестов: openspec archive local-extractor
- [ ] Создать репозиторий на gitverse.ru и git push -u origin main (внешнее действие, ждёт подтверждения)
- (ничего — все 5 задач закрыты; лаба в стабильном состоянии)
## Как запустить / проверить
```bash
cd /opt/hermes/openspec-lab
openspec validate local-extractor
openspec validate # все changes
openspec status --change local-extractor
# локальный экстрактор (тест)
./venv/bin/python .hermes/scripts/tavily_extract_proxy.py --port 8972 --local # путь: /opt/hermes/.hermes/hermes-agent/venv/bin/python
@@ -31,12 +30,13 @@ systemctl status tavily-proxy # порт 8971, forward через туннел
```
## Ключевые артефакты
- /opt/hermes/openspec-lab/openspec/changes/local-extractor/ — открытый change (4 артефакта)
- /opt/hermes/openspec-lab/openspec/specs/web-extract-tavily/spec.md — main spec после archive
- /opt/hermes/openspec-lab/openspec/specs/web-extract-tavily/spec.md — main spec про forward-прокси
- /opt/hermes/openspec-lab/openspec/specs/web-extract-local/spec.md — main spec про local-экстрактор (после archive local-extractor)
- /opt/hermes/openspec-lab/openspec/changes/archive/2026-09-06-local-extractor/
- /opt/hermes/.hermes/scripts/tavily_extract_proxy.py — прокси + local-режим
- /etc/systemd/system/tavily-proxy.service — юнит (forward)
- /etc/systemd/system/tavily-proxy.service — юнит (forward, без изменений)
- /opt/hermes/openspec-lab/.hermes/skills/openspec-*/ — Hermes-скиллы OpenSpec
- gitverse: https://gitverse.ru/kpa39l/openspec-lab (remote origin: https://kpa39l:<TOKEN>@gitverse.ru/kpa39l/openspec-lab.git)
## Открытые вопросы
- Делать ли push на gitverse (нужен репозиторий + подтверждение)
- Переключать ли systemd на --local после успешных тестов (отказ от облачного Tavily) или оставить forward как основной
- (нет)
+9 -4
View File
@@ -16,7 +16,12 @@
| 2026-09-06 | trafilatura 2.2.0 в venv; local-режим в tavily_extract_proxy.py (--local, --local-socks) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | systemd-юнит переведён на tavily_extract_proxy.py (forward-режим сохранён) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | git-репо openspec-lab + README + remote gitverse (токен obsidian) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | Тест local-режима 2.2 (example.com через --local) | 🔵 открыта | |
| 2026-09-06 | Тест local-режима 2.3 (страница через --local-socks) | 🔵 открыта | |
| 2026-09-06 | openspec archive local-extractor (после зелёных тестов) | 🔵 открыта | |
| 2026-09-06 | Создать репо на gitverse.ru, git push -u origin main | 🔵 открыта | |
| 2026-09-06 | Тест local-режима 2.2 (example.com через --local) | ✅ | @session:default/20260906_... |
| 2026-09-06 | Тест local-режима 2.3 (страница через --local-socks) | ✅ | @session:default/20260906_... |
| 2026-09-06 | openspec archive local-extractor (после зелёных тестов) | ✅ | @session:default/20260906_... |
| 2026-09-06 | Создать репо на gitverse.ru, git push -u origin main | ✅ | @session:default/20260906_... |
| 2026-09-06 | Тест 2.2: example.com + github.com через --local (HTTP 200, контент) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | Тест 2.3: tavily.com (geo-блок из РФ) через --local-socks 127.0.0.1:1080 (HTTP 200) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | openspec archive local-extractor (валиден, заархивирован, новая spec web-extract-local) | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | Репо создано на gitverse (kpa39l/openspec-lab, HTTP 201), push -u origin main OK | ✅ закрыта | @session:default/20260906_... |
| 2026-09-06 | Решение по systemd (задача 5): оставить forward-режим — подтверждено пользователем | ✅ закрыта | @session:default/20260906_... |
+21 -6
View File
@@ -46,12 +46,29 @@ openspec init --tools hermes --force --no-animation # в /opt/hermes/openspec-
```bash
git init -b main && git add ... (НЕ git add -A — зависает; добавлять файлы поимённо)
git -c user.name=... -c user.email=... commit -m "..."
git remote add origin https://estorozhenko:<TOKEN>@gitverse.ru/estorozhenko/openspec-lab.git
git remote add origin https://estorozhenko:<TOKEN>@gitverse.ru/estorozhenko/openspec-lab.git # УСТАРЕЛО: логин kpa39l, см. ниже
```
Токен: obsidian homelab/gitverse.ru.md (dc52c489...) и homelab/gitea.nixg.ru/«Токен для gitverse.ru.md» (a8c8f69e..., для зеркала gitea).
- Питфол: `git add -A` зависал (блок сессии) — добавлять файлы явно поимённо.
- Push НЕ делал (создание репо на gitverse — внешнее действие, ждёт пользователя).
### Сетевые тесты local-режима (2.2 / 2.3) — ЗЕЛЁНЫЕ
Запуск: `/opt/hermes/.hermes/hermes-agent/venv/bin/python /opt/hermes/.hermes/scripts/tavily_extract_proxy.py --port 8972 --local`.
- 2.2: `curl POST 127.0.0.1:8972/extract {"urls":["https://example.com"]}` → HTTP 200, 0.98s, `raw_content` = «This domain is for use in documentation examples...». GitHub: `https://github.com/` → HTTP 200, 0.84s, полный текст главной страницы. Хвост: процессы убиты, порт 8972 свободен.
- 2.3: `--local-socks 127.0.0.1:1080` (туннель telegram-tunnel active) на `https://tavily.com/` (geo-блок из РФ) → HTTP 200, 3.7s, контент Tavily извлечён (302 → www.tavily.com → 200). Туннель подтверждён.
- OpenSpec: `openspec validate local-extractor` → valid; `openspec archive local-extractor --yes` → новая spec `openspec/specs/web-extract-local/spec.md` (+3), change → archive/2026-09-06-local-extractor.
### GitVerse API (создание репозитория)
- UI/SPA: `https://gitverse.ru/api/v1/...` отдаёт HTML (Next.js) — НЕ JSON. Старые ссылки /api/v1 из WALKTHROUGH не работают.
- Правильный API: `https://api.gitverse.ru` (Gitea-совместимый: /user/repos, /repos/{owner}/{repo}/...)
- Создание: `POST https://api.gitverse.ru/user/repos` с `Authorization: Bearer <токен>` + `Accept: application/vnd.gitverse.object+json;version=1` → HTTP 201, id 327605.
- ВАЖНО: фактический логин аккаунта = **kpa39l** (не estorozhenko!). Remote пришлось поправить: `git remote set-url origin https://kpa39l:<TOKEN>@gitverse.ru/kpa39l/openspec-lab.git`. Репо: https://gitverse.ru/kpa39l/openspec-lab.
- `git push -u origin main` → `* [new branch] main -> main`, tracking установлен.
### Решение по systemd (задача 5) — forward оставлен
- Вопрос задан пользователю через clarify: переключать ли tavily-proxy.service на --local.
- Решение пользователя: **оставить forward-режим как есть** (облачный Tavily через туннель). Юнит не менялся: ExecStart=... --port 8971 --socks 127.0.0.1:1080 --upstream https://api.tavily.com.
## Подводные камни (сводно)
1. openspec init: нет --no-init-git.
2. trafilatura.fetch_url не принимает timeout (2.2.0).
@@ -60,10 +77,8 @@ git remote add origin https://estorozhenko:<TOKEN>@gitverse.ru/estorozhenko/open
5. Внешние curl (github, tavily через прокси) блокируются без ответа пользователя — для сетевых тестов нужно его присутствие.
6. Прокси Hermes глобально не трогать (deepseek/polza прямые).
7. Tavily требует туннель; .env — бэкап перед правкой.
8. gitverse.ru/api/v1/* → HTML (SPA). Только api.gitverse.ru отдаёт JSON; логин аккаунта kpa39l.
## Как продолжить (следующая сессия)
1. Спросить/получить подтверждение на сетевые тесты 2.2/2.3 (или выполнить при пользователе).
2. Прогнать: curl POST 127.0.0.1:8972/extract (--local) на example.com и github-страницу; --local-socks для заблокированных.
3. openspec validate + archive local-extractor.
4. Создать репо на gitverse (UI/API), git push -u origin main.
5. Решить: переключать ли systemd на --local (без облака) или оставить forward.
1. Все 5 задач закрыты (2.2/2.3 зелёные, archive сделан, репо запушено, systemd — решение принято, forward остался).
2. Лаба в стабильном состоянии. Дальше — по желанию: автоматизация сборки (миграция Gitea-зеркала на gitverse, если потребуется) либо новые changes по инфраструктуре.
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-06
@@ -0,0 +1,46 @@
# Design: local-extractor
## Approach
Расширить существующий `/opt/hermes/.hermes/scripts/tavily_extract_proxy.py`:
- В local-режиме (нет `--socks`) сервер НЕ проксирует в Tavily, а сам извлекает: для каждого URL делает HTTP-запрос (`httpx`, при `--local-socks` через SOCKS5-туннель) и конвертирует HTML → Markdown через `trafilatura` (включён в venv).
- Формат ответа в точности повторяет Tavily `/extract`: `{"results":[{url,title,raw_content,metadata}], "failed_results":[{url,error}], "failed_urls":[...]}` — Hermes-провайдер (`plugins/web/tavily/provider.py:_normalize_tavily_documents`) обрабатывает его без изменений.
- Режимы взаимозаменяемы через аргументы CLI:
- `--socks 127.0.0.1:1080` — forward в Tavily (как сейчас в systemd)
- `--local` или ничего — локальная экстракция
- `--local-socks 127.0.0.1:1080` — локальная, но страницы через туннель
- systemd-юнит указать на tavily_extract_proxy.py (уже сделано, forward-режим).
## Files
- `/opt/hermes/.hermes/scripts/tavily_extract_proxy.py` — добавлены `_fetch_page`, `_extract_local`, `_extract_local_batch`, `--local-socks`
- `/opt/hermes/.hermes/hermes-agent/venv/` — установлен trafilatura 2.2.0
- `/etc/systemd/system/tavily-proxy.service` — ExecStart → tavily_extract_proxy.py (forward), `--socks 127.0.0.1:1080`
## Commands
```bash
# Локальный режим (тест)
python3 /opt/hermes/.hermes/scripts/tavily_extract_proxy.py --port 8972 --local
curl -s -X POST http://127.0.0.1:8972/extract -H 'Content-Type: application/json' \
-d '{"urls":["https://example.com"]}' # → {"results":[...]}
# Переключение systemd на local (когда решено отказаться от облака)
# в /etc/systemd/system/tavily-proxy.service: ExecStart → ... --port 8971 --local
# sudo systemctl daemon-reload && sudo systemctl restart tavily-proxy
```
## Rollback
```bash
# Вернуть forward-режим:
# ExecStart → ... --socks 127.0.0.1:1080 --upstream https://api.tavily.com
sudo systemctl daemon-reload && sudo systemctl restart tavily-proxy
```
## Risks
- trafilatura хуже Tavily на JS-heavy страницах (нет headless-браузера) — приемлемо для типовых статей/доков.
- Локальная экстракция не обходит блокировки РФ сама по себе — нужен `--local-socks` через туннель для таких сайтов.
- Fetch без туннеля из РФ на github может висеть — ограничение сети, не кода.
@@ -0,0 +1,23 @@
## Why
Облачная веб-экстракция (Tavily API) зависит от внешнего сервиса и туннеля: если Tavily вводит планы, меняет лимиты или туннель падает — Hermes теряет web_extract. Нужен полностью локальный экстрактор страниц, который работает без облачного API и без туннеля, с тем же интерфейсом для Hermes.
## What Changes
- Добавляется локальная экстракция в tavily_extract_proxy.py: режим `--local` (trafilatura) / `--local-socks`
- Один и тот же эндпоинт POST /extract отдаёт контент локально, сохраняя Tavily-совместимый JSON (results[].url/title/raw_content)
- Устанавливается библиотека trafilatura в venv Hermes
- (пока) сохраняется forward-режим как основной; local-режим — как опция переключения
## Capabilities
### New Capabilities
- `web-extract-local`: Локальная экстракция страниц через trafilatura с Tavily-совместимым интерфейсом
### Modified Capabilities
- `web-extract-tavily` — добавляется реализация local-режима (не меняет требования: прокси-режим остаётся)
## Impact
- /opt/hermes/.hermes/scripts/tavily_extract_proxy.py — добавлен local-режим
- venv Hermes — установлен trafilatura (2.2.0)
@@ -0,0 +1,34 @@
# Delta for web-extract-local
## ADDED Requirements
### Requirement: Local Extraction Mode
The system MUST support extracting page content fully locally (no cloud API) from the same HTTP endpoint used by the Tavily provider.
#### Scenario: Extract page locally
- GIVEN tavily_extract_proxy.py runs with --local (or --local-socks)
- WHEN a POST /extract with {"urls":["https://..."]} is sent
- THEN the server fetches each URL, converts to markdown with trafilatura
- AND returns Tavily-shaped JSON: {"results":[{"url","title","raw_content","metadata"}], "failed_results":[], "failed_urls":[]}
#### Scenario: Page fails to fetch
- GIVEN a URL is unreachable or returns HTTP >= 400
- WHEN extraction is attempted
- THEN the URL lands in failed_results with an error message
- AND other URLs still process normally
### Requirement: SOCKS Support for Local Fetches
The local extractor MUST be able to fetch through the SOCKS5 tunnel (--local-socks 127.0.0.1:1080) for sites blocked from RU IPs.
#### Scenario: Fetch through tunnel
- GIVEN --local-socks 127.0.0.1:1080 is set
- WHEN page fetch happens
- THEN the request is routed through the SOCKS5 tunnel
### Requirement: Backward Compatible Interface
The local mode MUST NOT break the existing Tavily forward mode: the same port/endpoint keeps working for both.
#### Scenario: Forward still works
- GIVEN the service runs with --socks (forward mode)
- WHEN POST /extract is sent
- THEN the request is forwarded to api.tavily.com as before
@@ -8,8 +8,8 @@
## 2. Тест локального режима
- [x] 2.1 Запуск `--port 8972 --local` → отвечает, отдаёт Tavily-совместимый JSON
- [ ] 2.2 example.com через --local → контент извлечён (заблокировано сетью, тест отложен)
- [ ] 2.3 Страница через --local-socks 127.0.0.1:1080 (тест отложен — сетевые проверки требуют присутствия пользователя)
- [x] 2.2 example.com через --local → контент извлечён (HTTP 200, 0.98s; github.com тоже OK)
- [x] 2.3 Страница через --local-socks 127.0.0.1:1080 (tavily.com — заблокирован из РФ, HTTPS 200, 3.7s)
## 3. Интеграция с systemd
- [x] 3.1 Юнит переведён на tavily_extract_proxy.py (forward-режим — рабочий, сохраняет текущее поведение)
+37
View File
@@ -0,0 +1,37 @@
# web-extract-local Specification
## Purpose
TBD - created by archiving change local-extractor. Update Purpose after archive.
## Requirements
### Requirement: Local Extraction Mode
The system MUST support extracting page content fully locally (no cloud API) from the same HTTP endpoint used by the Tavily provider.
#### Scenario: Extract page locally
- GIVEN tavily_extract_proxy.py runs with --local (or --local-socks)
- WHEN a POST /extract with {"urls":["https://..."]} is sent
- THEN the server fetches each URL, converts to markdown with trafilatura
- AND returns Tavily-shaped JSON: {"results":[{"url","title","raw_content","metadata"}], "failed_results":[], "failed_urls":[]}
#### Scenario: Page fails to fetch
- GIVEN a URL is unreachable or returns HTTP >= 400
- WHEN extraction is attempted
- THEN the URL lands in failed_results with an error message
- AND other URLs still process normally
### Requirement: SOCKS Support for Local Fetches
The local extractor MUST be able to fetch through the SOCKS5 tunnel (--local-socks 127.0.0.1:1080) for sites blocked from RU IPs.
#### Scenario: Fetch through tunnel
- GIVEN --local-socks 127.0.0.1:1080 is set
- WHEN page fetch happens
- THEN the request is routed through the SOCKS5 tunnel
### Requirement: Backward Compatible Interface
The local mode MUST NOT break the existing Tavily forward mode: the same port/endpoint keeps working for both.
#### Scenario: Forward still works
- GIVEN the service runs with --socks (forward mode)
- WHEN POST /extract is sent
- THEN the request is forwarded to api.tavily.com as before