8.8 KiB
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 = cookiesession-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):
IMAP_HOST = "imap.gmail.com"
IMAP_USER = "..." # логин ящика
IMAP_PASS = "..." # app password (не основной пароль)
EMAIL_DOMAIN = "gmail.com"
EMAIL_LOCAL = "..." # локальная часть для алиасов
ШАГ 2. ФАРМ АККАУНТОВ
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. ЗАПУСК ШЛЮЗА
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-модель).
Проверка (ОБЯЗАТЕЛЬНА перед отдачей):
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 кредитов/день |
ПИТФОЛЫ (проверено на себе)
- НЕ используй app.plane.so для API — 405 на всё, это SPA. Только api.plane.so (auth/rest) и pi.plane.so (AI).
- CSRF обязателен на sign-up и на POST к /api/* — без X-CSRFToken будет csrf_failure.html с кодом 200 (ложный успех!). Проверяй Location на error_code.
- Gmail +alias: To-header в письме закодирован — перед матчем алиаса декодируй MIME (в combo уже сделано).
- Код в SUBJECT, не в теле:
login code is (\d{6}). - dict(headers) схлопывает несколько Set-Cookie → теряется session-id. Используй
headers.get_all("Set-Cookie"). - queue-answer требует: workspace_id + workspace_slug + chat_id +
context:{}+ mode. Без context → 422, без workspace → 400 "Workspace information is required". - stream SSE: события
reasoning/delta/cta_available/done. Бери толькоdelta.chunk. - Троттл auth 10/min — bulk sleep 7s не уменьшать.
- /api/v1/ (X-API-Key) — только CRUD проектов (work items и т.д.), чат НЕ отдаёт. Для чата — только pi.plane.so с cookie.
- Старые 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 теста):
- sync + tools →
tool_calls:[get_weather {"city":"Paris"}], finish_reason=tool_calls ✓ - round-trip с tool result → финальный ответ "sunny in Paris 22°C", finish=stop ✓
- stream gpt-5.6-terra → SSE chunks + [DONE] ✓
Ограничение эмуляции: модель должна слушаться протокола (Claude/GPT — да; слабые модели могут отдавать тулы текстом — тогда фильтр пропустит их как обычный текст).