33 KiB
ТЗ: AgentSkill "installer"
Статус: актуализировано после аудита
Дата: 2026-04-14
Автор: Стрим (по требованиям Славы)
Путь установки: ~/.openclaw/skills/installer/
Версия ТЗ: 3.3 (синхронизировано с реальным кодом и parameters.yaml)
Назначение
Универсальный скилл для любых изменений на файловой системе и конфигурировании хостов.
Использование обязательно при:
- Записи / удалении / перемещении файлов на любом хосте
- Изменении конфигурационных файлов
- Старте / стопе / рестарте сервисов
- Установке / удалении пакетов
- Изменении прав доступа (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)
├── lib.sh # общие функции (update_state, write_registry, remove_state)
└── yaml_get.js # YAML query helper (Node.js, читает parameters.yaml)
Рабочие данные (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"
- Скопировать ВСЕ изменяемые файлы в backup_dir хоста (из parameters.yaml)
- Сгенерировать 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 (через lib.sh → update_state).
При успешном завершении — удаляется (через lib.sh → remove_state).
При ошибке — остаётся для диагностики и возможности продолжить.
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 НЕ создаётся
# Бэкапы сохраняются в backup_dir хоста (из parameters.yaml → hosts.<id>.backup_dir)
# Лимиты: файл > 50MB → СТОП; суммарно > 500MB → WARNING
verify.sh
verify.sh --host <host_id> --session <session_id> --stage <pre|post>
# Exit 0 = JSON: {"status":"ok","check":"..."}
# Exit 1 = JSON: {"status":"failed","check":"...","error":"..."}
# --stage pre → health_check; --stage post → post_check
# Проверка entity state — ответственность прикладного скилла (HA и т.д.), не installer'а
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,"post_rollback_action":"..."}
# 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>] [--session <id>]
# Читает parameters.yaml (через yaml_get.js), строит 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)
# --session — опциональный, для логирования в контексте сессии
# Exit 0 = success; Exit 1 = error; Exit 3 = timeout
lib.sh
source ~/.openclaw/skills/installer/scripts/lib.sh
update_state "$SESSION" "<step>" # health | backup | validate | change | verify
remove_state "$SESSION" # удаление state.json при успехе
write_registry "$SESSION" "<host>" "<status>" [--files "<path>"] [--agent "<name>"]
# запись в registry.jsonl
# write_registry также поддерживает:
# --failed-step "<step>" --error "<msg>" # для failed/timeout
# --state-unknown # для timeout на шаге change
yaml_get.js
node scripts/yaml_get.js <params_file> <action> [args...]
# Actions:
# get <dot.path> → print value
# get-host-field <host> <field> → print host field (поддерживает вложенные пути)
# get-host-json <host> → print host as JSON
# get-via <host> → print via host IDs (newline-separated)
# list-hosts → print host IDs (one per line)
# check-version → exit 0 if config_version=1
# Зависимость: npm-модуль `yaml`
Общие правила для всех скриптов:
- Все пишут в лог-файл сессии автоматически (напрямую в файл, НЕ через 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>
Если НЕ откатывать:
⚠️ <риски оставить как есть>
💡 <возможные ручные действия>
Выполнить откат? (да / нет)
При state_unknown: true (timeout на шаге CHANGE) — особо предупредить:
⚠️ ВНИМАНИЕ: timeout во время записи файла — состояние может быть неконсистентным!
При 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/* |
| OpenClaw | openclaw.json |
Формат запроса:
⚠️ КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ: [тип]
📋 Файл: [путь]
🔍 Изменения: [diff]
✅ Бэкап готов: [путь]
Подтвердить изменение? (да / нет)
Логирование
Лог-файл сессии
Путь: ~/.openclaw/workspace/installer/logs/<session_id>.log
[2026-04-11T14:30:00Z] SESSION START: 20260411-143000_ha_automations-fix_a3f1
[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_a3f1","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):
<host.backup_dir>/YYYYMMDD-HHMMSS_<filename>.bak
<host.rollback_dir>/YYYYMMDD-HHMMSS_<desc>_rollback.sh
Пути backup_dir и rollback_dir — индивидуальные для каждого хоста (из parameters.yaml):
mva154: /home/slin/backups/openclaw (+ /rollback)
ruvpn-srv: /home/vpn/backups/openclaw (+ /rollback)
ha: /var/backups/openclaw (+ /rollback)
vpn-srv: /home/vpn/backups/openclaw (+ /rollback)
Локальная копия метаданных (всегда, для всех хостов):
~/.openclaw/workspace/installer/sessions/<session_id>/rollback_meta.json
# Содержит: список файлов, пути бэкапов, post_rollback_action
На 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_a3f1
# 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 — актуальная схема
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"
sudo: true
sudo_pass_env: "MVA154_SUDO_PASS"
health_check: "systemctl is-system-running"
post_check: "systemctl is-system-running"
backup_dir: "/home/slin/backups/openclaw"
rollback_dir: "/home/slin/backups/openclaw/rollback"
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"
sudo: true
# sudo_pass_env не нужен: настроен NOPASSWD в /etc/sudoers.d/vpn
health_check: null
post_check: null
backup_dir: "/home/vpn/backups/openclaw"
rollback_dir: "/home/vpn/backups/openclaw/rollback"
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"
via: [ruvpn-srv]
sudo: false
health_check: "ha core check"
post_check: "ha core check"
backup_dir: "/var/backups/openclaw"
rollback_dir: "/var/backups/openclaw/rollback"
# reload/restart — ответственность HA-скилла (через API/CLI), не installer'а
tags: [homeassistant, haos, critical]
vpn-srv:
label: "VPNSRV (homenet)"
type: ssh-chain
host: "192.168.2.200"
port: 22
user: "vpn"
auth_type: key
ssh_key_env: "VPNSRV_SSH_KEY"
via: [ruvpn-srv]
sudo: true
# sudo_pass_env не нужен: настроен NOPASSWD для пользователя vpn
health_check: "systemctl is-active frpc && systemctl is-active xray"
post_check: "systemctl is-active frpc && systemctl is-active xray"
backup_dir: "/home/vpn/backups/openclaw"
rollback_dir: "/home/vpn/backups/openclaw/rollback"
tags: [proxy, frp, xray, homenet]
localhost:
label: "OpenClaw container"
type: local
host: null
port: null
user: null
auth_type: null
ssh_key_env: null
sudo: false # sudo не установлен в контейнере
health_check: "node -e \"JSON.parse(require('fs').readFileSync('/home/node/.openclaw/openclaw.json'))\""
post_check: "pgrep -f openclaw-gateway > /dev/null && echo 'Gateway OK'"
tags: [local, openclaw]
storage:
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-хостов — на удалённом хосте (backup_dir/rollback_dir из hosts)
# Бэкапы для 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
lock_timeout_warn_minutes: 30
lock_timeout_force_minutes: 60
timeouts:
ssh_connect_timeout: 10
ssh_alive_interval: 15
ssh_alive_count_max: 3
command_timeout_default: 120
session:
id_format: "{YYYYMMDD-HHMMSS}_{host}_{description}_{XXXX}"
description_rules:
allowed_chars: "a-z0-9-"
max_length: 40
auto_normalize: true
fallback: "action-{XXXX}"
notifications:
on_success: false
on_failure: true
on_rollback: true
on_failure:
auto_rollback: false
ask_user: true
manager:
cleanup_cron: "0 3 * * *"
cleanup_heartbeat: true
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/*"
- pattern: "openclaw.json"
.env.example
# installer skill — подключения к хостам
# Все ключи и пароли только здесь, никогда в parameters.yaml или скриптах
# ruvpn-srv (ключ, NOPASSWD sudo)
RUVPN_SSH_KEY=/home/node/.openclaw/ha_ssh_key
# mva154 (пароль)
MVA154_PASSWORD=your_mva154_password
MVA154_SUDO_PASS=your_mva154_sudo_password
# ha (ключ)
HA_SSH_KEY=/home/node/.openclaw/ha_ssh_key
# vpn-srv (ключ, NOPASSWD sudo)
VPNSRV_SSH_KEY=/path/to/vpnsrv_key
# HA API (используется HA-скиллом, не installer'ом)
# HA_TOKEN=your_ha_long_lived_token
# HA_BOT_TOKEN=your_telegram_bot_token
checker.sh — проверка доступности хостов
Интерфейс
checker.sh [--host <host_id> [--host <host_id2> ...]] [--all]
# Exit 0 = все проверенные хосты доступны и секреты настроены
# Exit 1 = одна или более проверок не прошла
Уровни ошибок
| Код | Уровень | Смысл |
|---|---|---|
SECRET_MISSING |
Секрет не настроен | Переменная в .env отсутствует или пустая |
SECRET_INVALID |
Секрет некорректен | Файл ключа не найден / не читаем, токен <10 символов |
AUTH_FAILED |
Аутентификация провалилась | Ключ/пароль отвергнут хостом |
HOST_UNREACHABLE |
Хост недоступен | Timeout, refused, DNS не резолвится |
CHECK_FAILED |
Health check упал | Хост доступен, но health_check вернул ошибку |
Порядок проверок для каждого хоста
1. SECRET CHECK — все нужные переменные есть в .env и непустые?
1b. SECRET VALIDATE — значения корректны? (файл ключа существует, токен длинный)
2. CONNECTIVITY — хост достижим? (для ssh-chain: сначала каждый хоп в via[])
3. AUTH CHECK — аутентификация проходит?
4. SUDO CHECK — если sudo: true, проверить sudo доступность
5. HEALTH CHECK — выполнить health_check если задана
6. BACKUP DIR — проверить наличие backup_dir на хосте (из hosts.<id>.backup_dir)
Добавление нового хоста
- Добавить блок в
parameters.yaml:
new-host:
label: "Описание хоста"
type: ssh-direct # или ssh-chain, local
host: "IP_ADDRESS"
port: 22
user: "username"
auth_type: key # или password
ssh_key_env: "NEW_HOST_SSH_KEY" # или password_env
sudo: false
health_check: null
post_check: null
backup_dir: "~/backups/openclaw"
rollback_dir: "~/backups/openclaw/rollback"
tags: [tag1, tag2]
- Добавить ключ в
~/.openclaw/.env:
NEW_HOST_SSH_KEY=/path/to/key
- Проверить подключение:
scripts/checker.sh --host new-host
scripts/ssh_exec.sh --host new-host --cmd "echo OK"
- Настройка персистентности:
Installer создаст backup_dir/rollback_dir автоматически при первом запуске
backup.sh.
Добавление новых секретов
Только в ~/.openclaw/.env (никогда в parameters.yaml или скрипты):
NEW_SECRET=value
Формат ссылки в parameters.yaml: secret_env: "NEW_SECRET".
Управление (manager.sh)
Автоматическое — cron на OpenClaw
0 3 * * * ~/.openclaw/skills/installer/scripts/manager.sh --action cleanup
Вручную
manager.sh --action status
manager.sh --action list-sessions --days 7
manager.sh --action show-session --session <session_id>
manager.sh --action rollback --session <session_id>
Что реализует Dev
SKILL.md— инструкция для агентовparameters.yaml— хосты и настройки (уже заполнен).env.example— по шаблону вышеscripts/session.sh— создание сессии, lock, state machinescripts/backup.sh— бэкап файлов + генерация rollback.shscripts/verify.sh— health_check + post_checkscripts/rollback.sh— откат файлов без set -escripts/manager.sh— cleanup, status, list-sessions, show-session, rollbackscripts/checker.sh— проверка хостов с 5 уровнями ошибокscripts/ssh_exec.sh— SSH-хелпер: ssh-direct, ssh-chain, localscripts/lib.sh— общие функции (update_state, write_registry, remove_state)scripts/yaml_get.js— YAML query helper (Node.js)
Обязательные разделы SKILL.md
- Когда использовать (обязательно)
- Когда НЕ использовать
- Быстрый старт — 6 шагов
- Обновление state.json между шагами (lib.sh)
- Скрипты — справочник с сигнатурами
- Добавление нового хоста (включая backup_dir/rollback_dir)
- Добавление новых секретов
- Критические конфиги — подтверждение Славы
- Поведение при ошибке
- Управление (manager.sh)
- Логирование
- Troubleshooting
Не входит в скоуп Dev:
- Наполнение
parameters.yamlреальными IP/путями — уже готово - Настройка cron на OpenClaw — делает Слава/Стрим после установки
Changelog (от TZ 3.2 → 3.3)
| # | Что изменилось | Причина |
|---|---|---|
| 1 | Добавлен lib.sh в структуру скилла |
Фактически используется в SKILL.md, но отсутствовал в ТЗ |
| 2 | Добавлен yaml_get.js в структуру скилла |
Реализован, используется скриптами, отсутствовал в ТЗ |
| 3 | ruvpn-srv: убран sudo_pass_env |
На хосте настроен NOPASSWD |
| 4 | localhost: sudo: false, убран sudo_pass_env |
sudo не установлен в контейнере; добавлены health/post_check |
| 5 | Добавлен хост vpn-srv (ssh-chain через ruvpn-srv) |
Новый хост в реальном parameters.yaml |
| 6 | backup_dir/rollback_dir — индивидуальные для каждого хоста |
В ТЗ 3.2 были только глобальные; в реальности у каждого хоста свои |
| 7 | Убраны storage.backups_remote_dir / rollback_remote_dir |
Заменены на per-host backup_dir/rollback_dir |
| 8 | ha: убраны reload_commands |
Reload — ответственность HA-скилла, не installer'а |
| 9 | .env.example: убраны HA_TOKEN/HA_URL/RUVPN_SUDO_PASS/LOCALHOST_SUDO_PASS |
HA_TOKEN/HA_URL — HA-скилл; NOPASSWD хосты не требуют sudo_pass |
| 10 | openclaw.json добавлен в critical_configs |
Критичный конфиг OpenClaw |
| 11 | ssh_exec.sh: добавлен --session параметр |
Для логирования в контексте сессии |
| 12 | Добавлена backup_dir в шаблон «добавление нового хоста» |
Ранее отсутствовала |
| 13 | checker.sh: backup_dir берётся из hosts..backup_dir | Ранее проверял глобальный /var/backups/openclaw |
| 14 | Session ID в примерах: добавлен суффикс _XXXX | Для соответствия реальному формату |