Metadata-Version: 2.4
Name: s-iokit
Version: 0.1.0
Summary: Переиспользуемое ядро масштабируемой in-out архитектуры: 3-осевая композиция Transport × Connector × Service поверх s-librarykit. Generic Event + Router + DeliveryBus + Supervisor; белые API (токен/REST) и hidden (сессия/реверс) — единый контракт входящих/исходящих точек.
Author: Dmitry
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: s-librarykit>=0.5
Provides-Extra: adapter
Requires-Dist: s-adapterkit>=0.1.2; extra == 'adapter'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: mtproto
Requires-Dist: telethon>=1.36; extra == 'mtproto'
Provides-Extra: webhook
Requires-Dist: uvicorn>=0.30; extra == 'webhook'
Provides-Extra: ws
Requires-Dist: websockets>=12; extra == 'ws'
Description-Content-Type: text/markdown

# s-iokit

Переиспользуемое ядро **масштабируемой in-out архитектуры**: единая гибкая система
входящих/исходящих точек подключения к сервисам. Работает **и для белых API**
(токен/REST), **и для hidden API** (сессия/реверс/MTProto/антибот). Лёгкое внедрение
одной строкой в любой инструмент (gateway, Бубликтор и др.).

Kit уровня `adapterkit`: тонкий слой поверх КОРНЯ **`s-librarykit`** (транспорт/ошибки/
config/sessions — оттуда, не дублируем). import-имя `iokit`, dist-имя `s-iokit`.

## Три оси (Transport × Connector × Service)

| Ось | Модуль | Отвечает за | Реализации |
|-----|--------|-------------|------------|
| **Transport** | `iokit.transport` | *как получаем события* | `PollingTransport`, `LongPollTransport`, `WebhookTransport` |
| **Connector** | `iokit.connector` | *как подключены + какой сервис* | `TelegramBotConnector`, … |
| **Service** | `iokit.service` | реестр сервисов + `Endpoint` (композиция) | `register_service`, `get_service` |

Оси развязаны: **Transport** не знает сервиса (тянет сырьё), **Connector** не знает
механики транспорта (даёт `poll()`/`parse()`/`send()`), **Service** компонует. Один
сервис — разные способы подключения без дублирования транспорта.

## Generic Event

Ядро принимает **любые** события сервиса, не только чат: CRM-события (сделка/звонок/
лид), реакции, callback-кнопки — всё это `InboundEvent` с разным `kind`.
`MessageEvent` — частный случай (текст/медиа).

```python
from iokit import InboundEvent, MessageEvent, event_key

ev = MessageEvent(source="telegram", chat_id="100", user_id="7", text="привет")
event_key(ev)                      # "telegram:100:u7"

crm = InboundEvent(source="bitrix", kind="crm", actor="deal:42", payload={"stage": "won"})
```

## Лёгкое встраивание

```python
from iokit import ConnectorConfig, Endpoint, EventRouter, serve

async def handle(event, ctx):
    return f"эхо: {event.text}"

router = EventRouter().add_rule(handle, kind="message")

endpoint = Endpoint(
    service="telegram",
    connection_mode="bot",
    transport="long-poll",   # или "webhook" — ОДИН коннектор, оба транспорта
    config=ConnectorConfig("telegram", credentials={"token": "<BOT_TOKEN>"}),
)

await serve([endpoint], router)   # одна строка
```

- **`EventRouter`** — правила `match(kind/pattern/predicate) → Handler`. iokit
  нейтрален: handler'ы реализует потребитель (агент / инструмент / custom-сценарий).
- **`DeliveryBus`** — единый outbound: адрес `name:target` → `Connector.send`.
- **`Supervisor` / `serve`** — рантайм нескольких endpoint'ов параллельно + один
  общий webhook-listener (роутинг по path).

## Нейтральность к credentials

iokit не знает про пул аккаунтов — потребитель передаёт токен/сессию из СВОЕГО
источника через `ConnectorConfig(credentials=...)`. Секреты не попадают в `repr`/логи.

## Установка (co-разработка с librarykit)

```bash
uv sync --extra dev              # ядро + тесты (librarykit editable из ../librarykit)
uv sync --extra webhook          # + встроенный ASGI webhook-listener (uvicorn)
uv run python -m pytest -q
```

Ядро лёгкое: `import iokit` не требует ни browser/antibot/ws extra librarykit, ни
webhook extra iokit. Webhook-сервер (uvicorn) тянется LAZY только при `serve`/
`WebhookTransport.start()`.

## Расширение — новый сервис

```python
from iokit import Capabilities, register_service

class MyConnector:
    name = "myservice"
    service = "myservice"
    connection_mode = "app"
    capabilities = Capabilities(text=True)
    allowed_transports = ("polling",)
    def parse(self, raw): ...
    async def poll(self): ...            # для pull-транспортов
    async def send(self, target, reply, **kw): ...
    async def aclose(self): ...

register_service("myservice", lambda cfg: MyConnector(),
                 allowed_transports=("polling",), connection_modes=("app",))
```

Также поддержан автодискавери через entry-points группы `iokit.services`.

## Лицензия

MIT © Dmitry
