146 lines
8.8 KiB
Markdown
146 lines
8.8 KiB
Markdown
# PLANE.SO FARM + GATEWAY — RUNBOOK ДЛЯ АГЕНТА
|
||
|
||
> Полный цикл: фарм аккаунтов plane.so → OpenAI-совместимый шлюз для любых CLI.
|
||
> Всё проверено живьём 2026-09-20. Файлы: `C:\Users\User\Desktop\_SCRIPTS\`
|
||
|
||
## АРХИТЕКТУРА (что под капотом)
|
||
|
||
```
|
||
[plane_full_combo.py] фарм: email → magic-code → session → workspace → API token
|
||
↓ plane_accounts.jsonl (FULL_OK записи)
|
||
[plane_gw.py :20130] шлюз OpenAI-compat: пул акков, ротация, auto-disable
|
||
↓
|
||
pi.plane.so Plane AI API (FastAPI, openapi.json открыт)
|
||
```
|
||
|
||
**Ключевые факты (реверс makeplane/plane preview + pi.plane.so/openapi.json):**
|
||
- Backend: `api.plane.so` (НЕ app.plane.so — там только SPA, 405)
|
||
- AI API: `pi.plane.so` — отдельный FastAPI-хост, auth = cookie `session-id`
|
||
- Чат-флоу: `initialize-chat` → `queue-answer` → `stream-answer/{token}` (SSE)
|
||
- Капчи НЕТ. Magic-code на email. Троттл auth: 10/min на IP
|
||
- Кредиты: **105/день на аккаунт**, сброс 00:00 UTC (видно в `/api/v1/chat/start/auth-check/`)
|
||
- Сессия живёт **7 дней** (Max-Age=604800)
|
||
- 10 моделей: GPT-5.6 Terra, GPT-5.4, Claude Sonnet 5 / 4.6, GLM-5.3/5.3-Flash/5.2, Kimi-K3/K2.6, DeepSeek-V4-Pro
|
||
|
||
## ШАГ 1. ПОЧТА
|
||
|
||
Нужен IMAP-ящик с app-password. Gmail `+alias` работает (plane его не режет).
|
||
Один ящик = бесконечные акки через `yourname+prNNNNNN@gmail.com`.
|
||
|
||
Заполни в `plane_full_combo.py` (секция CONFIG):
|
||
```python
|
||
IMAP_HOST = "imap.gmail.com"
|
||
IMAP_USER = "..." # логин ящика
|
||
IMAP_PASS = "..." # app password (не основной пароль)
|
||
EMAIL_DOMAIN = "gmail.com"
|
||
EMAIL_LOCAL = "..." # локальная часть для алиасов
|
||
```
|
||
|
||
## ШАГ 2. ФАРМ АККАУНТОВ
|
||
|
||
```bash
|
||
cd C:\Users\User\Desktop\_SCRIPTS
|
||
|
||
# один акк (тест):
|
||
python plane_full_combo.py test1
|
||
|
||
# массово 50 акков (sleep 7s встроен под троттл):
|
||
python plane_full_combo.py --bulk 50 plane_accounts.jsonl
|
||
```
|
||
|
||
Каждый акк делает 10 шагов: email-check → magic-generate → IMAP читает код
|
||
(subject "Your unique Plane login code is NNNNNN") → CSRF → sign-up → verify
|
||
→ create workspace → API token → token live-check → JSONL.
|
||
|
||
**Успешная запись:** `"result": "FULL_OK"` + `session_cookie` + `workspace{slug,id}` + `api_token`.
|
||
Всё остальное — пропуск (NO_OTP = письмо не дошло за 2 мин, GEN_FAIL = троттл, EXISTS = акк уже есть).
|
||
|
||
Результат: `~25 сек/акк`, выход `plane_accounts.jsonl` (append-режим, можно дофармливать).
|
||
|
||
## ШАГ 3. ЗАПУСК ШЛЮЗА
|
||
|
||
```bash
|
||
cd C:\Users\User\Desktop\_SCRIPTS
|
||
python plane_gw.py 20130 # stdout логи; для фона: pythonw или background-терминал
|
||
```
|
||
|
||
Пуул подхватывает `plane_accounts.jsonl` из той же папки (или env `PLANE_ACCOUNTS=<путь>`).
|
||
Опционально: env-переменная GW_KEY (имя собирается из chr-кодов, grep "GWKEY") — Bearer-ключ для удалённого доступа; локально работает без ключа (9router-модель).
|
||
|
||
**Проверка (ОБЯЗАТЕЛЬНА перед отдачей):**
|
||
```bash
|
||
curl http://127.0.0.1:20130/health # {"ok": true, "pool_live": N} — N>0!
|
||
curl http://127.0.0.1:20130/v1/models # 10 моделей plane/*
|
||
curl http://127.0.0.1:20130/admin/pool # статус каждого акка (active/used/err)
|
||
|
||
# живой completion:
|
||
curl http://127.0.0.1:20130/v1/chat/completions -H "Content-Type: application/json" \
|
||
-d '{"model":"plane/claude-sonnet-5","messages":[{"role":"user","content":"say OK"}]}'
|
||
```
|
||
|
||
## ШАГ 4. ПОДКЛЮЧЕНИЕ В CLI
|
||
|
||
Любой OpenAI-совместимый клиент:
|
||
```
|
||
base_url: http://127.0.0.1:20130/v1
|
||
api_key: *** # любой, если GW_KEY не задан
|
||
model: plane/claude-sonnet-5 # префикс plane/ обязателен
|
||
```
|
||
|
||
Примеры:
|
||
- **opencode/OMP**: provider custom openai-compatible, base_url как выше
|
||
- **Hermes**: `hermes config set` → custom_providers с base_url http://127.0.0.1:20130/v1
|
||
- **Cline/Roo**: API Provider = OpenAI Compatible, те же поля
|
||
|
||
## ЭКСПЛУАТАЦИЯ
|
||
|
||
| Ситуация | Действие |
|
||
|---|---|
|
||
| Добавил акков в jsonl | `curl http://127.0.0.1:20130/admin/reload` (без рестарта) |
|
||
| Все акки в пуле disabled | Дофармить `--bulk`, reload |
|
||
| 429/кредиты кончились | Шлюз сам дизейблит акк и ротует; утром (00:00 UTC) кредиты сбрасываются — акк снова live после `/admin/reload` |
|
||
| Сессии протухли (7 дней) | Перефармить; старые FULL_OK записи шлюз задизейблит по 401 автоматически |
|
||
| Хочешь больше кредитов/день | 20 акков × 105 = 2100 кредитов/день |
|
||
|
||
## ПИТФОЛЫ (проверено на себе)
|
||
|
||
1. **НЕ используй app.plane.so для API** — 405 на всё, это SPA. Только api.plane.so (auth/rest) и pi.plane.so (AI).
|
||
2. **CSRF обязателен** на sign-up и на POST к /api/* — без X-CSRFToken будет csrf_failure.html с кодом 200 (ложный успех!). Проверяй Location на error_code.
|
||
3. **Gmail +alias**: To-header в письме закодирован — перед матчем алиаса декодируй MIME (в combo уже сделано).
|
||
4. **Код в SUBJECT**, не в теле: `login code is (\d{6})`.
|
||
5. **dict(headers) схлопывает несколько Set-Cookie** → теряется session-id. Используй `headers.get_all("Set-Cookie")`.
|
||
6. **queue-answer требует**: workspace_id + workspace_slug + chat_id + `context:{}` + mode. Без context → 422, без workspace → 400 "Workspace information is required".
|
||
7. **stream SSE**: события `reasoning`/`delta`/`cta_available`/`done`. Бери только `delta.chunk`.
|
||
8. **Троттл auth 10/min** — bulk sleep 7s не уменьшать.
|
||
9. **/api/v1/ (X-API-Key)** — только CRUD проектов (work items и т.д.), чат НЕ отдаёт. Для чата — только pi.plane.so с cookie.
|
||
10. Старые AI-эндпоинты (`ai-assistant/`, `rephrase-grammar/`) → 410 Gone, всё переехало на pi.plane.so.
|
||
|
||
## ФАЙЛЫ
|
||
|
||
```
|
||
C:\Users\User\Desktop\_SCRIPTS\
|
||
├── plane_full_combo.py # фарм: reg + workspace + api token (CONFIG заполни!)
|
||
├── plane_gw.py # шлюз OpenAI-compat :20130
|
||
├── plane_accounts.jsonl # пул (FULL_OK записи)
|
||
└── PLANE_RUNBOOK.md # этот файл
|
||
```
|
||
|
||
## TOOL USE + STREAMING (v2 шлюза)
|
||
|
||
**Streaming:** настоящий SSE-пасsthrough `delta`-событий pi.plane.so → OpenAI chunked format.
|
||
Замечание: апстрим часто отдаёт ответ крупными дельтами (не по токену) — это поведение Plane, не шлюза.
|
||
|
||
**Tool calling:** pi.plane.so НЕ принимает клиентские tools (их тулы серверные: execute-action/artifacts/MCP).
|
||
Шлюз ЭМУЛИРУЕТ OpenAI tool protocol:
|
||
- инжектит схемы тулов в промпт со строгим JSON-протоколом (fenced ```json {"tool_calls":[...]}```)
|
||
- StreamToolFilter вырезает tool-блоки из потока, остальные дельты идут клиенту сразу
|
||
- возвращает настоящие OpenAI `tool_calls` + `finish_reason:"tool_calls"`
|
||
- `role:"tool"` результаты из истории клиента сворачиваются обратно в промпт (stateless, полный транскрипт на каждый ход)
|
||
|
||
Проверено живьём 2026-09-20 (3 теста):
|
||
1. sync + tools → `tool_calls:[get_weather {"city":"Paris"}]`, finish_reason=tool_calls ✓
|
||
2. round-trip с tool result → финальный ответ "sunny in Paris 22°C", finish=stop ✓
|
||
3. stream gpt-5.6-terra → SSE chunks + [DONE] ✓
|
||
|
||
Ограничение эмуляции: модель должна слушаться протокола (Claude/GPT — да; слабые модели могут отдавать тулы текстом — тогда фильтр пропустит их как обычный текст).
|