Files
spaceshell/DOCS/RUNNING.md
T

223 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# spacesh — запуск и ручное тестирование
Практический ран-бук: как собрать, запустить и руками проверить всё, что реализовано (M0+M1, M2, M3, M4).
> Архитектура в двух словах: **демон `spaceshd`** владеет живыми PTY-сессиями и слушает Unix-socket; **GUI** (Tauri) и **CLI `spacesh`** — тонкие клиенты к нему. GUI и CLI поднимают демон лениво, если он не запущен.
---
## 1. Предусловия
- macOS (целевая платформа).
- Rust (стабильный) — `cargo --version`.
- Node ≥ 20 + npm — `node --version`, `npm --version`.
- Для GUI: системный WebView (есть в macOS из коробки).
Проверка тулчейна:
```bash
cargo --version && node --version && npm --version
```
---
## 2. Сборка
Из корня репозитория:
```bash
# Rust-крейты (демон, CLI, proto/core/pty)
cargo build
# Фронт-зависимости (один раз)
cd app && npm install && cd ..
```
Бинарники после `cargo build`:
- демон → `target/debug/spaceshd`
- CLI → `target/debug/spacesh`
Полный прогон автотестов (должно быть зелено):
```bash
cargo test --workspace
cd app && npm run build && cd .. # tsc + vite, проверяет фронт
```
---
## 3. Запуск GUI (основной путь)
Запускает Tauri-окно; демон поднимется лениво.
```bash
cd app && npm run tauri dev
```
Первый старт дольше (компиляция `src-tauri`). Откроется окно spacesh.
Базовый цикл в GUI:
1. **`+ New workspace`** (слева) → визард: укажи папку, выбери раскладку-пресет (1 / 2↔ / 2↕ / 2×2 / …), назначь агента на каждую панель (shell / claude / codex / gemini) → **Create workspace**.
2. Появится грид панелей с терминалами. Печатай — ввод уходит в PTY, вывод рисуется.
3. Слева — список воркспейсов (группы, счётчик панелей, кольцо статуса, точка «не забыть»).
4. Справа — **Event Center** (лента событий статуса).
---
## 4. Демон вручную (для CLI-тестов и отладки)
GUI поднимает демон сам, но для CLI-сценариев удобно держать **изолированный** демон на отдельном сокете через `SPACESH_SOCK` (чтобы не мешать «боевому»):
```bash
# терминал A — демон на временном сокете
SOCK=/tmp/spacesh-dev.sock
SPACESH_SOCK=$SOCK ./target/debug/spaceshd
# в логах: "spaceshd listening on /tmp/spacesh-dev.sock"
```
```bash
# терминал B — CLI к тому же сокету
SOCK=/tmp/spacesh-dev.sock
SPACESH_SOCK=$SOCK ./target/debug/spacesh status
```
Без `SPACESH_SOCK` сокет по умолчанию — `~/.spacesh/sock` (общий с GUI).
Остановить демон: `spacesh shutdown` (к нужному сокету) или `Ctrl-C` в терминале A.
### launchd (автоперезапуск, опционально)
```bash
./target/debug/spaceshd install-agent # ставит ~/Library/LaunchAgents/xyz.spacesh.daemon.plist (KeepAlive)
launchctl list | grep spacesh # проверить
# снять:
launchctl unload ~/Library/LaunchAgents/xyz.spacesh.daemon.plist
rm ~/Library/LaunchAgents/xyz.spacesh.daemon.plist
```
---
## 5. CLI `spacesh`
Все команды бьют в демон (лениво стартуют, если не запущен; `notify` — best-effort, без старта). Глобальный флаг `--json` — сырой ответ.
```bash
SOCK=/tmp/spacesh-dev.sock
S() { SPACESH_SOCK=$SOCK ./target/debug/spacesh "$@"; }
S open /tmp/myproject # → workspace_id (w_…)
S status # таблица: воркспейсы · панели (id, агент, running/stopped, статус)
S status --json # сырой JSON
S new-surface <w_id> # поднять панель (дефолт $SHELL) → surface_id (s_…)
S new-surface <w_id> --cmd claude
S split <s_id> --dir right # разбить панель соседней
S apply-preset <w_id> --preset 2x2 --agent claude --agent shell --agent shell --agent shell
S notify --surface <s_id> --state work # ← так же делают хуки агентов
S restart <s_id> # перезапустить stopped-панель
S close <s_id>
S group create --name production --color '#F4544E'
S set-meta <w_id> --group <g_id> --unread true
S close-workspace <w_id>
S completions zsh # скрипт автодополнения
S shutdown
```
Пресеты: `1 2lr 2tb 2+1 1+2 3 2x2 4 2x3 2x4`.
Состояния (`--state`): `work wait done error idle`.
---
## 6. Ручные сценарии по фичам
Запусти демон на `SPACESH_SOCK=/tmp/spacesh-dev.sock` (см. §4), GUI — `npm run tauri dev` (для GUI-проверок).
### M0 — живой терминал
1. GUI → `+ surface` (или `spacesh new-surface`).
2. Печатай `echo hi` → видишь вывод. Байты летают GUI↔демон↔PTY.
### M1 — переживаемость / reattach
1. В панели запусти долгое: `top` (или агента).
2. **Закрой окно GUI** (демон остаётся жив): проверь `pgrep spaceshd` и `ls ~/.spacesh/sock`.
3. Снова `npm run tauri dev` → панель на месте, экран восстановлен из снапшота, `top` продолжает обновляться.
### M2 — раскладки, воркспейсы, персист
1. Визард → пресет `2×2`, агенты на слоты → грид из 4 панелей.
2. Тяни **сплиттер** между панелями мышью — пропорции меняются.
3. Тулбар пресетов сверху — переразбивка активного воркспейса.
4. Закрой панель → дерево схлопывается.
5. **Холодный рестарт демона:** `spacesh shutdown` (или `pkill spaceshd`), затем снова открой GUI → воркспейсы и раскладка восстановлены с диска, панели показаны **`stopped`** с кнопкой **Restart** (autostart по умолчанию выкл). Жми Restart → панель поднимается.
6. Сайдбар: создай группу, перекрась, перетащи воркспейс (если drag включён), метка unread.
Состояние лежит в `~/.spacesh/state.json` (или своём, если задан `SPACESH_SOCK`-профиль — сокет отдельный, но state.json общий в `~/.spacesh`).
### M3 — статусы
1. **Claude-панель:** `new-surface <w> --cmd claude` (нужен установленный `claude` в PATH). Демон кладёт per-surface хуки в `~/.spacesh/hooks/<surface_id>/` и прокидывает `CLAUDE_CONFIG_DIR`. Поработай в агенте: кольцо панели меняется **work → wait → done** (Stop→done, Notification→wait, UserPromptSubmit→work). Глобальный `~/.claude/settings.json` не трогается.
2. **shell-панель (zsh) — OSC 133:** `new-surface <w>` (zsh по умолчанию). Запусти команду: пока идёт — кольцо **work**; завершилась с 0 — **done**; `false` (exit 1) — **error**. (Интеграция инжектится через `ZDOTDIR`; bash/fish — на эвристике fallback.)
3. **Эмуляция без агента:** `spacesh notify --surface <s_id> --state error` → кольцо краснеет, в `status` виден `error`.
4. **Нативные уведомления:** сверни/расфокусь окно GUI, доведи панель до `done/wait/error` → придёт macOS-уведомление (первый раз спросит разрешение). Клик по записи в **Event Center** фокусит панель.
5. **Авто-unread:** событие статуса в **неактивном** воркспейсе ставит ему синюю точку «не забыть»; выбор воркспейса снимает.
### SP2 — персистентный журнал событий / read-model
1. Доведи панель до `done`/`error` (или `spacesh notify --surface <s> --state error`). В Event Center появляется запись; бейдж на `bell` (в топ-баре) растёт.
2. **Перезапусти GUI** (демон жив): лента на месте — она берётся из демона, не из памяти GUI.
3. **Холодный рестарт демона** (`spacesh shutdown`, затем снова открой GUI): лента всё ещё на месте — восстановлена из `~/.spacesh/events.json`.
4. Клик по записи (или фокус её панели) помечает её прочитанной — запись тускнеет, бейдж уменьшается. `Mark all read` гасит бейдж полностью.
5. Табы фильтруют по реальным данным: `Unread` — только непрочитанные, `Errors` — только события `error`.
6. **Явное закрытие панели не логируется** (это намеренно — пользователь сам её закрыл); в журнал попадают только сами-завершившиеся/упавшие процессы и переходы статуса `done/wait/error`.
Файл журнала: `~/.spacesh/events.json` (кольцо на 1000 записей, атомарная запись + corrupt-backup, как у `state.json`).
### M4 — CLI
- `spacesh status --json` против живого демона; `spacesh notify` без демона → молча `exit 0`; `spacesh completions zsh` печатает скрипт.
---
## 7. Где что лежит / сброс
```
~/.spacesh/
sock # Unix-socket демона (или путь из $SPACESH_SOCK)
daemon.lock # single-instance лок
state.json # персист раскладок/воркспейсов/групп (M2)
hooks/<surface_id>/ # per-surface CLAUDE_CONFIG_DIR с settings.json (M3, чистится при close)
shellint/<surface_id>/ # per-surface ZDOTDIR с zsh OSC 133 (M3)
```
Полный сброс состояния (демон должен быть остановлен):
```bash
spacesh shutdown 2>/dev/null; pkill spaceshd 2>/dev/null
rm -rf ~/.spacesh # сбрасывает сокет, лок, state.json, hooks, shellint
```
---
## 8. Траблшутинг
- **`daemon unavailable` / CLI висит:** сокет битый. `pkill spaceshd; rm -f ~/.spacesh/sock` (или свой `$SPACESH_SOCK`), повтори.
- **«another spaceshd is already running»:** уже есть живой демон на этом сокете — это норма; используй его или `spacesh shutdown`.
- **GUI не видит демон / пусто:** проверь, что GUI и демон на одном сокете (если задавал `SPACESH_SOCK` для демона — задай тот же для GUI: `SPACESH_SOCK=… npm run tauri dev`).
- **GUI пишет «could not connect to spaceshd» (lazy-start не нашёл бинарь):** в `tauri dev` app-бинарь лежит в `app/src-tauri/target/` и демон ему не «сосед» — GUI ищет его по dev-пути в корневом `target/debug/spaceshd` (убедись, что сделан `cargo build -p spaceshd`). Можно явно указать: `SPACESHD_BIN=$PWD/target/debug/spaceshd npm run tauri dev`, либо просто подними демон сам перед GUI: `./target/debug/spaceshd &`. Окно теперь открывается даже без демона (не падает) — команды заработают, как только демон поднимется (перезапусти GUI).
- **Статусы у claude не меняются:** проверь, что `claude` в PATH, и что в `~/.spacesh/hooks/<sid>/settings.json` абсолютный путь к `spacesh` верный. Имена/формат хуков Claude Code дрейфуют по версиям — при несовпадении правится только `crates/spaceshd/src/hooks.rs`.
- **Нет уведомлений:** проверь разрешение macOS (Системные настройки → Уведомления → spacesh) и что окно действительно не в фокусе.
- **`npm run tauri dev` падает на компиляции:** прогони `cargo build -p spaceshd` отдельно, посмотри ошибку; затем `cd app && npm install`.
---
## 9. Известные ограничения (на сейчас)
- **Playwright/headless-браузер** видит только Vite-фронт (`npm run dev`, :1420) — Tauri-IPC там недоступен, живой daemon-флоу не тестируется. Полный e2e — только `npm run tauri dev` на дисплее.
- **OSC 133 — только zsh** (через `ZDOTDIR`); bash/fish работают на fallback-эвристике.
- **Клик по нативному уведомлению** не фокусит конкретную панель (клик по записи в Event Center — фокусит).
- **Event Center** — лента хранится в демоне и персистируется в `~/.spacesh/events.json` (переживает перезапуск GUI и холодный рестарт демона). Вкладки `Unread`/`Errors` и бейдж `bell` работают по реальным данным (флаги прочтения на уровне события). По-прежнему не реализованы: каналы Telegram/MAX в футере Event Center (SP5), а также `search`/`settings` и меню аккаунта в топ-баре.
- **Статус эфемерен** (work/wait/done/error/idle) — не персистится; после холодного рестарта демона панель `stopped`, статус `idle`.
- Авторизация / личный кабинет / внешние нотификации (Telegram/MAX) / зум / поиск по скроллбэку / diff-вьюер / remote — **не реализованы** (M5/M6/auth, см. `DOCS/MAIN.md`).
---
## 10. Быстрый smoke одной командой (без GUI)
```bash
cargo build
SOCK=/tmp/spacesh-smoke.sock
SPACESH_SOCK=$SOCK ./target/debug/spaceshd & DPID=$!
sleep 1
SPACESH_SOCK=$SOCK ./target/debug/spacesh open /tmp
SPACESH_SOCK=$SOCK ./target/debug/spacesh status
SPACESH_SOCK=$SOCK ./target/debug/spacesh shutdown
kill $DPID 2>/dev/null; rm -f $SOCK
```
Ожидаемо: `open` печатает `w_…`, `status` показывает воркспейс, `shutdown` гасит демон.