docs: manual run & test guide (build, daemon, GUI, CLI, per-milestone checks)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+211
@@ -0,0 +1,211 @@
|
|||||||
|
# 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:** событие статуса в **неактивном** воркспейсе ставит ему синюю точку «не забыть»; выбор воркспейса снимает.
|
||||||
|
|
||||||
|
### 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`).
|
||||||
|
- **Статусы у 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** — плоская лента (вкладки All/Unread/Errors пока без фильтра); живёт в памяти GUI, очищается при перезапуске GUI.
|
||||||
|
- **Статус эфемерен** (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` гасит демон.
|
||||||
Reference in New Issue
Block a user