izteamslots 1.6.1 → 1.7.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## [1.7.1](https://github.com/izzzzzi/izTeamSlots/compare/v1.7.0...v1.7.1) (2026-04-13)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **codex-switcher:** eliminate redundant _load_accounts calls in pick_first_ready ([1024e95](https://github.com/izzzzzi/izTeamSlots/commit/1024e9582137aeaaea43484c94002455160ed37d))
7
+ * deduplicate _decode_jwt_payload — single canonical implementation in codex_switcher ([5e579fe](https://github.com/izzzzzi/izTeamSlots/commit/5e579fe44caccf381d47d43484c56b4242589f87))
8
+ * **jobs:** move thread assignment inside lock to prevent race condition ([40a3be2](https://github.com/izzzzzi/izTeamSlots/commit/40a3be2ce0937ae6eeef72c508c820d5de2d1029))
9
+ * **logger:** use UTC timestamps consistent with rest of codebase ([302dfa1](https://github.com/izzzzzi/izTeamSlots/commit/302dfa1c2c5348fcab11c5606c5eb916ff0b5d26))
10
+ * remove redundant Mailbox creation in relogin_worker_email ([883af9b](https://github.com/izzzzzi/izTeamSlots/commit/883af9bd7650c887056885514329b38d6bd5acab))
11
+ * **security:** set chmod 0600 on meta.json and index.json files ([08c27af](https://github.com/izzzzzi/izTeamSlots/commit/08c27af9b330fae9f9a6316fd0fc7c6d38813506))
12
+ * **security:** strengthen API key masking — show at most 4 chars for long keys ([545ac15](https://github.com/izzzzzi/izTeamSlots/commit/545ac15c4505fa94aff59ef5f1ede2b11a8819c2))
13
+ * **workspace-api:** add pagination to get_members and get_pending_invites ([48229d2](https://github.com/izzzzzi/izTeamSlots/commit/48229d209a20593be9693df18f504dba9ee18ddc))
14
+
15
+ # [1.7.0](https://github.com/izzzzzi/izTeamSlots/compare/v1.6.1...v1.7.0) (2026-03-07)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * remove unused variable to satisfy ruff F841 ([e5cfb77](https://github.com/izzzzzi/izTeamSlots/commit/e5cfb773e4449037aa96f7f940c22cd7eb78ecdc))
21
+
22
+
23
+ ### Features
24
+
25
+ * add Codex account switcher with auto-rotation ([70c8953](https://github.com/izzzzzi/izTeamSlots/commit/70c8953a6cde92127a17a5ea220bd8476bbc3447))
26
+
1
27
  ## [1.6.1](https://github.com/izzzzzi/izTeamSlots/compare/v1.6.0...v1.6.1) (2026-03-07)
2
28
 
3
29
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # izTeamSlots
4
4
 
5
- **Менеджер ChatGPT Team слотов с авто-регистрацией через временную почту и обновлением Codex-сессий**
5
+ **Локальный менеджер ChatGPT Team слотов: админы, инвайты, регистрация, перелогин и Codex-сессии**
6
6
 
7
7
  [![CI](https://github.com/izzzzzi/izTeamSlots/actions/workflows/ci.yml/badge.svg)](https://github.com/izzzzzi/izTeamSlots/actions/workflows/ci.yml)
8
8
  [![Release](https://github.com/izzzzzi/izTeamSlots/actions/workflows/release.yml/badge.svg)](https://github.com/izzzzzi/izTeamSlots/actions/workflows/release.yml)
@@ -18,111 +18,54 @@
18
18
 
19
19
  </div>
20
20
 
21
- ---
22
-
23
- ## Обзор
24
-
25
- **izTeamSlots** — локальное приложение с архитектурой:
26
-
27
- - **Python backend** (бизнес-логика, браузерная автоматизация, storage);
28
- - **TypeScript OpenTUI frontend** (терминальный интерфейс оператора).
29
-
30
- ---
31
-
32
- ## Возможности
33
-
34
- - Управление админами: добавить, перелогинить, удалить, открыть браузерный профиль.
35
- - Пайплайн слотов: `создать почту -> инвайт -> регистрация -> OAuth-логин`.
36
- - Перелогин слотов: одного выбранного или всех по очереди.
37
- - Codex-файлы: авто-сохранение `codex-<email>-Team.json` в аккаунт и в `./codex/`.
38
- - Doctor-проверка: валидация/восстановление файловой структуры аккаунтов при старте.
39
- ---
40
-
41
- ## Структура проекта
42
-
43
- ```text
44
- izTeamSlots/
45
- ├── bin/ # CLI-бинарники
46
- │ └── izteamslots.mjs # Кроссплатформенный entrypoint (Node.js)
47
- ├── scripts/ # Установочные скрипты
48
- │ ├── setup.mjs # Диспетчер: выбирает .sh или .cmd
49
- │ ├── setup.sh # Unix: uv + venv + Bun
50
- │ └── setup.cmd # Windows: uv + venv + Bun
51
- ├── backend/ # Весь Python-бэкенд
52
- │ ├── __init__.py # PROJECT_ROOT
53
- │ ├── __main__.py # python -m backend
54
- │ ├── account_store.py # CRUD аккаунтов (JSON storage)
55
- │ ├── mail/ # Почтовые провайдеры (плагины)
56
- │ │ ├── __init__.py # Фабрики: create_provider, create_slot_provider
57
- │ │ ├── base.py # MailProvider ABC, Mailbox, Mail, Inbox
58
- │ │ ├── boomlify.py # Boomlify Temp Mail API
59
- │ │ ├── trickads.py # trickadsagencyltd.com temp mail
60
- │ │ └── imap.py # Любой IMAP-сервер
61
- │ ├── openai_web_auth.py # Браузерная автоматизация (SeleniumBase)
62
- │ ├── chatgpt_workspace_api.py # ChatGPT Workspace API через браузер
63
- │ ├── slot_orchestrator.py # Оркестратор пайплайна слотов
64
- │ ├── dto.py # DTO для UI
65
- │ ├── file_logger.py # Логирование в файл
66
- │ ├── jobs.py # Задачи в потоках
67
- │ ├── rpc_protocol.py # JSON-RPC протокол
68
- │ ├── rpc_server.py # RPC-сервер (stdio)
69
- │ └── ui_facade.py # Фасад между RPC и бизнес-логикой
70
- ├── ui/ # TypeScript TUI (OpenTUI)
71
- │ ├── package.json
72
- │ └── src/
73
- │ ├── main.ts # Entrypoint UI
74
- │ ├── screens/MainScreen.ts # Главный экран
75
- │ ├── transport/stdioClient.ts # JSON-RPC клиент
76
- │ └── menus/ # Меню, таблицы, форматирование
77
- ├── requirements.txt # Python-зависимости
78
- ├── ruff.toml # Конфиг линтера Python
79
- ├── LICENSE # MIT
80
- └── README.md
81
- ```
82
-
83
- ---
84
-
85
- ## Установка
86
-
87
- ### npm (рекомендуется)
21
+ ## Quick Start
88
22
 
89
23
  ```bash
90
- npm cache clean --force && npm install -g izteamslots@latest
24
+ npm install -g izteamslots@latest
25
+ izteamslots
91
26
  ```
92
27
 
93
- Установщик автоматически поставит Python-зависимости (через [uv](https://docs.astral.sh/uv/)), [Bun](https://bun.sh) и всё остальное.
28
+ Дальше:
29
+ 1. Откройте `Настройки` и задайте почтовый провайдер / API-ключ.
30
+ 2. Добавьте админа через ручной вход в браузере.
31
+ 3. Запустите создание слотов.
94
32
 
95
- > Chrome и chromedriver скачиваются автоматически при первом запуске через SeleniumBase.
33
+ ## Что умеет
96
34
 
97
- ### Где лежат данные после `npm install -g`
35
+ - Добавление и ручной перелогин админов.
36
+ - Создание слотов: `почта -> инвайт -> регистрация -> OAuth`.
37
+ - Перелогин одного слота или всех сразу.
38
+ - Сохранение `codex-<email>-Team.json`.
39
+ - Логи, локальные browser profiles и doctor-проверка.
40
+ - Синхронизация workspace с локальными слотами.
41
+ - Свитч Codex-аккаунтов: мониторинг usage, авто-ротация auth.json при достижении лимита.
98
42
 
99
- При глобальной установке runtime-данные сохраняются не в папку пакета, а в пользовательскую директорию `~/.izteamslots`.
43
+ ## Ограничения
100
44
 
101
- - `codex/`:
102
- - Windows: `C:\Users\<USER>\.izteamslots\codex`
103
- - macOS / Linux: `~/.izteamslots/codex`
104
- - `.env`:
105
- - Windows: `C:\Users\<USER>\.izteamslots\.env`
106
- - macOS / Linux: `~/.izteamslots/.env`
107
- - аккаунты и профили браузера:
108
- - Windows: `C:\Users\<USER>\.izteamslots\accounts`
109
- - macOS / Linux: `~/.izteamslots/accounts`
45
+ - Вход админа сейчас поддерживается только в ручном режиме.
46
+ - Проект зависит от текущего web UI OpenAI / ChatGPT.
47
+ - Браузерная автоматизация может ломаться после изменений на стороне сайта.
48
+ - Токены, профили браузера и `codex` хранятся локально.
49
+ - Основные платформы: macOS и Windows.
110
50
 
111
- Если вы обновляете старую версию, `codex`-файлы могут временно лежать ещё и внутри директории пакета. В актуальной версии основным путём считается именно `~/.izteamslots`.
51
+ ## Где лежат данные
112
52
 
113
- ### Из исходников
53
+ При глобальной установке данные сохраняются в `~/.izteamslots`.
114
54
 
115
- ```bash
116
- git clone https://github.com/izzzzzi/izTeamSlots.git
117
- cd izTeamSlots
118
- npm install
119
- ```
55
+ - `accounts/` — аккаунты и browser profiles
56
+ - `codex/` — сохранённые codex-файлы
57
+ - `logs/` — app/job logs
58
+ - `.env` — локальные настройки
120
59
 
121
- ### Настройка
60
+ Примеры:
61
+ - Windows: `C:\Users\<USER>\.izteamslots`
62
+ - macOS / Linux: `~/.izteamslots`
63
+
64
+ Если вы обновляете старую версию, `codex`-файлы могут временно лежать ещё и внутри директории пакета. В актуальной версии основным путём считается именно `~/.izteamslots`.
122
65
 
123
- API-ключи и провайдеры почты настраиваются прямо в приложении через меню **«Настройки»**. Значения сохраняются в `~/.izteamslots/.env`.
66
+ ## Настройка
124
67
 
125
- Альтернативно создайте файл вручную:
68
+ Настройки можно задать через меню `Настройки` внутри приложения или вручную через `~/.izteamslots/.env`.
126
69
 
127
70
  ```bash
128
71
  # Linux / macOS
@@ -134,122 +77,41 @@ mkdir "$env:USERPROFILE\.izteamslots" -Force
134
77
  echo "BOOMLIFY_API_KEY=your_api_key" > "$env:USERPROFILE\.izteamslots\.env"
135
78
  ```
136
79
 
137
- Конфиг загружается в порядке приоритета: `~/.izteamslots/.env` > `./.env` > встроенный.
138
-
139
80
  | Переменная | По умолчанию | Описание |
140
81
  |-----------|:------------:|----------|
141
- | `BOOMLIFY_API_KEY` | — | API-ключ Boomlify (обязательно для слотов) |
142
- | `BOOMLIFY_DOMAIN` | авто | Домен для временных почт |
82
+ | `BOOMLIFY_API_KEY` | — | API-ключ Boomlify |
83
+ | `BOOMLIFY_DOMAIN` | авто | Домен временных почт |
143
84
  | `BOOMLIFY_TIME` | `permanent` | Время жизни ящика |
144
85
  | `SLOT_MAIL_PROVIDER` | `boomlify` | Провайдер почты для слотов |
145
86
  | `MAIL_PROVIDER` | `trickads` | Провайдер почты для админов |
87
+ | `CODEX_SWITCHER_ENABLED` | `false` | Включить автосвитч Codex-аккаунтов |
88
+ | `CODEX_SWITCHER_INTERVAL_MINUTES` | `15` | Интервал фоновой проверки usage (минуты) |
146
89
 
147
- ## Запуск
148
-
149
- ```bash
150
- izteamslots
151
- ```
152
-
153
- Из исходников: `npm start`
154
-
155
- Это запустит OpenTUI frontend через **Bun**, который поднимет Python RPC backend (`python -m backend`) по `stdio`.
156
-
157
- ---
158
-
159
- ## Архитектура
90
+ ## Свитч Codex-аккаунтов
160
91
 
161
- ```mermaid
162
- flowchart TD
163
- A[izteamslots CLI] -->|bun| B[ui/src/main.ts]
164
- B --> C[MainScreen.ts]
165
- C --> D[StdioRpcClient]
166
- D -->|spawns + stdio JSON-RPC| E[python -m backend]
167
- E --> F[RPCServer]
168
- F --> G[UIFacade]
169
- G --> H[AccountStore]
170
- G --> I[SlotManager]
171
- I --> J[openai_web_auth]
172
- I --> K[chatgpt_workspace_api]
173
- I --> L[Mail Providers]
174
- ```
175
-
176
- ## Почтовые провайдеры (плагины)
177
-
178
- Система почты построена на плагинах — абстрактный класс `MailProvider` и конкретные реализации.
179
-
180
- ```mermaid
181
- flowchart TD
182
- MP[MailProvider — абстрактный класс]
183
- MP --> B[BoomlifyProvider]
184
- MP --> T[TrickAdsProvider]
185
- MP --> I[IMAPProvider]
186
-
187
- CF[create_provider] -->|MAIL_PROVIDER env| MP
188
- CSP[create_slot_provider] -->|SLOT_MAIL_PROVIDER env| MP
189
- CPM[create_provider_for_mailbox] -->|по формату password| MP
190
-
191
- style MP fill:#1e3a5f,color:#fff
192
- style B fill:#1a4731,color:#fff
193
- style T fill:#1a4731,color:#fff
194
- style I fill:#1a4731,color:#fff
195
- ```
196
-
197
- ### Встроенные провайдеры
198
-
199
- | Провайдер | Модуль | Описание | Env-переменные |
200
- |-----------|--------|----------|----------------|
201
- | `boomlify` | `mail/boomlify.py` | Boomlify Temp Mail API (по умолчанию для слотов) | `BOOMLIFY_API_KEY`, `BOOMLIFY_DOMAIN`, `BOOMLIFY_TIME` |
202
- | `trickads` | `mail/trickads.py` | trickadsagencyltd.com temp mail (по умолчанию для админов) | — |
203
- | `imap` | `mail/imap.py` | Любой IMAP-сервер | `IMAP_HOST`, `IMAP_PORT`, `IMAP_SSL`, `IMAP_FOLDER` |
204
-
205
- ### Фабричные функции
92
+ Встроенный механизм ротации Codex-аккаунтов. Все codex-файлы из пула (`<DATA_ROOT>/codex`) отображаются в разделе **Свитч аккаунтов** главного меню.
206
93
 
207
- - `create_provider(name)` — создаёт провайдер по имени (или `MAIL_PROVIDER` env, по умолчанию `trickads`)
208
- - `create_slot_provider(name)`для слотов (`SLOT_MAIL_PROVIDER` env, по умолчанию `boomlify`)
209
- - `create_provider_for_mailbox(mailbox)`автоопределение по формату пароля (если `boomlify:<uuid>` → Boomlify)
94
+ Что доступно:
95
+ - **Таблица аккаунтов** active-статус, primary usage %, reset time, состояние токена.
96
+ - **Ручное обновление** запросить usage по всем аккаунтам.
97
+ - **Ручное переключение** — выбрать аккаунт и записать его в `auth.json`.
98
+ - **Первый готовый** — автоматически выбрать первый аккаунт без near-limit.
99
+ - **Автосвитч** — фоновый шедулер проверяет usage и переключает `auth.json`, если `primary_used_percent >= 90%`. Включается через настройку `CODEX_SWITCHER_ENABLED`.
100
+ - **Авто-рефреш токенов** — если access token истекает, обновляется через OAuth.
210
101
 
211
- ### Свой провайдер
102
+ Путь к `auth.json` определяется через `CODEX_HOME` или `~/.codex/auth.json`.
212
103
 
213
- Наследуйте `MailProvider` из `backend/mail/base.py` и реализуйте два метода:
104
+ ## Почтовые провайдеры
214
105
 
215
- ```python
216
- from backend.mail.base import MailProvider, Mailbox, Inbox
217
-
218
- class MyProvider(MailProvider):
219
- name = "my_provider"
220
-
221
- def generate(self) -> Mailbox:
222
- # создать временный ящик, вернуть Mailbox(email, password)
223
- ...
224
-
225
- def inbox(self, mailbox: Mailbox) -> Inbox:
226
- # получить письма, вернуть Inbox(email, messages=[Mail(...), ...])
227
- ...
228
- ```
229
-
230
- ---
231
-
232
- ## Пайплайн слотов
233
-
234
- ```mermaid
235
- flowchart TD
236
- S1[Создать временную почту] --> S2[Отправить инвайт через API]
237
- S2 --> S3[Ожидать письмо с инвайт-ссылкой]
238
- S3 --> S4[Регистрация по ссылке в браузере]
239
- S4 --> S5[Ввод email / пароль / код подтверждения]
240
- S5 --> S6[Заполнение имени и даты рождения]
241
- S6 --> S7[Закрыть браузер регистрации]
242
- S7 --> S8[OAuth PKCE логин]
243
- S8 --> S9[Сохранить access_token и codex-файл]
244
-
245
- style S1 fill:#1e3a5f,color:#fff
246
- style S9 fill:#1a4731,color:#fff
247
- ```
106
+ - В проект уже встроены `boomlify`, `trickads` и `imap`.
107
+ - Можно добавлять собственные почтовые провайдеры.
108
+ - Для этого нужно реализовать `MailProvider` и зарегистрировать его в backend.
248
109
 
249
- ---
110
+ Подробности: [docs/providers.md](./docs/providers.md)
250
111
 
251
- ## Важно
112
+ ## Документация
252
113
 
253
- - Проект хранит токены и браузерные профили локально на диске.
254
- - Не публикуйте папки `accounts/` и `codex/` в публичные репозитории.
255
- - Для стабильной работы перелогина у worker должен быть `openai_password`.
114
+ - [docs/providers.md](./docs/providers.md) встроенные и кастомные почтовые провайдеры
115
+ - [docs/architecture.md](./docs/architecture.md) структура проекта, архитектура и пайплайн слотов
116
+ - [docs/troubleshooting.md](./docs/troubleshooting.md) частые проблемы и способы диагностики
117
+ - [CONTRIBUTING.md](./CONTRIBUTING.md) — вклад в проект, проверки и тесты
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import os
4
5
  import shutil
5
6
  import threading
6
7
  import uuid
@@ -77,6 +78,8 @@ class AccountStore:
77
78
  tmp = path.with_suffix(".tmp")
78
79
  tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
79
80
  tmp.replace(path)
81
+ if os.name != "nt":
82
+ os.chmod(path, 0o600)
80
83
 
81
84
  # --- Admin CRUD ---
82
85
 
@@ -91,20 +91,42 @@ class ChatGPTWorkspaceAPI:
91
91
  )
92
92
 
93
93
  def get_pending_invites(self) -> list[dict]:
94
- """Получить список ожидающих инвайтов."""
95
- data = self._request(
96
- "GET",
97
- f"/backend-api/accounts/{self.account_id}/invites?offset=0&limit=100",
94
+ """Получить список ожидающих инвайтов (с пагинацией)."""
95
+ return self._paginate(
96
+ f"/backend-api/accounts/{self.account_id}/invites",
97
+ items_key="invites",
98
98
  )
99
- return data.get("invites", [])
100
99
 
101
100
  def get_members(self) -> list[dict]:
102
- """Получить список участников workspace."""
103
- data = self._request(
104
- "GET",
105
- f"/backend-api/accounts/{self.account_id}/users?offset=0&limit=100",
101
+ """Получить список участников workspace (с пагинацией)."""
102
+ return self._paginate(
103
+ f"/backend-api/accounts/{self.account_id}/users",
104
+ items_key="items",
105
+ fallback_key="users",
106
106
  )
107
- return data.get("items", data.get("users", []))
107
+
108
+ def _paginate(
109
+ self,
110
+ path: str,
111
+ items_key: str,
112
+ fallback_key: str | None = None,
113
+ page_size: int = 100,
114
+ max_pages: int = 20,
115
+ ) -> list[dict]:
116
+ """Fetch all pages from a paginated endpoint."""
117
+ all_items: list[dict] = []
118
+ for page_num in range(max_pages):
119
+ offset = page_num * page_size
120
+ data = self._request("GET", f"{path}?offset={offset}&limit={page_size}")
121
+ items = data.get(items_key) or (data.get(fallback_key, []) if fallback_key else [])
122
+ all_items.extend(items)
123
+ if not data.get("has_more") and len(items) >= page_size:
124
+ continue
125
+ if len(items) < page_size:
126
+ break
127
+ if data.get("has_more") is False:
128
+ break
129
+ return all_items
108
130
 
109
131
  def delete_member(self, user_id: str) -> dict:
110
132
  """Удалить участника из workspace по user_id."""