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.
Files changed (122) hide show
  1. codex_lb-0.1.2/.dockerignore +24 -0
  2. codex_lb-0.1.2/.env.example +30 -0
  3. codex_lb-0.1.2/.github/release-please-config.json +13 -0
  4. codex_lb-0.1.2/.github/release-please-manifest.json +3 -0
  5. codex_lb-0.1.2/.github/workflows/ci.yml +95 -0
  6. codex_lb-0.1.2/.github/workflows/release-please.yml +25 -0
  7. codex_lb-0.1.2/.github/workflows/release.yml +226 -0
  8. codex_lb-0.1.2/.gitignore +37 -0
  9. codex_lb-0.1.2/.pre-commit-config.yaml +7 -0
  10. codex_lb-0.1.2/AGENTS.md +35 -0
  11. codex_lb-0.1.2/CHANGELOG.md +30 -0
  12. codex_lb-0.1.2/Dockerfile +38 -0
  13. codex_lb-0.1.2/LICENSE +21 -0
  14. codex_lb-0.1.2/PKG-INFO +108 -0
  15. codex_lb-0.1.2/README.md +55 -0
  16. codex_lb-0.1.2/app/__init__.py +5 -0
  17. codex_lb-0.1.2/app/cli.py +24 -0
  18. codex_lb-0.1.2/app/core/__init__.py +0 -0
  19. codex_lb-0.1.2/app/core/auth/__init__.py +96 -0
  20. codex_lb-0.1.2/app/core/auth/models.py +49 -0
  21. codex_lb-0.1.2/app/core/auth/refresh.py +144 -0
  22. codex_lb-0.1.2/app/core/balancer/__init__.py +19 -0
  23. codex_lb-0.1.2/app/core/balancer/logic.py +140 -0
  24. codex_lb-0.1.2/app/core/balancer/types.py +9 -0
  25. codex_lb-0.1.2/app/core/clients/__init__.py +0 -0
  26. codex_lb-0.1.2/app/core/clients/http.py +39 -0
  27. codex_lb-0.1.2/app/core/clients/oauth.py +340 -0
  28. codex_lb-0.1.2/app/core/clients/proxy.py +265 -0
  29. codex_lb-0.1.2/app/core/clients/usage.py +143 -0
  30. codex_lb-0.1.2/app/core/config/__init__.py +0 -0
  31. codex_lb-0.1.2/app/core/config/settings.py +69 -0
  32. codex_lb-0.1.2/app/core/crypto.py +37 -0
  33. codex_lb-0.1.2/app/core/errors.py +73 -0
  34. codex_lb-0.1.2/app/core/openai/__init__.py +0 -0
  35. codex_lb-0.1.2/app/core/openai/models.py +122 -0
  36. codex_lb-0.1.2/app/core/openai/parsing.py +55 -0
  37. codex_lb-0.1.2/app/core/openai/requests.py +59 -0
  38. codex_lb-0.1.2/app/core/types.py +4 -0
  39. codex_lb-0.1.2/app/core/usage/__init__.py +185 -0
  40. codex_lb-0.1.2/app/core/usage/logs.py +57 -0
  41. codex_lb-0.1.2/app/core/usage/models.py +35 -0
  42. codex_lb-0.1.2/app/core/usage/pricing.py +172 -0
  43. codex_lb-0.1.2/app/core/usage/types.py +95 -0
  44. codex_lb-0.1.2/app/core/utils/__init__.py +0 -0
  45. codex_lb-0.1.2/app/core/utils/request_id.py +30 -0
  46. codex_lb-0.1.2/app/core/utils/retry.py +16 -0
  47. codex_lb-0.1.2/app/core/utils/sse.py +13 -0
  48. codex_lb-0.1.2/app/core/utils/time.py +19 -0
  49. codex_lb-0.1.2/app/db/__init__.py +0 -0
  50. codex_lb-0.1.2/app/db/models.py +82 -0
  51. codex_lb-0.1.2/app/db/session.py +44 -0
  52. codex_lb-0.1.2/app/dependencies.py +123 -0
  53. codex_lb-0.1.2/app/main.py +124 -0
  54. codex_lb-0.1.2/app/modules/__init__.py +0 -0
  55. codex_lb-0.1.2/app/modules/accounts/__init__.py +0 -0
  56. codex_lb-0.1.2/app/modules/accounts/api.py +81 -0
  57. codex_lb-0.1.2/app/modules/accounts/repository.py +80 -0
  58. codex_lb-0.1.2/app/modules/accounts/schemas.py +66 -0
  59. codex_lb-0.1.2/app/modules/accounts/service.py +211 -0
  60. codex_lb-0.1.2/app/modules/health/__init__.py +0 -0
  61. codex_lb-0.1.2/app/modules/health/api.py +10 -0
  62. codex_lb-0.1.2/app/modules/oauth/__init__.py +0 -0
  63. codex_lb-0.1.2/app/modules/oauth/api.py +57 -0
  64. codex_lb-0.1.2/app/modules/oauth/schemas.py +32 -0
  65. codex_lb-0.1.2/app/modules/oauth/service.py +356 -0
  66. codex_lb-0.1.2/app/modules/oauth/templates/oauth_success.html +122 -0
  67. codex_lb-0.1.2/app/modules/proxy/__init__.py +0 -0
  68. codex_lb-0.1.2/app/modules/proxy/api.py +76 -0
  69. codex_lb-0.1.2/app/modules/proxy/auth_manager.py +51 -0
  70. codex_lb-0.1.2/app/modules/proxy/load_balancer.py +208 -0
  71. codex_lb-0.1.2/app/modules/proxy/schemas.py +85 -0
  72. codex_lb-0.1.2/app/modules/proxy/service.py +707 -0
  73. codex_lb-0.1.2/app/modules/proxy/types.py +37 -0
  74. codex_lb-0.1.2/app/modules/proxy/usage_updater.py +147 -0
  75. codex_lb-0.1.2/app/modules/request_logs/__init__.py +0 -0
  76. codex_lb-0.1.2/app/modules/request_logs/api.py +31 -0
  77. codex_lb-0.1.2/app/modules/request_logs/repository.py +86 -0
  78. codex_lb-0.1.2/app/modules/request_logs/schemas.py +25 -0
  79. codex_lb-0.1.2/app/modules/request_logs/service.py +77 -0
  80. codex_lb-0.1.2/app/modules/shared/__init__.py +0 -0
  81. codex_lb-0.1.2/app/modules/shared/schemas.py +8 -0
  82. codex_lb-0.1.2/app/modules/usage/__init__.py +0 -0
  83. codex_lb-0.1.2/app/modules/usage/api.py +31 -0
  84. codex_lb-0.1.2/app/modules/usage/repository.py +113 -0
  85. codex_lb-0.1.2/app/modules/usage/schemas.py +62 -0
  86. codex_lb-0.1.2/app/modules/usage/service.py +246 -0
  87. codex_lb-0.1.2/app/static/7.css +1336 -0
  88. codex_lb-0.1.2/app/static/index.css +543 -0
  89. codex_lb-0.1.2/app/static/index.html +457 -0
  90. codex_lb-0.1.2/app/static/index.js +1898 -0
  91. codex_lb-0.1.2/docker-compose.yml +35 -0
  92. codex_lb-0.1.2/docs/screenshots/accounts.jpeg +0 -0
  93. codex_lb-0.1.2/docs/screenshots/dashboard.jpeg +0 -0
  94. codex_lb-0.1.2/pyproject.toml +79 -0
  95. codex_lb-0.1.2/tests/__init__.py +0 -0
  96. codex_lb-0.1.2/tests/conftest.py +61 -0
  97. codex_lb-0.1.2/tests/integration/test_accounts_api.py +101 -0
  98. codex_lb-0.1.2/tests/integration/test_accounts_api_extended.py +80 -0
  99. codex_lb-0.1.2/tests/integration/test_codex_usage_api.py +132 -0
  100. codex_lb-0.1.2/tests/integration/test_db_models.py +76 -0
  101. codex_lb-0.1.2/tests/integration/test_health_and_errors.py +30 -0
  102. codex_lb-0.1.2/tests/integration/test_load_balancer_integration.py +143 -0
  103. codex_lb-0.1.2/tests/integration/test_oauth_flow.py +136 -0
  104. codex_lb-0.1.2/tests/integration/test_proxy_api_extended.py +215 -0
  105. codex_lb-0.1.2/tests/integration/test_proxy_compact.py +124 -0
  106. codex_lb-0.1.2/tests/integration/test_proxy_responses.py +107 -0
  107. codex_lb-0.1.2/tests/integration/test_repositories.py +64 -0
  108. codex_lb-0.1.2/tests/integration/test_request_logs_api.py +76 -0
  109. codex_lb-0.1.2/tests/integration/test_request_logs_filters.py +259 -0
  110. codex_lb-0.1.2/tests/integration/test_usage_api.py +197 -0
  111. codex_lb-0.1.2/tests/integration/test_usage_summary.py +109 -0
  112. codex_lb-0.1.2/tests/unit/test_auth.py +71 -0
  113. codex_lb-0.1.2/tests/unit/test_auth_refresh.py +28 -0
  114. codex_lb-0.1.2/tests/unit/test_load_balancer.py +67 -0
  115. codex_lb-0.1.2/tests/unit/test_oauth_client.py +46 -0
  116. codex_lb-0.1.2/tests/unit/test_pricing.py +59 -0
  117. codex_lb-0.1.2/tests/unit/test_proxy_utils.py +66 -0
  118. codex_lb-0.1.2/tests/unit/test_retry.py +19 -0
  119. codex_lb-0.1.2/tests/unit/test_sse.py +13 -0
  120. codex_lb-0.1.2/tests/unit/test_usage.py +37 -0
  121. codex_lb-0.1.2/tests/unit/test_usage_client.py +137 -0
  122. 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,13 @@
1
+ {
2
+ "release-type": "python",
3
+ "packages": {
4
+ ".": {
5
+ "release-type": "python",
6
+ "changelog-path": "CHANGELOG.md",
7
+ "extra-files": [
8
+ "app/__init__.py"
9
+ ],
10
+ "skip-github-release": true
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.2"
3
+ }
@@ -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
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.14.10
4
+ hooks:
5
+ - id: ruff
6
+ args: ["--fix"]
7
+ - id: ruff-format
@@ -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.