# 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 — да; слабые модели могут отдавать тулы текстом — тогда фильтр пропустит их как обычный текст).