34 KiB
ТЗ: AgentSkill "installer"
Статус: готово к разработке
Дата: 2026-04-11
Автор: Стрим (по требованиям Славы)
Путь установки: ~/.openclaw/skills/installer/
Версия ТЗ: 3.2 (после аудита безопасности и надёжности)
Назначение
Универсальный скилл для любых изменений на файловой системе и конфигурировании хостов.
Использование обязательно при:
- Записи / удалении / перемещении файлов на любом хосте
- Изменении конфигурационных файлов
- Старте / стопе / рестарте сервисов
- Установке / удалении пакетов
- Изменении прав доступа (chmod / chown)
Область: любые хосты — SSH прямой, SSH через jump, localhost.
Структура скилла
~/.openclaw/skills/installer/
├── SKILL.md # инструкция для агентов
├── parameters.yaml # хосты, пути, настройки (без секретов)
├── .env.example # список переменных окружения
└── scripts/
├── session.sh # создание сессии, lock, cleanup retention, state machine
├── backup.sh # бэкап файлов + генерация rollback.sh
├── verify.sh # health check + валидация изменений
├── rollback.sh # восстановление файлов из бэкапа
├── manager.sh # центральный менеджер (cleanup, status, list)
├── checker.sh # проверка доступности хостов и секретов
└── ssh_exec.sh # SSH-хелпер с ProxyCommand (shared)
Рабочие данные (workspace):
~/.openclaw/workspace/installer/
├── logs/ # лог-файлы сессий
├── sessions/ # state.json незавершённых сессий + rollback_meta.json
├── .lock/ # lock-директории по хостам
├── registry.jsonl # реестр всех изменений
└── backups/localhost/ # бэкапы для localhost
Pipeline — 6 обязательных шагов
1. ПОДГОТОВКА
- Загрузить parameters.yaml (проверить config_version) + секреты из .env
- Проверить незавершённые сессии для данного хоста → предложить продолжить / откатить
- Создать session ID: YYYYMMDD-HHMMSS_<host>_<description>_<XXXX>
- Инициализировать лог-файл сессии и state.json (current_step: "init")
- Захватить LOCK (mkdir — атомарно)
2. HEALTH CHECK
- Обновить state.json → current_step: "health"
- Проверить доступность хоста (SSH connectivity)
- Выполнить host-specific health_check команду (если задана)
- Залогировать результат
3. БЭКАП
- Обновить state.json → current_step: "backup"
- Скопировать ВСЕ изменяемые файлы в backups_dir
- Сгенерировать rollback.sh на хосте + сохранить rollback_meta.json локально
- Если бэкап хотя бы одного файла не удался — СТОП, cleanup частичных бэкапов
4. ВАЛИДАЦИЯ
- Обновить state.json → current_step: "validate"
- Проверить синтаксис новых файлов (YAML, JSON, nginx -t и т.д.)
- Выполнить dry-run / config check если доступен
- Показать агенту diff изменений
- Для критических конфигов — запросить явное подтверждение Славы
- Только при чистой валидации переходить к шагу 5
5. ИЗМЕНЕНИЕ
- Обновить state.json → current_step: "change"
- Применить изменения с логированием каждого действия
- При ошибке — немедленно СТОП, спросить об откате
6. VERIFY (post-check)
- Обновить state.json → current_step: "verify"
- Повторить health_check
- Проверить ожидаемый результат (--expect-entity / --expect-state)
- При ошибке — спросить об откате с развёрнутым объяснением
- Записать результат в registry.jsonl
- Удалить state.json сессии (при успехе)
- Освободить LOCK
- Запустить cleanup_old_sessions()
Интерфейс скриптов
session.sh
session.sh --host <host_id> --desc <описание> --agent <agent_name>
# Возвращает SESSION_ID в stdout (JSON: {"session_id":"..."})
# Exit 0 = успех
# Exit 1 = lock занят (JSON: {"error":"lock_busy","owner":"...","since":"...","age_minutes":N})
# Exit 2 = lock завис 30-60 мин (требует подтверждения Славы)
# Правила description: только a-z0-9-, макс 40 символов, авто-нормализация
# При старте проверяет незавершённые сессии для данного хоста
# Если есть → JSON: {"warning":"incomplete_session","session":"...","step":"..."}
# Агент обязан предложить: продолжить / откатить / отменить
Файл состояния сессии (state machine):
~/.openclaw/workspace/installer/sessions/<session_id>/state.json
Содержит: {"session_id":"...","host":"...","current_step":"health|backup|validate|change|verify",
"started":"...","updated":"..."}
Обновляется при переходе между шагами pipeline.
При успешном завершении — удаляется.
При ошибке — остаётся для диагностики и возможности продолжить.
backup.sh
backup.sh --session <session_id> --host <host_id> \
--files <file1> [<file2> ...] \
--post-rollback-action <reload_type|none>
# Exit 0 = JSON: {"backups":[...],"rollback":"<path>"}
# Exit 1 = JSON: {"error":"...","failed_file":"...","cleaned_up":true}
# При ошибке: СТОП, все уже скопированные бэкапы удаляются (staging), rollback.sh НЕ создаётся
verify.sh
verify.sh --host <host_id> --session <session_id> --stage <pre|post> \
[--expect-entity <entity_id> --expect-state <state>]
# Exit 0 = JSON: {"status":"ok","check":"..."}
# Exit 1 = JSON: {"status":"failed","check":"...","error":"..."}
rollback.sh
rollback.sh --session <session_id> --host <host_id>
# Восстанавливает файлы в обратном порядке изменений
# НЕ выполняет reload/restart — это делает агент после
# ⚠️ ОБЯЗАТЕЛЬНО: агент ДОЛЖЕН выполнить post_rollback_action после rollback
# ⚠️ ОБЯЗАТЕЛЬНО: агент ДОЛЖЕН запустить verify.sh --stage post после отката
# Exit 0 = JSON: {"status":"files_restored","post_rollback_action_required":true}
# Exit 1 = JSON: {"status":"partial","errors":N,"details":[...]}
# Exit 2 = JSON: {"status":"nothing_done","error":"backup not found"}
# Без set -e — явная проверка каждого шага
manager.sh
manager.sh --action cleanup [--host <host_id>] # чистка старых бэкапов + orphaned state.json/lock
manager.sh --action status [--host <host_id>] # кол-во бэкапов, размер, даты
manager.sh --action list-sessions [--host <host_id>] [--days N] # список сессий
manager.sh --action show-session --session <id> # детали сессии
manager.sh --action rollback --session <id> # откат конкретной сессии
ssh_exec.sh
ssh_exec.sh --host <host_id> --cmd "<команда>" [--timeout <seconds>]
# Читает parameters.yaml, строит SSH/ProxyCommand/local exec автоматически
# Для type=local: выполняет команду напрямую (не SSH)
# Для type=ssh-chain: использует ProxyCommand (ключ НЕ хранится на jump-хосте)
# SSH-опции берутся из parameters.yaml → timeouts (ConnectTimeout, ServerAliveInterval, ServerAliveCountMax)
# --timeout оборачивает команду в `timeout <seconds>` (по умолчанию: timeouts.command_timeout_default)
# Exit 3 = timeout (команда не завершилась за отведённое время)
Общие правила для всех скриптов:
- Все пишут в лог-файл сессии автоматически (напрямую в файл, НЕ через stdout)
- stdout — только JSON-результат для парсинга агентом, ничего больше
- stderr — человекочитаемые сообщения и отладка
- Успех → JSON в stdout + exit 0
- Ошибка → JSON
{"error":"...","step":"..."}в stdout + exit 1+ - Exit codes:
0= успех,1= ошибка,2= требуется подтверждение,3= timeout
Блокировка (Lock)
Lock-директория: ~/.openclaw/workspace/installer/.lock/<host>/
Создание: mkdir (атомарно) — если mkdir вернул ошибку, lock занят
Содержимое: info.json с полями agent, session, started, pid (если доступен)
Три уровня реакции по возрасту lock:
| Возраст | Действие |
|---|---|
| < 30 мин | Стоп. Сообщить кто держит и с какого времени. Ждать. |
| 30–60 мин | Уведомить Славу. Предложить снять. Ждать явного ОК. |
| > 60 мин | Считать потенциально мёртвым. Агент проверяет PID владельца (если доступен) и запрашивает подтверждение Славы. Снятие через атомарный rename (mv lock → lock.removing), затем создание нового — не через rm+mkdir. Логирует принудительное снятие. |
Освобождение: rm -rf "$LOCK_DIR" + trap 'rm -rf "$LOCK_DIR"' EXIT в скриптах.
Orphaned state.json: при принудительном снятии lock — state.json в sessions/ НЕ удаляется (нужен для диагностики). Очистка orphaned state.json выполняется manager.sh --action cleanup: удаляются state.json сессий старше retention_days, у которых нет активного lock.
Поведение при ошибке
Никогда не откатывать автоматически. Агент спрашивает пользователя.
❌ ОШИБКА на шаге: <название шага>
📋 Что изменялось: <файл/сервис/команда>
🔴 Текст ошибки: <stderr / exit code>
⚠️ Текущее состояние: <что уже применено, что нет>
Если ОТКАТИТЬ:
✅ <что вернётся в исходное состояние>
⚠️ <возможные побочные эффекты отката>
🔧 Команда: <путь к rollback.sh>
Если НЕ откатывать:
⚠️ <риски оставить как есть>
💡 <возможные ручные действия>
Выполнить откат? (да / нет)
При rollback_failed — немедленно уведомить Славу:
🚨 ТРЕБУЕТСЯ РУЧНОЕ ВМЕШАТЕЛЬСТВО
Откат не удался. Файл может быть в неконсистентном состоянии.
Сессия: <id>
Хост: <host>
Бэкап: <path>
Критические конфиги — обязательное подтверждение Славы
Перед шагом ИЗМЕНЕНИЕ — запросить явное "да":
| Тип | Примеры файлов |
|---|---|
| Docker | docker-compose.yml, Dockerfile, .env сервисов |
| Nginx | nginx.conf, sites-available/*, sites-enabled/* |
| Системные | /etc/fstab, /etc/hosts, /etc/ssh/sshd_config |
| HA Core | configuration.yaml |
| Сеть | /etc/network/interfaces, /etc/netplan/* |
Формат запроса:
⚠️ КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ: [тип]
📋 Файл: [путь]
🔍 Изменения: [diff]
✅ Бэкап готов: [путь]
Подтвердить изменение? (да / нет)
Логирование
Лог-файл сессии
Путь: ~/.openclaw/workspace/installer/logs/YYYYMMDD-HHMMSS_<host>_<desc>_<XXXX>.log
Примечание: в примерах ниже суффикс
_XXXXопущен для читаемости.
[2026-04-11T14:30:00Z] SESSION START: 20260411-143000_ha_automations-fix
[2026-04-11T14:30:00Z] HOST: ha (192.168.2.139 via ruvpn-srv)
[2026-04-11T14:30:01Z] LOCK: acquired
[2026-04-11T14:30:02Z] HEALTH CHECK: ha core check → OK
[2026-04-11T14:30:03Z] BACKUP: /homeassistant/automations.yaml → /var/backups/openclaw/20260411-143003_automations.yaml.bak
[2026-04-11T14:30:03Z] ROLLBACK SCRIPT: /var/backups/openclaw/rollback/20260411-143003_automations_rollback.sh
[2026-04-11T14:30:04Z] VALIDATE: yaml syntax OK
[2026-04-11T14:30:04Z] VALIDATE: ha core check with new file → OK
[2026-04-11T14:30:05Z] WRITE: /homeassistant/automations.yaml
[2026-04-11T14:30:06Z] RELOAD: automation/reload → HTTP 200
[2026-04-11T14:30:07Z] VERIFY: automation.alert_device_became_available state=on → OK
[2026-04-11T14:30:07Z] LOCK: released
[2026-04-11T14:30:07Z] CLEANUP: removed 0 old sessions
[2026-04-11T14:30:07Z] SESSION END: SUCCESS
Реестр изменений
Путь: ~/.openclaw/workspace/installer/registry.jsonl
При успехе:
{"ts":"2026-04-11T14:30:07Z","session":"20260411-143000_ha_automations-fix","host":"ha","files":["/homeassistant/automations.yaml"],"backups":["/var/backups/openclaw/20260411-143003_automations.yaml.bak"],"rollback":"/var/backups/openclaw/rollback/20260411-143003_automations_rollback.sh","post_rollback_action":"automations","status":"success","agent":"stream"}
При ошибке:
{"ts":"...","session":"...","host":"ha","files":[...],"backups":[...],"rollback":"...","post_rollback_action":"automations","status":"failed","failed_step":"verify","error":"ha core check: Invalid config for automation","rolled_back":false,"agent":"stream"}
Полный список статусов:
status |
Смысл | Доп. поля |
|---|---|---|
success |
Всё прошло | — |
failed |
Ошибка, состояние файлов известно | failed_step, error, rolled_back: false |
timeout |
Команда не завершилась за отведённое время, состояние файлов может быть неизвестно | failed_step, error, state_unknown: true/false, rolled_back: false |
rolled_back |
Ошибка + откат выполнен | failed_step, error, rolled_back: true, rollback_ts |
rollback_failed |
Ошибка + откат тоже упал | failed_step, error, rollback_error, rolled_back: false |
cancelled |
Отменено пользователем | cancelled_at_step |
Правило state_unknown: если timeout произошёл на шаге CHANGE — state_unknown: true (файл мог быть записан частично). Агент обязан показать это пользователю перед вопросом об откате.
Бэкапы и rollback.sh
Структура хранения
На удалённом хосте (SSH):
/var/backups/openclaw/YYYYMMDD-HHMMSS_<filename>.bak
/var/backups/openclaw/rollback/YYYYMMDD-HHMMSS_<desc>_rollback.sh
Локальная копия метаданных (всегда, для всех хостов):
~/.openclaw/workspace/installer/sessions/<session_id>/rollback_meta.json
# Содержит: список файлов, пути бэкапов, post_rollback_action
# Позволяет восстановить rollback.sh если хост временно недоступен
На localhost (OpenClaw workspace):
~/.openclaw/workspace/installer/backups/localhost/YYYYMMDD-HHMMSS_<filename>.bak
~/.openclaw/workspace/installer/backups/localhost/rollback/YYYYMMDD-HHMMSS_<desc>_rollback.sh
Содержимое rollback.sh
#!/bin/bash
# ROLLBACK SESSION: 20260411-143000_ha_automations-fix
# Generated: 2026-04-11T14:30:03Z
# Files: /homeassistant/automations.yaml
# POST ROLLBACK ACTION: automations (выполняет агент через API, не этот скрипт)
# Без set -e — явная проверка каждого шага
ERRORS=0
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ROLLBACK START" >&2
# Файл 1/1: восстановить
cp /var/backups/openclaw/20260411-143003_automations.yaml.bak \
/homeassistant/automations.yaml
if [ $? -eq 0 ]; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] FILE RESTORED: /homeassistant/automations.yaml" >&2
else
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ERROR: failed to restore /homeassistant/automations.yaml" >&2
ERRORS=$((ERRORS + 1))
fi
# Итог
if [ $ERRORS -eq 0 ]; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ROLLBACK COMPLETE: files restored" >&2
echo '{"status":"files_restored","post_rollback_action_required":true,"post_rollback_action":"automations"}'
exit 0
else
echo '{"status":"partial","errors":'$ERRORS'}'
exit 1
fi
parameters.yaml
# installer/parameters.yaml
# Только структура, пути, параметры подключений.
# Секреты (ключи, пароли, токены) — ТОЛЬКО в ~/.openclaw/.env
config_version: 1 # версия формата; скрипты проверяют совместимость при загрузке
hosts:
mva154:
label: "mva154 (основной сервер)"
type: ssh-direct
host: "82.22.50.71"
port: 22
user: "slin"
auth_type: password # аутентификация по паролю (не ключ)
password_env: "MVA154_PASSWORD" # имя переменной в .env с паролем
sudo: true
sudo_pass_env: "MVA154_SUDO_PASS"
health_check: "systemctl is-system-running"
post_check: "systemctl is-system-running"
tags: [docker, nginx, main]
ruvpn-srv:
label: "RUVPN-сервер"
type: ssh-direct
host: "185.130.212.192"
port: 3322
user: "vpn"
auth_type: key # аутентификация по ключу
ssh_key_env: "RUVPN_SSH_KEY" # имя переменной в .env с путём к ключу
sudo: true
sudo_pass_env: "RUVPN_SUDO_PASS"
health_check: null # только проверка доступности SSH
post_check: null
tags: [ruvpn, jump]
ha:
label: "Home Assistant (HAOS)"
type: ssh-chain
host: "192.168.2.139"
port: 22
user: "root"
auth_type: key # аутентификация по ключу
ssh_key_env: "HA_SSH_KEY" # имя переменной в .env с путём к ключу
via: [ruvpn-srv] # маршрут: OpenClaw → ruvpn-srv → ha
# Ключ НЕ хранится на jump-хосте — ProxyCommand с ключом из .env
sudo: false
health_check: "ha core check"
post_check: "ha core check"
reload_commands:
automations:
method: api
endpoint: "/api/services/automation/reload"
http_method: POST
auth_env: "HA_TOKEN"
base_url_env: "HA_URL"
scripts:
method: api
endpoint: "/api/services/script/reload"
http_method: POST
auth_env: "HA_TOKEN"
base_url_env: "HA_URL"
scenes:
method: api
endpoint: "/api/services/scene/reload"
http_method: POST
auth_env: "HA_TOKEN"
base_url_env: "HA_URL"
core:
method: cli
command: "ha core restart"
tags: [homeassistant, haos, critical]
localhost:
label: "OpenClaw container"
type: local # НЕ использует SSH — exec напрямую
host: null
port: null
user: null
auth_type: null
ssh_key_env: null
sudo: true
sudo_pass_env: "LOCALHOST_SUDO_PASS"
health_check: null
post_check: null
tags: [local, openclaw]
storage:
# Логи и реестр — на OpenClaw (workspace, персистентно)
logs_workspace: "~/.openclaw/workspace/installer/logs"
sessions_dir: "~/.openclaw/workspace/installer/sessions"
registry: "~/.openclaw/workspace/installer/registry.jsonl"
lock_dir: "~/.openclaw/workspace/installer/.lock"
# Бэкапы для SSH-хостов — на удалённом хосте
backups_remote_dir: "/var/backups/openclaw"
rollback_remote_dir: "/var/backups/openclaw/rollback"
# Бэкапы для localhost — в workspace
backups_local_dir: "~/.openclaw/workspace/installer/backups/localhost"
rollback_local_dir: "~/.openclaw/workspace/installer/backups/localhost/rollback"
retention_days: 30
max_backup_file_size_mb: 50 # макс. размер одного файла для бэкапа; больше — СТОП с ошибкой
max_backup_total_mb: 500 # макс. суммарный размер бэкапов на хосте; при превышении — WARNING в логе
lock_timeout_warn_minutes: 30 # предупреждение + спросить Славу
lock_timeout_force_minutes: 60 # считать потенциально мёртвым, проверить PID + подтверждение Славы
timeouts:
ssh_connect_timeout: 10 # ConnectTimeout для SSH (секунды)
ssh_alive_interval: 15 # ServerAliveInterval (секунды)
ssh_alive_count_max: 3 # ServerAliveCountMax (пропущенных keepalive до разрыва)
command_timeout_default: 120 # --timeout по умолчанию для ssh_exec.sh (секунды)
session:
id_format: "{YYYYMMDD-HHMMSS}_{host}_{description}_{XXXX}" # XXXX = 4 случайных hex-символа для уникальности
description_rules:
allowed_chars: "a-z0-9-"
max_length: 40
auto_normalize: true # авто-транслитерация и нормализация
fallback: "action-{XXXX}" # если description не передан; XXXX = 4 hex-символа (единый формат с session ID)
notifications:
on_success: false
on_failure: true
on_rollback: true
# Канал и механизм — на стороне агента, не скриптов
on_failure:
auto_rollback: false
ask_user: true
manager:
cleanup_cron: "0 3 * * *" # ежедневно в 03:00 UTC на OpenClaw
cleanup_heartbeat: true # также проверять статус через heartbeat
usage:
mandatory: true
scope:
- filesystem
- config
- services
- packages
- permissions
critical_configs:
- pattern: "docker-compose.yml"
- pattern: "Dockerfile"
- pattern: "nginx.conf"
- pattern: "sites-available/*"
- pattern: "sites-enabled/*"
- pattern: "/etc/fstab"
- pattern: "/etc/hosts"
- pattern: "/etc/ssh/sshd_config"
- pattern: "configuration.yaml"
- pattern: "/etc/network/*"
- pattern: "/etc/netplan/*"
.env.example
# installer skill — подключения к хостам
# Все ключи и пароли только здесь, никогда в parameters.yaml или скриптах
# ruvpn-srv (ключ)
RUVPN_SSH_KEY=/home/node/.openclaw/ha_ssh_key
RUVPN_SUDO_PASS=your_ruvpn_sudo_password
# mva154 (пароль)
MVA154_PASSWORD=your_mva154_password
MVA154_SUDO_PASS=your_mva154_sudo_password
# ha (ключ)
HA_SSH_KEY=/home/node/.openclaw/ha_ssh_key
HA_TOKEN=your_ha_long_lived_token
HA_URL=https://your-ha-url
# localhost
LOCALHOST_SUDO_PASS=your_localhost_sudo_password
checker.sh — проверка доступности хостов
Интерфейс
checker.sh [--host <host_id> [--host <host_id2> ...]] [--all]
# Exit 0 = все проверенные хосты доступны и секреты настроены
# Exit 1 = одна или более проверок не прошла
Уровни ошибок — локализация
Checker различает 5 уровней проблем и сообщает точно где сломалось:
| Код | Уровень | Смысл |
|---|---|---|
SECRET_MISSING |
Секрет не настроен | Переменная в .env отсутствует или пустая |
SECRET_INVALID |
Секрет некорректен | Переменная задана, но значение невалидно: файл ключа не найден / не читаем, токен слишком короткий (<10 символов) |
AUTH_FAILED |
Аутентификация провалилась | Секрет есть и валиден локально, но ключ/пароль отвергнут хостом |
HOST_UNREACHABLE |
Хост недоступен | Timeout, refused, DNS не резолвится |
CHECK_FAILED |
Health check упал | Хост доступен, но health_check вернул ошибку |
Вывод checker.sh
При успехе:
✅ ruvpn-srv — OK (12ms)
✅ mva154 — OK (45ms) | systemctl: running
✅ ha — OK (89ms) | ha core check: OK
✅ localhost — OK | sudo: OK
All hosts: 4/4 OK
При ошибках — с точной локализацией:
✅ ruvpn-srv — OK (12ms)
❌ mva154 — SECRET_MISSING: переменная MVA154_PASSWORD не найдена в .env
Добавьте в ~/.openclaw/.env: MVA154_PASSWORD=your_password
❌ ha — SECRET_INVALID: файл ключа /home/node/.openclaw/ha_ssh_key не найден
Проверьте путь в переменной HA_SSH_KEY
⚠️ localhost — AUTH_FAILED: sudo вернул ошибку (неверный пароль?)
Проверьте переменную LOCALHOST_SUDO_PASS в .env
Hosts: 1/4 OK | 2 errors | 1 warning
JSON-вывод (для агентов):
{
"summary": {"total": 4, "ok": 1, "failed": 2, "warning": 1},
"hosts": {
"ruvpn-srv": {"status": "ok", "latency_ms": 12},
"mva154": {
"status": "failed",
"error_code": "SECRET_MISSING",
"error": "MVA154_PASSWORD not found in .env",
"hint": "Add MVA154_PASSWORD=your_password to ~/.openclaw/.env"
},
"ha": {
"status": "failed",
"error_code": "SECRET_INVALID",
"error": "Key file /home/node/.openclaw/ha_ssh_key not found",
"hint": "Check HA_SSH_KEY path in .env"
},
"localhost": {
"status": "warning",
"error_code": "AUTH_FAILED",
"error": "sudo authentication failed",
"hint": "Check LOCALHOST_SUDO_PASS in .env"
}
}
}
Порядок проверок для каждого хоста
1. SECRET CHECK — все нужные переменные есть в .env и непустые?
→ если нет: SECRET_MISSING, стоп для этого хоста
1b. SECRET VALIDATE — значения переменных корректны?
→ для auth_type=key: файл ключа существует и читаем (-f && -r)?
→ для токенов (HA_TOKEN и т.д.): длина >10 символов?
→ если нет: SECRET_INVALID, стоп для этого хоста
2. CONNECTIVITY — хост достижим? (TCP connect с таймаутом 5 сек)
→ если нет: HOST_UNREACHABLE, стоп для этого хоста
→ для ssh-chain: сначала проверить каждый хоп в via[]
3. AUTH CHECK — аутентификация проходит? (ключ / пароль)
→ если нет: AUTH_FAILED, стоп для этого хоста
4. SUDO CHECK — если sudo: true, проверить sudo доступность
→ если нет: AUTH_FAILED (sudo), стоп
5. HEALTH CHECK — выполнить health_check команду если задана
→ если не OK: CHECK_FAILED
6. BACKUP DIR — проверить наличие /var/backups/openclaw на хосте
→ если нет: WARNING (не критично, создаётся при первом бэкапе)
Использование
- Перед первым запуском скилла — обязательно
- При добавлении нового хоста — проверить сразу после добавления в parameters.yaml
- При ошибках подключения — для диагностики
- Через manager.sh —
manager.sh --action statusвключает connectivity check
Добавление нового хоста
- Добавить блок в
parameters.yaml:
new-host:
label: "Описание хоста"
type: ssh-direct # или ssh-chain, local
host: "IP_ADDRESS"
port: 22
user: "username"
ssh_key_env: "NEW_HOST_SSH_KEY"
sudo: false
health_check: null # или "systemctl is-system-running"
post_check: null
tags: [tag1, tag2]
- Добавить ключ в
~/.openclaw/.env:
NEW_HOST_SSH_KEY=/path/to/key
- Проверить подключение:
scripts/ssh_exec.sh --host new-host --cmd "echo OK"
- Создать папку для бэкапов на хосте:
scripts/ssh_exec.sh --host new-host --cmd "mkdir -p /var/backups/openclaw/rollback"
Управление (manager.sh)
Автоматическое — cron на OpenClaw
# Один cron, только на OpenClaw — чистит все хосты централизованно
# Перед удалением записывает в registry.jsonl: {"action":"cleanup","deleted_sessions":[...],"ts":"..."}
0 3 * * * ~/.openclaw/skills/installer/scripts/manager.sh --action cleanup
Через heartbeat
HEARTBEAT.md → manager.sh --action status → показать состояние бэкапов
Вручную
# Статус всех хостов
manager.sh --action status
# Список сессий за последние 7 дней
manager.sh --action list-sessions --days 7
# Детали конкретной сессии
manager.sh --action show-session --session 20260411-143000_ha_automations-fix
# Откат конкретной сессии
manager.sh --action rollback --session 20260411-143000_ha_automations-fix
Что реализует Dev
SKILL.md— инструкция со всеми разделами (см. список ниже)parameters.yaml— по шаблону выше (уже заполнен).env.example— по шаблону вышеscripts/session.sh— создание сессии, lock (mkdir), нормализация description, cleanup, state machine (state.json)scripts/backup.sh— бэкап нескольких файлов, генерация rollback.shscripts/verify.sh— health_check + post_check + --expect-entity/statescripts/rollback.sh— откат файлов без set -e, явные статусыscripts/manager.sh— cleanup, status, list-sessions, show-session, rollback по session_idscripts/checker.sh— проверка доступности хостов с локализацией ошибок (5 уровней)scripts/ssh_exec.sh— SSH-хелпер: ssh-direct, ssh-chain (ProxyCommand), local
Обязательные разделы SKILL.md
- Когда использовать (обязательно)
- Когда НЕ использовать
- Быстрый старт — 6 шагов
- Скрипты — справочник с сигнатурами
- Добавление нового хоста
- Добавление новых секретов
- Критические конфиги — подтверждение Славы
- Поведение при ошибке
- Управление (manager.sh)
- Troubleshooting
Не входит в скоуп Dev:
- Наполнение
parameters.yamlреальными IP/путями — уже готово - Настройка cron на OpenClaw — делает Слава/Стрим после установки