@agfpd/iapeer 0.2.97 → 0.2.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/docs/01-overview.md +86 -0
- package/docs/02-quickstart.md +110 -0
- package/docs/03-peers-runtimes-identity.md +117 -0
- package/docs/04-lifecycle-and-daemon.md +101 -0
- package/docs/05-system-prompt.md +82 -0
- package/docs/06-storage.md +115 -0
- package/docs/07-install-and-update.md +87 -0
- package/docs/08-peer-management.md +119 -0
- package/docs/09-runtimes.md +103 -0
- package/docs/10-messaging.md +69 -0
- package/docs/11-extensions-and-memory.md +60 -0
- package/docs/12-cli-reference.md +96 -0
- package/docs/13-architecture-and-contract.md +190 -0
- package/docs/14-configuration-and-logs.md +91 -0
- package/docs/README.md +97 -0
- package/docs/ru/01-/320/276/320/261/320/267/320/276/321/200.md +86 -0
- package/docs/ru/02-/320/261/321/213/321/201/321/202/321/200/321/213/320/271-/321/201/321/202/320/260/321/200/321/202.md +110 -0
- package/docs/ru/03-/320/277/320/270/321/200/321/213-/321/200/320/260/320/275/321/202/320/260/320/271/320/274/321/213-/320/270/320/264/320/265/320/275/321/202/320/270/321/207/320/275/320/276/321/201/321/202/321/214.md +117 -0
- package/docs/ru/04-/320/266/320/270/320/267/320/275/320/265/320/275/320/275/321/213/320/271-/321/206/320/270/320/272/320/273-/320/270-/320/264/320/265/320/274/320/276/320/275.md +101 -0
- package/docs/ru/05-/321/201/320/270/321/201/321/202/320/265/320/274/320/275/321/213/320/271-/320/277/321/200/320/276/320/274/320/277/321/202.md +82 -0
- package/docs/ru/06-/321/205/321/200/320/260/320/275/320/265/320/275/320/270/320/265.md +115 -0
- package/docs/ru/07-/321/203/321/201/321/202/320/260/320/275/320/276/320/262/320/272/320/260-/320/270-/320/276/320/261/320/275/320/276/320/262/320/273/320/265/320/275/320/270/320/265.md +87 -0
- package/docs/ru/08-/321/203/320/277/321/200/320/260/320/262/320/273/320/265/320/275/320/270/320/265-/320/277/320/270/321/200/320/260/320/274/320/270.md +119 -0
- package/docs/ru/09-/321/200/320/260/320/275/321/202/320/260/320/271/320/274/321/213.md +103 -0
- package/docs/ru/10-/320/276/320/261/320/274/320/265/320/275-/321/201/320/276/320/276/320/261/321/211/320/265/320/275/320/270/321/217/320/274/320/270.md +69 -0
- package/docs/ru/11-/321/200/320/260/321/201/321/210/320/270/321/200/320/265/320/275/320/270/321/217-/320/270-/320/277/320/260/320/274/321/217/321/202/321/214.md +60 -0
- package/docs/ru/12-cli-reference.md +96 -0
- package/docs/ru/13-/320/260/321/200/321/205/320/270/321/202/320/265/320/272/321/202/321/203/321/200/320/260-/320/270-/320/272/320/276/320/275/321/202/321/200/320/260/320/272/321/202.md +190 -0
- package/docs/ru/14-/320/272/320/276/320/275/321/204/320/270/320/263/321/203/321/200/320/260/321/206/320/270/321/217-/320/270-/320/266/321/203/321/200/320/275/320/260/320/273/321/213.md +91 -0
- package/docs/ru/README.md +97 -0
- package/package.json +4 -2
- package/src/cli/index.ts +18 -5
- package/src/launch/adapters/claude.ts +16 -1
- package/src/onboard/index.ts +111 -14
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# 03 — Пиры, рантаймы, идентичность
|
|
2
|
+
|
|
3
|
+
**Русский** · [English](../03-peers-runtimes-identity.md)
|
|
4
|
+
|
|
5
|
+
Углублённая версия трёх базовых понятий из [обзора](01-обзор.md) — с уровнем детализации, нужным, чтобы создавать пиров, переключать их между рантаймами и читать профили.
|
|
6
|
+
|
|
7
|
+
## Пир
|
|
8
|
+
|
|
9
|
+
Пир — участник системы. У него есть рабочая папка, имя, набор рантаймов и природа.
|
|
10
|
+
|
|
11
|
+
### Имя (personality)
|
|
12
|
+
|
|
13
|
+
Имя — короткое логическое имя в нижнем регистре: `assistant`, `code-reviewer`, `timer`. Формат по шаблону `^[a-z][a-z0-9-]{0,31}$`: буква, дальше буквы, цифры, дефисы, до 32 символов.
|
|
14
|
+
|
|
15
|
+
Имя не хранится отдельной величиной — оно **выводится из имени рабочей папки**. Папка `~/.iapeer/peers/assistant` даёт имя `assistant`; не-латиница и прочие символы нормализуются к допустимому виду.
|
|
16
|
+
|
|
17
|
+
Отсюда жёсткое правило: **имя и рабочая папка связаны один к одному**. Занятое другим пиром имя — отказ, без молчаливого суффикса для развода коллизии. Имя — это адрес, и он обязан быть однозначным.
|
|
18
|
+
|
|
19
|
+
### Природа (intelligence)
|
|
20
|
+
|
|
21
|
+
Каждый пир несёт атрибут природы. Он едет вместе с сообщением, поэтому получатель знает, с кем говорит, не полагаясь на догадки.
|
|
22
|
+
|
|
23
|
+
| Значение | Кто это |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `artificial` | AI-агент (Claude, Codex) |
|
|
26
|
+
| `natural` | человек (через Telegram и подобные каналы) |
|
|
27
|
+
| `absent` | программа без интеллекта (планировщик, наблюдатель, веб-хук) |
|
|
28
|
+
|
|
29
|
+
Природа по умолчанию выводится из рантайма: агентские рантаймы дают `artificial`, Telegram — `natural`, notifier — `absent`. При создании пира её можно задать явно.
|
|
30
|
+
|
|
31
|
+
Раньше использовались значения `human` и `scripted` — они и сейчас читаются (понимаются как `natural` и `absent` соответственно), но при записи система сохраняет то значение, что уже стоит в профиле, и не переписывает уже заведённых пиров.
|
|
32
|
+
|
|
33
|
+
## Рантайм
|
|
34
|
+
|
|
35
|
+
Рантайм — способ присутствия пира. Имя рантайма короче имени пира: формат `^[a-z][a-z0-9]{0,31}$`, без дефисов. Это важно для разбора идентичности (ниже).
|
|
36
|
+
|
|
37
|
+
### Два класса рантаймов
|
|
38
|
+
|
|
39
|
+
**claude, codex** — AI-агенты. Сессию не держат запущенной постоянно: демон поднимает агента на входящее сообщение и закрывает сессию после часа простоя. Этим рантаймом демон управляет полностью — поднимает и усыпляет.
|
|
40
|
+
|
|
41
|
+
**telegram, notifier** — рантаймы-роутеры, не AI. Telegram-мост принимает входящие в любой момент, notifier срабатывает по расписанию — поэтому работают постоянно под launchd с автоперезапуском. Такой рантайм демон **не трогает**: пробуждение и усыпление к нему неприменимы, его жизненным циклом распоряжается launchd. Это инфраструктурные рантаймы.
|
|
42
|
+
|
|
43
|
+
Деление — граница ответственности: демон отвечает за claude/codex, launchd — за инфраструктурные. Команды `stop`/`start`/`new` работают по-разному в зависимости от класса (см. [08 — Управление пирами](08-управление-пирами.md)).
|
|
44
|
+
|
|
45
|
+
### Несколько рантаймов у одного пира
|
|
46
|
+
|
|
47
|
+
Пир может объявлять несколько рантаймов. В профиле это два поля:
|
|
48
|
+
|
|
49
|
+
- **`runtimes`** — список всех рантаймов, на которых пир может присутствовать, например `["claude", "codex"]`.
|
|
50
|
+
- **`default_runtime`** — основной рантайм: куда по умолчанию маршрутизируются сообщения и какой рантайм поднимается первым.
|
|
51
|
+
|
|
52
|
+
Добавить пиру рантайм и сделать его основным — разные операции. Первая (`add-runtime`) расширяет возможности, не меняя маршрутизацию; вторая (`default-runtime`) переключает основной рантайм. Массовое переключение `default_runtime` по всем пирам — это и есть момент «пересадки» команды с одного агентского рантайма на другой. См. [09 — Рантаймы](09-рантаймы.md).
|
|
53
|
+
|
|
54
|
+
Список рантаймов пополняется и сам: при инициализации пира система сканирует его папку на признаки рантаймов (каталоги `.claude`, `.codex` и подпапки в `runtimes/`) и добавляет найденное в `runtimes`. Так список остаётся актуальным, даже когда рантайм появился у пира органически, а не был объявлен явно.
|
|
55
|
+
|
|
56
|
+
## Идентичность
|
|
57
|
+
|
|
58
|
+
Идентичность — адрес конкретного живого присутствия пира. Складывается из рантайма и имени через дефис:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
PEER_IDENTITY = PEER_RUNTIME + "-" + PEER_PERSONALITY
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Пир `assistant` в Claude — это `claude-assistant`; он же в Codex — `codex-assistant`. Имя одно, идентичности две.
|
|
65
|
+
|
|
66
|
+
Разбор идентичности идёт **по первому дефису**: всё слева — рантайм, всё справа — имя. Поскольку в имени рантайма дефисов нет, а в имени пира они допустимы, разбор однозначен: `claude-code-reviewer` — это рантайм `claude` и пир `code-reviewer`.
|
|
67
|
+
|
|
68
|
+
### ABI идентичности
|
|
69
|
+
|
|
70
|
+
Три значения образуют ABI — набор переменных окружения, по которым любой компонент системы понимает, кто он:
|
|
71
|
+
|
|
72
|
+
| Переменная | Значение | Пример |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `PEER_PERSONALITY` | имя пира | `assistant` |
|
|
75
|
+
| `PEER_RUNTIME` | рантайм текущей сессии | `claude` |
|
|
76
|
+
| `PEER_IDENTITY` | полный адрес | `claude-assistant` |
|
|
77
|
+
|
|
78
|
+
Когда демон поднимает сессию агента, он передаёт эти переменные в окружение. Агент по ним знает своё имя и от чьего лица отправляет сообщения. Рантайм текущей сессии определяется в таком порядке: из заголовка идентичности (`PEER_IDENTITY`), затем из профиля, затем из `PEER_RUNTIME`, и в последнюю очередь — автоопределением по признакам рантайма в окружении: Codex выставляет `CODEX_THREAD_ID`, Claude — `CLAUDECODE=1`. Если не сработал ни один источник, подъём отклоняется с требованием задать `PEER_RUNTIME` или завести профиль.
|
|
79
|
+
|
|
80
|
+
## Профиль и реестр
|
|
81
|
+
|
|
82
|
+
У идентичности два места хранения, и важно понимать, какое из них главное.
|
|
83
|
+
|
|
84
|
+
### Профиль пира — источник истины
|
|
85
|
+
|
|
86
|
+
`<папка-пира>/.iapeer/peer-profile.json` — это карточка пира, единственный источник истины о нём. Вот реальный профиль:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"personality": "assistant",
|
|
91
|
+
"runtimes": ["codex", "claude"],
|
|
92
|
+
"default_runtime": "claude",
|
|
93
|
+
"runtime": "claude",
|
|
94
|
+
"description": "Помощник общего профиля. Принимает задачи через IAP.",
|
|
95
|
+
"intelligence": "artificial",
|
|
96
|
+
"initial_prompt": "Новая сессия. Поздоровайся с владельцем.",
|
|
97
|
+
"interfaces": {
|
|
98
|
+
"telegram": { "bot": "assistant", "bot_username": "assistant_bot" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Поля профиля делятся на три класса по тому, кто ими владеет:
|
|
104
|
+
|
|
105
|
+
- **Ядро идентичности** — `personality`, `default_runtime`, `runtimes`, `description`, `intelligence`, `initial_prompt`. Этим владеет ядро и жизненный цикл. Поле `initial_prompt` — затравка первого хода свежей сессии. Поле `runtime` рядом с `default_runtime` — это синхронная копия для совместимости со старыми читателями; оба пишутся согласованно.
|
|
106
|
+
- **Приватная конфигурация плагина** — секции вроде `expansion` (шорткаты-алиасы), `notifier` (настройка пира-планировщика) и прочие, по одной на плагин. Читаются только из локального профиля, в реестр не попадают.
|
|
107
|
+
- **Публичный паспорт** — секция `interfaces` (например `interfaces.telegram.bot`). Это атрибуты маршрутизации, и они проецируются в реестр.
|
|
108
|
+
|
|
109
|
+
Описание `description` ограничено по длине (сейчас 450 символов) — оно само-документирующее, его видят другие пиры в реестре.
|
|
110
|
+
|
|
111
|
+
### Реестр — производная
|
|
112
|
+
|
|
113
|
+
`~/.iapeer/peers-profiles.json` — общий реестр всех пиров. Демон читает его, чтобы понять, кому доставлять сообщение и какие у пира рантаймы. Запись реестра содержит проекцию профиля: имя, основной рантайм, список рантаймов, описание, природа, рабочая папка, публичные интерфейсы.
|
|
114
|
+
|
|
115
|
+
Реестр — не источник истины, а **проекция** локальных профилей. Его можно пересобрать из профилей в любой момент (`iapeer verify --fix`). Если реестр и профиль разошлись, прав профиль — реестр чинится по нему.
|
|
116
|
+
|
|
117
|
+
Реестр пишется только через единственного писателя под файловой блокировкой — прямое редактирование `peers-profiles.json` запрещено на уровне ядра. Это исключает гонки, когда несколько процессов одновременно меняют состав пиров. Подробнее о раскладке и владении — [06 — Хранение](06-хранение.md).
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# 04 — Жизненный цикл и демон
|
|
2
|
+
|
|
3
|
+
**Русский** · [English](../04-lifecycle-and-daemon.md)
|
|
4
|
+
|
|
5
|
+
## Демон-роутер
|
|
6
|
+
|
|
7
|
+
Демон — единственный постоянно работающий процесс ядра. У него три роли:
|
|
8
|
+
|
|
9
|
+
1. **Маршрутизация** — принимает `send_to_peer`, находит получателя в реестре, доставляет.
|
|
10
|
+
2. **Пробуждение** — поднимает сессию спящего получателя и доставляет после подъёма.
|
|
11
|
+
3. **Надзор** — раз в минуту обходит пиров: усыпляет простаивающих, замечает упавших, перезапускает помеченных на свежий старт.
|
|
12
|
+
|
|
13
|
+
Демон держит launchd по плисту `com.agfpd.iapeer` — отдельному от плистов пиров. launchd поднимает демон при загрузке системы и перезапускает при падении, поэтому роутер переживает и перезагрузку машины, и собственные сбои.
|
|
14
|
+
|
|
15
|
+
Слушает демон на двух входах одновременно:
|
|
16
|
+
|
|
17
|
+
- **локальный сокет** `~/.iapeer/state/iapeer/router.sock` (доступ только владельцу) — для CLI и рантаймов-роутеров;
|
|
18
|
+
- **TCP на петле** `127.0.0.1:8765` — для агентов Claude и Codex, подключённых к `send_to_peer` по HTTP.
|
|
19
|
+
|
|
20
|
+
### Надзорный тик
|
|
21
|
+
|
|
22
|
+
Раз в минуту демон выполняет надзорный обход. За один тик он усыпляет агентов, простоявших дольше часа; замечает умершие сессии и решает их судьбу; поднимает свежими помеченных на немедленный перезапуск; докармливает очереди эфемерных воркеров. Минута — это потолок задержки для отложенной работы: очередь, накопившаяся за окно перезапуска демона, разгребётся на ближайшем тике.
|
|
23
|
+
|
|
24
|
+
## Состояния пира
|
|
25
|
+
|
|
26
|
+
Агент claude/codex в каждый момент в одном из трёх состояний:
|
|
27
|
+
|
|
28
|
+
| Состояние | Что значит | Метка в `iapeer list` |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| **живой** | сессия запущена и отвечает | `●` |
|
|
31
|
+
| **спит** | сессии нет, пир готов проснуться по сообщению | `○` |
|
|
32
|
+
| **остановлен** | оператор поставил стоп-флаг; демон не будит | `✕` |
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
сообщение
|
|
36
|
+
спит ──────────────▶ живой
|
|
37
|
+
▲ │
|
|
38
|
+
│ снят стоп-флаг │ час простоя / падение
|
|
39
|
+
│ ▼
|
|
40
|
+
остановлен ◀────────── живой
|
|
41
|
+
стоп (оператор)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Доставка и пробуждение
|
|
45
|
+
|
|
46
|
+
На входящее сообщение демон смотрит на состояние получателя:
|
|
47
|
+
|
|
48
|
+
- **живой** — вставляет в сессию, подтверждает приём, отвечает `доставлено`;
|
|
49
|
+
- **спит** — поднимает сессию, перепроверяет получателя, доставляет, отвечает `доставлено, разбудил`;
|
|
50
|
+
- **остановлен** — отказывает и сообщает отправителю прямо.
|
|
51
|
+
|
|
52
|
+
Гарантию «`доставлено` значит легло в живую сессию» разбирает [10 — Обмен сообщениями](10-обмен-сообщениями.md).
|
|
53
|
+
|
|
54
|
+
### Свежий старт или продолжение диалога
|
|
55
|
+
|
|
56
|
+
Поднимая спящего, демон выбирает: начать **свежую** сессию или **продолжить** прежний диалог. Зависит от того, чем будят, от типа пира и — для агентов — от **топика** входящего сообщения.
|
|
57
|
+
|
|
58
|
+
Три типа пира — три базовых поведения: **эфемерный воркер** всегда стартует свежим, **человек** (Telegram) всегда продолжает, **агент** продолжает только по совпавшему топику. Полная развёртка:
|
|
59
|
+
|
|
60
|
+
| Чем разбудили | Условие | Результат |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `iapeer <runtime>` (из папки) | — | 🆕 свежий |
|
|
63
|
+
| `iapeer attach` | — | ▶️ продолжение (ошибка, если продолжать нечего) |
|
|
64
|
+
| сообщение | упал / закрылся сам / никогда не запускался | 🆕 свежий |
|
|
65
|
+
| сообщение | усыплён за простой, пир-**человек** (Telegram) | ▶️ продолжение (всегда) |
|
|
66
|
+
| сообщение | усыплён за простой, **агент** · топик совпал с нитью сессии | ▶️ продолжение |
|
|
67
|
+
| сообщение | усыплён за простой, **агент** · топик новый или его нет | 🆕 свежий |
|
|
68
|
+
| сообщение | пир остановлен (`stop`) | 🚫 не будится — явный отказ |
|
|
69
|
+
|
|
70
|
+
Ключевое для агентов: **продолжение включается явно — через топик**. Сообщение без топика после простоя поднимает свежую сессию, а не воскрешает старый контекст (иначе оживёт сессия многодневной давности и продолжит не ту работу). Чтобы продолжить нужную нить — отправитель указывает её топик. Топики **копятся**: в одной сессии может идти несколько нитей, и пинг по любой из них (не только последней) продолжит её. Для пиров-людей продолжение всегда — топик их не сбрасывает.
|
|
71
|
+
|
|
72
|
+
Ленивый сброс на свежесть — **`iapeer refresh`** (см. «Команды жизненного цикла» ниже): помечает пира, и на его ближайшем естественном пробуждении он стартует свежим вместо продолжения — для любого типа, включая людей-в-Telegram. Так после обновления доктрины команда перечитывает её сама собой, без ручного `/new` и без массового пробуждения.
|
|
73
|
+
|
|
74
|
+
## Усыпление при простое
|
|
75
|
+
|
|
76
|
+
Агент, не проявлявший активности дольше часа, усыпляется на ближайшем надзорном тике: демон закрывает сессию и ставит метку усыпления. Память освобождается, диалог сохраняется на диске — следующее сообщение **с топиком той работы** поднимет агента ровно там, где он остановился (без топика или с новым — свежим, см. выше). Час простоя — единственная временна́я граница: «слепого» TTL по времени жизни нет. Порог простоя по умолчанию — 3600 секунд; он и другие пороги жизненного цикла (загрузка, готовность, защита от цикла перезапусков) настраиваются переменными окружения — [14 — Конфигурация и журналы](14-конфигурация-и-журналы.md).
|
|
77
|
+
|
|
78
|
+
## Надзор и устойчивость
|
|
79
|
+
|
|
80
|
+
**Защита от цикла перезапусков.** Демон ведёт кольцо последних смертей каждого пира. Если за 5 минут пир умирает 3 раза, демон перестаёт его поднимать и оставляет спящим — это сигнал неисправности, а не сотая попытка вслепую.
|
|
81
|
+
|
|
82
|
+
**Сериализация пробуждений.** На спящего агента может прилететь несколько сообщений сразу; поднять его должна одна попытка, иначе родятся две конкурирующие сессии. Демон берёт блокировку на пробуждение конкретного пира — остальные ждут готовности первой.
|
|
83
|
+
|
|
84
|
+
**Посмертная диагностика.** Супервизор в момент смерти сессии фиксирует её причину (код выхода / сигнал) — это то, что обычная проверка «жив ли процесс» не ловит. На этот случай отдельный сторож (canary) держит блокирующее соединение с супервизором и при смерти сессии записывает обстоятельства — память, swap, список процессов, отчёты диагностики — в журнал.
|
|
85
|
+
|
|
86
|
+
## Граница: claude/codex против инфраструктурных
|
|
87
|
+
|
|
88
|
+
Демон управляет только агентами claude/codex и только теми, чей плист принадлежит ядру. Инфраструктурные пиры (Telegram, notifier) и любые пиры под чужим launchd-плистом для демона неприкосновенны: он их не будит, не усыпляет, не перезапускает. Их жизненным циклом распоряжается launchd.
|
|
89
|
+
|
|
90
|
+
Поэтому `stop`, `start`, `new` отказываются трогать чужие launchd-пиры — для них ядро только читатель. Это защита от драки ядра с launchd за управление процессом.
|
|
91
|
+
|
|
92
|
+
## Команды жизненного цикла
|
|
93
|
+
|
|
94
|
+
- **`iapeer stop` / `start`** — остановить (стоп-флаг + закрыть сессию) и снять остановку. Для инфраструктурных — через launchd.
|
|
95
|
+
- **`iapeer new`** — безусловный свежий перезапуск, аварийный рычаг для зависшей или мёртвой сессии; действует механически, в обход агента.
|
|
96
|
+
- **`iapeer refresh <пир> \| --all`** — ленивый «мягкий перезапуск»: пометить пира на свежий старт при его СЛЕДУЮЩЕМ естественном пробуждении (перечитать доктрину/фрагменты с диска), не убивая живую сессию и не будя остальных. В отличие от `new` (немедленный жёсткий перезапуск) — это отложенный, ненавязчивый рычаг: применяют после релиза доктрины, чтобы команда освежилась сама собой. Маркер перекрывает «продолжение» для всех типов пиров, включая людей-в-Telegram (которые иначе продолжают старую сессию до явного `/new`).
|
|
97
|
+
- **`iapeer compact`** — сжать диалог; спящего после усыпления демон сначала продолжит, потом сожмёт.
|
|
98
|
+
- **`iapeer interrupt`** — прервать текущий ход (Escape), не теряя контекст.
|
|
99
|
+
- **`iapeer self-fresh` / `self-done`** — самовызовы агента: пометить себя на свежий перезапуск или тихо завершиться (для эфемерных воркеров).
|
|
100
|
+
|
|
101
|
+
Поведение по классам рантаймов — [08 — Управление пирами](08-управление-пирами.md); эфемерные воркеры — [10 — Обмен сообщениями](10-обмен-сообщениями.md).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 05 — Идентичность и системный промпт
|
|
2
|
+
|
|
3
|
+
**Русский** · [English](../05-system-prompt.md)
|
|
4
|
+
|
|
5
|
+
Чтобы агент знал своё имя, роль и соседей по команде, iapeer собирает ему системный промпт — текст, который рантайм получает до начала диалога. Промпт складывается из слоёв: часть пишет человек, часть собирает система.
|
|
6
|
+
|
|
7
|
+
## Зачем подменять системный промпт
|
|
8
|
+
|
|
9
|
+
У Claude и Codex есть собственный встроенный системный промпт — тот, что делает их «ассистентом по коду». iapeer заменяет его целиком: вместо родного промпта рантайм получает собранный iapeer текст, где агент описан как пир команды — со своим именем, ролью, знанием о соседях и общими правилами.
|
|
10
|
+
|
|
11
|
+
Замена идёт штатным механизмом каждого рантайма:
|
|
12
|
+
|
|
13
|
+
- **Claude** — флагом `--system-prompt-file <файл>`;
|
|
14
|
+
- **Codex** — настройкой `model_instructions_file` (`codex -c model_instructions_file=<файл>`), которая подменяет компилированные базовые инструкции.
|
|
15
|
+
|
|
16
|
+
Инфраструктурные рантаймы (Telegram, notifier) системный промпт не получают — это не языковые модели, им нечего им наполнять.
|
|
17
|
+
|
|
18
|
+
## Слои промпта
|
|
19
|
+
|
|
20
|
+
Промпт собирается из слоёв, от общего к частному. Каждый слой при пустом содержимом просто выпадает — лишних разделов не появляется.
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
1. Блок идентичности (YAML) ← система, всегда свежий
|
|
24
|
+
2. Доктрина IAPEER.md ← человек: общая + персональная
|
|
25
|
+
3. Реестр пиров ← система: кто ещё в команде
|
|
26
|
+
4. Доменные файлы (любой .md по имени)← человек/плагин
|
|
27
|
+
5. Доктринальные фрагменты ← плагины-примитивы, машинно
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Слой 1 — блок идентичности
|
|
31
|
+
|
|
32
|
+
Динамический YAML-блок в начале промпта. Система синтезирует его при каждом запуске из профиля пира и фактов о хосте: имя, описание, рабочая папка, платформа, версия ОС, пользователь, имя машины, сегодняшняя дата. Значения экранируются, поэтому двоеточия, кавычки и переводы строк в описании не ломают разметку.
|
|
33
|
+
|
|
34
|
+
Этот слой всегда свежий — дата и факты о хосте подставляются заново на каждый запуск.
|
|
35
|
+
|
|
36
|
+
### Слой 2 — доктрина IAPEER.md
|
|
37
|
+
|
|
38
|
+
Главный слой, который наполняет человек, — для пира то же, что `CLAUDE.md` для проекта: постоянные инструкции и характер агента. Это два файла:
|
|
39
|
+
|
|
40
|
+
- **Общая доктрина** `~/.iapeer/IAPEER.md` — правила, действующие для всех пиров хоста: как отвечать на обращения, как обращаться с памятью, общие договорённости команды.
|
|
41
|
+
- **Персональная доктрина** `<папка-пира>/.iapeer/IAPEER.md` — роль и характер конкретного пира: кто он, чем занимается, какие у него зоны ответственности.
|
|
42
|
+
|
|
43
|
+
Порядок — от общего к частному: сначала общая доктрина, затем персональная. Частное идёт после общего и уточняет его. Каждый файл вставляется как отдельная помеченная секция со своей меткой-путём (HTML-комментарий с путём файла, видимый модели), чтобы агент понимал, откуда взялось каждое правило.
|
|
44
|
+
|
|
45
|
+
Оба файла редактирует оператор обычным markdown. Это и есть то место, где задаётся, кем будет агент.
|
|
46
|
+
|
|
47
|
+
### Слой 3 — реестр пиров
|
|
48
|
+
|
|
49
|
+
Система вставляет краткую сводку по всем пирам команды: имя, основной рантайм, список рантаймов, описание, природа. Так агент знает, к кому он может обратиться через `send_to_peer` и кто чем занимается. Сводка отсортирована по именам; если пиров нет — слой выпадает.
|
|
50
|
+
|
|
51
|
+
Здесь и работает само-документирующее `description` из профиля: оно ограничено по длине именно потому, что попадает в промпт каждого пира — раздутые описания стоили бы контекста всей команде.
|
|
52
|
+
|
|
53
|
+
Для сервис-пира — скажем, `timer` и `watcher` нотифаера — это описание несёт сам формат вызова: JSON-поля и пример. Так агент умеет управлять сервисом из одного своего промпта, а когда нужно больше — просто спрашивает сервис (на `help` тот отвечает полным форматом). Реестр учит вызову; остальное пир документирует сам.
|
|
54
|
+
|
|
55
|
+
### Слой 4 — доменные файлы по имени
|
|
56
|
+
|
|
57
|
+
Любой markdown-файл в корне `.iapeer/`, кроме `IAPEER.md`, едет в промпт своей секцией. Этот канал открыт и вам, не только плагинам: положите `SOUL.md` (или любое имя) в `~/.iapeer/` — его увидит каждый пир хоста; положите в папку `.iapeer/` одного пира — увидит только он. Выдать агенту дополнительный контекст — это просто добавить файл.
|
|
58
|
+
|
|
59
|
+
Как и доктрина, каждый файл бывает общим (`~/.iapeer/<ДОМЕН>.md`) и персональным (`<папка-пира>/.iapeer/<ДОМЕН>.md`), вставляется от общего к частному. Сканируется только корень `.iapeer/` — не подпапки. Файлы идут в детерминированном порядке (по алфавиту имени), каждый — отдельной помеченной секцией, пустые выпадают. Плагины пользуются этим же каналом для своих настроек (`<ДОМЕН>.md` на плагин); ваши файлы вручную и файлы плагина стоят бок о бок.
|
|
60
|
+
|
|
61
|
+
### Слой 5 — доктринальные фрагменты
|
|
62
|
+
|
|
63
|
+
Последний слой — фрагменты, которые генерируют машинно плагины-примитивы. Они лежат в подпапке `fragments/` обоих корней (`~/.iapeer/fragments/` и `<папка-пира>/.iapeer/fragments/`). Например, провайдер памяти кладёт сюда актуальную карту памяти агента.
|
|
64
|
+
|
|
65
|
+
Этот слой — самый изменчивый, поэтому он идёт последним и **перечитывается на каждом свежем подъёме сессии**. Когда у агента меняется память или другой машинный контекст, обновлённый фрагмент попадёт в промпт при следующем запуске.
|
|
66
|
+
|
|
67
|
+
## Сборка и неизменность в сессии
|
|
68
|
+
|
|
69
|
+
Слои склеиваются в один файл с пустой строкой между секциями. Этот файл и передаётся рантайму флагом подмены промпта.
|
|
70
|
+
|
|
71
|
+
Системный промпт фиксируется в момент подъёма сессии и не меняется, пока сессия живёт. Когда демон поднимает агента заново — свежим или продолжая прежний диалог, — он пересобирает файл, поэтому свежие фрагменты слоя 5 и актуальная дата подтягиваются на каждом подъёме. Это проверено: продолжение диалога переприменяет новый файл промпта, а не тянет старый.
|
|
72
|
+
|
|
73
|
+
Подмена доставляет системный промпт iapeer — личность пира. Она не отключает собственные нативные файлы инструкций рантайма: `CLAUDE.md` (Claude) или `AGENTS.md` (Codex) работают ровно так же, как вне iapeer, — читаются как постоянные инструкции, которым модель следует. Это два независимых канала — личность iapeer через подмену и нативные `CLAUDE.md`/`AGENTS.md` рантайма — и действуют они вместе. Так что если вы приносите готовый проект, где уже есть `CLAUDE.md`, он продолжит работать рядом с промптом iapeer; а сами эти файлы работают и без iapeer вообще.
|
|
74
|
+
|
|
75
|
+
## Что наполнять вам
|
|
76
|
+
|
|
77
|
+
Из всех слоёв руками наполняют два файла:
|
|
78
|
+
|
|
79
|
+
- `~/.iapeer/IAPEER.md` — общие правила команды (один раз на хост);
|
|
80
|
+
- `<папка-пира>/.iapeer/IAPEER.md` — роль каждого пира.
|
|
81
|
+
|
|
82
|
+
Остальное — блок идентичности, реестр, фрагменты — система и плагины собирают сами. Раскладку каталогов, где лежат эти файлы, разбирает [06 — Хранение](06-хранение.md).
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# 06 — Хранение
|
|
2
|
+
|
|
3
|
+
**Русский** · [English](../06-storage.md)
|
|
4
|
+
|
|
5
|
+
Состояние iapeer лежит в двух местах: общий каталог хоста `~/.iapeer/` и каталог `.iapeer/` внутри рабочей папки каждого пира.
|
|
6
|
+
|
|
7
|
+
## Два уровня хранения
|
|
8
|
+
|
|
9
|
+
**Общий уровень** `~/.iapeer/` — всё, что касается хоста целиком: реестр пиров, состояние демона, общие настройки, установленные пакеты рантаймов.
|
|
10
|
+
|
|
11
|
+
**Уровень пира** `<папка-пира>/.iapeer/` — всё, что касается одного пира: его профиль, доктрина, состояние его плагинов, журналы его сессий.
|
|
12
|
+
|
|
13
|
+
Один и тот же плагин может хранить данные на обоих уровнях: общие — в `~/.iapeer/`, привязанные к конкретному пиру — в его папке.
|
|
14
|
+
|
|
15
|
+
## Раскладка общего уровня
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
~/.iapeer/
|
|
19
|
+
├── IAPEER.md доктрина хоста (общая для всех пиров; слой 2 промпта)
|
|
20
|
+
├── peers-profiles.json реестр всех пиров (пишется под блокировкой)
|
|
21
|
+
├── peers-profiles.lock файл блокировки реестра
|
|
22
|
+
├── memory-provider.json слот памяти: кто провайдер (если занят)
|
|
23
|
+
├── fragments/ общие доктринальные фрагменты (слой 5 промпта)
|
|
24
|
+
├── peers/
|
|
25
|
+
│ └── <имя>/ рабочая папка пира по умолчанию
|
|
26
|
+
│ └── .iapeer/ (раскладка уровня пира — см. ниже)
|
|
27
|
+
├── state/
|
|
28
|
+
│ ├── iapeer/ состояние демона (router.sock, router.json …)
|
|
29
|
+
│ └── <плагин>/ изменяемое состояние плагина
|
|
30
|
+
├── logs/
|
|
31
|
+
│ ├── iapeer/ журналы демона: lifecycle.log, delivery.log, exits.log
|
|
32
|
+
│ └── <имя>/ журналы инфраструктурного пира (по имени)
|
|
33
|
+
├── cache/
|
|
34
|
+
│ ├── iapeer/ кеш ядра
|
|
35
|
+
│ └── <плагин>/ кеш плагина (можно удалять)
|
|
36
|
+
├── plugins/
|
|
37
|
+
│ ├── iap/ установка ядрового плагина IAP
|
|
38
|
+
│ └── <плагин>/ установка плагина (неизменяемая)
|
|
39
|
+
└── runtimes/
|
|
40
|
+
├── claude/
|
|
41
|
+
│ └── plugins/<плагин>/ конфиг плагина под рантаймом claude
|
|
42
|
+
├── codex/
|
|
43
|
+
│ └── plugins/<плагин>/
|
|
44
|
+
├── telegram/
|
|
45
|
+
│ └── runtime.json манифест рантайм-пакета
|
|
46
|
+
└── notifier/
|
|
47
|
+
└── runtime.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Несколько узлов стоит выделить:
|
|
51
|
+
|
|
52
|
+
- **`peers-profiles.json`** — реестр. Пишется только через единственного писателя под файловой блокировкой `peers-profiles.lock`; прямое редактирование запрещено на уровне ядра.
|
|
53
|
+
- **`state/iapeer/`** — здесь живёт демон: его локальный сокет `router.sock` и конфигурация `router.json`.
|
|
54
|
+
- **`logs/<имя>/`** — журналы инфраструктурных пиров кладутся по имени пира на общем уровне (а не в папке пира), потому что это вывод службы под launchd.
|
|
55
|
+
- **`runtimes/<рантайм>/`** — двойное владение: корень принадлежит самому рантайму (Telegram держит здесь токены ботов, состояние опроса), а подпапка `plugins/<плагин>/` — это пространство плагинов, где лежит их конфиг под этим рантаймом.
|
|
56
|
+
|
|
57
|
+
## Раскладка уровня пира
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
<папка-пира>/.iapeer/
|
|
61
|
+
├── peer-profile.json профиль пира — источник истины о нём
|
|
62
|
+
├── IAPEER.md персональная доктрина (слой 2 промпта)
|
|
63
|
+
├── fragments/ персональные доктринальные фрагменты (слой 5)
|
|
64
|
+
├── plugins/
|
|
65
|
+
│ ├── iap/
|
|
66
|
+
│ └── <плагин>/ данные плагина для этого пира
|
|
67
|
+
├── state/
|
|
68
|
+
│ ├── iap/
|
|
69
|
+
│ └── <плагин>/
|
|
70
|
+
├── logs/
|
|
71
|
+
│ ├── iap/
|
|
72
|
+
│ └── <плагин>/
|
|
73
|
+
├── cache/
|
|
74
|
+
│ ├── iap/
|
|
75
|
+
│ └── <плагин>/
|
|
76
|
+
└── runtimes/
|
|
77
|
+
├── claude/
|
|
78
|
+
│ ├── launch.env аргументы и переменные запуска для этого рантайма
|
|
79
|
+
│ └── plugins/<плагин>/
|
|
80
|
+
└── codex/
|
|
81
|
+
├── launch.env
|
|
82
|
+
└── plugins/<плагин>/
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **`peer-profile.json`** — карточка пира и единственный источник истины о нём (поля разобраны в [03 — Пиры, рантаймы, идентичность](03-пиры-рантаймы-идентичность.md)).
|
|
86
|
+
- **`launch.env`** — файл вида `KEY=VALUE` для конкретной пары пир+рантайм: дополнительные аргументы запуска (`PEER_START_ARGS`) и переменные окружения. Так одному пиру можно задать особые флаги запуска, не трогая остальных.
|
|
87
|
+
|
|
88
|
+
Рабочая папка пира не обязана лежать в `~/.iapeer/peers/`. По умолчанию `iapeer create` кладёт её туда, но пиром можно сделать любую папку командой `iapeer init` — тогда `.iapeer/` появится прямо в ней.
|
|
89
|
+
|
|
90
|
+
## Кто чем владеет
|
|
91
|
+
|
|
92
|
+
Граница владения проста: каждый владеет содержимым своей папки и не лезет в чужие.
|
|
93
|
+
|
|
94
|
+
| Владелец | Что принадлежит |
|
|
95
|
+
|---|---|
|
|
96
|
+
| **Ядро (`iapeer`)** | `peers-profiles.json`, `state/iapeer/`, `logs/iapeer/`, `cache/iapeer/` |
|
|
97
|
+
| **Рантайм** | корень `runtimes/<рантайм>/` — кроме подпапки `plugins/` |
|
|
98
|
+
| **Плагин** | своя папка `<плагин>/` во всех категориях (`state`, `logs`, `cache`, `plugins`) на обоих уровнях |
|
|
99
|
+
|
|
100
|
+
Имя `iapeer` в категориях зарезервировано за ядром. Рантайм никогда не трогает `plugins/<плагин>/` внутри своего пространства, а плагин — корень рантайма.
|
|
101
|
+
|
|
102
|
+
## Классы данных по сроку жизни
|
|
103
|
+
|
|
104
|
+
Установка отделена от изменяемых данных — это важно для обновлений и для уборки:
|
|
105
|
+
|
|
106
|
+
- **Установка** (`plugins/<плагин>/`) — неизменяемая. Это разложенный код плагина; обновление заменяет его целиком.
|
|
107
|
+
- **Состояние** (`state/`) — изменяемые данные, которые нужно беречь: их потеря — потеря работы.
|
|
108
|
+
- **Журналы** (`logs/`) — вывод процессов; полезны для разбора, но не критичны. Что пишется в `lifecycle.log`, `delivery.log`, `exits.log` и как настроить их ротацию — [14 — Конфигурация и журналы](14-конфигурация-и-журналы.md).
|
|
109
|
+
- **Кеш** (`cache/`) — восстановимые данные; их можно удалить без последствий, система пересоздаст.
|
|
110
|
+
|
|
111
|
+
Такое разделение означает, что обновить плагин — значит заменить его папку установки, не задев его состояние; а почистить место — значит удалить кеш, не рискуя данными.
|
|
112
|
+
|
|
113
|
+
## Реестр как производная
|
|
114
|
+
|
|
115
|
+
Стоит повторить ключевой принцип хранения: **реестр `peers-profiles.json` — это проекция локальных профилей, а не отдельный источник истины.** Источник истины о пире — его `peer-profile.json`. Реестр можно пересобрать из профилей в любой момент командой `iapeer verify --fix`. При расхождении между реестром и профилем прав профиль. Поэтому потеря или порча реестра не фатальна — он восстанавливается обходом рабочих папок пиров.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# 07 — Установка и обновление
|
|
2
|
+
|
|
3
|
+
**Русский** · [English](../07-install-and-update.md)
|
|
4
|
+
|
|
5
|
+
Установка — два шага: разложить ядро и подготовить хост. Демон поднимать вручную не нужно — это делает онбординг. Ниже — оба шага, плюс обновление ядра и откат.
|
|
6
|
+
|
|
7
|
+
## Что нужно заранее
|
|
8
|
+
|
|
9
|
+
- **macOS.** Процессами управляет launchd — другой платформы пока нет.
|
|
10
|
+
- **[bun](https://bun.sh)** на `PATH`. Ядро исполняется через bun; скомпилированного JS в пакете нет.
|
|
11
|
+
- **`~/.local/bin` на `PATH`.** Сюда ставится бинарь `iapeer`. Нет в `PATH` — команда не найдётся после установки.
|
|
12
|
+
- **Хотя бы один агентский рантайм** — Claude Code или Codex CLI. Без него `iapeer create` и `iapeer init` откажут с явной ошибкой.
|
|
13
|
+
|
|
14
|
+
## Шаг 1. Установите ядро
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @agfpd/iapeer
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Запуск без аргументов выполняет установку. Она делает ровно три вещи:
|
|
21
|
+
|
|
22
|
+
1. **Раскладка.** Создаёт `~/.iapeer/` со всеми каталогами: реестр, состояние, логи, кеш, пространства рантаймов.
|
|
23
|
+
2. **Бинарь.** Собирает `iapeer` и атомарно кладёт в `~/.local/bin/iapeer`. Прежняя версия остаётся рядом как `iapeer.prev` — резерв для отката. Атомарность важна: между старой и новой версией нет момента, когда по пути лежит пустышка.
|
|
24
|
+
3. **Плист демона.** Пишет `~/Library/LaunchAgents/com.agfpd.iapeer.plist`, но **не загружает** его. Загрузку берёт на себя `iapeer onboard` (шаг 2): на работающем хосте демон уже может крутиться, и миграция на новый бинарь — осознанное действие, а не побочный эффект установки.
|
|
25
|
+
|
|
26
|
+
При установке однократно создаётся локальная подпись бинаря (для разрешений macOS). Подпись стабильна и переживает обновления.
|
|
27
|
+
|
|
28
|
+
Установка идемпотентна — повторный запуск безопасен.
|
|
29
|
+
|
|
30
|
+
Та же операция доступна как `iapeer install`, если бинарь уже стоит. Но самый первый раз — всегда через `npx` или из исходников: скомпилированный бинарь не умеет пересобирать сам себя.
|
|
31
|
+
|
|
32
|
+
## Шаг 2. Подготовьте хост
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
iapeer onboard
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`onboard` готовит хост к появлению агентов. Первым делом он **поднимает демон** — загружает плист в launchd, дальше launchd держит демон сам, поднимая его заново при падении и перезагрузке. Отдельная команда `launchctl` для этого не нужна. Затем onboard регистрирует marketplace расширений в Claude и Codex, разворачивает планировщик (`notifier`), создаёт пир-человека в Telegram и ставит провайдера памяти. Старт демона идёт всегда; остальные шаги включены по умолчанию и отключаются флагами (`--no-notifier`, `--no-telegram`, `--no-memory`).
|
|
39
|
+
|
|
40
|
+
Проверьте, что демон жив и хост готов:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
iapeer status
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Вы увидите версию ядра, здоровье демона и состояние слота памяти. Код возврата ненулевой, если демон нездоров, — это удобно для скриптов-проверок.
|
|
47
|
+
|
|
48
|
+
Разбор каждого шага onboard — в [02 — Быстрый старт](02-быстрый-старт.md); рантаймы и память подробно — в [09](09-рантаймы.md) и [11](11-расширения-и-память.md).
|
|
49
|
+
|
|
50
|
+
## Обновление ядра
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
iapeer update # до последней версии
|
|
54
|
+
iapeer update 0.2.80 # до конкретной версии
|
|
55
|
+
iapeer update --force # переустановить, даже если версия уже стоит
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Обновление тянет версию ядра из npm и перезапускает демон на неё. По шагам:
|
|
59
|
+
|
|
60
|
+
1. Узнаёт нужную версию из npm. Если установленная уже совпадает и нет `--force` — ничего не делает.
|
|
61
|
+
2. Скачивает и собирает новый бинарь. Это делается через `npm pack`, а не `npx`: бинарь не пересобирает себя сам.
|
|
62
|
+
3. Перезапускает демон на новый бинарь.
|
|
63
|
+
4. Перезапускает launchd-джобы инфраструктуры, которыми владеет ядро (`com.iapeer.*` — роутеры `timer`, `watcher`, Telegram), переводя их на только что заменённый бинарь. Их плисты запускают тот же бинарь ядра, что и демон, — без перезапуска джоб остался бы на старом коде. Перезапускается именно джоб (снимается и поднимается заново); **пакетный код** рантаймов (telegram-runtime, notifier-runtime) при этом не обновляется — это отдельная операция `update-runtime`. Провайдер памяти (`iapeer-memory`) и плисты обычных пиров не трогаются вовсе. Если демон на шаге 3 не поднялся, этот шаг пропускается — нет смысла переводить джобы на бинарь, на котором демон не ожил.
|
|
64
|
+
5. Проверяет, что демон действительно поднялся здоровым, — не по тому, что команда перезапуска завершилась успешно, а пробой: подключается к сокету демона (тому самому, что демон открывает прямо перед сигналом готовности). Здоровым демон считается, только когда сокет принимает соединение **дважды подряд** — одиночная проба прошла бы и у демона, который привязал сокет и тут же упал. На проверку отведено 15 секунд; если за это время серия успешных проб не набралась — команда говорит об этом прямо и советует откатиться.
|
|
65
|
+
|
|
66
|
+
Обновление трогает **только ядро**. Плист демона и сторонние пакеты остаются как есть.
|
|
67
|
+
|
|
68
|
+
Указать конкретную версию (`iapeer update <версия>`) полезно, когда нужно откатиться глубже, чем на один шаг назад, или встать на проверенную версию.
|
|
69
|
+
|
|
70
|
+
## Откат
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
iapeer rollback
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Восстанавливает предыдущий бинарь (тот самый `iapeer.prev`), перезапускает демон на него и проверяет здоровье. Откат идёт **на один уровень назад** — хранится одна предыдущая версия. Это локальное «отменить последнее обновление», пока в npm выкладывают исправленную версию. Если нужно встать на версию глубже — `iapeer update <версия>`.
|
|
77
|
+
|
|
78
|
+
## Если демон не поднялся после обновления
|
|
79
|
+
|
|
80
|
+
Признак — `iapeer status` показывает нездоровый демон, или `update` завершился с предупреждением. Порядок действий:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
iapeer rollback # вернуться на прежний бинарь
|
|
84
|
+
# если демон всё ещё лежит — поднять вручную:
|
|
85
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.agfpd.iapeer.plist
|
|
86
|
+
iapeer status # убедиться, что ожил
|
|
87
|
+
```
|