codex-lb 0.1.2__tar.gz
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.
- codex_lb-0.1.2/.dockerignore +24 -0
- codex_lb-0.1.2/.env.example +30 -0
- codex_lb-0.1.2/.github/release-please-config.json +13 -0
- codex_lb-0.1.2/.github/release-please-manifest.json +3 -0
- codex_lb-0.1.2/.github/workflows/ci.yml +95 -0
- codex_lb-0.1.2/.github/workflows/release-please.yml +25 -0
- codex_lb-0.1.2/.github/workflows/release.yml +226 -0
- codex_lb-0.1.2/.gitignore +37 -0
- codex_lb-0.1.2/.pre-commit-config.yaml +7 -0
- codex_lb-0.1.2/AGENTS.md +35 -0
- codex_lb-0.1.2/CHANGELOG.md +30 -0
- codex_lb-0.1.2/Dockerfile +38 -0
- codex_lb-0.1.2/LICENSE +21 -0
- codex_lb-0.1.2/PKG-INFO +108 -0
- codex_lb-0.1.2/README.md +55 -0
- codex_lb-0.1.2/app/__init__.py +5 -0
- codex_lb-0.1.2/app/cli.py +24 -0
- codex_lb-0.1.2/app/core/__init__.py +0 -0
- codex_lb-0.1.2/app/core/auth/__init__.py +96 -0
- codex_lb-0.1.2/app/core/auth/models.py +49 -0
- codex_lb-0.1.2/app/core/auth/refresh.py +144 -0
- codex_lb-0.1.2/app/core/balancer/__init__.py +19 -0
- codex_lb-0.1.2/app/core/balancer/logic.py +140 -0
- codex_lb-0.1.2/app/core/balancer/types.py +9 -0
- codex_lb-0.1.2/app/core/clients/__init__.py +0 -0
- codex_lb-0.1.2/app/core/clients/http.py +39 -0
- codex_lb-0.1.2/app/core/clients/oauth.py +340 -0
- codex_lb-0.1.2/app/core/clients/proxy.py +265 -0
- codex_lb-0.1.2/app/core/clients/usage.py +143 -0
- codex_lb-0.1.2/app/core/config/__init__.py +0 -0
- codex_lb-0.1.2/app/core/config/settings.py +69 -0
- codex_lb-0.1.2/app/core/crypto.py +37 -0
- codex_lb-0.1.2/app/core/errors.py +73 -0
- codex_lb-0.1.2/app/core/openai/__init__.py +0 -0
- codex_lb-0.1.2/app/core/openai/models.py +122 -0
- codex_lb-0.1.2/app/core/openai/parsing.py +55 -0
- codex_lb-0.1.2/app/core/openai/requests.py +59 -0
- codex_lb-0.1.2/app/core/types.py +4 -0
- codex_lb-0.1.2/app/core/usage/__init__.py +185 -0
- codex_lb-0.1.2/app/core/usage/logs.py +57 -0
- codex_lb-0.1.2/app/core/usage/models.py +35 -0
- codex_lb-0.1.2/app/core/usage/pricing.py +172 -0
- codex_lb-0.1.2/app/core/usage/types.py +95 -0
- codex_lb-0.1.2/app/core/utils/__init__.py +0 -0
- codex_lb-0.1.2/app/core/utils/request_id.py +30 -0
- codex_lb-0.1.2/app/core/utils/retry.py +16 -0
- codex_lb-0.1.2/app/core/utils/sse.py +13 -0
- codex_lb-0.1.2/app/core/utils/time.py +19 -0
- codex_lb-0.1.2/app/db/__init__.py +0 -0
- codex_lb-0.1.2/app/db/models.py +82 -0
- codex_lb-0.1.2/app/db/session.py +44 -0
- codex_lb-0.1.2/app/dependencies.py +123 -0
- codex_lb-0.1.2/app/main.py +124 -0
- codex_lb-0.1.2/app/modules/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/accounts/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/accounts/api.py +81 -0
- codex_lb-0.1.2/app/modules/accounts/repository.py +80 -0
- codex_lb-0.1.2/app/modules/accounts/schemas.py +66 -0
- codex_lb-0.1.2/app/modules/accounts/service.py +211 -0
- codex_lb-0.1.2/app/modules/health/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/health/api.py +10 -0
- codex_lb-0.1.2/app/modules/oauth/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/oauth/api.py +57 -0
- codex_lb-0.1.2/app/modules/oauth/schemas.py +32 -0
- codex_lb-0.1.2/app/modules/oauth/service.py +356 -0
- codex_lb-0.1.2/app/modules/oauth/templates/oauth_success.html +122 -0
- codex_lb-0.1.2/app/modules/proxy/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/proxy/api.py +76 -0
- codex_lb-0.1.2/app/modules/proxy/auth_manager.py +51 -0
- codex_lb-0.1.2/app/modules/proxy/load_balancer.py +208 -0
- codex_lb-0.1.2/app/modules/proxy/schemas.py +85 -0
- codex_lb-0.1.2/app/modules/proxy/service.py +707 -0
- codex_lb-0.1.2/app/modules/proxy/types.py +37 -0
- codex_lb-0.1.2/app/modules/proxy/usage_updater.py +147 -0
- codex_lb-0.1.2/app/modules/request_logs/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/request_logs/api.py +31 -0
- codex_lb-0.1.2/app/modules/request_logs/repository.py +86 -0
- codex_lb-0.1.2/app/modules/request_logs/schemas.py +25 -0
- codex_lb-0.1.2/app/modules/request_logs/service.py +77 -0
- codex_lb-0.1.2/app/modules/shared/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/shared/schemas.py +8 -0
- codex_lb-0.1.2/app/modules/usage/__init__.py +0 -0
- codex_lb-0.1.2/app/modules/usage/api.py +31 -0
- codex_lb-0.1.2/app/modules/usage/repository.py +113 -0
- codex_lb-0.1.2/app/modules/usage/schemas.py +62 -0
- codex_lb-0.1.2/app/modules/usage/service.py +246 -0
- codex_lb-0.1.2/app/static/7.css +1336 -0
- codex_lb-0.1.2/app/static/index.css +543 -0
- codex_lb-0.1.2/app/static/index.html +457 -0
- codex_lb-0.1.2/app/static/index.js +1898 -0
- codex_lb-0.1.2/docker-compose.yml +35 -0
- codex_lb-0.1.2/docs/screenshots/accounts.jpeg +0 -0
- codex_lb-0.1.2/docs/screenshots/dashboard.jpeg +0 -0
- codex_lb-0.1.2/pyproject.toml +79 -0
- codex_lb-0.1.2/tests/__init__.py +0 -0
- codex_lb-0.1.2/tests/conftest.py +61 -0
- codex_lb-0.1.2/tests/integration/test_accounts_api.py +101 -0
- codex_lb-0.1.2/tests/integration/test_accounts_api_extended.py +80 -0
- codex_lb-0.1.2/tests/integration/test_codex_usage_api.py +132 -0
- codex_lb-0.1.2/tests/integration/test_db_models.py +76 -0
- codex_lb-0.1.2/tests/integration/test_health_and_errors.py +30 -0
- codex_lb-0.1.2/tests/integration/test_load_balancer_integration.py +143 -0
- codex_lb-0.1.2/tests/integration/test_oauth_flow.py +136 -0
- codex_lb-0.1.2/tests/integration/test_proxy_api_extended.py +215 -0
- codex_lb-0.1.2/tests/integration/test_proxy_compact.py +124 -0
- codex_lb-0.1.2/tests/integration/test_proxy_responses.py +107 -0
- codex_lb-0.1.2/tests/integration/test_repositories.py +64 -0
- codex_lb-0.1.2/tests/integration/test_request_logs_api.py +76 -0
- codex_lb-0.1.2/tests/integration/test_request_logs_filters.py +259 -0
- codex_lb-0.1.2/tests/integration/test_usage_api.py +197 -0
- codex_lb-0.1.2/tests/integration/test_usage_summary.py +109 -0
- codex_lb-0.1.2/tests/unit/test_auth.py +71 -0
- codex_lb-0.1.2/tests/unit/test_auth_refresh.py +28 -0
- codex_lb-0.1.2/tests/unit/test_load_balancer.py +67 -0
- codex_lb-0.1.2/tests/unit/test_oauth_client.py +46 -0
- codex_lb-0.1.2/tests/unit/test_pricing.py +59 -0
- codex_lb-0.1.2/tests/unit/test_proxy_utils.py +66 -0
- codex_lb-0.1.2/tests/unit/test_retry.py +19 -0
- codex_lb-0.1.2/tests/unit/test_sse.py +13 -0
- codex_lb-0.1.2/tests/unit/test_usage.py +37 -0
- codex_lb-0.1.2/tests/unit/test_usage_client.py +137 -0
- codex_lb-0.1.2/uv.lock +1612 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.gitignore
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.pyc
|
|
5
|
+
*.pyo
|
|
6
|
+
*.pyd
|
|
7
|
+
*.db
|
|
8
|
+
*.sqlite
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
.venv/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.vscode/
|
|
16
|
+
.idea/
|
|
17
|
+
node_modules/
|
|
18
|
+
coverage/
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
htmlcov/
|
|
22
|
+
refs/
|
|
23
|
+
docs/
|
|
24
|
+
tests/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Database
|
|
2
|
+
CODEX_LB_DATABASE_URL=sqlite+aiosqlite:///~/.codex-lb/store.db
|
|
3
|
+
|
|
4
|
+
# Upstream ChatGPT base URL (no /codex suffix)
|
|
5
|
+
CODEX_LB_UPSTREAM_BASE_URL=https://chatgpt.com/backend-api
|
|
6
|
+
|
|
7
|
+
# Timeouts (seconds)
|
|
8
|
+
CODEX_LB_UPSTREAM_CONNECT_TIMEOUT_SECONDS=30
|
|
9
|
+
CODEX_LB_STREAM_IDLE_TIMEOUT_SECONDS=300
|
|
10
|
+
|
|
11
|
+
# OAuth / token refresh
|
|
12
|
+
CODEX_LB_AUTH_BASE_URL=https://auth.openai.com
|
|
13
|
+
CODEX_LB_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
|
|
14
|
+
CODEX_LB_OAUTH_SCOPE="openid profile email"
|
|
15
|
+
CODEX_LB_OAUTH_TIMEOUT_SECONDS=30
|
|
16
|
+
CODEX_LB_OAUTH_REDIRECT_URI=http://localhost:1455/auth/callback
|
|
17
|
+
CODEX_LB_OAUTH_CALLBACK_HOST=127.0.0.1
|
|
18
|
+
# Do not change the port. OpenAI dislikes changes.
|
|
19
|
+
CODEX_LB_OAUTH_CALLBACK_PORT=1455
|
|
20
|
+
CODEX_LB_TOKEN_REFRESH_TIMEOUT_SECONDS=30
|
|
21
|
+
CODEX_LB_TOKEN_REFRESH_INTERVAL_DAYS=8
|
|
22
|
+
|
|
23
|
+
# Encryption key file (optional override; recommended for Docker volumes)
|
|
24
|
+
# CODEX_LB_ENCRYPTION_KEY_FILE=/var/lib/codex-lb/encryption.key
|
|
25
|
+
|
|
26
|
+
# Upstream usage fetch
|
|
27
|
+
CODEX_LB_USAGE_FETCH_TIMEOUT_SECONDS=10
|
|
28
|
+
CODEX_LB_USAGE_FETCH_MAX_RETRIES=2
|
|
29
|
+
CODEX_LB_USAGE_REFRESH_ENABLED=true
|
|
30
|
+
CODEX_LB_USAGE_REFRESH_INTERVAL_SECONDS=60
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint (ruff)
|
|
15
|
+
runs-on: ubuntu-24.04
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.13"
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Ruff check
|
|
28
|
+
run: uvx ruff check .
|
|
29
|
+
|
|
30
|
+
- name: Ruff format (check)
|
|
31
|
+
run: uvx ruff format --check .
|
|
32
|
+
|
|
33
|
+
test:
|
|
34
|
+
name: Tests (pytest)
|
|
35
|
+
runs-on: ubuntu-24.04
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout repository
|
|
39
|
+
uses: actions/checkout@v4
|
|
40
|
+
|
|
41
|
+
- name: Set up uv
|
|
42
|
+
uses: astral-sh/setup-uv@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: "3.13"
|
|
45
|
+
enable-cache: true
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: uv sync --dev --frozen
|
|
49
|
+
|
|
50
|
+
- name: Run tests
|
|
51
|
+
run: uv run pytest
|
|
52
|
+
|
|
53
|
+
package:
|
|
54
|
+
name: Package (build)
|
|
55
|
+
runs-on: ubuntu-24.04
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- name: Checkout repository
|
|
59
|
+
uses: actions/checkout@v4
|
|
60
|
+
|
|
61
|
+
- name: Set up uv
|
|
62
|
+
uses: astral-sh/setup-uv@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.13"
|
|
65
|
+
enable-cache: true
|
|
66
|
+
|
|
67
|
+
- name: Install package (runtime deps)
|
|
68
|
+
run: uv pip install -e .
|
|
69
|
+
|
|
70
|
+
- name: Import check
|
|
71
|
+
run: python -c "import app; import app.main; print('import ok')"
|
|
72
|
+
|
|
73
|
+
- name: Build sdist + wheel
|
|
74
|
+
run: uvx --from build python -m build
|
|
75
|
+
|
|
76
|
+
docker:
|
|
77
|
+
name: Docker build
|
|
78
|
+
runs-on: ubuntu-24.04
|
|
79
|
+
|
|
80
|
+
steps:
|
|
81
|
+
- name: Checkout repository
|
|
82
|
+
uses: actions/checkout@v4
|
|
83
|
+
|
|
84
|
+
- name: Set up Docker Buildx
|
|
85
|
+
uses: docker/setup-buildx-action@v3
|
|
86
|
+
|
|
87
|
+
- name: Build Docker image
|
|
88
|
+
uses: docker/build-push-action@v6
|
|
89
|
+
with:
|
|
90
|
+
context: .
|
|
91
|
+
file: Dockerfile
|
|
92
|
+
push: false
|
|
93
|
+
tags: codex-lb:ci
|
|
94
|
+
cache-from: type=gha
|
|
95
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Release Please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
pull-requests: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
release-please:
|
|
18
|
+
runs-on: ubuntu-24.04
|
|
19
|
+
steps:
|
|
20
|
+
- name: Run release-please
|
|
21
|
+
uses: googleapis/release-please-action@v4
|
|
22
|
+
with:
|
|
23
|
+
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
|
|
24
|
+
config-file: .github/release-please-config.json
|
|
25
|
+
manifest-file: .github/release-please-manifest.json
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
name: Build (sdist + wheel)
|
|
16
|
+
runs-on: ubuntu-24.04
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout repository
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- name: Set up uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.13"
|
|
28
|
+
enable-cache: true
|
|
29
|
+
|
|
30
|
+
- name: Verify tag matches pyproject version
|
|
31
|
+
shell: bash
|
|
32
|
+
run: |
|
|
33
|
+
python - <<'PY'
|
|
34
|
+
import os
|
|
35
|
+
import re
|
|
36
|
+
import sys
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
try:
|
|
39
|
+
import tomllib # py3.11+
|
|
40
|
+
except Exception: # pragma: no cover
|
|
41
|
+
import tomli as tomllib
|
|
42
|
+
|
|
43
|
+
tag_ref = os.environ.get("GITHUB_REF", "")
|
|
44
|
+
tag = tag_ref.split("/")[-1]
|
|
45
|
+
m = re.fullmatch(r"v(\d+\.\d+\.\d+)", tag)
|
|
46
|
+
if not m:
|
|
47
|
+
print(f"Invalid tag format: {tag!r}")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
tag_ver = m.group(1)
|
|
50
|
+
|
|
51
|
+
data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
|
|
52
|
+
proj_ver = data["project"]["version"]
|
|
53
|
+
if proj_ver != tag_ver:
|
|
54
|
+
print(f"pyproject.toml version ({proj_ver}) does not match tag ({tag_ver})")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
print(f"OK: {tag} matches pyproject.toml version {proj_ver}")
|
|
57
|
+
PY
|
|
58
|
+
|
|
59
|
+
- name: Build sdist + wheel
|
|
60
|
+
run: uvx --from build python -m build
|
|
61
|
+
|
|
62
|
+
- name: Upload dist artifacts
|
|
63
|
+
uses: actions/upload-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/*
|
|
67
|
+
|
|
68
|
+
publish-to-pypi:
|
|
69
|
+
name: Publish to PyPI
|
|
70
|
+
needs: [build]
|
|
71
|
+
runs-on: ubuntu-24.04
|
|
72
|
+
|
|
73
|
+
permissions:
|
|
74
|
+
id-token: write
|
|
75
|
+
|
|
76
|
+
environment:
|
|
77
|
+
name: pypi
|
|
78
|
+
url: https://pypi.org/project/codex-lb/
|
|
79
|
+
|
|
80
|
+
steps:
|
|
81
|
+
- name: Download dist artifacts
|
|
82
|
+
uses: actions/download-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
name: dist
|
|
85
|
+
path: dist
|
|
86
|
+
|
|
87
|
+
- name: List dist files
|
|
88
|
+
run: ls -la dist/
|
|
89
|
+
|
|
90
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
91
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
92
|
+
with:
|
|
93
|
+
verbose: true
|
|
94
|
+
print-hash: true
|
|
95
|
+
|
|
96
|
+
docker-image:
|
|
97
|
+
name: Build and push Docker image
|
|
98
|
+
needs: [build]
|
|
99
|
+
runs-on: ubuntu-24.04
|
|
100
|
+
|
|
101
|
+
permissions:
|
|
102
|
+
contents: read
|
|
103
|
+
packages: write
|
|
104
|
+
|
|
105
|
+
steps:
|
|
106
|
+
- name: Checkout repository
|
|
107
|
+
uses: actions/checkout@v4
|
|
108
|
+
|
|
109
|
+
- name: Set up Docker Buildx
|
|
110
|
+
uses: docker/setup-buildx-action@v3
|
|
111
|
+
|
|
112
|
+
- name: Log in to GHCR
|
|
113
|
+
uses: docker/login-action@v3
|
|
114
|
+
with:
|
|
115
|
+
registry: ghcr.io
|
|
116
|
+
username: ${{ github.actor }}
|
|
117
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
118
|
+
|
|
119
|
+
- name: Extract Docker metadata
|
|
120
|
+
id: meta
|
|
121
|
+
uses: docker/metadata-action@v5
|
|
122
|
+
with:
|
|
123
|
+
images: ghcr.io/${{ github.repository }}
|
|
124
|
+
tags: |
|
|
125
|
+
type=semver,pattern={{version}}
|
|
126
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
127
|
+
type=semver,pattern={{major}}
|
|
128
|
+
type=raw,value=latest
|
|
129
|
+
|
|
130
|
+
- name: Build and push Docker image
|
|
131
|
+
uses: docker/build-push-action@v6
|
|
132
|
+
with:
|
|
133
|
+
context: .
|
|
134
|
+
file: Dockerfile
|
|
135
|
+
push: true
|
|
136
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
137
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
138
|
+
cache-from: type=gha
|
|
139
|
+
cache-to: type=gha,mode=max
|
|
140
|
+
|
|
141
|
+
create-github-release:
|
|
142
|
+
name: Create GitHub Release
|
|
143
|
+
needs: [publish-to-pypi, docker-image]
|
|
144
|
+
runs-on: ubuntu-24.04
|
|
145
|
+
|
|
146
|
+
permissions:
|
|
147
|
+
contents: write
|
|
148
|
+
|
|
149
|
+
steps:
|
|
150
|
+
- name: Checkout repository
|
|
151
|
+
uses: actions/checkout@v4
|
|
152
|
+
with:
|
|
153
|
+
fetch-depth: 0
|
|
154
|
+
|
|
155
|
+
- name: Download dist artifacts
|
|
156
|
+
uses: actions/download-artifact@v4
|
|
157
|
+
with:
|
|
158
|
+
name: dist
|
|
159
|
+
path: dist
|
|
160
|
+
|
|
161
|
+
- name: Generate release notes
|
|
162
|
+
id: notes
|
|
163
|
+
shell: bash
|
|
164
|
+
run: |
|
|
165
|
+
set -euo pipefail
|
|
166
|
+
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
|
167
|
+
TAG_VERSION="${TAG_NAME#v}"
|
|
168
|
+
RELEASE_DATE="$(date -u +%Y-%m-%d)"
|
|
169
|
+
|
|
170
|
+
PREV_TAG=""
|
|
171
|
+
if git describe --tags --abbrev=0 "${TAG_NAME}^" >/dev/null 2>&1; then
|
|
172
|
+
PREV_TAG="$(git describe --tags --abbrev=0 "${TAG_NAME}^")"
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
if [ -n "${PREV_TAG}" ]; then
|
|
176
|
+
RANGE="${PREV_TAG}..${TAG_NAME}"
|
|
177
|
+
else
|
|
178
|
+
RANGE="${TAG_NAME}"
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
COMMIT_COUNT="$(git rev-list --count "${RANGE}")"
|
|
182
|
+
|
|
183
|
+
{
|
|
184
|
+
echo "## What's New in ${TAG_NAME}"
|
|
185
|
+
echo
|
|
186
|
+
echo "- Release date: ${RELEASE_DATE}"
|
|
187
|
+
echo "- Commits: ${COMMIT_COUNT}"
|
|
188
|
+
echo
|
|
189
|
+
if [ -n "${PREV_TAG}" ]; then
|
|
190
|
+
echo "### Changes"
|
|
191
|
+
git log --pretty=format:"- %s" "${RANGE}"
|
|
192
|
+
else
|
|
193
|
+
echo "### Changes"
|
|
194
|
+
git log --pretty=format:"- %s" "${RANGE}"
|
|
195
|
+
fi
|
|
196
|
+
echo
|
|
197
|
+
echo
|
|
198
|
+
echo "### Install / Run"
|
|
199
|
+
echo '```bash'
|
|
200
|
+
echo 'uvx codex-lb'
|
|
201
|
+
echo 'pip install codex-lb'
|
|
202
|
+
echo "docker pull ghcr.io/${GITHUB_REPOSITORY}:${TAG_VERSION}"
|
|
203
|
+
echo '```'
|
|
204
|
+
echo
|
|
205
|
+
echo "### Artifacts"
|
|
206
|
+
if ls dist >/dev/null 2>&1; then
|
|
207
|
+
ls -1 dist | sed 's/^/- dist\\//'
|
|
208
|
+
else
|
|
209
|
+
echo "- dist/ (not available)"
|
|
210
|
+
fi
|
|
211
|
+
echo
|
|
212
|
+
echo "### Contributors"
|
|
213
|
+
git shortlog -s "${RANGE}" | awk '{$1=$1}1' | sed 's/^/- /'
|
|
214
|
+
} > release_notes.md
|
|
215
|
+
|
|
216
|
+
echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT"
|
|
217
|
+
|
|
218
|
+
- name: Create GitHub Release
|
|
219
|
+
uses: softprops/action-gh-release@v2
|
|
220
|
+
with:
|
|
221
|
+
tag_name: ${{ steps.notes.outputs.tag_name }}
|
|
222
|
+
name: Release ${{ steps.notes.outputs.tag_name }}
|
|
223
|
+
body_path: release_notes.md
|
|
224
|
+
files: "dist/*"
|
|
225
|
+
draft: false
|
|
226
|
+
prerelease: false
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Project-specific
|
|
2
|
+
refs/
|
|
3
|
+
poc/
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
.venv/
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
|
|
13
|
+
# Env/config
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
.python-version
|
|
18
|
+
|
|
19
|
+
# Build artifacts
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
|
|
23
|
+
# Node
|
|
24
|
+
node_modules/
|
|
25
|
+
|
|
26
|
+
# Editors/OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
*.swp
|
|
31
|
+
*.swo
|
|
32
|
+
*~
|
|
33
|
+
|
|
34
|
+
# Local
|
|
35
|
+
.local/
|
|
36
|
+
|
|
37
|
+
app/static/components
|
codex_lb-0.1.2/AGENTS.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# AGENTS
|
|
2
|
+
|
|
3
|
+
## Environment
|
|
4
|
+
- Python: .venv/bin/python (uv, CPython 3.13.3)
|
|
5
|
+
|
|
6
|
+
## Code Conventions (Typing & Data Contracts)
|
|
7
|
+
- Prefer strict typing end-to-end. Avoid `dict`, `Mapping[str, object]`, and `object` in app/service/repository layers when the shape is known.
|
|
8
|
+
- Use explicit dataclasses or Pydantic models for internal payloads; convert to response schemas at the edge.
|
|
9
|
+
- ORM models should be passed through services instead of generic containers; avoid `getattr`/`[]` access on ORM results.
|
|
10
|
+
- Expose time values in dashboard APIs as ISO 8601 strings (`datetime` in schemas), not epoch numbers.
|
|
11
|
+
- If a test depends on a contract change (field name/type), update the test to match the new typed schema.
|
|
12
|
+
|
|
13
|
+
## Code Conventions (Structure & Responsibilities)
|
|
14
|
+
- Keep domain boundaries clear: `core/` for reusable logic, `modules/*` for API-facing features, `db/` for persistence, `static/` for dashboard assets.
|
|
15
|
+
- Follow module layout conventions in `app/modules/<feature>/`: `api.py` (routes), `service.py` (business logic), `repository.py` (DB access), `schemas.py` (Pydantic I/O models).
|
|
16
|
+
- Prefer small, focused files; split when a file grows beyond a single responsibility or mixes layers.
|
|
17
|
+
- Avoid god-classes: a class should have one reason to change and a narrow public surface.
|
|
18
|
+
- Functions should be single-purpose and side-effect aware; separate pure transformations from I/O.
|
|
19
|
+
- Do not mix API schema construction with persistence/query logic; map data in service layer.
|
|
20
|
+
- Validate inputs early and fail fast with clear errors; never silently coerce invalid types.
|
|
21
|
+
|
|
22
|
+
## Code Conventions (Testing / TC)
|
|
23
|
+
- Add or update tests whenever contracts change (field names/types, response formats, default values).
|
|
24
|
+
- Keep unit tests under `tests/unit` and integration tests under `tests/integration` using existing markers.
|
|
25
|
+
- Tests should assert public behavior (API responses, service outputs) rather than internal implementation details.
|
|
26
|
+
- Use fixtures for DB/session setup; do not introduce network calls outside the test server stubs.
|
|
27
|
+
- Prefer deterministic inputs (fixed timestamps, explicit payloads) to avoid flaky tests.
|
|
28
|
+
|
|
29
|
+
## Code Conventions (DI & Context)
|
|
30
|
+
- Use FastAPI `Depends` providers in `app/dependencies.py` to construct per-request contexts (`*Context` dataclasses).
|
|
31
|
+
- Contexts should hold only the session, repositories, and service for a single module; avoid cross-module service coupling.
|
|
32
|
+
- Repositories must be constructed with the request-scoped `AsyncSession` from `get_session`; no global sessions.
|
|
33
|
+
- Services should be instantiated inside context providers and receive repositories via constructor injection.
|
|
34
|
+
- Background tasks or standalone scripts must create and manage their own session; do not reuse request contexts.
|
|
35
|
+
- When adding a new module, define `api.py` endpoints that depend on a module-specific context provider.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.2](https://github.com/Soju06/codex-lb/compare/v0.1.1...v0.1.2) (2026-01-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* sync package __version__ ([3dd97e6](https://github.com/Soju06/codex-lb/commit/3dd97e6397a8ea9d3528c166d1e729936f98f737))
|
|
9
|
+
|
|
10
|
+
## [0.1.1](https://github.com/Soju06/codex-lb/compare/v0.1.0...v0.1.1) (2026-01-12)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* address lint warnings ([7c3cc06](https://github.com/Soju06/codex-lb/commit/7c3cc06c9a6a9a9a8895c1dd5fcc57b3c0eebdb3))
|
|
16
|
+
* reactivate accounts when secondary quota clears ([58a4263](https://github.com/Soju06/codex-lb/commit/58a42630d644559f96f045a96c25d0126810542e))
|
|
17
|
+
* skip project install in docker build ([64e9156](https://github.com/Soju06/codex-lb/commit/64e9156075c256ef48c0587ea1abb7cc092b97a5))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Documentation
|
|
21
|
+
|
|
22
|
+
* add dashboard hero and accounts view ([3522654](https://github.com/Soju06/codex-lb/commit/3522654fe5d09adbe32895d4b24e8b00faac9dfe))
|
|
23
|
+
|
|
24
|
+
## [0.1.0](https://github.com/Soju06/codex-lb/releases/tag/v0.1.0) (2026-01-07)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* address lint warnings ([7c3cc06](https://github.com/Soju06/codex-lb/commit/7c3cc06c9a6a9a8895c1dd5fcc57b3c0eebdb3))
|
|
30
|
+
* skip project install in docker build ([64e9156](https://github.com/Soju06/codex-lb/commit/64e9156075c256ef48c0587ea1abb7cc092b97a5))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
FROM python:3.13-slim AS build
|
|
3
|
+
|
|
4
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
5
|
+
PYTHONUNBUFFERED=1 \
|
|
6
|
+
UV_PROJECT_ENVIRONMENT=/opt/venv \
|
|
7
|
+
UV_LINK_MODE=copy
|
|
8
|
+
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
RUN python -m venv /opt/venv
|
|
12
|
+
ENV PATH="/opt/venv/bin:$PATH"
|
|
13
|
+
|
|
14
|
+
RUN pip install --no-cache-dir uv
|
|
15
|
+
|
|
16
|
+
COPY pyproject.toml uv.lock ./
|
|
17
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
18
|
+
uv sync --frozen --no-dev --no-install-project
|
|
19
|
+
|
|
20
|
+
FROM python:3.13-slim AS runtime
|
|
21
|
+
|
|
22
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
23
|
+
PYTHONUNBUFFERED=1 \
|
|
24
|
+
PATH="/opt/venv/bin:$PATH"
|
|
25
|
+
|
|
26
|
+
WORKDIR /app
|
|
27
|
+
|
|
28
|
+
RUN adduser --disabled-password --gecos "" app \
|
|
29
|
+
&& mkdir -p /var/lib/codex-lb \
|
|
30
|
+
&& chown -R app:app /var/lib/codex-lb
|
|
31
|
+
|
|
32
|
+
COPY --from=build /opt/venv /opt/venv
|
|
33
|
+
COPY app app
|
|
34
|
+
|
|
35
|
+
USER app
|
|
36
|
+
EXPOSE 2455 1455
|
|
37
|
+
|
|
38
|
+
CMD ["fastapi", "run", "--host", "0.0.0.0", "--port", "2455"]
|
codex_lb-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Soju06
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|